bigint-crypto-utils/node_modules/table-layout/lib/columns.js

158 lines
4.2 KiB
JavaScript

'use strict'
const t = require('typical')
const arrayify = require('array-back')
const Column = require('./column')
const wrap = require('wordwrapjs')
const Cell = require('./cell')
const ansi = require('./ansi')
const _maxWidth = new WeakMap()
/**
* @module columns
*/
class Columns {
constructor (columns) {
this.list = []
arrayify(columns).forEach(this.add.bind(this))
}
/**
* sum of all generatedWidth fields
* @return {number}
*/
totalWidth () {
return this.list.length
? this.list.map(col => col.generatedWidth).reduce((a, b) => a + b)
: 0
}
totalFixedWidth () {
return this.getFixed()
.map(col => col.generatedWidth)
.reduce((a, b) => a + b, 0)
}
get (columnName) {
return this.list.find(column => column.name === columnName)
}
getResizable () {
return this.list.filter(column => column.isResizable())
}
getFixed () {
return this.list.filter(column => column.isFixed())
}
add (column) {
const col = column instanceof Column ? column : new Column(column)
this.list.push(col)
return col
}
set maxWidth (val) {
_maxWidth.set(this, val)
}
/**
* sets `generatedWidth` for each column
* @chainable
*/
autoSize () {
const maxWidth = _maxWidth.get(this)
/* size */
this.list.forEach(column => {
column.generateWidth()
column.generateMinWidth()
})
/* adjust if user set a min or maxWidth */
this.list.forEach(column => {
if (t.isDefined(column.maxWidth) && column.generatedWidth > column.maxWidth) {
column.generatedWidth = column.maxWidth
}
if (t.isDefined(column.minWidth) && column.generatedWidth < column.minWidth) {
column.generatedWidth = column.minWidth
}
})
const width = {
total: this.totalWidth(),
view: maxWidth,
diff: this.totalWidth() - maxWidth,
totalFixed: this.totalFixedWidth(),
totalResizable: Math.max(maxWidth - this.totalFixedWidth(), 0)
}
/* adjust if short of space */
if (width.diff > 0) {
/* share the available space between resizeable columns */
let resizableColumns = this.getResizable()
resizableColumns.forEach(column => {
column.generatedWidth = Math.floor(width.totalResizable / resizableColumns.length)
})
/* at this point, the generatedWidth should never end up bigger than the contentWidth */
const grownColumns = this.list.filter(column => column.generatedWidth > column.contentWidth)
const shrunkenColumns = this.list.filter(column => column.generatedWidth < column.contentWidth)
let salvagedSpace = 0
grownColumns.forEach(column => {
const currentGeneratedWidth = column.generatedWidth
column.generateWidth()
salvagedSpace += currentGeneratedWidth - column.generatedWidth
})
shrunkenColumns.forEach(column => {
column.generatedWidth += Math.floor(salvagedSpace / shrunkenColumns.length)
})
/* if, after autosizing, we still don't fit within maxWidth then give up */
}
return this
}
/**
* Factory method returning all distinct columns from input
* @param {object[]} - input recordset
* @return {module:columns}
*/
static getColumns (rows) {
var columns = new Columns()
arrayify(rows).forEach(row => {
for (let columnName in row) {
let column = columns.get(columnName)
if (!column) {
column = columns.add({ name: columnName, contentWidth: 0, minContentWidth: 0 })
}
let cell = new Cell(row[columnName], column)
let cellValue = cell.value
if (ansi.has(cellValue)) {
cellValue = ansi.remove(cellValue)
}
if (cellValue.length > column.contentWidth) column.contentWidth = cellValue.length
let longestWord = getLongestWord(cellValue)
if (longestWord > column.minContentWidth) {
column.minContentWidth = longestWord
}
if (!column.contentWrappable) column.contentWrappable = wrap.isWrappable(cellValue)
}
})
return columns
}
}
function getLongestWord (line) {
const words = wrap.getChunks(line)
return words.reduce((max, word) => {
return Math.max(word.length, max)
}, 0)
}
module.exports = Columns