Switch.tsx 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142
  1. // Switch: two-state toggle. `label` is required and has no default, so a render
  2. // site cannot ship the control without an accessible name.
  3. import clsx from 'clsx'
  4. import css from './Switch.module.css'
  5. /**
  6. * Render a toggle switch.
  7. * @param props.checked - the current state; the control is fully controlled.
  8. * @param props.onChange - called with the state the click asks for.
  9. * @param props.label - localized accessible name, owned by the render site.
  10. * @param props.disabled - whether the control refuses input; owners also set it
  11. * while a write is in flight, not only when a deployment locks the toggle.
  12. * @param props.title - localized hover text, typically why the toggle is locked.
  13. * @param props.className - extra class for layout placement.
  14. * @returns the switch element.
  15. */
  16. export function Switch({ checked, onChange, label, disabled = false, title, className }: {
  17. checked: boolean
  18. onChange: (next: boolean) => void
  19. label: string
  20. disabled?: boolean
  21. title?: string | undefined
  22. // `| undefined` so a caller can forward an optional class straight through
  23. // under exactOptionalPropertyTypes (a CSS-module lookup is string|undefined).
  24. className?: string | undefined
  25. }) {
  26. return (
  27. <button
  28. type="button"
  29. role="switch"
  30. aria-checked={checked}
  31. aria-label={label}
  32. title={title}
  33. disabled={disabled}
  34. className={clsx(css.switch, className)}
  35. onClick={() => { onChange(!checked) }}
  36. >
  37. <span className={css.thumb} />
  38. </button>
  39. )
  40. }