index.tsx 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. 'use client'
  2. import { useBoolean } from 'ahooks'
  3. import React, { useEffect, useRef, useState } from 'react'
  4. import type { FC } from 'react'
  5. import { createRoot } from 'react-dom/client'
  6. type IPortalToFollowElementProps = {
  7. portalElem: React.ReactNode
  8. children: React.ReactNode
  9. controlShow?: number
  10. controlHide?: number
  11. }
  12. const PortalToFollowElement: FC<IPortalToFollowElementProps> = ({
  13. portalElem,
  14. children,
  15. controlShow,
  16. controlHide,
  17. }) => {
  18. const [isShowContent, { setTrue: showContent, setFalse: hideContent, toggle: toggleContent }] = useBoolean(false)
  19. const [wrapElem, setWrapElem] = useState<HTMLDivElement | null>(null)
  20. useEffect(() => {
  21. if (controlShow)
  22. showContent()
  23. }, [controlShow])
  24. useEffect(() => {
  25. if (controlHide)
  26. hideContent()
  27. }, [controlHide])
  28. // todo use click outside hidden
  29. const triggerElemRef = useRef<HTMLDivElement>(null)
  30. const calLoc = () => {
  31. const triggerElem = triggerElemRef.current
  32. if (!triggerElem) {
  33. return {
  34. display: 'none',
  35. }
  36. }
  37. const {
  38. left: triggerLeft,
  39. top: triggerTop,
  40. height,
  41. } = triggerElem.getBoundingClientRect()
  42. return {
  43. position: 'fixed',
  44. left: triggerLeft,
  45. top: triggerTop + height,
  46. zIndex: 999,
  47. }
  48. }
  49. useEffect(() => {
  50. if (isShowContent) {
  51. const holder = document.createElement('div')
  52. const root = createRoot(holder)
  53. const style = calLoc()
  54. root.render(
  55. <div style={style as React.CSSProperties}>
  56. {portalElem}
  57. </div>,
  58. )
  59. document.body.appendChild(holder)
  60. setWrapElem(holder)
  61. console.log(holder)
  62. }
  63. else {
  64. wrapElem?.remove?.()
  65. setWrapElem(null)
  66. }
  67. }, [isShowContent])
  68. return (
  69. <div ref={triggerElemRef as React.RefObject<HTMLDivElement>} onClick={toggleContent}>
  70. {children}
  71. </div>
  72. )
  73. }
  74. export default React.memo(PortalToFollowElement)