This Node.js module allows you to split and fit the text in console, dividing it in pages which user can go through.
I decided to create this based on Inquirer paginator util used on some prompts, 'cause it's quite useful when creating a CLI.
npm install cl-paginator
import { Paginator } from 'cl-paginator'
// const { Paginator } = require('cl-paginator')
var paginator = new Paginator(/* PageOptions */)
paginator.options({/* PageOptions */})
paginator.print(/* just text*/, /* PageOptions */)
Also, you can try it on Repl.it
paginator.print(text: string, options?: pageOptions)
Firstly, splits the given text to fit well in the console or the terminal option terminal: {cols, rows}
, without intercalated words. Then, listens to data
signals from a NodeJS.ReadStream
like process.stdin
(keyboard input) to check if Up or Down keys are pressed, then moving the current page.
/**
* Capture key arrows for moving the text UP and DOWN with a determinate PageSize.
* Use Message option for show an static text above the text.
* @param {string} text Defines the text to split up and fit in the console.
* @param {pageOptions | undefined} options Defines the options.
* @async Uses Promises, awaiting for the Return key pressed.
* @returns Promise, resolving in Boolean True.
*/
async paginator.print(text: string, options?: pageOptions)
interface pageOptions {
/**
* Defines the max number of lines to show in console. Use a positive integer
* @default 5
*/
page_size?: number;
/**
* Shows an static message above the text
* @default "Paginated text:"
*/
message?: string;
/**
* Shows a short suffix to help the user
* @default "(Use arrow keys)"
*/
suffix?: string;
/**
* Shows a message for help to continue executing the code
* @default "Press return key to exit"
*/
exit_message?: string;
/**
* Whether it's necessary to read all (going to bottom) or not to quit
* @default true
*/
read_to_return?: boolean;
/**
* The `NodeJS.WriteStream` to write on
* @default process.stdout
*/
writable?: NodeJS.WriteStream;
/**
* The `NodeJS.ReadStream` to read from
* @default process.stdin
*/
readable?: NodeJS.ReadStream;
/**
* Sets the maximum number of columns and rows.
* @default { cols: 0, rows: 0 } // Automatic
*/
terminal?: { cols?: number; rows?: number };
/**
* Sets the paginator style
* @default { }
*/
style?: { enum?: chalk.Chalk };
}
Also, you can define the default options to be used in every print.
paginator.options(options?: pageOptions): this
// or
var pagCustom = new Paginator(options?: pageOptions)
Stops the Paginator instance, without closing the program.
paginator.print('a') // a
paginator.end() // Stops the paginator inmediately, without closing the program with 'process.exit()'
Returns the Paginator instance options.
paginator.opts: pageOptions
Returns the Paginator instance total pages.
paginator.pages: number
Returns or sets the actual position in Paginator instance.
paginator.position: number // Returns the position
paginator.position = 2 // Sets the position
Returns the actualText, the text that is writed on writable ReadStream.
paginator.actualText: string
paginator.options(/* pageOptions */).print('Hello')
// `print` method is promise-based, therefore you would need to use `then` to chain next `print`s.
paginator.print('A').then(pag => pag.print('E')).then(pag => pag.print('I'))
var text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua...' // Just a big lorem ipsum
paginator.print(text, {page_size: 10}) // Shows 10 lines per page
var text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua...' // The same big lorem ipsum
paginator.print(lorem, {
page_size: 10,
exit_message: 'Please, press return to exit :D',
message: 'A big lorem ipsum!',
suffix: 'Use those arrows',
read_to_return: false
})
import chalk from 'chalk'
var text = chalk`Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod {bold.green tempor incididunt ut labore et dolore magna aliqua...}`
paginator.print(text, {
page_size: 10,
suffix: 'Use the arrows!',
read_to_return: false
})
var writable = new Writable() // new NodeJS.WriteStream
var readable = new Readable() // new NodeJS.ReadStream
readable.setRawMode(true) // Allows Ansi Escaping
readable.resume()
var text = var text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua...' // The same big lorem ipsum
paginator.print(text, {
page_size: 10,
writable: writable, // This allows to write on 'writable' WriteStream.
readable: readable, // This allows to read the input on 'readable' ReadStream.
})
// Check the examples folder for more info.
This needs just a bit of configuration, unless you want to make it really useful, such as storing every chunk that is writed in the instance.
Recommendation: As this package is oriented to be used in a console, it uses Ansi Escapes to do a lot of things, like erasing the console or hiding/showing the cursor, I recommend you to use a filter that erases all your data when this kind of Ansi Escapes are writed.
Ansi-Escapes,
Ansi-Regex
const clearAnsi = ['\x1B[0J', ansiEscapes.clearScreen, ansiEscapes.clearTerminal, /* and so on... */]
// This method is extended from 'WriteStream'. You need to put your code here to make it work, don't overwrite the 'write' method, use '_write' instead
public _write(chunk: any, encoding: BufferEncoding, callback: (error?: Error | null | undefined) => void) {
this.all += chunk // Saves the chunk in a permanent data
this.data += chunk // Saves the chunk in a temporal data
this.emit('writed', this.data) // Emits a 'writed' event with the temporal data, to make it versatille
var reg = this.data.match(ansiRegex()) // Matchs every Ansi Escape
var match = clearAnsi.some((val) => { return reg?.includes(val) })
if (match) { this.clear() } // If matchs any Ansi Escape oriented to cleaning~ erase the temporal data
callback()
}
If you want to create an automated ReadStream with Cl-Paginator
, you need to use ANSI escape codes, line \x0d
or x1b[A
.
Such as:
class Readable extends ReadStream {
constructor() {
super(0)
}
public emitKey(key: string) { // You could make an external function, instead
this.emit('data', Buffer.from(data.split("").map((v) => v.charCodeAt(0))))
}
}
// Then~
var readable = new Readable()
var paginator = new Paginator({ readable })
paginator.print(/* What you want~ */)
readable.emitKey("\x1b[B") // "Presses" the down key
readable.emitKey("\x0d") // "Presses" the return key
readable.emitKey("\x03") // Exits the program
- It's possible that Streams don't work properly.