UNPKG

@athosws/react-components

Version:

This is a set of useful ReactJS components developed by Athos.\n Email:ladiesman217.as@gmail.com

130 lines (115 loc) 4.05 kB
import { useEffect, useRef, useState } from "react"; import type { ATHOSTooltipProps } from "./interface"; import { ATTooltipWrapper } from "./styled"; export const ATHOSTooltip = (props: ATHOSTooltipProps) => { const { children, forceOpen, position = "top", followCursor, tooltipContent, gap = 2, className, style } = props; const [open, setOpen] = useState(false); const tooltipRef = useRef<HTMLDivElement>(null); const childRef = useRef<HTMLDivElement>(null); const FollowChildPos = (mousePageX: number, mousePageY: number) => { if (!childRef.current || !tooltipRef.current) { return; } const childRect = childRef.current.getBoundingClientRect(); const tooltipRect = tooltipRef.current.getBoundingClientRect(); const screenHeight = window.innerHeight; const topPlusTT = childRect.top - tooltipRect.height - gap; const bottomPlusTT = screenHeight - childRect.bottom - tooltipRect.height - gap; const leftPlusTT = childRect.left - tooltipRect.width / 2 + childRect.width / 2; const compensateX = mousePageX - tooltipRect.width / 2; const compensateY = mousePageY - tooltipRect.height + gap; if (followCursor) { if (compensateY > 0) tooltipRef.current.style.top = `${compensateY}px`; if (compensateX > 0) tooltipRef.current.style.left = `${compensateX}px`; } else { if (position) { switch (position) { case "top": tooltipRef.current.style.top = `${topPlusTT}px`; break; case "bottom": tooltipRef.current.style.bottom = `${bottomPlusTT}px`; break; } } else { if (topPlusTT <= 0) { tooltipRef.current.style.bottom = `${bottomPlusTT}px`; } else { tooltipRef.current.style.top = `${topPlusTT}px`; } } if (leftPlusTT <= 0) { tooltipRef.current.style.left = `0px`; } else { tooltipRef.current.style.left = `${leftPlusTT}px`; } } }; const InitFollowChildPos = () => { if (!childRef.current || !tooltipRef.current) { return; } const childRect = childRef.current.getBoundingClientRect(); const tooltipRect = tooltipRef.current.getBoundingClientRect(); const screenHeight = window.innerHeight; const topPlusTT = childRect.top - tooltipRect.height - gap; const bottomPlusTT = screenHeight - childRect.bottom - tooltipRect.height - gap; const leftPlusTT = childRect.left - tooltipRect.width / 2 + childRect.width / 2; if (position) { switch (position) { case "top": tooltipRef.current.style.top = `${topPlusTT}px`; break; case "bottom": tooltipRef.current.style.bottom = `${bottomPlusTT}px`; break; } } else { if (topPlusTT <= 0) { tooltipRef.current.style.bottom = `${bottomPlusTT}px`; } else { tooltipRef.current.style.top = `${topPlusTT}px`; } } if (leftPlusTT <= 0) { tooltipRef.current.style.left = `0px`; } else { tooltipRef.current.style.left = `${leftPlusTT}px`; } }; useEffect(() => { InitFollowChildPos(); }, [tooltipRef.current, childRef.current]); return ( <> {(forceOpen || open) && ( <ATTooltipWrapper initial={{ opacity: 0 }} transition={{ duration: 0.14 }} /* exit={{ opacity: 0 }} */ animate={{ opacity: 1 }} ref={tooltipRef} className={className} style={style} > {tooltipContent} </ATTooltipWrapper> )} <div ref={childRef} onMouseMove={(e) => { if (followCursor && tooltipRef.current) { const { clientX: mouseX, clientY: mouseY } = e; FollowChildPos(mouseX, mouseY); } }} className="w-fit" onMouseOver={() => setOpen(true)} onMouseLeave={() => setOpen(false)} onMouseOut={() => setOpen(false)} > {children} </div> </> ); };