mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-11-16 03:40:37 +08:00
Move context menu key navigation into lib
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
Reference in New Issue
Block a user