Spaces:
Running
Running
File size: 2,906 Bytes
5f3e9f5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | import { useId } from 'react'
import type { ReactNode } from 'react'
import { Check } from 'lucide-react'
interface Props {
checked: boolean
onChange: (checked: boolean) => void
/** Visible label rendered next to the box. Pass empty string for an icon-only checkbox. */
label?: ReactNode
/** Helper text below the label. */
description?: ReactNode
disabled?: boolean
/** When true, no visible label is rendered — but `aria-label` must be provided. */
hideLabel?: boolean
ariaLabel?: string
/** Extra class on the outer label/wrapper. */
className?: string
/** ID for the underlying input — defaults to a generated one. */
id?: string
}
/**
* Design-system checkbox. A real `<input type="checkbox">` (visually hidden)
* with a rendered box + check glyph so styling is consistent across browsers
* and brand-recoloring works through CSS variables.
*/
export default function Checkbox({
checked,
onChange,
label,
description,
disabled,
hideLabel,
ariaLabel,
className,
id,
}: Props) {
const generatedId = useId()
const inputId = id ?? generatedId
const descId = description ? `${inputId}-desc` : undefined
const visibleLabel = !hideLabel && label !== undefined && label !== ''
return (
<label
htmlFor={inputId}
className={
'group inline-flex items-start gap-2 ' +
(disabled ? 'cursor-not-allowed opacity-60' : 'cursor-pointer') +
(className ? ' ' + className : '')
}
>
<span className="relative mt-0.5 inline-flex">
<input
id={inputId}
type="checkbox"
checked={checked}
disabled={disabled}
aria-describedby={descId}
aria-label={!visibleLabel ? ariaLabel : undefined}
onChange={(e) => onChange(e.target.checked)}
className="peer absolute inset-0 h-full w-full cursor-inherit opacity-0 disabled:cursor-not-allowed"
/>
<span
aria-hidden="true"
className={
'inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-[5px] border transition-colors ' +
'peer-focus-visible:outline peer-focus-visible:outline-2 peer-focus-visible:outline-offset-2 peer-focus-visible:outline-brand-500 ' +
(checked
? 'border-brand-500 bg-brand-500 text-white'
: 'border-[rgb(var(--line))] bg-[rgb(var(--bg-surface))]')
}
>
{checked && <Check size={11} strokeWidth={3} />}
</span>
</span>
{visibleLabel && (
<span className="min-w-0">
<span className="block text-sm leading-snug text-[rgb(var(--text-strong))]">
{label}
</span>
{description && (
<span id={descId} className="block text-xs text-muted">
{description}
</span>
)}
</span>
)}
</label>
)
}
|