Package: charts - uhop/console-toolkit GitHub Wiki

The charts package provides bar/column charts for displaying data. The charts can be classified into several types:

  • Orientation:
    • Bar charts: horizontal rectangles.
    • Column charts: vertical rectangles.
  • Comparative drawing:
    • Stacked: all values are stacked on top of each other.
    • Grouped: all values are grouped together in a cluster.
  • Drawing elements:
    • Plain: each series is drawn with characters.
    • Frac: each series is draw as a rectangle with fractional width or height.
    • Block: each series is drawn as a rectangle.
    • Block-frac: each series is drawn as a rectangle with fractional width or height to provide visual gaps between series.

Charts are drawn using numeric data and a theme.

Logically data is a 2D array of non-negative numbers. Data is represented as an array of series. Series is an array of non-negative numbers, null (no data), or objects with the following properties:

  • value — a non-negative number. All other values will be replaced with 0.

The rest of properties are optional:

  • label — a string that labels the value.
  • colorState — a state object. See Package: ansi for details.
  • state — a state object.
  • symbol — a character that will be used to draw the rectangle.

If a series has only one element in the array, it can be used directly as is. If a data object has just value, it can be represented by the value itself. For example: [1, 2] instead of [1], [2](/uhop/console-toolkit/wiki/1],-[2) or [{value: 1}], [{value: 2}](/uhop/console-toolkit/wiki/{value:-1}],-[{value:-2}).

Not all charts can use all properties. See the documentation of the chart type for details. For example, column charts don't use label property, while block-based charts don't use symbol property.

colorState and state are combined together to form the final state object in this order.

All bar charts can be found in bars/. All column charts can be found in columns/.

All charts are represented by drawing functions, which return strings and look like this: drawBarChart(values, width, options).

The arguments values and width are required. The options argument is optional. values is the data array described above. width is the width (or height for column charts) of the chart in characters.

The options argument is an object with the following properties:

  • theme — a chart theme object. It is described below.
  • maxValue — an optional maximum value. If not specified, the maximum value is calculated from the data. If it is value is -1, the data series will fit into width.
  • gap — an optional gap between series in characters. If not specified, the gap is 0.
  • groupGap — an optional gap between groups in characters. If not specified, the gap is 1. This property is used only for grouped charts.
  • initState — an optional initial state object.
  • reverse — an optional flag that reverses the order of the series. This flag is used to draw charts in an alternative order: from right to left or top to bottom.
    • Bar charts always produce strings. Strings produced with {reverse: true} are meant to be aligned to the right. It can be done with Box.make(strings, {align: 'right'}).
  • rectSize — an optional height of a bar (width of a column) in characters. Defaults to 1.
  • blockTheme — an optional block theme object. See block themes for details. This property is used only for block-based charts.
    • Additionally: t, l, r and b flags are used to draw (if set) the top, left, right and bottom borders.
  • drawEmptyBorder — an optional flag that draws an empty border. This property is used only for block-frac-based charts. See Module: draw-block-frac for details.
  • drawItem() — an optional function that draws an item. This property is used only by bars/plain.js charts. It overlays the label property over an item.
    • truncate — an optional flag that truncates the label if it is too long.
    • useEllipsis — an optional flag that uses ellipsis if the label is too long.

Example:

import drawChart from 'console-toolkit/charts/bars/plain.js';

console.log('Stacked bars:');

const chart = drawChart(
  [
    [2, 1, 2],
    [5, 1, 4],
    [1, 1],
    [3, 1, 3]
  ],
  50
);

for (const line of chart) console.log(line);

All chart modules export drawChart() function by name and as the default export.

charts/bars/plain.js

The charts/bars/plain.js module draws a chart with stacked bars.

The module exports two functions used to draw individual items: defaultDrawItem() and drawItemLabel(). The former is used as the default for drawItem option. The latter is used when we want to overlay a label over an item. See how to use it in the example below.

defaultDrawItem() uses the 7/8th block symbol if no symbol is inherited from a data item or its themes. It creates a nice separation line between series. drawItemLabel() uses the label property to draw the label. It converts colors to background colors to create its effect.

Example

import drawChart, {drawItemLabel} from 'console-toolkit/charts/bars/plain.js';
import style from 'console-toolkit/style.js';
import Box from 'console-toolkit/box.js';

const data = [
  [2, 1, 2],
  [5, 1, 4],
  [1, 1],
  [3, 1, 3]
];

console.log('Stacked bars: {}');
const chart1 = drawChart(data, 50);
for (const line of chart1) console.log(line);

console.log('\nStacked bars: {maxValue: -1}');
const chart2 = drawChart(data, 50, {maxValue: -1});
for (const line of chart2) console.log(line);

