37 lines (32 loc) · 1.11 KB
 1import { $$, asElement } from "./dom";
 2
 3const isInteractive = (target: EventTarget | null): boolean =>
 4  !!asElement(target)?.closest("a, button, input, textarea, select");
 5
 6/**
 7 * Make [data-card-link] elements clickable/keyboard-activatable as a whole,
 8 * while leaving inner interactive elements working. Internal links go through
 9 * the injected navigate(); external links open in a new tab.
10 */
11export const initCards = (
12  signal: AbortSignal,
13  navigate: (url: string) => void,
14): void => {
15  $$<HTMLElement>("[data-card-link]").forEach((card) => {
16    const href = card.dataset.cardLink;
17    if (!href) return;
18    const external = card.dataset.cardExternal === "true";
19
20    const go = () => {
21      if (external) window.open(href, "_blank", "noopener,noreferrer");
22      else navigate(href);
23    };
24
25    card.addEventListener("click", (e) => {
26      if (!isInteractive(e.target)) go();
27    }, { signal });
28
29    card.addEventListener("keydown", (e) => {
30      if (isInteractive(e.target)) return;
31      if (e.key === "Enter" || e.key === " ") {
32        e.preventDefault();
33        go();
34      }
35    }, { signal });
36  });
37};