├── .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 | 
7 | 
8 |
9 | ```
10 | npm install menu
11 | ```
12 |
13 | 
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 | }
--------------------------------------------------------------------------------