You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

735 lines
18 KiB

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