2023-05-15 08:51:32 +08:00
|
|
|
'use client'
|
|
|
|
import { useBoolean } from 'ahooks'
|
2023-08-22 10:58:06 +08:00
|
|
|
import React, { useEffect, useRef, useState } from 'react'
|
|
|
|
import type { FC } from 'react'
|
2023-05-15 08:51:32 +08:00
|
|
|
import { createRoot } from 'react-dom/client'
|
|
|
|
|
2023-08-22 10:58:06 +08:00
|
|
|
type IPortalToFollowElementProps = {
|
2023-05-15 08:51:32 +08:00
|
|
|
portalElem: React.ReactNode
|
|
|
|
children: React.ReactNode
|
|
|
|
controlShow?: number
|
|
|
|
controlHide?: number
|
|
|
|
}
|
|
|
|
|
|
|
|
const PortalToFollowElement: FC<IPortalToFollowElementProps> = ({
|
|
|
|
portalElem,
|
|
|
|
children,
|
|
|
|
controlShow,
|
2023-08-22 10:58:06 +08:00
|
|
|
controlHide,
|
2023-05-15 08:51:32 +08:00
|
|
|
}) => {
|
|
|
|
const [isShowContent, { setTrue: showContent, setFalse: hideContent, toggle: toggleContent }] = useBoolean(false)
|
|
|
|
const [wrapElem, setWrapElem] = useState<HTMLDivElement | null>(null)
|
|
|
|
|
|
|
|
useEffect(() => {
|
2023-08-22 10:58:06 +08:00
|
|
|
if (controlShow)
|
2023-05-15 08:51:32 +08:00
|
|
|
showContent()
|
|
|
|
}, [controlShow])
|
|
|
|
|
|
|
|
useEffect(() => {
|
2023-08-22 10:58:06 +08:00
|
|
|
if (controlHide)
|
2023-05-15 08:51:32 +08:00
|
|
|
hideContent()
|
|
|
|
}, [controlHide])
|
|
|
|
|
|
|
|
// todo use click outside hidden
|
2023-08-22 10:58:06 +08:00
|
|
|
const triggerElemRef = useRef<HTMLDivElement>(null)
|
2023-05-15 08:51:32 +08:00
|
|
|
|
|
|
|
const calLoc = () => {
|
|
|
|
const triggerElem = triggerElemRef.current
|
|
|
|
if (!triggerElem) {
|
|
|
|
return {
|
2023-08-22 10:58:06 +08:00
|
|
|
display: 'none',
|
2023-05-15 08:51:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
const {
|
|
|
|
left: triggerLeft,
|
|
|
|
top: triggerTop,
|
2023-08-22 10:58:06 +08:00
|
|
|
height,
|
|
|
|
} = triggerElem.getBoundingClientRect()
|
2023-05-15 08:51:32 +08:00
|
|
|
|
|
|
|
return {
|
|
|
|
position: 'fixed',
|
|
|
|
left: triggerLeft,
|
|
|
|
top: triggerTop + height,
|
2023-08-22 10:58:06 +08:00
|
|
|
zIndex: 999,
|
2023-05-15 08:51:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
if (isShowContent) {
|
|
|
|
const holder = document.createElement('div')
|
|
|
|
const root = createRoot(holder)
|
|
|
|
const style = calLoc()
|
|
|
|
root.render(
|
|
|
|
<div style={style as React.CSSProperties}>
|
|
|
|
{portalElem}
|
2023-08-22 10:58:06 +08:00
|
|
|
</div>,
|
2023-05-15 08:51:32 +08:00
|
|
|
)
|
|
|
|
document.body.appendChild(holder)
|
|
|
|
setWrapElem(holder)
|
|
|
|
console.log(holder)
|
2023-08-22 10:58:06 +08:00
|
|
|
}
|
|
|
|
else {
|
2023-05-15 08:51:32 +08:00
|
|
|
wrapElem?.remove?.()
|
|
|
|
setWrapElem(null)
|
|
|
|
}
|
|
|
|
}, [isShowContent])
|
|
|
|
|
|
|
|
return (
|
2023-08-22 10:58:06 +08:00
|
|
|
<div ref={triggerElemRef as React.RefObject<HTMLDivElement>} onClick={toggleContent}>
|
2023-05-15 08:51:32 +08:00
|
|
|
{children}
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
export default React.memo(PortalToFollowElement)
|