veza/apps/web/src/components/ui/radio-group.tsx
senke 3fb12b2ce2 aesthetic-improvements: automated replacement of decorative cyan with steel (80/20 rule, Action 11.3.1.3)
- Created automated script (scripts/replace-decorative-cyan.py) to systematically replace decorative/informational kodo-cyan instances with kodo-steel variants
- Script intelligently preserves active/functional states, design system variants, semantic indicators, and interactive states
- Modified 85 files, replaced 145 decorative instances, preserved 47 functional instances
- No linter errors, type safety maintained
- Action 11.3.1.3 significantly advanced (total: ~302 instances replaced across ~229 files including previous batches)
2026-01-16 11:40:13 +01:00

181 lines
4.8 KiB
TypeScript

import * as React from 'react';
import { Circle } from 'lucide-react';
import { cn } from '@/lib/utils';
/**
* RadioGroupProps - Propriétés du composant RadioGroup
*
* @interface RadioGroupProps
* @extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'>
*/
export interface RadioGroupProps
extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {
/**
* Valeur sélectionnée du groupe de boutons radio
*
* @example
* ```tsx
* <RadioGroup value={selected} onValueChange={setSelected}>
* <RadioGroupItem value="option1" />
* <RadioGroupItem value="option2" />
* </RadioGroup>
* ```
*/
value?: string;
/**
* Fonction appelée lorsque la valeur sélectionnée change
*
* @param {string} value - Nouvelle valeur sélectionnée
*
* @example
* ```tsx
* <RadioGroup onValueChange={(value) => console.log('Selected:', value)}>
* ...
* </RadioGroup>
* ```
*/
onValueChange?: (value: string) => void;
/**
* Si `true`, désactive tous les boutons radio du groupe
*
* @default false
*/
disabled?: boolean;
}
/**
* RadioGroup - Composant de groupe de boutons radio avec design system Kodo
*
* Composant pour gérer un groupe de boutons radio mutuellement exclusifs.
* Utilise le design system Kodo avec des styles cohérents et support pour l'accessibilité.
*
* @example
* ```tsx
* // Groupe de boutons radio simple
* <RadioGroup value={selected} onValueChange={setSelected}>
* <RadioGroupItem value="option1" />
* <RadioGroupItem value="option2" />
* <RadioGroupItem value="option3" />
* </RadioGroup>
* ```
*
* @example
* ```tsx
* // Avec labels
* <RadioGroup value={selected} onValueChange={setSelected}>
* <label>
* <RadioGroupItem value="option1" />
* Option 1
* </label>
* <label>
* <RadioGroupItem value="option2" />
* Option 2
* </label>
* </RadioGroup>
* ```
*
* @component
* @param {RadioGroupProps} props - Propriétés du composant
* @returns {JSX.Element} Élément div avec role="radiogroup" contenant les boutons radio
*/
const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>(
({ className, value, onValueChange, disabled, children, ...props }, ref) => {
return (
<div
ref={ref}
className={cn('grid gap-2', className)}
role="radiogroup"
{...props}
>
{React.Children.map(children, (child) => {
if (React.isValidElement(child) && child.type === RadioGroupItem) {
return React.cloneElement(child, {
checked: child.props.value === value,
onCheckedChange: () => onValueChange?.(child.props.value),
disabled: disabled || child.props.disabled,
} as any);
}
return child;
})}
</div>
);
},
);
RadioGroup.displayName = 'RadioGroup';
/**
* RadioGroupItemProps - Propriétés du composant RadioGroupItem
*
* @interface RadioGroupItemProps
* @extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type'>
*/
export interface RadioGroupItemProps
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type'> {
/**
* Valeur unique du bouton radio (doit être unique dans le groupe)
*
* @example
* ```tsx
* <RadioGroupItem value="option1" />
* ```
*/
value: string;
/**
* État checked du bouton radio (géré automatiquement par RadioGroup)
* @internal
*/
checked?: boolean;
/**
* Fonction appelée lors du clic (gérée automatiquement par RadioGroup)
* @internal
*/
onCheckedChange?: () => void;
}
/**
* RadioGroupItem - Bouton radio individuel
*
* Bouton radio à utiliser à l'intérieur d'un RadioGroup.
* L'état checked est géré automatiquement par le RadioGroup parent.
*
* @component
*/
const RadioGroupItem = React.forwardRef<HTMLInputElement, RadioGroupItemProps>(
({ className, value, checked, onCheckedChange, disabled, ...props }, ref) => {
return (
<label
className={cn(
'aspect-square h-4 w-4 rounded-full border border-kodo-steel text-kodo-steel',
'ring-offset-kodo-void focus-within:outline-none focus-within:ring-2 focus-within:ring-kodo-steel focus-within:ring-offset-2',
'disabled:cursor-not-allowed disabled:opacity-50',
'cursor-pointer relative inline-flex items-center justify-center',
checked && 'border-kodo-steel',
className,
)}
>
<input
ref={ref}
type="radio"
value={value}
checked={checked}
onChange={onCheckedChange}
disabled={disabled}
className="sr-only"
{...props}
/>
{checked && (
<Circle className="h-2.5 w-2.5 fill-current text-current" />
)}
</label>
);
},
);
RadioGroupItem.displayName = 'RadioGroupItem';
export { RadioGroup, RadioGroupItem };