@react-aria/focus 3.19.1 → 3.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,38 @@
1
+ import {getOwnerDocument as $hpDQO$getOwnerDocument, getActiveElement as $hpDQO$getActiveElement} from "@react-aria/utils";
2
+
3
+
4
+ function $55f9b1ae81f22853$export$76e4e37e5339496d(to) {
5
+ let from = $55f9b1ae81f22853$export$759df0d867455a91((0, $hpDQO$getOwnerDocument)(to));
6
+ if (from !== to) {
7
+ if (from) $55f9b1ae81f22853$export$6c5dc7e81d2cc29a(from, to);
8
+ if (to) $55f9b1ae81f22853$export$2b35b76d2e30e129(to, from);
9
+ }
10
+ }
11
+ function $55f9b1ae81f22853$export$6c5dc7e81d2cc29a(from, to) {
12
+ from.dispatchEvent(new FocusEvent('blur', {
13
+ relatedTarget: to
14
+ }));
15
+ from.dispatchEvent(new FocusEvent('focusout', {
16
+ bubbles: true,
17
+ relatedTarget: to
18
+ }));
19
+ }
20
+ function $55f9b1ae81f22853$export$2b35b76d2e30e129(to, from) {
21
+ to.dispatchEvent(new FocusEvent('focus', {
22
+ relatedTarget: from
23
+ }));
24
+ to.dispatchEvent(new FocusEvent('focusin', {
25
+ bubbles: true,
26
+ relatedTarget: from
27
+ }));
28
+ }
29
+ function $55f9b1ae81f22853$export$759df0d867455a91(document) {
30
+ let activeElement = (0, $hpDQO$getActiveElement)(document);
31
+ let activeDescendant = activeElement === null || activeElement === void 0 ? void 0 : activeElement.getAttribute('aria-activedescendant');
32
+ if (activeDescendant) return document.getElementById(activeDescendant) || activeElement;
33
+ return activeElement;
34
+ }
35
+
36
+
37
+ export {$55f9b1ae81f22853$export$76e4e37e5339496d as moveVirtualFocus, $55f9b1ae81f22853$export$759df0d867455a91 as getVirtuallyFocusedElement, $55f9b1ae81f22853$export$6c5dc7e81d2cc29a as dispatchVirtualBlur, $55f9b1ae81f22853$export$2b35b76d2e30e129 as dispatchVirtualFocus};
38
+ //# sourceMappingURL=virtualFocus.module.js.map
@@ -0,0 +1 @@
1
+ {"mappings":";;;AAEO,SAAS,0CAAiB,EAAkB;IACjD,IAAI,OAAO,0CAA2B,CAAA,GAAA,uBAAe,EAAE;IACvD,IAAI,SAAS,IAAI;QACf,IAAI,MACF,0CAAoB,MAAM;QAE5B,IAAI,IACF,0CAAqB,IAAI;IAE7B;AACF;AAEO,SAAS,0CAAoB,IAAa,EAAE,EAAkB;IACnE,KAAK,aAAa,CAAC,IAAI,WAAW,QAAQ;QAAC,eAAe;IAAE;IAC5D,KAAK,aAAa,CAAC,IAAI,WAAW,YAAY;QAAC,SAAS;QAAM,eAAe;IAAE;AACjF;AAEO,SAAS,0CAAqB,EAAW,EAAE,IAAoB;IACpE,GAAG,aAAa,CAAC,IAAI,WAAW,SAAS;QAAC,eAAe;IAAI;IAC7D,GAAG,aAAa,CAAC,IAAI,WAAW,WAAW;QAAC,SAAS;QAAM,eAAe;IAAI;AAChF;AAEO,SAAS,0CAA2B,QAAkB;IAC3D,IAAI,gBAAgB,CAAA,GAAA,uBAAe,EAAE;IACrC,IAAI,mBAAmB,0BAAA,oCAAA,cAAe,YAAY,CAAC;IACnD,IAAI,kBACF,OAAO,SAAS,cAAc,CAAC,qBAAqB;IAGtD,OAAO;AACT","sources":["packages/@react-aria/focus/src/virtualFocus.ts"],"sourcesContent":["import {getActiveElement, getOwnerDocument} from '@react-aria/utils';\n\nexport function moveVirtualFocus(to: Element | null) {\n let from = getVirtuallyFocusedElement(getOwnerDocument(to));\n if (from !== to) {\n if (from) {\n dispatchVirtualBlur(from, to);\n }\n if (to) {\n dispatchVirtualFocus(to, from);\n }\n }\n}\n\nexport function dispatchVirtualBlur(from: Element, to: Element | null) {\n from.dispatchEvent(new FocusEvent('blur', {relatedTarget: to}));\n from.dispatchEvent(new FocusEvent('focusout', {bubbles: true, relatedTarget: to}));\n}\n\nexport function dispatchVirtualFocus(to: Element, from: Element | null) {\n to.dispatchEvent(new FocusEvent('focus', {relatedTarget: from}));\n to.dispatchEvent(new FocusEvent('focusin', {bubbles: true, relatedTarget: from}));\n}\n\nexport function getVirtuallyFocusedElement(document: Document) {\n let activeElement = getActiveElement(document);\n let activeDescendant = activeElement?.getAttribute('aria-activedescendant');\n if (activeDescendant) {\n return document.getElementById(activeDescendant) || activeElement;\n }\n\n return activeElement;\n}\n"],"names":[],"version":3,"file":"virtualFocus.module.js.map"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-aria/focus",
3
- "version": "3.19.1",
3
+ "version": "3.20.0",
4
4
  "description": "Spectrum UI components in React",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/main.js",
@@ -22,9 +22,9 @@
22
22
  "url": "https://github.com/adobe/react-spectrum"
23
23
  },
24
24
  "dependencies": {
25
- "@react-aria/interactions": "^3.23.0",
26
- "@react-aria/utils": "^3.27.0",
27
- "@react-types/shared": "^3.27.0",
25
+ "@react-aria/interactions": "^3.24.0",
26
+ "@react-aria/utils": "^3.28.0",
27
+ "@react-types/shared": "^3.28.0",
28
28
  "@swc/helpers": "^0.5.0",
29
29
  "clsx": "^2.0.0"
30
30
  },