const labels = ['zero', 'one', 'two', 'three', 'four', 'five'];
const dataWithLabels = data.map(series =>
  series.map(datum => ({value: datum, label: labels[datum]})));

console.log('\nStacked bars with labels:');
const chart3 = drawChart(dataWithLabels, 50,
  {drawItem: drawItemLabel, initState: style.black});
for (const line of chart3) console.log(line);

console.log('\nStacked bars with labels and reversed:');
const chart4 = drawChart(dataWithLabels, 50,
  {drawItem: drawItemLabel, initState: style.black, reverse: true});
const box = Box.make(chart4, {align: 'right'});
for (const line of box.box) console.log(line);

It produces the following output:

Stacked bars

charts/bars/plain-grouped.js

The charts/bars/plain-grouped.js module draws a chart with grouped bars. See charts/bars/plain.js for details.

Example

import drawChart from 'console-toolkit/charts/bars/plain-grouped.js';
import Box from 'console-toolkit/box.js';

const data = [
  [2.1, 1.9, 2.2],
  [1.1, 1.2]
];

console.log('Grouped bars: {}');
const chart1 = drawChart(data, 50);
for (const line of chart1) console.log(line);

console.log('\nGrouped bars: {reverse: true}');
const chart2 = drawChart(data, 50, {reverse: true});
const box = Box.make(chart2, {align: 'right'});
for (const line of box.box) console.log(line);

It produces the following output:

Grouped bars

charts/bars/frac-grouped.js

The charts/bars/frac-grouped.js module draws a chart with grouped fractional bars. The width of each bar is accurate up to 1/8th of a character.

This chart ignores the symbol and label properties because it uses special symbols to represent bars.

Currently it doesn't support reverse option.

Example

import drawChart from 'console-toolkit/charts/bars/frac-grouped.js';

const data = [
  [2.1, 1.9, 2.2],
  [1.1, 1.2]
];

console.log('Grouped bars: {maxValue: 10}');
const chart1 = drawChart(data, 50, {maxValue: 10});
for (const line of chart1) console.log(line);

It produces the following output:

Grouped bars

charts/bars/block.js

The charts/bars/block.js module draws a chart with block-based bars.

Such bars consists of two major parts: a frame and a body. The body is filled with solid full blocks. The frame is drawn with fractional blocks. In order to avoid visual gaps between bars, the frame is not drawn on certain sides. It can be forced with the t, l, r and b flags. If a flag is falsy, the corresponding part is not drawn.

This chart ignores the symbol and label properties because it uses special symbols to represent bars. These symbols can be specified in the blockTheme option. See Module: draw-block.

Example

import drawChart from 'console-toolkit/charts/bars/block.js';
import Box from 'console-toolkit/box.js';

const data = [
  [2, 1, 2],
  [5, 1, 4],
  [1, 1],
  [3, 1, 3]
];

console.log('Stacked bars (no bottom border):');
const chart1 = drawChart(data, 50, {b: false});
for (const line of chart1) console.log(line);

console.log('\nStacked bars (no top border): {maxValue: -1, t: false}');
const chart2 = drawChart(data, 50, {maxValue: -1, t: false});
for (const line of chart2) console.log(line);

console.log('\nStacked bars (no bottom border): {reverse: true, b: false}');
const chart3 = drawChart(data, 50, {b: false, reverse: true});
const box = Box.make(chart3, {align: 'right'});
for (const line of box.box) console.log(line);

It produces the following output:

Stacked bars

charts/bars/block-grouped.js

The charts/bars/block-grouped.js module draws a chart with grouped block-based bars. See charts/bars/block.js for details.

Example

import drawChart from 'console-toolkit/charts/bars/block-grouped.js';
import Box from 'console-toolkit/box.js';

const data = [
  [2.1, 1.9, 2.2],
  [1.1, 1.2]
];

console.log('Grouped bars: {t: false}');
const chart1 = drawChart(data, 50, {t: false});
for (const line of chart1) console.log(line);

console.log('Grouped bars: {t: false, reverse: true}');
const chart2 = drawChart(data, 50, {t: false, reverse: true});
const box = Box.make(chart2, {align: 'right'});
for (const line of box.box) console.log(line);

It produces the following output:

Grouped bars

charts/bars/block-frac.js

The charts/bars/block-frac.js module draws a chart with block-based fractional bars. This chart can use a fractional height for bars specified by the rectSize option. See charts/bars/block.js for details.

Example

import drawChart from 'console-toolkit/charts/bars/block-frac.js';
import Box from 'console-toolkit/box.js';

const data = [
  [2, 1, 2],
  [5, 1, 4],
  [1, 1],
  [3, 1, 3]
];

