Add a go to first/last menu-item helper method in the Menu class

This fixes a bug when using the <kbd>Home</kbd> and <kbd>End</kbd> keyboard shortcuts to navigate through a `Menu` instance. These two buttons didn't update the `#lastIndex` field, which means that e.g. a following <kbd>ArrowDown</kbd> or <kbd>ArrowUp</kbd> press could make focus "jump" to an unexpected menu-item.

Also, the helper method reduces a little bit of code duplication in the event handlers.
This commit is contained in:
Jonas Jenwald 2026-08-02 14:58:59 +02:00
parent b4ba666b0c
commit fc610d3e59

View File

@ -146,19 +146,11 @@ class Menu {
stopEvent(e); stopEvent(e);
break; break;
case "Home": case "Home":
this.#menuItems this.#goToFirstLast(false);
.find(
item => !item.disabled && !item.classList.contains("hidden")
)
?.focus();
stopEvent(e); stopEvent(e);
break; break;
case "End": case "End":
this.#menuItems this.#goToFirstLast(true);
.findLast(
item => !item.disabled && !item.classList.contains("hidden")
)
?.focus();
stopEvent(e); stopEvent(e);
break; break;
default: default:
@ -194,11 +186,7 @@ class Menu {
if (!this.#openMenuAC) { if (!this.#openMenuAC) {
this.#openMenu(); this.#openMenu();
} }
this.#menuItems this.#goToFirstLast(false);
.find(
item => !item.disabled && !item.classList.contains("hidden")
)
?.focus();
break; break;
case "ArrowUp": case "ArrowUp":
case "End": case "End":
@ -206,11 +194,7 @@ class Menu {
if (!this.#openMenuAC) { if (!this.#openMenuAC) {
this.#openMenu(); this.#openMenu();
} }
this.#menuItems this.#goToFirstLast(true);
.findLast(
item => !item.disabled && !item.classList.contains("hidden")
)
?.focus();
break; break;
case "Escape": case "Escape":
this.#closeMenu(); this.#closeMenu();
@ -252,6 +236,20 @@ class Menu {
} }
} }
/**
* Go to the first/last menu item.
* @param {boolean} [last]
*/
#goToFirstLast(last = false) {
const i = this.#menuItems[last ? "findLastIndex" : "findIndex"](
item => !item.disabled && !item.classList.contains("hidden")
);
if (i >= 0) {
this.#menuItems[i].focus();
this.#lastIndex = i;
}
}
destroy() { destroy() {
this.#closeMenu(); this.#closeMenu();
this.#menuAC?.abort(); this.#menuAC?.abort();