UNPKG

tabulator-tables

Version:

Interactive table generation JavaScript library

283 lines (213 loc) 7.59 kB
import CoreFeature from '../CoreFeature.js'; import Helpers from './Helpers.js'; export default class Popup extends CoreFeature{ constructor(table, element, parent){ super(table); this.element = element; this.container = this._lookupContainer(); this.parent = parent; this.reversedX = false; this.childPopup = null; this.blurable = false; this.blurCallback = null; this.renderedCallback = null; this.visible = false; this.hideable = true; this.element.classList.add("tabulator-popup-container"); this.blurEvent = this.hide.bind(this, false); this.escEvent = this._escapeCheck.bind(this); this.destroyBinding = this.tableDestroyed; this.destroyed = false; } tableDestroyed(){ this.destroyed = true; this.hide(true); } _lookupContainer(){ var container = this.table.options.popupContainer; if(typeof container === "string"){ container = document.querySelector(container); if(!container){ console.warn("Menu Error - no container element found matching selector:", this.table.options.popupContainer , "(defaulting to document body)"); } }else if (container === true){ container = this.table.element; } if(container && !this._checkContainerIsParent(container)){ container = false; console.warn("Menu Error - container element does not contain this table:", this.table.options.popupContainer , "(defaulting to document body)"); } if(!container){ container = document.body; } return container; } _checkContainerIsParent(container, element = this.table.element){ if(container === element){ return true; }else{ return element.parentNode ? this._checkContainerIsParent(container, element.parentNode) : false; } } renderCallback(callback){ this.renderedCallback = callback; } containerEventCoords(e){ var touch = !(e instanceof MouseEvent); var x = touch ? e.touches[0].pageX : e.pageX; var y = touch ? e.touches[0].pageY : e.pageY; if(this.container !== document.body){ let parentOffset = Helpers.elOffset(this.container); x -= parentOffset.left; y -= parentOffset.top; } return {x, y}; } elementPositionCoords(element, position = "right"){ var offset = Helpers.elOffset(element), containerOffset, x, y; if(this.container !== document.body){ containerOffset = Helpers.elOffset(this.container); offset.left -= containerOffset.left; offset.top -= containerOffset.top; } switch(position){ case "right": x = offset.left + element.offsetWidth; y = offset.top - 1; break; case "bottom": x = offset.left; y = offset.top + element.offsetHeight; break; } return {x, y, offset}; } show(origin, position){ var x, y, parentEl, parentOffset, coords; if(this.destroyed || this.table.destroyed){ return this; } if(origin instanceof HTMLElement){ parentEl = origin; coords = this.elementPositionCoords(origin, position); parentOffset = coords.offset; x = coords.x; y = coords.y; }else if(typeof origin === "number"){ parentOffset = {top:0, left:0}; x = origin; y = position; }else{ coords = this.containerEventCoords(origin); x = coords.x; y = coords.y; this.reversedX = false; } this.element.style.top = y + "px"; this.element.style.left = x + "px"; this.container.appendChild(this.element); if(typeof this.renderedCallback === "function"){ this.renderedCallback(); } this._fitToScreen(x, y, parentEl, parentOffset, position); this.visible = true; this.subscribe("table-destroy", this.destroyBinding); this.element.addEventListener("mousedown", (e) => { e.stopPropagation(); }); return this; } _fitToScreen(x, y, parentEl, parentOffset, position){ var scrollTop = this.container === document.body ? document.documentElement.scrollTop : this.container.scrollTop; //move menu to start on right edge if it is too close to the edge of the screen if((x + this.element.offsetWidth) >= this.container.offsetWidth || this.reversedX){ this.element.style.left = ""; if(parentEl){ this.element.style.right = (this.container.offsetWidth - parentOffset.left) + "px"; }else{ this.element.style.right = (this.container.offsetWidth - x) + "px"; } this.reversedX = true; } //move menu to start on bottom edge if it is too close to the edge of the screen if((y + this.element.offsetHeight) > Math.max(this.container.offsetHeight, scrollTop ? this.container.scrollHeight : 0)) { if(parentEl){ switch(position){ case "bottom": this.element.style.top = (parseInt(this.element.style.top) - this.element.offsetHeight - parentEl.offsetHeight - 1) + "px"; break; default: this.element.style.top = (parseInt(this.element.style.top) - this.element.offsetHeight + parentEl.offsetHeight + 1) + "px"; } }else{ this.element.style.top = (parseInt(this.element.style.top) - this.element.offsetHeight) + "px"; } } } isVisible(){ return this.visible; } hideOnBlur(callback){ this.blurable = true; if(this.visible){ setTimeout(() => { this.table.rowManager.element.addEventListener("scroll", this.blurEvent); this.subscribe("cell-editing", this.blurEvent); document.body.addEventListener("click", this.blurEvent); document.body.addEventListener("contextmenu", this.blurEvent); document.body.addEventListener("mousedown", this.blurEvent); window.addEventListener("resize", this.blurEvent); document.body.addEventListener("keydown", this.escEvent); }, 100); this.blurCallback = callback; } return this; } _escapeCheck(e){ if(e.keyCode == 27){ this.hide(); } } blockHide(){ this.hideable = false; } restoreHide(){ this.hideable = true; } hide(silent = false){ if(this.visible && this.hideable){ if(this.blurable){ document.body.removeEventListener("keydown", this.escEvent); document.body.removeEventListener("click", this.blurEvent); document.body.removeEventListener("contextmenu", this.blurEvent); document.body.removeEventListener("mousedown", this.blurEvent); window.removeEventListener("resize", this.blurEvent); this.table.rowManager.element.removeEventListener("scroll", this.blurEvent); this.unsubscribe("cell-editing", this.blurEvent); } if(this.childPopup){ this.childPopup.hide(); } if(this.parent){ this.parent.childPopup = null; } if(this.element.parentNode){ this.element.parentNode.removeChild(this.element); } this.visible = false; if(this.blurCallback && !silent){ this.blurCallback(); } this.unsubscribe("table-destroy", this.destroyBinding); } return this; } child(element){ if(this.childPopup){ this.childPopup.hide(); } this.childPopup = new Popup(this.table, element, this); return this.childPopup; } }