UNPKG

tabulator-tables

Version:

Interactive table generation JavaScript library

1,987 lines (1,558 loc) 57.1 kB
var Edit = function(table){ this.table = table; //hold Tabulator object this.currentCell = false; //hold currently editing cell this.mouseClick = false; //hold mousedown state to prevent click binding being overriden by editor opening this.recursionBlock = false; //prevent focus recursion this.invalidEdit = false; this.editedCells = []; }; //initialize column editor Edit.prototype.initializeColumn = function(column){ var self = this, config = { editor:false, blocked:false, check:column.definition.editable, params:column.definition.editorParams || {} }; //set column editor switch(typeof column.definition.editor){ case "string": if(column.definition.editor === "tick"){ column.definition.editor = "tickCross"; console.warn("DEPRECATION WARNING - the tick editor has been deprecated, please use the tickCross editor"); } if(self.editors[column.definition.editor]){ config.editor = self.editors[column.definition.editor]; }else{ console.warn("Editor Error - No such editor found: ", column.definition.editor); } break; case "function": config.editor = column.definition.editor; break; case "boolean": if(column.definition.editor === true){ if(typeof column.definition.formatter !== "function"){ if(column.definition.formatter === "tick"){ column.definition.formatter = "tickCross"; console.warn("DEPRECATION WARNING - the tick editor has been deprecated, please use the tickCross editor"); } if(self.editors[column.definition.formatter]){ config.editor = self.editors[column.definition.formatter]; }else{ config.editor = self.editors["input"]; } }else{ console.warn("Editor Error - Cannot auto lookup editor for a custom formatter: ", column.definition.formatter); } } break; } if(config.editor){ column.modules.edit = config; } }; Edit.prototype.getCurrentCell = function(){ return this.currentCell ? this.currentCell.getComponent() : false; }; Edit.prototype.clearEditor = function(cancel){ var cell = this.currentCell, cellEl; this.invalidEdit = false; if(cell){ this.currentCell = false; cellEl = cell.getElement(); if(cancel){ cell.validate(); }else{ cellEl.classList.remove("tabulator-validation-fail"); } cellEl.classList.remove("tabulator-editing"); while(cellEl.firstChild) cellEl.removeChild(cellEl.firstChild); cell.row.getElement().classList.remove("tabulator-row-editing"); } }; Edit.prototype.cancelEdit = function(){ if(this.currentCell){ var cell = this.currentCell; var component = this.currentCell.getComponent(); this.clearEditor(true); cell.setValueActual(cell.getValue()); cell.cellRendered(); if(cell.column.definition.editor == "textarea" || cell.column.definition.variableHeight){ cell.row.normalizeHeight(true); } if(cell.column.cellEvents.cellEditCancelled){ cell.column.cellEvents.cellEditCancelled.call(this.table, component); } this.table.options.cellEditCancelled.call(this.table, component); } }; //return a formatted value for a cell Edit.prototype.bindEditor = function(cell){ var self = this, element = cell.getElement(true); element.setAttribute("tabindex", 0); element.addEventListener("click", function(e){ if(!element.classList.contains("tabulator-editing")){ element.focus({preventScroll: true}); } }); element.addEventListener("mousedown", function(e){ if (e.button === 2) { e.preventDefault(); }else{ self.mouseClick = true; } }); element.addEventListener("focus", function(e){ if(!self.recursionBlock){ self.edit(cell, e, false); } }); }; Edit.prototype.focusCellNoEvent = function(cell, block){ this.recursionBlock = true; if(!(block && this.table.browser === "ie")){ cell.getElement().focus({preventScroll: true}); } this.recursionBlock = false; }; Edit.prototype.editCell = function(cell, forceEdit){ this.focusCellNoEvent(cell); this.edit(cell, false, forceEdit); }; Edit.prototype.focusScrollAdjust = function(cell){ if(this.table.rowManager.getRenderMode() == "virtual"){ var topEdge = this.table.rowManager.element.scrollTop, bottomEdge = this.table.rowManager.element.clientHeight + this.table.rowManager.element.scrollTop, rowEl = cell.row.getElement(), offset = rowEl.offsetTop; if(rowEl.offsetTop < topEdge){ this.table.rowManager.element.scrollTop -= (topEdge - rowEl.offsetTop); }else{ if(rowEl.offsetTop + rowEl.offsetHeight > bottomEdge){ this.table.rowManager.element.scrollTop += (rowEl.offsetTop + rowEl.offsetHeight - bottomEdge); } } var leftEdge = this.table.rowManager.element.scrollLeft, rightEdge = this.table.rowManager.element.clientWidth + this.table.rowManager.element.scrollLeft, cellEl = cell.getElement(), offset = cellEl.offsetLeft; if(this.table.modExists("frozenColumns")){ leftEdge += parseInt(this.table.modules.frozenColumns.leftMargin); rightEdge -= parseInt(this.table.modules.frozenColumns.rightMargin); } if(this.table.options.virtualDomHoz){ leftEdge -= parseInt(this.table.vdomHoz.vDomPadLeft); rightEdge -= parseInt(this.table.vdomHoz.vDomPadLeft); } if(cellEl.offsetLeft < leftEdge){ this.table.rowManager.element.scrollLeft -= (leftEdge - cellEl.offsetLeft); }else{ if(cellEl.offsetLeft + cellEl.offsetWidth > rightEdge){ this.table.rowManager.element.scrollLeft += (cellEl.offsetLeft + cellEl.offsetWidth - rightEdge); } } } }; Edit.prototype.edit = function(cell, e, forceEdit){ var self = this, allowEdit = true, rendered = function(){}, element = cell.getElement(), cellEditor, component, params; //prevent editing if another cell is refusing to leave focus (eg. validation fail) if(this.currentCell){ if(!this.invalidEdit){ this.cancelEdit(); } return; } //handle successfull value change function success(value){ if(self.currentCell === cell){ var valid = true; if(cell.column.modules.validate && self.table.modExists("validate") && self.table.options.validationMode != "manual"){ valid = self.table.modules.validate.validate(cell.column.modules.validate, cell, value); } if(valid === true || self.table.options.validationMode === "highlight"){ self.clearEditor(); if(!cell.modules.edit){ cell.modules.edit = {}; } cell.modules.edit.edited = true; if(self.editedCells.indexOf(cell) == -1){ self.editedCells.push(cell); } cell.setValue(value, true); if(self.table.options.dataTree && self.table.modExists("dataTree")){ self.table.modules.dataTree.checkForRestyle(cell); } if(valid !== true){ element.classList.add("tabulator-validation-fail"); self.table.options.validationFailed.call(self.table, cell.getComponent(), value, valid); return false; } return true; }else{ self.invalidEdit = true; element.classList.add("tabulator-validation-fail"); self.focusCellNoEvent(cell, true); rendered(); self.table.options.validationFailed.call(self.table, cell.getComponent(), value, valid); return false; } }else{ // console.warn("Edit Success Error - cannot call success on a cell that is no longer being edited"); } } //handle aborted edit function cancel(){ if(self.currentCell === cell){ self.cancelEdit(); if(self.table.options.dataTree && self.table.modExists("dataTree")){ self.table.modules.dataTree.checkForRestyle(cell); } }else{ // console.warn("Edit Success Error - cannot call cancel on a cell that is no longer being edited"); } } function onRendered(callback){ rendered = callback; } if(!cell.column.modules.edit.blocked){ if(e){ e.stopPropagation(); } switch(typeof cell.column.modules.edit.check){ case "function": allowEdit = cell.column.modules.edit.check(cell.getComponent()); break; case "boolean": allowEdit = cell.column.modules.edit.check; break; } if(allowEdit || forceEdit){ self.cancelEdit(); self.currentCell = cell; this.focusScrollAdjust(cell); component = cell.getComponent(); if(this.mouseClick){ this.mouseClick = false; if(cell.column.cellEvents.cellClick){ cell.column.cellEvents.cellClick.call(this.table, e, component); } } if(cell.column.cellEvents.cellEditing){ cell.column.cellEvents.cellEditing.call(this.table, component); } self.table.options.cellEditing.call(this.table, component); params = typeof cell.column.modules.edit.params === "function" ? cell.column.modules.edit.params(component) : cell.column.modules.edit.params; cellEditor = cell.column.modules.edit.editor.call(self, component, onRendered, success, cancel, params); //if editor returned, add to DOM, if false, abort edit if(cellEditor !== false){ if(cellEditor instanceof Node){ element.classList.add("tabulator-editing"); cell.row.getElement().classList.add("tabulator-row-editing"); while(element.firstChild) element.removeChild(element.firstChild); element.appendChild(cellEditor); //trigger onRendered Callback rendered(); //prevent editing from triggering rowClick event var children = element.children; for (var i = 0; i < children.length; i++) { children[i].addEventListener("click", function(e){ e.stopPropagation(); }); } }else{ console.warn("Edit Error - Editor should return an instance of Node, the editor returned:", cellEditor); element.blur(); return false; } }else{ element.blur(); return false; } return true; }else{ this.mouseClick = false; element.blur(); return false; } }else{ this.mouseClick = false; element.blur(); return false; } }; Edit.prototype.maskInput = function(el, options){ var mask = options.mask, maskLetter = typeof options.maskLetterChar !== "undefined" ? options.maskLetterChar : "A", maskNumber = typeof options.maskNumberChar !== "undefined" ? options.maskNumberChar : "9", maskWildcard = typeof options.maskWildcardChar !== "undefined" ? options.maskWildcardChar : "*", success = false; function fillSymbols(index){ var symbol = mask[index]; if(typeof symbol !== "undefined" && symbol !== maskWildcard && symbol !== maskLetter && symbol !== maskNumber){ el.value = el.value + "" + symbol; fillSymbols(index+1); } } el.addEventListener("keydown", (e) => { var index = el.value.length, char = e.key; if(e.keyCode > 46){ if(index >= mask.length){ e.preventDefault(); e.stopPropagation(); success = false; return false; }else{ switch(mask[index]){ case maskLetter: if(char.toUpperCase() == char.toLowerCase()){ e.preventDefault(); e.stopPropagation(); success = false; return false; } break; case maskNumber: if(isNaN(char)){ e.preventDefault(); e.stopPropagation(); success = false; return false; } break; case maskWildcard: break; default: if(char !== mask[index]){ e.preventDefault(); e.stopPropagation(); success = false; return false; } } } success = true; } return; }); el.addEventListener("keyup", (e) => { if(e.keyCode > 46){ if(options.maskAutoFill){ fillSymbols(el.value.length); } } }); if(!el.placeholder){ el.placeholder = mask; } if(options.maskAutoFill){ fillSymbols(el.value.length); } }; Edit.prototype.getEditedCells = function(){ var output = []; this.editedCells.forEach((cell) => { output.push(cell.getComponent()); }); return output; }; Edit.prototype.clearEdited = function(cell){ var editIndex; if(cell.modules.edit && cell.modules.edit.edited){ cell.modules.edit.edited = false; if(cell.modules.validate){ cell.modules.validate.invalid = false; } } editIndex = this.editedCells.indexOf(cell); if(editIndex > -1){ this.editedCells.splice(editIndex, 1); } }; //default data editors Edit.prototype.editors = { //input element input:function(cell, onRendered, success, cancel, editorParams){ //create and style input var cellValue = cell.getValue(), input = document.createElement("input"); input.setAttribute("type", editorParams.search ? "search" : "text"); input.style.padding = "4px"; input.style.width = "100%"; input.style.boxSizing = "border-box"; if(editorParams.elementAttributes && typeof editorParams.elementAttributes == "object"){ for (let key in editorParams.elementAttributes){ if(key.charAt(0) == "+"){ key = key.slice(1); input.setAttribute(key, input.getAttribute(key) + editorParams.elementAttributes["+" + key]); }else{ input.setAttribute(key, editorParams.elementAttributes[key]); } } } input.value = typeof cellValue !== "undefined" ? cellValue : ""; onRendered(function(){ input.focus({preventScroll: true}); input.style.height = "100%"; }); function onChange(e){ if(((cellValue === null || typeof cellValue === "undefined") && input.value !== "") || input.value !== cellValue){ if(success(input.value)){ cellValue = input.value; //persist value if successfully validated incase editor is used as header filter } }else{ cancel(); } } //submit new value on blur or change input.addEventListener("change", onChange); input.addEventListener("blur", onChange); //submit new value on enter input.addEventListener("keydown", function(e){ switch(e.keyCode){ // case 9: case 13: onChange(e); break; case 27: cancel(); break; case 35: case 36: e.stopPropagation(); break; } }); if(editorParams.mask){ this.table.modules.edit.maskInput(input, editorParams); } return input; }, //resizable text area element textarea:function(cell, onRendered, success, cancel, editorParams){ var self = this, cellValue = cell.getValue(), vertNav = editorParams.verticalNavigation || "hybrid", value = String(cellValue !== null && typeof cellValue !== "undefined" ? cellValue : ""), count = (value.match(/(?:\r\n|\r|\n)/g) || []).length + 1, input = document.createElement("textarea"), scrollHeight = 0; //create and style input input.style.display = "block"; input.style.padding = "2px"; input.style.height = "100%"; input.style.width = "100%"; input.style.boxSizing = "border-box"; input.style.whiteSpace = "pre-wrap"; input.style.resize = "none"; if(editorParams.elementAttributes && typeof editorParams.elementAttributes == "object"){ for (let key in editorParams.elementAttributes){ if(key.charAt(0) == "+"){ key = key.slice(1); input.setAttribute(key, input.getAttribute(key) + editorParams.elementAttributes["+" + key]); }else{ input.setAttribute(key, editorParams.elementAttributes[key]); } } } input.value = value; onRendered(function(){ input.focus({preventScroll: true}); input.style.height = "100%"; input.scrollHeight; input.style.height = input.scrollHeight + "px"; cell.getRow().normalizeHeight(); }); function onChange(e){ if(((cellValue === null || typeof cellValue === "undefined") && input.value !== "") || input.value !== cellValue){ if(success(input.value)){ cellValue = input.value; //persist value if successfully validated incase editor is used as header filter } setTimeout(function(){ cell.getRow().normalizeHeight(); },300) }else{ cancel(); } } //submit new value on blur or change input.addEventListener("change", onChange); input.addEventListener("blur", onChange); input.addEventListener("keyup", function(){ input.style.height = ""; var heightNow = input.scrollHeight; input.style.height = heightNow + "px"; if(heightNow != scrollHeight){ scrollHeight = heightNow; cell.getRow().normalizeHeight(); } }); input.addEventListener("keydown", function(e){ switch(e.keyCode){ case 27: cancel(); break; case 38: //up arrow if(vertNav == "editor" || (vertNav == "hybrid" && input.selectionStart)){ e.stopImmediatePropagation(); e.stopPropagation(); } break; case 40: //down arrow if(vertNav == "editor" || (vertNav == "hybrid" && input.selectionStart !== input.value.length)){ e.stopImmediatePropagation(); e.stopPropagation(); } break; case 35: case 36: e.stopPropagation(); break; } }); if(editorParams.mask){ this.table.modules.edit.maskInput(input, editorParams); } return input; }, //input element with type of number number:function(cell, onRendered, success, cancel, editorParams){ var cellValue = cell.getValue(), vertNav = editorParams.verticalNavigation || "editor", input = document.createElement("input"); input.setAttribute("type", "number"); if(typeof editorParams.max != "undefined"){ input.setAttribute("max", editorParams.max); } if(typeof editorParams.min != "undefined"){ input.setAttribute("min", editorParams.min); } if(typeof editorParams.step != "undefined"){ input.setAttribute("step", editorParams.step); } //create and style input input.style.padding = "4px"; input.style.width = "100%"; input.style.boxSizing = "border-box"; if(editorParams.elementAttributes && typeof editorParams.elementAttributes == "object"){ for (let key in editorParams.elementAttributes){ if(key.charAt(0) == "+"){ key = key.slice(1); input.setAttribute(key, input.getAttribute(key) + editorParams.elementAttributes["+" + key]); }else{ input.setAttribute(key, editorParams.elementAttributes[key]); } } } input.value = cellValue; var blurFunc = function(e){ onChange(); }; onRendered(function () { //submit new value on blur input.removeEventListener("blur", blurFunc); input.focus({preventScroll: true}); input.style.height = "100%"; //submit new value on blur input.addEventListener("blur", blurFunc); }); function onChange(){ var value = input.value; if(!isNaN(value) && value !==""){ value = Number(value); } if(value !== cellValue){ if(success(value)){ cellValue = value; //persist value if successfully validated incase editor is used as header filter } }else{ cancel(); } } //submit new value on enter input.addEventListener("keydown", function(e){ switch(e.keyCode){ case 13: // case 9: onChange(); break; case 27: cancel(); break; case 38: //up arrow case 40: //down arrow if(vertNav == "editor"){ e.stopImmediatePropagation(); e.stopPropagation(); } break; case 35: case 36: e.stopPropagation(); break; } }); if(editorParams.mask){ this.table.modules.edit.maskInput(input, editorParams); } return input; }, //input element with type of number range:function(cell, onRendered, success, cancel, editorParams){ var cellValue = cell.getValue(), input = document.createElement("input"); input.setAttribute("type", "range"); if (typeof editorParams.max != "undefined") { input.setAttribute("max", editorParams.max); } if (typeof editorParams.min != "undefined") { input.setAttribute("min", editorParams.min); } if (typeof editorParams.step != "undefined") { input.setAttribute("step", editorParams.step); } //create and style input input.style.padding = "4px"; input.style.width = "100%"; input.style.boxSizing = "border-box"; if(editorParams.elementAttributes && typeof editorParams.elementAttributes == "object"){ for (let key in editorParams.elementAttributes){ if(key.charAt(0) == "+"){ key = key.slice(1); input.setAttribute(key, input.getAttribute(key) + editorParams.elementAttributes["+" + key]); }else{ input.setAttribute(key, editorParams.elementAttributes[key]); } } } input.value = cellValue; onRendered(function () { input.focus({preventScroll: true}); input.style.height = "100%"; }); function onChange(){ var value = input.value; if(!isNaN(value) && value !==""){ value = Number(value); } if(value != cellValue){ if(success(value)){ cellValue = value; //persist value if successfully validated incase editor is used as header filter } }else{ cancel(); } } //submit new value on blur input.addEventListener("blur", function(e){ onChange(); }); //submit new value on enter input.addEventListener("keydown", function(e){ switch(e.keyCode){ case 13: // case 9: onChange(); break; case 27: cancel(); break; } }); return input; }, //select select:function(cell, onRendered, success, cancel, editorParams){ var self = this, cellEl = cell.getElement(), initialValue = cell.getValue(), vertNav = editorParams.verticalNavigation || "editor", initialDisplayValue = typeof initialValue !== "undefined" || initialValue === null ? (Array.isArray(initialValue) ? initialValue : [initialValue]) : (typeof editorParams.defaultValue !== "undefined" ? editorParams.defaultValue : []), input = document.createElement("input"), listEl = document.createElement("div"), multiselect = editorParams.multiselect, dataItems = [], currentItem = {}, displayItems = [], currentItems = [], blurable = true, blockListShow = false, searchWord = "", searchWordTimeout = null; if(Array.isArray(editorParams) || (!Array.isArray(editorParams) && typeof editorParams === "object" && !editorParams.values)){ console.warn("DEPRECATION WARNING - values for the select editor must now be passed into the values property of the editorParams object, not as the editorParams object"); editorParams = {values:editorParams}; } function getUniqueColumnValues(field){ var output = {}, data = self.table.getData(), column; if(field){ column = self.table.columnManager.getColumnByField(field); }else{ column = cell.getColumn()._getSelf(); } if(column){ data.forEach(function(row){ var val = column.getFieldValue(row); if(val !== null && typeof val !== "undefined" && val !== ""){ output[val] = true; } }); }else{ console.warn("unable to find matching column to create select lookup list:", field); } return Object.keys(output); } function parseItems(inputValues, curentValues){ var dataList = []; var displayList = []; function processComplexListItem(item){ var item = { label:item.label, value:item.value, itemParams:item.itemParams, elementAttributes: item.elementAttributes, element:false, }; // if(item.value === curentValue || (!isNaN(parseFloat(item.value)) && !isNaN(parseFloat(item.value)) && parseFloat(item.value) === parseFloat(curentValue))){ // setCurrentItem(item); // } if(curentValues.indexOf(item.value) > -1){ setItem(item); } dataList.push(item); displayList.push(item); return item; } if(typeof inputValues == "function"){ inputValues = inputValues(cell); } if(Array.isArray(inputValues)){ inputValues.forEach(function(value){ var item; if(typeof value === "object"){ if(value.options){ item = { label:value.label, group:true, itemParams:value.itemParams, elementAttributes:value.elementAttributes, element:false, }; displayList.push(item); value.options.forEach(function(item){ processComplexListItem(item); }); }else{ processComplexListItem(value); } }else{ item = { label:value, value:value, element:false, }; // if(item.value === curentValue || (!isNaN(parseFloat(item.value)) && !isNaN(parseFloat(item.value)) && parseFloat(item.value) === parseFloat(curentValue))){ // setCurrentItem(item); // } if(curentValues.indexOf(item.value) > -1){ setItem(item); } dataList.push(item); displayList.push(item); } }); }else{ for(var key in inputValues){ var item = { label:inputValues[key], value:key, element:false, }; // if(item.value === curentValue || (!isNaN(parseFloat(item.value)) && !isNaN(parseFloat(item.value)) && parseFloat(item.value) === parseFloat(curentValue))){ // setCurrentItem(item); // } if(curentValues.indexOf(item.value) > -1){ setItem(item); } dataList.push(item); displayList.push(item); } } if(editorParams.sortValuesList){ dataList.sort((a, b) => { return a.label < b.label ? -1 : (a.label > b.label ? 1 : 0); }); displayList.sort((a, b) => { return a.label < b.label ? -1 : (a.label > b.label ? 1 : 0); }); if(editorParams.sortValuesList !== "asc"){ dataList.reverse(); displayList.reverse(); } } dataItems = dataList; displayItems = displayList; fillList(); } function fillList(){ while(listEl.firstChild) listEl.removeChild(listEl.firstChild); displayItems.forEach(function(item){ var el = item.element; if(!el){ el = document.createElement("div"); item.label = editorParams.listItemFormatter ? editorParams.listItemFormatter(item.value, item.label, cell, el, item.itemParams) : item.label; if(item.group){ el.classList.add("tabulator-edit-select-list-group"); el.tabIndex = 0; el.innerHTML = item.label === "" ? "&nbsp;" : item.label; }else{ el.classList.add("tabulator-edit-select-list-item"); el.tabIndex = 0; el.innerHTML = item.label === "" ? "&nbsp;" : item.label; el.addEventListener("click", function(){ blockListShow = true; setTimeout(() => { blockListShow = false; }, 10); // setCurrentItem(item); // chooseItem(); if(multiselect){ toggleItem(item); input.focus(); }else{ chooseItem(item); } }); // if(item === currentItem){ // el.classList.add("active"); // } if(currentItems.indexOf(item) > -1){ el.classList.add("active"); } } if(item.elementAttributes && typeof item.elementAttributes == "object"){ for (let key in item.elementAttributes){ if(key.charAt(0) == "+"){ key = key.slice(1); el.setAttribute(key, input.getAttribute(key) + item.elementAttributes["+" + key]); }else{ el.setAttribute(key, item.elementAttributes[key]); } } } el.addEventListener("mousedown", function(){ blurable = false; setTimeout(function(){ blurable = true; }, 10); }); item.element = el; } listEl.appendChild(el); }); } function setCurrentItem(item, active){ if(!multiselect && currentItem && currentItem.element){ currentItem.element.classList.remove("active"); } if(currentItem && currentItem.element){ currentItem.element.classList.remove("focused"); } currentItem = item; if(item.element){ item.element.classList.add("focused"); if(active){ item.element.classList.add("active"); } } if(item && item.element && item.element.scrollIntoView){ item.element.scrollIntoView({behavior: 'smooth', block: 'nearest', inline: 'start'}); } } // function chooseItem(){ // hideList(); // if(initialValue !== currentItem.value){ // initialValue = currentItem.value; // success(currentItem.value); // }else{ // cancel(); // } // } function setItem(item) { var index = currentItems.indexOf(item); if(index == -1){ currentItems.push(item); setCurrentItem(item, true); } fillInput(); } function unsetItem(index) { var item = currentItems[index]; if(index > -1){ currentItems.splice(index, 1); if(item.element){ item.element.classList.remove("active"); } } } function toggleItem(item) { if(!item){ item = currentItem; } var index = currentItems.indexOf(item); if(index > -1){ unsetItem(index); }else{ if(multiselect !== true && currentItems.length >= multiselect){ unsetItem(0); } setItem(item); } fillInput(); } function chooseItem(item){ hideList(); if(!item){ item = currentItem; } if(item){ input.value = item.label; success(item.value); } initialDisplayValue = [item.value]; } function chooseItems(silent){ if(!silent){ hideList(); } var output = []; currentItems.forEach((item) => { output.push(item.value); }); initialDisplayValue = output; success(output); } function fillInput(){ var output = []; currentItems.forEach((item) => { output.push(item.label); }); input.value = output.join(", "); if(self.currentCell === false){ chooseItems(true); } } function unsetItems() { var len = currentItems.length; for(let i = 0; i < len; i++){ unsetItem(0); } } function cancelItem(){ hideList(); cancel(); } function showList(){ currentItems = []; if(!listEl.parentNode){ if(editorParams.values === true){ parseItems(getUniqueColumnValues(), initialDisplayValue); }else if(typeof editorParams.values === "string"){ parseItems(getUniqueColumnValues(editorParams.values), initialDisplayValue); }else{ parseItems(editorParams.values || [], initialDisplayValue); } var offset = Tabulator.prototype.helpers.elOffset(cellEl); listEl.style.minWidth = cellEl.offsetWidth + "px"; listEl.style.top = (offset.top + cellEl.offsetHeight) + "px"; listEl.style.left = offset.left + "px"; listEl.addEventListener("mousedown", function(e){ blurable = false; setTimeout(function(){ blurable = true; }, 10); }); document.body.appendChild(listEl); } } function hideList(){ if(listEl.parentNode){ listEl.parentNode.removeChild(listEl); } removeScrollListener(); } function removeScrollListener() { self.table.rowManager.element.removeEventListener("scroll", cancelItem); } function scrollTovalue(char){ clearTimeout(searchWordTimeout); var character = String.fromCharCode(event.keyCode).toLowerCase(); searchWord += character.toLowerCase(); var match = dataItems.find((item) => { return typeof item.label !== "undefined" && item.label.toLowerCase().startsWith(searchWord); }); if(match){ setCurrentItem(match, !multiselect); } searchWordTimeout = setTimeout(() => { searchWord = ""; }, 800) } //style input input.setAttribute("type", "text"); input.style.padding = "4px"; input.style.width = "100%"; input.style.boxSizing = "border-box"; input.style.cursor = "default"; input.readOnly = (this.currentCell != false); if(editorParams.elementAttributes && typeof editorParams.elementAttributes == "object"){ for (let key in editorParams.elementAttributes){ if(key.charAt(0) == "+"){ key = key.slice(1); input.setAttribute(key, input.getAttribute(key) + editorParams.elementAttributes["+" + key]); }else{ input.setAttribute(key, editorParams.elementAttributes[key]); } } } input.value = typeof initialValue !== "undefined" || initialValue === null ? initialValue : ""; // if(editorParams.values === true){ // parseItems(getUniqueColumnValues(), initialValue); // }else if(typeof editorParams.values === "string"){ // parseItems(getUniqueColumnValues(editorParams.values), initialValue); // }else{ // parseItems(editorParams.values || [], initialValue); // } input.addEventListener("search", function(e){ if(!input.value){ unsetItems(); chooseItems(); } }); //allow key based navigation input.addEventListener("keydown", function(e){ var index; switch(e.keyCode){ case 38: //up arrow index = dataItems.indexOf(currentItem); if(vertNav == "editor" || (vertNav == "hybrid" && index)){ e.stopImmediatePropagation(); e.stopPropagation(); e.preventDefault(); if(index > 0){ setCurrentItem(dataItems[index - 1], !multiselect); } } break; case 40: //down arrow index = dataItems.indexOf(currentItem); if(vertNav == "editor" || (vertNav == "hybrid" && index < dataItems.length - 1)){ e.stopImmediatePropagation(); e.stopPropagation(); e.preventDefault(); if(index < dataItems.length - 1){ if(index == -1){ setCurrentItem(dataItems[0], !multiselect); }else{ setCurrentItem(dataItems[index + 1], !multiselect); } } } break; case 37: //left arrow case 39: //right arrow e.stopImmediatePropagation(); e.stopPropagation(); e.preventDefault(); break; case 13: //enter // chooseItem(); if(multiselect){ toggleItem(); }else{ chooseItem(); } break; case 27: //escape cancelItem(); break; case 9: //tab break; default: if(self.currentCell === false){ e.preventDefault(); } if(e.keyCode >= 38 && e.keyCode <= 90){ scrollTovalue(e.keyCode); } } }); input.addEventListener("blur", function(e){ if(blurable){ if(multiselect){ chooseItems(); }else{ cancelItem(); } } }); input.addEventListener("focus", function(e){ if(!blockListShow){ showList(); } }); //style list element listEl = document.createElement("div"); listEl.classList.add("tabulator-edit-select-list"); onRendered(function(){ input.style.height = "100%"; input.focus({preventScroll: true}); }); setTimeout(() => { this.table.rowManager.element.addEventListener("scroll", cancelItem); }, 10); return input; }, //autocomplete autocomplete:function(cell, onRendered, success, cancel, editorParams){ var self = this, cellEl = cell.getElement(), initialValue = cell.getValue(), vertNav = editorParams.verticalNavigation || "editor", initialDisplayValue = typeof initialValue !== "undefined" || initialValue === null ? initialValue : (typeof editorParams.defaultValue !== "undefined" ? editorParams.defaultValue : ""), input = document.createElement("input"), listEl = document.createElement("div"), allItems = [], displayItems = [], values = [], currentItem = false, blurable = true, uniqueColumnValues = false; //style input input.setAttribute("type", "search"); input.style.padding = "4px"; input.style.width = "100%"; input.style.boxSizing = "border-box"; if(editorParams.elementAttributes && typeof editorParams.elementAttributes == "object"){ for (let key in editorParams.elementAttributes){ if(key.charAt(0) == "+"){ key = key.slice(1); input.setAttribute(key, input.getAttribute(key) + editorParams.elementAttributes["+" + key]); }else{ input.setAttribute(key, editorParams.elementAttributes[key]); } } } //style list element listEl.classList.add("tabulator-edit-select-list"); listEl.addEventListener("mousedown", function(e){ blurable = false; setTimeout(function(){ blurable = true; }, 10); }); function genUniqueColumnValues(){ if(editorParams.values === true){ uniqueColumnValues = getUniqueColumnValues(); }else if(typeof editorParams.values === "string"){ uniqueColumnValues = getUniqueColumnValues(editorParams.values); } } function getUniqueColumnValues(field){ var output = {}, data = self.table.getData(), column; if(field){ column = self.table.columnManager.getColumnByField(field); }else{ column = cell.getColumn()._getSelf(); } if(column){ data.forEach(function(row){ var val = column.getFieldValue(row); if(val !== null && typeof val !== "undefined" && val !== ""){ output[val] = true; } }); if(editorParams.sortValuesList){ if(editorParams.sortValuesList == "asc"){ output = Object.keys(output).sort(); }else{ output = Object.keys(output).sort().reverse(); } }else{ output = Object.keys(output); } }else{ console.warn("unable to find matching column to create autocomplete lookup list:", field); } return output; } function filterList(term, intialLoad){ var matches = [], values, items, searchEl; //lookup base values list if(uniqueColumnValues){ values = uniqueColumnValues; }else{ values = editorParams.values || []; } if(editorParams.searchFunc){ matches = editorParams.searchFunc(term, values); if(matches instanceof Promise){ addNotice(typeof editorParams.searchingPlaceholder !== "undefined" ? editorParams.searchingPlaceholder : "Searching..."); matches.then((result) => { fillListIfNotEmpty(parseItems(result), intialLoad); }).catch((err) => { console.err("error in autocomplete search promise:", err); }); }else{ fillListIfNotEmpty(parseItems(matches), intialLoad); } }else{ items = parseItems(values); if(term === ""){ if(editorParams.showListOnEmpty){ matches = items; } }else{ items.forEach(function(item){ if(item.value !== null || typeof item.value !== "undefined"){ if(String(item.value).toLowerCase().indexOf(String(term).toLowerCase()) > -1 || String(item.title).toLowerCase().indexOf(String(term).toLowerCase()) > -1){ matches.push(item); } } }); } fillListIfNotEmpty(matches, intialLoad); } } function addNotice(notice){ var searchEl = document.createElement("div"); clearList(); if(notice !== false){ searchEl.classList.add("tabulator-edit-select-list-notice"); searchEl.tabIndex = 0; if(notice instanceof Node){ searchEl.appendChild(notice); }else{ searchEl.innerHTML = notice; } listEl.appendChild(searchEl); } } function parseItems(inputValues){ var itemList = []; if(Array.isArray(inputValues)){ inputValues.forEach(function(value){ var item = {}; if(typeof value === "object"){ item.title = editorParams.listItemFormatter ? editorParams.listItemFormatter(value.value, value.label) : value.label; item.value = value.value; }else{ item.title = editorParams.listItemFormatter ? editorParams.listItemFormatter(value, value) : value; item.value = value; } itemList.push(item); }); }else{ for(var key in inputValues){ var item = { title:editorParams.listItemFormatter ? editorParams.listItemFormatter(key, inputValues[key]) : inputValues[key], value:key, }; itemList.push(item); } } return itemList; } function clearList(){ while(listEl.firstChild) listEl.removeChild(listEl.firstChild); } function fillListIfNotEmpty(items, intialLoad){ if(items.length){ fillList(items, intialLoad); }else{ if(editorParams.emptyPlaceholder){ addNotice(editorParams.emptyPlaceholder); } } } function fillList(items, intialLoad){ var current = false; clearList(); displayItems = items; displayItems.forEach(function(item){ var el = item.element; if(!el){ el = document.createElement("div"); el.classList.add("tabulator-edit-select-list-item"); el.tabIndex = 0; el.innerHTML = item.title; el.addEventListener("click", function(e){ setCurrentItem(item); chooseItem(); }); el.addEventListener("mousedown", function(e){ blurable = false; setTimeout(function(){ blurable = true; }, 10); }); item.element = el; if(intialLoad && item.value == initialValue){ input.value = item.title; item.element.classList.add("active"); current = true; } if(item === currentItem){ item.element.classList.add("active"); current = true; } } listEl.appendChild(el); }); if(!current){ setCurrentItem(false); } } function chooseItem(){ hideList(); if(currentItem){ if(initialValue !== currentItem.value){ initialValue = currentItem.value; input.value = currentItem.title; success(currentItem.value); }else{ cancel(); } }else{ if(editorParams.freetext){ initialValue = input.value; success(input.value); }else{ if(editorParams.allowEmpty && input.value === ""){ initialValue = input.value; success(input.value); }else{ cancel(); } } } } function showList(){ if(!listEl.parentNode){ console.log("show", initialDisplayValue) while(listEl.firstChild) listEl.removeChild(listEl.firstChild); var offset = Tabulator.prototype.helpers.elOffset(cellEl); listEl.style.minWidth = cellEl.offsetWidth + "px"; listEl.style.top = (offset.top + cellEl.offsetHeight) + "px"; listEl.style.left = offset.left + "px"; document.body.appendChild(listEl); } } function setCurrentItem(item, showInputValue){ if(currentItem && currentItem.element){ currentItem.element.classList.remove("active"); } currentItem = item; if(item && item.element){ item.element.classList.add("active"); } if(item && item.element && item.element.scrollIntoView){ item.element.scrollIntoView({behavior: 'smooth', block: 'nearest', inline: 'start'}); } } function hideList(){ if(listEl.parentNode){ listEl.parentNode.removeChild(listEl); } removeScrollListener(); } function cancelItem(){ hideList(); cancel(); } function removeScrollListener() { self.table.rowManager.element.removeEventListener("scroll", cancelItem); } //allow key based navigation input.addEventListener("keydown", function(e){ var index; switch(e.keyCode){ case 38: //up arrow index = displayItems.indexOf(currentItem); if(vertNav == "editor" || (vertNav == "hybrid" && index)){ e.stopImmediatePropagation(); e.stopPropagation(); e.preventDefault(); if(index > 0){ setCurrentItem(displayItems[index - 1]); }else{ setCurrentItem(false); } } break; case 40: //down arrow index = displayItems.indexOf(currentItem); if(vertNav == "editor" || (vertNav == "hybrid" && index < displayItems.length - 1)){ e.stopImmediatePropagation(); e.stopPropagation(); e.preventDefault(); if(index < displayItems.length - 1){ if(index == -1){ setCurrentItem(displayItems[0]); }else{ setCurrentItem(displayItems[index + 1]); } } } break; case 37: //left arrow case 39: //right arrow e.stopImmediatePropagation(); e.stopPropagation(); // e.preventDefault(); break; case 13: //enter chooseItem(); break; case 27: //escape cancelItem(); break; case 36: //home case 35: //end //prevent table navigation while using input element e.stopImmediatePropagation(); break; } }); input.addEventListener("keyup", function(e){ switch(e.keyCode){ case 38: //up arrow case 37: //left arrow case 39: //up arrow case 40: //right arrow case 13: //enter case 27: //escape break; default: filterList(input.value); } }); input.addEventListener("search", function(e){ filterList(input.value); }); input.addEventListener("blur", function(e){ if(blurable){ chooseItem(); } }); input.addEventListener("focus", function(e){ var value = initialDisplayValue; genUniqueColumnValues(); showList(); input.value = value; filterList(value, true); }); onRendered(function(){ input.style.height = "100%"; input.focus({preventScroll: true}); }); if(editorParams.mask){ this.table.modules.edit.maskInput(input, editorParams); } setTimeout(() => { this.table.rowManager.element.addEventListener("scroll", cancelItem); }, 10); genUniqueColumnValues(); input.value = initialDisplayValue; filterList(initialDisplayValue, true); return input; }, //star rating star:function(cell, onRendered, success, cancel, editorParams){ var self = this, element = cell.getElement(), value = cell.getValue(), maxStars = element.getElementsByTagName("svg").length || 5, size = element.getElementsByTagName("svg")[0] ? element.getElementsByTagName("svg")[0].getAttribute("width") : 14, stars = [], starsHolder = document.createElement("div"), star = document.createElementNS('http://www.w3.org/2000/svg', "svg"); //change star type function starChange(val){ stars.forEach(function(star, i){ if(i < val){ if(self.table.browser == "ie"){ star.setAttribute("class", "tabulator-star-active"); }else{ star.classList.replace("tabulator-star-inactive", "tabulator-star-active"); } star.innerHTML = '<polygon fill="#488CE9" stroke="#014AAE" stroke-width="37.6152" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="259.216,29.942 330.27,173.919 489.16,197.007 374.185,309.08 401.33,467.31 259.216,392.612 117.104,467.31 144.25,309.08 29.274,197.007 188.165,173.919 "/>'; }else{ if(self.table.browser == "ie"){ star.setAttribute("class", "tabulator-star-inactive"); }else{ star.classList.replace("tabulator-star-active", "tabulator-star-inactive"); } star.innerHTML = '<polygon fill="#010155" stroke="#686868" stroke-width="37.6152" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="259.216,29.942 330.27,173.919 489.16,197.007 374.185,309.08 401.33,467.31 259.216,392.612 117.104,467.31 144.25,309.08 29.274,197.007 188.165,173.919 "/>'; } }); } //build stars function buildStar(i){ var starHolder = document.createElement("span"); var nextStar = star.cloneNode(true); stars.push(nextStar); starHolder.addEventListener("mouseenter", function(e){ e.stopPropagation(); e.stopImmediatePropagation(); starChange(i); }); starHolder.addEventListener("mousemove", function(e){ e.stopPropagation(); e.stopImmediatePropagation(); }); starHolder.addEventListener("click", function(e){ e.stopPropagation(); e.stopImmediatePropagation(); success(i); element.blur(); }); starHolder.appendChild(nextStar); starsHolder.appendChild(starHolder); } //handle keyboard navigation value change function changeValue(val){ value = val; starChange(val); } //style cell element.style.whiteSpace = "nowrap"; element.style.overflow = "hidden"; element.style.textOverflow = "ellipsis"; //style holding element starsHolder.style.verticalAlign = "middle"; starsHolder.style.display = "inline-block"; starsHolder.style.padding = "4px"; //style star star.setAttribute("width", size); star.setAttribute("height", size); star.setAttribute("viewBox", "0 0 512 512"); star.setAttribute("xml:space", "preserve"); star.style.padding = "0 1px"; if(editorParams.elementAttributes && typeof editorParams.elementAttributes == "object"){ for (let key in editorParams.elementAttributes){ if(key.charAt(0) == "+"){ key = key.slice(1