Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 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 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | 2x 2x 2x 2x 2x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 2x 2x 2x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 2x 28x 28x 28x 28x 28x 28x 28x 28x 28x 2x 2x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 28x 28x 28x 28x 28x | import { createContext, useContext, useId, useState, ReactNode } from 'react';
import { CircleIcon } from 'lucide-react';
import { cn } from '../lib/utils';
interface RadioGroupContextValue {
value: string | null;
onChange: (value: string) => void;
name: string;
}
const RadioGroupContext = createContext<RadioGroupContextValue | null>(null);
interface RadioGroupProps {
value?: string;
defaultValue?: string;
onValueChange?: (value: string) => void;
name?: string;
className?: string;
children: ReactNode;
orientation?: 'horizontal' | 'vertical';
}
export function RadioGroup({
value,
defaultValue,
onValueChange,
name,
className,
children,
orientation = 'vertical',
...props
}: RadioGroupProps) {
const generatedName = useId();
const groupName = name || `radio-group-${generatedName}`;
const [internalValue, setInternalValue] = useState<string | null>(
defaultValue ?? null
);
const selectedValue = value !== undefined ? value : internalValue;
const handleChange = (val: string) => {
setInternalValue(val);
onValueChange?.(val);
};
return (
<RadioGroupContext.Provider
value={{ value: selectedValue, onChange: handleChange, name: groupName }}
>
<div
role='radiogroup'
aria-orientation={orientation}
className={cn(
orientation === 'vertical' ? 'grid gap-3' : 'flex gap-4',
className
)}
{...props}
>
{children}
</div>
</RadioGroupContext.Provider>
);
}
interface RadioGroupItemProps {
value: string;
id?: string;
disabled?: boolean;
className?: string;
children?: ReactNode;
}
export function RadioGroupItem({
value,
id,
disabled = false,
className,
children,
...props
}: RadioGroupItemProps) {
const context = useContext(RadioGroupContext);
if (!context) {
throw new Error('RadioGroupItem must be used within a RadioGroup');
}
const { value: groupValue, onChange, name } = context;
const checked = groupValue === value;
return (
<label
htmlFor={id}
data-slot='radio-group-item'
className={cn('inline-flex items-center space-x-2', className)}
>
<input
type='radio'
id={id}
name={name}
value={value}
checked={checked}
disabled={disabled}
className='sr-only'
onChange={() => onChange(value)}
{...props}
/>
<span
className={cn(
'relative border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
checked && 'bg-primary/20'
)}
>
{checked && (
<CircleIcon className='stroke-primary fill-primary absolute top-1/2 left-1/2 h-3.5 w-3.5 -translate-x-1/2 -translate-y-1/2' />
)}
</span>
{children && <span className='text-sm'>{children}</span>}
</label>
);
}
|