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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user