← Components/Layouts

SplitPane

splitpane-1779379671390.tsx
'use client'
import { useState, useRef, useCallback, useEffect } from 'react'

export default function SplitPane() {
  const [split, setSplit] = useState(50)
  const [dragging, setDragging] = useState(false)
  const [collapsed, setCollapsed] = useState<'left' | 'right' | null>(null)
  const containerRef = useRef<HTMLDivElement>(null)

  const startDrag = useCallback((e: React.MouseEvent) => {
    e.preventDefault()
    setDragging(true)
    setCollapsed(null)
  }, [])

  useEffect(() => {
    if (!dragging) return
    function onMove(e: MouseEvent) {
      if (!containerRef.current) return
      const rect = containerRef.current.getBoundingClientRect()
      const pct = ((e.clientX - rect.left) / rect.width) * 100
      setSplit(Math.max(15, Math.min(85, pct)))
    }
    function onUp() { setDragging(false) }
    window.addEventListener('mousemove', onMove)
    window.addEventListener('mouseup', onUp)
    return () => { window.removeEventListener('mousemove', onMove); window.removeEventListener('mouseup', onUp) }
  }, [dragging])

  const leftW = collapsed === 'left' ? 0 : collapsed === 'right' ? 100 : split
  const rightW = 100 - leftW

  return (
    <div style={{ background: '#0A0A0A', padding: 24, display: 'flex', flexDirection: 'column', gap: 16 }}>
      <div style={{ display: 'flex', gap: 8 }}>
        {(['left', 'right'] as const).map(side => (
          <button key={side} onClick={() => setCollapsed(c => c === side ? null : side)}
            style={{ background: 'rgba(255,255,255,0.06)', border: '1px solid rgba(255,255,255,0.1)', color: '#F5F5F0', borderRadius: 6, padding: '5px 12px', cursor: 'pointer', fontSize: 12 }}>
            {collapsed === side ? 'Expand' : 'Collapse'} {side}
          </button>
        ))}
      </div>

      <div ref={containerRef} style={{ position: 'relative', display: 'flex', height: 300, background: '#111', border: '1px solid rgba(255,255,255,0.06)', borderRadius: 12, overflow: 'hidden', cursor: dragging ? 'col-resize' : 'default', userSelect: 'none' }}>
        {/* Left panel */}
        <div style={{ width: `${leftW}%`, background: '#111', overflow: 'hidden', transition: dragging ? 'none' : 'width 0.25s ease', borderRight: leftW > 0 && rightW > 0 ? '1px solid rgba(255,255,255,0.06)' : 'none' }}>
          <div style={{ padding: 20, height: '100%' }}>
            <div style={{ color: '#C9A84C', fontSize: 11, textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: 12 }}>File Explorer</div>
            {['src/', 'components/', 'GlowButton.tsx', 'HoverCard.tsx', 'public/', 'package.json'].map(f => (
              <div key={f} style={{ color: f.endsWith('/') ? '#6366f1' : 'rgba(255,255,255,0.6)', fontSize: 13, padding: '4px 0', paddingLeft: f.endsWith('/') || f === 'package.json' ? 0 : 16 }}>{f}</div>
            ))}
          </div>
        </div>

        {/* Divider */}
        {leftW > 0 && rightW > 0 && (
          <div onMouseDown={startDrag} style={{ width: 4, background: dragging ? '#C9A84C' : 'transparent', cursor: 'col-resize', flexShrink: 0, position: 'relative', zIndex: 10, transition: 'background 0.15s' }}>
            <div style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%,-50%)', width: 16, height: 32, display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'rgba(255,255,255,0.2)', fontSize: 10 }}>⋮⋮</div>
          </div>
        )}

        {/* Right panel */}
        <div style={{ flex: 1, overflow: 'hidden', transition: dragging ? 'none' : 'flex 0.25s ease' }}>
          <div style={{ padding: 20 }}>
            <div style={{ color: '#C9A84C', fontSize: 11, textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: 12 }}>GlowButton.tsx</div>
            <pre style={{ color: 'rgba(255,255,255,0.6)', fontSize: 12, margin: 0, fontFamily: 'monospace', lineHeight: 1.7 }}>{"'use client'
import { useState } from 'react'

export default function GlowButton() {
  const [h, setH] = useState(false)
  return (
    <button
      onMouseEnter={() => setH(true)}
      onMouseLeave={() => setH(false)}
      style={{ boxShadow: h
        ? '0 0 20px #C9A84C'
        : 'none' }}
    >
      Click me
    </button>
  )
}"}</pre>
          </div>
        </div>
      </div>
      <div style={{ color: 'rgba(255,255,255,0.3)', fontSize: 12, textAlign: 'center' }}>Drag divider to resize · {Math.round(leftW)}% / {Math.round(rightW)}%</div>
    </div>
  )
}

Component info

CategoryLayouts
Frameworkreact
TierFREE
Views0
Copies0

About

Resizable split pane layout with drag divider, min/max constraints, and collapse support

More from Layouts

'use client'
import { useState } from 'react'

interface Block {
  id: number
  label: string
  color: string
  colSpan: number
  rowSpan: number
}

const INITIAL: Block[] = [
  { id: 1, label: 'Header', color: '#C9A84C', colSpan: 12, rowSpan: 1 },
 
GridLayout
Layouts