Skip to content

Instantly share code, notes, and snippets.

@nicolasmelo1
Last active November 21, 2024 21:18
Show Gist options
  • Save nicolasmelo1/fd3b2f425c4a2b8264f2bf5f42f06606 to your computer and use it in GitHub Desktop.
Save nicolasmelo1/fd3b2f425c4a2b8264f2bf5f42f06606 to your computer and use it in GitHub Desktop.
import { stdin as input, stdout as output } from 'process';
import { emitKeypressEvents } from 'readline';
function clearConsole(lengthOfOptions) {
//adapted from sindresorhus ansi-escape module
const ESC = '\u001B['
const eraseLine = ESC + '2K';
const cursorUp = (count = 1) => ESC + count + 'A'
const cursorLeft = ESC + 'G'
let clear = '';
for (let i = 0; i < lengthOfOptions; i++)
clear += eraseLine + (i < lengthOfOptions - 1 ? cursorUp() : '');
if (lengthOfOptions) clear += cursorLeft;
return clear;
}
function ask(question, { lengthOfOptions, onUp, onDown, onSelect, renderOption }) {
console.log(question);
const renderOptions = renderOptionsBuilder(renderOption, lengthOfOptions);
emitKeypressEvents(input);
input.setRawMode(true);
input.resume();
input.on('keypress', (_, key) => {
if (key) {
if (key.name === 'down') {
onDown()
renderOptions()
}
else if (key.name === 'up') {
onUp();
renderOptions()
}
else if (key.name === 'escape' || (key.name === 'c' && key.ctrl)) {
close()
} else if (key.name === 'return') {
output.write(clearConsole(lengthOfOptions+1));
onSelect()
close()
}
}
});
renderOptions(true);
}
function close() {
input.setRawMode(false)
input.pause()
process.exit(0)
}
function renderOptionsBuilder(renderOption, lengthOfOptions) {
return (isFirstRender = false) => {
if (!isFirstRender) output.write(clearConsole(lengthOfOptions));
for (let i=0; i < lengthOfOptions; i++) output.write(renderOption(i))
}
}
const main = () => {
const question = 'O que você prefere comer?';
const options = ['bosta', 'cocô', 'kiwi', 'insetos no geral', 'sei la'];
let selectedOption = 0;
ask(question, {
lengthOfOptions: options.length,
onUp: () => {
if (selectedOption <= 0) return;
selectedOption -= 1;
},
onDown: () => {
if (selectedOption >= options.length - 1) return;
selectedOption += 1;
},
onSelect: () => {
console.log(`${question} ${options[selectedOption]}`);
},
renderOption: (index) => {
const isSelected = index === selectedOption;
const isEnding = index === options.length - 1;
const optionText = `${isSelected ? `> ${options[index]}` : ` ${options[index]}`}${isEnding ? '' : '\n'}`;
return optionText;
}
})
}
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment