API Design and Building Blocks

DialPanel Component

The DialPanel component renders dial controls based on schemas. It accepts the following props:

Props

PropTypeDefaultDescription
schemasDialSchema[]RequiredArray of dial schemas to render
groupsDialGroupConfig[]undefinedOptional group configurations
labelLayoutLabelPositionTundefinedDefault label position layout for all inputs ("top", "left", "right", "inline", etc.)

Label Position Priority

The label position for inputs is determined by the following priority order:

  1. Component-specific: Label position tags on individual properties (highest priority)
    • @dial-label-top, @dial-label-left, @dial-label-right, @dial-label-inline
  2. Panel labelLayout: labelLayout prop on DialPanel
  3. Component default: Individual input component's default behavior (lowest priority)

Example Usage

import { DialPanel, DialProvider } from '@vuer-ai/vuer-uikit';

// With default label layout for all inputs
<DialProvider schemas={schemas}>
  <DialPanel 
    schemas={schemas} 
    labelLayout="top"  // All inputs will have top-aligned labels by default
  />
</DialProvider>

// Without specifying (components use their own defaults)
<DialProvider schemas={schemas}>
  <DialPanel schemas={schemas} />
</DialProvider>

Individual components can still override the panel's default label layout:

interface Props {
  /**
   * @dial-label-left  // This overrides the panel's labelLayout
   */
  specialField: number;
}

Building Blocks

We want to specify the property menu without duplicating the code. Here are the basic building blocks:

Control EntryControl Group
const controlEntry = {
dtype: 'number',
value: 10,
min: 0,
max: 100,
step: 1,
}
const controlGroup = {
tag: 'group',
children: [
controlEntry,
],
layout: 'row',
}

Now, let's convert this into a react schema that we can create using react.createElement:

<Dial.Provider>
  <Dial.Row>
    <DialInput label="Position" column type="number" key="prop-name" value={10} min={0} max={100} step={1}/>
    <DialInput label="Rotation" column type="number" key="prop-name" value={10} min={0} max={100} step={1}/>
    <DialInput label="Scale" column type="number" key="prop-name" value={10} min={0} max={100} step={1}/>
  </Dial.Row>
</Dial.Provider>

this can be written via react createElement below:

React.createElement(
  Dial.Provider,
  null,
  React.createElement(
    Dial.Row,
    null,
    React.createElement(DialInput, {
      label: "Position",
      column: true,
      type: "number",
      key: "prop-name",
      value: 10,
      min: 0,
      max: 100,
      step: 1
    }),
    React.createElement(DialInput, {
      label: "Rotation",
      column: true,
      type: "number",
      key: "prop-name",
      value: 10,
      min: 0,
      max: 100,
      step: 1
    }),
    React.createElement(DialInput, {
      label: "Scale",
      column: true,
      type: "number",
      key: "prop-name",
      value: 10,
      min: 0,
      max: 100,
      step: 1
    })
  )
)

We can do this via a nested json object using a convenient helper function:

function build({tag, children, ...props}) {
  return React.createElement(tag, props, children)
}

We can then rewrite the schema as:

{ name: 'position', dtype: 'vector3', value: [0, 0, 0], min: 0, max: 100, options: [10, 20, 30, 40, 50],
  tags: { grouping: 'transform', col: true } }
{ name: 'rotation', dtype: 'euler', value: [0, 0, 0], min: 0, max: 100, options: [10, 20, 30, 40, 50],
  tags: { grouping: 'transform', col: true } }
{ name: 'scale', dtype: 'vector3', value: [1, 1, 1], min: 0, max: 100, options: [10, 20, 30, 40, 50],
  tags: { grouping: 'transform', col: true } }