// — a generic modal overlay, native `` element rather // than a hand-rolled focus-trap + backdrop: `showModal()` gives focus // trapping, Escape-to-close, and a real `::backdrop` for free, and // `@hive/shared`'s own shadow-DOM `hive-dialog` custom element isn't an // option here — swarm-ui's esbuild config can't consume those custom // elements at all (a separate, already-tracked gap). // No opinion on content beyond a close button; the caller's own markup // supplies whatever heading/layout it needs (its first real caller, // the create-agent form, already has an established two-panel layout // from its former life as a standalone route — reusing that unchanged // is simpler and lower-risk than re-deriving a modal-specific layout // for content mara has already reviewed). import { useEffect, useRef } from 'preact/hooks'; import type { ComponentChildren } from 'preact'; import './Dialog.css'; export function Dialog({ open, onClose, label, children, }: { open: boolean; onClose: () => void; // `aria-label` only, no visible title row — see file-top comment on // why content owns its own heading. label: string; children: ComponentChildren; }) { const ref = useRef(null); // Drives the native open/closed state from the `open` prop rather // than mounting/unmounting the element — `showModal()`/`close()` are // imperative, there's no declarative `` equivalent that // also gets you the backdrop + focus trap. useEffect(() => { const el = ref.current; if (!el) return; if (open && !el.open) el.showModal(); else if (!open && el.open) el.close(); }, [open]); // The dialog's own `close` event fires on Escape (and would fire on // a native
submit, unused here) — syncing it // back to the caller's state keeps `open` truthful even when nothing // in this component's own JS drove the close. useEffect(() => { const el = ref.current; if (!el) return; function handleClose() { onClose(); } el.addEventListener('close', handleClose); return () => el.removeEventListener('close', handleClose); }, [onClose]); return ( ` element itself (not any child) // exactly when it's outside the rendered content box — inside // the box, the click target is always some descendant. Standard // "click the backdrop to close" trick for native ``. onClick={(e) => { if (e.target === ref.current) onClose(); }} >
{children}
); }