Files

103 lines
2.6 KiB
TypeScript

import { type ReactNode, useId } from 'react';
import { tv } from 'tailwind-variants';
export interface RadioProps {
name: string;
value: string;
checked?: boolean;
defaultChecked?: boolean;
onChange?: (value: string) => void;
disabled?: boolean;
readOnly?: boolean;
children?: ReactNode;
className?: string;
}
const radioStyles = tv({
base: [
'shrink-0 w-4 h-4 min-w-4 min-h-4 box-border rounded-full',
'border-2 border-dabeeo-gray-be bg-white',
'flex items-center justify-center',
'transition-all duration-200',
'peer-focus-visible:ring-2 peer-focus-visible:ring-offset-2 peer-focus-visible:ring-primary',
],
variants: {
isSelected: {
true: 'border-primary bg-primary',
},
isDisabled: {
true: 'border-dabeeo-gray-be bg-dabeeo-gray-eb',
},
isReadOnly: {
true: '',
},
},
compoundVariants: [
{
isSelected: true,
isDisabled: true,
className: 'border-dabeeo-gray-be bg-dabeeo-gray-eb',
},
],
});
const innerCircleStyles = tv({
base: 'w-2 h-2 rounded-full transition-all duration-200',
variants: {
isSelected: {
true: 'bg-white',
false: 'bg-transparent',
},
isDisabled: {
true: 'bg-dabeeo-gray-eb',
},
},
compoundVariants: [
{
isSelected: true,
isDisabled: true,
className: 'bg-dabeeo-gray-eb',
},
],
});
const labelStyles = tv({
base: 'text-dabeeo-black-22 text-sm font-medium inline-flex items-center',
variants: {
isDisabled: {
true: 'text-dabeeo-gray-be',
},
},
});
export const Radio = (props: RadioProps) => {
const { name, value, checked, defaultChecked, onChange, disabled, readOnly, children, className } = props;
const id = useId();
const isInteractive = !disabled && !readOnly;
return (
<label
htmlFor={id}
className={`inline-flex h-9 items-center gap-2.5 ${isInteractive ? 'cursor-pointer' : 'cursor-not-allowed'} ${className ?? ''}`}
>
<input
type="radio"
id={id}
name={name}
value={value}
checked={checked}
defaultChecked={defaultChecked}
onChange={(e) => isInteractive && onChange?.(e.target.value)}
disabled={disabled}
readOnly={readOnly}
className="sr-only peer"
/>
<span className={radioStyles({ isSelected: checked, isDisabled: disabled, isReadOnly: readOnly })}>
<span className={innerCircleStyles({ isSelected: checked, isDisabled: disabled })} />
</span>
{children && <span className={labelStyles({ isDisabled: disabled })}>{children}</span>}
</label>
);
};