commit extjs-2.2.1
[extjs.git] / examples / grid / test.html
1 <html>
2 <head>
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 {
11     padding: 4px;
12 }
13
14 .add-button {
15     background: url(../shared/icons/fam/add.gif)!important;
16 }
17
18 .delete-button {
19     background: url(../shared/icons/fam/bin.png)!important;
20 }
21
22 .condition-row-disabled {
23     background: url(../shared/icons/fam/delete.gif)!important;
24 }
25
26 .condition-row-enabled {
27     background: url(../shared/extjs/images/checked.gif)!important;
28 }
29
30 </style>
31 <script type="text/javascript">
32 Ext.ux.FilterCondition = Ext.extend(Ext.Container, {
33     layout: 'table',
34
35     layoutConfig: {
36         columns: 9
37     },
38
39     autoEl: {
40         cls: 'x-filter-condition'
41     },
42
43     Field: Ext.data.Record.create(['name', 'type']),
44
45     initComponent: function() {
46         this.fields = this.store.reader.recordType.prototype.fields;
47         this.fieldStore = new Ext.data.Store();
48
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))
53         }, this);
54
55 //      Create a Combo which allows selection of a field
56         this.fieldCombo = new Ext.form.ComboBox({
57             triggerAction: 'all',
58             store: this.fieldStore,
59             valueField: 'name',
60             displayField: 'name',
61             editable: false,
62             forceSelection: true,
63             mode: 'local',
64             listeners: {
65                 select: this.onFieldSelect,
66                 scope: this
67             }
68         });
69
70 //      Create a Combo which allows selection of a test
71         this.testCombo = new Ext.form.ComboBox({
72             triggerAction: 'all',
73             editable: false,
74             forceSelection: true,
75             store: ['<', '<=', '=', '!=', '>=', '>'],
76             listeners: {
77                 select: this.onTestSelect,
78                 scope: this
79             }
80         });
81
82 //      Inputs for each type of field. Hidden and shown as necessary
83         this.booleanInput = new Ext.form.Checkbox({
84             hideParent: true,
85             hidden: true,
86             testFilter: function(rec) {
87                 var t = rec.text;
88                 return (t == '=') || (t == '!=');
89             },
90             listeners: {
91                 check: this.onTestValueChange,
92                 scope: this
93             }
94         });
95         this.intInput = new Ext.form.NumberField({
96             allowDecimals: false,
97             hideParent: true,
98             hidden: true,
99             listeners: {
100                 change: this.onTestValueChange,
101                 scope: this
102             }
103         });
104         this.floatInput = new Ext.form.NumberField({
105             hideParent: true,
106             hidden: true,
107             listeners: {
108                 change: this.onTestValueChange,
109                 scope: this
110             }
111         });
112         this.textInput = new Ext.form.TextField({
113             hideParent: true,
114             hidden: true,
115             listeners: {
116                 change: this.onTestValueChange,
117                 scope: this
118             }
119         });
120         this.dateInput = new Ext.form.DateField({
121             hideParent: true,
122             hidden: true,
123             convertValue: function(d) {
124                 return d.valueOf();
125             },
126             listeners: {
127                 change: this.onTestValueChange,
128                 scope: this
129             }
130         });
131
132         this.cls = 'x-filter-condition';
133         this.items = [{
134             xtype: 'button',
135             iconCls: 'delete-button',
136             handler: this.removeSelf,
137             scope: this,
138             tooltip: 'Remove this condition'
139         }, {
140           xtype: 'button',
141             handler: this.toggleEnabled,
142             scope: this,
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);
147     },
148
149     removeSelf: function() {
150         var o = this.ownerCt;
151         o.remove(this, true);
152         o.doLayout();
153     },
154
155     toggleEnabled: function(b) {
156         b = Ext.get(b.el.query(b.buttonSelector));
157         if (this.disabled) {
158             b.removeClass('condition-row-disabled');
159             b.addClass('condition-row-enabled');
160             this.enable();
161         } else {
162             b.removeClass('condition-row-enabled');
163             b.addClass('condition-row-disabled');
164             this.disable();
165         }
166     },
167
168     focus: function() {
169         this.fieldCombo.focus();
170     },
171
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)) {
175                 it[i].disable();
176             }
177         }
178         this.disabled = true;
179         this.fireEvent("change", this);
180     },
181
182     onEnable: function() {
183         for (var i = 0, it = this.items.items, l = it.length; i < l; i++) {
184             it[i].enable();
185         }
186         this.disabled = false;
187         this.fireEvent("change", this);
188     },
189
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;
209         } else {
210             this.textInput.show();
211             this.valueInput = this.textInput;
212         }
213
214 //      Allow the value input to determine which test types are allowed.
215         var fn = this.valueInput.testFilter;
216         if (fn) {
217             this.testCombo.store.filterBy(fn);
218         } else {
219             this.testCombo.store.clearFilter();
220         }
221         this.fireEvent("change", this);
222     },
223
224     onTestSelect: function(combo, rec, index) {
225         this.fireEvent("change", this);
226     },
227
228     onTestValueChange: function() {
229         this.fireEvent("change", this);
230     },
231
232     getValue: function() {
233         return {
234             field: this.fieldCombo.getValue(),
235             test: this.testCombo.getValue(),
236             value: this.valueInput.getValue()
237         };
238     },
239
240     getFilterFunction: function() {
241         if (!this.filterFunction) {
242             this.filterFunction = this.filterFunctionImpl.createDelegate(this);
243         }
244         return this.filterFunction;
245     },
246
247     filterFunctionImpl: function(rec) {
248         var fieldValue = rec.get(this.fieldCombo.getValue());
249         var v = this.valueInput.getValue();
250
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);
255         }
256
257         switch (this.testCombo.getValue()) {
258             case '<':
259                 return fieldValue < v;
260
261             case '<=':
262                 return fieldValue <= v;
263
264             case '=':
265                 return fieldValue == v;
266
267             case '!=':
268                 return fieldValue != v;
269
270             case '>=':
271                 return fieldValue >= v;
272
273             case '>':
274                 return fieldValue > v;
275
276         }
277     },
278
279     isEmpty: function() {
280         return ((this.fieldCombo.getValue.length == 0) && (this.testCombo.getValue().length == 0)) || !this.valueInput;
281     }
282 });
283
284 Ext.ux.StoreFilter = Ext.extend(Ext.Panel, {
285     constructor: function(config) {
286         config = Ext.apply({}, {
287             layout: 'anchor',
288             bodyStyle: {
289                 padding: '10px 0px 10px 10px',
290                 overflow: 'auto'
291             },
292             defaults: {
293                 xtype: 'container',
294                 autoEl: {}
295             },
296             items: [{
297                 cls: 'x-condition-header',
298                 anchor: '-25',
299                 layout: 'column',
300                 style: {
301                     'text-decoration': 'underline',
302                     'font': 'bold small verdana',
303                     'margin-bottom': '5px'
304                 },
305                 defaults: {
306                     xtype: 'box',
307                     style: {
308                         'padding-left': '5px'
309                     }
310                 },
311                 items: [{
312                     style: {
313                         'padding-left': '65px'
314                     },
315                     width: 235,
316                     autoEl: {html: 'Field to test'}
317                 }, {
318                     width: 170,
319                     autoEl: {html: 'Test type'}
320                 }, {
321                     autoEl: {html: 'Test value'}
322                 }]
323             }, this.addConditionButton = new Ext.Button({
324                 iconCls: 'add-button',
325                 handler: this.onAddConditionButtonClick,
326                 scope: this,
327                 tooltip: 'Add condition',
328                 style: {
329                     'margin-left': '4px'
330                 }
331             })],
332             bbar: new Ext.Toolbar([
333                 this.filterButton = new Ext.Button({
334                     text: "Filter",
335                     tooltip: 'Filter grid',
336                     handler: this.doFilter,
337                     scope: this
338                 }),
339                 this.clearFilterButton = new Ext.Button({
340                     text: "Clear Filter",
341                     tooltip: 'Clear filters',
342                     handler: this.clearFilter,
343                     scope: this
344                 })
345             ])
346         }, config);
347         Ext.ux.StoreFilter.superclass.constructor.call(this, config);
348     },
349
350     onAddConditionButtonClick: function() {
351         var c, j = this.items.getCount();
352         if (j > 2) {
353             c = this.items.items[j - 2];
354             if (c.isEmpty()) {
355                 return;
356             }
357         }
358         c = new Ext.ux.FilterCondition({store: this.store});
359         if (this.autoApply) {
360             c.on('change', this.doFilter, this)
361         }
362         this.insert(this.items.getCount() - 1, c);
363         this.doLayout();
364         this.addConditionButton.getEl().scrollIntoView(this.body);
365         c.focus();
366     },
367
368     doFilter: function() {
369         this.store.filterBy(this.getFilterFunction());
370     },
371
372     clearFilter: function() {
373         this.store.clearFilter();
374     },
375
376     getFilterFunction: function() {
377         if (!this.filterFunction) {
378             this.filterFunction = this.filterFunctionImpl.createDelegate(this);
379         }
380         return this.filterFunction;
381     },
382
383     filterFunctionImpl: function(rec) {
384         for (var i = 0, it = this.items.items, l = it.length; i < l; i++) {
385             var c = it[i];
386             if ((c instanceof Ext.ux.FilterCondition) && (!c.isEmpty()) && (!c.disabled)) {
387                 var fn = c.getFilterFunction();
388                 if (!fn(rec)) {
389                     return false;
390                 }
391             }
392         }
393         return true;
394     }
395 });
396
397 Ext.override(Ext.ToolTip, {
398     onTargetOver : function(e){
399         if(this.disabled || e.within(this.target.dom, true)){
400             return;
401         }
402         var t = e.getTarget(this.delegate);
403         if (t) {
404             this.triggerElement = t;
405             this.clearTimer('hide');
406             this.targetXY = e.getXY();
407             this.delayShow();
408         }
409     },
410     onMouseMove : function(e){
411         var t = e.getTarget(this.delegate);
412         if (t) {
413             this.targetXY = e.getXY();
414             if (t === this.triggerElement) {
415                 if(!this.hidden && this.trackMouse){
416                     this.setPagePosition(this.getTargetXY());
417                 }
418             } else {
419                 this.hide();
420                 this.lastActive = new Date(0);
421                 this.onTargetOver(e);
422             }
423         } else if (!this.closable && this.isVisible()) {
424             this.hide();
425         }
426     },
427     hide: function(){
428         this.clearTimer('dismiss');
429         this.lastActive = new Date();
430         delete this.triggerElement;
431         Ext.ToolTip.superclass.hide.call(this);
432     }
433 });
434
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});
440
441         grid.on("headerclick", this.onHeaderClick, this);
442
443         if(grid.trackMouseOver){
444             grid.on("mouseover", this.onRowOver, this);
445             grid.on("mouseout", this.onRowOut, this);
446         }
447     },
448
449     processRows : function(startRow, skipStripe){
450         this.fireEvent('processrows', startRow, skipStripe);
451     },
452
453 //  private
454     doProcessRows: function(startRow, skipStripe) {
455
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';
460         }
461
462         if(this.ds.getCount() < 1){
463             return;
464         }
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++){
470             var row = rows[i];
471             row.rowIndex = i;
472             if(!skipStripe){
473                 var isAlt = ((i+1) % 2 == 0);
474                 var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
475                 if(isAlt == hasAlt){
476                     continue;
477                 }
478                 if(isAlt){
479                     row.className += " x-grid3-row-alt";
480                 }else{
481                     row.className = row.className.replace("x-grid3-row-alt", "");
482                 }
483             }
484         }
485     }
486 });
487
488 Ext.onReady(function(){
489
490     Ext.QuickTips.init();
491
492     var myData = [
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']
522     ];
523
524     // example of custom renderer function
525     function change(val){
526         if(val > 0){
527             return '<span style="color:green;">' + val + '</span>';
528         }else if(val < 0){
529             return '<span style="color:red;">' + val + '</span>';
530         }
531         return val;
532     }
533
534     // example of custom renderer function
535     function pctChange(val){
536         if(val > 0){
537             return '<span style="color:green;">' + val + '%</span>';
538         }else if(val < 0){
539             return '<span style="color:red;">' + val + '%</span>';
540         }
541         return val;
542     }
543
544     // create the data store
545     var store = new Ext.data.ArrayStore({
546         fields: [
547            {name: 'company'},
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'}
552         ],
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));
556         }
557     });
558     store.loadData(myData);
559
560     // create the Grid
561     grid = new Ext.grid.GridPanel({
562         id: 'static-grid',
563         store: store,
564         columns: [
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'}
570         ],
571         viewConfig: {
572             emptyText: 'No matching data'
573         },
574         stripeRows: true,
575         autoExpandColumn: 'company',
576         title:'Array Grid',
577         region: 'center',
578         margins: '0 5 5 5'
579     });
580     var myGrid = grid;
581
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',
587             trackMouse: true,
588             renderTo: document.body,
589             listeners: {
590                 beforeshow: function updateTipBody(tip) {
591                     tip.body.dom.innerHTML = "Over row " + tip.view.findRowIndex(tip.triggerElement);
592                 }
593             }
594         });
595     });
596
597     var filter = new Ext.ux.StoreFilter({
598         store: store,
599         autoApply: true,
600         title: 'Filters',
601         collapsible: true,
602         height: 200,
603         region: 'north',
604         margins: '5 5 0 5',
605         cmargins: '5 5 5 5',
606         split: true
607     });
608
609     new Ext.Viewport({
610         layout:'border',
611         items: [ filter, grid ]
612     });
613 });
614 </script>
615 </head>
616 <body>
617 </body>
618 </html>