outlookemailmicrosoft-teamsdiscordmessengercustom-servicesmacoslinuxwindowsinboxwhatsappicloudtweetdeckhipchattelegramhangoutsslackgmailskypefacebook-workplace
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.
306 lines
9.1 KiB
306 lines
9.1 KiB
/** |
|
* 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); |
|
} |
|
} |
|
} |
|
});
|
|
|