task(CAD-14): phase 4a - tool options store + panel (optionsSchema-driven UI in right sidebar, dispatcher getOptions wired, registry defaults seeded) - truss options length/chordCount/angle now actually reach the tools
This commit is contained in:
@@ -20,6 +20,7 @@ import { setActiveDocument, getActiveDocument } from '../kernel/document/documen
|
||||
import { V2_TOOLS_ENABLED, USE_PIXI_RENDERER } from '../kernel/featureFlags';
|
||||
import { PixiRenderer } from '../render/pixi/PixiRenderer';
|
||||
import { pluginRegistry } from '../plugins';
|
||||
import { getToolOptions, setToolOptionsDefaults, defaultsFromSchema } from '../services/toolOptionsService';
|
||||
|
||||
const CanvasArea: React.FC<CanvasAreaProps> = ({
|
||||
cursorPos, viewMode, onViewChange, gridEnabled, orthoEnabled, snapEnabled,
|
||||
@@ -169,6 +170,9 @@ const CanvasArea: React.FC<CanvasAreaProps> = ({
|
||||
},
|
||||
setPreview: (el) => renderEngine.setPreviewElement(el as CADElement | null),
|
||||
getDoc: () => v2Doc,
|
||||
// CAD-14 Phase 4a: Werkzeug-Optionen aus dem zentralen Store
|
||||
// (Laenge/Breite/chordCount/Winkel... erreichen die V2-Werkzeuge).
|
||||
getOptions: (toolId: string) => getToolOptions(toolId),
|
||||
getSelectionBridge: () => ({
|
||||
getIds: () => Array.from(selectionEngine.getSelectedIds()),
|
||||
setIds: (ids) => selectionEngine.selectByIds(ids, false),
|
||||
@@ -223,6 +227,14 @@ const CanvasArea: React.FC<CanvasAreaProps> = ({
|
||||
pixi!.setSelection({ selectedIds: ids, elements: selected });
|
||||
};
|
||||
}
|
||||
// CAD-14 Phase 4a: Defaults aus den optionsSchemas aller V2-Werkzeuge
|
||||
// in den toolOptionsStore fuellen (Quelle fuer OptionsPanel + Dispatcher).
|
||||
for (const tool of pluginRegistry.getToolsV2()) {
|
||||
if (tool.optionsSchema?.length) {
|
||||
setToolOptionsDefaults(tool.manifest.id, defaultsFromSchema(tool.optionsSchema as never));
|
||||
}
|
||||
}
|
||||
|
||||
logger.info('InteractionDispatcher v2 aktiviert (VITE_TOOL_DISPATCHER=v2)');
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useCallback } from 'react';
|
||||
import type { RightSidebarProps, RightPanel } from '../types/ui.types';
|
||||
import type { LibraryProviderExtension } from '../plugins/types';
|
||||
import PropertiesPanel from './PropertiesPanel';
|
||||
import ToolOptionsPanel from './ToolOptionsPanel';
|
||||
import LayerPanel from './LayerPanel';
|
||||
import BlockLibrary from './BlockLibrary';
|
||||
import BlockLibraryTree from './BlockLibraryTree';
|
||||
@@ -23,6 +24,7 @@ const tabs: Array<{ id: RightPanel; label: string; svg: React.ReactNode; badge?:
|
||||
|
||||
const RightSidebar: React.FC<RightSidebarProps> = ({
|
||||
activePanel, onPanelChange, selectedElement, layers, blocks,
|
||||
activeToolId,
|
||||
className,
|
||||
activeLayerId, onSelectLayer, onAddLayer, onToggleLayer,
|
||||
onDeleteLayer, onRenameLayer, onDuplicateLayer, onToggleLock,
|
||||
@@ -116,6 +118,7 @@ const RightSidebar: React.FC<RightSidebarProps> = ({
|
||||
</div>
|
||||
<div className="rightbar-content">
|
||||
<div className={`rightbar-panel${activePanel === 'tool' ? ' active' : ''}`} data-panel-content="tool">
|
||||
<ToolOptionsPanel activeToolId={activeToolId} />
|
||||
{activePanel === 'tool' && selectedElement && onUpdateElement && (
|
||||
<PropertiesPanel selectedElement={selectedElement} layers={layers} unit={unit} scaleFactor={scaleFactor} onDelete={selectedElement && onElementsDeleted ? () => onElementsDeleted([selectedElement.id]) : undefined} onUpdateProperty={(key, value) => {
|
||||
if (!selectedElement || !onUpdateElement) return;
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* CAD-14 Phase 4a - Werkzeug-Optionen-Panel.
|
||||
*
|
||||
* Rendert das optionsSchema des aktiven V2-Werkzeugs und bindet
|
||||
* die Werte an den toolOptionsService (Quelle fuer den Dispatcher).
|
||||
*/
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import { pluginRegistry } from '../plugins';
|
||||
import { getToolOption, setToolOption, subscribeToolOptions } from '../services/toolOptionsService';
|
||||
|
||||
interface ToolOptionsPanelProps {
|
||||
activeToolId: string | null | undefined;
|
||||
}
|
||||
|
||||
const ToolOptionsPanel: React.FC<ToolOptionsPanelProps> = ({ activeToolId }) => {
|
||||
const [, forceRender] = useState(0);
|
||||
|
||||
// Store-Abonnement: Panel bei Optionsaenderung neu rendern
|
||||
React.useEffect(() => {
|
||||
return subscribeToolOptions(() => forceRender((n) => n + 1));
|
||||
}, []);
|
||||
|
||||
const tool = useMemo(() => {
|
||||
if (!activeToolId) return null;
|
||||
return pluginRegistry.getToolV2(activeToolId) ?? null;
|
||||
}, [activeToolId]);
|
||||
|
||||
const schema = tool?.optionsSchema ?? [];
|
||||
if (!tool || schema.length === 0) return null;
|
||||
|
||||
const renderField = (field: { key: string; label: string; type: string; min?: number; max?: number; options?: Array<{ value: string; label: string }> }) => {
|
||||
const value = getToolOption(tool.manifest.id, field.key);
|
||||
const onChange = (v: unknown) => setToolOption(tool.manifest.id, field.key, v);
|
||||
if (field.type === 'number') {
|
||||
return (
|
||||
<label key={field.key} className="toolopt-row">
|
||||
<span>{field.label}</span>
|
||||
<input
|
||||
type="number"
|
||||
value={typeof value === 'number' ? value : Number(value) || 0}
|
||||
min={field.min}
|
||||
onChange={(e) => onChange(Number(e.target.value))}
|
||||
/>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
if (field.type === 'color') {
|
||||
return (
|
||||
<label key={field.key} className="toolopt-row">
|
||||
<span>{field.label}</span>
|
||||
<input type="color" value={String(value ?? '#9aa0a6')} onChange={(e) => onChange(e.target.value)} />
|
||||
</label>
|
||||
);
|
||||
}
|
||||
if (field.type === 'select' && field.options) {
|
||||
return (
|
||||
<label key={field.key} className="toolopt-row">
|
||||
<span>{field.label}</span>
|
||||
<select value={String(value ?? '')} onChange={(e) => onChange(e.target.value)}>
|
||||
{field.options.map((o) => (
|
||||
<option key={o.value} value={o.value}>{o.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
if (field.type === 'boolean') {
|
||||
return (
|
||||
<label key={field.key} className="toolopt-row toolopt-check">
|
||||
<input type="checkbox" checked={value === true} onChange={(e) => onChange(e.target.checked)} />
|
||||
<span>{field.label}</span>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="tool-options-panel">
|
||||
<div className="toolopt-title">Werkzeug-Optionen: {tool.manifest.label}</div>
|
||||
{schema.map((f) => renderField(f as never))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ToolOptionsPanel;
|
||||
Reference in New Issue
Block a user