tabulator-tables
Version:
Interactive table generation JavaScript library
655 lines (500 loc) • 18.3 kB
JavaScript
import Module from '../../core/Module.js';
import ExportRow from './ExportRow.js';
import ExportColumn from './ExportColumn.js';
import columnLookups from './defaults/columnLookups.js';
import rowLookups from './defaults/rowLookups.js';
export default class Export extends Module{
static moduleName = "export";
static columnLookups = columnLookups;
static rowLookups = rowLookups;
constructor(table){
super(table);
this.config = {};
this.cloneTableStyle = true;
this.colVisProp = "";
this.colVisPropAttach = "";
this.registerTableOption("htmlOutputConfig", false); //html output config
this.registerColumnOption("htmlOutput");
this.registerColumnOption("titleHtmlOutput");
}
initialize(){
this.registerTableFunction("getHtml", this.getHtml.bind(this));
}
///////////////////////////////////
///////// Internal Logic //////////
///////////////////////////////////
generateExportList(config, style, range, colVisProp){
var headers, body, columns, colLookup;
this.cloneTableStyle = style;
this.config = config || {};
this.colVisProp = colVisProp;
this.colVisPropAttach = this.colVisProp.charAt(0).toUpperCase() + this.colVisProp.slice(1);
colLookup = Export.columnLookups[range];
if(colLookup){
columns = colLookup.call(this.table);
columns = columns.filter(col => this.columnVisCheck(col));
}
headers = this.config.columnHeaders !== false ? this.headersToExportRows(this.generateColumnGroupHeaders(columns)) : [];
if(columns){
columns = columns.map(col => col.getComponent());
}
body = this.bodyToExportRows(this.rowLookup(range), columns);
return headers.concat(body);
}
generateTable(config, style, range, colVisProp){
var list = this.generateExportList(config, style, range, colVisProp);
return this.generateTableElement(list);
}
rowLookup(range){
var rows = [],
rowLookup;
if(typeof range == "function"){
range.call(this.table).forEach((row) =>{
row = this.table.rowManager.findRow(row);
if(row){
rows.push(row);
}
});
}else{
rowLookup = Export.rowLookups[range] || Export.rowLookups["active"];
rows = rowLookup.call(this.table);
}
return Object.assign([], rows);
}
generateColumnGroupHeaders(columns){
var output = [];
if (!columns) {
columns = this.config.columnGroups !== false ? this.table.columnManager.columns : this.table.columnManager.columnsByIndex;
}
columns.forEach((column) => {
var colData = this.processColumnGroup(column);
if(colData){
output.push(colData);
}
});
return output;
}
processColumnGroup(column){
var subGroups = column.columns,
maxDepth = 0,
title = column.definition["title" + (this.colVisPropAttach)] || column.definition.title;
var groupData = {
title:title,
column:column,
depth:1,
};
if(subGroups.length){
groupData.subGroups = [];
groupData.width = 0;
subGroups.forEach((subGroup) => {
var subGroupData = this.processColumnGroup(subGroup);
if(subGroupData){
groupData.width += subGroupData.width;
groupData.subGroups.push(subGroupData);
if(subGroupData.depth > maxDepth){
maxDepth = subGroupData.depth;
}
}
});
groupData.depth += maxDepth;
if(!groupData.width){
return false;
}
}else{
if(this.columnVisCheck(column)){
groupData.width = 1;
}else{
return false;
}
}
return groupData;
}
columnVisCheck(column){
var visProp = column.definition[this.colVisProp];
if(this.config.rowHeaders === false && column.isRowHeader){
return false;
}
if(typeof visProp === "function"){
visProp = visProp.call(this.table, column.getComponent());
}
if(visProp === false || visProp === true){
return visProp;
}
return column.visible && column.field;
}
headersToExportRows(columns){
var headers = [],
headerDepth = 0,
exportRows = [];
function parseColumnGroup(column, level){
var depth = headerDepth - level;
if(typeof headers[level] === "undefined"){
headers[level] = [];
}
column.height = column.subGroups ? 1 : (depth - column.depth) + 1;
headers[level].push(column);
if(column.height > 1){
for(let i = 1; i < column.height; i ++){
if(typeof headers[level + i] === "undefined"){
headers[level + i] = [];
}
headers[level + i].push(false);
}
}
if(column.width > 1){
for(let i = 1; i < column.width; i ++){
headers[level].push(false);
}
}
if(column.subGroups){
column.subGroups.forEach(function(subGroup){
parseColumnGroup(subGroup, level+1);
});
}
}
//calculate maximum header depth
columns.forEach(function(column){
if(column.depth > headerDepth){
headerDepth = column.depth;
}
});
columns.forEach(function(column){
parseColumnGroup(column,0);
});
headers.forEach((header) => {
var columns = [];
header.forEach((col) => {
if(col){
let title = typeof col.title === "undefined" ? "" : col.title;
columns.push(new ExportColumn(title, col.column.getComponent(), col.width, col.height, col.depth));
}else{
columns.push(null);
}
});
exportRows.push(new ExportRow("header", columns));
});
return exportRows;
}
bodyToExportRows(rows, columns = []){
var exportRows = [];
if (columns.length === 0) {
this.table.columnManager.columnsByIndex.forEach((column) => {
if (this.columnVisCheck(column)) {
columns.push(column.getComponent());
}
});
}
if(this.config.columnCalcs !== false && this.table.modExists("columnCalcs")){
if(this.table.modules.columnCalcs.topInitialized){
rows.unshift(this.table.modules.columnCalcs.topRow);
}
if(this.table.modules.columnCalcs.botInitialized){
rows.push(this.table.modules.columnCalcs.botRow);
}
}
rows = rows.filter((row) => {
switch(row.type){
case "group":
return this.config.rowGroups !== false;
case "calc":
return this.config.columnCalcs !== false;
case "row":
return !(this.table.options.dataTree && this.config.dataTree === false && row.modules.dataTree.parent);
}
return true;
});
rows.forEach((row, i) => {
var rowData = row.getData(this.colVisProp);
var exportCols = [];
var indent = 0;
switch(row.type){
case "group":
indent = row.level;
exportCols.push(new ExportColumn(row.key, row.getComponent(), columns.length, 1));
break;
case "calc" :
case "row" :
columns.forEach((col) => {
exportCols.push(new ExportColumn(col._column.getFieldValue(rowData), col, 1, 1));
});
if(this.table.options.dataTree && this.config.dataTree !== false){
indent = row.modules.dataTree.index;
}
break;
}
exportRows.push(new ExportRow(row.type, exportCols, row.getComponent(), indent));
});
return exportRows;
}
generateTableElement(list){
var table = document.createElement("table"),
headerEl = document.createElement("thead"),
bodyEl = document.createElement("tbody"),
styles = this.lookupTableStyles(),
rowFormatter = this.table.options["rowFormatter" + (this.colVisPropAttach)],
setup = {};
setup.rowFormatter = rowFormatter !== null ? rowFormatter : this.table.options.rowFormatter;
if(this.table.options.dataTree &&this.config.dataTree !== false && this.table.modExists("columnCalcs")){
setup.treeElementField = this.table.modules.dataTree.elementField;
}
//assign group header formatter
setup.groupHeader = this.table.options["groupHeader" + (this.colVisPropAttach)];
if(setup.groupHeader && !Array.isArray(setup.groupHeader)){
setup.groupHeader = [setup.groupHeader];
}
table.classList.add("tabulator-print-table");
this.mapElementStyles(this.table.columnManager.getHeadersElement(), headerEl, ["border-top", "border-left", "border-right", "border-bottom", "background-color", "color", "font-weight", "font-family", "font-size"]);
if(list.length > 1000){
console.warn("It may take a long time to render an HTML table with more than 1000 rows");
}
list.forEach((row, i) => {
let rowEl;
switch(row.type){
case "header":
headerEl.appendChild(this.generateHeaderElement(row, setup, styles));
break;
case "group":
bodyEl.appendChild(this.generateGroupElement(row, setup, styles));
break;
case "calc":
bodyEl.appendChild(this.generateCalcElement(row, setup, styles));
break;
case "row":
rowEl = this.generateRowElement(row, setup, styles);
this.mapElementStyles(((i % 2) && styles.evenRow) ? styles.evenRow : styles.oddRow, rowEl, ["border-top", "border-left", "border-right", "border-bottom", "color", "font-weight", "font-family", "font-size", "background-color"]);
bodyEl.appendChild(rowEl);
break;
}
});
if(headerEl.innerHTML){
table.appendChild(headerEl);
}
table.appendChild(bodyEl);
this.mapElementStyles(this.table.element, table, ["border-top", "border-left", "border-right", "border-bottom"]);
return table;
}
lookupTableStyles(){
var styles = {};
//lookup row styles
if(this.cloneTableStyle && window.getComputedStyle){
styles.oddRow = this.table.element.querySelector(".tabulator-row-odd:not(.tabulator-group):not(.tabulator-calcs)");
styles.evenRow = this.table.element.querySelector(".tabulator-row-even:not(.tabulator-group):not(.tabulator-calcs)");
styles.calcRow = this.table.element.querySelector(".tabulator-row.tabulator-calcs");
styles.firstRow = this.table.element.querySelector(".tabulator-row:not(.tabulator-group):not(.tabulator-calcs)");
styles.firstGroup = this.table.element.getElementsByClassName("tabulator-group")[0];
if(styles.firstRow){
styles.styleCells = styles.firstRow.getElementsByClassName("tabulator-cell");
styles.styleRowHeader = styles.firstRow.getElementsByClassName("tabulator-row-header")[0];
styles.firstCell = styles.styleCells[0];
styles.lastCell = styles.styleCells[styles.styleCells.length - 1];
}
}
return styles;
}
generateHeaderElement(row, setup, styles){
var rowEl = document.createElement("tr");
row.columns.forEach((column) => {
if(column){
var cellEl = document.createElement("th");
var classNames = column.component._column.definition.cssClass ? column.component._column.definition.cssClass.split(" ") : [];
cellEl.colSpan = column.width;
cellEl.rowSpan = column.height;
cellEl.innerHTML = column.value;
if(this.cloneTableStyle){
cellEl.style.boxSizing = "border-box";
}
classNames.forEach(function(className) {
cellEl.classList.add(className);
});
this.mapElementStyles(column.component.getElement(), cellEl, ["text-align", "border-left", "border-right", "background-color", "color", "font-weight", "font-family", "font-size"]);
this.mapElementStyles(column.component._column.contentElement, cellEl, ["padding-top", "padding-left", "padding-right", "padding-bottom"]);
if(column.component._column.visible){
this.mapElementStyles(column.component.getElement(), cellEl, ["width"]);
}else{
if(column.component._column.definition.width){
cellEl.style.width = column.component._column.definition.width + "px";
}
}
if(column.component._column.parent && column.component._column.parent.isGroup){
this.mapElementStyles(column.component._column.parent.groupElement, cellEl, ["border-top"]);
}else{
this.mapElementStyles(column.component.getElement(), cellEl, ["border-top"]);
}
if(column.component._column.isGroup){
this.mapElementStyles(column.component.getElement(), cellEl, ["border-bottom"]);
}else{
this.mapElementStyles(this.table.columnManager.getElement(), cellEl, ["border-bottom"]);
}
rowEl.appendChild(cellEl);
}
});
return rowEl;
}
generateGroupElement(row, setup, styles){
var rowEl = document.createElement("tr"),
cellEl = document.createElement("td"),
group = row.columns[0];
rowEl.classList.add("tabulator-print-table-row");
if(setup.groupHeader && setup.groupHeader[row.indent]){
group.value = setup.groupHeader[row.indent](group.value, row.component._group.getRowCount(), row.component._group.getData(), row.component);
}else{
if(setup.groupHeader !== false){
group.value = row.component._group.generator(group.value, row.component._group.getRowCount(), row.component._group.getData(), row.component);
}
}
cellEl.colSpan = group.width;
cellEl.innerHTML = group.value;
rowEl.classList.add("tabulator-print-table-group");
rowEl.classList.add("tabulator-group-level-" + row.indent);
if(group.component.isVisible()){
rowEl.classList.add("tabulator-group-visible");
}
this.mapElementStyles(styles.firstGroup, rowEl, ["border-top", "border-left", "border-right", "border-bottom", "color", "font-weight", "font-family", "font-size", "background-color"]);
this.mapElementStyles(styles.firstGroup, cellEl, ["padding-top", "padding-left", "padding-right", "padding-bottom"]);
rowEl.appendChild(cellEl);
return rowEl;
}
generateCalcElement(row, setup, styles){
var rowEl = this.generateRowElement(row, setup, styles);
rowEl.classList.add("tabulator-print-table-calcs");
this.mapElementStyles(styles.calcRow, rowEl, ["border-top", "border-left", "border-right", "border-bottom", "color", "font-weight", "font-family", "font-size", "background-color"]);
return rowEl;
}
generateRowElement(row, setup, styles){
var rowEl = document.createElement("tr");
rowEl.classList.add("tabulator-print-table-row");
row.columns.forEach((col, i) => {
if(col){
var cellEl = document.createElement("td"),
column = col.component._column,
table = this.table,
index = table.columnManager.findColumnIndex(column),
value = col.value,
cellStyle, styleProps;
var cellWrapper = {
modules:{},
getValue:function(){
return value;
},
getField:function(){
return column.definition.field;
},
getElement:function(){
return cellEl;
},
getType:function(){
return "cell";
},
getColumn:function(){
return column.getComponent();
},
getData:function(){
return row.component.getData();
},
getRow:function(){
return row.component;
},
getTable:function(){
return table;
},
getComponent:function(){
return cellWrapper;
},
column:column,
};
var classNames = column.definition.cssClass ? column.definition.cssClass.split(" ") : [];
classNames.forEach(function(className) {
cellEl.classList.add(className);
});
if(this.table.modExists("format") && this.config.formatCells !== false){
value = this.table.modules.format.formatExportValue(cellWrapper, this.colVisProp);
}else{
switch(typeof value){
case "object":
value = value !== null ? JSON.stringify(value) : "";
break;
case "undefined":
value = "";
break;
}
}
if(value instanceof Node){
cellEl.appendChild(value);
}else{
cellEl.innerHTML = value;
}
styleProps = ["padding-top", "padding-left", "padding-right", "padding-bottom", "border-top", "border-left", "border-right", "border-bottom", "color", "font-weight", "font-family", "font-size", "text-align"];
if(column.isRowHeader){
cellStyle = styles.styleRowHeader;
styleProps.push("background-color");
}else{
cellStyle = styles.styleCells && styles.styleCells[index] ? styles.styleCells[index] : styles.firstCell;
}
if(cellStyle){
this.mapElementStyles(cellStyle, cellEl, styleProps);
if(column.definition.align){
cellEl.style.textAlign = column.definition.align;
}
}
if(this.table.options.dataTree && this.config.dataTree !== false){
if((setup.treeElementField && setup.treeElementField == column.field) || (!setup.treeElementField && i == 0)){
if(row.component._row.modules.dataTree.controlEl){
cellEl.insertBefore(row.component._row.modules.dataTree.controlEl.cloneNode(true), cellEl.firstChild);
}
if(row.component._row.modules.dataTree.branchEl){
cellEl.insertBefore(row.component._row.modules.dataTree.branchEl.cloneNode(true), cellEl.firstChild);
}
}
}
rowEl.appendChild(cellEl);
if(cellWrapper.modules.format && cellWrapper.modules.format.renderedCallback){
cellWrapper.modules.format.renderedCallback();
}
}
});
if(setup.rowFormatter && row.type === "row" && this.config.formatCells !== false){
let formatComponent = Object.assign(row.component);
formatComponent.getElement = function(){return rowEl;};
setup.rowFormatter(row.component);
}
return rowEl;
}
generateHTMLTable(list){
var holder = document.createElement("div");
holder.appendChild(this.generateTableElement(list));
return holder.innerHTML;
}
getHtml(visible, style, config, colVisProp){
var list = this.generateExportList(config || this.table.options.htmlOutputConfig, style, visible, colVisProp || "htmlOutput");
return this.generateHTMLTable(list);
}
mapElementStyles(from, to, props){
if(this.cloneTableStyle && from && to){
var lookup = {
"background-color" : "backgroundColor",
"color" : "fontColor",
"width" : "width",
"font-weight" : "fontWeight",
"font-family" : "fontFamily",
"font-size" : "fontSize",
"text-align" : "textAlign",
"border-top" : "borderTop",
"border-left" : "borderLeft",
"border-right" : "borderRight",
"border-bottom" : "borderBottom",
"padding-top" : "paddingTop",
"padding-left" : "paddingLeft",
"padding-right" : "paddingRight",
"padding-bottom" : "paddingBottom",
};
if(window.getComputedStyle){
var fromStyle = window.getComputedStyle(from);
props.forEach(function(prop){
if(!to.style[lookup[prop]]){
to.style[lookup[prop]] = fromStyle.getPropertyValue(prop);
}
});
}
}
}
}