feat: delete function, sidebar collapse, layer drag&drop, library tree
- Delete: trash button in PropertiesPanel + existing Delete key support - Left sidebar: desktop collapse toggle button - Right sidebar: desktop collapse toggle + SVG icon size fix (replaced emoji) - Layer panel: drag&drop enabled (reorder, move to layers, sub-layers) - Library tree: BlockLibrary added to left sidebar - App.tsx: handleReorderLayer, handleAddSubLayer, handleUpdateElement - styles.css: collapse + drag&drop visual indicators
This commit is contained in:
+103
-1
@@ -147,6 +147,9 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
|
||||
// Mobile drawers
|
||||
const [mobileLeftOpen, setMobileLeftOpen] = useState(false);
|
||||
const [mobileRightOpen, setMobileRightOpen] = useState(false);
|
||||
// Desktop sidebar collapse
|
||||
const [leftSidebarCollapsed, setLeftSidebarCollapsed] = useState(false);
|
||||
const [rightSidebarCollapsed, setRightSidebarCollapsed] = useState(false);
|
||||
const [settingsOpen, setSettingsOpen] = useState(false);
|
||||
const [activeDrawerTab, setActiveDrawerTab] = useState<DrawerTab>('tool');
|
||||
|
||||
@@ -792,6 +795,89 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
|
||||
setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Zoom: an Ansicht angepasst', type: 'info' }]);
|
||||
}, []);
|
||||
|
||||
// Layer reorder (drag & drop)
|
||||
const handleReorderLayer = useCallback((draggedId: string, targetId: string, position: 'before' | 'after' | 'inside') => {
|
||||
setLayers((prev) => {
|
||||
const dragged = prev.find((l) => l.id === draggedId);
|
||||
if (!dragged) return prev;
|
||||
|
||||
if (position === 'inside') {
|
||||
// Make dragged a child of target
|
||||
const updated = prev.map((l) =>
|
||||
l.id === draggedId ? { ...l, parentId: targetId } : l
|
||||
);
|
||||
return updated;
|
||||
}
|
||||
|
||||
// before/after: reorder at same level
|
||||
const target = prev.find((l) => l.id === targetId);
|
||||
if (!target) return prev;
|
||||
|
||||
const parentId = target.parentId;
|
||||
const siblings = prev
|
||||
.filter((l) => l.parentId === parentId && l.id !== draggedId)
|
||||
.sort((a, b) => a.sortOrder - b.sortOrder);
|
||||
|
||||
const targetIdx = siblings.findIndex((l) => l.id === targetId);
|
||||
const insertIdx = position === 'before' ? targetIdx : targetIdx + 1;
|
||||
|
||||
siblings.splice(insertIdx, 0, { ...dragged, parentId });
|
||||
|
||||
// Reassign sortOrder
|
||||
const reordered = siblings.map((l, i) => ({ ...l, sortOrder: i }));
|
||||
|
||||
// Merge back: non-siblings keep original, siblings get reordered
|
||||
const nonSiblings = prev.filter((l) => l.parentId !== parentId || l.id === draggedId);
|
||||
const result = [...nonSiblings.filter((l) => l.id !== draggedId), ...reordered];
|
||||
return result;
|
||||
});
|
||||
setCommandHistory((prev) => [...prev, { prefix: '·', text: 'Ebene verschoben', type: 'info' }]);
|
||||
}, []);
|
||||
|
||||
// Add sub-layer
|
||||
const handleAddSubLayer = useCallback((parentId: string) => {
|
||||
setLayers((prev) => {
|
||||
const parent = prev.find((l) => l.id === parentId);
|
||||
if (!parent) return prev;
|
||||
const newId = `layer-${Date.now()}`;
|
||||
const siblings = prev.filter((l) => l.parentId === parentId);
|
||||
const newLayer: CADLayer = {
|
||||
id: newId, name: `Teilebene ${siblings.length + 1}`, visible: true, locked: false,
|
||||
color: parent.color, lineType: 'solid', transparency: 0,
|
||||
sortOrder: siblings.length, parentId,
|
||||
};
|
||||
if (drawingId && token) {
|
||||
createLayerTyped(token, drawingId, newLayer).catch((err) => {
|
||||
console.error('Failed to save sub-layer:', err);
|
||||
});
|
||||
}
|
||||
return [...prev, newLayer];
|
||||
});
|
||||
}, [drawingId, token]);
|
||||
|
||||
// Update single element (from properties panel)
|
||||
const handleUpdateElement = useCallback((updated: CADElement) => {
|
||||
setElements((prev) => {
|
||||
const newElements = prev.map((e) => (e.id === updated.id ? updated : e));
|
||||
historyManagerRef.current.pushSnapshot({
|
||||
elements: newElements, layers, blocks, groups, bgConfig,
|
||||
}, 'Element geändert');
|
||||
return newElements;
|
||||
});
|
||||
syncHistory();
|
||||
collab.setElement(updated);
|
||||
if (token) {
|
||||
setSavedStatus('Speichert…');
|
||||
updateElement(token, updated.id, updated).then(() => {
|
||||
setSavedStatus('gespeichert');
|
||||
}).catch((err) => {
|
||||
console.error('Failed to update element:', err);
|
||||
setSavedStatus('Fehler beim Speichern');
|
||||
});
|
||||
}
|
||||
setSelectedElement(updated);
|
||||
}, [layers, blocks, groups, bgConfig, syncHistory, token, collab]);
|
||||
|
||||
const handleBgApply = useCallback((config: BackgroundConfig, _image: HTMLImageElement | null) => {
|
||||
setBgConfig(config);
|
||||
setBgImportOpen(false);
|
||||
@@ -950,7 +1036,7 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
|
||||
onTabChange={setActiveRibbonTab}
|
||||
onAction={handleRibbonAction}
|
||||
/>
|
||||
<div className="app-body">
|
||||
<div className={`app-body${leftSidebarCollapsed ? ' left-collapsed' : ''}${rightSidebarCollapsed ? ' right-collapsed' : ''}`}>
|
||||
<LeftSidebar
|
||||
activeTool={activeTool}
|
||||
onToolChange={handleToolChange}
|
||||
@@ -958,6 +1044,17 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
|
||||
onTemplateSelect={handleTemplateSelect}
|
||||
className={mobileLeftOpen ? 'open' : ''}
|
||||
onCollapse={() => setMobileLeftOpen(false)}
|
||||
collapsed={leftSidebarCollapsed}
|
||||
onToggleCollapse={() => setLeftSidebarCollapsed((p) => !p)}
|
||||
blocks={blocks}
|
||||
onDragBlock={handleDragBlock}
|
||||
onBlockCategoryChange={handleBlockCategoryChange}
|
||||
onBlockSearch={handleBlockSearch}
|
||||
onRenameBlock={handleRenameBlock}
|
||||
onDuplicateBlock={handleDuplicateBlock}
|
||||
onDeleteBlock={handleDeleteBlock}
|
||||
onSvgImport={handleSvgImport}
|
||||
onSaveGroupAsBlock={handleSaveGroupAsBlock}
|
||||
/>
|
||||
<CanvasArea
|
||||
cursorPos={cursorPos}
|
||||
@@ -1022,6 +1119,11 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
|
||||
kiLoading={kiLoading}
|
||||
className={mobileRightOpen ? 'open' : ''}
|
||||
onCollapse={() => setMobileRightOpen(false)}
|
||||
collapsed={rightSidebarCollapsed}
|
||||
onToggleCollapse={() => setRightSidebarCollapsed((p) => !p)}
|
||||
onReorder={handleReorderLayer}
|
||||
onAddSubLayer={handleAddSubLayer}
|
||||
onUpdateElement={handleUpdateElement}
|
||||
/>
|
||||
</div>
|
||||
<CommandLine
|
||||
|
||||
Reference in New Issue
Block a user