Outlook_Addin_LLM/node_modules/@fluentui/react-positioning/lib/createPositionManager.js

133 lines
5.6 KiB
JavaScript
Raw Normal View History

import { computePosition } from '@floating-ui/dom';
import { isHTMLElement } from '@fluentui/react-utilities';
import { debounce, writeArrowUpdates, writeContainerUpdates } from './utils';
import { listScrollParents } from './utils/listScrollParents';
import { POSITIONING_END_EVENT } from './constants';
import { createResizeObserver } from './utils/createResizeObserver';
/**
* @internal
* @returns manager that handles positioning out of the react lifecycle
*/ export 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 : 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) {
listScrollParents(container).forEach((scrollParent)=>scrollParents.add(scrollParent));
if (isHTMLElement(target)) {
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 (isHTMLElement(target)) {
resizeObserver === null || resizeObserver === void 0 ? void 0 : resizeObserver.observe(target);
}
isFirstUpdate = false;
}
Object.assign(container.style, {
position: strategy
});
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;
}
writeArrowUpdates({
arrow,
middlewareData
});
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(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 = 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
};
}