@react-aria/overlays 3.10.1 → 3.12.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.
@@ -1 +1 @@
1
- {"mappings":"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","sources":["packages/@react-aria/overlays/src/packages/@react-aria/overlays/src/calculatePosition.ts","packages/@react-aria/overlays/src/packages/@react-aria/overlays/src/useCloseOnScroll.ts","packages/@react-aria/overlays/src/packages/@react-aria/overlays/src/useOverlayPosition.ts","packages/@react-aria/overlays/src/packages/@react-aria/overlays/src/useOverlay.ts","packages/@react-aria/overlays/src/packages/@react-aria/overlays/src/useOverlayTrigger.ts","packages/@react-aria/overlays/src/packages/@react-aria/overlays/src/usePreventScroll.ts","packages/@react-aria/overlays/src/packages/@react-aria/overlays/src/useModal.tsx","packages/@react-aria/overlays/src/packages/@react-aria/overlays/src/DismissButton.tsx","packages/@react-aria/overlays/src/packages/@react-aria/overlays/src/ariaHideOutside.ts","packages/@react-aria/overlays/src/packages/@react-aria/overlays/src/index.ts","packages/@react-aria/overlays/src/index.ts"],"sourcesContent":[null,null,null,null,null,null,null,null,null,null,"/*\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 */\nexport {useOverlayPosition} from './useOverlayPosition';\nexport {useOverlay} from './useOverlay';\nexport {useOverlayTrigger} from './useOverlayTrigger';\nexport {usePreventScroll} from './usePreventScroll';\nexport {ModalProvider, useModalProvider, OverlayProvider, OverlayContainer, useModal} from './useModal';\nexport {DismissButton} from './DismissButton';\nexport {ariaHideOutside} from './ariaHideOutside';\n\nexport type {AriaPositionProps, PositionAria} from './useOverlayPosition';\nexport type {AriaOverlayProps, OverlayAria} from './useOverlay';\nexport type {OverlayTriggerAria, OverlayTriggerProps} from './useOverlayTrigger';\nexport type {AriaModalOptions, ModalAria, ModalProviderAria, ModalProviderProps, OverlayContainerProps} from './useModal';\nexport type {DismissButtonProps} from './DismissButton';\n"],"names":[],"version":3,"file":"types.d.ts.map"}
1
+ {"mappings":"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","sources":["packages/@react-aria/overlays/src/packages/@react-aria/overlays/src/calculatePosition.ts","packages/@react-aria/overlays/src/packages/@react-aria/overlays/src/useCloseOnScroll.ts","packages/@react-aria/overlays/src/packages/@react-aria/overlays/src/useOverlayPosition.ts","packages/@react-aria/overlays/src/packages/@react-aria/overlays/src/useOverlay.ts","packages/@react-aria/overlays/src/packages/@react-aria/overlays/src/useOverlayTrigger.ts","packages/@react-aria/overlays/src/packages/@react-aria/overlays/src/usePreventScroll.ts","packages/@react-aria/overlays/src/packages/@react-aria/overlays/src/useModal.tsx","packages/@react-aria/overlays/src/packages/@react-aria/overlays/src/DismissButton.tsx","packages/@react-aria/overlays/src/packages/@react-aria/overlays/src/ariaHideOutside.ts","packages/@react-aria/overlays/src/packages/@react-aria/overlays/src/usePopover.ts","packages/@react-aria/overlays/src/packages/@react-aria/overlays/src/Overlay.tsx","packages/@react-aria/overlays/src/packages/@react-aria/overlays/src/useModalOverlay.ts","packages/@react-aria/overlays/src/packages/@react-aria/overlays/src/index.ts","packages/@react-aria/overlays/src/index.ts"],"sourcesContent":[null,null,null,null,null,null,null,null,null,null,null,null,null,"/*\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 */\nexport {useOverlayPosition} from './useOverlayPosition';\nexport {useOverlay} from './useOverlay';\nexport {useOverlayTrigger} from './useOverlayTrigger';\nexport {usePreventScroll} from './usePreventScroll';\nexport {ModalProvider, useModalProvider, OverlayProvider, OverlayContainer, useModal} from './useModal';\nexport {DismissButton} from './DismissButton';\nexport {ariaHideOutside} from './ariaHideOutside';\nexport {usePopover} from './usePopover';\nexport {useModalOverlay} from './useModalOverlay';\nexport {Overlay, useOverlayFocusContain} from './Overlay';\n\nexport type {AriaPositionProps, PositionAria} from './useOverlayPosition';\nexport type {AriaOverlayProps, OverlayAria} from './useOverlay';\nexport type {OverlayTriggerAria, OverlayTriggerProps} from './useOverlayTrigger';\nexport type {AriaModalOptions, ModalAria, ModalProviderAria, ModalProviderProps, OverlayContainerProps} from './useModal';\nexport type {DismissButtonProps} from './DismissButton';\nexport type {AriaPopoverProps, PopoverAria} from './usePopover';\nexport type {AriaModalOverlayProps, ModalOverlayAria} from './useModalOverlay';\nexport type {OverlayProps} from './Overlay';\n"],"names":[],"version":3,"file":"types.d.ts.map"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-aria/overlays",
3
- "version": "3.10.1",
3
+ "version": "3.12.0",
4
4
  "description": "Spectrum UI components in React",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/main.js",
@@ -18,15 +18,16 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@babel/runtime": "^7.6.2",
21
- "@react-aria/i18n": "^3.6.0",
22
- "@react-aria/interactions": "^3.11.0",
23
- "@react-aria/ssr": "^3.3.0",
24
- "@react-aria/utils": "^3.13.3",
25
- "@react-aria/visually-hidden": "^3.4.1",
26
- "@react-stately/overlays": "^3.4.1",
27
- "@react-types/button": "^3.6.1",
28
- "@react-types/overlays": "^3.6.3",
29
- "@react-types/shared": "^3.14.1"
21
+ "@react-aria/focus": "^3.10.0",
22
+ "@react-aria/i18n": "^3.6.2",
23
+ "@react-aria/interactions": "^3.13.0",
24
+ "@react-aria/ssr": "^3.4.0",
25
+ "@react-aria/utils": "^3.14.1",
26
+ "@react-aria/visually-hidden": "^3.6.0",
27
+ "@react-stately/overlays": "^3.4.3",
28
+ "@react-types/button": "^3.7.0",
29
+ "@react-types/overlays": "^3.6.5",
30
+ "@react-types/shared": "^3.16.0"
30
31
  },
