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 | /** |
---|
8 | * @class Ext.ListView |
---|
9 | * @extends Ext.DataView |
---|
10 | * <p>Ext.ListView is a fast and light-weight implentation of a |
---|
11 | * {@link Ext.grid.GridPanel Grid} like view with the following characteristics:</p> |
---|
12 | * <div class="mdetail-params"><ul> |
---|
13 | * <li>resizable columns</li> |
---|
14 | * <li>selectable</li> |
---|
15 | * <li>column widths are initially proportioned by percentage based on the container |
---|
16 | * width and number of columns</li> |
---|
17 | * <li>uses templates to render the data in any required format</li> |
---|
18 | * <li>no horizontal scrolling</li> |
---|
19 | * <li>no editing</li> |
---|
20 | * </ul></div> |
---|
21 | * <p>Example usage:</p> |
---|
22 | * <pre><code> |
---|
23 | // consume JSON of this form: |
---|
24 | { |
---|
25 | "images":[ |
---|
26 | { |
---|
27 | "name":"dance_fever.jpg", |
---|
28 | "size":2067, |
---|
29 | "lastmod":1236974993000, |
---|
30 | "url":"images\/thumbs\/dance_fever.jpg" |
---|
31 | }, |
---|
32 | { |
---|
33 | "name":"zack_sink.jpg", |
---|
34 | "size":2303, |
---|
35 | "lastmod":1236974993000, |
---|
36 | "url":"images\/thumbs\/zack_sink.jpg" |
---|
37 | } |
---|
38 | ] |
---|
39 | } |
---|
40 | var store = new Ext.data.JsonStore({ |
---|
41 | url: 'get-images.php', |
---|
42 | root: 'images', |
---|
43 | fields: [ |
---|
44 | 'name', 'url', |
---|
45 | {name:'size', type: 'float'}, |
---|
46 | {name:'lastmod', type:'date', dateFormat:'timestamp'} |
---|
47 | ] |
---|
48 | }); |
---|
49 | store.load(); |
---|
50 | |
---|
51 | var listView = new Ext.ListView({ |
---|
52 | store: store, |
---|
53 | multiSelect: true, |
---|
54 | emptyText: 'No images to display', |
---|
55 | reserveScrollOffset: true, |
---|
56 | columns: [{ |
---|
57 | header: 'File', |
---|
58 | width: .5, |
---|
59 | dataIndex: 'name' |
---|
60 | },{ |
---|
61 | header: 'Last Modified', |
---|
62 | width: .35, |
---|
63 | dataIndex: 'lastmod', |
---|
64 | tpl: '{lastmod:date("m-d h:i a")}' |
---|
65 | },{ |
---|
66 | header: 'Size', |
---|
67 | dataIndex: 'size', |
---|
68 | tpl: '{size:fileSize}', // format using Ext.util.Format.fileSize() |
---|
69 | align: 'right' |
---|
70 | }] |
---|
71 | }); |
---|
72 | |
---|
73 | // put it in a Panel so it looks pretty |
---|
74 | var panel = new Ext.Panel({ |
---|
75 | id:'images-view', |
---|
76 | width:425, |
---|
77 | height:250, |
---|
78 | collapsible:true, |
---|
79 | layout:'fit', |
---|
80 | title:'Simple ListView <i>(0 items selected)</i>', |
---|
81 | items: listView |
---|
82 | }); |
---|
83 | panel.render(document.body); |
---|
84 | |
---|
85 | // little bit of feedback |
---|
86 | listView.on('selectionchange', function(view, nodes){ |
---|
87 | var l = nodes.length; |
---|
88 | var s = l != 1 ? 's' : ''; |
---|
89 | panel.setTitle('Simple ListView <i>('+l+' item'+s+' selected)</i>'); |
---|
90 | }); |
---|
91 | * </code></pre> |
---|
92 | * @constructor |
---|
93 | * @param {Object} config |
---|
94 | * @xtype listview |
---|
95 | */ |
---|
96 | Ext.ListView = Ext.extend(Ext.DataView, { |
---|
97 | /** |
---|
98 | * Set this property to <tt>true</tt> to disable the header click handler disabling sort |
---|
99 | * (defaults to <tt>false</tt>). |
---|
100 | * @type Boolean |
---|
101 | * @property disableHeaders |
---|
102 | */ |
---|
103 | /** |
---|
104 | * @cfg {Boolean} hideHeaders |
---|
105 | * <tt>true</tt> to hide the {@link #internalTpl header row} (defaults to <tt>false</tt> so |
---|
106 | * the {@link #internalTpl header row} will be shown). |
---|
107 | */ |
---|
108 | /** |
---|
109 | * @cfg {String} itemSelector |
---|
110 | * Defaults to <tt>'dl'</tt> to work with the preconfigured <b><tt>{@link Ext.DataView#tpl tpl}</tt></b>. |
---|
111 | * This setting specifies the CSS selector (e.g. <tt>div.some-class</tt> or <tt>span:first-child</tt>) |
---|
112 | * that will be used to determine what nodes the ListView will be working with. |
---|
113 | */ |
---|
114 | itemSelector: 'dl', |
---|
115 | /** |
---|
116 | * @cfg {String} selectedClass The CSS class applied to a selected row (defaults to |
---|
117 | * <tt>'x-list-selected'</tt>). An example overriding the default styling: |
---|
118 | <pre><code> |
---|
119 | .x-list-selected {background-color: yellow;} |
---|
120 | </code></pre> |
---|
121 | * @type String |
---|
122 | */ |
---|
123 | selectedClass:'x-list-selected', |
---|
124 | /** |
---|
125 | * @cfg {String} overClass The CSS class applied when over a row (defaults to |
---|
126 | * <tt>'x-list-over'</tt>). An example overriding the default styling: |
---|
127 | <pre><code> |
---|
128 | .x-list-over {background-color: orange;} |
---|
129 | </code></pre> |
---|
130 | * @type String |
---|
131 | */ |
---|
132 | overClass:'x-list-over', |
---|
133 | /** |
---|
134 | * @cfg {Boolean} reserveScrollOffset |
---|
135 | * By default will defer accounting for the configured <b><tt>{@link #scrollOffset}</tt></b> |
---|
136 | * for 10 milliseconds. Specify <tt>true</tt> to account for the configured |
---|
137 | * <b><tt>{@link #scrollOffset}</tt></b> immediately. |
---|
138 | */ |
---|
139 | /** |
---|
140 | * @cfg {Number} scrollOffset The amount of space to reserve for the scrollbar (defaults to |
---|
141 | * <tt>19</tt> pixels) |
---|
142 | */ |
---|
143 | scrollOffset : 19, |
---|
144 | /** |
---|
145 | * @cfg {Boolean/Object} columnResize |
---|
146 | * Specify <tt>true</tt> or specify a configuration object for {@link Ext.ListView.ColumnResizer} |
---|
147 | * to enable the columns to be resizable (defaults to <tt>true</tt>). |
---|
148 | */ |
---|
149 | columnResize: true, |
---|
150 | /** |
---|
151 | * @cfg {Array} columns An array of column configuration objects, for example: |
---|
152 | * <pre><code> |
---|
153 | { |
---|
154 | align: 'right', |
---|
155 | dataIndex: 'size', |
---|
156 | header: 'Size', |
---|
157 | tpl: '{size:fileSize}', |
---|
158 | width: .35 |
---|
159 | } |
---|
160 | * </code></pre> |
---|
161 | * Acceptable properties for each column configuration object are: |
---|
162 | * <div class="mdetail-params"><ul> |
---|
163 | * <li><b><tt>align</tt></b> : String<div class="sub-desc">Set the CSS text-align property |
---|
164 | * of the column. Defaults to <tt>'left'</tt>.</div></li> |
---|
165 | * <li><b><tt>dataIndex</tt></b> : String<div class="sub-desc">See {@link Ext.grid.Column}. |
---|
166 | * {@link Ext.grid.Column#dataIndex dataIndex} for details.</div></li> |
---|
167 | * <li><b><tt>header</tt></b> : String<div class="sub-desc">See {@link Ext.grid.Column}. |
---|
168 | * {@link Ext.grid.Column#header header} for details.</div></li> |
---|
169 | * <li><b><tt>tpl</tt></b> : String<div class="sub-desc">Specify a string to pass as the |
---|
170 | * configuration string for {@link Ext.XTemplate}. By default an {@link Ext.XTemplate} |
---|
171 | * will be implicitly created using the <tt>dataIndex</tt>.</div></li> |
---|
172 | * <li><b><tt>width</tt></b> : Number<div class="sub-desc">Percentage of the container width |
---|
173 | * this column should be allocated. Columns that have no width specified will be |
---|
174 | * allocated with an equal percentage to fill 100% of the container width. To easily take |
---|
175 | * advantage of the full container width, leave the width of at least one column undefined. |
---|
176 | * Note that if you do not want to take up the full width of the container, the width of |
---|
177 | * every column needs to be explicitly defined.</div></li> |
---|
178 | * </ul></div> |
---|
179 | */ |
---|
180 | /** |
---|
181 | * @cfg {Boolean/Object} columnSort |
---|
182 | * Specify <tt>true</tt> or specify a configuration object for {@link Ext.ListView.Sorter} |
---|
183 | * to enable the columns to be sortable (defaults to <tt>true</tt>). |
---|
184 | */ |
---|
185 | columnSort: true, |
---|
186 | /** |
---|
187 | * @cfg {String/Array} internalTpl |
---|
188 | * The template to be used for the header row. See {@link #tpl} for more details. |
---|
189 | */ |
---|
190 | |
---|
191 | initComponent : function(){ |
---|
192 | if(this.columnResize){ |
---|
193 | this.colResizer = new Ext.ListView.ColumnResizer(this.colResizer); |
---|
194 | this.colResizer.init(this); |
---|
195 | } |
---|
196 | if(this.columnSort){ |
---|
197 | this.colSorter = new Ext.ListView.Sorter(this.columnSort); |
---|
198 | this.colSorter.init(this); |
---|
199 | } |
---|
200 | if(!this.internalTpl){ |
---|
201 | this.internalTpl = new Ext.XTemplate( |
---|
202 | '<div class="x-list-header"><div class="x-list-header-inner">', |
---|
203 | '<tpl for="columns">', |
---|
204 | '<div style="width:{width}%;text-align:{align};"><em unselectable="on" id="',this.id, '-xlhd-{#}">', |
---|
205 | '{header}', |
---|
206 | '</em></div>', |
---|
207 | '</tpl>', |
---|
208 | '<div class="x-clear"></div>', |
---|
209 | '</div></div>', |
---|
210 | '<div class="x-list-body"><div class="x-list-body-inner">', |
---|
211 | '</div></div>' |
---|
212 | ); |
---|
213 | } |
---|
214 | if(!this.tpl){ |
---|
215 | this.tpl = new Ext.XTemplate( |
---|
216 | '<tpl for="rows">', |
---|
217 | '<dl>', |
---|
218 | '<tpl for="parent.columns">', |
---|
219 | '<dt style="width:{width}%;text-align:{align};"><em unselectable="on">', |
---|
220 | '{[values.tpl.apply(parent)]}', |
---|
221 | '</em></dt>', |
---|
222 | '</tpl>', |
---|
223 | '<div class="x-clear"></div>', |
---|
224 | '</dl>', |
---|
225 | '</tpl>' |
---|
226 | ); |
---|
227 | }; |
---|
228 | var cs = this.columns, allocatedWidth = 0, colsWithWidth = 0, len = cs.length; |
---|
229 | for(var i = 0; i < len; i++){ |
---|
230 | var c = cs[i]; |
---|
231 | if(!c.tpl){ |
---|
232 | c.tpl = new Ext.XTemplate('{' + c.dataIndex + '}'); |
---|
233 | }else if(Ext.isString(c.tpl)){ |
---|
234 | c.tpl = new Ext.XTemplate(c.tpl); |
---|
235 | } |
---|
236 | c.align = c.align || 'left'; |
---|
237 | if(Ext.isNumber(c.width)){ |
---|
238 | c.width *= 100; |
---|
239 | allocatedWidth += c.width; |
---|
240 | colsWithWidth++; |
---|
241 | } |
---|
242 | } |
---|
243 | // auto calculate missing column widths |
---|
244 | if(colsWithWidth < len){ |
---|
245 | var remaining = len - colsWithWidth; |
---|
246 | if(allocatedWidth < 100){ |
---|
247 | var perCol = ((100-allocatedWidth) / remaining); |
---|
248 | for(var j = 0; j < len; j++){ |
---|
249 | var c = cs[j]; |
---|
250 | if(!Ext.isNumber(c.width)){ |
---|
251 | c.width = perCol; |
---|
252 | } |
---|
253 | } |
---|
254 | } |
---|
255 | } |
---|
256 | Ext.ListView.superclass.initComponent.call(this); |
---|
257 | }, |
---|
258 | |
---|
259 | onRender : function(){ |
---|
260 | Ext.ListView.superclass.onRender.apply(this, arguments); |
---|
261 | |
---|
262 | this.internalTpl.overwrite(this.el, {columns: this.columns}); |
---|
263 | |
---|
264 | this.innerBody = Ext.get(this.el.dom.childNodes[1].firstChild); |
---|
265 | this.innerHd = Ext.get(this.el.dom.firstChild.firstChild); |
---|
266 | |
---|
267 | if(this.hideHeaders){ |
---|
268 | this.el.dom.firstChild.style.display = 'none'; |
---|
269 | } |
---|
270 | }, |
---|
271 | |
---|
272 | getTemplateTarget : function(){ |
---|
273 | return this.innerBody; |
---|
274 | }, |
---|
275 | |
---|
276 | /** |
---|
277 | * <p>Function which can be overridden which returns the data object passed to this |
---|
278 | * view's {@link #tpl template} to render the whole ListView. The returned object |
---|
279 | * shall contain the following properties:</p> |
---|
280 | * <div class="mdetail-params"><ul> |
---|
281 | * <li><b>columns</b> : String<div class="sub-desc">See <tt>{@link #columns}</tt></div></li> |
---|
282 | * <li><b>rows</b> : String<div class="sub-desc">See |
---|
283 | * <tt>{@link Ext.DataView}.{@link Ext.DataView#collectData collectData}</div></li> |
---|
284 | * </ul></div> |
---|
285 | * @param {Array} records An Array of {@link Ext.data.Record}s to be rendered into the DataView. |
---|
286 | * @param {Number} startIndex the index number of the Record being prepared for rendering. |
---|
287 | * @return {Object} A data object containing properties to be processed by a repeating |
---|
288 | * XTemplate as described above. |
---|
289 | */ |
---|
290 | collectData : function(){ |
---|
291 | var rs = Ext.ListView.superclass.collectData.apply(this, arguments); |
---|
292 | return { |
---|
293 | columns: this.columns, |
---|
294 | rows: rs |
---|
295 | } |
---|
296 | }, |
---|
297 | |
---|
298 | verifyInternalSize : function(){ |
---|
299 | if(this.lastSize){ |
---|
300 | this.onResize(this.lastSize.width, this.lastSize.height); |
---|
301 | } |
---|
302 | }, |
---|
303 | |
---|
304 | // private |
---|
305 | onResize : function(w, h){ |
---|
306 | var bd = this.innerBody.dom; |
---|
307 | var hd = this.innerHd.dom |
---|
308 | if(!bd){ |
---|
309 | return; |
---|
310 | } |
---|
311 | var bdp = bd.parentNode; |
---|
312 | if(Ext.isNumber(w)){ |
---|
313 | var sw = w - this.scrollOffset; |
---|
314 | if(this.reserveScrollOffset || ((bdp.offsetWidth - bdp.clientWidth) > 10)){ |
---|
315 | bd.style.width = sw + 'px'; |
---|
316 | hd.style.width = sw + 'px'; |
---|
317 | }else{ |
---|
318 | bd.style.width = w + 'px'; |
---|
319 | hd.style.width = w + 'px'; |
---|
320 | setTimeout(function(){ |
---|
321 | if((bdp.offsetWidth - bdp.clientWidth) > 10){ |
---|
322 | bd.style.width = sw + 'px'; |
---|
323 | hd.style.width = sw + 'px'; |
---|
324 | } |
---|
325 | }, 10); |
---|
326 | } |
---|
327 | } |
---|
328 | if(Ext.isNumber(h == 'number')){ |
---|
329 | bdp.style.height = (h - hd.parentNode.offsetHeight) + 'px'; |
---|
330 | } |
---|
331 | }, |
---|
332 | |
---|
333 | updateIndexes : function(){ |
---|
334 | Ext.ListView.superclass.updateIndexes.apply(this, arguments); |
---|
335 | this.verifyInternalSize(); |
---|
336 | }, |
---|
337 | |
---|
338 | findHeaderIndex : function(hd){ |
---|
339 | hd = hd.dom || hd; |
---|
340 | var pn = hd.parentNode, cs = pn.parentNode.childNodes; |
---|
341 | for(var i = 0, c; c = cs[i]; i++){ |
---|
342 | if(c == pn){ |
---|
343 | return i; |
---|
344 | } |
---|
345 | } |
---|
346 | return -1; |
---|
347 | }, |
---|
348 | |
---|
349 | setHdWidths : function(){ |
---|
350 | var els = this.innerHd.dom.getElementsByTagName('div'); |
---|
351 | for(var i = 0, cs = this.columns, len = cs.length; i < len; i++){ |
---|
352 | els[i].style.width = cs[i].width + '%'; |
---|
353 | } |
---|
354 | } |
---|
355 | }); |
---|
356 | |
---|
357 | Ext.reg('listview', Ext.ListView); |
---|