103 lines
3.4 KiB
TypeScript
103 lines
3.4 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import { useHotkeys } from "react-hotkeys-hook";
|
|
import { cIf, cJoin } from "helpers/className";
|
|
import { atoms } from "contexts/atoms";
|
|
import { useAtomSetter } from "helpers/atoms";
|
|
import { Button } from "components/Inputs/Button";
|
|
|
|
/*
|
|
* ╭─────────────╮
|
|
* ───────────────────────────────────────╯ COMPONENT ╰───────────────────────────────────────────
|
|
*/
|
|
|
|
interface Props {
|
|
onOpen?: () => void;
|
|
onCloseRequest?: () => void;
|
|
isVisible: boolean;
|
|
children: React.ReactNode;
|
|
fillViewport?: boolean;
|
|
hideBackground?: boolean;
|
|
padding?: boolean;
|
|
withCloseButton?: boolean;
|
|
}
|
|
|
|
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
|
|
|
export const Popup = ({
|
|
onOpen,
|
|
onCloseRequest,
|
|
isVisible,
|
|
children,
|
|
fillViewport,
|
|
hideBackground = false,
|
|
padding = true,
|
|
withCloseButton = true,
|
|
}: Props): JSX.Element => {
|
|
const setMenuGesturesEnabled = useAtomSetter(atoms.layout.menuGesturesEnabled);
|
|
const [isHidden, setHidden] = useState(!isVisible);
|
|
const [isActuallyVisible, setActuallyVisible] = useState(isVisible && !isHidden);
|
|
|
|
useHotkeys("escape", () => onCloseRequest?.(), { enabled: isVisible }, [onCloseRequest]);
|
|
|
|
useEffect(() => {
|
|
setMenuGesturesEnabled(!isVisible);
|
|
}, [isVisible, setMenuGesturesEnabled]);
|
|
|
|
// Used to unload the component if not visible
|
|
useEffect(() => {
|
|
const timeouts: NodeJS.Timeout[] = [];
|
|
if (isVisible) {
|
|
setHidden(false);
|
|
// We delay the visiblity of the element so that the opening animation is played
|
|
timeouts.push(
|
|
setTimeout(() => {
|
|
setActuallyVisible(true);
|
|
onOpen?.();
|
|
}, 100)
|
|
);
|
|
} else {
|
|
setActuallyVisible(false);
|
|
timeouts.push(setTimeout(() => setHidden(true), 600));
|
|
}
|
|
return () => timeouts.forEach(clearTimeout);
|
|
}, [isVisible, onOpen]);
|
|
|
|
return isHidden ? (
|
|
<></>
|
|
) : (
|
|
<div
|
|
className={cJoin(
|
|
"fixed inset-0 z-50 grid place-content-center transition-filter duration-500",
|
|
cIf(isActuallyVisible, "backdrop-blur", "pointer-events-none touch-none")
|
|
)}>
|
|
<div
|
|
className={cJoin(
|
|
"fixed inset-0 transition-colors duration-500",
|
|
cIf(isActuallyVisible, "bg-shade/50", "bg-shade/0")
|
|
)}
|
|
onClick={onCloseRequest}
|
|
/>
|
|
|
|
<div
|
|
className={cJoin(
|
|
"grid place-items-center gap-4 transition-transform",
|
|
cIf(padding, "p-10"),
|
|
cIf(isActuallyVisible, "scale-100", "scale-0"),
|
|
cIf(
|
|
fillViewport,
|
|
"absolute inset-10 content-start overflow-scroll",
|
|
"relative max-h-[80vh] overflow-y-auto"
|
|
),
|
|
cIf(!hideBackground, "rounded-lg bg-light shadow-2xl shadow-shade")
|
|
)}>
|
|
{withCloseButton && (
|
|
<div className="absolute right-6 top-6">
|
|
<Button icon="close" onClick={onCloseRequest} />
|
|
</div>
|
|
)}
|
|
{children}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|