Files
AgentHub/client/src/components/Switch.tsx
T

52 lines
1.9 KiB
TypeScript

/**
* @file Switch.tsx
* @description The app's ONE on/off switch (`role="switch"`): an accent track
* with a white knob, the same size everywhere. Settings rows, webhook targets
* and the AI Tools list all draw this — do not hand-roll another one; a
* yes/no choice inside a form or list is a `Checkbox` instead.
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
*/
export function Switch({
checked,
onChange,
label,
title,
disabled = false,
}: {
checked: boolean;
onChange: (v: boolean) => void;
/** Accessible name — a switch usually sits beside its visible label. */
label?: string;
title?: string;
disabled?: boolean;
}) {
return (
<button
type="button"
role="switch"
aria-checked={checked}
aria-label={label}
title={title}
disabled={disabled}
onClick={() => onChange(!checked)}
// On: the solid accent (white knob clears 3:1 in every theme). Off: a
// border-light track with an inset fg-muted/80 ring (≥3:1 in every theme), so the control's edge
// is visible on any surface (surface-4 alone was ~1:1 on a light card).
// Hover darkens the track (enabled only), so it reads as clickable.
className={`relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent focus-visible:ring-offset-1 focus-visible:ring-offset-surface-0 disabled:cursor-not-allowed disabled:opacity-40 ${
checked
? "bg-accent-solid enabled:hover:bg-accent-solid-hover"
: "bg-border-light ring-1 ring-inset ring-fg-muted/80 enabled:hover:ring-fg-secondary"
}`}
>
<span
aria-hidden
className={`inline-block h-4 w-4 rounded-full bg-white shadow-sm ring-1 ring-black/10 transition-transform duration-200 ease-out ${
checked ? "translate-x-[18px]" : "translate-x-0.5"
}`}
/>
</button>
);
}