feat: click-drag drawing, layer tree, settings modal, UI improvements

- interaction: click-drag drawing for 2-point tools (line, rect, circle, dimension, leader)
- interaction: preview rendering during drag with setPreviewElement
- interaction: handleDrawDown only sets phase=drawing on first point
- CanvasArea: sync activeLayerId to LayerManager
- CanvasArea: callback refresh useEffect prevents stale closures
- App.tsx: functional setElements updates for handleElementCreated/Deleted/Modified
- LayerPanel: tree structure with sub-layers (onReorder, onAddSubLayer)
- TreeView: drag-and-drop reordering support
- RightSidebar: onReorder, onAddSubLayer, onUpdateElement props
- PropertiesPanel: onUpdateElement prop
- SettingsModal: personal/password/language/theme/users/plugins/AI tabs
- styles.css: extensive UI styling additions
- types: updated RightSidebarProps, CanvasAreaProps, LayerPanelProps
This commit is contained in:
2026-06-27 14:12:37 +02:00
parent 39422a4348
commit 4303076ce4
17 changed files with 929 additions and 144 deletions
+15 -57
View File
@@ -4,12 +4,6 @@ import type { CADLayer } from '../types/cad.types';
import type { TreeNode } from '../types/ui.types';
import TreeView from './TreeView';
const lineTypeOptions: Array<{ value: 'solid' | 'dashed' | 'dotted'; label: string }> = [
{ value: 'solid', label: 'Solid' },
{ value: 'dashed', label: 'Dashed' },
{ value: 'dotted', label: 'Dotted' },
];
const LayerPanel: React.FC<LayerPanelProps> = ({
layers,
activeLayerId,
@@ -20,9 +14,8 @@ const LayerPanel: React.FC<LayerPanelProps> = ({
onRenameLayer,
onDuplicateLayer,
onToggleLock,
onUpdateLayerColor,
onUpdateLayerLineType,
onUpdateLayerTransparency,
onAddSubLayer,
onReorder,
}) => {
const [editingId, setEditingId] = useState<string | null>(null);
const [editName, setEditName] = useState('');
@@ -98,20 +91,19 @@ const LayerPanel: React.FC<LayerPanelProps> = ({
</button>
)}
{/* Color swatch */}
{onUpdateLayerColor && (
<label
className="layer-color-swatch"
title="Ebenenfarbe"
style={{ backgroundColor: layer.color, display: 'inline-block', width: '14px', height: '14px', borderRadius: '3px', cursor: 'pointer', border: '1px solid var(--color-border)' }}
{/* Add sub-layer button */}
{onAddSubLayer && (
<button
className="layer-action-btn"
title="Unter-Ebene hinzufügen"
aria-label="Unter-Ebene hinzufügen"
onClick={(e) => {
e.stopPropagation();
onAddSubLayer(layer.id);
}}
>
<input
type="color"
value={layer.color}
onChange={(e) => onUpdateLayerColor(layer.id, e.target.value)}
style={{ opacity: 0, width: 0, height: 0 }}
/>
</label>
<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>
)}
</React.Fragment>
);
@@ -140,36 +132,6 @@ const LayerPanel: React.FC<LayerPanelProps> = ({
return (
<div className="layer-detail-row" style={{ display: 'flex', gap: '6px', padding: '2px 8px', alignItems: 'center', fontSize: '11px', color: 'var(--color-text-muted)' }}>
{/* Line type selector */}
{onUpdateLayerLineType && (
<select
value={layer.lineType}
onChange={(e) => onUpdateLayerLineType(layer.id, e.target.value as 'solid' | 'dashed' | 'dotted')}
style={{ fontSize: '10px', padding: '1px 4px' }}
title="Linientyp"
>
{lineTypeOptions.map((opt) => (
<option key={opt.value} value={opt.value}>{opt.label}</option>
))}
</select>
)}
{/* Transparency slider */}
{onUpdateLayerTransparency && (
<React.Fragment>
<input
type="range"
min="0"
max="100"
value={layer.transparency}
onChange={(e) => onUpdateLayerTransparency(layer.id, parseInt(e.target.value, 10))}
style={{ width: '60px' }}
title={`Transparenz: ${layer.transparency}%`}
/>
<span style={{ minWidth: '28px' }}>{layer.transparency}%</span>
</React.Fragment>
)}
{/* Rename button */}
{onRenameLayer && (
<button
@@ -226,10 +188,6 @@ const LayerPanel: React.FC<LayerPanelProps> = ({
style={{ display: 'flex', alignItems: 'center', gap: '6px', padding: '4px 8px', cursor: 'pointer' }}
onClick={() => onSelectLayer(layer.id)}
>
<span
className="layer-color-dot"
style={{ width: '10px', height: '10px', borderRadius: '2px', backgroundColor: layer.color, flexShrink: 0 }}
/>
<span className="layer-name" style={{ flex: 1, fontSize: '12px' }}>{layer.name}</span>
{renderLayerActions({ id: layer.id, name: layer.name })}
</div>
@@ -247,7 +205,7 @@ const LayerPanel: React.FC<LayerPanelProps> = ({
</button>
</div>
<div className="tree tree-layer" role="tree" aria-label="Layer-Struktur">
<TreeView nodes={nodes} selectedId={activeLayerId} onSelect={onSelectLayer} onToggle={onToggleLayer} renderActions={renderLayerActions} renderDetail={(node) => { const layer = layers.find(l => l.id === node.id); return layer ? renderLayerDetail(layer) : null; }} />
<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>