blob: a74a77fb57d0250ef6b6c7a9c6e054f3c625c4e8 [file] [log] [blame]
{
"version": 3,
"sources": ["../treenav.ts"],
"sourcesContent": ["/**\n * @license\n * Copyright 2024 The Go Authors. All rights reserved.\n * Use of this source code is governed by a BSD-style\n * license that can be found in the LICENSE file.\n */\n\n/**\n * A treeNavController adds dynamic expansion and selection of index list\n * elements based on scroll position.\n *\n * Use it as follows:\n * - Add the .js-Tree class to a parent element of your index and content.\n * - Add the .js-Tree-item class to <li> elements of your index.\n * - Add the .js-Tree-heading class to <hN> heading elements of your content.\n *\n * Then, when you scroll content, the 'aria-selected' and 'aria-expanded'\n * attributes of your tree items will be set according to the current content\n * scroll position. The included treenav.css implements styling to expand and\n * highlight index elements according to these attributes.\n */\nexport function treeNavController(el: HTMLElement) {\n const headings = el.querySelectorAll<HTMLHeadingElement>(\".js-Tree-heading\");\n const callback = () => {\n // Collect heading elements above the scroll position.\n let above: HTMLHeadingElement[] = [];\n for (const h of headings) {\n const rect = h.getBoundingClientRect();\n if (rect.height && rect.top < 80) {\n above.unshift(h);\n }\n }\n // Highlight the first heading even if we're not yet scrolled below it.\n if (above.length == 0 && headings[0] instanceof HTMLHeadingElement) {\n above = [headings[0]];\n }\n // Collect the set of heading levels we're immediately below, at most one\n // per heading level, by decresing level.\n // e.g. [<h3 element>, <h2 element>, <h1 element>]\n let threshold = Infinity;\n const active: HTMLHeadingElement[] = [];\n for (const h of above) {\n const level = Number(h.tagName[1]);\n if (level < threshold) {\n threshold = level;\n active.push(h);\n }\n }\n // Update aria-selected and aria-expanded for all items, per the current\n // position.\n const navItems = el.querySelectorAll<HTMLElement>(\".js-Tree-item\");\n for (const item of navItems) {\n const headingId = item.dataset[\"headingId\"];\n let selected = false,\n expanded = false;\n for (const h of active) {\n if (h.id === headingId) {\n if (h === active[0]) {\n selected = true;\n } else {\n expanded = true;\n }\n break;\n }\n }\n item.setAttribute(\"aria-selected\", selected ? \"true\" : \"false\");\n item.setAttribute(\"aria-expanded\", expanded ? \"true\" : \"false\");\n }\n };\n\n // Update on changes to viewport intersection, defensively debouncing to\n // guard against performance issues.\n const observer = new IntersectionObserver(debounce(callback, 20));\n for (const h of headings) {\n observer.observe(h);\n }\n}\n\nexport function debounce<T extends (...args: unknown[]) => unknown>(\n callback: T,\n wait: number\n) {\n let timeout: number;\n return (...args: unknown[]) => {\n clearTimeout(timeout);\n timeout = setTimeout(() => callback(...args), wait);\n };\n}\n"],
"mappings": ";mBAqBO,SAASA,EAAkBC,EAAiB,CACjD,IAAMC,EAAWD,EAAG,iBAAqC,kBAAkB,EACrEE,EAAW,IAAM,CAErB,IAAIC,EAA8B,CAAC,EACnC,QAAWC,KAAKH,EAAU,CACxB,IAAMI,EAAOD,EAAE,sBAAsB,EACjCC,EAAK,QAAUA,EAAK,IAAM,IAC5BF,EAAM,QAAQC,CAAC,EAIfD,EAAM,QAAU,GAAKF,EAAS,CAAC,YAAa,qBAC9CE,EAAQ,CAACF,EAAS,CAAC,CAAC,GAKtB,IAAIK,EAAY,IACVC,EAA+B,CAAC,EACtC,QAAWH,KAAKD,EAAO,CACrB,IAAMK,EAAQ,OAAOJ,EAAE,QAAQ,CAAC,CAAC,EAC7BI,EAAQF,IACVA,EAAYE,EACZD,EAAO,KAAKH,CAAC,GAKjB,IAAMK,EAAWT,EAAG,iBAA8B,eAAe,EACjE,QAAWU,KAAQD,EAAU,CAC3B,IAAME,EAAYD,EAAK,QAAQ,UAC3BE,EAAW,GACbC,EAAW,GACb,QAAWT,KAAKG,EACd,GAAIH,EAAE,KAAOO,EAAW,CAClBP,IAAMG,EAAO,CAAC,EAChBK,EAAW,GAEXC,EAAW,GAEb,MAGJH,EAAK,aAAa,gBAAiBE,EAAW,OAAS,OAAO,EAC9DF,EAAK,aAAa,gBAAiBG,EAAW,OAAS,OAAO,EAElE,EAIMC,EAAW,IAAI,qBAAqBC,EAASb,EAAU,EAAE,CAAC,EAChE,QAAWE,KAAKH,EACda,EAAS,QAAQV,CAAC,CAEtB,CAEO,SAASW,EACdb,EACAc,EACA,CACA,IAAIC,EACJ,MAAO,IAAIC,IAAoB,CAC7B,aAAaD,CAAO,EACpBA,EAAU,WAAW,IAAMf,EAAS,GAAGgB,CAAI,EAAGF,CAAI,CACpD,CACF",
"names": ["treeNavController", "el", "headings", "callback", "above", "h", "rect", "threshold", "active", "level", "navItems", "item", "headingId", "selected", "expanded", "observer", "debounce", "wait", "timeout", "args"]
}