202 lines
6.1 KiB
JavaScript
202 lines
6.1 KiB
JavaScript
'use strict'
|
|
const os = require('os')
|
|
|
|
/**
|
|
* @module table-layout
|
|
*/
|
|
|
|
/**
|
|
* Recordset data in (array of objects), text table out.
|
|
* @alias module:table-layout
|
|
* @example
|
|
* > Table = require('table-layout')
|
|
* > jsonData = [{
|
|
* col1: 'Some text you wish to read in table layout',
|
|
* col2: 'And some more text in column two. '
|
|
* }]
|
|
* > table = new Table(jsonData, { maxWidth: 30 })
|
|
* > console.log(table.toString())
|
|
* Some text you And some more
|
|
* wish to read text in
|
|
* in table column two.
|
|
* layout
|
|
*/
|
|
class Table {
|
|
|
|
/**
|
|
* @param {object[]} - input data
|
|
* @param [options] {object} - optional settings
|
|
* @param [options.maxWidth] {number} - maximum width of layout
|
|
* @param [options.noWrap] {boolean} - disable wrapping on all columns
|
|
* @param [options.noTrim] {boolean} - disable line-trimming
|
|
* @param [options.break] {boolean} - enable word-breaking on all columns
|
|
* @param [options.columns] {module:table-layout~columnOption} - array of column-specific options
|
|
* @param [options.ignoreEmptyColumns] {boolean} - if set, empty columns or columns containing only whitespace are not rendered.
|
|
* @param [options.padding] {object} - Padding values to set on each column. Per-column overrides can be set in the `options.columns` array.
|
|
* @param [options.padding.left] {string} - Defaults to a single space.
|
|
* @param [options.padding.right] {string} - Defaults to a single space.
|
|
* @alias module:table-layout
|
|
*/
|
|
constructor (data, options) {
|
|
let ttyWidth = (process && (process.stdout.columns || process.stderr.columns)) || 0
|
|
|
|
/* Windows quirk workaround */
|
|
if (ttyWidth && os.platform() === 'win32') ttyWidth--
|
|
|
|
let defaults = {
|
|
padding: {
|
|
left: ' ',
|
|
right: ' '
|
|
},
|
|
maxWidth: ttyWidth || 80,
|
|
columns: []
|
|
}
|
|
|
|
const extend = require('deep-extend')
|
|
this.options = extend(defaults, options)
|
|
this.load(data)
|
|
}
|
|
|
|
load (data) {
|
|
const Rows = require('./lib/rows')
|
|
const Columns = require('./lib/columns')
|
|
|
|
let options = this.options
|
|
|
|
/* remove empty columns */
|
|
if (options.ignoreEmptyColumns) {
|
|
data = Rows.removeEmptyColumns(data)
|
|
}
|
|
|
|
this.columns = Columns.getColumns(data)
|
|
this.rows = new Rows(data, this.columns)
|
|
|
|
/* load default column properties from options */
|
|
this.columns.maxWidth = options.maxWidth
|
|
this.columns.list.forEach(column => {
|
|
if (options.padding) column.padding = options.padding
|
|
if (options.noWrap) column.noWrap = options.noWrap
|
|
if (options.break) {
|
|
column.break = options.break
|
|
column.contentWrappable = true
|
|
}
|
|
})
|
|
|
|
/* load column properties from options.columns */
|
|
options.columns.forEach(optionColumn => {
|
|
let column = this.columns.get(optionColumn.name)
|
|
if (column) {
|
|
if (optionColumn.padding) {
|
|
column.padding.left = optionColumn.padding.left
|
|
column.padding.right = optionColumn.padding.right
|
|
}
|
|
if (optionColumn.width) column.width = optionColumn.width
|
|
if (optionColumn.maxWidth) column.maxWidth = optionColumn.maxWidth
|
|
if (optionColumn.minWidth) column.minWidth = optionColumn.minWidth
|
|
if (optionColumn.noWrap) column.noWrap = optionColumn.noWrap
|
|
if (optionColumn.break) {
|
|
column.break = optionColumn.break
|
|
column.contentWrappable = true
|
|
}
|
|
}
|
|
})
|
|
|
|
this.columns.autoSize()
|
|
return this
|
|
}
|
|
|
|
getWrapped () {
|
|
const wrap = require('wordwrapjs')
|
|
|
|
this.columns.autoSize()
|
|
return this.rows.list.map(row => {
|
|
let line = []
|
|
row.forEach((cell, column) => {
|
|
if (column.noWrap) {
|
|
line.push(cell.value.split(/\r\n?|\n/))
|
|
} else {
|
|
line.push(wrap.lines(cell.value, {
|
|
width: column.wrappedContentWidth,
|
|
break: column.break,
|
|
noTrim: this.options.noTrim
|
|
}))
|
|
}
|
|
})
|
|
return line
|
|
})
|
|
}
|
|
|
|
getLines () {
|
|
var wrappedLines = this.getWrapped()
|
|
var lines = []
|
|
wrappedLines.forEach(wrapped => {
|
|
let mostLines = getLongestArray(wrapped)
|
|
for (let i = 0; i < mostLines; i++) {
|
|
let line = []
|
|
wrapped.forEach(cell => {
|
|
line.push(cell[i] || '')
|
|
})
|
|
lines.push(line)
|
|
}
|
|
})
|
|
return lines
|
|
}
|
|
|
|
/**
|
|
* Identical to `.toString()` with the exception that the result will be an array of lines, rather than a single, multi-line string.
|
|
* @returns {string[]}
|
|
*/
|
|
renderLines () {
|
|
var lines = this.getLines()
|
|
return lines.map(line => {
|
|
return line.reduce((prev, cell, index) => {
|
|
let column = this.columns.list[index]
|
|
return prev + padCell(cell, column.padding, column.generatedWidth)
|
|
}, '')
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Returns the input data as a text table.
|
|
* @returns {string}
|
|
*/
|
|
toString () {
|
|
return this.renderLines().join(os.EOL) + os.EOL
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Array of arrays in.. Returns the length of the longest one
|
|
* @returns {number}
|
|
* @private
|
|
*/
|
|
function getLongestArray (arrays) {
|
|
var lengths = arrays.map(array => array.length)
|
|
return Math.max.apply(null, lengths)
|
|
}
|
|
|
|
function padCell (cellValue, padding, width) {
|
|
const ansi = require('./lib/ansi')
|
|
const padEnd = require('lodash.padend')
|
|
var ansiLength = cellValue.length - ansi.remove(cellValue).length
|
|
cellValue = cellValue || ''
|
|
return (padding.left || '') +
|
|
padEnd(cellValue, width - padding.length() + ansiLength) +
|
|
(padding.right || '')
|
|
}
|
|
|
|
/**
|
|
* @typedef module:table-layout~columnOption
|
|
* @property name {string} - column name, must match a property name in the input
|
|
* @property [width] {number} - A specific column width. Supply either this or a min and/or max width.
|
|
* @property [minWidth] {number} - column min width
|
|
* @property [maxWidth] {number} - column max width
|
|
* @property [nowrap] {boolean} - disable wrapping for this column
|
|
* @property [break] {boolean} - enable word-breaking for this columns
|
|
* @property [padding] {object} - padding options
|
|
* @property [padding.left] {string} - a string to pad the left of each cell (default: `' '`)
|
|
* @property [padding.right] {string} - a string to pad the right of each cell (default: `' '`)
|
|
*/
|
|
|
|
module.exports = Table
|