UNPKG

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