@react-md/utils
Version:
General utils for react-md.
151 lines • 6.5 kB
JavaScript
import { scrollbarSize } from "../layout/scrollbarSize";
import { BELOW_CENTER_ANCHOR } from "./constants";
import { createHorizontalPosition } from "./createHorizontalPosition";
import { createVerticalPosition } from "./createVerticalPosition";
import { findSizingContainer } from "./findSizingContainer";
import { getElementRect } from "./getElementRect";
import { getTransformOrigin } from "./getTransformOrigin";
/**
* This is used when there is no `container` element so that some styles can
* still be created. The main use-case for this is context menus and when the
* `initialX` and `initialY` options have been provided.
*
* @internal
* @remarks \@since 5.0.0
*/
var FALLBACK_DOM_RECT = {
x: 0,
y: 0,
height: 0,
width: 0,
left: 0,
right: 0,
top: 0,
bottom: 0,
toJSON: function () {
// do nothing
},
};
/**
* One of the most complicated functions in this project that will attempt to
* position an element relative to another container element while still being
* visible within the viewport. Below is the logical flow for attempting to fix
* the element to the container:
*
* No Container: If there is no container element, return the provided x and y
* positions and no styles since there's nothing we can use to calculate the
* position.
*
* No Element: If the container was provided but the element to position does
* not exist, return an style object containing the `left` and `top` values for
* the container and apply as many of the positioning options as possible so
* that the styles are "as close as possible" before the fixed element is added
* to the DOM. This will also return the provided x and y positions since
* nothing could be swapped around yet.
*
* Container and Element: If both the container and fixed element were provided,
* apply all the positioning options to the `left` and `top` values of the
* container based on the sizes of both elements.
*
* Now that the `left` and `top` values were applied, check to see if the
* element is fully visible within the viewport with the provided positioning
* options. If it is fully visible, do nothing else. If it isn't... follow the
* next flow:
*
* First, check the horizontal sizes and make sure that the element is still
* within the viewport with the provided view width margin. If it isn't, first
* try to swap only to a `right` style instead of left to see if that fixes it,
* otherwise keep both the `left` and `right` styles.
*/
export function getFixedPosition(options) {
var _a;
var element = options.element, _b = options.anchor, anchor = _b === void 0 ? BELOW_CENTER_ANCHOR : _b, initialX = options.initialX, _c = options.vwMargin, vwMargin = _c === void 0 ? 16 : _c, _d = options.vhMargin, vhMargin = _d === void 0 ? 16 : _d, _e = options.xMargin, xMargin = _e === void 0 ? 0 : _e, _f = options.yMargin, yMargin = _f === void 0 ? 0 : _f, _g = options.width, widthType = _g === void 0 ? "auto" : _g, _h = options.preventOverlap, preventOverlap = _h === void 0 ? false : _h, _j = options.transformOrigin, transformOrigin = _j === void 0 ? false : _j, _k = options.disableSwapping, disableSwapping = _k === void 0 ? false : _k, _l = options.disableVHBounds, disableVHBounds = _l === void 0 ? false : _l;
var initialY = options.initialY;
var container = findSizingContainer(options.container);
if (process.env.NODE_ENV !== "production") {
if (preventOverlap && anchor.y !== "above" && anchor.y !== "below") {
throw new Error('Unable to prevent overlap when the vertical anchor is not `"above"` or `"below"`');
}
}
if (!element) {
return {
actualX: anchor.x,
actualY: anchor.y,
style: {
left: initialX,
top: initialY,
position: disableVHBounds ? "absolute" : "fixed",
transformOrigin: transformOrigin
? getTransformOrigin({ x: anchor.x, y: anchor.y })
: undefined,
},
};
}
var containerRect = (_a = container === null || container === void 0 ? void 0 : container.getBoundingClientRect()) !== null && _a !== void 0 ? _a : FALLBACK_DOM_RECT;
var vh = window.innerHeight;
var vw = window.innerWidth;
var elMinWidth = element.style.minWidth;
// Note: This makes it "min-content" or "min-container-width"
if (widthType === "min") {
element.style.overflow = "visible";
element.style.minWidth = "";
}
var elementRect = getElementRect(element);
var height = elementRect.height;
var elWidth = elementRect.width;
if (widthType === "min") {
elWidth += scrollbarSize();
element.style.overflow = "";
element.style.minWidth = elMinWidth;
}
if (disableVHBounds) {
var dialog = element.closest("[role='dialog']");
if (!dialog) {
initialY = (initialY !== null && initialY !== void 0 ? initialY : 0) + window.scrollY;
}
}
var _m = createHorizontalPosition({
x: anchor.x,
vw: vw,
vwMargin: vwMargin,
xMargin: xMargin,
width: widthType,
elWidth: elWidth,
initialX: initialX,
containerRect: containerRect,
disableSwapping: disableSwapping,
}), left = _m.left, right = _m.right, width = _m.width, minWidth = _m.minWidth, actualX = _m.actualX;
var _o = createVerticalPosition({
y: anchor.y,
vh: vh,
vhMargin: vhMargin,
yMargin: yMargin,
initialY: initialY,
elHeight: height,
containerRect: containerRect,
disableSwapping: disableSwapping,
preventOverlap: preventOverlap,
disableVHBounds: disableVHBounds,
}), top = _o.top, bottom = _o.bottom, actualY = _o.actualY, transformOriginY = _o.transformOriginY;
return {
actualX: actualX,
actualY: actualY,
style: {
left: left,
top: top,
right: right,
bottom: bottom,
width: width,
minWidth: minWidth,
position: disableVHBounds ? "absolute" : "fixed",
transformOrigin: transformOrigin
? getTransformOrigin({
x: actualX,
y: actualY,
transformOriginY: transformOriginY,
})
: undefined,
},
};
}
//# sourceMappingURL=getFixedPosition.js.map