31
32
  "peerDependencies": {
32
33
  "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0",
@@ -35,5 +36,5 @@
35
36
  "publishConfig": {
36
37
  "access": "public"
37
38
  },
38
- "gitHead": "b03ef51e6317547dd0a840f151e59d039b1e1fd3"
39
+ "gitHead": "2954307ddbefe149241685440c81f80ece6b2c83"
39
40
  }
@@ -0,0 +1,63 @@
1
+ /*
2
+ * Copyright 2022 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ import {FocusScope} from '@react-aria/focus';
14
+ import React, {ReactNode, useContext, useMemo, useState} from 'react';
15
+ import ReactDOM from 'react-dom';
16
+ import {useIsSSR} from '@react-aria/ssr';
17
+ import {useLayoutEffect} from '@react-aria/utils';
18
+
19
+ export interface OverlayProps {
20
+ /**
21
+ * The container element in which the overlay portal will be placed.
22
+ * @default document.body
23
+ */
24
+ portalContainer?: Element,
25
+ /** The overlay to render in the portal. */
26
+ children: ReactNode
27
+ }
28
+
29
+ export const OverlayContext = React.createContext(null);
30
+
31
+ /**
32
+ * A container which renders an overlay such as a popover or modal in a portal,
33
+ * and provides a focus scope for the child elements.
34
+ */
35
+ export function Overlay(props: OverlayProps) {
36
+ let isSSR = useIsSSR();
37
+ let {portalContainer = isSSR ? null : document.body} = props;
38
+ let [contain, setContain] = useState(false);
39
+ let contextValue = useMemo(() => ({contain, setContain}), [contain, setContain]);
40
+
41
+ if (!portalContainer) {
42
+ return null;
43
+ }
44
+
45
+ let contents = (
46
+ <OverlayContext.Provider value={contextValue}>
47
+ <FocusScope restoreFocus contain={contain}>
48
+ {props.children}
49
+ </FocusScope>
50
+ </OverlayContext.Provider>
51
+ );
52
+
53
+ return ReactDOM.createPortal(contents, portalContainer);
54
+ }
55
+
56
+ /** @private */
57
+ export function useOverlayFocusContain() {
58
+ let ctx = useContext(OverlayContext);
59
+ let setContain = ctx?.setContain;
60
+ useLayoutEffect(() => {
61
+ setContain?.(true);
62
+ }, [setContain]);
63
+ }
@@ -13,6 +13,7 @@
13
13
  // Keeps a ref count of all hidden elements. Added to when hiding an element, and
