[619] | 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); |
---|