@@ -35,5 +35,5 @@
35
35
  "publishConfig": {
36
36
  "access": "public"
37
37
  },
38
- "gitHead": "09e7f44bebdc9d89122926b2b439a0a38a2814ea"
38
+ "gitHead": "4d3c72c94eea2d72eb3a0e7d56000c6ef7e39726"
39
39
  }
@@ -10,10 +10,20 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
 
13
+ import {
14
+ createShadowTreeWalker,
15
+ getActiveElement,
16
+ getEventTarget,
17
+ getOwnerDocument,
18
+ isAndroid,
19
+ isChrome,
20
+ isFocusable,
21
+ isTabbable,
22
+ ShadowTreeWalker,
23
+ useLayoutEffect
24
+ } from '@react-aria/utils';
13
25
  import {FocusableElement, RefObject} from '@react-types/shared';
14
- import {focusSafely} from './focusSafely';
15
- import {getInteractionModality} from '@react-aria/interactions';
16
- import {getOwnerDocument, isAndroid, isChrome, useLayoutEffect} from '@react-aria/utils';
26
+ import {focusSafely, getInteractionModality} from '@react-aria/interactions';
17
27
  import {isElementVisible} from './isElementVisible';
18
28
  import React, {ReactNode, useContext, useEffect, useMemo, useRef} from 'react';
19
29
 
@@ -55,7 +65,7 @@ export interface FocusManager {
55
65
  focusPrevious(opts?: FocusManagerOptions): FocusableElement | null,
56
66
  /** Moves focus to the first focusable or tabbable element in the focus scope. */
57
67
  focusFirst(opts?: FocusManagerOptions): FocusableElement | null,
58
- /** Moves focus to the last focusable or tabbable element in the focus scope. */
68
+ /** Moves focus to the last focusable or tabbable element in the focus scope. */
59
69
  focusLast(opts?: FocusManagerOptions): FocusableElement | null
60
70
  }
61
71
 
