Components layout

by jwerre

HTML

<div id="main-container">
  <aside id="left-column">
    <header>
      SurveyPlanet
      <input type="search" />
    </header>
    <div class="sp-menu" role="menu">
      <ul>
        <li class="sp-menu--item sp-menu--item--submenu">
          <button class="sp-menu--item--btn" data-id="actions">
            <span class="sp-menu--item--label">Actions</span>
          </button>
        </li>
        <li class="sp-menu--item">
          <button class="sp-menu--item--btn" data-id="alert">
            <span class="sp-menu--item--label">Alert</span>
          </button>
        </li>
        <li class="sp-menu--item">
          <button class="sp-menu--item--btn" data-id="avatar">
            <span class="sp-menu--item--label">Avatar</span>
          </button>
        </li>
        <li class="sp-menu--item">
          <button class="sp-menu--item--btn" data-id="badge">
            <span class="sp-menu--item--label">Badge</span>
          </button>
        </li>
        <li class="sp-menu--item">
          <button class="sp-menu--item--btn">
            <span class="sp-menu--item--label">...</span>
          </button>
        </li>
        <li class="sp-menu--item">
          <button class="sp-menu--item--btn">
            <span class="sp-menu--item--label">...</span>
          </button>
        </li>
        <li class="sp-menu--item">
          <button class="sp-menu--item--btn">
            <span class="sp-menu--item--label">...</span>
          </button>
        </li>
        <li class="sp-menu--item">
          <button class="sp-menu--item--btn">
            <span class="sp-menu--item--label">...</span>
          </button>
        </li>
        <li class="sp-menu--item">
          <button class="sp-menu--item--btn">
            <span class="sp-menu--item--label">...</span>
          </button>
        </li>
        <li class="sp-menu--item">
          <button class="sp-menu--item--btn">
            <span class="sp-menu--item--label">...</span>
          </button>
  ...

CSS

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: Arial, sans-serif;
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
ul {
	list-style: none;
}
p {
 margin-bottom: 12px;
}

#main-container {
  display: flex;
  flex: 1;
  overflow: hidden;
}

#left-column {
  width: 250px;
  background-color: #f5f5f5;
  padding: 20px;
  overflow-y: auto;
  border-right: 1px solid #ddd;
  transition: width 0.3s ease;
}

#left-column.collapsed {
  width: 0;
  padding: 0;
  overflow: hidden;
}

#right-column {
  width: 400px;
  background-color: #e9e9e9;
  padding: 20px;
  overflow-y: auto;
  position: relative;
}

#main-content {
  flex: 1;
  padding: 20px;
  overflow-y: auto;
  background-color: white;
}

#main-content--header {
	border-bottom: 1px solid #ccc;
}

#main-content--nav ul {
	display: flex;
	gap: 10px;
}

#right-column--handle {
  width: 8px;
  background-color: #ddd;
  cursor: col-resize;
  display: flex;
  align-items: center;
  justify-content: center;
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
}

#right-column--handle::after {
  content: "";
  width: 2px;
  height: 30px;
  background-color: #999;
}

#right-column--handle:hover {
  background-color: #ccc;
}



#main-footer {
  background-color: #333;
  color: white;
  overflow: hidden;
  transition: height 0.3s;
  position: relative;
}

#main-footer--handle {
  height: 10px;
  width: 100%;
  background-color: #555;
  cursor: ns-resize;
  display: flex;
  justify-content: center;
  align-items: center;
}

#main-footer--handle::before {
  content: "";
  width: 30px;
  height: 2px;
  background-color: #ccc;
}

#main-footer--handle:hover {
  background-color: #444;
}

#main-footer--content {
  padding: 10px 20px;
}

.collapsed {
  height: 10px;
}

.expanded {
  height: 150px;
}

JavaScript

// Initialize elements
const leftColumn = document.getElementById("left-column")
const rightColumn = document.getElementById("right-column")
// const leftResizeHandle = document.getElementById("left-resize")
const rightResizeHandle = document.getElementById("right-column--handle")
const footer = document.getElementById("main-footer")
const footerHandle = document.getElementById("main-footer--handle")
const leftToggle = document.getElementById("left-toggle")
const toggleIcon = leftToggle.querySelector("button")

// Right column resize functionality
let isRightResizing = false
let rightStartX
let rightStartWidth

rightResizeHandle.addEventListener("mousedown", (e) => {
  isRightResizing = true
  rightStartX = e.clientX
  rightStartWidth = parseInt(getComputedStyle(rightColumn).width, 10)
  document.body.style.cursor = "col-resize"
})



leftToggle.addEventListener("click", () => {
  leftColumn.classList.toggle("collapsed")
  if (leftColumn.classList.contains("collapsed")) {
    toggleIcon.textContent = "▶"
  } else {
    toggleIcon.textContent = "◀"
  }
})

// Footer resize functionality
let isFooterResizing = false
let footerStartY
let footerStartHeight

footerHandle.addEventListener("mousedown", (e) => {
  isFooterResizing = true
  footerStartY = e.clientY
  footerStartHeight = parseInt(getComputedStyle(footer).height, 10)
  document.body.style.cursor = "ns-resize"
  e.preventDefault()
})

// Toggle footer on click
footerHandle.addEventListener("click", () => {
  if (!isFooterResizing) {
    // Only toggle if we're not resizing
    footer.classList.toggle("collapsed")
    footer.classList.toggle("expanded")
  }
})

// Handle all resizing with mousemove
document.addEventListener("mousemove", (e) => {
  // Right column resize
  if (isRightResizing) {
    const width = rightStartWidth - (e.clientX - rightStartX)
    if (width > 100 && width < window.innerWidth - 400) {
      rightColumn.style.width = `${width}px`
    }
  }

  // Footer resize
  if (isFooterResizing)...