@@ -84,6 +84,8 @@ const eventListeners:
8484 ($Shape< PartialEventObject > ) => void ,
8585 > = new PossiblyWeakMap ( ) ;
8686
87+ let alreadyDispatching = false ;
88+
8789let currentTimers = new Map ( ) ;
8890let currentOwner = null ;
8991let currentInstance : null | ReactEventComponentInstance = null ;
@@ -322,61 +324,65 @@ const eventResponderContext: ReactResponderContext = {
322324 }
323325 }
324326 } ,
325- getEventTargetsFromTarget (
326- target : Element | Document ,
327- queryType ? : Symbol | number ,
328- queryKey ? : string ,
329- ) : Array < {
330- node : Element ,
331- props : null | Object ,
332- } > {
333- validateResponderContext ( ) ;
334- const eventTargetHostComponents = [ ] ;
335- let node = getClosestInstanceFromNode ( target ) ;
336- // We traverse up the fiber tree from the target fiber, to the
337- // current event component fiber. Along the way, we check if
338- // the fiber has any children that are event targets. If there
339- // are, we query them (optionally) to ensure they match the
340- // specified type and key. We then push the event target props
341- // along with the associated parent host component of that event
342- // target.
327+ getFocusableElementsInScope ( ) : Array < HTMLElement > {
328+ const focusableElements = [ ] ;
329+ const eventComponentInstance = ( ( currentInstance : any ) : ReactEventComponentInstance ) ;
330+ let node = ( ( eventComponentInstance . currentFiber : any ) : Fiber ) . child ;
331+
343332 while ( node !== null ) {
344333 if ( node . stateNode === currentInstance ) {
345334 break ;
346335 }
347- let child = node . child ;
348-
349- while ( child !== null ) {
350- if (
351- child . tag === EventTargetWorkTag &&
352- queryEventTarget ( child , queryType , queryKey )
353- ) {
354- const props = child . stateNode . props ;
355- let parent = child . return ;
356-
357- if ( parent !== null ) {
358- if ( parent . stateNode === currentInstance ) {
359- break ;
360- }
361- if ( parent . tag === HostComponent ) {
362- eventTargetHostComponents . push ( {
363- node : parent . stateNode ,
364- props,
365- } ) ;
366- break ;
367- }
368- parent = parent . return ;
369- }
370- break ;
336+ if ( isFiberHostComponentFocusable ( node ) ) {
337+ focusableElements . push ( node . stateNode ) ;
338+ } else {
339+ const child = node . child ;
340+
341+ if ( child !== null ) {
342+ node = child ;
343+ continue ;
371344 }
372- child = child . sibling ;
373345 }
374- node = node . return ;
346+ const sibling = node . sibling ;
347+
348+ if ( sibling !== null ) {
349+ node = sibling ;
350+ continue ;
351+ }
352+ const parent = node . return ;
353+ if ( parent === null ) {
354+ break ;
355+ }
356+ node = parent . sibling ;
375357 }
376- return eventTargetHostComponents ;
358+
359+ return focusableElements ;
377360 } ,
378361} ;
379362
363+ function isFiberHostComponentFocusable ( fiber : Fiber ) : boolean {
364+ if ( fiber . tag !== HostComponent ) {
365+ return false ;
366+ }
367+ const { type, memoizedProps} = fiber ;
368+ if ( memoizedProps . tabIndex === - 1 || memoizedProps . disabled ) {
369+ return false ;
370+ }
371+ if ( memoizedProps . tabIndex === 0 ) {
372+ return true ;
373+ }
374+ if ( type === 'a' || type === 'area' ) {
375+ return ! ! memoizedProps . href ;
376+ }
377+ return (
378+ type === 'button' ||
379+ type === 'textarea' ||
380+ type === 'input' ||
381+ type === 'object' ||
382+ type === 'select'
383+ ) ;
384+ }
385+
380386function processTimers ( timers : Map < Symbol , ResponderTimer > ) : void {
381387 const timersArr = Array . from ( timers . values ( ) ) ;
382388 currentEventQueue = createEventQueue ( ) ;
@@ -398,20 +404,6 @@ function processTimers(timers: Map<Symbol, ResponderTimer>): void {
398404 }
399405}
400406
401- function queryEventTarget (
402- child : Fiber ,
403- queryType : void | Symbol | number ,
404- queryKey : void | string ,
405- ) : boolean {
406- if ( queryType !== undefined && child . type . type !== queryType ) {
407- return false ;
408- }
409- if ( queryKey !== undefined && child . key !== queryKey ) {
410- return false ;
411- }
412- return true ;
413- }
414-
415407function createResponderEvent (
416408 topLevelType : string ,
417409 nativeEvent : AnyNativeEvent ,
@@ -467,7 +459,7 @@ export function processEventQueue(): void {
467459 }
468460}
469461
470- function getTargetEventTypes (
462+ function getTargetEventTypesSet (
471463 eventTypes : Array < ReactEventResponderEventType > ,
472464) : Set < DOMTopLevelEventType > {
473465 let cachedSet = targetEventTypeCached . get ( eventTypes ) ;
@@ -497,12 +489,13 @@ function getTargetEventResponderInstances(
497489 const eventComponentInstance = node . stateNode ;
498490 if ( currentOwner === null || currentOwner === eventComponentInstance ) {
499491 const responder = eventComponentInstance . responder ;
492+ const targetEventTypes = responder . targetEventTypes ;
500493 // Validate the target event type exists on the responder
501- const targetEventTypes = getTargetEventTypes (
502- responder . targetEventTypes ,
503- ) ;
504- if ( targetEventTypes . has ( topLevelType ) ) {
505- eventResponderInstances . push ( eventComponentInstance ) ;
494+ if ( targetEventTypes !== undefined ) {
495+ const targetEventTypesSet = getTargetEventTypesSet ( targetEventTypes ) ;
496+ if ( targetEventTypesSet . has ( topLevelType ) ) {
497+ eventResponderInstances . push ( eventComponentInstance ) ;
498+ }
506499 }
507500 }
508501 }
@@ -716,6 +709,10 @@ export function dispatchEventForResponderEventSystem(
716709 eventSystemFlags : EventSystemFlags ,
717710) : void {
718711 if ( enableEventAPI ) {
712+ if ( alreadyDispatching ) {
713+ return ;
714+ }
715+ alreadyDispatching = true ;
719716 currentEventQueue = createEventQueue ( ) ;
720717 try {
721718 traverseAndHandleEventResponderInstances (
@@ -730,6 +727,7 @@ export function dispatchEventForResponderEventSystem(
730727 currentTimers = null ;
731728 currentInstance = null ;
732729 currentEventQueue = null ;
730+ alreadyDispatching = false ;
733731 }
734732 }
735733}
0 commit comments