Dial

The Dial system turns a flat array of control descriptors — the schemas — into a rendered panel of typed inputs. Each schema has a name and a dtype; the dtype decides which input is drawn (a slider, a color picker, a vector3 field, a button, and so on). You write the data shape, not the widgets.

Two pieces work together:

  • DialProvider owns the values. It holds state, exposes getValue / setValue through context, and supports both controlled and uncontrolled modes. Inputs read and write through it — they never talk to each other directly.
  • DialPanel reads the same schemas and renders the matching inputs, optionally arranging them into groups.

Controlled value flow

In controlled mode you pass values and an onValueChange callback to DialProvider. Every edit fires onValueChange(name, value) and you store the result yourself, feeding it back through values — exactly like a controlled <input>, but for the whole panel. Omit values to run the panel uncontrolled, where the provider keeps its own internal state seeded from each schema's value (or initialValues).

The example below covers several dtypes and mirrors the live values as JSON.

{
  "opacity": 0.75,
  "segments": 12,
  "wireframe": false,
  "tint": "3b82f6",
  "position": [
    0,
    1,
    0
  ],
  "label": "camera_main"
}

Grouping

Tag any schema with grouping to file it under a section header. Pass a matching groups config to DialPanel to control each section's layout — grid column count, flex wrapping, and so on. A group named transform always sorts first; the rest follow alphabetically.

transform
material
{
  "position": [
    0,
    0,
    0
  ],
  "rotation": [
    0,
    0,
    0
  ],
  "scale": 1,
  "color": "f59e0b",
  "metalness": 0.2,
  "roughness": 0.8
}

DialProvider props

PropTypeDefaultDescription
schemasDialSchema[]The control descriptors. Used to seed defaults and supply context to the inputs.
valuesRecord<string, unknown>Controlled value map keyed by schema name. Passing a non-empty object switches the provider into controlled mode.
initialValuesRecord<string, unknown>{}Initial values for uncontrolled mode; falls back to each schema's value.
onValueChange(name: string, value: unknown) => voidCalled on every edit. Required to persist changes in controlled mode.
childrenReactNodeTypically a DialPanel, but any consumer of the Dial context works.

DialPanel props

PropTypeDefaultDescription
schemasDialSchema[]The controls to render. Usually the same array passed to DialProvider.
groupsGroupSchema[]Per-group layout config, matched to schemas by grouping / group name.
labelLayoutLabelPositionTDefault label position for inputs ("left", "top", or "inline"). Overridden per-schema by labelPosition.

DialSchema fields

Every control is one DialSchema. The most commonly used fields:

FieldTypeDescription
namestringUnique key. Also the key used in the values map.
dtypeDialDtypeSelects the input to render (see below).
labelstringDisplay label. Defaults to a capitalized name.
valueunknownSeed value used for uncontrolled defaults.
groupingstringSection tag; matches a GroupSchema.name.
min / maxnumberBounds for numeric inputs. For number, supplying both renders a bounded field.
stepnumberIncrement for numeric / vector / euler inputs.
optionsstring[]Choices for select / string inputs (renders a dropdown).
placeholderstringPlaceholder for text inputs.
iconstringOptional leading icon name.
onClick() => voidClick handler for button controls.
variant'primary' | 'secondary' | 'ghost' | 'destructive' | 'link'Style for button controls.
disabledbooleanDisables a button control.
colSpan / rowSpannumberGrid span when laid out inside a group.

Supported dtype values

dtypeRenders
numberNumber field; a slider when both min and max are set, or a dropdown when options are given.
number-intInteger stepper.
number-sliderAlways a slider.
number-radRadian value displayed in degrees.
booleanToggle.
colorColor picker (hex).
string / text / selectText field, or a dropdown when options are present.
vector3Three-component (x, y, z) numeric input.
vector / vector-6 / number-groupGeneric N-component numeric input.
euler / euler-rad / euler-deg / euler-piEuler-angle inputs in radians, degrees, or π units.
arrayList of primitive values (arrayElementType).
tuple / interface / objectNested composite inputs driven by typeDefinition.
buttonAction button (onClick, variant, disabled).
customCaller-provided render function or children.