]> git.proxmox.com Git - extjs.git/blame - extjs/packages/ux/classic/src/form/ItemSelector.js
add extjs 6.0.1 sources
[extjs.git] / extjs / packages / ux / classic / src / form / ItemSelector.js
CommitLineData
6527f429
DM
1/*\r
2 * Note that this control will most likely remain as an example, and not as a core Ext form\r
3 * control. However, the API will be changing in a future release and so should not yet be\r
4 * treated as a final, stable API at this time.\r
5 */\r
6\r
7/**\r
8 * A control that allows selection of between two Ext.ux.form.MultiSelect controls.\r
9 */\r
10Ext.define('Ext.ux.form.ItemSelector', {\r
11 extend: 'Ext.ux.form.MultiSelect',\r
12 alias: ['widget.itemselectorfield', 'widget.itemselector'],\r
13 alternateClassName: ['Ext.ux.ItemSelector'],\r
14 requires: [\r
15 'Ext.button.Button',\r
16 'Ext.ux.form.MultiSelect'\r
17 ],\r
18\r
19 /**\r
20 * @cfg {Boolean} [hideNavIcons=false] True to hide the navigation icons\r
21 */\r
22 hideNavIcons:false,\r
23\r
24 /**\r
25 * @cfg {Array} buttons Defines the set of buttons that should be displayed in between the ItemSelector\r
26 * fields. Defaults to <tt>['top', 'up', 'add', 'remove', 'down', 'bottom']</tt>. These names are used\r
27 * to build the button CSS class names, and to look up the button text labels in {@link #buttonsText}.\r
28 * This can be overridden with a custom Array to change which buttons are displayed or their order.\r
29 */\r
30 buttons: ['top', 'up', 'add', 'remove', 'down', 'bottom'],\r
31\r
32 /**\r
33 * @cfg {Object} buttonsText The tooltips for the {@link #buttons}.\r
34 * Labels for buttons.\r
35 */\r
36 buttonsText: {\r
37 top: "Move to Top",\r
38 up: "Move Up",\r
39 add: "Add to Selected",\r
40 remove: "Remove from Selected",\r
41 down: "Move Down",\r
42 bottom: "Move to Bottom"\r
43 },\r
44\r
45 layout: {\r
46 type: 'hbox',\r
47 align: 'stretch'\r
48 },\r
49 \r
50 ariaRole: 'group',\r
51\r
52 initComponent: function() {\r
53 var me = this;\r
54\r
55 me.ddGroup = me.id + '-dd';\r
56 me.ariaRenderAttributes = me.ariaRenderAttributes || {};\r
57 me.ariaRenderAttributes['aria-labelledby'] = me.id + '-labelEl';\r
58 \r
59 me.callParent();\r
60\r
61 // bindStore must be called after the fromField has been created because\r
62 // it copies records from our configured Store into the fromField's Store\r
63 me.bindStore(me.store);\r
64 },\r
65\r
66 createList: function(title){\r
67 var me = this;\r
68\r
69 return Ext.create('Ext.ux.form.MultiSelect', {\r
70 // We don't want the multiselects themselves to act like fields,\r
71 // so override these methods to prevent them from including\r
72 // any of their values\r
73 submitValue: false,\r
74 getSubmitData: function(){\r
75 return null;\r
76 },\r
77 getModelData: function(){\r
78 return null; \r
79 },\r
80 flex: 1,\r
81 dragGroup: me.ddGroup,\r
82 dropGroup: me.ddGroup,\r
83 title: title,\r
84 store: {\r
85 model: me.store.model,\r
86 data: []\r
87 },\r
88 displayField: me.displayField,\r
89 valueField: me.valueField,\r
90 disabled: me.disabled,\r
91 listeners: {\r
92 boundList: {\r
93 scope: me,\r
94 itemdblclick: me.onItemDblClick,\r
95 drop: me.syncValue\r
96 }\r
97 }\r
98 });\r
99 },\r
100\r
101 setupItems: function() {\r
102 var me = this;\r
103\r
104 me.fromField = me.createList(me.fromTitle);\r
105 me.toField = me.createList(me.toTitle);\r
106\r
107 return [\r
108 me.fromField,\r
109 {\r
110 xtype: 'toolbar',\r
111 margin: '0 4',\r
112 padding: 0,\r
113 layout: {\r
114 type: 'vbox',\r
115 pack: 'center'\r
116 },\r
117 items: me.createButtons()\r
118 },\r
119 me.toField\r
120 ];\r
121 },\r
122\r
123 createButtons: function() {\r
124 var me = this,\r
125 buttons = [];\r
126\r
127 if (!me.hideNavIcons) {\r
128 Ext.Array.forEach(me.buttons, function(name) {\r
129 buttons.push({\r
130 xtype: 'button',\r
131 ui: 'default',\r
132 tooltip: me.buttonsText[name],\r
133 ariaLabel: me.buttonsText[name],\r
134 handler: me['on' + Ext.String.capitalize(name) + 'BtnClick'],\r
135 cls: Ext.baseCSSPrefix + 'form-itemselector-btn',\r
136 iconCls: Ext.baseCSSPrefix + 'form-itemselector-' + name,\r
137 navBtn: true,\r
138 scope: me,\r
139 margin: '4 0 0 0'\r
140 });\r
141 });\r
142 }\r
143 return buttons;\r
144 },\r
145\r
146 /**\r
147 * Get the selected records from the specified list.\r
148 * \r
149 * Records will be returned *in store order*, not in order of selection.\r
150 * @param {Ext.view.BoundList} list The list to read selections from.\r
151 * @return {Ext.data.Model[]} The selected records in store order.\r
152 * \r
153 */\r
154 getSelections: function(list) {\r
155 var store = list.getStore();\r
156\r
157 return Ext.Array.sort(list.getSelectionModel().getSelection(), function(a, b) {\r
158 a = store.indexOf(a);\r
159 b = store.indexOf(b);\r
160\r
161 if (a < b) {\r
162 return -1;\r
163 } else if (a > b) {\r
164 return 1;\r
165 }\r
166 return 0;\r
167 });\r
168 },\r
169\r
170 onTopBtnClick : function() {\r
171 var list = this.toField.boundList,\r
172 store = list.getStore(),\r
173 selected = this.getSelections(list);\r
174\r
175 store.suspendEvents();\r
176 store.remove(selected, true);\r
177 store.insert(0, selected);\r
178 store.resumeEvents();\r
179 list.refresh();\r
180 this.syncValue(); \r
181 list.getSelectionModel().select(selected);\r
182 },\r
183\r
184 onBottomBtnClick : function() {\r
185 var list = this.toField.boundList,\r
186 store = list.getStore(),\r
187 selected = this.getSelections(list);\r
188\r
189 store.suspendEvents();\r
190 store.remove(selected, true);\r
191 store.add(selected);\r
192 store.resumeEvents();\r
193 list.refresh();\r
194 this.syncValue();\r
195 list.getSelectionModel().select(selected);\r
196 },\r
197\r
198 onUpBtnClick : function() {\r
199 var list = this.toField.boundList,\r
200 store = list.getStore(),\r
201 selected = this.getSelections(list),\r
202 rec,\r
203 i = 0,\r
204 len = selected.length,\r
205 index = 0;\r
206\r
207 // Move each selection up by one place if possible\r
208 store.suspendEvents();\r
209 for (; i < len; ++i, index++) {\r
210 rec = selected[i];\r
211 index = Math.max(index, store.indexOf(rec) - 1);\r
212 store.remove(rec, true);\r
213 store.insert(index, rec);\r
214 }\r
215 store.resumeEvents();\r
216 list.refresh();\r
217 this.syncValue();\r
218 list.getSelectionModel().select(selected);\r
219 },\r
220\r
221 onDownBtnClick : function() {\r
222 var list = this.toField.boundList,\r
223 store = list.getStore(),\r
224 selected = this.getSelections(list),\r
225 rec,\r
226 i = selected.length - 1,\r
227 index = store.getCount() - 1;\r
228\r
229 // Move each selection down by one place if possible\r
230 store.suspendEvents();\r
231 for (; i > -1; --i, index--) {\r
232 rec = selected[i];\r
233 index = Math.min(index, store.indexOf(rec) + 1);\r
234 store.remove(rec, true);\r
235 store.insert(index, rec);\r
236 }\r
237 store.resumeEvents();\r
238 list.refresh();\r
239 this.syncValue();\r
240 list.getSelectionModel().select(selected);\r
241 },\r
242\r
243 onAddBtnClick : function() {\r
244 var me = this,\r
245 selected = me.getSelections(me.fromField.boundList);\r
246\r
247 me.moveRec(true, selected);\r
248 me.toField.boundList.getSelectionModel().select(selected);\r
249 },\r
250\r
251 onRemoveBtnClick : function() {\r
252 var me = this,\r
253 selected = me.getSelections(me.toField.boundList);\r
254\r
255 me.moveRec(false, selected);\r
256 me.fromField.boundList.getSelectionModel().select(selected);\r
257 },\r
258\r
259 moveRec: function(add, recs) {\r
260 var me = this,\r
261 fromField = me.fromField,\r
262 toField = me.toField,\r
263 fromStore = add ? fromField.store : toField.store,\r
264 toStore = add ? toField.store : fromField.store;\r
265\r
266 fromStore.suspendEvents();\r
267 toStore.suspendEvents();\r
268 fromStore.remove(recs);\r
269 toStore.add(recs);\r
270 fromStore.resumeEvents();\r
271 toStore.resumeEvents();\r
272 \r
273 // If the list item was focused when moved (e.g. via double-click)\r
274 // then removing it will cause the focus to be thrown back to the\r
275 // document body. Which might disrupt things if ItemSelector is\r
276 // contained by a floating thingie like a Menu.\r
277 // Focusing the list itself will prevent that.\r
278 if (fromField.boundList.containsFocus) {\r
279 fromField.boundList.focus();\r
280 }\r
281\r
282 fromField.boundList.refresh();\r
283 toField.boundList.refresh();\r
284\r
285 me.syncValue();\r
286 },\r
287\r
288 // Synchronizes the submit value with the current state of the toStore\r
289 syncValue: function() {\r
290 var me = this; \r
291 me.mixins.field.setValue.call(me, me.setupValue(me.toField.store.getRange()));\r
292 },\r
293\r
294 onItemDblClick: function(view, rec) {\r
295 this.moveRec(view === this.fromField.boundList, rec);\r
296 },\r
297\r
298 setValue: function(value) {\r
299 var me = this,\r
300 fromField = me.fromField,\r
301 toField = me.toField,\r
302 fromStore = fromField.store,\r
303 toStore = toField.store,\r
304 selected;\r
305\r
306 // Wait for from store to be loaded\r
307 if (!me.fromStorePopulated) {\r
308 me.fromField.store.on({\r
309 load: Ext.Function.bind(me.setValue, me, [value]),\r
310 single: true\r
311 });\r
312 return;\r
313 }\r
314\r
315 value = me.setupValue(value);\r
316 me.mixins.field.setValue.call(me, value);\r
317\r
318 selected = me.getRecordsForValue(value);\r
319\r
320 // Clear both left and right Stores.\r
321 // Both stores must not fire events during this process.\r
322 fromStore.suspendEvents();\r
323 toStore.suspendEvents();\r
324 fromStore.removeAll();\r
325 toStore.removeAll();\r
326\r
327 // Reset fromStore\r
328 me.populateFromStore(me.store);\r
329\r
330 // Copy selection across to toStore\r
331 Ext.Array.forEach(selected, function(rec){\r
332 // In the from store, move it over\r
333 if (fromStore.indexOf(rec) > -1) {\r
334 fromStore.remove(rec);\r
335 }\r
336 toStore.add(rec);\r
337 });\r
338\r
339 // Stores may now fire events\r
340 fromStore.resumeEvents();\r
341 toStore.resumeEvents();\r
342\r
343 // Refresh both sides and then update the app layout\r
344 Ext.suspendLayouts();\r
345 fromField.boundList.refresh();\r
346 toField.boundList.refresh();\r
347 Ext.resumeLayouts(true); \r
348 },\r
349\r
350 onBindStore: function(store, initial) {\r
351 var me = this;\r
352\r
353 if (me.fromField) {\r
354 me.fromField.store.removeAll();\r
355 me.toField.store.removeAll();\r
356\r
357 // Add everything to the from field as soon as the Store is loaded\r
358 if (store.getCount()) {\r
359 me.populateFromStore(store);\r
360 } else {\r
361 me.store.on('load', me.populateFromStore, me);\r
362 }\r
363 }\r
364 },\r
365\r
366 populateFromStore: function(store) {\r
367 var fromStore = this.fromField.store;\r
368\r
369 // Flag set when the fromStore has been loaded\r
370 this.fromStorePopulated = true;\r
371\r
372 fromStore.add(store.getRange());\r
373\r
374 // setValue waits for the from Store to be loaded\r
375 fromStore.fireEvent('load', fromStore);\r
376 },\r
377\r
378 onEnable: function(){\r
379 var me = this;\r
380\r
381 me.callParent();\r
382 me.fromField.enable();\r
383 me.toField.enable();\r
384\r
385 Ext.Array.forEach(me.query('[navBtn]'), function(btn){\r
386 btn.enable();\r
387 });\r
388 },\r
389\r
390 onDisable: function(){\r
391 var me = this;\r
392\r
393 me.callParent();\r
394 me.fromField.disable();\r
395 me.toField.disable();\r
396\r
397 Ext.Array.forEach(me.query('[navBtn]'), function(btn){\r
398 btn.disable();\r
399 });\r
400 },\r
401\r
402 onDestroy: function(){\r
403 this.bindStore(null);\r
404 this.callParent();\r
405 }\r
406});\r