const multiline = require("./multiline.js");
const extended = require ("./bloat.js");
const {EventEmitter} = require("events");
/**
* @typedef dualFunction
* @property {function} [hover] - the function to call when the element is selected but not called
* @property {function} [run] - the function to call when an element is selected and called
*/
/**
* A helper class for creating menus
* @extends multiline
*/
class menuCore extends multiline {
/**
* @param {Object} functions
* @param {dualFunction} functions.<propertyName> - The functions for each object, the option being named after the property name. Multiple of these are to be used.
*
* @param {Object} options
* @param {string} [options.up="up"] - the key to use for scrolling up in the menu
* @param {string} [options.down="down"] - the key to use for scrolling down in the menu
* @param {string} [options.confirmKey="return"] - the key that is listened for selecting options
* @param {string} [options.updateMode="internal"] - the mode to use for updating hovered elements, either internal (process inside the class), event (emit an event), function (run a custom function)
* @param {function|string} [options.modifier="\x1b[7m"] - the modifier for selected text. when mode is internal, this can for example be a color code. If it is set to function, this will be the function that gets called
* @param {Container} [options.instance] - the container to use for modifying text if mode is set to internal. Else this is not necessary to be provided.
*/
constructor(functions, options) {
super(Object.keys(functions));
if (options !== undefined) {
if (!options.hasOwnProperty("up")) {
options.up = "up";
}
if (!options.hasOwnProperty("down")) {
options.down = "down";
}
if (!options.hasOwnProperty("confirmKey")) {
options.confirmKey = "return";
}
if (!options.hasOwnProperty("updateMode")) {
options.updateMode = "internal";
}
if (!options.hasOwnProperty("modifier")) {
options.modifier = "\x1b[7m"
}
if (!options.hasOwnProperty("instance")) {
options.instance = undefined;
}
} else {
let options = {
"up" : "up",
"down" : "down",
"confirm" : true,
"confirmKey" : "enter"
}
}
for (let key in functions) {
if (typeof functions[key] === "function") {
let f = functions[key];
functions[key] = {};
functions[key].run = f;
functions[key].hover = () => {};
continue;
}
if (!functions[key].hasOwnProperty("hover")) {
functions[key].hover = () => {};
}
if (!functions[key].hasOwnProperty("run")) {
functions[key].run = () => {};
}
}
this.enabled = false;
//register Handler
this.eventUpdate = new EventEmitter();
this.updateOption = () => {
switch (options.updateMode) {
case "internal":
if (options.hasOwnProperty("instance") && options.instance !== undefined) {
let temparr = JSON.parse(JSON.stringify(super.textArr));
temparr[super.cur] = options.modifier+super.gC()+"\x1b[0m";
options.instance.setTextByArray(temparr, false);
}
break;
case "event":
this.eventUpdate.emit("update", super.cur, super.textArr);
break;
case "function":
options.modifier(super.textArr, super.cur);
break;
default:
break;
}
}
const readline = require('readline');
readline.emitKeypressEvents(process.stdin);
if (process.stdin.setRawMode){
process.stdin.setRawMode(true);
}
process.stdin.resume();
process.stdin.on("keypress", (ch, key) => {
if (this.enabled) {
if (key.ctrl || key.meta) {
return;
}
switch (key.name) {
case options.up:
super.dec();
this.updateOption();
functions[super.gC()].hover();
break;
case options.down:
super.inc();
this.updateOption();
functions[super.gC()].hover();
break;
case options.confirmKey:
this.enabled = false;
functions[super.gC()].run();
break;
}
}
});
this.init = () => {
this.updateOption();
functions[super.gC()].hover();
}
}
/**
* Enable the menu.
* Will disable once an option is selected, and will emit hover update events if set to this mode in constructor, else the return can be ignored.
* @returns {module:events.EventEmitter.EventEmitter}
*/
enable () {
this.enabled = true;
this.init();
return this.eventUpdate;
}
}
module.exports = menuCore;