ascii-bar
Version:
A zero dependency ascii progress bar with spinner, colors and typescript support
269 lines (268 loc) • 10.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var AsciiBar = /** @class */ (function () {
function AsciiBar(options) {
var _this = this;
/**
* Format of the displayed progressbar
*/
this.formatString = '#percent #bar';
/**
* Number of steps to finish progress
*/
this.total = 100;
/**
* Startdate to calculate elapsed time (in milliseconds)
*/
this.startDate = new Date().getTime();
/**
* Which timespan to use for timing calculation - If you are unsure allways use false here!
*/
this.lastUpdateForTiming = false;
/**
* Width of the progress bar (only the #bar part)
*/
this.width = 20;
/**
* Symbol for the done progress in the #bar part
*/
this.doneSymbol = ">";
/**
* Symbol for the undone progress in the #bar part
*/
this.undoneSymbol = "-";
/**
* Wether to print to configured stream or not
*/
this.print = true;
/**
* A spinner object describing how the spinner looks like
* Change this for another spinner
*/
this.spinner = exports.defaultSpinner;
/**
* The message displayed at the #message placeholder
*/
this.message = "";
/**
* wether to call progressbar's stop() function automatically if the progress reaches 100%
*/
this.autoStop = true;
/**
* wether to hide the terminal's cursor while displaying the progress bar
*/
this.hideCursor = false;
this.elapsed = 0;
this.lastUpdate = new Date().getTime();
this.timeToFinish = 0;
this.overallTime = 0;
this.stream = process.stdout;
this.enableSpinner = false;
this.currentSpinnerSymbol = "";
this.current = 0;
/**
* Updates the spinner if enabled
*/
this.updateSpinner = function () {
if (_this.spinner.currentFrame === undefined) {
_this.spinner.currentFrame = 0;
}
_this.spinner.currentFrame = (_this.spinner.currentFrame + 1) % _this.spinner.frames.length;
_this.currentSpinnerSymbol = _this.spinner.frames[_this.spinner.currentFrame];
_this.printLine();
_this.spinnerTimeout = setTimeout(_this.updateSpinner, _this.spinner.interval);
};
if (options) {
//if only a string was provided, use this as formatString
if (typeof options == "string") {
options = { formatString: options };
}
//set other options
for (var opt in options) {
if (this[opt] !== undefined) {
this[opt] = options[opt];
}
}
//set start value
if (options.start) {
this.current = options.start;
}
//use simple spinner on windows
if (process.platform === 'win32') {
this.spinner = exports.simpleSpinner;
}
//enable spinner
if (this.enableSpinner) {
this.spinnerTimeout = setTimeout(this.updateSpinner, this.spinner.interval);
}
}
}
/**
* Creates the progressbar string with all configured settings
* @returns a string representating the progressbar
*/
AsciiBar.prototype.renderLine = function () {
var plusCount = Math.round(this.current / this.total * this.width);
var minusCount = this.width - plusCount;
var plusString = "";
var minusString = "";
for (var i = 0; i < plusCount; i++) {
plusString += this.doneSymbol;
}
for (var i = 0; i < minusCount; i++) {
minusString += this.undoneSymbol;
}
var barString = "[" + plusString + minusString + "]";
var currentString = String(this.current);
while (currentString.length < String(this.total).length) {
currentString = "0" + currentString;
}
var countString = "[" + currentString + "/" + this.total + "]";
var percentString = String(Math.round((this.current / this.total) * 100));
while (percentString.length < 3) {
percentString = " " + percentString;
}
percentString += "%";
var overAllString = this.formatTime(this.overallTime);
var elapsedString = this.formatTime(this.elapsed);
var ttfString = this.formatTime(this.timeToFinish);
//Replace macros
var line = this.formatString.replace(/#bar/g, barString).replace(/#count/g, countString).replace(/#percent/g, percentString).replace(/#overall/g, overAllString).replace(/#elapsed/g, elapsedString).replace(/#ttf/g, ttfString).replace(/#message/g, this.message).replace(/#spinner/g, this.currentSpinnerSymbol);
//Colors :-)
line = line.replace(/##default/g, colorCodes.Reset).replace(/##green/g, colorCodes.Green).replace(/##blue/g, colorCodes.Blue).replace(/##red/g, colorCodes.Red).replace(/##yellow/g, colorCodes.Yellow).replace(/##bright/g, colorCodes.Bright).replace(/##dim/g, colorCodes.Dim);
line += colorCodes.Reset;
//Hide cursor
if (this.hideCursor) {
line = colorCodes.HideCursor + line;
}
return line;
};
/**
* Render the progressbar and print it to output stream
*/
AsciiBar.prototype.printLine = function () {
if (!this.print) {
return;
}
this.stream.cursorTo(0);
this.stream.write(this.renderLine());
this.stream.clearLine(1);
};
/**
* update the progress. This will trigger re-rendering the progressbar
* @param current the new absolute progress value
* @param message [optional] update the message displayed at the #message placeholder
*/
AsciiBar.prototype.update = function (current, message) {
this.current = current;
if (message) {
this.message = message;
}
//timePerTick * max = overallTime
//timePerTick = elapsed / current
//overallTime = (elapsed / current) * max
//timeToFinish = overallTime - elapsed
var now = new Date().getTime();
//how to calculate time per step
var timePerStep = this.lastUpdateForTiming ? (now - this.lastUpdate) : (this.elapsed / this.current);
this.elapsed = now - this.startDate;
this.overallTime = (timePerStep * this.total);
this.timeToFinish = (timePerStep * (this.total - this.current));
this.printLine();
//Stop if finished
if (this.autoStop && (this.current / this.total >= 1)) {
this.stop();
}
this.lastUpdate = now;
return this;
};
/**
* Formats a time span (given in milliseconds) to a easy human readable string
* @param millis timespan in milliseconds
*/
AsciiBar.prototype.formatTime = function (millis) {
//Milliseconds
if (millis < 500) {
return Math.round(millis) + "ms";
}
//Seconds
if (millis < 60 * 1000) {
return Math.round(millis / 1000) + "s";
}
//Minutes
if (millis < 60 * 60 * 1000) {
var minutes_1 = Math.round(millis / (1000 * 60));
var seconds = Math.round(millis % (1000 * 60) / 1000);
return minutes_1 + "m " + seconds + "s";
}
//Hours
if (millis < 24 * 60 * 60 * 1000) {
var hours_1 = Math.round(millis / (1000 * 60 * 60));
var minutes_2 = Math.round(millis % (1000 * 60 * 60) / (1000 * 60));
return hours_1 + "h " + minutes_2 + "m";
}
//Days
var days = Math.round(millis / (1000 * 60 * 60 * 24));
var hours = Math.round(millis % (1000 * 60 * 60 * 24) / (1000 * 60 * 60));
var minutes = Math.round(millis % (1000 * 60 * 60) / (1000 * 60));
return days + "d " + hours + "h " + minutes + "m";
};
/**
* Stop the progressbar
* This will stop the spinner and change it's symbol to a checkmark (if not disabled)
* Message will be changed to a string describing the elapsed time (if not disabled)
* This function will be triggered automatically if the progressbar reaches 100% (if not disabled)
* @param withInfo wether to auto-update the progressbar's spinner and message after stopping
*/
AsciiBar.prototype.stop = function (withInfo) {
if (withInfo === void 0) { withInfo = true; }
//Stop the spinner
if (this.spinnerTimeout) {
clearTimeout(this.spinnerTimeout);
}
if (withInfo) {
//change spinner to checkmark
this.currentSpinnerSymbol = colorCodes.Green + colorCodes.Bright + "✓" + colorCodes.Reset;
if (process.platform === 'win32') {
this.currentSpinnerSymbol = "OK ";
}
;
//set overalltime to really elapsed time
this.overallTime = this.elapsed;
this.message = "Finished in " + this.formatTime(this.overallTime);
this.printLine();
}
//add newline and re-enable cursor
console.log(this.hideCursor ? colorCodes.ShowCursor : "");
};
return AsciiBar;
}());
exports.default = AsciiBar;
//ColorCodes from https://stackoverflow.com/questions/9781218/how-to-change-node-jss-console-font-color
var colorCodes = {
Reset: "\x1b[0m",
Bright: "\x1b[1m",
Dim: "\x1b[2m",
Underscore: "\x1b[4m",
Blink: "\x1b[5m",
Reverse: "\x1b[7m",
Hidden: "\x1b[8m",
Black: "\x1b[30m",
Red: "\x1b[31m",
Green: "\x1b[32m",
Yellow: "\x1b[33m",
Blue: "\x1b[34m",
Magenta: "\x1b[35m",
Cyan: "\x1b[36m",
White: "\x1b[37m",
HideCursor: "\x1B[?25l",
ShowCursor: "\x1B[?25h",
};
exports.defaultSpinner = {
interval: 120,
frames: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
};
exports.simpleSpinner = {
interval: 120,
frames: ["-", "\\", "|", "/"]
};