UNPKG

tabulator-tables

Version:

Interactive table generation JavaScript library

634 lines (492 loc) 18.2 kB
var MoveRows = function(table){ this.table = table; //hold Tabulator object this.placeholderElement = this.createPlaceholderElement(); this.hoverElement = false; //floating row header element this.checkTimeout = false; //click check timeout holder this.checkPeriod = 150; //period to wait on mousedown to consider this a move and not a click this.moving = false; //currently moving row this.toRow = false; //destination row this.toRowAfter = false; //position of moving row relative to the desitnation row this.hasHandle = false; //row has handle instead of fully movable row this.startY = 0; //starting Y position within header element this.startX = 0; //starting X position within header element this.moveHover = this.moveHover.bind(this); this.endMove = this.endMove.bind(this); this.tableRowDropEvent = false; this.touchMove = false; this.connection = false; this.connectionSelectorsTables = false; this.connectionSelectorsElements = false; this.connectionElements = []; this.connections = []; this.connectedTable = false; this.connectedRow = false; }; MoveRows.prototype.createPlaceholderElement = function(){ var el = document.createElement("div"); el.classList.add("tabulator-row"); el.classList.add("tabulator-row-placeholder"); return el; }; MoveRows.prototype.initialize = function(handle){ this.connectionSelectorsTables = this.table.options.movableRowsConnectedTables; this.connectionSelectorsElements = this.table.options.movableRowsConnectedElements; this.connection = this.connectionSelectorsTables || this.connectionSelectorsElements; }; MoveRows.prototype.setHandle = function(handle){ this.hasHandle = handle; }; MoveRows.prototype.initializeGroupHeader = function(group){ var self = this, config = {}, rowEl; //inter table drag drop config.mouseup = function(e){ self.tableRowDrop(e, row); }.bind(self); //same table drag drop config.mousemove = function(e){ if(((e.pageY - Tabulator.prototype.helpers.elOffset(group.element).top) + self.table.rowManager.element.scrollTop) > (group.getHeight() / 2)){ if(self.toRow !== group || !self.toRowAfter){ var rowEl = group.getElement(); rowEl.parentNode.insertBefore(self.placeholderElement, rowEl.nextSibling); self.moveRow(group, true); } }else{ if(self.toRow !== group || self.toRowAfter){ var rowEl = group.getElement(); if(rowEl.previousSibling){ rowEl.parentNode.insertBefore(self.placeholderElement, rowEl); self.moveRow(group, false); } } } }.bind(self); group.modules.moveRow = config; }; MoveRows.prototype.initializeRow = function(row){ var self = this, config = {}, rowEl; //inter table drag drop config.mouseup = function(e){ self.tableRowDrop(e, row); }.bind(self); //same table drag drop config.mousemove = function(e){ var rowEl = row.getElement(); if(((e.pageY - Tabulator.prototype.helpers.elOffset(rowEl).top) + self.table.rowManager.element.scrollTop) > (row.getHeight() / 2)){ if(self.toRow !== row || !self.toRowAfter){ rowEl.parentNode.insertBefore(self.placeholderElement, rowEl.nextSibling); self.moveRow(row, true); } }else{ if(self.toRow !== row || self.toRowAfter){ rowEl.parentNode.insertBefore(self.placeholderElement, rowEl); self.moveRow(row, false); } } }.bind(self); if(!this.hasHandle){ rowEl = row.getElement(); rowEl.addEventListener("mousedown", function(e){ if(e.which === 1){ self.checkTimeout = setTimeout(function(){ self.startMove(e, row); }, self.checkPeriod); } }); rowEl.addEventListener("mouseup", function(e){ if(e.which === 1){ if(self.checkTimeout){ clearTimeout(self.checkTimeout); } } }); this.bindTouchEvents(row, row.getElement()); } row.modules.moveRow = config; }; MoveRows.prototype.initializeCell = function(cell){ var self = this, cellEl = cell.getElement(true); cellEl.addEventListener("mousedown", function(e){ if(e.which === 1){ self.checkTimeout = setTimeout(function(){ self.startMove(e, cell.row); }, self.checkPeriod); } }); cellEl.addEventListener("mouseup", function(e){ if(e.which === 1){ if(self.checkTimeout){ clearTimeout(self.checkTimeout); } } }); this.bindTouchEvents(cell.row, cellEl); }; MoveRows.prototype.bindTouchEvents = function(row, element){ var self = this, startYMove = false, //shifting center position of the cell dir = false, currentRow, nextRow, prevRow, nextRowHeight, prevRowHeight, nextRowHeightLast, prevRowHeightLast; element.addEventListener("touchstart", function(e){ self.checkTimeout = setTimeout(function(){ self.touchMove = true; currentRow = row; nextRow = row.nextRow(); nextRowHeight = nextRow ? nextRow.getHeight()/2 : 0; prevRow = row.prevRow(); prevRowHeight = prevRow ? prevRow.getHeight()/2 : 0; nextRowHeightLast = 0; prevRowHeightLast = 0; startYMove = false; self.startMove(e, row); }, self.checkPeriod); }, {passive: true}); this.moving, this.toRow, this.toRowAfter element.addEventListener("touchmove", function(e){ var halfCol, diff, moveToRow; if(self.moving){ e.preventDefault(); self.moveHover(e); if(!startYMove){ startYMove = e.touches[0].pageY; } diff = e.touches[0].pageY - startYMove; if(diff > 0){ if(nextRow && diff - nextRowHeightLast > nextRowHeight){ moveToRow = nextRow; if(moveToRow !== row){ startYMove = e.touches[0].pageY moveToRow.getElement().parentNode.insertBefore(self.placeholderElement, moveToRow.getElement().nextSibling); self.moveRow(moveToRow, true); } } }else{ if(prevRow && -diff - prevRowHeightLast > prevRowHeight){ moveToRow = prevRow; if(moveToRow !== row){ startYMove = e.touches[0].pageY; moveToRow.getElement().parentNode.insertBefore(self.placeholderElement, moveToRow.getElement()); self.moveRow(moveToRow, false); } } } if(moveToRow){ currentRow = moveToRow; nextRow = moveToRow.nextRow(); nextRowHeightLast = nextRowHeight; nextRowHeight = nextRow ? nextRow.getHeight() / 2 : 0; prevRow = moveToRow.prevRow(); prevRowHeightLast = prevRowHeight; prevRowHeight = prevRow ? prevRow.getHeight() / 2 : 0; } } }); element.addEventListener("touchend", function(e){ if(self.checkTimeout){ clearTimeout(self.checkTimeout); } if(self.moving){ self.endMove(e); self.touchMove = false; } }); }; MoveRows.prototype._bindMouseMove = function(){ var self = this; self.table.rowManager.getDisplayRows().forEach(function(row){ if((row.type === "row" || row.type === "group") && row.modules.moveRow.mousemove){ row.getElement().addEventListener("mousemove", row.modules.moveRow.mousemove); } }); }; MoveRows.prototype._unbindMouseMove = function(){ var self = this; self.table.rowManager.getDisplayRows().forEach(function(row){ if((row.type === "row" || row.type === "group") && row.modules.moveRow.mousemove){ row.getElement().removeEventListener("mousemove", row.modules.moveRow.mousemove); } }); }; MoveRows.prototype.startMove = function(e, row){ var element = row.getElement(); this.setStartPosition(e, row); this.moving = row; this.table.element.classList.add("tabulator-block-select"); //create placeholder this.placeholderElement.style.width = row.getWidth() + "px"; this.placeholderElement.style.height = row.getHeight() + "px"; if(!this.connection){ element.parentNode.insertBefore(this.placeholderElement, element); element.parentNode.removeChild(element); }else{ this.table.element.classList.add("tabulator-movingrow-sending"); this.connectToTables(row); } //create hover element this.hoverElement = element.cloneNode(true); this.hoverElement.classList.add("tabulator-moving"); if(this.connection){ document.body.appendChild(this.hoverElement); this.hoverElement.style.left = "0"; this.hoverElement.style.top = "0"; this.hoverElement.style.width = this.table.element.clientWidth + "px"; this.hoverElement.style.whiteSpace = "nowrap"; this.hoverElement.style.overflow = "hidden"; this.hoverElement.style.pointerEvents = "none"; }else{ this.table.rowManager.getTableElement().appendChild(this.hoverElement); this.hoverElement.style.left = "0"; this.hoverElement.style.top = "0"; this._bindMouseMove(); } document.body.addEventListener("mousemove", this.moveHover); document.body.addEventListener("mouseup", this.endMove); this.moveHover(e); }; MoveRows.prototype.setStartPosition = function(e, row){ var pageX = this.touchMove ? e.touches[0].pageX : e.pageX, pageY = this.touchMove ? e.touches[0].pageY : e.pageY, element, position; element = row.getElement(); if(this.connection){ position = element.getBoundingClientRect(); this.startX = position.left - pageX + window.pageXOffset; this.startY = position.top - pageY + window.pageYOffset; }else{ this.startY = (pageY - element.getBoundingClientRect().top); } }; MoveRows.prototype.endMove = function(e){ if(!e || e.which === 1 || this.touchMove){ this._unbindMouseMove(); if(!this.connection){ this.placeholderElement.parentNode.insertBefore(this.moving.getElement(), this.placeholderElement.nextSibling); this.placeholderElement.parentNode.removeChild(this.placeholderElement); } this.hoverElement.parentNode.removeChild(this.hoverElement); this.table.element.classList.remove("tabulator-block-select"); if(this.toRow){ this.table.rowManager.moveRow(this.moving, this.toRow, this.toRowAfter); } this.moving = false; this.toRow = false; this.toRowAfter = false; document.body.removeEventListener("mousemove", this.moveHover); document.body.removeEventListener("mouseup", this.endMove); if(this.connection){ this.table.element.classList.remove("tabulator-movingrow-sending"); this.disconnectFromTables(); } } }; MoveRows.prototype.moveRow = function(row, after){ this.toRow = row; this.toRowAfter = after; }; MoveRows.prototype.moveHover = function(e){ if(this.connection){ this.moveHoverConnections.call(this, e); }else{ this.moveHoverTable.call(this, e); } }; MoveRows.prototype.moveHoverTable = function(e){ var rowHolder = this.table.rowManager.getElement(), scrollTop = rowHolder.scrollTop, yPos = ((this.touchMove ? e.touches[0].pageY : e.pageY) - rowHolder.getBoundingClientRect().top) + scrollTop, scrollPos; this.hoverElement.style.top = (yPos - this.startY) + "px"; }; MoveRows.prototype.moveHoverConnections = function(e){ this.hoverElement.style.left = (this.startX + (this.touchMove ? e.touches[0].pageX : e.pageX)) + "px"; this.hoverElement.style.top = (this.startY + (this.touchMove ? e.touches[0].pageY : e.pageY)) + "px"; }; MoveRows.prototype.elementRowDrop = function(e, element, row){ if(this.table.options.movableRowsElementDrop){ this.table.options.movableRowsElementDrop(e, element, row ? row.getComponent() : false); } }; //establish connection with other tables MoveRows.prototype.connectToTables = function(row){ var connectionTables; if(this.connectionSelectorsTables){ connectionTables = this.table.modules.comms.getConnections(this.connectionSelectorsTables); this.table.options.movableRowsSendingStart.call(this.table, connectionTables); this.table.modules.comms.send(this.connectionSelectorsTables, "moveRow", "connect", { row:row, }); } if(this.connectionSelectorsElements){ this.connectionElements = []; if(!Array.isArray(this.connectionSelectorsElements)){ this.connectionSelectorsElements = [this.connectionSelectorsElements]; } this.connectionSelectorsElements.forEach((query) => { if(typeof query === "string"){ this.connectionElements = this.connectionElements.concat(Array.prototype.slice.call(document.querySelectorAll(query))); }else{ this.connectionElements.push(query); } }); this.connectionElements.forEach((element) => { var dropEvent = (e) => { this.elementRowDrop(e, element, this.moving); }; element.addEventListener("mouseup", dropEvent); element.tabulatorElementDropEvent = dropEvent; element.classList.add("tabulator-movingrow-receiving"); }); } }; //disconnect from other tables MoveRows.prototype.disconnectFromTables = function(){ var connectionTables; if(this.connectionSelectorsTables){ connectionTables = this.table.modules.comms.getConnections(this.connectionSelectorsTables); this.table.options.movableRowsSendingStop.call(this.table, connectionTables); this.table.modules.comms.send(this.connectionSelectorsTables, "moveRow", "disconnect"); } this.connectionElements.forEach((element) => { element.classList.remove("tabulator-movingrow-receiving"); element.removeEventListener("mouseup", element.tabulatorElementDropEvent); delete element.tabulatorElementDropEvent; }); }; //accept incomming connection MoveRows.prototype.connect = function(table, row){ var self = this; if(!this.connectedTable){ this.connectedTable = table; this.connectedRow = row; this.table.element.classList.add("tabulator-movingrow-receiving"); self.table.rowManager.getDisplayRows().forEach(function(row){ if(row.type === "row" && row.modules.moveRow && row.modules.moveRow.mouseup){ row.getElement().addEventListener("mouseup", row.modules.moveRow.mouseup); } }); self.tableRowDropEvent = self.tableRowDrop.bind(self); self.table.element.addEventListener("mouseup", self.tableRowDropEvent); this.table.options.movableRowsReceivingStart.call(this.table, row, table); return true; }else{ console.warn("Move Row Error - Table cannot accept connection, already connected to table:", this.connectedTable); return false; } }; //close incomming connection MoveRows.prototype.disconnect = function(table){ var self = this; if(table === this.connectedTable){ this.connectedTable = false; this.connectedRow = false; this.table.element.classList.remove("tabulator-movingrow-receiving"); self.table.rowManager.getDisplayRows().forEach(function(row){ if(row.type === "row" && row.modules.moveRow && row.modules.moveRow.mouseup){ row.getElement().removeEventListener("mouseup", row.modules.moveRow.mouseup); } }); self.table.element.removeEventListener("mouseup", self.tableRowDropEvent); this.table.options.movableRowsReceivingStop.call(this.table, table); }else{ console.warn("Move Row Error - trying to disconnect from non connected table") } }; MoveRows.prototype.dropComplete = function(table, row, success){ var sender = false; if(success){ switch(typeof this.table.options.movableRowsSender){ case "string": sender = this.senders[this.table.options.movableRowsSender]; break; case "function": sender = this.table.options.movableRowsSender; break; } if(sender){ sender.call(this, this.moving.getComponent(), row ? row.getComponent() : undefined, table) }else{ if(this.table.options.movableRowsSender){ console.warn("Mover Row Error - no matching sender found:", this.table.options.movableRowsSender); } } this.table.options.movableRowsSent.call(this.table, this.moving.getComponent(), row ? row.getComponent() : undefined, table); }else{ this.table.options.movableRowsSentFailed.call(this.table, this.moving.getComponent(), row ? row.getComponent() : undefined, table); } this.endMove(); }; MoveRows.prototype.tableRowDrop = function(e, row){ var receiver = false, success = false; e.stopImmediatePropagation(); switch(typeof this.table.options.movableRowsReceiver){ case "string": receiver = this.receivers[this.table.options.movableRowsReceiver]; break; case "function": receiver = this.table.options.movableRowsReceiver; break; } if(receiver){ success = receiver.call(this, this.connectedRow.getComponent(), row ? row.getComponent() : undefined, this.connectedTable) }else{ console.warn("Mover Row Error - no matching receiver found:", this.table.options.movableRowsReceiver) } if(success){ this.table.options.movableRowsReceived.call(this.table, this.connectedRow.getComponent(), row ? row.getComponent() : undefined, this.connectedTable); }else{ this.table.options.movableRowsReceivedFailed.call(this.table, this.connectedRow.getComponent(), row ? row.getComponent() : undefined, this.connectedTable); } this.table.modules.comms.send(this.connectedTable, "moveRow", "dropcomplete", { row:row, success:success, }); }; MoveRows.prototype.receivers = { insert:function(fromRow, toRow, fromTable){ this.table.addRow(fromRow.getData(), undefined, toRow); return true; }, add:function(fromRow, toRow, fromTable){ this.table.addRow(fromRow.getData()); return true; }, update:function(fromRow, toRow, fromTable){ if(toRow){ toRow.update(fromRow.getData()); return true; } return false; }, replace:function(fromRow, toRow, fromTable){ if(toRow){ this.table.addRow(fromRow.getData(), undefined, toRow); toRow.delete(); return true; } return false; }, }; MoveRows.prototype.senders = { delete:function(fromRow, toRow, toTable){ fromRow.delete(); } }; MoveRows.prototype.commsReceived = function(table, action, data){ switch(action){ case "connect": return this.connect(table, data.row); break; case "disconnect": return this.disconnect(table); break; case "dropcomplete": return this.dropComplete(table, data.row, data.success); break; } }; Tabulator.prototype.registerModule("moveRow", MoveRows);