@syncfusion/ej2-navigations
Version:
A package of Essential JS 2 navigation components such as Tree-view, Tab, Toolbar, Context-menu, and Accordion which is used to navigate from one page to another
1,318 lines (1,314 loc) • 872 kB
JavaScript
import { Component, Browser, getUniqueID, Touch, EventHandler, isNullOrUndefined, selectAll, detach, classList, Property, NotifyPropertyChanges, createElement, removeClass, select, ChildProperty, Collection, Event, Complex, Animation, getValue, setValue, append, closest, addClass, isBlazor, rippleEffect, KeyboardEvents, getInstance, isVisible, SanitizeHtmlHelper, formatUnit, setStyleAttribute, attributes, compile, isRippleEnabled, animationMode, remove, getRandomId, L10n, Draggable, isUndefined, getElement, Droppable, matches, extend, merge, initializeCSPTemplate } from '@syncfusion/ej2-base';
import { ListBase } from '@syncfusion/ej2-lists';
import { getScrollableParent, Popup, getZindexPartial, fit, isCollide, calculatePosition, createSpinner, showSpinner, hideSpinner, Tooltip } from '@syncfusion/ej2-popups';
import { Button, createCheckBox, rippleMouseHandler } from '@syncfusion/ej2-buttons';
import { DataManager, Query } from '@syncfusion/ej2-data';
import { Input } from '@syncfusion/ej2-inputs';
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;
};
const CLS_ROOT = 'e-hscroll';
const CLS_RTL = 'e-rtl';
const CLS_DISABLE = 'e-overlay';
const CLS_HSCROLLBAR = 'e-hscroll-bar';
const CLS_HSCROLLCON = 'e-hscroll-content';
const CLS_NAVARROW = 'e-nav-arrow';
const CLS_NAVRIGHTARROW = 'e-nav-right-arrow';
const CLS_NAVLEFTARROW = 'e-nav-left-arrow';
const CLS_HSCROLLNAV = 'e-scroll-nav';
const CLS_HSCROLLNAVRIGHT = 'e-scroll-right-nav';
const CLS_HSCROLLNAVLEFT = 'e-scroll-left-nav';
const CLS_DEVICE = 'e-scroll-device';
const CLS_OVERLAY = 'e-scroll-overlay';
const CLS_RIGHTOVERLAY = 'e-scroll-right-overlay';
const CLS_LEFTOVERLAY = 'e-scroll-left-overlay';
const OVERLAY_MAXWID = 40;
/**
* HScroll module is introduces horizontal scroller when content exceeds the current viewing area.
* It can be useful for the components like Toolbar, Tab which needs horizontal scrolling alone.
* Hidden content can be view by touch moving or icon click.
* ```html
* <div id="scroll"/>
* <script>
* var scrollObj = new HScroll();
* scrollObj.appendTo("#scroll");
* </script>
* ```
*/
let HScroll = class HScroll extends Component {
/**
* Initializes a new instance of the HScroll class.
*
* @param {HScrollModel} options - Specifies HScroll model properties as options.
* @param {string | HTMLElement} element - Specifies the element for which horizontal scrolling applies.
*/
constructor(options, element) {
super(options, element);
}
/**
* Initialize the event handler
*
* @private
* @returns {void}
*/
preRender() {
this.browser = Browser.info.name;
this.browserCheck = this.browser === 'mozilla';
this.isDevice = Browser.isDevice;
this.customStep = true;
const element = this.element;
this.ieCheck = this.browser === 'edge' || this.browser === 'msie';
this.initialize();
if (element.id === '') {
element.id = getUniqueID('hscroll');
this.uniqueId = true;
}
element.style.display = 'block';
if (this.enableRtl) {
element.classList.add(CLS_RTL);
}
}
/**
* To Initialize the horizontal scroll rendering
*
* @private
* @returns {void}
*/
render() {
this.touchModule = new Touch(this.element, { scroll: this.touchHandler.bind(this), swipe: this.swipeHandler.bind(this) });
EventHandler.add(this.scrollEle, 'scroll', this.scrollHandler, this);
if (!this.isDevice) {
this.createNavIcon(this.element);
}
else {
this.element.classList.add(CLS_DEVICE);
this.createOverlay(this.element);
}
this.setScrollState();
}
setScrollState() {
if (isNullOrUndefined(this.scrollStep) || this.scrollStep < 0) {
this.scrollStep = this.scrollEle.offsetWidth;
this.customStep = false;
}
else {
this.customStep = true;
}
}
initialize() {
const scrollEle = this.createElement('div', { className: CLS_HSCROLLCON });
const scrollDiv = this.createElement('div', { className: CLS_HSCROLLBAR });
scrollDiv.setAttribute('tabindex', '-1');
const ele = this.element;
const innerEle = [].slice.call(ele.children);
for (const ele of innerEle) {
scrollEle.appendChild(ele);
}
scrollDiv.appendChild(scrollEle);
ele.appendChild(scrollDiv);
scrollDiv.style.overflowX = 'hidden';
this.scrollEle = scrollDiv;
this.scrollItems = scrollEle;
}
getPersistData() {
const keyEntity = ['scrollStep'];
return this.addOnPersist(keyEntity);
}
/**
* Returns the current module name.
*
* @returns {string} - It returns the current module name.
* @private
*/
getModuleName() {
return 'hScroll';
}
/**
* Removes the control from the DOM and also removes all its related events.
*
* @returns {void}
*/
destroy() {
const ele = this.element;
ele.style.display = '';
ele.classList.remove(CLS_ROOT);
ele.classList.remove(CLS_DEVICE);
ele.classList.remove(CLS_RTL);
const nav = selectAll('.e-' + ele.id + '_nav.' + CLS_HSCROLLNAV, ele);
const overlay = selectAll('.' + CLS_OVERLAY, ele);
[].slice.call(overlay).forEach((ele) => {
detach(ele);
});
for (const elem of [].slice.call(this.scrollItems.children)) {
ele.appendChild(elem);
}
if (this.uniqueId) {
this.element.removeAttribute('id');
}
detach(this.scrollEle);
if (nav.length > 0) {
detach(nav[0]);
if (!isNullOrUndefined(nav[1])) {
detach(nav[1]);
}
}
EventHandler.remove(this.scrollEle, 'scroll', this.scrollHandler);
this.touchModule.destroy();
this.touchModule = null;
super.destroy();
}
/**
* Specifies the value to disable/enable the HScroll component.
* When set to `true` , the component will be disabled.
*
* @param {boolean} value - Based on this Boolean value, HScroll will be enabled (false) or disabled (true).
* @returns {void}.
*/
disable(value) {
const navEles = selectAll('.e-scroll-nav:not(.' + CLS_DISABLE + ')', this.element);
if (value) {
this.element.classList.add(CLS_DISABLE);
}
else {
this.element.classList.remove(CLS_DISABLE);
}
[].slice.call(navEles).forEach((el) => {
el.setAttribute('tabindex', !value ? '0' : '-1');
});
}
createOverlay(element) {
const id = element.id.concat('_nav');
const rightOverlayEle = this.createElement('div', { className: CLS_OVERLAY + ' ' + CLS_RIGHTOVERLAY });
const clsRight = 'e-' + element.id.concat('_nav ' + CLS_HSCROLLNAV + ' ' + CLS_HSCROLLNAVRIGHT);
const rightEle = this.createElement('div', { id: id.concat('_right'), className: clsRight });
const navItem = this.createElement('div', { className: CLS_NAVRIGHTARROW + ' ' + CLS_NAVARROW + ' e-icons' });
rightEle.appendChild(navItem);
const leftEle = this.createElement('div', { className: CLS_OVERLAY + ' ' + CLS_LEFTOVERLAY });
if (this.ieCheck) {
rightEle.classList.add('e-ie-align');
}
element.appendChild(rightOverlayEle);
element.appendChild(rightEle);
element.insertBefore(leftEle, element.firstChild);
this.eventBinding([rightEle]);
}
createNavIcon(element) {
const id = element.id.concat('_nav');
const clsRight = 'e-' + element.id.concat('_nav ' + CLS_HSCROLLNAV + ' ' + CLS_HSCROLLNAVRIGHT);
const rightAttributes = { 'role': 'button', 'id': id.concat('_right'), 'aria-label': 'Scroll right' };
const nav = this.createElement('div', { className: clsRight, attrs: rightAttributes });
nav.setAttribute('aria-disabled', 'false');
const navItem = this.createElement('div', { className: CLS_NAVRIGHTARROW + ' ' + CLS_NAVARROW + ' e-icons' });
const clsLeft = 'e-' + element.id.concat('_nav ' + CLS_HSCROLLNAV + ' ' + CLS_HSCROLLNAVLEFT);
const leftAttributes = { 'role': 'button', 'id': id.concat('_left'), 'aria-label': 'Scroll left' };
const navEle = this.createElement('div', { className: clsLeft + ' ' + CLS_DISABLE, attrs: leftAttributes });
navEle.setAttribute('aria-disabled', 'true');
const navLeftItem = this.createElement('div', { className: CLS_NAVLEFTARROW + ' ' + CLS_NAVARROW + ' e-icons' });
navEle.appendChild(navLeftItem);
nav.appendChild(navItem);
element.appendChild(nav);
element.insertBefore(navEle, element.firstChild);
if (this.ieCheck) {
nav.classList.add('e-ie-align');
navEle.classList.add('e-ie-align');
}
this.eventBinding([nav, navEle]);
}
onKeyPress(e) {
if (e.key === 'Enter') {
const timeoutFun = () => {
this.keyTimeout = true;
this.eleScrolling(10, e.target, true);
};
this.keyTimer = window.setTimeout(() => {
timeoutFun();
}, 100);
}
}
onKeyUp(e) {
if (e.key !== 'Enter') {
return;
}
if (this.keyTimeout) {
this.keyTimeout = false;
}
else {
e.target.click();
}
clearTimeout(this.keyTimer);
}
eventBinding(ele) {
[].slice.call(ele).forEach((el) => {
new Touch(el, { tapHold: this.tabHoldHandler.bind(this), tapHoldThreshold: 500 });
el.addEventListener('keydown', this.onKeyPress.bind(this));
el.addEventListener('keyup', this.onKeyUp.bind(this));
el.addEventListener('mouseup', this.repeatScroll.bind(this));
el.addEventListener('touchend', this.repeatScroll.bind(this));
el.addEventListener('contextmenu', (e) => {
e.preventDefault();
});
EventHandler.add(el, 'click', this.clickEventHandler, this);
});
}
repeatScroll() {
clearInterval(this.timeout);
}
tabHoldHandler(e) {
let trgt = e.originalEvent.target;
trgt = this.contains(trgt, CLS_HSCROLLNAV) ? trgt.firstElementChild : trgt;
const scrollDis = 10;
const timeoutFun = () => {
this.eleScrolling(scrollDis, trgt, true);
};
this.timeout = window.setInterval(() => {
timeoutFun();
}, 50);
}
contains(ele, className) {
return ele.classList.contains(className);
}
eleScrolling(scrollDis, trgt, isContinuous) {
const rootEle = this.element;
let classList = trgt.classList;
if (classList.contains(CLS_HSCROLLNAV)) {
classList = trgt.querySelector('.' + CLS_NAVARROW).classList;
}
if (this.contains(rootEle, CLS_RTL) && this.browserCheck) {
scrollDis = -scrollDis;
}
if ((!this.contains(rootEle, CLS_RTL) || this.browserCheck) || this.ieCheck) {
if (classList.contains(CLS_NAVRIGHTARROW)) {
this.frameScrollRequest(scrollDis, 'add', isContinuous);
}
else {
this.frameScrollRequest(scrollDis, '', isContinuous);
}
}
else {
if (classList.contains(CLS_NAVLEFTARROW)) {
this.frameScrollRequest(scrollDis, 'add', isContinuous);
}
else {
this.frameScrollRequest(scrollDis, '', isContinuous);
}
}
}
clickEventHandler(e) {
this.eleScrolling(this.scrollStep, e.target, false);
}
swipeHandler(e) {
const swipeEle = this.scrollEle;
let distance;
if (e.velocity <= 1) {
distance = e.distanceX / (e.velocity * 10);
}
else {
distance = e.distanceX / e.velocity;
}
let start = 0.5;
const animate = () => {
const step = Math.sin(start);
if (step <= 0) {
window.cancelAnimationFrame(step);
}
else {
if (e.swipeDirection === 'Left') {
swipeEle.scrollLeft += distance * step;
}
else if (e.swipeDirection === 'Right') {
swipeEle.scrollLeft -= distance * step;
}
start -= 0.5;
window.requestAnimationFrame(animate);
}
};
animate();
}
scrollUpdating(scrollVal, action) {
if (action === 'add') {
this.scrollEle.scrollLeft += scrollVal;
}
else {
this.scrollEle.scrollLeft -= scrollVal;
}
if (this.enableRtl && this.scrollEle.scrollLeft > 0) {
this.scrollEle.scrollLeft = 0;
}
}
frameScrollRequest(scrollVal, action, isContinuous) {
const step = 10;
if (isContinuous) {
this.scrollUpdating(scrollVal, action);
return;
}
if (!this.customStep) {
[].slice.call(selectAll('.' + CLS_OVERLAY, this.element)).forEach((el) => {
scrollVal -= el.offsetWidth;
});
}
const animate = () => {
let scrollValue;
let scrollStep;
if (this.contains(this.element, CLS_RTL) && this.browserCheck) {
scrollValue = -scrollVal;
scrollStep = -step;
}
else {
scrollValue = scrollVal;
scrollStep = step;
}
if (scrollValue < step) {
window.cancelAnimationFrame(scrollStep);
}
else {
this.scrollUpdating(scrollStep, action);
scrollVal -= scrollStep;
window.requestAnimationFrame(animate);
}
};
animate();
}
touchHandler(e) {
const ele = this.scrollEle;
let distance = e.distanceX;
if ((this.ieCheck) && this.contains(this.element, CLS_RTL)) {
distance = -distance;
}
if (e.scrollDirection === 'Left') {
ele.scrollLeft = ele.scrollLeft + distance;
}
else if (e.scrollDirection === 'Right') {
ele.scrollLeft = ele.scrollLeft - distance;
}
}
arrowDisabling(addDisable, removeDisable) {
if (this.isDevice) {
const arrowEle = isNullOrUndefined(addDisable) ? removeDisable : addDisable;
const arrowIcon = arrowEle.querySelector('.' + CLS_NAVARROW);
if (isNullOrUndefined(addDisable)) {
classList(arrowIcon, [CLS_NAVRIGHTARROW], [CLS_NAVLEFTARROW]);
}
else {
classList(arrowIcon, [CLS_NAVLEFTARROW], [CLS_NAVRIGHTARROW]);
}
}
else if (addDisable && removeDisable) {
addDisable.classList.add(CLS_DISABLE);
addDisable.setAttribute('aria-disabled', 'true');
addDisable.removeAttribute('tabindex');
removeDisable.classList.remove(CLS_DISABLE);
removeDisable.setAttribute('aria-disabled', 'false');
removeDisable.setAttribute('tabindex', '0');
}
this.repeatScroll();
}
scrollHandler(e) {
const target = e.target;
const width = target.offsetWidth;
const rootEle = this.element;
const navLeftEle = this.element.querySelector('.' + CLS_HSCROLLNAVLEFT);
const navRightEle = this.element.querySelector('.' + CLS_HSCROLLNAVRIGHT);
let leftOverlay = this.element.querySelector('.' + CLS_LEFTOVERLAY);
let rightOverlay = this.element.querySelector('.' + CLS_RIGHTOVERLAY);
let scrollLeft = target.scrollLeft;
if (scrollLeft <= 0) {
scrollLeft = -scrollLeft;
}
if (this.isDevice) {
if (this.enableRtl && !(this.browserCheck || this.ieCheck)) {
leftOverlay = this.element.querySelector('.' + CLS_RIGHTOVERLAY);
rightOverlay = this.element.querySelector('.' + CLS_LEFTOVERLAY);
}
if (scrollLeft < OVERLAY_MAXWID) {
leftOverlay.style.width = scrollLeft + 'px';
}
else {
leftOverlay.style.width = '40px';
}
if ((target.scrollWidth - Math.ceil(width + scrollLeft)) < OVERLAY_MAXWID) {
rightOverlay.style.width = (target.scrollWidth - Math.ceil(width + scrollLeft)) + 'px';
}
else {
rightOverlay.style.width = '40px';
}
}
if (scrollLeft === 0) {
this.arrowDisabling(navLeftEle, navRightEle);
}
else if (Math.ceil(width + scrollLeft + .1) >= target.scrollWidth) {
this.arrowDisabling(navRightEle, navLeftEle);
}
else {
const disEle = this.element.querySelector('.' + CLS_HSCROLLNAV + '.' + CLS_DISABLE);
if (disEle) {
disEle.classList.remove(CLS_DISABLE);
disEle.setAttribute('aria-disabled', 'false');
disEle.setAttribute('tabindex', '0');
}
}
}
/**
* Gets called when the model property changes.The data that describes the old and new values of property that changed.
*
* @param {HScrollModel} newProp - It contains the new value of data.
* @param {HScrollModel} oldProp - It contains the old value of data.
* @returns {void}
* @private
*/
onPropertyChanged(newProp, oldProp) {
for (const prop of Object.keys(newProp)) {
switch (prop) {
case 'scrollStep':
this.setScrollState();
break;
case 'enableRtl':
newProp.enableRtl ? this.element.classList.add(CLS_RTL) : this.element.classList.remove(CLS_RTL);
break;
}
}
}
};
__decorate([
Property(null)
], HScroll.prototype, "scrollStep", void 0);
HScroll = __decorate([
NotifyPropertyChanges
], HScroll);
var __decorate$1 = (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;
};
const CLS_ROOT$1 = 'e-vscroll';
const CLS_RTL$1 = 'e-rtl';
const CLS_DISABLE$1 = 'e-overlay';
const CLS_VSCROLLBAR = 'e-vscroll-bar';
const CLS_VSCROLLCON = 'e-vscroll-content';
const CLS_NAVARROW$1 = 'e-nav-arrow';
const CLS_NAVUPARROW = 'e-nav-up-arrow';
const CLS_NAVDOWNARROW = 'e-nav-down-arrow';
const CLS_VSCROLLNAV = 'e-scroll-nav';
const CLS_VSCROLLNAVUP = 'e-scroll-up-nav';
const CLS_VSCROLLNAVDOWN = 'e-scroll-down-nav';
const CLS_DEVICE$1 = 'e-scroll-device';
const CLS_OVERLAY$1 = 'e-scroll-overlay';
const CLS_UPOVERLAY = 'e-scroll-up-overlay';
const CLS_DOWNOVERLAY = 'e-scroll-down-overlay';
const OVERLAY_MAXWID$1 = 40;
/**
* VScroll module is introduces vertical scroller when content exceeds the current viewing area.
* It can be useful for the components like Toolbar, Tab which needs vertical scrolling alone.
* Hidden content can be view by touch moving or icon click.
* ```html
* <div id="scroll"/>
* <script>
* var scrollObj = new VScroll();
* scrollObj.appendTo("#scroll");
* </script>
* ```
*/
let VScroll = class VScroll extends Component {
/**
* Initializes a new instance of the VScroll class.
*
* @param {VScrollModel} options - Specifies VScroll model properties as options.
* @param {string | HTMLElement} element - Specifies the element for which vertical scrolling applies.
*/
constructor(options, element) {
super(options, element);
}
/**
* Initialize the event handler
*
* @private
* @returns {void}
*/
preRender() {
this.browser = Browser.info.name;
this.browserCheck = this.browser === 'mozilla';
this.isDevice = Browser.isDevice;
this.customStep = true;
const ele = this.element;
this.ieCheck = this.browser === 'edge' || this.browser === 'msie';
this.initialize();
if (ele.id === '') {
ele.id = getUniqueID('vscroll');
this.uniqueId = true;
}
ele.style.display = 'block';
if (this.enableRtl) {
ele.classList.add(CLS_RTL$1);
}
}
/**
* To Initialize the vertical scroll rendering
*
* @private
* @returns {void}
*/
render() {
this.touchModule = new Touch(this.element, { scroll: this.touchHandler.bind(this), swipe: this.swipeHandler.bind(this) });
EventHandler.add(this.scrollEle, 'scroll', this.scrollEventHandler, this);
if (!this.isDevice) {
this.createNavIcon(this.element);
}
else {
this.element.classList.add(CLS_DEVICE$1);
this.createOverlayElement(this.element);
}
this.setScrollState();
EventHandler.add(this.element, 'wheel', this.wheelEventHandler, this);
}
setScrollState() {
if (isNullOrUndefined(this.scrollStep) || this.scrollStep < 0) {
this.scrollStep = this.scrollEle.offsetHeight;
this.customStep = false;
}
else {
this.customStep = true;
}
}
initialize() {
const scrollCnt = createElement('div', { className: CLS_VSCROLLCON });
const scrollBar = createElement('div', { className: CLS_VSCROLLBAR });
scrollBar.setAttribute('tabindex', '-1');
const ele = this.element;
const innerEle = [].slice.call(ele.children);
for (const ele of innerEle) {
scrollCnt.appendChild(ele);
}
scrollBar.appendChild(scrollCnt);
ele.appendChild(scrollBar);
scrollBar.style.overflow = 'hidden';
this.scrollEle = scrollBar;
this.scrollItems = scrollCnt;
}
getPersistData() {
const keyEntity = ['scrollStep'];
return this.addOnPersist(keyEntity);
}
/**
* Returns the current module name.
*
* @returns {string} - It returns the current module name.
* @private
*/
getModuleName() {
return 'vScroll';
}
/**
* Removes the control from the DOM and also removes all its related events.
*
* @returns {void}
*/
destroy() {
const el = this.element;
el.style.display = '';
removeClass([this.element], [CLS_ROOT$1, CLS_DEVICE$1, CLS_RTL$1]);
const navs = selectAll('.e-' + el.id + '_nav.' + CLS_VSCROLLNAV, el);
const overlays = selectAll('.' + CLS_OVERLAY$1, el);
[].slice.call(overlays).forEach((ele) => {
detach(ele);
});
for (const elem of [].slice.call(this.scrollItems.children)) {
el.appendChild(elem);
}
if (this.uniqueId) {
this.element.removeAttribute('id');
}
detach(this.scrollEle);
if (navs.length > 0) {
detach(navs[0]);
if (!isNullOrUndefined(navs[1])) {
detach(navs[1]);
}
}
EventHandler.remove(this.scrollEle, 'scroll', this.scrollEventHandler);
this.touchModule.destroy();
this.touchModule = null;
super.destroy();
}
/**
* Specifies the value to disable/enable the VScroll component.
* When set to `true` , the component will be disabled.
*
* @param {boolean} value - Based on this Boolean value, VScroll will be enabled (false) or disabled (true).
* @returns {void}.
*/
disable(value) {
const navEle = selectAll('.e-scroll-nav:not(.' + CLS_DISABLE$1 + ')', this.element);
if (value) {
this.element.classList.add(CLS_DISABLE$1);
}
else {
this.element.classList.remove(CLS_DISABLE$1);
}
[].slice.call(navEle).forEach((el) => {
el.setAttribute('tabindex', !value ? '0' : '-1');
});
}
createOverlayElement(element) {
const id = element.id.concat('_nav');
const downOverlayEle = createElement('div', { className: CLS_OVERLAY$1 + ' ' + CLS_DOWNOVERLAY });
const clsDown = 'e-' + element.id.concat('_nav ' + CLS_VSCROLLNAV + ' ' + CLS_VSCROLLNAVDOWN);
const downEle = createElement('div', { id: id.concat('down'), className: clsDown });
const navItem = createElement('div', { className: CLS_NAVDOWNARROW + ' ' + CLS_NAVARROW$1 + ' e-icons' });
downEle.appendChild(navItem);
const upEle = createElement('div', { className: CLS_OVERLAY$1 + ' ' + CLS_UPOVERLAY });
if (this.ieCheck) {
downEle.classList.add('e-ie-align');
}
element.appendChild(downOverlayEle);
element.appendChild(downEle);
element.insertBefore(upEle, element.firstChild);
this.eventBinding([downEle]);
}
createNavIcon(element) {
const id = element.id.concat('_nav');
const clsDown = 'e-' + element.id.concat('_nav ' + CLS_VSCROLLNAV + ' ' + CLS_VSCROLLNAVDOWN);
const nav = createElement('div', { id: id.concat('_down'), className: clsDown });
nav.setAttribute('aria-disabled', 'false');
const navItem = createElement('div', { className: CLS_NAVDOWNARROW + ' ' + CLS_NAVARROW$1 + ' e-icons' });
const clsUp = 'e-' + element.id.concat('_nav ' + CLS_VSCROLLNAV + ' ' + CLS_VSCROLLNAVUP);
const navElement = createElement('div', { id: id.concat('_up'), className: clsUp + ' ' + CLS_DISABLE$1 });
navElement.setAttribute('aria-disabled', 'true');
const navUpItem = createElement('div', { className: CLS_NAVUPARROW + ' ' + CLS_NAVARROW$1 + ' e-icons' });
navElement.appendChild(navUpItem);
nav.appendChild(navItem);
nav.setAttribute('tabindex', '0');
element.appendChild(nav);
element.insertBefore(navElement, element.firstChild);
if (this.ieCheck) {
nav.classList.add('e-ie-align');
navElement.classList.add('e-ie-align');
}
this.eventBinding([nav, navElement]);
}
onKeyPress(ev) {
if (ev.key === 'Enter') {
const timeoutFun = () => {
this.keyTimeout = true;
this.eleScrolling(10, ev.target, true);
};
this.keyTimer = window.setTimeout(() => {
timeoutFun();
}, 100);
}
}
onKeyUp(ev) {
if (ev.key !== 'Enter') {
return;
}
if (this.keyTimeout) {
this.keyTimeout = false;
}
else {
ev.target.click();
}
clearTimeout(this.keyTimer);
}
eventBinding(element) {
[].slice.call(element).forEach((ele) => {
new Touch(ele, { tapHold: this.tabHoldHandler.bind(this), tapHoldThreshold: 500 });
ele.addEventListener('keydown', this.onKeyPress.bind(this));
ele.addEventListener('keyup', this.onKeyUp.bind(this));
ele.addEventListener('mouseup', this.repeatScroll.bind(this));
ele.addEventListener('touchend', this.repeatScroll.bind(this));
ele.addEventListener('contextmenu', (e) => {
e.preventDefault();
});
EventHandler.add(ele, 'click', this.clickEventHandler, this);
});
}
repeatScroll() {
clearInterval(this.timeout);
}
tabHoldHandler(ev) {
let trgt = ev.originalEvent.target;
trgt = this.contains(trgt, CLS_VSCROLLNAV) ? trgt.firstElementChild : trgt;
const scrollDistance = 10;
const timeoutFun = () => {
this.eleScrolling(scrollDistance, trgt, true);
};
this.timeout = window.setInterval(() => {
timeoutFun();
}, 50);
}
contains(element, className) {
return element.classList.contains(className);
}
eleScrolling(scrollDis, trgt, isContinuous) {
let classList = trgt.classList;
if (classList.contains(CLS_VSCROLLNAV)) {
classList = trgt.querySelector('.' + CLS_NAVARROW$1).classList;
}
if (classList.contains(CLS_NAVDOWNARROW)) {
this.frameScrollRequest(scrollDis, 'add', isContinuous);
}
else if (classList.contains(CLS_NAVUPARROW)) {
this.frameScrollRequest(scrollDis, '', isContinuous);
}
}
clickEventHandler(event) {
this.eleScrolling(this.scrollStep, event.target, false);
}
wheelEventHandler(e) {
e.preventDefault();
this.frameScrollRequest(this.scrollStep, (e.deltaY > 0 ? 'add' : ''), false);
}
swipeHandler(e) {
const swipeElement = this.scrollEle;
let distance;
if (e.velocity <= 1) {
distance = e.distanceY / (e.velocity * 10);
}
else {
distance = e.distanceY / e.velocity;
}
let start = 0.5;
const animate = () => {
const step = Math.sin(start);
if (step <= 0) {
window.cancelAnimationFrame(step);
}
else {
if (e.swipeDirection === 'Up') {
swipeElement.scrollTop += distance * step;
}
else if (e.swipeDirection === 'Down') {
swipeElement.scrollTop -= distance * step;
}
start -= 0.02;
window.requestAnimationFrame(animate);
}
};
animate();
}
scrollUpdating(scrollVal, action) {
if (action === 'add') {
this.scrollEle.scrollTop += scrollVal;
}
else {
this.scrollEle.scrollTop -= scrollVal;
}
}
frameScrollRequest(scrollValue, action, isContinuous) {
const step = 10;
if (isContinuous) {
this.scrollUpdating(scrollValue, action);
return;
}
if (!this.customStep) {
[].slice.call(selectAll('.' + CLS_OVERLAY$1, this.element)).forEach((el) => {
scrollValue -= el.offsetHeight;
});
}
const animate = () => {
if (scrollValue < step) {
window.cancelAnimationFrame(step);
}
else {
this.scrollUpdating(step, action);
scrollValue -= step;
window.requestAnimationFrame(animate);
}
};
animate();
}
touchHandler(e) {
const el = this.scrollEle;
const distance = e.distanceY;
if (e.scrollDirection === 'Up') {
el.scrollTop = el.scrollTop + distance;
}
else if (e.scrollDirection === 'Down') {
el.scrollTop = el.scrollTop - distance;
}
}
arrowDisabling(addDisableCls, removeDisableCls) {
if (this.isDevice) {
const arrowEle = isNullOrUndefined(addDisableCls) ? removeDisableCls : addDisableCls;
const arrowIcon = arrowEle.querySelector('.' + CLS_NAVARROW$1);
if (isNullOrUndefined(addDisableCls)) {
classList(arrowIcon, [CLS_NAVDOWNARROW], [CLS_NAVUPARROW]);
}
else {
classList(arrowIcon, [CLS_NAVUPARROW], [CLS_NAVDOWNARROW]);
}
}
else {
addDisableCls.classList.add(CLS_DISABLE$1);
addDisableCls.setAttribute('aria-disabled', 'true');
addDisableCls.removeAttribute('tabindex');
removeDisableCls.classList.remove(CLS_DISABLE$1);
removeDisableCls.setAttribute('aria-disabled', 'false');
removeDisableCls.setAttribute('tabindex', '0');
}
this.repeatScroll();
}
scrollEventHandler(e) {
const target = e.target;
const height = target.offsetHeight;
const navUpEle = this.element.querySelector('.' + CLS_VSCROLLNAVUP);
const navDownEle = this.element.querySelector('.' + CLS_VSCROLLNAVDOWN);
const upOverlay = this.element.querySelector('.' + CLS_UPOVERLAY);
const downOverlay = this.element.querySelector('.' + CLS_DOWNOVERLAY);
let scrollTop = target.scrollTop;
if (scrollTop <= 0) {
scrollTop = -scrollTop;
}
if (this.isDevice) {
if (scrollTop < OVERLAY_MAXWID$1) {
upOverlay.style.height = scrollTop + 'px';
}
else {
upOverlay.style.height = '40px';
}
if ((target.scrollHeight - Math.ceil(height + scrollTop)) < OVERLAY_MAXWID$1) {
downOverlay.style.height = (target.scrollHeight - Math.ceil(height + scrollTop)) + 'px';
}
else {
downOverlay.style.height = '40px';
}
}
if (scrollTop === 0) {
this.arrowDisabling(navUpEle, navDownEle);
}
else if (Math.ceil(height + scrollTop + .1) >= target.scrollHeight) {
this.arrowDisabling(navDownEle, navUpEle);
}
else {
const disEle = this.element.querySelector('.' + CLS_VSCROLLNAV + '.' + CLS_DISABLE$1);
if (disEle) {
disEle.classList.remove(CLS_DISABLE$1);
disEle.setAttribute('aria-disabled', 'false');
disEle.setAttribute('tabindex', '0');
}
}
}
/**
* Gets called when the model property changes.The data that describes the old and new values of property that changed.
*
* @param {VScrollModel} newProp - It contains the new value of data.
* @param {VScrollModel} oldProp - It contains the old value of data.
* @returns {void}
* @private
*/
onPropertyChanged(newProp, oldProp) {
for (const prop of Object.keys(newProp)) {
switch (prop) {
case 'scrollStep':
this.setScrollState();
break;
case 'enableRtl':
if (newProp.enableRtl) {
this.element.classList.add(CLS_RTL$1);
}
else {
this.element.classList.remove(CLS_RTL$1);
}
break;
}
}
}
};
__decorate$1([
Property(null)
], VScroll.prototype, "scrollStep", void 0);
VScroll = __decorate$1([
NotifyPropertyChanges
], VScroll);
/**
* Used to add scroll in menu.
*
* @param {createElementType} createElement - Specifies the create element model
* @param {HTMLElement} container - Specifies the element container
* @param {HTMLElement} content - Specifies the content element
* @param {string} scrollType - Specifies the scroll type
* @param {boolean} enableRtl - Specifies the enable RTL property
* @param {boolean} offset - Specifies the offset value
* @returns {HTMLElement} - Element
* @hidden
*/
function addScrolling(createElement, container, content, scrollType, enableRtl, offset) {
let containerOffset;
let contentOffset;
const parentElem = container.parentElement;
if (scrollType === 'vscroll') {
containerOffset = offset || container.getBoundingClientRect().height;
contentOffset = content.getBoundingClientRect().height;
}
else {
containerOffset = container.getBoundingClientRect().width;
contentOffset = content.getBoundingClientRect().width;
}
if (containerOffset < contentOffset) {
return createScrollbar(createElement, container, content, scrollType, enableRtl, offset);
}
else if (parentElem) {
const width = parentElem.getBoundingClientRect().width;
if (width < containerOffset && scrollType === 'hscroll') {
contentOffset = width;
container.style.maxWidth = width + 'px';
return createScrollbar(createElement, container, content, scrollType, enableRtl, offset);
}
return content;
}
else {
return content;
}
}
/**
* Used to create scroll bar in menu.
*
* @param {createElementType} createElement - Specifies the create element model
* @param {HTMLElement} container - Specifies the element container
* @param {HTMLElement} content - Specifies the content element
* @param {string} scrollType - Specifies the scroll type
* @param {boolean} enableRtl - Specifies the enable RTL property
* @param {boolean} offset - Specifies the offset value
* @returns {HTMLElement} - Element
* @hidden
*/
function createScrollbar(createElement, container, content, scrollType, enableRtl, offset) {
const scrollEle = createElement('div', { className: 'e-menu-' + scrollType });
container.appendChild(scrollEle);
scrollEle.appendChild(content);
if (offset) {
scrollEle.style.overflow = 'hidden';
scrollEle.style.height = offset + 'px';
}
else {
scrollEle.style.maxHeight = container.style.maxHeight;
container.style.overflow = 'hidden';
}
let scrollObj;
if (scrollType === 'vscroll') {
scrollObj = new VScroll({ enableRtl: enableRtl }, scrollEle);
scrollObj.scrollStep = select('.e-' + scrollType + '-bar', container).offsetHeight / 2;
}
else {
scrollObj = new HScroll({ enableRtl: enableRtl }, scrollEle);
scrollObj.scrollStep = select('.e-' + scrollType + '-bar', container).offsetWidth;
}
return scrollEle;
}
/**
* Used to destroy the scroll option.
*
* @param {VScroll | HScroll} scrollObj - Specifies the scroller object
* @param {Element} element - Specifies the element
* @param {HTMLElement} skipEle - Specifies the skip element
* @returns {void}
* @hidden
*/
function destroyScroll(scrollObj, element, skipEle) {
if (scrollObj) {
const menu = select('.e-menu-parent', element);
if (menu) {
if (!skipEle || skipEle === menu) {
scrollObj.destroy();
element.parentElement.appendChild(menu);
detach(element);
}
}
else {
scrollObj.destroy();
detach(element);
}
}
}
var __decorate$2 = (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;
};
const ENTER = 'enter';
const ESCAPE = 'escape';
const FOCUSED = 'e-focused';
const HEADER = 'e-menu-header';
const SELECTED = 'e-selected';
const SEPARATOR = 'e-separator';
const UPARROW = 'uparrow';
const DOWNARROW = 'downarrow';
const LEFTARROW = 'leftarrow';
const RIGHTARROW = 'rightarrow';
const HOME = 'home';
const END = 'end';
const TAB = 'tab';
const CARET = 'e-caret';
const ITEM = 'e-menu-item';
const DISABLED = 'e-disabled';
const HIDE = 'e-menu-hide';
const ICONS = 'e-icons';
const RTL = 'e-rtl';
const POPUP = 'e-menu-popup';
const TEMPLATE_PROPERTY = 'Template';
/**
* Configures the field options of the Menu.
*/
class FieldSettings extends ChildProperty {
}
__decorate$2([
Property('id')
], FieldSettings.prototype, "itemId", void 0);
__decorate$2([
Property('parentId')
], FieldSettings.prototype, "parentId", void 0);
__decorate$2([
Property('text')
], FieldSettings.prototype, "text", void 0);
__decorate$2([
Property('iconCss')
], FieldSettings.prototype, "iconCss", void 0);
__decorate$2([
Property('url')
], FieldSettings.prototype, "url", void 0);
__decorate$2([
Property('separator')
], FieldSettings.prototype, "separator", void 0);
__decorate$2([
Property('items')
], FieldSettings.prototype, "children", void 0);
/**
* Specifies menu items.
*/
class MenuItem extends ChildProperty {
}
__decorate$2([
Property(null)
], MenuItem.prototype, "iconCss", void 0);
__decorate$2([
Property('')
], MenuItem.prototype, "id", void 0);
__decorate$2([
Property(false)
], MenuItem.prototype, "separator", void 0);
__decorate$2([
Collection([], MenuItem)
], MenuItem.prototype, "items", void 0);
__decorate$2([
Property('')
], MenuItem.prototype, "text", void 0);
__decorate$2([
Property('')
], MenuItem.prototype, "url", void 0);
__decorate$2([
Property()
], MenuItem.prototype, "htmlAttributes", void 0);
/**
* Animation configuration settings.
*/
class MenuAnimationSettings extends ChildProperty {
}
__decorate$2([
Property('SlideDown')
], MenuAnimationSettings.prototype, "effect", void 0);
__decorate$2([
Property(400)
], MenuAnimationSettings.prototype, "duration", void 0);
__decorate$2([
Property('ease')
], MenuAnimationSettings.prototype, "easing", void 0);
/**
* Base class for Menu and ContextMenu components.
*
* @private
*/
let MenuBase = class MenuBase extends Component {
/**
* Constructor for creating the widget.
*
* @private
* @param {MenuBaseModel} options - Specifies the menu base model
* @param {string | HTMLUListElement} element - Specifies the element
*/
constructor(options, element) {
super(options, element);
this.navIdx = [];
this.animation = new Animation({});
this.isTapHold = false;
this.tempItem = [];
this.showSubMenuOn = 'Auto';
this.isAnimationNone = false;
this.isKBDAction = false;
}
/**
* Initialized third party configuration settings.
*
* @private
* @returns {void}
*/
preRender() {
if (!this.isMenu) {
let ul;
if (this.element.tagName === 'EJS-CONTEXTMENU') {
ul = this.createElement('ul', {
id: getUniqueID(this.getModuleName()), className: 'e-control e-lib e-' + this.getModuleName()
});
const ejInst = getValue('ej2_instances', this.element);
removeClass([this.element], ['e-control', 'e-lib', 'e-' + this.getModuleName()]);
this.clonedElement = this.element;
this.element = ul;
setValue('ej2_instances', ejInst, this.element);
}
else {
ul = this.createElement('ul', { id: getUniqueID(this.getModuleName()) });
append([].slice.call(this.element.cloneNode(true).children), ul);
const refEle = this.element.nextElementSibling;
if (refEle) {
this.element.parentElement.insertBefore(ul, refEle);
}
else {
this.element.parentElement.appendChild(ul);
}
this.clonedElement = ul;
}
this.clonedElement.style.display = 'none';
}
if (this.element.tagName === 'EJS-MENU') {
let ele = this.element;
const ejInstance = getValue('ej2_instances', ele);
const ul = this.createElement('ul');
const wrapper = this.createElement('EJS-MENU', { className: 'e-' + this.getModuleName() + '-wrapper' });
for (let idx = 0, len = ele.attributes.length; idx < len; idx++) {
ul.setAttribute(ele.attributes[idx].nodeName, ele.attributes[idx].nodeValue);
}
ele.parentNode.insertBefore(wrapper, ele);
detach(ele);
ele = ul;
wrapper.appendChild(ele);
setValue('ej2_instances', ejInstance, ele);
this.clonedElement = wrapper;
this.element = ele;
if (!this.element.id) {
this.element.id = getUniqueID(this.getModuleName());
}
}
}
/**
* Initialize the control rendering.
*
* @private
* @returns {void}
*/
render() {
this.initialize();
this.renderItems();
this.wireEvents();
this.renderComplete();
const wrapper = this.getWrapper();
// eslint-disable-next-line
if (this.template && this.enableScrolling && (this.isReact || this.isAngular)) {
requestAnimationFrame(() => {
addScrolling(this.createElement, wrapper, this.element, 'hscroll', this.enableRtl);
});
}
}
enableTouchScroll(scrollList) {
let touchStartY = 0;
this.touchStartFn = (e) => {
touchStartY = e.touches[0].clientY;
};
this.touchMoveFn = (e) => {
const touchEndY = e.touches[0].clientY;
const touchDiff = touchStartY - touchEndY;
const atTop = scrollList.scrollTop === 0;
const atBottom = scrollList.scrollTop + scrollList.clientHeight === scrollList.scrollHeight;
if ((atTop && touchDiff < 0) || (atBottom && touchDiff > 0)) {
e.preventDefault();
}
touchStartY = touchEndY;
};
scrollList.addEventListener('touchstart', this.touchStartFn, { passive: false });
scrollList.addEventListener('touchmove', this.touchMoveFn, { passive: false });
}
touchOutsideHandler(e) {
const target = e.target;
if (!closest(target, '.e-' + this.getModuleName() + '-wrapper')) {
this.closeMenu();
}
}
initialize() {
let wrapper = this.getWrapper();
if (!wrapper) {
wrapper = this.createElement('div', { className: 'e-' + this.getModuleName() + '-wrapper' });
if (this.isMenu) {
this.element.parentElement.insertBefore(wrapper, this.element);
}
else {
document.body.appendChild(wrapper);
}
}
if (this.cssClass) {
addClass([wrapper], this.cssClass.replace(/\s+/g, ' ').trim().split(' '));
}
if (this.enableRtl) {
wrapper.classList.add(RTL);
}
wrapper.appendChild(this.element);
if (this.isMenu && this.hamburgerMode) {
if (!this.target) {
this.createHeaderContainer(wrapper);
}
}
this.defaultOption = this.showItemOnClick;
}
renderItems() {
if (!this.items.length) {
const items = ListBase.createJsonFromElement(this.element, { fields: { child: 'items' } });
this.setProperties({ items: items }, true);
if (isBlazor() && !this.isMenu) {
this.element = this.removeChildElement(this.element);
}
else {
this.element.innerHTML = '';
}
}
const ul = this.createItems(this.items);
append(Array.prototype.slice.call(ul.children), this.element);
this.element.classList.add('e-menu-parent');
if (this.isMenu) {
if (!this.hamburgerMode && this.element.classList.contains('e-vertical')) {
this.setBlankIconStyle(this.element);
}
if (this.enableScrolling) {
const wrapper = this.getWrapper();
if (this.element.classList.contains('e-vertical')) {
addScrolling(this.createElement, wrapper, this.element, 'vscroll', this.enableRtl);
}
else {
addScrolling(this.createElement, wrapper, this.element, 'hscroll', this.enableRtl);
}
}
}
else {
this.element.parentElement.setAttribute('role', 'dialog');
this.element.parentElement.setAttribute('aria-label', 'context menu');
}
}
wireEvents() {
const wrapper = this.getWrapper();
if (this.target) {
let target;
const targetElems = selectAll(this.target);
for (let i = 0, len = targetElems.length; i < len; i++) {
target = targetElems[i];
if (this.isMenu) {
EventHandler.add(target, 'click', this.menuHeaderClickHandler, this);
}
else {
if (Browser.isIos) {
new Touch(target, { tapHold: this.touchHandler.bind(this) });
}
else {
EventHandler.add(target, 'contextmenu', this.cmenuHandler, this);
}
}
}
this.targetElement =