Composer

A live playground: drive a set of props and watch the generated source follow. Assembled from the kit’s own controls, so anything awkward about Select, Switch or NumberInput surfaces here first.

src/components/ui/composer.tsx

About

From the component's own source, where it was written.

A live playground: drive a set of props and watch generated source follow.

Assembled entirely from the kit's own controls, which is the point rather than a flourish — this is the densest form on the site, so anything awkward about Select, Switch or NumberInput surfaces here before it reaches anyone else's form.

It owns no knowledge of the documentation registry. controls describes the inputs, render draws the result and code writes the snippet; a composer works the same in a README page, a design review or a prop explorer.

State can be controlled. The uncontrolled default is what a docs page wants, but a page that needs two composers in sync — a light and a dark preview of one configuration — has to be able to lift it.

Install

npx astralyx-ui add composer

Usage

import { Composer } from '@/components/ui/composer'

<Composer
  controls={[
    { type: 'select', prop: 'variant', label: 'variant', options: ['default', 'secondary'], default: 'default' },
    { type: 'boolean', prop: 'disabled', label: 'disabled', default: false },
  ]}
  render={(state) => <Button variant={state.variant} disabled={state.disabled}>Click</Button>}
  code={(state) => `<Button variant="${state.variant}" />`}
/>

Composer

Change the props and the source below follows.

Props
tsx
<Button variant="default" disabled={false} />

Examples

Every control type

API

Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.

Composer

Extends Omit<ComponentProps<'div'>, 'onChange'>.

PropTypeDefaultDescription
controlsComposerControl[]requiredOne per prop: select, boolean, text, number or color. Each names the prop it drives and its default.
render(state: ComposerState) => ReactNoderequiredDraws the live result from the current state.
code(state: ComposerState) => string—The snippet shown beneath the preview. Omit it for a preview with no source.
languageLanguage'tsx'—
tallbooleanfalseA taller preview box, for anything with real height.
panelLabelReactNode'Props'—
stateComposerState—Controlled mode, for keeping two composers in sync — a light and a dark preview of one configuration.
onStateChange(state: ComposerState) => void—Controlled mode, for keeping two composers in sync — a light and a dark preview of one configuration.
resetLabelReactNode'Reset'—
fullscreenbooleantrueThe expand button in the panel header. A composer driving something small does not need it — the room was never the constraint there.
fullscreenLabelstring'Expand to full screen'—
exitFullscreenLabelstring'Exit full screen'—

Functions

FunctionSignatureDescription
composerInitialState(controls: ComposerControl[]) => ComposerState—

Types

TypeDefinitionDescription
ComposerValue
string | boolean | number
A live playground: drive a set of props and watch generated source follow.
ComposerControl
| {
    type: 'select'
    prop: string
    label: string
    options: readonly string[]
    default: string
  }
| { type: 'boolean'; prop: string; label: string; default: boolean }
| {
    type: 'text'
    prop: string
    label: string
    default: string
    placeholder?: string
  }
| {
    type: 'number'
    prop: string
    label: string
    default: number
    min?: number
    max?: number
    step?: number
  }
| { type: 'color'; prop: string; label: string; default: string }
—
ComposerState
Record<string, ComposerValue>
—

Behaviour

TopicDetailDescription
filling the screenFullscreen API, then a fallbackThe real thing where the browser allows it, so the page chrome goes too and Escape is handled for us. iPhone Safari exposes no element fullscreen and a request can simply be refused, so a refusal covers the viewport instead rather than leaving a button that does nothing. Leaving it is read back from fullscreenchange, not assumed from the click that started it.
independenceno registry couplingThe component knows nothing about the documentation registry; the registry describes its specs in terms of these types, not the reverse.