const smPopup = document.createElement('template') smPopup.innerHTML = ` `; customElements.define('sm-popup', class extends HTMLElement { constructor() { super() this.attachShadow({ mode: 'open' }).append(smPopup.content.cloneNode(true)) this.allowClosing = false this.isOpen = false this.resumeScrolling = this.resumeScrolling.bind(this) this.show = this.show.bind(this) this.hide = this.hide.bind(this) this.handleTouchStart = this.handleTouchStart.bind(this) this.handleTouchMove = this.handleTouchMove.bind(this) this.handleTouchEnd = this.handleTouchEnd.bind(this) this.movePopup = this.movePopup.bind(this) } get open() { return this.isOpen } resumeScrolling(){ const scrollY = document.body.style.top; window.scrollTo(0, parseInt(scrollY || '0') * -1); setTimeout(() => { document.body.style.overflow = 'auto'; document.body.style.top= 'initial' }, 300); } show(pinned, popupStack){ if (popupStack) this.popupStack = popupStack if (this.popupStack && !this.hasAttribute('open')) { this.popupStack.push({ popup: this, permission: pinned }) if (this.popupStack.items.length > 1) { this.popupStack.items[this.popupStack.items.length - 2].popup.classList.add('stacked') } this.dispatchEvent( new CustomEvent("popupopened", { bubbles: true, detail: { popup: this, popupStack: this.popupStack } }) ) this.setAttribute('open', '') this.pinned = pinned this.isOpen = true } this.popupContainer.classList.remove('hide') this.popup.style.transform = 'none'; document.body.style.overflow = 'hidden'; document.body.style.top= `-${window.scrollY}px` return this.popupStack } hide(){ if (window.innerWidth < 640) this.popup.style.transform = 'translateY(100%)'; else this.popup.style.transform = 'translateY(3rem)'; this.popupContainer.classList.add('hide') this.removeAttribute('open') if (typeof this.popupStack !== 'undefined') { this.popupStack.pop() if (this.popupStack.items.length) { this.popupStack.items[this.popupStack.items.length - 1].popup.classList.remove('stacked') } else { this.resumeScrolling() } } else { this.resumeScrolling() } if (this.inputFields.length) { setTimeout(() => { this.inputFields.forEach(field => { if (field.type === 'radio' || field.tagName === 'SM-CHECKBOX') field.checked = false if (field.tagName === 'SM-INPUT' || field.tagName === 'TEXTAREA'|| field.tagName === 'SM-TEXTAREA') field.value = '' }) }, 300); } setTimeout(() => { this.dispatchEvent( new CustomEvent("popupclosed", { bubbles: true, detail: { popup: this, popupStack: this.popupStack } }) ) this.isOpen = false }, 300); } handleTouchStart(e){ this.touchStartY = e.changedTouches[0].clientY this.popup.style.transition = 'transform 0.1s' this.touchStartTime = e.timeStamp } handleTouchMove(e){ if (this.touchStartY < e.changedTouches[0].clientY) { this.offset = e.changedTouches[0].clientY - this.touchStartY; this.touchEndAnimataion = window.requestAnimationFrame(() => this.movePopup()) } /*else { this.offset = this.touchStartY - e.changedTouches[0].clientY; this.popup.style.transform = `translateY(-${this.offset}px)` }*/ } handleTouchEnd(e){ this.touchEndTime = e.timeStamp cancelAnimationFrame(this.touchEndAnimataion) this.touchEndY = e.changedTouches[0].clientY this.popup.style.transition = 'transform 0.3s' this.threshold = this.popup.getBoundingClientRect().height * 0.3 if (this.touchEndTime - this.touchStartTime > 200) { if (this.touchEndY - this.touchStartY > this.threshold) { if (this.pinned) { this.show() return } else this.hide() } else { this.show() } } else { if (this.touchEndY > this.touchStartY) if (this.pinned) { this.show() return } else this.hide() } } movePopup(){ this.popup.style.transform = `translateY(${this.offset}px)` } connectedCallback() { this.pinned = false this.popupStack this.popupContainer = this.shadowRoot.querySelector('.popup-container') this.popup = this.shadowRoot.querySelector('.popup') this.popupBodySlot = this.shadowRoot.querySelector('.popup-body slot') this.offset this.popupHeader = this.shadowRoot.querySelector('.popup-top') this.touchStartY = 0 this.touchEndY = 0 this.touchStartTime = 0 this.touchEndTime = 0 this.touchEndAnimataion; this.threshold = this.popup.getBoundingClientRect().height * 0.3 if (this.hasAttribute('open')) this.show() this.popupContainer.addEventListener('mousedown', e => { if (e.target === this.popupContainer && !this.pinned) { if (this.pinned) { this.show() return } else this.hide() } }) this.popupBodySlot.addEventListener('slotchange', () => { setTimeout(() => { this.threshold = this.popup.getBoundingClientRect().height * 0.3 }, 200); this.inputFields = this.querySelectorAll('sm-input', 'sm-checkbox', 'textarea', 'sm-textarea', 'radio') }) this.popupHeader.addEventListener('touchstart', (e) => { this.handleTouchStart(e) }, {passive: true}) this.popupHeader.addEventListener('touchmove', (e) => {this.handleTouchMove(e)}, {passive: true}) this.popupHeader.addEventListener('touchend', (e) => {this.handleTouchEnd(e)}, {passive: true}) } disconnectedCallback() { this.popupHeader.removeEventListener('touchstart', this.handleTouchStart, {passive: true}) this.popupHeader.removeEventListener('touchmove', this.handleTouchMove, {passive: true}) this.popupHeader.removeEventListener('touchend', this.handleTouchEnd, {passive: true}) } })