UNPKG

@syncfusion/ej2-popups

Version:

A package of Essential JS 2 popup components such as Dialog and Tooltip that is used to display information or messages in separate pop-ups.

1,386 lines (1,383 loc) 250 kB
import { isNullOrUndefined, ChildProperty, Property, Component, setStyleAttribute, formatUnit, Browser, EventHandler, removeClass, addClass, Animation as Animation$1, Complex, Event, NotifyPropertyChanges, createElement, detach, L10n, attributes, Draggable, isBlazor, compile, append, SanitizeHtmlHelper, extend, prepend, animationMode, Collection, select, selectAll, remove, closest, getUniqueID, Touch, classList } from '@syncfusion/ej2-base'; import { Button } from '@syncfusion/ej2-buttons'; /** * Position library */ let elementRect; let popupRect; let element; let parentDocument; let fixedParent = false; /** * * @param {HTMLElement} anchor - specifies the element * @param {HTMLElement} element - specifies the element * @returns {OffsetPosition} - returns the value */ function calculateRelativeBasedPosition(anchor, element) { let fixedElement = false; const anchorPos = { left: 0, top: 0 }; const tempAnchor = anchor; if (!anchor || !element) { return anchorPos; } if (isNullOrUndefined(element.offsetParent) && element.style.position === 'fixed') { fixedElement = true; } while ((element.offsetParent || fixedElement) && anchor && element.offsetParent !== anchor) { anchorPos.left += anchor.offsetLeft; anchorPos.top += anchor.offsetTop; anchor = anchor.offsetParent; } anchor = tempAnchor; while ((element.offsetParent || fixedElement) && anchor && element.offsetParent !== anchor) { anchorPos.left -= anchor.scrollLeft; anchorPos.top -= anchor.scrollTop; anchor = anchor.parentElement; } return anchorPos; } /** * * @param {Element} currentElement - specifies the element * @param {string} positionX - specifies the position * @param {string} positionY - specifies the position * @param {boolean} parentElement - specifies the boolean * @param {ClientRect} targetValues - specifies the client * @returns {OffsetPosition} - returns the position */ function calculatePosition(currentElement, positionX, positionY, parentElement, targetValues) { popupRect = undefined; popupRect = targetValues; fixedParent = parentElement ? true : false; if (!currentElement) { return { left: 0, top: 0 }; } if (!positionX) { positionX = 'left'; } if (!positionY) { positionY = 'top'; } parentDocument = currentElement.ownerDocument; element = currentElement; const pos = { left: 0, top: 0 }; return updatePosition(positionX.toLowerCase(), positionY.toLowerCase(), pos); } /** * * @param {number} value - specifies the number * @param {OffsetPosition} pos - specifies the position * @returns {void} */ function setPosx(value, pos) { pos.left = value; } /** * * @param {number} value - specifies the number * @param {OffsetPosition} pos - specifies the position * @returns {void} */ function setPosy(value, pos) { pos.top = value; } /** * * @param {string} posX - specifies the position * @param {string} posY - specifies the position * @param {OffsetPosition} pos - specifies the position * @returns {OffsetPosition} - returns the postion */ function updatePosition(posX, posY, pos) { elementRect = element.getBoundingClientRect(); switch (posY + posX) { case 'topcenter': setPosx(getElementHCenter(), pos); setPosy(getElementTop(), pos); break; case 'topright': setPosx(getElementRight(), pos); setPosy(getElementTop(), pos); break; case 'centercenter': setPosx(getElementHCenter(), pos); setPosy(getElementVCenter(), pos); break; case 'centerright': setPosx(getElementRight(), pos); setPosy(getElementVCenter(), pos); break; case 'centerleft': setPosx(getElementLeft(), pos); setPosy(getElementVCenter(), pos); break; case 'bottomcenter': setPosx(getElementHCenter(), pos); setPosy(getElementBottom(), pos); break; case 'bottomright': setPosx(getElementRight(), pos); setPosy(getElementBottom(), pos); break; case 'bottomleft': setPosx(getElementLeft(), pos); setPosy(getElementBottom(), pos); break; default: case 'topleft': setPosx(getElementLeft(), pos); setPosy(getElementTop(), pos); break; } element = null; return pos; } /** * @returns {number} - specifies the number value */ function getBodyScrollTop() { return parentDocument.documentElement.scrollTop || parentDocument.body.scrollTop; } /** * @returns {number} - specifies the number value */ function getBodyScrollLeft() { return parentDocument.documentElement.scrollLeft || parentDocument.body.scrollLeft; } /** * @returns {number} - specifies the number value */ function getElementBottom() { return fixedParent ? elementRect.bottom : elementRect.bottom + getBodyScrollTop(); } /** * @returns {number} - specifies the number value */ function getElementVCenter() { return getElementTop() + (elementRect.height / 2); } /** * @returns {number} - specifies the number value */ function getElementTop() { return fixedParent ? elementRect.top : elementRect.top + getBodyScrollTop(); } /** * @returns {number} - specifies the number value */ function getElementLeft() { return elementRect.left + getBodyScrollLeft(); } /** * @returns {number} - specifies the number value */ function getElementRight() { let popupWidth = (element && (((element.classList.contains('e-date-wrapper') || element.classList.contains('e-datetime-wrapper')) && element.classList.contains('e-rtl')) || (element.classList.contains('e-ddl') && element.classList.contains('e-rtl')) || element.classList.contains('e-date-range-wrapper'))) ? (popupRect ? popupRect.width : 0) : (popupRect && (elementRect.width >= popupRect.width) ? popupRect.width : 0); if (element && element.classList.contains('e-rtl') && element.classList.contains('e-multiselect')) { popupWidth = popupRect.width; } return elementRect.right + getBodyScrollLeft() - popupWidth; } /** * @returns {number} - specifies the number value */ function getElementHCenter() { return getElementLeft() + (elementRect.width / 2); } /** * Collision module. */ let parentDocument$1; let targetContainer; /** * * @param {HTMLElement} element - specifies the element * @param {HTMLElement} viewPortElement - specifies the element * @param {CollisionCoordinates} axis - specifies the collision coordinates * @param {OffsetPosition} position - specifies the position * @returns {void} */ function fit(element, viewPortElement = null, axis = { X: false, Y: false }, position) { if (!axis.Y && !axis.X) { return { left: 0, top: 0 }; } const elemData = element.getBoundingClientRect(); targetContainer = viewPortElement; parentDocument$1 = 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; } /** * * @param {HTMLElement} element - specifies the html element * @param {HTMLElement} viewPortElement - specifies the html element * @param {number} x - specifies the number * @param {number} y - specifies the number * @returns {string[]} - returns the string value */ function isCollide(element, viewPortElement = null, x, y) { const elemOffset = calculatePosition(element, 'left', 'top'); if (x) { elemOffset.left = x; } if (y) { elemOffset.top = y; } const data = []; targetContainer = viewPortElement; parentDocument$1 = element.ownerDocument; const elementRect = element.getBoundingClientRect(); 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; } /** * * @param {HTMLElement} element - specifies the element * @param {HTMLElement} target - specifies the element * @param {number} offsetX - specifies the number * @param {number} offsetY - specifies the number * @param {string} positionX - specifies the string value * @param {string} positionY - specifies the string value * @param {HTMLElement} viewPortElement - specifies the element * @param {CollisionCoordinates} axis - specifies the collision axis * @param {boolean} fixedParent - specifies the boolean * @returns {void} */ function flip(element, target, offsetX, offsetY, positionX, positionY, viewPortElement = null, /* eslint-disable */ axis = { X: true, Y: true }, fixedParent) { if (!target || !element || !positionX || !positionY || (!axis.X && !axis.Y)) { return; } const tEdge = { TL: null, TR: null, BL: null, BR: null }, eEdge = { TL: null, TR: null, BL: null, BR: null /* eslint-enable */ }; let elementRect; if (window.getComputedStyle(element).display === 'none') { const oldVisibility = element.style.visibility; element.style.visibility = 'hidden'; element.style.display = 'block'; elementRect = element.getBoundingClientRect(); element.style.removeProperty('display'); element.style.visibility = oldVisibility; } else { elementRect = element.getBoundingClientRect(); } const pos = { posX: positionX, posY: positionY, offsetX: offsetX, offsetY: offsetY, position: { left: 0, top: 0 } }; targetContainer = viewPortElement; parentDocument$1 = target.ownerDocument; updateElementData(target, tEdge, pos, fixedParent, elementRect); 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); } setPopup(element, pos, elementRect); } /** * * @param {HTMLElement} element - specifies the element * @param {PositionLocation} pos - specifies the location * @param {ClientRect} elementRect - specifies the client rect * @returns {void} */ function setPopup(element, pos, elementRect) { let left = 0; let top = 0; if (element.offsetParent != null && (getComputedStyle(element.offsetParent).position === 'absolute' || getComputedStyle(element.offsetParent).position === 'relative')) { const data = calculatePosition(element.offsetParent, 'left', 'top', false, elementRect); left = data.left; top = data.top; } let scaleX = 1; let scaleY = 1; const tranformElement = getTransformElement(element); if (tranformElement) { const transformStyle = getComputedStyle(tranformElement).transform; if (transformStyle !== 'none') { const matrix = new DOMMatrix(transformStyle); scaleX = matrix.a; scaleY = matrix.d; } const zoomStyle = getComputedStyle(tranformElement).zoom; if (zoomStyle !== 'none') { const bodyZoom = getZoomValue(document.body); scaleX = bodyZoom * scaleX; scaleY = bodyZoom * scaleY; } } element.style.top = ((pos.position.top / scaleY) + pos.offsetY - (top / scaleY)) + 'px'; element.style.left = ((pos.position.left / scaleX) + pos.offsetX - (left / scaleX)) + 'px'; } function getZoomValue(element) { const zoomValue = getComputedStyle(element).zoom; return parseFloat(zoomValue) || 1; // Default zoom value is 1 (no zoom) } /** * * @param {HTMLElement} element - specifies the element * @returns {HTMLElement} The modified element. */ function getTransformElement(element) { while (element) { const transform = window.getComputedStyle(element).transform; const zoom = getZoomValue(document.body); if ((transform && transform !== 'none') || (zoom && zoom !== 1)) { return element; } if (element === document.body) { return null; } element = (element.offsetParent || element.parentElement); } return null; } /** * * @param {HTMLElement} target - specifies the element * @param {EdgeOffset} edge - specifies the offset * @param {PositionLocation} pos - specifies theloaction * @param {boolean} fixedParent - specifies the boolean * @param {ClientRect} elementRect - specifies the client rect * @returns {void} */ function updateElementData(target, edge, pos, fixedParent, elementRect) { pos.position = calculatePosition(target, pos.posX, pos.posY, fixedParent, elementRect); edge.TL = calculatePosition(target, 'left', 'top', fixedParent, elementRect); edge.TR = calculatePosition(target, 'right', 'top', fixedParent, elementRect); edge.BR = calculatePosition(target, 'left', 'bottom', fixedParent, elementRect); edge.BL = calculatePosition(target, 'right', 'bottom', fixedParent, elementRect); } /** * * @param {EdgeOffset} eStatus - specifies the status * @param {PositionLocation} pos - specifies the location * @param {ClientRect} elementRect - specifies the client * @returns {void} */ function 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 }; } /** * * @param {number} left - specifies the number * @param {number} right - specifies the number * @returns {LeftCorners} - returns the value */ function leftCollideCheck(left, right) { //eslint-disable-next-line let leftSide = false, rightSide = false; if (((left - getBodyScrollLeft$1()) < ContainerLeft())) { leftSide = true; } if (right > ContainerRight()) { rightSide = true; } return { leftSide: leftSide, rightSide: rightSide }; } /** * * @param {HTMLElement} target - specifies the element * @param {EdgeOffset} edge - specifes the element * @param {EdgeOffset} tEdge - specifies the edge offset * @param {PositionLocation} pos - specifes the location * @param {ClientRect} elementRect - specifies the client * @param {boolean} deepCheck - specifies the boolean value * @returns {void} */ function leftFlip(target, edge, tEdge, pos, elementRect, deepCheck) { const collideSide = leftCollideCheck(edge.TL.left, edge.TR.left); if ((tEdge.TL.left - getBodyScrollLeft$1()) <= ContainerLeft()) { collideSide.leftSide = false; } if (tEdge.TR.left > ContainerRight()) { collideSide.rightSide = false; } if ((collideSide.leftSide && !collideSide.rightSide) || (!collideSide.leftSide && collideSide.rightSide)) { if (pos.posX === 'right') { pos.posX = 'left'; } else { pos.posX = 'right'; } pos.offsetX = pos.offsetX + elementRect.width; pos.offsetX = -1 * pos.offsetX; pos.position = calculatePosition(target, pos.posX, pos.posY, false); setPosition(edge, pos, elementRect); if (deepCheck) { leftFlip(target, edge, tEdge, pos, elementRect, false); } } } /** * * @param {HTMLElement} target - specifies the element * @param {EdgeOffset} edge - specifies the offset * @param {EdgeOffset} tEdge - specifies the offset * @param {PositionLocation} pos - specifies the location * @param {ClientRect} elementRect - specifies the client rect * @param {boolean} deepCheck - specifies the boolean * @returns {void} */ function topFlip(target, edge, tEdge, pos, elementRect, deepCheck) { const collideSide = topCollideCheck(edge.TL.top, edge.BL.top); if ((tEdge.TL.top - getBodyScrollTop$1()) <= 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)) { if (pos.posY === 'top') { pos.posY = 'bottom'; } else { pos.posY = 'top'; } pos.offsetY = pos.offsetY + elementRect.height; pos.offsetY = -1 * pos.offsetY; pos.position = calculatePosition(target, pos.posX, pos.posY, false, elementRect); setPosition(edge, pos, elementRect); if (deepCheck) { topFlip(target, edge, tEdge, pos, elementRect, false); } } } /** * * @param {number} top - specifies the number * @param {number} bottom - specifies the number * @returns {TopCorners} - retyrns the value */ function topCollideCheck(top, bottom) { //eslint-disable-next-line let topSide = false, bottomSide = false; if ((top - getBodyScrollTop$1()) < ContainerTop()) { topSide = true; } if (bottom > ContainerBottom()) { bottomSide = true; } return { topSide: topSide, bottomSide: bottomSide }; } /** * @returns {void} */ function getTargetContainerWidth() { return targetContainer.getBoundingClientRect().width; } /** * @returns {void} */ function getTargetContainerHeight() { return targetContainer.getBoundingClientRect().height; } /** * @returns {void} */ function getTargetContainerLeft() { return targetContainer.getBoundingClientRect().left; } /** * @returns {void} */ function getTargetContainerTop() { return targetContainer.getBoundingClientRect().top; } //eslint-disable-next-line function ContainerTop() { if (targetContainer) { return getTargetContainerTop(); } return 0; } //eslint-disable-next-line function ContainerLeft() { if (targetContainer) { return getTargetContainerLeft(); } return 0; } //eslint-disable-next-line function ContainerRight() { if (targetContainer) { return (getBodyScrollLeft$1() + getTargetContainerLeft() + getTargetContainerWidth()); } return (getBodyScrollLeft$1() + getViewPortWidth()); } //eslint-disable-next-line function ContainerBottom() { if (targetContainer) { return (getBodyScrollTop$1() + getTargetContainerTop() + getTargetContainerHeight()); } return (getBodyScrollTop$1() + getViewPortHeight()); } /** * @returns {number} - returns the scroll top value */ function getBodyScrollTop$1() { // if(targetContainer) // return targetContainer.scrollTop; return parentDocument$1.documentElement.scrollTop || parentDocument$1.body.scrollTop; } /** * @returns {number} - returns the scroll left value */ function getBodyScrollLeft$1() { // if(targetContainer) // return targetContainer.scrollLeft; return parentDocument$1.documentElement.scrollLeft || parentDocument$1.body.scrollLeft; } /** * @returns {number} - returns the viewport height */ function getViewPortHeight() { return window.innerHeight; } /** * @returns {number} - returns the viewport width */ function getViewPortWidth() { const windowWidth = window.innerWidth; const documentReact = document.documentElement.getBoundingClientRect(); const offsetWidth = (isNullOrUndefined(document.documentElement)) ? 0 : documentReact.width; return windowWidth - (windowWidth - offsetWidth); } /** * @returns {void} */ function destroy() { targetContainer = null; parentDocument$1 = null; } var __decorate = (undefined && undefined.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; /** * Specifies the offset position values. */ class PositionData extends ChildProperty { } __decorate([ Property('left') ], PositionData.prototype, "X", void 0); __decorate([ Property('top') ], PositionData.prototype, "Y", void 0); // don't use space in classNames const CLASSNAMES = { ROOT: 'e-popup', RTL: 'e-rtl', OPEN: 'e-popup-open', CLOSE: 'e-popup-close' }; /** * Represents the Popup Component * ```html * <div id="popup" style="position:absolute;height:100px;width:100px;"> * <div style="margin:35px 25px;">Popup Content</div></div> * ``` * ```typescript * <script> * var popupObj = new Popup(); * popupObj.appendTo("#popup"); * </script> * ``` */ let Popup = class Popup extends Component { constructor(element, options) { super(options, element); } /** * Called internally if any of the property value changed. * * @param {PopupModel} newProp - specifies the new property * @param {PopupModel} oldProp - specifies the old property * @private * @returns {void} */ onPropertyChanged(newProp, oldProp) { for (const prop of Object.keys(newProp)) { switch (prop) { case 'width': setStyleAttribute(this.element, { 'width': formatUnit(newProp.width) }); break; case 'height': setStyleAttribute(this.element, { 'height': formatUnit(newProp.height) }); break; case 'zIndex': setStyleAttribute(this.element, { 'zIndex': newProp.zIndex }); break; case 'enableRtl': this.setEnableRtl(); break; case 'position': case 'relateTo': this.refreshPosition(); break; case 'offsetX': { const x = newProp.offsetX - oldProp.offsetX; this.element.style.left = (parseInt(this.element.style.left, 10) + (x)).toString() + 'px'; break; } case 'offsetY': { const y = newProp.offsetY - oldProp.offsetY; this.element.style.top = (parseInt(this.element.style.top, 10) + (y)).toString() + 'px'; break; } case 'content': this.setContent(); break; case 'actionOnScroll': if (newProp.actionOnScroll !== 'none') { this.wireScrollEvents(); } else { this.unwireScrollEvents(); } break; } } } /** * gets the Component module name. * * @returns {void} * @private */ getModuleName() { return 'popup'; } /** * To resolve if any collision occurs. * * @returns {void} */ resolveCollision() { this.checkCollision(); } /** * gets the persisted state properties of the Component. * * @returns {void} */ getPersistData() { return this.addOnPersist([]); } /** * To destroy the control. * * @returns {void} */ destroy() { if (this.element.classList.contains('e-popup-open')) { this.unwireEvents(); } this.element.classList.remove(CLASSNAMES.ROOT, CLASSNAMES.RTL, CLASSNAMES.OPEN, CLASSNAMES.CLOSE); this.content = null; this.relateTo = null; destroy(); super.destroy(); } /** * To Initialize the control rendering * * @returns {void} * @private */ render() { this.element.classList.add(CLASSNAMES.ROOT); const styles = {}; if (this.zIndex !== 1000) { styles.zIndex = this.zIndex; } if (this.width !== 'auto') { styles.width = formatUnit(this.width); } if (this.height !== 'auto') { styles.height = formatUnit(this.height); } setStyleAttribute(this.element, styles); this.fixedParent = false; this.setEnableRtl(); this.setContent(); } wireEvents() { if (Browser.isDevice) { EventHandler.add(window, 'orientationchange', this.orientationOnChange, this); } if (this.actionOnScroll !== 'none') { this.wireScrollEvents(); } } wireScrollEvents() { if (this.getRelateToElement()) { for (const parent of this.getScrollableParent(this.getRelateToElement())) { EventHandler.add(parent, 'scroll', this.scrollRefresh, this); } } } unwireEvents() { if (Browser.isDevice) { EventHandler.remove(window, 'orientationchange', this.orientationOnChange); } if (this.actionOnScroll !== 'none') { this.unwireScrollEvents(); } } unwireScrollEvents() { if (this.getRelateToElement()) { for (const parent of this.getScrollableParent(this.getRelateToElement())) { EventHandler.remove(parent, 'scroll', this.scrollRefresh); } } } getRelateToElement() { const relateToElement = this.relateTo === '' || isNullOrUndefined(this.relateTo) ? document.body : this.relateTo; this.setProperties({ relateTo: relateToElement }, true); return ((typeof this.relateTo) === 'string') ? document.querySelector(this.relateTo) : this.relateTo; } scrollRefresh(e) { if (this.actionOnScroll === 'reposition') { if (!isNullOrUndefined(this.element) && !(this.element.offsetParent === e.target || (this.element.offsetParent && this.element.offsetParent.tagName === 'BODY' && e.target.parentElement == null))) { this.refreshPosition(); } } else if (this.actionOnScroll === 'hide') { this.hide(); } if (this.actionOnScroll !== 'none') { if (this.getRelateToElement()) { const targetVisible = this.isElementOnViewport(this.getRelateToElement(), e.target); if (!targetVisible && !this.targetInvisibleStatus) { this.trigger('targetExitViewport'); this.targetInvisibleStatus = true; } else if (targetVisible) { this.targetInvisibleStatus = false; } } } } /** * This method is to get the element visibility on viewport when scroll * the page. This method will returns true even though 1 px of element * part is in visible. * * @param {HTMLElement} relateToElement - specifies the element * @param {HTMLElement} scrollElement - specifies the scroll element * @returns {boolean} - retruns the boolean */ // eslint-disable-next-line isElementOnViewport(relateToElement, scrollElement) { const scrollParents = this.getScrollableParent(relateToElement); for (let parent = 0; parent < scrollParents.length; parent++) { if (this.isElementVisible(relateToElement, scrollParents[parent])) { continue; } else { return false; } } return true; } isElementVisible(relateToElement, scrollElement) { const rect = this.checkGetBoundingClientRect(relateToElement); if (!rect.height || !rect.width) { return false; } if (!isNullOrUndefined(this.checkGetBoundingClientRect(scrollElement))) { const parent = scrollElement.getBoundingClientRect(); return !(rect.bottom < parent.top) && (!(rect.bottom > parent.bottom) && (!(rect.right > parent.right) && !(rect.left < parent.left))); } else { const win = window; const windowView = { top: win.scrollY, left: win.scrollX, right: win.scrollX + win.outerWidth, bottom: win.scrollY + win.outerHeight }; const off = calculatePosition(relateToElement); const ele = { top: off.top, left: off.left, right: off.left + rect.width, bottom: off.top + rect.height }; const elementView = { top: windowView.bottom - ele.top, left: windowView.right - ele.left, bottom: ele.bottom - windowView.top, right: ele.right - windowView.left }; return elementView.top > 0 && elementView.left > 0 && elementView.right > 0 && elementView.bottom > 0; } } /** * Initialize the event handler * * @returns {void} * @private */ preRender() { //There is no event handler } setEnableRtl() { this.reposition(); if (this.enableRtl) { this.element.classList.add(CLASSNAMES.RTL); } else { this.element.classList.remove(CLASSNAMES.RTL); } } setContent() { if (!isNullOrUndefined(this.content)) { this.element.innerHTML = ''; if (typeof (this.content) === 'string') { this.element.textContent = this.content; } else { const relateToElem = this.getRelateToElement(); // eslint-disable-next-line const props = this.content.props; if (!relateToElem.classList.contains('e-dropdown-btn') || isNullOrUndefined(props)) { this.element.appendChild(this.content); } } } } orientationOnChange() { setTimeout(() => { this.refreshPosition(); }, 200); } /** * Based on the `relative` element and `offset` values, `Popup` element position will refreshed. * * @param {HTMLElement} target - The target element. * @param {boolean} collision - Specifies whether to check for collision. * @returns {void} */ refreshPosition(target, collision) { if (!isNullOrUndefined(target)) { this.checkFixedParent(target); } this.reposition(); if (!collision) { this.checkCollision(); } } reposition() { let pos; let position; const relateToElement = this.getRelateToElement(); if (typeof this.position.X === 'number' && typeof this.position.Y === 'number') { pos = { left: this.position.X, top: this.position.Y }; } else if ((typeof this.position.X === 'string' && typeof this.position.Y === 'number') || (typeof this.position.X === 'number' && typeof this.position.Y === 'string')) { let parentDisplay; const display = this.element.style.display; this.element.style.display = 'block'; if (this.element.classList.contains('e-dlg-modal')) { parentDisplay = this.element.parentElement.style.display; this.element.parentElement.style.display = 'block'; } position = this.getAnchorPosition(relateToElement, this.element, this.position, this.offsetX, this.offsetY); if (typeof this.position.X === 'string') { pos = { left: position.left, top: this.position.Y }; } else { pos = { left: this.position.X, top: position.top }; } this.element.style.display = display; if (this.element.classList.contains('e-dlg-modal')) { this.element.parentElement.style.display = parentDisplay; } } else if (relateToElement) { const height = this.element.clientHeight; const display = this.element.style.display; this.element.style.display = 'block'; pos = this.getAnchorPosition(relateToElement, this.element, this.position, this.offsetX, this.offsetY, height); this.element.style.display = display; } else { pos = { left: 0, top: 0 }; } if (!isNullOrUndefined(pos)) { this.element.style.left = pos.left + 'px'; this.element.style.top = pos.top + 'px'; } } checkGetBoundingClientRect(ele) { let eleRect; try { eleRect = ele.getBoundingClientRect(); return eleRect; } catch (error) { return null; } } getAnchorPosition(anchorEle, ele, position, offsetX, offsetY, height = 0) { const eleRect = this.checkGetBoundingClientRect(ele); const anchorRect = this.checkGetBoundingClientRect(anchorEle); if (isNullOrUndefined(eleRect) || isNullOrUndefined(anchorRect)) { return null; } const anchor = anchorEle; let anchorPos = { left: 0, top: 0 }; if (ele.offsetParent && ele.offsetParent.tagName === 'BODY' && anchorEle.tagName === 'BODY') { anchorPos = calculatePosition(anchorEle); } else { if ((ele.classList.contains('e-dlg-modal') && anchor.tagName !== 'BODY')) { ele = ele.parentElement; } anchorPos = calculateRelativeBasedPosition(anchor, ele); } switch (position.X) { default: case 'left': break; case 'center': if ((ele.classList.contains('e-dlg-modal') && anchor.tagName === 'BODY' && this.targetType === 'container')) { anchorPos.left += (window.innerWidth / 2 - eleRect.width / 2); } else if (this.targetType === 'container') { anchorPos.left += (anchorRect.width / 2 - eleRect.width / 2); } else { anchorPos.left += (anchorRect.width / 2); } break; case 'right': if ((ele.classList.contains('e-dlg-modal') && anchor.tagName === 'BODY' && this.targetType === 'container')) { anchorPos.left += (window.innerWidth - eleRect.width); } else if (this.targetType === 'container') { let scaleX = 1; const tranformElement = getTransformElement(ele); if (tranformElement) { const transformStyle = getComputedStyle(tranformElement).transform; if (transformStyle !== 'none') { const matrix = new DOMMatrix(transformStyle); scaleX = matrix.a; } const zoomStyle = getComputedStyle(tranformElement).zoom; if (zoomStyle !== 'none') { const bodyZoom = getZoomValue(document.body); scaleX = bodyZoom * scaleX; } } anchorPos.left += ((anchorRect.width - eleRect.width) / scaleX); } else { anchorPos.left += (anchorRect.width); } break; } switch (position.Y) { default: case 'top': break; case 'center': if ((ele.classList.contains('e-dlg-modal') && anchor.tagName === 'BODY' && this.targetType === 'container')) { anchorPos.top += (window.innerHeight / 2 - eleRect.height / 2); } else if (this.targetType === 'container') { anchorPos.top += (anchorRect.height / 2 - eleRect.height / 2); } else { anchorPos.top += (anchorRect.height / 2); } break; case 'bottom': if ((ele.classList.contains('e-dlg-modal') && anchor.tagName === 'BODY' && this.targetType === 'container')) { anchorPos.top += (window.innerHeight - eleRect.height); } else if (this.targetType === 'container' && !ele.classList.contains('e-dialog')) { anchorPos.top += (anchorRect.height - eleRect.height); } else if (this.targetType === 'container' && ele.classList.contains('e-dialog')) { anchorPos.top += (anchorRect.height - height); } else { anchorPos.top += (anchorRect.height); } break; } anchorPos.left += offsetX; anchorPos.top += offsetY; return anchorPos; } callFlip(param) { const relateToElement = this.getRelateToElement(); flip(this.element, relateToElement, this.offsetX, this.offsetY, this.position.X, this.position.Y, this.viewPortElement, param, this.fixedParent); } callFit(param) { if (isCollide(this.element, this.viewPortElement).length !== 0) { if (isNullOrUndefined(this.viewPortElement)) { const data = fit(this.element, this.viewPortElement, param); if (param.X) { this.element.style.left = data.left + 'px'; } if (param.Y) { this.element.style.top = data.top + 'px'; } } else { const elementRect = this.checkGetBoundingClientRect(this.element); const viewPortRect = this.checkGetBoundingClientRect(this.viewPortElement); if (isNullOrUndefined(elementRect) || isNullOrUndefined(viewPortRect)) { return null; } if (param && param.Y === true) { if (viewPortRect.top > elementRect.top) { this.element.style.top = '0px'; } else if (viewPortRect.bottom < elementRect.bottom) { this.element.style.top = parseInt(this.element.style.top, 10) - (elementRect.bottom - viewPortRect.bottom) + 'px'; } } if (param && param.X === true) { if (viewPortRect.right < elementRect.right) { this.element.style.left = parseInt(this.element.style.left, 10) - (elementRect.right - viewPortRect.right) + 'px'; } else if (viewPortRect.left > elementRect.left) { this.element.style.left = parseInt(this.element.style.left, 10) + (viewPortRect.left - elementRect.left) + 'px'; } } } } } checkCollision() { const horz = this.collision.X; const vert = this.collision.Y; if (horz === 'none' && vert === 'none') { return; } if (horz === 'flip' && vert === 'flip') { this.callFlip({ X: true, Y: true }); } else if (horz === 'fit' && vert === 'fit') { this.callFit({ X: true, Y: true }); } else { if (horz === 'flip') { this.callFlip({ X: true, Y: false }); } else if (vert === 'flip') { this.callFlip({ Y: true, X: false }); } if (horz === 'fit') { this.callFit({ X: true, Y: false }); } else if (vert === 'fit') { this.callFit({ X: false, Y: true }); } } } /** * Shows the popup element from screen. * * @returns {void} * @param {AnimationModel} animationOptions - specifies the model * @param { HTMLElement } relativeElement - To calculate the zIndex value dynamically. */ show(animationOptions, relativeElement) { this.wireEvents(); this.getRelateToElement(); if (this.zIndex === 1000 || !isNullOrUndefined(relativeElement)) { const zIndexElement = (isNullOrUndefined(relativeElement)) ? this.element : relativeElement; this.zIndex = getZindexPartial(zIndexElement); setStyleAttribute(this.element, { 'zIndex': this.zIndex }); } animationOptions = (!isNullOrUndefined(animationOptions) && typeof animationOptions === 'object') ? animationOptions : this.showAnimation; if (this.collision.X !== 'none' || this.collision.Y !== 'none') { removeClass([this.element], CLASSNAMES.CLOSE); addClass([this.element], CLASSNAMES.OPEN); this.checkCollision(); removeClass([this.element], CLASSNAMES.OPEN); addClass([this.element], CLASSNAMES.CLOSE); } if (!isNullOrUndefined(animationOptions)) { animationOptions.begin = () => { if (!this.isDestroyed) { removeClass([this.element], CLASSNAMES.CLOSE); addClass([this.element], CLASSNAMES.OPEN); } }; animationOptions.end = () => { if (!this.isDestroyed) { this.trigger('open'); } }; new Animation$1(animationOptions).animate(this.element); } else { removeClass([this.element], CLASSNAMES.CLOSE); addClass([this.element], CLASSNAMES.OPEN); this.trigger('open'); } } /** * Hides the popup element from screen. * * @param {AnimationModel} animationOptions - To give the animation options. * @returns {void} */ hide(animationOptions) { animationOptions = (!isNullOrUndefined(animationOptions) && typeof animationOptions === 'object') ? animationOptions : this.hideAnimation; if (!isNullOrUndefined(animationOptions)) { animationOptions.end = () => { if (!this.isDestroyed) { removeClass([this.element], CLASSNAMES.OPEN); addClass([this.element], CLASSNAMES.CLOSE); this.trigger('close'); } }; new Animation$1(animationOptions).animate(this.element); } else { removeClass([this.element], CLASSNAMES.OPEN); addClass([this.element], CLASSNAMES.CLOSE); this.trigger('close'); } this.unwireEvents(); } /** * Gets scrollable parent elements for the given element. * * @returns {void} * @param { HTMLElement } element - Specify the element to get the scrollable parents of it. */ getScrollableParent(element) { this.checkFixedParent(element); return getScrollableParent(element, this.fixedParent); } checkFixedParent(element) { let parent = element.parentElement; while (parent && parent.tagName !== 'HTML') { const parentStyle = getComputedStyle(parent); if ((parentStyle.position === 'fixed' || parentStyle.position === 'sticky') && !isNullOrUndefined(this.element) && this.element.offsetParent && this.element.offsetParent.tagName === 'BODY' && getComputedStyle(this.element.offsetParent).overflow !== 'hidden') { this.element.style.top = window.scrollY > parseInt(this.element.style.top, 10) ? formatUnit(window.scrollY - parseInt(this.element.style.top, 10)) : formatUnit(parseInt(this.element.style.top, 10) - window.scrollY); this.element.style.position = 'fixed'; this.fixedParent = true; } parent = parent.parentElement; if (!isNullOrUndefined(this.element) && isNullOrUndefined(this.element.offsetParent) && parentStyle.position === 'fixed' && this.element.style.position === 'fixed') { this.fixedParent = true; } } } }; __decorate([ Property('auto') ], Popup.prototype, "height", void 0); __decorate([ Property('auto') ], Popup.prototype, "width", void 0); __decorate([ Property(null) ], Popup.prototype, "content", void 0); __decorate([ Property('container') ], Popup.prototype, "targetType", void 0); __decorate([ Property(null) ], Popup.prototype, "viewPortElement", void 0); __decorate([ Property({ X: 'none', Y: 'none' }) ], Popup.prototype, "collision", void 0); __decorate([ Property('') ], Popup.prototype, "relateTo", void 0); __decorate([ Complex({}, PositionData) ], Popup.prototype, "position", void 0); __decorate([ Property(0) ], Popup.prototype, "offsetX", void 0); __decorate([ Property(0) ], Popup.prototype, "offsetY", void 0); __decorate([ Property(1000) ], Popup.prototype, "zIndex", void 0); __decorate([ Property(false) ], Popup.prototype, "enableRtl", void 0); __decorate([ Property('reposition') ], Popup.prototype, "actionOnScroll", void 0); __decorate([ Property(null) ], Popup.prototype, "showAnimation", void 0); __decorate([ Property(null) ], Popup.prototype, "hideAnimation", void 0); __decorate([ Event() ], Popup.prototype, "open", void 0); __decorate([ Event() ], Popup.prototype, "close", void 0); __decorate([ Event() ], Popup.prototype, "targetExitViewport", void 0); Popup = __decorate([ NotifyPropertyChanges ], Popup); /** * Gets scrollable parent elements for the given element. * * @param { HTMLElement } element - Specify the element to get the scrollable parents of it. * @param {boolean} fixedParent - specifies the parent element * @private * @returns {void} */ function getScrollableParent(element, fixedParent) { const eleStyle = getComputedStyle(element); const scrollParents = []; const overflowRegex = /(auto|scroll)/; let parent = element.parentElement; while (parent && parent.tagName !== 'HTML') { const parentStyle = getComputedStyle(parent); if (!(eleStyle.position === 'absolute' && parentStyle.position === 'static') && overflowRegex.test(parentStyle.overflow + parentStyle.overflowY + parentStyle.overflowX)) { scrollParents.push(parent); } parent = parent.parentElement; } if (!fixedParent) { scrollParents.push(document); } return scrollParents; } /** * Gets the maximum z-index of the given element. * * @returns {void} * @param { HTMLElement } element - Specify the element to get the maximum z-index of it. * @private */ function getZindexPartial(element) { // upto body traversal let parent = element.parentElement; const parentZindex = []; while (parent) { if (parent.tagName !== 'BODY') { const index = document.defaultView.getComputedStyle(parent, null).getPropertyValue('z-index'); const position = docume