import { useEffect, useRef, useState } from "react"; import { createPortal } from "react-dom"; import { DotsThreeVertical } from "@phosphor-icons/react"; import { cn } from "../../lib/cn"; import { IconButton } from "../actions/icon-button"; import { Menu, type MenuItem } from "../actions/menu"; export type DataRowAction = Omit & { onClick: (row: T) => void; }; export function RowActions({ actions, row, }: { actions: DataRowAction[]; row: T; }) { const containerRef = useRef(null); const menuRef = useRef(null); const [open, setOpen] = useState(false); const [menuPosition, setMenuPosition] = useState({ left: 0, top: 0 }); function updateMenuPosition() { const bounds = containerRef.current?.getBoundingClientRect(); if (!bounds) { return; } setMenuPosition({ left: bounds.right, top: bounds.bottom + 6, }); } useEffect(() => { function handlePointerDown(event: globalThis.PointerEvent) { const target = event.target as Node; if (!containerRef.current?.contains(target) && !menuRef.current?.contains(target)) { setOpen(false); } } function handleEscape(event: KeyboardEvent) { if (event.key === "Escape") { setOpen(false); } } window.addEventListener("pointerdown", handlePointerDown); window.addEventListener("keydown", handleEscape); return () => { window.removeEventListener("pointerdown", handlePointerDown); window.removeEventListener("keydown", handleEscape); }; }, []); useEffect(() => { if (!open) { return; } updateMenuPosition(); window.addEventListener("resize", updateMenuPosition); window.addEventListener("scroll", updateMenuPosition, true); return () => { window.removeEventListener("resize", updateMenuPosition); window.removeEventListener("scroll", updateMenuPosition, true); }; }, [open]); const portalRoot = typeof document !== "undefined" ? document.querySelector("[data-uhuru-root]") ?? document.body : null; return (
} label="Row actions" onClick={(event) => { event.stopPropagation(); setOpen((current) => !current); }} tone="ghost" /> {open && portalRoot ? createPortal(
({ ...action, onSelect: () => { action.onClick(row); setOpen(false); }, }))} />
, portalRoot, ) : null}
); }