mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
fix(utils): update to track pointer instead of keyboard
This commit is contained in:
@@ -22,51 +22,49 @@ export interface FocusVisibleUtility {
|
||||
|
||||
export const startFocusVisible = (rootEl?: HTMLElement): FocusVisibleUtility => {
|
||||
let currentFocus: Element[] = [];
|
||||
let keyboardMode = false;
|
||||
let lastPointerType: string | null = null;
|
||||
// Tracks if the last interaction was a pointer event (mouse, touch, pen)
|
||||
// Used to distinguish between pointer and keyboard navigation for focus styling
|
||||
let hadPointerEvent = false;
|
||||
|
||||
const ref = rootEl ? rootEl.shadowRoot! : document;
|
||||
const root = rootEl ? rootEl : document.body;
|
||||
|
||||
// Adds or removes the focused class for styling
|
||||
const setFocus = (elements: Element[]) => {
|
||||
currentFocus.forEach((el) => el.classList.remove(ION_FOCUSED));
|
||||
elements.forEach((el) => el.classList.add(ION_FOCUSED));
|
||||
currentFocus = elements;
|
||||
};
|
||||
|
||||
// Do not set focus on pointer interactions
|
||||
const pointerDown = (ev: Event) => {
|
||||
const pointerEvent = ev as PointerEvent;
|
||||
lastPointerType = pointerEvent.pointerType;
|
||||
keyboardMode = false;
|
||||
setFocus([]);
|
||||
if (ev instanceof PointerEvent && ev.pointerType !== '') {
|
||||
hadPointerEvent = true;
|
||||
// Reset after the event loop so only the immediate focusin is suppressed
|
||||
setTimeout(() => { hadPointerEvent = false; }, 0);
|
||||
}
|
||||
};
|
||||
|
||||
// Clear hadPointerEvent so keyboard navigation shows focus
|
||||
// Also, clear focus if the key is not a navigation key
|
||||
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;
|
||||
hadPointerEvent = false;
|
||||
|
||||
// If it's not a focus key, clear focus immediately
|
||||
const keyboardEvent = ev as KeyboardEvent;
|
||||
if (!FOCUS_KEYS.includes(keyboardEvent.key)) {
|
||||
setFocus([]);
|
||||
}
|
||||
};
|
||||
|
||||
// Set focus if the last interaction was NOT a pointer event
|
||||
// This works around iOS/Safari bugs where keydown is not fired for Tab
|
||||
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);
|
||||
}
|
||||
if (target.classList.contains(ION_FOCUSABLE) && !hadPointerEvent) {
|
||||
const toFocus = ev.composedPath().filter((el): el is HTMLElement =>
|
||||
el instanceof HTMLElement && el.classList.contains(ION_FOCUSABLE)
|
||||
);
|
||||
setFocus(toFocus);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -80,16 +78,12 @@ export const startFocusVisible = (rootEl?: HTMLElement): FocusVisibleUtility =>
|
||||
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);
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user