UNPKG

tabulator-tables

Version:

Interactive table generation JavaScript library

829 lines (647 loc) 22.8 kB
var Filter = function(table){ this.table = table; //hold Tabulator object this.filterList = []; //hold filter list this.headerFilters = {}; //hold column filters this.headerFilterColumns = []; //hold columns that use header filters this.prevHeaderFilterChangeCheck = ""; this.prevHeaderFilterChangeCheck = "{}"; this.changed = false; //has filtering changed since last render }; //initialize column header filter Filter.prototype.initializeColumn = function(column, value){ var self = this, field = column.getField(), params; //handle successfull value change function success(value){ var filterType = (column.modules.filter.tagType == "input" && column.modules.filter.attrType == "text") || column.modules.filter.tagType == "textarea" ? "partial" : "match", type = "", filterChangeCheck = "", filterFunc; if(typeof column.modules.filter.prevSuccess === "undefined" || column.modules.filter.prevSuccess !== value){ column.modules.filter.prevSuccess = value; if(!column.modules.filter.emptyFunc(value)){ column.modules.filter.value = value; switch(typeof column.definition.headerFilterFunc){ case "string": if(self.filters[column.definition.headerFilterFunc]){ type = column.definition.headerFilterFunc; filterFunc = function(data){ var params = column.definition.headerFilterFuncParams || {}; var fieldVal = column.getFieldValue(data); params = typeof params === "function" ? params(value, fieldVal, data) : params; return self.filters[column.definition.headerFilterFunc](value, fieldVal, data, params); }; }else{ console.warn("Header Filter Error - Matching filter function not found: ", column.definition.headerFilterFunc); } break; case "function": filterFunc = function(data){ var params = column.definition.headerFilterFuncParams || {}; var fieldVal = column.getFieldValue(data); params = typeof params === "function" ? params(value, fieldVal, data) : params; return column.definition.headerFilterFunc(value, fieldVal, data, params); }; type = filterFunc; break; } if(!filterFunc){ switch(filterType){ case "partial": filterFunc = function(data){ var colVal = column.getFieldValue(data); if(typeof colVal !== 'undefined' && colVal !== null){ return String(colVal).toLowerCase().indexOf(String(value).toLowerCase()) > -1; }else{ return false; } }; type = "like"; break; default: filterFunc = function(data){ return column.getFieldValue(data) == value; }; type = "="; } } self.headerFilters[field] = {value:value, func:filterFunc, type:type, params:params || {}}; }else{ delete self.headerFilters[field]; } filterChangeCheck = JSON.stringify(self.headerFilters); if(self.prevHeaderFilterChangeCheck !== filterChangeCheck){ self.prevHeaderFilterChangeCheck = filterChangeCheck; self.changed = true; self.table.rowManager.filterRefresh(); } } return true; } column.modules.filter = { success:success, attrType:false, tagType:false, emptyFunc:false, }; this.generateHeaderFilterElement(column); }; Filter.prototype.generateHeaderFilterElement = function(column, initialValue, reinitialize){ var self = this, success = column.modules.filter.success, field = column.getField(), filterElement, editor, editorElement, cellWrapper, typingTimer, searchTrigger, params; //handle aborted edit function cancel(){} if(column.modules.filter.headerElement && column.modules.filter.headerElement.parentNode){ column.contentElement.removeChild(column.modules.filter.headerElement.parentNode); } if(field){ //set empty value function column.modules.filter.emptyFunc = column.definition.headerFilterEmptyCheck || function(value){ return !value && value !== "0" && value !== 0; }; filterElement = document.createElement("div"); filterElement.classList.add("tabulator-header-filter"); //set column editor switch(typeof column.definition.headerFilter){ case "string": if(self.table.modules.edit.editors[column.definition.headerFilter]){ editor = self.table.modules.edit.editors[column.definition.headerFilter]; if((column.definition.headerFilter === "tick" || column.definition.headerFilter === "tickCross") && !column.definition.headerFilterEmptyCheck){ column.modules.filter.emptyFunc = function(value){ return value !== true && value !== false; }; } }else{ console.warn("Filter Error - Cannot build header filter, No such editor found: ", column.definition.editor); } break; case "function": editor = column.definition.headerFilter; break; case "boolean": if(column.modules.edit && column.modules.edit.editor){ editor = column.modules.edit.editor; }else{ if(column.definition.formatter && self.table.modules.edit.editors[column.definition.formatter]){ editor = self.table.modules.edit.editors[column.definition.formatter]; if((column.definition.formatter === "tick" || column.definition.formatter === "tickCross") && !column.definition.headerFilterEmptyCheck){ column.modules.filter.emptyFunc = function(value){ return value !== true && value !== false; }; } }else{ editor = self.table.modules.edit.editors["input"]; } } break; } if(editor){ cellWrapper = { getValue:function(){ return typeof initialValue !== "undefined" ? initialValue : ""; }, getField:function(){ return column.definition.field; }, getElement:function(){ return filterElement; }, getColumn:function(){ return column.getComponent(); }, getRow:function(){ return { normalizeHeight:function(){ } }; } }; params = column.definition.headerFilterParams || {}; params = typeof params === "function" ? params.call(self.table) : params; editorElement = editor.call(this.table.modules.edit, cellWrapper, function(){}, success, cancel, params); if(!editorElement){ console.warn("Filter Error - Cannot add filter to " + field + " column, editor returned a value of false"); return; } if(!(editorElement instanceof Node)){ console.warn("Filter Error - Cannot add filter to " + field + " column, editor should return an instance of Node, the editor returned:", editorElement); return; } //set Placeholder Text if(field){ self.table.modules.localize.bind("headerFilters|columns|" + column.definition.field, function(value){ editorElement.setAttribute("placeholder", typeof value !== "undefined" && value ? value : self.table.modules.localize.getText("headerFilters|default")); }); }else{ self.table.modules.localize.bind("headerFilters|default", function(value){ editorElement.setAttribute("placeholder", typeof self.column.definition.headerFilterPlaceholder !== "undefined" && self.column.definition.headerFilterPlaceholder ? self.column.definition.headerFilterPlaceholder : value); }); } //focus on element on click editorElement.addEventListener("click", function(e){ e.stopPropagation(); editorElement.focus(); }); editorElement.addEventListener("focus", (e) => { var left = this.table.columnManager.element.scrollLeft; if(left !== this.table.rowManager.element.scrollLeft){ this.table.rowManager.scrollHorizontal(left); this.table.columnManager.scrollHorizontal(left); } }); //live update filters as user types typingTimer = false; searchTrigger = function(e){ if(typingTimer){ clearTimeout(typingTimer); } typingTimer = setTimeout(function(){ success(editorElement.value); },self.table.options.headerFilterLiveFilterDelay); }; column.modules.filter.headerElement = editorElement; column.modules.filter.attrType = editorElement.hasAttribute("type") ? editorElement.getAttribute("type").toLowerCase() : "" ; column.modules.filter.tagType = editorElement.tagName.toLowerCase(); if(column.definition.headerFilterLiveFilter !== false){ if ( !( column.definition.headerFilter === 'autocomplete' || column.definition.headerFilter === 'tickCross' || ((column.definition.editor === 'autocomplete' || column.definition.editor === 'tickCross') && column.definition.headerFilter === true) ) ) { editorElement.addEventListener("keyup", searchTrigger); editorElement.addEventListener("search", searchTrigger); //update number filtered columns on change if(column.modules.filter.attrType == "number"){ editorElement.addEventListener("change", function(e){ success(editorElement.value); }); } //change text inputs to search inputs to allow for clearing of field if(column.modules.filter.attrType == "text" && this.table.browser !== "ie"){ editorElement.setAttribute("type", "search"); // editorElement.off("change blur"); //prevent blur from triggering filter and preventing selection click } } //prevent input and select elements from propegating click to column sorters etc if(column.modules.filter.tagType == "input" || column.modules.filter.tagType == "select" || column.modules.filter.tagType == "textarea"){ editorElement.addEventListener("mousedown",function(e){ e.stopPropagation(); }); } } filterElement.appendChild(editorElement); column.contentElement.appendChild(filterElement); if(!reinitialize){ self.headerFilterColumns.push(column); } } }else{ console.warn("Filter Error - Cannot add header filter, column has no field set:", column.definition.title); } }; //hide all header filter elements (used to ensure correct column widths in "fitData" layout mode) Filter.prototype.hideHeaderFilterElements = function(){ this.headerFilterColumns.forEach(function(column){ if(column.modules.filter && column.modules.filter.headerElement){ column.modules.filter.headerElement.style.display = 'none'; } }); }; //show all header filter elements (used to ensure correct column widths in "fitData" layout mode) Filter.prototype.showHeaderFilterElements = function(){ this.headerFilterColumns.forEach(function(column){ if(column.modules.filter && column.modules.filter.headerElement){ column.modules.filter.headerElement.style.display = ''; } }); }; //programatically set focus of header filter Filter.prototype.setHeaderFilterFocus = function(column){ if(column.modules.filter && column.modules.filter.headerElement){ column.modules.filter.headerElement.focus(); }else{ console.warn("Column Filter Focus Error - No header filter set on column:", column.getField()); } }; //programmatically get value of header filter Filter.prototype.getHeaderFilterValue = function(column){ if(column.modules.filter && column.modules.filter.headerElement){ return column.modules.filter.headerElement.value; } else { console.warn("Column Filter Error - No header filter set on column:", column.getField()); } }; //programatically set value of header filter Filter.prototype.setHeaderFilterValue = function(column, value){ if (column){ if(column.modules.filter && column.modules.filter.headerElement){ this.generateHeaderFilterElement(column, value, true); column.modules.filter.success(value); }else{ console.warn("Column Filter Error - No header filter set on column:", column.getField()); } } }; Filter.prototype.reloadHeaderFilter = function(column){ if (column){ if(column.modules.filter && column.modules.filter.headerElement){ this.generateHeaderFilterElement(column, column.modules.filter.value, true); }else{ console.warn("Column Filter Error - No header filter set on column:", column.getField()); } } }; //check if the filters has changed since last use Filter.prototype.hasChanged = function(){ var changed = this.changed; this.changed = false; return changed; }; //set standard filters Filter.prototype.setFilter = function(field, type, value, params){ var self = this; self.filterList = []; if(!Array.isArray(field)){ field = [{field:field, type:type, value:value, params:params}]; } self.addFilter(field); }; //add filter to array Filter.prototype.addFilter = function(field, type, value, params){ var self = this; if(!Array.isArray(field)){ field = [{field:field, type:type, value:value, params:params}]; } field.forEach(function(filter){ filter = self.findFilter(filter); if(filter){ self.filterList.push(filter); self.changed = true; } }); if(this.table.options.persistence && this.table.modExists("persistence", true) && this.table.modules.persistence.config.filter){ this.table.modules.persistence.save("filter"); } }; Filter.prototype.findFilter = function(filter){ var self = this, column; if(Array.isArray(filter)){ return this.findSubFilters(filter); } var filterFunc = false; if(typeof filter.field == "function"){ filterFunc = function(data){ return filter.field(data, filter.type || {})// pass params to custom filter function }; }else{ if(self.filters[filter.type]){ column = self.table.columnManager.getColumnByField(filter.field); if(column){ filterFunc = function(data){ return self.filters[filter.type](filter.value, column.getFieldValue(data), data, filter.params || {}); }; }else{ filterFunc = function(data){ return self.filters[filter.type](filter.value, data[filter.field], data, filter.params || {}); }; } }else{ console.warn("Filter Error - No such filter type found, ignoring: ", filter.type); } } filter.func = filterFunc; return filter.func ? filter : false; }; Filter.prototype.findSubFilters = function(filters){ var self = this, output = []; filters.forEach(function(filter){ filter = self.findFilter(filter); if(filter){ output.push(filter); } }); return output.length ? output : false; }; //get all filters Filter.prototype.getFilters = function(all, ajax){ var output = []; if(all){ output = this.getHeaderFilters(); } if(ajax){ output.forEach(function(item){ if(typeof item.type == "function"){ item.type = "function"; } }); } output = output.concat(this.filtersToArray(this.filterList, ajax)); return output; }; //filter to Object Filter.prototype.filtersToArray = function(filterList, ajax){ var output = []; filterList.forEach((filter) => { var item; if(Array.isArray(filter)){ output.push(this.filtersToArray(filter, ajax)); }else{ item = {field:filter.field, type:filter.type, value:filter.value} if(ajax){ if(typeof item.type == "function"){ item.type = "function"; } } output.push(item); } }); return output; }; //get all filters Filter.prototype.getHeaderFilters = function(){ var self = this, output = []; for(var key in this.headerFilters){ output.push({field:key, type:this.headerFilters[key].type, value:this.headerFilters[key].value}); } return output; }; //remove filter from array Filter.prototype.removeFilter = function(field, type, value){ var self = this; if(!Array.isArray(field)){ field = [{field:field, type:type, value:value}]; } field.forEach(function(filter){ var index = -1; if(typeof filter.field == "object"){ index = self.filterList.findIndex(function(element){ return filter === element; }); }else{ index = self.filterList.findIndex(function(element){ return filter.field === element.field && filter.type === element.type && filter.value === element.value; }); } if(index > -1){ self.filterList.splice(index, 1); self.changed = true; }else{ console.warn("Filter Error - No matching filter type found, ignoring: ", filter.type); } }); if(this.table.options.persistence && this.table.modExists("persistence", true) && this.table.modules.persistence.config.filter){ this.table.modules.persistence.save("filter"); } }; //clear filters Filter.prototype.clearFilter = function(all){ this.filterList = []; if(all){ this.clearHeaderFilter(); } this.changed = true; if(this.table.options.persistence && this.table.modExists("persistence", true) && this.table.modules.persistence.config.filter){ this.table.modules.persistence.save("filter"); } }; //clear header filters Filter.prototype.clearHeaderFilter = function(){ var self = this; this.headerFilters = {}; self.prevHeaderFilterChangeCheck = "{}"; this.headerFilterColumns.forEach(function(column){ if(typeof column.modules.filter.value !== "undefined"){ delete column.modules.filter.value; } column.modules.filter.prevSuccess = undefined; self.reloadHeaderFilter(column); }); this.changed = true; }; //search data and return matching rows Filter.prototype.search = function (searchType, field, type, value){ var self = this, activeRows = [], filterList = []; if(!Array.isArray(field)){ field = [{field:field, type:type, value:value}]; } field.forEach(function(filter){ filter = self.findFilter(filter); if(filter){ filterList.push(filter); } }); this.table.rowManager.rows.forEach(function(row){ var match = true; filterList.forEach(function(filter){ if(!self.filterRecurse(filter, row.getData())){ match = false; } }); if(match){ activeRows.push(searchType === "data" ? row.getData("data") : row.getComponent()); } }); return activeRows; }; //filter row array Filter.prototype.filter = function(rowList, filters){ var self = this, activeRows = [], activeRowComponents = []; if(self.table.options.dataFiltering){ self.table.options.dataFiltering.call(self.table, self.getFilters()); } if(!self.table.options.ajaxFiltering && (self.filterList.length || Object.keys(self.headerFilters).length)){ rowList.forEach(function(row){ if(self.filterRow(row)){ activeRows.push(row); } }); }else{ activeRows = rowList.slice(0); } if(self.table.options.dataFiltered){ activeRows.forEach(function(row){ activeRowComponents.push(row.getComponent()); }); self.table.options.dataFiltered.call(self.table, self.getFilters(), activeRowComponents); } return activeRows; }; //filter individual row Filter.prototype.filterRow = function(row, filters){ var self = this, match = true, data = row.getData(); self.filterList.forEach(function(filter){ if(!self.filterRecurse(filter, data)){ match = false; } }); for(var field in self.headerFilters){ if(!self.headerFilters[field].func(data)){ match = false; } } return match; }; Filter.prototype.filterRecurse = function(filter, data){ var self = this, match = false; if(Array.isArray(filter)){ filter.forEach(function(subFilter){ if(self.filterRecurse(subFilter, data)){ match = true; } }); }else{ match = filter.func(data); } return match; }; //list of available filters Filter.prototype.filters ={ //equal to "=":function(filterVal, rowVal, rowData, filterParams){ return rowVal == filterVal ? true : false; }, //less than "<":function(filterVal, rowVal, rowData, filterParams){ return rowVal < filterVal ? true : false; }, //less than or equal to "<=":function(filterVal, rowVal, rowData, filterParams){ return rowVal <= filterVal ? true : false; }, //greater than ">":function(filterVal, rowVal, rowData, filterParams){ return rowVal > filterVal ? true : false; }, //greater than or equal to ">=":function(filterVal, rowVal, rowData, filterParams){ return rowVal >= filterVal ? true : false; }, //not equal to "!=":function(filterVal, rowVal, rowData, filterParams){ return rowVal != filterVal ? true : false; }, "regex":function(filterVal, rowVal, rowData, filterParams){ if(typeof filterVal == "string"){ filterVal = new RegExp(filterVal); } return filterVal.test(rowVal); }, //contains the string "like":function(filterVal, rowVal, rowData, filterParams){ if(filterVal === null || typeof filterVal === "undefined"){ return rowVal === filterVal ? true : false; }else{ if(typeof rowVal !== 'undefined' && rowVal !== null){ return String(rowVal).toLowerCase().indexOf(filterVal.toLowerCase()) > -1; } else{ return false; } } }, //contains the keywords "keywords":function(filterVal, rowVal, rowData, filterParams){ var keywords = filterVal.toLowerCase().split(typeof filterParams.separator === "undefined" ? " " : filterParams.separator), value = String(rowVal === null || typeof rowVal === "undefined" ? "" : rowVal).toLowerCase(), matches = []; keywords.forEach((keyword) =>{ if(value.includes(keyword)){ matches.push(true); } }); return filterParams.matchAll ? matches.length === keywords.length : !!matches.length; }, //starts with the string "starts":function(filterVal, rowVal, rowData, filterParams){ if(filterVal === null || typeof filterVal === "undefined"){ return rowVal === filterVal ? true : false; }else{ if(typeof rowVal !== 'undefined' && rowVal !== null){ return String(rowVal).toLowerCase().startsWith(filterVal.toLowerCase()); } else{ return false; } } }, //ends with the string "ends":function(filterVal, rowVal, rowData, filterParams){ if(filterVal === null || typeof filterVal === "undefined"){ return rowVal === filterVal ? true : false; }else{ if(typeof rowVal !== 'undefined' && rowVal !== null){ return String(rowVal).toLowerCase().endsWith(filterVal.toLowerCase()); } else{ return false; } } }, //in array "in":function(filterVal, rowVal, rowData, filterParams){ if(Array.isArray(filterVal)){ return filterVal.length ? filterVal.indexOf(rowVal) > -1 : true; }else{ console.warn("Filter Error - filter value is not an array:", filterVal); return false; } }, }; Tabulator.prototype.registerModule("filter", Filter);