14
14
  // subtracted from when showing it again. When it reaches zero, aria-hidden is removed.
15
15
  let refCountMap = new WeakMap<Element, number>();
16
+ let observerStack = [];
16
17
 
17
18
  /**
18
19
  * Hides all elements in the DOM outside the given targets from screen readers using aria-hidden,
@@ -36,20 +37,16 @@ export function ariaHideOutside(targets: Element[], root = document.body) {
36
37
  }
37
38
 
38
39
  // Skip this node and its children if it is one of the target nodes, or a live announcer.
39
- // Also skip children of already hidden nodes, as aria-hidden is recursive.
40
+ // Also skip children of already hidden nodes, as aria-hidden is recursive. An exception is
41
+ // made for elements with role="row" since VoiceOver on iOS has issues hiding elements with role="row".
42
+ // For that case we want to hide the cells inside as well (https://bugs.webkit.org/show_bug.cgi?id=222623).
40
43
  if (
41
44
  visibleNodes.has(node as Element) ||
42
- hiddenNodes.has(node.parentElement)
45
+ (hiddenNodes.has(node.parentElement) && node.parentElement.getAttribute('role') !== 'row')
43
46
  ) {
44
47
  return NodeFilter.FILTER_REJECT;
45
48
  }
46
49
 
47
- // VoiceOver on iOS has issues hiding elements with role="row". Hide the cells inside instead.
48
- // https://bugs.webkit.org/show_bug.cgi?id=222623
49
- if (node instanceof Element && node.getAttribute('role') === 'row') {
50
- return NodeFilter.FILTER_SKIP;
51
- }
52
-
53
50
  // Skip this node but continue to children if one of the targets is inside the node.
54
51
  if (targets.some(target => node.contains(target))) {
55
52
  return NodeFilter.FILTER_SKIP;
@@ -77,6 +74,12 @@ export function ariaHideOutside(targets: Element[], root = document.body) {
77
74
  refCountMap.set(node, refCount + 1);
78
75
  };
79
76
 
77
+ // If there is already a MutationObserver listening from a previous call,
78
+ // disconnect it so the new on takes over.
79
+ if (observerStack.length) {
80
+ observerStack[observerStack.length - 1].disconnect();
81
+ }
82
+
80
83
  let node = walker.nextNode() as Element;
81
84
  while (node != null) {
82
85
  hide(node);
@@ -105,6 +108,17 @@ export function ariaHideOutside(targets: Element[], root = document.body) {
105
108
 
106
109
  observer.observe(root, {childList: true, subtree: true});
107
110
 
111
+ let observerWrapper = {
112
+ observe() {
113
+ observer.observe(root, {childList: true, subtree: true});
114
+ },
115
+ disconnect() {
116
+ observer.disconnect();
117
+ }
118
+ };
119
+
120
+ observerStack.push(observerWrapper);
121
+
108
122
  return () => {
109
123
  observer.disconnect();
110
124
 
@@ -117,5 +131,15 @@ export function ariaHideOutside(targets: Element[], root = document.body) {
117
131
  refCountMap.set(node, count - 1);
118
132
  }
119
133
  }
134
+
135
+ // Remove this observer from the stack, and start the previous one.
136
+ if (observerWrapper === observerStack[observerStack.length - 1]) {
137
+ observerStack.pop();
138
+ if (observerStack.length) {
139
+ observerStack[observerStack.length - 1].observe();
140
+ }
141
+ } else {
142
+ observerStack.splice(observerStack.indexOf(observerWrapper), 1);
143
+ }
120
144
  };
121
145
  }
package/src/index.ts CHANGED
@@ -16,9 +16,15 @@ export {usePreventScroll} from './usePreventScroll';
16
16
  export {ModalProvider, useModalProvider, OverlayProvider, OverlayContainer, useModal} from './useModal';
17
17
  export {DismissButton} from './DismissButton';
18
18
  export {ariaHideOutside} from './ariaHideOutside';
19
+ export {usePopover} from './usePopover';
20
+ export {useModalOverlay} from './useModalOverlay';
21
+ export {Overlay, useOverlayFocusContain} from './Overlay';
19
22
 
20
23
  export type {AriaPositionProps, PositionAria} from './useOverlayPosition';
21
24
  export type {AriaOverlayProps, OverlayAria} from './useOverlay';
22
25
  export type {OverlayTriggerAria, OverlayTriggerProps} from './useOverlayTrigger';
23
26
  export type {AriaModalOptions, ModalAria, ModalProviderAria, ModalProviderProps, OverlayContainerProps} from './useModal';
24
27
  export type {DismissButtonProps} from './DismissButton';
28
+ export type {AriaPopoverProps, PopoverAria} from './usePopover';
29
+ export type {AriaModalOverlayProps, ModalOverlayAria} from './useModalOverlay';
30
+ export type {OverlayProps} from './Overlay';
@@ -30,7 +30,7 @@ export function useCloseOnScroll(opts: CloseOnScrollOptions) {
30
30
  let {triggerRef, isOpen, onClose} = opts;
31
31
 
32
32
  useEffect(() => {
33
- if (!isOpen) {
33
+ if (!isOpen || onClose === null) {
34
34
  return;
35
35
  }
36
36
 
@@ -0,0 +1,69 @@
1
+ /*
2
+ * Copyright 2022 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ import {ariaHideOutside} from './ariaHideOutside';
14
+ import {DOMAttributes} from '@react-types/shared';
15
+ import {mergeProps} from '@react-aria/utils';
16
+ import {OverlayTriggerState} from '@react-stately/overlays';
17
+ import {RefObject, useEffect} from 'react';
18
+ import {useOverlay} from './useOverlay';
19
+ import {useOverlayFocusContain} from './Overlay';
20
+ import {usePreventScroll} from './usePreventScroll';
21
+
22
+ export interface AriaModalOverlayProps {
23
+ /**
24
+ * Whether to close the modal when the user interacts outside it.
25
+ * @default false
26
+ */
27
+ isDismissable?: boolean,
28
+ /**
29
+ * Whether pressing the escape key to close the modal should be disabled.
30
+ * @default false
31
+ */
32
+ isKeyboardDismissDisabled?: boolean
33
+ }
34
+
35
+ export interface ModalOverlayAria {
36
+ /** Props for the modal element. */
37
+ modalProps: DOMAttributes,
38
+ /** Props for the underlay element. */
39
+ underlayProps: DOMAttributes
40
+ }
41
+
42
+ /**
43
+ * Provides the behavior and accessibility implementation for a modal component.
44
+ * A modal is an overlay element which blocks interaction with elements outside it.
45
+ */
46
+ export function useModalOverlay(props: AriaModalOverlayProps, state: OverlayTriggerState, ref: RefObject<HTMLElement>): ModalOverlayAria {
47
+ let {overlayProps, underlayProps} = useOverlay({
48
+ ...props,
49
+ isOpen: state.isOpen,
50
+ onClose: state.close
51
+ }, ref);
52
+
53
+ usePreventScroll({
54
+ isDisabled: !state.isOpen
55
+ });
56
+
57
+ useOverlayFocusContain();
58
+
59
+ useEffect(() => {
60
+ if (state.isOpen) {
61
+ return ariaHideOutside([ref.current]);
62
+ }
63
+ }, [state.isOpen, ref]);
64
+
65
+ return {
66
+ modalProps: mergeProps(overlayProps),
67
+ underlayProps
68
+ };
69
+ }
package/src/useOverlay.ts CHANGED
@@ -11,6 +11,7 @@
11
11
  */
