audit pass 13: context menu arrow key navigation

Add ArrowUp/ArrowDown/Home/End keyboard navigation between context
menu items per WAI-ARIA menu widget specification. Focus wraps at
top and bottom boundaries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-27 14:18:17 -04:00
parent 7630da8abd
commit 5caaf38e9a
+9 -1
View File
@@ -1697,7 +1697,15 @@
el.setAttribute('tabindex', '0'); el.setAttribute('tabindex', '0');
el.innerHTML = `${item.icon ? `<span class="icon">${escHtml(item.icon)}</span>` : '<span class="icon"></span>'}<span>${escHtml(item.label || '')}</span>${item.kbd ? `<kbd>${escHtml(item.kbd)}</kbd>` : ''}`; el.innerHTML = `${item.icon ? `<span class="icon">${escHtml(item.icon)}</span>` : '<span class="icon"></span>'}<span>${escHtml(item.label || '')}</span>${item.kbd ? `<kbd>${escHtml(item.kbd)}</kbd>` : ''}`;
el.addEventListener('click', () => { _ctxHide(); if (item.action) item.action(); }); el.addEventListener('click', () => { _ctxHide(); if (item.action) item.action(); });
el.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); el.click(); } }); el.addEventListener('keydown', e => {
const items = Array.from(_ctxMenu.querySelectorAll('[role="menuitem"]'));
const idx = items.indexOf(e.currentTarget);
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); el.click(); }
else if (e.key === 'ArrowDown') { e.preventDefault(); (items[idx + 1] || items[0]).focus(); }
else if (e.key === 'ArrowUp') { e.preventDefault(); (items[idx - 1] || items[items.length - 1]).focus(); }
else if (e.key === 'Home') { e.preventDefault(); items[0].focus(); }
else if (e.key === 'End') { e.preventDefault(); items[items.length - 1].focus(); }
});
_ctxMenu.appendChild(el); _ctxMenu.appendChild(el);
}); });
_ctxMenu.classList.add('is-open'); _ctxMenu.classList.add('is-open');