index.tsx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. import { Popover, Transition } from '@headlessui/react'
  2. import { Fragment, cloneElement, useRef } from 'react'
  3. import s from './style.module.css'
  4. type IPopover = {
  5. className?: string
  6. htmlContent: React.ReactNode
  7. trigger?: 'click' | 'hover'
  8. position?: 'bottom' | 'br'
  9. btnElement?: string | React.ReactNode
  10. btnClassName?: string | ((open: boolean) => string)
  11. }
  12. const timeoutDuration = 100
  13. export default function CustomPopover({
  14. trigger = 'hover',
  15. position = 'bottom',
  16. htmlContent,
  17. btnElement,
  18. className,
  19. btnClassName,
  20. }: IPopover) {
  21. const buttonRef = useRef<HTMLButtonElement>(null)
  22. const timeOutRef = useRef<NodeJS.Timeout | null>(null)
  23. const onMouseEnter = (isOpen: boolean) => {
  24. timeOutRef.current && clearTimeout(timeOutRef.current)
  25. !isOpen && buttonRef.current?.click()
  26. }
  27. const onMouseLeave = (isOpen: boolean) => {
  28. timeOutRef.current = setTimeout(() => {
  29. isOpen && buttonRef.current?.click()
  30. }, timeoutDuration)
  31. }
  32. return (
  33. <Popover className="relative">
  34. {({ open }: { open: boolean }) => {
  35. return (
  36. <>
  37. <div
  38. {...(trigger !== 'hover'
  39. ? {}
  40. : {
  41. onMouseLeave: () => onMouseLeave(open),
  42. onMouseEnter: () => onMouseEnter(open),
  43. })}
  44. >
  45. <Popover.Button
  46. ref={buttonRef}
  47. className={`group ${s.popupBtn} ${open ? '' : 'bg-gray-100'} ${
  48. !btnClassName
  49. ? ''
  50. : typeof btnClassName === 'string'
  51. ? btnClassName
  52. : btnClassName?.(open)
  53. }`}
  54. >
  55. {btnElement}
  56. </Popover.Button>
  57. <Transition as={Fragment}>
  58. <Popover.Panel
  59. className={`${s.popupPanel} ${
  60. position === 'br'
  61. ? 'right-0'
  62. : 'transform -translate-x-1/2 left-1/2'
  63. } ${className}`}
  64. {...(trigger !== 'hover'
  65. ? {}
  66. : {
  67. onMouseLeave: () => onMouseLeave(open),
  68. onMouseEnter: () => onMouseEnter(open),
  69. })}
  70. >
  71. {({ close }) => (
  72. <div
  73. className={s.panelContainer}
  74. {...(trigger !== 'hover'
  75. ? {}
  76. : {
  77. onMouseLeave: () => onMouseLeave(open),
  78. onMouseEnter: () => onMouseEnter(open),
  79. })}
  80. >
  81. {cloneElement(htmlContent as React.ReactElement, {
  82. onClose: () => close(),
  83. })}
  84. </div>
  85. )}
  86. </Popover.Panel>
  87. </Transition>
  88. </div>
  89. </>
  90. )
  91. }}
  92. </Popover>
  93. )
  94. }