/** * Abstract base class for filter implementations. */Ext.define('Ext.grid.filters.filter.Base', { mixins: [ 'Ext.mixin.Factoryable' ], factoryConfig: { type: 'grid.filter' }, $configPrefixed: false, $configStrict: false, config: { /** * @cfg {Object} [itemDefaults] * The default configuration options for any menu items created by this filter. * * Example usage: * * itemDefaults: { * width: 150 * }, */ itemDefaults: null, menuDefaults: { xtype: 'menu' }, /** * @cfg {Number} updateBuffer * Number of milliseconds to wait after user interaction to fire an update. Only supported * by filters: 'list', 'numeric', and 'string'. */ updateBuffer: 500, /** * @cfg {Function} [serializer] * A function to post-process any serialization. Accepts a filter state object * containing `property`, `value` and `operator` properties, and may either * mutate it, or return a completely new representation. * @since 6.2.0 */ serializer: null }, /** * @property {Boolean} active * True if this filter is active. Use setActive() to alter after configuration. If * you set a value, the filter will be actived automatically. */ /** * @cfg {Boolean} active * Indicates the initial status of the filter (defaults to false). */ active: false, /** * @property {String} type * The filter type. Used by the filters.Feature class when adding filters and applying state. */ type: 'string', /** * @cfg {String} dataIndex * The {@link Ext.data.Store} dataIndex of the field this filter represents. * The dataIndex does not actually have to exist in the store. */ dataIndex: null, /** * @property {Ext.menu.Menu} menu * The filter configuration menu that will be installed into the filter submenu of a column menu. */ menu: null, isGridFilter: true, defaultRoot: 'data', /** * The prefix for id's used to track stateful Store filters. * @private */ filterIdPrefix: Ext.baseCSSPrefix + 'gridfilter', /** * @event activate * Fires when an inactive filter becomes active * @param {Ext.grid.filters.Filters} this */ /** * @event deactivate * Fires when an active filter becomes inactive * @param {Ext.grid.filters.Filters} this */ /** * @event update * Fires when a filter configuration has changed * @param {Ext.grid.filters.Filters} this The filter object. */ /** * Initializes the filter given its configuration. * @param {Object} config */ constructor: function (config) { var me = this, column; // Calling Base constructor is very desirable for testing //<debug> me.callParent([config]); //</debug> me.initConfig(config); column = me.column; me.columnListeners = column.on('destroy', me.destroy, me, { destroyable: true }); me.dataIndex = me.dataIndex || column.dataIndex; me.task = new Ext.util.DelayedTask(me.setValue, me); }, /** * Destroys this filter by purging any event listeners, and removing any menus. */ destroy: function() { var me = this; if (me.task) { me.task.cancel(); me.task = null; } me.columnListeners = me.columnListeners.destroy(); me.grid = me.menu = Ext.destroy(me.menu); me.callParent(); }, addStoreFilter: function (filter) { var filters = this.getGridStore().getFilters(), idx = filters.indexOf(filter), existing = idx !== -1 ? filters.getAt(idx) : null; // If the filter being added doesn't exist in the collection we should add it. // But if there is a filter with the same id (indexOf tests for the same id), we should // check if the filter being added has the same properties as the existing one if (!existing || !Ext.util.Filter.isEqual(existing, filter)) { filters.add(filter); } }, createFilter: function (config, key) { var filter = new Ext.util.Filter(this.getFilterConfig(config, key)); filter.isGridFilter = true; return filter; }, // Note that some derived classes may need to do specific processing and will have its own version of this method // before calling parent (see the List filter). getFilterConfig: function(config, key) { config.id = this.getBaseIdPrefix(); if (!config.property) { config.property = this.dataIndex; } if (!config.root) { config.root = this.defaultRoot; } if (key) { config.id += '-' + key; } config.serializer = this.getSerializer(); return config; }, /** * @private * Creates the Menu for this filter. * @param {Object} config Filter configuration * @return {Ext.menu.Menu} */ createMenu: function () { this.menu = Ext.widget(this.getMenuConfig()); }, getActiveState: function (config, value) { // An `active` config must take precedence over a `value` config. var active = config.active; return (active !== undefined) ? active : value !== undefined; }, getBaseIdPrefix: function () { return this.filterIdPrefix + '-' + this.dataIndex; }, getMenuConfig: function () { return Ext.apply({}, this.getMenuDefaults()); }, getGridStore: function() { return this.grid.getStore(); }, getStoreFilter: function (key) { var id = this.getBaseIdPrefix(); if (key) { id += '-' + key; } return this.getGridStore().getFilters().get(id); }, /** * @private * Handler method called when there is a significant event on an input item. */ onValueChange: function (field, e) { var me = this, keyCode = e.getKey(), updateBuffer = me.updateBuffer, value; // Don't process tabs! if (keyCode === e.TAB) { return; } //<debug> if (!field.isFormField) { Ext.raise('`field` should be a form field instance.'); } //</debug> if (field.isValid()) { if (keyCode === e.RETURN) { me.menu.hide(); return; } value = me.getValue(field); if (value === me.value) { return; } if (updateBuffer) { me.task.delay(updateBuffer, null, null, [value]); } else { me.setValue(value); } } }, /** * @private * @method preprocess * Template method to be implemented by all subclasses that need to perform * any operations before the column filter has finished construction. * @template */ preprocess: Ext.emptyFn, removeStoreFilter: function (filter) { this.getGridStore().getFilters().remove(filter); }, /** * @private * @method getValue * Template method to be implemented by all subclasses that is to * get and return the value of the filter. * @return {Object} The 'serialized' form of this filter * @template */ getValue: Ext.emptyFn, /** * @private * @method setValue * Template method to be implemented by all subclasses that is to * set the value of the filter and fire the 'update' event. * @param {Object} data The value to set the filter * @template */ /** * Sets the status of the filter and fires the appropriate events. * @param {Boolean} active The new filter state. */ setActive: function (active) { var me = this, menuItem = me.owner.activeFilterMenuItem, filterCollection; if (me.active !== active) { me.active = active; filterCollection = me.getGridStore().getFilters(); filterCollection.beginUpdate(); if (active) { me.activate(); } else { me.deactivate(); } filterCollection.endUpdate(); // Make sure we update the 'Filters' menu item. if (menuItem && menuItem.activeFilter === me) { menuItem.setChecked(active); } me.setColumnActive(active); // TODO: fire activate/deactivate } }, setColumnActive: function(active) { this.column[active ? 'addCls' : 'removeCls'](this.owner.filterCls); }, showMenu: function (menuItem) { var me = this; if (!me.menu) { me.createMenu(); } menuItem.activeFilter = me; menuItem.setMenu(me.menu, false); menuItem.setChecked(me.active); // Disable the menu if filter.disabled explicitly set to true. menuItem.setDisabled(me.disabled === true); me.activate(/*showingMenu*/ true); }, updateStoreFilter: function () { this.getGridStore().getFilters().notify('endupdate'); }});