Inspector
The properties panel for whatever is selected — the other half of a canvas. Driven by a field schema rather than by children, because the fields change with the selection and writing that as JSX means a switch statement per kind at the call site.
src/components/ui/inspector.tsxAbout
From the component's own source, where it was written.
The properties panel for whatever is selected.
The other half of a canvas: NodeCanvas owns position and wiring, this owns everything else about the selected thing. Also the right shape for a layer panel, a chart configurator, or any "click a thing, edit its fields" surface.
Driven by a schema rather than by children, because the fields change with the selection — a model node and a tool node have almost nothing in common, and writing that as JSX means a switch statement per node kind at the call site. Describe the fields and the panel renders them.
**One onChange for the whole object, not one per field.** A panel that emits (key, value) pushes the merge onto every caller, and the merge is where the stale-closure bugs live. This emits the next object.
Sections are flat and always visible. An inspector is read at a glance while doing something else; collapsing groups saves vertical space and costs the glance, which is the whole point of the panel.
Install
npx astralyx-ui add inspector
Usage
import { Inspector } from '@/components/ui/inspector'
<Inspector sections={sections} value={node.data} onChange={updateNode} title={node.name} />Composer
Change the props and the source below follows.
Plan the reply
Model call
Step
plan
Model
Zero for anything that touches money.
<Inspector
sections={sections}
value={value}
onChange={setValue}
title="Plan the reply"
/>Examples
Editing a canvas node
<Inspector sections={sections} value={value} onChange={setValue} />Nothing selected
<Inspector sections={[]} value={{}} onChange={setValue} />API
Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.
Inspector
Extends Omit<ComponentProps<'div'>, 'onChange' | 'title'>.
| Prop | Type | Default | Description |
|---|---|---|---|
| sections | InspectorSection[] | required | { label?, fields }. Fields are text, textarea, number, boolean, select or readonly. |
| value | InspectorValue | required | One onChange for the whole object, not one per field. A (key, value) callback pushes the merge onto every caller, and the merge is where the stale-closure bugs live. |
| onChange | (next: InspectorValue) => void | required | One onChange for the whole object, not one per field. A (key, value) callback pushes the merge onto every caller, and the merge is where the stale-closure bugs live. |
| title | ReactNode | — | Panel heading — usually the selected thing’s name. |
| description | ReactNode | — | Panel heading — usually the selected thing’s name. |
| emptyLabel | string | 'Nothing selected.' | Pass an empty sections array alongside it. |
Types
InspectorSection
| Field | Type | Description |
|---|---|---|
| label? | string | — |
| fields | InspectorField[] | — |
| Type | Definition | Description |
|---|---|---|
InspectorField | | { type: 'text'; key: string; label: string; placeholder?: string; hint?: ReactNode }
| { type: 'textarea'; key: string; label: string; rows?: number; placeholder?: string; hint?: ReactNode }
| { type: 'number'; key: string; label: string; min?: number; max?: number; step?: number; hint?: ReactNode }
| { type: 'boolean'; key: string; label: string; hint?: ReactNode }
| {
type: 'select'
key: string
label: string
options: { value: string; label: string }[]
hint?: ReactNode
}
/** Not editable — an id, a computed total, a timestamp. */
| { type: 'readonly'; key: string; label: string; hint?: ReactNode } | The properties panel for whatever is selected. |
InspectorValue | Record<string, unknown> | — |
Behaviour
| Topic | Detail | Description |
|---|---|---|
| accessibility | Field + useFieldControl | Every control is wired to its label and hint through Field’s context, so ids and aria-describedby are generated rather than hand-managed. |