/** * FlowRowMenu — the "⋯" overflow menu on a Workflows list row. Holds the * secondary/rare actions (Export, Duplicate, Delete) so the row's primary * actions (View runs, Run) stay uncluttered, and keeps the destructive Delete * out of the flat button row. Closes on Escape, outside click, or item select. * * Presentational + local open state only — each item calls back up to * `FlowListRow`, which routes to `FlowsPage`'s handlers. */ import { useCallback, useEffect, useRef, useState } from 'react'; import { useEscapeKey } from '../../hooks/useEscapeKey'; import { useT } from '../../lib/i18n/I18nContext'; export interface FlowRowMenuItem { key: string; label: string; onSelect: () => void; /** Renders the item in the destructive coral tone (e.g. Delete). */ danger?: boolean; testId?: string; } export interface FlowRowMenuProps { items: FlowRowMenuItem[]; /** Suffixed onto test ids so multiple rows stay addressable. */ rowId: string; } function KebabIcon() { return ( ); } export default function FlowRowMenu({ items, rowId }: FlowRowMenuProps) { const { t } = useT(); const [open, setOpen] = useState(false); const containerRef = useRef(null); useEscapeKey(() => setOpen(false), open); // Close on any click outside the menu container. useEffect(() => { if (!open) return; const onPointerDown = (event: MouseEvent) => { if (!containerRef.current?.contains(event.target as Node | null)) setOpen(false); }; document.addEventListener('mousedown', onPointerDown); return () => document.removeEventListener('mousedown', onPointerDown); }, [open]); const select = useCallback((onSelect: () => void) => { setOpen(false); onSelect(); }, []); return (
{open && (
{items.map(item => ( ))}
)}
); }