index.tsx 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. 'use client'
  2. import React, { useEffect, useState } from 'react'
  3. import { Switch as OriginalSwitch } from '@headlessui/react'
  4. import classNames from '@/utils/classnames'
  5. type SwitchProps = {
  6. onChange?: (value: boolean) => void
  7. size?: 'xs' | 'sm' | 'md' | 'lg' | 'l'
  8. defaultValue?: boolean
  9. disabled?: boolean
  10. className?: string
  11. }
  12. const Switch = React.forwardRef(
  13. ({ onChange, size = 'md', defaultValue = false, disabled = false, className }: SwitchProps,
  14. propRef: React.Ref<HTMLButtonElement>) => {
  15. const [enabled, setEnabled] = useState(defaultValue)
  16. useEffect(() => {
  17. setEnabled(defaultValue)
  18. }, [defaultValue])
  19. const wrapStyle = {
  20. lg: 'h-6 w-11',
  21. l: 'h-5 w-9',
  22. md: 'h-4 w-7',
  23. sm: 'h-3 w-5',
  24. xs: 'h-2.5 w-3.5',
  25. }
  26. const circleStyle = {
  27. lg: 'h-5 w-5',
  28. l: 'h-4 w-4',
  29. md: 'h-3 w-3',
  30. sm: 'h-2 w-2',
  31. xs: 'h-1.5 w-1',
  32. }
  33. const translateLeft = {
  34. lg: 'translate-x-5',
  35. l: 'translate-x-4',
  36. md: 'translate-x-3',
  37. sm: 'translate-x-2',
  38. xs: 'translate-x-1.5',
  39. }
  40. return (
  41. <OriginalSwitch
  42. ref={propRef}
  43. checked={enabled}
  44. onChange={(checked: boolean) => {
  45. if (disabled)
  46. return
  47. setEnabled(checked)
  48. onChange?.(checked)
  49. }}
  50. className={classNames(
  51. wrapStyle[size],
  52. enabled ? 'bg-components-toggle-bg' : 'bg-components-toggle-bg-unchecked',
  53. 'relative inline-flex flex-shrink-0 cursor-pointer rounded-[5px] border-2 border-transparent transition-colors duration-200 ease-in-out',
  54. disabled ? '!opacity-50 !cursor-not-allowed' : '',
  55. size === 'xs' && 'rounded-sm',
  56. className,
  57. )}
  58. >
  59. <span
  60. aria-hidden="true"
  61. className={classNames(
  62. circleStyle[size],
  63. enabled ? translateLeft[size] : 'translate-x-0',
  64. size === 'xs' && 'rounded-[1px]',
  65. 'pointer-events-none inline-block transform rounded-[3px] bg-components-toggle-knob shadow ring-0 transition duration-200 ease-in-out',
  66. )}
  67. />
  68. </OriginalSwitch>
  69. )
  70. })
  71. Switch.displayName = 'Switch'
  72. export default React.memo(Switch)