Search code examples
javascripthtmlcsslayoutresponsive-design

resizeable Window not expand fully due to flex-grow


I'm designing a layout similar to the sidebar of the CodePen. While I've managed to replicate most of its behavior, I'm facing an issue when sliding windows upwards—everything works smoothly. However, when sliding downwards, the containers at the bottom fail to expand fully, unlike the demonstration in the provided code. Could someone point out where I might be making an error and help me rectify it?

function handleEditorWindowResize () {

    const parent = document.querySelector("aside")
    const editorWindows = document.querySelectorAll("aside > div")
    const labelHeight = document.querySelector(".lang-label")
        .getBoundingClientRect().height-4

    const handleResize = (ele)=> {
        let startY =0, height = 0;
        
        const disableInteraction = (ele, state)=> {
            if(state) {
                ele.style.userSelect = 'none';
                ele.style.pointerEvents = 'none';
            } else {
                ele.style.removeProperty('user-select');
                ele.style.removeProperty('pointer-events');
            }
        }

        const handleMouseDown = (e)=> {
            startY = e.clientY - 2; // 2 coming from cuz we have 2px border of hr in css
            height = ele.previousElementSibling.getBoundingClientRect().height;

            editorWindows.forEach((ele) => {
                disableInteraction(ele, true)
            });

            window.addEventListener("mousemove", handleMouseMove);
            window.addEventListener("mouseup", handleMouseUp);

            ele.style.borderWidth = `5px`;

        }
        
        const handleMouseMove = (e)=> {
            const dy = e.clientY - startY;
            const h = ((height + dy) * 100) / parent.getBoundingClientRect().height;
            ele.previousElementSibling.style.height = `calc(max(${h}%, ${labelHeight}px)`;

            editorWindows.forEach(win=> {
                if(
                    win === ele.nextElementSibling 
                ) {
                    win.style.flex = '1';
                    win.style.height = `${win.clientHeight}px`;
                } else {
                    win.style.removeProperty("flex")
                    win.style.height = `${win.clientHeight}px`
                }
            })
        }   
        
        const handleMouseUp = (e)=> {
            editorWindows.forEach(ele => disableInteraction(ele, false));
            window.removeEventListener("mousemove", handleMouseMove);
            window.removeEventListener("mouseup", handleMouseUp);
            ele.style.borderWidth = `2px`;
        }

        ele.addEventListener("mousedown",handleMouseDown);
    }

    document.querySelectorAll("aside > .h-divider")
        .forEach(ele => handleResize(ele));
}

handleEditorWindowResize()
:root {
  --black: #111;
  --maroon: rgb(117, 14, 33);
  --orange: rgb(227, 101, 29);
  --green: rgb(190, 215, 84);
  --text-color: rgba(255,255,255,1);
  --light-text-color: rgba(255,255,255,0.8);
  --overlay-color: rgba(255,255,255,0.1);

  --gray-100: #111;
  --gray-400: #444;
}

body, html {
  width: 100%;
  height: 100%;
  overflow-x: hidden;
}

main {
  display: flex;
  width: 100%;
  height: 100%;
}

.divider {
  background-color: var(--gray-400);
  cursor: ew-resize;
  height: 100%;
  width: 8px;
}

