├── .gitignore ├── .travis.yml ├── package.json ├── LICENSE.md ├── example.js ├── README.md └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .project 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | language: node_js 3 | node_js: 4 | - "0.12" 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "menu", 3 | "version": "0.2.5", 4 | "author": "Brandon Barber (@roecrew)", 5 | "license": "MIT", 6 | "description": "A terminal menu for your node.", 7 | "main": "index.js", 8 | "repository": { 9 | "type": "git", 10 | "url": "git://github.com/roecrew/menu.git" 11 | }, 12 | "bin": { 13 | "menu": "./example.js" 14 | }, 15 | "keywords": [ 16 | "menu" 17 | ], 18 | "dependencies": { 19 | "keypress": "*", 20 | "clivas": "*" 21 | } 22 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Brandon Barber 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | process.stdin.setEncoding('utf8'); 4 | 5 | var options; 6 | var content = { 7 | tab1: {id:1, title:"column1", seltitle: "COLUMN1", type:"toggle", val:"true", color:"blue"}, 8 | tab2: {id:2, title:["column2", "column22"], seltitle: ["COLUMN2", "COLUMN22"], type:"search", input: 0, val:"abcdefgh", color:"red"}, 9 | tab3: {id:3, title:"column3", seltitle: "COLUMN3", type:"list", input: 0, val:["a", "b", "c"], color:"white"}, 10 | tab4: {id:4, title:"column4", seltitle: "COLUMN4", type:"select", input: 0, val:["1","2","3","4","5","6"], color:"green"} 11 | } 12 | 13 | var menu = require('./index.js'); 14 | menu.init(content); 15 | 16 | function work() { 17 | menu.start(options, function(result) { 18 | var key = result[0]; 19 | var column = result[1]; 20 | 21 | if(key.name=="return" && column.id === 4) { 22 | menu.draw(); 23 | menu.clivas.line(Math.abs(column.input%column.val.length)); 24 | } else if(key.name=="return" && column.id === 2) { 25 | menu.draw(); 26 | menu.clivas.line("You searched for "+column.val); 27 | column.val = ""; 28 | } else if(key.ctrl && key.name ==="c") { 29 | process.exit(); 30 | } else if(key.name ==="backspace") { 31 | menu.draw(); 32 | } else if(key.name ==="left") { 33 | menu.draw(); 34 | } else if(key.name ==="up") { 35 | menu.draw(); 36 | } else if(key.name ==="down") { 37 | menu.draw(); 38 | } else if(key.name ==="right") { 39 | menu.draw(); 40 | } else { 41 | menu.draw(); 42 | } 43 | 44 | if(column.id === 1) { 45 | if(column.val) { 46 | menu.columns["tab4"].val = ["z","y","x","w"]; 47 | } else { 48 | menu.columns["tab4"].val = ["1","2","3","4","5","6"]; 49 | } 50 | menu.draw(); 51 | } 52 | }) 53 | } work(); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # menu # 2 | 3 | A cli menu for your node. 4 | 5 | ## Install ## 6 | ![build alt](https://travis-ci.org/roecrew/menu.svg?branch=master) 7 | ![platform alt](https://img.shields.io/badge/platform-windows%20|%20linux%20|%20osx-blue.svg) 8 | 9 | ``` 10 | npm install menu 11 | ``` 12 | 13 | ![ScreenShot1](http://s23.postimg.org/l4zi60cuj/Screen_Shot_2015_04_03_at_19_02_29.png) 14 | 15 | ## API ## 16 | #### Tabs 17 | Menu is built around tabs.

