index.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import { Dialog, Transition } from '@headlessui/react'
  2. import { Fragment } from 'react'
  3. import { XMarkIcon } from '@heroicons/react/24/outline'
  4. import classNames from '@/utils/classnames'
  5. // https://headlessui.com/react/dialog
  6. type IModal = {
  7. className?: string
  8. wrapperClassName?: string
  9. isShow: boolean
  10. onClose?: () => void
  11. title?: React.ReactNode
  12. description?: React.ReactNode
  13. children?: React.ReactNode
  14. closable?: boolean
  15. overflowVisible?: boolean
  16. }
  17. export default function Modal({
  18. className,
  19. wrapperClassName,
  20. isShow,
  21. onClose = () => { },
  22. title,
  23. description,
  24. children,
  25. closable = false,
  26. overflowVisible = false,
  27. }: IModal) {
  28. return (
  29. <Transition appear show={isShow} as={Fragment}>
  30. <Dialog as="div" className={classNames('modal-dialog', wrapperClassName)} onClose={onClose}>
  31. <Transition.Child
  32. as={Fragment}
  33. enter="ease-out duration-300"
  34. enterFrom="opacity-0"
  35. enterTo="opacity-100"
  36. leave="ease-in duration-200"
  37. leaveFrom="opacity-100"
  38. leaveTo="opacity-0"
  39. >
  40. <div className="fixed inset-0 bg-black bg-opacity-25" />
  41. </Transition.Child>
  42. <div
  43. className="fixed inset-0 overflow-y-auto"
  44. onClick={(e) => {
  45. e.preventDefault()
  46. e.stopPropagation()
  47. }}
  48. >
  49. <div className="flex min-h-full items-center justify-center p-4 text-center">
  50. <Transition.Child
  51. as={Fragment}
  52. enter="ease-out duration-300"
  53. enterFrom="opacity-0 scale-95"
  54. enterTo="opacity-100 scale-100"
  55. leave="ease-in duration-200"
  56. leaveFrom="opacity-100 scale-100"
  57. leaveTo="opacity-0 scale-95"
  58. >
  59. <Dialog.Panel className={classNames(
  60. 'modal-panel',
  61. overflowVisible ? 'overflow-visible' : 'overflow-hidden',
  62. className,
  63. )}>
  64. {title && <Dialog.Title
  65. as="h3"
  66. className="text-lg font-medium leading-6 text-gray-900"
  67. >
  68. {title}
  69. </Dialog.Title>}
  70. {description && <Dialog.Description className='text-gray-500 text-xs font-normal mt-2'>
  71. {description}
  72. </Dialog.Description>}
  73. {closable
  74. && <div className='absolute z-10 top-6 right-6 w-5 h-5 rounded-2xl flex items-center justify-center hover:cursor-pointer hover:bg-gray-100'>
  75. <XMarkIcon className='w-4 h-4 text-gray-500' onClick={
  76. (e) => {
  77. e.stopPropagation()
  78. onClose()
  79. }
  80. } />
  81. </div>}
  82. {children}
  83. </Dialog.Panel>
  84. </Transition.Child>
  85. </div>
  86. </div>
  87. </Dialog>
  88. </Transition>
  89. )
  90. }