import { useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; import { useEscapeLayer } from "../escape"; import { useFocusTrap } from "../focus"; import { Icon } from "./Icon"; interface RowMenuProps { label: string; onDuplicate?: () => void; onMove?: () => void; onDelete: () => void; onToggleTitle?: () => void; titleHidden?: boolean; onToggleMargin?: () => void; marginHidden?: boolean; onOpenChange?: (open: boolean) => void; className?: string; tabIndex?: number; } export function RowMenu({ label, onDuplicate, onMove, onDelete, onToggleTitle, titleHidden, onToggleMargin, marginHidden, onOpenChange, className = "", tabIndex }: RowMenuProps) { const [open, setOpen] = useState(false); useEffect(() => { onOpenChange?.(open); }, [open]); const [coords, setCoords] = useState({ top: 0, right: 0 }); const btnRef = useRef(null); const popRef = useRef(null); const toggle = (e: React.MouseEvent) => { e.stopPropagation(); if (!open && btnRef.current) { const r = btnRef.current.getBoundingClientRect(); setCoords({ top: r.bottom + 4, right: window.innerWidth - r.right }); } setOpen((v) => !v); }; useFocusTrap(popRef, open); useEscapeLayer(open, () => { setOpen(false); btnRef.current?.focus(); }); const onMenuKeyDown = (e: React.KeyboardEvent) => { if (e.key === "ArrowDown" || e.key === "ArrowUp") { e.preventDefault(); const items = Array.from(popRef.current?.querySelectorAll(".row-menu-item") ?? []); const idx = items.indexOf(document.activeElement as HTMLElement); const nextIdx = e.key === "ArrowDown" ? (idx + 1) % items.length : (idx - 1 + items.length) % items.length; items[nextIdx]?.focus(); } }; useEffect(() => { if (!open) return; const onDown = (e: MouseEvent) => { if (btnRef.current?.contains(e.target as Node) || popRef.current?.contains(e.target as Node)) return; setOpen(false); }; const close = () => setOpen(false); document.addEventListener("mousedown", onDown, true); document.addEventListener("scroll", close, true); window.addEventListener("resize", close); return () => { document.removeEventListener("mousedown", onDown, true); document.removeEventListener("scroll", close, true); window.removeEventListener("resize", close); }; }, [open]); const toggleTitle = (e: React.MouseEvent) => { e.stopPropagation(); setOpen(false); onToggleTitle?.(); }; const toggleMargin = (e: React.MouseEvent) => { e.stopPropagation(); setOpen(false); onToggleMargin?.(); }; const duplicate = (e: React.MouseEvent) => { e.stopPropagation(); setOpen(false); onDuplicate?.(); }; const moveOut = (e: React.MouseEvent) => { e.stopPropagation(); setOpen(false); onMove?.(); }; const remove = (e: React.MouseEvent) => { e.stopPropagation(); setOpen(false); onDelete(); }; return ( <> {open && createPortal(
{onToggleTitle && ( )} {onToggleMargin && ( )} {onDuplicate && ( )} {onMove && ( )}
, document.body )} ); }