@syncfusion/react-popups
Version:
Syncfusion React popup package is a feature-rich collection of UI components such as Dialog and Tooltip, used to display contextual information or messages in separate pop-ups.
389 lines (388 loc) • 14.7 kB
JavaScript
import { calculatePosition } from './position';
let parentDocument;
let targetContainer;
export const fit = (element, viewPortElement = null, axis = { X: false, Y: false }, position) => {
if (!axis.Y && !axis.X) {
return { left: position?.left, top: position?.top };
}
const elemData = element.getBoundingClientRect();
targetContainer = viewPortElement;
parentDocument = element.ownerDocument;
if (!position) {
position = calculatePosition(element, 'left', 'top');
}
if (axis.X) {
const containerWidth = targetContainer ? getTargetContainerWidth() : getViewPortWidth();
const containerLeft = ContainerLeft();
const containerRight = ContainerRight();
const overLeft = containerLeft - position.left;
const overRight = position.left + elemData.width - containerRight;
if (elemData.width > containerWidth) {
if (overLeft > 0 && overRight <= 0) {
position.left = containerRight - elemData.width;
}
else if (overRight > 0 && overLeft <= 0) {
position.left = containerLeft;
}
else {
position.left = overLeft > overRight ? (containerRight - elemData.width) : containerLeft;
}
}
else if (overLeft > 0) {
position.left += overLeft;
}
else if (overRight > 0) {
position.left -= overRight;
}
}
if (axis.Y) {
const containerHeight = targetContainer ? getTargetContainerHeight() : getViewPortHeight();
const containerTop = ContainerTop();
const containerBottom = ContainerBottom();
const overTop = containerTop - position.top;
const overBottom = position.top + elemData.height - containerBottom;
if (elemData.height > containerHeight) {
if (overTop > 0 && overBottom <= 0) {
position.top = containerBottom - elemData.height;
}
else if (overBottom > 0 && overTop <= 0) {
position.top = containerTop;
}
else {
position.top = overTop > overBottom ? (containerBottom - elemData.height) : containerTop;
}
}
else if (overTop > 0) {
position.top += overTop;
}
else if (overBottom > 0) {
position.top -= overBottom;
}
}
return position;
};
export const isCollide = (element, viewPortElement = null, x, y) => {
const elemOffset = calculatePosition(element, 'left', 'top');
if (typeof x === 'number') {
elemOffset.left = x;
}
if (typeof y === 'number') {
elemOffset.top = y;
}
const data = [];
targetContainer = viewPortElement;
parentDocument = element.ownerDocument;
const elementRect = getElementReact(element);
const top = elemOffset.top;
const left = elemOffset.left;
const right = elemOffset.left + elementRect.width;
const bottom = elemOffset.top + elementRect.height;
const yAxis = topCollideCheck(top, bottom);
const xAxis = leftCollideCheck(left, right);
if (yAxis.topSide) {
data.push('top');
}
if (xAxis.rightSide) {
data.push('right');
}
if (xAxis.leftSide) {
data.push('left');
}
if (yAxis.bottomSide) {
data.push('bottom');
}
return data;
};
export const flip = (element, target, offsetX, offsetY, positionX, positionY, viewPortElement = null, axis = { X: true, Y: true }) => {
if (!target || !element || !positionX || !positionY || (!axis.X && !axis.Y)) {
return null;
}
const tEdge = { TL: null, TR: null, BL: null, BR: null };
const eEdge = { TL: null, TR: null, BL: null, BR: null };
const elementRect = getElementReact(element);
const pos = {
posX: positionX,
posY: positionY,
offsetX: offsetX,
offsetY: offsetY,
position: { left: 0, top: 0 }
};
targetContainer = viewPortElement;
parentDocument = target.ownerDocument;
updateElementData(target, tEdge, pos);
setPosition(eEdge, pos, elementRect);
if (axis.X) {
leftFlip(target, eEdge, tEdge, pos, elementRect, true);
}
if (axis.Y && tEdge.TL.top > -1) {
topFlip(target, eEdge, tEdge, pos, elementRect, true);
}
const cssPos = setPopup(element, pos);
return cssPos;
};
export const getElementReact = (element) => {
if (!element) {
return null;
}
let elementRect;
if (window.getComputedStyle(element).display === 'none') {
const oldVisibility = element.style.visibility;
const oldDisplay = element.style.display;
element.style.visibility = 'hidden';
element.style.display = 'block';
elementRect = element.getBoundingClientRect();
element.style.display = oldDisplay;
element.style.visibility = oldVisibility;
}
else {
elementRect = element.getBoundingClientRect();
}
return elementRect;
};
const setPopup = (element, pos) => {
let left = 0;
let top = 0;
const offsetParent = element.offsetParent;
if (offsetParent != null &&
(getComputedStyle(offsetParent).position === 'absolute' ||
getComputedStyle(offsetParent).position === 'relative')) {
const data = calculatePosition(offsetParent, 'left', 'top');
left = data.left;
top = data.top;
}
let scaleX = 1;
let scaleY = 1;
const transformElement = getTransformElement(element);
if (transformElement) {
const transformStyle = getComputedStyle(transformElement).transform;
if (transformStyle && transformStyle !== 'none') {
const values = transformStyle.match(/matrix\(([^)]+)\)/);
if (values && values[1]) {
const parts = values[1].split(',').map(parseFloat);
scaleX = parts[0];
scaleY = parts[3];
}
}
const zoomStyle = getComputedStyle(transformElement).zoom;
const zoomVal = parseFloat(zoomStyle);
if (!isNaN(zoomVal) && zoomVal !== 1) {
const bodyZoom = getZoomValue(document.body);
scaleX = bodyZoom * scaleX;
scaleY = bodyZoom * scaleY;
}
}
const topCss = pos.position.top / scaleY + pos.offsetY - top / scaleY;
const leftCss = pos.position.left / scaleX + pos.offsetX - left / scaleX;
element.style.top = topCss + 'px';
element.style.left = leftCss + 'px';
return { left: leftCss, top: topCss };
};
const updateElementData = (target, edge, pos) => {
pos.position = calculatePosition(target, pos.posX, pos.posY);
edge.TL = calculatePosition(target, 'left', 'top');
edge.TR = calculatePosition(target, 'right', 'top');
edge.BR = calculatePosition(target, 'left', 'bottom');
edge.BL = calculatePosition(target, 'right', 'bottom');
};
const setPosition = (eStatus, pos, elementRect) => {
eStatus.TL = { top: pos.position.top + pos.offsetY, left: pos.position.left + pos.offsetX };
eStatus.TR = { top: eStatus.TL.top, left: eStatus.TL.left + elementRect.width };
eStatus.BL = { top: eStatus.TL.top + elementRect.height, left: eStatus.TL.left };
eStatus.BR = { top: eStatus.TL.top + elementRect.height, left: eStatus.TL.left +
elementRect.width };
};
const leftCollideCheck = (left, right) => {
let leftSide = false;
let rightSide = false;
if (left - getBodyScrollLeft() < ContainerLeft()) {
leftSide = true;
}
if (right > ContainerRight()) {
rightSide = true;
}
return { leftSide, rightSide };
};
const leftFlip = (target, edge, tEdge, pos, elementRect, deepCheck) => {
const collideSide = leftCollideCheck(edge.TL.left, edge.TR.left);
if ((tEdge.TL.left - getBodyScrollLeft()) <= ContainerLeft()) {
collideSide.leftSide = false;
}
if (tEdge.TR.left > ContainerRight()) {
collideSide.rightSide = false;
}
if ((collideSide.leftSide && !collideSide.rightSide) || (!collideSide.leftSide && collideSide.rightSide)) {
pos.posX = pos.posX === 'right' ? 'left' : 'right';
pos.offsetX = -1 * (pos.offsetX + elementRect.width);
pos.position = calculatePosition(target, pos.posX, pos.posY);
setPosition(edge, pos, elementRect);
if (deepCheck) {
leftFlip(target, edge, tEdge, pos, elementRect, false);
}
}
};
const topFlip = (target, edge, tEdge, pos, elementRect, deepCheck) => {
const collideSide = topCollideCheck(edge.TL.top, edge.BL.top);
if ((tEdge.TL.top - getBodyScrollTop()) <= ContainerTop()) {
collideSide.topSide = false;
}
if (tEdge.BL.top >= ContainerBottom() && target.getBoundingClientRect().bottom < window.innerHeight) {
collideSide.bottomSide = false;
}
if ((collideSide.topSide && !collideSide.bottomSide) || (!collideSide.topSide && collideSide.bottomSide)) {
pos.posY = pos.posY === 'top' ? 'bottom' : 'top';
pos.offsetY = -1 * (pos.offsetY + elementRect.height);
pos.position = calculatePosition(target, pos.posX, pos.posY);
setPosition(edge, pos, elementRect);
if (deepCheck) {
topFlip(target, edge, tEdge, pos, elementRect, false);
}
}
};
const topCollideCheck = (top, bottom) => {
let topSide = false;
let bottomSide = false;
if (top - getBodyScrollTop() < ContainerTop()) {
topSide = true;
}
if (bottom > ContainerBottom()) {
bottomSide = true;
}
return { topSide, bottomSide };
};
const getTargetContainerWidth = () => {
return targetContainer.getBoundingClientRect().width;
};
const getTargetContainerHeight = () => {
return targetContainer.getBoundingClientRect().height;
};
const getTargetContainerLeft = () => {
return targetContainer.getBoundingClientRect().left;
};
const getTargetContainerTop = () => {
return targetContainer.getBoundingClientRect().top;
};
const ContainerTop = () => {
if (targetContainer) {
return getTargetContainerTop();
}
return 0;
};
const ContainerLeft = () => {
if (targetContainer) {
return getTargetContainerLeft();
}
return 0;
};
const ContainerRight = () => {
if (targetContainer) {
return getBodyScrollLeft() + getTargetContainerLeft() + getTargetContainerWidth();
}
return getBodyScrollLeft() + getViewPortWidth();
};
const ContainerBottom = () => {
if (targetContainer) {
return getBodyScrollTop() + getTargetContainerTop() + getTargetContainerHeight();
}
return getBodyScrollTop() + getViewPortHeight();
};
const getBodyScrollTop = () => {
return parentDocument.documentElement.scrollTop || parentDocument.body.scrollTop || 0;
};
const getBodyScrollLeft = () => {
return parentDocument.documentElement.scrollLeft || parentDocument.body.scrollLeft || 0;
};
const getViewPortHeight = () => {
return window.innerHeight;
};
const getViewPortWidth = () => {
const windowWidth = window.innerWidth;
const documentRect = document.documentElement.getBoundingClientRect();
const offsetWidth = documentRect?.width || 0;
return windowWidth - (windowWidth - offsetWidth);
};
export const getZoomValue = (element) => {
const zoomValue = getComputedStyle(element).zoom;
const parsed = parseFloat(zoomValue);
return parsed || 1;
};
export const getTransformElement = (element) => {
let current = element;
while (current) {
const transform = window.getComputedStyle(current).transform;
const zoomVal = getZoomValue(document.body);
if ((transform && transform !== 'none') || (zoomVal && zoomVal !== 1)) {
return current;
}
if (current === document.body) {
return null;
}
current = (current.offsetParent || current.parentElement);
}
return null;
};
export const getFixedScrollableParent = (element, fixedParent = false) => {
const scrollParents = [];
const overflowRegex = /(auto|scroll)/;
let parent = element.parentElement;
while (parent && parent.tagName !== 'HTML') {
const { position, overflow, overflowY, overflowX } = getComputedStyle(parent);
if (!(getComputedStyle(element).position === 'absolute' && position === 'static')
&& overflowRegex.test(`${overflow} ${overflowY} ${overflowX}`)) {
scrollParents.push(parent);
}
parent = parent.parentElement;
}
if (!fixedParent) {
scrollParents.push(document.documentElement);
}
return scrollParents;
};
export const getZindexPartial = (element) => {
let parent = element.parentElement;
const parentZindex = [];
while (parent) {
if (parent.tagName !== 'BODY') {
const computedStyle = window.getComputedStyle(parent);
const index = computedStyle.zIndex;
const position = computedStyle.position;
if (index !== 'auto' && position !== 'static') {
parentZindex.push(index);
}
parent = parent.parentElement;
}
else {
break;
}
}
const childrenZindex = [];
for (let i = 0; i < document.body.children.length; i++) {
const child = document.body.children[i];
if (!element.isEqualNode(child) && child instanceof HTMLElement) {
const computedStyle = window.getComputedStyle(child);
const index = computedStyle.zIndex;
const position = computedStyle.position;
if (index !== 'auto' && position !== 'static') {
childrenZindex.push(index);
}
}
}
childrenZindex.push('999');
const siblingsZindex = [];
if (element.parentElement && element.parentElement.tagName !== 'BODY') {
const childNodes = Array.from(element.parentElement.children);
for (let i = 0; i < childNodes.length; i++) {
const child = childNodes[i];
if (!element.isEqualNode(child) && child instanceof HTMLElement) {
const computedStyle = window.getComputedStyle(child);
const index = computedStyle.zIndex;
const position = computedStyle.position;
if (index !== 'auto' && position !== 'static') {
siblingsZindex.push(index);
}
}
}
}
const finalValue = parentZindex.concat(childrenZindex, siblingsZindex);
const currentZindexValue = Math.max(...finalValue.map(Number)) + 1;
return currentZindexValue > 2147483647 ? 2147483647 : currentZindexValue;
};