Upgrade to ExtJS 3.0.0 - Released 07/06/2009
[extjs.git] / examples / ux / RowExpander.js
1 /*!
2  * Ext JS Library 3.0.0
3  * Copyright(c) 2006-2009 Ext JS, LLC
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 Ext.ns('Ext.ux.grid');\r
8 \r
9 /**\r
10  * @class Ext.ux.grid.RowExpander\r
11  * @extends Ext.util.Observable\r
12  * Plugin (ptype = 'rowexpander') that adds the ability to have a Column in a grid which enables\r
13  * a second row body which expands/contracts.  The expand/contract behavior is configurable to react\r
14  * on clicking of the column, double click of the row, and/or hitting enter while a row is selected.\r
15  *\r
16  * @ptype rowexpander\r
17  */\r
18 Ext.ux.grid.RowExpander = Ext.extend(Ext.util.Observable, {\r
19     /**\r
20      * @cfg {Boolean} expandOnEnter\r
21      * <tt>true</tt> to toggle selected row(s) between expanded/collapsed when the enter\r
22      * key is pressed (defaults to <tt>true</tt>).\r
23      */\r
24     expandOnEnter : true,\r
25     /**\r
26      * @cfg {Boolean} expandOnDblClick\r
27      * <tt>true</tt> to toggle a row between expanded/collapsed when double clicked\r
28      * (defaults to <tt>true</tt>).\r
29      */\r
30     expandOnDblClick : true,\r
31 \r
32     header : '',\r
33     width : 20,\r
34     sortable : false,\r
35     fixed : true,\r
36     menuDisabled : true,\r
37     dataIndex : '',\r
38     id : 'expander',\r
39     lazyRender : true,\r
40     enableCaching : true,\r
41 \r
42     constructor: function(config){\r
43         Ext.apply(this, config);\r
44 \r
45         this.addEvents({\r
46             /**\r
47              * @event beforeexpand\r
48              * Fires before the row expands. Have the listener return false to prevent the row from expanding.\r
49              * @param {Object} this RowExpander object.\r
50              * @param {Object} Ext.data.Record Record for the selected row.\r
51              * @param {Object} body body element for the secondary row.\r
52              * @param {Number} rowIndex The current row index.\r
53              */\r
54             beforeexpand: true,\r
55             /**\r
56              * @event expand\r
57              * Fires after the row expands.\r
58              * @param {Object} this RowExpander object.\r
59              * @param {Object} Ext.data.Record Record for the selected row.\r
60              * @param {Object} body body element for the secondary row.\r
61              * @param {Number} rowIndex The current row index.\r
62              */\r
63             expand: true,\r
64             /**\r
65              * @event beforecollapse\r
66              * Fires before the row collapses. Have the listener return false to prevent the row from collapsing.\r
67              * @param {Object} this RowExpander object.\r
68              * @param {Object} Ext.data.Record Record for the selected row.\r
69              * @param {Object} body body element for the secondary row.\r
70              * @param {Number} rowIndex The current row index.\r
71              */\r
72             beforecollapse: true,\r
73             /**\r
74              * @event collapse\r
75              * Fires after the row collapses.\r
76              * @param {Object} this RowExpander object.\r
77              * @param {Object} Ext.data.Record Record for the selected row.\r
78              * @param {Object} body body element for the secondary row.\r
79              * @param {Number} rowIndex The current row index.\r
80              */\r
81             collapse: true\r
82         });\r
83 \r
84         Ext.ux.grid.RowExpander.superclass.constructor.call(this);\r
85 \r
86         if(this.tpl){\r
87             if(typeof this.tpl == 'string'){\r
88                 this.tpl = new Ext.Template(this.tpl);\r
89             }\r
90             this.tpl.compile();\r
91         }\r
92 \r
93         this.state = {};\r
94         this.bodyContent = {};\r
95     },\r
96 \r
97     getRowClass : function(record, rowIndex, p, ds){\r
98         p.cols = p.cols-1;\r
99         var content = this.bodyContent[record.id];\r
100         if(!content && !this.lazyRender){\r
101             content = this.getBodyContent(record, rowIndex);\r
102         }\r
103         if(content){\r
104             p.body = content;\r
105         }\r
106         return this.state[record.id] ? 'x-grid3-row-expanded' : 'x-grid3-row-collapsed';\r
107     },\r
108 \r
109     init : function(grid){\r
110         this.grid = grid;\r
111 \r
112         var view = grid.getView();\r
113         view.getRowClass = this.getRowClass.createDelegate(this);\r
114 \r
115         view.enableRowBody = true;\r
116 \r
117 \r
118         grid.on('render', this.onRender, this);\r
119         grid.on('destroy', this.onDestroy, this);\r
120     },\r
121 \r
122     // @private\r
123     onRender: function() {\r
124         var grid = this.grid;\r
125         var mainBody = grid.getView().mainBody;\r
126         mainBody.on('mousedown', this.onMouseDown, this, {delegate: '.x-grid3-row-expander'});\r
127         if (this.expandOnEnter) {\r
128             this.keyNav = new Ext.KeyNav(this.grid.getGridEl(), {\r
129                 'enter' : this.onEnter,\r
130                 scope: this\r
131             });\r
132         }\r
133         if (this.expandOnDblClick) {\r
134             grid.on('rowdblclick', this.onRowDblClick, this);\r
135         }\r
136     },\r
137     \r
138     // @private    \r
139     onDestroy: function() {\r
140         this.keyNav.disable();\r
141         delete this.keyNav;\r
142         var mainBody = this.grid.getView().mainBody;\r
143         mainBody.un('mousedown', this.onMouseDown, this);\r
144     },\r
145     // @private\r
146     onRowDblClick: function(grid, rowIdx, e) {\r
147         this.toggleRow(rowIdx);\r
148     },\r
149 \r
150     onEnter: function(e) {\r
151         var g = this.grid;\r
152         var sm = g.getSelectionModel();\r
153         var sels = sm.getSelections();\r
154         for (var i = 0, len = sels.length; i < len; i++) {\r
155             var rowIdx = g.getStore().indexOf(sels[i]);\r
156             this.toggleRow(rowIdx);\r
157         }\r
158     },\r
159 \r
160     getBodyContent : function(record, index){\r
161         if(!this.enableCaching){\r
162             return this.tpl.apply(record.data);\r
163         }\r
164         var content = this.bodyContent[record.id];\r
165         if(!content){\r
166             content = this.tpl.apply(record.data);\r
167             this.bodyContent[record.id] = content;\r
168         }\r
169         return content;\r
170     },\r
171 \r
172     onMouseDown : function(e, t){\r
173         e.stopEvent();\r
174         var row = e.getTarget('.x-grid3-row');\r
175         this.toggleRow(row);\r
176     },\r
177 \r
178     renderer : function(v, p, record){\r
179         p.cellAttr = 'rowspan="2"';\r
180         return '<div class="x-grid3-row-expander">&#160;</div>';\r
181     },\r
182 \r
183     beforeExpand : function(record, body, rowIndex){\r
184         if(this.fireEvent('beforeexpand', this, record, body, rowIndex) !== false){\r
185             if(this.tpl && this.lazyRender){\r
186                 body.innerHTML = this.getBodyContent(record, rowIndex);\r
187             }\r
188             return true;\r
189         }else{\r
190             return false;\r
191         }\r
192     },\r
193 \r
194     toggleRow : function(row){\r
195         if(typeof row == 'number'){\r
196             row = this.grid.view.getRow(row);\r
197         }\r
198         this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'expandRow' : 'collapseRow'](row);\r
199     },\r
200 \r
201     expandRow : function(row){\r
202         if(typeof row == 'number'){\r
203             row = this.grid.view.getRow(row);\r
204         }\r
205         var record = this.grid.store.getAt(row.rowIndex);\r
206         var body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row);\r
207         if(this.beforeExpand(record, body, row.rowIndex)){\r
208             this.state[record.id] = true;\r
209             Ext.fly(row).replaceClass('x-grid3-row-collapsed', 'x-grid3-row-expanded');\r
210             this.fireEvent('expand', this, record, body, row.rowIndex);\r
211         }\r
212     },\r
213 \r
214     collapseRow : function(row){\r
215         if(typeof row == 'number'){\r
216             row = this.grid.view.getRow(row);\r
217         }\r
218         var record = this.grid.store.getAt(row.rowIndex);\r
219         var body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true);\r
220         if(this.fireEvent('beforecollapse', this, record, body, row.rowIndex) !== false){\r
221             this.state[record.id] = false;\r
222             Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');\r
223             this.fireEvent('collapse', this, record, body, row.rowIndex);\r
224         }\r
225     }\r
226 });\r
227 \r
228 Ext.preg('rowexpander', Ext.ux.grid.RowExpander);\r
229 \r
230 //backwards compat\r
231 Ext.grid.RowExpander = Ext.ux.grid.RowExpander;