index.tsx 1.6 KB

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