radioGroupNav now accepts an onChange callback; PersonTypeSelector passes select() as the callback so ArrowLeft/Right navigation updates the hidden input value. aria-live region starts empty and announces only on user interaction (fixes initial page-load announcement). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
38 lines
1.1 KiB
TypeScript
38 lines
1.1 KiB
TypeScript
export function radioGroupNav(
|
|
node: HTMLElement,
|
|
onChange?: (value: string) => void
|
|
): { destroy: () => void; update: (onChange?: (value: string) => void) => void } {
|
|
let onChangeFn = onChange;
|
|
|
|
function getRadios(): HTMLElement[] {
|
|
return Array.from(node.querySelectorAll<HTMLElement>('[role="radio"]'));
|
|
}
|
|
|
|
function handleKeydown(event: KeyboardEvent) {
|
|
if (event.key !== 'ArrowRight' && event.key !== 'ArrowLeft') return;
|
|
|
|
const radios = getRadios();
|
|
const current = radios.indexOf(document.activeElement as HTMLElement);
|
|
if (current === -1) return;
|
|
|
|
const delta = event.key === 'ArrowRight' ? 1 : -1;
|
|
const next = (current + delta + radios.length) % radios.length;
|
|
|
|
radios[current].setAttribute('aria-checked', 'false');
|
|
radios[next].setAttribute('aria-checked', 'true');
|
|
radios[next].focus();
|
|
onChangeFn?.(radios[next].getAttribute('value') ?? '');
|
|
}
|
|
|
|
node.addEventListener('keydown', handleKeydown);
|
|
|
|
return {
|
|
update(newOnChange) {
|
|
onChangeFn = newOnChange;
|
|
},
|
|
destroy() {
|
|
node.removeEventListener('keydown', handleKeydown);
|
|
}
|
|
};
|
|
}
|