From 0bf9c617935a0d8eaae2121706037808c020b122 Mon Sep 17 00:00:00 2001 From: A0 Orchestrator Date: Thu, 2 Jul 2026 10:52:40 +0200 Subject: [PATCH] feat: leftsidebar collapsible tool groups + scroll + resizable width --- frontend/src/App.tsx | 3 ++ frontend/src/components/LeftSidebar.tsx | 64 ++++++++++++++++++++++--- frontend/src/styles.css | 25 ++++++++++ frontend/src/types/ui.types.ts | 2 + 4 files changed, 87 insertions(+), 7 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index a43e119..ea1819c 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -149,6 +149,7 @@ const CADEditor: React.FC = ({ projectId, token, onNavigateBack const [mobileRightOpen, setMobileRightOpen] = useState(false); // Desktop sidebar collapse const [leftSidebarCollapsed, setLeftSidebarCollapsed] = useState(false); + const [leftbarWidth, setLeftbarWidth] = useState(200); const [rightSidebarCollapsed, setRightSidebarCollapsed] = useState(false); const [settingsOpen, setSettingsOpen] = useState(false); const [activeDrawerTab, setActiveDrawerTab] = useState('tool'); @@ -1553,6 +1554,8 @@ const CADEditor: React.FC = ({ projectId, token, onNavigateBack onSaveGroupAsBlock={handleSaveGroupAsBlock} onGroup={() => handleRibbonAction('group')} onUngroup={() => handleRibbonAction('ungroup')} + leftbarWidth={leftbarWidth} + onWidthChange={setLeftbarWidth} /> = [ { label: 'Bestuhlung', tools: sectionBestuhlung }, ]; -const LeftSidebar: React.FC = ({ activeTool, onToolChange, selectedTemplate, onTemplateSelect, onCollapse, className, collapsed, onToggleCollapse, onGroup, onUngroup }) => { +const MIN_LEFTBAR_WIDTH = 140; +const MAX_LEFTBAR_WIDTH = 400; + +const LeftSidebar: React.FC = ({ activeTool, onToolChange, selectedTemplate, onTemplateSelect, onCollapse, className, collapsed, onToggleCollapse, onGroup, onUngroup, leftbarWidth = 200, onWidthChange }) => { + const [collapsedSections, setCollapsedSections] = useState>({}); + + const toggleSection = useCallback((label: string) => { + setCollapsedSections((prev) => ({ ...prev, [label]: !prev[label] })); + }, []); + + const handleResizeMouseDown = useCallback((e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + const startClientX = e.clientX; + const startWidth = leftbarWidth; + + const handleMouseMove = (ev: MouseEvent) => { + const delta = ev.clientX - startClientX; + const newWidth = Math.min(MAX_LEFTBAR_WIDTH, Math.max(MIN_LEFTBAR_WIDTH, startWidth + delta)); + document.documentElement.style.setProperty('--leftbar-w', newWidth + 'px'); + if (onWidthChange) { + onWidthChange(newWidth); + } + }; + + const handleMouseUp = () => { + document.removeEventListener('mousemove', handleMouseMove); + document.removeEventListener('mouseup', handleMouseUp); + document.body.style.cursor = ''; + }; + + document.body.style.cursor = 'col-resize'; + document.addEventListener('mousemove', handleMouseMove); + document.addEventListener('mouseup', handleMouseUp); + }, [leftbarWidth, onWidthChange]); + + const renderSectionLabel = (label: string, sectionKey: string) => ( +
toggleSection(sectionKey)} + style={{ cursor: 'pointer', userSelect: 'none', display: 'flex', alignItems: 'center', gap: '4px' }} + > + {collapsedSections[sectionKey] ? '▶' : '▼'} + {label} +
+ ); + return (