add shrinkBox. add forms. add RadioSet. misc refactor.
This commit is contained in:
parent
cac1924b14
commit
7981f8d749
125
README.md
125
README.md
|
@ -91,6 +91,7 @@ The base node which everything inherits from.
|
|||
##### Properties:
|
||||
|
||||
- inherits all from EventEmitter.
|
||||
- **type** - type of the node (e.g. `box`).
|
||||
- **options** - original options object.
|
||||
- **parent** - parent node.
|
||||
- **screen** - parent screen.
|
||||
|
@ -241,7 +242,8 @@ The base element.
|
|||
- **label** - a simple text label for the element.
|
||||
- **align** - text alignment: `left`, `center`, or `right`.
|
||||
- **valign** - vertical text alignment: `top`, `middle`, or `bottom`.
|
||||
- **shrink** - shrink/flex/grow to content width during render.
|
||||
- **shrink** - shrink/flex/grow to content width/height during render.
|
||||
- **shrinkBox** - shrink/flex/grow to combined coordinates of all child boxes.
|
||||
- **padding** - amount of padding on the inside of the element.
|
||||
|
||||
##### Properties:
|
||||
|
@ -268,6 +270,7 @@ The base element.
|
|||
- **rright** - calculated relative right offset.
|
||||
- **rtop** - calculated relative top offset.
|
||||
- **rbottom** - calculated relative bottom offset.
|
||||
- **name** - name of the element. useful for form submission.
|
||||
|
||||
##### Events:
|
||||
|
||||
|
@ -456,6 +459,39 @@ pre-existing newlines and escape codes.
|
|||
|
||||
- inherits all from ScrollableBox.
|
||||
|
||||
|
||||
#### Form (from Box)
|
||||
|
||||
A form which can contain form elements.
|
||||
|
||||
##### Options:
|
||||
|
||||
- inherits all from Box.
|
||||
- **keys** - allow default keys (tab, vi keys, enter).
|
||||
- **vi** - allow vi keys.
|
||||
|
||||
##### Properties:
|
||||
|
||||
- inherits all from Box.
|
||||
- **submission** - last submitted data.
|
||||
|
||||
##### Events:
|
||||
|
||||
- inherits all from Box.
|
||||
- **submit** - form is submitted. receives a data object.
|
||||
- **cancel** - form is discarded.
|
||||
- **reset** - form is cleared.
|
||||
|
||||
##### Methods:
|
||||
|
||||
- inherits all from Box.
|
||||
- **focusNext()** - focus next form element.
|
||||
- **focusPrevious()** - focus previous form element.
|
||||
- **submit()** - submit the form.
|
||||
- **cancel()** - discard the form.
|
||||
- **reset()** - clear the form.
|
||||
|
||||
|
||||
#### Input (from Box)
|
||||
|
||||
A form input.
|
||||
|
@ -476,6 +512,9 @@ A box which allows text input.
|
|||
##### Events:
|
||||
|
||||
- inherits all from Input.
|
||||
- **submit** - value is submitted (enter).
|
||||
- **cancel** - value is discared (escape).
|
||||
- **action** - either submit or cancel.
|
||||
|
||||
##### Methods:
|
||||
|
||||
|
@ -502,6 +541,9 @@ A box which allows multiline text input.
|
|||
##### Events:
|
||||
|
||||
- inherits all from Input/ScrollableText.
|
||||
- **submit** - value is submitted (enter).
|
||||
- **cancel** - value is discared (escape).
|
||||
- **action** - either submit or cancel.
|
||||
|
||||
##### Methods:
|
||||
|
||||
|
@ -542,7 +584,7 @@ A button which can be focused and allows key and mouse input.
|
|||
|
||||
#### ProgressBar (from Input)
|
||||
|
||||
A progress bar allowing various styles.
|
||||
A progress bar allowing various styles. This can also be used as a form input.
|
||||
|
||||
##### Options:
|
||||
|
||||
|
@ -551,6 +593,9 @@ A progress bar allowing various styles.
|
|||
(can be contained in `style`: e.g. `style.bar.fg`).
|
||||
- **ch** - the character to fill the bar with (default is space).
|
||||
- **filled** - the amount filled (0 - 100).
|
||||
- **value** - same as `filled`.
|
||||
- **keys** - enable key support.
|
||||
- **mouse** - enable mouse support.
|
||||
|
||||
##### Properties:
|
||||
|
||||
|
@ -566,6 +611,7 @@ A progress bar allowing various styles.
|
|||
|
||||
- inherits all from Input.
|
||||
- **progress(amount)** - progress the bar by a fill amount.
|
||||
- **setProgress(amount)** - set progress to specific amount.
|
||||
- **reset()** - reset the bar.
|
||||
|
||||
|
||||
|
@ -598,6 +644,81 @@ A very simple file manager for selecting files.
|
|||
- **reset([cwd], [callback])** - reset back to original cwd.
|
||||
|
||||
|
||||
#### Checkbox (from Input)
|
||||
|
||||
A checkbox which can be used in a form element.
|
||||
|
||||
##### Options:
|
||||
|
||||
- inherits all from Input.
|
||||
- **checked** - whether the element is checked or not.
|
||||
- **mouse** - enable mouse support.
|
||||
|
||||
##### Properties:
|
||||
|
||||
- inherits all from Input.
|
||||
- **text** - the text next to the checkbox (do not use setContent, use
|
||||
`check.text = ''`).
|
||||
- **checked** - whether the element is checked or not.
|
||||
- **value** - same as `checked`.
|
||||
|
||||
##### Events:
|
||||
|
||||
- inherits all from Input.
|
||||
- **check** - received when element is checked.
|
||||
- **uncheck** received when element is unchecked.
|
||||
|
||||
##### Methods:
|
||||
|
||||
- inherits all from Input.
|
||||
- **check()** - check the element.
|
||||
- **uncheck()** - uncheck the element.
|
||||
- **toggle()** - toggle checked state.
|
||||
|
||||
|
||||
#### RadioSet (from Box)
|
||||
|
||||
An element wrapping RadioButtons. RadioButtons within this element will be
|
||||
mutually exclusive with each other.
|
||||
|
||||
##### Options:
|
||||
|
||||
- inherits all from Box.
|
||||
|
||||
##### Properties:
|
||||
|
||||
- inherits all from Box.
|
||||
|
||||
##### Events:
|
||||
|
||||
- inherits all from Box.
|
||||
|
||||
##### Methods:
|
||||
|
||||
- inherits all from Box.
|
||||
|
||||
|
||||
#### RadioButton (from Checkbox)
|
||||
|
||||
A radio button which can be used in a form element.
|
||||
|
||||
##### Options:
|
||||
|
||||
- inherits all from Checkbox.
|
||||
|
||||
##### Properties:
|
||||
|
||||
- inherits all from Checkbox.
|
||||
|
||||
##### Events:
|
||||
|
||||
- inherits all from Checkbox.
|
||||
|
||||
##### Methods:
|
||||
|
||||
- inherits all from Checkbox.
|
||||
|
||||
|
||||
### Positioning
|
||||
|
||||
Offsets may be a number, a percentage (e.g. `50%`), or a keyword (e.g.
|
||||
|
|
439
lib/widget.js
439
lib/widget.js
|
@ -119,8 +119,8 @@ Node.prototype.remove = function(element) {
|
|||
if (this.type !== 'screen') {
|
||||
i = this.screen.clickable.indexOf(element);
|
||||
if (~i) this.screen.clickable.splice(i, 1);
|
||||
i = this.screen.input.indexOf(element);
|
||||
if (~i) this.screen.input.splice(i, 1);
|
||||
i = this.screen.keyable.indexOf(element);
|
||||
if (~i) this.screen.keyable.splice(i, 1);
|
||||
}
|
||||
|
||||
if (this.type === 'screen' && this.focused === element) {
|
||||
|
@ -267,7 +267,7 @@ function Screen(options) {
|
|||
this.hover = null;
|
||||
this.history = [];
|
||||
this.clickable = [];
|
||||
this.input = [];
|
||||
this.keyable = [];
|
||||
this.grabKeys = false;
|
||||
this.lockKeys = false;
|
||||
this.focused;
|
||||
|
@ -372,6 +372,7 @@ Screen.prototype._listenMouse = function(el) {
|
|||
var self = this;
|
||||
|
||||
if (el && !~this.clickable.indexOf(el)) {
|
||||
el.clickable = true;
|
||||
this.clickable.push(el);
|
||||
}
|
||||
|
||||
|
@ -471,7 +472,8 @@ Screen.prototype._listenMouse = function(el) {
|
|||
Screen.prototype._listenKeys = function(el) {
|
||||
var self = this;
|
||||
|
||||
if (el && !~this.input.indexOf(el)) {
|
||||
if (el && !~this.keyable.indexOf(el)) {
|
||||
el.keyable = true;
|
||||
// Listen for click, but do not enable
|
||||
// mouse if it's not enabled yet.
|
||||
if (el.options.autoFocus !== false) {
|
||||
|
@ -480,7 +482,7 @@ Screen.prototype._listenKeys = function(el) {
|
|||
el.on('click', el.focus.bind(el));
|
||||
this._listenedMouse = lm;
|
||||
}
|
||||
this.input.push(el);
|
||||
this.keyable.push(el);
|
||||
}
|
||||
|
||||
if (this._listenedKeys) return;
|
||||
|
@ -509,11 +511,11 @@ Screen.prototype._listenKeys = function(el) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (~self.input.indexOf(focused)) {
|
||||
if (~self.keyable.indexOf(focused)) {
|
||||
focused.emit('keypress', ch, key);
|
||||
focused.emit('key ' + key.full, ch, key);
|
||||
// self.emit('element keypress', focused, ch, key);
|
||||
// self.emit('element key ' + key.full, focused, ch, key);
|
||||
self.emit('element keypress', focused, ch, key);
|
||||
self.emit('element key ' + key.full, focused, ch, key);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -1075,25 +1077,25 @@ Screen.prototype.codeAttr = function(code) {
|
|||
};
|
||||
|
||||
Screen.prototype.focus = function(offset) {
|
||||
var shown = this.input.filter(function(el) {
|
||||
var shown = this.keyable.filter(function(el) {
|
||||
return el.visible;
|
||||
});
|
||||
if (!shown.length || !offset) return;
|
||||
var i = this.input.indexOf(this.focused);
|
||||
var i = this.keyable.indexOf(this.focused);
|
||||
if (!~i) return;
|
||||
if (offset > 0) {
|
||||
while (offset--) {
|
||||
if (++i > this.input.length - 1) i = 0;
|
||||
if (!this.input[i].visible) offset++;
|
||||
if (++i > this.keyable.length - 1) i = 0;
|
||||
if (!this.keyable[i].visible) offset++;
|
||||
}
|
||||
} else {
|
||||
offset = -offset;
|
||||
while (offset--) {
|
||||
if (--i < 0) i = this.input.length - 1;
|
||||
if (!this.input[i].visible) offset++;
|
||||
if (--i < 0) i = this.keyable.length - 1;
|
||||
if (!this.keyable[i].visible) offset++;
|
||||
}
|
||||
}
|
||||
return this.input[i].focus();
|
||||
return this.keyable[i].focus();
|
||||
};
|
||||
|
||||
Screen.prototype.focusPrev = function() {
|
||||
|
@ -1359,6 +1361,8 @@ function Element(options) {
|
|||
|
||||
Node.call(this, options);
|
||||
|
||||
this.name = options.name;
|
||||
|
||||
this.position = {
|
||||
left: options.left || 0,
|
||||
right: options.right || 0,
|
||||
|
@ -1408,7 +1412,7 @@ function Element(options) {
|
|||
this.screen._listenMouse(this);
|
||||
}
|
||||
|
||||
if (options.input) {
|
||||
if (options.input || options.keyable) {
|
||||
this.screen._listenKeys(this);
|
||||
}
|
||||
|
||||
|
@ -1968,7 +1972,9 @@ Box.prototype.render = function(stop) {
|
|||
, xll
|
||||
, yll
|
||||
, ret
|
||||
, cci;
|
||||
, cci
|
||||
, el
|
||||
, i;
|
||||
|
||||
if (this.position.width) {
|
||||
xl = xi_ + this.width;
|
||||
|
@ -1995,6 +2001,29 @@ Box.prototype.render = function(stop) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Possibly do both shrinkBox and shrink
|
||||
// and use whichever values are higher.
|
||||
// Slower than below, but more foolproof.
|
||||
if (this.options.shrinkBox && this.children.length) {
|
||||
xl = 0, yl = 0;
|
||||
for (i = 0; i < this.children.length; i++) {
|
||||
el = this.children[i];
|
||||
|
||||
// Recurse
|
||||
el.options._shrinkBox = !!el.options.shrinkBox;
|
||||
el.options.shrinkBox = true;
|
||||
|
||||
ret = el.render(true);
|
||||
|
||||
// Reset
|
||||
el.options.shrinkBox = el.options._shrinkBox;
|
||||
delete el.options._shrinkBox;
|
||||
|
||||
if (ret.xl > xl) xl = ret.xl;
|
||||
if (ret.yl > yl) yl = ret.yl;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Check for 'center', recalculate yi, and xi. Better
|
||||
// yet, simply move this check into this.left/width/etc.
|
||||
if (this.shrink) {
|
||||
|
@ -2039,6 +2068,11 @@ Box.prototype.render = function(stop) {
|
|||
}
|
||||
}
|
||||
|
||||
// NOTE: Won't work because parent is rendered first.
|
||||
//if (this.options.shrinkBox && this._lastPos) {
|
||||
// xl = this._lastPos.xl, yl = this._lastPos.yl;
|
||||
//}
|
||||
|
||||
// TODO:
|
||||
// Calculate whether we moved/resized by checking the previous _lastPos.
|
||||
// Maybe clear based on that. Possibly emit events here.
|
||||
|
@ -2046,9 +2080,14 @@ Box.prototype.render = function(stop) {
|
|||
xi: xi_,
|
||||
xl: xl,
|
||||
yi: yi_,
|
||||
yl: yl
|
||||
yl: yl,
|
||||
//mxl: xl,
|
||||
//myl: yl
|
||||
};
|
||||
|
||||
//this.parent._lastPos.mxl = Math.max(this.parent._lastPos.mxl, xl);
|
||||
//this.parent._lastPos.myl = Math.max(this.parent._lastPos.myl, yl);
|
||||
|
||||
if (stop) return ret;
|
||||
|
||||
battr = this.border
|
||||
|
@ -2574,6 +2613,7 @@ function List(options) {
|
|||
|
||||
ScrollableBox.call(this, options);
|
||||
|
||||
this.value = '';
|
||||
this.items = [];
|
||||
this.ritems = [];
|
||||
this.selected = 0;
|
||||
|
@ -2782,6 +2822,9 @@ List.prototype.remove = function(child) {
|
|||
this._remove(child);
|
||||
};
|
||||
|
||||
List.prototype.appendItem = List.prototype.add;
|
||||
List.prototype.removeItem = List.prototype.remove;
|
||||
|
||||
List.prototype.setItems = function(items) {
|
||||
var i = 0
|
||||
, original = this.items.slice()
|
||||
|
@ -2825,6 +2868,7 @@ List.prototype.select = function(index) {
|
|||
|
||||
var diff = index - this.selected;
|
||||
this.selected = index;
|
||||
this.value = this.ritems[this.selected];
|
||||
this.scroll(diff);
|
||||
};
|
||||
|
||||
|
@ -2977,6 +3021,203 @@ ScrollableText.prototype._recalculateIndex = function() {
|
|||
this.contentIndex = t;
|
||||
};
|
||||
|
||||
/**
|
||||
* Form
|
||||
*/
|
||||
|
||||
function Form(options) {
|
||||
var self = this;
|
||||
|
||||
if (!(this instanceof Form)) {
|
||||
return new Form(options);
|
||||
}
|
||||
|
||||
Box.call(this, options);
|
||||
|
||||
if (options.keys) {
|
||||
this.screen._listenKeys(this);
|
||||
this.screen.on('element keypress', function(el, ch, key) {
|
||||
// Make sure we're not entering input into a textbox.
|
||||
if (self.screen.grabKeys || self.screen.lockKeys) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we're a form or input element.
|
||||
if (el !== self && !el.hasAncestor(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((key.name === 'tab' && !key.shift) || key.name === 'down' || (options.vi && key.name === 'j')) {
|
||||
self.focusNext();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((key.name === 'tab' && key.shift) || key.name === 'up' || (options.vi && key.name === 'k')) {
|
||||
self.focusPrevious();
|
||||
return;
|
||||
}
|
||||
|
||||
if (key.name === 'escape') {
|
||||
self.focus();
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Form.prototype.__proto__ = Box.prototype;
|
||||
|
||||
Form.prototype.type = 'form';
|
||||
|
||||
Form.prototype._refresh = function() {
|
||||
if (!this._children) {
|
||||
var out = [];
|
||||
|
||||
this.children.forEach(function fn(el) {
|
||||
if (el.keyable) out.push(el);
|
||||
el.children.forEach(fn);
|
||||
});
|
||||
|
||||
this._children = out;
|
||||
}
|
||||
};
|
||||
|
||||
Form.prototype.next = function() {
|
||||
this._refresh();
|
||||
|
||||
if (!this._selected) {
|
||||
return this._selected = this._children[0];
|
||||
}
|
||||
|
||||
var i = this._children.indexOf(this._selected);
|
||||
if (!~i || !this._children[i + 1]) {
|
||||
return this._selected = this._children[0];
|
||||
}
|
||||
|
||||
return this._selected = this._children[i + 1];
|
||||
};
|
||||
|
||||
Form.prototype.previous = function() {
|
||||
this._refresh();
|
||||
|
||||
if (!this._selected) {
|
||||
return this._selected = this._children[this._children.length - 1];
|
||||
}
|
||||
|
||||
var i = this._children.indexOf(this._selected);
|
||||
if (!~i || !this._children[i - 1]) {
|
||||
return this._selected = this._children[this._children.length - 1];
|
||||
}
|
||||
|
||||
return this._selected = this._children[i - 1];
|
||||
};
|
||||
|
||||
Form.prototype.focusNext = function() {
|
||||
this.next().focus();
|
||||
};
|
||||
|
||||
Form.prototype.focusPrevious = function() {
|
||||
this.previous().focus();
|
||||
};
|
||||
|
||||
Form.prototype.submit = function() {
|
||||
var self = this
|
||||
, out = {};
|
||||
|
||||
(function get(el) {
|
||||
if (el.value != null) {
|
||||
var name = el.name || el.type;
|
||||
if (Array.isArray(out[name])) {
|
||||
out[name].push(el.value);
|
||||
} else if (out[name]) {
|
||||
out[name] = [out[name], el.value];
|
||||
} else {
|
||||
out[name] = el.value;
|
||||
}
|
||||
}
|
||||
el.children.forEach(get);
|
||||
})(this);
|
||||
|
||||
this.emit('submit', out);
|
||||
|
||||
return this.submission = out;
|
||||
};
|
||||
|
||||
Form.prototype.cancel = function() {
|
||||
this.emit('cancel');
|
||||
};
|
||||
|
||||
Form.prototype.reset = function() {
|
||||
(function clear(el) {
|
||||
switch (el.type) {
|
||||
case 'screen':
|
||||
break;
|
||||
case 'box':
|
||||
break;
|
||||
case 'text':
|
||||
break;
|
||||
case 'line':
|
||||
break;
|
||||
case 'scrollable-box':
|
||||
break;
|
||||
case 'list':
|
||||
el.select(0);
|
||||
return;
|
||||
case 'form':
|
||||
break;
|
||||
case 'input':
|
||||
break;
|
||||
case 'textbox':
|
||||
el.clearInput();
|
||||
return;
|
||||
case 'textarea':
|
||||
el.clearInput();
|
||||
return;
|
||||
case 'button':
|
||||
break;
|
||||
case 'progress-bar':
|
||||
el.setProgress(0);
|
||||
break;
|
||||
case 'file-manager':
|
||||
el.refresh(el.options.cwd);
|
||||
return;
|
||||
case 'checkbox':
|
||||
el.uncheck();
|
||||
return;
|
||||
case 'radio-set':
|
||||
break;
|
||||
case 'radio-button':
|
||||
el.uncheck();
|
||||
return;
|
||||
case 'prompt':
|
||||
break;
|
||||
case 'question':
|
||||
break;
|
||||
case 'message':
|
||||
break;
|
||||
case 'info':
|
||||
break;
|
||||
case 'loading':
|
||||
break;
|
||||
case 'pick-list':
|
||||
el.select(0);
|
||||
break;
|
||||
case 'list-bar':
|
||||
//el.select(0);
|
||||
break;
|
||||
case 'dir-manager':
|
||||
el.refresh(el.options.cwd);
|
||||
return;
|
||||
case 'passbox':
|
||||
el.clearInput();
|
||||
return;
|
||||
}
|
||||
el.children.forEach(clear);
|
||||
})(this);
|
||||
|
||||
this.emit('reset');
|
||||
};
|
||||
|
||||
/**
|
||||
* Input
|
||||
*/
|
||||
|
@ -2997,6 +3238,8 @@ Input.prototype.type = 'input';
|
|||
*/
|
||||
|
||||
function Textbox(options) {
|
||||
var self = this;
|
||||
|
||||
if (!(this instanceof Textbox)) {
|
||||
return new Textbox(options);
|
||||
}
|
||||
|
@ -3009,8 +3252,6 @@ function Textbox(options) {
|
|||
this.secret = options.secret;
|
||||
this.censor = options.censor;
|
||||
|
||||
var self = this;
|
||||
|
||||
this.on('resize', updateCursor);
|
||||
this.on('move', updateCursor);
|
||||
|
||||
|
@ -3057,6 +3298,17 @@ Textbox.prototype.setInput = function(callback) {
|
|||
self.screen.restoreFocus();
|
||||
}
|
||||
|
||||
if (err) {
|
||||
self.emit('error', err);
|
||||
} else if (value != null) {
|
||||
self.emit('submit', value);
|
||||
} else {
|
||||
self.emit('cancel', value);
|
||||
}
|
||||
self.emit('action', value);
|
||||
|
||||
if (!callback) return;
|
||||
|
||||
return err
|
||||
? callback(err)
|
||||
: callback(null, value);
|
||||
|
@ -3072,7 +3324,6 @@ Textbox.prototype._listener = function(ch, key) {
|
|||
|
||||
if (key.name === 'escape' || key.name === 'enter') {
|
||||
delete this._callback;
|
||||
this.value = '';
|
||||
this.removeListener('keypress', this.__listener);
|
||||
delete this.__listener;
|
||||
callback(null, key.name === 'enter' ? value : null);
|
||||
|
@ -3140,7 +3391,7 @@ Textbox.prototype.readEditor =
|
|||
Textbox.prototype.setEditor = function(callback) {
|
||||
var self = this;
|
||||
return this.screen.readEditor({ value: this.value }, function(err, value) {
|
||||
if (err) return callback(err);
|
||||
if (err) return callback && callback(err);
|
||||
value = value.replace(/[\r\n]/g, '');
|
||||
self.value = value;
|
||||
return self.readInput(callback);
|
||||
|
@ -3243,6 +3494,17 @@ Textarea.prototype.setInput = function(callback) {
|
|||
self.screen.restoreFocus();
|
||||
}
|
||||
|
||||
if (err) {
|
||||
self.emit('error', err);
|
||||
} else if (value != null) {
|
||||
self.emit('submit', value);
|
||||
} else {
|
||||
self.emit('cancel', value);
|
||||
}
|
||||
self.emit('action', value);
|
||||
|
||||
if (!callback) return;
|
||||
|
||||
return err
|
||||
? callback(err)
|
||||
: callback(null, value);
|
||||
|
@ -3315,7 +3577,7 @@ Textarea.prototype.readEditor =
|
|||
Textarea.prototype.setEditor = function(callback) {
|
||||
var self = this;
|
||||
return this.screen.readEditor({ value: this.value }, function(err, value) {
|
||||
if (err) return callback(err);
|
||||
if (err) return callback && callback(err);
|
||||
self.value = value;
|
||||
self.setContent(self.value);
|
||||
self._typeScroll();
|
||||
|
@ -3390,17 +3652,61 @@ function ProgressBar(options) {
|
|||
return new ProgressBar(options);
|
||||
}
|
||||
Input.call(this, options);
|
||||
|
||||
this.filled = options.filled || 0;
|
||||
if (typeof this.filled === 'string') {
|
||||
this.filled = +this.filled.slice(0, -1);
|
||||
}
|
||||
this.value = this.filled;
|
||||
|
||||
this.ch = options.ch || ' ';
|
||||
|
||||
if (!this.style.bar) {
|
||||
this.style.bar = {};
|
||||
this.style.bar.fg = options.barFg;
|
||||
this.style.bar.bg = options.barBg;
|
||||
}
|
||||
|
||||
this.orientation = options.orientation || 'horizontal';
|
||||
|
||||
if (options.keys) {
|
||||
this.on('keypress', function(ch, key) {
|
||||
var back, forward;
|
||||
if (self.orientation === 'horizontal') {
|
||||
back = ['left', 'h'];
|
||||
forward = ['right', 'l'];
|
||||
} else if (self.orientation === 'vertical') {
|
||||
back = ['down', 'j'];
|
||||
forward = ['up', 'k'];
|
||||
}
|
||||
if (key.name === back[0] || (options.vi && key.name === back[1])) {
|
||||
self.progress(-5);
|
||||
self.screen.render();
|
||||
return;
|
||||
}
|
||||
if (key.name === forward[0] || (options.vi && key.name === forward[1])) {
|
||||
self.progress(5);
|
||||
self.screen.render();
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (options.mouse) {
|
||||
this.on('click', function(data) {
|
||||
var x, y, m, p;
|
||||
if (self.orientation === 'horizontal') {
|
||||
x = data.x - self.left;
|
||||
m = self.width - (self.border ? 2 : 0) - self.padding * 2;
|
||||
p = x / m * 100 | 0;
|
||||
} else if (self.orientation === 'vertical') {
|
||||
y = data.y - self.top;
|
||||
m = self.height - (self.border ? 2 : 0) - self.padding * 2;
|
||||
p = y / m * 100 | 0;
|
||||
}
|
||||
self.setProgress(p);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ProgressBar.prototype.__proto__ = Input.prototype;
|
||||
|
@ -3444,11 +3750,18 @@ ProgressBar.prototype.progress = function(filled) {
|
|||
if (this.filled === 100) {
|
||||
this.emit('complete');
|
||||
}
|
||||
this.value = this.filled;
|
||||
};
|
||||
|
||||
ProgressBar.prototype.setProgress = function(filled) {
|
||||
this.filled = 0;
|
||||
this.progress(filled);
|
||||
};
|
||||
|
||||
ProgressBar.prototype.reset = function() {
|
||||
this.emit('reset');
|
||||
this.filled = 0;
|
||||
this.value = this.filled;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -3467,6 +3780,8 @@ function FileManager(options) {
|
|||
List.call(this, options);
|
||||
|
||||
this.cwd = options.cwd || process.cwd();
|
||||
this.file = this.cwd;
|
||||
this.value = this.cwd;
|
||||
|
||||
this.on('select', function(item) {
|
||||
var value = item.content.replace(/\{[^{}]+\}/g, '').replace(/@$/, '')
|
||||
|
@ -3476,6 +3791,8 @@ function FileManager(options) {
|
|||
if (err) {
|
||||
return self.emit('error', err, file);
|
||||
}
|
||||
self.file = file;
|
||||
self.value = file;
|
||||
if (stat.isDirectory()) {
|
||||
self.emit('cd', file, self.cwd);
|
||||
self.cwd = file;
|
||||
|
@ -3497,8 +3814,10 @@ FileManager.prototype.refresh = function(cwd, callback) {
|
|||
cwd = null;
|
||||
}
|
||||
|
||||
var self = this
|
||||
, cwd = cwd || this.cwd;
|
||||
var self = this;
|
||||
|
||||
if (cwd) this.cwd = cwd;
|
||||
else cwd = this.cwd;
|
||||
|
||||
return fs.readdir(cwd, function(err, list) {
|
||||
if (err && err.code === 'ENOENT') {
|
||||
|
@ -3636,30 +3955,32 @@ function Checkbox(options) {
|
|||
|
||||
Input.call(this, options);
|
||||
|
||||
this.value = options.value || '';
|
||||
this.checked = options.checked || false;
|
||||
this.text = options.content || options.text || '';
|
||||
this.checked = this.value = options.checked || false;
|
||||
|
||||
this.on('keypress', function(ch, key) {
|
||||
if (key.name === 'enter' || key.name === 'space') {
|
||||
self.check();
|
||||
self.toggle();
|
||||
self.screen.render();
|
||||
}
|
||||
});
|
||||
|
||||
if (this.options.mouse) {
|
||||
if (options.mouse) {
|
||||
this.on('click', function() {
|
||||
self.check();
|
||||
self.screen.render();
|
||||
});
|
||||
}
|
||||
|
||||
this.on('focus', function() {
|
||||
self.program.saveCursor();
|
||||
self.program.cup(this.top, this.left + 1);
|
||||
self.program.showCursor();
|
||||
this.on('focus', function(old) {
|
||||
self.screen.program.saveCursor();
|
||||
self.screen.program.cup(this.top, this.left + 1);
|
||||
self.screen.program.showCursor();
|
||||
});
|
||||
|
||||
this.on('blur', function() {
|
||||
self.program.hideCursor();
|
||||
self.program.restoreCursor();
|
||||
self.screen.program.hideCursor();
|
||||
self.screen.program.restoreCursor();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -3669,19 +3990,23 @@ Checkbox.prototype.type = 'checkbox';
|
|||
|
||||
Checkbox.prototype._render = Checkbox.prototype.render;
|
||||
Checkbox.prototype.render = function(stop) {
|
||||
this.setContent('[' + (this.checked ? 'x' : ' ') + '] ' + this.value);
|
||||
if (this.type === 'radio-button') {
|
||||
this.setContent('(' + (this.checked ? '*' : ' ') + ') ' + this.text);
|
||||
} else {
|
||||
this.setContent('[' + (this.checked ? 'x' : ' ') + '] ' + this.text);
|
||||
}
|
||||
return this._render(stop);
|
||||
};
|
||||
|
||||
Checkbox.prototype.check = function() {
|
||||
if (this.checked) return;
|
||||
this.checked = true;
|
||||
this.checked = this.value = true;
|
||||
this.emit('check');
|
||||
};
|
||||
|
||||
Checkbox.prototype.uncheck = function() {
|
||||
if (!this.checked) return;
|
||||
this.checked = false;
|
||||
this.checked = this.value = false;
|
||||
this.emit('uncheck');
|
||||
};
|
||||
|
||||
|
@ -3691,12 +4016,24 @@ Checkbox.prototype.toggle = function() {
|
|||
: this.check();
|
||||
};
|
||||
|
||||
Checkbox.prototype.setChecked = function(val) {
|
||||
val = !!val;
|
||||
if (this.checked === val) return;
|
||||
this.checked = val;
|
||||
this.emit('check', val);
|
||||
};
|
||||
/**
|
||||
* RadioSet
|
||||
*/
|
||||
|
||||
function RadioSet(options) {
|
||||
var self = this;
|
||||
|
||||
if (!(this instanceof RadioSet)) {
|
||||
return new RadioSet(options);
|
||||
}
|
||||
|
||||
Box.call(this, options);
|
||||
}
|
||||
|
||||
RadioSet.prototype.__proto__ = Box.prototype;
|
||||
|
||||
RadioSet.prototype.type = 'radio-set';
|
||||
|
||||
|
||||
/**
|
||||
* RadioButton
|
||||
|
@ -3711,10 +4048,9 @@ function RadioButton(options) {
|
|||
|
||||
Checkbox.call(this, options);
|
||||
|
||||
self.group = options.group || [];
|
||||
|
||||
this.on('check', function() {
|
||||
self.group.forEach(function(el) {
|
||||
if (self.parent.type !== 'radio-set') return;
|
||||
self.parent.children.forEach(function(el) {
|
||||
if (el === self) return;
|
||||
el.uncheck();
|
||||
});
|
||||
|
@ -3725,6 +4061,8 @@ RadioButton.prototype.__proto__ = Checkbox.prototype;
|
|||
|
||||
RadioButton.prototype.type = 'radio-button';
|
||||
|
||||
RadioButton.prototype.toggle = RadioButton.prototype.check;
|
||||
|
||||
/**
|
||||
* Prompt
|
||||
*/
|
||||
|
@ -4075,7 +4413,7 @@ function PickList(options) {
|
|||
|
||||
PickList.prototype.__proto__ = List.prototype;
|
||||
|
||||
PickList.prototype.type = 'popup-menu';
|
||||
PickList.prototype.type = 'pick-list';
|
||||
|
||||
PickList.prototype.pick = function(callback) {
|
||||
this.screen.saveFocus();
|
||||
|
@ -4109,7 +4447,7 @@ function Listbar(options) {
|
|||
|
||||
Listbar.prototype.__proto__ = Box.prototype;
|
||||
|
||||
Listbar.prototype.type = 'menubar';
|
||||
Listbar.prototype.type = 'listbar';
|
||||
|
||||
Listbar.prototype.setOptions =
|
||||
Listbar.prototype.setCommands =
|
||||
|
@ -4387,6 +4725,7 @@ exports.Line = exports.line = Line;
|
|||
exports.ScrollableBox = exports.scrollablebox = ScrollableBox;
|
||||
exports.List = exports.list = List;
|
||||
exports.ScrollableText = exports.scrollabletext = ScrollableText;
|
||||
exports.Form = exports.form = Form;
|
||||
exports.Input = exports.input = Input;
|
||||
exports.Textbox = exports.textbox = Textbox;
|
||||
exports.Textarea = exports.textarea = Textarea;
|
||||
|
@ -4395,7 +4734,9 @@ exports.ProgressBar = exports.progressbar = ProgressBar;
|
|||
exports.FileManager = exports.filemanager = FileManager;
|
||||
|
||||
exports.Checkbox = exports.checkbox = Checkbox;
|
||||
exports.RadioSet = exports.radioset = RadioSet;
|
||||
exports.RadioButton = exports.radiobutton = RadioButton;
|
||||
|
||||
exports.Prompt = exports.prompt = Prompt;
|
||||
exports.Question = exports.question = Question;
|
||||
exports.Message = exports.message = Message;
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
var blessed = require('blessed')
|
||||
, screen = blessed.screen();
|
||||
|
||||
var form = blessed.form({
|
||||
parent: screen,
|
||||
mouse: true,
|
||||
keys: true,
|
||||
vi: true,
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: '100%',
|
||||
height: 5,
|
||||
bg: 'green',
|
||||
content: 'foobar'
|
||||
});
|
||||
|
||||
form.on('submit', function(data) {
|
||||
output.setContent(JSON.stringify(data, null, 2));
|
||||
screen.render();
|
||||
});
|
||||
|
||||
var set = blessed.radioset({
|
||||
parent: form,
|
||||
left: 0,
|
||||
top: 0,
|
||||
shrinkBox: true,
|
||||
height: 1,
|
||||
bg: 'magenta'
|
||||
});
|
||||
|
||||
var radio1 = blessed.radiobutton({
|
||||
parent: set,
|
||||
mouse: true,
|
||||
keys: true,
|
||||
shrink: true,
|
||||
bg: 'magenta',
|
||||
height: 1,
|
||||
left: 0,
|
||||
top: 0,
|
||||
name: 'radio1',
|
||||
content: 'radio1'
|
||||
});
|
||||
|
||||
var radio2 = blessed.radiobutton({
|
||||
parent: set,
|
||||
mouse: true,
|
||||
keys: true,
|
||||
shrink: true,
|
||||
bg: 'magenta',
|
||||
height: 1,
|
||||
left: 15,
|
||||
top: 0,
|
||||
name: 'radio2',
|
||||
content: 'radio2'
|
||||
});
|
||||
|
||||
var text = blessed.textbox({
|
||||
parent: form,
|
||||
mouse: true,
|
||||
keys: true,
|
||||
bg: 'blue',
|
||||
height: 1,
|
||||
width: 20,
|
||||
left: 1,
|
||||
top: 1,
|
||||
name: 'text'
|
||||
});
|
||||
|
||||
text.on('focus', function() {
|
||||
text.readInput();
|
||||
});
|
||||
|
||||
var check = blessed.checkbox({
|
||||
parent: form,
|
||||
mouse: true,
|
||||
keys: true,
|
||||
shrink: true,
|
||||
bg: 'magenta',
|
||||
height: 1,
|
||||
left: 24,
|
||||
top: 1,
|
||||
name: 'check',
|
||||
content: 'check'
|
||||
});
|
||||
|
||||
var submit = blessed.button({
|
||||
parent: form,
|
||||
mouse: true,
|
||||
keys: true,
|
||||
height: 1,
|
||||
left: 30,
|
||||
top: 0,
|
||||
shrink: true,
|
||||
bg: 'blue',
|
||||
name: 'submit',
|
||||
content: 'submit',
|
||||
focusEffects: {
|
||||
bg: 'red'
|
||||
}
|
||||
});
|
||||
|
||||
submit.on('press', function() {
|
||||
form.submit();
|
||||
});
|
||||
|
||||
var output = blessed.scrollabletext({
|
||||
parent: screen,
|
||||
mouse: true,
|
||||
keys: true,
|
||||
left: 0,
|
||||
top: 5,
|
||||
width: '100%',
|
||||
bg: 'red',
|
||||
content: 'foobar'
|
||||
});
|
||||
|
||||
screen.key('q', function() {
|
||||
return process.exit(0);
|
||||
});
|
||||
|
||||
form.focus();
|
||||
|
||||
form.submit();
|
||||
|
||||
screen.render();
|
Loading…
Reference in New Issue