Keep keyboard selection visible in pickers

This commit is contained in:
Federico Jaramillo Martinez
2026-05-09 23:52:55 +02:00
parent 56590d8a6a
commit 359df2d1ff
3 changed files with 29 additions and 7 deletions
+9 -3
View File
@@ -53,9 +53,15 @@ export class ActionPalette extends LitElement {
} }
protected override updated(changed: PropertyValues) { protected override updated(changed: PropertyValues) {
if (!changed.has("actions") && !changed.has("queryText")) return; if (changed.has("actions") || changed.has("queryText")) {
const maxIndex = Math.max(0, this.filteredActions().length - 1); const maxIndex = Math.max(0, this.filteredActions().length - 1);
if (this.selectedIndex > maxIndex) this.selectedIndex = maxIndex; if (this.selectedIndex > maxIndex) this.selectedIndex = maxIndex;
}
if (changed.has("selectedIndex") || changed.has("actions") || changed.has("queryText")) this.scrollSelectedIntoView();
}
private scrollSelectedIntoView() {
this.renderRoot.querySelector<HTMLElement>(".options button.selected")?.scrollIntoView({ block: "nearest" });
} }
private filteredActions(): AppAction[] { private filteredActions(): AppAction[] {
@@ -1,4 +1,4 @@
import { LitElement, html } from "lit"; import { LitElement, html, type PropertyValues } from "lit";
import { customElement, property } from "lit/decorators.js"; import { customElement, property } from "lit/decorators.js";
import { autocompleteStyles, type CompletionItem } from "./shared"; import { autocompleteStyles, type CompletionItem } from "./shared";
@@ -23,5 +23,13 @@ export class AutocompleteMenu extends LitElement {
`; `;
} }
protected override updated(changed: PropertyValues) {
if (changed.has("selectedIndex") || changed.has("items")) this.scrollSelectedIntoView();
}
private scrollSelectedIntoView() {
this.renderRoot.querySelector<HTMLElement>("button.selected")?.scrollIntoView({ block: "nearest" });
}
static override styles = autocompleteStyles; static override styles = autocompleteStyles;
} }
+11 -3
View File
@@ -1,4 +1,4 @@
import { LitElement, html } from "lit"; import { LitElement, html, type PropertyValues } from "lit";
import { customElement, property, state } from "lit/decorators.js"; import { customElement, property, state } from "lit/decorators.js";
import type { CommandOption } from "../api"; import type { CommandOption } from "../api";
import { commandPickerStyles } from "./shared"; import { commandPickerStyles } from "./shared";
@@ -36,16 +36,24 @@ export class CommandPicker extends LitElement {
this.renderRoot.querySelector<HTMLElement>(".options")?.focus(); this.renderRoot.querySelector<HTMLElement>(".options")?.focus();
} }
protected override updated(changed: PropertyValues) {
if (changed.has("selectedIndex") || changed.has("options")) this.scrollSelectedIntoView();
}
private scrollSelectedIntoView() {
this.renderRoot.querySelector<HTMLElement>(".options button.selected")?.scrollIntoView({ block: "nearest" });
}
private handleKeyDown(event: KeyboardEvent) { private handleKeyDown(event: KeyboardEvent) {
if (event.key === "Escape") { if (event.key === "Escape") {
event.preventDefault(); event.preventDefault();
this.onCancel?.(); this.onCancel?.();
} else if (event.key === "ArrowDown") { } else if (event.key === "ArrowDown") {
event.preventDefault(); event.preventDefault();
this.selectedIndex = (this.selectedIndex + 1) % this.options.length; if (this.options.length > 0) this.selectedIndex = (this.selectedIndex + 1) % this.options.length;
} else if (event.key === "ArrowUp") { } else if (event.key === "ArrowUp") {
event.preventDefault(); event.preventDefault();
this.selectedIndex = (this.selectedIndex - 1 + this.options.length) % this.options.length; if (this.options.length > 0) this.selectedIndex = (this.selectedIndex - 1 + this.options.length) % this.options.length;
} else if (event.key === "Enter") { } else if (event.key === "Enter") {
event.preventDefault(); event.preventDefault();
const option = this.options[this.selectedIndex]; const option = this.options[this.selectedIndex];