UNPKG

@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
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; };