mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
108 lines
2.8 KiB
TypeScript
108 lines
2.8 KiB
TypeScript
import React from 'react';
|
|
import { clsx } from 'clsx';
|
|
import Checkbox from './Checkbox';
|
|
|
|
export interface CheckboxOption {
|
|
value: string;
|
|
label: string;
|
|
description?: string;
|
|
disabled?: boolean;
|
|
}
|
|
|
|
export interface CheckboxGroupProps {
|
|
options: CheckboxOption[];
|
|
value?: string[];
|
|
onChange?: (value: string[]) => void;
|
|
name: string;
|
|
className?: string;
|
|
orientation?: 'horizontal' | 'vertical';
|
|
label?: string;
|
|
description?: string;
|
|
error?: string;
|
|
}
|
|
|
|
/**
|
|
* Checkbox group component
|
|
*/
|
|
export const CheckboxGroup = ({
|
|
options,
|
|
value = [],
|
|
onChange,
|
|
name,
|
|
className,
|
|
orientation = 'vertical',
|
|
label,
|
|
description,
|
|
error,
|
|
}: CheckboxGroupProps) => {
|
|
const handleChange = (optionValue: string, checked: boolean) => {
|
|
if (!onChange) return;
|
|
|
|
if (checked) {
|
|
onChange([...value, optionValue]);
|
|
} else {
|
|
onChange(value.filter((v) => v !== optionValue));
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className={clsx('flex flex-col gap-2', className)}>
|
|
{label && <label className="text-sm font-medium text-foreground">{label}</label>}
|
|
{description && <p className="text-xs text-muted-foreground">{description}</p>}
|
|
<div
|
|
className={clsx('flex gap-4', {
|
|
'flex-col': orientation === 'vertical',
|
|
'flex-row flex-wrap': orientation === 'horizontal',
|
|
})}
|
|
role="group"
|
|
aria-labelledby={label ? `${name}-label` : undefined}
|
|
>
|
|
{options.map((option) => (
|
|
<label
|
|
key={option.value}
|
|
className={clsx('flex items-start gap-3 cursor-pointer', 'group', {
|
|
'opacity-50 cursor-not-allowed': option.disabled,
|
|
})}
|
|
>
|
|
<Checkbox
|
|
name={name}
|
|
value={option.value}
|
|
checked={value.includes(option.value)}
|
|
onChange={(e) => {
|
|
if (!option.disabled) {
|
|
handleChange(option.value, e.target.checked);
|
|
}
|
|
}}
|
|
disabled={option.disabled}
|
|
className="mt-0.5"
|
|
/>
|
|
<div className="flex flex-col">
|
|
<span
|
|
className={clsx('text-sm font-medium', {
|
|
'text-muted-foreground': option.disabled,
|
|
})}
|
|
>
|
|
{option.label}
|
|
</span>
|
|
{option.description && (
|
|
<span
|
|
className={clsx('text-xs text-muted-foreground mt-0.5', {
|
|
'opacity-70': option.disabled,
|
|
})}
|
|
>
|
|
{option.description}
|
|
</span>
|
|
)}
|
|
</div>
|
|
</label>
|
|
))}
|
|
</div>
|
|
{error && (
|
|
<p className="text-sm text-destructive" role="alert">
|
|
{error}
|
|
</p>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|