MCPcopy Create free account
hub / github.com/adobe/react-spectrum / useRestoreFocus

Function useRestoreFocus

packages/react-aria/src/focus/FocusScope.tsx:625–822  ·  view source on GitHub ↗
(
  scopeRef: RefObject<Element[] | null>,
  restoreFocus?: boolean,
  contain?: boolean
)

Source from the content-addressed store, hash-verified

623}
624
625function useRestoreFocus(
626 scopeRef: RefObject<Element[] | null>,
627 restoreFocus?: boolean,
628 contain?: boolean
629) {
630 // create a ref during render instead of useLayoutEffect so the active element is saved before a child with autoFocus=true mounts.
631
632 const nodeToRestoreRef = useRef(
633 typeof document !== 'undefined'
634 ? (getActiveElement(
635 getOwnerDocument(scopeRef.current ? scopeRef.current[0] : undefined)
636 ) as FocusableElement)
637 : null
638 );
639
640 // restoring scopes should all track if they are active regardless of contain, but contain already tracks it plus logic to contain the focus
641 // restoring-non-containing scopes should only care if they become active so they can perform the restore
642 useLayoutEffect(() => {
643 let scope = scopeRef.current;
644 const ownerDocument = getOwnerDocument(scope ? scope[0] : undefined);
645 if (!restoreFocus || contain) {
646 return;
647 }
648
649 let onFocus = () => {
650 // If focusing an element in a child scope of the currently active scope, the child becomes active.
651 // Moving out of the active scope to an ancestor is not allowed.
652 if (
653 (!activeScope || isAncestorScope(activeScope, scopeRef)) &&
654 isElementInScope(getActiveElement(ownerDocument), scopeRef.current)
655 ) {
656 activeScope = scopeRef;
657 }
658 };
659
660 ownerDocument.addEventListener('focusin', onFocus, false);
661 scope?.forEach(element => element.addEventListener('focusin', onFocus, false));
662 return () => {
663 ownerDocument.removeEventListener('focusin', onFocus, false);
664 scope?.forEach(element => element.removeEventListener('focusin', onFocus, false));
665 };
666 // eslint-disable-next-line react-hooks/exhaustive-deps
667 }, [scopeRef, contain]);
668
669 useLayoutEffect(() => {
670 const ownerDocument = getOwnerDocument(scopeRef.current ? scopeRef.current[0] : undefined);
671
672 if (!restoreFocus) {
673 return;
674 }
675
676 // Handle the Tab key so that tabbing out of the scope goes to the next element
677 // after the node that had focus when the scope mounted. This is important when
678 // using portals for overlays, so that focus goes to the expected element when
679 // tabbing out of the overlay.
680 let onKeyDown = (e: KeyboardEvent) => {
681 if (
682 e.key !== 'Tab' ||

Callers 1

FocusScopeFunction · 0.85

Calls 10

getActiveElementFunction · 0.90
getOwnerDocumentFunction · 0.90
isElementInChildScopeFunction · 0.85
shouldRestoreFocusFunction · 0.85
restoreFocusToElementFunction · 0.85
getFirstInScopeFunction · 0.85
addEventListenerMethod · 0.80
removeEventListenerMethod · 0.80
getTreeNodeMethod · 0.80
cloneMethod · 0.65

Tested by

no test coverage detected