10
10
import * as React from 'react' ;
11
11
import useEvent from './useEvent' ;
12
12
13
- const { useEffect, useRef} = React ;
13
+ const { useCallback , useEffect, useRef} = React ;
14
14
15
15
type UseFocusOptions = { |
16
16
disabled ? : boolean ,
@@ -126,7 +126,7 @@ function handleGlobalFocusVisibleEvent(
126
126
}
127
127
128
128
const passiveObject = { passive : true } ;
129
- const passiveCaptureObject = { capture : true , passive : false } ;
129
+ const passiveObjectWithPriority = { passive : true , priority : 0 } ;
130
130
131
131
function handleFocusVisibleTargetEvent (
132
132
type : string ,
@@ -243,8 +243,8 @@ export function useFocus(
243
243
) : void {
244
244
// Setup controlled state for this useFocus hook
245
245
const stateRef = useRef ( { isFocused : false , isFocusVisible : false } ) ;
246
- const focusHandle = useEvent ( 'focus ' , passiveCaptureObject ) ;
247
- const blurHandle = useEvent ( 'blur ' , passiveCaptureObject ) ;
246
+ const focusHandle = useEvent ( 'focusin ' , passiveObjectWithPriority ) ;
247
+ const blurHandle = useEvent ( 'focusout ' , passiveObjectWithPriority ) ;
248
248
const focusVisibleHandles = useFocusVisibleInputHandles ( ) ;
249
249
250
250
useEffect ( ( ) => {
@@ -317,7 +317,9 @@ export function useFocus(
317
317
}
318
318
319
319
export function useFocusWithin (
320
- focusWithinTargetRef : { current : null | Node } ,
320
+ focusWithinTargetRef :
321
+ | { current : null | Node }
322
+ | ( ( focusWithinTarget : null | Node ) => void ) ,
321
323
{
322
324
disabled ,
323
325
onAfterBlurWithin ,
@@ -327,114 +329,134 @@ export function useFocusWithin(
327
329
onFocusWithinChange ,
328
330
onFocusWithinVisibleChange ,
329
331
} : UseFocusWithinOptions ,
330
- ) {
332
+ ) : ( focusWithinTarget : null | Node ) => void {
331
333
// Setup controlled state for this useFocus hook
332
- const stateRef = useRef ( { isFocused : false , isFocusVisible : false } ) ;
333
- const focusHandle = useEvent ( 'focus' , passiveCaptureObject ) ;
334
- const blurHandle = useEvent ( 'blur' , passiveCaptureObject ) ;
334
+ const stateRef = useRef < null | { isFocused : boolean , isFocusVisible : boolean } > (
335
+ { isFocused : false , isFocusVisible : false } ,
336
+ ) ;
337
+ const focusHandle = useEvent ( 'focusin' , passiveObjectWithPriority ) ;
338
+ const blurHandle = useEvent ( 'focusout' , passiveObjectWithPriority ) ;
335
339
const afterBlurHandle = useEvent ( 'afterblur' , passiveObject ) ;
336
340
const beforeBlurHandle = useEvent ( 'beforeblur' , passiveObject ) ;
337
341
const focusVisibleHandles = useFocusVisibleInputHandles ( ) ;
338
342
339
- useEffect ( ( ) => {
340
- const focusWithinTarget = focusWithinTargetRef . current ;
341
- const state = stateRef . current ;
343
+ const useFocusWithinRef = useCallback (
344
+ ( focusWithinTarget : null | Node ) => {
345
+ // Handle the incoming focusTargetRef. It can be either a function ref
346
+ // or an object ref.
347
+ if ( typeof focusWithinTargetRef === 'function' ) {
348
+ focusWithinTargetRef ( focusWithinTarget ) ;
349
+ } else {
350
+ focusWithinTargetRef . current = focusWithinTarget ;
351
+ }
352
+ const state = stateRef . current ;
353
+
354
+ if ( focusWithinTarget !== null && state !== null ) {
355
+ // Handle focus visible
356
+ setFocusVisibleListeners (
357
+ focusVisibleHandles ,
358
+ focusWithinTarget ,
359
+ isFocusVisible => {
360
+ if ( state . isFocused && state . isFocusVisible !== isFocusVisible ) {
361
+ state . isFocusVisible = isFocusVisible ;
362
+ if ( onFocusWithinVisibleChange ) {
363
+ onFocusWithinVisibleChange ( isFocusVisible ) ;
364
+ }
365
+ }
366
+ } ,
367
+ ) ;
342
368
343
- if ( focusWithinTarget !== null && state !== null ) {
344
- // Handle focus visible
345
- setFocusVisibleListeners (
346
- focusVisibleHandles ,
347
- focusWithinTarget ,
348
- isFocusVisible => {
349
- if ( state . isFocused && state . isFocusVisible !== isFocusVisible ) {
350
- state . isFocusVisible = isFocusVisible ;
369
+ // Handle focus
370
+ focusHandle . setListener ( focusWithinTarget , event => {
371
+ if ( disabled ) {
372
+ return ;
373
+ }
374
+ if ( ! state . isFocused ) {
375
+ state . isFocused = true ;
376
+ state . isFocusVisible = isGlobalFocusVisible ;
377
+ if ( onFocusWithinChange ) {
378
+ onFocusWithinChange ( true ) ;
379
+ }
380
+ if ( state . isFocusVisible && onFocusWithinVisibleChange ) {
381
+ onFocusWithinVisibleChange ( true ) ;
382
+ }
383
+ }
384
+ if ( ! state . isFocusVisible && isGlobalFocusVisible ) {
385
+ state . isFocusVisible = isGlobalFocusVisible ;
351
386
if ( onFocusWithinVisibleChange ) {
352
- onFocusWithinVisibleChange ( isFocusVisible ) ;
387
+ onFocusWithinVisibleChange ( true ) ;
353
388
}
354
389
}
355
- } ,
356
- ) ;
357
-
358
- // Handle focus
359
- focusHandle . setListener ( focusWithinTarget , event => {
360
- if ( disabled ) {
361
- return ;
362
- }
363
- if ( ! state . isFocused ) {
364
- state . isFocused = true ;
365
- state . isFocusVisible = isGlobalFocusVisible ;
366
- if ( onFocusWithinChange ) {
367
- onFocusWithinChange ( true ) ;
390
+ if ( onFocusWithin ) {
391
+ onFocusWithin ( event ) ;
368
392
}
369
- if ( state . isFocusVisible && onFocusWithinVisibleChange ) {
370
- onFocusWithinVisibleChange ( true ) ;
393
+ } ) ;
394
+
395
+ // Handle blur
396
+ blurHandle . setListener ( focusWithinTarget , event => {
397
+ if ( disabled ) {
398
+ return ;
371
399
}
372
- }
373
- if ( ! state . isFocusVisible && isGlobalFocusVisible ) {
374
- state . isFocusVisible = isGlobalFocusVisible ;
375
- if ( onFocusWithinVisibleChange ) {
376
- onFocusWithinVisibleChange ( true ) ;
400
+ const { relatedTarget} = ( event . nativeEvent : any ) ;
401
+
402
+ if (
403
+ state . isFocused &&
404
+ // $FlowFixMe: focusWithinTarget is never null
405
+ ! isRelatedTargetWithin ( focusWithinTarget , relatedTarget )
406
+ ) {
407
+ state . isFocused = false ;
408
+ if ( onFocusWithinChange ) {
409
+ onFocusWithinChange ( false ) ;
410
+ }
411
+ if ( state . isFocusVisible && onFocusWithinVisibleChange ) {
412
+ onFocusWithinVisibleChange ( false ) ;
413
+ }
414
+ if ( onBlurWithin ) {
415
+ onBlurWithin ( event ) ;
416
+ }
377
417
}
378
- }
379
- if ( onFocusWithin ) {
380
- onFocusWithin ( event ) ;
381
- }
382
- isEmulatingMouseEvents = false ;
383
- } ) ;
418
+ } ) ;
384
419
385
- // Handle blur
386
- blurHandle . setListener ( focusWithinTarget , event => {
387
- if ( disabled ) {
388
- return ;
389
- }
390
- const { relatedTarget} = ( event : any ) ;
391
-
392
- if (
393
- state . isFocused &&
394
- ! isRelatedTargetWithin ( focusWithinTarget , relatedTarget )
395
- ) {
396
- state . isFocused = false ;
397
- if ( onFocusWithinChange ) {
398
- onFocusWithinChange ( false ) ;
420
+ // Handle before blur. This is a special
421
+ // React provided event.
422
+ beforeBlurHandle . setListener ( focusWithinTarget , event => {
423
+ if ( disabled ) {
424
+ return ;
399
425
}
400
- if ( state . isFocusVisible && onFocusWithinVisibleChange ) {
401
- onFocusWithinVisibleChange ( false ) ;
426
+ if ( onBeforeBlurWithin ) {
427
+ onBeforeBlurWithin ( event ) ;
428
+ // Add an "afterblur" listener on document. This is a special
429
+ // React provided event.
430
+ afterBlurHandle . setListener ( document , afterBlurEvent => {
431
+ if ( onAfterBlurWithin ) {
432
+ onAfterBlurWithin ( afterBlurEvent ) ;
433
+ }
434
+ // Clear listener on document
435
+ afterBlurHandle . setListener ( document , null ) ;
436
+ } ) ;
402
437
}
403
- if ( onBlurWithin ) {
404
- onBlurWithin ( event ) ;
405
- }
406
- }
407
- isEmulatingMouseEvents = false ;
408
- } ) ;
409
-
410
- // Handle before blur. This is a special
411
- // React provided event.
412
- beforeBlurHandle . setListener ( focusWithinTarget , event => {
413
- if ( disabled ) {
414
- return ;
415
- }
416
- if ( onBeforeBlurWithin ) {
417
- onBeforeBlurWithin ( event ) ;
418
- // Add an "afterblur" listener on document. This is a special
419
- // React provided event.
420
- afterBlurHandle . setListener ( document , afterBlurEvent => {
421
- if ( onAfterBlurWithin ) {
422
- onAfterBlurWithin ( afterBlurEvent ) ;
423
- }
424
- // Clear listener on document
425
- afterBlurHandle . setListener ( document , null ) ;
426
- } ) ;
427
- }
428
- } ) ;
429
- }
430
- } , [
431
- disabled ,
432
- onBlurWithin ,
433
- onFocusWithin ,
434
- onFocusWithinChange ,
435
- onFocusWithinVisibleChange ,
436
- ] ) ;
438
+ } ) ;
439
+ }
440
+ } ,
441
+ [
442
+ afterBlurHandle ,
443
+ beforeBlurHandle ,
444
+ blurHandle ,
445
+ disabled ,
446
+ focusHandle ,
447
+ focusVisibleHandles ,
448
+ focusWithinTargetRef ,
449
+ onAfterBlurWithin ,
450
+ onBeforeBlurWithin ,
451
+ onBlurWithin ,
452
+ onFocusWithin ,
453
+ onFocusWithinChange ,
454
+ onFocusWithinVisibleChange ,
455
+ ] ,
456
+ ) ;
437
457
438
458
// Mount/Unmount logic
439
- useFocusLifecycles ( stateRef ) ;
459
+ useFocusLifecycles ( ) ;
460
+
461
+ return useFocusWithinRef ;
440
462
}
0 commit comments