index.tsx 1.7 KB

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