3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * Note that this control will most likely remain as an example, and not as a core Ext form
17 * control. However, the API will be changing in a future release and so should not yet be
18 * treated as a final, stable API at this time.
22 * @class Ext.ux.form.ItemSelector
23 * @extends Ext.form.field.Base
24 * A control that allows selection of between two Ext.ux.form.MultiSelect controls.
27 * 2008-06-19 bpm Original code contributed by Toby Stuart (with contributions from Robert Williams)
30 * Create a new ItemSelector
31 * @param {Object} config Configuration options
34 Ext.define('Ext.ux.form.ItemSelector', {
35 extend: 'Ext.ux.form.MultiSelect',
36 alias: ['widget.itemselectorfield', 'widget.itemselector'],
37 alternateClassName: ['Ext.ux.ItemSelector'],
38 requires: ['Ext.ux.layout.component.form.ItemSelector', 'Ext.button.Button'],
43 * @cfg {Array} buttons Defines the set of buttons that should be displayed in between the ItemSelector
44 * fields. Defaults to <tt>['top', 'up', 'add', 'remove', 'down', 'bottom']</tt>. These names are used
45 * to build the button CSS class names, and to look up the button text labels in {@link #buttonsText}.
46 * This can be overridden with a custom Array to change which buttons are displayed or their order.
48 buttons: ['top', 'up', 'add', 'remove', 'down', 'bottom'],
53 add: "Add to Selected",
54 remove: "Remove from Selected",
56 bottom: "Move to Bottom"
60 * @cfg {Array} multiselects An optional array of {@link Ext.ux.form.MultiSelect} config objects, containing
61 * additional configuration to be applied to the internal MultiSelect fields.
65 componentLayout: 'itemselectorfield',
67 fieldBodyCls: Ext.baseCSSPrefix + 'form-itemselector-body',
70 bindStore: function(store, initial) {
73 fromField = me.fromField,
76 me.callParent(arguments);
79 // Clear both field stores
80 toField.store.removeAll();
81 fromField.store.removeAll();
83 // Clone the contents of the main store into the fromField
85 me.store.each(function(model) {
86 models.push(model.copy(model.getId()));
88 fromField.store.add(models);
92 onRender: function(ct, position) {
94 baseCSSPrefix = Ext.baseCSSPrefix,
95 ddGroup = 'ItemSelectorDD-' + Ext.id(),
97 displayField: me.displayField,
98 valueField: me.valueField,
103 disabled: me.disabled
105 fromConfig = Ext.apply({
106 listTitle: 'Available',
107 store: Ext.create('Ext.data.Store', {model: me.store.model}), //blank store to begin
110 itemdblclick: me.onItemDblClick,
114 }, me.multiselects[0], commonConfig),
115 toConfig = Ext.apply({
116 listTitle: 'Selected',
117 store: Ext.create('Ext.data.Store', {model: me.store.model}), //blank store to begin
120 itemdblclick: me.onItemDblClick,
123 change: me.onToFieldChange,
126 }, me.multiselects[1], commonConfig),
127 fromField = Ext.widget('multiselect', fromConfig),
128 toField = Ext.widget('multiselect', toConfig),
132 // Skip MultiSelect's onRender as we don't want its content
133 Ext.ux.form.MultiSelect.superclass.onRender.call(me, ct, position);
135 me.fromField = fromField;
136 me.toField = toField;
138 if (!me.hideNavIcons) {
139 Ext.Array.forEach(me.buttons, function(name) {
142 tooltip: me.buttonsText[name],
143 handler: me['on' + Ext.String.capitalize(name) + 'BtnClick'],
144 cls: baseCSSPrefix + 'form-itemselector-btn',
145 iconCls: baseCSSPrefix + 'form-itemselector-' + name,
148 //div separator to force vertical stacking
149 buttons.push({xtype: 'component', height: 3, width: 1, style: 'font-size:0;line-height:0'});
153 innerCt = me.innerCt = Ext.widget('container', {
170 // Must set upward link after first render
171 innerCt.ownerCt = me;
173 // Rebind the store so it gets cloned to the fromField
174 me.bindStore(me.store);
176 // Set the initial value
177 me.setRawValue(me.rawValue);
180 onToFieldChange: function() {
184 getSelections: function(list){
185 var store = list.getStore(),
186 selections = list.getSelectionModel().getSelection(),
188 len = selections.length;
190 return Ext.Array.sort(selections, function(a, b){
191 a = store.indexOf(a);
192 b = store.indexOf(b);
203 onTopBtnClick : function() {
204 var list = this.toField.boundList,
205 store = list.getStore(),
206 selected = this.getSelections(list),
207 i = selected.length - 1,
211 store.suspendEvents();
212 for (; i > -1; --i) {
213 selection = selected[i];
214 store.remove(selected);
215 store.insert(0, selected);
217 store.resumeEvents();
221 onBottomBtnClick : function() {
222 var list = this.toField.boundList,
223 store = list.getStore(),
224 selected = this.getSelections(list),
226 len = selected.length,
229 store.suspendEvents();
230 for (; i < len; ++i) {
231 selection = selected[i];
232 store.remove(selection);
233 store.add(selection);
235 store.resumeEvents();
239 onUpBtnClick : function() {
240 var list = this.toField.boundList,
241 store = list.getStore(),
242 selected = this.getSelections(list),
244 len = selected.length,
248 store.suspendEvents();
249 for (; i < len; ++i) {
250 selection = selected[i];
251 index = Math.max(0, store.indexOf(selection) - 1);
252 store.remove(selection);
253 store.insert(index, selection);
255 store.resumeEvents();
259 onDownBtnClick : function() {
260 var list = this.toField.boundList,
261 store = list.getStore(),
262 selected = this.getSelections(list),
264 len = selected.length,
265 max = store.getCount(),
269 store.suspendEvents();
270 for (; i < len; ++i) {
271 selection = selected[i];
272 index = Math.min(max, store.indexOf(selection) + 1);
273 store.remove(selection);
274 store.insert(index, selection);
276 store.resumeEvents();
280 onAddBtnClick : function() {
282 fromList = me.fromField.boundList,
283 selected = this.getSelections(fromList);
285 fromList.getStore().remove(selected);
286 this.toField.boundList.getStore().add(selected);
289 onRemoveBtnClick : function() {
291 toList = me.toField.boundList,
292 selected = this.getSelections(toList);
294 toList.getStore().remove(selected);
295 this.fromField.boundList.getStore().add(selected);
298 onItemDblClick : function(view) {
300 if (view == me.toField.boundList){
301 me.onRemoveBtnClick();
303 else if (view == me.fromField.boundList) {
308 setRawValue: function(value) {
311 toStore, fromStore, models;
313 value = Array.from(value);
317 toStore = me.toField.boundList.getStore();
318 fromStore = me.fromField.boundList.getStore();
320 // Move any selected values back to the fromField
321 fromStore.add(toStore.getRange());
324 // Move the new values over to the toField
326 Ext.Array.forEach(value, function(val) {
328 model = fromStore.findRecord(me.valueField, val, undef, undef, true, true);
333 fromStore.remove(models);
340 getRawValue: function() {
342 toField = me.toField,
343 rawValue = me.rawValue;
346 rawValue = Ext.Array.map(toField.boundList.getStore().getRange(), function(model) {
347 return model.get(me.valueField);
351 me.rawValue = rawValue;
356 * @private Cascade readOnly/disabled state to the sub-fields and buttons
358 updateReadOnly: function() {
360 readOnly = me.readOnly || me.disabled;
363 me.toField.setReadOnly(readOnly);
364 me.fromField.setReadOnly(readOnly);
365 Ext.Array.forEach(me.innerCt.query('button'), function(button) {
366 button.setDisabled(readOnly);
371 onDisable: function(){
373 var fromField = this.fromField;
375 // if we have one, we have both, they get created at the same time
378 this.toField.disable();
382 onEnable: function(){
384 var fromField = this.fromField;
386 // if we have one, we have both, they get created at the same time
389 this.toField.enable();
393 onDestroy: function() {
394 Ext.destroyMembers(this, 'innerCt');