@fluentui/react-positioning 9.1.2 → 9.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. package/CHANGELOG.json +83 -1
  2. package/CHANGELOG.md +29 -2
  3. package/dist/index.d.ts +9 -16
  4. package/lib/constants.js +5 -0
  5. package/lib/constants.js.map +1 -0
  6. package/lib/middleware/coverTarget.js +40 -0
  7. package/lib/middleware/coverTarget.js.map +1 -0
  8. package/lib/middleware/flip.js +19 -0
  9. package/lib/middleware/flip.js.map +1 -0
  10. package/lib/middleware/index.js +7 -0
  11. package/lib/middleware/index.js.map +1 -0
  12. package/lib/middleware/intersecting.js +21 -0
  13. package/lib/middleware/intersecting.js.map +1 -0
  14. package/lib/middleware/maxSize.js +43 -0
  15. package/lib/middleware/maxSize.js.map +1 -0
  16. package/lib/middleware/offset.js +11 -0
  17. package/lib/middleware/offset.js.map +1 -0
  18. package/lib/middleware/shift.js +30 -0
  19. package/lib/middleware/shift.js.map +1 -0
  20. package/lib/usePositioning.js +237 -339
  21. package/lib/usePositioning.js.map +1 -1
  22. package/lib/utils/debounce.js +22 -0
  23. package/lib/utils/debounce.js.map +1 -0
  24. package/lib/utils/fromFloatingUIPlacement.js +43 -0
  25. package/lib/utils/fromFloatingUIPlacement.js.map +1 -0
  26. package/lib/utils/getBoundary.js +5 -1
  27. package/lib/utils/getBoundary.js.map +1 -1
  28. package/lib/utils/getFloatingUIOffset.js +36 -0
  29. package/lib/utils/getFloatingUIOffset.js.map +1 -0
  30. package/lib/utils/getScrollParent.js +6 -0
  31. package/lib/utils/getScrollParent.js.map +1 -1
  32. package/lib/utils/hasAutoFocusFilter.js +28 -0
  33. package/lib/utils/hasAutoFocusFilter.js.map +1 -0
  34. package/lib/utils/index.js +6 -2
  35. package/lib/utils/index.js.map +1 -1
  36. package/lib/utils/mergeArrowOffset.js +1 -1
  37. package/lib/utils/mergeArrowOffset.js.map +1 -1
  38. package/lib/utils/parseFloatingUIPlacement.js +14 -0
  39. package/lib/utils/parseFloatingUIPlacement.js.map +1 -0
  40. package/lib/utils/toFloatingUIPlacement.js +40 -0
  41. package/lib/utils/toFloatingUIPlacement.js.map +1 -0
  42. package/lib/utils/toggleScrollListener.js +24 -0
  43. package/lib/utils/toggleScrollListener.js.map +1 -0
  44. package/lib-commonjs/constants.js +11 -0
  45. package/lib-commonjs/constants.js.map +1 -0
  46. package/lib-commonjs/middleware/coverTarget.js +50 -0
  47. package/lib-commonjs/middleware/coverTarget.js.map +1 -0
  48. package/lib-commonjs/middleware/flip.js +30 -0
  49. package/lib-commonjs/middleware/flip.js.map +1 -0
  50. package/lib-commonjs/middleware/index.js +20 -0
  51. package/lib-commonjs/middleware/index.js.map +1 -0
  52. package/lib-commonjs/middleware/intersecting.js +31 -0
  53. package/lib-commonjs/middleware/intersecting.js.map +1 -0
  54. package/lib-commonjs/middleware/maxSize.js +54 -0
  55. package/lib-commonjs/middleware/maxSize.js.map +1 -0
  56. package/lib-commonjs/middleware/offset.js +22 -0
  57. package/lib-commonjs/middleware/offset.js.map +1 -0
  58. package/lib-commonjs/middleware/shift.js +41 -0
  59. package/lib-commonjs/middleware/shift.js.map +1 -0
  60. package/lib-commonjs/usePositioning.js +239 -340
  61. package/lib-commonjs/usePositioning.js.map +1 -1
  62. package/lib-commonjs/utils/debounce.js +31 -0
  63. package/lib-commonjs/utils/debounce.js.map +1 -0
  64. package/lib-commonjs/utils/fromFloatingUIPlacement.js +52 -0
  65. package/lib-commonjs/utils/fromFloatingUIPlacement.js.map +1 -0
  66. package/lib-commonjs/utils/getBoundary.js +5 -1
  67. package/lib-commonjs/utils/getBoundary.js.map +1 -1
  68. package/lib-commonjs/utils/getFloatingUIOffset.js +46 -0
  69. package/lib-commonjs/utils/getFloatingUIOffset.js.map +1 -0
  70. package/lib-commonjs/utils/getScrollParent.js +10 -1
  71. package/lib-commonjs/utils/getScrollParent.js.map +1 -1
  72. package/lib-commonjs/utils/hasAutoFocusFilter.js +37 -0
  73. package/lib-commonjs/utils/hasAutoFocusFilter.js.map +1 -0
  74. package/lib-commonjs/utils/index.js +10 -2
  75. package/lib-commonjs/utils/index.js.map +1 -1
  76. package/lib-commonjs/utils/mergeArrowOffset.js +1 -1
  77. package/lib-commonjs/utils/mergeArrowOffset.js.map +1 -1
  78. package/lib-commonjs/utils/parseFloatingUIPlacement.js +23 -0
  79. package/lib-commonjs/utils/parseFloatingUIPlacement.js.map +1 -0
  80. package/lib-commonjs/utils/toFloatingUIPlacement.js +49 -0
  81. package/lib-commonjs/utils/toFloatingUIPlacement.js.map +1 -0
  82. package/lib-commonjs/utils/toggleScrollListener.js +34 -0
  83. package/lib-commonjs/utils/toggleScrollListener.js.map +1 -0
  84. package/package.json +7 -7
  85. package/dist/tsdoc-metadata.json +0 -11
  86. package/lib/isIntersectingModifier.js +0 -26
  87. package/lib/isIntersectingModifier.js.map +0 -1
  88. package/lib/utils/fromPopperPlacement.js +0 -40
  89. package/lib/utils/fromPopperPlacement.js.map +0 -1
  90. package/lib/utils/getPopperOffset.js +0 -44
  91. package/lib/utils/getPopperOffset.js.map +0 -1
  92. package/lib/utils/parsePopperPlacement.js +0 -14
  93. package/lib/utils/parsePopperPlacement.js.map +0 -1
  94. package/lib/utils/positioningHelper.js +0 -49
  95. package/lib/utils/positioningHelper.js.map +0 -1
  96. package/lib-commonjs/isIntersectingModifier.js +0 -34
  97. package/lib-commonjs/isIntersectingModifier.js.map +0 -1
  98. package/lib-commonjs/utils/fromPopperPlacement.js +0 -50
  99. package/lib-commonjs/utils/fromPopperPlacement.js.map +0 -1
  100. package/lib-commonjs/utils/getPopperOffset.js +0 -54
  101. package/lib-commonjs/utils/getPopperOffset.js.map +0 -1
  102. package/lib-commonjs/utils/parsePopperPlacement.js +0 -23
  103. package/lib-commonjs/utils/parsePopperPlacement.js.map +0 -1
  104. package/lib-commonjs/utils/positioningHelper.js +0 -61
  105. package/lib-commonjs/utils/positioningHelper.js.map +0 -1