@@ -144,7 +154,7 @@ export function FocusScope(props: FocusScopeProps) {
144
154
  // This needs to be an effect so that activeScope is updated after the FocusScope tree is complete.
145
155
  // It cannot be a useLayoutEffect because the parent of this node hasn't been attached in the tree yet.
146
156
  useEffect(() => {
147
- const activeElement = getOwnerDocument(scopeRef.current ? scopeRef.current[0] : undefined).activeElement;
157
+ const activeElement = getActiveElement(getOwnerDocument(scopeRef.current ? scopeRef.current[0] : undefined));
148
158
  let scope: TreeNode | null = null;
149
159
 
150
160
  if (isElementInScope(activeElement, scopeRef.current)) {
@@ -208,7 +218,7 @@ function createFocusManagerForScope(scopeRef: React.RefObject<Element[] | null>)
208
218
  focusNext(opts: FocusManagerOptions = {}) {
209
219
  let scope = scopeRef.current!;
210
220
  let {from, tabbable, wrap, accept} = opts;
211
- let node = from || getOwnerDocument(scope[0]).activeElement!;
221
+ let node = from || getActiveElement(getOwnerDocument(scope[0] ?? undefined))!;
212
222
  let sentinel = scope[0].previousElementSibling!;
213
223
  let scopeRoot = getScopeRoot(scope);
214
224
  let walker = getFocusableTreeWalker(scopeRoot, {tabbable, accept}, scope);
@@ -226,11 +236,11 @@ function createFocusManagerForScope(scopeRef: React.RefObject<Element[] | null>)
226
236
  focusPrevious(opts: FocusManagerOptions = {}) {
227
237
  let scope = scopeRef.current!;
228
238
  let {from, tabbable, wrap, accept} = opts;
229
- let node = from || getOwnerDocument(scope[0]).activeElement!;
239
+ let node = from || getActiveElement(getOwnerDocument(scope[0] ?? undefined))!;
230
240
  let sentinel = scope[scope.length - 1].nextElementSibling!;
231
241
  let scopeRoot = getScopeRoot(scope);
232
242
  let walker = getFocusableTreeWalker(scopeRoot, {tabbable, accept}, scope);
233
- walker.currentNode = isElementInScope(node, scope) ? node : sentinel;
243
+ walker.currentNode = isElementInScope(node, scope) ? node : sentinel;
234
244
  let previousNode = walker.previousNode() as FocusableElement;
235
245
  if (!previousNode && wrap) {
236
246
  walker.currentNode = sentinel;
@@ -268,31 +278,6 @@ function createFocusManagerForScope(scopeRef: React.RefObject<Element[] | null>)
268
278
  };
269
279
  }
270
280
 
271
- const focusableElements = [
272
- 'input:not([disabled]):not([type=hidden])',
273
- 'select:not([disabled])',
274
- 'textarea:not([disabled])',
275
- 'button:not([disabled])',
276
- 'a[href]',
277
- 'area[href]',
278
- 'summary',
279
- 'iframe',
280
- 'object',
281
- 'embed',
282
- 'audio[controls]',
283
- 'video[controls]',
284
- '[contenteditable]:not([contenteditable^="false"])'
285
- ];
286
-
287
- const FOCUSABLE_ELEMENT_SELECTOR = focusableElements.join(':not([hidden]),') + ',[tabindex]:not([disabled]):not([hidden])';
288
-
289
- focusableElements.push('[tabindex]:not([tabindex="-1"]):not([disabled])');
290
- const TABBABLE_ELEMENT_SELECTOR = focusableElements.join(':not([hidden]):not([tabindex="-1"]),');
291
-
292
- export function isFocusable(element: HTMLElement) {
293
- return element.matches(FOCUSABLE_ELEMENT_SELECTOR);
294
- }
295
-
296
281
  function getScopeRoot(scope: Element[]) {
297
282
  return scope[0].parentElement!;
298
283
  }
@@ -333,7 +318,7 @@ function useFocusContainment(scopeRef: RefObject<Element[] | null>, contain?: bo
333
318
  return;
334
319
  }
335
320
 
336
- let focusedElement = ownerDocument.activeElement;
321
+ let focusedElement = getActiveElement(ownerDocument);
337
322
  let scope = scopeRef.current;
338
323
  if (!scope || !isElementInScope(focusedElement, scope)) {
339
324
  return;
@@ -357,13 +342,13 @@ function useFocusContainment(scopeRef: RefObject<Element[] | null>, contain?: bo
357
342
  }
358
343
  };
359
344
 
360
- let onFocus = (e) => {
345
+ let onFocus: EventListener = (e) => {
361
346
  // If focusing an element in a child scope of the currently active scope, the child becomes active.
362
347
  // Moving out of the active scope to an ancestor is not allowed.
363
- if ((!activeScope || isAncestorScope(activeScope, scopeRef)) && isElementInScope(e.target, scopeRef.current)) {
348
+ if ((!activeScope || isAncestorScope(activeScope, scopeRef)) && isElementInScope(getEventTarget(e) as Element, scopeRef.current)) {
364
349
  activeScope = scopeRef;
365
- focusedNode.current = e.target;
366
- } else if (shouldContainFocus(scopeRef) && !isElementInChildScope(e.target, scopeRef)) {
350
+ focusedNode.current = getEventTarget(e) as FocusableElement;
351
+ } else if (shouldContainFocus(scopeRef) && !isElementInChildScope(getEventTarget(e) as Element, scopeRef)) {
367
352
  // If a focus event occurs outside the active scope (e.g. user tabs from browser location bar),
368
353
  // restore focus to the previously focused node or the first tabbable element in the active scope.
369
354
  if (focusedNode.current) {
@@ -372,11 +357,11 @@ function useFocusContainment(scopeRef: RefObject<Element[] | null>, contain?: bo
372
357
  focusFirstInScope(activeScope.current);
373
358
  }
374
359
  } else if (shouldContainFocus(scopeRef)) {
375
- focusedNode.current = e.target;
360
+ focusedNode.current = getEventTarget(e) as FocusableElement;
376
361
  }
377
362
  };
378
363
 
379
- let onBlur = (e) => {
364
+ let onBlur: EventListener = (e) => {
380
365
  // Firefox doesn't shift focus back to the Dialog properly without this
381
366
  if (raf.current) {
382
367
  cancelAnimationFrame(raf.current);
@@ -389,10 +374,12 @@ function useFocusContainment(scopeRef: RefObject<Element[] | null>, contain?: bo
389
374
  let shouldSkipFocusRestore = (modality === 'virtual' || modality === null) && isAndroid() && isChrome();
390
375
 
391
376
  // 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)) {
377
+ let activeElement = getActiveElement(ownerDocument);
378
+ if (!shouldSkipFocusRestore && activeElement && shouldContainFocus(scopeRef) && !isElementInChildScope(activeElement, scopeRef)) {
393
379
  activeScope = scopeRef;
394
- if (ownerDocument.body.contains(e.target)) {
395
- focusedNode.current = e.target;
380
+ let target = getEventTarget(e) as FocusableElement;
381
+ if (target && target.isConnected) {
382
+ focusedNode.current = target;
396
383
  focusedNode.current?.focus();
397
384
  } else if (activeScope.current) {
398
385
  focusFirstInScope(activeScope.current);
@@ -515,7 +502,7 @@ function useAutoFocus(scopeRef: RefObject<Element[] | null>, autoFocus?: boolean
515
502
  if (autoFocusRef.current) {
516
503
  activeScope = scopeRef;
517
504
  const ownerDocument = getOwnerDocument(scopeRef.current ? scopeRef.current[0] : undefined);
518
- if (!isElementInScope(ownerDocument.activeElement, activeScope.current) && scopeRef.current) {
505
+ if (!isElementInScope(getActiveElement(ownerDocument), activeScope.current) && scopeRef.current) {
519
506
  focusFirstInScope(scopeRef.current);
520
507
  }
521
508
  }
@@ -535,7 +522,7 @@ function useActiveScopeTracker(scopeRef: RefObject<Element[] | null>, restore?:
535
522
  const ownerDocument = getOwnerDocument(scope ? scope[0] : undefined);
536
523
 
537
524
  let onFocus = (e) => {
538
- let target = e.target as Element;
525
+ let target = getEventTarget(e) as Element;
539
526
  if (isElementInScope(target, scopeRef.current)) {
540
527
  activeScope = scopeRef;
541
528
  } else if (!isElementInAnyScope(target)) {
@@ -568,7 +555,7 @@ function shouldRestoreFocus(scopeRef: ScopeRef) {
568
555
  function useRestoreFocus(scopeRef: RefObject<Element[] | null>, restoreFocus?: boolean, contain?: boolean) {
569
556
  // create a ref during render instead of useLayoutEffect so the active element is saved before a child with autoFocus=true mounts.
570
557
  // eslint-disable-next-line no-restricted-globals
571
- const nodeToRestoreRef = useRef(typeof document !== 'undefined' ? getOwnerDocument(scopeRef.current ? scopeRef.current[0] : undefined).activeElement as FocusableElement : null);
558
+ const nodeToRestoreRef = useRef(typeof document !== 'undefined' ? getActiveElement(getOwnerDocument(scopeRef.current ? scopeRef.current[0] : undefined)) as FocusableElement : null);
572
559
 
573
560
  // restoring scopes should all track if they are active regardless of contain, but contain already tracks it plus logic to contain the focus
574
561
  // restoring-non-containing scopes should only care if they become active so they can perform the restore
@@ -583,7 +570,7 @@ function useRestoreFocus(scopeRef: RefObject<Element[] | null>, restoreFocus?: b
583
570
  // If focusing an element in a child scope of the currently active scope, the child becomes active.
584
571
  // Moving out of the active scope to an ancestor is not allowed.
585
572
  if ((!activeScope || isAncestorScope(activeScope, scopeRef)) &&
586
- isElementInScope(ownerDocument.activeElement, scopeRef.current)
573
+ isElementInScope(getActiveElement(ownerDocument), scopeRef.current)
587
574
  ) {
588
575
  activeScope = scopeRef;
589
576
  }
@@ -595,7 +582,7 @@ function useRestoreFocus(scopeRef: RefObject<Element[] | null>, restoreFocus?: b
595
582
  ownerDocument.removeEventListener('focusin', onFocus, false);
596
583
  scope?.forEach(element => element.removeEventListener('focusin', onFocus, false));
597
584
  };
598
- // eslint-disable-next-line react-hooks/exhaustive-deps
585
+ // eslint-disable-next-line react-hooks/exhaustive-deps
599
586
  }, [scopeRef, contain]);
600
587
 
601
588
  useLayoutEffect(() => {
@@ -631,7 +618,7 @@ function useRestoreFocus(scopeRef: RefObject<Element[] | null>, restoreFocus?: b
631
618
  walker.currentNode = focusedElement;
632
619
  let nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as FocusableElement;
633
620
 
634
- if (!nodeToRestore || !ownerDocument.body.contains(nodeToRestore) || nodeToRestore === ownerDocument.body) {
621
+ if (!nodeToRestore || !nodeToRestore.isConnected || nodeToRestore === ownerDocument.body) {
635
622
  nodeToRestore = undefined;
636
623
  treeNode.nodeToRestore = undefined;
637
624
  }
@@ -651,9 +638,9 @@ function useRestoreFocus(scopeRef: RefObject<Element[] | null>, restoreFocus?: b
651
638
  if (nextElement) {
652
639
  focusElement(nextElement, true);
653
640
  } else {
654
- // If there is no next element and the nodeToRestore isn't within a FocusScope (i.e. we are leaving the top level focus scope)
655
- // then move focus to the body.
656
- // Otherwise restore focus to the nodeToRestore (e.g menu within a popover -> tabbing to close the menu should move focus to menu trigger)
641
+ // If there is no next element and the nodeToRestore isn't within a FocusScope (i.e. we are leaving the top level focus scope)
642
+ // then move focus to the body.
643
+ // Otherwise restore focus to the nodeToRestore (e.g menu within a popover -> tabbing to close the menu should move focus to menu trigger)
657
644
  if (!isElementInAnyScope(nodeToRestore)) {
658
645
  focusedElement.blur();
659
646
  } else {
@@ -664,12 +651,12 @@ function useRestoreFocus(scopeRef: RefObject<Element[] | null>, restoreFocus?: b
664
651
  };
665
652
 
666
653
  if (!contain) {
667
- ownerDocument.addEventListener('keydown', onKeyDown, true);
654
+ ownerDocument.addEventListener('keydown', onKeyDown as EventListener, true);
668
655
  }
669
656
 
670
657
  return () => {
671
658
  if (!contain) {
672
- ownerDocument.removeEventListener('keydown', onKeyDown, true);
659
+ ownerDocument.removeEventListener('keydown', onKeyDown as EventListener, true);
673
660
  }
674
661
  };
675
662
  }, [scopeRef, restoreFocus, contain]);
@@ -695,11 +682,12 @@ function useRestoreFocus(scopeRef: RefObject<Element[] | null>, restoreFocus?: b
695
682
  let nodeToRestore = treeNode.nodeToRestore;
696
683
 
697
684
  // if we already lost focus to the body and this was the active scope, then we should attempt to restore
685
+ let activeElement = getActiveElement(ownerDocument);
698
686
  if (
699
687
  restoreFocus
700
688
  && nodeToRestore
701
689
  && (
702
- ((ownerDocument.activeElement && isElementInChildScope(ownerDocument.activeElement, scopeRef)) || (ownerDocument.activeElement === ownerDocument.body && shouldRestoreFocus(scopeRef)))
690
+ ((activeElement && isElementInChildScope(activeElement, scopeRef)) || (activeElement === ownerDocument.body && shouldRestoreFocus(scopeRef)))
703
691
  )
704
692
  ) {
705
693
  // freeze the focusScopeTree so it persists after the raf, otherwise during unmount nodes are removed from it
@@ -748,10 +736,19 @@ function restoreFocusToElement(node: FocusableElement) {
748
736
  * Create a [TreeWalker]{@link https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker}
749
737
  * that matches all focusable/tabbable elements.
750
738
  */
751
- export function getFocusableTreeWalker(root: Element, opts?: FocusManagerOptions, scope?: Element[]) {
752
- let selector = opts?.tabbable ? TABBABLE_ELEMENT_SELECTOR : FOCUSABLE_ELEMENT_SELECTOR;
753
- let walker = getOwnerDocument(root).createTreeWalker(
754
- root,
739
+ export function getFocusableTreeWalker(root: Element, opts?: FocusManagerOptions, scope?: Element[]): ShadowTreeWalker | TreeWalker {
740
+ let filter = opts?.tabbable ? isTabbable : isFocusable;
741
+
742
+ // Ensure that root is an Element or fall back appropriately
743
+ let rootElement = root?.nodeType === Node.ELEMENT_NODE ? (root as Element) : null;
744
+
745
+ // Determine the document to use
746
+ let doc = getOwnerDocument(rootElement);
747
+
748
+ // Create a TreeWalker, ensuring the root is an Element or Document
749
+ let walker = createShadowTreeWalker(
750
+ doc,
751
+ root || doc,
755
752
  NodeFilter.SHOW_ELEMENT,
756
753
  {
757
754
  acceptNode(node) {
@@ -760,7 +757,7 @@ export function getFocusableTreeWalker(root: Element, opts?: FocusManagerOptions
760
757
  return NodeFilter.FILTER_REJECT;
761
758
  }
762
759
 
763
- if ((node as Element).matches(selector)
760
+ if (filter(node as Element)
764
761
  && isElementVisible(node as Element)
765
762
  && (!scope || isElementInScope(node as Element, scope))
766
763
  && (!opts?.accept || opts.accept(node as Element))
@@ -791,7 +788,7 @@ export function createFocusManager(ref: RefObject<Element | null>, defaultOption
791
788
  return null;
792
789
  }
793
790
  let {from, tabbable = defaultOptions.tabbable, wrap = defaultOptions.wrap, accept = defaultOptions.accept} = opts;
794
- let node = from || getOwnerDocument(root).activeElement;
791
+ let node = from || getActiveElement(getOwnerDocument(root));
795
792
  let walker = getFocusableTreeWalker(root, {tabbable, accept});
796
793
  if (root.contains(node)) {
797
794
  walker.currentNode = node!;
@@ -812,7 +809,7 @@ export function createFocusManager(ref: RefObject<Element | null>, defaultOption
812
809
  return null;
813
810
  }
814
811
  let {from, tabbable = defaultOptions.tabbable, wrap = defaultOptions.wrap, accept = defaultOptions.accept} = opts;
815
- let node = from || getOwnerDocument(root).activeElement;
812
+ let node = from || getActiveElement(getOwnerDocument(root));
816
813
  let walker = getFocusableTreeWalker(root, {tabbable, accept});
817
814
  if (root.contains(node)) {
818
815
  walker.currentNode = node!;
@@ -867,7 +864,7 @@ export function createFocusManager(ref: RefObject<Element | null>, defaultOption
867
864
  };
868
865
  }
869
866
 
870
- function last(walker: TreeWalker) {
867
+ function last(walker: ShadowTreeWalker | TreeWalker) {
871
868
  let next: FocusableElement | undefined = undefined;
872
869
  let last: FocusableElement;
873
870
  do {
package/src/index.ts CHANGED
@@ -10,14 +10,16 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
 
13
- export {FocusScope, useFocusManager, getFocusableTreeWalker, createFocusManager, isElementInChildOfActiveScope, isFocusable} from './FocusScope';
13
+ export {FocusScope, useFocusManager, getFocusableTreeWalker, createFocusManager, isElementInChildOfActiveScope} from './FocusScope';
14
14
  export {FocusRing} from './FocusRing';
15
- export {FocusableProvider, useFocusable} from './useFocusable';
16
15
  export {useFocusRing} from './useFocusRing';
17
- export {focusSafely} from './focusSafely';
18
16
  export {useHasTabbableChild} from './useHasTabbableChild';
17
+ export {moveVirtualFocus, dispatchVirtualBlur, dispatchVirtualFocus, getVirtuallyFocusedElement} from './virtualFocus';
18
+ // For backward compatibility.
19
+ export {isFocusable} from '@react-aria/utils';
20
+ export {FocusableProvider, Focusable, useFocusable, focusSafely} from '@react-aria/interactions';
19
21
 
20
22
  export type {FocusScopeProps, FocusManager, FocusManagerOptions} from './FocusScope';
21
23
  export type {FocusRingProps} from './FocusRing';
22
- export type {FocusableAria, FocusableOptions, FocusableProviderProps} from './useFocusable';
24
+ export type {FocusableAria, FocusableOptions, FocusableProviderProps} from '@react-aria/interactions';
23
25
  export type {AriaFocusRingProps, FocusRingAria} from './useFocusRing';
@@ -0,0 +1,33 @@
1
+ import {getActiveElement, getOwnerDocument} from '@react-aria/utils';
2
+
3
+ export function moveVirtualFocus(to: Element | null) {
4
+ let from = getVirtuallyFocusedElement(getOwnerDocument(to));
5
+ if (from !== to) {
6
+ if (from) {
7
+ dispatchVirtualBlur(from, to);
8
+ }
9
+ if (to) {
10
+ dispatchVirtualFocus(to, from);
11
+ }
12
+ }
13
+ }
14
+
15
+ export function dispatchVirtualBlur(from: Element, to: Element | null) {
16
+ from.dispatchEvent(new FocusEvent('blur', {relatedTarget: to}));
17
+ from.dispatchEvent(new FocusEvent('focusout', {bubbles: true, relatedTarget: to}));
18
+ }
19
+
20
+ export function dispatchVirtualFocus(to: Element, from: Element | null) {
21
+ to.dispatchEvent(new FocusEvent('focus', {relatedTarget: from}));
22
+ to.dispatchEvent(new FocusEvent('focusin', {bubbles: true, relatedTarget: from}));
23
+ }
24
+
25
+ export function getVirtuallyFocusedElement(document: Document) {
26
+ let activeElement = getActiveElement(document);
27
+ let activeDescendant = activeElement?.getAttribute('aria-activedescendant');
28
+ if (activeDescendant) {
29
+ return document.getElementById(activeDescendant) || activeElement;
30
+ }
31
+
32
+ return activeElement;
33
+ }
@@ -1,39 +0,0 @@
1
- var $cBYLt$reactariautils = require("@react-aria/utils");
2
- var $cBYLt$reactariainteractions = require("@react-aria/interactions");
3
-
4
-
5
- function $parcel$export(e, n, v, s) {
6
- Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
7
- }
8
-
9
- $parcel$export(module.exports, "focusSafely", () => $1c7f9157d722357d$export$80f3e147d781571c);
10
- /*
11
- * Copyright 2020 Adobe. All rights reserved.
12
- * This file is licensed to you under the Apache License, Version 2.0 (the 'License');
13
- * you may not use this file except in compliance with the License. You may obtain a copy
14
- * of the License at http://www.apache.org/licenses/LICENSE-2.0
15
- *
16
- * Unless required by applicable law or agreed to in writing, software distributed under
17
- * the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
18
- * OF ANY KIND, either express or implied. See the License for the specific language
19
- * governing permissions and limitations under the License.
20
- */
21
-
22
- function $1c7f9157d722357d$export$80f3e147d781571c(element) {
23
- // If the user is interacting with a virtual cursor, e.g. screen reader, then
24
- // wait until after any animated transitions that are currently occurring on
25
- // the page before shifting focus. This avoids issues with VoiceOver on iOS
26
- // causing the page to scroll when moving focus if the element is transitioning
27
- // from off the screen.
28
- const ownerDocument = (0, $cBYLt$reactariautils.getOwnerDocument)(element);
29
- if ((0, $cBYLt$reactariainteractions.getInteractionModality)() === 'virtual') {
30
- let lastFocusedElement = ownerDocument.activeElement;
31
- (0, $cBYLt$reactariautils.runAfterTransition)(()=>{
32
- // If focus did not move and the element is still in the document, focus it.
33
- if (ownerDocument.activeElement === lastFocusedElement && element.isConnected) (0, $cBYLt$reactariautils.focusWithoutScrolling)(element);
34
- });
35
- } else (0, $cBYLt$reactariautils.focusWithoutScrolling)(element);
36
- }
37
-
38
-
39
- //# sourceMappingURL=focusSafely.main.js.map
@@ -1 +0,0 @@
1
- {"mappings":";;;;;;;;;AAAA;;;;;;;;;;CAUC;;AAUM,SAAS,0CAAY,OAAyB;IACnD,6EAA6E;IAC7E,4EAA4E;IAC5E,2EAA2E;IAC3E,+EAA+E;IAC/E,uBAAuB;IACvB,MAAM,gBAAgB,CAAA,GAAA,sCAAe,EAAE;IACvC,IAAI,CAAA,GAAA,mDAAqB,QAAQ,WAAW;QAC1C,IAAI,qBAAqB,cAAc,aAAa;QACpD,CAAA,GAAA,wCAAiB,EAAE;YACjB,4EAA4E;YAC5E,IAAI,cAAc,aAAa,KAAK,sBAAsB,QAAQ,WAAW,EAC3E,CAAA,GAAA,2CAAoB,EAAE;QAE1B;IACF,OACE,CAAA,GAAA,2CAAoB,EAAE;AAE1B","sources":["packages/@react-aria/focus/src/focusSafely.ts"],"sourcesContent":["/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the 'License');\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {FocusableElement} from '@react-types/shared';\nimport {focusWithoutScrolling, getOwnerDocument, runAfterTransition} from '@react-aria/utils';\nimport {getInteractionModality} from '@react-aria/interactions';\n\n/**\n * A utility function that focuses an element while avoiding undesired side effects such\n * as page scrolling and screen reader issues with CSS transitions.\n */\nexport function focusSafely(element: FocusableElement) {\n // If the user is interacting with a virtual cursor, e.g. screen reader, then\n // wait until after any animated transitions that are currently occurring on\n // the page before shifting focus. This avoids issues with VoiceOver on iOS\n // causing the page to scroll when moving focus if the element is transitioning\n // from off the screen.\n const ownerDocument = getOwnerDocument(element);\n if (getInteractionModality() === 'virtual') {\n let lastFocusedElement = ownerDocument.activeElement;\n runAfterTransition(() => {\n // If focus did not move and the element is still in the document, focus it.\n if (ownerDocument.activeElement === lastFocusedElement && element.isConnected) {\n focusWithoutScrolling(element);\n }\n });\n } else {\n focusWithoutScrolling(element);\n }\n}\n"],"names":[],"version":3,"file":"focusSafely.main.js.map"}
@@ -1,34 +0,0 @@
1
- import {getOwnerDocument as $jlOai$getOwnerDocument, runAfterTransition as $jlOai$runAfterTransition, focusWithoutScrolling as $jlOai$focusWithoutScrolling} from "@react-aria/utils";
2
- import {getInteractionModality as $jlOai$getInteractionModality} from "@react-aria/interactions";
3
-
4
- /*
5
- * Copyright 2020 Adobe. All rights reserved.
6
- * This file is licensed to you under the Apache License, Version 2.0 (the 'License');
7
- * you may not use this file except in compliance with the License. You may obtain a copy
8
- * of the License at http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software distributed under
11
- * the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
12
- * OF ANY KIND, either express or implied. See the License for the specific language
13
- * governing permissions and limitations under the License.
14
- */
15
-
16
- function $6a99195332edec8b$export$80f3e147d781571c(element) {
17
- // If the user is interacting with a virtual cursor, e.g. screen reader, then
18
- // wait until after any animated transitions that are currently occurring on
19
- // the page before shifting focus. This avoids issues with VoiceOver on iOS
20
- // causing the page to scroll when moving focus if the element is transitioning
21
- // from off the screen.
22
- const ownerDocument = (0, $jlOai$getOwnerDocument)(element);
23
- if ((0, $jlOai$getInteractionModality)() === 'virtual') {
24
- let lastFocusedElement = ownerDocument.activeElement;
25
- (0, $jlOai$runAfterTransition)(()=>{
26
- // If focus did not move and the element is still in the document, focus it.
27
- if (ownerDocument.activeElement === lastFocusedElement && element.isConnected) (0, $jlOai$focusWithoutScrolling)(element);
28
- });
29
- } else (0, $jlOai$focusWithoutScrolling)(element);
30
- }
31
-
32
-
33
- export {$6a99195332edec8b$export$80f3e147d781571c as focusSafely};
34
- //# sourceMappingURL=focusSafely.module.js.map
@@ -1,34 +0,0 @@
1
- import {getOwnerDocument as $jlOai$getOwnerDocument, runAfterTransition as $jlOai$runAfterTransition, focusWithoutScrolling as $jlOai$focusWithoutScrolling} from "@react-aria/utils";
2
- import {getInteractionModality as $jlOai$getInteractionModality} from "@react-aria/interactions";
3
-
4
- /*
5
- * Copyright 2020 Adobe. All rights reserved.
6
- * This file is licensed to you under the Apache License, Version 2.0 (the 'License');
7
- * you may not use this file except in compliance with the License. You may obtain a copy
8
- * of the License at http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software distributed under
11
- * the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
12
- * OF ANY KIND, either express or implied. See the License for the specific language
13
- * governing permissions and limitations under the License.
14
- */
15
-
16
- function $6a99195332edec8b$export$80f3e147d781571c(element) {
17
- // If the user is interacting with a virtual cursor, e.g. screen reader, then
18
- // wait until after any animated transitions that are currently occurring on
19
- // the page before shifting focus. This avoids issues with VoiceOver on iOS
20
- // causing the page to scroll when moving focus if the element is transitioning
21
- // from off the screen.
22
- const ownerDocument = (0, $jlOai$getOwnerDocument)(element);
23
- if ((0, $jlOai$getInteractionModality)() === 'virtual') {
24
- let lastFocusedElement = ownerDocument.activeElement;
25
- (0, $jlOai$runAfterTransition)(()=>{
26
- // If focus did not move and the element is still in the document, focus it.
27
- if (ownerDocument.activeElement === lastFocusedElement && element.isConnected) (0, $jlOai$focusWithoutScrolling)(element);
28
- });
29
- } else (0, $jlOai$focusWithoutScrolling)(element);
30
- }
31
-
32
-
33
- export {$6a99195332edec8b$export$80f3e147d781571c as focusSafely};
34
- //# sourceMappingURL=focusSafely.module.js.map
@@ -1 +0,0 @@
1
- {"mappings":";;;AAAA;;;;;;;;;;CAUC;;AAUM,SAAS,0CAAY,OAAyB;IACnD,6EAA6E;IAC7E,4EAA4E;IAC5E,2EAA2E;IAC3E,+EAA+E;IAC/E,uBAAuB;IACvB,MAAM,gBAAgB,CAAA,GAAA,uBAAe,EAAE;IACvC,IAAI,CAAA,GAAA,6BAAqB,QAAQ,WAAW;QAC1C,IAAI,qBAAqB,cAAc,aAAa;QACpD,CAAA,GAAA,yBAAiB,EAAE;YACjB,4EAA4E;YAC5E,IAAI,cAAc,aAAa,KAAK,sBAAsB,QAAQ,WAAW,EAC3E,CAAA,GAAA,4BAAoB,EAAE;QAE1B;IACF,OACE,CAAA,GAAA,4BAAoB,EAAE;AAE1B","sources":["packages/@react-aria/focus/src/focusSafely.ts"],"sourcesContent":["/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the 'License');\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {FocusableElement} from '@react-types/shared';\nimport {focusWithoutScrolling, getOwnerDocument, runAfterTransition} from '@react-aria/utils';\nimport {getInteractionModality} from '@react-aria/interactions';\n\n/**\n * A utility function that focuses an element while avoiding undesired side effects such\n * as page scrolling and screen reader issues with CSS transitions.\n */\nexport function focusSafely(element: FocusableElement) {\n // If the user is interacting with a virtual cursor, e.g. screen reader, then\n // wait until after any animated transitions that are currently occurring on\n // the page before shifting focus. This avoids issues with VoiceOver on iOS\n // causing the page to scroll when moving focus if the element is transitioning\n // from off the screen.\n const ownerDocument = getOwnerDocument(element);\n if (getInteractionModality() === 'virtual') {\n let lastFocusedElement = ownerDocument.activeElement;\n runAfterTransition(() => {\n // If focus did not move and the element is still in the document, focus it.\n if (ownerDocument.activeElement === lastFocusedElement && element.isConnected) {\n focusWithoutScrolling(element);\n }\n });\n } else {\n focusWithoutScrolling(element);\n }\n}\n"],"names":[],"version":3,"file":"focusSafely.module.js.map"}
@@ -1,72 +0,0 @@
1
- var $1c7f9157d722357d$exports = require("./focusSafely.main.js");
2
- var $ggOO2$reactariautils = require("@react-aria/utils");
3
- var $ggOO2$react = require("react");
4
- var $ggOO2$reactariainteractions = require("@react-aria/interactions");
5
-
6
-
7
- function $parcel$interopDefault(a) {
8
- return a && a.__esModule ? a.default : a;
9
- }
10
-
11
- function $parcel$export(e, n, v, s) {
12
- Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
13
- }
14
-
15
- $parcel$export(module.exports, "FocusableProvider", () => $fb504d83237fd6ac$export$13f3202a3e5ddd5);
16
- $parcel$export(module.exports, "useFocusable", () => $fb504d83237fd6ac$export$4c014de7c8940b4c);
17
- /*
18
- * Copyright 2020 Adobe. All rights reserved.
19
- * This file is licensed to you under the Apache License, Version 2.0 (the "License");
20
- * you may not use this file except in compliance with the License. You may obtain a copy
21
- * of the License at http://www.apache.org/licenses/LICENSE-2.0
22
- *
23
- * Unless required by applicable law or agreed to in writing, software distributed under
24
- * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
25
- * OF ANY KIND, either express or implied. See the License for the specific language
26
- * governing permissions and limitations under the License.
27
- */
28
-
29
-
30
-
31
- let $fb504d83237fd6ac$var$FocusableContext = /*#__PURE__*/ (0, ($parcel$interopDefault($ggOO2$react))).createContext(null);
32
- function $fb504d83237fd6ac$var$useFocusableContext(ref) {
33
- let context = (0, $ggOO2$react.useContext)($fb504d83237fd6ac$var$FocusableContext) || {};
34
- (0, $ggOO2$reactariautils.useSyncRef)(context, ref);
35
- // eslint-disable-next-line
36
- let { ref: _, ...otherProps } = context;
37
- return otherProps;
38
- }
39
- const $fb504d83237fd6ac$export$13f3202a3e5ddd5 = /*#__PURE__*/ (0, ($parcel$interopDefault($ggOO2$react))).forwardRef(function FocusableProvider(props, ref) {
40
- let { children: children, ...otherProps } = props;
41
- let objRef = (0, $ggOO2$reactariautils.useObjectRef)(ref);
42
- let context = {
43
- ...otherProps,
44
- ref: objRef
45
- };
46
- return /*#__PURE__*/ (0, ($parcel$interopDefault($ggOO2$react))).createElement($fb504d83237fd6ac$var$FocusableContext.Provider, {
47
- value: context
48
- }, children);
49
- });
50
- function $fb504d83237fd6ac$export$4c014de7c8940b4c(props, domRef) {
51
- let { focusProps: focusProps } = (0, $ggOO2$reactariainteractions.useFocus)(props);
52
- let { keyboardProps: keyboardProps } = (0, $ggOO2$reactariainteractions.useKeyboard)(props);
53
- let interactions = (0, $ggOO2$reactariautils.mergeProps)(focusProps, keyboardProps);
54
- let domProps = $fb504d83237fd6ac$var$useFocusableContext(domRef);
55
- let interactionProps = props.isDisabled ? {} : domProps;
56
- let autoFocusRef = (0, $ggOO2$react.useRef)(props.autoFocus);
57
- (0, $ggOO2$react.useEffect)(()=>{
58
- if (autoFocusRef.current && domRef.current) (0, $1c7f9157d722357d$exports.focusSafely)(domRef.current);
59
- autoFocusRef.current = false;
60
- }, [
61
- domRef
62
- ]);
63
- return {
64
- focusableProps: (0, $ggOO2$reactariautils.mergeProps)({
65
- ...interactions,
66
- tabIndex: props.excludeFromTabOrder && !props.isDisabled ? -1 : undefined
67
- }, interactionProps)
68
- };
69
- }
70
-
71
-
72
- //# sourceMappingURL=useFocusable.main.js.map
@@ -1 +0,0 @@
1
- {"mappings":";;;;;;;;;;;;;;;;AAAA;;;;;;;;;;CAUC;;;;AAsBD,IAAI,uDAAmB,CAAA,GAAA,sCAAI,EAAE,aAAa,CAA+B;AAEzE,SAAS,0CAAoB,GAAuC;IAClE,IAAI,UAAU,CAAA,GAAA,uBAAS,EAAE,2CAAqB,CAAC;IAC/C,CAAA,GAAA,gCAAS,EAAE,SAAS;IAEpB,2BAA2B;IAC3B,IAAI,EAAC,KAAK,CAAC,EAAE,GAAG,YAAW,GAAG;IAC9B,OAAO;AACT;AAKO,MAAM,yDAAoB,CAAA,GAAA,sCAAI,EAAE,UAAU,CAAC,SAAS,kBAAkB,KAA6B,EAAE,GAAmC;IAC7I,IAAI,YAAC,QAAQ,EAAE,GAAG,YAAW,GAAG;IAChC,IAAI,SAAS,CAAA,GAAA,kCAAW,EAAE;IAC1B,IAAI,UAAU;QACZ,GAAG,UAAU;QACb,KAAK;IACP;IAEA,qBACE,0DAAC,uCAAiB,QAAQ;QAAC,OAAO;OAC/B;AAGP;AAUO,SAAS,0CAA4D,KAA0B,EAAE,MAA0C;IAChJ,IAAI,cAAC,UAAU,EAAC,GAAG,CAAA,GAAA,qCAAO,EAAE;IAC5B,IAAI,iBAAC,aAAa,EAAC,GAAG,CAAA,GAAA,wCAAU,EAAE;IAClC,IAAI,eAAe,CAAA,GAAA,gCAAS,EAAE,YAAY;IAC1C,IAAI,WAAW,0CAAoB;IACnC,IAAI,mBAAmB,MAAM,UAAU,GAAG,CAAC,IAAI;IAC/C,IAAI,eAAe,CAAA,GAAA,mBAAK,EAAE,MAAM,SAAS;IAEzC,CAAA,GAAA,sBAAQ,EAAE;QACR,IAAI,aAAa,OAAO,IAAI,OAAO,OAAO,EACxC,CAAA,GAAA,qCAAU,EAAE,OAAO,OAAO;QAE5B,aAAa,OAAO,GAAG;IACzB,GAAG;QAAC;KAAO;IAEX,OAAO;QACL,gBAAgB,CAAA,GAAA,gCAAS,EACvB;YACE,GAAG,YAAY;YACf,UAAU,MAAM,mBAAmB,IAAI,CAAC,MAAM,UAAU,GAAG,KAAK;QAClE,GACA;IAEJ;AACF","sources":["packages/@react-aria/focus/src/useFocusable.tsx"],"sourcesContent":["/*\n * Copyright 2020 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {DOMAttributes, FocusableDOMProps, FocusableElement, FocusableProps, RefObject} from '@react-types/shared';\nimport {focusSafely} from './';\nimport {mergeProps, useObjectRef, useSyncRef} from '@react-aria/utils';\nimport React, {ForwardedRef, MutableRefObject, ReactNode, useContext, useEffect, useRef} from 'react';\nimport {useFocus, useKeyboard} from '@react-aria/interactions';\n\nexport interface FocusableOptions<T = FocusableElement> extends FocusableProps<T>, FocusableDOMProps {\n /** Whether focus should be disabled. */\n isDisabled?: boolean\n}\n\nexport interface FocusableProviderProps extends DOMAttributes {\n /** The child element to provide DOM props to. */\n children?: ReactNode\n}\n\ninterface FocusableContextValue extends FocusableProviderProps {\n ref?: MutableRefObject<FocusableElement | null>\n}\n\nlet FocusableContext = React.createContext<FocusableContextValue | null>(null);\n\nfunction useFocusableContext(ref: RefObject<FocusableElement | null>): FocusableContextValue {\n let context = useContext(FocusableContext) || {};\n useSyncRef(context, ref);\n\n // eslint-disable-next-line\n let {ref: _, ...otherProps} = context;\n return otherProps;\n}\n\n/**\n * Provides DOM props to the nearest focusable child.\n */\nexport const FocusableProvider = React.forwardRef(function FocusableProvider(props: FocusableProviderProps, ref: ForwardedRef<FocusableElement>) {\n let {children, ...otherProps} = props;\n let objRef = useObjectRef(ref);\n let context = {\n ...otherProps,\n ref: objRef\n };\n\n return (\n <FocusableContext.Provider value={context}>\n {children}\n </FocusableContext.Provider>\n );\n});\n\nexport interface FocusableAria {\n /** Props for the focusable element. */\n focusableProps: DOMAttributes\n}\n\n/**\n * Used to make an element focusable and capable of auto focus.\n */\nexport function useFocusable<T extends FocusableElement = FocusableElement>(props: FocusableOptions<T>, domRef: RefObject<FocusableElement | null>): FocusableAria {\n let {focusProps} = useFocus(props);\n let {keyboardProps} = useKeyboard(props);\n let interactions = mergeProps(focusProps, keyboardProps);\n let domProps = useFocusableContext(domRef);\n let interactionProps = props.isDisabled ? {} : domProps;\n let autoFocusRef = useRef(props.autoFocus);\n\n useEffect(() => {\n if (autoFocusRef.current && domRef.current) {\n focusSafely(domRef.current);\n }\n autoFocusRef.current = false;\n }, [domRef]);\n\n return {\n focusableProps: mergeProps(\n {\n ...interactions,\n tabIndex: props.excludeFromTabOrder && !props.isDisabled ? -1 : undefined\n },\n interactionProps\n )\n };\n}\n"],"names":[],"version":3,"file":"useFocusable.main.js.map"}