85 lines
1.9 KiB
TypeScript
Raw Normal View History

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)