Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | 2x 6x 6x 6x 18x 18x 18x 18x 18x 18x 5x 4x 7x 4x 1x 1x 4x 6x 6x 6x 6x 6x 6x 6x 1x 1x 2x 2x 1x 1x 1x 1x 1x 5x 5x 5x 5x 4x 4x 4x 4x 4x 5x | 'use client';
import { useEffect } from 'react';
/**
* Wires up the `::::tabs` directive (rendered at build time as an ARIA
* tablist, first panel visible). Clicking a tab or moving with the arrow
* keys / Home / End selects it and shows its panel (automatic activation, per
* the APG tabs pattern). Listeners are delegated on `document`, so tabs
* injected after client-side navigation work too.
*/
function select(tab: HTMLElement) {
const list = tab.closest('[role="tablist"]');
Iif (!list) return;
list.querySelectorAll<HTMLElement>('[role="tab"]').forEach((t) => {
const selected = t === tab;
t.setAttribute('aria-selected', String(selected));
t.tabIndex = selected ? 0 : -1;
const panelId = t.getAttribute('aria-controls');
const panel = panelId ? document.getElementById(panelId) : null;
Eif (panel) panel.hidden = !selected;
});
}
export function InlineTabsHydrator() {
useEffect(() => {
const tabFrom = (e: Event) =>
e.target instanceof Element ? e.target.closest<HTMLElement>('.inline-tabs__tab') : null;
const onClick = (e: Event) => {
const tab = tabFrom(e);
Eif (tab) select(tab);
};
const onKeyDown = (e: KeyboardEvent) => {
const tab = tabFrom(e);
const list = tab?.closest<HTMLElement>('[role="tablist"]');
Iif (!tab || !list) return;
const tabs = Array.from(list.querySelectorAll<HTMLElement>('[role="tab"]'));
const index = tabs.indexOf(tab);
// Arrow keys follow the reading direction.
const step = getComputedStyle(list).direction === 'rtl' ? -1 : 1;
let next: number;
switch (e.key) {
case 'ArrowRight':
next = index + step;
break;
case 'ArrowLeft':
next = index - step;
break;
case 'Home':
next = 0;
break;
case 'End':
next = tabs.length - 1;
break;
default:
return;
}
e.preventDefault();
const target = tabs[(next + tabs.length) % tabs.length];
select(target);
target.focus();
};
document.addEventListener('click', onClick);
document.addEventListener('keydown', onKeyDown);
return () => {
document.removeEventListener('click', onClick);
document.removeEventListener('keydown', onKeyDown);
};
}, []);
return null;
}
|