feat: add structured placement properties
This commit is contained in:
20
src/App.tsx
20
src/App.tsx
@@ -54,7 +54,7 @@ import {
|
||||
ZoomOut,
|
||||
} from 'lucide-react'
|
||||
import { menuDefinitions, pinnedWorkbenches, workbenchDefinitions, type MenuName, type WorkbenchId } from './freecadManifest'
|
||||
import { createMockFacade, type BitBybitViewportAdapter, type BitBybitWebCadFacade, type DiagnosticTreeNode, type DocumentSnapshot, type FcstdInspection, type ModelTreeItem, type ObjectPropertySnapshot, type ProjectSummary, type PropertyValue, type ShapeHandle } from './facade'
|
||||
import { createMockFacade, type BitBybitViewportAdapter, type BitBybitWebCadFacade, type DiagnosticTreeNode, type DocumentSnapshot, type FcstdInspection, type ModelTreeItem, type ObjectPropertySnapshot, type PlacementValue, type ProjectSummary, type PropertyValue, type ShapeHandle } from './facade'
|
||||
|
||||
type Page = 'start' | 'projects' | 'workspace' | 'import' | 'export' | 'settings' | 'help' | 'diagnostics' | 'sync'
|
||||
type Workbench = WorkbenchId
|
||||
@@ -451,9 +451,23 @@ function PropertyEditor({ facade, objectId, property, showNotice }: { facade: Bi
|
||||
const ids = [...new Set(document.tree.flatMap((item) => [item.id, ...(item.children ?? [])]))]
|
||||
editor = <select className="property-control property-link-control" value={String(property.value ?? '')} aria-label={property.label} onChange={(event) => commit(event.target.value || null)}><option value="">None</option>{ids.filter((id) => id !== objectId).map((id) => <option value={id} key={id}>{document.tree.find((item) => item.id === id)?.label ?? id}</option>)}</select>
|
||||
} else if (property.type === 'App::PropertyLinkSub') {
|
||||
const ref = property.value && typeof property.value === 'object' ? property.value : null
|
||||
const ref = property.value && typeof property.value === 'object' && !Array.isArray(property.value) && 'schemaVersion' in property.value ? property.value : null
|
||||
editor = ref ? <div className="property-link-sub"><span title={ref.persistentId}>{ref.objectId} / {ref.kind} · {ref.status}</span><IconButton icon={X} label="Clear subshape reference" onClick={() => commit(null)} /></div> : <span className="property-readonly">None</span>
|
||||
} else if (property.type === 'App::PropertyColor') editor = <label className="property-color"><input type="color" value={String(property.value)} aria-label={property.label} onChange={(event) => commit(event.target.value)} /><span>{String(property.value).toUpperCase()}</span></label>
|
||||
} else if (property.type === 'App::PropertyPlacement') {
|
||||
const placement = property.value as PlacementValue
|
||||
const updatePlacement = (path: 'position' | 'axis' | 'angle', key: 'x' | 'y' | 'z' | null, next: number) => {
|
||||
const value: PlacementValue = { position: { ...placement.position }, rotation: { axis: { ...placement.rotation.axis }, angle: placement.rotation.angle } }
|
||||
if (path === 'position' && key) value.position[key] = next
|
||||
else if (path === 'axis' && key) value.rotation.axis[key] = next
|
||||
else value.rotation.angle = next
|
||||
return commit(value)
|
||||
}
|
||||
editor = <details className="property-placement"><summary>{placement.position.x}, {placement.position.y}, {placement.position.z}</summary><div className="property-placement-grid"><span>Position</span>{(['x', 'y', 'z'] as const).map((key) => <input key={`p-${key}`} type="number" aria-label={`${property.label} position ${key}`} defaultValue={placement.position[key]} onBlur={(event) => updatePlacement('position', key, Number(event.target.value))} />)}<span>Axis</span>{(['x', 'y', 'z'] as const).map((key) => <input key={`a-${key}`} type="number" aria-label={`${property.label} axis ${key}`} defaultValue={placement.rotation.axis[key]} onBlur={(event) => updatePlacement('axis', key, Number(event.target.value))} />)}<span>Angle</span><input type="number" aria-label={`${property.label} angle`} defaultValue={placement.rotation.angle} onBlur={(event) => updatePlacement('angle', null, Number(event.target.value))} /><span /><span /></div></details>
|
||||
} else if (property.type === 'App::PropertyVector') {
|
||||
const vector = property.value as { x: number; y: number; z: number }
|
||||
editor = <div className="property-vector">{(['x', 'y', 'z'] as const).map((key) => <input key={key} type="number" aria-label={`${property.label} ${key}`} defaultValue={vector[key]} onBlur={(event) => commit({ ...vector, [key]: Number(event.target.value) })} />)}</div>
|
||||
} else if (property.type === 'App::PropertyLinkList' || property.type === 'App::PropertyStringList') editor = <input className="property-control" defaultValue={(property.value as string[]).join(', ')} aria-label={property.label} onBlur={(event) => commit(event.target.value.split(',').map((entry) => entry.trim()).filter(Boolean))} />
|
||||
else if (property.type === 'App::PropertyColor') editor = <label className="property-color"><input type="color" value={String(property.value)} aria-label={property.label} onChange={(event) => commit(event.target.value)} /><span>{String(property.value).toUpperCase()}</span></label>
|
||||
else if (property.type === 'App::PropertyLength' || property.type === 'App::PropertyAngle' || property.type === 'App::PropertyPercent' || property.type === 'App::PropertyFloat' || property.type === 'App::PropertyInteger') editor = <label className="property-number"><input className="property-control" type="number" defaultValue={Number(property.value)} min={property.name === 'Occurrences' ? 2 : property.type === 'App::PropertyLength' || property.type === 'App::PropertyAngle' || property.type === 'App::PropertyPercent' ? 0 : undefined} max={property.name === 'Occurrences' ? 100 : property.type === 'App::PropertyAngle' ? 360 : property.type === 'App::PropertyPercent' ? 100 : undefined} step={property.type === 'App::PropertyLength' ? 0.1 : 1} aria-label={property.label} onBlur={(event) => { if (!commit(Number(event.target.value))) event.target.value = String(property.value) }} onKeyDown={(event) => { if (event.key === 'Enter') event.currentTarget.blur() }} /><span>{property.unit}</span></label>
|
||||
else editor = <input className="property-control" defaultValue={String(property.value ?? '')} aria-label={property.label} onBlur={(event) => { if (!commit(event.target.value)) event.target.value = String(property.value ?? '') }} onKeyDown={(event) => { if (event.key === 'Enter') event.currentTarget.blur() }} />
|
||||
return <><div className="property-row"><span className="property-label">{property.label}</span><div className="property-editor">{editor}</div></div>{property.expression && <div className="property-expression"><Code2 size={12} /><span>{property.expression}</span></div>}</>
|
||||
|
||||
Reference in New Issue
Block a user