[625] | 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); |
---|