windowsinboxwhatsappicloudtweetdeckhipchattelegramhangoutsslackgmailskypefacebook-workplaceoutlookemailmicrosoft-teamsdiscordmessengercustom-servicesmacoslinux
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
307 lines
9.1 KiB
307 lines
9.1 KiB
9 years ago
|
/**
|
||
|
* This component provides a grid holding selected items from a second store of potential
|
||
|
* members. The `store` of this component represents the selected items. The `searchStore`
|
||
|
* represents the potentially selected items.
|
||
|
*
|
||
|
* The default view defined by this class is intended to be easily replaced by deriving a
|
||
|
* new class and overriding the appropriate methods. For example, the following is a very
|
||
|
* different view that uses a date range and a data view:
|
||
|
*
|
||
|
* Ext.define('App.view.DateBoundSearch', {
|
||
|
* extend: 'Ext.view.MultiSelectorSearch',
|
||
|
*
|
||
|
* makeDockedItems: function () {
|
||
|
* return {
|
||
|
* xtype: 'toolbar',
|
||
|
* items: [{
|
||
|
* xtype: 'datefield',
|
||
|
* emptyText: 'Start date...',
|
||
|
* flex: 1
|
||
|
* },{
|
||
|
* xtype: 'datefield',
|
||
|
* emptyText: 'End date...',
|
||
|
* flex: 1
|
||
|
* }]
|
||
|
* };
|
||
|
* },
|
||
|
*
|
||
|
* makeItems: function () {
|
||
|
* return [{
|
||
|
* xtype: 'dataview',
|
||
|
* itemSelector: '.search-item',
|
||
|
* selModel: 'rowselection',
|
||
|
* store: this.store,
|
||
|
* scrollable: true,
|
||
|
* tpl:
|
||
|
* '<tpl for=".">' +
|
||
|
* '<div class="search-item">' +
|
||
|
* '<img src="{icon}">' +
|
||
|
* '<div>{name}</div>' +
|
||
|
* '</div>' +
|
||
|
* '</tpl>'
|
||
|
* }];
|
||
|
* },
|
||
|
*
|
||
|
* getSearchStore: function () {
|
||
|
* return this.items.getAt(0).getStore();
|
||
|
* },
|
||
|
*
|
||
|
* selectRecords: function (records) {
|
||
|
* var view = this.items.getAt(0);
|
||
|
* return view.getSelectionModel().select(records);
|
||
|
* }
|
||
|
* });
|
||
|
*
|
||
|
* **Important**: This class assumes there are two components with specific `reference`
|
||
|
* names assigned to them. These are `"searchField"` and `"searchGrid"`. These components
|
||
|
* are produced by the `makeDockedItems` and `makeItems` method, respectively. When
|
||
|
* overriding these it is important to remember to place these `reference` values on the
|
||
|
* appropriate components.
|
||
|
*/
|
||
|
Ext.define('Ext.view.MultiSelectorSearch', {
|
||
|
extend: 'Ext.panel.Panel',
|
||
|
|
||
|
xtype: 'multiselector-search',
|
||
|
|
||
|
layout: 'fit',
|
||
|
|
||
|
floating: true,
|
||
|
resizable: true,
|
||
|
minWidth: 200,
|
||
|
minHeight: 200,
|
||
|
border: true,
|
||
|
|
||
|
defaultListenerScope: true,
|
||
|
referenceHolder: true,
|
||
|
|
||
|
/**
|
||
|
* @cfg {String} field
|
||
|
* A field from your grid's store that will be used for filtering your search results.
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* @cfg store
|
||
|
* @inheritdoc Ext.panel.Table#store
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* @cfg {String} searchText
|
||
|
* This text is displayed as the "emptyText" of the search `textfield`.
|
||
|
*/
|
||
|
searchText: 'Search...',
|
||
|
|
||
|
initComponent: function () {
|
||
|
var me = this,
|
||
|
owner = me.owner,
|
||
|
items = me.makeItems(),
|
||
|
i, item, records, store;
|
||
|
|
||
|
me.dockedItems = me.makeDockedItems();
|
||
|
me.items = items;
|
||
|
|
||
|
store = Ext.data.StoreManager.lookup(me.store);
|
||
|
|
||
|
for (i = items.length; i--; ) {
|
||
|
if ((item = items[i]).xtype === 'grid') {
|
||
|
item.store = store;
|
||
|
item.isSearchGrid = true;
|
||
|
item.selModel = item.selModel || {
|
||
|
type: 'checkboxmodel',
|
||
|
pruneRemoved: false,
|
||
|
listeners: {
|
||
|
selectionchange: 'onSelectionChange'
|
||
|
}
|
||
|
};
|
||
|
|
||
|
Ext.merge(item, me.grid);
|
||
|
|
||
|
if (!item.columns) {
|
||
|
item.hideHeaders = true;
|
||
|
item.columns = [{
|
||
|
flex: 1,
|
||
|
dataIndex: me.field
|
||
|
}];
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
me.callParent();
|
||
|
|
||
|
records = me.getOwnerStore().getRange();
|
||
|
if (!owner.convertSelectionRecord.$nullFn) {
|
||
|
for (i = records.length; i--; ) {
|
||
|
records[i] = owner.convertSelectionRecord(records[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (store.isLoading() || (store.loadCount === 0 && !store.getCount())) {
|
||
|
|
||
|
// If it is NOT a preloaded store, then unless a Session is being used,
|
||
|
// The newly loaded records will NOT match any in the ownerStore.
|
||
|
// So we must match them by ID in order to select the same dataset.
|
||
|
store.on('load', function() {
|
||
|
var len = records.length,
|
||
|
i,
|
||
|
record,
|
||
|
toSelect = [];
|
||
|
|
||
|
if (!me.destroyed) {
|
||
|
for (i = 0; i < len; i++) {
|
||
|
record = store.getById(records[i].getId());
|
||
|
if (record) {
|
||
|
toSelect.push(record);
|
||
|
}
|
||
|
}
|
||
|
me.selectRecords(toSelect);
|
||
|
}
|
||
|
}, null, {single: true});
|
||
|
} else {
|
||
|
me.selectRecords(records);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
getOwnerStore: function() {
|
||
|
return this.owner.getStore();
|
||
|
},
|
||
|
|
||
|
afterShow: function () {
|
||
|
var searchField = this.lookupReference('searchField');
|
||
|
|
||
|
this.callParent(arguments);
|
||
|
|
||
|
if (searchField) {
|
||
|
searchField.focus();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Returns the store that holds search results. By default this comes from the
|
||
|
* "search grid". If this aspect of the view is changed sufficiently so that the
|
||
|
* search grid cannot be found, this method should be overridden to return the proper
|
||
|
* store.
|
||
|
* @return {Ext.data.Store}
|
||
|
*/
|
||
|
getSearchStore: function () {
|
||
|
var searchGrid = this.lookupReference('searchGrid');
|
||
|
return searchGrid.getStore();
|
||
|
},
|
||
|
|
||
|
makeDockedItems: function () {
|
||
|
return [{
|
||
|
xtype: 'textfield',
|
||
|
reference: 'searchField',
|
||
|
dock: 'top',
|
||
|
hideFieldLabel: true,
|
||
|
emptyText: this.searchText,
|
||
|
triggers: {
|
||
|
clear: {
|
||
|
cls: Ext.baseCSSPrefix + 'form-clear-trigger',
|
||
|
handler: 'onClearSearch',
|
||
|
hidden: true
|
||
|
}
|
||
|
},
|
||
|
listeners: {
|
||
|
change: 'onSearchChange',
|
||
|
buffer: 300
|
||
|
}
|
||
|
}];
|
||
|
},
|
||
|
|
||
|
makeItems: function () {
|
||
|
return [{
|
||
|
xtype: 'grid',
|
||
|
reference: 'searchGrid',
|
||
|
trailingBufferZone: 2,
|
||
|
leadingBufferZone: 2,
|
||
|
viewConfig: {
|
||
|
deferEmptyText: false,
|
||
|
emptyText: 'No results.'
|
||
|
}
|
||
|
}];
|
||
|
},
|
||
|
|
||
|
selectRecords: function (records) {
|
||
|
var searchGrid = this.lookupReference('searchGrid');
|
||
|
return searchGrid.getSelectionModel().select(records);
|
||
|
},
|
||
|
|
||
|
deselectRecords: function(records) {
|
||
|
var searchGrid = this.lookupReference('searchGrid');
|
||
|
return searchGrid.getSelectionModel().deselect(records);
|
||
|
},
|
||
|
|
||
|
search: function (text) {
|
||
|
var me = this,
|
||
|
filter = me.searchFilter,
|
||
|
filters = me.getSearchStore().getFilters();
|
||
|
|
||
|
if (text) {
|
||
|
filters.beginUpdate();
|
||
|
|
||
|
if (filter) {
|
||
|
filter.setValue(text);
|
||
|
} else {
|
||
|
me.searchFilter = filter = new Ext.util.Filter({
|
||
|
id: 'search',
|
||
|
property: me.field,
|
||
|
value: text
|
||
|
});
|
||
|
}
|
||
|
|
||
|
filters.add(filter);
|
||
|
|
||
|
filters.endUpdate();
|
||
|
} else if (filter) {
|
||
|
filters.remove(filter);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
privates: {
|
||
|
onClearSearch: function () {
|
||
|
var searchField = this.lookupReference('searchField');
|
||
|
searchField.setValue(null);
|
||
|
searchField.focus();
|
||
|
},
|
||
|
|
||
|
onSearchChange: function (searchField) {
|
||
|
var value = searchField.getValue(),
|
||
|
trigger = searchField.getTrigger('clear');
|
||
|
|
||
|
trigger.setHidden(!value);
|
||
|
this.search(value);
|
||
|
},
|
||
|
|
||
|
onSelectionChange: function (selModel, selection) {
|
||
|
var owner = this.owner,
|
||
|
store = owner.getStore(),
|
||
|
data = store.data,
|
||
|
remove = 0,
|
||
|
map = {},
|
||
|
add, i, id, record;
|
||
|
|
||
|
for (i = selection.length; i--; ) {
|
||
|
record = selection[i];
|
||
|
id = record.id;
|
||
|
map[id] = record;
|
||
|
|
||
|
if (!data.containsKey(id)) {
|
||
|
(add || (add = [])).push(owner.convertSearchRecord(record));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (i = data.length; i--; ) {
|
||
|
record = data.getAt(i);
|
||
|
if (!map[record.id]) {
|
||
|
(remove || (remove = [])).push(record);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (add || remove) {
|
||
|
data.splice(data.length, remove, add);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
});
|