UNPKG

tablegger

Version:
221 lines (218 loc) 5.81 kB
import { defu } from 'defu'; import { consola } from 'consola'; import stringWidth from 'string-width'; const defaultOption = { table: { border: false }, cell: { paddingX: 0, paddingY: 0, align: "left", gapX: 0 } }; class Tablegger { constructor(option) { /** * User's data source */ this.data = [[]]; /** * Current row index */ this.i = 0; /** * Current column index */ this.j = 0; /** * Maximum valid character width for each column */ this.columnsWidth = []; /** * Output result */ this.result = ""; /** * Columns for table * Must be set by `SetColumn` or `SetHeader` * @default 3 */ this.column = 3; this.option = defu(option, defaultOption); } push(word) { if (this.j >= this.column) { this.i++; this.j = 0; this.data.push(new Array(this.column).fill("")); } this.data[this.i][this.j] = word; this.j++; } calcColumnsWidth() { for (let j = 0; j < this.column; j++) { const columWords = []; for (let i = 0; i < this.data.length; i++) columWords.push(this.data[i][j]); this.columnsWidth[j] = calcMaxEffectWordLength(columWords); } } addBorderHeader() { const { cell: { paddingX } } = this.option; this.result += "\u250C"; this.columnsWidth.map((column) => column + paddingX * 2).forEach((column, index) => { this.result += "\u2500".repeat(column); if (index < this.columnsWidth.length - 1) this.result += "\u252C"; }); this.result += "\u2510\n"; } addBorderRow() { const { cell: { paddingX } } = this.option; this.result += "\u251C"; this.columnsWidth.map((column) => column + paddingX * 2).forEach((column, index) => { this.result += "\u2500".repeat(column); if (index < this.columnsWidth.length - 1) this.result += "\u253C"; }); this.result += "\u2524\n"; } addPaddingYRow() { const { table: { border }, cell: { paddingX } } = this.option; if (border) { this.result += "\u2502"; this.columnsWidth.map((column) => column + paddingX * 2).forEach((column, index) => { this.result += " ".repeat(column); if (index < this.columnsWidth.length - 1) this.result += "\u2502"; }); this.result += "\u2502\n"; } else { this.result += "\n".repeat(this.option.cell.paddingY); } } addBorderFooter() { const { cell: { paddingX } } = this.option; this.result += "\u2514"; this.columnsWidth.map((column) => column + paddingX * 2).forEach((column, index) => { this.result += "\u2500".repeat(column); if (index < this.columnsWidth.length - 1) this.result += "\u2534"; }); this.result += "\u2518\n"; } /** * Add table elements * @param words */ add(words = "") { if (!Array.isArray(words)) this.push(words.toString()); else words.forEach((word) => this.push(word.toString())); return this; } /** * Set table header * @param words */ setHeader(words) { this.column = words.length; this.add(words); return this; } /** * Set the number of table columns * @param column */ setColumn(column) { this.data = new Array( new Array(column).fill("") ); this.columnsWidth = new Array(column); return this; } /** * Modify data at a location * @param i Abscissa * @param j Ordinate * @param word your data */ set(i, j, word) { if (i > this.i || j > this.j) consola.error(`Invalid parameters, i must be less than or equal to ${this.i}, and j must be greater than or equal to ${this.j}`); this.data[i][j] = word; return this; } /** * Override config * @param option */ setConfig(option) { this.option = defu(option, this.option); return this; } /** * Get raw data */ get rawData() { return this.data; } /** * Generate result */ toString() { const { table: { border }, cell: { paddingY } } = this.option; this.result = ""; this.calcColumnsWidth(); if (border) this.addBorderHeader(); for (let i = 0; i < this.data.length; i++) { let t = 0; while (t++ < paddingY) this.addPaddingYRow(); if (border) this.result += "\u2502"; for (let j = 0; j < this.data[i].length; j++) { this.result += " ".repeat(this.option.cell.paddingX); const rawWord = this.data[i][j]; const pureWordLen = stringWidth(rawWord); const gapLen = this.columnsWidth[j] - pureWordLen; if (this.option.cell.align === "right") { this.result = this.result + " ".repeat(gapLen) + rawWord; } else if (this.option.cell.align === "left") { this.result = this.result + rawWord + " ".repeat(gapLen); } else { const startLen = Math.floor(gapLen / 2); const endLen = gapLen - startLen; this.result = this.result + " ".repeat(startLen) + rawWord + " ".repeat(endLen); } this.result += " ".repeat(this.option.cell.paddingX); if (border) this.result += "\u2502"; if (j < this.data[i].length - 1) this.result += " ".repeat(this.option.cell.gapX); } this.result += "\n"; t = 0; while (t++ < paddingY) this.addPaddingYRow(); if (border && i < this.data.length - 1) this.addBorderRow(); } if (border) this.addBorderFooter(); return this.result; } } function calcMaxEffectWordLength(rawWords) { let len = 0; for (const word of rawWords) { const effectWordLength = stringWidth(word); len = Math.max(len, effectWordLength); } return len; } export { Tablegger };