UNPKG

symfony-style-console

Version:

Use the style and utilities of the Symfony Console in Node.js

653 lines (652 loc) 23.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var Helper_1 = require("./Helper"); var TableCell_1 = require("./TableCell"); var TableSeparator_1 = require("./TableSeparator"); var TableStyle_1 = require("./TableStyle"); /** * Provides helpers to display a table. * * @author Fabien Potencier <fabien@symfony.com> * * Original PHP Class * * @author Саша Стаменковић <umpirsky@gmail.com> * @author Abdellatif Ait boudad <a.aitboudad@gmail.com> * @author Max Grigorian <maxakawizard@gmail.com> * * @author Florian Reuschel <florian@loilo.de> * * Port to TypeScript */ var Table = /** @class */ (function () { function Table(output) { /** * Table headers. */ this.headers = []; /** * Table rows. */ this.rows = []; /** * Column widths cache. */ this.effectiveColumnWidths = []; /** * Column Styles */ this.columnStyles = []; /** * User set column widths */ this.columnWidths = []; this.output = output; if (!Table.styles) { Table.styles = Table.initStyles(); } } /** * Sets a style definition. * * @param name The style name * @param style A TableStyle instance */ Table.setStyleDefinition = function (name, style) { if (!Table.styles) { Table.styles = Table.initStyles(); } Table.styles[name] = style; }; /** * Gets a style definition by name. * * @param name The style name * @return TableStyle */ Table.getStyleDefinition = function (name) { if (!Table.styles) { Table.styles = Table.initStyles(); } if (Table.styles[name]) { return Table.styles[name]; } throw new Error("Style \"" + name + "\" is not defined."); }; Table.initStyles = function () { var borderless = new TableStyle_1.default(); borderless .setHorizontalBorderChar('=') .setVerticalBorderChar(' ') .setCrossingChar(' '); var compact = new TableStyle_1.default(); compact .setHorizontalBorderChar('') .setVerticalBorderChar(' ') .setCrossingChar('') .setCellRowContentFormat('%s'); var styleGuide = new TableStyle_1.default(); styleGuide .setHorizontalBorderChar('-') .setVerticalBorderChar(' ') .setCrossingChar(' ') .setCellHeaderFormat('%s'); return { default: new TableStyle_1.default(), borderless: borderless, compact: compact, 'symfony-style-guide': styleGuide }; }; /** * Sets table style. * * @param name The style name or a TableStyle instance * @return this */ Table.prototype.setStyle = function (name) { this.style = this.resolveStyle(name); return this; }; /** * Gets the current table style. * * @return TableStyle */ Table.prototype.getStyle = function () { return this.style; }; /** * Sets table column style. * * @param columnIndex Column index * @param name The style name or a TableStyle instance * * @return this */ Table.prototype.setColumnStyle = function (columnIndex, name) { columnIndex = Math.round(columnIndex); this.columnStyles[columnIndex] = this.resolveStyle(name); return this; }; /** * Gets the current style for a column. * * If style was not set, it returns the global table style. * * @param columnIndex Column index * * @return TableStyle */ Table.prototype.getColumnStyle = function (columnIndex) { if (this.columnStyles[columnIndex]) { return this.columnStyles[columnIndex]; } return this.getStyle(); }; /** * Sets the minimum width of a column. * * @param columnIndex Column index * @param width Minimum column width in characters * * @return this */ Table.prototype.setColumnWidth = function (columnIndex, width) { this.columnWidths[Math.round(columnIndex)] = Math.round(width); return this; }; /** * Sets the minimum width of all columns. * * @param widths * * @return this */ Table.prototype.setColumnWidths = function (widths) { this.columnWidths = []; for (var index = 0; index < widths.length; index++) { if (typeof widths[index] === 'undefined') continue; var width = widths[index]; this.setColumnWidth(index, width); } return this; }; Table.prototype.setHeaders = function (headers) { var isNestedRows = function (headers) { return !(headers.length && !Array.isArray(headers[0])); }; if (!isNestedRows(headers)) { headers = [headers]; } this.headers = headers; return this; }; Table.prototype.setRows = function (rows) { this.rows = []; return this.addRows(rows); }; Table.prototype.addRows = function (rows) { for (var _i = 0, rows_1 = rows; _i < rows_1.length; _i++) { var row = rows_1[_i]; this.addRow(row); } return this; }; Table.prototype.addRow = function (row) { if (row instanceof TableSeparator_1.default) { this.rows.push(row); return this; } if (!Array.isArray(row)) { throw new Error('A row must be an array or a TableSeparator instance.'); } this.rows.push(row); return this; }; Table.prototype.setRow = function (column, row) { this.rows[column] = row; return this; }; /** * Renders table to output. * * Example: * +---------------+-----------------------+------------------+ * | ISBN | Title | Author | * +---------------+-----------------------+------------------+ * | 99921-58-10-7 | Divine Comedy | Dante Alighieri | * | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | * | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | * +---------------+-----------------------+------------------+ */ Table.prototype.render = function () { this.calculateNumberOfColumns(); var rows = this.buildTableRows(this.rows); var headers = this.buildTableRows(this.headers); this.calculateColumnsWidth(headers.concat(rows)); this.renderRowSeparator(); if (headers.length) { for (var _i = 0, headers_1 = headers; _i < headers_1.length; _i++) { var header = headers_1[_i]; this.renderRow(header, this.style.getCellHeaderFormat()); this.renderRowSeparator(); } } for (var _a = 0, rows_2 = rows; _a < rows_2.length; _a++) { var row = rows_2[_a]; if (row instanceof TableSeparator_1.default) { this.renderRowSeparator(); } else { this.renderRow(row, this.style.getCellRowFormat()); } } if (rows.length) { this.renderRowSeparator(); } this.cleanup(); }; /** * Gets number of columns by row. * * @param row * * @return int */ Table.prototype.getNumberOfColumns = function (row) { var columns = row.filter(function (cell) { return typeof cell !== 'undefined'; }).length; for (var _i = 0, row_1 = row; _i < row_1.length; _i++) { var column = row_1[_i]; columns += column instanceof TableCell_1.default ? column.getColspan() - 1 : 0; } return columns; }; /** * Gets list of columns for the given row. * * @param array row * * @return array */ Table.prototype.getRowColumns = function (row) { var columns = Helper_1.range(0, this.numberOfColumns - 1); var _loop_1 = function (cellKey) { if (typeof row[cellKey] === 'undefined') return "continue"; var cell = row[cellKey]; if (cell instanceof TableCell_1.default && cell.getColspan() > 1) { // exclude grouped columns. var diffRange_1 = Helper_1.range(cellKey + 1, cellKey + cell.getColspan() - 1); columns = columns.filter(function (column) { return !Helper_1.arrContains(diffRange_1, column); }); } }; for (var cellKey = 0; cellKey < row.length; cellKey++) { _loop_1(cellKey); } return columns; }; /** * Gets column width. * * @return int */ Table.prototype.getColumnSeparatorWidth = function () { return Helper_1.sprintf(this.style.getBorderFormat(), this.style.getVerticalBorderChar()).length; }; /** * Gets cell width. * * @param row * @param column * * @return int */ Table.prototype.getCellWidth = function (row, column) { var cellWidth = 0; if (row[column]) { var cell = row[column]; var cellStr = String(cell); cellWidth = Helper_1.lengthWithoutDecoration(this.output.getFormatter(), cellStr); } var columnWidth = this.columnWidths[column] || 0; return Math.max(cellWidth, columnWidth); }; /** * Renders horizontal header separator. * * Example: +-----+-----------+-------+ */ Table.prototype.renderRowSeparator = function () { var count = this.numberOfColumns; if (!count) { return; } if (!this.style.getHorizontalBorderChar() && !this.style.getCrossingChar()) { return; } var markup = this.style.getCrossingChar(); for (var column = 0; column < count; ++column) { markup += this.style .getHorizontalBorderChar() .repeat(this.effectiveColumnWidths[column]) + this.style.getCrossingChar(); } this.output.writeln(Helper_1.sprintf(this.style.getBorderFormat(), markup)); }; /** * Renders vertical column separator. */ Table.prototype.renderColumnSeparator = function () { return Helper_1.sprintf(this.style.getBorderFormat(), this.style.getVerticalBorderChar()); }; /** * Renders table row. * * Example: | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | * * @param row * @param cellFormat */ Table.prototype.renderRow = function (row, cellFormat) { if (!row.length) { return; } var rowContent = this.renderColumnSeparator(); for (var _i = 0, _a = this.getRowColumns(row); _i < _a.length; _i++) { var column = _a[_i]; rowContent += this.renderCell(row, column, cellFormat); rowContent += this.renderColumnSeparator(); } this.output.writeln(rowContent); }; /** * Renders table cell with padding. * * @param row * @param column * @param cellFormat */ Table.prototype.renderCell = function (row, column, cellFormat) { var cell = row[column] || ''; var cellStr = String(cell); var width = this.effectiveColumnWidths[column]; if (cell instanceof TableCell_1.default && cell.getColspan() > 1) { // add the width of the following columns(numbers of colspan). for (var _i = 0, _a = Helper_1.range(column + 1, column + cell.getColspan() - 1); _i < _a.length; _i++) { var nextColumn = _a[_i]; width += this.getColumnSeparatorWidth() + this.effectiveColumnWidths[nextColumn]; } } var style = this.getColumnStyle(column); if (cell instanceof TableSeparator_1.default) { return Helper_1.sprintf(style.getBorderFormat(), style.getHorizontalBorderChar().repeat(width)); } width += cellStr.length - Helper_1.lengthWithoutDecoration(this.output.getFormatter(), cellStr); var content = Helper_1.sprintf(style.getCellRowContentFormat(), cell); return Helper_1.sprintf(cellFormat, Helper_1.strPad(content, width, style.getPaddingChar(), style.getPadType())); }; /** * Calculate number of columns for this table. */ Table.prototype.calculateNumberOfColumns = function () { if (null != this.numberOfColumns) { return; } var columns = [0]; for (var _i = 0, _a = this.headers.concat(this.rows); _i < _a.length; _i++) { var row = _a[_i]; if (row instanceof TableSeparator_1.default) { continue; } columns.push(this.getNumberOfColumns(row)); } this.numberOfColumns = Math.max.apply(Math, columns); }; Table.prototype.buildTableRows = function (rows) { var unmergedRows = []; rows = rows.slice(0); for (var rowKey = 0; rowKey < rows.length; ++rowKey) { if (typeof rows[rowKey] === 'undefined') continue; if (rows[rowKey] instanceof TableSeparator_1.default) continue; rows = this.fillNextRows(rows, rowKey); var row = rows[rowKey]; // Remove any new line breaks and replace it with a new line for (var column = 0; column < row.length; column++) { if (typeof row[column] === 'undefined') continue; var cell = row[column]; var cellStr = String(cell); if (cellStr.includes('\n')) { continue; } var lines = cellStr.split('\n'); for (var lineKey = 0; lineKey < lines.length; lineKey++) { var line = lines[lineKey]; if (cell instanceof TableCell_1.default) { line = new TableCell_1.default(line, { colspan: cell.getColspan() }); } if (0 === lineKey) { row[column] = line; } else { if (!Array.isArray(unmergedRows[rowKey])) unmergedRows[rowKey] = []; if (!Array.isArray(unmergedRows[rowKey][lineKey])) unmergedRows[rowKey][lineKey] = []; unmergedRows[rowKey][lineKey][column] = line; } } } } var tableRows = []; for (var rowKey = 0; rowKey < rows.length; rowKey++) { if (typeof rows[rowKey] === 'undefined') continue; if (rows[rowKey] instanceof TableSeparator_1.default) continue; var row = rows[rowKey]; tableRows.push(this.fillCells(row)); if (unmergedRows[rowKey]) { tableRows = tableRows.concat(unmergedRows[rowKey]); } } return tableRows; }; /** * fill rows that contains rowspan > 1. * * @param inputRows * @param line * * @return array */ Table.prototype.fillNextRows = function (inputRows, line) { var unmergedRows = ([]); if (inputRows[line] instanceof TableSeparator_1.default) return inputRows.slice(0); var rows = inputRows.slice(0); for (var column = 0; column < rows[line].length; column++) { var cell = rows[line][column]; if (cell instanceof TableCell_1.default && cell.getRowspan() > 1) { var cellStr = String(cell); var nbLines = cell.getRowspan() - 1; var lines = [cellStr]; if (cellStr.includes('\n')) { lines = cellStr.split('\n'); nbLines = lines.length > nbLines ? Helper_1.countOccurences(cellStr, '\n') : nbLines; rows[line][column] = new TableCell_1.default(lines[0], { colspan: cell.getColspan() }); delete lines[0]; } // create a two dimensional array (rowspan x colspan) unmergedRows = Helper_1.arrayReplaceRecursive(Helper_1.arrayFill(line + 1, nbLines, []), unmergedRows); for (var unmergedRowKey = 0; unmergedRowKey < unmergedRows.length; unmergedRowKey++) { if (typeof unmergedRows[unmergedRowKey] === 'undefined') continue; var unmergedRow = unmergedRows[unmergedRowKey]; var value = lines[unmergedRowKey - line] || ''; unmergedRows[unmergedRowKey][column] = new TableCell_1.default(value, { colspan: cell.getColspan() }); if (nbLines === unmergedRowKey - line) { break; } } } } for (var unmergedRowKey = 0; unmergedRowKey < unmergedRows.length; unmergedRowKey++) { if (typeof unmergedRows[unmergedRowKey] === 'undefined') continue; var unmergedRow = unmergedRows[unmergedRowKey]; // we need to know if unmergedRow will be merged or inserted into rows if (typeof rows[unmergedRowKey] !== 'undefined' && Array.isArray(rows[unmergedRowKey]) && this.getNumberOfColumns(rows[unmergedRowKey]) + this.getNumberOfColumns(unmergedRows[unmergedRowKey]) <= this.numberOfColumns) { for (var cellKey = 0; cellKey < unmergedRow.length; cellKey++) { if (typeof unmergedRow[cellKey] === 'undefined') continue; var cell = unmergedRow[cellKey]; // insert cell into row at cellKey position rows[unmergedRowKey].splice(cellKey, 0, cell); } } else { var row = this.copyRow(rows, unmergedRowKey - 1); for (var column = 0; column < unmergedRow.length; column++) { if (typeof unmergedRow[column] === 'undefined') continue; var cell = unmergedRow[column]; var cellStr = String(cell); if (cellStr.length) { row[column] = unmergedRow[column]; } } rows.splice(unmergedRowKey, 0, row); } } return rows; }; /** * fill cells for a row that contains colspan > 1. * * @param row * * @return array */ Table.prototype.fillCells = function (row) { var newRow = []; for (var column = 0; column < row.length; column++) { if (typeof row[column] === 'undefined') continue; var cell = row[column]; newRow.push(cell); if (cell instanceof TableCell_1.default && cell.getColspan() > 1) { for (var _i = 0, _a = Helper_1.range(column + 1, column + cell.getColspan() - 1); _i < _a.length; _i++) { var position = _a[_i]; // insert empty value at column position newRow.push(''); } } } return newRow || row; }; /** * @param rows * @param line * * @return array */ Table.prototype.copyRow = function (rows, line) { var row = rows[line].slice(0); for (var cellKey = 0; cellKey < row.length; cellKey++) { if (typeof row[cellKey] === 'undefined') continue; var cellValue = row[cellKey]; row[cellKey] = ''; if (cellValue instanceof TableCell_1.default) { row[cellKey] = new TableCell_1.default('', { colspan: cellValue.getColspan() }); } } return row; }; /** * Calculates columns widths. * * @param array rows */ Table.prototype.calculateColumnsWidth = function (rows) { rows = rows.slice(0); for (var column = 0; column < this.numberOfColumns; ++column) { var lengths = []; for (var _i = 0, rows_3 = rows; _i < rows_3.length; _i++) { var row = rows_3[_i]; if (row instanceof TableSeparator_1.default) { continue; } row = row.slice(0); for (var i = 0; i < row.length; i++) { if (typeof row[i] === 'undefined') continue; var cell = row[i]; if (cell instanceof TableCell_1.default) { var textContent = Helper_1.removeDecoration(this.output.getFormatter(), String(cell)); var textLength = textContent.length; if (textLength > 0) { var contentColumns = Helper_1.chunkString(textContent, Math.ceil(textLength / cell.getColspan())); if (contentColumns === false) { throw new Error("Could not chunk string: " + textContent); } for (var position = 0; position < contentColumns.length; position++) { if (typeof contentColumns[position] === 'undefined') continue; var content = contentColumns[position]; row[i + position] = content; } } } } lengths.push(this.getCellWidth(row, column)); } this.effectiveColumnWidths[column] = Math.max.apply(Math, lengths) + this.style.getCellRowContentFormat().length - 2; } }; /** * Called after rendering to cleanup cache data. */ Table.prototype.cleanup = function () { this.effectiveColumnWidths = []; this.numberOfColumns = null; }; Table.prototype.resolveStyle = function (name) { if (name instanceof TableStyle_1.default) { return name; } if (Table.styles[name]) { return Table.styles[name]; } throw new Error("Style \"" + name + "\" is not defined."); }; return Table; }()); exports.default = Table;