console.log('Stacked bars: {rectSize: 0.75}');
const chart1 = drawChart(data, 50, {rectSize: 0.75});
for (const line of chart1) console.log(line);

console.log('\nStacked bars: {rectSize: 0.5, maxValue: -1}');
const chart2 = drawChart(data, 50, {maxValue: -1, rectSize: 0.5});
for (const line of chart2) console.log(line);

console.log('\nStacked bars: {rectSize: 0.875,reverse: true}');
const chart3 = drawChart(data, 50, {rectSize: 0.875, reverse: true});
const box = Box.make(chart3, {align: 'right'});
for (const line of box.box) console.log(line);

It produces the following output:

Stacked bars

charts/bars/block-frac-grouped.js

The charts/bars/block-frac-grouped.js module draws a chart with grouped block-based fractional bars. See charts/bars/block-frac.js for details.

Example

import drawChart from 'console-toolkit/charts/bars/block-frac-grouped.js';
import Box from 'console-toolkit/box.js';

const data = [
  [2.1, 1.9, 2.2],
  [1.1, 1.2]
];

console.log('Grouped bars: {rectSize: 0.9}');
const chart1 = drawChart(data, 50, {rectSize: 0.9});
for (const line of chart1) console.log(line);

console.log('Grouped bars: {rectSize: 0.1, reverse: true}');
const chart2 = drawChart(data, 50, {rectSize: 0.1, reverse: true});
const box = Box.make(chart2, {align: 'right'});
for (const line of box.box) console.log(line);

It produces the following output:

Grouped bars

charts/columns/plain.js

The charts/columns/plain.js module draws a chart with stacked columns.

Example

import drawChart from 'console-toolkit/charts/columns/plain.js';

const data = [
  [2, 1, 2],
  [5, 1, 4],
  [1, 1],
  [3, 1, 3]
];

console.log('Stacked columns: {}');
const chart1 = drawChart(data, 8);
for (const line of chart1) console.log(line);

console.log('\nStacked columns: {maxValue: -1}');
const chart2 = drawChart(data, 8, {maxValue: -1});
for (const line of chart2) console.log(line);

console.log('\nStacked columns: {reverse: true}');
const chart3 = drawChart(data, 8, {reverse: true});
for (const line of chart3) console.log(line);

It produces the following output:

Stacked columns

charts/columns/plain-grouped.js

The charts/columns/plain-grouped.js module draws a chart with grouped columns. See charts/columns/plain.js for details.

Example

import drawChart from 'console-toolkit/charts/columns/plain-grouped.js';

const data = [
  [2.1, 1.9, 2.2],
  [1.1, 1.2]
];

console.log('Grouped columns: {}');
const chart1 = drawChart(data, 8);
for (const line of chart1) console.log(line);

console.log('\nGrouped columns: {reverse: true}');
const chart2 = drawChart(data, 8, {reverse: true});
for (const line of chart2) console.log(line);

It produces the following output:

Grouped columns

charts/columns/frac-grouped.js

The charts/columns/frac-grouped.js module draws a chart with grouped fractional columns. The width of each column is accurate up to 1/8th of a character. See its sister module charts/bars/frac-grouped.js for details.

Example

import drawChart from 'console-toolkit/charts/columns/frac-grouped.js';

const data = [
  [2.1, 1.9, 2.2],
  [1.1, 1.2]
];

console.log('Grouped columns: {maxValue: 5}');
const chart1 = drawChart(data, 8, {maxValue: 5});
for (const line of chart1) console.log(line);

It produces the following output:

Grouped bars

charts/columns/block.js

The charts/columns/block.js module draws a chart with block-based columns. See its sister module charts/bars/block.js for details.

Example

import drawChart from 'console-toolkit/charts/columns/block.js';

const data = [
  [2, 1, 2],
  [5, 1, 4],
  [1, 1],
  [3, 1, 3]
];

console.log('Stacked columns (no right border):');
const chart1 = drawChart(data, 8, {r: false});
for (const line of chart1) console.log(line);

console.log('\nStacked columns (no left border): {maxValue: -1, l: false}');
const chart2 = drawChart(data, 8, {maxValue: -1, l: false});
for (const line of chart2) console.log(line);

console.log('\nStacked columns (no right border): {reverse: true, r: false}');
const chart3 = drawChart(data, 8, {r: false, reverse: true});
for (const line of chart3) console.log(line);

It produces the following output:

Stacked columns

charts/columns/block-grouped.js

The charts/columns/block-grouped.js module draws a chart with grouped block-based columns. See charts/columns/block.js for details.

Example

import drawChart from 'console-toolkit/charts/columns/block-grouped.js';