12
12
 
13
13
  import {DOMAttributes} from '@react-types/shared';
14
+ import {isElementInChildOfActiveScope} from '@react-aria/focus';
14
15
  import {RefObject, SyntheticEvent, useEffect} from 'react';
15
16
  import {useFocusWithin, useInteractOutside} from '@react-aria/interactions';
16
17
 
@@ -124,6 +125,13 @@ export function useOverlay(props: AriaOverlayProps, ref: RefObject<Element>): Ov
124
125
  let {focusWithinProps} = useFocusWithin({
125
126
  isDisabled: !shouldCloseOnBlur,
126
127
  onBlurWithin: (e) => {
128
+ // If focus is moving into a child focus scope (e.g. menu inside a dialog),
129
+ // do not close the outer overlay. At this point, the active scope should
130
+ // still be the outer overlay, since blur events run before focus.
131
+ if (e.relatedTarget && isElementInChildOfActiveScope(e.relatedTarget)) {
132
+ return;
133
+ }
134
+
127
135
  if (!shouldCloseOnInteractOutside || shouldCloseOnInteractOutside(e.relatedTarget as Element)) {
128
136
  onClose();
129
137
  }
@@ -15,7 +15,7 @@ import {DOMAttributes} from '@react-types/shared';
15
15
  import {Placement, PlacementAxis, PositionProps} from '@react-types/overlays';
16
16
  import {RefObject, useCallback, useRef, useState} from 'react';
17
17
  import {useCloseOnScroll} from './useCloseOnScroll';
18
- import {useLayoutEffect} from '@react-aria/utils';
18
+ import {useLayoutEffect, useResizeObserver} from '@react-aria/utils';
19
19
  import {useLocale} from '@react-aria/i18n';
20
20
 
21
21
  export interface AriaPositionProps extends PositionProps {
@@ -129,14 +129,22 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria {
129
129
  maxHeight
130
130
  })
131
131
  );
132
+ // eslint-disable-next-line react-hooks/exhaustive-deps
132
133
  }, deps);
