Something went wrong. Try again.
Web frontend and supporting services for lance.blue
Something went wrong. Try again.
2.1 kB · 60 lines
TypeScript
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061/** * A masthead control that opens a panel under itself. * * There are two of them now — the account menu and the theme menu — and they * close the same three ways: the button again, a click anywhere outside, and * Escape. Written once here so that a fix to one is a fix to both. * * The document listeners exist only while a panel is open, so a page sitting * with both menus closed has none of them attached. */
/** * Wire a button to the panel it opens. * * `control` is the element that contains both, and is what an outside click is * measured against — the button's own click reaches the document listener too, * after its handler has already toggled the panel open, and the containment * check is what stops that closing it again. * * A link inside the panel closes it. On the app page a menu link is a hash * change, so nothing reloads and nothing else would ever put it away. * * Returns the open/close switch, for a panel item that is a button rather * than a link — the account menu's "Report an issue" opens a dialog, and the * menu has to put itself away first or it sits open behind the backdrop. */export function attachMenu( control: HTMLElement, button: HTMLElement, panel: HTMLElement,): (open: boolean) => void { const setOpen = (open: boolean): void => { panel.hidden = !open; button.setAttribute("aria-expanded", String(open)); if (open) { document.addEventListener("click", onDocumentClick); document.addEventListener("keydown", onKeyDown); } else { document.removeEventListener("click", onDocumentClick); document.removeEventListener("keydown", onKeyDown); } };
function onDocumentClick(event: MouseEvent): void { if (!control.contains(event.target as Node)) setOpen(false); }
function onKeyDown(event: KeyboardEvent): void { if (event.key !== "Escape") return; setOpen(false); button.focus(); }
button.addEventListener("click", () => setOpen(panel.hidden)); for (const item of panel.querySelectorAll("a")) { item.addEventListener("click", () => setOpen(false)); }
return setOpen;}