const data = [
  [2.1, 1.9, 2.2],
  [1.1, 1.2]
];

console.log('Grouped columns: {r: false}');
const chart1 = drawChart(data, 8, {r: false});
for (const line of chart1) console.log(line);

console.log('\nGrouped columns: {l: false, reverse: true}');
const chart2 = drawChart(data, 8, {l: false, reverse: true});
for (const line of chart2) console.log(line);

It produces the following output:

Grouped bars

charts/columns/block-frac.js

The charts/columns/block-frac.js module draws a chart with block-based fractional columns. See its sister module charts/bars/block-frac.js for details.

Example

import drawChart from 'console-toolkit/charts/columns/block-frac.js';
import Box from 'console-toolkit/box.js';

const data = [
  [2, 1, 2],
  [5, 1, 4],
  [1, 1],
  [3, 1, 3]
];

console.log('Stacked columns: {rectSize: 0.75}');
const chart1 = drawChart(data, 8, {rectSize: 0.75});
for (const line of chart1) console.log(line);

console.log('\nStacked columns: {rectSize: 0.5, maxValue: -1}');
const chart2 = drawChart(data, 8, {maxValue: -1, rectSize: 0.5});
for (const line of chart2) console.log(line);

console.log('\nStacked columns: {rectSize: 0.875,reverse: true}');
const chart3 = drawChart(data, 8, {rectSize: 0.875, reverse: true});
const box = Box.make(chart3, {align: 'right'});
for (const line of box.box) console.log(line);

It produces the following output:

Stacked columns

charts/columns/block-frac-grouped.js

The charts/columns/block-frac-grouped.js module draws a chart with grouped block-based fractional columns. See charts/columns/block-frac.js for details.

Example

import drawChart from 'console-toolkit/charts/columns/block-frac-grouped.js';
import Box from 'console-toolkit/box.js';

const data = [
  [2.1, 1.9, 2.2],
  [1.1, 1.2]
];

console.log('Grouped columns: {rectSize: 0.9}');
const chart1 = drawChart(data, 8, {rectSize: 0.9});
for (const line of chart1) console.log(line);

console.log('\nGrouped columns: {rectSize: 0.1, reverse: true}');
const chart2 = drawChart(data, 8, {rectSize: 0.1, reverse: true});
const box = Box.make(chart2, {align: 'right'});
for (const line of box.box) console.log(line);

It produces the following output:

Grouped columns

charts/themes/default.js

The charts/themes/default.js module contains the default chart theme. It will be used if no other theme is specified.

A theme is defined by an array of objects, which mixed in with data objects defined above. Typically it defines colorState, state and/or symbol properties. These objects are selected in the order they appear in the array.

The empty part of a chart is defined in the empty property of the theme object (the array) with the same structure. Bar charts produce strings, which should be properly aligned. They don't use empty parts. But column charts use Box instances and have to fill empty parts.

The default theme uses the following colors in this order:

  • Bright versions of colors as colorState: cyan, magenta, blue, yellow, green, red.
  • Dark versions of the same colors in the same order.

The default empty property defined as follows:

  • symbol — a space character (' ').
  • state — a "reset all" state object.

The default theme serves as a foundation for other themes. Its elements are copied into a data object initially, then a custom theme is merged, then a custom data object is merged.

Example

import drawChart from 'console-toolkit/charts/bars/plain.js';
import style from 'console-toolkit/style.js';
import {shades} from 'console-toolkit/symbols.js';

const data = [
  [2, 1, 2],
  [5, 1, 4],
  [1, 1],
  [3, 1, 3]
];

console.log('Custom theme: ASCII');

const customTheme1 = [...'#=o'].
  map(symbol => ({state: style.reset.all, symbol}));

const chart1 = drawChart(data, 50, {theme: customTheme1});
for (const line of chart1) console.log(line);

console.log('\nCustom theme: shades');

const customTheme2 = shades.toReversed().
  map(symbol => ({state: style.reset.all.bright.yellow, symbol}));

const chart2 = drawChart(data, 50, {theme: customTheme2});
for (const line of chart2) console.log(line);

console.log('\nCustom theme: rainbow');

const customTheme3 = [
  {colorState: style.hex(0xee82ee)}, // violet
  {colorState: style.hex(0x4b0082)}, // indigo
  {colorState: style.hex(0x0000ff)}, // blue
  {colorState: style.hex(0x008000)}, // green
  {colorState: style.hex(0xffff00)}, // yellow
  {colorState: style.hex(0xffa500)}, // orange
  {colorState: style.hex(0xff0000)} // red
];

const chart3 = drawChart(data, 50, {theme: customTheme3});
for (const line of chart3) console.log(line);

It produces the following output:

Custom theme