Issue: #23853: Fixed order relation widget
[xtuple] / lib / enyo-x / source / widgets / combobox.js
1 /*jshint indent:2, curly:true, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
2 regexp:true, undef:true, trailing:true, white:true, browser:true */
3 /*global XT:true, XV:true, XM:true, Backbone:true, enyo:true, _:true */
4
5 (function () {
6
7   /**
8     @name XV.ComboboxWidget
9     @class An input control consisting of fittable columns:
10       label, decorator, and combobox.<br />
11     Used to implement a styled combobox
12     @extends XV.Input
13    */
14   enyo.kind(
15     /** @lends XV.CheckboxWidget# */{
16     name: "XV.ComboboxWidget",
17     kind: "XV.InputWidget",
18     published: {
19       attr: null,
20       collection: "",
21       filter: null,
22       disabled: false,
23       keyAttribute: "name",
24       tabStop: true
25     },
26     classes: "xv-combobox xv-input",
27     handlers: {
28       onValueChange: "controlValueChanged"
29     },
30     components: [
31       {controlClasses: 'enyo-inline', name: "container", components: [
32         {name: "label", classes: "xv-label"},
33         {kind: "onyx.InputDecorator", tag: "div", classes: "xv-icon-decorator", components: [
34           {name: "input", kind: "onyx.Input", onkeyup: "keyUp", onkeydown: "keyDown",
35             onblur: "receiveBlur", onchange: "inputChanged"},
36           {kind: "onyx.IconButton", name: "iconButton", ontap: "toggleCompleter",
37             classes: "icon-sort"}
38         ]},
39         {name: "completer", kind: "XV.Completer", onSelect: "itemSelected"}
40       ]}
41     ],
42     /**
43     @todo Document the create method.
44     */
45     create: function () {
46       this.inherited(arguments);
47       this.collectionChanged();
48       this.filterChanged();
49       this.tabStopChanged();
50     },
51     /**
52     @todo Document the autocomplete method.
53     */
54     autocomplete: function () {
55       var key = this.getKeyAttribute(),
56         value = this.$.input.getValue(),
57         models = this._collection ? this._collection.models : null,
58         regexp = value ? new RegExp("^" + value, "i") : null,
59         res;
60
61       if (models && models.length && regexp) {
62         res = _.find(models, function (model) {
63           var value = model.get(key) || "";
64           return value.match(regexp);
65         });
66       }
67       if (res) { this.setValue(res.get(key)); }
68     },
69     /**
70     @todo Document the buildList method.
71     */
72     buildList: function () {
73       var key = this.getKeyAttribute(),
74         value = this.$.input.getValue(),
75         models = this.filteredList();
76       this.$.completer.buildList(key, value, models);
77     },
78     /**
79     @todo Document the clear method.
80     */
81     clear: function (options) {
82       this.setValue(false, options);
83     },
84     /**
85       The value sent to setCollection() can be an object or a string.
86       If it's a string, resolve it via the cache.
87       The actual collection will be this._collection
88     */
89     collectionChanged: function () {
90       var that = this,
91         callback;
92
93       if (_.isObject(this.collection)) {
94         this._collection = this.collection;
95       } else {
96         this._collection = XT.getObjectByName(this.collection);
97         // If we don't have data yet, try again after start up tasks complete
98         if (!this._collection) {
99           callback = function () {
100             that.collectionChanged();
101           };
102           XT.getStartupManager().registerCallback(callback);
103           return;
104         }
105       }
106       this.buildList();
107     },
108     controlValueChanged: function (inSender, inEvent) {
109       if (inEvent.originator !== this) {
110         this.setValue(inEvent.value);
111         return true;
112       }
113     },
114     setDisabled: function (isDisabled) {
115       this.inherited(arguments);
116       this.$.iconButton.setDisabled(isDisabled);
117     },
118     /**
119     @todo Document the keyDown method.
120     */
121     keyDown: function (inSender, inEvent) {
122       this.inherited(arguments);
123
124       // If tabbed out...
125       if (inEvent.keyCode === XV.KEY_TAB) {
126         this.$.completer.hide();
127         this.autocomplete();
128       }
129     },
130     /**
131     @todo Document the keyUp method.
132     */
133     keyUp: function (inSender, inEvent) {
134       if (inEvent.keyCode === XV.KEY_TAB) { return; }
135       inEvent.activator = this;
136       if (this.$.completer.controls.length) {
137         this.$.completer.waterfall("onRequestHideMenu", inEvent);
138       }
139       this.buildList();
140       if (this.$.completer.controls.length) {
141         this.$.completer.waterfall("onRequestShowMenu", inEvent);
142       }
143     },
144     /**
145     @todo Document the itemSelected method.
146     */
147     itemSelected: function (inSender, inEvent) {
148       this.setValue(inEvent.originator.content);
149       this.$.completer.hide();
150       return true;
151     },
152     /**
153     @todo Document the filterChanged method.
154     */
155     filterChanged: function () {
156       this.buildList();
157     },
158     /**
159     @todo Document the filteredLists method.
160     */
161     filteredList: function (inSender, inEvent) {
162       var models = this._collection ? this._collection.models : null,
163         filter = this.getFilter();
164       if (filter) {
165         models = filter(models);
166       }
167       return models;
168     },
169     /**
170     @todo Document the receiveBlur method.
171     */
172     receiveBlur: function (inSender, inEvent) {
173       this.autocomplete();
174     },
175     tabStopChanged: function () {
176       this.$.input.setAttribute("tabIndex", this.getTabStop() ? null: -1);
177     },
178     /**
179     @todo Document the toggleCompleter method.
180     */
181     toggleCompleter: function (inSender, inEvent) {
182       inEvent.activator = this;
183       var completer = this.$.completer;
184       if (completer.showing) {
185         completer.waterfall("onRequestHideMenu", inEvent);
186       } else {
187         completer.waterfall("onRequestShowMenu", inEvent);
188       }
189     }
190   });
191
192 }());