1<a name="table"></a> 2# Table 3 4[![GitSpo Mentions](https://gitspo.com/badges/mentions/gajus/table?style=flat-square)](https://gitspo.com/mentions/gajus/table) 5[![Travis build status](http://img.shields.io/travis/gajus/table/master.svg?style=flat-square)](https://travis-ci.org/gajus/table) 6[![Coveralls](https://img.shields.io/coveralls/gajus/table.svg?style=flat-square)](https://coveralls.io/github/gajus/table) 7[![NPM version](http://img.shields.io/npm/v/table.svg?style=flat-square)](https://www.npmjs.org/package/table) 8[![Canonical Code Style](https://img.shields.io/badge/code%20style-canonical-blue.svg?style=flat-square)](https://github.com/gajus/canonical) 9[![Twitter Follow](https://img.shields.io/twitter/follow/kuizinas.svg?style=social&label=Follow)](https://twitter.com/kuizinas) 10 11* [Table](#table) 12 * [Features](#table-features) 13 * [Install](#table-install) 14 * [Usage](#table-usage) 15 * [Cell Content Alignment](#table-usage-cell-content-alignment) 16 * [Column Width](#table-usage-column-width) 17 * [Custom Border](#table-usage-custom-border) 18 * [Draw Horizontal Line](#table-usage-draw-horizontal-line) 19 * [Single Line Mode](#table-usage-single-line-mode) 20 * [Padding Cell Content](#table-usage-padding-cell-content) 21 * [Predefined Border Templates](#table-usage-predefined-border-templates) 22 * [Streaming](#table-usage-streaming) 23 * [Text Truncation](#table-usage-text-truncation) 24 * [Text Wrapping](#table-usage-text-wrapping) 25 26 27Produces a string that represents array data in a text table. 28 29![Demo of table displaying a list of missions to the Moon.](./.README/demo.png) 30 31<a name="table-features"></a> 32## Features 33 34* Works with strings containing [fullwidth](https://en.wikipedia.org/wiki/Halfwidth_and_fullwidth_forms) characters. 35* Works with strings containing [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code). 36* Configurable border characters. 37* Configurable content alignment per column. 38* Configurable content padding per column. 39* Configurable column width. 40* Text wrapping. 41 42<a name="table-install"></a> 43## Install 44 45```bash 46npm install table 47 48``` 49 50[![Buy Me A Coffee](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/gajus) 51[![Become a Patron](https://c5.patreon.com/external/logo/become_a_patron_button.png)](https://www.patreon.com/gajus) 52 53<a name="table-usage"></a> 54## Usage 55 56Table data is described using an array (rows) of array (cells). 57 58```js 59import { 60 table 61} from 'table'; 62 63// Using commonjs? 64// const {table} = require('table'); 65 66let data, 67 output; 68 69data = [ 70 ['0A', '0B', '0C'], 71 ['1A', '1B', '1C'], 72 ['2A', '2B', '2C'] 73]; 74 75/** 76 * @typedef {string} table~cell 77 */ 78 79/** 80 * @typedef {table~cell[]} table~row 81 */ 82 83/** 84 * @typedef {Object} table~columns 85 * @property {string} alignment Cell content alignment (enum: left, center, right) (default: left). 86 * @property {number} width Column width (default: auto). 87 * @property {number} truncate Number of characters are which the content will be truncated (default: Infinity). 88 * @property {number} paddingLeft Cell content padding width left (default: 1). 89 * @property {number} paddingRight Cell content padding width right (default: 1). 90 */ 91 92/** 93 * @typedef {Object} table~border 94 * @property {string} topBody 95 * @property {string} topJoin 96 * @property {string} topLeft 97 * @property {string} topRight 98 * @property {string} bottomBody 99 * @property {string} bottomJoin 100 * @property {string} bottomLeft 101 * @property {string} bottomRight 102 * @property {string} bodyLeft 103 * @property {string} bodyRight 104 * @property {string} bodyJoin 105 * @property {string} joinBody 106 * @property {string} joinLeft 107 * @property {string} joinRight 108 * @property {string} joinJoin 109 */ 110 111/** 112 * Used to dynamically tell table whether to draw a line separating rows or not. 113 * The default behavior is to always return true. 114 * 115 * @typedef {function} drawHorizontalLine 116 * @param {number} index 117 * @param {number} size 118 * @return {boolean} 119 */ 120 121/** 122 * @typedef {Object} table~config 123 * @property {table~border} border 124 * @property {table~columns[]} columns Column specific configuration. 125 * @property {table~columns} columnDefault Default values for all columns. Column specific settings overwrite the default values. 126 * @property {table~drawHorizontalLine} drawHorizontalLine 127 */ 128 129/** 130 * Generates a text table. 131 * 132 * @param {table~row[]} rows 133 * @param {table~config} config 134 * @return {String} 135 */ 136output = table(data); 137 138console.log(output); 139``` 140 141``` 142╔════╤════╤════╗ 143║ 0A │ 0B │ 0C ║ 144╟────┼────┼────╢ 145║ 1A │ 1B │ 1C ║ 146╟────┼────┼────╢ 147║ 2A │ 2B │ 2C ║ 148╚════╧════╧════╝ 149 150``` 151 152 153<a name="table-usage-cell-content-alignment"></a> 154### Cell Content Alignment 155 156`{string} config.columns[{number}].alignment` property controls content horizontal alignment within a cell. 157 158Valid values are: "left", "right" and "center". 159 160```js 161let config, 162 data, 163 output; 164 165data = [ 166 ['0A', '0B', '0C'], 167 ['1A', '1B', '1C'], 168 ['2A', '2B', '2C'] 169]; 170 171config = { 172 columns: { 173 0: { 174 alignment: 'left', 175 width: 10 176 }, 177 1: { 178 alignment: 'center', 179 width: 10 180 }, 181 2: { 182 alignment: 'right', 183 width: 10 184 } 185 } 186}; 187 188output = table(data, config); 189 190console.log(output); 191``` 192 193``` 194╔════════════╤════════════╤════════════╗ 195║ 0A │ 0B │ 0C ║ 196╟────────────┼────────────┼────────────╢ 197║ 1A │ 1B │ 1C ║ 198╟────────────┼────────────┼────────────╢ 199║ 2A │ 2B │ 2C ║ 200╚════════════╧════════════╧════════════╝ 201``` 202 203<a name="table-usage-column-width"></a> 204### Column Width 205 206`{number} config.columns[{number}].width` property restricts column width to a fixed width. 207 208```js 209let data, 210 output, 211 options; 212 213data = [ 214 ['0A', '0B', '0C'], 215 ['1A', '1B', '1C'], 216 ['2A', '2B', '2C'] 217]; 218 219options = { 220 columns: { 221 1: { 222 width: 10 223 } 224 } 225}; 226 227output = table(data, options); 228 229console.log(output); 230``` 231 232``` 233╔════╤════════════╤════╗ 234║ 0A │ 0B │ 0C ║ 235╟────┼────────────┼────╢ 236║ 1A │ 1B │ 1C ║ 237╟────┼────────────┼────╢ 238║ 2A │ 2B │ 2C ║ 239╚════╧════════════╧════╝ 240``` 241 242<a name="table-usage-custom-border"></a> 243### Custom Border 244 245`{object} config.border` property describes characters used to draw the table border. 246 247```js 248let config, 249 data, 250 output; 251 252data = [ 253 ['0A', '0B', '0C'], 254 ['1A', '1B', '1C'], 255 ['2A', '2B', '2C'] 256]; 257 258config = { 259 border: { 260 topBody: `─`, 261 topJoin: `┬`, 262 topLeft: `┌`, 263 topRight: `┐`, 264 265 bottomBody: `─`, 266 bottomJoin: `┴`, 267 bottomLeft: `└`, 268 bottomRight: `┘`, 269 270 bodyLeft: `│`, 271 bodyRight: `│`, 272 bodyJoin: `│`, 273 274 joinBody: `─`, 275 joinLeft: `├`, 276 joinRight: `┤`, 277 joinJoin: `┼` 278 } 279}; 280 281output = table(data, config); 282 283console.log(output); 284``` 285 286``` 287┌────┬────┬────┐ 288│ 0A │ 0B │ 0C │ 289├────┼────┼────┤ 290│ 1A │ 1B │ 1C │ 291├────┼────┼────┤ 292│ 2A │ 2B │ 2C │ 293└────┴────┴────┘ 294``` 295 296<a name="table-usage-draw-horizontal-line"></a> 297### Draw Horizontal Line 298 299`{function} config.drawHorizontalLine` property is a function that is called for every non-content row in the table. The result of the function `{boolean}` determines whether a row is drawn. 300 301```js 302let data, 303 output, 304 options; 305 306data = [ 307 ['0A', '0B', '0C'], 308 ['1A', '1B', '1C'], 309 ['2A', '2B', '2C'], 310 ['3A', '3B', '3C'], 311 ['4A', '4B', '4C'] 312]; 313 314options = { 315 /** 316 * @typedef {function} drawHorizontalLine 317 * @param {number} index 318 * @param {number} size 319 * @return {boolean} 320 */ 321 drawHorizontalLine: (index, size) => { 322 return index === 0 || index === 1 || index === size - 1 || index === size; 323 } 324}; 325 326output = table(data, options); 327 328console.log(output); 329 330``` 331 332``` 333╔════╤════╤════╗ 334║ 0A │ 0B │ 0C ║ 335╟────┼────┼────╢ 336║ 1A │ 1B │ 1C ║ 337║ 2A │ 2B │ 2C ║ 338║ 3A │ 3B │ 3C ║ 339╟────┼────┼────╢ 340║ 4A │ 4B │ 4C ║ 341╚════╧════╧════╝ 342 343``` 344 345<a name="table-usage-single-line-mode"></a> 346### Single Line Mode 347 348Horizontal lines inside the table are not drawn. 349 350```js 351import { 352 table, 353 getBorderCharacters 354} from 'table'; 355 356const data = [ 357 ['-rw-r--r--', '1', 'pandorym', 'staff', '1529', 'May 23 11:25', 'LICENSE'], 358 ['-rw-r--r--', '1', 'pandorym', 'staff', '16327', 'May 23 11:58', 'README.md'], 359 ['drwxr-xr-x', '76', 'pandorym', 'staff', '2432', 'May 23 12:02', 'dist'], 360 ['drwxr-xr-x', '634', 'pandorym', 'staff', '20288', 'May 23 11:54', 'node_modules'], 361 ['-rw-r--r--', '1,', 'pandorym', 'staff', '525688', 'May 23 11:52', 'package-lock.json'], 362 ['-rw-r--r--@', '1', 'pandorym', 'staff', '2440', 'May 23 11:25', 'package.json'], 363 ['drwxr-xr-x', '27', 'pandorym', 'staff', '864', 'May 23 11:25', 'src'], 364 ['drwxr-xr-x', '20', 'pandorym', 'staff', '640', 'May 23 11:25', 'test'], 365]; 366 367const config = { 368 singleLine: true 369}; 370 371const output = table(data, config); 372console.log(output); 373``` 374 375``` 376╔═════════════╤═════╤══════════╤═══════╤════════╤══════════════╤═══════════════════╗ 377║ -rw-r--r-- │ 1 │ pandorym │ staff │ 1529 │ May 23 11:25 │ LICENSE ║ 378║ -rw-r--r-- │ 1 │ pandorym │ staff │ 16327 │ May 23 11:58 │ README.md ║ 379║ drwxr-xr-x │ 76 │ pandorym │ staff │ 2432 │ May 23 12:02 │ dist ║ 380║ drwxr-xr-x │ 634 │ pandorym │ staff │ 20288 │ May 23 11:54 │ node_modules ║ 381║ -rw-r--r-- │ 1, │ pandorym │ staff │ 525688 │ May 23 11:52 │ package-lock.json ║ 382║ -rw-r--r--@ │ 1 │ pandorym │ staff │ 2440 │ May 23 11:25 │ package.json ║ 383║ drwxr-xr-x │ 27 │ pandorym │ staff │ 864 │ May 23 11:25 │ src ║ 384║ drwxr-xr-x │ 20 │ pandorym │ staff │ 640 │ May 23 11:25 │ test ║ 385╚═════════════╧═════╧══════════╧═══════╧════════╧══════════════╧═══════════════════╝ 386``` 387 388<a name="table-usage-padding-cell-content"></a> 389### Padding Cell Content 390 391`{number} config.columns[{number}].paddingLeft` and `{number} config.columns[{number}].paddingRight` properties control content padding within a cell. Property value represents a number of whitespaces used to pad the content. 392 393```js 394let config, 395 data, 396 output; 397 398data = [ 399 ['0A', 'AABBCC', '0C'], 400 ['1A', '1B', '1C'], 401 ['2A', '2B', '2C'] 402]; 403 404config = { 405 columns: { 406 0: { 407 paddingLeft: 3 408 }, 409 1: { 410 width: 2, 411 paddingRight: 3 412 } 413 } 414}; 415 416output = table(data, config); 417 418console.log(output); 419``` 420 421``` 422╔══════╤══════╤════╗ 423║ 0A │ AA │ 0C ║ 424║ │ BB │ ║ 425║ │ CC │ ║ 426╟──────┼──────┼────╢ 427║ 1A │ 1B │ 1C ║ 428╟──────┼──────┼────╢ 429║ 2A │ 2B │ 2C ║ 430╚══════╧══════╧════╝ 431``` 432 433<a name="table-usage-predefined-border-templates"></a> 434### Predefined Border Templates 435 436You can load one of the predefined border templates using `getBorderCharacters` function. 437 438```js 439import { 440 table, 441 getBorderCharacters 442} from 'table'; 443 444let config, 445 data; 446 447data = [ 448 ['0A', '0B', '0C'], 449 ['1A', '1B', '1C'], 450 ['2A', '2B', '2C'] 451]; 452 453config = { 454 border: getBorderCharacters(`name of the template`) 455}; 456 457table(data, config); 458``` 459 460``` 461# honeywell 462 463╔════╤════╤════╗ 464║ 0A │ 0B │ 0C ║ 465╟────┼────┼────╢ 466║ 1A │ 1B │ 1C ║ 467╟────┼────┼────╢ 468║ 2A │ 2B │ 2C ║ 469╚════╧════╧════╝ 470 471# norc 472 473┌────┬────┬────┐ 474│ 0A │ 0B │ 0C │ 475├────┼────┼────┤ 476│ 1A │ 1B │ 1C │ 477├────┼────┼────┤ 478│ 2A │ 2B │ 2C │ 479└────┴────┴────┘ 480 481# ramac (ASCII; for use in terminals that do not support Unicode characters) 482 483+----+----+----+ 484| 0A | 0B | 0C | 485|----|----|----| 486| 1A | 1B | 1C | 487|----|----|----| 488| 2A | 2B | 2C | 489+----+----+----+ 490 491# void (no borders; see "bordless table" section of the documentation) 492 493 0A 0B 0C 494 495 1A 1B 1C 496 497 2A 2B 2C 498 499``` 500 501Raise [an issue](https://github.com/gajus/table/issues) if you'd like to contribute a new border template. 502 503<a name="table-usage-predefined-border-templates-borderless-table"></a> 504#### Borderless Table 505 506Simply using "void" border character template creates a table with a lot of unnecessary spacing. 507 508To create a more plesant to the eye table, reset the padding and remove the joining rows, e.g. 509 510```js 511let output; 512 513output = table(data, { 514 border: getBorderCharacters(`void`), 515 columnDefault: { 516 paddingLeft: 0, 517 paddingRight: 1 518 }, 519 drawHorizontalLine: () => { 520 return false 521 } 522}); 523 524console.log(output); 525``` 526 527``` 5280A 0B 0C 5291A 1B 1C 5302A 2B 2C 531``` 532 533<a name="table-usage-streaming"></a> 534### Streaming 535 536`table` package exports `createStream` function used to draw a table and append rows. 537 538`createStream` requires `{number} columnDefault.width` and `{number} columnCount` configuration properties. 539 540```js 541import { 542 createStream 543} from 'table'; 544 545let config, 546 stream; 547 548config = { 549 columnDefault: { 550 width: 50 551 }, 552 columnCount: 1 553}; 554 555stream = createStream(config); 556 557setInterval(() => { 558 stream.write([new Date()]); 559}, 500); 560``` 561 562![Streaming current date.](./.README/streaming.gif) 563 564`table` package uses ANSI escape codes to overwrite the output of the last line when a new row is printed. 565 566The underlying implementation is explained in this [Stack Overflow answer](http://stackoverflow.com/a/32938658/368691). 567 568Streaming supports all of the configuration properties and functionality of a static table (such as auto text wrapping, alignment and padding), e.g. 569 570```js 571import { 572 createStream 573} from 'table'; 574 575import _ from 'lodash'; 576 577let config, 578 stream, 579 i; 580 581config = { 582 columnDefault: { 583 width: 50 584 }, 585 columnCount: 3, 586 columns: { 587 0: { 588 width: 10, 589 alignment: 'right' 590 }, 591 1: { 592 alignment: 'center', 593 }, 594 2: { 595 width: 10 596 } 597 } 598}; 599 600stream = createStream(config); 601 602i = 0; 603 604setInterval(() => { 605 let random; 606 607 random = _.sample('abcdefghijklmnopqrstuvwxyz', _.random(1, 30)).join(''); 608 609 stream.write([i++, new Date(), random]); 610}, 500); 611``` 612 613![Streaming random data.](./.README/streaming-random.gif) 614 615<a name="table-usage-text-truncation"></a> 616### Text Truncation 617 618To handle a content that overflows the container width, `table` package implements [text wrapping](#table-usage-text-wrapping). However, sometimes you may want to truncate content that is too long to be displayed in the table. 619 620`{number} config.columns[{number}].truncate` property (default: `Infinity`) truncates the text at the specified length. 621 622```js 623let config, 624 data, 625 output; 626 627data = [ 628 ['Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus pulvinar nibh sed mauris convallis dapibus. Nunc venenatis tempus nulla sit amet viverra.'] 629]; 630 631config = { 632 columns: { 633 0: { 634 width: 20, 635 truncate: 100 636 } 637 } 638}; 639 640output = table(data, config); 641 642console.log(output); 643``` 644 645``` 646╔══════════════════════╗ 647║ Lorem ipsum dolor si ║ 648║ t amet, consectetur ║ 649║ adipiscing elit. Pha ║ 650║ sellus pulvinar nibh ║ 651║ sed mauris conva... ║ 652╚══════════════════════╝ 653``` 654 655<a name="table-usage-text-wrapping"></a> 656### Text Wrapping 657 658`table` package implements auto text wrapping, i.e. text that has width greater than the container width will be separated into multiple lines, e.g. 659 660```js 661let config, 662 data, 663 output; 664 665data = [ 666 ['Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus pulvinar nibh sed mauris convallis dapibus. Nunc venenatis tempus nulla sit amet viverra.'] 667]; 668 669config = { 670 columns: { 671 0: { 672 width: 20 673 } 674 } 675}; 676 677output = table(data, config); 678 679console.log(output); 680``` 681 682``` 683╔══════════════════════╗ 684║ Lorem ipsum dolor si ║ 685║ t amet, consectetur ║ 686║ adipiscing elit. Pha ║ 687║ sellus pulvinar nibh ║ 688║ sed mauris convallis ║ 689║ dapibus. Nunc venena ║ 690║ tis tempus nulla sit ║ 691║ amet viverra. ║ 692╚══════════════════════╝ 693``` 694 695When `wrapWord` is `true` the text is broken at the nearest space or one of the special characters ("-", "_", "\", "/", ".", ",", ";"), e.g. 696 697```js 698let config, 699 data, 700 output; 701 702data = [ 703 ['Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus pulvinar nibh sed mauris convallis dapibus. Nunc venenatis tempus nulla sit amet viverra.'] 704]; 705 706config = { 707 columns: { 708 0: { 709 width: 20, 710 wrapWord: true 711 } 712 } 713}; 714 715output = table(data, config); 716 717console.log(output); 718``` 719 720``` 721╔══════════════════════╗ 722║ Lorem ipsum dolor ║ 723║ sit amet, ║ 724║ consectetur ║ 725║ adipiscing elit. ║ 726║ Phasellus pulvinar ║ 727║ nibh sed mauris ║ 728║ convallis dapibus. ║ 729║ Nunc venenatis ║ 730║ tempus nulla sit ║ 731║ amet viverra. ║ 732╚══════════════════════╝ 733 734``` 735 736