"use client"; import { useCallback, useEffect, useRef, useState } from "react"; const cmsDetails = [ ["Edition", "EpicNext"], ["Interface", "Modern CMS"], ["Runtime", "Next.js App Router"], ]; const credits = [ ["Remco", "EpicNext direction and development"], ["Life", "Next.js architecture and CMS refinements"], ]; const EXIT_MS = 180; export function CmsInfoPopup() { const [open, setOpen] = useState(false); // The dialog used to unmount through AnimatePresence so its fade-out could // finish. With the animation in CSS the component holds it for the length of // the fade instead; this popup lives in the public footer, so pulling in // motion/react for it was not worth it. const [closing, setClosing] = useState(false); const exitTimer = useRef | null>(null); // Stable identity so the keydown effect below does not re-subscribe on every // render. const close = useCallback(() => { if (exitTimer.current) return; setClosing(true); exitTimer.current = setTimeout(() => { exitTimer.current = null; setOpen(false); setClosing(false); }, EXIT_MS); }, []); useEffect( () => () => { if (exitTimer.current) clearTimeout(exitTimer.current); }, [], ); useEffect(() => { if (window.location.hash === "#cms-info") setOpen(true); }, []); useEffect(() => { if (!open) return; const onKeyDown = (event: KeyboardEvent) => { if (event.key === "Escape") close(); }; document.addEventListener("keydown", onKeyDown); return () => document.removeEventListener("keydown", onKeyDown); }, [open, close]); return ( <> {open ? (
{/* A real button, so click-outside-to-dismiss is reachable by keyboard and assistive tech instead of being a pointer-only handler on a static element. The layer itself is pointer-transparent so only these two children catch clicks. */}
CMS information

EpicNext

Modern Next.js CMS

A modern web layer for the hotel community, focused on fast navigation, clean administration, and a coherent EpicNext experience.

{cmsDetails.map(([label, value]) => (
{label}
{value}
))}
{credits.map(([name, role]) => (
{name} {role}
))}
) : null} ); }