diff --git a/frontend/src/components/layout/Sidebar.tsx b/frontend/src/components/layout/Sidebar.tsx index b48c5b88..ddf9b6e3 100644 --- a/frontend/src/components/layout/Sidebar.tsx +++ b/frontend/src/components/layout/Sidebar.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useRef, useState } from 'react' +import { useCallback, useEffect, useRef, useState, type PointerEvent as ReactPointerEvent } from 'react' import { Link, useLocation } from 'react-router-dom' import type { LucideIcon } from 'lucide-react' import { @@ -185,6 +185,33 @@ export function Sidebar() { if (flyoutTimeout.current) clearTimeout(flyoutTimeout.current) } + /* ── Drawer resize ───────────────────────────────── */ + + const [drawerWidth, setDrawerWidth] = useState(240) + const isResizing = useRef(false) + + const handleResizeStart = (e: ReactPointerEvent) => { + e.preventDefault() + isResizing.current = true + const startX = e.clientX + const startWidth = drawerWidth + + const onMove = (ev: globalThis.PointerEvent) => { + if (!isResizing.current) return + const newWidth = Math.max(180, Math.min(400, startWidth + (ev.clientX - startX))) + setDrawerWidth(newWidth) + } + + const onUp = () => { + isResizing.current = false + document.removeEventListener('pointermove', onMove) + document.removeEventListener('pointerup', onUp) + } + + document.addEventListener('pointermove', onMove) + document.addEventListener('pointerup', onUp) + } + /* Close flyout on Escape */ useEffect(() => { const handleKey = (e: KeyboardEvent) => { @@ -247,45 +274,7 @@ export function Sidebar() { - {/* Flyout panel (icon rail only) */} - {hasChildren && !sidebarPinned && flyoutIndex === key && ( -
-
- {item.children!.map(child => ( - - {child.label} - {child.count !== undefined && ( - {child.count} - )} - - ))} -
-
- )} + {/* Flyout rendered as drawer below */} ) } @@ -364,15 +353,12 @@ export function Sidebar() { ) } - /* ── Flyout positioning: use data attribute for lookup ── */ + /* ── Find active flyout group for drawer ── */ - const renderRailItemWithRef = (item: NavEntry, key: string) => { - return ( -
- {renderRailItem(item, key + '-inner')} -
- ) - } + const activeFlyoutGroup = flyoutIndex && !sidebarPinned + ? railGroups.find((_, i) => `rail-${i}` === flyoutIndex) || + footerItems.find((_, i) => `footer-${i}` === flyoutIndex) + : null /* ── Main render ──────────────────────────────────── */ @@ -419,40 +405,91 @@ export function Sidebar() { /* Icon Rail (default) — 5 grouped icons, Sentry-style */ return ( - +
+ {/* Drawer header */} +
+

+ {activeFlyoutGroup.label} +

+
+ + {/* Drawer items */} +
+ {activeFlyoutGroup.children.map(child => ( + + {child.label} + {child.count !== undefined && ( + {child.count} + )} + + ))} +
+
+ + {/* Resize handle */} +
+
+ )} + ) }