Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add suffixText option #223

Merged
merged 5 commits into from
Mar 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
28 changes: 26 additions & 2 deletions example.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import process from 'node:process';
import chalk from 'chalk';
import logSymbols from 'log-symbols';
import ora from './index.js';

const spinner = ora({
Expand Down Expand Up @@ -46,7 +47,30 @@ setTimeout(() => {
}, 6000);

setTimeout(() => {
spinner.succeed();
}, 7000);
spinner.prefixText = chalk.dim('[info]');
spinner.spinner = 'dots';
spinner.text = 'Loading with prefix text';
}, 8000);

setTimeout(() => {
spinner.prefixText = '';
spinner.suffixText = chalk.dim('[info]');
spinner.text = 'Loading with suffix text';
}, 10_000);

setTimeout(() => {
spinner.prefixText = chalk.dim('[info]');
spinner.suffixText = chalk.dim('[info]');
spinner.text = 'Loading with prefix and suffix text';
}, 12_000);

setTimeout(() => {
spinner.stopAndPersist({
prefixText: '',
suffixText: '',
symbol: logSymbols.info,
text: 'Stopping with different text!',
});
}, 14_000);

// $ node example.js nameOfSpinner
21 changes: 21 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export type Color =

export type PrefixTextGenerator = () => string;

export type SuffixTextGenerator = () => string;

export interface Options {
/**
Text to display after the spinner.
Expand All @@ -29,6 +31,11 @@ export interface Options {
*/
readonly prefixText?: string | PrefixTextGenerator;

/**
Text or a function that returns text to display after the spinner text. No suffix text will be displayed if set to an empty string.
*/
readonly suffixText?: string | SuffixTextGenerator;

/**
Name of one of the provided spinners. See [`example.js`](https://github.com/BendingBender/ora/blob/main/example.js) in this repo if you want to test out different spinners. On Windows, it will always use the line spinner as the Windows command-line doesn't have proper Unicode support.

Expand Down Expand Up @@ -130,6 +137,13 @@ export interface PersistOptions {
Default: Current `prefixText`.
*/
readonly prefixText?: string | PrefixTextGenerator;

/**
Text or a function that returns text to be persisted after the text after the symbol. No suffix text will be displayed if set to an empty string.

Default: Current `suffixText`.
*/
readonly suffixText?: string | SuffixTextGenerator;
}

