index.tsx 2.1 KB

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