1 | /* |
---|
2 | * Ext JS Library 2.2.1 |
---|
3 | * Copyright(c) 2006-2009, Ext JS, LLC. |
---|
4 | * licensing@extjs.com |
---|
5 | * |
---|
6 | * http://extjs.com/license |
---|
7 | */ |
---|
8 | |
---|
9 | /* |
---|
10 | * Note that this control should still be treated as an example and that the API will most likely |
---|
11 | * change once it is ported into the Ext core as a standard form control. This is still planned |
---|
12 | * for a future release, so this should not yet be treated as a final, stable API at this time. |
---|
13 | */ |
---|
14 | |
---|
15 | /** |
---|
16 | * @class Ext.ux.MultiSelect |
---|
17 | * @extends Ext.form.Field |
---|
18 | * A control that allows selection and form submission of multiple list items. The MultiSelect control |
---|
19 | * depends on the Ext.ux.DDView class to provide drag/drop capability both within the list and also |
---|
20 | * between multiple MultiSelect controls (see the Ext.ux.ItemSelector). |
---|
21 | * |
---|
22 | * @history |
---|
23 | * 2008-06-19 bpm Original code contributed by Toby Stuart (with contributions from Robert Williams) |
---|
24 | * 2008-06-19 bpm Docs and demo code clean up |
---|
25 | * |
---|
26 | * @constructor |
---|
27 | * Create a new MultiSelect |
---|
28 | * @param {Object} config Configuration options |
---|
29 | */ |
---|
30 | Ext.ux.Multiselect = Ext.extend(Ext.form.Field, { |
---|
31 | /** |
---|
32 | * @cfg {String} legend Wraps the object with a fieldset and specified legend. |
---|
33 | */ |
---|
34 | /** |
---|
35 | * @cfg {Store} store The {@link Ext.data.Store} used by the underlying Ext.ux.DDView. |
---|
36 | */ |
---|
37 | /** |
---|
38 | * @cfg {Ext.ux.DDView} view The Ext.ux.DDView used to render the multiselect list. |
---|
39 | */ |
---|
40 | /** |
---|
41 | * @cfg {String/Array} dragGroup The ddgroup name(s) for the DDView's DragZone (defaults to undefined). |
---|
42 | */ |
---|
43 | /** |
---|
44 | * @cfg {String/Array} dropGroup The ddgroup name(s) for the DDView's DropZone (defaults to undefined). |
---|
45 | */ |
---|
46 | /** |
---|
47 | * @cfg {Object/Array} tbar The top toolbar of the control. This can be a {@link Ext.Toolbar} object, a |
---|
48 | * toolbar config, or an array of buttons/button configs to be added to the toolbar. |
---|
49 | */ |
---|
50 | /** |
---|
51 | * @cfg {String} fieldName The name of the field to sort by when sorting is enabled. |
---|
52 | */ |
---|
53 | /** |
---|
54 | * @cfg {String} appendOnly True if the list should only allow append drops when drag/drop is enabled |
---|
55 | * (use for lists which are sorted, defaults to false). |
---|
56 | */ |
---|
57 | appendOnly:false, |
---|
58 | /** |
---|
59 | * @cfg {Array} dataFields Inline data definition when not using a pre-initialised store. Known to cause problems |
---|
60 | * in some browswers for very long lists. Use store for large datasets. |
---|
61 | */ |
---|
62 | dataFields:[], |
---|
63 | /** |
---|
64 | * @cfg {Array} data Inline data when not using a pre-initialised store. Known to cause problems in some |
---|
65 | * browswers for very long lists. Use store for large datasets. |
---|
66 | */ |
---|
67 | data:[], |
---|
68 | /** |
---|
69 | * @cfg {Number} width Width in pixels of the control (defaults to 100). |
---|
70 | */ |
---|
71 | width:100, |
---|
72 | /** |
---|
73 | * @cfg {Number} height Height in pixels of the control (defaults to 100). |
---|
74 | */ |
---|
75 | height:100, |
---|
76 | /** |
---|
77 | * @cfg {String/Number} displayField Name/Index of the desired display field in the dataset (defaults to 0). |
---|
78 | */ |
---|
79 | displayField:0, |
---|
80 | /** |
---|
81 | * @cfg {String/Number} valueField Name/Index of the desired value field in the dataset (defaults to 1). |
---|
82 | */ |
---|
83 | valueField:1, |
---|
84 | /** |
---|
85 | * @cfg {Boolean} allowBlank True to require at least one item in the list to be selected, false to allow no |
---|
86 | * selection (defaults to true). |
---|
87 | */ |
---|
88 | allowBlank:true, |
---|
89 | /** |
---|
90 | * @cfg {Number} minLength Minimum number of selections allowed (defaults to 0). |
---|
91 | */ |
---|
92 | minLength:0, |
---|
93 | /** |
---|
94 | * @cfg {Number} maxLength Maximum number of selections allowed (defaults to Number.MAX_VALUE). |
---|
95 | */ |
---|
96 | maxLength:Number.MAX_VALUE, |
---|
97 | /** |
---|
98 | * @cfg {String} blankText Default text displayed when the control contains no items (defaults to the same value as |
---|
99 | * {@link Ext.form.TextField#blankText}. |
---|
100 | */ |
---|
101 | blankText:Ext.form.TextField.prototype.blankText, |
---|
102 | /** |
---|
103 | * @cfg {String} minLengthText Validation message displayed when {@link #minLength} is not met (defaults to 'Minimum {0} |
---|
104 | * item(s) required'). The {0} token will be replaced by the value of {@link #minLength}. |
---|
105 | */ |
---|
106 | minLengthText:'Minimum {0} item(s) required', |
---|
107 | /** |
---|
108 | * @cfg {String} maxLengthText Validation message displayed when {@link #maxLength} is not met (defaults to 'Maximum {0} |
---|
109 | * item(s) allowed'). The {0} token will be replaced by the value of {@link #maxLength}. |
---|
110 | */ |
---|
111 | maxLengthText:'Maximum {0} item(s) allowed', |
---|
112 | /** |
---|
113 | * @cfg {String} delimiter The string used to delimit between items when set or returned as a string of values |
---|
114 | * (defaults to ','). |
---|
115 | */ |
---|
116 | delimiter:',', |
---|
117 | |
---|
118 | // DDView settings |
---|
119 | copy:false, |
---|
120 | allowDup:false, |
---|
121 | allowTrash:false, |
---|
122 | focusClass:undefined, |
---|
123 | sortDir:'ASC', |
---|
124 | |
---|
125 | // private |
---|
126 | defaultAutoCreate : {tag: "div"}, |
---|
127 | |
---|
128 | // private |
---|
129 | initComponent: function(){ |
---|
130 | Ext.ux.Multiselect.superclass.initComponent.call(this); |
---|
131 | this.addEvents({ |
---|
132 | 'dblclick' : true, |
---|
133 | 'click' : true, |
---|
134 | 'change' : true, |
---|
135 | 'drop' : true |
---|
136 | }); |
---|
137 | }, |
---|
138 | |
---|
139 | // private |
---|
140 | onRender: function(ct, position){ |
---|
141 | Ext.ux.Multiselect.superclass.onRender.call(this, ct, position); |
---|
142 | |
---|
143 | var cls = 'ux-mselect'; |
---|
144 | var fs = new Ext.form.FieldSet({ |
---|
145 | renderTo:this.el, |
---|
146 | title:this.legend, |
---|
147 | height:this.height, |
---|
148 | width:this.width, |
---|
149 | style:"padding:0;", |
---|
150 | tbar:this.tbar |
---|
151 | }); |
---|
152 | //if(!this.legend)fs.el.down('.'+fs.headerCls).remove(); |
---|
153 | fs.body.addClass(cls); |
---|
154 | |
---|
155 | var tpl = '<tpl for="."><div class="' + cls + '-item'; |
---|
156 | if(Ext.isIE || Ext.isIE7){ |
---|
157 | tpl+='" unselectable=on'; |
---|
158 | }else{ |
---|
159 | tpl+=' x-unselectable"'; |
---|
160 | } |
---|
161 | tpl+='>{' + this.displayField + '}</div></tpl>'; |
---|
162 | |
---|
163 | if(!this.store){ |
---|
164 | this.store = new Ext.data.SimpleStore({ |
---|
165 | fields: this.dataFields, |
---|
166 | data : this.data |
---|
167 | }); |
---|
168 | } |
---|
169 | |
---|
170 | this.view = new Ext.ux.DDView({ |
---|
171 | multiSelect: true, |
---|
172 | store: this.store, |
---|
173 | selectedClass: cls+"-selected", |
---|
174 | tpl:tpl, |
---|
175 | allowDup:this.allowDup, |
---|
176 | copy: this.copy, |
---|
177 | allowTrash: this.allowTrash, |
---|
178 | dragGroup: this.dragGroup, |
---|
179 | dropGroup: this.dropGroup, |
---|
180 | itemSelector:"."+cls+"-item", |
---|
181 | isFormField:false, |
---|
182 | applyTo:fs.body, |
---|
183 | appendOnly:this.appendOnly, |
---|
184 | sortField:this.sortField, |
---|
185 | sortDir:this.sortDir |
---|
186 | }); |
---|
187 | |
---|
188 | fs.add(this.view); |
---|
189 | |
---|
190 | this.view.on('click', this.onViewClick, this); |
---|
191 | this.view.on('beforeClick', this.onViewBeforeClick, this); |
---|
192 | this.view.on('dblclick', this.onViewDblClick, this); |
---|
193 | this.view.on('drop', function(ddView, n, dd, e, data){ |
---|
194 | return this.fireEvent("drop", ddView, n, dd, e, data); |
---|
195 | }, this); |
---|
196 | |
---|
197 | this.hiddenName = this.name; |
---|
198 | var hiddenTag={tag: "input", type: "hidden", value: "", name:this.name}; |
---|
199 | if (this.isFormField) { |
---|
200 | this.hiddenField = this.el.createChild(hiddenTag); |
---|
201 | } else { |
---|
202 | this.hiddenField = Ext.get(document.body).createChild(hiddenTag); |
---|
203 | } |
---|
204 | fs.doLayout(); |
---|
205 | }, |
---|
206 | |
---|
207 | // private |
---|
208 | initValue:Ext.emptyFn, |
---|
209 | |
---|
210 | // private |
---|
211 | onViewClick: function(vw, index, node, e) { |
---|
212 | var arrayIndex = this.preClickSelections.indexOf(index); |
---|
213 | if (arrayIndex != -1) |
---|
214 | { |
---|
215 | this.preClickSelections.splice(arrayIndex, 1); |
---|
216 | this.view.clearSelections(true); |
---|
217 | this.view.select(this.preClickSelections); |
---|
218 | } |
---|
219 | this.fireEvent('change', this, this.getValue(), this.hiddenField.dom.value); |
---|
220 | this.hiddenField.dom.value = this.getValue(); |
---|
221 | this.fireEvent('click', this, e); |
---|
222 | this.validate(); |
---|
223 | }, |
---|
224 | |
---|
225 | // private |
---|
226 | onViewBeforeClick: function(vw, index, node, e) { |
---|
227 | this.preClickSelections = this.view.getSelectedIndexes(); |
---|
228 | if (this.disabled) {return false;} |
---|
229 | }, |
---|
230 | |
---|
231 | // private |
---|
232 | onViewDblClick : function(vw, index, node, e) { |
---|
233 | return this.fireEvent('dblclick', vw, index, node, e); |
---|
234 | }, |
---|
235 | |
---|
236 | /** |
---|
237 | * Returns an array of data values for the selected items in the list. The values will be separated |
---|
238 | * by {@link #delimiter}. |
---|
239 | * @return {Array} value An array of string data values |
---|
240 | */ |
---|
241 | getValue: function(valueField){ |
---|
242 | var returnArray = []; |
---|
243 | var selectionsArray = this.view.getSelectedIndexes(); |
---|
244 | if (selectionsArray.length == 0) {return '';} |
---|
245 | for (var i=0; i<selectionsArray.length; i++) { |
---|
246 | returnArray.push(this.store.getAt(selectionsArray[i]).get(((valueField != null)? valueField : this.valueField))); |
---|
247 | } |
---|
248 | return returnArray.join(this.delimiter); |
---|
249 | }, |
---|
250 | |
---|
251 | /** |
---|
252 | * Sets a delimited string (using {@link #delimiter}) or array of data values into the list. |
---|
253 | * @param {String/Array} values The values to set |
---|
254 | */ |
---|
255 | setValue: function(values) { |
---|
256 | var index; |
---|
257 | var selections = []; |
---|
258 | this.view.clearSelections(); |
---|
259 | this.hiddenField.dom.value = ''; |
---|
260 | |
---|
261 | if (!values || (values == '')) { return; } |
---|
262 | |
---|
263 | if (!(values instanceof Array)) { values = values.split(this.delimiter); } |
---|
264 | for (var i=0; i<values.length; i++) { |
---|
265 | index = this.view.store.indexOf(this.view.store.query(this.valueField, |
---|
266 | new RegExp('^' + values[i] + '$', "i")).itemAt(0)); |
---|
267 | selections.push(index); |
---|
268 | } |
---|
269 | this.view.select(selections); |
---|
270 | this.hiddenField.dom.value = this.getValue(); |
---|
271 | this.validate(); |
---|
272 | }, |
---|
273 | |
---|
274 | // inherit docs |
---|
275 | reset : function() { |
---|
276 | this.setValue(''); |
---|
277 | }, |
---|
278 | |
---|
279 | // inherit docs |
---|
280 | getRawValue: function(valueField) { |
---|
281 | var tmp = this.getValue(valueField); |
---|
282 | if (tmp.length) { |
---|
283 | tmp = tmp.split(this.delimiter); |
---|
284 | } |
---|
285 | else{ |
---|
286 | tmp = []; |
---|
287 | } |
---|
288 | return tmp; |
---|
289 | }, |
---|
290 | |
---|
291 | // inherit docs |
---|
292 | setRawValue: function(values){ |
---|
293 | setValue(values); |
---|
294 | }, |
---|
295 | |
---|
296 | // inherit docs |
---|
297 | validateValue : function(value){ |
---|
298 | if (value.length < 1) { // if it has no value |
---|
299 | if (this.allowBlank) { |
---|
300 | this.clearInvalid(); |
---|
301 | return true; |
---|
302 | } else { |
---|
303 | this.markInvalid(this.blankText); |
---|
304 | return false; |
---|
305 | } |
---|
306 | } |
---|
307 | if (value.length < this.minLength) { |
---|
308 | this.markInvalid(String.format(this.minLengthText, this.minLength)); |
---|
309 | return false; |
---|
310 | } |
---|
311 | if (value.length > this.maxLength) { |
---|
312 | this.markInvalid(String.format(this.maxLengthText, this.maxLength)); |
---|
313 | return false; |
---|
314 | } |
---|
315 | return true; |
---|
316 | } |
---|
317 | }); |
---|
318 | |
---|
319 | Ext.reg("multiselect", Ext.ux.Multiselect); |
---|