(props: FocusScopeProps)
| 86 | * to user events. |
| 87 | */ |
| 88 | export function FocusScope(props: FocusScopeProps): JSX.Element { |
| 89 | let {children, contain, restoreFocus, autoFocus} = props; |
| 90 | let startRef = useRef<HTMLSpanElement>(null); |
| 91 | let endRef = useRef<HTMLSpanElement>(null); |
| 92 | let scopeRef = useRef<Element[]>([]); |
| 93 | let {parentNode} = useContext(FocusContext) || {}; |
| 94 | |
| 95 | // Create a tree node here so we can add children to it even before it is added to the tree. |
| 96 | let node = useMemo(() => new TreeNode({scopeRef}), [scopeRef]); |
| 97 | |
| 98 | useLayoutEffect(() => { |
| 99 | // If a new scope mounts outside the active scope, (e.g. DialogContainer launched from a menu), |
| 100 | // use the active scope as the parent instead of the parent from context. Layout effects run bottom |
| 101 | // up, so if the parent is not yet added to the tree, don't do this. Only the outer-most FocusScope |
| 102 | // that is being added should get the activeScope as its parent. |
| 103 | let parent = parentNode || focusScopeTree.root; |
| 104 | if ( |
| 105 | focusScopeTree.getTreeNode(parent.scopeRef) && |
| 106 | activeScope && |
| 107 | !isAncestorScope(activeScope, parent.scopeRef) |
| 108 | ) { |
| 109 | let activeNode = focusScopeTree.getTreeNode(activeScope); |
| 110 | if (activeNode) { |
| 111 | parent = activeNode; |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | // Add the node to the parent, and to the tree. |
| 116 | parent.addChild(node); |
| 117 | focusScopeTree.addNode(node); |
| 118 | }, [node, parentNode]); |
| 119 | |
| 120 | useLayoutEffect(() => { |
| 121 | let node = focusScopeTree.getTreeNode(scopeRef); |
| 122 | if (node) { |
| 123 | node.contain = !!contain; |
| 124 | } |
| 125 | }, [contain]); |
| 126 | |
| 127 | useLayoutEffect(() => { |
| 128 | // Find all rendered nodes between the sentinels and add them to the scope. |
| 129 | let node = startRef.current?.nextSibling!; |
| 130 | let nodes: Element[] = []; |
| 131 | let stopPropagation = e => e.stopPropagation(); |
| 132 | while (node && node !== endRef.current) { |
| 133 | nodes.push(node as Element); |
| 134 | // Stop custom restore focus event from propagating to parent focus scopes. |
| 135 | node.addEventListener(RESTORE_FOCUS_EVENT, stopPropagation); |
| 136 | node = node.nextSibling as Element; |
| 137 | } |
| 138 | |
| 139 | scopeRef.current = nodes; |
| 140 | |
| 141 | return () => { |
| 142 | for (let node of nodes) { |
| 143 | node.removeEventListener(RESTORE_FOCUS_EVENT, stopPropagation); |
| 144 | } |
| 145 | }; |
nothing calls this directly
no test coverage detected