18 | There are currently four types:
19 | * **Toggle**: This tab functions like a button. 20 | * **List**: This tab selects a single value from its list. 21 | * **Search**: This tab takes a string input, and has the function of a list tab. 22 | * **Select**: This tab creates a selectable drop-down list. 23 |
24 | 25 | These tabs are declared as such... 26 | 27 | var content = { 28 | tab1: {title:"column1", seltitle: "COLUMN1", type:"toggle", val:"true", color:"blue"}, 29 | tab2: {title:["column2", "column22"], seltitle: ["COLUMN2", "COLUMN22"], type:"search", input: 0, val:"abcdefgh", color:"red"}, 30 | tab3: {title:"column3", seltitle: "COLUMN3", type:"list", input: 0, val:["a", "b", "c"], color:"white"}, 31 | tab4: {title:"column4", seltitle: "COLUMN4", type:"select", input: 0, val:["1","2","3","4","5","6"], color:"green"} 32 | } 33 | 34 | #### Properties 35 | 36 | title: This is the tabs label when it's not focused.
37 | seltitle: This is the tabs label when it is focused.
38 | type: This is the tab's type.
39 | input: This is the index value for a **list**, **select**, and **search** tabs.
40 | val: For type **toggle**, it's a boolean value. For types **list** and **select**, it's an array of values. For type **search** it's a string.
41 | color: The tab's set color. Look at the node **clivas** for all possible values. 42 | 43 | #### Initialization 44 | 45 | var menu = require('./index.js'); 46 | 47 | menu.init(content); 48 | 49 | menu.draw() //this draws the headers 50 | 51 | menu.clivas //you can custom draw with clivas 52 | 53 | #### Delegate 54 | 55 | function work() { 56 | menu.start(options, function(result) { 57 | var key = result[0]; //refer to node keypress for all properties 58 | var column = result[1]; //the focused tab 59 | 60 | menu.draw(); //draw tabs 61 | 62 | if(key.name=="return" && column.id === 4) { 63 | menu.clivas.line(Math.abs(column.input%column.val.length)); //always mod your column input to determine focused index 64 | } else if(key.name=="return" && column.id === 2) { 65 | menu.clivas.line("You searched for "+column.val); 66 | column.val = ""; 67 | } else if(key.ctrl && key.name ==="c") { 68 | process.exit(); 69 | } else if(key.name ==="backspace") { 70 | 71 | } else if(key.name ==="left") { 72 | 73 | } else if(key.name ==="up") { 74 | 75 | } else if(key.name ==="down") { 76 | 77 | } else if(key.name ==="right") { 78 | 79 | } else { 80 | 81 | } 82 | 83 | if(column.id === 1) { //use tab 1 to toggle tab 4's select contents 84 | if(column.val) { 85 | menu.columns["tab4"].val = ["z","y","x","w"]; 86 | } else { 87 | menu.columns["tab4"].val = ["1","2","3","4","5","6"]; 88 | } 89 | menu.draw(); 90 | } 91 | }) 92 | } work(); 93 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var clivas = require('clivas') 2 | var keypress = require('keypress') 3 | keypress(process.stdin); 4 | 5 | var currentCol; 6 | var currentSel; 7 | 8 | var idx = module.exports = {}; 9 | idx.clivas = clivas; 10 | idx.columns; 11 | idx.column_cnt = 0; 12 | idx.column_val = 1000; 13 | 14 | idx.init = function(cols) { 15 | idx.columns = cols; 16 | for(var key in idx.columns) { 17 | idx.column_cnt++; 18 | if(idx.columns[key].type == "list") { 19 | var tmp = idx.columns[key].val.length 20 | idx.columns[key].input = tmp*tmp*tmp; 21 | } else if(idx.columns[key].type == "select") { 22 | var tmp = idx.columns[key].val.length 23 | idx.columns[key].input = -1*tmp*tmp*tmp; 24 | } else if(idx.columns[key].type == "search") { 25 | var tmp = idx.columns[key].title.length; 26 | idx.columns[key].input = tmp*tmp*tmp; 27 | } 28 | } 29 | idx.draw(); 30 | } 31 | 32 | function header_tab() { 33 | var cnt = 0; 34 | var modul = idx.column_val%idx.column_cnt; 35 | for(var key in idx.columns) { 36 | if(cnt === modul-1) { 37 | if(idx.columns[key].type === "search") { 38 | var i = idx.columns[key].input%idx.columns[key].title.length; 39 | clivas.write("{bold+"+idx.columns[key].color+":"+idx.columns[key].title[i]+" }"); 40 | clivas.write("{bold:┃ }"); 41 | } else { 42 | clivas.write("{bold+"+idx.columns[key].color+":"+idx.columns[key].title+" }"); 43 | clivas.write("{bold:┃ }"); 44 | } 45 | } else if(cnt === modul){ 46 | currentCol = idx.columns[key]; 47 | if(currentCol.type === "toggle") { 48 | clivas.write("{bold+"+currentCol.color+":"+currentCol.seltitle+" "+currentCol.val+" }"); 49 | } else if(currentCol.type === "list") { 50 | var i = Math.abs(currentCol.input%currentCol.val.length); 51 | clivas.write("{bold+"+currentCol.color+":"+currentCol.seltitle+" "+currentCol.val[i]+" }"); 52 | } else if(currentCol.type === "search") { 53 | var i = Math.abs(currentCol.input%currentCol.title.length); 54 | clivas.write("{bold+"+currentCol.color+":"+currentCol.seltitle[i]+" }"); 55 | } 56 | else { 57 | currentSel = currentCol; 58 | clivas.write("{bold+"+idx.columns[key].color+":"+idx.columns[key].seltitle+" }"); 59 | } 60 | clivas.write("{bold:┃ }"); 61 | } else { 62 | if(idx.columns[key].type === "search") { 63 | var i = idx.columns[key].input%idx.columns[key].title.length; 64 | 65 | clivas.write("{bold+"+idx.columns[key].color+":"+idx.columns[key].title[i]+" }"); 66 | clivas.write("{bold:│ }"); 67 | } else { 68 | clivas.write("{bold+"+idx.columns[key].color+":"+idx.columns[key].title+" }"); 69 | clivas.write("{bold:│ }"); 70 | } 71 | } 72 | cnt++; 73 | } 74 | } 75 | 76 | idx.draw = function() { 77 | 78 | process.stdout.clearLine(); 79 | process.stdout.cursorTo(0); 80 | clivas.clear() 81 | clivas.line("{bold:┎──────────────────────────────────────────────────────────────────────────────────────────────────────────────────}") 82 | clivas.write("{bold:┃ }"); 83 | header_tab(); 84 | clivas.line("") 85 | clivas.line("{bold:┠──────────────────────────────────────────────────────────────────────────────────────────────────────────────────}") 86 | 87 | if(currentSel !== undefined) { 88 | var x = Math.abs(currentSel.input%currentSel.val.length); 89 | for(var i=0; i}"+"{bold+cyan: "+currentSel.val[i]+"}"); 92 | } 93 | else { 94 | clivas.line("{bold:┃ "+currentSel.val[i]+"}"); 95 | } 96 | } 97 | } 98 | 99 | if(currentCol.type === "toggle") { 100 | } else if(currentCol.type === "search") { 101 | clivas.line("{bold:┖──────────────────────────────────────────────────────────────────────────────────────────────────────────────────}") 102 | clivas.write(" Input:"+ currentCol.val) 103 | } else if(currentCol.type === "list") { 104 | } 105 | } 106 | 107 | idx.start = function(params, callback) { 108 | var stdin = process.openStdin() 109 | process.stdin.setRawMode(true) 110 | stdin.on('keypress', function (chunk, key) { 111 | if (key && key.ctrl && key.name == 'c') { 112 | callback([key, currentCol]); 113 | } 114 | else if(key.name == "right") { 115 | idx.column_val++; 116 | callback([key, currentCol]); 117 | } 118 | else if(key.name == "left") { 119 | idx.column_val--; 120 | callback([key, currentCol]); 121 | } 122 | else if(key.name == "up") { 123 | 124 | if(currentCol.type === "toggle") { 125 | if(currentCol.val != false) { 126 | currentCol.val = false; 127 | } else { 128 | currentCol.val = true; 129 | } 130 | } else if(currentCol.type === "list") { 131 | ++currentCol.input; 132 | } else if(currentCol.type === "search") { 133 | ++currentCol.input; 134 | } else if(currentCol.type === "select") { 135 | ++currentCol.input; 136 | } 137 | 138 | callback([key, currentCol]); 139 | } 140 | else if(key.name == "down") { 141 | 142 | if(currentCol.type === "toggle") { 143 | if(currentCol.val != false) { 144 | currentCol.val = false; 145 | } else { 146 | currentCol.val = true; 147 | } 148 | } else if(currentCol.type === "list") { 149 | --currentCol.input; 150 | } else if(currentCol.type === "search") { 151 | --currentCol.input; 152 | } else if(currentCol.type === "select") { 153 | --currentCol.input; 154 | } 155 | 156 | callback([key, currentCol]); 157 | } 158 | else if(key.name == "backspace") { 159 | 160 | if(currentCol.type === "toggle") { 161 | if(currentCol.val != false) { 162 | currentCol.val = false; 163 | } else { 164 | currentCol.val = true; 165 | } 166 | } else if(currentCol.type === "list") { 167 | 168 | } else if(currentCol.type === "search") { 169 | currentCol.val = currentCol.val.slice(0, currentCol.val.length-1) 170 | clivas.write(" Search:"+ currentCol.val) 171 | } 172 | 173 | callback([key, currentCol]); 174 | } 175 | else if(key.name == "return") { 176 | if(currentCol.type === "toggle") { 177 | if(currentCol.val != false) { 178 | currentCol.val = false; 179 | } else { 180 | currentCol.val = true; 181 | } 182 | } else if(currentCol.type === "list") { 183 | } else if(currentCol.type === "search") { 184 | } 185 | 186 | callback([key, currentCol]); 187 | } 188 | else { 189 | if(currentCol.type === "toggle") { 190 | 191 | } else if(currentCol.type === "list") { 192 | 193 | } else if(currentCol.type === "search") { 194 | currentCol.val += chunk 195 | clivas.write(" Search:"+ currentCol.val) 196 | } 197 | callback([key, currentCol, clivas]); 198 | } 199 | }) 200 | } --------------------------------------------------------------------------------