3 <title>Array Grid Example With Filter</title>
4 <link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css" />
5 <script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>
6 <script type="text/javascript" src="../../ext-all-debug.js"></script>
7 <link rel="stylesheet" type="text/css" href="grid-examples.css" />
8 <link rel="stylesheet" type="text/css" href="../examples.css" />
9 <style type="text/css">
10 .x-filter-condition td {
15 background: url(../shared/icons/fam/add.gif)!important;
19 background: url(../shared/icons/fam/bin.png)!important;
22 .condition-row-disabled {
23 background: url(../shared/icons/fam/delete.gif)!important;
26 .condition-row-enabled {
27 background: url(../shared/extjs/images/checked.gif)!important;
31 <script type="text/javascript">
32 Ext.ux.FilterCondition = Ext.extend(Ext.Container, {
40 cls: 'x-filter-condition'
43 Field: Ext.data.Record.create(['name', 'type']),
45 initComponent: function() {
46 this.fields = this.store.reader.recordType.prototype.fields;
47 this.fieldStore = new Ext.data.Store();
49 // Create a Store containing the field names and types
50 // in the passed Store.
51 this.fields.each(function(f) {
52 this.fieldStore.add(new this.Field(f))
55 // Create a Combo which allows selection of a field
56 this.fieldCombo = new Ext.form.ComboBox({
58 store: this.fieldStore,
65 select: this.onFieldSelect,
70 // Create a Combo which allows selection of a test
71 this.testCombo = new Ext.form.ComboBox({
75 store: ['<', '<=', '=', '!=', '>=', '>'],
77 select: this.onTestSelect,
82 // Inputs for each type of field. Hidden and shown as necessary
83 this.booleanInput = new Ext.form.Checkbox({
86 testFilter: function(rec) {
88 return (t == '=') || (t == '!=');
91 check: this.onTestValueChange,
95 this.intInput = new Ext.form.NumberField({
100 change: this.onTestValueChange,
104 this.floatInput = new Ext.form.NumberField({
108 change: this.onTestValueChange,
112 this.textInput = new Ext.form.TextField({
116 change: this.onTestValueChange,
120 this.dateInput = new Ext.form.DateField({
123 convertValue: function(d) {
127 change: this.onTestValueChange,
132 this.cls = 'x-filter-condition';
135 iconCls: 'delete-button',
136 handler: this.removeSelf,
138 tooltip: 'Remove this condition'
141 handler: this.toggleEnabled,
143 iconCls: 'condition-row-enabled',
144 tooltip: 'Enable/disable this condition'
145 }, this.fieldCombo, this.testCombo, this.booleanInput, this.intInput, this.floatInput, this.textInput, this.dateInput];
146 Ext.ux.FilterCondition.superclass.initComponent.apply(this, arguments);
149 removeSelf: function() {
150 var o = this.ownerCt;
151 o.remove(this, true);
155 toggleEnabled: function(b) {
156 b = Ext.get(b.el.query(b.buttonSelector));
158 b.removeClass('condition-row-disabled');
159 b.addClass('condition-row-enabled');
162 b.removeClass('condition-row-enabled');
163 b.addClass('condition-row-disabled');
169 this.fieldCombo.focus();
172 onDisable: function() {
173 for (var i = 0, it = this.items.items, l = it.length; i < l; i++) {
174 if (!(it[i] instanceof Ext.Button)) {
178 this.disabled = true;
179 this.fireEvent("change", this);
182 onEnable: function() {
183 for (var i = 0, it = this.items.items, l = it.length; i < l; i++) {
186 this.disabled = false;
187 this.fireEvent("change", this);
190 onFieldSelect: function(combo, rec, index) {
191 this.booleanInput.hide();
192 this.intInput.hide();
193 this.floatInput.hide();
194 this.textInput.hide();
195 this.dateInput.hide();
196 var t = rec.get('type');
197 if (t == 'boolean') {
198 this.booleanInput.show();
199 this.valueInput = this.booleanInput;
200 } else if (t == 'int') {
201 this.intInput.show();
202 this.valueInput = this.intInput;
203 } else if (t == 'float') {
204 this.floatInput.show();
205 this.valueInput = this.floatInput;
206 } else if (t == 'date') {
207 this.dateInput.show();
208 this.valueInput = this.dateInput;
210 this.textInput.show();
211 this.valueInput = this.textInput;
214 // Allow the value input to determine which test types are allowed.
215 var fn = this.valueInput.testFilter;
217 this.testCombo.store.filterBy(fn);
219 this.testCombo.store.clearFilter();
221 this.fireEvent("change", this);
224 onTestSelect: function(combo, rec, index) {
225 this.fireEvent("change", this);
228 onTestValueChange: function() {
229 this.fireEvent("change", this);
232 getValue: function() {
234 field: this.fieldCombo.getValue(),
235 test: this.testCombo.getValue(),
236 value: this.valueInput.getValue()
240 getFilterFunction: function() {
241 if (!this.filterFunction) {
242 this.filterFunction = this.filterFunctionImpl.createDelegate(this);
244 return this.filterFunction;
247 filterFunctionImpl: function(rec) {
248 var fieldValue = rec.get(this.fieldCombo.getValue());
249 var v = this.valueInput.getValue();
251 // If the field knows how to preprocess...
252 if (this.valueInput.convertValue) {
253 fieldValue = this.valueInput.convertValue(fieldValue);
254 v = this.valueInput.convertValue(v);
257 switch (this.testCombo.getValue()) {
259 return fieldValue < v;
262 return fieldValue <= v;
265 return fieldValue == v;
268 return fieldValue != v;
271 return fieldValue >= v;
274 return fieldValue > v;
279 isEmpty: function() {
280 return ((this.fieldCombo.getValue.length == 0) && (this.testCombo.getValue().length == 0)) || !this.valueInput;
284 Ext.ux.StoreFilter = Ext.extend(Ext.Panel, {
285 constructor: function(config) {
286 config = Ext.apply({}, {
289 padding: '10px 0px 10px 10px',
297 cls: 'x-condition-header',
301 'text-decoration': 'underline',
302 'font': 'bold small verdana',
303 'margin-bottom': '5px'
308 'padding-left': '5px'
313 'padding-left': '65px'
316 autoEl: {html: 'Field to test'}
319 autoEl: {html: 'Test type'}
321 autoEl: {html: 'Test value'}
323 }, this.addConditionButton = new Ext.Button({
324 iconCls: 'add-button',
325 handler: this.onAddConditionButtonClick,
327 tooltip: 'Add condition',
332 bbar: new Ext.Toolbar([
333 this.filterButton = new Ext.Button({
335 tooltip: 'Filter grid',
336 handler: this.doFilter,
339 this.clearFilterButton = new Ext.Button({
340 text: "Clear Filter",
341 tooltip: 'Clear filters',
342 handler: this.clearFilter,
347 Ext.ux.StoreFilter.superclass.constructor.call(this, config);
350 onAddConditionButtonClick: function() {
351 var c, j = this.items.getCount();
353 c = this.items.items[j - 2];
358 c = new Ext.ux.FilterCondition({store: this.store});
359 if (this.autoApply) {
360 c.on('change', this.doFilter, this)
362 this.insert(this.items.getCount() - 1, c);
364 this.addConditionButton.getEl().scrollIntoView(this.body);
368 doFilter: function() {
369 this.store.filterBy(this.getFilterFunction());
372 clearFilter: function() {
373 this.store.clearFilter();
376 getFilterFunction: function() {
377 if (!this.filterFunction) {
378 this.filterFunction = this.filterFunctionImpl.createDelegate(this);
380 return this.filterFunction;
383 filterFunctionImpl: function(rec) {
384 for (var i = 0, it = this.items.items, l = it.length; i < l; i++) {
386 if ((c instanceof Ext.ux.FilterCondition) && (!c.isEmpty()) && (!c.disabled)) {
387 var fn = c.getFilterFunction();
397 Ext.override(Ext.ToolTip, {
398 onTargetOver : function(e){
399 if(this.disabled || e.within(this.target.dom, true)){
402 var t = e.getTarget(this.delegate);
404 this.triggerElement = t;
405 this.clearTimer('hide');
406 this.targetXY = e.getXY();
410 onMouseMove : function(e){
411 var t = e.getTarget(this.delegate);
413 this.targetXY = e.getXY();
414 if (t === this.triggerElement) {
415 if(!this.hidden && this.trackMouse){
416 this.setPagePosition(this.getTargetXY());
420 this.lastActive = new Date(0);
421 this.onTargetOver(e);
423 } else if (!this.closable && this.isVisible()) {
428 this.clearTimer('dismiss');
429 this.lastActive = new Date();
430 delete this.triggerElement;
431 Ext.ToolTip.superclass.hide.call(this);
435 Ext.override(Ext.grid.GridView, {
436 initUI : function(grid){
437 // Buffer processing of rows so that multiple changes to the GridView
438 // only trigger this expensive operation when they are all done.
439 this.on("processrows", this.doProcessRows, this, {buffer: 100});
441 grid.on("headerclick", this.onHeaderClick, this);
443 if(grid.trackMouseOver){
444 grid.on("mouseover", this.onRowOver, this);
445 grid.on("mouseout", this.onRowOut, this);
449 processRows : function(startRow, skipStripe){
450 this.fireEvent('processrows', startRow, skipStripe);
454 doProcessRows: function(startRow, skipStripe) {
456 // Ensure the focus element doesn't get stranded outside the occupied area.
457 var h = this.mainBody.dom.offsetHeight;
458 if (parseInt(this.focusEl.dom.style.top) > h) {
459 this.focusEl.dom.style.top = h + 'px';
462 if(this.ds.getCount() < 1){
465 skipStripe = skipStripe || !this.grid.stripeRows;
466 startRow = startRow || 0;
467 var rows = this.getRows();
468 var cls = ' x-grid3-row-alt ';
469 for(var i = startRow, len = rows.length; i < len; i++){
473 var isAlt = ((i+1) % 2 == 0);
474 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
479 row.className += " x-grid3-row-alt";
481 row.className = row.className.replace("x-grid3-row-alt", "");
488 Ext.onReady(function(){
490 Ext.QuickTips.init();
493 ['Alcoa Inc',29.01,0.42,1.47,'9/1 12:00am'],
494 ['Altria Group Inc',83.81,0.28,0.34,'9/1 12:00am'],
495 ['American Express Company',52.55,0.01,0.02,'9/1 12:00am'],
496 ['American International Group, Inc.',64.13,0.31,0.49,'9/1 12:00am'],
497 ['AT&T Inc.',31.61,-0.48,-1.54,'9/1 12:00am'],
498 ['Boeing Co.',75.43,0.53,0.71,'9/1 12:00am'],
499 ['Caterpillar Inc.',67.27,0.92,1.39,'9/1 12:00am'],
500 ['Citigroup, Inc.',49.37,0.02,0.04,'9/1 12:00am'],
501 ['E.I. du Pont de Nemours and Company',40.48,0.51,1.28,'9/1 12:00am'],
502 ['Exxon Mobil Corp',68.1,-0.43,-0.64,'9/1 12:00am'],
503 ['General Electric Company',34.14,-0.08,-0.23,'9/1 12:00am'],
504 ['General Motors Corporation',30.27,1.09,3.74,'9/1 12:00am'],
505 ['Hewlett-Packard Co.',36.53,-0.03,-0.08,'9/1 12:00am'],
506 ['Honeywell Intl Inc',38.77,0.05,0.13,'9/1 12:00am'],
507 ['Intel Corporation',19.88,0.31,1.58,'9/1 12:00am'],
508 ['International Business Machines',81.41,0.44,0.54,'9/1 12:00am'],
509 ['Johnson & Johnson',64.72,0.06,0.09,'9/1 12:00am'],
510 ['JP Morgan & Chase & Co',45.73,0.07,0.15,'9/1 12:00am'],
511 ['McDonald\'s Corporation',36.76,0.86,2.40,'9/1 12:00am'],
512 ['Merck & Co., Inc.',40.96,0.41,1.01,'9/1 12:00am'],
513 ['Microsoft Corporation',25.84,0.14,0.54,'9/1 12:00am'],
514 ['Pfizer Inc',27.96,0.4,1.45,'9/1 12:00am'],
515 ['The Coca-Cola Company',45.07,0.26,0.58,'9/1 12:00am'],
516 ['The Home Depot, Inc.',34.64,0.35,1.02,'9/1 12:00am'],
517 ['The Procter & Gamble Company',61.91,0.01,0.02,'9/1 12:00am'],
518 ['United Technologies Corporation',63.26,0.55,0.88,'9/1 12:00am'],
519 ['Verizon Communications',35.57,0.39,1.11,'9/1 12:00am'],
520 ['Wal-Mart Stores, Inc.',45.45,0.73,1.63,'9/1 12:00am'],
521 ['3m Co',71.20,0.02,0.03,'9/1 12:00am']
524 // example of custom renderer function
525 function change(val){
527 return '<span style="color:green;">' + val + '</span>';
529 return '<span style="color:red;">' + val + '</span>';
534 // example of custom renderer function
535 function pctChange(val){
537 return '<span style="color:green;">' + val + '%</span>';
539 return '<span style="color:red;">' + val + '%</span>';
544 // create the data store
545 var store = new Ext.data.ArrayStore({
548 {name: 'price', type: 'float'},
549 {name: 'change', type: 'float'},
550 {name: 'pctChange', type: 'float'},
551 {name: 'lastChange', type: 'date', dateFormat: 'n/j h:ia'}
553 comparator: function(r1, r2){
554 var v1 = r1.data[this.sortInfo.field], v2 = r2.data[this.sortInfo.field];
555 return -(v1 > v2 ? 1 : (v1 < v2 ? -1 : 0));
558 store.loadData(myData);
561 grid = new Ext.grid.GridPanel({
565 {id:'company',header: "Company", width: 160, sortable: true, dataIndex: 'company'},
566 {header: "Price", width: 75, sortable: true, renderer: 'usMoney', dataIndex: 'price'},
567 {header: "Change", width: 75, sortable: true, renderer: change, dataIndex: 'change'},
568 {header: "% Change", width: 75, sortable: true, renderer: pctChange, dataIndex: 'pctChange'},
569 {header: "Last<br/>Updated", width: 85, sortable: true, renderer: Ext.util.Format.dateRenderer('m/d/Y'), dataIndex: 'lastChange'}
572 emptyText: 'No matching data'
575 autoExpandColumn: 'company',
582 myGrid.on('render', function() {
583 myGrid.tip = new Ext.ToolTip({
584 view: myGrid.getView(),
585 target: myGrid.getView().mainBody,
586 delegate: '.x-grid3-row',
588 renderTo: document.body,
590 beforeshow: function updateTipBody(tip) {
591 tip.body.dom.innerHTML = "Over row " + tip.view.findRowIndex(tip.triggerElement);
597 var filter = new Ext.ux.StoreFilter({
611 items: [ filter, grid ]