aside {
  width: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

aside > div {
  width: 100%;
  display: flex;
  flex-direction: column;
  height: calc(100% / 3);
  position: relative;
}  

aside > div:last-child {
  /* flex: 1; */
  /* height: auto; */
}

aside > .h-divider {
  border: 2px solid var(--gray-400);
  position: sticky;
  cursor: row-resize;
}

aside textarea {
  overflow-y: auto;
  min-width: 100%;
  max-width: 100%;
  flex:1;
  padding: 0.5rem;
  font-size: 0.8em;
  background: var(--black);
  border: none;
  font-weight: 300;
  border-bottom: 1px solid var(--gray-400);
  background: var(--gray-100);
  color: var(--light-text-color);
}

aside textarea:focus { outline: none; }

.lang-label {
  padding: 0.3rem;
  background-color: var(--maroon);
  font-weight: 700;
  display: flex;
  align-items: center; 
  gap: 0.3rem;
  position: relative;
}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
  
  <!-- main layout -->

  <main>
    <!-- code -->
    <aside>
      <div>
        <div class="lang-label">
          HTML
        </div>
        <textarea data-lang="html"></textarea>
      </div>
      <span class="h-divider"></span>

      <div>
        <div class="lang-label">
          CSS
        </div>
        <textarea data-lang="css"></textarea>
      </div>
      
      <span class="h-divider"></span>

      <div>
        <div class="lang-label">
          JS
        </div>
        <textarea data-lang="js"></textarea>
      </div>
    </aside>
  </main>
</body>
</html>


Solution

  • Consider applying a small min-height to the aside > div elements. They implicitly have min-height: min-content due to the vertical flex layout, so applying some other a small min-height should enable the elements to shrink.

    For example, here it is with min-height: 0:

    function handleEditorWindowResize () {
    
        const parent = document.querySelector("aside")
        const editorWindows = document.querySelectorAll("aside > div")
        const labelHeight = document.querySelector(".lang-label")
            .getBoundingClientRect().height-4
    
        const handleResize = (ele)=> {
            let startY =0, height = 0;
            
            const disableInteraction = (ele, state)=> {
                if(state) {
                    ele.style.userSelect = 'none';
                    ele.style.pointerEvents = 'none';
                } else {
                    ele.style.removeProperty('user-select');
                    ele.style.removeProperty('pointer-events');
                }
            }
    
            const handleMouseDown = (e)=> {
                startY = e.clientY - 2; // 2 coming from cuz we have 2px border of hr in css
                height = ele.previousElementSibling.getBoundingClientRect().height;
    
                editorWindows.forEach((ele) => {
                    disableInteraction(ele, true)
                });
    
                window.addEventListener("mousemove", handleMouseMove);
                window.addEventListener("mouseup", handleMouseUp);
    
                ele.style.borderWidth = `5px`;
    
            }
            
            const handleMouseMove = (e)=> {
                const dy = e.clientY - startY;
                const h = ((height + dy) * 100) / parent.getBoundingClientRect().height;
                ele.previousElementSibling.style.height = `calc(max(${h}%, ${labelHeight}px)`;
    
                editorWindows.forEach(win=> {
                    if(
                        win === ele.nextElementSibling 
                    ) {
                        win.style.flex = '1';
                        win.style.height = `${win.clientHeight}px`;
                    } else {
                        win.style.removeProperty("flex")
                        win.style.height = `${win.clientHeight}px`
                    }
                })
            }   
            
            const handleMouseUp = (e)=> {
                editorWindows.forEach(ele => disableInteraction(ele, false));
                window.removeEventListener("mousemove", handleMouseMove);
                window.removeEventListener("mouseup", handleMouseUp);
                ele.style.borderWidth = `2px`;
            }
    
            ele.addEventListener("mousedown",handleMouseDown);
        }
    
        document.querySelectorAll("aside > .h-divider")
            .forEach(ele => handleResize(ele));
    }
    
    handleEditorWindowResize()
    :root {
      --black: #111;
      --maroon: rgb(117, 14, 33);
      --orange: rgb(227, 101, 29);
      --green: rgb(190, 215, 84);
      --text-color: rgba(255,255,255,1);
      --light-text-color: rgba(255,255,255,0.8);
      --overlay-color: rgba(255,255,255,0.1);
    
      --gray-100: #111;
      --gray-400: #444;
    }
    
    body, html {
      width: 100%;
      height: 100%;
      overflow-x: hidden;
    }
    
    main {
      display: flex;
      width: 100%;
      height: 100%;
    }
    
    .divider {
      background-color: var(--gray-400);
      cursor: ew-resize;
      height: 100%;
      width: 8px;
    }
    
    aside {
      width: 100%;
      display: flex;
      flex-direction: column;
      overflow: hidden;
    }
    
    aside > div {
      width: 100%;
      display: flex;
      flex-direction: column;
      height: calc(100% / 3);
      position: relative;
      min-height: 0;
    }  
    
    aside > div:last-child {
      /* flex: 1; */
      /* height: auto; */
    }
    
    aside > .h-divider {
      border: 2px solid var(--gray-400);
      position: sticky;
      cursor: row-resize;
    }
    
    aside textarea {
      overflow-y: auto;
      min-width: 100%;
      max-width: 100%;
      flex:1;
      padding: 0.5rem;
      font-size: 0.8em;
      background: var(--black);
      border: none;
      font-weight: 300;
      border-bottom: 1px solid var(--gray-400);
      background: var(--gray-100);
      color: var(--light-text-color);
    }
    
    aside textarea:focus { outline: none; }
    
    .lang-label {
      padding: 0.3rem;
      background-color: var(--maroon);
      font-weight: 700;
      display: flex;
      align-items: center; 
      gap: 0.3rem;
      position: relative;
    }
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
      
      <!-- main layout -->
    
      <main>
        <!-- code -->
        <aside>
          <div>
            <div class="lang-label">
              HTML
            </div>
            <textarea data-lang="html"></textarea>
          </div>
          <span class="h-divider"></span>
    
          <div>
            <div class="lang-label">
              CSS
            </div>
            <textarea data-lang="css"></textarea>
          </div>
          
          <span class="h-divider"></span>
    
          <div>
            <div class="lang-label">
              JS
            </div>
            <textarea data-lang="js"></textarea>
          </div>
        </aside>
      </main>
    </body>
    </html>

    The lang-label gets hidden though, so perhaps have the min-height be set to the height of the lang-label.