export interface PromiseOptions<T> extends Options {
Expand Down Expand Up @@ -161,6 +175,13 @@ export interface Ora {
*/
prefixText: string;

/**
Change the text or function that returns text after the spinner text.

No suffix text will be displayed if set to an empty string.
*/
suffixText: string;

/**
Change the spinner color.
*/
Expand Down
45 changes: 40 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class Ora {
#indent;
#text;
#prefixText;
#suffixText;

color;

Expand Down Expand Up @@ -57,6 +58,7 @@ class Ora {
// It's important that these use the public setters.
this.text = this.#options.text;
this.prefixText = this.#options.prefixText;
this.suffixText = this.#options.suffixText;
this.indent = this.#options.indent;

if (process.env.NODE_ENV === 'test') {
Expand Down Expand Up @@ -147,6 +149,15 @@ class Ora {
this.updateLineCount();
}

get suffixText() {
return this.#suffixText;
}

set suffixText(value) {
this.#suffixText = value || '';
this.updateLineCount();
}

get isSpinning() {
return this.#id !== undefined;
}
Expand All @@ -164,12 +175,26 @@ class Ora {
return '';
}

getFullSuffixText(suffixText = this.#suffixText, prefix = ' ') {
if (typeof suffixText === 'string' && suffixText !== '') {
return prefix + suffixText;
}

if (typeof suffixText === 'function') {
return prefix + suffixText();
}

return '';
}

updateLineCount() {
const columns = this.#stream.columns || 80;
const fullPrefixText = this.getFullPrefixText(this.#prefixText, '-');
const fullSuffixText = this.getFullSuffixText(this.#suffixText, '-');
const fullText = ' '.repeat(this.#indent) + fullPrefixText + '--' + this.#text + '--' + fullSuffixText;

this.#lineCount = 0;
for (const line of stripAnsi(' '.repeat(this.#indent) + fullPrefixText + '--' + this.#text).split('\n')) {
for (const line of stripAnsi(fullText).split('\n')) {
this.#lineCount += Math.max(1, Math.ceil(wcwidth(line) / columns));
}
}
Expand Down Expand Up @@ -209,8 +234,9 @@ class Ora {
this.#frameIndex = ++this.#frameIndex % frames.length;
const fullPrefixText = (typeof this.#prefixText === 'string' && this.#prefixText !== '') ? this.#prefixText + ' ' : '';
const fullText = typeof this.text === 'string' ? ' ' + this.text : '';
const fullSuffixText = (typeof this.#suffixText === 'string' && this.#suffixText !== '') ? ' ' + this.#suffixText : '';

return fullPrefixText + frame + fullText;
return fullPrefixText + frame + fullText + fullSuffixText;
}

clear() {
Expand Down Expand Up @@ -328,12 +354,21 @@ class Ora {
return this;
}

const prefixText = options.prefixText || this.#prefixText;
const text = options.text || this.text;
const prefixText = options.prefixText ?? this.#prefixText;
const fullPrefixText = this.getFullPrefixText(prefixText, ' ');

const symbolText = options.symbol ?? ' ';

const text = options.text ?? this.text;
const fullText = (typeof text === 'string') ? ' ' + text : '';

const suffixText = options.suffixText ?? this.#suffixText;
const fullSuffixText = this.getFullSuffixText(suffixText, ' ');

const textToWrite = fullPrefixText + symbolText + fullText + fullSuffixText + '\n';

this.stop();
this.#stream.write(`${this.getFullPrefixText(prefixText, ' ')}${options.symbol || ' '}${fullText}\n`);
this.#stream.write(textToWrite);

return this;
}
Expand Down
4 changes: 4 additions & 0 deletions index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const spinner = ora('Loading unicorns');
ora({text: 'Loading unicorns'});
ora({prefixText: 'Loading unicorns'});
ora({prefixText: () => 'Loading unicorns dynamically'});
ora({suffixText: 'Loading unicorns'});
ora({suffixText: () => 'Loading unicorns dynamically'});
ora({spinner: 'squish'});
ora({spinner: {frames: ['-', '+', '-']}});
ora({spinner: {interval: 80, frames: ['-', '+', '-']}});
Expand Down Expand Up @@ -39,6 +41,7 @@ spinner.stopAndPersist();
spinner.stopAndPersist({text: 'all done'});
spinner.stopAndPersist({symbol: '@', text: 'all done'});
spinner.stopAndPersist({prefixText: 'all done'});
spinner.stopAndPersist({suffixText: 'all done'});
spinner.clear();
spinner.render();
spinner.frame();
Expand All @@ -57,6 +60,7 @@ void oraPromise(async () => {
}, 'foo');
void oraPromise(async spinner => {
spinner.prefixText = 'foo';
spinner.suffixText = '[loading]';
await resolves;
return 7;
}, {
Expand Down
21 changes: 20 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ Type: `string | () => string`

Text or a function that returns text to display before the spinner. No prefix text will be displayed if set to an empty string.

##### suffixText

Type: `string | () => string`

Text or a function that returns text to display after the spinner text. No suffix text will be displayed if set to an empty string.

##### spinner

Type: `string | object`\
Expand Down Expand Up @@ -142,6 +148,12 @@ Change the text before the spinner.

No prefix text will be displayed if set to an empty string.

#### .suffixText <sup>get/set</sup>

Change the text after the spinner text.

No suffix text will be displayed if set to an empty string.

#### .color <sup>get/set</sup>

Change the spinner color.
Expand Down Expand Up @@ -208,7 +220,7 @@ Symbol to replace the spinner with.
Type: `string`\
Default: Current `'text'`

Text to be persisted after the symbol
Text to be persisted after the symbol.

###### prefixText

Expand All @@ -217,6 +229,13 @@ Default: Current `prefixText`

Text to be persisted before the symbol. No prefix text will be displayed if set to an empty string.

###### suffixText

Type: `string`\
Default: Current `suffixText`

Text to be persisted after the text after the symbol. No suffix text will be displayed if set to an empty string.

<img src="screenshot-2.gif" width="480">

#### .clear()
Expand Down
48 changes: 42 additions & 6 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,16 @@ test('erases wrapped lines', t => {
t.is(clearedLines, 3); // Cleared 'foo\n\nbar'
t.is(cursorAtRow, -2);

spinner.clear();
reset();
spinner.prefixText = 'foo\n';
spinner.text = '\nbar';
spinner.suffixText = '\nbaz';
spinner.render();
spinner.render();
t.is(clearedLines, 4); // Cleared 'foo\n\nbar \nbaz'
t.is(cursorAtRow, -3);

spinner.stop();
});

Expand Down Expand Up @@ -399,6 +409,30 @@ test('.stopAndPersist() with dynamic prefixText', macro, spinner => {
spinner.stopAndPersist({symbol: '&', prefixText: () => 'babeee', text: 'yorkie'});
}, /babeee & yorkie\n$/, {prefixText: () => 'babeee'});

test('.stopAndPersist() with suffixText', macro, spinner => {
spinner.stopAndPersist({symbol: '@', text: 'foo'});
}, /@ foo bar\n$/, {suffixText: 'bar'});

test('.stopAndPersist() with empty suffixText', macro, spinner => {
spinner.stopAndPersist({symbol: '@', text: 'foo'});
}, /@ foo\n$/, {suffixText: ''});

test('.stopAndPersist() with manual suffixText', macro, spinner => {
spinner.stopAndPersist({symbol: '@', suffixText: 'baz', text: 'foo'});
}, /@ foo baz\n$/, {suffixText: 'bar'});

test('.stopAndPersist() with manual empty suffixText', macro, spinner => {
spinner.stopAndPersist({symbol: '@', suffixText: '', text: 'foo'});
}, /@ foo\n$/, {suffixText: 'bar'});

test('.stopAndPersist() with dynamic suffixText', macro, spinner => {
spinner.stopAndPersist({symbol: '&', suffixText: () => 'babeee', text: 'yorkie'});
}, /& yorkie babeee\n$/, {suffixText: () => 'babeee'});

test('.stopAndPersist() with prefixText and suffixText', macro, spinner => {
spinner.stopAndPersist({symbol: '@', text: 'foo'});
}, /bar @ foo baz\n$/, {prefixText: 'bar', suffixText: 'baz'});

// New clear method tests

const currentClearMethod = transFormTTY => {
Expand Down Expand Up @@ -597,21 +631,23 @@ test('new clear method test, erases wrapped lines', t => {
currentOra.clear();
currentOra.prefixText = 'foo\n';
currentOra.text = '\nbar';
currentOra.suffixText = '\nbaz';
currentOra.render();
currentOra.render();

spinner.clear();
spinner.prefixText = 'foo\n';
spinner.text = '\nbar';
spinner.suffixText = '\nbaz';
spinner.render();
spinner.render();
t.is(clearedLines(), 3); // Cleared 'foo\n\nbar'
t.is(cursorAtRow(), -2);
t.is(clearedLines(), 4); // Cleared 'foo\n\nbar \nbaz'
t.is(cursorAtRow(), -3);

const [sequenceString, clearedSequenceString] = transformTTY.getSequenceStrings();
const [frames, clearedFrames] = transformTTY.getFrames();

t.is(sequenceString, 'foo\n - \nbar');
t.is(sequenceString, 'foo\n - \nbar \nbaz');
t.is(sequenceString, clearedSequenceString);

t.deepEqual(clearedFrames, [
Expand All @@ -631,14 +667,14 @@ test('new clear method test, erases wrapped lines', t => {
'- 🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄\n'
+ '🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄🦄\n'
+ 'foo',
'foo\n - \nbar',
'foo\n - \nbar',
'foo\n - \nbar \nbaz',
'foo\n - \nbar \nbaz',
]);

t.deepEqual(frames, clearedFrames);

const currentClearString = currentClearTTY.toString();
t.is(currentClearString, 'foo\n - \nbar');
t.is(currentClearString, 'foo\n - \nbar \nbaz');

const currentFrames = currentClearTTY.getFrames();
t.deepEqual(frames, currentFrames);
Expand Down