/** * Base class for a filter type used by the {@link Ext.grid.plugin.FilterBar plugin} */Ext.define('Ext.grid.plugin.filterbar.filters.Base', { mixins: [ 'Ext.mixin.Factoryable' ], requires: [ 'Ext.field.Text', 'Ext.grid.plugin.filterbar.Operator' ], factoryConfig: { type: 'grid.filterbar' }, $configPrefixed: false, $configStrict: false, config: { /** * @cfg {Object} fieldDefaults * * Default settings for the field that is created for this filter */ fieldDefaults: { xtype: 'textfield', hideLabel: true, selectOnFocus: true, autoComplete: false }, /** * @cfg {Ext.grid.Grid} grid * * The reference to the grid */ grid: null, /** * @cfg {Ext.grid.column.Column} column * * The reference to the grid column */ column: null, field: null, /** * @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, /** * @cfg {String} operator * * Default operator for this filter */ operator: '==', /** * @cfg {String[]} operators * * Array of operators available for this filter. Check {@link Ext.util.Filter} for * a list of operators supported by store filtering. This list can be extended * with your own operators. */ operators: null, /** * @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. */ serializer: null, fieldListeners: { change: 'onValueChange', operatorchange: 'onOperatorChange', initialize: 'onFieldRender', specialkey: 'onFieldSpecialKey' } }, /** * @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, /** * The prefix for id's used to track stateful Store filters. * @private */ filterIdPrefix: Ext.baseCSSPrefix + 'gridfilter', isGridFilter: true, defaultRoot: 'data', fieldCls: Ext.baseCSSPrefix + 'grid-filter-base', /** * Initializes the filter given its configuration. * @param {Object} config */ constructor: function(config) { var me = this; // Calling Base constructor is very desirable for testing //<debug> me.callParent([config]); //</debug> me.initConfig(config); me.initFilter(config); me.createField(); me.task = new Ext.util.DelayedTask(me.setValue, me); }, destroy: function() { var me = this; if (me.task) { me.task.cancel(); me.task = null; } me.setColumn(null); me.setField(null); me.setGrid(null); me.callParent(); }, initFilter: Ext.emptyFn, createField: function() { this.setField(Ext.widget(this.getFieldConfig())); }, getFieldConfig: function() { var me = this, column = me.getColumn(), filter = me.filter, config = {}; if (column) { config.width = column.getWidth(); config.hidden = column.getHidden(); } if (filter) { config.value = filter.getValue(); } return Ext.apply(config, me.getFieldDefaults()); }, 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; }, 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; }, 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; }, getGridStore: function() { return this.grid.getStore(); }, getStoreFilter: function(key) { var me = this, filters = me.getGridStore().getFilters(false), id = me.getBaseIdPrefix(), filter, f, len, i, oldKey; if (key) { id += '-' + key; } if (filters) { filter = filters.get(id); if (!filter) { // let's search by dataIndex len = filters.length; for (i = 0; i < len; i++) { f = filters.items[i]; if (f.getProperty() === me.dataIndex) { filter = f; // we need to change the id and reindex the filters oldKey = filters.getKey(f); filter.setId(id); filters.itemChanged(f, ['id'], oldKey); break; } } } } return filter; }, /** * Sets the status of the filter and fires the appropriate events. * @param {Boolean} active The new filter state. */ setActive: function(active) { var me = this, filterCollection; if (me.active !== active) { me.active = active; me.changingFilters = true; filterCollection = me.getGridStore().getFilters(); filterCollection.beginUpdate(); if (active) { me.activate(); } else { me.deactivate(); } filterCollection.endUpdate(); me.changingFilters = false; me.setColumnActive(active); me.grid.fireEventArgs(active ? 'filteractivate' : 'filterdeactivate', [me, me.column]); } }, activate: Ext.emptyFn, deactivate: Ext.emptyFn, setColumnActive: function(active) { this.column[active ? 'addCls' : 'removeCls'](this.owner.filterCls); }, /** * @private * Handler method called when there is a significant event on an input item. */ onValueChange: function(field, value) { var me = this, updateBuffer = me.updateBuffer; if (field.isValid()) { if (value === me.value) { return; } if (updateBuffer) { me.task.delay(updateBuffer, null, null, [value]); } else { me.setValue(value); } } }, onOperatorChange: function(field, operator) { var value = field.getValue(), emptyOp = (operator === 'empty' || operator === 'nempty'); this.setOperator(operator); if (!Ext.isEmpty(value) && !emptyOp) { this.setValue(value); } }, removeStoreFilter: function(filter) { this.getGridStore().getFilters().remove(filter); }, updateColumn: function(column) { var me = this; me.columnListeners = Ext.destroy(me.columnListeners); if (column) { if (!me.getDataIndex()) { me.setDataIndex(column.getDataIndex()); } me.columnListeners = column.on({ destroy: me.destroy, scope: me, destroyable: true }); } }, updateField: function(field, oldField) { var me = this; if (!this.grid.isDestroying) { Ext.destroy(oldField); } if (field) { field.addCls(me.fieldCls); if (field.isFormField) { field.addPlugin({ type: 'operator', operator: me.getOperator(), operators: me.getOperators() }); field.on(Ext.apply({ scope: me }, me.getFieldListeners())); } } }, updateOperator: function(operator) { var me = this, field = me.getField(); if (!me.isConfiguring && me.filter) { me.filter.setOperator(operator); if (field && field.setOperator) { field.setOperator(operator); } } }, updateStoreFilter: function() { this.getGridStore().getFilters().notify('endupdate'); }, onFieldRender: function() { this.resizeField(); }, resizeField: function(width) { var field = this.getField(), column = this.getColumn(); if (field && field.rendered && column && column.rendered) { field.flex = null; if (width != null) { field.setWidth(width); } else { field.setWidth(column.getWidth()); } } }, resetFilter: Ext.emptyFn, /** * Handles "specialkey" event of filter editor and clears it when user hits ESC. * * @param {*} field * @param {*} e * @private */ onFieldSpecialKey: function(field, e) { if ((e.getKey() === e.ESC) && !Ext.isEmpty(field.getValue())) { if (field.clearValue) { field.clearValue(); } else { field.setValue(null); } } }});