UNPKG

tabulator-tables

Version:

Interactive table generation JavaScript library

520 lines (409 loc) 12.9 kB
import Module from '../../core/Module.js'; import CalcComponent from './CalcComponent.js'; import Cell from '../../core/cell/Cell.js'; import Column from '../../core/column/Column.js'; import Row from '../../core/row/Row.js'; import defaultCalculations from './defaults/calculations.js'; class ColumnCalcs extends Module{ constructor(table){ super(table); this.topCalcs = []; this.botCalcs = []; this.genColumn = false; this.topElement = this.createElement(); this.botElement = this.createElement(); this.topRow = false; this.botRow = false; this.topInitialized = false; this.botInitialized = false; this.registerTableOption("columnCalcs", true); this.registerColumnOption("topCalc"); this.registerColumnOption("topCalcParams"); this.registerColumnOption("topCalcFormatter"); this.registerColumnOption("topCalcFormatterParams"); this.registerColumnOption("bottomCalc"); this.registerColumnOption("bottomCalcParams"); this.registerColumnOption("bottomCalcFormatter"); this.registerColumnOption("bottomCalcFormatterParams"); } createElement (){ var el = document.createElement("div"); el.classList.add("tabulator-calcs-holder"); return el; } initialize(){ this.genColumn = new Column({field:"value"}, this); this.subscribe("cell-value-changed", this.cellValueChanged.bind(this)); this.subscribe("column-init", this.initializeColumnCheck.bind(this)); this.subscribe("row-deleted", this.rowsUpdated.bind(this)); this.subscribe("scroll-horizontal", this.scrollHorizontal.bind(this)); this.subscribe("row-added", this.rowsUpdated.bind(this)); this.subscribe("column-moved", this.recalcActiveRows.bind(this)); this.subscribe("column-add", this.recalcActiveRows.bind(this)); this.subscribe("data-refreshed", this.recalcActiveRowsRefresh.bind(this)); this.subscribe("table-redraw", this.tableRedraw.bind(this)); this.subscribe("rows-visible", this.visibleRows.bind(this)); this.registerTableFunction("getCalcResults", this.getResults.bind(this)); this.registerTableFunction("recalc", this.userRecalc.bind(this)); } tableRedraw(force){ this.recalc(this.table.rowManager.activeRows); if(force){ this.redraw(); } } /////////////////////////////////// ///////// Table Functions ///////// /////////////////////////////////// userRecalc(){ this.recalc(this.table.rowManager.activeRows); } /////////////////////////////////// ///////// Internal Logic ////////// /////////////////////////////////// visibleRows(viewable, rows){ if(this.topRow){ rows.unshift(this.topRow); } if(this.botRow){ rows.push(this.botRow); } return rows; } rowsUpdated(row){ if(this.table.options.groupBy){ this.recalcRowGroup(row); }else{ this.recalcActiveRows(); } } recalcActiveRowsRefresh(){ if(this.table.options.groupBy && this.table.options.dataTreeStartExpanded && this.table.options.dataTree){ this.recalcAll(); }else{ this.recalcActiveRows(); } } recalcActiveRows(){ this.recalc(this.table.rowManager.activeRows); } cellValueChanged(cell){ if(cell.column.definition.topCalc || cell.column.definition.bottomCalc){ if(this.table.options.groupBy){ if(this.table.options.columnCalcs == "table" || this.table.options.columnCalcs == "both"){ this.recalcActiveRows(); } if(this.table.options.columnCalcs != "table"){ this.recalcRowGroup(cell.row); } }else{ this.recalcActiveRows(); } } } initializeColumnCheck(column){ if(column.definition.topCalc || column.definition.bottomCalc){ this.initializeColumn(column); } } //initialize column calcs initializeColumn(column){ var def = column.definition; var config = { topCalcParams:def.topCalcParams || {}, botCalcParams:def.bottomCalcParams || {}, }; if(def.topCalc){ switch(typeof def.topCalc){ case "string": if(ColumnCalcs.calculations[def.topCalc]){ config.topCalc = ColumnCalcs.calculations[def.topCalc]; }else{ console.warn("Column Calc Error - No such calculation found, ignoring: ", def.topCalc); } break; case "function": config.topCalc = def.topCalc; break; } if(config.topCalc){ column.modules.columnCalcs = config; this.topCalcs.push(column); if(this.table.options.columnCalcs != "group"){ this.initializeTopRow(); } } } if(def.bottomCalc){ switch(typeof def.bottomCalc){ case "string": if(ColumnCalcs.calculations[def.bottomCalc]){ config.botCalc = ColumnCalcs.calculations[def.bottomCalc]; }else{ console.warn("Column Calc Error - No such calculation found, ignoring: ", def.bottomCalc); } break; case "function": config.botCalc = def.bottomCalc; break; } if(config.botCalc){ column.modules.columnCalcs = config; this.botCalcs.push(column); if(this.table.options.columnCalcs != "group"){ this.initializeBottomRow(); } } } } //dummy functions to handle being mock column manager registerColumnField(){} removeCalcs(){ var changed = false; if(this.topInitialized){ this.topInitialized = false; this.topElement.parentNode.removeChild(this.topElement); changed = true; } if(this.botInitialized){ this.botInitialized = false; this.footerRemove(this.botElement); changed = true; } if(changed){ this.table.rowManager.adjustTableSize(); } } initializeTopRow(){ if(!this.topInitialized){ this.table.columnManager.getElement().insertBefore(this.topElement, this.table.columnManager.headersElement.nextSibling); this.topInitialized = true; } } initializeBottomRow(){ if(!this.botInitialized){ this.footerPrepend(this.botElement); this.botInitialized = true; } } scrollHorizontal(left){ if(this.botInitialized && this.botRow){ if(this.table.rtl){ this.botRow.getElement().style.marginRight = (left) + "px"; }else{ this.botRow.getElement().style.marginLeft = (-left) + "px"; } } } recalc(rows){ var data, row; if(this.topInitialized || this.botInitialized){ data = this.rowsToData(rows); if(this.topInitialized){ if(this.topRow){ this.topRow.deleteCells(); } row = this.generateRow("top", data); this.topRow = row; while(this.topElement.firstChild) this.topElement.removeChild(this.topElement.firstChild); this.topElement.appendChild(row.getElement()); row.initialize(true); } if(this.botInitialized){ if(this.botRow){ this.botRow.deleteCells(); } row = this.generateRow("bottom", data); this.botRow = row; while(this.botElement.firstChild) this.botElement.removeChild(this.botElement.firstChild); this.botElement.appendChild(row.getElement()); row.initialize(true); } this.table.rowManager.adjustTableSize(); //set resizable handles if(this.table.modExists("frozenColumns")){ this.table.modules.frozenColumns.layout(); } } } recalcRowGroup(row){ this.recalcGroup(this.table.modules.groupRows.getRowGroup(row)); } recalcAll(){ if(this.topCalcs.length || this.botCalcs.length){ if(this.table.options.columnCalcs !== "group"){ this.recalcActiveRows(); } if(this.table.options.groupBy && this.table.options.columnCalcs !== "table"){ var groups = this.table.modules.groupRows.getChildGroups(); groups.forEach((group) => { this.recalcGroup(group); }); } } } recalcGroup(group){ var data, rowData; if(group){ if(group.calcs){ if(group.calcs.bottom){ data = this.rowsToData(group.rows); rowData = this.generateRowData("bottom", data); group.calcs.bottom.updateData(rowData); group.calcs.bottom.reinitialize(); } if(group.calcs.top){ data = this.rowsToData(group.rows); rowData = this.generateRowData("top", data); group.calcs.top.updateData(rowData); group.calcs.top.reinitialize(); } } } } //generate top stats row generateTopRow(rows){ return this.generateRow("top", this.rowsToData(rows)); } //generate bottom stats row generateBottomRow(rows){ return this.generateRow("bottom", this.rowsToData(rows)); } rowsToData(rows){ var data = []; rows.forEach((row) => { data.push(row.getData()); if(this.table.options.dataTree && this.table.options.dataTreeChildColumnCalcs){ if(row.modules.dataTree && row.modules.dataTree.open){ var children = this.rowsToData(this.table.modules.dataTree.getFilteredTreeChildren(row)); data = data.concat(children); } } }); return data; } //generate stats row generateRow(pos, data){ var rowData = this.generateRowData(pos, data), row; if(this.table.modExists("mutator")){ this.table.modules.mutator.disable(); } row = new Row(rowData, this, "calc"); if(this.table.modExists("mutator")){ this.table.modules.mutator.enable(); } row.getElement().classList.add("tabulator-calcs", "tabulator-calcs-" + pos); row.component = false; row.getComponent = () => { if(!row.component){ row.component = new CalcComponent(row); } return row.component; }; row.generateCells = () => { var cells = []; this.table.columnManager.columnsByIndex.forEach((column) => { //set field name of mock column this.genColumn.setField(column.getField()); this.genColumn.hozAlign = column.hozAlign; if(column.definition[pos + "CalcFormatter"] && this.table.modExists("format")){ this.genColumn.modules.format = { formatter: this.table.modules.format.getFormatter(column.definition[pos + "CalcFormatter"]), params: column.definition[pos + "CalcFormatterParams"] || {}, }; }else{ this.genColumn.modules.format = { formatter: this.table.modules.format.getFormatter("plaintext"), params:{} }; } //ensure css class definition is replicated to calculation cell this.genColumn.definition.cssClass = column.definition.cssClass; //generate cell and assign to correct column var cell = new Cell(this.genColumn, row); cell.getElement(); cell.column = column; cell.setWidth(); column.cells.push(cell); cells.push(cell); if(!column.visible){ cell.hide(); } }); row.cells = cells; }; return row; } //generate stats row generateRowData(pos, data){ var rowData = {}, calcs = pos == "top" ? this.topCalcs : this.botCalcs, type = pos == "top" ? "topCalc" : "botCalc", params, paramKey; calcs.forEach(function(column){ var values = []; if(column.modules.columnCalcs && column.modules.columnCalcs[type]){ data.forEach(function(item){ values.push(column.getFieldValue(item)); }); paramKey = type + "Params"; params = typeof column.modules.columnCalcs[paramKey] === "function" ? column.modules.columnCalcs[paramKey](values, data) : column.modules.columnCalcs[paramKey]; column.setFieldValue(rowData, column.modules.columnCalcs[type](values, data, params)); } }); return rowData; } hasTopCalcs(){ return !!(this.topCalcs.length); } hasBottomCalcs(){ return !!(this.botCalcs.length); } //handle table redraw redraw(){ if(this.topRow){ this.topRow.normalizeHeight(true); } if(this.botRow){ this.botRow.normalizeHeight(true); } } //return the calculated getResults(){ var results = {}, groups; if(this.table.options.groupBy && this.table.modExists("groupRows")){ groups = this.table.modules.groupRows.getGroups(true); groups.forEach((group) => { results[group.getKey()] = this.getGroupResults(group); }); }else{ results = { top: this.topRow ? this.topRow.getData() : {}, bottom: this.botRow ? this.botRow.getData() : {}, }; } return results; } //get results from a group getGroupResults(group){ var groupObj = group._getSelf(), subGroups = group.getSubGroups(), subGroupResults = {}, results = {}; subGroups.forEach((subgroup) => { subGroupResults[subgroup.getKey()] = this.getGroupResults(subgroup); }); results = { top: groupObj.calcs.top ? groupObj.calcs.top.getData() : {}, bottom: groupObj.calcs.bottom ? groupObj.calcs.bottom.getData() : {}, groups: subGroupResults, }; return results; } } ColumnCalcs.moduleName = "columnCalcs"; //load defaults ColumnCalcs.calculations = defaultCalculations; export default ColumnCalcs;