/** * NOTE: This file should not be modified. * It is a legacy library that should be replaced at some point. */ import browser from '../../scripts/browser'; import dom from '../../scripts/dom'; import './navdrawer.scss'; import '../../assets/css/scrollstyles.scss'; import { getIsRTL } from '../../scripts/globalize'; function getTouches(e) { return e.changedTouches || e.targetTouches || e.touches; } function disableEvent(e) { e.preventDefault(); e.stopPropagation(); } class NavDrawer { mask; newPos = 0; currentPos = 0; startPoint = 0; countStart = 0; velocity = 0; dragMode = 0; menuTouchStartX; menuTouchStartY; menuTouchStartTime; edgeContainer = document.querySelector('.mainDrawerHandle'); isPeeking = false; backgroundTouchStartX; backgroundTouchStartTime; _edgeSwipeEnabled; constructor(options) { this.options = options; this.defaults = { width: 260, handleSize: 10, disableMask: false, maxMaskOpacity: 0.5 }; options.target.classList.add('transition'); this.scrollContainer = options.target.querySelector('.mainDrawer-scrollContainer'); this.scrollContainer.classList.add('scrollY'); this.isVisible = false; this.initialize(); } setVelocity(deltaX) { const time = new Date().getTime() - (this.menuTouchStartTime || 0); this.velocity = Math.abs(deltaX) / time; } onMenuTouchStart = (e) => { const options = this.options; options.target.classList.remove('transition'); const touches = getTouches(e); const touch = touches[0] || {}; this.menuTouchStartX = touch.clientX; this.menuTouchStartY = touch.clientY; this.menuTouchStartTime = new Date().getTime(); }; onMenuTouchMove = (e) => { const scrollContainer = this.scrollContainer; const isOpen = this.visible; const touches = getTouches(e); const touch = touches[0] || {}; const endX = touch.clientX || 0; const endY = touch.clientY || 0; const deltaX = endX - (this.menuTouchStartX || 0); const deltaY = endY - (this.menuTouchStartY || 0); this.setVelocity(deltaX); if (isOpen && this.dragMode !== 1 && deltaX > 0) { this.dragMode = 2; } if (this.dragMode === 0 && (!isOpen || Math.abs(deltaX) >= 10) && Math.abs(deltaY) < 5) { this.dragMode = 1; scrollContainer.addEventListener('scroll', disableEvent); this.showMask(); } else if (this.dragMode === 0 && Math.abs(deltaY) >= 5) { this.dragMode = 2; } if (this.dragMode === 1) { this.newPos = this.currentPos + deltaX; this.changeMenuPos(); } }; onMenuTouchEnd = (e) => { const options = this.options; const scrollContainer = this.scrollContainer; options.target.classList.add('transition'); scrollContainer.removeEventListener('scroll', disableEvent); this.dragMode = 0; const touches = getTouches(e); const touch = touches[0] || {}; const endX = touch.clientX || 0; const endY = touch.clientY || 0; const deltaX = endX - (this.menuTouchStartX || 0); const deltaY = endY - (this.menuTouchStartY || 0); this.currentPos = deltaX; this.checkMenuState(deltaX, deltaY); }; onEdgeTouchMove = (e) => { e.preventDefault(); e.stopPropagation(); this.onEdgeTouchStart(e); }; onEdgeTouchStart = (e) => { const options = this.options; if (this.isPeeking) { this.onMenuTouchMove(e); } else { if (((getTouches(e)[0] || {}).clientX || 0) <= options.handleSize) { this.isPeeking = true; if (e.type === 'touchstart') { dom.removeEventListener(this.edgeContainer, 'touchmove', this.onEdgeTouchMove, {}); dom.addEventListener(this.edgeContainer, 'touchmove', this.onEdgeTouchMove, {}); } this.onMenuTouchStart(e); } } }; onEdgeTouchEnd = (e) => { if (this.isPeeking) { this.isPeeking = false; dom.removeEventListener(this.edgeContainer, 'touchmove', this.onEdgeTouchMove, {}); this.onMenuTouchEnd(e); } }; onBackgroundTouchStart = (e) => { const touches = getTouches(e); const touch = touches[0] || {}; this.backgroundTouchStartX = touch.clientX; this.backgroundTouchStartTime = new Date().getTime(); }; onBackgroundTouchMove = (e) => { const options = this.options; const touches = getTouches(e); const touch = touches[0] || {}; const endX = touch.clientX || 0; if (endX <= options.width && this.isVisible) { this.countStart++; const deltaX = endX - (this.backgroundTouchStartX || 0); if (this.countStart == 1) { this.startPoint = deltaX; } if (deltaX < 0 && this.dragMode !== 2) { this.dragMode = 1; this.newPos = deltaX - this.startPoint + options.width; this.changeMenuPos(); const time = new Date().getTime() - (this.backgroundTouchStartTime || 0); this.velocity = Math.abs(deltaX) / time; } } e.preventDefault(); e.stopPropagation(); }; onBackgroundTouchEnd = (e) => { const touches = getTouches(e); const touch = touches[0] || {}; const endX = touch.clientX || 0; const deltaX = endX - (this.backgroundTouchStartX || 0); this.checkMenuState(deltaX); this.countStart = 0; }; initElements() { const options = this.options; options.target.classList.add('touch-menu-la'); options.target.style.width = options.width + 'px'; options.target.style.insetInlineStart = -options.width + 'px'; if (!options.disableMask) { this.mask = document.createElement('div'); this.mask.className = 'tmla-mask hide'; document.body.appendChild(this.mask); } } animateToPosition(pos) { const options = this.options; requestAnimationFrame(function () { options.target.style.transform = pos ? 'translateX(' + (getIsRTL() ? -pos : pos) + 'px)' : 'none'; }); } changeMenuPos() { const options = this.options; if (this.newPos <= options.width) { this.animateToPosition(this.newPos); } } clickMaskClose() { this.mask.addEventListener('click', () => { this.close(); }); } checkMenuState(deltaX, deltaY) { if (this.velocity >= 0.4) { if (deltaX >= 0 || Math.abs(deltaY || 0) >= 70) { this.open(); } else { this.close(); } } else { if (this.newPos >= 100) { this.open(); } else { if (this.newPos) { this.close(); } } } } open() { const options = this.options; this.animateToPosition(options.width); this.currentPos = options.width; this.isVisible = true; options.target.classList.add('drawer-open'); this.showMask(); this.invoke(options.onChange); } close() { const options = this.options; this.animateToPosition(0); this.currentPos = 0; this.isVisible = false; options.target.classList.remove('drawer-open'); this.hideMask(); this.invoke(options.onChange); } toggle() { if (this.isVisible) { this.close(); } else { this.open(); } } showMask() { this.mask.classList.remove('hide'); this.mask.classList.add('backdrop'); } hideMask() { this.mask.classList.add('hide'); this.mask.classList.remove('backdrop'); } invoke(fn) { if (fn) { fn.apply(this); } } setEdgeSwipeEnabled(enabled) { const options = this.options; if (!options.disableEdgeSwipe) { if (browser.touch) { if (enabled) { if (!this._edgeSwipeEnabled) { this._edgeSwipeEnabled = true; dom.addEventListener(this.edgeContainer, 'touchstart', this.onEdgeTouchStart, { passive: true }); dom.addEventListener(this.edgeContainer, 'touchend', this.onEdgeTouchEnd, { passive: true }); dom.addEventListener(this.edgeContainer, 'touchcancel', this.onEdgeTouchEnd, { passive: true }); } } else { if (this._edgeSwipeEnabled) { this._edgeSwipeEnabled = false; dom.removeEventListener(this.edgeContainer, 'touchstart', this.onEdgeTouchStart, { passive: true }); dom.removeEventListener(this.edgeContainer, 'touchend', this.onEdgeTouchEnd, { passive: true }); dom.removeEventListener(this.edgeContainer, 'touchcancel', this.onEdgeTouchEnd, { passive: true }); } } } } } initialize() { const options = Object.assign({}, this.defaults, this.options || {}); this.options = options; if (browser.edge) { options.disableEdgeSwipe = true; } this.initElements(); if (browser.touch) { dom.addEventListener(options.target, 'touchstart', this.onMenuTouchStart, { passive: true }); dom.addEventListener(options.target, 'touchmove', this.onMenuTouchMove, { passive: true }); dom.addEventListener(options.target, 'touchend', this.onMenuTouchEnd, { passive: true }); dom.addEventListener(options.target, 'touchcancel', this.onMenuTouchEnd, { passive: true }); dom.addEventListener(this.mask, 'touchstart', this.onBackgroundTouchStart, { passive: true }); dom.addEventListener(this.mask, 'touchmove', this.onBackgroundTouchMove, {}); dom.addEventListener(this.mask, 'touchend', this.onBackgroundTouchEnd, { passive: true }); dom.addEventListener(this.mask, 'touchcancel', this.onBackgroundTouchEnd, { passive: true }); } this.clickMaskClose(); } } export default NavDrawer;