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