import * as React from 'react'; import { Checkbox } from '@/components/checkbox'; import type { CheckboxProps } from '@/components/checkbox'; import { Switch } from '@/components/switch'; import type { SwitchProps } from '@/components/switch'; import { RadioGroup, RadioGroupItem } from '@/components/radio-group'; import { Label } from '@/components/label'; import { PALETTES } from './axis-values'; import type { Entry, Palette } from './types'; const SIZES = ['xs', 'sm', 'md', 'lg'] as const; /** * The parent/child group — the reason the third state exists. * * Named, with an explicit `displayName`, for `chrome/lib/codegen.ts` — see the note * on `ControlledSortDemo` in `registry/data.tsx`. */ function ParentChildDemo() { const [items, setItems] = React.useState([true, false, false]); const all = items.every(Boolean); const some = items.some(Boolean); return (
setItems(items.map(() => next === true))} />
{items.map((checked, index) => (
setItems(items.map((value, i) => (i === index ? next === true : value))) } />
))}
); } ParentChildDemo.displayName = 'ParentChildDemo'; export const checkbox: Entry = { slug: 'checkbox', name: 'Checkbox', group: 'Forms', description: 'A tri-state box (on / off / indeterminate). The palette colours the checked fill; the unchecked box stays neutral so a form does not read as decorated.', imports: { subpath: 'checkbox', names: ['Checkbox'] }, /* CBAR folds checked, hover, invalid and disabled into one `state` axis and spells its resting value with a capital. The kit keeps them as separate props and draws the interaction states in CSS, so the axis is pinned. */ figma: { set: 'Checkmark', pin: { state: 'Default' } }, axes: { colorPalette: PALETTES, size: SIZES, checked: [true, false], disabled: [false, true], }, defaults: { colorPalette: 'primary', size: 'md', checked: true, disabled: false }, render: (props) => , matrices: [ { title: 'Size × colorPalette', description: 'Checked, which is the only state the palette is visible in.', rows: 'size', cols: 'colorPalette', pin: { checked: true }, }, { title: 'Size × state', rows: 'size', cols: 'checked', pin: { colorPalette: 'primary' }, }, ], compositions: [ { name: 'With a label', description: 'Label htmlFor + Checkbox id makes the whole label a hit target.', render: () => (
), }, { name: 'Indeterminate', description: 'The parent of a partially selected group.', render: () => , }, { name: 'Parent and children', description: 'What that third state is for: the parent goes indeterminate when only some children are checked, rather than lying about the group in either direction.', render: () => , }, ], }; export const switchEntry: Entry = { slug: 'switch', name: 'Switch', group: 'Forms', description: 'An immediate on/off toggle. Reach for it when the change applies at once; use a Checkbox when it applies on submit.', imports: { subpath: 'switch', names: ['Switch'] }, figma: { set: 'Switch', axisMap: { Size: 'size' }, pin: { State: 'default' } }, axes: { colorPalette: PALETTES, size: SIZES, checked: [true, false], disabled: [false, true], }, defaults: { colorPalette: 'primary', size: 'md', checked: true, disabled: false }, render: (props) => , matrices: [ { title: 'Size × colorPalette', rows: 'size', cols: 'colorPalette', pin: { checked: true }, }, { title: 'Size × state', rows: 'size', cols: 'checked', pin: { colorPalette: 'primary' } }, { title: 'Disabled', rows: 'size', cols: 'checked', pin: { colorPalette: 'primary', disabled: true }, }, ], compositions: [ { name: 'With a label', render: () => (
), }, ], }; export const radioGroup: Entry = { slug: 'radio-group', name: 'RadioGroup', group: 'Forms', description: 'One choice out of several. `colorPalette` and `size` live on RadioGroupItem, not on the group — the group itself is only a roving-focus container.', imports: { subpath: 'radio-group', names: ['RadioGroup', 'RadioGroupItem'] }, /* CBAR draws two RadioMark sets on one page; the capture keeps both and this resolves to the first, whose `state` axis is the long one. */ figma: { set: 'RadioMark', pin: { state: 'default' } }, axes: { colorPalette: PALETTES, size: SIZES, disabled: [false, true], }, defaults: { colorPalette: 'primary', size: 'sm', disabled: false }, /* `value` is required on the item and must not be overwritten by the bag, so the axes are passed by name. */ render: (props) => ( ), matrices: [ { title: 'Size × colorPalette', rows: 'size', cols: 'colorPalette' }, { title: 'Disabled', rows: 'size', cols: 'colorPalette', pin: { disabled: true }, }, ], compositions: [ { name: 'A real group', render: () => ( {[ ['monthly', 'Monthly'], ['yearly', 'Yearly — two months free'], ['lifetime', 'Lifetime'], ].map(([value, label]) => (
))}
), }, { name: 'With a disabled option', description: 'A disabled item is skipped by the arrow keys but stays in the list, so the choice that is unavailable is still visible as a choice.', render: () => ( {[ ['standard', 'Standard delivery', false], ['express', 'Express delivery', false], ['same-day', 'Same day (unavailable in your area)', true], ].map(([value, label, disabled]) => (
))}
), }, { name: 'Horizontal', description: 'Layout is entirely the container’s — the group is a roving-focus wrapper and draws no direction of its own.', render: () => ( {[ ['yes', 'Yes'], ['no', 'No'], ].map(([value, label]) => (
))}
), }, ], };