Toggle group wrappers
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
...import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
function FormatIcon(props: { kind: 'bold' | 'italic' | 'underline' }) {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
{props.kind === 'bold' ? <path d="M7 5h6a4 4 0 0 1 0 8H7zm0 8h7a3 3 0 0 1 0 6H7z" /> : null}
{props.kind === 'italic' ? <path d="M10 4h6M8 20h6M14 4 10 20" /> : null}
{props.kind === 'underline' ? <><path d="M6 4v6a6 6 0 0 0 12 0V4" /><path d="M4 20h16" /></> : null}
</svg>
)
}
export default function ToggleGroupDemoExample() {
return (
<ToggleGroup variant="outline" type="multiple">
<ToggleGroupItem value="bold" aria-label="Toggle bold"><FormatIcon kind="bold" /></ToggleGroupItem>
<ToggleGroupItem value="italic" aria-label="Toggle italic"><FormatIcon kind="italic" /></ToggleGroupItem>
<ToggleGroupItem value="underline" aria-label="Toggle underline"><FormatIcon kind="underline" /></ToggleGroupItem>
</ToggleGroup>
)
}
Installation
npx @fictjs/shadcn@latest add toggle-groupInstall the runtime dependencies:
npm install @fictjs/radix-uiCopy the Fict shadcn component into your project.
Source
components/ui/toggle-group.tsx
src/components/ui/toggle-group.tsx
import { ToggleGroup as ToggleGroupPrimitive } from '@fictjs/radix-ui'
import { createContext, useContext } from 'fict'
import { cn } from '@/lib/cn'
import { toggleVariants } from '@/components/ui/toggle'
type GroupProps = {
class?: string
children?: unknown
variant?: 'default' | 'outline'
size?: 'default' | 'sm' | 'lg'
spacing?: number
style?: Record<string, string | number>
[key: string]: unknown
}
type ItemProps = {
class?: string
variant?: 'default' | 'outline'
size?: 'default' | 'sm' | 'lg'
children?: unknown
[key: string]: unknown
}
type ToggleGroupContextValue = Pick<GroupProps, 'variant' | 'size' | 'spacing'>
const ToggleGroupContext = createContext<ToggleGroupContextValue>({ spacing: 0 })
export function ToggleGroup(props: GroupProps) {
const {
class: className,
children,
variant,
size,
spacing = 0,
style,
...rest
} = props
return (
<ToggleGroupPrimitive.Root
data-slot='toggle-group'
data-variant={variant}
data-size={size}
data-spacing={spacing}
style={{ '--toggle-group-gap': `${spacing * 0.25}rem`, ...style }}
class={cn(
'inline-flex w-fit items-center justify-center gap-[var(--toggle-group-gap)] rounded-md data-[orientation=vertical]:flex-col',
className,
)}
{...rest}
>
<ToggleGroupContext.Provider value={{ variant, size, spacing }}>
{children}
</ToggleGroupContext.Provider>
</ToggleGroupPrimitive.Root>
)
}
export function ToggleGroupItem(props: ItemProps) {
const { class: className, variant, size, children, ...rest } = props
const context = useContext(ToggleGroupContext)
const resolvedVariant = context.variant ?? variant
const resolvedSize = context.size ?? size
return (
<ToggleGroupPrimitive.Item
data-slot='toggle-group-item'
data-variant={resolvedVariant}
data-size={resolvedSize}
data-spacing={context.spacing ?? 0}
class={cn(
toggleVariants({ variant: resolvedVariant, size: resolvedSize }),
'w-auto min-w-0 shrink-0 px-3 focus:z-10 focus-visible:z-10',
'data-[spacing=0]:rounded-none data-[spacing=0]:shadow-none data-[spacing=0]:first:rounded-l-md data-[spacing=0]:last:rounded-r-md data-[spacing=0]:data-[variant=outline]:border-l-0 data-[spacing=0]:data-[variant=outline]:first:border-l',
className,
)}
{...rest}
>
{children}
</ToggleGroupPrimitive.Item>
)
}
Usage
Examples
Outline
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
...import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
export default function ToggleGroupOutlineExample() {
return (
<ToggleGroup variant="outline" type="single" defaultValue="all">
<ToggleGroupItem value="all" aria-label="Toggle all">All</ToggleGroupItem>
<ToggleGroupItem value="missed" aria-label="Toggle missed">Missed</ToggleGroupItem>
</ToggleGroup>
)
}
Sizes
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
...import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
const directions = ['top', 'bottom', 'left', 'right'] as const
export default function ToggleGroupSizesExample() {
return (
<div class="flex flex-col gap-4">
<ToggleGroup type="single" size="sm" defaultValue="top" variant="outline">
{directions.map(value => <ToggleGroupItem value={value} aria-label={`Toggle ${value}`}>{value[0].toUpperCase() + value.slice(1)}</ToggleGroupItem>)}
</ToggleGroup>
<ToggleGroup type="single" defaultValue="top" variant="outline">
{directions.map(value => <ToggleGroupItem value={value} aria-label={`Toggle ${value}`}>{value[0].toUpperCase() + value.slice(1)}</ToggleGroupItem>)}
</ToggleGroup>
</div>
)
}
Spacing
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
...import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
const directions = ['top', 'bottom', 'left', 'right'] as const
export default function ToggleGroupSpacingExample() {
return (
<ToggleGroup type="single" size="sm" defaultValue="top" variant="outline" spacing={2}>
{directions.map(value => <ToggleGroupItem value={value} aria-label={`Toggle ${value}`}>{value[0].toUpperCase() + value.slice(1)}</ToggleGroupItem>)}
</ToggleGroup>
)
}
Vertical
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
...import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
export default function ToggleGroupVerticalExample() {
return (
<ToggleGroup type="multiple" orientation="vertical" spacing={1} defaultValue={['bold', 'italic']}>
<ToggleGroupItem value="bold" aria-label="Toggle bold">B</ToggleGroupItem>
<ToggleGroupItem value="italic" aria-label="Toggle italic">I</ToggleGroupItem>
<ToggleGroupItem value="underline" aria-label="Toggle underline">U</ToggleGroupItem>
</ToggleGroup>
)
}
Disabled
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
...import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
export default function ToggleGroupDisabledExample() {
return (
<ToggleGroup disabled type="multiple">
<ToggleGroupItem value="bold" aria-label="Toggle bold">B</ToggleGroupItem>
<ToggleGroupItem value="italic" aria-label="Toggle italic">I</ToggleGroupItem>
<ToggleGroupItem value="underline" aria-label="Toggle underline">U</ToggleGroupItem>
</ToggleGroup>
)
}
Font Weight Selector
Use font-normal to set the font weight.
import { Field, FieldDescription, FieldLabel } from '@/components/ui/field'
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
...import { Field, FieldDescription, FieldLabel } from '@/components/ui/field'
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
const weights = ['light', 'normal', 'medium', 'bold'] as const
export default function ToggleGroupFontWeightSelectorExample() {
let fontWeight = $state<(typeof weights)[number]>('normal')
return (
<Field>
<FieldLabel>Font Weight</FieldLabel>
<ToggleGroup type="single" value={fontWeight} onValueChange={value => { if (value) fontWeight = value as typeof fontWeight }} variant="outline" spacing={2} size="lg">
{weights.map(value => (
<ToggleGroupItem value={value} aria-label={value[0].toUpperCase() + value.slice(1)} class="flex size-16 flex-col items-center justify-center rounded-xl">
<span class={`text-2xl leading-none font-${value}`}>Aa</span>
<span class="text-xs text-muted-foreground">{value[0].toUpperCase() + value.slice(1)}</span>
</ToggleGroupItem>
))}
</ToggleGroup>
<FieldDescription>Use <code class="rounded-md bg-muted px-1 py-0.5 font-mono">font-{fontWeight}</code> to set the font weight.</FieldDescription>
</Field>
)
}
Rtl
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
...import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
const translations = {
ar: { direction: 'rtl', list: 'قائمة', grid: 'شبكة', cards: 'بطاقات' },
he: { direction: 'rtl', list: 'רשימה', grid: 'רשת', cards: 'כרטיסים' },
en: { direction: 'ltr', list: 'List', grid: 'Grid', cards: 'Cards' },
} as const
export default function ToggleGroupRtlExample() {
let language = $state<keyof typeof translations>('ar')
const text = () => translations[language]
return (
<div class="grid gap-4">
<select value={language} onChange={event => { language = event.currentTarget.value as keyof typeof translations }}>
<option value="ar">Arabic (العربية)</option>
<option value="he">Hebrew (עברית)</option>
<option value="en">English</option>
</select>
<ToggleGroup dir={text().direction} variant="outline" type="single" defaultValue="list">
<ToggleGroupItem value="list" aria-label={text().list}>{text().list}</ToggleGroupItem>
<ToggleGroupItem value="grid" aria-label={text().grid}>{text().grid}</ToggleGroupItem>
<ToggleGroupItem value="cards" aria-label={text().cards}>{text().cards}</ToggleGroupItem>
</ToggleGroup>
</div>
)
}