11const  ION_FOCUSED  =  'ion-focused' ; 
22const  ION_FOCUSABLE  =  'ion-focusable' ; 
3+ const  FOCUS_KEYS  =  [ 
4+   'Tab' , 
5+   'ArrowDown' , 
6+   'Space' , 
7+   'Escape' , 
8+   ' ' , 
9+   'Shift' , 
10+   'Enter' , 
11+   'ArrowLeft' , 
12+   'ArrowRight' , 
13+   'ArrowUp' , 
14+   'Home' , 
15+   'End' , 
16+ ] ; 
317
418export  interface  FocusVisibleUtility  { 
519  destroy : ( )  =>  void ; 
@@ -8,6 +22,8 @@ export interface FocusVisibleUtility {
822
923export  const  startFocusVisible  =  ( rootEl ?: HTMLElement ) : FocusVisibleUtility  =>  { 
1024  let  currentFocus : Element [ ]  =  [ ] ; 
25+   let  keyboardMode  =  false ; 
26+   let  lastPointerType : string  |  null  =  null ; 
1127
1228  const  ref  =  rootEl  ? rootEl . shadowRoot !  : document ; 
1329  const  root  =  rootEl  ? rootEl  : document . body ; 
@@ -18,16 +34,40 @@ export const startFocusVisible = (rootEl?: HTMLElement): FocusVisibleUtility =>
1834    currentFocus  =  elements ; 
1935  } ; 
2036
21-   const  pointerDown  =  ( )  =>  { 
37+   const  pointerDown  =  ( ev : Event )  =>  { 
38+     const  pointerEvent  =  ev  as  PointerEvent ; 
39+     lastPointerType  =  pointerEvent . pointerType ; 
40+     keyboardMode  =  false ; 
2241    setFocus ( [ ] ) ; 
2342  } ; 
2443
44+   const  onKeydown  =  ( ev : Event )  =>  { 
45+     const  keyboardEvent  =  ev  as  KeyboardEvent ; 
46+     // Always set keyboard mode to true when any key is pressed 
47+     // This handles the WebKit Tab key bug where keydown might not fire 
48+     keyboardMode  =  true ; 
49+ 
50+     // If it's not a focus key, clear focus immediately 
51+     if  ( ! FOCUS_KEYS . includes ( keyboardEvent . key ) )  { 
52+       setFocus ( [ ] ) ; 
53+     } 
54+   } ; 
55+ 
2556  const  onFocusin  =  ( ev : Event )  =>  { 
26-     const  toFocus  =  ev . composedPath ( ) . filter ( ( el ) : el  is HTMLElement  =>  { 
27-       return  el  instanceof  HTMLElement  &&  el . classList . contains ( ION_FOCUSABLE ) ; 
28-     } ) ; 
57+     // Check if this focus event is likely from keyboard navigation 
58+     // We can detect this by checking if there was a recent keydown event 
59+     // or if the focus target is a focusable element that typically gets focus via keyboard 
60+     const  target  =  ev . target  as  HTMLElement ; 
2961
30-     setFocus ( toFocus ) ; 
62+     if  ( target . classList . contains ( ION_FOCUSABLE ) )  { 
63+       // If we're in keyboard mode or this looks like keyboard navigation 
64+       if  ( keyboardMode  ||  ! lastPointerType )  { 
65+         const  toFocus  =  ev . composedPath ( ) . filter ( ( el ) : el  is HTMLElement  =>  { 
66+           return  el  instanceof  HTMLElement  &&  el . classList . contains ( ION_FOCUSABLE ) ; 
67+         } ) ; 
68+         setFocus ( toFocus ) ; 
69+       } 
70+     } 
3171  } ; 
3272
3373  const  onFocusout  =  ( )  =>  { 
@@ -36,14 +76,18 @@ export const startFocusVisible = (rootEl?: HTMLElement): FocusVisibleUtility =>
3676    } 
3777  } ; 
3878
79+   ref . addEventListener ( 'keydown' ,  onKeydown ) ; 
3980  ref . addEventListener ( 'focusin' ,  onFocusin ) ; 
4081  ref . addEventListener ( 'focusout' ,  onFocusout ) ; 
82+   ref . addEventListener ( 'pointerdown' ,  pointerDown ,  {  passive : true  } ) ; 
4183  ref . addEventListener ( 'touchstart' ,  pointerDown ,  {  passive : true  } ) ; 
4284  ref . addEventListener ( 'mousedown' ,  pointerDown ) ; 
4385
4486  const  destroy  =  ( )  =>  { 
87+     ref . removeEventListener ( 'keydown' ,  onKeydown ) ; 
4588    ref . removeEventListener ( 'focusin' ,  onFocusin ) ; 
4689    ref . removeEventListener ( 'focusout' ,  onFocusout ) ; 
90+     ref . removeEventListener ( 'pointerdown' ,  pointerDown ) ; 
4791    ref . removeEventListener ( 'touchstart' ,  pointerDown ) ; 
4892    ref . removeEventListener ( 'mousedown' ,  pointerDown ) ; 
4993  } ; 
0 commit comments