Skip to content

Commit 2378eaf

Browse files
authoredMar 24, 2023
Add suffixText option (#223)
1 parent 16cc211 commit 2378eaf

File tree

6 files changed

+153
-14
lines changed

6 files changed

+153
-14
lines changed
 

β€Žexample.js

+26-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import process from 'node:process';
22
import chalk from 'chalk';
3+
import logSymbols from 'log-symbols';
34
import ora from './index.js';
45

56
const spinner = ora({
@@ -46,7 +47,30 @@ setTimeout(() => {
4647
}, 6000);
4748

4849
setTimeout(() => {
49-
spinner.succeed();
50-
}, 7000);
50+
spinner.prefixText = chalk.dim('[info]');
51+
spinner.spinner = 'dots';
52+
spinner.text = 'Loading with prefix text';
53+
}, 8000);
54+
55+
setTimeout(() => {
56+
spinner.prefixText = '';
57+
spinner.suffixText = chalk.dim('[info]');
58+
spinner.text = 'Loading with suffix text';
59+
}, 10_000);
60+
61+
setTimeout(() => {
62+
spinner.prefixText = chalk.dim('[info]');
63+
spinner.suffixText = chalk.dim('[info]');
64+
spinner.text = 'Loading with prefix and suffix text';
65+
}, 12_000);
66+
67+
setTimeout(() => {
68+
spinner.stopAndPersist({
69+
prefixText: '',
70+
suffixText: '',
71+
symbol: logSymbols.info,
72+
text: 'Stopping with different text!',
73+
});
74+
}, 14_000);
5175

5276
// $ node example.js nameOfSpinner

β€Žindex.d.ts

+21
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ export type Color =
1818

1919
export type PrefixTextGenerator = () => string;
2020

21+
export type SuffixTextGenerator = () => string;
22+
2123
export interface Options {
2224
/**
2325
Text to display after the spinner.
@@ -29,6 +31,11 @@ export interface Options {
2931
*/
3032
readonly prefixText?: string | PrefixTextGenerator;
3133

34+
/**
35+
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.
36+
*/
37+
readonly suffixText?: string | SuffixTextGenerator;
38+
3239
/**
3340
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.
3441
@@ -130,6 +137,13 @@ export interface PersistOptions {
130137
Default: Current `prefixText`.
131138
*/
132139
readonly prefixText?: string | PrefixTextGenerator;
140+
141+
/**
142+
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.
143+
144+
Default: Current `suffixText`.
145+
*/
146+
readonly suffixText?: string | SuffixTextGenerator;
133147
}
134148

135149
export interface PromiseOptions<T> extends Options {
@@ -161,6 +175,13 @@ export interface Ora {
161175
*/
162176
prefixText: string;
163177

178+
/**
179+
Change the text or function that returns text after the spinner text.
180+
181+
No suffix text will be displayed if set to an empty string.
182+
*/
183+
suffixText: string;
184+
164185
/**
165186
Change the spinner color.
166187
*/

β€Žindex.js

+40-5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class Ora {
2424
#indent;
2525
#text;
2626
#prefixText;
27+
#suffixText;
2728

2829
color;
2930

@@ -57,6 +58,7 @@ class Ora {
5758
// It's important that these use the public setters.
5859
this.text = this.#options.text;
5960
this.prefixText = this.#options.prefixText;
61+
this.suffixText = this.#options.suffixText;
6062
this.indent = this.#options.indent;
6163

6264
if (process.env.NODE_ENV === 'test') {
@@ -147,6 +149,15 @@ class Ora {
147149
this.updateLineCount();
148150
}
149151

152+
get suffixText() {
153+
return this.#suffixText;
154+
}
155+
156+
set suffixText(value) {
157+
this.#suffixText = value || '';
158+
this.updateLineCount();
159+
}
160+
150161
get isSpinning() {
151162
return this.#id !== undefined;
152163
}
@@ -164,12 +175,26 @@ class Ora {
164175
return '';
165176
}
166177

178+
getFullSuffixText(suffixText = this.#suffixText, prefix = ' ') {
179+
if (typeof suffixText === 'string' && suffixText !== '') {
180+
return prefix + suffixText;
181+
}
182+
183+
if (typeof suffixText === 'function') {
184+
return prefix + suffixText();
185+
}
186+
187+
return '';
188+
}
189+
167190
updateLineCount() {
168191
const columns = this.#stream.columns || 80;
169192
const fullPrefixText = this.getFullPrefixText(this.#prefixText, '-');
193+
const fullSuffixText = this.getFullSuffixText(this.#suffixText, '-');
194+
const fullText = ' '.repeat(this.#indent) + fullPrefixText + '--' + this.#text + '--' + fullSuffixText;
170195

171196
this.#lineCount = 0;
172-
for (const line of stripAnsi(' '.repeat(this.#indent) + fullPrefixText + '--' + this.#text).split('\n')) {
197+
for (const line of stripAnsi(fullText).split('\n')) {
173198
this.#lineCount += Math.max(1, Math.ceil(wcwidth(line) / columns));
174199
}
175200
}
@@ -209,8 +234,9 @@ class Ora {
209234
this.#frameIndex = ++this.#frameIndex % frames.length;
210235
const fullPrefixText = (typeof this.#prefixText === 'string' && this.#prefixText !== '') ? this.#prefixText + ' ' : '';
211236
const fullText = typeof this.text === 'string' ? ' ' + this.text : '';
237+
const fullSuffixText = (typeof this.#suffixText === 'string' && this.#suffixText !== '') ? ' ' + this.#suffixText : '';
212238

213-
return fullPrefixText + frame + fullText;
239+
return fullPrefixText + frame + fullText + fullSuffixText;
214240
}
215241

216242
clear() {
@@ -328,12 +354,21 @@ class Ora {
328354
return this;
329355
}
330356

331-
const prefixText = options.prefixText || this.#prefixText;
332-
const text = options.text || this.text;
357+
const prefixText = options.prefixText ?? this.#prefixText;
358+
const fullPrefixText = this.getFullPrefixText(prefixText, ' ');
359+
360+
const symbolText = options.symbol ?? ' ';
361+
362+
const text = options.text ?? this.text;
333363
const fullText = (typeof text === 'string') ? ' ' + text : '';
334364

365+
const suffixText = options.suffixText ?? this.#suffixText;
366+
const fullSuffixText = this.getFullSuffixText(suffixText, ' ');
367+
368+
const textToWrite = fullPrefixText + symbolText + fullText + fullSuffixText + '\n';
369+
335370
this.stop();
336-
this.#stream.write(`${this.getFullPrefixText(prefixText, ' ')}${options.symbol || ' '}${fullText}\n`);
371+
this.#stream.write(textToWrite);
337372

338373
return this;
339374
}

β€Žindex.test-d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ const spinner = ora('Loading unicorns');
77
ora({text: 'Loading unicorns'});
88
ora({prefixText: 'Loading unicorns'});
99
ora({prefixText: () => 'Loading unicorns dynamically'});
10+
ora({suffixText: 'Loading unicorns'});
11+
ora({suffixText: () => 'Loading unicorns dynamically'});
1012
ora({spinner: 'squish'});
1113
ora({spinner: {frames: ['-', '+', '-']}});
1214
ora({spinner: {interval: 80, frames: ['-', '+', '-']}});
@@ -40,6 +42,7 @@ spinner.stopAndPersist();
4042
spinner.stopAndPersist({text: 'all done'});
4143
spinner.stopAndPersist({symbol: '@', text: 'all done'});
4244
spinner.stopAndPersist({prefixText: 'all done'});
45+
spinner.stopAndPersist({suffixText: 'all done'});
4346
spinner.clear();
4447
spinner.render();
4548
spinner.frame();
@@ -58,6 +61,7 @@ void oraPromise(async () => {
5861
}, 'foo');
5962
void oraPromise(async spinner => {
6063
spinner.prefixText = 'foo';
64+
spinner.suffixText = '[loading]';
6165
await resolves;
6266
return 7;
6367
}, {

β€Žreadme.md

+20-1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ Type: `string | () => string`
5050

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

53+
##### suffixText
54+
55+
Type: `string | () => string`
56+
57+
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.
58+
5359
##### spinner
5460

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

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

151+
#### .suffixText <sup>get/set</sup>
152+
153+
Change the text after the spinner text.
154+
155+
No suffix text will be displayed if set to an empty string.
156+
145157
#### .color <sup>get/set</sup>
146158

147159
Change the spinner color.
@@ -208,7 +220,7 @@ Symbol to replace the spinner with.
208220
Type: `string`\
209221
Default: Current `'text'`
210222

211-
Text to be persisted after the symbol
223+
Text to be persisted after the symbol.
212224

213225
###### prefixText
214226

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

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

232+
###### suffixText
233+
234+
Type: `string`\
235+
Default: Current `suffixText`
236+
237+
Text to be persisted after the text after the symbol. No suffix text will be displayed if set to an empty string.
238+
220239
<img src="screenshot-2.gif" width="480">
221240

222241
#### .clear()

β€Žtest.js

+42-6
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,16 @@ test('erases wrapped lines', t => {
239239
t.is(clearedLines, 3); // Cleared 'foo\n\nbar'
240240
t.is(cursorAtRow, -2);
241241

242+
spinner.clear();
243+
reset();
244+
spinner.prefixText = 'foo\n';
245+
spinner.text = '\nbar';
246+
spinner.suffixText = '\nbaz';
247+
spinner.render();
248+
spinner.render();
249+
t.is(clearedLines, 4); // Cleared 'foo\n\nbar \nbaz'
250+
t.is(cursorAtRow, -3);
251+
242252
spinner.stop();
243253
});
244254

@@ -398,6 +408,30 @@ test('.stopAndPersist() with dynamic prefixText', macro, spinner => {
398408
spinner.stopAndPersist({symbol: '&', prefixText: () => 'babeee', text: 'yorkie'});
399409
}, /babeee & yorkie\n$/, {prefixText: () => 'babeee'});
400410

411+
test('.stopAndPersist() with suffixText', macro, spinner => {
412+
spinner.stopAndPersist({symbol: '@', text: 'foo'});
413+
}, /@ foo bar\n$/, {suffixText: 'bar'});
414+
415+
test('.stopAndPersist() with empty suffixText', macro, spinner => {
416+
spinner.stopAndPersist({symbol: '@', text: 'foo'});
417+
}, /@ foo\n$/, {suffixText: ''});
418+
419+
test('.stopAndPersist() with manual suffixText', macro, spinner => {
420+
spinner.stopAndPersist({symbol: '@', suffixText: 'baz', text: 'foo'});
421+
}, /@ foo baz\n$/, {suffixText: 'bar'});
422+
423+
test('.stopAndPersist() with manual empty suffixText', macro, spinner => {
424+
spinner.stopAndPersist({symbol: '@', suffixText: '', text: 'foo'});
425+
}, /@ foo\n$/, {suffixText: 'bar'});
426+
427+
test('.stopAndPersist() with dynamic suffixText', macro, spinner => {
428+
spinner.stopAndPersist({symbol: '&', suffixText: () => 'babeee', text: 'yorkie'});
429+
}, /& yorkie babeee\n$/, {suffixText: () => 'babeee'});
430+
431+
test('.stopAndPersist() with prefixText and suffixText', macro, spinner => {
432+
spinner.stopAndPersist({symbol: '@', text: 'foo'});
433+
}, /bar @ foo baz\n$/, {prefixText: 'bar', suffixText: 'baz'});
434+
401435
// New clear method tests
402436

403437
const currentClearMethod = transFormTTY => {
@@ -596,21 +630,23 @@ test('new clear method test, erases wrapped lines', t => {
596630
currentOra.clear();
597631
currentOra.prefixText = 'foo\n';
598632
currentOra.text = '\nbar';
633+
currentOra.suffixText = '\nbaz';
599634
currentOra.render();
600635
currentOra.render();
601636

602637
spinner.clear();
603638
spinner.prefixText = 'foo\n';
604639
spinner.text = '\nbar';
640+
spinner.suffixText = '\nbaz';
605641
spinner.render();
606642
spinner.render();
607-
t.is(clearedLines(), 3); // Cleared 'foo\n\nbar'
608-
t.is(cursorAtRow(), -2);
643+
t.is(clearedLines(), 4); // Cleared 'foo\n\nbar \nbaz'
644+
t.is(cursorAtRow(), -3);
609645

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

613-
t.is(sequenceString, 'foo\n - \nbar');
649+
t.is(sequenceString, 'foo\n - \nbar \nbaz');
614650
t.is(sequenceString, clearedSequenceString);
615651

616652
t.deepEqual(clearedFrames, [
@@ -630,14 +666,14 @@ test('new clear method test, erases wrapped lines', t => {
630666
'- πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„\n'
631667
+ 'πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„πŸ¦„\n'
632668
+ 'foo',
633-
'foo\n - \nbar',
634-
'foo\n - \nbar',
669+
'foo\n - \nbar \nbaz',
670+
'foo\n - \nbar \nbaz',
635671
]);
636672

637673
t.deepEqual(frames, clearedFrames);
638674

639675
const currentClearString = currentClearTTY.toString();
640-
t.is(currentClearString, 'foo\n - \nbar');
676+
t.is(currentClearString, 'foo\n - \nbar \nbaz');
641677

642678
const currentFrames = currentClearTTY.getFrames();
643679
t.deepEqual(frames, currentFrames);

0 commit comments

Comments
 (0)
Please sign in to comment.