133
134
 
134
135
  // Update position when anything changes
136
+ // eslint-disable-next-line react-hooks/exhaustive-deps
135
137
  useLayoutEffect(updatePosition, deps);
136
138
 
137
139
  // Update position on window resize
138
140
  useResize(updatePosition);
139
141
 
142
+ // Update position when the overlay changes size (might need to flip).
143
+ useResizeObserver({
144
+ ref: overlayRef,
145
+ onResize: updatePosition
146
+ });
147
+
140
148
  // Reposition the overlay and do not close on scroll while the visual viewport is resizing.
141
149
  // This will ensure that overlays adjust their positioning when the iOS virtual keyboard appears.
142
150
  let isResizing = useRef(false);
@@ -171,7 +179,7 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria {
171
179
  useCloseOnScroll({
172
180
  triggerRef: targetRef,
173
181
  isOpen,
174
- onClose: onClose ? close : undefined
182
+ onClose: onClose && close
175
183
  });
176
184
 
177
185
  return {
@@ -0,0 +1,121 @@
1
+ /*
2
+ * Copyright 2022 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ import {ariaHideOutside} from './ariaHideOutside';
14
+ import {AriaPositionProps, useOverlayPosition} from './useOverlayPosition';
15
+ import {DOMAttributes} from '@react-types/shared';
16
+ import {mergeProps, useLayoutEffect} from '@react-aria/utils';
17
+ import {OverlayTriggerState} from '@react-stately/overlays';
18
+ import {PlacementAxis} from '@react-types/overlays';
19
+ import {RefObject, useState} from 'react';
20
+ import {useOverlay} from './useOverlay';
21
+ import {usePreventScroll} from './usePreventScroll';
22
+
23
+ export interface AriaPopoverProps extends Omit<AriaPositionProps, 'isOpen' | 'onClose' | 'targetRef' | 'overlayRef'> {
24
+ /**
25
+ * The ref for the element which the popover positions itself with respect to.
26
+ */
27
+ triggerRef: RefObject<Element>,
28
+ /**
29
+ * The ref for the popover element.
30
+ */
31
+ popoverRef: RefObject<Element>,
32
+ /**
33
+ * Whether the popover is non-modal, i.e. elements outside the popover may be
34
+ * interacted with by assistive technologies.
35
+ *
36
+ * Most popovers should not use this option as it may negatively impact the screen
37
+ * reader experience. Only use with components such as combobox, which are designed
38
+ * to handle this situation carefully.
39
+ */
40
+ isNonModal?: boolean,
41
+ /**
42
+ * Whether pressing the escape key to close the popover should be disabled.
43
+ *
44
+ * Most popovers should not use this option. When set to true, an alternative
45
+ * way to close the popover with a keyboard must be provided.
46
+ *
47
+ * @default false
48
+ */
49
+ isKeyboardDismissDisabled?: boolean
50
+ }
51
+
52
+ export interface PopoverAria {
53
+ /** Props for the popover element. */
54
+ popoverProps: DOMAttributes,
55
+ /** Props for the popover tip arrow if any. */
56
+ arrowProps: DOMAttributes,
57
+ /** Props to apply to the underlay element, if any. */
58
+ underlayProps: DOMAttributes,
59
+ /** Placement of the popover with respect to the trigger. */
60
+ placement: PlacementAxis
61
+ }
62
+
63
+ /**
64
+ * Provides the behavior and accessibility implementation for a popover component.
65
+ * A popover is an overlay element positioned relative to a trigger.
66
+ */
67
+ export function usePopover(props: AriaPopoverProps, state: OverlayTriggerState): PopoverAria {
68
+ let {
69
+ triggerRef,
70
+ popoverRef,
71
+ isNonModal,
72
+ isKeyboardDismissDisabled,
73
+ ...otherProps
74
+ } = props;
75
+
76
+ let {overlayProps, underlayProps} = useOverlay(
77
+ {
78
+ isOpen: state.isOpen,
79
+ onClose: state.close,
80
+ shouldCloseOnBlur: true,
81
+ isDismissable: !isNonModal,
82
+ isKeyboardDismissDisabled
83
+ },
84
+ popoverRef
85
+ );
86
+
87
+ let {overlayProps: positionProps, arrowProps, placement} = useOverlayPosition({
88
+ ...otherProps,
89
+ targetRef: triggerRef,
90
+ overlayRef: popoverRef,
91
+ isOpen: state.isOpen,
92
+ onClose: null
93
+ });
94
+
95
+ // Delay preventing scroll until popover is positioned to avoid extra scroll padding.
96
+ // This requires a layout effect so that positioning has been committed to the DOM
97
+ // by the time usePreventScroll measures the element.
98
+ let [isPositioned, setPositioned] = useState(false);
99
+ useLayoutEffect(() => {
100
+ if (!isNonModal && placement) {
101
+ setPositioned(true);
102
+ }
103
+ }, [isNonModal, placement]);
104
+
105
+ usePreventScroll({
106
+ isDisabled: isNonModal || !isPositioned
107
+ });
108
+
109
+ useLayoutEffect(() => {
110
+ if (state.isOpen && !isNonModal && popoverRef.current) {
111
+ return ariaHideOutside([popoverRef.current]);
112
+ }
113
+ }, [isNonModal, state.isOpen, popoverRef]);
114
+
115
+ return {
116
+ popoverProps: mergeProps(overlayProps, positionProps),
117
+ arrowProps,
118
+ underlayProps,
119
+ placement
120
+ };
121
+ }