geraete/components/ui/Switch.tsx
2025-11-26 15:00:05 +01:00

69 lines
1.7 KiB
TypeScript

// components/ui/Switch.tsx
'use client';
import * as React from 'react';
import clsx from 'clsx';
type SwitchProps = {
id?: string;
name?: string;
checked: boolean;
onChange: (checked: boolean) => void;
ariaLabel?: string;
ariaLabelledBy?: string;
ariaDescribedBy?: string;
disabled?: boolean;
className?: string;
};
export default function Switch({
id,
name,
checked,
onChange,
ariaLabel,
ariaLabelledBy,
ariaDescribedBy,
disabled = false,
className,
}: SwitchProps) {
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (disabled) return;
onChange(e.target.checked);
};
return (
<div
className={clsx(
'group relative inline-flex w-11 shrink-0 rounded-full bg-gray-200 p-0.5',
'inset-ring inset-ring-gray-900/5 outline-offset-2 outline-indigo-600',
'transition-colors duration-200 ease-in-out',
'has-checked:bg-indigo-600 has-focus-visible:outline-2',
'dark:bg-white/5 dark:inset-ring-white/10 dark:outline-indigo-500 dark:has-checked:bg-indigo-500',
disabled ? 'opacity-60 cursor-not-allowed' : 'cursor-pointer',
className,
)}
>
<span
className={clsx(
'size-5 rounded-full bg-white shadow-xs ring-1 ring-gray-900/5',
'transition-transform duration-200 ease-in-out',
'group-has-checked:translate-x-5',
)}
/>
<input
id={id}
name={name}
type="checkbox"
className="absolute inset-0 appearance-none focus:outline-hidden"
aria-label={ariaLabel}
aria-labelledby={ariaLabelledBy}
aria-describedby={ariaDescribedBy}
checked={checked}
onChange={handleChange}
disabled={disabled}
/>
</div>
);
}