diff --git a/core/pfe-core/controllers/at-focus-controller.ts b/core/pfe-core/controllers/at-focus-controller.ts index c8d099df06..03da55a838 100644 --- a/core/pfe-core/controllers/at-focus-controller.ts +++ b/core/pfe-core/controllers/at-focus-controller.ts @@ -49,8 +49,13 @@ export abstract class ATFocusController { set atFocusedItemIndex(index: number) { const previousIndex = this.#atFocusedItemIndex; - const direction = index > previousIndex ? 1 : -1; const { items, atFocusableItems } = this; + // - Home (index=0): always search forward to find first focusable item + // - End (index=last): always search backward to find last focusable item + // - Other cases: use comparison to determine direction + const direction = index === 0 ? 1 + : index >= items.length - 1 ? -1 + : index > previousIndex ? 1 : -1; const itemsIndexOfLastATFocusableItem = items.indexOf(this.atFocusableItems.at(-1)!); let itemToGainFocus = items.at(index); let itemToGainFocusIsFocusable = atFocusableItems.includes(itemToGainFocus!); diff --git a/core/pfe-core/controllers/combobox-controller.ts b/core/pfe-core/controllers/combobox-controller.ts index c69e129ea5..ca5292c017 100644 --- a/core/pfe-core/controllers/combobox-controller.ts +++ b/core/pfe-core/controllers/combobox-controller.ts @@ -735,9 +735,12 @@ export class ComboboxController< #onFocusoutListbox = (event: FocusEvent) => { if (!this.#hasTextInput && this.options.isExpanded()) { const root = this.#element?.getRootNode(); + // Check if focus moved to the toggle button + // If so, let the click handler manage toggle + const isToggleButton = event.relatedTarget === this.#button; if ((root instanceof ShadowRoot || root instanceof Document) && !this.items.includes(event.relatedTarget as Item) - ) { + && !isToggleButton) { this.#hide(); } } diff --git a/core/pfe-core/controllers/roving-tabindex-controller.ts b/core/pfe-core/controllers/roving-tabindex-controller.ts index 9659a11095..6189ac689b 100644 --- a/core/pfe-core/controllers/roving-tabindex-controller.ts +++ b/core/pfe-core/controllers/roving-tabindex-controller.ts @@ -77,6 +77,21 @@ export class RovingTabindexController< if (container instanceof HTMLElement) { container.addEventListener('focusin', () => this.#gainedInitialFocus = true, { once: true }); + // Sync atFocusedItemIndex when an item receives DOM focus (e.g., via mouse click) + // This ensures keyboard navigation starts from the correct position + container.addEventListener('focusin', (event: FocusEvent) => { + const target = event.target as Item; + const index = this.items.indexOf(target); + // Only update if the target is a valid item and index differs + if (index >= 0 && index !== this.atFocusedItemIndex) { + // Update index via setter, but avoid the focus() call by temporarily + // clearing #gainedInitialFocus to prevent redundant focus + const hadInitialFocus = this.#gainedInitialFocus; + this.#gainedInitialFocus = false; + this.atFocusedItemIndex = index; + this.#gainedInitialFocus = hadInitialFocus; + } + }); } else { this.#logger.warn('RovingTabindexController requires a getItemsContainer function'); }