diff --git a/docs/ProgressBar.md b/docs/ProgressBar.md new file mode 100644 index 000000000..e10805c5e --- /dev/null +++ b/docs/ProgressBar.md @@ -0,0 +1,42 @@ +# ProgressBar + +The ProgressBar component represents progress. See examples/progress.js for an example app. + +## Usage + +```js +const {h, Text, ProgressBar} = require('ink'); + + +``` + +## Props + +### character + +The character to use for each item in the ProgressBar. Defaults to █ (block). + +### progress + +The percentage (between 0 and 1) of progress in the ProgressBar. + +### left/right + +The number of characters to subtract from each side of the ProgressBar. examples/progress.js demonstrates this. Commonly used if you want text before/after the progress bar on the same line. + + +### {color} + +Pass any chalk colors (e.g. `green`, `bgBlue`), similar to Text. + +### ... + +Any other props are passed to Text as-is. + + diff --git a/examples/progress.js b/examples/progress.js new file mode 100644 index 000000000..c30b4dd27 --- /dev/null +++ b/examples/progress.js @@ -0,0 +1,49 @@ +/* @jsx h */ +const {h, mount, Component, Text, ProgressBar} = require('../'); + +const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); + +const TASKS = 30; + +class ProgressApp extends Component { + constructor() { + super(); + + this.state = { + done: 0 + }; + } + + render() { + const text = `Running `; + return ( +
+ + {text} + + +
+ ); + } + + componentDidMount() { + const promises = Array.from({length: TASKS}, () => + delay(Math.floor(Math.random() * 1500)) + .then(() => { + this.setState(state => ({done: state.done + 1})); + }) + ); + + Promise.all(promises) + .then(() => delay(50)) + // eslint-disable-next-line unicorn/no-process-exit + .then(() => process.exit(0)); + } +} + +mount(, process.stdout); + diff --git a/examples/run b/examples/run new file mode 100755 index 000000000..34545713e --- /dev/null +++ b/examples/run @@ -0,0 +1,4 @@ +#!/bin/bash + +./node_modules/.bin/babel-node examples/$1.js + diff --git a/index.js b/index.js index 50e157748..3d98ddad9 100644 --- a/index.js +++ b/index.js @@ -11,6 +11,7 @@ const Newline = require('./lib/components/newline'); const Indent = require('./lib/components/indent'); const Group = require('./lib/components/group'); const Text = require('./lib/components/text'); +const ProgressBar = require('./lib/components/progress-bar'); exports.StringComponent = StringComponent; exports.Component = Component; @@ -19,6 +20,7 @@ exports.Newline = Newline; exports.Indent = Indent; exports.Group = Group; exports.Text = Text; +exports.ProgressBar = ProgressBar; const noop = () => {}; diff --git a/lib/components/progress-bar.js b/lib/components/progress-bar.js new file mode 100644 index 000000000..5cc2ea0ac --- /dev/null +++ b/lib/components/progress-bar.js @@ -0,0 +1,31 @@ +'use strict'; + +const blacklist = require('blacklist'); +const Component = require('../component'); +const h = require('../h'); +const Text = require('./text'); + +const PROPS = ['percent', 'left', 'right', 'columns', 'character']; + +class Bar extends Component { + getString() { + const { + percent = 1, + left = 0, + right = 0, + character = '█' + } = this.props; + const screen = this.props.columns || process.stdout.columns || 80; + const space = screen - right - left; + const max = Math.min(Math.floor(space * percent), space); + return character.repeat(max); + } + + render() { + const props = blacklist(this.props, PROPS); + return h(Text, props, this.getString()); + } +} + +module.exports = Bar; + diff --git a/package.json b/package.json index 74c747cf9..0e3bd298d 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "node": ">= 4" }, "scripts": { - "test": "xo && ava" + "test": "xo && ava", + "example:progress": "babel-node examples/progress.js" }, "files": [ "lib", @@ -25,6 +26,7 @@ ], "dependencies": { "arrify": "^1.0.1", + "blacklist": "^1.1.4", "chalk": "^2.0.1", "indent-string": "^3.1.0", "lodash.flattendeep": "^4.4.0", @@ -33,7 +35,10 @@ }, "devDependencies": { "ava": "^0.19.1", + "babel-cli": "^6.24.1", + "babel-core": "^6.25.0", "babel-plugin-transform-react-jsx": "^6.24.1", + "babel-preset-react": "^6.24.1", "babel-register": "^6.24.1", "eslint-config-xo-react": "^0.12.0", "eslint-plugin-react": "^7.1.0", @@ -73,5 +78,16 @@ "pragma": "h" } } + }, + "babel": { + "presets": ["babel-preset-react"], + "plugins": [ + [ + "transform-react-jsx", + { + "pragma": "h" + } + ] + ] } } diff --git a/test/progress-bar.js b/test/progress-bar.js new file mode 100644 index 000000000..1761188e6 --- /dev/null +++ b/test/progress-bar.js @@ -0,0 +1,16 @@ +import test from 'ava'; + +const ProgressBar = require('../lib/components/progress-bar.js'); + +const run = (columns, left, right) => ProgressBar.prototype.getString.call({ + props: {columns, left, right, char: 'x'} +}); + +test(`has correct length`, t => { + const str = run(50, 0, 0); + t.is(str.length, 50); + + const str2 = run(60, 10, 9); + t.is(str2.length, 41); +}); +