menu-dialog.tsx 1.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. import { Fragment, useCallback, useEffect } from 'react'
  2. import type { ReactNode } from 'react'
  3. import { Dialog, DialogPanel, Transition, TransitionChild } from '@headlessui/react'
  4. import cn from '@/utils/classnames'
  5. type DialogProps = {
  6. className?: string
  7. children: ReactNode
  8. show: boolean
  9. onClose?: () => void
  10. }
  11. const MenuDialog = ({
  12. className,
  13. children,
  14. show,
  15. onClose,
  16. }: DialogProps) => {
  17. const close = useCallback(() => onClose?.(), [onClose])
  18. useEffect(() => {
  19. const handleKeyDown = (event: KeyboardEvent) => {
  20. if (event.key === 'Escape') {
  21. event.preventDefault()
  22. close()
  23. }
  24. }
  25. document.addEventListener('keydown', handleKeyDown)
  26. return () => {
  27. document.removeEventListener('keydown', handleKeyDown)
  28. }
  29. }, [close])
  30. return (
  31. <Transition appear show={show} as={Fragment}>
  32. <Dialog as="div" className="relative z-[60]" onClose={() => { }}>
  33. <div className="fixed inset-0">
  34. <div className="flex min-h-full flex-col items-center justify-center">
  35. <TransitionChild>
  36. <DialogPanel className={cn(
  37. 'relative h-full w-full grow overflow-hidden bg-background-sidenav-bg p-0 text-left align-middle backdrop-blur-md transition-all',
  38. 'duration-300 ease-in data-[closed]:scale-95 data-[closed]:opacity-0',
  39. 'data-[enter]:scale-100 data-[enter]:opacity-100',
  40. 'data-[enter]:scale-95 data-[leave]:opacity-0',
  41. className,
  42. )}>
  43. <div className='absolute right-0 top-0 h-full w-1/2 bg-components-panel-bg' />
  44. {children}
  45. </DialogPanel>
  46. </TransitionChild>
  47. </div>
  48. </div>
  49. </Dialog>
  50. </Transition >
  51. )
  52. }
  53. export default MenuDialog