Sprint 1 — Quick A11y wins: - progress.tsx: role=progressbar + aria-value* + aria-label - switch.tsx: role=switch + aria-checked - skeleton.tsx: aria-hidden=true - alert.tsx, Toast.tsx, SelectTrigger.tsx: aria-labels on close buttons - PostCard.tsx: alt on images + aria-labels on icon buttons - ProductCard.tsx: aria-labels on play/view buttons - modal.tsx: role=dialog + aria-modal + aria-labelledby - input.tsx: error state + aria-invalid + aria-describedby - FAB.tsx: forward aria-label from label prop Sprint 2 — Structural A11y + View States: - tabs/: full ARIA tablist/tab/tabpanel + arrow key navigation - radio-group.tsx: role=radio + arrow key navigation - select/: aria-activedescendant + full keyboard navigation - List.tsx + card.tsx: focus-visible states on interactive elements - DashboardPage, LibraryPage, LiveView, QueueView: error states - WishlistView, AdminDashboard, AnalyticsView, SellerDashboard: loading/empty states Co-authored-by: Cursor <cursoragent@cursor.com>
102 lines
2.7 KiB
TypeScript
102 lines
2.7 KiB
TypeScript
import * as React from 'react';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
/**
|
|
* SwitchProps - Propriétés du composant Switch
|
|
*
|
|
* @interface SwitchProps
|
|
* @extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'>
|
|
*/
|
|
export interface SwitchProps
|
|
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {
|
|
/**
|
|
* État checked du switch
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* <Switch checked={isEnabled} onCheckedChange={setIsEnabled} />
|
|
* ```
|
|
*/
|
|
checked?: boolean;
|
|
|
|
/**
|
|
* Fonction appelée lorsque l'état checked change
|
|
*
|
|
* @param {boolean} checked - Nouvel état checked
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* <Switch onCheckedChange={(checked) => console.log('Switch:', checked)} />
|
|
* ```
|
|
*/
|
|
onCheckedChange?: (checked: boolean) => void;
|
|
}
|
|
|
|
/**
|
|
* Switch - Composant d'interrupteur avec design system Kodo
|
|
*
|
|
* Composant d'interrupteur (toggle) pour activer/désactiver une option.
|
|
* Utilise le design system Kodo avec une animation de transition fluide.
|
|
*
|
|
* @example
|
|
* ```tsx
|
|
* // Switch simple
|
|
* <Switch />
|
|
*
|
|
* // Switch contrôlé
|
|
* <Switch
|
|
* checked={isEnabled}
|
|
* onCheckedChange={setIsEnabled}
|
|
* />
|
|
*
|
|
* // Switch désactivé
|
|
* <Switch disabled />
|
|
* ```
|
|
*
|
|
* @component
|
|
* @param {SwitchProps} props - Propriétés du composant
|
|
* @returns {JSX.Element} Élément label contenant un switch stylisé
|
|
*/
|
|
|
|
const Switch = React.forwardRef<HTMLInputElement, SwitchProps>(
|
|
({ className, checked, onCheckedChange, disabled, ...props }, ref) => {
|
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
if (onCheckedChange) {
|
|
onCheckedChange(e.target.checked);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<label
|
|
role="switch"
|
|
aria-checked={!!checked}
|
|
className={cn(
|
|
'peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors',
|
|
'focus-within:outline-none focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 focus-within:ring-offset-background',
|
|
'disabled:cursor-not-allowed disabled:opacity-50',
|
|
checked ? 'bg-primary' : 'bg-muted',
|
|
className,
|
|
)}
|
|
>
|
|
<input
|
|
ref={ref}
|
|
type="checkbox"
|
|
className="sr-only"
|
|
checked={checked}
|
|
onChange={handleChange}
|
|
disabled={disabled}
|
|
{...props}
|
|
/>
|
|
<span
|
|
className={cn(
|
|
'pointer-events-none block h-5 w-5 rounded-full bg-white shadow-lg ring-0 transition-transform',
|
|
checked ? 'translate-x-5' : 'translate-x-0',
|
|
)}
|
|
/>
|
|
</label>
|
|
);
|
|
},
|
|
);
|
|
Switch.displayName = 'Switch';
|
|
|
|
export { Switch };
|