2023-10-08 19:35:11 +02:00
|
|
|
import classNames from "classnames";
|
|
|
|
import { ReactNode, useCallback, useEffect, useRef, useState } from "react";
|
|
|
|
import { createPortal } from "react-dom";
|
|
|
|
|
|
|
|
import { Transition } from "@/components/Transition";
|
2023-10-08 20:12:31 +02:00
|
|
|
import { useOverlayRouter } from "@/hooks/useOverlayRouter";
|
2023-10-08 19:35:11 +02:00
|
|
|
|
|
|
|
export interface OverlayProps {
|
2023-10-08 20:12:31 +02:00
|
|
|
id: string;
|
2023-10-08 19:35:11 +02:00
|
|
|
children?: ReactNode;
|
|
|
|
darken?: boolean;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function OverlayDisplay(props: { children: ReactNode }) {
|
|
|
|
return <div className="popout-location">{props.children}</div>;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function Overlay(props: OverlayProps) {
|
2023-10-08 20:12:31 +02:00
|
|
|
const router = useOverlayRouter(props.id);
|
2023-10-08 19:35:11 +02:00
|
|
|
const [portalElement, setPortalElement] = useState<Element | null>(null);
|
|
|
|
const ref = useRef<HTMLDivElement>(null);
|
|
|
|
const target = useRef<Element | null>(null);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
function listen(e: MouseEvent) {
|
|
|
|
target.current = e.target as Element;
|
|
|
|
}
|
|
|
|
document.addEventListener("mousedown", listen);
|
|
|
|
return () => {
|
|
|
|
document.removeEventListener("mousedown", listen);
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
const click = useCallback(
|
|
|
|
(e: React.MouseEvent) => {
|
|
|
|
const startedTarget = target.current;
|
|
|
|
target.current = null;
|
|
|
|
if (e.currentTarget !== e.target) return;
|
|
|
|
if (!startedTarget) return;
|
|
|
|
if (!startedTarget.isEqualNode(e.currentTarget as Element)) return;
|
2023-10-08 20:12:31 +02:00
|
|
|
router.close();
|
2023-10-08 19:35:11 +02:00
|
|
|
},
|
2023-10-08 20:12:31 +02:00
|
|
|
[router]
|
2023-10-08 19:35:11 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
const element = ref.current?.closest(".popout-location");
|
|
|
|
setPortalElement(element ?? document.body);
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div ref={ref}>
|
|
|
|
{portalElement
|
|
|
|
? createPortal(
|
2023-10-08 20:12:31 +02:00
|
|
|
<Transition show={router.isOverlayActive()} animation="none">
|
2023-10-08 19:35:11 +02:00
|
|
|
<div className="popout-wrapper pointer-events-auto fixed inset-0 z-[999] select-none">
|
2023-10-08 20:12:31 +02:00
|
|
|
<Transition animation="fade" isChild>
|
|
|
|
<div
|
|
|
|
onClick={click}
|
|
|
|
className={classNames({
|
|
|
|
"absolute inset-0": true,
|
|
|
|
"bg-black opacity-90": props.darken,
|
|
|
|
})}
|
|
|
|
/>
|
|
|
|
</Transition>
|
2023-10-08 19:35:11 +02:00
|
|
|
<Transition animation="slide-up" className="h-0" isChild>
|
|
|
|
{props.children}
|
|
|
|
</Transition>
|
|
|
|
</div>
|
|
|
|
</Transition>,
|
|
|
|
portalElement
|
|
|
|
)
|
|
|
|
: null}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|