@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
JavaScript
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