mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
fix(utils): update focus visible to work with keydown again
This commit is contained in:
@@ -1,5 +1,19 @@
|
||||
const ION_FOCUSED = 'ion-focused';
|
||||
const ION_FOCUSABLE = 'ion-focusable';
|
||||
const FOCUS_KEYS = [
|
||||
'Tab',
|
||||
'ArrowDown',
|
||||
'Space',
|
||||
'Escape',
|
||||
' ',
|
||||
'Shift',
|
||||
'Enter',
|
||||
'ArrowLeft',
|
||||
'ArrowRight',
|
||||
'ArrowUp',
|
||||
'Home',
|
||||
'End',
|
||||
];
|
||||
|
||||
export interface FocusVisibleUtility {
|
||||
destroy: () => void;
|
||||
@@ -8,6 +22,8 @@ export interface FocusVisibleUtility {
|
||||
|
||||
export const startFocusVisible = (rootEl?: HTMLElement): FocusVisibleUtility => {
|
||||
let currentFocus: Element[] = [];
|
||||
let keyboardMode = false;
|
||||
let lastPointerType: string | null = null;
|
||||
|
||||
const ref = rootEl ? rootEl.shadowRoot! : document;
|
||||
const root = rootEl ? rootEl : document.body;
|
||||
@@ -18,16 +34,40 @@ export const startFocusVisible = (rootEl?: HTMLElement): FocusVisibleUtility =>
|
||||
currentFocus = elements;
|
||||
};
|
||||
|
||||
const pointerDown = () => {
|
||||
const pointerDown = (ev: Event) => {
|
||||
const pointerEvent = ev as PointerEvent;
|
||||
lastPointerType = pointerEvent.pointerType;
|
||||
keyboardMode = false;
|
||||
setFocus([]);
|
||||
};
|
||||
|
||||
const onFocusin = (ev: Event) => {
|
||||
const toFocus = ev.composedPath().filter((el): el is HTMLElement => {
|
||||
return el instanceof HTMLElement && el.classList.contains(ION_FOCUSABLE);
|
||||
});
|
||||
const onKeydown = (ev: Event) => {
|
||||
const keyboardEvent = ev as KeyboardEvent;
|
||||
// Always set keyboard mode to true when any key is pressed
|
||||
// This handles the WebKit Tab key bug where keydown might not fire
|
||||
keyboardMode = true;
|
||||
|
||||
setFocus(toFocus);
|
||||
// If it's not a focus key, clear focus immediately
|
||||
if (!FOCUS_KEYS.includes(keyboardEvent.key)) {
|
||||
setFocus([]);
|
||||
}
|
||||
};
|
||||
|
||||
const onFocusin = (ev: Event) => {
|
||||
// Check if this focus event is likely from keyboard navigation
|
||||
// We can detect this by checking if there was a recent keydown event
|
||||
// or if the focus target is a focusable element that typically gets focus via keyboard
|
||||
const target = ev.target as HTMLElement;
|
||||
|
||||
if (target.classList.contains(ION_FOCUSABLE)) {
|
||||
// If we're in keyboard mode or this looks like keyboard navigation
|
||||
if (keyboardMode || !lastPointerType) {
|
||||
const toFocus = ev.composedPath().filter((el): el is HTMLElement => {
|
||||
return el instanceof HTMLElement && el.classList.contains(ION_FOCUSABLE);
|
||||
});
|
||||
setFocus(toFocus);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onFocusout = () => {
|
||||
@@ -36,14 +76,18 @@ export const startFocusVisible = (rootEl?: HTMLElement): FocusVisibleUtility =>
|
||||
}
|
||||
};
|
||||
|
||||
ref.addEventListener('keydown', onKeydown);
|
||||
ref.addEventListener('focusin', onFocusin);
|
||||
ref.addEventListener('focusout', onFocusout);
|
||||
ref.addEventListener('pointerdown', pointerDown, { passive: true });
|
||||
ref.addEventListener('touchstart', pointerDown, { passive: true });
|
||||
ref.addEventListener('mousedown', pointerDown);
|
||||
|
||||
const destroy = () => {
|
||||
ref.removeEventListener('keydown', onKeydown);
|
||||
ref.removeEventListener('focusin', onFocusin);
|
||||
ref.removeEventListener('focusout', onFocusout);
|
||||
ref.removeEventListener('pointerdown', pointerDown);
|
||||
ref.removeEventListener('touchstart', pointerDown);
|
||||
ref.removeEventListener('mousedown', pointerDown);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user