140 lines
5.8 KiB
JavaScript
140 lines
5.8 KiB
JavaScript
|
"use strict";
|
||
|
Object.defineProperty(exports, "__esModule", {
|
||
|
value: true
|
||
|
});
|
||
|
Object.defineProperty(exports, "createPositionManager", {
|
||
|
enumerable: true,
|
||
|
get: function() {
|
||
|
return createPositionManager;
|
||
|
}
|
||
|
});
|
||
|
const _dom = require("@floating-ui/dom");
|
||
|
const _reactutilities = require("@fluentui/react-utilities");
|
||
|
const _utils = require("./utils");
|
||
|
const _listScrollParents = require("./utils/listScrollParents");
|
||
|
const _constants = require("./constants");
|
||
|
const _createResizeObserver = require("./utils/createResizeObserver");
|
||
|
function createPositionManager(options) {
|
||
|
let isDestroyed = false;
|
||
|
const { container, target, arrow, strategy, middleware, placement, useTransform = true, disableUpdateOnResize = false } = options;
|
||
|
const targetWindow = container.ownerDocument.defaultView;
|
||
|
if (!target || !container || !targetWindow) {
|
||
|
return {
|
||
|
updatePosition: ()=>undefined,
|
||
|
dispose: ()=>undefined
|
||
|
};
|
||
|
}
|
||
|
// When the dimensions of the target or the container change - trigger a position update
|
||
|
const resizeObserver = disableUpdateOnResize ? null : (0, _createResizeObserver.createResizeObserver)(targetWindow, (entries)=>{
|
||
|
// If content rect dimensions to go 0 -> very likely that `display: none` is being used to hide the element
|
||
|
// In this case don't update and let users update imperatively
|
||
|
const shouldUpdateOnResize = entries.every((entry)=>{
|
||
|
return entry.contentRect.width > 0 && entry.contentRect.height > 0;
|
||
|
});
|
||
|
if (shouldUpdateOnResize) {
|
||
|
updatePosition();
|
||
|
}
|
||
|
});
|
||
|
let isFirstUpdate = true;
|
||
|
const scrollParents = new Set();
|
||
|
// When the container is first resolved, set position `fixed` to avoid scroll jumps.
|
||
|
// Without this scroll jumps can occur when the element is rendered initially and receives focus
|
||
|
Object.assign(container.style, {
|
||
|
position: 'fixed',
|
||
|
left: 0,
|
||
|
top: 0,
|
||
|
margin: 0
|
||
|
});
|
||
|
const forceUpdate = ()=>{
|
||
|
// debounced update can still occur afterwards
|
||
|
// early return to avoid memory leaks
|
||
|
if (isDestroyed) {
|
||
|
return;
|
||
|
}
|
||
|
if (isFirstUpdate) {
|
||
|
(0, _listScrollParents.listScrollParents)(container).forEach((scrollParent)=>scrollParents.add(scrollParent));
|
||
|
if ((0, _reactutilities.isHTMLElement)(target)) {
|
||
|
(0, _listScrollParents.listScrollParents)(target).forEach((scrollParent)=>scrollParents.add(scrollParent));
|
||
|
}
|
||
|
scrollParents.forEach((scrollParent)=>{
|
||
|
scrollParent.addEventListener('scroll', updatePosition, {
|
||
|
passive: true
|
||
|
});
|
||
|
});
|
||
|
resizeObserver === null || resizeObserver === void 0 ? void 0 : resizeObserver.observe(container);
|
||
|
if ((0, _reactutilities.isHTMLElement)(target)) {
|
||
|
resizeObserver === null || resizeObserver === void 0 ? void 0 : resizeObserver.observe(target);
|
||
|
}
|
||
|
isFirstUpdate = false;
|
||
|
}
|
||
|
Object.assign(container.style, {
|
||
|
position: strategy
|
||
|
});
|
||
|
(0, _dom.computePosition)(target, container, {
|
||
|
placement,
|
||
|
middleware,
|
||
|
strategy
|
||
|
}).then(({ x, y, middlewareData, placement: computedPlacement })=>{
|
||
|
// Promise can still resolve after destruction
|
||
|
// early return to avoid applying outdated position
|
||
|
if (isDestroyed) {
|
||
|
return;
|
||
|
}
|
||
|
(0, _utils.writeArrowUpdates)({
|
||
|
arrow,
|
||
|
middlewareData
|
||
|
});
|
||
|
(0, _utils.writeContainerUpdates)({
|
||
|
container,
|
||
|
middlewareData,
|
||
|
placement: computedPlacement,
|
||
|
coordinates: {
|
||
|
x,
|
||
|
y
|
||
|
},
|
||
|
lowPPI: ((targetWindow === null || targetWindow === void 0 ? void 0 : targetWindow.devicePixelRatio) || 1) <= 1,
|
||
|
strategy,
|
||
|
useTransform
|
||
|
});
|
||
|
container.dispatchEvent(new CustomEvent(_constants.POSITIONING_END_EVENT));
|
||
|
}).catch((err)=>{
|
||
|
// https://github.com/floating-ui/floating-ui/issues/1845
|
||
|
// FIXME for node > 14
|
||
|
// node 15 introduces promise rejection which means that any components
|
||
|
// tests need to be `it('', async () => {})` otherwise there can be race conditions with
|
||
|
// JSDOM being torn down before this promise is resolved so globals like `window` and `document` don't exist
|
||
|
// Unless all tests that ever use `usePositioning` are turned into async tests, any logging during testing
|
||
|
// will actually be counter productive
|
||
|
if (process.env.NODE_ENV === 'development') {
|
||
|
// eslint-disable-next-line no-console
|
||
|
console.error('[usePositioning]: Failed to calculate position', err);
|
||
|
}
|
||
|
});
|
||
|
};
|
||
|
const updatePosition = (0, _utils.debounce)(()=>forceUpdate());
|
||
|
const dispose = ()=>{
|
||
|
isDestroyed = true;
|
||
|
if (targetWindow) {
|
||
|
targetWindow.removeEventListener('scroll', updatePosition);
|
||
|
targetWindow.removeEventListener('resize', updatePosition);
|
||
|
}
|
||
|
scrollParents.forEach((scrollParent)=>{
|
||
|
scrollParent.removeEventListener('scroll', updatePosition);
|
||
|
});
|
||
|
scrollParents.clear();
|
||
|
resizeObserver === null || resizeObserver === void 0 ? void 0 : resizeObserver.disconnect();
|
||
|
};
|
||
|
if (targetWindow) {
|
||
|
targetWindow.addEventListener('scroll', updatePosition, {
|
||
|
passive: true
|
||
|
});
|
||
|
targetWindow.addEventListener('resize', updatePosition);
|
||
|
}
|
||
|
// Update the position on initialization
|
||
|
updatePosition();
|
||
|
return {
|
||
|
updatePosition,
|
||
|
dispose
|
||
|
};
|
||
|
}
|