1 | /*! |
---|
2 | * Ext JS Library 3.4.0 |
---|
3 | * Copyright(c) 2006-2011 Sencha Inc. |
---|
4 | * licensing@sencha.com |
---|
5 | * http://www.sencha.com/license |
---|
6 | */ |
---|
7 | /** |
---|
8 | * @class Ext.list.ListView |
---|
9 | * @extends Ext.DataView |
---|
10 | * <p>Ext.list.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.list.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.list.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>undefined</tt>). If an explicit value isn't specified, this will be automatically |
---|
142 | * calculated. |
---|
143 | */ |
---|
144 | scrollOffset : undefined, |
---|
145 | /** |
---|
146 | * @cfg {Boolean/Object} columnResize |
---|
147 | * Specify <tt>true</tt> or specify a configuration object for {@link Ext.list.ListView.ColumnResizer} |
---|
148 | * to enable the columns to be resizable (defaults to <tt>true</tt>). |
---|
149 | */ |
---|
150 | columnResize: true, |
---|
151 | /** |
---|
152 | * @cfg {Array} columns An array of column configuration objects, for example: |
---|
153 | * <pre><code> |
---|
154 | { |
---|
155 | align: 'right', |
---|
156 | dataIndex: 'size', |
---|
157 | header: 'Size', |
---|
158 | tpl: '{size:fileSize}', |
---|
159 | width: .35 |
---|
160 | } |
---|
161 | * </code></pre> |
---|
162 | * Acceptable properties for each column configuration object are: |
---|
163 | * <div class="mdetail-params"><ul> |
---|
164 | * <li><b><tt>align</tt></b> : String<div class="sub-desc">Set the CSS text-align property |
---|
165 | * of the column. Defaults to <tt>'left'</tt>.</div></li> |
---|
166 | * <li><b><tt>dataIndex</tt></b> : String<div class="sub-desc">See {@link Ext.grid.Column}. |
---|
167 | * {@link Ext.grid.Column#dataIndex dataIndex} for details.</div></li> |
---|
168 | * <li><b><tt>header</tt></b> : String<div class="sub-desc">See {@link Ext.grid.Column}. |
---|
169 | * {@link Ext.grid.Column#header header} for details.</div></li> |
---|
170 | * <li><b><tt>tpl</tt></b> : String<div class="sub-desc">Specify a string to pass as the |
---|
171 | * configuration string for {@link Ext.XTemplate}. By default an {@link Ext.XTemplate} |
---|
172 | * will be implicitly created using the <tt>dataIndex</tt>.</div></li> |
---|
173 | * <li><b><tt>width</tt></b> : Number<div class="sub-desc">Percentage of the container width |
---|
174 | * this column should be allocated. Columns that have no width specified will be |
---|
175 | * allocated with an equal percentage to fill 100% of the container width. To easily take |
---|
176 | * advantage of the full container width, leave the width of at least one column undefined. |
---|
177 | * Note that if you do not want to take up the full width of the container, the width of |
---|
178 | * every column needs to be explicitly defined.</div></li> |
---|
179 | * </ul></div> |
---|
180 | */ |
---|
181 | /** |
---|
182 | * @cfg {Boolean/Object} columnSort |
---|
183 | * Specify <tt>true</tt> or specify a configuration object for {@link Ext.list.ListView.Sorter} |
---|
184 | * to enable the columns to be sortable (defaults to <tt>true</tt>). |
---|
185 | */ |
---|
186 | columnSort: true, |
---|
187 | /** |
---|
188 | * @cfg {String/Array} internalTpl |
---|
189 | * The template to be used for the header row. See {@link #tpl} for more details. |
---|
190 | */ |
---|
191 | |
---|
192 | /* |
---|
193 | * IE has issues when setting percentage based widths to 100%. Default to 99. |
---|
194 | */ |
---|
195 | maxColumnWidth: Ext.isIE ? 99 : 100, |
---|
196 | |
---|
197 | initComponent : function(){ |
---|
198 | if(this.columnResize){ |
---|
199 | this.colResizer = new Ext.list.ColumnResizer(this.colResizer); |
---|
200 | this.colResizer.init(this); |
---|
201 | } |
---|
202 | if(this.columnSort){ |
---|
203 | this.colSorter = new Ext.list.Sorter(this.columnSort); |
---|
204 | this.colSorter.init(this); |
---|
205 | } |
---|
206 | if(!this.internalTpl){ |
---|
207 | this.internalTpl = new Ext.XTemplate( |
---|
208 | '<div class="x-list-header"><div class="x-list-header-inner">', |
---|
209 | '<tpl for="columns">', |
---|
210 | '<div style="width:{[values.width*100]}%;text-align:{align};"><em unselectable="on" id="',this.id, '-xlhd-{#}">', |
---|
211 | '{header}', |
---|
212 | '</em></div>', |
---|
213 | '</tpl>', |
---|
214 | '<div class="x-clear"></div>', |
---|
215 | '</div></div>', |
---|
216 | '<div class="x-list-body"><div class="x-list-body-inner">', |
---|
217 | '</div></div>' |
---|
218 | ); |
---|
219 | } |
---|
220 | if(!this.tpl){ |
---|
221 | this.tpl = new Ext.XTemplate( |
---|
222 | '<tpl for="rows">', |
---|
223 | '<dl>', |
---|
224 | '<tpl for="parent.columns">', |
---|
225 | '<dt style="width:{[values.width*100]}%;text-align:{align};">', |
---|
226 | '<em unselectable="on"<tpl if="cls"> class="{cls}</tpl>">', |
---|
227 | '{[values.tpl.apply(parent)]}', |
---|
228 | '</em></dt>', |
---|
229 | '</tpl>', |
---|
230 | '<div class="x-clear"></div>', |
---|
231 | '</dl>', |
---|
232 | '</tpl>' |
---|
233 | ); |
---|
234 | }; |
---|
235 | |
---|
236 | var cs = this.columns, |
---|
237 | allocatedWidth = 0, |
---|
238 | colsWithWidth = 0, |
---|
239 | len = cs.length, |
---|
240 | columns = []; |
---|
241 | |
---|
242 | for(var i = 0; i < len; i++){ |
---|
243 | var c = cs[i]; |
---|
244 | if(!c.isColumn) { |
---|
245 | c.xtype = c.xtype ? (/^lv/.test(c.xtype) ? c.xtype : 'lv' + c.xtype) : 'lvcolumn'; |
---|
246 | c = Ext.create(c); |
---|
247 | } |
---|
248 | if(c.width) { |
---|
249 | allocatedWidth += c.width*100; |
---|
250 | if(allocatedWidth > this.maxColumnWidth){ |
---|
251 | c.width -= (allocatedWidth - this.maxColumnWidth) / 100; |
---|
252 | } |
---|
253 | colsWithWidth++; |
---|
254 | } |
---|
255 | columns.push(c); |
---|
256 | } |
---|
257 | |
---|
258 | cs = this.columns = columns; |
---|
259 | |
---|
260 | // auto calculate missing column widths |
---|
261 | if(colsWithWidth < len){ |
---|
262 | var remaining = len - colsWithWidth; |
---|
263 | if(allocatedWidth < this.maxColumnWidth){ |
---|
264 | var perCol = ((this.maxColumnWidth-allocatedWidth) / remaining)/100; |
---|
265 | for(var j = 0; j < len; j++){ |
---|
266 | var c = cs[j]; |
---|
267 | if(!c.width){ |
---|
268 | c.width = perCol; |
---|
269 | } |
---|
270 | } |
---|
271 | } |
---|
272 | } |
---|
273 | Ext.list.ListView.superclass.initComponent.call(this); |
---|
274 | }, |
---|
275 | |
---|
276 | onRender : function(){ |
---|
277 | this.autoEl = { |
---|
278 | cls: 'x-list-wrap' |
---|
279 | }; |
---|
280 | Ext.list.ListView.superclass.onRender.apply(this, arguments); |
---|
281 | |
---|
282 | this.internalTpl.overwrite(this.el, {columns: this.columns}); |
---|
283 | |
---|
284 | this.innerBody = Ext.get(this.el.dom.childNodes[1].firstChild); |
---|
285 | this.innerHd = Ext.get(this.el.dom.firstChild.firstChild); |
---|
286 | |
---|
287 | if(this.hideHeaders){ |
---|
288 | this.el.dom.firstChild.style.display = 'none'; |
---|
289 | } |
---|
290 | }, |
---|
291 | |
---|
292 | getTemplateTarget : function(){ |
---|
293 | return this.innerBody; |
---|
294 | }, |
---|
295 | |
---|
296 | /** |
---|
297 | * <p>Function which can be overridden which returns the data object passed to this |
---|
298 | * view's {@link #tpl template} to render the whole ListView. The returned object |
---|
299 | * shall contain the following properties:</p> |
---|
300 | * <div class="mdetail-params"><ul> |
---|
301 | * <li><b>columns</b> : String<div class="sub-desc">See <tt>{@link #columns}</tt></div></li> |
---|
302 | * <li><b>rows</b> : String<div class="sub-desc">See |
---|
303 | * <tt>{@link Ext.DataView}.{@link Ext.DataView#collectData collectData}</div></li> |
---|
304 | * </ul></div> |
---|
305 | * @param {Array} records An Array of {@link Ext.data.Record}s to be rendered into the DataView. |
---|
306 | * @param {Number} startIndex the index number of the Record being prepared for rendering. |
---|
307 | * @return {Object} A data object containing properties to be processed by a repeating |
---|
308 | * XTemplate as described above. |
---|
309 | */ |
---|
310 | collectData : function(){ |
---|
311 | var rs = Ext.list.ListView.superclass.collectData.apply(this, arguments); |
---|
312 | return { |
---|
313 | columns: this.columns, |
---|
314 | rows: rs |
---|
315 | }; |
---|
316 | }, |
---|
317 | |
---|
318 | verifyInternalSize : function(){ |
---|
319 | if(this.lastSize){ |
---|
320 | this.onResize(this.lastSize.width, this.lastSize.height); |
---|
321 | } |
---|
322 | }, |
---|
323 | |
---|
324 | // private |
---|
325 | onResize : function(w, h){ |
---|
326 | var body = this.innerBody.dom, |
---|
327 | header = this.innerHd.dom, |
---|
328 | scrollWidth = w - Ext.num(this.scrollOffset, Ext.getScrollBarWidth()) + 'px', |
---|
329 | parentNode; |
---|
330 | |
---|
331 | if(!body){ |
---|
332 | return; |
---|
333 | } |
---|
334 | parentNode = body.parentNode; |
---|
335 | if(Ext.isNumber(w)){ |
---|
336 | if(this.reserveScrollOffset || ((parentNode.offsetWidth - parentNode.clientWidth) > 10)){ |
---|
337 | body.style.width = scrollWidth; |
---|
338 | header.style.width = scrollWidth; |
---|
339 | }else{ |
---|
340 | body.style.width = w + 'px'; |
---|
341 | header.style.width = w + 'px'; |
---|
342 | setTimeout(function(){ |
---|
343 | if((parentNode.offsetWidth - parentNode.clientWidth) > 10){ |
---|
344 | body.style.width = scrollWidth; |
---|
345 | header.style.width = scrollWidth; |
---|
346 | } |
---|
347 | }, 10); |
---|
348 | } |
---|
349 | } |
---|
350 | if(Ext.isNumber(h)){ |
---|
351 | parentNode.style.height = Math.max(0, h - header.parentNode.offsetHeight) + 'px'; |
---|
352 | } |
---|
353 | }, |
---|
354 | |
---|
355 | updateIndexes : function(){ |
---|
356 | Ext.list.ListView.superclass.updateIndexes.apply(this, arguments); |
---|
357 | this.verifyInternalSize(); |
---|
358 | }, |
---|
359 | |
---|
360 | findHeaderIndex : function(header){ |
---|
361 | header = header.dom || header; |
---|
362 | var parentNode = header.parentNode, |
---|
363 | children = parentNode.parentNode.childNodes, |
---|
364 | i = 0, |
---|
365 | c; |
---|
366 | for(; c = children[i]; i++){ |
---|
367 | if(c == parentNode){ |
---|
368 | return i; |
---|
369 | } |
---|
370 | } |
---|
371 | return -1; |
---|
372 | }, |
---|
373 | |
---|
374 | setHdWidths : function(){ |
---|
375 | var els = this.innerHd.dom.getElementsByTagName('div'), |
---|
376 | i = 0, |
---|
377 | columns = this.columns, |
---|
378 | len = columns.length; |
---|
379 | |
---|
380 | for(; i < len; i++){ |
---|
381 | els[i].style.width = (columns[i].width*100) + '%'; |
---|
382 | } |
---|
383 | } |
---|
384 | }); |
---|
385 | |
---|
386 | Ext.reg('listview', Ext.list.ListView); |
---|
387 | |
---|
388 | // Backwards compatibility alias |
---|
389 | Ext.ListView = Ext.list.ListView; |
---|