X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/0494b8d9b9bb03ab6c22b34dae81261e3cd7e3e6..7a654f8d43fdb43d78b63d90528bed6e86b608cc:/examples/ux/form/ItemSelector.js diff --git a/examples/ux/form/ItemSelector.js b/examples/ux/form/ItemSelector.js new file mode 100644 index 00000000..72f04644 --- /dev/null +++ b/examples/ux/form/ItemSelector.js @@ -0,0 +1,361 @@ +/* + * Note that this control will most likely remain as an example, and not as a core Ext form + * control. However, the API will be changing in a future release and so should not yet be + * treated as a final, stable API at this time. + */ + +/** + * @class Ext.ux.form.ItemSelector + * @extends Ext.form.field.Base + * A control that allows selection of between two Ext.ux.form.MultiSelect controls. + * + * @history + * 2008-06-19 bpm Original code contributed by Toby Stuart (with contributions from Robert Williams) + * + * @constructor + * Create a new ItemSelector + * @param {Object} config Configuration options + * @xtype itemselector + */ +Ext.define('Ext.ux.form.ItemSelector', { + extend: 'Ext.ux.form.MultiSelect', + alias: ['widget.itemselectorfield', 'widget.itemselector'], + alternateClassName: ['Ext.ux.ItemSelector'], + requires: ['Ext.ux.layout.component.form.ItemSelector', 'Ext.button.Button'], + + hideNavIcons:false, + + /** + * @cfg {Array} buttons Defines the set of buttons that should be displayed in between the ItemSelector + * fields. Defaults to ['top', 'up', 'add', 'remove', 'down', 'bottom']. These names are used + * to build the button CSS class names, and to look up the button text labels in {@link #buttonsText}. + * This can be overridden with a custom Array to change which buttons are displayed or their order. + */ + buttons: ['top', 'up', 'add', 'remove', 'down', 'bottom'], + + buttonsText: { + top: "Move to Top", + up: "Move Up", + add: "Add to Selected", + remove: "Remove from Selected", + down: "Move Down", + bottom: "Move to Bottom" + }, + + /** + * @cfg {Array} multiselects An optional array of {@link Ext.ux.form.MultiSelect} config objects, containing + * additional configuration to be applied to the internal MultiSelect fields. + */ + multiselects: [], + + componentLayout: 'itemselectorfield', + + fieldBodyCls: Ext.baseCSSPrefix + 'form-itemselector-body', + + + bindStore: function(store, initial) { + var me = this, + toField = me.toField, + fromField = me.fromField, + models; + + me.callParent(arguments); + + if (toField) { + // Clear both field stores + toField.store.removeAll(); + fromField.store.removeAll(); + + // Clone the contents of the main store into the fromField + models = []; + me.store.each(function(model) { + models.push(model.copy(model.getId())); + }); + fromField.store.add(models); + } + }, + + onRender: function(ct, position) { + var me = this, + baseCSSPrefix = Ext.baseCSSPrefix, + ddGroup = 'ItemSelectorDD-' + Ext.id(), + commonConfig = { + displayField: me.displayField, + valueField: me.valueField, + dragGroup: ddGroup, + dropGroup: ddGroup, + flex: 1, + hideLabel: true + }, + fromConfig = Ext.apply({ + listTitle: 'Available', + store: Ext.create('Ext.data.Store', {model: me.store.model}), //blank store to begin + listeners: { + boundList: { + itemdblclick: me.onItemDblClick, + scope: me + } + } + }, me.multiselects[0], commonConfig), + toConfig = Ext.apply({ + listTitle: 'Selected', + store: Ext.create('Ext.data.Store', {model: me.store.model}), //blank store to begin + listeners: { + boundList: { + itemdblclick: me.onItemDblClick, + scope: me + }, + change: me.onToFieldChange, + scope: me + } + }, me.multiselects[1], commonConfig), + fromField = Ext.widget('multiselect', fromConfig), + toField = Ext.widget('multiselect', toConfig), + innerCt, + buttons = []; + + // Skip MultiSelect's onRender as we don't want its content + Ext.ux.form.MultiSelect.superclass.onRender.call(me, ct, position); + + me.fromField = fromField; + me.toField = toField; + + if (!me.hideNavIcons) { + Ext.Array.forEach(me.buttons, function(name) { + buttons.push({ + xtype: 'button', + tooltip: me.buttonsText[name], + handler: me['on' + Ext.String.capitalize(name) + 'BtnClick'], + cls: baseCSSPrefix + 'form-itemselector-btn', + iconCls: baseCSSPrefix + 'form-itemselector-' + name, + scope: me + }); + //div separator to force vertical stacking + buttons.push({xtype: 'component', height: 3, width: 1, style: 'font-size:0;line-height:0'}); + }); + } + + innerCt = me.innerCt = Ext.widget('container', { + renderTo: me.bodyEl, + layout: { + type: 'hbox', + align: 'middle' + }, + items: [ + me.fromField, + { + xtype: 'container', + margins: '0 4', + items: buttons + }, + me.toField + ] + }); + + // Must set upward link after first render + innerCt.ownerCt = me; + + // Rebind the store so it gets cloned to the fromField + me.bindStore(me.store); + + // Set the initial value + me.setRawValue(me.rawValue); + }, + + onToFieldChange: function() { + this.checkChange(); + }, + + getSelections: function(list){ + var store = list.getStore(), + selections = list.getSelectionModel().getSelection(), + i = 0, + len = selections.length; + + return Ext.Array.sort(selections, function(a, b){ + a = store.indexOf(a); + b = store.indexOf(b); + + if (a < b) { + return -1; + } else if (a > b) { + return 1; + } + return 0; + }); + }, + + onTopBtnClick : function() { + var list = this.toField.boundList, + store = list.getStore(), + selected = this.getSelections(list), + i = selected.length - 1, + selection; + + + store.suspendEvents(); + for (; i > -1; --i) { + selection = selected[i]; + store.remove(selected); + store.insert(0, selected); + } + store.resumeEvents(); + list.refresh(); + }, + + onBottomBtnClick : function() { + var list = this.toField.boundList, + store = list.getStore(), + selected = this.getSelections(list), + i = 0, + len = selected.length, + selection; + + store.suspendEvents(); + for (; i < len; ++i) { + selection = selected[i]; + store.remove(selection); + store.add(selection); + } + store.resumeEvents(); + list.refresh(); + }, + + onUpBtnClick : function() { + var list = this.toField.boundList, + store = list.getStore(), + selected = this.getSelections(list), + i = 0, + len = selected.length, + selection, + index; + + store.suspendEvents(); + for (; i < len; ++i) { + selection = selected[i]; + index = Math.max(0, store.indexOf(selection) - 1); + store.remove(selection); + store.insert(index, selection); + } + store.resumeEvents(); + list.refresh(); + }, + + onDownBtnClick : function() { + var list = this.toField.boundList, + store = list.getStore(), + selected = this.getSelections(list), + i = 0, + len = selected.length, + max = store.getCount(), + selection, + index; + + store.suspendEvents(); + for (; i < len; ++i) { + selection = selected[i]; + index = Math.min(max, store.indexOf(selection) + 1); + store.remove(selection); + store.insert(index, selection); + } + store.resumeEvents(); + list.refresh(); + }, + + onAddBtnClick : function() { + var me = this, + fromList = me.fromField.boundList, + selected = this.getSelections(fromList); + + fromList.getStore().remove(selected); + this.toField.boundList.getStore().add(selected); + }, + + onRemoveBtnClick : function() { + var me = this, + toList = me.toField.boundList, + selected = this.getSelections(toList); + + toList.getStore().remove(selected); + this.fromField.boundList.getStore().add(selected); + }, + + onItemDblClick : function(view) { + var me = this; + if (view == me.toField.boundList){ + me.onRemoveBtnClick(); + } + else if (view == me.fromField.boundList) { + me.onAddBtnClick(); + } + }, + + setRawValue: function(value) { + var me = this, + Array = Ext.Array, + toStore, fromStore, models; + + value = Array.from(value); + me.rawValue = value; + + if (me.toField) { + toStore = me.toField.boundList.getStore(); + fromStore = me.fromField.boundList.getStore(); + + // Move any selected values back to the fromField + fromStore.add(toStore.getRange()); + toStore.removeAll(); + + // Move the new values over to the toField + models = []; + Ext.Array.forEach(value, function(val) { + var undef, + model = fromStore.findRecord(me.valueField, val, undef, undef, true, true); + if (model) { + models.push(model); + } + }); + fromStore.remove(models); + toStore.add(models); + } + + return value; + }, + + getRawValue: function() { + var me = this, + toField = me.toField, + rawValue = me.rawValue; + + if (toField) { + rawValue = Ext.Array.map(toField.boundList.getStore().getRange(), function(model) { + return model.get(me.valueField); + }); + } + + me.rawValue = rawValue; + return rawValue; + }, + + /** + * @private Cascade readOnly/disabled state to the sub-fields and buttons + */ + updateReadOnly: function() { + var me = this, + readOnly = me.readOnly || me.disabled; + + if (me.rendered) { + me.toField.setReadOnly(readOnly); + me.fromField.setReadOnly(readOnly); + Ext.Array.forEach(me.innerCt.query('button'), function(button) { + button.setDisabled(readOnly); + }); + } + }, + + onDestroy: function() { + Ext.destroyMembers(this, 'innerCt'); + this.callParent(); + } + +});