// the list beside the paper: selection, and folding. a section opens when the selection moves
// into it and closes again when it leaves, unless you opened it by hand with its triangle.

export interface Tree {
  /** select the entry linking to #key ("" is the top of the page) */
  select(key: string): void;
  /** entries in list order that aren't folded away, for ↑↓ */
  visible(): string[];
}

/** `side` is Tree.astro's column: the title and the list */
export function tree(side: HTMLElement): Tree {
  const list = side.querySelector<HTMLElement>(".tree")!;
  const links = new Map([...side.querySelectorAll<HTMLAnchorElement>("[data-to]")].map((a) => [a.dataset.to!, a]));
  const open = new Set<string>();
  let auto: string | null = null;

  const fold = () =>
    list.querySelectorAll<HTMLElement>("[data-grp]").forEach((g) => {
      const isOpen = open.has(g.dataset.grp!);
      g.classList.toggle("open", isOpen);
      g.querySelector("[data-fold]")?.setAttribute("aria-expanded", String(isOpen));
    });

  list.addEventListener("click", (e) => {
    const btn = (e.target as HTMLElement).closest<HTMLElement>("[data-fold]");
    if (!btn) return;
    const g = btn.dataset.fold!;
    open.has(g) ? open.delete(g) : open.add(g);
    if (auto === g) auto = null;
    fold();
  });

  return {
    select(key) {
      const link = links.get(key);
      links.forEach((a, k) => a.classList.toggle("sel", k === key));
      const group = link?.closest<HTMLElement>("[data-grp]")?.dataset.grp ?? null;
      if (auto && auto !== group) open.delete(auto);
      if (group && !open.has(group)) {
        open.add(group);
        auto = group;
      }
      fold();
      // scroll the list only, never the page around it
      if (link) {
        const r = link.getBoundingClientRect(), box = list.getBoundingClientRect();
        if (r.top < box.top) list.scrollTop -= box.top - r.top;
        else if (r.bottom > box.bottom) list.scrollTop += r.bottom - box.bottom;
      }
    },
    visible: () => [...links].filter(([, a]) => a.offsetParent).map(([k]) => k),
  };
}
