Switch field
One on or off, for a setting that applies at once.
Installation
npx shadcn@latest add @formdef/switch-fieldRegister it under widgets in lib/formdef/index.tsx, as widgets: { switch: SwitchField }. The
widget serves boolean values.
Usage
export const = createForm({
: .({ : .() }),
: { : false },
fields: {
newsletter: {
: 'switch',
: { : 'Newsletter', : 'One email a month.', : 'list' },
},
},
})variant decides the look:
variant | Look |
|---|---|
split (default) | label and description on the left, the switch on the right |
list | the switch first, then label and description |
card | the whole row is a bordered card, and a click anywhere on it toggles the switch |
A switch suggests that the change takes effect at once. A choice that only counts once the form is submitted, such as accepting terms, reads better as a checkbox.
Props
Prop
Type
Source
import { type FieldOf, fieldParts } from '@formdef/core'
import type { ComponentProps } from 'react'
import { FieldNotes } from '@/components/formdef/field-notes'
import { Field, FieldContent, FieldLabel, FieldTitle } from '@/components/ui/field'
import { Switch } from '@/components/ui/switch'
import type { FieldLabels } from '@/lib/formdef/field-labels'
type Attrs = Pick<ComponentProps<typeof Switch>, 'className' | 'disabled' | 'readOnly' | 'required'>
const TITLE_SUFFIX = '-title'
type SwitchFieldProps = FieldOf<boolean> &
Omit<FieldLabels, 'labelAside'> &
Attrs & {
variant?: 'split' | 'list' | 'card' | undefined
}
export const SwitchField = ({
field,
label,
description,
variant = 'split',
...attrs
}: SwitchFieldProps) => {
const parts = fieldParts(field, { description })
const { name: _carriedByTheForm, ...control } = parts.control
const checked = field.state.value ?? false
const titleId = `${control.id}${TITLE_SUFFIX}`
const isCard = variant === 'card'
const toggle = (
<Switch
{...attrs}
{...control}
aria-labelledby={isCard && label !== undefined ? titleId : undefined}
checked={checked}
onCheckedChange={(isChecked) => field.handleChange(isChecked)}
/>
)
const notes = <FieldNotes parts={parts} description={description} />
if (isCard) {
return (
<FieldLabel htmlFor={control.id}>
<Field
orientation="horizontal"
data-invalid={parts.isErrorDue}
data-disabled={attrs.disabled}
>
<FieldContent>
{label !== undefined && <FieldTitle id={titleId}>{label}</FieldTitle>}
{notes}
</FieldContent>
{toggle}
</Field>
</FieldLabel>
)
}
const caption = label !== undefined && <FieldLabel htmlFor={control.id}>{label}</FieldLabel>
return (
<Field orientation="horizontal" data-invalid={parts.isErrorDue} data-disabled={attrs.disabled}>
{variant === 'list' && toggle}
<FieldContent>
{caption}
{notes}
</FieldContent>
{variant === 'split' && toggle}
</Field>
)
}