import { Checkbox } from '@/components/checkbox'; import { Combobox } from '@/components/combobox'; import { Switch } from '@/components/switch'; import { Field, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSet, } from '@/components/field'; import type { FieldProps } from '@/components/field'; import { Input } from '@/components/input'; import type { InputProps } from '@/components/input'; import { InputNumber } from '@/components/input-number'; import { Label } from '@/components/label'; import { SearchIcon } from '@/icons'; import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue, } from '@/components/select'; import type { SelectTriggerProps } from '@/components/select'; import { Textarea } from '@/components/textarea'; import type { TextareaProps } from '@/components/textarea'; import { FIELD_SIZES } from './axis-values'; import type { Entry } from './types'; export const input: Entry = { slug: 'input', name: 'Input', group: 'Forms', description: 'A single-line text field. Form fields run on their own height ladder — 32 / 36 / 44 / 56px — because CBAR draws them taller than buttons from `md` up. That mismatch is deliberate.', imports: { subpath: 'input', names: ['Input'] }, /* Focus, filled and invalid are variants on the canvas and CSS here, so the resting cell is the one to compare against. */ figma: { set: 'Input', pin: { state: 'default', '.isInvalid?': 'False' } }, axes: { variant: ['outline', 'flushed'], size: FIELD_SIZES, disabled: [false, true], }, defaults: { variant: 'outline', size: 'md', placeholder: 'you@example.com' }, render: (props) => , matrices: [ { title: 'Variant × size', rows: 'variant', cols: 'size' }, { title: 'Disabled', rows: 'variant', cols: 'size', pin: { disabled: true } }, ], compositions: [ { name: 'Invalid', description: 'aria-invalid drives the styling — no separate `error` prop to keep in sync.', /* Every bare Input here carries an `aria-label`. They are not decoration: the `?a11y=on` panel reported all of them as axe `label` failures, and a documentation page showing an unlabelled field teaches the wrong thing however small the example is. Where a composition has a real Label or a FieldLabel it does the naming instead. */ render: () => ( ), }, { name: 'Types', render: () => (
), }, { name: 'Figma slots', description: "CBAR's Input set carries `.label?`, `showPlaceholder?`, `showErrorMessage?` and `.isRequired?`. All four land here, but as composition rather than props on Input: Field supplies the label, the required marker and the error, and the placeholder is a native attribute.", render: () => (
Email Email This is an error text
), }, { name: 'Adornments', description: "Figma's `.leftElement?` and `.rightElement?`, both on by default. `startElement` is inert — a click passes through to the field, which is what an icon or a currency symbol wants. `endElement` stays interactive, so a clear button or a reveal toggle works there. With either set, the box becomes a wrapper and `className` sizes it; use `classNames.input` to reach the field.", render: () => (
} placeholder="Search" /> ₼} defaultValue="1200" /> } endElement={⌘K} placeholder="Both" /> } placeholder="Disabled" disabled /> } defaultValue="nope" aria-invalid />
), }, { name: 'Adornments across sizes', description: 'The wrapper follows the same 32 / 36 / 44 / 56px ladder as the bare field.', render: () => (
{(['xs', 'sm', 'md', 'lg'] as const).map((size) => ( } placeholder={size} /> ))}
), }, ], spec: { caption: "CBAR's field ladder, which is not the Button ladder.", head: ['Size', 'Height', 'Radius'], rows: [ ['xs', '32px', '4px'], ['sm', '36px', '4px'], ['md', '44px', '4px'], ['lg', '56px', '4px'], ], }, }; export const textarea: Entry = { slug: 'textarea', name: 'Textarea', group: 'Forms', description: 'Multi-line free text. Same variants as Input; height comes from `rows`.', imports: { subpath: 'textarea', names: ['Textarea'] }, figma: { set: 'Textarea', pin: { state: 'default', '.isInvalid?': 'False', '.isFilled?': 'False' }, }, axes: { variant: ['outline', 'flushed'], size: FIELD_SIZES, }, defaults: { variant: 'outline', size: 'md', placeholder: 'Tell us more…' }, render: (props) =>