index.tsx 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  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?: '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. }
  25. const circleStyle = {
  26. lg: 'h-5 w-5',
  27. l: 'h-4 w-4',
  28. md: 'h-3 w-3',
  29. sm: 'h-2 w-2',
  30. }
  31. const translateLeft = {
  32. lg: 'translate-x-5',
  33. l: 'translate-x-4',
  34. md: 'translate-x-3',
  35. sm: 'translate-x-2',
  36. }
  37. return (
  38. <OriginalSwitch
  39. ref={propRef}
  40. checked={enabled}
  41. onChange={(checked: boolean) => {
  42. if (disabled)
  43. return
  44. setEnabled(checked)
  45. onChange?.(checked)
  46. }}
  47. className={classNames(
  48. wrapStyle[size],
  49. enabled ? 'bg-components-toggle-bg' : 'bg-components-toggle-bg-unchecked',
  50. 'relative inline-flex flex-shrink-0 cursor-pointer rounded-[5px] border-2 border-transparent transition-colors duration-200 ease-in-out',
  51. disabled ? '!opacity-50 !cursor-not-allowed' : '',
  52. className,
  53. )}
  54. >
  55. <span
  56. aria-hidden="true"
  57. className={classNames(
  58. circleStyle[size],
  59. enabled ? translateLeft[size] : 'translate-x-0',
  60. 'pointer-events-none inline-block transform rounded-[3px] bg-components-toggle-knob shadow ring-0 transition duration-200 ease-in-out',
  61. )}
  62. />
  63. </OriginalSwitch>
  64. )
  65. })
  66. Switch.displayName = 'Switch'
  67. export default React.memo(Switch)