| "use client"; |
|
|
| import { cn } from "@/shared/utils/cn"; |
|
|
| export default function Toggle({ |
| checked = false, |
| onChange, |
| label, |
| description, |
| disabled = false, |
| size = "md", |
| className, |
| }) { |
| const sizes = { |
| sm: { track: "w-8 h-4", thumb: "size-3", translate: "translate-x-4" }, |
| md: { track: "w-11 h-6", thumb: "size-5", translate: "translate-x-5" }, |
| lg: { track: "w-14 h-7", thumb: "size-6", translate: "translate-x-7" }, |
| }; |
|
|
| const handleClick = () => { |
| if (!disabled && onChange) onChange(!checked); |
| }; |
|
|
| return ( |
| <div |
| className={cn( |
| "flex items-center gap-3", |
| disabled && "opacity-50 cursor-not-allowed", |
| className |
| )} |
| > |
| <button |
| type="button" |
| role="switch" |
| aria-checked={checked} |
| disabled={disabled} |
| onClick={handleClick} |
| className={cn( |
| "relative inline-flex shrink-0 cursor-pointer rounded-full", |
| "transition-colors duration-200 ease-in-out", |
| "focus:outline-none focus:ring-2 focus:ring-brand-500/30", |
| checked ? "bg-brand-500" : "bg-surface-3", |
| sizes[size].track, |
| disabled && "cursor-not-allowed" |
| )} |
| > |
| <span |
| className={cn( |
| "pointer-events-none inline-block rounded-full bg-white shadow-sm", |
| "transform transition duration-200 ease-in-out", |
| checked ? sizes[size].translate : "translate-x-0.5", |
| sizes[size].thumb, |
| "mt-0.5" |
| )} |
| /> |
| </button> |
| {(label || description) && ( |
| <div className="flex flex-col"> |
| {label && ( |
| <span className="text-sm font-medium text-text-main">{label}</span> |
| )} |
| {description && ( |
| <span className="text-xs text-text-muted">{description}</span> |
| )} |
| </div> |
| )} |
| </div> |
| ); |
| } |
|
|