@slickgrid-universal/row-detail-view-plugin 5.12.2 → 5.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/slickRowDetailView.js +191 -152
- package/dist/cjs/slickRowDetailView.js.map +1 -1
- package/dist/esm/slickRowDetailView.js +191 -152
- package/dist/esm/slickRowDetailView.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/slickRowDetailView.d.ts +34 -24
- package/dist/types/slickRowDetailView.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/slickRowDetailView.ts +221 -166
|
@@ -18,14 +18,14 @@ class SlickRowDetailView {
|
|
|
18
18
|
this.pluginName = 'RowDetailView';
|
|
19
19
|
this._dataViewIdProperty = 'id';
|
|
20
20
|
this._expandableOverride = null;
|
|
21
|
-
this.
|
|
21
|
+
this._expandedRowIds = new Set();
|
|
22
22
|
this._gridRowBuffer = 0;
|
|
23
23
|
this._gridUid = '';
|
|
24
24
|
this._keyPrefix = '';
|
|
25
|
-
this.
|
|
26
|
-
this._outsideRange = 5;
|
|
25
|
+
this._disposedRows = new Set();
|
|
27
26
|
this._rowIdsOutOfViewport = new Set();
|
|
28
|
-
this.
|
|
27
|
+
this._renderedViewportRowIds = new Set();
|
|
28
|
+
this._renderedIds = new Set();
|
|
29
29
|
this._defaults = {
|
|
30
30
|
alwaysRenderColumn: true,
|
|
31
31
|
columnId: '_detail_selector',
|
|
@@ -34,13 +34,12 @@ class SlickRowDetailView {
|
|
|
34
34
|
collapseAllOnSort: true,
|
|
35
35
|
collapsedClass: undefined,
|
|
36
36
|
expandedClass: undefined,
|
|
37
|
-
keyPrefix: '
|
|
37
|
+
keyPrefix: '__',
|
|
38
38
|
loadOnce: false,
|
|
39
39
|
maxRows: undefined,
|
|
40
40
|
reorderable: false,
|
|
41
41
|
saveDetailViewOnScroll: true,
|
|
42
42
|
singleRowExpand: false,
|
|
43
|
-
useSimpleViewportCalc: false,
|
|
44
43
|
toolTip: '',
|
|
45
44
|
width: 30,
|
|
46
45
|
};
|
|
@@ -49,6 +48,7 @@ class SlickRowDetailView {
|
|
|
49
48
|
this.onAsyncResponse = new common_1.SlickEvent('onAsyncResponse');
|
|
50
49
|
this.onAfterRowDetailToggle = new common_1.SlickEvent('onAfterRowDetailToggle');
|
|
51
50
|
this.onBeforeRowDetailToggle = new common_1.SlickEvent('onBeforeRowDetailToggle');
|
|
51
|
+
this.onBeforeRowOutOfViewportRange = new common_1.SlickEvent('onBeforeRowOutOfViewportRange');
|
|
52
52
|
this.onRowBackToViewportRange = new common_1.SlickEvent('onRowBackToViewportRange');
|
|
53
53
|
this.onRowOutOfViewportRange = new common_1.SlickEvent('onRowOutOfViewportRange');
|
|
54
54
|
}
|
|
@@ -72,21 +72,14 @@ class SlickRowDetailView {
|
|
|
72
72
|
get gridUid() {
|
|
73
73
|
return this._gridUid || this._grid?.getUID() || '';
|
|
74
74
|
}
|
|
75
|
-
set lastRange(range) {
|
|
76
|
-
this._lastRange = range;
|
|
77
|
-
}
|
|
78
75
|
set rowIdsOutOfViewport(rowIds) {
|
|
79
76
|
this._rowIdsOutOfViewport = new Set(rowIds);
|
|
80
77
|
}
|
|
81
|
-
get visibleRenderedCellCount() {
|
|
82
|
-
return this._visibleRenderedCellCount;
|
|
83
|
-
}
|
|
84
78
|
/**
|
|
85
79
|
* Initialize the Export Service
|
|
86
80
|
* @param _grid
|
|
87
81
|
*/
|
|
88
82
|
init(grid) {
|
|
89
|
-
this._grid = grid;
|
|
90
83
|
if (!grid) {
|
|
91
84
|
throw new Error('[Slickgrid-Universal] RowDetailView Plugin requires the Grid instance to be passed as argument to the "init()" method.');
|
|
92
85
|
}
|
|
@@ -95,7 +88,7 @@ class SlickRowDetailView {
|
|
|
95
88
|
if (!this._addonOptions) {
|
|
96
89
|
this._addonOptions = (0, utils_1.extend)(true, {}, this._defaults, this.gridOptions.rowDetailView);
|
|
97
90
|
}
|
|
98
|
-
this._keyPrefix = this._addonOptions?.keyPrefix || '
|
|
91
|
+
this._keyPrefix = this._addonOptions?.keyPrefix || '__';
|
|
99
92
|
// add PubSub instance to all SlickEvent
|
|
100
93
|
common_1.Utils.addSlickEventPubSubWhenDefined(this.pubSubService, this);
|
|
101
94
|
// Update the minRowBuffer so that the view doesn't disappear when it's at top of screen + the original default 3
|
|
@@ -104,20 +97,40 @@ class SlickRowDetailView {
|
|
|
104
97
|
this._eventHandler
|
|
105
98
|
.subscribe(this._grid.onClick, this.handleClick.bind(this))
|
|
106
99
|
.subscribe(this._grid.onBeforeEditCell, () => this.collapseAll())
|
|
107
|
-
.subscribe(this._grid.onScroll, this.
|
|
100
|
+
.subscribe(this._grid.onScroll, () => this.recalculateOutOfRangeViews(true, 0))
|
|
101
|
+
.subscribe(this._grid.onBeforeRemoveCachedRow, (_e, args) => this.handleRemoveRow(args.row));
|
|
108
102
|
// Sort will, by default, Collapse all of the open items (unless user implements his own onSort which deals with open row and padding)
|
|
109
103
|
if (this._addonOptions.collapseAllOnSort) {
|
|
110
104
|
// sort event can be triggered by column header click or from header menu
|
|
111
105
|
this.pubSubService.subscribe('onSortChanged', () => this.collapseAll());
|
|
112
|
-
this._expandedRows.clear();
|
|
113
|
-
this._rowIdsOutOfViewport.clear();
|
|
114
106
|
}
|
|
115
107
|
this._eventHandler.subscribe(this.dataView.onRowCountChanged, () => {
|
|
116
108
|
this._grid.updateRowCount();
|
|
117
109
|
this._grid.render();
|
|
118
110
|
});
|
|
119
111
|
this._eventHandler.subscribe(this.dataView.onRowsChanged, (_e, args) => {
|
|
120
|
-
this._grid.
|
|
112
|
+
const cachedRows = Object.keys(this._grid.getRowCache()).map(Number);
|
|
113
|
+
const toInvalidateRows = [];
|
|
114
|
+
const intersectedRows = args.rows.filter((nb) => cachedRows.includes(nb));
|
|
115
|
+
// only consider rows to invalidate as rows that exists in the viewport (cached rows)
|
|
116
|
+
this._expandedRowIds.forEach((itemId) => {
|
|
117
|
+
const idx = this.dataView.getRowById(itemId);
|
|
118
|
+
if (idx !== undefined && intersectedRows.includes(idx)) {
|
|
119
|
+
toInvalidateRows.push(idx);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
// don't invalidate row detail that were already rendered and visible
|
|
123
|
+
// for example, if we open row 3 and then row 1, row 3 will be pushed down but it was already rendered so no need to re-render it
|
|
124
|
+
this._renderedIds.forEach((rowId) => {
|
|
125
|
+
const dataRowIdx = this.dataView.getRowById(rowId);
|
|
126
|
+
if (dataRowIdx !== undefined) {
|
|
127
|
+
const invRowIdx = toInvalidateRows.findIndex((r) => r === dataRowIdx);
|
|
128
|
+
if (invRowIdx >= 0) {
|
|
129
|
+
toInvalidateRows.splice(invRowIdx, 1);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
this._grid.invalidateRows(toInvalidateRows);
|
|
121
134
|
this._grid.render();
|
|
122
135
|
});
|
|
123
136
|
// subscribe to the onAsyncResponse so that the plugin knows when the user server side calls finished
|
|
@@ -126,22 +139,14 @@ class SlickRowDetailView {
|
|
|
126
139
|
this._eventHandler.subscribe(this.dataView.onSetItemsCalled, () => {
|
|
127
140
|
this._dataViewIdProperty = this.dataView?.getIdPropertyName() || 'id';
|
|
128
141
|
});
|
|
129
|
-
// if we use the alternative & simpler calculation of the out of viewport range
|
|
130
|
-
// we will need to know how many rows are rendered on the screen and we need to wait for grid to be rendered
|
|
131
|
-
// unfortunately there is no triggered event for knowing when grid is finished, so we use 250ms delay and it's typically more than enough
|
|
132
|
-
if (this._addonOptions.useSimpleViewportCalc) {
|
|
133
|
-
this._eventHandler.subscribe(this._grid.onRendered, (_e, args) => {
|
|
134
|
-
if (args?.endRow) {
|
|
135
|
-
this._visibleRenderedCellCount = args.endRow - args.startRow;
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
142
|
}
|
|
140
143
|
/** Dispose of the Slick Row Detail View */
|
|
141
144
|
dispose() {
|
|
142
145
|
this._eventHandler?.unsubscribeAll();
|
|
143
|
-
this.
|
|
146
|
+
this._expandedRowIds.clear();
|
|
144
147
|
this._rowIdsOutOfViewport.clear();
|
|
148
|
+
this._renderedViewportRowIds.clear();
|
|
149
|
+
clearTimeout(this._backViewportTimer);
|
|
145
150
|
}
|
|
146
151
|
create(columnDefinitions, gridOptions) {
|
|
147
152
|
if (!gridOptions.rowDetailView) {
|
|
@@ -188,16 +193,17 @@ class SlickRowDetailView {
|
|
|
188
193
|
/** Collapse all of the open items */
|
|
189
194
|
collapseAll() {
|
|
190
195
|
this.dataView.beginUpdate();
|
|
191
|
-
this.
|
|
192
|
-
this.collapseDetailView(
|
|
196
|
+
this._expandedRowIds.forEach((itemId) => {
|
|
197
|
+
this.collapseDetailView(itemId, true);
|
|
193
198
|
});
|
|
194
199
|
this.dataView.endUpdate();
|
|
195
200
|
}
|
|
196
|
-
/**
|
|
197
|
-
collapseDetailView(
|
|
201
|
+
/** Collapse an Item so it is not longer seen */
|
|
202
|
+
collapseDetailView(itemId, isMultipleCollapsing = false) {
|
|
198
203
|
if (!isMultipleCollapsing) {
|
|
199
204
|
this.dataView.beginUpdate();
|
|
200
205
|
}
|
|
206
|
+
const item = this.dataView.getItemById(itemId);
|
|
201
207
|
// Save the details on the collapse assuming onetime loading
|
|
202
208
|
if (this._addonOptions.loadOnce) {
|
|
203
209
|
this.saveDetailView(item);
|
|
@@ -208,20 +214,26 @@ class SlickRowDetailView {
|
|
|
208
214
|
}
|
|
209
215
|
item[`${this._keyPrefix}sizePadding`] = 0;
|
|
210
216
|
this.dataView.updateItem(item[this._dataViewIdProperty], item);
|
|
211
|
-
// Remove the item from the expandedRows
|
|
212
|
-
this.
|
|
217
|
+
// Remove the item from the expandedRows & renderedIds
|
|
218
|
+
this._expandedRowIds = new Set(Array.from(this._expandedRowIds).filter((expItemId) => expItemId !== item[this._dataViewIdProperty]));
|
|
219
|
+
this._renderedIds.delete(item[this._dataViewIdProperty]);
|
|
220
|
+
// we need to reevaluate & invalidate any row detail that are shown on top of the row that we're closing
|
|
221
|
+
this.reevaluateRenderedRowIds(item);
|
|
213
222
|
if (!isMultipleCollapsing) {
|
|
214
223
|
this.dataView.endUpdate();
|
|
215
224
|
}
|
|
216
225
|
}
|
|
217
226
|
/** Expand a row given the dataview item that is to be expanded */
|
|
218
|
-
expandDetailView(
|
|
227
|
+
expandDetailView(itemId) {
|
|
219
228
|
if (this._addonOptions?.singleRowExpand) {
|
|
220
229
|
this.collapseAll();
|
|
221
230
|
}
|
|
231
|
+
const item = this.dataView.getItemById(itemId);
|
|
232
|
+
// we need to reevaluate & invalidate any row detail that are shown on top of the row that we're closing
|
|
233
|
+
this.reevaluateRenderedRowIds(item);
|
|
222
234
|
item[`${this._keyPrefix}collapsed`] = false;
|
|
223
|
-
this.
|
|
224
|
-
//
|
|
235
|
+
this._expandedRowIds.add(itemId);
|
|
236
|
+
// in the case something went wrong loading it the first time such a scroll of screen before loaded
|
|
225
237
|
if (!item[`${this._keyPrefix}detailContent`]) {
|
|
226
238
|
item[`${this._keyPrefix}detailViewLoaded`] = false;
|
|
227
239
|
}
|
|
@@ -245,13 +257,20 @@ class SlickRowDetailView {
|
|
|
245
257
|
// async server call
|
|
246
258
|
this._addonOptions.process(item);
|
|
247
259
|
}
|
|
260
|
+
/** reset all Set rows/ids cache and start empty (but keep expanded rows ref) */
|
|
261
|
+
resetRenderedRows() {
|
|
262
|
+
this._renderedViewportRowIds.clear();
|
|
263
|
+
this._disposedRows.clear();
|
|
264
|
+
}
|
|
248
265
|
/** Saves the current state of the detail view */
|
|
249
266
|
saveDetailView(item) {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
267
|
+
if (this._addonOptions.loadOnce) {
|
|
268
|
+
const view = document.querySelector(`.${this.gridUid} .innerDetailView_${item[this._dataViewIdProperty]}`);
|
|
269
|
+
if (view) {
|
|
270
|
+
const html = view.innerHTML;
|
|
271
|
+
if (html !== undefined) {
|
|
272
|
+
item[`${this._keyPrefix}detailContent`] = html;
|
|
273
|
+
}
|
|
255
274
|
}
|
|
256
275
|
}
|
|
257
276
|
}
|
|
@@ -264,13 +283,14 @@ class SlickRowDetailView {
|
|
|
264
283
|
console.error('SlickRowDetailView plugin requires the onAsyncResponse() to supply "args.item" property.');
|
|
265
284
|
return;
|
|
266
285
|
}
|
|
267
|
-
// we accept item/itemDetail, just get the one which has data
|
|
286
|
+
// @deprecated `args.itemDetail` we accept item/itemDetail, just get the one which has data
|
|
268
287
|
const itemDetail = args.item || args.itemDetail;
|
|
269
|
-
//
|
|
288
|
+
// if we just want to load in a view directly we can use detailView property to do so
|
|
270
289
|
itemDetail[`${this._keyPrefix}detailContent`] = args.detailView ?? this._addonOptions?.postTemplate?.(itemDetail);
|
|
271
290
|
itemDetail[`${this._keyPrefix}detailViewLoaded`] = true;
|
|
272
291
|
this.dataView.updateItem(itemDetail[this._dataViewIdProperty], itemDetail);
|
|
273
292
|
// trigger an event once the post template is finished loading
|
|
293
|
+
this._renderedIds.add(itemDetail[this.dataViewIdProperty]);
|
|
274
294
|
this.onAsyncEndUpdate.notify({
|
|
275
295
|
grid: this._grid,
|
|
276
296
|
item: itemDetail,
|
|
@@ -278,7 +298,6 @@ class SlickRowDetailView {
|
|
|
278
298
|
}, e, this);
|
|
279
299
|
}
|
|
280
300
|
/**
|
|
281
|
-
* TODO interface only has a GETTER not a SETTER..why?
|
|
282
301
|
* Override the logic for showing (or not) the expand icon (use case example: only every 2nd row is expandable)
|
|
283
302
|
* Method that user can pass to override the default behavior or making every row an expandable row.
|
|
284
303
|
* In order word, user can choose which rows to be an available row detail (or not) by providing his own logic.
|
|
@@ -313,8 +332,8 @@ class SlickRowDetailView {
|
|
|
313
332
|
};
|
|
314
333
|
}
|
|
315
334
|
/** return the currently expanded rows */
|
|
316
|
-
|
|
317
|
-
return Array.from(this.
|
|
335
|
+
getExpandedRowIds() {
|
|
336
|
+
return Array.from(this._expandedRowIds);
|
|
318
337
|
}
|
|
319
338
|
/** return the rows that are out of the viewport */
|
|
320
339
|
getOutOfViewportRows() {
|
|
@@ -332,7 +351,7 @@ class SlickRowDetailView {
|
|
|
332
351
|
if (!item) {
|
|
333
352
|
return;
|
|
334
353
|
}
|
|
335
|
-
//
|
|
354
|
+
// Grab each of the DOM elements
|
|
336
355
|
const mainContainer = document.querySelector(`.${this.gridUid} .detailViewContainer_${item[this._dataViewIdProperty]}`);
|
|
337
356
|
const cellItem = document.querySelector(`.${this.gridUid} .cellDetailView_${item[this._dataViewIdProperty]}`);
|
|
338
357
|
const inner = document.querySelector(`.${this.gridUid} .innerDetailView_${item[this._dataViewIdProperty]}`);
|
|
@@ -373,11 +392,61 @@ class SlickRowDetailView {
|
|
|
373
392
|
// Lastly save the updated state
|
|
374
393
|
this.saveDetailView(item);
|
|
375
394
|
}
|
|
395
|
+
/**
|
|
396
|
+
* (re)calculate/sync row detail views that are out of range of the viewport and trigger events (when enabled)
|
|
397
|
+
* @param {Boolean} [triggerEvent] - should trigger notify event which will re-render the detail view
|
|
398
|
+
* @param {Number} [delay] - optional delay to execute the calculation of out of range views
|
|
399
|
+
*/
|
|
400
|
+
recalculateOutOfRangeViews(triggerEvent = true, delay) {
|
|
401
|
+
clearTimeout(this._backViewportTimer);
|
|
402
|
+
const calculateFn = () => this._expandedRowIds.forEach((itemId) => {
|
|
403
|
+
const item = this.dataView.getItemById(itemId);
|
|
404
|
+
const rowIdx = this.dataView.getRowById(itemId);
|
|
405
|
+
const cachedRows = Object.keys(this._grid.getRowCache()).map(Number);
|
|
406
|
+
const visible = this._grid.getRenderedRange();
|
|
407
|
+
const rowDetailCount = this.gridOptions.rowDetailView?.panelRows ?? 0;
|
|
408
|
+
this._visibleRenderedCell = { startRow: visible.top, endRow: visible.bottom };
|
|
409
|
+
let { startRow, endRow } = this._visibleRenderedCell;
|
|
410
|
+
if (rowIdx >= startRow && rowIdx <= endRow) {
|
|
411
|
+
const rowSum = rowIdx + (this.gridOptions.rowDetailView?.panelRows ?? 0);
|
|
412
|
+
if (rowSum > endRow) {
|
|
413
|
+
endRow = rowSum;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
const rdEndRow = rowIdx + rowDetailCount;
|
|
417
|
+
if (startRow > rowIdx && rowIdx < rdEndRow && rdEndRow > this._visibleRenderedCell.startRow + 1) {
|
|
418
|
+
startRow = rowIdx;
|
|
419
|
+
}
|
|
420
|
+
this._visibleRenderedCell = { startRow, endRow };
|
|
421
|
+
if (!this._renderedViewportRowIds.has(itemId) &&
|
|
422
|
+
this._visibleRenderedCell &&
|
|
423
|
+
rowIdx >= this._visibleRenderedCell.startRow &&
|
|
424
|
+
rowIdx <= this._visibleRenderedCell.endRow &&
|
|
425
|
+
cachedRows.includes(rowIdx)) {
|
|
426
|
+
this._disposedRows.delete(rowIdx);
|
|
427
|
+
this.notifyViewportChange(item, 'add', triggerEvent);
|
|
428
|
+
}
|
|
429
|
+
else if ((this._disposedRows.has(rowIdx) && !cachedRows.includes(rowIdx)) ||
|
|
430
|
+
(!cachedRows.includes(rowIdx) &&
|
|
431
|
+
this._renderedViewportRowIds.has(itemId) &&
|
|
432
|
+
this._visibleRenderedCell &&
|
|
433
|
+
(rowIdx < this._visibleRenderedCell.startRow || rowIdx > this._visibleRenderedCell.endRow))) {
|
|
434
|
+
this.notifyViewportChange(item, 'remove', triggerEvent);
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
if (delay !== undefined) {
|
|
438
|
+
this._backViewportTimer = setTimeout(calculateFn, delay);
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
calculateFn();
|
|
442
|
+
}
|
|
443
|
+
}
|
|
376
444
|
// --
|
|
377
445
|
// protected functions
|
|
378
446
|
// ------------------
|
|
379
447
|
/**
|
|
380
|
-
* create the detail ctr node. this belongs to the dev & can be custom-styled as per
|
|
448
|
+
* create the row detail ctr node. this belongs to the dev & can be custom-styled as per
|
|
449
|
+
* @param {Object} item
|
|
381
450
|
*/
|
|
382
451
|
applyTemplateNewLineHeight(item) {
|
|
383
452
|
// the height is calculated by the template row count (how many line of items does the template view have)
|
|
@@ -392,82 +461,17 @@ class SlickRowDetailView {
|
|
|
392
461
|
this.dataView.insertItem((idxParent || 0) + idx, this.getPaddingItem(item, idx));
|
|
393
462
|
}
|
|
394
463
|
}
|
|
395
|
-
|
|
396
|
-
if (
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
if (this._lastRange.top === renderedRange.top && this._lastRange.bottom === renderedRange.bottom) {
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
// If our new top is smaller we are scrolling up
|
|
409
|
-
if (this._lastRange.top > renderedRange.top ||
|
|
410
|
-
// Or we are at very top but our bottom is increasing
|
|
411
|
-
(this._lastRange.top === 0 && renderedRange.top === 0 && this._lastRange.bottom > renderedRange.bottom)) {
|
|
412
|
-
scrollDir = 'UP';
|
|
413
|
-
}
|
|
414
|
-
}
|
|
464
|
+
notifyViewportChange(item, action, triggerEvent = true) {
|
|
465
|
+
if (item) {
|
|
466
|
+
const itemId = item[this._dataViewIdProperty];
|
|
467
|
+
if (action === 'add') {
|
|
468
|
+
this._renderedViewportRowIds.add(itemId);
|
|
469
|
+
triggerEvent && this.notifyBackToViewportWhenDomExist(item);
|
|
470
|
+
}
|
|
471
|
+
else if (action === 'remove') {
|
|
472
|
+
this._renderedViewportRowIds.delete(itemId);
|
|
473
|
+
triggerEvent && this.notifyOutOfViewport(item);
|
|
415
474
|
}
|
|
416
|
-
this._expandedRows.forEach((row) => {
|
|
417
|
-
const rowIndex = this.dataView.getRowById(row[this._dataViewIdProperty]);
|
|
418
|
-
const rowPadding = row[`${this._keyPrefix}sizePadding`];
|
|
419
|
-
const isRowOutOfRange = this._rowIdsOutOfViewport.has(row[this._dataViewIdProperty]);
|
|
420
|
-
if (scrollDir === 'UP') {
|
|
421
|
-
// save the view when asked
|
|
422
|
-
if (this._addonOptions.saveDetailViewOnScroll) {
|
|
423
|
-
// If the bottom item within buffer range is an expanded row save it.
|
|
424
|
-
if (rowIndex >= renderedRange.bottom - this._gridRowBuffer) {
|
|
425
|
-
this.saveDetailView(row);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
// If the row expanded area is within the buffer notify that it is back in range
|
|
429
|
-
if (isRowOutOfRange && rowIndex - this._outsideRange < renderedRange.top && rowIndex >= renderedRange.top) {
|
|
430
|
-
this.notifyBackToViewportWhenDomExist(row, row[this._dataViewIdProperty]);
|
|
431
|
-
}
|
|
432
|
-
else if (!isRowOutOfRange && rowIndex + rowPadding > renderedRange.bottom) {
|
|
433
|
-
// if our first expanded row is about to go off the bottom
|
|
434
|
-
this.notifyOutOfViewport(row, row[this._dataViewIdProperty]);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
else if (scrollDir === 'DOWN') {
|
|
438
|
-
// save the view when asked
|
|
439
|
-
if (this._addonOptions.saveDetailViewOnScroll) {
|
|
440
|
-
// If the top item within buffer range is an expanded row save it.
|
|
441
|
-
if (rowIndex <= renderedRange.top + this._gridRowBuffer) {
|
|
442
|
-
this.saveDetailView(row);
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
// If row index is i higher than bottom with some added value (To ignore top rows off view) and is with view and was our of range
|
|
446
|
-
if (isRowOutOfRange && rowIndex + rowPadding + this._outsideRange > renderedRange.bottom && rowIndex < rowIndex + rowPadding) {
|
|
447
|
-
this.notifyBackToViewportWhenDomExist(row, row[this._dataViewIdProperty]);
|
|
448
|
-
}
|
|
449
|
-
else if (!isRowOutOfRange && rowIndex < renderedRange.top) {
|
|
450
|
-
// if our row is outside top of and the buffering zone but not in the array of outOfVisable range notify it
|
|
451
|
-
this.notifyOutOfViewport(row, row[this._dataViewIdProperty]);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
});
|
|
455
|
-
this._lastRange = renderedRange;
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
calculateOutOfRangeViewsSimplerVersion() {
|
|
459
|
-
if (this._grid) {
|
|
460
|
-
const renderedRange = this._grid.getRenderedRange();
|
|
461
|
-
this._expandedRows.forEach((row) => {
|
|
462
|
-
const rowIndex = this.dataView.getRowById(row[this._dataViewIdProperty]);
|
|
463
|
-
const isOutOfVisibility = this.checkIsRowOutOfViewportRange(rowIndex, renderedRange);
|
|
464
|
-
if (!isOutOfVisibility && this._rowIdsOutOfViewport.has(row[this._dataViewIdProperty])) {
|
|
465
|
-
this.notifyBackToViewportWhenDomExist(row, row[this._dataViewIdProperty]);
|
|
466
|
-
}
|
|
467
|
-
else if (isOutOfVisibility) {
|
|
468
|
-
this.notifyOutOfViewport(row, row[this._dataViewIdProperty]);
|
|
469
|
-
}
|
|
470
|
-
});
|
|
471
475
|
}
|
|
472
476
|
}
|
|
473
477
|
checkExpandableOverride(row, dataContext, grid) {
|
|
@@ -476,9 +480,6 @@ class SlickRowDetailView {
|
|
|
476
480
|
}
|
|
477
481
|
return true;
|
|
478
482
|
}
|
|
479
|
-
checkIsRowOutOfViewportRange(rowIndex, renderedRange) {
|
|
480
|
-
return Math.abs(renderedRange.bottom - this._gridRowBuffer - rowIndex) > this._visibleRenderedCellCount * 2;
|
|
481
|
-
}
|
|
482
483
|
/** Get the Row Detail padding (which are the rows dedicated to the detail panel) */
|
|
483
484
|
getPaddingItem(parent, offset) {
|
|
484
485
|
const item = {};
|
|
@@ -562,11 +563,12 @@ class SlickRowDetailView {
|
|
|
562
563
|
/** When row is getting toggled, we will handle the action of collapsing/expanding */
|
|
563
564
|
handleAccordionShowHide(item) {
|
|
564
565
|
if (item) {
|
|
566
|
+
const itemId = item[this._dataViewIdProperty];
|
|
565
567
|
if (!item[`${this._keyPrefix}collapsed`]) {
|
|
566
|
-
this.collapseDetailView(
|
|
568
|
+
this.collapseDetailView(itemId);
|
|
567
569
|
}
|
|
568
570
|
else {
|
|
569
|
-
this.expandDetailView(
|
|
571
|
+
this.expandDetailView(itemId);
|
|
570
572
|
}
|
|
571
573
|
}
|
|
572
574
|
}
|
|
@@ -592,52 +594,89 @@ class SlickRowDetailView {
|
|
|
592
594
|
.getReturnValue() === false) {
|
|
593
595
|
return;
|
|
594
596
|
}
|
|
597
|
+
// tag any row details that will need to be re-rendered after the row detail is toggled.
|
|
598
|
+
// for example if row(2) is open and we open row(1) then row(2) needs to be re-rendered,
|
|
599
|
+
// if however row(1) is open and we open row(2) then there is nothing to re-render
|
|
600
|
+
const toReRenderItems = [];
|
|
601
|
+
const visible = this._grid.getRenderedRange();
|
|
602
|
+
this._expandedRowIds.forEach((itemId) => {
|
|
603
|
+
const row = this.dataView.getRowById(itemId);
|
|
604
|
+
if (row !== undefined && row > args.row && row >= visible.top && row <= visible.bottom) {
|
|
605
|
+
const item = this.dataView.getItemById(itemId);
|
|
606
|
+
toReRenderItems.push(item);
|
|
607
|
+
this.notifyOutOfViewport(item);
|
|
608
|
+
}
|
|
609
|
+
});
|
|
595
610
|
this.toggleRowSelection(args.row, dataContext);
|
|
596
611
|
// trigger an event after toggling
|
|
597
612
|
this.onAfterRowDetailToggle.notify({
|
|
598
613
|
grid: this._grid,
|
|
599
614
|
item: dataContext,
|
|
600
|
-
expandedRows: Array.from(this.
|
|
615
|
+
expandedRows: Array.from(this._expandedRowIds).map((id) => this.dataView.getItemById(id)),
|
|
601
616
|
}, e, this);
|
|
617
|
+
// re-render the row details that were tagged as
|
|
618
|
+
toReRenderItems.forEach(item => this.notifyViewportChange(item, 'add', true));
|
|
602
619
|
e.stopPropagation();
|
|
603
620
|
e.stopImmediatePropagation();
|
|
604
621
|
}
|
|
605
622
|
}
|
|
606
623
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
624
|
+
handleRemoveRow(rowIndex) {
|
|
625
|
+
const item = this.dataView.getItemByIdx(rowIndex);
|
|
626
|
+
const rowId = item[this.dataViewIdProperty];
|
|
627
|
+
if (this._expandedRowIds.has(rowId)) {
|
|
628
|
+
this.onBeforeRowOutOfViewportRange.notify({
|
|
629
|
+
grid: this._grid,
|
|
630
|
+
item,
|
|
631
|
+
rowId,
|
|
632
|
+
rowIndex,
|
|
633
|
+
expandedRows: Array.from(this._expandedRowIds).map((id) => this.dataView.getItemById(id)),
|
|
634
|
+
rowIdsOutOfViewport: Array.from(this.syncOutOfViewportArray(rowId, true)),
|
|
635
|
+
}, null, this);
|
|
636
|
+
this._disposedRows.add(rowIndex);
|
|
613
637
|
}
|
|
614
638
|
}
|
|
615
|
-
notifyOutOfViewport(item
|
|
639
|
+
notifyOutOfViewport(item) {
|
|
616
640
|
const rowIndex = item.rowIndex || this.dataView.getRowById(item[this._dataViewIdProperty]);
|
|
641
|
+
const rowId = item[this.dataViewIdProperty];
|
|
642
|
+
this._renderedIds.delete(rowId);
|
|
617
643
|
this.onRowOutOfViewportRange.notify({
|
|
618
644
|
grid: this._grid,
|
|
619
645
|
item,
|
|
620
646
|
rowId,
|
|
621
647
|
rowIndex,
|
|
622
|
-
expandedRows: Array.from(this.
|
|
648
|
+
expandedRows: Array.from(this._expandedRowIds).map((id) => this.dataView.getItemById(id)),
|
|
623
649
|
rowIdsOutOfViewport: Array.from(this.syncOutOfViewportArray(rowId, true)),
|
|
624
650
|
}, null, this);
|
|
625
651
|
}
|
|
626
|
-
notifyBackToViewportWhenDomExist(item
|
|
652
|
+
notifyBackToViewportWhenDomExist(item) {
|
|
627
653
|
const rowIndex = item.rowIndex || this.dataView.getRowById(item[this._dataViewIdProperty]);
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
654
|
+
const rowId = item[this.dataViewIdProperty];
|
|
655
|
+
// make sure View Row DOM Element really exist before notifying that it's a row that is visible again
|
|
656
|
+
if (document.querySelector(`.${this.gridUid} .cellDetailView_${item[this._dataViewIdProperty]}`)) {
|
|
657
|
+
this.onRowBackToViewportRange.notify({
|
|
658
|
+
grid: this._grid,
|
|
659
|
+
item,
|
|
660
|
+
rowId,
|
|
661
|
+
rowIndex,
|
|
662
|
+
expandedRows: Array.from(this._expandedRowIds).map((id) => this.dataView.getItemById(id)),
|
|
663
|
+
rowIdsOutOfViewport: Array.from(this.syncOutOfViewportArray(rowId, false)),
|
|
664
|
+
}, null, this);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* keep any row detail that are shown on top of the row that we're opening
|
|
669
|
+
* but invalidate any rows that are after the row that we're opening
|
|
670
|
+
*/
|
|
671
|
+
reevaluateRenderedRowIds(item) {
|
|
672
|
+
// get current item row index
|
|
673
|
+
const rowIdx = this.dataView.getRowById(item[this._dataViewIdProperty]);
|
|
674
|
+
this._renderedViewportRowIds.forEach((rid) => {
|
|
675
|
+
const invRowIdx = this.dataView.getRowById(rid);
|
|
676
|
+
if (invRowIdx !== undefined && invRowIdx > rowIdx) {
|
|
677
|
+
this.notifyViewportChange(this.dataView.getItemById(rid), 'remove');
|
|
639
678
|
}
|
|
640
|
-
}
|
|
679
|
+
});
|
|
641
680
|
}
|
|
642
681
|
syncOutOfViewportArray(rowId, isAdding) {
|
|
643
682
|
const hasRowId = this._rowIdsOutOfViewport.has(rowId);
|