Threaded View

  1. #1
    Sencha User
    Join Date
    Jun 2010
    Location
    Buenos Aires, Argentina
    Posts
    213
    Vote Rating
    9
    ldonofrio will become famous soon enough

      3  

    Default Ext.ux.grid.FilterBar plugin

    Ext.ux.grid.FilterBar plugin


    v1.1 on Sencha Market. Supports operators for number and date filters
    https://market.sencha.com/users/68/addons/95


    UPDATED CODE FOR 4.1 (2012-06-01):
    Code:
    /**
     * Plugin that enable filters on the grid header.<br>
     * The header filters are integrated with new Ext4 <code>Ext.data.Store</code> filters.<br>
     *
     * @author Ing. Leonardo D'Onofrio (leonardo_donofrio at hotmail.com)
     * @version 1.0 beta 1 (supports 4.1 beta)
     * @updated 2011-10-18 by Ing. Leonardo D'Onofrio (leonardo_donofrio at hotmail.com)
     * Support renderHidden config option, isVisible(), and setVisible() methods (added getFilterBar() method to the grid)
     * Fix filter bug that append filters to Store filters MixedCollection
     * Fix layout broken on initial render when columns have width property
     * @updated 2011-10-24 by Ing. Leonardo D'Onofrio (leonardo_donofrio at hotmail.com)
     * Rendering code rewrited, filters are now rendered inside de column headers, this solves scrollable grids issues, now scroll, columnMove, and columnHide/Show is handled by the headerCt
     * Support showClearButton config option, render a clear Button for each filter to clear the applied filter (uses Ext.ux.form.field.ClearButton plugin)
     * Added clearFilters() method.
     * @updated 2011-10-25 by Ing. Leonardo D'Onofrio (leonardo_donofrio at hotmail.com)
     * Allow preconfigured filter's types and auto based on store field data types
     * Auto generated stores for combo and list filters (local collect or server in autoStoresRemoteProperty response property)
     * @updated 2011-10-26 by Ing. Leonardo D'Onofrio (leonardo_donofrio at hotmail.com)
     * Completelly rewriten to support reconfigure filters on grid's reconfigure
     * Supports clearAll and showHide buttons rendered in an actioncolumn or in new generetad small column
     * @updated 2011-10-27 by Ing. Leonardo D'Onofrio (leonardo_donofrio at hotmail.com)
     * Added support to 4.0.7 (columnresize not fired correctly on this build)
     * @updated 2011-11-02 by Ing. Leonardo D'Onofrio (leonardo_donofrio at hotmail.com)
     * Filter on ENTER
     * Defaults submitFormat on date filter to 'Y-m-d' and use that in applyFilters for local filtering
     * Added null value support on combo and list filters (autoStoresNullValue and autoStoresNullText)
     * Fixed some combo styles
     * @updated 2011-11-10 by Ing. Leonardo D'Onofrio (leonardo_donofrio at hotmail.com)
     * Parse and show initial filters applied to the store (only property -> value filters, filterFn is unsuported)
     * @updated 2011-12-12 by Ing. Leonardo D'Onofrio (leonardo_donofrio at hotmail.com)
     * Extends AbstractPlugin and use Observable as a Mixin
     * Yes/No localization on constructor
     * @updated 2012-01-03 by Ing. Leonardo D'Onofrio (leonardo_donofrio at hotmail.com)
     * Added some support for 4.1 beta
     * @updated 2012-01-05 by Ing. Leonardo D'Onofrio (leonardo_donofrio at hotmail.com)
     * 99% support for 4.1 beta. Seems to be working
     * @updated 2012-03-22 by Ing. Leonardo D'Onofrio (leonardo_donofrio at hotmail.com)
     * Fix focusFirstField method
     * Allow to specify listConfig in combo filter
     * Intercept column's setPadding for all columns except actionColumn or extraColumn (fix checkBoxSelectionModel header)
     * @updated 2012-05-07 by Ing. Leonardo D'Onofrio (leonardo_donofrio at hotmail.com)
     * Fully tested on 4.1 final
     * @updated 2012-05-31 by Ing. Leonardo D'Onofrio (leonardo_donofrio at hotmail.com)
     * Fix padding issue on checkbox column
    */
    
    Ext.define('Ext.ux.grid.FilterBar', {
        extend: 'Ext.AbstractPlugin',
        alias: 'plugin.filterbar',
        uses: [
            'Ext.window.MessageBox',
            'Ext.ux.form.field.ClearButton',
            'Ext.container.Container',
            'Ext.util.DelayedTask',
            'Ext.layout.container.HBox',
            'Ext.data.ArrayStore',
            'Ext.button.Button',
            'Ext.form.field.Text',
            'Ext.form.field.Number',
            'Ext.form.field.Date',
            'Ext.form.field.ComboBox'
        ],
        mixins: {
            observable: 'Ext.util.Observable'
        },
    
        updateBuffer                : 800,                    // buffer time to apply filtering when typing/selecting
    
        columnFilteredCls            : Ext.baseCSSPrefix + 'column-filtered', // CSS class to apply to the filtered column header
    
        renderHidden                : true,                    // renders the filters hidden by default, use in combination with showShowHideButton
        showShowHideButton            : true,                    // add show/hide button in actioncolumn header if found, if not a new small column is created
        showHideButtonTooltipDo        : 'Show filter bar',    // button tooltip show
        showHideButtonTooltipUndo    : 'Hide filter bar',    // button tooltip hide
        showHideButtonIconCls        : 'filter',                // button iconCls
    
        showClearButton                : true,                    // use Ext.ux.form.field.ClearButton to allow user to clear each filter, the same as showShowHideButton
        showClearAllButton            : true,                    // add clearAll button in actioncolumn header if found, if not a new small column is created
        clearAllButtonIconCls        : 'clear-filters',         // css class with the icon of the clear all button
        clearAllButtonTooltip        : 'Clear all filters',    // button tooltip
    
        autoStoresRemoteProperty    : 'autoStores',            // if no store is configured for a combo filter then stores are created automatically, if remoteFilter is true then use this property to return arrayStores from the server
        autoStoresNullValue            : '###NULL###',            // value send to the server to expecify null filter
        autoStoresNullText            : '[empty]',            // NULL Display Text
        autoUpdateAutoStores        : false,                // if set to true combo autoStores are updated each time that a filter is applied
    
        boolTpl: {
            xtype: 'combo',
            queryMode: 'local',
            forceSelection: true,
            triggerAction: 'all',
            editable: false,
            store: [
                [1, 'Yes'],
                [0, 'No']
            ],
            operator: 'eq'
        },
        dateTpl: {
            xtype: 'datefield',
            editable: true,
            submitFormat: 'Y-m-d',
            operator: 'eq'
        },
        floatTpl: {
            xtype: 'numberfield',
            allowDecimals: true,
            minValue: 0,
            hideTrigger: true,
            keyNavEnabled: false,
            mouseWheelEnabled: false,
            operator: 'eq'
        },
        intTpl: {
            xtype: 'numberfield',
            allowDecimals: false,
            minValue: 0,
            operator: 'eq'
        },
        stringTpl: {
            xtype: 'textfield',
            operator: 'like'
        },
        comboTpl: {
            xtype: 'combo',
            queryMode: 'local',
            forceSelection: true,
            editable: false,
            triggerAction: 'all',
            operator: 'eq'
        },
        listTpl: {
            xtype: 'combo',
            queryMode: 'local',
            forceSelection: true,
            editable: false,
            triggerAction: 'all',
            multiSelect: true,
            operator: 'in'
        },
    
        constructor: function() {
            var me = this;
    
            me.boolTpl.store[0][1] = Ext.htmlDecode(Ext.MessageBox.buttonText.yes);
            me.boolTpl.store[1][1] = Ext.htmlDecode(Ext.MessageBox.buttonText.no);
    
            me.mixins.observable.constructor.call(me);
            me.callParent(arguments);
        },
    
        // private
        init: function(grid) {
            var me = this;
    
            grid.on({
                columnresize: me.resizeContainer,
                columnhide: me.resizeContainer,
                columnshow: me.resizeContainer,
                beforedestroy: me.unsetup,
                reconfigure: me.resetup,
                scope: me
            });
    
            grid.addEvents('filterupdated');
    
            Ext.apply(grid, {
                filterBar: me,
                getFilterBar: function() {
                    return this.filterBar;
                }
            });
    
            me.setup(grid);
        },
    
        // private
        setup: function(grid) {
            var me = this;
    
            me.grid = grid;
            me.visible = !me.renderHidden;
            me.autoStores = Ext.create('Ext.util.MixedCollection');
            me.autoStoresLoaded = false;
            me.columns = Ext.create('Ext.util.MixedCollection');
            me.containers = Ext.create('Ext.util.MixedCollection');
            me.fields = Ext.create('Ext.util.MixedCollection');
            me.actionColumn = me.grid.down('actioncolumn') || me.grid.down('actioncolumnpro');
            me.extraColumn = null;
            me.clearAllEl = null;
            me.showHideEl = null;
            me.task = Ext.create('Ext.util.DelayedTask');
            me.filterArray = [];
    
            me.overrideProxy();
            me.parseFiltersConfig();     // sets me.columns and me.autoStores
            me.parseInitialFilters();   // sets me.filterArray with the store previous filters if any (adds operator and type if missing)
            me.renderExtraColumn();     // sets me.extraColumn if applicable
    
            // renders the filter's bar
            if (grid.rendered) {
                me.renderFilterBar(grid);
            } else {
                grid.on('afterrender', me.renderFilterBar, me, { single: true });
            }
        },
    
        // private
        unsetup: function(grid) {
            var me = this;
    
            if (me.autoStores.getCount()) {
                me.grid.store.un('load', me.fillAutoStores, me);
            }
    
            me.autoStores.each(function(item) {
                Ext.destroy(item);
            });
            me.autoStores.clear();
            me.autoStores = null;
            me.columns.each(function(column) {
                if (column.rendered) {
                    if(column.getEl().hasCls(me.columnFilteredCls)) {
                        column.getEl().removeCls(me.columnFilteredCls);
                    }
                }
            }, me);
            me.columns.clear();
            me.columns = null;
            me.fields.each(function(item) {
                Ext.destroy(item);
            });
            me.fields.clear();
            me.fields = null;
            me.containers.each(function(item) {
                Ext.destroy(item);
            });
            me.containers.clear();
            me.containers = null;
            if (me.clearAllEl) {
                Ext.destroy(me.clearAllEl);
                me.clearAllEl = null;
            }
            if (me.showHideEl) {
                Ext.destroy(me.showHideEl);
                me.showHideEl = null;
            }
            if (me.extraColumn) {
                me.grid.headerCt.items.remove(me.extraColumn);
                Ext.destroy(me.extraColumn);
                me.extraColumn = null;
            }
            me.task = null;
            me.filterArray = null;
        },
    
        // private
        resetup: function(grid) {
            var me = this;
    
            me.unsetup(grid);
            me.setup(grid);
        },
    
        // private
        overrideProxy: function() {
            var me = this;
    
            // override encodeFilters to append type and operator in remote filtering
            Ext.apply(me.grid.store.proxy, {
                encodeFilters: function(filters) {
                    var min = [],
                        length = filters.length,
                        i = 0;
    
                    for (; i < length; i++) {
                        min[i] = {
                            property: filters[i].property,
                            value   : filters[i].value
                        };
                        if (filters[i].type) {
                            min[i].type = filters[i].type;
                        }
                        if (filters[i].operator) {
                            min[i].operator = filters[i].operator;
                        }
                    }
                    return this.applyEncoding(min);
                }
            });
        },
    
        // private
        parseFiltersConfig: function() {
            var me = this;
            var columns = this.grid.headerCt.getGridColumns(true);
            me.columns.clear();
            me.autoStores.clear();
            Ext.each(columns, function(column) {
                if (column.filter) {
                    if (column.filter === true || column.filter === 'auto') { // automatic types configuration (store based)
                        var type = me.grid.store.model.prototype.fields.get(column.dataIndex).type.type;
                        if (type == 'auto') type = 'string';
                        column.filter = type;
                    }
                    if (Ext.isString(column.filter)) {
                        column.filter = {
                            type: column.filter // only set type to then use templates
                        };
                    }
                    if (column.filter.type) {
                        column.filter = Ext.applyIf(column.filter, me[column.filter.type + 'Tpl']); // also use templates but with user configuration
                    }
    
                    if (column.filter.xtype == 'combo' && !column.filter.store) {
                        column.autoStore = true;
                        column.filter.store = Ext.create('Ext.data.ArrayStore', {
                            fields: [{
                                name: 'text'
                            },{
                                name: 'id'
                            }]
                        });
                        me.autoStores.add(column.dataIndex, column.filter.store);
                        column.filter = Ext.apply(column.filter, {
                            displayField: 'text',
                            valueField: 'id'
                        });
                    }
    
                    if (!column.filter.type) {
                        switch(column.filter.xtype) {
                            case 'combo':
                                column.filter.type = (column.filter.multiSelect ? 'list' : 'combo');
                                break;
                            case 'datefield':
                                column.filter.type = 'date';
                                break;
                            case 'numberfield':
                                column.filter.type = (column.filter.allowDecimals ? 'float' : 'int');
                                break;
                            default:
                                column.filter.type = 'string'
                        }
                    }
    
                    if (!column.filter.operator) {
                        column.filter.operator = me[column.filter.type + 'Tpl'].operator;
                    }
                    me.columns.add(column.dataIndex, column);
                }
            }, me);
            if (me.autoStores.getCount()) {
                if (me.grid.store.getCount() > 0) {
                    me.fillAutoStores(me.grid.store);
                }
                if (me.grid.store.remoteFilter) {
                    var autoStores = [];
                    me.autoStores.eachKey(function(key, item) {
                        autoStores.push(key);
                    });
                    me.grid.store.proxy.extraParams = me.grid.store.proxy.extraParams || {};
                    me.grid.store.proxy.extraParams[me.autoStoresRemoteProperty] = autoStores;
                }
                me.grid.store.on('load', me.fillAutoStores, me);
            }
        },
    
        // private
        fillAutoStores: function(store) {
            var me = this;
    
            if (!me.autoUpdateAutoStores && me.autoStoresLoaded) return;
    
            me.autoStores.eachKey(function(key, item) {
                var field = me.fields.get(key);
                if (field) {
                    field.suspendEvents();
                    var fieldValue = field.getValue();
                }
                if (!store.remoteFilter) { // values from local store
                    var data = store.collect(key, true, false).sort();
                    var records = [];
                    Ext.each(data, function(txt) {
                        if (Ext.isEmpty(txt)) {
                            Ext.Array.insert(records, 0, [{
                                text: me.autoStoresNullText,
                                id: me.autoStoresNullValue
                            }]);
                        } else {
                            records.push({
                                text: txt,
                                id: txt
                            });
                        }
                    });
                    item.loadData(records);
                } else { // values from server
                    if (store.proxy.reader.rawData[me.autoStoresRemoteProperty]) {
                        var data = store.proxy.reader.rawData[me.autoStoresRemoteProperty];
                        if (data[key]) {
                            var records = [];
                            Ext.each(data[key].sort(), function(txt) {
                                if (Ext.isEmpty(txt)) {
                                    Ext.Array.insert(records, 0, [{
                                        text: me.autoStoresNullText,
                                        id: me.autoStoresNullValue
                                    }]);
                                } else {
                                    records.push({
                                        text: txt,
                                        id: txt
                                    });
                                }
                            });
                            item.loadData(records);
                        }
                    }
                }
                if (field) {
                    field.setValue(fieldValue);
                    field.resumeEvents();
                }
            }, me);
            me.autoStoresLoaded = true;
            if (me.grid.store.remoteFilter && !me.autoUpdateAutoStores) {
                delete me.grid.store.proxy.extraParams[me.autoStoresRemoteProperty];
            }
        },
    
        // private
        parseInitialFilters: function() {
            var me = this;
    
            me.filterArray = [];
            me.grid.store.filters.each(function(filter) {
                // try to parse initial filters, for now filterFn is unsuported
                if (filter.property && !Ext.isEmpty(filter.value) && me.columns.get(filter.property)) {
                    if (!filter.type) filter.type = me.columns.get(filter.property).filter.type;
                    if (!filter.operator) filter.operator = me.columns.get(filter.property).filter.operator;
                    me.filterArray.push(filter);
                }
            }, me);
        },
    
        // private
        renderExtraColumn: function() {
            var me = this;
    
            if (me.columns.getCount() && !me.actionColumn && (me.showClearAllButton || me.showShowHideButton)) {
                var extraColumnCssClass = Ext.baseCSSPrefix + 'filter-bar-extra-column-hack';
                if (!document.getElementById(extraColumnCssClass)) {
                    var style = document.createElement('style');
                    var css = 'tr.' + Ext.baseCSSPrefix + 'grid-row td.' + extraColumnCssClass + ' { background-color: #ffffff !important; border-color: #ffffff !important; }';
                    style.setAttribute('type', 'text/css');
                    style.setAttribute('id', extraColumnCssClass);
                    document.body.appendChild(style);
                    if (style.styleSheet) {       // IE
                        style.styleSheet.cssText = css;
                    } else {                    // others
                        var cssNode = document.createTextNode(css);
                        style.appendChild(cssNode);
                    }
                }
                me.extraColumn = Ext.create('Ext.grid.column.Column', {
                    draggable: false,
                    hideable: false,
                    menuDisabled: true,
                    sortable: false,
                    resizable: false,
                    fixed: true,
                    width: 28,
                    minWidth: 28,
                    maxWidth: 28,
                    header: '&nbsp;',
                    tdCls: extraColumnCssClass
                });
                me.grid.headerCt.add(me.extraColumn);
            }
        },
    
        // private
        renderFilterBar: function(grid) {
            var me = this;
    
            me.containers.clear();
            me.fields.clear();
            me.columns.eachKey(function(key, column) {
                var listConfig = column.filter.listConfig || {};
                listConfig = Ext.apply(listConfig, {
                    style: 'border-top-width: 1px'
                });
                var field = Ext.widget(column.filter.xtype, Ext.apply(column.filter, {
                    dataIndex: key,
                    flex: 1,
                    margin: 0,
                    fieldStyle: 'border-left-width: 0px; border-bottom-width: 0px;',
                    listConfig: listConfig,
                    preventMark: true,
                    enableKeyEvents: true,
                    listeners: {
                        change: me.applyFilters,
                        keypress: function(txt, e){
                            if(e.getCharCode() == 13) {
                                me.applyFilters(txt, txt.getValue());
                                e.stopEvent();
                            }
                        },
                        scope: me
                    },
                    plugins: (!me.showClearButton ? [] : [{
                        ptype: 'clearbutton'
                    }])
                }));
                me.fields.add(column.dataIndex, field);
                var container = Ext.create('Ext.container.Container', {
                    dataIndex: key,
                    layout: 'hbox',
                    bodyStyle: 'background-color: "transparent";',
                    width: column.getWidth(),
                    items: [field],
                    listeners: {
                        scope: me,
                        element: 'el',
                        mousedown: function(e) { e.stopPropagation(); },
                        click: function(e) { e.stopPropagation(); },
                        dblclick: function(e) { e.stopPropagation(); }/*,
                        keydown: function(e) { e.stopPropagation(); },
                        keypress: function(e) { e.stopPropagation(); },
                        keyup: function(e) { e.stopPropagation(); }*/
                    }
                });
                me.containers.add(column.dataIndex, container);
                container.render(Ext.get(column.id));
            }, me);
            var excludedCols = [];
            if (me.actionColumn) excludedCols.push(me.actionColumn.id);
            if (me.extraColumn) excludedCols.push(me.extraColumn.id);
            Ext.each(me.grid.headerCt.getGridColumns(true), function(column) {
                if (!Ext.Array.contains(excludedCols, column.id)) {
                    column.setPadding = Ext.Function.createInterceptor(column.setPadding, function(h) {
                        if (column.text == ' ') { //checkbox column
                            this.titleEl.setStyle({
                                paddingTop: '4px'
                            });
                        }
                        return false;
                    });
                }
            });
    
    
            me.setVisible(me.visible);
    
            me.renderButtons();
    
            me.showInitialFilters();
        },
    
        //private
        renderButtons: function() {
            var me = this;
    
            if (me.showShowHideButton && me.columns.getCount()) {
                var column = me.actionColumn || me.extraColumn;
                var buttonEl = column.el.first().first();
                me.showHideEl = Ext.get(Ext.core.DomHelper.append(buttonEl, {
                    tag: 'div',
                    style: 'position: absolute; width: 16px; height: 16px; top: 3px; cursor: pointer; left: ' + parseInt((column.el.getWidth() - 16) / 2) + 'px',
                    cls: me.showHideButtonIconCls,
                    'data-qtip': (me.renderHidden ? me.showHideButtonTooltipDo : me.showHideButtonTooltipUndo)
                }));
                me.showHideEl.on('click', function() {
                    me.setVisible(!me.isVisible());
                    me.showHideEl.set({
                        'data-qtip': (!me.isVisible() ? me.showHideButtonTooltipDo : me.showHideButtonTooltipUndo)
                    });
                });
            }
    
            if (me.showClearAllButton && me.columns.getCount()) {
                var column = me.actionColumn || me.extraColumn;
                var buttonEl = column.el.first().first();
                me.clearAllEl = Ext.get(Ext.core.DomHelper.append(buttonEl, {
                    tag: 'div',
                    style: 'position: absolute; width: 16px; height: 16px; top: 25px; cursor: pointer; left: ' + parseInt((column.el.getWidth() - 16) / 2) + 'px',
                    cls: me.clearAllButtonIconCls,
                    'data-qtip': me.clearAllButtonTooltip
                }));
    
                me.clearAllEl.hide();
                me.clearAllEl.on('click', function() {
                    me.clearFilters();
                });
            }
        },
    
        // private
        showInitialFilters: function() {
            var me = this;
    
            Ext.each(me.filterArray, function(filter) {
                var column = me.columns.get(filter.property);
                var field = me.fields.get(filter.property);
                if(!column.getEl().hasCls(me.columnFilteredCls)) {
                    column.getEl().addCls(me.columnFilteredCls);
                }
                field.suspendEvents();
                field.setValue(filter.value);
                field.resumeEvents();
            });
    
            if (me.filterArray.length && me.showClearAllButton) {
                me.clearAllEl.show({duration: 1000});
            }
        },
    
        // private
        resizeContainer: function(headerCt, col) {
            var me = this;
            var dataIndex = col.dataIndex;
    
            if (!dataIndex) return;
            var item = me.containers.get(dataIndex);
            if (item && item.rendered) {
                var itemWidth = item.getWidth();
                var colWidth = me.columns.get(dataIndex).getWidth();
                if (itemWidth != colWidth) {
                    item.setWidth(me.columns.get(dataIndex).getWidth());
                    item.doLayout();
                }
            }
        },
    
        // private
        applyFilters: function(field, newVal) {
            var me = this;
    
            if (!field.isValid()) {
                return;
            }
            var grid = me.grid;
            var column = me.columns.get(field.dataIndex);
            newVal = (grid.store.remoteFilter ? field.getSubmitValue() : newVal);
            me.task.delay(me.updateBuffer, function() {
                if (Ext.isArray(newVal) && newVal.length == 0) {
                    newVal = '';
                }
                var myIndex = -1;
                Ext.each(me.filterArray, function(item2, index, allItems) {
                    if(item2.property === column.dataIndex) {
                        myIndex = index;
                    }
                });
                if(myIndex != -1) {
                    me.filterArray.splice(myIndex, 1);
                }
                if(!Ext.isEmpty(newVal)) {
                    if (!grid.store.remoteFilter) {
                        var filterFn;
                        switch(column.filter.operator) {
                            case 'eq':
                                filterFn = function(item) {
                                    if (column.filter.type == 'date') {
                                        return Ext.Date.clearTime(item.get(column.dataIndex), true).getTime() == Ext.Date.clearTime(newVal, true).getTime();
                                    } else {
                                        return (Ext.isEmpty(item.get(column.dataIndex)) ? me.autoStoresNullValue : item.get(column.dataIndex)) == (Ext.isEmpty(newVal) ? me.autoStoresNullValue : newVal);
                                    }
                                };
                                break;
                            case 'like':
                                filterFn = function(item) {
                                    var re = new RegExp(newVal, 'i');
                                    return re.test(item.get(column.dataIndex));
                                };
                                break;
                            case 'in':
                                filterFn = function(item) {
                                    var re = new RegExp('^' + newVal.join('|') + '$', 'i');
                                    return re.test((Ext.isEmpty(item.get(column.dataIndex)) ? me.autoStoresNullValue : item.get(column.dataIndex)));
                                };
                                break;
                        }
                        me.filterArray.push(Ext.create('Ext.util.Filter', {
                            property: column.dataIndex,
                            filterFn: filterFn,
                            me: me
                        }));
                    } else {
                        me.filterArray.push(Ext.create('Ext.util.Filter', {
                            property: column.dataIndex,
                            value: newVal,
                            type: column.filter.type,
                            operator: column.filter.operator
                        }));
                    }
                    if(!column.getEl().hasCls(me.columnFilteredCls)) {
                        column.getEl().addCls(me.columnFilteredCls);
                    }
                } else {
                    if(column.getEl().hasCls(me.columnFilteredCls)) {
                        column.getEl().removeCls(me.columnFilteredCls);
                    }
                }
                grid.store.currentPage = 1;
                if(me.filterArray.length > 0) {
                    if (!grid.store.remoteFilter) grid.store.clearFilter();
                    grid.store.filters.clear();
                    grid.store.filter(me.filterArray);
                    if (me.clearAllEl) {
                        me.clearAllEl.show({duration: 1000});
                    }
                } else {
                    grid.store.clearFilter();
                    if (me.clearAllEl) {
                        me.clearAllEl.hide({duration: 1000});
                    }
                }
                if (!grid.store.remoteFilter && me.autoUpdateAutoStores) {
                    me.fillAutoStores();
                }
                me.fireEvent('filterupdated', me.filterArray);
            }, me);
        },
    
        //private
        getFirstField: function() {
            var me = this,
                field = undefined;
    
            Ext.each(me.grid.headerCt.getGridColumns(true), function(col) {
                if (col.filter) {
                    field = me.fields.get(col.dataIndex);
                    return false;
                }
            });
    
            return field;
        },
    
        //private
        focusFirstField: function() {
            var me = this;
    
            var field = me.getFirstField();
    
            if (field) {
                field.focus(false, 200);
            }
        },
    
        clearFilters: function() {
            var me = this;
    
            if (me.filterArray.length == 0) return;
            me.filterArray = [];
            me.fields.eachKey(function(key, field) {
                field.suspendEvents();
                field.reset();
                field.resumeEvents();
                var column = me.columns.get(key);
                if(column.getEl().hasCls(Ext.baseCSSPrefix + 'column-filtered')) {
                    column.getEl().removeCls(Ext.baseCSSPrefix + 'column-filtered');
                }
            }, me);
            me.grid.store.clearFilter();
            if (me.clearAllEl) {
                me.clearAllEl.hide({duration: 1000});
            }
    
            me.fireEvent('filterupdated', me.filterArray);
        },
    
        isVisible: function() {
            var me = this;
    
            return me.visible;
        },
    
        setVisible: function(visible) {
            var me = this;
    
            me.containers.each(function(item) {
                item.setVisible(visible);
            });
    
            if (visible) {
                me.focusFirstField();
            }
            me.grid.headerCt.doLayout();
            me.visible = visible;
        }
    });

    None of the 4.0 filter extensións seems to work correctly in all scenarios, so i've decided to create my own.
    Inially i use as a base this:
    http://www.sencha.com/forum/showthre...d.HeaderFIlter
    and this
    http://www.sencha.com/forum/showthre...Header-Filters

    Finally last week i've rewriten the code to support all escenarios (remoteFilter true and false, grid reconfigure, etc).

    I've added a lot of features like, autoConfigurated fields, autoLoaded combo stores, clearButton, clearAllButton, show/hide button, etc (See full features in the code comments).

    Plugin Code and test example attached as zip.

    Regards

    Leonardo
    Last edited by ldonofrio; 27 Sep 2012 at 6:37 AM. Reason: v1.1 supports operators

Turkiyenin en sevilen filmlerinin yer aldigi xnxx internet sitemiz olan ve porn sex tarzi bir site olan mobil porno izle sitemiz gercekten dillere destan bir durumda herkesin sevdigi bir site olarak tarihe gececege benziyor. Sitenin en belirgin ozelliklerinden birisi de Turkiyede gercekten kaliteli ve muntazam, duzenli porno izle siteleri olmamasidir. Bu yuzden iste. Ayrica en net goruntu kalitesine sahip adresinde yayinlanmaktadir. Mesela diğer sitelerimizden bahsedecek olursak, en iyi hd porno video arşivine sahip bir siteyiz. "The Best anal porn videos and slut anus, big asses movies set..." hd porno faketaxi