styled-cli-table
Terminal table rendering library with cascading style system and composable architecture.
Install
npm i styled-cli-table
Overview by examples
Usage:
; ; // for CommonJS usage replace 'module' to 'commonjs' in path const data = '#' 'name' 'price' 'quantity' 'total' 1 'apple' 2 3 6 2 'banana' 1 10 10 3 'lemon' 15 3 45; const styles = ... // expands to { borderLeft: true, borderRight: true, borderTop: true, borderBottom: true } borderCharacters: single paddingLeft: 1 paddingRight: 1 rows: // rows styles 0: // first row styles align: 'center' columns: // columns styles 1: // second column styles minWidth: 6 maxWidth: 12 // width calculated by the content, but will be at least 6 and at most 12 characters -1: // last column styles width: 9 // fixed width // widths of the remaining columns are calculated by the content ; const table = data styles; console; // print all at once // or print line by linefor const line of table console;
Output:
┌───┬────────┬───────┬──────────┬─────────┐│ # │ name │ price │ quantity │ total │├───┼────────┼───────┼──────────┼─────────┤│ 1 │ apple │ 2 │ 3 │ 6 │├───┼────────┼───────┼──────────┼─────────┤│ 2 │ banana │ 1 │ 10 │ 10 │├───┼────────┼───────┼──────────┼─────────┤│ 3 │ lemon │ 1.5 │ 3 │ 4.5 │└───┴────────┴───────┴──────────┴─────────┘
Vertical styles work as well:
const data = '#' 'name' 'price' 'quantity' 'total' 1 'apple' 'Granny Smith' /* arrays force rows to grow by height */ 2 3 6 2 'banana' 1 10 10 3 'lemon' 15 3 45; const styles = ... borderCharacters: single paddingLeft: 1 paddingRight: 1 rows: 0: align: 'center' paddingTop: 1 paddingBottom: 1 2: height: 3 // fixed row height verticalAlign: 'bottom' ;
Output:
┌───┬──────────────┬───────┬──────────┬───────┐│ │ │ │ │ ││ # │ name │ price │ quantity │ total ││ │ │ │ │ │├───┼──────────────┼───────┼──────────┼───────┤│ 1 │ apple │ 2 │ 3 │ 6 ││ │ Granny Smith │ │ │ │├───┼──────────────┼───────┼──────────┼───────┤│ │ │ │ │ ││ │ │ │ │ ││ 2 │ banana │ 1 │ 10 │ 10 │├───┼──────────────┼───────┼──────────┼───────┤│ 3 │ lemon │ 1.5 │ 3 │ 4.5 │└───┴──────────────┴───────┴──────────┴───────┘
Custom borders:
const styles = borderCharacters: single paddingLeft: 1 paddingRight: 1 rows: 0: align: 'center' borderTop: true borderBottom: true -1: borderBottom: true { // functional styles (return value applies to all columns) return borderLeft: true borderRight: true ; };
Output:
┌───┬────────┬───────┬──────────┬───────┐│ # │ name │ price │ quantity │ total │├───┼────────┼───────┼──────────┼───────┤│ 1 │ apple │ 2 │ 3 │ 6 ││ 2 │ banana │ 1 │ 10 │ 10 ││ 3 │ lemon │ 1.5 │ 3 │ 4.5 │└───┴────────┴───────┴──────────┴───────┘
Conditional data rendering:
const styles = borderCharacters: single paddingLeft: 1 paddingRight: 1 rows: 0: borderTop: true borderBottom: true -1: borderBottom: true { return borderLeft: true borderRight: true ; } { const isNumber = typeof value === 'number'; return content: isNumber ? value : `''` // non-numbers are quoted align: rowIndex === 0 ? 'center' : isNumber ? 'right' : 'left' // first row aligned to center, numbers to right, other to left ; }
Output:
┌─────┬──────────┬─────────┬────────────┬─────────┐│ '#' │ 'name' │ 'price' │ 'quantity' │ 'total' │├─────┼──────────┼─────────┼────────────┼─────────┤│ 1 │ 'apple' │ 2 │ 3 │ 6 ││ 2 │ 'banana' │ 1 │ 10 │ 10 ││ 3 │ 'lemon' │ 1.5 │ 3 │ 4.5 │└─────┴──────────┴─────────┴────────────┴─────────┘
Terminal color styling supported by seperate class:
;; const styles = ... borderCharacters: single paddingLeft: 1 paddingRight: 1 backgroundColor: colorbgGreen color: colorblue rows: 0: align: 'center' color: coloryellow ; const table = data styles;
Output:
┌───┬────────┬───────┬──────────┬───────┐ │ # │ name │ price │ quantity │ total │ ├───┼────────┼───────┼──────────┼───────┤ │ 1 │ apple │ 2 │ 3 │ 6 │ │ 2 │ banana │ 1 │ 10 │ 10 │ │ 3 │ lemon │ 1.5 │ 3 │ 4.5 │ └───┴────────┴───────┴──────────┴───────┘
Library overview
The library consists of the following main parts:
Style
class that provides a cascading style system;StyledTable
class that allows to iterate through tabular data with applying cascading styles;printline
module that provides mutable string buffers, that are used by therenderers
;renderers
module consisting of mixin classes that implement various styles;styles
module which consists of styles helper functions and types;ComposableRenderedStyledTable
function that composesStyledTable
and variousrenderers
mixin classes to the final reusable class.
Style
class
Styles, cascading and the Styles are plain JavaScript objects consisting of keys that denote the names of styles and values that denote the corresponding values:
const styles = align: 'center' width: 10 borderTop: true;
To create cascading styles the Style
class is used:
; const style = ;style;style; const align // 'center' padLeft // 2 padRight // 4 width // 10 borderBottom // undefined} = style; // get multiple styles at once const width = style; // get single style
StyledTable
class
The StyledTable
class allows to iterate through tabular data (wich are two-dimensional arrays) while apply cascading styles.
There are four levels of cascade (from low priority to high): table, columns, rows and cells.
; const data = '#' 'name' 'price' 'quantity' 'total' 1 'apple' 2 3 6 2 'banana' 1 10 10 3 'lemon' 15 3 45; const styles = // table level styles width: 10 align: 'left' columns: // column level styles 0: // first column width: 15 -1: // last column width: 20 rows: // row level styles 0: align: 'center' cells: // cell level styles 1: 1: // cell at data[1][1] align: 'right' width: 12 ; const table = data styles;for const row of tablerows for const cell of rowcells const styles = cellstyle; console;
The columns
, rows
and cells
properties of table styles can be the functions returning styles objects. This allows you to define conditional styles.
const styles = // table level styles width: 10 align: 'left' { // functional column styles if columnIndex === 0 // first column return width: 15 ; if reversedColumnIndex === -1 // last column return width: 20 ; } { // functional row styles if rowIndex === 0 // first row return align: 'center' ; } { // functional cell styles if rowIndex === 1 && columnIndex === 1 // cell at data[1][1] return align: 'right' width: 12 ; };
The printlines and buffers
The printlines are some sort of mutable strings with predefined length. printline
module provides two kind of printlines: PrintLine
which is basic printline and BreakPrintLine
, which adds another layer of line breaks (can be used to add terminal styles for example).
;; const line = 10 '.'; // width and space characterline;line;line; // out of rangeconsole; // 'aaa..bb...' const line2 = 10 '.';line2;line2;line2;line2;console; // 'aaa..<i>bb</i>...'
The AbstractPrintLineBuffer
class allows to allocate some number of underlying printlines, fill them and consume filled lines on demand. There are two predifined buffers: PrintLineBuffer
and BreakPrintLineBuffer
.
; const buffer = '.'; buffer; // allocates 2 lines of width 11buffer; // filling first linebuffer;buffer; // filling second linebuffer; // out of range for const line of buffer // consumes first two lines of buffer console; // 'aaa.....bbb' and 'xxx........' // buffer is empty now, you can allocate new lines
Renderers
The renderer is a class that have render
method that accepts StyledTable
and generates its string representation line by line.
The core class of the library is AbstractBufferedRenderer
, which provides two-pass buffered rendering: on the first pass the necessary styles are computed (e.g. column widths and row heights), on the second pass the table data are rendered using computed styles and printline buffer.
Here is a simplified version of the render
method of class AbstractBufferedRenderer
:
To implement concrete styles or features, mixin functions are used. There are several predefined mixins provided by the library.
GenericBufferedRenderer
Accepts the subclass of AbstractPrintLineBuffer
(e.g. PrintLineBuffer
or BreakPrintLineBuffer
) and returns concrete subclass of AbstractBufferedRenderer
parameterized with this buffer. This is the base mixin for all library mixins.
FlexSizeRenderer
Computes the height and width of cells depending on the content. You can specify a fixed width or height using width
and height
styles respectively. You can specify a fixed range of width or height using styles minWidth
, maxWidth
, minHeight
and maxHeight
respectively. Usually this mixin should be used immediately after GenericBufferedRenderer
.
PaddingRenderer
Adds horizontal and vertical paddings. You can set paddings explicitly by setting paddingTop
, paddingRight
, paddingBottom
or paddingLeft
properties, or by using a helper padding
function:
; const styles = ... // expands to { paddingTop: 0, paddingRight: 1, paddingBottom: 0, paddingLeft: 1 };
AlignRenderer
Aligns the content horizontally and vertically. To align the content horizontally set align
property to left
, center
or right
. To align the content vertically set verticallAlign
property to top
, middle
or bottom
.
BorderRenderer
Renders borders around cells. You can set borders explicitly by setting borderTop
, borderRight
, borderBottom
or borderLeft
properties, or by using a helper border
function. It is also necessary to explicitly set the border characters by specifiyng borderCharacters
proprty, otherwise the borders will not be displayed. The library provides two character sets by default: single
and double
:
; const styles = ... // expands to { borderTop: true, borderRight: true, borderBottom: true, borderLeft: true } borderCharacters: single;
You can create your own sets by using the borderCharacters
function:
; const styles = ... borderCharacters: ;
ColorRenderer
and BackgroundColorRenderer
Allows you to apply terminal styles for the entire cell (BackgroundColorRenderer
) or for the content only (ColorRenderer
). To set whole cell style use backgroundColor
property. To set only content style use color
property. Predefined terminal styles can be found inside styles/color
module:
; const styles = backgroundColor: colorbgYellow color: colorblack;
These mixins should be use with BreakPrintLineBuffer
.
Composing renderers
You can compose renderers by chaining mixin functions and providing printline buffer to break the chain, e.g.:
;;; const CustomRenderer = ;const renderer = ;const table = data styles;console;
To simplify creation and usage of renderers, GenericRenderedStyledTable
function is provided. It accepts the base class as first parameter, and one or more mixins functions as rest, chains them internally and returns new class that can be used to create and render tabular data:
;;; const CustomStyledTable = ; const table = data styles;console;
Creating your own renderers
;;;; { return { super; } ;} { return contentlength > width ? content + cropString : content;} const CustomStyledTable = ; const data = '#' 'name' 'price' 'quantity' 'total' 1 'apple' 2 3 6 2 'banana' 1 10 10 3 'strawberry' 3 3 9; const styles = ... borderCharacters: single paddingLeft: 1 paddingRight: 1 crop: '..' rows: 0: align: 'center' columns: 1: width: 8 ; const table = data styles;console;
Output:
┌───┬────────┬───────┬──────────┬───────┐│ # │ name │ price │ quantity │ total │├───┼────────┼───────┼──────────┼───────┤│ 1 │ apple │ 2 │ 3 │ 6 │├───┼────────┼───────┼──────────┼───────┤│ 2 │ banana │ 1 │ 10 │ 10 │├───┼────────┼───────┼──────────┼───────┤│ 3 │ stra.. │ 3 │ 3 │ 9 │└───┴────────┴───────┴──────────┴───────┘
The same in Typescript:
;;;;;;;;
Browser usage example
styled-cli-table example