feat: layer panel with element tree - shows layers + elements as children with count badges, sub-layers supported

This commit is contained in:
2026-06-27 14:27:34 +02:00
parent 4303076ce4
commit affa6be151
5 changed files with 1228 additions and 183 deletions
+1
View File
@@ -980,6 +980,7 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
onRenameLayer={handleRenameLayer} onRenameLayer={handleRenameLayer}
onDuplicateLayer={handleDuplicateLayer} onDuplicateLayer={handleDuplicateLayer}
onToggleLock={handleToggleLock} onToggleLock={handleToggleLock}
elements={elements}
onRenameBlock={handleRenameBlock} onRenameBlock={handleRenameBlock}
onDuplicateBlock={handleDuplicateBlock} onDuplicateBlock={handleDuplicateBlock}
onDeleteBlock={handleDeleteBlock} onDeleteBlock={handleDeleteBlock}
File diff suppressed because it is too large Load Diff
+126 -161
View File
@@ -1,11 +1,62 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import type { LayerPanelProps } from '../types/ui.types'; import type { LayerPanelProps } from '../types/ui.types';
import type { CADLayer } from '../types/cad.types'; import type { CADLayer, CADElement } from '../types/cad.types';
import type { TreeNode } from '../types/ui.types'; import type { TreeNode } from '../types/ui.types';
import TreeView from './TreeView'; import TreeView from './TreeView';
const elementTypeLabels: Record<string, string> = {
line: 'Linie',
rect: 'Rechteck',
circle: 'Kreis',
arc: 'Bogen',
polyline: 'Polylinie',
polygon: 'Polygon',
text: 'Text',
dimension: 'Bemassung',
leader: 'Hinweislinie',
revcloud: 'Revisionswolke',
hatch: 'Schraffur',
chair: 'Stuhl',
'seating-row': 'Reihe',
'seating-block': 'Block',
table: 'Tisch',
stage: 'Buhne',
'seating-template': 'Vorlage',
};
const layerIcon = (
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<polygon points="12,2 22,8 12,14 2,8" />
<polyline points="2,16 12,22 22,16" />
<polyline points="2,12 12,18 22,12" />
</svg>
);
const elementIcons: Record<string, React.ReactNode> = {
line: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><line x1="4" y1="20" x2="20" y2="4"/></svg>,
rect: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="4" y="4" width="16" height="16"/></svg>,
circle: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="12" cy="12" r="8"/></svg>,
arc: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M4 20 A16 16 0 0 1 20 4"/></svg>,
polyline: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><polyline points="4,18 8,8 14,14 20,6"/></svg>,
polygon: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><polygon points="12,4 20,10 16,20 8,20 4,10"/></svg>,
text: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M4 7V4h16v3M9 20h6M12 4v16"/></svg>,
dimension: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M4 16 L20 16 M4 16 L4 12 M20 16 L20 12 M8 16 L8 14 M16 16 L16 14"/></svg>,
leader: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M4 4 L12 12 L12 20"/><circle cx="4" cy="4" r="2"/></svg>,
revcloud: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M4 16 Q6 12 10 14 Q12 10 16 14 Q20 12 20 16 Q20 20 16 20 L8 20 Q4 20 4 16Z"/></svg>,
hatch: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="4" y="4" width="16" height="16"/><line x1="4" y1="8" x2="20" y2="8"/><line x1="4" y1="12" x2="20" y2="12"/><line x1="4" y1="16" x2="20" y2="16"/></svg>,
chair: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M6 4v8h12V4 M6 12v8 M18 12v8 M4 12h16"/></svg>,
'seating-row': <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="2" y="8" width="4" height="8"/><rect x="8" y="8" width="4" height="8"/><rect x="14" y="8" width="4" height="8"/><rect x="20" y="8" width="2" height="8"/></svg>,
'seating-block': <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="2" y="4" width="6" height="6"/><rect x="10" y="4" width="6" height="6"/><rect x="2" y="12" width="6" height="6"/><rect x="10" y="12" width="6" height="6"/></svg>,
table: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="4" y="6" width="16" height="12"/><line x1="4" y1="10" x2="20" y2="10"/><line x1="4" y1="14" x2="20" y2="14"/></svg>,
stage: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M4 18 L12 6 L20 18Z"/></svg>,
'seating-template': <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="4" y="4" width="16" height="16"/><circle cx="8" cy="8" r="1"/><circle cx="12" cy="8" r="1"/><circle cx="16" cy="8" r="1"/><circle cx="8" cy="12" r="1"/><circle cx="12" cy="12" r="1"/><circle cx="16" cy="12" r="1"/></svg>,
};
const defaultIcon = <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="12" cy="12" r="8"/></svg>;
const LayerPanel: React.FC<LayerPanelProps> = ({ const LayerPanel: React.FC<LayerPanelProps> = ({
layers, layers,
elements = [],
activeLayerId, activeLayerId,
onSelectLayer, onSelectLayer,
onAddLayer, onAddLayer,
@@ -14,203 +65,117 @@ const LayerPanel: React.FC<LayerPanelProps> = ({
onRenameLayer, onRenameLayer,
onDuplicateLayer, onDuplicateLayer,
onToggleLock, onToggleLock,
onAddSubLayer,
onReorder, onReorder,
onAddSubLayer,
}) => { }) => {
const [editingId, setEditingId] = useState<string | null>(null);
const [editName, setEditName] = useState('');
// Build tree nodes from layers (parentId hierarchy)
const buildTree = (parentId: string | null): TreeNode[] => { const buildTree = (parentId: string | null): TreeNode[] => {
return layers return layers
.filter((l) => l.parentId === parentId) .filter((l) => l.parentId === parentId)
.sort((a, b) => a.sortOrder - b.sortOrder) .sort((a, b) => a.sortOrder - b.sortOrder)
.map((l) => ({ .map((l) => {
const layerElements = elements.filter((e) => e.layerId === l.id);
const elementNodes: TreeNode[] = layerElements.map((e) => ({
id: e.id,
name: elementTypeLabels[e.type] || e.type,
icon: elementIcons[e.type] || defaultIcon,
expanded: false,
active: false,
children: [],
}));
const subLayerNodes = buildTree(l.id);
return {
id: l.id, id: l.id,
name: l.name, name: l.name,
icon: layerIcon,
expanded: true, expanded: true,
active: l.id === activeLayerId, active: l.id === activeLayerId,
children: buildTree(l.id), children: [...subLayerNodes, ...elementNodes],
})); count: layerElements.length > 0 ? layerElements.length : undefined,
} as TreeNode;
});
}; };
const nodes = buildTree(null); const tree = buildTree(null);
const handleStartRename = (layer: CADLayer) => { const handleAddSubLayer = (parentId: string) => {
setEditingId(layer.id); if (onAddSubLayer) {
setEditName(layer.name); onAddSubLayer(parentId);
};
const handleConfirmRename = () => {
if (editingId && onRenameLayer) {
onRenameLayer(editingId, editName);
} }
setEditingId(null);
setEditName('');
}; };
const renderLayerActions = (node: TreeNode) => {
const layer = layers.find((l) => l.id === node.id);
if (!layer) return null;
return ( return (
<React.Fragment> <div className="layer-panel" style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
{/* Visibility toggle */} <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '8px 12px', borderBottom: '1px solid var(--border-color, #333)' }}>
<span style={{ fontWeight: 600, fontSize: '13px' }}>Ebenen</span>
<button <button
className="layer-action-btn" onClick={onAddLayer}
title={layer.visible ? 'Verstecken' : 'Anzeigen'} title="Neue Ebene"
aria-label={layer.visible ? 'Verstecken' : 'Anzeigen'} style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--text-color, #ccc)', padding: '4px' }}
onClick={(e) => {
e.stopPropagation();
onToggleLayer(layer.id);
}}
> >
{layer.visible ? ( <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg> <line x1="12" y1="5" x2="12" y2="19"/>
) : ( <line x1="5" y1="12" x2="19" y2="12"/>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/><line x1="1" y1="1" x2="23" y2="23"/></svg> </svg>
)}
</button> </button>
</div>
{/* Lock toggle */} <div style={{ flex: 1, overflow: 'auto', padding: '4px 0' }}>
{onToggleLock && ( {tree.length === 0 ? (
<div style={{ padding: '12px', textAlign: 'center', color: 'var(--text-muted, #888)', fontSize: '12px' }}>
Keine Ebenen vorhanden
</div>
) : (
<TreeView
nodes={tree}
selectedId={activeLayerId}
onSelect={onSelectLayer}
onReorder={onReorder}
renderIcon={(node) => node.icon}
renderActions={(node) => {
const layer = layers.find((l) => l.id === node.id);
if (!layer) return null;
return (
<div style={{ display: 'flex', gap: '2px', opacity: 0.6 }}>
<button <button
className="layer-action-btn" onClick={(e) => { e.stopPropagation(); onToggleLayer(layer.id); }}
title={layer.locked ? 'Entsperren' : 'Sperren'} title={layer.visible ? 'Sichtbar' : 'Versteckt'}
aria-label={layer.locked ? 'Entsperren' : 'Sperren'} style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '2px', color: layer.visible ? 'var(--text-color, #ccc)' : 'var(--text-muted, #666)' }}
onClick={(e) => {
e.stopPropagation();
onToggleLock(layer.id);
}}
> >
{layer.locked ? ( {layer.visible ? '👁' : '🚫'}
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
) : (
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 9.9-1"/></svg>
)}
</button> </button>
)}
{/* Add sub-layer button */}
{onAddSubLayer && (
<button <button
className="layer-action-btn" onClick={(e) => { e.stopPropagation(); onToggleLock?.(layer.id); }}
title="Unter-Ebene hinzufügen" title={layer.locked ? 'Gesperrt' : 'Entsperrt'}
aria-label="Unter-Ebene hinzufügen" style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '2px', color: layer.locked ? 'var(--accent, #f59e0b)' : 'var(--text-muted, #666)' }}
onClick={(e) => { >
e.stopPropagation(); {layer.locked ? '🔒' : '🔓'}
onAddSubLayer(layer.id); </button>
}} <button
onClick={(e) => { e.stopPropagation(); handleAddSubLayer(layer.id); }}
title="Teilebene hinzufugen"
style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '2px', color: 'var(--text-color, #ccc)' }}
> >
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg> <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
</button> </button>
)}
</React.Fragment>
);
};
const renderLayerDetail = (layer: CADLayer) => {
if (editingId === layer.id) {
return (
<div className="layer-edit-row" style={{ display: 'flex', gap: '4px', padding: '4px 8px' }}>
<input
type="text"
value={editName}
onChange={(e) => setEditName(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') handleConfirmRename();
if (e.key === 'Escape') setEditingId(null);
}}
autoFocus
style={{ flex: 1, fontSize: '12px' }}
/>
<button onClick={handleConfirmRename} title="Bestätigen"></button>
<button onClick={() => setEditingId(null)} title="Abbrechen"></button>
</div>
);
}
return (
<div className="layer-detail-row" style={{ display: 'flex', gap: '6px', padding: '2px 8px', alignItems: 'center', fontSize: '11px', color: 'var(--color-text-muted)' }}>
{/* Rename button */}
{onRenameLayer && (
<button <button
className="layer-action-btn" onClick={(e) => { e.stopPropagation(); onDuplicateLayer?.(layer.id); }}
title="Umbenennen"
onClick={(e) => {
e.stopPropagation();
handleStartRename(layer);
}}
>
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
</button>
)}
{/* Duplicate button */}
{onDuplicateLayer && (
<button
className="layer-action-btn"
title="Duplizieren" title="Duplizieren"
onClick={(e) => { style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '2px', color: 'var(--text-color, #ccc)' }}
e.stopPropagation();
onDuplicateLayer(layer.id);
}}
> >
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
</button> </button>
)}
{/* Delete button */}
{onDeleteLayer && (
<button <button
className="layer-action-btn" onClick={(e) => { e.stopPropagation(); onDeleteLayer?.(layer.id); }}
title="Löschen" title="Loschen"
onClick={(e) => { style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '2px', color: '#f44336' }}
e.stopPropagation();
onDeleteLayer(layer.id);
}}
> >
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
</button> </button>
)}
</div> </div>
); );
}; }}
/>
// Render expanded layer list with details )}
const renderLayerList = () => {
return layers
.sort((a, b) => a.sortOrder - b.sortOrder)
.map((layer) => (
<div key={layer.id} className={`layer-item${layer.id === activeLayerId ? ' active' : ''}`}>
<div
className="layer-item-header"
style={{ display: 'flex', alignItems: 'center', gap: '6px', padding: '4px 8px', cursor: 'pointer' }}
onClick={() => onSelectLayer(layer.id)}
>
<span className="layer-name" style={{ flex: 1, fontSize: '12px' }}>{layer.name}</span>
{renderLayerActions({ id: layer.id, name: layer.name })}
</div> </div>
{renderLayerDetail(layer)}
</div>
));
};
return (
<div className="panel-section">
<div className="panel-section-title">
<span>Ebenen-Struktur</span>
<button style={{ color: 'var(--color-text-muted)' }} title="Optionen" aria-label="Layer-Optionen">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
</button>
</div>
<div className="tree tree-layer" role="tree" aria-label="Layer-Struktur">
<TreeView nodes={nodes} selectedId={activeLayerId} onSelect={onSelectLayer} onToggle={onToggleLayer} onReorder={onReorder} draggable={true} renderActions={renderLayerActions} renderDetail={(node) => { const layer = layers.find(l => l.id === node.id); return layer ? renderLayerDetail(layer) : null; }} />
</div>
<button className="add-layer-btn" onClick={onAddLayer}>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
Ebene hinzufügen
</button>
</div> </div>
); );
}; };
+2 -1
View File
@@ -17,6 +17,7 @@ const RightSidebar: React.FC<RightSidebarProps> = ({
activeLayerId, onSelectLayer, onAddLayer, onToggleLayer, activeLayerId, onSelectLayer, onAddLayer, onToggleLayer,
onDeleteLayer, onRenameLayer, onDuplicateLayer, onToggleLock, onDeleteLayer, onRenameLayer, onDuplicateLayer, onToggleLock,
onReorder, onAddSubLayer, onReorder, onAddSubLayer,
elements,
onRenameBlock, onDuplicateBlock, onDeleteBlock, onSvgImport, onSaveGroupAsBlock, onRenameBlock, onDuplicateBlock, onDeleteBlock, onSvgImport, onSaveGroupAsBlock,
onBlockCategoryChange, onBlockSearch, onDragBlock, onBlockCategoryChange, onBlockSearch, onDragBlock,
kiMessages, kiSuggestions, onKISend, onKISuggestionClick, kiLoading, onUpdateElement, kiMessages, kiSuggestions, onKISend, onKISuggestionClick, kiLoading, onUpdateElement,
@@ -56,7 +57,7 @@ const RightSidebar: React.FC<RightSidebarProps> = ({
}} />} }} />}
</div> </div>
<div className={`rightbar-panel${activePanel === 'layer' ? ' active' : ''}`} data-panel-content="layer"> <div className={`rightbar-panel${activePanel === 'layer' ? ' active' : ''}`} data-panel-content="layer">
{activePanel === 'layer' && <LayerPanel layers={layers} activeLayerId={activeLayerId} onSelectLayer={onSelectLayer ?? (() => {})} onAddLayer={onAddLayer ?? (() => {})} onToggleLayer={onToggleLayer ?? (() => {})} onDeleteLayer={onDeleteLayer} onRenameLayer={onRenameLayer} onDuplicateLayer={onDuplicateLayer} onToggleLock={onToggleLock} onReorder={onReorder} onAddSubLayer={onAddSubLayer} />} {activePanel === 'layer' && <LayerPanel layers={layers} elements={elements} activeLayerId={activeLayerId} onSelectLayer={onSelectLayer ?? (() => {})} onAddLayer={onAddLayer ?? (() => {})} onToggleLayer={onToggleLayer ?? (() => {})} onDeleteLayer={onDeleteLayer} onRenameLayer={onRenameLayer} onDuplicateLayer={onDuplicateLayer} onToggleLock={onToggleLock} onReorder={onReorder} onAddSubLayer={onAddSubLayer} />}
</div> </div>
<div className={`rightbar-panel${activePanel === 'library' ? ' active' : ''}`} data-panel-content="library"> <div className={`rightbar-panel${activePanel === 'library' ? ' active' : ''}`} data-panel-content="library">
{activePanel === 'library' && <BlockLibrary blocks={blocks} category="Alle" onCategoryChange={onBlockCategoryChange ?? (() => {})} onSearch={onBlockSearch ?? (() => {})} onDragBlock={onDragBlock ?? (() => {})} onRenameBlock={onRenameBlock} onDuplicateBlock={onDuplicateBlock} onDeleteBlock={onDeleteBlock} onSvgImport={onSvgImport} onSaveGroupAsBlock={onSaveGroupAsBlock} />} {activePanel === 'library' && <BlockLibrary blocks={blocks} category="Alle" onCategoryChange={onBlockCategoryChange ?? (() => {})} onSearch={onBlockSearch ?? (() => {})} onDragBlock={onDragBlock ?? (() => {})} onRenameBlock={onRenameBlock} onDuplicateBlock={onDuplicateBlock} onDeleteBlock={onDeleteBlock} onSvgImport={onSvgImport} onSaveGroupAsBlock={onSaveGroupAsBlock} />}
+2
View File
@@ -124,6 +124,7 @@ export interface RightSidebarProps {
onPanelChange: (panel: RightPanel) => void; onPanelChange: (panel: RightPanel) => void;
selectedElement: CADElement | null; selectedElement: CADElement | null;
layers: CADLayer[]; layers: CADLayer[];
elements?: CADElement[];
blocks: BlockDefinition[]; blocks: BlockDefinition[];
activeLayerId?: string; activeLayerId?: string;
onSelectLayer?: (id: string) => void; onSelectLayer?: (id: string) => void;
@@ -162,6 +163,7 @@ export interface PropertiesPanelProps {
export interface LayerPanelProps { export interface LayerPanelProps {
layers: CADLayer[]; layers: CADLayer[];
elements?: CADElement[];
activeLayerId?: string; activeLayerId?: string;
onSelectLayer: (id: string) => void; onSelectLayer: (id: string) => void;
onAddLayer: () => void; onAddLayer: () => void;