nico-martin's picture
nico-martin HF Staff
init
9b72f0d
import cn from "@utils/classnames.ts";
import { type ReactNode } from "react";
import { FormError } from "../index.ts";
import LabelTooltip from "./LabelTooltip.tsx";
interface CheckboxOption {
name: string;
label: string;
description?: string;
}
interface InputCheckboxListProps {
label: string;
options: CheckboxOption[];
value: string[];
onChange: (value: string[]) => void;
error?: string;
required?: boolean;
className?: string;
id?: string;
tooltip?: string | ReactNode;
}
export default function InputCheckboxList({
label,
options,
value,
onChange,
error,
required,
className = "",
id,
tooltip = "",
}: InputCheckboxListProps) {
const handleCheckboxChange = (optionName: string, checked: boolean) => {
if (checked) {
onChange([...value, optionName]);
} else {
onChange(value.filter((name) => name !== optionName));
}
};
return (
<div className={cn("flex flex-col gap-2", className)}>
<div className="relative text-sm font-medium text-gray-900 dark:text-gray-100">
{label}
{required && (
<span className="ml-1 text-blue-500 dark:text-blue-400">*</span>
)}
{tooltip !== "" && <LabelTooltip text={<>{tooltip}</>} />}
</div>
<div className="flex flex-col gap-3">
{options.map((option) => {
const checkboxId = `${id || "checkbox-list"}-${option.name}`;
const isChecked = value.includes(option.name);
return (
<label
key={option.name}
htmlFor={checkboxId}
className="flex cursor-pointer items-start gap-3"
>
<input
type="checkbox"
id={checkboxId}
checked={isChecked}
onChange={(e) =>
handleCheckboxChange(option.name, e.target.checked)
}
className={cn(
"mt-0.5 h-4 w-4 cursor-pointer rounded border transition-colors focus:ring-2 focus:ring-offset-2 focus:outline-none",
"bg-white dark:bg-gray-800",
"focus:ring-offset-white dark:focus:ring-offset-gray-900",
error
? "border-red-300 text-red-600 focus:border-red-500 focus:ring-red-500 dark:border-red-700 dark:text-red-500 dark:focus:border-red-600 dark:focus:ring-red-600"
: "border-gray-300 text-yellow-600 focus:border-yellow-500 focus:ring-yellow-500 dark:border-gray-600 dark:text-yellow-500 dark:focus:border-yellow-400 dark:focus:ring-yellow-400"
)}
/>
<div className="flex flex-col gap-1">
<span className="text-sm font-medium text-gray-900 dark:text-gray-100">
{option.label}
</span>
{option.description && (
<span className="text-xs text-gray-600 dark:text-gray-400">
{option.description}
</span>
)}
</div>
</label>
);
})}
</div>
{error && <FormError>{error}</FormError>}
</div>
);
}