]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /**\r |
2 | * This class is the base class for both {@link Ext.tree.Panel TreePanel} and\r | |
3 | * {@link Ext.grid.Panel GridPanel}.\r | |
4 | *\r | |
5 | * TablePanel aggregates:\r | |
6 | *\r | |
7 | * - a Selection Model\r | |
8 | * - a View\r | |
9 | * - a Store\r | |
10 | * - Ext.grid.header.Container\r | |
11 | * \r | |
12 | * @mixins Ext.grid.locking.Lockable\r | |
13 | */\r | |
14 | Ext.define('Ext.panel.Table', {\r | |
15 | extend: 'Ext.panel.Panel',\r | |
16 | \r | |
17 | alias: 'widget.tablepanel',\r | |
18 | \r | |
19 | requires: [\r | |
20 | 'Ext.layout.container.Fit'\r | |
21 | ],\r | |
22 | \r | |
23 | uses: [\r | |
24 | 'Ext.selection.RowModel',\r | |
25 | 'Ext.selection.CellModel',\r | |
26 | 'Ext.selection.CheckboxModel',\r | |
27 | 'Ext.grid.plugin.BufferedRenderer',\r | |
28 | 'Ext.grid.header.Container',\r | |
29 | 'Ext.grid.locking.Lockable',\r | |
30 | 'Ext.grid.NavigationModel'\r | |
31 | ],\r | |
32 | \r | |
33 | extraBaseCls: Ext.baseCSSPrefix + 'grid',\r | |
34 | extraBodyCls: Ext.baseCSSPrefix + 'grid-body',\r | |
35 | actionableModeCls: Ext.baseCSSPrefix + 'grid-actionable',\r | |
36 | noHeaderBordersCls: Ext.baseCSSPrefix + 'no-header-borders',\r | |
37 | \r | |
38 | defaultBindProperty: 'store',\r | |
39 | \r | |
40 | layout: 'fit',\r | |
41 | \r | |
42 | ariaRole: 'grid',\r | |
43 | \r | |
44 | config: {\r | |
45 | /**\r | |
46 | * @cfg {Ext.data.Model} selection\r | |
47 | * The selected model. Typically used with {@link #bind binding}.\r | |
48 | */\r | |
49 | selection: null,\r | |
50 | \r | |
51 | /**\r | |
52 | * @cfg {Boolean} [headerBorders=`true`]\r | |
53 | * To show no borders around grid headers, configure this as `false`.\r | |
54 | */\r | |
55 | headerBorders: true\r | |
56 | },\r | |
57 | \r | |
58 | publishes: ['selection'],\r | |
59 | twoWayBindable: ['selection'],\r | |
60 | \r | |
61 | /**\r | |
62 | * @cfg {Boolean} [autoLoad=false]\r | |
63 | * Use `true` to load the store as soon as this component is fully constructed. It is\r | |
64 | * best to initiate the store load this way to allow this component and potentially\r | |
65 | * its plugins (such as `{@link Ext.grid.filters.Filters}`) to be ready to load.\r | |
66 | */\r | |
67 | autoLoad: false,\r | |
68 | \r | |
69 | /**\r | |
70 | * @cfg {Boolean} [variableRowHeight=false]\r | |
71 | * @deprecated 5.0.0 Use {@link Ext.grid.column.Column#variableRowHeight} instead.\r | |
72 | * Configure as `true` if the row heights are not all the same height as the first row.\r | |
73 | */\r | |
74 | variableRowHeight: false,\r | |
75 | \r | |
76 | /**\r | |
77 | * @cfg {Number} [numFromEdge]\r | |
78 | * This configures the zone which causes new rows to be appended to the view. As soon as the edge\r | |
79 | * of the rendered grid is this number of rows from the edge of the viewport, the view is moved.\r | |
80 | */\r | |
81 | numFromEdge: 2,\r | |
82 | \r | |
83 | /**\r | |
84 | * @cfg {Number} [trailingBufferZone]\r | |
85 | * TableViews are buffer rendered in 5.x and above which means that only the visible subset of data rows\r | |
86 | * are rendered into the DOM. These are removed and added as scrolling demands.\r | |
87 | *\r | |
88 | * This configures the number of extra rows to render on the trailing side of scrolling\r | |
89 | * **outside the {@link #numFromEdge}** buffer as scrolling proceeds.\r | |
90 | */\r | |
91 | trailingBufferZone: 10,\r | |
92 | \r | |
93 | /**\r | |
94 | * @cfg {Number} [leadingBufferZone]\r | |
95 | * TableViews are buffer rendered in 5.x and above which means that only the visible subset of data rows\r | |
96 | * are rendered into the DOM. These are removed and added as scrolling demands.\r | |
97 | *\r | |
98 | * This configures the number of extra rows to render on the leading side of scrolling\r | |
99 | * **outside the {@link #numFromEdge}** buffer as scrolling proceeds.\r | |
100 | */\r | |
101 | leadingBufferZone: 20,\r | |
102 | \r | |
103 | /**\r | |
104 | * @property {Boolean} hasView\r | |
105 | * True to indicate that a view has been injected into the panel.\r | |
106 | */\r | |
107 | hasView: false,\r | |
108 | \r | |
109 | /**\r | |
110 | * @property items\r | |
111 | * @hide\r | |
112 | */\r | |
113 | \r | |
114 | /**\r | |
115 | * @cfg {String} viewType\r | |
116 | * An xtype of view to use. This is automatically set to 'tableview' by {@link Ext.grid.Panel Grid}\r | |
117 | * and to 'treeview' by {@link Ext.tree.Panel Tree}.\r | |
118 | * @protected\r | |
119 | */\r | |
120 | viewType: null,\r | |
121 | \r | |
122 | /**\r | |
123 | * @cfg {Object} viewConfig\r | |
124 | * A config object that will be applied to the grid's UI view. Any of the config options available for\r | |
125 | * {@link Ext.view.Table} can be specified here. This option is ignored if {@link #view} is specified.\r | |
126 | */\r | |
127 | \r | |
128 | /**\r | |
129 | * @cfg {Ext.view.Table} view\r | |
130 | * The {@link Ext.view.Table} used by the grid. Use {@link #viewConfig} to just supply some config options to\r | |
131 | * view (instead of creating an entire View instance).\r | |
132 | */\r | |
133 | \r | |
134 | /**\r | |
135 | * @cfg {String} [selType]\r | |
136 | * An xtype of selection model to use. This is used to create selection model if just\r | |
137 | * a config object or nothing at all given in {@link #selModel} config.\r | |
138 | *\r | |
139 | * @deprecated 5.1.0 Use the {@link #selModel}'s `type` property. Or, if no other\r | |
140 | * configs are required, use the string form of selModel.\r | |
141 | */\r | |
142 | \r | |
143 | /**\r | |
144 | * @cfg {Ext.selection.Model/Object/String} [selModel=rowmodel]\r | |
145 | * A {@link Ext.selection.Model selection model} instance or config object, or the selection model class's alias string.\r | |
146 | *\r | |
147 | * In latter case its `type` property determines to which type of selection model this config is applied.\r | |
148 | */\r | |
149 | \r | |
150 | /**\r | |
151 | * @cfg {Boolean} [multiSelect=false]\r | |
152 | * True to enable 'MULTI' selection mode on selection model.\r | |
153 | * @deprecated 4.1.1 Use {@link Ext.selection.Model#mode} 'MULTI' instead.\r | |
154 | */\r | |
155 | \r | |
156 | /**\r | |
157 | * @cfg {Boolean} [simpleSelect=false]\r | |
158 | * True to enable 'SIMPLE' selection mode on selection model.\r | |
159 | * @deprecated 4.1.1 Use {@link Ext.selection.Model#mode} 'SIMPLE' instead.\r | |
160 | */\r | |
161 | \r | |
162 | /**\r | |
163 | * @cfg {Ext.data.Store/String/Object} store (required)\r | |
164 | * The data source to which the grid / tree is bound. Acceptable values for this \r | |
165 | * property are:\r | |
166 | *\r | |
167 | * - **any {@link Ext.data.Store Store} class / subclass**\r | |
168 | * - **an {@link Ext.data.Store#storeId ID of a store}**\r | |
169 | * - **a {@link Ext.data.Store Store} config object**. When passing a config you can \r | |
170 | * specify the store type by alias. Passing a config object with a store type will \r | |
171 | * dynamically create a new store of that type when the grid / tree is instantiated.\r | |
172 | *\r | |
173 | * For example:\r | |
174 | * \r | |
175 | * Ext.define('MyApp.store.Customers', {\r | |
176 | * extend: 'Ext.data.Store',\r | |
177 | * alias: 'store.customerstore',\r | |
178 | * fields: ['name']\r | |
179 | * });\r | |
180 | * \r | |
181 | * Ext.create({\r | |
182 | * xtype: 'gridpanel',\r | |
183 | * renderTo: document.body,\r | |
184 | * store: {\r | |
185 | * type: 'customerstore',\r | |
186 | * data: [{\r | |
187 | * name: 'Foo'\r | |
188 | * }]\r | |
189 | * },\r | |
190 | * columns: [{\r | |
191 | * text: 'Name',\r | |
192 | * dataIndex: 'name'\r | |
193 | * }]\r | |
194 | * });\r | |
195 | */\r | |
196 | \r | |
197 | /**\r | |
198 | * @cfg {String/Boolean} scroll\r | |
199 | * Scrollers configuration. Valid values are 'both', 'horizontal' or 'vertical'.\r | |
200 | * True implies 'both'. False implies 'none'.\r | |
201 | * @deprecated 5.1.0 Use {@link #scrollable} instead\r | |
202 | */\r | |
203 | \r | |
204 | /**\r | |
205 | * @cfg {Boolean} [reserveScrollbar=false]\r | |
206 | * Set this to true to **always** leave a scrollbar sized space at the end of the grid content when\r | |
207 | * fitting content into the width of the grid.\r | |
208 | *\r | |
209 | * If the grid's record count fluctuates enough to hide and show the scrollbar regularly, this setting\r | |
210 | * avoids the multiple layouts associated with switching from scrollbar present to scrollbar not present.\r | |
211 | */\r | |
212 | \r | |
213 | /**\r | |
214 | * @cfg {Ext.grid.column.Column[]/Object} columns\r | |
215 | * An array of {@link Ext.grid.column.Column column} definition objects which define all columns that appear in this\r | |
216 | * grid. Each column definition provides the header text for the column, and a definition of where the data for that\r | |
217 | * column comes from.\r | |
218 | *\r | |
219 | * This can also be a configuration object for a {@link Ext.grid.header.Container HeaderContainer} which may override\r | |
220 | * certain default configurations if necessary. For example, the special layout may be overridden to use a simpler\r | |
221 | * layout, or one can set default values shared by all columns:\r | |
222 | * \r | |
223 | * columns: {\r | |
224 | * items: [\r | |
225 | * {\r | |
226 | * text: "Column A",\r | |
227 | * dataIndex: "field_A"\r | |
228 | * },{\r | |
229 | * text: "Column B",\r | |
230 | * dataIndex: "field_B"\r | |
231 | * }, \r | |
232 | * ...\r | |
233 | * ],\r | |
234 | * defaults: {\r | |
235 | * flex: 1\r | |
236 | * }\r | |
237 | * }\r | |
238 | */\r | |
239 | \r | |
240 | /**\r | |
241 | * @cfg {Boolean} forceFit\r | |
242 | * True to force the columns to fit into the available width. Headers are first sized according to configuration,\r | |
243 | * whether that be a specific width, or flex. Then they are all proportionally changed in width so that the entire\r | |
244 | * content width is used. For more accurate control, it is more optimal to specify a flex setting on the columns\r | |
245 | * that are to be stretched & explicit widths on columns that are not.\r | |
246 | */\r | |
247 | \r | |
248 | /**\r | |
249 | * @cfg {Ext.grid.feature.Feature[]/Object[]/Ext.enums.Feature[]} features\r | |
250 | * An array of grid Features to be added to this grid. Can also be just a single feature instead of array.\r | |
251 | *\r | |
252 | * Features config behaves much like {@link #plugins}.\r | |
253 | * A feature can be added by either directly referencing the instance:\r | |
254 | *\r | |
255 | * features: [Ext.create('Ext.grid.feature.GroupingSummary', {groupHeaderTpl: 'Subject: {name}'})],\r | |
256 | *\r | |
257 | * By using config object with ftype:\r | |
258 | *\r | |
259 | * features: [{ftype: 'groupingsummary', groupHeaderTpl: 'Subject: {name}'}],\r | |
260 | *\r | |
261 | * Or with just a ftype:\r | |
262 | *\r | |
263 | * features: ['grouping', 'groupingsummary'],\r | |
264 | *\r | |
265 | * See {@link Ext.enums.Feature} for list of all ftypes.\r | |
266 | */\r | |
267 | \r | |
268 | /**\r | |
269 | * @cfg {Boolean} [hideHeaders=false]\r | |
270 | * True to hide column headers.\r | |
271 | */\r | |
272 | \r | |
273 | /**\r | |
274 | * @cfg {Boolean} [deferRowRender=false]\r | |
275 | * Configure as `true` to enable deferred row rendering.\r | |
276 | *\r | |
277 | * This allows the View to execute a refresh quickly, with the update of the row structure deferred so\r | |
278 | * that layouts with GridPanels appear, and lay out more quickly.\r | |
279 | */\r | |
280 | deferRowRender: false,\r | |
281 | \r | |
282 | /**\r | |
283 | * @cfg {Boolean} [sortableColumns=true]\r | |
284 | * False to disable column sorting via clicking the header and via the Sorting menu items.\r | |
285 | */\r | |
286 | sortableColumns: true,\r | |
287 | \r | |
288 | /**\r | |
289 | * @cfg {Boolean} [multiColumnSort=false]\r | |
290 | * Configure as `true` to have columns remember their sorted state after other columns have been clicked upon to sort.\r | |
291 | *\r | |
292 | * As subsequent columns are clicked upon, they become the new primary sort key.\r | |
293 | *\r | |
294 | * The maximum number of sorters allowed in a Store is configurable via its underlying data collection. See {@link Ext.util.Collection#multiSortLimit}\r | |
295 | */\r | |
296 | multiColumnSort: false,\r | |
297 | \r | |
298 | /**\r | |
299 | * @cfg {Boolean} [enableLocking=false]\r | |
300 | * Configure as `true` to enable locking support for this grid. Alternatively, locking will also be automatically\r | |
301 | * enabled if any of the columns in the {@link #columns columns} configuration contain a {@link Ext.grid.column.Column#locked locked} config option.\r | |
302 | * \r | |
303 | * A locking grid is processed in a special way. The configuration options are cloned and *two* grids are created to be the locked (left) side\r | |
304 | * and the normal (right) side. This Panel becomes merely a {@link Ext.container.Container container} which arranges both in an {@link Ext.layout.container.HBox HBox} layout.\r | |
305 | * \r | |
306 | * {@link #plugins Plugins} may be targeted at either locked, or unlocked grid, or, both, in which case the plugin is cloned and used on both sides.\r | |
307 | * \r | |
308 | * Plugins may also be targeted at the containing locking Panel.\r | |
309 | * \r | |
310 | * This is configured by specifying a `lockableScope` property in your plugin which may have the following values:\r | |
311 | * \r | |
312 | * * `"both"` (the default) - The plugin is added to both grids\r | |
313 | * * `"top"` - The plugin is added to the containing Panel\r | |
314 | * * `"locked"` - The plugin is added to the locked (left) grid\r | |
315 | * * `"normal"` - The plugin is added to the normal (right) grid\r | |
316 | *\r | |
317 | * If `both` is specified, then each copy of the plugin gains a property `lockingPartner` which references its sibling on the other side so that they\r | |
318 | * can synchronize operations is necessary.\r | |
319 | * \r | |
320 | * {@link #features Features} may also be configured with `lockableScope` and may target the locked grid, the normal grid or both grids. Features\r | |
321 | * also get a `lockingPartner` reference injected.\r | |
322 | */\r | |
323 | enableLocking: false,\r | |
324 | \r | |
325 | /**\r | |
326 | * @private\r | |
327 | * Used to determine where to go down to find views\r | |
328 | * this is here to support locking.\r | |
329 | */\r | |
330 | scrollerOwner: true,\r | |
331 | \r | |
332 | /**\r | |
333 | * @cfg {Boolean} [enableColumnMove=true]\r | |
334 | * False to disable column dragging within this grid.\r | |
335 | */\r | |
336 | enableColumnMove: true,\r | |
337 | \r | |
338 | /**\r | |
339 | * @cfg {Boolean} [sealedColumns=false]\r | |
340 | * True to constrain column dragging so that a column cannot be dragged in or out of it's\r | |
341 | * current group. Only relevant while {@link #enableColumnMove} is enabled.\r | |
342 | */\r | |
343 | sealedColumns: false,\r | |
344 | \r | |
345 | /**\r | |
346 | * @cfg {Boolean} [enableColumnResize=true]\r | |
347 | * False to disable column resizing within this grid.\r | |
348 | */\r | |
349 | enableColumnResize: true,\r | |
350 | \r | |
351 | /**\r | |
352 | * @cfg {Boolean} [enableColumnHide=true]\r | |
353 | * False to disable column hiding within this grid.\r | |
354 | */\r | |
355 | \r | |
356 | /**\r | |
357 | * @cfg {Boolean} columnLines Adds column line styling\r | |
358 | */\r | |
359 | \r | |
360 | /**\r | |
361 | * @cfg {Boolean} [rowLines=true] Adds row line styling\r | |
362 | */\r | |
363 | rowLines: true,\r | |
364 | \r | |
365 | /**\r | |
366 | * @cfg {Boolean} [disableSelection=false]\r | |
367 | * True to disable selection model.\r | |
368 | */\r | |
369 | \r | |
370 | /**\r | |
371 | * @cfg {String} emptyText Default text (HTML tags are accepted) to display in the \r | |
372 | * Panel body when the Store is empty. When specified, and the Store is empty, the \r | |
373 | * text will be rendered inside a DIV with the CSS class "x-grid-empty". The emptyText \r | |
374 | * will not display until the first load of the associated store by default. If you \r | |
375 | * want the text to be displayed prior to the first store load use the \r | |
376 | * {@link Ext.view.Table#deferEmptyText deferEmptyText} config in the {@link #viewConfig} config.\r | |
377 | */\r | |
378 | \r | |
379 | /**\r | |
380 | * @cfg {Boolean} [allowDeselect=false]\r | |
381 | * True to allow deselecting a record. This config is forwarded to {@link Ext.selection.Model#allowDeselect}.\r | |
382 | */\r | |
383 | \r | |
384 | /**\r | |
385 | * @cfg {Boolean} [bufferedRenderer=true]\r | |
386 | * Buffered rendering is enabled by default.\r | |
387 | * \r | |
388 | * Configure as `false` to disable buffered rendering. See {@link Ext.grid.plugin.BufferedRenderer}.\r | |
389 | *\r | |
390 | * @since 5.0.0\r | |
391 | */\r | |
392 | bufferedRenderer: true,\r | |
393 | \r | |
394 | /**\r | |
395 | * @cfg stateEvents\r | |
396 | * @inheritdoc Ext.state.Stateful#cfg-stateEvents\r | |
397 | * @localdoc By default the following stateEvents are added:\r | |
398 | * \r | |
399 | * - {@link #event-resize} - _(added by Ext.Component)_\r | |
400 | * - {@link #event-collapse} - _(added by Ext.panel.Panel)_\r | |
401 | * - {@link #event-expand} - _(added by Ext.panel.Panel)_\r | |
402 | * - {@link #event-columnresize}\r | |
403 | * - {@link #event-columnmove}\r | |
404 | * - {@link #event-columnhide}\r | |
405 | * - {@link #event-columnshow}\r | |
406 | * - {@link #event-sortchange}\r | |
407 | * - {@link #event-filterchange}\r | |
408 | * - {@link #event-groupchange}\r | |
409 | */\r | |
410 | \r | |
411 | /**\r | |
412 | * @property {Boolean} optimizedColumnMove\r | |
413 | * If you are writing a grid plugin or a {Ext.grid.feature.Feature Feature} which creates a column-based structure which\r | |
414 | * needs a view refresh when columns are moved, then set this property in the grid.\r | |
415 | *\r | |
416 | * An example is the built in {@link Ext.grid.feature.AbstractSummary Summary} Feature. This creates summary rows, and the\r | |
417 | * summary columns must be in the same order as the data columns. This plugin sets the `optimizedColumnMove` to `false.\r | |
418 | */\r | |
419 | \r | |
420 | /**\r | |
421 | * @property {Ext.panel.Table} ownerGrid\r | |
422 | * A reference to the top-level owning grid component.\r | |
423 | * \r | |
424 | * This is a reference to this GridPanel if this GridPanel is not part of a locked grid arrangement.\r | |
425 | * @readonly\r | |
426 | * @private\r | |
427 | * @since 5.0.0\r | |
428 | */\r | |
429 | ownerGrid: null,\r | |
430 | \r | |
431 | colLinesCls: Ext.baseCSSPrefix + 'grid-with-col-lines',\r | |
432 | rowLinesCls: Ext.baseCSSPrefix + 'grid-with-row-lines',\r | |
433 | noRowLinesCls: Ext.baseCSSPrefix + 'grid-no-row-lines',\r | |
434 | hiddenHeaderCtCls: Ext.baseCSSPrefix + 'grid-header-ct-hidden',\r | |
435 | hiddenHeaderCls: Ext.baseCSSPrefix + 'grid-header-hidden',\r | |
436 | resizeMarkerCls: Ext.baseCSSPrefix + 'grid-resize-marker',\r | |
437 | emptyCls: Ext.baseCSSPrefix + 'grid-empty',\r | |
438 | \r | |
439 | // The TablePanel claims to be focusable, but it does not place a tabIndex\r | |
440 | // on any of its elements.\r | |
441 | // Its focus implementation delegates to its view. TableViews are focusable.\r | |
442 | focusable: true,\r | |
443 | \r | |
444 | /**\r | |
445 | * @event viewready\r | |
446 | * Fires when the grid view is available (use this for selecting a default row).\r | |
447 | * @param {Ext.panel.Table} this\r | |
448 | */\r | |
449 | \r | |
450 | constructor: function (config) {\r | |
451 | var me = this,\r | |
452 | topGrid = config && config.ownerGrid,\r | |
453 | store;\r | |
454 | \r | |
455 | me.ownerGrid = topGrid || me;\r | |
456 | \r | |
457 | /**\r | |
458 | * @property {Array} actionables An array of objects which register themselves with a grid panel using\r | |
459 | * {@link #registerActionable} which are consulted upon entry into actionable mode.\r | |
460 | *\r | |
461 | * These must implement the following methods:\r | |
462 | *\r | |
463 | * - activateCell Called when actionable mode is requested upon a cell. A {@link Ext.grid.CellContext CellContext}\r | |
464 | * object is passed. If that cell is actionable by the terms of the callee, the callee should return `true` if it\r | |
465 | * ascertains that the cell is actionable, and that it now contains focusable elements which may be tabbed to. \r | |
466 | * - activateRow Called when the user enters actionable mode in a row. The row DOM is passed. Actionables\r | |
467 | * should take any action they need to prime the row for cell activation which happens as users TAB from cell to cell.\r | |
468 | * @readonly\r | |
469 | */\r | |
470 | me.actionables = topGrid ? topGrid.actionables : []; // One shared array when there's a lockable at the top\r | |
471 | \r | |
472 | me.callParent([config]);\r | |
473 | \r | |
474 | store = me.store;\r | |
475 | \r | |
476 | // Any further changes become stateful.\r | |
477 | store.trackStateChanges = true;\r | |
478 | \r | |
479 | if (me.autoLoad) {\r | |
480 | // Note: if there is a store bound by a VM, we (might) do the load in #setStore.\r | |
481 | if (!store.isEmptyStore) {\r | |
482 | store.load();\r | |
483 | }\r | |
484 | }\r | |
485 | },\r | |
486 | \r | |
487 | /**\r | |
488 | * \r | |
489 | * @param {Object} actionable An object which has an interest in the implementation of actionable mode in\r | |
490 | * this grid.\r | |
491 | *\r | |
492 | * An actionable object may be a Plugin which upon activation injects tabbable elements or Components into\r | |
493 | * a grid row.\r | |
494 | */\r | |
495 | registerActionable: function(actionable) {\r | |
496 | // If a lockableScope: 'both' plugin/feature registers on each side, only include it in the actionables once.\r | |
497 | Ext.Array.include(this.actionables, actionable);\r | |
498 | },\r | |
499 | \r | |
500 | initComponent: function() {\r | |
501 | //<debug>\r | |
502 | if (this.verticalScroller) {\r | |
503 | Ext.raise("The verticalScroller config is not supported.");\r | |
504 | }\r | |
505 | if (!this.viewType) {\r | |
506 | Ext.raise("You must specify a viewType config.");\r | |
507 | }\r | |
508 | if (this.headers) {\r | |
509 | Ext.raise("The headers config is not supported. Please specify columns instead.");\r | |
510 | }\r | |
511 | //</debug>\r | |
512 | \r | |
513 | var me = this,\r | |
514 | headerCtCfg = me.columns || me.colModel || [],\r | |
515 | store, view, i, len, bufferedRenderer, columns, viewScroller, headerCt;\r | |
516 | \r | |
517 | // Look up the configured Store. If none configured, use the fieldless, empty Store\r | |
518 | // defined in Ext.data.Store.\r | |
519 | store = me.store = Ext.data.StoreManager.lookup(me.store || 'ext-empty-store');\r | |
520 | \r | |
521 | me.enableLocking = me.enableLocking || me.hasLockedColumns(headerCtCfg);\r | |
522 | \r | |
523 | // Construct the plugins now rather than in the constructor of AbstractComponent because the component may have a subclass\r | |
524 | // that has overridden initComponent and defined plugins in it. For plugins like RowExpander that rely upon a grid feature,\r | |
525 | // this is a problem because the view needs to know about all its features before it's constructed. Constructing the plugins\r | |
526 | // now ensures that plugins defined in the instance config or in initComponent are all constructed before the view.\r | |
527 | // See EXTJSIV-11927.\r | |
528 | //\r | |
529 | // Note that any components that do not inherit from this class will still have their plugins constructed in\r | |
530 | // AbstractComponent:initComponent.\r | |
531 | if (me.plugins) {\r | |
532 | me.plugins = me.constructPlugins();\r | |
533 | }\r | |
534 | \r | |
535 | // Add the row/column line classes to the body element so that the settings are not inherited by docked grids (https://sencha.jira.com/browse/EXTJSIV-9263).\r | |
536 | if (me.columnLines) {\r | |
537 | me.addBodyCls(me.colLinesCls);\r | |
538 | }\r | |
539 | \r | |
540 | me.addBodyCls(me.rowLines ? me.rowLinesCls : me.noRowLinesCls);\r | |
541 | me.addBodyCls(me.extraBodyCls);\r | |
542 | \r | |
543 | \r | |
544 | // If any of the Column objects contain a locked property, and are not processed, this is a lockable TablePanel, a\r | |
545 | // special view will be injected by the Ext.grid.locking.Lockable mixin, so no processing of .\r | |
546 | if (me.enableLocking) {\r | |
547 | me.self.mixin('lockable', Ext.grid.locking.Lockable);\r | |
548 | me.injectLockable();\r | |
549 | headerCt = me.headerCt;\r | |
550 | }\r | |
551 | // Not lockable - create the HeaderContainer\r | |
552 | else {\r | |
553 | // It's a fully instantiated HeaderContainer\r | |
554 | if (headerCtCfg.isRootHeader) {\r | |
555 | if (me.hideHeaders) {\r | |
556 | headerCtCfg.setHeight(0);\r | |
557 | // don't set the hidden property, we still need these to layout\r | |
558 | headerCtCfg.hiddenHeaders = true;\r | |
559 | } else {\r | |
560 | // the header container is not user scrollable, but it has a scroller instance\r | |
561 | // so that we can sync its scroll position with that of the grid view\r | |
562 | headerCtCfg.setScrollable({\r | |
563 | x: false,\r | |
564 | y: false\r | |
565 | });\r | |
566 | }\r | |
567 | \r | |
568 | me.headerCt = headerCt = headerCtCfg;\r | |
569 | \r | |
570 | headerCt.grid = me;\r | |
571 | headerCt.forceFit = !!me.forceFit;\r | |
572 | headerCt.$initParent = me;\r | |
573 | \r | |
574 | // If it's an instance then the column managers were already created and bound to the headerCt.\r | |
575 | me.columnManager = headerCtCfg.columnManager;\r | |
576 | me.visibleColumnManager = headerCtCfg.visibleColumnManager;\r | |
577 | }\r | |
578 | // It's an array of Column definitions, or a config object of a HeaderContainer\r | |
579 | else {\r | |
580 | if (Ext.isArray(headerCtCfg)) {\r | |
581 | headerCtCfg = {\r | |
582 | items: headerCtCfg\r | |
583 | };\r | |
584 | }\r | |
585 | Ext.apply(headerCtCfg, {\r | |
586 | grid: me,\r | |
587 | $initParent: me,\r | |
588 | forceFit: me.forceFit,\r | |
589 | sortable: me.sortableColumns,\r | |
590 | enableColumnMove: me.enableColumnMove,\r | |
591 | enableColumnResize: me.enableColumnResize,\r | |
592 | columnLines: me.columnLines,\r | |
593 | sealed: me.sealedColumns,\r | |
594 | // the header container is not user scrollable, but if it is visible,\r | |
595 | // it has a scroller instance so that we can sync its scroll position with that of the grid view\r | |
596 | scrollable: me.hideHeaders ? undefined : {\r | |
597 | x: false,\r | |
598 | y: false\r | |
599 | }\r | |
600 | });\r | |
601 | if (me.hideHeaders) {\r | |
602 | headerCtCfg.height = 0;\r | |
603 | // don't set the hidden property, we still need these to layout\r | |
604 | headerCtCfg.hiddenHeaders = true;\r | |
605 | }\r | |
606 | \r | |
607 | if (Ext.isDefined(me.enableColumnHide)) {\r | |
608 | headerCtCfg.enableColumnHide = me.enableColumnHide;\r | |
609 | }\r | |
610 | me.headerCt = headerCt = new Ext.grid.header.Container(headerCtCfg);\r | |
611 | }\r | |
612 | }\r | |
613 | \r | |
614 | // Maintain backward compatibiliy by providing the initial leaf column set as a property.\r | |
615 | me.columns = columns = headerCt.getGridColumns();\r | |
616 | \r | |
617 | me.scrollTask = new Ext.util.DelayedTask(me.syncHorizontalScroll, me);\r | |
618 | \r | |
619 | me.cls = (me.cls || '') + (' ' + me.extraBaseCls);\r | |
620 | \r | |
621 | // autoScroll is not a valid configuration\r | |
622 | delete me.autoScroll;\r | |
623 | \r | |
624 | bufferedRenderer = me.plugins && Ext.Array.findBy(me.plugins, function(p) {\r | |
625 | return p.isBufferedRenderer;\r | |
626 | });\r | |
627 | \r | |
628 | // If we find one in the plugins, just use that.\r | |
629 | if (bufferedRenderer) {\r | |
630 | me.bufferedRenderer = bufferedRenderer;\r | |
631 | }\r | |
632 | \r | |
633 | // If this TablePanel is lockable (Either configured lockable, or any of the defined columns has a 'locked' property)\r | |
634 | // then a special lockable view containing 2 side-by-side grids will have been injected so we do not need to set up any UI.\r | |
635 | if (!me.hasView) {\r | |
636 | \r | |
637 | // If the Store is paging blocks of the dataset in, then it can only be sorted remotely.\r | |
638 | if (store.isBufferedStore && !store.getRemoteSort()) {\r | |
639 | for (i = 0, len = columns.length; i < len; i++) {\r | |
640 | columns[i].sortable = false;\r | |
641 | }\r | |
642 | }\r | |
643 | \r | |
644 | if (me.hideHeaders) {\r | |
645 | me.headerCt.addCls(me.hiddenHeaderCtCls);\r | |
646 | me.addCls(me.hiddenHeaderCls);\r | |
647 | }\r | |
648 | \r | |
649 | me.relayHeaderCtEvents(headerCt);\r | |
650 | me.features = me.features || [];\r | |
651 | if (!Ext.isArray(me.features)) {\r | |
652 | me.features = [me.features];\r | |
653 | }\r | |
654 | me.dockedItems = [].concat(me.dockedItems || []);\r | |
655 | me.dockedItems.unshift(headerCt);\r | |
656 | me.viewConfig = me.viewConfig || {};\r | |
657 | \r | |
658 | // AbstractDataView will look up a Store configured as an object\r | |
659 | // getView converts viewConfig into a View instance\r | |
660 | view = me.getView();\r | |
661 | \r | |
662 | me.items = [view];\r | |
663 | me.hasView = true;\r | |
664 | \r | |
665 | // Add a listener to synchronize the horizontal scroll position of the headers\r | |
666 | // with the table view's element... Unless we are not showing headers!\r | |
667 | if (!me.hideHeaders) {\r | |
668 | // sync the horizontal scroll position of the headerCt as the view is scrolled.\r | |
669 | viewScroller = view.getScrollable();\r | |
670 | if (viewScroller) {\r | |
671 | headerCt.getScrollable().addPartner(viewScroller, 'x');\r | |
672 | }\r | |
673 | }\r | |
674 | \r | |
675 | // Attach this Panel to the Store\r | |
676 | me.bindStore(store, true);\r | |
677 | \r | |
678 | me.mon(view, {\r | |
679 | viewready: me.onViewReady,\r | |
680 | refresh: me.onRestoreHorzScroll,\r | |
681 | scope: me\r | |
682 | });\r | |
683 | }\r | |
684 | \r | |
685 | // Whatever kind of View we have, be it a TableView, or a LockingView, we are interested in the selection model\r | |
686 | me.selModel = me.view.getSelectionModel();\r | |
687 | if (me.selModel.isRowModel) {\r | |
688 | me.selModel.on({\r | |
689 | scope: me,\r | |
690 | lastselectedchanged: me.updateBindSelection,\r | |
691 | selectionchange: me.updateBindSelection\r | |
692 | });\r | |
693 | }\r | |
694 | \r | |
695 | // Relay events from the View whether it be a LockingView, or a regular GridView\r | |
696 | me.relayEvents(me.view, [\r | |
697 | /**\r | |
698 | * @event beforeitemmousedown\r | |
699 | * @inheritdoc Ext.view.View#beforeitemmousedown\r | |
700 | */\r | |
701 | 'beforeitemmousedown',\r | |
702 | /**\r | |
703 | * @event beforeitemmouseup\r | |
704 | * @inheritdoc Ext.view.View#beforeitemmouseup\r | |
705 | */\r | |
706 | 'beforeitemmouseup',\r | |
707 | /**\r | |
708 | * @event beforeitemmouseenter\r | |
709 | * @inheritdoc Ext.view.View#beforeitemmouseenter\r | |
710 | */\r | |
711 | 'beforeitemmouseenter',\r | |
712 | /**\r | |
713 | * @event beforeitemmouseleave\r | |
714 | * @inheritdoc Ext.view.View#beforeitemmouseleave\r | |
715 | */\r | |
716 | 'beforeitemmouseleave',\r | |
717 | /**\r | |
718 | * @event beforeitemclick\r | |
719 | * @inheritdoc Ext.view.View#beforeitemclick\r | |
720 | */\r | |
721 | 'beforeitemclick',\r | |
722 | /**\r | |
723 | * @event beforeitemdblclick\r | |
724 | * @inheritdoc Ext.view.View#beforeitemdblclick\r | |
725 | */\r | |
726 | 'beforeitemdblclick',\r | |
727 | /**\r | |
728 | * @event beforeitemcontextmenu\r | |
729 | * @inheritdoc Ext.view.View#beforeitemcontextmenu\r | |
730 | */\r | |
731 | 'beforeitemcontextmenu',\r | |
732 | /**\r | |
733 | * @event itemmousedown\r | |
734 | * @inheritdoc Ext.view.View#itemmousedown\r | |
735 | */\r | |
736 | 'itemmousedown',\r | |
737 | /**\r | |
738 | * @event itemmouseup\r | |
739 | * @inheritdoc Ext.view.View#itemmouseup\r | |
740 | */\r | |
741 | 'itemmouseup',\r | |
742 | /**\r | |
743 | * @event itemmouseenter\r | |
744 | * @inheritdoc Ext.view.View#itemmouseenter\r | |
745 | */\r | |
746 | 'itemmouseenter',\r | |
747 | /**\r | |
748 | * @event itemmouseleave\r | |
749 | * @inheritdoc Ext.view.View#itemmouseleave\r | |
750 | */\r | |
751 | 'itemmouseleave',\r | |
752 | /**\r | |
753 | * @event itemclick\r | |
754 | * @inheritdoc Ext.view.View#itemclick\r | |
755 | */\r | |
756 | 'itemclick',\r | |
757 | /**\r | |
758 | * @event itemdblclick\r | |
759 | * @inheritdoc Ext.view.View#itemdblclick\r | |
760 | */\r | |
761 | 'itemdblclick',\r | |
762 | /**\r | |
763 | * @event itemcontextmenu\r | |
764 | * @inheritdoc Ext.view.View#itemcontextmenu\r | |
765 | */\r | |
766 | 'itemcontextmenu',\r | |
767 | /**\r | |
768 | * @event beforecellclick\r | |
769 | * @inheritdoc Ext.view.Table#beforecellclick\r | |
770 | */\r | |
771 | 'beforecellclick',\r | |
772 | /**\r | |
773 | * @event cellclick\r | |
774 | * @inheritdoc Ext.view.Table#cellclick\r | |
775 | */\r | |
776 | 'cellclick',\r | |
777 | /**\r | |
778 | * @event beforecelldblclick\r | |
779 | * @inheritdoc Ext.view.Table#beforecelldblclick\r | |
780 | */\r | |
781 | 'beforecelldblclick',\r | |
782 | /**\r | |
783 | * @event celldblclick\r | |
784 | * @inheritdoc Ext.view.Table#celldblclick\r | |
785 | */\r | |
786 | 'celldblclick',\r | |
787 | /**\r | |
788 | * @event beforecellcontextmenu\r | |
789 | * @inheritdoc Ext.view.Table#beforecellcontextmenu\r | |
790 | */\r | |
791 | 'beforecellcontextmenu',\r | |
792 | /**\r | |
793 | * @event cellcontextmenu\r | |
794 | * @inheritdoc Ext.view.Table#cellcontextmenu\r | |
795 | */\r | |
796 | 'cellcontextmenu',\r | |
797 | /**\r | |
798 | * @event beforecellmousedown\r | |
799 | * @inheritdoc Ext.view.Table#beforecellmousedown\r | |
800 | */\r | |
801 | 'beforecellmousedown',\r | |
802 | /**\r | |
803 | * @event cellmousedown\r | |
804 | * @inheritdoc Ext.view.Table#cellmousedown\r | |
805 | */\r | |
806 | 'cellmousedown',\r | |
807 | /**\r | |
808 | * @event beforecellmouseup\r | |
809 | * @inheritdoc Ext.view.Table#beforecellmouseup\r | |
810 | */\r | |
811 | 'beforecellmouseup',\r | |
812 | /**\r | |
813 | * @event cellmouseup\r | |
814 | * @inheritdoc Ext.view.Table#cellmouseup\r | |
815 | */\r | |
816 | 'cellmouseup',\r | |
817 | /**\r | |
818 | * @event beforecellkeydown\r | |
819 | * @inheritdoc Ext.view.Table#beforecellkeydown\r | |
820 | */\r | |
821 | 'beforecellkeydown',\r | |
822 | /**\r | |
823 | * @event cellkeydown\r | |
824 | * @inheritdoc Ext.view.Table#cellkeydown\r | |
825 | */\r | |
826 | 'cellkeydown',\r | |
827 | /**\r | |
828 | * @event rowclick\r | |
829 | * @inheritdoc Ext.view.Table#rowclick\r | |
830 | */\r | |
831 | 'rowclick',\r | |
832 | /**\r | |
833 | * @event rowdblclick\r | |
834 | * @inheritdoc Ext.view.Table#rowdblclick\r | |
835 | */\r | |
836 | 'rowdblclick',\r | |
837 | /**\r | |
838 | * @event rowcontextmenu\r | |
839 | * @inheritdoc Ext.view.Table#rowcontextmenu\r | |
840 | */\r | |
841 | 'rowcontextmenu',\r | |
842 | /**\r | |
843 | * @event rowmousedown\r | |
844 | * @inheritdoc Ext.view.Table#rowmousedown\r | |
845 | */\r | |
846 | 'rowmousedown',\r | |
847 | /**\r | |
848 | * @event rowmouseup\r | |
849 | * @inheritdoc Ext.view.Table#rowmouseup\r | |
850 | */\r | |
851 | 'rowmouseup',\r | |
852 | /**\r | |
853 | * @event rowkeydown\r | |
854 | * @inheritdoc Ext.view.Table#rowkeydown\r | |
855 | */\r | |
856 | 'rowkeydown',\r | |
857 | /**\r | |
858 | * @event beforeitemkeydown\r | |
859 | * @inheritdoc Ext.view.Table#beforeitemkeydown\r | |
860 | */\r | |
861 | 'beforeitemkeydown',\r | |
862 | /**\r | |
863 | * @event itemkeydown\r | |
864 | * @inheritdoc Ext.view.Table#itemkeydown\r | |
865 | */\r | |
866 | 'itemkeydown',\r | |
867 | /**\r | |
868 | * @event beforeitemkeyup\r | |
869 | * @inheritdoc Ext.view.Table#beforeitemkeyup\r | |
870 | */\r | |
871 | 'beforeitemkeyup',\r | |
872 | /**\r | |
873 | * @event itemkeyup\r | |
874 | * @inheritdoc Ext.view.Table#itemkeyup\r | |
875 | */\r | |
876 | 'itemkeyup',\r | |
877 | /**\r | |
878 | * @event beforeitemkeypress\r | |
879 | * @inheritdoc Ext.view.Table#beforeitemkeypress\r | |
880 | */\r | |
881 | 'beforeitemkeypress',\r | |
882 | /**\r | |
883 | * @event itemkeypress\r | |
884 | * @inheritdoc Ext.view.Table#itemkeypress\r | |
885 | */\r | |
886 | 'itemkeypress',\r | |
887 | /**\r | |
888 | * @event beforecontainermousedown\r | |
889 | * @inheritdoc Ext.view.View#beforecontainermousedown\r | |
890 | */\r | |
891 | 'beforecontainermousedown',\r | |
892 | /**\r | |
893 | * @event beforecontainermouseup\r | |
894 | * @inheritdoc Ext.view.View#beforecontainermouseup\r | |
895 | */\r | |
896 | 'beforecontainermouseup',\r | |
897 | /**\r | |
898 | * @event beforecontainermouseover\r | |
899 | * @inheritdoc Ext.view.View#beforecontainermouseover\r | |
900 | */\r | |
901 | 'beforecontainermouseover',\r | |
902 | /**\r | |
903 | * @event beforecontainermouseout\r | |
904 | * @inheritdoc Ext.view.View#beforecontainermouseout\r | |
905 | */\r | |
906 | 'beforecontainermouseout',\r | |
907 | /**\r | |
908 | * @event beforecontainerclick\r | |
909 | * @inheritdoc Ext.view.View#beforecontainerclick\r | |
910 | */\r | |
911 | 'beforecontainerclick',\r | |
912 | /**\r | |
913 | * @event beforecontainerdblclick\r | |
914 | * @inheritdoc Ext.view.View#beforecontainerdblclick\r | |
915 | */\r | |
916 | 'beforecontainerdblclick',\r | |
917 | /**\r | |
918 | * @event beforecontainercontextmenu\r | |
919 | * @inheritdoc Ext.view.View#beforecontainercontextmenu\r | |
920 | */\r | |
921 | 'beforecontainercontextmenu',\r | |
922 | /**\r | |
923 | * @event beforecontainerkeydown\r | |
924 | * @inheritdoc Ext.view.View#beforecontainerkeydown\r | |
925 | */\r | |
926 | 'beforecontainerkeydown',\r | |
927 | /**\r | |
928 | * @event beforecontainerkeyup\r | |
929 | * @inheritdoc Ext.view.View#beforecontainerkeyup\r | |
930 | */\r | |
931 | 'beforecontainerkeyup',\r | |
932 | /**\r | |
933 | * @event beforecontainerkeypress\r | |
934 | * @inheritdoc Ext.view.View#beforecontainerkeypress\r | |
935 | */\r | |
936 | 'beforecontainerkeypress',\r | |
937 | /**\r | |
938 | * @event containermouseup\r | |
939 | * @inheritdoc Ext.view.View#containermouseup\r | |
940 | */\r | |
941 | 'containermouseup',\r | |
942 | /**\r | |
943 | * @event containermousedown\r | |
944 | * @inheritdoc Ext.view.View#containermousedown\r | |
945 | */\r | |
946 | 'containermousedown',\r | |
947 | /**\r | |
948 | * @event containermouseover\r | |
949 | * @inheritdoc Ext.view.View#containermouseover\r | |
950 | */\r | |
951 | 'containermouseover',\r | |
952 | /**\r | |
953 | * @event containermouseout\r | |
954 | * @inheritdoc Ext.view.View#containermouseout\r | |
955 | */\r | |
956 | 'containermouseout',\r | |
957 | /**\r | |
958 | * @event containerclick\r | |
959 | * @inheritdoc Ext.view.View#containerclick\r | |
960 | */\r | |
961 | 'containerclick',\r | |
962 | /**\r | |
963 | * @event containerdblclick\r | |
964 | * @inheritdoc Ext.view.View#containerdblclick\r | |
965 | */\r | |
966 | 'containerdblclick',\r | |
967 | /**\r | |
968 | * @event containercontextmenu\r | |
969 | * @inheritdoc Ext.view.View#containercontextmenu\r | |
970 | */\r | |
971 | 'containercontextmenu',\r | |
972 | /**\r | |
973 | * @event containerkeydown\r | |
974 | * @inheritdoc Ext.view.View#containerkeydown\r | |
975 | */\r | |
976 | 'containerkeydown',\r | |
977 | /**\r | |
978 | * @event containerkeyup\r | |
979 | * @inheritdoc Ext.view.View#containerkeyup\r | |
980 | */\r | |
981 | 'containerkeyup',\r | |
982 | /**\r | |
983 | * @event containerkeypress\r | |
984 | * @inheritdoc Ext.view.View#containerkeypress\r | |
985 | */\r | |
986 | 'containerkeypress',\r | |
987 | /**\r | |
988 | * @event selectionchange\r | |
989 | * @inheritdoc Ext.selection.Model#selectionchange\r | |
990 | */\r | |
991 | 'selectionchange',\r | |
992 | /**\r | |
993 | * @event beforeselect\r | |
994 | * @inheritdoc Ext.selection.RowModel#beforeselect\r | |
995 | */\r | |
996 | 'beforeselect',\r | |
997 | /**\r | |
998 | * @event select\r | |
999 | * @inheritdoc Ext.selection.RowModel#select\r | |
1000 | */\r | |
1001 | 'select',\r | |
1002 | /**\r | |
1003 | * @event beforedeselect\r | |
1004 | * @inheritdoc Ext.selection.RowModel#beforedeselect\r | |
1005 | */\r | |
1006 | 'beforedeselect',\r | |
1007 | /**\r | |
1008 | * @event deselect\r | |
1009 | * @inheritdoc Ext.selection.RowModel#deselect\r | |
1010 | */\r | |
1011 | 'deselect'\r | |
1012 | ]);\r | |
1013 | \r | |
1014 | me.callParent();\r | |
1015 | if (me.enableLocking) {\r | |
1016 | me.afterInjectLockable();\r | |
1017 | } else {\r | |
1018 | delete headerCt.$initParent;\r | |
1019 | }\r | |
1020 | me.addStateEvents(['columnresize', 'columnmove', 'columnhide', 'columnshow', 'sortchange', 'filterchange', 'groupchange']);\r | |
1021 | \r | |
1022 | // rowBody feature events\r | |
1023 | /**\r | |
1024 | * @event beforerowbodymousedown\r | |
1025 | * @preventable\r | |
1026 | * @inheritdoc Ext.view.Table#event-beforerowbodymousedown\r | |
1027 | */\r | |
1028 | \r | |
1029 | /**\r | |
1030 | * @event beforerowbodymouseup\r | |
1031 | * @preventable\r | |
1032 | * @inheritdoc Ext.view.Table#event-beforerowbodymouseup\r | |
1033 | */\r | |
1034 | \r | |
1035 | /**\r | |
1036 | * @event beforerowbodyclick\r | |
1037 | * @preventable\r | |
1038 | * @inheritdoc Ext.view.Table#event-beforerowbodyclick\r | |
1039 | */\r | |
1040 | \r | |
1041 | /**\r | |
1042 | * @event beforerowbodydblclick\r | |
1043 | * @preventable\r | |
1044 | * @inheritdoc Ext.view.Table#event-beforerowbodydblclick\r | |
1045 | */\r | |
1046 | \r | |
1047 | /**\r | |
1048 | * @event beforerowbodycontextmenu\r | |
1049 | * @preventable\r | |
1050 | * @inheritdoc Ext.view.Table#event-beforerowbodycontextmenu\r | |
1051 | */\r | |
1052 | \r | |
1053 | /**\r | |
1054 | * @event beforerowbodylongpress\r | |
1055 | * @preventable\r | |
1056 | * @inheritdoc Ext.view.Table#event-beforerowbodylongpress\r | |
1057 | */\r | |
1058 | \r | |
1059 | /**\r | |
1060 | * @event beforerowbodykeydown\r | |
1061 | * @preventable\r | |
1062 | * @inheritdoc Ext.view.Table#event-beforerowbodykeydown\r | |
1063 | */\r | |
1064 | \r | |
1065 | /**\r | |
1066 | * @event beforerowbodykeyup\r | |
1067 | * @preventable\r | |
1068 | * @inheritdoc Ext.view.Table#event-beforerowbodykeyup\r | |
1069 | */\r | |
1070 | \r | |
1071 | /**\r | |
1072 | * @event beforerowbodykeypress\r | |
1073 | * @preventable\r | |
1074 | * @inheritdoc Ext.view.Table#event-beforerowbodykeypress\r | |
1075 | */\r | |
1076 | \r | |
1077 | /**\r | |
1078 | * @event rowbodymousedown\r | |
1079 | * @inheritdoc Ext.view.Table#event-rowbodymousedown\r | |
1080 | */\r | |
1081 | \r | |
1082 | /**\r | |
1083 | * @event rowbodymouseup\r | |
1084 | * @inheritdoc Ext.view.Table#event-rowbodymouseup\r | |
1085 | */\r | |
1086 | \r | |
1087 | /**\r | |
1088 | * @event rowbodyclick\r | |
1089 | * @inheritdoc Ext.view.Table#event-rowbodyclick\r | |
1090 | */\r | |
1091 | \r | |
1092 | /**\r | |
1093 | * @event rowbodydblclick\r | |
1094 | * @inheritdoc Ext.view.Table#event-rowbodydblclick\r | |
1095 | */\r | |
1096 | \r | |
1097 | /**\r | |
1098 | * @event rowbodycontextmenu\r | |
1099 | * @inheritdoc Ext.view.Table#event-rowbodycontextmenu\r | |
1100 | */\r | |
1101 | \r | |
1102 | /**\r | |
1103 | * @event rowbodylongpress\r | |
1104 | * @inheritdoc Ext.view.Table#event-rowbodylongpress\r | |
1105 | */\r | |
1106 | \r | |
1107 | /**\r | |
1108 | * @event rowbodykeydown\r | |
1109 | * @inheritdoc Ext.view.Table#event-rowbodykeydown\r | |
1110 | */\r | |
1111 | \r | |
1112 | /**\r | |
1113 | * @event rowbodykeyup\r | |
1114 | * @inheritdoc Ext.view.Table#event-rowbodykeyup\r | |
1115 | */\r | |
1116 | \r | |
1117 | /**\r | |
1118 | * @event rowbodykeypress\r | |
1119 | * @inheritdoc Ext.view.Table#event-rowbodykeypress\r | |
1120 | */\r | |
1121 | },\r | |
1122 | \r | |
1123 | beforeRender: function() {\r | |
1124 | var me = this,\r | |
1125 | bufferedRenderer = me.bufferedRenderer,\r | |
1126 | ariaAttr;\r | |
1127 | \r | |
1128 | // If this is the topmost container of a lockable assembly, add the special class body\r | |
1129 | if (me.lockable) {\r | |
1130 | me.getProtoBody().addCls(me.lockingBodyCls);\r | |
1131 | }\r | |
1132 | \r | |
1133 | // Don't create a buffered renderer for a locked grid.\r | |
1134 | else {\r | |
1135 | // If we're auto heighting, we can't buffered render, so don't create it\r | |
1136 | if (bufferedRenderer && me.getSizeModel().height.auto) {\r | |
1137 | //<debug>\r | |
1138 | if (bufferedRenderer.isBufferedRenderer) {\r | |
1139 | Ext.raise('Cannot use buffered rendering with auto height');\r | |
1140 | }\r | |
1141 | //</debug>\r | |
1142 | me.bufferedRenderer = bufferedRenderer = false;\r | |
1143 | }\r | |
1144 | \r | |
1145 | if (bufferedRenderer && !bufferedRenderer.isBufferedRenderer) {\r | |
1146 | // Create a BufferedRenderer as a plugin if we have not already configured with one.\r | |
1147 | bufferedRenderer = {\r | |
1148 | xclass: 'Ext.grid.plugin.BufferedRenderer'\r | |
1149 | };\r | |
1150 | Ext.copy(bufferedRenderer, me, 'variableRowHeight,numFromEdge,trailingBufferZone,leadingBufferZone,scrollToLoadBuffer');\r | |
1151 | me.bufferedRenderer = me.addPlugin(bufferedRenderer);\r | |
1152 | }\r | |
1153 | \r | |
1154 | ariaAttr = me.ariaRenderAttributes || (me.ariaRenderAttributes = {});\r | |
1155 | ariaAttr['aria-readonly'] = !me.isEditable;\r | |
1156 | ariaAttr['aria-multiselectable'] = me.selModel.selectionMode !== 'SINGLE';\r | |
1157 | }\r | |
1158 | \r | |
1159 | me.callParent(arguments);\r | |
1160 | },\r | |
1161 | \r | |
1162 | onRender: function() {\r | |
1163 | var me = this,\r | |
1164 | gridPanelBorderWidth,\r | |
1165 | totalColumnWidth;\r | |
1166 | \r | |
1167 | // If this is the locked side, include border width in calculated locked grid width.\r | |
1168 | // TODO: Use shrinkWrapDock on the locked grid's headerCt when it works.\r | |
1169 | if (me.isLocked && me.getSizeModel().width.shrinkWrap) {\r | |
1170 | me.shrinkWrapColumns = true;\r | |
1171 | totalColumnWidth = me.headerCt.getTableWidth();\r | |
1172 | //<debug>\r | |
1173 | if (isNaN(totalColumnWidth)) {\r | |
1174 | Ext.raise("Locked columns in an unsized locked side do NOT support a flex width.");\r | |
1175 | }\r | |
1176 | //</debug>\r | |
1177 | gridPanelBorderWidth = me.gridPanelBorderWidth || (me.gridPanelBorderWidth = me.el.getBorderWidth('lr'));\r | |
1178 | me.width = totalColumnWidth + gridPanelBorderWidth;\r | |
1179 | }\r | |
1180 | me.callParent();\r | |
1181 | },\r | |
1182 | \r | |
1183 | /**\r | |
1184 | * Gets the {@link Ext.grid.header.Container headercontainer} for this grid / tree.\r | |
1185 | * @return {Ext.grid.header.Container} headercontainer\r | |
1186 | *\r | |
1187 | * **Note:** While a locked grid / tree will return an instance of\r | |
1188 | * {@link Ext.grid.locking.HeaderContainer} you will code to the\r | |
1189 | * {@link Ext.grid.header.Container} API.\r | |
1190 | */\r | |
1191 | getHeaderContainer: function () {\r | |
1192 | return this.getView().getHeaderCt();\r | |
1193 | },\r | |
1194 | \r | |
1195 | /**\r | |
1196 | * @inheritdoc Ext.grid.header.Container#getGridColumns\r | |
1197 | */\r | |
1198 | getColumns: function () {\r | |
1199 | return this.getColumnManager().getColumns();\r | |
1200 | },\r | |
1201 | \r | |
1202 | /**\r | |
1203 | * @inheritdoc Ext.grid.header.Container#getVisibleGridColumns\r | |
1204 | */\r | |
1205 | getVisibleColumns: function () {\r | |
1206 | return this.getVisibleColumnManager().getColumns();\r | |
1207 | },\r | |
1208 | \r | |
1209 | focus: function() {\r | |
1210 | // TablePanel is not focusable, but allow a call to delegate into the view\r | |
1211 | this.getView().focus();\r | |
1212 | },\r | |
1213 | \r | |
1214 | /**\r | |
1215 | * Disables interaction with, and masks this grid's column headers.\r | |
1216 | */\r | |
1217 | disableColumnHeaders: function() {\r | |
1218 | this.headerCt.disable();\r | |
1219 | },\r | |
1220 | \r | |
1221 | /**\r | |
1222 | * Enables interaction with, and unmasks this grid's column headers after a call to {#disableColumnHeaders}.\r | |
1223 | */\r | |
1224 | enableColumnHeaders: function() {\r | |
1225 | this.headerCt.enable();\r | |
1226 | },\r | |
1227 | \r | |
1228 | /**\r | |
1229 | * @private\r | |
1230 | * Determine if there are any columns with a locked configuration option.\r | |
1231 | */\r | |
1232 | hasLockedColumns: function(columns) {\r | |
1233 | var i,\r | |
1234 | len,\r | |
1235 | column;\r | |
1236 | \r | |
1237 | // Fully instantiated HeaderContainer\r | |
1238 | if (columns.isRootHeader) {\r | |
1239 | columns = columns.items.items;\r | |
1240 | }\r | |
1241 | // Config object with items\r | |
1242 | else if (Ext.isObject(columns)) {\r | |
1243 | columns = columns.items;\r | |
1244 | }\r | |
1245 | for (i = 0, len = columns.length; i < len; i++) {\r | |
1246 | column = columns[i];\r | |
1247 | if (!column.processed && column.locked) {\r | |
1248 | return true;\r | |
1249 | }\r | |
1250 | }\r | |
1251 | },\r | |
1252 | \r | |
1253 | relayHeaderCtEvents: function (headerCt) {\r | |
1254 | this.relayEvents(headerCt, [\r | |
1255 | /**\r | |
1256 | * @event columnresize\r | |
1257 | * @inheritdoc Ext.grid.header.Container#columnresize\r | |
1258 | */\r | |
1259 | 'columnresize',\r | |
1260 | /**\r | |
1261 | * @event columnmove\r | |
1262 | * @inheritdoc Ext.grid.header.Container#columnmove\r | |
1263 | */\r | |
1264 | 'columnmove',\r | |
1265 | /**\r | |
1266 | * @event columnhide\r | |
1267 | * @inheritdoc Ext.grid.header.Container#columnhide\r | |
1268 | */\r | |
1269 | 'columnhide',\r | |
1270 | /**\r | |
1271 | * @event columnshow\r | |
1272 | * @inheritdoc Ext.grid.header.Container#columnshow\r | |
1273 | */\r | |
1274 | 'columnshow',\r | |
1275 | /**\r | |
1276 | * @event columnschanged\r | |
1277 | * @inheritdoc Ext.grid.header.Container#columnschanged\r | |
1278 | */\r | |
1279 | 'columnschanged',\r | |
1280 | /**\r | |
1281 | * @event sortchange\r | |
1282 | * @inheritdoc Ext.grid.header.Container#sortchange\r | |
1283 | */\r | |
1284 | 'sortchange',\r | |
1285 | /**\r | |
1286 | * @event headerclick\r | |
1287 | * @inheritdoc Ext.grid.header.Container#headerclick\r | |
1288 | */\r | |
1289 | 'headerclick',\r | |
1290 | /**\r | |
1291 | * @event headercontextmenu\r | |
1292 | * @inheritdoc Ext.grid.header.Container#headercontextmenu\r | |
1293 | */\r | |
1294 | 'headercontextmenu',\r | |
1295 | /**\r | |
1296 | * @event headertriggerclick\r | |
1297 | * @inheritdoc Ext.grid.header.Container#headertriggerclick\r | |
1298 | */\r | |
1299 | 'headertriggerclick'\r | |
1300 | ]);\r | |
1301 | },\r | |
1302 | \r | |
1303 | getState: function(){\r | |
1304 | var me = this,\r | |
1305 | state = me.callParent(),\r | |
1306 | storeState = me.store.getState();\r | |
1307 | \r | |
1308 | state = me.addPropertyToState(state, 'columns', me.headerCt.getColumnsState());\r | |
1309 | \r | |
1310 | if (storeState) {\r | |
1311 | state.storeState = storeState;\r | |
1312 | }\r | |
1313 | return state;\r | |
1314 | },\r | |
1315 | \r | |
1316 | applyState: function (state) {\r | |
1317 | var me = this,\r | |
1318 | sorter = state.sort,\r | |
1319 | storeState = state.storeState,\r | |
1320 | store = me.store,\r | |
1321 | columns = state.columns;\r | |
1322 | \r | |
1323 | delete state.columns;\r | |
1324 | \r | |
1325 | // Ensure superclass has applied *its* state.\r | |
1326 | // Component saves dimensions (and anchor/flex) plus collapsed state.\r | |
1327 | me.callParent(arguments);\r | |
1328 | \r | |
1329 | if (columns) {\r | |
1330 | // Column state restoration needs to examine store state\r | |
1331 | me.headerCt.applyColumnsState(columns, storeState);\r | |
1332 | }\r | |
1333 | \r | |
1334 | // Old stored sort state. Deprecated and will die out.\r | |
1335 | if (sorter) {\r | |
1336 | if (store.getRemoteSort()) {\r | |
1337 | // Pass false to prevent a sort from occurring.\r | |
1338 | store.sort({\r | |
1339 | property: sorter.property,\r | |
1340 | direction: sorter.direction,\r | |
1341 | root: sorter.root\r | |
1342 | }, null, false);\r | |
1343 | } else {\r | |
1344 | store.sort(sorter.property, sorter.direction);\r | |
1345 | }\r | |
1346 | }\r | |
1347 | // New storeState which encapsulates groupers, sorters and filters.\r | |
1348 | else if (storeState) {\r | |
1349 | store.applyState(storeState);\r | |
1350 | }\r | |
1351 | },\r | |
1352 | \r | |
1353 | /**\r | |
1354 | * Returns the store associated with this Panel.\r | |
1355 | * @return {Ext.data.Store} The store\r | |
1356 | */\r | |
1357 | getStore: function(){\r | |
1358 | return this.store;\r | |
1359 | },\r | |
1360 | \r | |
1361 | /**\r | |
1362 | * Gets the view for this panel.\r | |
1363 | * @return {Ext.view.Table}\r | |
1364 | */\r | |
1365 | getView: function() {\r | |
1366 | var me = this,\r | |
1367 | scroll, scrollable, viewConfig;\r | |
1368 | \r | |
1369 | if (!me.view) {\r | |
1370 | viewConfig = me.viewConfig;\r | |
1371 | scroll = viewConfig.scroll || me.scroll;\r | |
1372 | scrollable = me.scrollable;\r | |
1373 | \r | |
1374 | if (scrollable == null && viewConfig.scrollable == null && scroll !== null) {\r | |
1375 | // transform deprecated scroll config into scrollable config\r | |
1376 | if (scroll === true || scroll === 'both') {\r | |
1377 | scrollable = true;\r | |
1378 | } else if (scroll === false || scroll === 'none') {\r | |
1379 | scrollable = false;\r | |
1380 | } else if (scroll === 'vertical') {\r | |
1381 | scrollable = {\r | |
1382 | x: false,\r | |
1383 | y: true\r | |
1384 | };\r | |
1385 | } else if (scroll === 'horizontal') {\r | |
1386 | scrollable = {\r | |
1387 | x: true,\r | |
1388 | y: false\r | |
1389 | };\r | |
1390 | }\r | |
1391 | }\r | |
1392 | \r | |
1393 | viewConfig = Ext.apply({\r | |
1394 | // TableView injects the view reference into this grid so that we have a reference as early as possible\r | |
1395 | // and Features need a reference to the grid.\r | |
1396 | // For these reasons, we configure a reference to this grid into the View\r | |
1397 | grid: me,\r | |
1398 | ownerGrid: me.ownerGrid,\r | |
1399 | deferInitialRefresh: me.deferRowRender,\r | |
1400 | variableRowHeight: me.variableRowHeight,\r | |
1401 | preserveScrollOnRefresh: true,\r | |
1402 | trackOver: me.trackMouseOver !== false,\r | |
1403 | throttledUpdate: me.throttledUpdate === true,\r | |
1404 | xtype: me.viewType,\r | |
1405 | store: me.store,\r | |
1406 | headerCt: me.headerCt,\r | |
1407 | columnLines: me.columnLines,\r | |
1408 | rowLines: me.rowLines,\r | |
1409 | navigationModel: 'grid',\r | |
1410 | features: me.features,\r | |
1411 | panel: me,\r | |
1412 | emptyText: me.emptyText || ''\r | |
1413 | }, me.viewConfig);\r | |
1414 | \r | |
1415 | if (scrollable != null) {\r | |
1416 | viewConfig.scrollable = scrollable;\r | |
1417 | me.scrollable = null;\r | |
1418 | }\r | |
1419 | \r | |
1420 | Ext.create(viewConfig);\r | |
1421 | \r | |
1422 | // Normalize the application of the markup wrapping the emptyText config.\r | |
1423 | // `emptyText` can now be defined on the grid as well as on its viewConfig, and this led to the emptyText not\r | |
1424 | // having the wrapping markup when it was defined in the viewConfig. It should be backwards compatible.\r | |
1425 | // Note that in the unlikely event that emptyText is defined on both the grid config and the viewConfig that the viewConfig wins.\r | |
1426 | if (me.view.emptyText) {\r | |
1427 | me.view.emptyText = '<div class="' + me.emptyCls + '">' + me.view.emptyText + '</div>';\r | |
1428 | }\r | |
1429 | \r | |
1430 | // TableView's custom component layout, Ext.view.TableLayout requires a reference to the headerCt because it depends on the headerCt doing its work.\r | |
1431 | me.view.getComponentLayout().headerCt = me.headerCt;\r | |
1432 | \r | |
1433 | me.mon(me.view, {\r | |
1434 | uievent: me.processEvent,\r | |
1435 | scope: me\r | |
1436 | });\r | |
1437 | me.headerCt.view = me.view;\r | |
1438 | \r | |
1439 | // Plugins and features may need to access the view as soon as it is created.\r | |
1440 | if (me.hasListeners.viewcreated) {\r | |
1441 | me.fireEvent('viewcreated', me, me.view);\r | |
1442 | }\r | |
1443 | }\r | |
1444 | return me.view;\r | |
1445 | },\r | |
1446 | \r | |
1447 | getColumnManager: function() {\r | |
1448 | return this.columnManager;\r | |
1449 | },\r | |
1450 | \r | |
1451 | getVisibleColumnManager: function() {\r | |
1452 | return this.visibleColumnManager;\r | |
1453 | },\r | |
1454 | \r | |
1455 | getTopLevelColumnManager: function() {\r | |
1456 | return this.ownerGrid.getColumnManager();\r | |
1457 | },\r | |
1458 | \r | |
1459 | getTopLevelVisibleColumnManager: function() {\r | |
1460 | return this.ownerGrid.getVisibleColumnManager();\r | |
1461 | },\r | |
1462 | \r | |
1463 | /**\r | |
1464 | * @private\r | |
1465 | * autoScroll is never valid for all classes which extend TablePanel.\r | |
1466 | */\r | |
1467 | setAutoScroll: Ext.emptyFn,\r | |
1468 | \r | |
1469 | applyScrollable: function(scrollable) {\r | |
1470 | if (this.view) {\r | |
1471 | this.view.setScrollable(scrollable);\r | |
1472 | }\r | |
1473 | \r | |
1474 | return scrollable;\r | |
1475 | },\r | |
1476 | \r | |
1477 | getScrollable: function() {\r | |
1478 | return null;\r | |
1479 | },\r | |
1480 | \r | |
1481 | /**\r | |
1482 | * @private\r | |
1483 | * Processes UI events from the view. Propagates them to whatever internal Components need to process them.\r | |
1484 | * @param {String} type Event type, eg 'click'\r | |
1485 | * @param {Ext.view.Table} view TableView Component\r | |
1486 | * @param {HTMLElement} cell Cell HTMLElement the event took place within\r | |
1487 | * @param {Number} recordIndex Index of the associated Store Model (-1 if none)\r | |
1488 | * @param {Number} cellIndex Cell index within the row\r | |
1489 | * @param {Ext.event.Event} e Original event\r | |
1490 | */\r | |
1491 | processEvent: function(type, view, cell, recordIndex, cellIndex, e, record, row) {\r | |
1492 | var header = e.position.column;\r | |
1493 | \r | |
1494 | if (header) {\r | |
1495 | return header.processEvent.apply(header, arguments);\r | |
1496 | }\r | |
1497 | },\r | |
1498 | \r | |
1499 | /**\r | |
1500 | * Scrolls the specified record into view.\r | |
1501 | * @param {Number/String/Ext.data.Model} record The record, record id, or the zero-based position in the dataset to scroll to.\r | |
1502 | * @param {Object} [options] An object containing options to modify the operation.\r | |
1503 | * @param {Boolean} [options.animate] Pass `true` to animate the row into view.\r | |
1504 | * @param {Boolean} [options.highlight] Pass `true` to highlight the row with a glow animation when it is in view.\r | |
1505 | * @param {Boolean} [options.select] Pass as `true` to select the specified row.\r | |
1506 | * @param {Boolean} [options.focus] Pass as `true` to focus the specified row.\r | |
1507 | * @param {Function} [options.callback] A function to execute when the record is in view. This may be necessary if the\r | |
1508 | * first parameter is a record index and the view is backed by a {@link Ext.data.BufferedStore buffered store}\r | |
1509 | * which does not contain that record.\r | |
1510 | * @param {Boolean} options.callback.success `true` if acquiring the record's view node was successful.\r | |
1511 | * @param {Ext.data.Model} options.callback.record If successful, the target record.\r | |
1512 | * @param {HTMLElement} options.callback.node If successful, the record's view node.\r | |
1513 | * @param {Object} [options.scope] The scope (`this` reference) in which the callback function is executed.\r | |
1514 | */\r | |
1515 | ensureVisible: function(record, options) {\r | |
1516 | this.doEnsureVisible(record, options);\r | |
1517 | },\r | |
1518 | \r | |
1519 | scrollByDeltaY: function(yDelta, animate) {\r | |
1520 | this.getView().scrollBy(0, yDelta, animate);\r | |
1521 | },\r | |
1522 | \r | |
1523 | scrollByDeltaX: function(xDelta, animate) {\r | |
1524 | this.getView().scrollBy(xDelta, 0, animate);\r | |
1525 | },\r | |
1526 | \r | |
1527 | afterCollapse: function() {\r | |
1528 | this.saveScrollPos();\r | |
1529 | this.callParent(arguments);\r | |
1530 | },\r | |
1531 | \r | |
1532 | afterExpand: function() {\r | |
1533 | this.callParent(arguments);\r | |
1534 | this.restoreScrollPos();\r | |
1535 | },\r | |
1536 | \r | |
1537 | saveScrollPos: Ext.emptyFn,\r | |
1538 | \r | |
1539 | restoreScrollPos: Ext.emptyFn,\r | |
1540 | \r | |
1541 | onHeaderResize: function() {\r | |
1542 | var scroller = this.view.getScrollable(),\r | |
1543 | size;\r | |
1544 | \r | |
1545 | if (scroller && scroller.isTouchScroller) {\r | |
1546 | size = scroller.getSize();\r | |
1547 | if (size) {\r | |
1548 | scroller.setSize({\r | |
1549 | x: this.headerCt.getTableWidth(),\r | |
1550 | y: size.y\r | |
1551 | });\r | |
1552 | }\r | |
1553 | }\r | |
1554 | },\r | |
1555 | \r | |
1556 | // Update the view when a header moves\r | |
1557 | onHeaderMove: function(headerCt, header, colsToMove, fromIdx, toIdx) {\r | |
1558 | var me = this;\r | |
1559 | \r | |
1560 | // If there are Features or Plugins which create DOM which must match column order, they set the optimizedColumnMove flag to false.\r | |
1561 | // In this case we must refresh the view on column move.\r | |
1562 | if (me.optimizedColumnMove === false) {\r | |
1563 | me.view.refreshView();\r | |
1564 | }\r | |
1565 | \r | |
1566 | // Simplest case for default DOM structure is just to swap the columns round in the view.\r | |
1567 | else {\r | |
1568 | me.view.moveColumn(fromIdx, toIdx, colsToMove);\r | |
1569 | }\r | |
1570 | me.delayScroll();\r | |
1571 | },\r | |
1572 | \r | |
1573 | // Section onHeaderHide is invoked after view.\r | |
1574 | onHeaderHide: function(headerCt, header) {\r | |
1575 | var view = this.view;\r | |
1576 | // The headerCt may be hiding multiple children if a leaf level column\r | |
1577 | // causes a parent (and possibly other parents) to be hidden. Only run the refresh\r | |
1578 | // once we're done\r | |
1579 | if (!headerCt.childHideCount && view.refreshCounter) {\r | |
1580 | view.refreshView();\r | |
1581 | }\r | |
1582 | },\r | |
1583 | \r | |
1584 | onHeaderShow: function(headerCt, header) {\r | |
1585 | var view = this.view;\r | |
1586 | if (view.refreshCounter) {\r | |
1587 | view.refreshView();\r | |
1588 | }\r | |
1589 | },\r | |
1590 | \r | |
1591 | // To be triggered on add/remove/move for a leaf header\r | |
1592 | onHeadersChanged: function(headerCt, header) {\r | |
1593 | var me = this;\r | |
1594 | if (me.rendered && !me.reconfiguring) {\r | |
1595 | me.view.refreshView();\r | |
1596 | me.delayScroll();\r | |
1597 | }\r | |
1598 | },\r | |
1599 | \r | |
1600 | delayScroll: function(){\r | |
1601 | var target = this.view;\r | |
1602 | if (target) {\r | |
1603 | // Do not cause a layout by reading scrollX now.\r | |
1604 | // It must be read from the target when the task finally executes.\r | |
1605 | this.scrollTask.delay(10, null, null, [target]);\r | |
1606 | }\r | |
1607 | },\r | |
1608 | \r | |
1609 | /**\r | |
1610 | * @private\r | |
1611 | * Fires the TablePanel's viewready event when the view declares that its internal DOM is ready\r | |
1612 | */\r | |
1613 | onViewReady: function() {\r | |
1614 | this.fireEvent('viewready', this); \r | |
1615 | },\r | |
1616 | \r | |
1617 | /**\r | |
1618 | * @private\r | |
1619 | * Tracks when things happen to the view and preserves the horizontal scroll position.\r | |
1620 | */\r | |
1621 | onRestoreHorzScroll: function() {\r | |
1622 | var me = this,\r | |
1623 | x = me.scrollXPos;\r | |
1624 | \r | |
1625 | if (x) {\r | |
1626 | // We need to restore the body scroll position here\r | |
1627 | me.syncHorizontalScroll(me, true);\r | |
1628 | }\r | |
1629 | },\r | |
1630 | \r | |
1631 | getScrollerOwner: function() {\r | |
1632 | var rootCmp = this;\r | |
1633 | if (!this.scrollerOwner) {\r | |
1634 | rootCmp = this.up('[scrollerOwner]');\r | |
1635 | }\r | |
1636 | return rootCmp;\r | |
1637 | },\r | |
1638 | \r | |
1639 | /**\r | |
1640 | * Gets left hand side marker for header resizing.\r | |
1641 | * @private\r | |
1642 | */\r | |
1643 | getLhsMarker: function() {\r | |
1644 | var me = this;\r | |
1645 | return me.lhsMarker || (me.lhsMarker = Ext.DomHelper.append(me.el, {\r | |
1646 | role: 'presentation',\r | |
1647 | cls: me.resizeMarkerCls\r | |
1648 | }, true));\r | |
1649 | },\r | |
1650 | \r | |
1651 | /**\r | |
1652 | * Gets right hand side marker for header resizing.\r | |
1653 | * @private\r | |
1654 | */\r | |
1655 | getRhsMarker: function() {\r | |
1656 | var me = this;\r | |
1657 | \r | |
1658 | return me.rhsMarker || (me.rhsMarker = Ext.DomHelper.append(me.el, {\r | |
1659 | role: 'presentation',\r | |
1660 | cls: me.resizeMarkerCls\r | |
1661 | }, true));\r | |
1662 | },\r | |
1663 | \r | |
1664 | /**\r | |
1665 | * Returns the grid's selection. See `{@link Ext.selection.Model#getSelection}`.\r | |
1666 | * @inheritdoc Ext.selection.Model#getSelection\r | |
1667 | */\r | |
1668 | getSelection: function () {\r | |
1669 | return this.getSelectionModel().getSelection();\r | |
1670 | },\r | |
1671 | \r | |
1672 | updateSelection: function(selection) {\r | |
1673 | var me = this,\r | |
1674 | sm;\r | |
1675 | \r | |
1676 | if (!me.ignoreNextSelection) {\r | |
1677 | me.ignoreNextSelection = true;\r | |
1678 | sm = me.getSelectionModel();\r | |
1679 | if (selection) {\r | |
1680 | sm.select(selection);\r | |
1681 | } else {\r | |
1682 | sm.deselectAll();\r | |
1683 | }\r | |
1684 | me.ignoreNextSelection = false;\r | |
1685 | }\r | |
1686 | },\r | |
1687 | \r | |
1688 | updateBindSelection: function(selModel, selection) {\r | |
1689 | var me = this,\r | |
1690 | selected = null;\r | |
1691 | \r | |
1692 | if (!me.ignoreNextSelection) {\r | |
1693 | me.ignoreNextSelection = true;\r | |
1694 | if (selection.length) {\r | |
1695 | selected = selModel.getLastSelected();\r | |
1696 | me.hasHadSelection = true;\r | |
1697 | }\r | |
1698 | if (me.hasHadSelection) {\r | |
1699 | me.setSelection(selected);\r | |
1700 | }\r | |
1701 | me.ignoreNextSelection = false;\r | |
1702 | }\r | |
1703 | },\r | |
1704 | \r | |
1705 | updateHeaderBorders: function(headerBorders) {\r | |
1706 | this[headerBorders ? 'removeCls' : 'addCls'](this.noHeaderBordersCls);\r | |
1707 | },\r | |
1708 | \r | |
1709 | getNavigationModel: function() {\r | |
1710 | return this.getView().getNavigationModel();\r | |
1711 | },\r | |
1712 | \r | |
1713 | /**\r | |
1714 | * Returns the selection model being used by this grid's {@link Ext.view.Table view}.\r | |
1715 | * @return {Ext.selection.Model} The selection model being used by this grid's {@link Ext.view.Table view}.\r | |
1716 | */\r | |
1717 | getSelectionModel: function() {\r | |
1718 | return this.getView().getSelectionModel();\r | |
1719 | },\r | |
1720 | \r | |
1721 | getScrollTarget: function(){\r | |
1722 | var items = this.getScrollerOwner().query('tableview');\r | |
1723 | \r | |
1724 | // Last view has the scroller\r | |
1725 | return items[items.length - 1];\r | |
1726 | },\r | |
1727 | \r | |
1728 | syncHorizontalScroll: function(target, setBody) {\r | |
1729 | var me = this,\r | |
1730 | x = me.view.getScrollX(),\r | |
1731 | scrollTarget;\r | |
1732 | \r | |
1733 | setBody = setBody === true;\r | |
1734 | // Only set the horizontal scroll if we've changed position,\r | |
1735 | // so that we don't set this on vertical scrolls\r | |
1736 | if (me.rendered && (setBody || x !== me.scrollXPos)) {\r | |
1737 | // Only set the body position if we're reacting to a refresh, otherwise\r | |
1738 | // we just need to set the header.\r | |
1739 | if (setBody) { \r | |
1740 | scrollTarget = me.getScrollTarget();\r | |
1741 | scrollTarget.setScrollX(x);\r | |
1742 | }\r | |
1743 | me.headerCt.setScrollX(x);\r | |
1744 | me.scrollXPos = x;\r | |
1745 | }\r | |
1746 | },\r | |
1747 | \r | |
1748 | // template method meant to be overriden\r | |
1749 | onStoreLoad: Ext.emptyFn,\r | |
1750 | \r | |
1751 | getEditorParent: function() {\r | |
1752 | return this.body;\r | |
1753 | },\r | |
1754 | \r | |
1755 | bindStore: function(store, initial) {\r | |
1756 | var me = this,\r | |
1757 | view = me.getView();\r | |
1758 | \r | |
1759 | // Normally, this method will always be called with a valid store (because there is a symmetric\r | |
1760 | // .unbindStore method), but there are cases where this method will be called and passed a null\r | |
1761 | // value, i.e., a panel is used as a pickerfield. See EXTJS-13089.\r | |
1762 | if (store) {\r | |
1763 | // Bind to store immediately because subsequent processing looks for grid's store property\r | |
1764 | me.store = store;\r | |
1765 | \r | |
1766 | if (view.store !== store) {\r | |
1767 | // If coming from a reconfigure, we need to set the actual store property on the view. Setting the\r | |
1768 | // store will then also set the dataSource.\r | |
1769 | //\r | |
1770 | // Note that if it's a grid feature then this is sorted out in view.bindStore(), and it's own\r | |
1771 | // implementation of .bindStore() will be called.\r | |
1772 | view.bindStore(store, false);\r | |
1773 | }\r | |
1774 | \r | |
1775 | me.mon(store, {\r | |
1776 | load: me.onStoreLoad,\r | |
1777 | scope: me\r | |
1778 | });\r | |
1779 | me.storeRelayers = me.relayEvents(store, [\r | |
1780 | /**\r | |
1781 | * @event filterchange\r | |
1782 | * @inheritdoc Ext.data.Store#filterchange\r | |
1783 | */\r | |
1784 | 'filterchange',\r | |
1785 | /**\r | |
1786 | * @event groupchange\r | |
1787 | * @inheritdoc Ext.data.Store#groupchange\r | |
1788 | */\r | |
1789 | 'groupchange'\r | |
1790 | ]);\r | |
1791 | } else {\r | |
1792 | me.unbindStore();\r | |
1793 | }\r | |
1794 | },\r | |
1795 | \r | |
1796 | unbindStore: function() {\r | |
1797 | var me = this,\r | |
1798 | store = me.store,\r | |
1799 | view;\r | |
1800 | \r | |
1801 | if (store) {\r | |
1802 | store.trackStateChanges = false;\r | |
1803 | \r | |
1804 | me.store = null;\r | |
1805 | \r | |
1806 | me.mun(store, {\r | |
1807 | load: me.onStoreLoad,\r | |
1808 | scope: me\r | |
1809 | });\r | |
1810 | \r | |
1811 | Ext.destroy(me.storeRelayers);\r | |
1812 | \r | |
1813 | view = me.view;\r | |
1814 | if (view.store) {\r | |
1815 | view.bindStore(null);\r | |
1816 | }\r | |
1817 | }\r | |
1818 | },\r | |
1819 | \r | |
1820 | setColumns: function(columns) {\r | |
1821 | // If being reconfigured from zero columns to zero columns, skip operation.\r | |
1822 | // This can happen if columns are being set from a binding and the initial value\r | |
1823 | // of the bound data in the ViewModel is []\r | |
1824 | if (columns.length || this.getColumnManager().getColumns().length) {\r | |
1825 | this.reconfigure(undefined, columns);\r | |
1826 | }\r | |
1827 | },\r | |
1828 | \r | |
1829 | /**\r | |
1830 | * A convenience method that fires {@link #reconfigure} with the store param. To set the store AND change columns,\r | |
1831 | * use the {@link #reconfigure reconfigure method}.\r | |
1832 | *\r | |
1833 | * @param {Ext.data.Store} [store] The new store.\r | |
1834 | */\r | |
1835 | setStore: function (store) {\r | |
1836 | this.reconfigure(store);\r | |
1837 | \r | |
1838 | if (this.autoLoad && !store.isEmptyStore && !(store.loading || store.isLoaded())) {\r | |
1839 | store.load();\r | |
1840 | }\r | |
1841 | },\r | |
1842 | \r | |
1843 | /**\r | |
1844 | * Reconfigures the grid or tree with a new store and/or columns. Stores and columns \r | |
1845 | * may also be passed as params.\r | |
1846 | *\r | |
1847 | * grid.reconfigure(store, columns);\r | |
1848 | *\r | |
1849 | * Additionally, you can pass just a store or columns.\r | |
1850 | *\r | |
1851 | * tree.reconfigure(store);\r | |
1852 | * // or\r | |
1853 | * grid.reconfigure(columns);\r | |
1854 | * // or\r | |
1855 | * tree.reconfigure(null, columns);\r | |
1856 | *\r | |
1857 | * If you're using locked columns, the {@link #enableLocking} config should be set \r | |
1858 | * to `true` before the reconfigure method is executed.\r | |
1859 | *\r | |
1860 | * @param {Ext.data.Store/Object} [store] The new store instance or store config. You can \r | |
1861 | * pass `null` if no new store.\r | |
1862 | * @param {Object[]} [columns] An array of column configs\r | |
1863 | */\r | |
1864 | reconfigure: function(store, columns) {\r | |
1865 | var me = this,\r | |
1866 | oldStore = me.store,\r | |
1867 | headerCt = me.headerCt,\r | |
1868 | lockable = me.lockable,\r | |
1869 | oldColumns = headerCt ? headerCt.items.getRange() : me.columns,\r | |
1870 | view = me.getView(),\r | |
1871 | block, refreshCounter;\r | |
1872 | \r | |
1873 | // Allow optional store argument to be fully omitted, and the columns argument to be solo\r | |
1874 | if (arguments.length === 1 && Ext.isArray(store)) {\r | |
1875 | columns = store;\r | |
1876 | store = null;\r | |
1877 | }\r | |
1878 | \r | |
1879 | // Make copy in case the beforereconfigure listener mutates it.\r | |
1880 | if (columns) {\r | |
1881 | columns = Ext.Array.slice(columns);\r | |
1882 | }\r | |
1883 | \r | |
1884 | me.reconfiguring = true;\r | |
1885 | if (store) {\r | |
1886 | store = Ext.StoreManager.lookup(store);\r | |
1887 | }\r | |
1888 | me.fireEvent('beforereconfigure', me, store, columns, oldStore, oldColumns);\r | |
1889 | \r | |
1890 | Ext.suspendLayouts();\r | |
1891 | \r | |
1892 | if (lockable) {\r | |
1893 | me.reconfigureLockable(store, columns);\r | |
1894 | } else {\r | |
1895 | // Prevent the view from refreshing until we have resumed layouts and any columns are rendered\r | |
1896 | block = view.blockRefresh;\r | |
1897 | view.blockRefresh = true;\r | |
1898 | \r | |
1899 | // The following test compares the result of an assignment of the store var with the oldStore var.\r | |
1900 | // This saves a large amount of code.\r | |
1901 | //\r | |
1902 | // Note that we need to process the store first in case one or more passed columns (if there are any)\r | |
1903 | // have active gridfilters with values which would filter the currently-bound store.\r | |
1904 | if (store && store !== oldStore) {\r | |
1905 | me.unbindStore();\r | |
1906 | me.bindStore(store);\r | |
1907 | }\r | |
1908 | \r | |
1909 | if (columns) {\r | |
1910 | // new columns, delete scroll pos\r | |
1911 | delete me.scrollXPos;\r | |
1912 | headerCt.removeAll();\r | |
1913 | headerCt.add(columns);\r | |
1914 | }\r | |
1915 | \r | |
1916 | view.blockRefresh = block;\r | |
1917 | refreshCounter = view.refreshCounter;\r | |
1918 | }\r | |
1919 | \r | |
1920 | Ext.resumeLayouts(true);\r | |
1921 | if (lockable) {\r | |
1922 | me.afterReconfigureLockable();\r | |
1923 | } else if (view.refreshCounter === refreshCounter) {\r | |
1924 | // If the layout resumption didn't trigger the view to refresh, do it here\r | |
1925 | view.refreshView();\r | |
1926 | }\r | |
1927 | \r | |
1928 | me.fireEvent('reconfigure', me, store, columns, oldStore, oldColumns);\r | |
1929 | delete me.reconfiguring;\r | |
1930 | },\r | |
1931 | \r | |
1932 | beforeDestroy: function(){\r | |
1933 | var me = this,\r | |
1934 | task = me.scrollTask;\r | |
1935 | \r | |
1936 | if (task) {\r | |
1937 | task.cancel();\r | |
1938 | me.scrollTask = null;\r | |
1939 | }\r | |
1940 | Ext.destroy(me.focusEnterLeaveListeners);\r | |
1941 | me.callParent();\r | |
1942 | },\r | |
1943 | \r | |
1944 | onDestroy: function(){\r | |
1945 | var me = this;\r | |
1946 | if (me.lockable) {\r | |
1947 | me.destroyLockable();\r | |
1948 | }\r | |
1949 | me.unbindStore();\r | |
1950 | me.callParent();\r | |
1951 | me.columns = me.storeRelayers = me.columnManager = me.visibleColumnManager = null;\r | |
1952 | },\r | |
1953 | \r | |
1954 | destroy: function() {\r | |
1955 | // Clear out references here because other things (plugins/features) may need to know about them during destruction\r | |
1956 | var me = this;\r | |
1957 | me.callParent();\r | |
1958 | if (me.destroyed) {\r | |
1959 | me.view = me.selModel = me.headerCt = null;\r | |
1960 | }\r | |
1961 | },\r | |
1962 | \r | |
1963 | privates: {\r | |
1964 | // The focusable flag is set, but there is no focusable element.\r | |
1965 | // Focus is delegated to the view by the focus implementation.\r | |
1966 | initFocusableElement: function() {},\r | |
1967 | \r | |
1968 | doEnsureVisible: function(record, options) {\r | |
1969 | // Handle the case where this is a lockable assembly\r | |
1970 | if (this.lockable) {\r | |
1971 | return this.ensureLockedVisible(record, options);\r | |
1972 | }\r | |
1973 | \r | |
1974 | // Allow them to pass the record id.\r | |
1975 | if (typeof record !== 'number' && !record.isEntity) {\r | |
1976 | record = this.store.getById(record);\r | |
1977 | }\r | |
1978 | var me = this,\r | |
1979 | view = me.getView(),\r | |
1980 | domNode = view.getNode(record),\r | |
1981 | callback, scope, animate,\r | |
1982 | highlight, select, doFocus, scrollable, column, cell;\r | |
1983 | \r | |
1984 | if (options) {\r | |
1985 | callback = options.callback;\r | |
1986 | scope = options.scope;\r | |
1987 | animate = options.animate;\r | |
1988 | highlight = options.highlight;\r | |
1989 | select = options.select;\r | |
1990 | doFocus = options.focus;\r | |
1991 | column = options.column;\r | |
1992 | }\r | |
1993 | \r | |
1994 | // Always supercede any prior deferred request\r | |
1995 | if (me.deferredEnsureVisible) {\r | |
1996 | me.deferredEnsureVisible.destroy();\r | |
1997 | }\r | |
1998 | \r | |
1999 | // We have not yet run the layout.\r | |
2000 | // Add this to the end of the first sizing process.\r | |
2001 | // By using the resize event, we will come in AFTER any Component's onResize and onBoxReady handling.\r | |
2002 | if (!view.componentLayoutCounter) {\r | |
2003 | me.deferredEnsureVisible = view.on({\r | |
2004 | resize: me.doEnsureVisible,\r | |
2005 | args: Ext.Array.slice(arguments),\r | |
2006 | scope: me,\r | |
2007 | single: true,\r | |
2008 | destroyable: true\r | |
2009 | });\r | |
2010 | return;\r | |
2011 | }\r | |
2012 | \r | |
2013 | if (typeof column === 'number') {\r | |
2014 | column = me.ownerGrid.getVisibleColumnManager().getColumns()[column];\r | |
2015 | }\r | |
2016 | \r | |
2017 | // We found the DOM node associated with the record\r | |
2018 | if (domNode) {\r | |
2019 | scrollable = view.getScrollable();\r | |
2020 | if (column) {\r | |
2021 | cell = Ext.fly(domNode).selectNode(column.getCellSelector());\r | |
2022 | }\r | |
2023 | if (scrollable) {\r | |
2024 | scrollable.scrollIntoView(cell || domNode, !!column, animate, highlight);\r | |
2025 | }\r | |
2026 | if (!record.isEntity) {\r | |
2027 | record = view.getRecord(domNode);\r | |
2028 | }\r | |
2029 | if (select) {\r | |
2030 | view.getSelectionModel().select(record);\r | |
2031 | }\r | |
2032 | if (doFocus) {\r | |
2033 | view.getNavigationModel().setPosition(record, 0);\r | |
2034 | }\r | |
2035 | Ext.callback(callback, scope || me, [true, record, domNode]);\r | |
2036 | }\r | |
2037 | // If we didn't find it, it's probably because of buffered rendering\r | |
2038 | else if (view.bufferedRenderer) {\r | |
2039 | view.bufferedRenderer.scrollTo(record, {\r | |
2040 | animate: animate,\r | |
2041 | highlight: highlight,\r | |
2042 | select: select,\r | |
2043 | focus: doFocus,\r | |
2044 | column: column,\r | |
2045 | callback: function(recordIdx, record, domNode) {\r | |
2046 | Ext.callback(callback, scope || me, [true, record, domNode]);\r | |
2047 | }\r | |
2048 | });\r | |
2049 | } else {\r | |
2050 | Ext.callback(callback, scope || me, [false, null]);\r | |
2051 | }\r | |
2052 | },\r | |
2053 | \r | |
2054 | getFocusEl: function() {\r | |
2055 | return this.getView().getFocusEl();\r | |
2056 | },\r | |
2057 | \r | |
2058 | /**\r | |
2059 | * Toggles ARIA actionable mode on/off\r | |
2060 | * @param {Boolean} enabled\r | |
2061 | * @return {Boolean} `true` if actionable mode was entered\r | |
2062 | * @private\r | |
2063 | */\r | |
2064 | setActionableMode: function(enabled, position) {\r | |
2065 | // Always set the topmost grid in a lockable assembly\r | |
2066 | var me = this.ownerGrid;\r | |
2067 | \r | |
2068 | // Can be called to exit actionable mode upon a focusLeave caused by destruction\r | |
2069 | if (!me.destroying && me.view.setActionableMode(enabled, position) !== false) {\r | |
2070 | me.fireEvent('actionablemodechange', enabled);\r | |
2071 | me[enabled ? 'addCls' : 'removeCls'](me.actionableModeCls);\r | |
2072 | return true;\r | |
2073 | }\r | |
2074 | }\r | |
2075 | }\r | |
2076 | });\r |