@react-stately/layout 4.0.3 → 4.1.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/LICENSE +201 -0
- package/dist/GridLayout.main.js +1 -1
- package/dist/GridLayout.main.js.map +1 -1
- package/dist/GridLayout.mjs +1 -1
- package/dist/GridLayout.module.js +1 -1
- package/dist/GridLayout.module.js.map +1 -1
- package/dist/ListLayout.main.js +41 -25
- package/dist/ListLayout.main.js.map +1 -1
- package/dist/ListLayout.mjs +41 -25
- package/dist/ListLayout.module.js +41 -25
- package/dist/ListLayout.module.js.map +1 -1
- package/dist/TableLayout.main.js +45 -34
- package/dist/TableLayout.main.js.map +1 -1
- package/dist/TableLayout.mjs +45 -34
- package/dist/TableLayout.module.js +45 -34
- package/dist/TableLayout.module.js.map +1 -1
- package/dist/types.d.ts +13 -11
- package/dist/types.d.ts.map +1 -1
- package/package.json +11 -10
- package/src/GridLayout.ts +15 -15
- package/src/ListLayout.ts +60 -46
- package/src/TableLayout.ts +54 -39
package/src/ListLayout.ts
CHANGED
|
@@ -51,16 +51,15 @@ const DEFAULT_HEIGHT = 48;
|
|
|
51
51
|
* the virtualizer itself).
|
|
52
52
|
*/
|
|
53
53
|
export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTargetDelegate {
|
|
54
|
-
protected rowHeight: number;
|
|
55
|
-
protected estimatedRowHeight: number;
|
|
56
|
-
protected headingHeight: number;
|
|
57
|
-
protected estimatedHeadingHeight: number;
|
|
58
|
-
protected loaderHeight: number;
|
|
54
|
+
protected rowHeight: number | null;
|
|
55
|
+
protected estimatedRowHeight: number | null;
|
|
56
|
+
protected headingHeight: number | null;
|
|
57
|
+
protected estimatedHeadingHeight: number | null;
|
|
58
|
+
protected loaderHeight: number | null;
|
|
59
59
|
protected dropIndicatorThickness: number;
|
|
60
60
|
protected layoutNodes: Map<Key, LayoutNode>;
|
|
61
61
|
protected contentSize: Size;
|
|
62
|
-
protected
|
|
63
|
-
private lastCollection: Collection<Node<T>>;
|
|
62
|
+
protected lastCollection: Collection<Node<T>> | null;
|
|
64
63
|
private lastWidth: number;
|
|
65
64
|
protected rootNodes: LayoutNode[];
|
|
66
65
|
private invalidateEverything: boolean;
|
|
@@ -75,21 +74,27 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
|
|
|
75
74
|
*/
|
|
76
75
|
constructor(options: ListLayoutOptions = {}) {
|
|
77
76
|
super();
|
|
78
|
-
this.rowHeight = options.rowHeight;
|
|
79
|
-
this.estimatedRowHeight = options.estimatedRowHeight;
|
|
80
|
-
this.headingHeight = options.headingHeight;
|
|
81
|
-
this.estimatedHeadingHeight = options.estimatedHeadingHeight;
|
|
82
|
-
this.loaderHeight = options.loaderHeight;
|
|
77
|
+
this.rowHeight = options.rowHeight ?? null;
|
|
78
|
+
this.estimatedRowHeight = options.estimatedRowHeight ?? null;
|
|
79
|
+
this.headingHeight = options.headingHeight ?? null;
|
|
80
|
+
this.estimatedHeadingHeight = options.estimatedHeadingHeight ?? null;
|
|
81
|
+
this.loaderHeight = options.loaderHeight ?? null;
|
|
83
82
|
this.dropIndicatorThickness = options.dropIndicatorThickness || 2;
|
|
84
83
|
this.layoutNodes = new Map();
|
|
85
84
|
this.rootNodes = [];
|
|
86
85
|
this.lastWidth = 0;
|
|
87
86
|
this.lastCollection = null;
|
|
87
|
+
this.invalidateEverything = false;
|
|
88
88
|
this.validRect = new Rect();
|
|
89
89
|
this.requestedRect = new Rect();
|
|
90
90
|
this.contentSize = new Size();
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
// Backward compatibility for subclassing.
|
|
94
|
+
protected get collection(): Collection<Node<T>> {
|
|
95
|
+
return this.virtualizer!.collection;
|
|
96
|
+
}
|
|
97
|
+
|
|
93
98
|
getLayoutInfo(key: Key) {
|
|
94
99
|
this.ensureLayoutInfo(key);
|
|
95
100
|
return this.layoutNodes.get(key)?.layoutInfo || null;
|
|
@@ -99,7 +104,7 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
|
|
|
99
104
|
// Adjust rect to keep number of visible rows consistent.
|
|
100
105
|
// (only if height > 1 for getDropTargetFromPoint)
|
|
101
106
|
if (rect.height > 1) {
|
|
102
|
-
let rowHeight =
|
|
107
|
+
let rowHeight = this.rowHeight ?? this.estimatedRowHeight ?? DEFAULT_HEIGHT;
|
|
103
108
|
rect.y = Math.floor(rect.y / rowHeight) * rowHeight;
|
|
104
109
|
rect.height = Math.ceil(rect.height / rowHeight) * rowHeight;
|
|
105
110
|
}
|
|
@@ -137,7 +142,7 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
|
|
|
137
142
|
}
|
|
138
143
|
|
|
139
144
|
// Ensure all of the persisted keys are available.
|
|
140
|
-
for (let key of this.virtualizer
|
|
145
|
+
for (let key of this.virtualizer!.persistedKeys) {
|
|
141
146
|
if (this.ensureLayoutInfo(key)) {
|
|
142
147
|
return;
|
|
143
148
|
}
|
|
@@ -159,32 +164,32 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
|
|
|
159
164
|
}
|
|
160
165
|
|
|
161
166
|
protected isVisible(node: LayoutNode, rect: Rect) {
|
|
162
|
-
return node.layoutInfo.rect.intersects(rect) || node.layoutInfo.isSticky || node.layoutInfo.type === 'header' || this.virtualizer
|
|
167
|
+
return node.layoutInfo.rect.intersects(rect) || node.layoutInfo.isSticky || node.layoutInfo.type === 'header' || this.virtualizer!.isPersistedKey(node.layoutInfo.key);
|
|
163
168
|
}
|
|
164
169
|
|
|
165
170
|
protected shouldInvalidateEverything(invalidationContext: InvalidationContext<O>) {
|
|
166
171
|
// Invalidate cache if the size of the collection changed.
|
|
167
172
|
// In this case, we need to recalculate the entire layout.
|
|
168
|
-
return invalidationContext.sizeChanged;
|
|
173
|
+
return invalidationContext.sizeChanged || false;
|
|
169
174
|
}
|
|
170
175
|
|
|
171
176
|
update(invalidationContext: InvalidationContext<O>) {
|
|
172
|
-
|
|
177
|
+
let collection = this.virtualizer!.collection;
|
|
173
178
|
|
|
174
179
|
// Reset valid rect if we will have to invalidate everything.
|
|
175
180
|
// Otherwise we can reuse cached layout infos outside the current visible rect.
|
|
176
181
|
this.invalidateEverything = this.shouldInvalidateEverything(invalidationContext);
|
|
177
182
|
if (this.invalidateEverything) {
|
|
178
|
-
this.requestedRect = this.virtualizer
|
|
183
|
+
this.requestedRect = this.virtualizer!.visibleRect.copy();
|
|
179
184
|
this.layoutNodes.clear();
|
|
180
185
|
}
|
|
181
186
|
|
|
182
187
|
this.rootNodes = this.buildCollection();
|
|
183
188
|
|
|
184
189
|
// Remove deleted layout nodes
|
|
185
|
-
if (this.lastCollection &&
|
|
190
|
+
if (this.lastCollection && collection !== this.lastCollection) {
|
|
186
191
|
for (let key of this.lastCollection.getKeys()) {
|
|
187
|
-
if (!
|
|
192
|
+
if (!collection.getItem(key)) {
|
|
188
193
|
let layoutNode = this.layoutNodes.get(key);
|
|
189
194
|
if (layoutNode) {
|
|
190
195
|
this.layoutNodes.delete(key);
|
|
@@ -193,17 +198,18 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
|
|
|
193
198
|
}
|
|
194
199
|
}
|
|
195
200
|
|
|
196
|
-
this.lastWidth = this.virtualizer
|
|
197
|
-
this.lastCollection =
|
|
201
|
+
this.lastWidth = this.virtualizer!.visibleRect.width;
|
|
202
|
+
this.lastCollection = collection;
|
|
198
203
|
this.invalidateEverything = false;
|
|
199
204
|
this.validRect = this.requestedRect.copy();
|
|
200
205
|
}
|
|
201
206
|
|
|
202
207
|
protected buildCollection(y = 0): LayoutNode[] {
|
|
208
|
+
let collection = this.virtualizer!.collection;
|
|
203
209
|
let skipped = 0;
|
|
204
|
-
let nodes = [];
|
|
205
|
-
for (let node of
|
|
206
|
-
let rowHeight = this.rowHeight ?? this.estimatedRowHeight;
|
|
210
|
+
let nodes: LayoutNode[] = [];
|
|
211
|
+
for (let node of collection) {
|
|
212
|
+
let rowHeight = this.rowHeight ?? this.estimatedRowHeight ?? DEFAULT_HEIGHT;
|
|
207
213
|
|
|
208
214
|
// Skip rows before the valid rectangle unless they are already cached.
|
|
209
215
|
if (node.type === 'item' && y + rowHeight < this.requestedRect.y && !this.isValid(node, y)) {
|
|
@@ -217,12 +223,12 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
|
|
|
217
223
|
nodes.push(layoutNode);
|
|
218
224
|
|
|
219
225
|
if (node.type === 'item' && y > this.requestedRect.maxY) {
|
|
220
|
-
y += (
|
|
226
|
+
y += (collection.size - (nodes.length + skipped)) * rowHeight;
|
|
221
227
|
break;
|
|
222
228
|
}
|
|
223
229
|
}
|
|
224
230
|
|
|
225
|
-
this.contentSize = new Size(this.virtualizer
|
|
231
|
+
this.contentSize = new Size(this.virtualizer!.visibleRect.width, y);
|
|
226
232
|
return nodes;
|
|
227
233
|
}
|
|
228
234
|
|
|
@@ -240,7 +246,7 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
|
|
|
240
246
|
|
|
241
247
|
protected buildChild(node: Node<T>, x: number, y: number, parentKey: Key | null): LayoutNode {
|
|
242
248
|
if (this.isValid(node, y)) {
|
|
243
|
-
return this.layoutNodes.get(node.key)
|
|
249
|
+
return this.layoutNodes.get(node.key)!;
|
|
244
250
|
}
|
|
245
251
|
|
|
246
252
|
let layoutNode = this.buildNode(node, x, y);
|
|
@@ -260,14 +266,16 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
|
|
|
260
266
|
return this.buildSectionHeader(node, x, y);
|
|
261
267
|
case 'loader':
|
|
262
268
|
return this.buildLoader(node, x, y);
|
|
269
|
+
default:
|
|
270
|
+
throw new Error('Unsupported node type: ' + node.type);
|
|
263
271
|
}
|
|
264
272
|
}
|
|
265
273
|
|
|
266
274
|
protected buildLoader(node: Node<T>, x: number, y: number): LayoutNode {
|
|
267
275
|
let rect = new Rect(x, y, 0, 0);
|
|
268
276
|
let layoutInfo = new LayoutInfo('loader', node.key, rect);
|
|
269
|
-
rect.width = this.virtualizer
|
|
270
|
-
rect.height = this.loaderHeight || this.rowHeight || this.estimatedRowHeight;
|
|
277
|
+
rect.width = this.virtualizer!.contentSize.width;
|
|
278
|
+
rect.height = this.loaderHeight || this.rowHeight || this.estimatedRowHeight || DEFAULT_HEIGHT;
|
|
271
279
|
|
|
272
280
|
return {
|
|
273
281
|
layoutInfo,
|
|
@@ -276,15 +284,16 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
|
|
|
276
284
|
}
|
|
277
285
|
|
|
278
286
|
protected buildSection(node: Node<T>, x: number, y: number): LayoutNode {
|
|
279
|
-
let
|
|
287
|
+
let collection = this.virtualizer!.collection;
|
|
288
|
+
let width = this.virtualizer!.visibleRect.width;
|
|
280
289
|
let rect = new Rect(0, y, width, 0);
|
|
281
290
|
let layoutInfo = new LayoutInfo(node.type, node.key, rect);
|
|
282
291
|
|
|
283
292
|
let startY = y;
|
|
284
293
|
let skipped = 0;
|
|
285
|
-
let children = [];
|
|
286
|
-
for (let child of getChildNodes(node,
|
|
287
|
-
let rowHeight = (this.rowHeight ?? this.estimatedRowHeight);
|
|
294
|
+
let children: LayoutNode[] = [];
|
|
295
|
+
for (let child of getChildNodes(node, collection)) {
|
|
296
|
+
let rowHeight = (this.rowHeight ?? this.estimatedRowHeight ?? DEFAULT_HEIGHT);
|
|
288
297
|
|
|
289
298
|
// Skip rows before the valid rectangle unless they are already cached.
|
|
290
299
|
if (y + rowHeight < this.requestedRect.y && !this.isValid(node, y)) {
|
|
@@ -299,7 +308,7 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
|
|
|
299
308
|
|
|
300
309
|
if (y > this.requestedRect.maxY) {
|
|
301
310
|
// Estimate the remaining height for rows that we don't need to layout right now.
|
|
302
|
-
y += ([...getChildNodes(node,
|
|
311
|
+
y += ([...getChildNodes(node, collection)].length - (children.length + skipped)) * rowHeight;
|
|
303
312
|
break;
|
|
304
313
|
}
|
|
305
314
|
}
|
|
@@ -315,7 +324,7 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
|
|
|
315
324
|
}
|
|
316
325
|
|
|
317
326
|
protected buildSectionHeader(node: Node<T>, x: number, y: number): LayoutNode {
|
|
318
|
-
let width = this.virtualizer
|
|
327
|
+
let width = this.virtualizer!.visibleRect.width;
|
|
319
328
|
let rectHeight = this.headingHeight;
|
|
320
329
|
let isEstimated = false;
|
|
321
330
|
|
|
@@ -327,7 +336,7 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
|
|
|
327
336
|
let previousLayoutNode = this.layoutNodes.get(node.key);
|
|
328
337
|
let previousLayoutInfo = previousLayoutNode?.layoutInfo;
|
|
329
338
|
if (previousLayoutInfo) {
|
|
330
|
-
let curNode = this.collection.getItem(node.key);
|
|
339
|
+
let curNode = this.virtualizer!.collection.getItem(node.key);
|
|
331
340
|
let lastNode = this.lastCollection ? this.lastCollection.getItem(node.key) : null;
|
|
332
341
|
rectHeight = previousLayoutInfo.rect.height;
|
|
333
342
|
isEstimated = width !== this.lastWidth || curNode !== lastNode || previousLayoutInfo.estimatedSize;
|
|
@@ -353,7 +362,7 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
|
|
|
353
362
|
}
|
|
354
363
|
|
|
355
364
|
protected buildItem(node: Node<T>, x: number, y: number): LayoutNode {
|
|
356
|
-
let width = this.virtualizer
|
|
365
|
+
let width = this.virtualizer!.visibleRect.width;
|
|
357
366
|
let rectHeight = this.rowHeight;
|
|
358
367
|
let isEstimated = false;
|
|
359
368
|
|
|
@@ -394,6 +403,7 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
|
|
|
394
403
|
return false;
|
|
395
404
|
}
|
|
396
405
|
|
|
406
|
+
let collection = this.virtualizer!.collection;
|
|
397
407
|
let layoutInfo = layoutNode.layoutInfo;
|
|
398
408
|
layoutInfo.estimatedSize = false;
|
|
399
409
|
if (layoutInfo.rect.height !== size.height) {
|
|
@@ -412,10 +422,10 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
|
|
|
412
422
|
// Invalidate layout for this layout node and all parents
|
|
413
423
|
this.updateLayoutNode(key, layoutInfo, newLayoutInfo);
|
|
414
424
|
|
|
415
|
-
let node =
|
|
425
|
+
let node = layoutInfo.parentKey != null ? collection.getItem(layoutInfo.parentKey) : null;
|
|
416
426
|
while (node) {
|
|
417
427
|
this.updateLayoutNode(node.key, layoutInfo, newLayoutInfo);
|
|
418
|
-
node =
|
|
428
|
+
node = node.parentKey != null ? collection.getItem(node.parentKey) : null;
|
|
419
429
|
}
|
|
420
430
|
|
|
421
431
|
return true;
|
|
@@ -441,16 +451,20 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
|
|
|
441
451
|
return this.contentSize;
|
|
442
452
|
}
|
|
443
453
|
|
|
444
|
-
getDropTargetFromPoint(x: number, y: number, isValidDropTarget: (target: DropTarget) => boolean): DropTarget {
|
|
445
|
-
x += this.virtualizer
|
|
446
|
-
y += this.virtualizer
|
|
454
|
+
getDropTargetFromPoint(x: number, y: number, isValidDropTarget: (target: DropTarget) => boolean): DropTarget | null {
|
|
455
|
+
x += this.virtualizer!.visibleRect.x;
|
|
456
|
+
y += this.virtualizer!.visibleRect.y;
|
|
447
457
|
|
|
448
|
-
let key = this.virtualizer
|
|
449
|
-
if (key == null || this.collection.size === 0) {
|
|
458
|
+
let key = this.virtualizer!.keyAtPoint(new Point(x, y));
|
|
459
|
+
if (key == null || this.virtualizer!.collection.size === 0) {
|
|
450
460
|
return {type: 'root'};
|
|
451
461
|
}
|
|
452
462
|
|
|
453
463
|
let layoutInfo = this.getLayoutInfo(key);
|
|
464
|
+
if (!layoutInfo) {
|
|
465
|
+
return null;
|
|
466
|
+
}
|
|
467
|
+
|
|
454
468
|
let rect = layoutInfo.rect;
|
|
455
469
|
let target: DropTarget = {
|
|
456
470
|
type: 'item',
|
|
@@ -477,7 +491,7 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
|
|
|
477
491
|
}
|
|
478
492
|
|
|
479
493
|
getDropTargetLayoutInfo(target: ItemDropTarget): LayoutInfo {
|
|
480
|
-
let layoutInfo = this.getLayoutInfo(target.key)
|
|
494
|
+
let layoutInfo = this.getLayoutInfo(target.key)!;
|
|
481
495
|
let rect: Rect;
|
|
482
496
|
if (target.dropPosition === 'before') {
|
|
483
497
|
rect = new Rect(layoutInfo.rect.x, layoutInfo.rect.y - this.dropIndicatorThickness / 2, layoutInfo.rect.width, this.dropIndicatorThickness);
|
package/src/TableLayout.ts
CHANGED
|
@@ -22,11 +22,13 @@ export interface TableLayoutProps {
|
|
|
22
22
|
columnWidths?: Map<Key, number>
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
const DEFAULT_ROW_HEIGHT = 48;
|
|
26
|
+
|
|
25
27
|
export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> extends ListLayout<T, O> {
|
|
26
|
-
protected
|
|
27
|
-
private columnWidths: Map<Key, number
|
|
28
|
+
protected lastCollection: TableCollection<T> | null = null;
|
|
29
|
+
private columnWidths: Map<Key, number> = new Map();
|
|
28
30
|
private stickyColumnIndices: number[];
|
|
29
|
-
private lastPersistedKeys: Set<Key> = null;
|
|
31
|
+
private lastPersistedKeys: Set<Key> | null = null;
|
|
30
32
|
private persistedIndices: Map<Key, number[]> = new Map();
|
|
31
33
|
|
|
32
34
|
constructor(options: ListLayoutOptions) {
|
|
@@ -34,6 +36,11 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
|
|
|
34
36
|
this.stickyColumnIndices = [];
|
|
35
37
|
}
|
|
36
38
|
|
|
39
|
+
// Backward compatibility for subclassing.
|
|
40
|
+
protected get collection(): TableCollection<T> {
|
|
41
|
+
return this.virtualizer!.collection as TableCollection<T>;
|
|
42
|
+
}
|
|
43
|
+
|
|
37
44
|
private columnsChanged(newCollection: TableCollection<T>, oldCollection: TableCollection<T> | null) {
|
|
38
45
|
return !oldCollection ||
|
|
39
46
|
newCollection.columns !== oldCollection.columns &&
|
|
@@ -47,7 +54,7 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
|
|
|
47
54
|
}
|
|
48
55
|
|
|
49
56
|
update(invalidationContext: InvalidationContext<O>): void {
|
|
50
|
-
let newCollection = this.virtualizer
|
|
57
|
+
let newCollection = this.virtualizer!.collection as TableCollection<T>;
|
|
51
58
|
|
|
52
59
|
// If columnWidths were provided via layoutOptions, update those.
|
|
53
60
|
// Otherwise, calculate column widths ourselves.
|
|
@@ -56,9 +63,9 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
|
|
|
56
63
|
this.columnWidths = invalidationContext.layoutOptions.columnWidths;
|
|
57
64
|
invalidationContext.sizeChanged = true;
|
|
58
65
|
}
|
|
59
|
-
} else if (invalidationContext.sizeChanged || this.columnsChanged(newCollection, this.
|
|
66
|
+
} else if (invalidationContext.sizeChanged || this.columnsChanged(newCollection, this.lastCollection)) {
|
|
60
67
|
let columnLayout = new TableColumnLayout({});
|
|
61
|
-
this.columnWidths = columnLayout.buildColumnWidths(this.virtualizer
|
|
68
|
+
this.columnWidths = columnLayout.buildColumnWidths(this.virtualizer!.visibleRect.width, newCollection, new Map());
|
|
62
69
|
invalidationContext.sizeChanged = true;
|
|
63
70
|
}
|
|
64
71
|
|
|
@@ -68,10 +75,11 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
|
|
|
68
75
|
protected buildCollection(): LayoutNode[] {
|
|
69
76
|
this.stickyColumnIndices = [];
|
|
70
77
|
|
|
71
|
-
|
|
78
|
+
let collection = this.virtualizer!.collection as TableCollection<T>;
|
|
79
|
+
for (let column of collection.columns) {
|
|
72
80
|
// The selection cell and any other sticky columns always need to be visible.
|
|
73
81
|
// In addition, row headers need to be in the DOM for accessibility labeling.
|
|
74
|
-
if (this.isStickyColumn(column) ||
|
|
82
|
+
if (this.isStickyColumn(column) || collection.rowHeaderColumnKeys.has(column.key)) {
|
|
75
83
|
this.stickyColumnIndices.push(column.index);
|
|
76
84
|
}
|
|
77
85
|
}
|
|
@@ -90,15 +98,16 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
|
|
|
90
98
|
}
|
|
91
99
|
|
|
92
100
|
protected buildTableHeader(): LayoutNode {
|
|
101
|
+
let collection = this.virtualizer!.collection as TableCollection<T>;
|
|
93
102
|
let rect = new Rect(0, 0, 0, 0);
|
|
94
|
-
let layoutInfo = new LayoutInfo('header',
|
|
103
|
+
let layoutInfo = new LayoutInfo('header', collection.head?.key ?? 'header', rect);
|
|
95
104
|
layoutInfo.isSticky = true;
|
|
96
105
|
layoutInfo.zIndex = 1;
|
|
97
106
|
|
|
98
107
|
let y = 0;
|
|
99
108
|
let width = 0;
|
|
100
109
|
let children: LayoutNode[] = [];
|
|
101
|
-
for (let headerRow of
|
|
110
|
+
for (let headerRow of collection.headerRows) {
|
|
102
111
|
let layoutNode = this.buildChild(headerRow, 0, y, layoutInfo.key);
|
|
103
112
|
layoutNode.layoutInfo.parentKey = layoutInfo.key;
|
|
104
113
|
y = layoutNode.layoutInfo.rect.maxY;
|
|
@@ -114,7 +123,7 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
|
|
|
114
123
|
layoutInfo,
|
|
115
124
|
children,
|
|
116
125
|
validRect: layoutInfo.rect,
|
|
117
|
-
node:
|
|
126
|
+
node: collection.head
|
|
118
127
|
};
|
|
119
128
|
}
|
|
120
129
|
|
|
@@ -124,7 +133,7 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
|
|
|
124
133
|
|
|
125
134
|
let height = 0;
|
|
126
135
|
let columns: LayoutNode[] = [];
|
|
127
|
-
for (let cell of getChildNodes(headerRow, this.collection)) {
|
|
136
|
+
for (let cell of getChildNodes(headerRow, this.virtualizer!.collection)) {
|
|
128
137
|
let layoutNode = this.buildChild(cell, x, y, row.key);
|
|
129
138
|
layoutNode.layoutInfo.parentKey = row.key;
|
|
130
139
|
x = layoutNode.layoutInfo.rect.maxX;
|
|
@@ -161,20 +170,21 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
|
|
|
161
170
|
|
|
162
171
|
// used to get the column widths when rendering to the DOM
|
|
163
172
|
private getRenderedColumnWidth(node: GridNode<T>) {
|
|
173
|
+
let collection = this.virtualizer!.collection as TableCollection<T>;
|
|
164
174
|
let colspan = node.colspan ?? 1;
|
|
165
175
|
let colIndex = node.colIndex ?? node.index;
|
|
166
176
|
let width = 0;
|
|
167
177
|
for (let i = colIndex; i < colIndex + colspan; i++) {
|
|
168
|
-
let column =
|
|
178
|
+
let column = collection.columns[i];
|
|
169
179
|
if (column?.key != null) {
|
|
170
|
-
width += this.columnWidths.get(column.key);
|
|
180
|
+
width += this.columnWidths.get(column.key) ?? 0;
|
|
171
181
|
}
|
|
172
182
|
}
|
|
173
183
|
|
|
174
184
|
return width;
|
|
175
185
|
}
|
|
176
186
|
|
|
177
|
-
private getEstimatedHeight(node: GridNode<T>, width: number, height: number, estimatedHeight: number) {
|
|
187
|
+
private getEstimatedHeight(node: GridNode<T>, width: number, height: number | null, estimatedHeight: number | null) {
|
|
178
188
|
let isEstimated = false;
|
|
179
189
|
|
|
180
190
|
// If no explicit height is available, use an estimated height.
|
|
@@ -187,7 +197,7 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
|
|
|
187
197
|
height = previousLayoutNode.layoutInfo.rect.height;
|
|
188
198
|
isEstimated = node !== previousLayoutNode.node || width !== previousLayoutNode.layoutInfo.rect.width || previousLayoutNode.layoutInfo.estimatedSize;
|
|
189
199
|
} else {
|
|
190
|
-
height = estimatedHeight;
|
|
200
|
+
height = estimatedHeight ?? DEFAULT_ROW_HEIGHT;
|
|
191
201
|
isEstimated = true;
|
|
192
202
|
}
|
|
193
203
|
}
|
|
@@ -196,12 +206,12 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
|
|
|
196
206
|
}
|
|
197
207
|
|
|
198
208
|
protected getEstimatedRowHeight(): number {
|
|
199
|
-
return this.rowHeight ?? this.estimatedRowHeight;
|
|
209
|
+
return this.rowHeight ?? this.estimatedRowHeight ?? DEFAULT_ROW_HEIGHT;
|
|
200
210
|
}
|
|
201
211
|
|
|
202
212
|
protected buildColumn(node: GridNode<T>, x: number, y: number): LayoutNode {
|
|
203
213
|
let width = this.getRenderedColumnWidth(node);
|
|
204
|
-
let {height, isEstimated} = this.getEstimatedHeight(node, width, this.headingHeight, this.estimatedHeadingHeight);
|
|
214
|
+
let {height, isEstimated} = this.getEstimatedHeight(node, width, this.headingHeight ?? this.rowHeight, this.estimatedHeadingHeight ?? this.estimatedRowHeight);
|
|
205
215
|
let rect = new Rect(x, y, width, height);
|
|
206
216
|
let layoutInfo = new LayoutInfo(node.type, node.key, rect);
|
|
207
217
|
layoutInfo.isSticky = this.isStickyColumn(node);
|
|
@@ -223,15 +233,16 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
|
|
|
223
233
|
}
|
|
224
234
|
|
|
225
235
|
protected buildBody(y: number): LayoutNode {
|
|
236
|
+
let collection = this.virtualizer!.collection as TableCollection<T>;
|
|
226
237
|
let rect = new Rect(0, y, 0, 0);
|
|
227
|
-
let layoutInfo = new LayoutInfo('rowgroup',
|
|
238
|
+
let layoutInfo = new LayoutInfo('rowgroup', collection.body.key, rect);
|
|
228
239
|
|
|
229
240
|
let startY = y;
|
|
230
241
|
let skipped = 0;
|
|
231
242
|
let width = 0;
|
|
232
243
|
let children: LayoutNode[] = [];
|
|
233
244
|
let rowHeight = this.getEstimatedRowHeight();
|
|
234
|
-
for (let node of getChildNodes(
|
|
245
|
+
for (let node of getChildNodes(collection.body, collection)) {
|
|
235
246
|
// Skip rows before the valid rectangle unless they are already cached.
|
|
236
247
|
if (y + rowHeight < this.requestedRect.y && !this.isValid(node, y)) {
|
|
237
248
|
y += rowHeight;
|
|
@@ -248,13 +259,13 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
|
|
|
248
259
|
|
|
249
260
|
if (y > this.requestedRect.maxY) {
|
|
250
261
|
// Estimate the remaining height for rows that we don't need to layout right now.
|
|
251
|
-
y += (
|
|
262
|
+
y += (collection.size - (skipped + children.length)) * rowHeight;
|
|
252
263
|
break;
|
|
253
264
|
}
|
|
254
265
|
}
|
|
255
266
|
|
|
256
267
|
if (children.length === 0) {
|
|
257
|
-
y = this.virtualizer
|
|
268
|
+
y = this.virtualizer!.visibleRect.maxY;
|
|
258
269
|
}
|
|
259
270
|
|
|
260
271
|
rect.width = width;
|
|
@@ -264,7 +275,7 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
|
|
|
264
275
|
layoutInfo,
|
|
265
276
|
children,
|
|
266
277
|
validRect: layoutInfo.rect.intersection(this.requestedRect),
|
|
267
|
-
node:
|
|
278
|
+
node: collection.body
|
|
268
279
|
};
|
|
269
280
|
}
|
|
270
281
|
|
|
@@ -287,12 +298,13 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
|
|
|
287
298
|
}
|
|
288
299
|
|
|
289
300
|
protected buildRow(node: GridNode<T>, x: number, y: number): LayoutNode {
|
|
301
|
+
let collection = this.virtualizer!.collection as TableCollection<T>;
|
|
290
302
|
let rect = new Rect(x, y, 0, 0);
|
|
291
303
|
let layoutInfo = new LayoutInfo('row', node.key, rect);
|
|
292
304
|
|
|
293
305
|
let children: LayoutNode[] = [];
|
|
294
306
|
let height = 0;
|
|
295
|
-
for (let child of getChildNodes(node,
|
|
307
|
+
for (let child of getChildNodes(node, collection)) {
|
|
296
308
|
if (child.type === 'cell') {
|
|
297
309
|
if (x > this.requestedRect.maxX) {
|
|
298
310
|
// Adjust existing cached layoutInfo to ensure that it is out of view.
|
|
@@ -316,7 +328,7 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
|
|
|
316
328
|
|
|
317
329
|
this.setChildHeights(children, height);
|
|
318
330
|
|
|
319
|
-
rect.width = this.layoutNodes.get(
|
|
331
|
+
rect.width = this.layoutNodes.get(collection.head?.key ?? 'header')!.layoutInfo.rect.width;
|
|
320
332
|
rect.height = height;
|
|
321
333
|
|
|
322
334
|
return {
|
|
@@ -480,20 +492,20 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
|
|
|
480
492
|
}
|
|
481
493
|
|
|
482
494
|
private buildPersistedIndices() {
|
|
483
|
-
if (this.virtualizer
|
|
495
|
+
if (this.virtualizer!.persistedKeys === this.lastPersistedKeys) {
|
|
484
496
|
return;
|
|
485
497
|
}
|
|
486
498
|
|
|
487
|
-
this.lastPersistedKeys = this.virtualizer
|
|
499
|
+
this.lastPersistedKeys = this.virtualizer!.persistedKeys;
|
|
488
500
|
this.persistedIndices.clear();
|
|
489
501
|
|
|
490
502
|
// Build a map of parentKey => indices of children to persist.
|
|
491
|
-
for (let key of this.virtualizer
|
|
503
|
+
for (let key of this.virtualizer!.persistedKeys) {
|
|
492
504
|
let layoutInfo = this.layoutNodes.get(key)?.layoutInfo;
|
|
493
505
|
|
|
494
506
|
// Walk up ancestors so parents are also persisted if children are.
|
|
495
507
|
while (layoutInfo && layoutInfo.parentKey) {
|
|
496
|
-
let collectionNode = this.collection.getItem(layoutInfo.key);
|
|
508
|
+
let collectionNode = this.virtualizer!.collection.getItem(layoutInfo.key);
|
|
497
509
|
let indices = this.persistedIndices.get(layoutInfo.parentKey);
|
|
498
510
|
if (!indices) {
|
|
499
511
|
// stickyColumnIndices are always persisted along with any cells from persistedKeys.
|
|
@@ -501,9 +513,8 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
|
|
|
501
513
|
this.persistedIndices.set(layoutInfo.parentKey, indices);
|
|
502
514
|
}
|
|
503
515
|
|
|
504
|
-
let index = this.layoutNodes.get(layoutInfo.key)
|
|
505
|
-
|
|
506
|
-
if (!indices.includes(index)) {
|
|
516
|
+
let index = this.layoutNodes.get(layoutInfo.key)?.index;
|
|
517
|
+
if (index != null && !indices.includes(index)) {
|
|
507
518
|
indices.push(index);
|
|
508
519
|
}
|
|
509
520
|
|
|
@@ -516,15 +527,15 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
|
|
|
516
527
|
}
|
|
517
528
|
}
|
|
518
529
|
|
|
519
|
-
getDropTargetFromPoint(x: number, y: number, isValidDropTarget: (target: DropTarget) => boolean): DropTarget {
|
|
520
|
-
x += this.virtualizer
|
|
521
|
-
y += this.virtualizer
|
|
530
|
+
getDropTargetFromPoint(x: number, y: number, isValidDropTarget: (target: DropTarget) => boolean): DropTarget | null {
|
|
531
|
+
x += this.virtualizer!.visibleRect.x;
|
|
532
|
+
y += this.virtualizer!.visibleRect.y;
|
|
522
533
|
|
|
523
534
|
// Custom variation of this.virtualizer.keyAtPoint that ignores body
|
|
524
|
-
let key: Key;
|
|
535
|
+
let key: Key | null = null;
|
|
525
536
|
let point = new Point(x, y);
|
|
526
537
|
let rectAtPoint = new Rect(point.x, point.y, 1, 1);
|
|
527
|
-
let layoutInfos = this.virtualizer
|
|
538
|
+
let layoutInfos = this.virtualizer!.layout.getVisibleLayoutInfos(rectAtPoint).filter(info => info.type === 'row');
|
|
528
539
|
|
|
529
540
|
// Layout may return multiple layout infos in the case of
|
|
530
541
|
// persisted keys, so find the first one that actually intersects.
|
|
@@ -534,11 +545,15 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
|
|
|
534
545
|
}
|
|
535
546
|
}
|
|
536
547
|
|
|
537
|
-
if (key == null || this.collection.size === 0) {
|
|
548
|
+
if (key == null || this.virtualizer!.collection.size === 0) {
|
|
538
549
|
return {type: 'root'};
|
|
539
550
|
}
|
|
540
551
|
|
|
541
552
|
let layoutInfo = this.getLayoutInfo(key);
|
|
553
|
+
if (!layoutInfo) {
|
|
554
|
+
return null;
|
|
555
|
+
}
|
|
556
|
+
|
|
542
557
|
let rect = layoutInfo.rect;
|
|
543
558
|
let target: DropTarget = {
|
|
544
559
|
type: 'item',
|
|
@@ -566,7 +581,7 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
|
|
|
566
581
|
|
|
567
582
|
getDropTargetLayoutInfo(target: ItemDropTarget): LayoutInfo {
|
|
568
583
|
let layoutInfo = super.getDropTargetLayoutInfo(target);
|
|
569
|
-
layoutInfo.parentKey = this.collection.body.key;
|
|
584
|
+
layoutInfo.parentKey = (this.virtualizer!.collection as TableCollection<T>).body.key;
|
|
570
585
|
return layoutInfo;
|
|
571
586
|
}
|
|
572
587
|
}
|