|
12 | 12 |
|
13 | 13 | import {FocusableElement, RefObject} from '@react-types/shared';
|
14 | 14 | import {focusSafely} from './focusSafely';
|
15 |
| -import {getOwnerDocument, useLayoutEffect} from '@react-aria/utils'; |
| 15 | +import {getInteractionModality} from '@react-aria/interactions'; |
| 16 | +import {getOwnerDocument, isAndroid, isChrome, useLayoutEffect} from '@react-aria/utils'; |
16 | 17 | import {isElementVisible} from './isElementVisible';
|
17 | 18 | import React, {ReactNode, useContext, useEffect, useMemo, useRef} from 'react';
|
18 | 19 |
|
@@ -376,12 +377,19 @@ function useFocusContainment(scopeRef: RefObject<Element[] | null>, contain?: bo
|
376 | 377 | };
|
377 | 378 |
|
378 | 379 | let onBlur = (e) => {
|
379 |
| - // Use raf to delay long enough that subdialogs will fully unmount and not move focus away from parent menu's hovered item that triggered the close |
| 380 | + // Firefox doesn't shift focus back to the Dialog properly without this |
380 | 381 | if (raf.current) {
|
381 | 382 | cancelAnimationFrame(raf.current);
|
382 | 383 | }
|
383 | 384 | raf.current = requestAnimationFrame(() => {
|
384 |
| - if (e.relatedTarget && shouldContainFocus(scopeRef) && !isElementInChildScope(e.relatedTarget, scopeRef)) { |
| 385 | + // Patches infinite focus coersion loop for Android Talkback where the user isn't able to move the virtual cursor |
| 386 | + // if within a containing focus scope. Bug filed against Chrome: https://issuetracker.google.com/issues/384844019. |
| 387 | + // Note that this means focus can leave focus containing modals due to this, but it is isolated to Chrome Talkback. |
| 388 | + let modality = getInteractionModality(); |
| 389 | + let shouldSkipFocusRestore = (modality === 'virtual' || modality === null) && isAndroid() && isChrome(); |
| 390 | + |
| 391 | + // Use document.activeElement instead of e.relatedTarget so we can tell if user clicked into iframe |
| 392 | + if (!shouldSkipFocusRestore && ownerDocument.activeElement && shouldContainFocus(scopeRef) && !isElementInChildScope(ownerDocument.activeElement, scopeRef)) { |
385 | 393 | activeScope = scopeRef;
|
386 | 394 | if (ownerDocument.body.contains(e.target)) {
|
387 | 395 | focusedNode.current = e.target;
|
|
0 commit comments