import React, { useState, useEffect } from 'react'; import { polygonArea } from '../tools/modification/geometry'; import { formatArea } from '../utils/format'; import type { PropertiesPanelProps } from '../types/ui.types'; import { formatInputValue, parseDistance, type UnitType } from '../utils/format'; import { getActiveDocument } from '../kernel/document/documentService'; const parseNum = (s: string) => parseFloat(s.replace(/[^0-9.\-]/g, '')) || 0; const PropertiesPanel: React.FC = ({ selectedElement, layers, onUpdateProperty, onDelete, unit = 'mm', scaleFactor = 1, }) => { const el = selectedElement; // Local input state — allows user to type freely, synced when element or unit changes const [xInput, setXInput] = useState(''); const [yInput, setYInput] = useState(''); const [wInput, setWInput] = useState(''); const [hInput, setHInput] = useState(''); const [rotInput, setRotInput] = useState(''); const [radiusInput, setRadiusInput] = useState(''); // Sync displayed values when element changes or unit changes useEffect(() => { if (el) { setXInput(formatInputValue(el.x, unit, scaleFactor)); setYInput(formatInputValue(el.y, unit, scaleFactor)); setWInput(formatInputValue(el.width, unit, scaleFactor)); setHInput(formatInputValue(el.height, unit, scaleFactor)); setRotInput(String(el.properties.rotation ?? 0)); const radius = el.properties.radius as number | undefined; setRadiusInput(radius !== undefined ? formatInputValue(radius, unit, scaleFactor) : ''); } }, [el, unit, scaleFactor]); const color = (el?.properties.stroke as string) || '#3b82f6'; const blockName = el?.properties.blockId || ''; const desc = (el?.properties.text as string) || ''; const handleXChange = (val: string) => { setXInput(val); const world = parseDistance(val, unit, scaleFactor); onUpdateProperty('x', world); }; const handleYChange = (val: string) => { setYInput(val); const world = parseDistance(val, unit, scaleFactor); onUpdateProperty('y', world); }; const handleWChange = (val: string) => { setWInput(val); const world = parseDistance(val, unit, scaleFactor); onUpdateProperty('width', world); }; const handleHChange = (val: string) => { setHInput(val); const world = parseDistance(val, unit, scaleFactor); onUpdateProperty('height', world); }; const handleRadiusChange = (val: string) => { setRadiusInput(val); const world = parseDistance(val, unit, scaleFactor); onUpdateProperty('radius', world); }; return ( <>
Auswahl {el ? `· ${el.type} {((el.type === 'polygon' || el.type === 'polyline') && ((el.properties.points as Array<{ x: number; y: number }> | undefined)?.length ?? 0) >= 3) && (
{formatArea(polygonArea(el.properties.points as Array<{ x: number; y: number }>), unit, scaleFactor)}
)} {/* ─── Task D12: Block-Attribute-Formular (block_instance) ─── */} {el.type === 'block_instance' && (() => { const blockId = el.properties.blockId as string | undefined; if (!blockId) return null; const block = getActiveDocument()?.getBlock(blockId); const attrDefs = block?.attrDefs ?? []; if (attrDefs.length === 0) return null; const attrValues = (el.properties.attrValues as Record | undefined) ?? {}; return (
Block-Attribute
{attrDefs.map((def) => (
{ const next = { ...attrValues, [def.tag]: ev.target.value }; onUpdateProperty('attrValues', next); }} />
))}
); })()}` : ''}
{onDelete && el && ( )}
Geometrie
Position X ({unit}) handleXChange(e.target.value)} />
Position Y ({unit}) handleYChange(e.target.value)} />
Breite ({unit}) handleWChange(e.target.value)} />
Tiefe ({unit}) handleHChange(e.target.value)} />
{el?.type === 'circle' && (
Radius ({unit}) handleRadiusChange(e.target.value)} />
)}
Drehung (°) { setRotInput(e.target.value); onUpdateProperty('rotation', parseNum(e.target.value)); }} />
Darstellung
Farbe
onUpdateProperty('stroke', e.target.value)} /> onUpdateProperty('stroke', e.target.value)} />
Linientyp
Stärke
Layer
{el?.type === 'block_instance' && (
Block
Block-Name onUpdateProperty('blockId', e.target.value)} />
Beschreibung onUpdateProperty('text', e.target.value)} />
)} ); }; export default PropertiesPanel;