index.tsx 1.9 KB

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