@@ -1,337 +1,90 @@
1
- import { useEventCallback, useIsomorphicLayoutEffect, useFirstMount, canUseDOM } from '@fluentui/react-utilities';
1
+ import { computePosition, hide as hideMiddleware, arrow as arrowMiddleware } from '@floating-ui/dom';
2
2
  import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';
3
- import * as PopperJs from '@popperjs/core';
3
+ import { canUseDOM, useIsomorphicLayoutEffect } from '@fluentui/react-utilities';
4
+ import { useEventCallback } from '@fluentui/react-utilities';
4
5
  import * as React from 'react';
5
- import { isIntersectingModifier } from './isIntersectingModifier';
6
- import { getScrollParent, applyRtlToOffset, getPlacement, getReactFiberFromNode, getBoundary, useCallbackRef, parsePopperPlacement } from './utils/index';
7
- import { getPopperOffset } from './utils/getPopperOffset'; //
8
- // Dev utils to detect if nodes have "autoFocus" props.
9
- //
10
-
6
+ import { useCallbackRef, toFloatingUIPlacement, toggleScrollListener, hasAutofocusFilter, debounce, hasScrollParent } from './utils';
7
+ import { shift as shiftMiddleware, flip as flipMiddleware, coverTarget as coverTargetMiddleware, maxSize as maxSizeMiddleware, offset as offsetMiddleware, intersecting as intersectingMiddleware } from './middleware';
8
+ import { DATA_POSITIONING_ESCAPED, DATA_POSITIONING_INTERSECTING, DATA_POSITIONING_HIDDEN, DATA_POSITIONING_PLACEMENT } from './constants';
11
9
  /**
12
- * Detects if a passed HTML node has "autoFocus" prop on a React's fiber. Is needed as React handles autofocus behavior
13
- * in React DOM and will not pass "autoFocus" to an actual HTML.
14
- */
15
-
16
- function hasAutofocusProp(node) {
17
- var _a; // https://github.com/facebook/react/blob/848bb2426e44606e0a55dfe44c7b3ece33772485/packages/react-dom/src/client/ReactDOMHostConfig.js#L157-L166
18
-
19
-
20
- const isAutoFocusableElement = node.nodeName === 'BUTTON' || node.nodeName === 'INPUT' || node.nodeName === 'SELECT' || node.nodeName === 'TEXTAREA';
21
-
22
- if (isAutoFocusableElement) {
23
- return !!((_a = getReactFiberFromNode(node)) === null || _a === void 0 ? void 0 : _a.pendingProps.autoFocus);
24
- }
25
-
26
- return false;
27
- }
28
-
29
- function hasAutofocusFilter(node) {
30
- return hasAutofocusProp(node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
31
- }
32
- /**
33
- * Provides a callback to resolve Popper options, it's stable and should be used as a dependency to trigger updates
34
- * of Popper options.
35
- *
36
- * A callback is used there intentionally as some of Popper.js modifiers require DOM nodes (targer, container, arrow)
37
- * that can't be resolved properly during an initial rendering.
10
+ * @internal
38
11
  */
39
12
 
40
-
41
- function usePopperOptions(options, popperOriginalPositionRef) {
13
+ export function usePositioning(options) {
42
14
  const {
43
- align,
44
- arrowPadding,
45
- autoSize,
46
- coverTarget,
47
- flipBoundary,
48
- offset,
49
- overflowBoundary,
50
- pinned,
51
- position,
52
- positionFixed,
53
- // eslint-disable-next-line @typescript-eslint/naming-convention
54
- unstable_disableTether
15
+ targetDocument
16
+ } = useFluent();
17
+ const {
18
+ enabled = true
55
19
  } = options;
56
- const isRtl = useFluent().dir === 'rtl';
57
- const placement = getPlacement(align, position, isRtl);
58
- const strategy = positionFixed ? 'fixed' : 'absolute';
59
- const offsetModifier = React.useMemo(() => offset ? {
60
- name: 'offset',
61
- options: {
62
- offset: isRtl ? applyRtlToOffset(getPopperOffset(offset)) : getPopperOffset(offset)
63
- }
64
- } : null, [offset, isRtl]);
65
- return React.useCallback((target, container, arrow) => {
20
+ const resolvePositioningOptions = usePositioningOptions(options);
21
+ const forceUpdate = useEventCallback(() => {
66
22
  var _a;
67
23
 
68
- const scrollParentElement = getScrollParent(container);
69
- const hasScrollableElement = scrollParentElement ? scrollParentElement !== ((_a = scrollParentElement.ownerDocument) === null || _a === void 0 ? void 0 : _a.body) : false;
70
- const modifiers = [isIntersectingModifier,
71
- /**
72
- * We are setting the position to `fixed` in the first effect to prevent scroll jumps in case of the content
73
- * with managed focus. Modifier sets the position to `fixed` before all other modifier effects. Another part of
74
- * a patch modifies ".forceUpdate()" directly after a Popper will be created.
75
- */
76
- {
77
- name: 'positionStyleFix',
78
- enabled: true,
79
- phase: 'afterWrite',
80
- effect: ({
81
- state,
82
- instance
83
- }) => {
84
- // ".isFirstRun" is a part of our patch, on a first evaluation it will "undefined"
85
- // should be disabled for subsequent runs as it breaks positioning for them
86
- if (instance.isFirstRun !== false) {
87
- popperOriginalPositionRef.current = state.elements.popper.style.position;
88
- state.elements.popper.style.position = 'fixed';
89
- }
90
-
91
- return () => undefined;
92
- },
93
- requires: []
94
- }, {
95
- name: 'flip',
96
- options: {
97
- flipVariations: true
98
- }
99
- },
100
- /**
101
- * pinned disables the flip modifier by setting flip.enabled to false; this
102
- * disables automatic repositioning of the popper box; it will always be placed according to
103
- * the values of `align` and `position` props, regardless of the size of the component, the
104
- * reference element or the viewport.
105
- */
106
- pinned && {
107
- name: 'flip',
108
- enabled: false
109
- },
110
- /**
111
- * When the popper box is placed in the context of a scrollable element, we need to set
112
- * preventOverflow.escapeWithReference to true and flip.boundariesElement to 'scrollParent'
113
- * (default is 'viewport') so that the popper box will stick with the targetRef when we
114
- * scroll targetRef out of the viewport.
115
- */
116
- hasScrollableElement && {
117
- name: 'flip',
118
- options: {
119
- boundary: 'clippingParents'
120
- }
121
- }, hasScrollableElement && {
122
- name: 'preventOverflow',
123
- options: {
124
- boundary: 'clippingParents'
125
- }
126
- }, offsetModifier,
127
- /**
128
- * This modifier is necessary to retain behaviour from popper v1 where untethered poppers are allowed by
129
- * default. i.e. popper is still rendered fully in the viewport even if anchor element is no longer in the
130
- * viewport.
131
- */
132
- unstable_disableTether && {
133
- name: 'preventOverflow',
134
- options: {
135
- altAxis: unstable_disableTether === 'all',
136
- tether: false
137
- }
138
- }, flipBoundary && {
139
- name: 'flip',
140
- options: {
141
- altBoundary: true,
142
- boundary: getBoundary(container, flipBoundary)
143
- }
144
- }, overflowBoundary && {
145
- name: 'preventOverflow',
146
- options: {
147
- altBoundary: true,
148
- boundary: getBoundary(container, overflowBoundary)
149
- }
150
- }, {
151
- // Similar code as popper-maxsize-modifier: https://github.com/atomiks/popper.js/blob/master/src/modifiers/maxSize.js
152
- // popper-maxsize-modifier only calculates the max sizes.
153
- // This modifier can apply max sizes always, or apply the max sizes only when overflow is detected
154
- name: 'applyMaxSize',
155
- enabled: !!autoSize,
156
- phase: 'beforeWrite',
157
- requiresIfExists: ['offset', 'preventOverflow', 'flip'],
158
- options: {
159
- altBoundary: true,
160
- boundary: getBoundary(container, overflowBoundary)
161
- },
162
-
163
- fn({
164
- state,
165
- options: modifierOptions
166
- }) {
167
- const overflow = PopperJs.detectOverflow(state, modifierOptions);
168
- const {
169
- x,
170
- y
171
- } = state.modifiersData.preventOverflow || {
172
- x: 0,
173
- y: 0
174
- };
175
- const {
176
- width,
177
- height
178
- } = state.rects.popper;
179
- const basePlacement = parsePopperPlacement(state.placement).basePlacement;
180
- const widthProp = basePlacement === 'left' ? 'left' : 'right';
181
- const heightProp = basePlacement === 'top' ? 'top' : 'bottom';
182
- const applyMaxWidth = autoSize === 'always' || autoSize === 'width-always' || overflow[widthProp] > 0 && (autoSize === true || autoSize === 'width');
183
- const applyMaxHeight = autoSize === 'always' || autoSize === 'height-always' || overflow[heightProp] > 0 && (autoSize === true || autoSize === 'height');
184
-
185
- if (applyMaxWidth) {
186
- state.styles.popper.maxWidth = `${width - overflow[widthProp] - x}px`;
187
- }
188
-
189
- if (applyMaxHeight) {
190
- state.styles.popper.maxHeight = `${height - overflow[heightProp] - y}px`;
191
- }
192
- }
24
+ const target = (_a = overrideTargetRef.current) !== null && _a !== void 0 ? _a : targetRef.current;
193
25
 
194
- },
195
- /**
196
- * This modifier is necessary in order to render the pointer. Refs are resolved in effects, so it can't be
197
- * placed under computed modifiers. Deep merge is not required as this modifier has only these properties.
198
- */
199
- {
200
- name: 'arrow',
201
- enabled: !!arrow,
202
- options: {
203
- element: arrow,
204
- padding: arrowPadding
205
- }
206
- },
207
- /**
208
- * Modifies popper offsets to cover the reference rect, but still keep edge alignment
209
- */
210
- {
211
- name: 'coverTarget',
212
- enabled: !!coverTarget,
213
- phase: 'main',
214
- requiresIfExists: ['offset', 'preventOverflow', 'flip'],
215
-
216
- fn({
217
- state
218
- }) {
219
- const basePlacement = parsePopperPlacement(state.placement).basePlacement;
220
-
221
- switch (basePlacement) {
222
- case 'bottom':
223
- state.modifiersData.popperOffsets.y -= state.rects.reference.height;
224
- break;
225
-
226
- case 'top':
227
- state.modifiersData.popperOffsets.y += state.rects.reference.height;
228
- break;
229
-
230
- case 'left':
231
- state.modifiersData.popperOffsets.x += state.rects.reference.width;
232
- break;
233
-
234
- case 'right':
235
- state.modifiersData.popperOffsets.x -= state.rects.reference.width;
236
- break;
237
- }
238
- }
239
-
240
- }].filter(Boolean); // filter boolean conditional spreading values
26
+ if (!canUseDOM || !enabled || !target || !containerRef.current) {
27
+ return;
28
+ }
241
29
 
242
- const popperOptions = {
243
- modifiers,
30
+ const {
244
31
  placement,
32
+ middleware,
245
33
  strategy
246
- };
247
- return popperOptions;
248
- }, [arrowPadding, autoSize, coverTarget, flipBoundary, offsetModifier, overflowBoundary, placement, strategy, unstable_disableTether, pinned, // These can be skipped from deps as they will not ever change
249
- popperOriginalPositionRef]);
250
- }
251
- /**
252
- * @internal
253
- * Exposes Popper positioning API via React hook. Contains few important differences between an official "react-popper"
254
- * package:
255
- * - style attributes are applied directly on DOM to avoid re-renders
256
- * - refs changes and resolution is handled properly without re-renders
257
- * - contains a specific to React fix related to initial positioning when containers have components with managed focus
258
- * to avoid focus jumps
259
- */
34
+ } = resolvePositioningOptions(target, containerRef.current, arrowRef.current); // Container is always initialized with `position: fixed` to avoid scroll jumps
35
+ // Before computing the positioned coordinates, revert the container to the deisred positioning strategy
260
36
 
37
+ Object.assign(containerRef.current.style, {
38
+ position: strategy
39
+ });
40
+ computePosition(target, containerRef.current, {
41
+ placement,
42
+ middleware,
43
+ strategy
44
+ }).then(({
45
+ x,
46
+ y,
47
+ middlewareData,
48
+ placement: computedPlacement
49
+ }) => {
50
+ var _a;
261
51
 
262
- export function usePositioning(options = {}) {
263
- const {
264
- enabled = true
265
- } = options;
266
- const isFirstMount = useFirstMount();
267
- const popperOriginalPositionRef = React.useRef('absolute');
268
- const resolvePopperOptions = usePopperOptions(options, popperOriginalPositionRef);
269
- const popperInstanceRef = React.useRef(null);
270
- const handlePopperUpdate = useEventCallback(() => {
271
- var _a, _b;
272
-
273
- (_a = popperInstanceRef.current) === null || _a === void 0 ? void 0 : _a.destroy();
274
- popperInstanceRef.current = null;
275
- const target = (_b = overrideTargetRef.current) !== null && _b !== void 0 ? _b : targetRef.current;
276
- let popperInstance = null;
277
-
278
- if (canUseDOM() && enabled) {
279
- if (target && containerRef.current) {
280
- popperInstance = PopperJs.createPopper(target, containerRef.current, resolvePopperOptions(target, containerRef.current, arrowRef.current));
52
+ writeArrowUpdates({
53
+ arrow: arrowRef.current,
54
+ middlewareData
55
+ });
56
+ writeContainerUpdates({
57
+ container: containerRef.current,
58
+ middlewareData,
59
+ placement: computedPlacement,
60
+ coordinates: {
61
+ x,
62
+ y
63
+ },
64
+ lowPPI: (((_a = targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.defaultView) === null || _a === void 0 ? void 0 : _a.devicePixelRatio) || 1) <= 1,
65
+ strategy
66
+ });
67
+ }).catch(err => {
68
+ // https://github.com/floating-ui/floating-ui/issues/1845
69
+ // FIXME for node > 14
70
+ // node 15 introduces promise rejection which means that any components
71
+ // tests need to be `it('', async () => {})` otherwise there can be race conditions with
72
+ // JSDOM being torn down before this promise is resolved so globals like `window` and `document` don't exist
73
+ // Unless all tests that ever use `usePositioning` are turned into async tests, any logging during testing
74
+ // will actually be counter productive
75
+ if (process.env.NODE_ENV === 'development') {
76
+ // eslint-disable-next-line no-console
77
+ console.error('[usePositioning]: Failed to calculate position', err);
281
78
  }
282
- }
283
-
284
- if (popperInstance) {
285
- /**
286
- * The patch updates `.forceUpdate()` Popper function which restores the original position before the first
287
- * forceUpdate() call. See also "positionStyleFix" modifier in usePopperOptions().
288
- */
289
- const originalForceUpdate = popperInstance.forceUpdate;
290
- popperInstance.isFirstRun = true;
291
-
292
- popperInstance.forceUpdate = () => {
293
- if (popperInstance === null || popperInstance === void 0 ? void 0 : popperInstance.isFirstRun) {
294
- popperInstance.state.elements.popper.style.position = popperOriginalPositionRef.current;
295
- popperInstance.isFirstRun = false;
296
- }
297
-
298
- originalForceUpdate();
299
- };
300
- }
301
-
302
- popperInstanceRef.current = popperInstance;
303
- }); // Refs are managed by useCallbackRef() to handle ref updates scenarios.
304
- //
305
- // The first scenario are updates for a targetRef, we can handle it properly only via callback refs, but it causes
306
- // re-renders (we would like to avoid them).
307
- //
308
- // The second problem is related to refs resolution on React's layer: refs are resolved in the same order as effects
309
- // that causes an issue when you have a container inside a target, for example: a menu in ChatMessage.
310
- //
311
- // function ChatMessage(props) {
312
- // <div className="message" ref={targetRef}> // 3) ref will be resolved only now, but it's too late already
313
- // <Popper target={targetRef}> // 2) create a popper instance
314
- // <div className="menu" /> // 1) capture ref from this element
315
- // </Popper>
316
- // </div>
317
- // }
318
- //
319
- // Check a demo on CodeSandbox: https://codesandbox.io/s/popper-refs-emy60.
320
- //
321
- // This again can be solved with callback refs. It's not a huge issue as with hooks we are moving popper's creation
322
- // to ChatMessage itself, however, without `useCallback()` refs it's still a Pandora box.
323
-
324
- const targetRef = useCallbackRef(null, handlePopperUpdate, true);
325
- const containerRef = useCallbackRef(null, handlePopperUpdate, true);
326
- const arrowRef = useCallbackRef(null, handlePopperUpdate, true); // Stores external target from options.target or setTarget
327
-
328
- const overrideTargetRef = useCallbackRef(null, handlePopperUpdate, true);
79
+ });
80
+ });
81
+ const updatePosition = React.useState(() => debounce(forceUpdate))[0];
82
+ const targetRef = useTargetRef(updatePosition);
83
+ const overrideTargetRef = useTargetRef(updatePosition);
84
+ const containerRef = useContainerRef(updatePosition, enabled);
85
+ const arrowRef = useArrowRef(updatePosition);
329
86
  React.useImperativeHandle(options.positioningRef, () => ({
330
- updatePosition: () => {
331
- var _a;
332
-
333
- (_a = popperInstanceRef.current) === null || _a === void 0 ? void 0 : _a.update();
334
- },
87
+ updatePosition,
335
88
  setTarget: target => {
336
89
  if (options.target && process.env.NODE_ENV !== 'production') {
337
90
  const err = new Error(); // eslint-disable-next-line no-console
@@ -345,34 +98,31 @@ export function usePositioning(options = {}) {
345
98
  }
346
99
  }), // Missing deps:
347
100
  // options.target - only used for a runtime warning
348
- // targetRef - Stable between renders
101
+ // overrideTargetRef - Stable between renders
102
+ // updatePosition - Stable between renders
349
103
  // eslint-disable-next-line react-hooks/exhaustive-deps
350
104
  []);
351
105
  useIsomorphicLayoutEffect(() => {
352
- if (options.target) {
353
- overrideTargetRef.current = options.target;
354
- }
355
- }, [options.target, overrideTargetRef]);
356
- useIsomorphicLayoutEffect(() => {
357
- handlePopperUpdate();
358
- return () => {
359
- var _a;
106
+ var _a;
360
107
 
361
- (_a = popperInstanceRef.current) === null || _a === void 0 ? void 0 : _a.destroy();
362
- popperInstanceRef.current = null;
363
- };
364
- }, [handlePopperUpdate, options.enabled]);
108
+ overrideTargetRef.current = (_a = options.target) !== null && _a !== void 0 ? _a : null;
109
+ }, [options.target, overrideTargetRef, containerRef]);
365
110
  useIsomorphicLayoutEffect(() => {
366
- var _a, _b;
111
+ updatePosition();
112
+ }, [enabled, resolvePositioningOptions, updatePosition]); // Add window resize and scroll listeners to update position
367
113
 
368
- if (!isFirstMount) {
369
- (_a = popperInstanceRef.current) === null || _a === void 0 ? void 0 : _a.setOptions(resolvePopperOptions((_b = overrideTargetRef.current) !== null && _b !== void 0 ? _b : targetRef.current, containerRef.current, arrowRef.current));
114
+ useIsomorphicLayoutEffect(() => {
115
+ const win = targetDocument === null || targetDocument === void 0 ? void 0 : targetDocument.defaultView;
116
+
117
+ if (win) {
118
+ win.addEventListener('resize', updatePosition);
119
+ win.addEventListener('scroll', updatePosition);
120
+ return () => {
121
+ win.removeEventListener('resize', updatePosition);
122
+ win.removeEventListener('scroll', updatePosition);
123
+ };
370
124
  }
371
- }, // Missing deps:
372
- // isFirstMount - Should never change after mount
373
- // arrowRef, containerRef, targetRef - Stable between renders
374
- // eslint-disable-next-line react-hooks/exhaustive-deps
375
- [resolvePopperOptions]);
125
+ }, [updatePosition, targetDocument]);
376
126
 
377
127
  if (process.env.NODE_ENV !== 'production') {
378
128
  // This checked should run only in development mode
@@ -386,7 +136,7 @@ export function usePositioning(options = {}) {
386
136
  acceptNode: hasAutofocusFilter
387
137
  });
388
138
 
389
- while (treeWalker === null || treeWalker === void 0 ? void 0 : treeWalker.nextNode()) {
139
+ while (treeWalker.nextNode()) {
390
140
  const node = treeWalker.currentNode; // eslint-disable-next-line no-console
391
141
 
392
142
  console.warn('<Popper>:', node); // eslint-disable-next-line no-console
@@ -406,4 +156,152 @@ export function usePositioning(options = {}) {
406
156
  arrowRef
407
157
  };
408
158
  }
159
+
160
+ function usePositioningOptions(options) {
161
+ const {
162
+ align,
163
+ arrowPadding,
164
+ autoSize,
165
+ coverTarget,
166
+ flipBoundary,
167
+ offset,
168
+ overflowBoundary,
169
+ pinned,
170
+ position,
171
+ unstable_disableTether: disableTether,
172
+ positionFixed
173
+ } = options;
174
+ const {
175
+ dir
176
+ } = useFluent();
177
+ const isRtl = dir === 'rtl';
178
+ const strategy = positionFixed ? 'fixed' : 'absolute';
179
+ return React.useCallback((target, container, arrow) => {
180
+ const hasScrollableElement = hasScrollParent(container);
181
+ const placement = toFloatingUIPlacement(align, position, isRtl);
182
+ const middleware = [offset && offsetMiddleware(offset), coverTarget && coverTargetMiddleware(), !pinned && flipMiddleware({
183
+ container,
184
+ flipBoundary,
185
+ hasScrollableElement
186
+ }), shiftMiddleware({
187
+ container,
188
+ hasScrollableElement,
189
+ overflowBoundary,
190
+ disableTether
191
+ }), autoSize && maxSizeMiddleware(autoSize), intersectingMiddleware(), arrow && arrowMiddleware({
192
+ element: arrow,
193
+ padding: arrowPadding
194
+ }), hideMiddleware({
195
+ strategy: 'referenceHidden'
196
+ }), hideMiddleware({
197
+ strategy: 'escaped'
198
+ })].filter(Boolean);
199
+ return {
200
+ placement,
201
+ middleware,
202
+ strategy
203
+ };
204
+ }, [align, arrowPadding, autoSize, coverTarget, disableTether, flipBoundary, isRtl, offset, overflowBoundary, pinned, position, strategy]);
205
+ }
206
+
207
+ function useContainerRef(updatePosition, enabled) {
208
+ return useCallbackRef(null, (container, prevContainer) => {
209
+ if (container && enabled) {
210
+ // When the container is first resolved, set position `fixed` to avoid scroll jumps.
211
+ // Without this scroll jumps can occur when the element is rendered initially and receives focus
212
+ Object.assign(container.style, {
213
+ position: 'fixed',
214
+ left: 0,
215
+ top: 0,
216
+ margin: 0
217
+ });
218
+ }
219
+
220
+ toggleScrollListener(container, prevContainer, updatePosition);
221
+ updatePosition();
222
+ });
223
+ }
224
+
225
+ function useTargetRef(updatePosition) {
226
+ return useCallbackRef(null, (target, prevTarget) => {
227
+ toggleScrollListener(target, prevTarget, updatePosition);
228
+ updatePosition();
229
+ });
230
+ }
231
+
232
+ function useArrowRef(updatePosition) {
233
+ return useCallbackRef(null, updatePosition);
234
+ }
235
+ /**
236
+ * Writes all DOM element updates after position is computed
237
+ */
238
+
239
+
240
+ function writeContainerUpdates(options) {
241
+ var _a, _b;
242
+
243
+ const {
244
+ container,
245
+ placement,
246
+ middlewareData,
247
+ strategy,
248
+ lowPPI,
249
+ coordinates: {
250
+ x,
251
+ y
252
+ }
253
+ } = options;
254
+
255
+ if (!container) {
256
+ return;
257
+ }
258
+
259
+ container.setAttribute(DATA_POSITIONING_PLACEMENT, placement);
260
+ container.removeAttribute(DATA_POSITIONING_INTERSECTING);
261
+
262
+ if (middlewareData.intersectionObserver.intersecting) {
263
+ container.setAttribute(DATA_POSITIONING_INTERSECTING, '');
264
+ }
265
+
266
+ container.removeAttribute(DATA_POSITIONING_ESCAPED);
267
+
268
+ if ((_a = middlewareData.hide) === null || _a === void 0 ? void 0 : _a.escaped) {
269
+ container.setAttribute(DATA_POSITIONING_ESCAPED, '');
270
+ }
271
+
272
+ container.removeAttribute(DATA_POSITIONING_HIDDEN);
273
+
274
+ if ((_b = middlewareData.hide) === null || _b === void 0 ? void 0 : _b.referenceHidden) {
275
+ container.setAttribute(DATA_POSITIONING_HIDDEN, '');
276
+ }
277
+
278
+ Object.assign(container.style, {
279
+ transform: lowPPI ? `translate(${x}px, ${y}px)` : `translate3d(${x}px, ${y}px, 0)`,
280
+ position: strategy
281
+ });
282
+ }
283
+ /**
284
+ * Writes all DOM element updates after position is computed
285
+ */
286
+
287
+
288
+ function writeArrowUpdates(options) {
289
+ const {
290
+ arrow,
291
+ middlewareData
292
+ } = options;
293
+
294
+ if (!middlewareData.arrow || !arrow) {
295
+ return;
296
+ }
297
+
298
+ const {
299
+ x: arrowX,
300
+ y: arrowY
301
+ } = middlewareData.arrow;
302
+ Object.assign(arrow.style, {
303
+ left: `${arrowX}px`,
304
+ top: `${arrowY}px`
305
+ });
306
+ }
409
307
  //# sourceMappingURL=usePositioning.js.map