Move context menu key navigation into lib

This commit is contained in:
Rūdolfs Ošiņš
2026-10-01 18:20:25 +02:00
parent 9a023b625c
commit 08057ba962
3 changed files with 40 additions and 25 deletions
+5 -25
View File
@@ -4,6 +4,7 @@
import { autoUpdate, computePosition, flip, shift } from "@floating-ui/dom";
import { onMount } from "svelte";
import { menuFocusIndex } from "@app/lib/menuNavigation";
import { portal } from "@app/lib/portal";
import { getScrollViewport } from "@app/components/ScrollArea.svelte";
@@ -46,35 +47,14 @@
);
}
function focusItem(index: number) {
const items = menuItems();
if (items.length === 0) return;
const wrapped = ((index % items.length) + items.length) % items.length;
items[wrapped].focus();
}
function onMenuKeydown(ev: KeyboardEvent) {
const items = menuItems();
if (items.length === 0) return;
const current = items.findIndex(el => el === document.activeElement);
switch (ev.key) {
case "ArrowDown":
ev.preventDefault();
focusItem(current + 1);
break;
case "ArrowUp":
ev.preventDefault();
focusItem(current - 1);
break;
case "Home":
ev.preventDefault();
focusItem(0);
break;
case "End":
ev.preventDefault();
focusItem(items.length - 1);
break;
}
const next = menuFocusIndex(ev.key, current, items.length);
if (next === undefined) return;
ev.preventDefault();
items[next].focus();
}
function onMenuClick(ev: MouseEvent) {
+19
View File
@@ -0,0 +1,19 @@
export function menuFocusIndex(
key: string,
current: number,
count: number,
): number | undefined {
const wrap = (index: number) => ((index % count) + count) % count;
switch (key) {
case "ArrowDown":
return wrap(current + 1);
case "ArrowUp":
return wrap(current - 1);
case "Home":
return 0;
case "End":
return count - 1;
default:
return undefined;
}
}
+16
View File
@@ -0,0 +1,16 @@
import { expect, test } from "vitest";
import { menuFocusIndex } from "@app/lib/menuNavigation";
test.each([
["ArrowDown", -1, 0],
["ArrowDown", 0, 1],
["ArrowDown", 2, 0],
["ArrowUp", 0, 2],
["ArrowUp", 2, 1],
["Home", 1, 0],
["End", 0, 2],
["Enter", 1, undefined],
])("%j from %j of three focuses %j", (key, current, expected) => {
expect(menuFocusIndex(key, current, 3)).toBe(expected);
});