55 lines
1.4 KiB
TypeScript
55 lines
1.4 KiB
TypeScript
"use client";
|
|
|
|
import { FC, PropsWithChildren, ReactNode, useCallback, useState } from "react";
|
|
import { PoppableContent } from "./poppable-content";
|
|
import { useDebounce } from "../../../hooks/useDebounce";
|
|
|
|
interface IProps {
|
|
content: ReactNode;
|
|
className?: string;
|
|
preferredEdge: "top" | "bottom" | "left" | "right";
|
|
preferredAlign: "centered" | "top" | "bottom" | "left" | "right";
|
|
spacing?: number;
|
|
}
|
|
|
|
export const Poppable: FC<PropsWithChildren<IProps>> = (
|
|
{ className, content, children, preferredEdge, preferredAlign, spacing },
|
|
) => {
|
|
const [isHovered, setIsHovered] = useState(false);
|
|
const closing = useDebounce(!isHovered, 1000);
|
|
const closed = useDebounce(closing, 300);
|
|
|
|
const [ref, setRef] = useState<HTMLElement>();
|
|
|
|
const updateRef = useCallback((node: HTMLElement) => {
|
|
if (!node) return;
|
|
setRef(node);
|
|
}, []);
|
|
|
|
return (
|
|
<>
|
|
<span
|
|
ref={updateRef}
|
|
className={className}
|
|
onMouseEnter={() => setIsHovered(true)}
|
|
onMouseLeave={() => setIsHovered(false)}
|
|
>
|
|
{children}
|
|
</span>
|
|
{!!ref && (
|
|
<PoppableContent
|
|
preferredAlign={preferredAlign}
|
|
preferredEdge={preferredEdge}
|
|
spacing={spacing}
|
|
isClosing={closing}
|
|
isClosed={closed}
|
|
relativeElement={ref}
|
|
setHover={setIsHovered}
|
|
>
|
|
{content}
|
|
</PoppableContent>
|
|
)}
|
|
</>
|
|
);
|
|
};
|