72 lines
2.0 KiB
TypeScript
72 lines
2.0 KiB
TypeScript
"use client";
|
|
|
|
/**
|
|
* Checkbox — themed replacement for the native `<input type="checkbox" />`.
|
|
*
|
|
* The native control is rendered visually hidden (sr-only) so screen
|
|
* readers and keyboard focus still work the way browsers expect, while
|
|
* we paint a small rounded box next to it that fills with the primary
|
|
* color when checked. Fully controlled — caller owns `checked` state.
|
|
*
|
|
* Usage:
|
|
*
|
|
* <Checkbox checked={ok} onCheckedChange={setOk} />
|
|
*
|
|
* <label className="flex items-center gap-2">
|
|
* <Checkbox checked={ok} onCheckedChange={setOk} />
|
|
* Accept terms
|
|
* </label>
|
|
*/
|
|
|
|
import { Check } from "lucide-react";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
export function Checkbox({
|
|
checked,
|
|
onCheckedChange,
|
|
disabled,
|
|
className,
|
|
ariaLabel,
|
|
}: {
|
|
checked: boolean;
|
|
onCheckedChange: (next: boolean) => void;
|
|
disabled?: boolean;
|
|
className?: string;
|
|
ariaLabel?: string;
|
|
}) {
|
|
return (
|
|
<span
|
|
className={cn(
|
|
"relative inline-flex items-center justify-center shrink-0",
|
|
className,
|
|
)}
|
|
>
|
|
<input
|
|
type="checkbox"
|
|
checked={checked}
|
|
disabled={disabled}
|
|
aria-label={ariaLabel}
|
|
onChange={(e) => onCheckedChange(e.target.checked)}
|
|
className="peer absolute inset-0 size-full appearance-none m-0 cursor-pointer disabled:cursor-not-allowed rounded-md"
|
|
/>
|
|
<span
|
|
aria-hidden
|
|
className={cn(
|
|
"pointer-events-none size-5 rounded-md border transition-colors",
|
|
"flex items-center justify-center",
|
|
checked
|
|
? "bg-primary border-primary"
|
|
: "bg-background border-border",
|
|
"peer-hover:border-foreground/40",
|
|
"peer-focus-visible:ring-2 peer-focus-visible:ring-ring peer-focus-visible:ring-offset-1 peer-focus-visible:ring-offset-background",
|
|
disabled && "opacity-50",
|
|
)}
|
|
>
|
|
{checked ? (
|
|
<Check className="size-3.5 text-primary-foreground" strokeWidth={3} />
|
|
) : null}
|
|
</span>
|
|
</span>
|
|
);
|
|
}
|