Skip to content

Commit

Permalink
added support for column contents wrapping
Browse files Browse the repository at this point in the history
  • Loading branch information
NB10328 authored and NB10328 committed Nov 17, 2020
1 parent 272a39a commit a60d6db
Show file tree
Hide file tree
Showing 5 changed files with 382 additions and 13 deletions.
107 changes: 106 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ Ascii Table 3
[![Build stats](https://travis-ci.com/AllMightySauron/ascii-table3.png)](https://travis-ci.com/AllMightySauron/ascii-table3)
[![npm version](https://badge.fury.io/js/ascii-table3.png)](https://badge.fury.io/js/ascii-table3)

`ascii-table3` is a pure ascii table renderer and beautifier, heavily inspired by the ascii-table package created by Beau Sorensen <[email protected]> (http://github.com/sorensen). The original package lacked support for multiple table styles and that is what motivated me to create this new one.
`ascii-table3` is a pure ascii table renderer and beautifier, heavily inspired by the `ascii-table` package created by Beau Sorensen. The original package lacked support for multiple table styles and that is what motivated me to create this new one.

Currently with **over a dozen** predefined table styles, the collection style keeps growing. I am pretty sure there is a style for everyone. If not, you can even design your own custom stye and add it to the library!

Please direct any issues, suggestions or feature requests to the [ascii-table3 github] (https://github.com/AllMightySauron/ascii-table3) page.

Existing code for the original `ascii-table` package should run fine with very few changes (see examples below).

# Usage
Expand Down Expand Up @@ -146,11 +148,24 @@ var table = AsciiTable3.AsciiTable3('Data');
Returns wether a `val` is numeric or not, irrespective of its type.
* `val` - value to check
```javascript
AsciiTable3.isNumeric('test') // false
AsciiTable3.isNumeric(10) // true
AsciiTable3.isNumeric(3.14) // true
```
### AsciiTable3.isWhiteSpace(str)
Return whether this character is whitespace (used internally for word wrapping purposes).
* `str` - character to test
```javascript
AsciiTable3.isWhiteSpace(' '') // true
AsciiTable3.isWhiteSpace('\t') // true
AsciiTable3.isWhiteSpace('*') // false
```
### AsciiTable3.align(direction, val, len, [pad])
Expand Down Expand Up @@ -218,6 +233,30 @@ Example:
AsciiTable3.align(AsciiTable3.LEFT, 'hey', 7) // 'hey '
```
### AsciiTable3.wordWrap(str, maxWidth)
Wraps a string into multiple lines of a limited width.
* `str` - string to wrap
* `maxWidth` - maximum width for the wrapped string
```javascript
AsciiTable3.wordWrap('dummy', 5) // dummy
AsciiTable3.wordWrap('this is a test', 5)
// this
// is a
// test
AsciiTable3.wordWrap('this is a test', 3)
// thi
// s
// is
// a
// tes
// t
```
### AsciiTable3.truncateString(str, len)
Truncates a string up to a maximum number of characters (if needed).
Expand Down Expand Up @@ -957,6 +996,72 @@ table.setAlignRight(2);
table.getAlign(2) // AsciiTable3.RIGHT
```
#### instance.setWrapped(idx, [wrap])
Sets the wrapping property for a specific column (wrapped content will generate more than one data row if needed).
* `idx` - column to wrap (starts at 1).
* `wrap` - wrap boolean setting (default is true).
```javascript
var table =
new AsciiTable3.AsciiTable3('Sample table')
.setHeading('Name', 'Age', 'Eye color')
.setAlign(3, AsciiTable3.CENTER)
.addRowMatrix([
['James Bond', 41, 'blue'],
['Harry Potter', 18, 'brown'],
['Scooby Doo', 23, 'brown'],
['Mickey Mouse', 120, 'black']
]);
// first column width is 8 characters and wrapped
table.setWidth(1, 8).setWrapped(1);
console.log(table.toString());
```
```asciidoc
.------------------------------.
| Sample table |
:--------.--------.------------:
| Name | Age | Eye color |
:--------+--------+------------:
| James | 41 | blue |
| Bond | | |
| Harry | 18 | brown |
| Potter | | |
| Scooby | 23 | brown |
| Doo | | |
| Mickey | 120 | black |
| Mouse | | |
'--------'--------'------------'
```
#### instance.isWrapped(idx)
Gets the wrapping setting for a given column (true or false).
* `idx` - column to check (starts at 1)
```javascript
var table =
new AsciiTable3.AsciiTable3('Sample table')
.setHeading('Name', 'Age', 'Eye color')
.setAlign(3, AsciiTable3.CENTER)
.addRowMatrix([
['James Bond', 41, 'blue'],
['Harry Potter', 18, 'brown'],
['Scooby Doo', 23, 'brown'],
['Mickey Mouse', 120, 'black']
]);
// first column width is 8 characters and wrapped
table.setWidth(1, 8).setWrapped(1);
table.isWrapped(1) // true
table.isWrapped(2) // false
```
#### instance.setCellMargin(margin)
Sets internal margin for cell data (table default is 1).
Expand Down
185 changes: 176 additions & 9 deletions ascii-table3.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,18 @@ class AsciiTable3 {
return !isNaN(parseFloat(value)) && isFinite(value);
}

/**
* Returns on whether the character is white space.
* @static
* @param {string} x The character to test.
* @returns {boolean} Whether we have found a white char.
*/
static isWhiteSpace(x) {
var white = new RegExp(/^\s$/);

return white.test(x.charAt(0));
}

/**
* Generic string alignment.
* @static
Expand Down Expand Up @@ -144,6 +156,43 @@ class AsciiTable3 {
}
}

/**
* Wraps a string into multiple lines of a limited width.
* @param {string} str The string to wrap.
* @param {num} maxWidth The maximum width for the wrapped string.
* @returns {string} The wrapped string.
*/
static wordWrap(str, maxWidth) {
const NEW_LINE = "\n";

// make sure we have a string as parameter
str = '' + str;

var found = false;
var res = '';

while (str.length > maxWidth) {
found = false;
// Inserts new line at first whitespace of the line
for (var i = maxWidth - 1; i >= 0; i--) {
if (AsciiTable3.isWhiteSpace(str.charAt(i))) {
res += str.substring(0, i).trimStart() + NEW_LINE;
str = str.slice(i + 1);
found = true;
break;
}
}

// Inserts new line at maxWidth position, the word is too long to wrap
if (!found) {
res += str.substring(0, maxWidth).trimStart() + NEW_LINE;
str = str.slice(maxWidth);
}
}

return res + str.trimStart();
}

/**
* Truncates a string up to a maximum number of characters (if needed).
* @static
Expand Down Expand Up @@ -617,6 +666,43 @@ class AsciiTable3 {
return this.setAlign(idx, AlignmentEnum.CENTER);
}

/**
* Sets the wrapping property for a specific column (wrapped content will generate more than one data row if needed).
* @param {number} idx Column index to align (starts at 1).
* @param {boolean} wrap Whether to wrap the content (default is true).
* @returns {AsciiTable3} The AsciiTable3 object instance.
*/
setWrapped(idx, wrap = true) {
if (this.wrapping) {
// resize if needed
AsciiTable3.arrayResize(this.wrapping, idx);
} else {
// create array
this.wrapping = AsciiTable3.arrayFill(idx);
}

// arrays are 0-based
this.wrapping[idx - 1] = wrap;

return this;
}

/**
* Gets the wrapping setting for a given column.
* @param {number} idx Column index to get wrapping (starts at 1).
*/
isWrapped(idx) {
// wrapping defaults to false
var result = false;

if (this.wrapping && idx <= this.wrapping.length) {
// arrays are 0-based
result = this.wrapping[idx - 1];
}

return result;
}

/**
* Return the JSON representation of the table, this also allows us to call JSON.stringify on the instance.
* @returns {string} The table JSON representation.
Expand Down Expand Up @@ -747,19 +833,59 @@ class AsciiTable3 {
}

/**
* Get string with the rendering of a heading row.
* Get array of wrapped row data from a "normal" row.
* @private
* @param {*[]} row Row of data.
* @returns Array of data rows after word wrapping.
*/
getWrappedRows(row) {
// setup a new wrapped row
const wrappedRow = AsciiTable3.arrayFill(row.length);

var maxRows = 1;

// loop over columns and wrap
for (var col = 0; col < row.length; col++) {
const cell = row[col];

if (this.getWidth(col + 1) && this.isWrapped(col + 1)) {
wrappedRow[col] = AsciiTable3.wordWrap(cell, this.getWidth(col + 1) - this.getCellMargin() * 2).split("\n");

if (wrappedRow[col].length > maxRows) maxRows = wrappedRow[col].length;
} else {
wrappedRow[col] = [ cell ];
}
}

// create resulting array with (potentially) multiple rows
const result = AsciiTable3.arrayFill(maxRows);
for (var i = 0; i < maxRows; i++) {
result[i] = AsciiTable3.arrayFill(row.length, '');
}

// fill in values
for (var nCol = 0; nCol < row.length; nCol++) {
for (var nRow = 0; nRow < wrappedRow[nCol].length; nRow++) {
result[nRow][nCol] = wrappedRow[nCol][nRow];
}
}

return result;
}

/**
* Get string with the rendering of a heading row (truncating if needed).
* @private
* @param {Style} posStyle The heading row style.
* @param {number[]} colsWidth Array with the desired width for each heading column.
* @param {string} row The heading row to generate.
* @returns {string} String representation of table heading row line.
*/
getHeadingRow(posStyle, colsWidth) {
const heading = this.getHeading();

getHeadingRowTruncated(posStyle, colsWidth, row) {
var result = posStyle.left;

for (var col = 0; col < heading.length; col++) {
const cell = '' + heading[col];
for (var col = 0; col < row.length; col++) {
const cell = '' + row[col];

// align contents disregarding margins
const cellAligned = AsciiTable3.align(this.getHeadingAlign(), cell, colsWidth[col] - this.getCellMargin() * 2);
Expand All @@ -768,22 +894,42 @@ class AsciiTable3 {
AsciiTable3.truncateString(cellAligned, colsWidth[col] - this.getCellMargin() * 2) +
''.padStart(this.getCellMargin());

if (col < heading.length - 1) result += posStyle.colSeparator;
if (col < row.length - 1) result += posStyle.colSeparator;
}
result += posStyle.right + '\n';

return result;
}

/**
* Get string with the rendering of a data row.
* Get string with the rendering of a heading row.
* @private
* @param {Style} posStyle The heading row style.
* @param {number[]} colsWidth Array with the desired width for each heading column.
* @returns {string} String representation of table heading row line.
*/
getHeadingRow(posStyle, colsWidth) {
var result = '';

// wrap heading if needed
const rows = this.getWrappedRows(this.getHeading());

rows.forEach(aRow => {
result += this.getHeadingRowTruncated(posStyle, colsWidth, aRow);
});

return result;
}

/**
* Get string with the rendering of a data row (truncating if needed).
* @private
* @param {Style} posStyle The data row style.
* @param {number[]} colsWidth Array with the desired width for each data column.
* @param {*[]} row Array with cell values for this row.
* @returns {string} String representation of table data row line.
*/
getDataRow(posStyle, colsWidth, row) {
getDataRowTruncated(posStyle, colsWidth, row) {
var result = posStyle.left;

// loop over data columns in row
Expand All @@ -804,6 +950,27 @@ class AsciiTable3 {
return result;
}

/**
* Get string with the rendering of a data row (please not that it may result in several rows, depending on wrap settings).
* @private
* @param {Style} posStyle The data row style.
* @param {number[]} colsWidth Array with the desired width for each data column.
* @param {*[]} row Array with cell values for this row.
* @returns {string} String representation of table data row line.
*/
getDataRow(posStyle, colsWidth, row) {
var result = '';

// wrap data row if needed
const rows = this.getWrappedRows(row);

rows.forEach(aRow => {
result += this.getDataRowTruncated(posStyle, colsWidth, aRow);
});

return result;
}

/**
* Render the instance as a string for output.
* @returns {string} String rendiring of this instance table.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ascii-table3",
"version": "0.3.0",
"version": "0.4.0",
"author": "João Simões <[email protected]> (https://github.com/AllMightySauron)",
"description": "Javascript ASCII renderer for beautiful console-based tables",
"repository": {
Expand Down
Loading

0 comments on commit a60d6db

Please sign in to comment.