Something went wrong. Try again.
Monorepo for the nibling.run platform
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114<!doctype html><html lang="en"> <head> <meta charset="utf-8" /> <title>nib-tablist fixture</title> <style> body { margin: 0; background: var(--nib-color-canvas); color: var(--nib-color-text); font: var(--nib-font-ui); } /* A narrow stack-shaped container to force the tablist's overflow and container queries. */ #stack { width: 220px; border: var(--nib-size-border) solid var(--nib-color-border); } #stack[data-focused] nib-tablist nib-tab { --nib-tab-indicator: var(--nib-color-accent); } nib-tabpanel { padding: var(--nib-space-md); } #log { padding: var(--nib-space-sm); color: var(--nib-color-text-muted); } </style> </head> <body> <div id="stack"> <nib-tablist id="tablist"></nib-tablist> <nib-tabpanel id="panel-readme" data-for="readme"> <p>README contents.</p> </nib-tabpanel> <nib-tabpanel id="panel-main" data-for="main" hidden> <p>main.ts contents.</p> </nib-tabpanel> <nib-tabpanel id="panel-greet" data-for="greet" hidden> <label>Rename: <input /></label> </nib-tabpanel> <nib-tabpanel id="panel-notes" data-for="notes" hidden> <p>Scratch notes, nothing focusable here.</p> </nib-tabpanel> </div> <p id="log" aria-live="polite"></p> <script type="module"> // A <link> to a stylesheet outside the fixtures dir 404s: Vite's dev server treats fixtures/ // as the URL root, so "../src/..." in an href can't resolve above it. A JS import goes // through Vite's module graph instead, which resolves against the real filesystem. import "../src/tokens/tokens.css"; import { TabSelectEvent } from "../src/tabs/element.ts";
const stack = document.getElementById("stack"); const tablist = document.getElementById("tablist"); const log = document.getElementById("log"); tablist.label = "Open files";
// Two closable (main, greet, with greet last so closing it exercises the "previous" side of // focus recovery) and enough labels to overflow the narrow stack below. const files = [ { id: "readme", label: "README.md", closable: false }, { id: "notes", label: "notes.txt", closable: false }, { id: "main", label: "main.ts", closable: true }, { id: "greet", label: "greet.ts", closable: true }, ];
for (const file of files) { const tab = document.createElement("nib-tab"); tab.id = `tab-${file.id}`; tab.textContent = file.label; tab.closable = file.closable; const panel = document.getElementById(`panel-${file.id}`); tab.panel = panel; panel.tab = tab; tablist.append(tab); }
// The list owns focus and keyboard handling; selection and panel visibility stay with us. function select(tab) { for (const t of tablist.children) t.selected = t === tab; for (const p of stack.querySelectorAll("nib-tabpanel")) p.hidden = p !== tab.panel; log.textContent = `selected ${tab.textContent}`; }
tablist.addEventListener("tabselect", (event) => select(event.tab)); tablist.addEventListener("tabclose", (event) => { const tab = event.tab; const panel = tab.panel; const wasCurrent = tab.selected; const siblings = Array.from(tablist.children); const index = siblings.indexOf(tab); tab.remove(); panel?.remove(); log.textContent = `closed ${tab.textContent}`; if (wasCurrent) { const next = siblings[index + 1] ?? siblings[index - 1]; if (next) select(next); } });
// Simulate the initial activation the same way a real interaction would, so the tablist's // roving tabindex and our own `selected` state agree before anyone has touched the tabs. // Picking a tab other than the first exercises that agreement, rather than hiding behind // index 0 being both the default and the one under test. const initial = document.getElementById("tab-main"); initial.dispatchEvent(new TabSelectEvent(initial));
stack.addEventListener("focusin", () => stack.setAttribute("data-focused", "")); stack.addEventListener("focusout", () => stack.removeAttribute("data-focused")); </script> </body></html>