You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
383 lines
11 KiB
JavaScript
383 lines
11 KiB
JavaScript
3 years ago
|
/*
|
||
|
* 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.
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* A control that allows selection of between two Ext.ux.form.MultiSelect controls.
|
||
|
*/
|
||
|
Ext.define('Ext.ux.form.ItemSelector', {
|
||
|
extend: 'Ext.ux.form.MultiSelect',
|
||
|
alias: ['widget.itemselectorfield', 'widget.itemselector'],
|
||
|
alternateClassName: ['Ext.ux.ItemSelector'],
|
||
|
requires: [
|
||
|
'Ext.button.Button',
|
||
|
'Ext.ux.form.MultiSelect'
|
||
|
],
|
||
|
|
||
|
/**
|
||
|
* @cfg {Boolean} [hideNavIcons=false] True to hide the navigation icons
|
||
|
*/
|
||
|
hideNavIcons:false,
|
||
|
|
||
|
/**
|
||
|
* @cfg {Array} buttons Defines the set of buttons that should be displayed in between the ItemSelector
|
||
|
* fields. Defaults to <tt>['top', 'up', 'add', 'remove', 'down', 'bottom']</tt>. 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'],
|
||
|
|
||
|
/**
|
||
|
* @cfg {Object} buttonsText The tooltips for the {@link #buttons}.
|
||
|
* Labels for buttons.
|
||
|
*/
|
||
|
buttonsText: {
|
||
|
top: "Move to Top",
|
||
|
up: "Move Up",
|
||
|
add: "Add to Selected",
|
||
|
remove: "Remove from Selected",
|
||
|
down: "Move Down",
|
||
|
bottom: "Move to Bottom"
|
||
|
},
|
||
|
|
||
|
initComponent: function() {
|
||
|
var me = this;
|
||
|
|
||
|
me.ddGroup = me.id + '-dd';
|
||
|
me.callParent();
|
||
|
|
||
|
// bindStore must be called after the fromField has been created because
|
||
|
// it copies records from our configured Store into the fromField's Store
|
||
|
me.bindStore(me.store);
|
||
|
},
|
||
|
|
||
|
createList: function(title){
|
||
|
var me = this;
|
||
|
|
||
|
return Ext.create('Ext.ux.form.MultiSelect', {
|
||
|
submitValue: false,
|
||
|
flex: 1,
|
||
|
dragGroup: me.ddGroup,
|
||
|
dropGroup: me.ddGroup,
|
||
|
title: title,
|
||
|
store: {
|
||
|
model: me.store.model,
|
||
|
data: []
|
||
|
},
|
||
|
displayField: me.displayField,
|
||
|
disabled: me.disabled,
|
||
|
listeners: {
|
||
|
boundList: {
|
||
|
scope: me,
|
||
|
itemdblclick: me.onItemDblClick,
|
||
|
drop: me.syncValue
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
|
||
|
setupItems: function() {
|
||
|
var me = this;
|
||
|
|
||
|
me.fromField = me.createList(me.fromTitle);
|
||
|
me.toField = me.createList(me.toTitle);
|
||
|
|
||
|
return {
|
||
|
border: false,
|
||
|
layout: {
|
||
|
type: 'hbox',
|
||
|
align: 'stretch'
|
||
|
},
|
||
|
items: [
|
||
|
me.fromField,
|
||
|
{
|
||
|
xtype: 'container',
|
||
|
margins: '0 4',
|
||
|
width: 22,
|
||
|
layout: {
|
||
|
type: 'vbox',
|
||
|
pack: 'center'
|
||
|
},
|
||
|
items: me.createButtons()
|
||
|
},
|
||
|
me.toField
|
||
|
]
|
||
|
};
|
||
|
},
|
||
|
|
||
|
createButtons: function(){
|
||
|
var me = this,
|
||
|
buttons = [];
|
||
|
|
||
|
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: Ext.baseCSSPrefix + 'form-itemselector-btn',
|
||
|
iconCls: Ext.baseCSSPrefix + 'form-itemselector-' + name,
|
||
|
navBtn: true,
|
||
|
scope: me,
|
||
|
margin: '4 0 0 0'
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
return buttons;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Get the selected records from the specified list.
|
||
|
*
|
||
|
* Records will be returned *in store order*, not in order of selection.
|
||
|
* @param {Ext.view.BoundList} list The list to read selections from.
|
||
|
* @return {Ext.data.Model[]} The selected records in store order.
|
||
|
*
|
||
|
*/
|
||
|
getSelections: function(list) {
|
||
|
var store = list.getStore();
|
||
|
|
||
|
return Ext.Array.sort(list.getSelectionModel().getSelection(), 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);
|
||
|
|
||
|
store.suspendEvents();
|
||
|
store.remove(selected, true);
|
||
|
store.insert(0, selected);
|
||
|
store.resumeEvents();
|
||
|
list.refresh();
|
||
|
this.syncValue();
|
||
|
list.getSelectionModel().select(selected);
|
||
|
},
|
||
|
|
||
|
onBottomBtnClick : function() {
|
||
|
var list = this.toField.boundList,
|
||
|
store = list.getStore(),
|
||
|
selected = this.getSelections(list);
|
||
|
|
||
|
store.suspendEvents();
|
||
|
store.remove(selected, true);
|
||
|
store.add(selected);
|
||
|
store.resumeEvents();
|
||
|
list.refresh();
|
||
|
this.syncValue();
|
||
|
list.getSelectionModel().select(selected);
|
||
|
},
|
||
|
|
||
|
onUpBtnClick : function() {
|
||
|
var list = this.toField.boundList,
|
||
|
store = list.getStore(),
|
||
|
selected = this.getSelections(list),
|
||
|
rec,
|
||
|
i = 0,
|
||
|
len = selected.length,
|
||
|
index = 0;
|
||
|
|
||
|
// Move each selection up by one place if possible
|
||
|
store.suspendEvents();
|
||
|
for (; i < len; ++i, index++) {
|
||
|
rec = selected[i];
|
||
|
index = Math.max(index, store.indexOf(rec) - 1);
|
||
|
store.remove(rec, true);
|
||
|
store.insert(index, rec);
|
||
|
}
|
||
|
store.resumeEvents();
|
||
|
list.refresh();
|
||
|
this.syncValue();
|
||
|
list.getSelectionModel().select(selected);
|
||
|
},
|
||
|
|
||
|
onDownBtnClick : function() {
|
||
|
var list = this.toField.boundList,
|
||
|
store = list.getStore(),
|
||
|
selected = this.getSelections(list),
|
||
|
rec,
|
||
|
i = selected.length - 1,
|
||
|
index = store.getCount() - 1;
|
||
|
|
||
|
// Move each selection down by one place if possible
|
||
|
store.suspendEvents();
|
||
|
for (; i > -1; --i, index--) {
|
||
|
rec = selected[i];
|
||
|
index = Math.min(index, store.indexOf(rec) + 1);
|
||
|
store.remove(rec, true);
|
||
|
store.insert(index, rec);
|
||
|
}
|
||
|
store.resumeEvents();
|
||
|
list.refresh();
|
||
|
this.syncValue();
|
||
|
list.getSelectionModel().select(selected);
|
||
|
},
|
||
|
|
||
|
onAddBtnClick : function() {
|
||
|
var me = this,
|
||
|
selected = me.getSelections(me.fromField.boundList);
|
||
|
|
||
|
me.moveRec(true, selected);
|
||
|
me.toField.boundList.getSelectionModel().select(selected);
|
||
|
},
|
||
|
|
||
|
onRemoveBtnClick : function() {
|
||
|
var me = this,
|
||
|
selected = me.getSelections(me.toField.boundList);
|
||
|
|
||
|
me.moveRec(false, selected);
|
||
|
me.fromField.boundList.getSelectionModel().select(selected);
|
||
|
},
|
||
|
|
||
|
moveRec: function(add, recs) {
|
||
|
var me = this,
|
||
|
fromField = me.fromField,
|
||
|
toField = me.toField,
|
||
|
fromStore = add ? fromField.store : toField.store,
|
||
|
toStore = add ? toField.store : fromField.store;
|
||
|
|
||
|
fromStore.suspendEvents();
|
||
|
toStore.suspendEvents();
|
||
|
fromStore.remove(recs);
|
||
|
toStore.add(recs);
|
||
|
fromStore.resumeEvents();
|
||
|
toStore.resumeEvents();
|
||
|
|
||
|
fromField.boundList.refresh();
|
||
|
toField.boundList.refresh();
|
||
|
|
||
|
me.syncValue();
|
||
|
},
|
||
|
|
||
|
// Synchronizes the submit value with the current state of the toStore
|
||
|
syncValue: function() {
|
||
|
var me = this;
|
||
|
me.mixins.field.setValue.call(me, me.setupValue(me.toField.store.getRange()));
|
||
|
},
|
||
|
|
||
|
onItemDblClick: function(view, rec) {
|
||
|
this.moveRec(view === this.fromField.boundList, rec);
|
||
|
},
|
||
|
|
||
|
setValue: function(value) {
|
||
|
var me = this,
|
||
|
fromField = me.fromField,
|
||
|
toField = me.toField,
|
||
|
fromStore = fromField.store,
|
||
|
toStore = toField.store,
|
||
|
selected;
|
||
|
|
||
|
// Wait for from store to be loaded
|
||
|
if (!me.fromStorePopulated) {
|
||
|
me.fromField.store.on({
|
||
|
load: Ext.Function.bind(me.setValue, me, [value]),
|
||
|
single: true
|
||
|
});
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
value = me.setupValue(value);
|
||
|
me.mixins.field.setValue.call(me, value);
|
||
|
|
||
|
selected = me.getRecordsForValue(value);
|
||
|
|
||
|
// Clear both left and right Stores.
|
||
|
// Both stores must not fire events during this process.
|
||
|
fromStore.suspendEvents();
|
||
|
toStore.suspendEvents();
|
||
|
fromStore.removeAll();
|
||
|
toStore.removeAll();
|
||
|
|
||
|
// Reset fromStore
|
||
|
me.populateFromStore(me.store);
|
||
|
|
||
|
// Copy selection across to toStore
|
||
|
Ext.Array.forEach(selected, function(rec){
|
||
|
// In the from store, move it over
|
||
|
if (fromStore.indexOf(rec) > -1) {
|
||
|
fromStore.remove(rec);
|
||
|
}
|
||
|
toStore.add(rec);
|
||
|
});
|
||
|
|
||
|
// Stores may now fire events
|
||
|
fromStore.resumeEvents();
|
||
|
toStore.resumeEvents();
|
||
|
|
||
|
// Refresh both sides and then update the app layout
|
||
|
Ext.suspendLayouts();
|
||
|
fromField.boundList.refresh();
|
||
|
toField.boundList.refresh();
|
||
|
Ext.resumeLayouts(true);
|
||
|
},
|
||
|
|
||
|
onBindStore: function(store, initial) {
|
||
|
var me = this;
|
||
|
|
||
|
if (me.fromField) {
|
||
|
me.fromField.store.removeAll()
|
||
|
me.toField.store.removeAll();
|
||
|
|
||
|
// Add everything to the from field as soon as the Store is loaded
|
||
|
if (store.getCount()) {
|
||
|
me.populateFromStore(store);
|
||
|
} else {
|
||
|
me.store.on('load', me.populateFromStore, me);
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
populateFromStore: function(store) {
|
||
|
var fromStore = this.fromField.store;
|
||
|
|
||
|
// Flag set when the fromStore has been loaded
|
||
|
this.fromStorePopulated = true;
|
||
|
|
||
|
fromStore.add(store.getRange());
|
||
|
|
||
|
// setValue waits for the from Store to be loaded
|
||
|
fromStore.fireEvent('load', fromStore);
|
||
|
},
|
||
|
|
||
|
onEnable: function(){
|
||
|
var me = this;
|
||
|
|
||
|
me.callParent();
|
||
|
me.fromField.enable();
|
||
|
me.toField.enable();
|
||
|
|
||
|
Ext.Array.forEach(me.query('[navBtn]'), function(btn){
|
||
|
btn.enable();
|
||
|
});
|
||
|
},
|
||
|
|
||
|
onDisable: function(){
|
||
|
var me = this;
|
||
|
|
||
|
me.callParent();
|
||
|
me.fromField.disable();
|
||
|
me.toField.disable();
|
||
|
|
||
|
Ext.Array.forEach(me.query('[navBtn]'), function(btn){
|
||
|
btn.disable();
|
||
|
});
|
||
|
},
|
||
|
|
||
|
onDestroy: function(){
|
||
|
this.bindStore(null);
|
||
|
this.callParent();
|
||
|
}
|
||
|
});
|