@react-stately/layout 3.13.10-nightly.4685 → 3.13.10-nightly.4691

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/src/ListLayout.ts CHANGED
@@ -14,66 +14,48 @@ import {Collection, DropTarget, DropTargetDelegate, Key, Node} from '@react-type
14
14
  import {getChildNodes} from '@react-stately/collections';
15
15
  import {InvalidationContext, Layout, LayoutInfo, Point, Rect, Size} from '@react-stately/virtualizer';
16
16
 
17
- export type ListLayoutOptions<T> = {
18
- /** The height of a row in px. */
17
+ export interface ListLayoutOptions {
18
+ /** The fixed height of a row in px. */
19
19
  rowHeight?: number,
20
+ /** The estimated height of a row, when row heights are variable. */
20
21
  estimatedRowHeight?: number,
22
+ /** The fixed height of a section header in px. */
21
23
  headingHeight?: number,
22
- estimatedHeadingHeight?: number,
23
- padding?: number,
24
- indentationForItem?: (collection: Collection<Node<T>>, key: Key) => number,
25
- loaderHeight?: number,
26
- placeholderHeight?: number,
27
- forceSectionHeaders?: boolean,
28
- enableEmptyState?: boolean
29
- };
24
+ /** The estimated height of a section header, when the height is variable. */
25
+ estimatedHeadingHeight?: number
26
+ }
30
27
 
31
28
  // A wrapper around LayoutInfo that supports hierarchy
32
29
  export interface LayoutNode {
33
30
  node?: Node<unknown>,
34
31
  layoutInfo: LayoutInfo,
35
- header?: LayoutInfo,
36
32
  children?: LayoutNode[],
37
33
  validRect: Rect,
38
34
  index?: number
39
35
  }
40
36
 
41
- export interface ListLayoutProps {
42
- isLoading?: boolean
43
- }
44
-
45
37
  const DEFAULT_HEIGHT = 48;
46
38
 
47
39
  /**
48
- * The ListLayout class is an implementation of a virtualizer {@link Layout}
49
- * it is used for creating lists and lists with indented sub-lists.
50
- *
40
+ * The ListLayout class is an implementation of a virtualizer {@link Layout}.
51
41
  * To configure a ListLayout, you can use the properties to define the
52
42
  * layouts and/or use the method for defining indentation.
53
43
  * The {@link ListKeyboardDelegate} extends the existing virtualizer
54
44
  * delegate with an additional method to do this (it uses the same delegate object as
55
45
  * the virtualizer itself).
56
46
  */
57
- export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements DropTargetDelegate {
47
+ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTargetDelegate {
58
48
  protected rowHeight: number;
59
49
  protected estimatedRowHeight: number;
60
50
  protected headingHeight: number;
61
51
  protected estimatedHeadingHeight: number;
62
- protected forceSectionHeaders: boolean;
63
- protected padding: number;
64
- protected indentationForItem?: (collection: Collection<Node<T>>, key: Key) => number;
65
- protected layoutInfos: Map<Key, LayoutInfo>;
66
52
  protected layoutNodes: Map<Key, LayoutNode>;
67
53
  protected contentSize: Size;
68
54
  protected collection: Collection<Node<T>>;
69
- protected isLoading: boolean;
70
- protected lastWidth: number;
71
- protected lastCollection: Collection<Node<T>>;
55
+ private lastCollection: Collection<Node<T>>;
56
+ private lastWidth: number;
72
57
  protected rootNodes: LayoutNode[];
73
- protected invalidateEverything: boolean;
74
- protected loaderHeight: number;
75
- protected placeholderHeight: number;
76
- protected enableEmptyState: boolean;
58
+ private invalidateEverything: boolean;
77
59
  /** The rectangle containing currently valid layout infos. */
78
60
  protected validRect: Rect;
79
61
  /** The rectangle of requested layout infos so far. */
@@ -83,19 +65,12 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
83
65
  * Creates a new ListLayout with options. See the list of properties below for a description
84
66
  * of the options that can be provided.
85
67
  */
86
- constructor(options: ListLayoutOptions<T> = {}) {
68
+ constructor(options: ListLayoutOptions = {}) {
87
69
  super();
88
70
  this.rowHeight = options.rowHeight;
89
71
  this.estimatedRowHeight = options.estimatedRowHeight;
90
72
  this.headingHeight = options.headingHeight;
91
73
  this.estimatedHeadingHeight = options.estimatedHeadingHeight;
92
- this.forceSectionHeaders = options.forceSectionHeaders;
93
- this.padding = options.padding || 0;
94
- this.indentationForItem = options.indentationForItem;
95
- this.loaderHeight = options.loaderHeight;
96
- this.placeholderHeight = options.placeholderHeight;
97
- this.enableEmptyState = options.enableEmptyState || false;
98
- this.layoutInfos = new Map();
99
74
  this.layoutNodes = new Map();
100
75
  this.rootNodes = [];
101
76
  this.lastWidth = 0;
@@ -107,7 +82,7 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
107
82
 
108
83
  getLayoutInfo(key: Key) {
109
84
  this.ensureLayoutInfo(key);
110
- return this.layoutInfos.get(key)!;
85
+ return this.layoutNodes.get(key)?.layoutInfo || null;
111
86
  }
112
87
 
113
88
  getVisibleLayoutInfos(rect: Rect) {
@@ -129,9 +104,6 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
129
104
  for (let node of nodes) {
130
105
  if (this.isVisible(node, rect)) {
131
106
  res.push(node.layoutInfo);
132
- if (node.header) {
133
- res.push(node.header);
134
- }
135
107
 
136
108
  if (node.children) {
137
109
  addNodes(node.children);
@@ -166,7 +138,7 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
166
138
  // If the layout info wasn't found, it might be outside the bounds of the area that we've
167
139
  // computed layout for so far. This can happen when accessing a random key, e.g pressing Home/End.
168
140
  // Compute the full layout and try again.
169
- if (!this.layoutInfos.has(key) && this.requestedRect.area < this.contentSize.area && this.lastCollection) {
141
+ if (!this.layoutNodes.has(key) && this.requestedRect.area < this.contentSize.area && this.lastCollection) {
170
142
  this.requestedRect = new Rect(0, 0, Infinity, Infinity);
171
143
  this.rootNodes = this.buildCollection();
172
144
  this.requestedRect = new Rect(0, 0, this.contentSize.width, this.contentSize.height);
@@ -176,19 +148,18 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
176
148
  return false;
177
149
  }
178
150
 
179
- private isVisible(node: LayoutNode, rect: Rect) {
180
- return node.layoutInfo.rect.intersects(rect) || node.layoutInfo.isSticky || this.virtualizer.isPersistedKey(node.layoutInfo.key);
151
+ protected isVisible(node: LayoutNode, rect: Rect) {
152
+ return node.layoutInfo.rect.intersects(rect) || node.layoutInfo.isSticky || node.layoutInfo.type === 'header' || this.virtualizer.isPersistedKey(node.layoutInfo.key);
181
153
  }
182
154
 
183
- protected shouldInvalidateEverything(invalidationContext: InvalidationContext<ListLayoutProps>) {
155
+ protected shouldInvalidateEverything(invalidationContext: InvalidationContext<O>) {
184
156
  // Invalidate cache if the size of the collection changed.
185
157
  // In this case, we need to recalculate the entire layout.
186
158
  return invalidationContext.sizeChanged;
187
159
  }
188
160
 
189
- validate(invalidationContext: InvalidationContext<ListLayoutProps>) {
161
+ validate(invalidationContext: InvalidationContext<O>) {
190
162
  this.collection = this.virtualizer.collection;
191
- this.isLoading = invalidationContext.layoutOptions?.isLoading || false;
192
163
 
193
164
  // Reset valid rect if we will have to invalidate everything.
194
165
  // Otherwise we can reuse cached layout infos outside the current visible rect.
@@ -205,8 +176,6 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
205
176
  if (!this.collection.getItem(key)) {
206
177
  let layoutNode = this.layoutNodes.get(key);
207
178
  if (layoutNode) {
208
- this.layoutInfos.delete(layoutNode.layoutInfo.key);
209
- this.layoutInfos.delete(layoutNode.header?.key);
210
179
  this.layoutNodes.delete(key);
211
180
  }
212
181
  }
@@ -219,12 +188,11 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
219
188
  this.validRect = this.requestedRect.copy();
220
189
  }
221
190
 
222
- protected buildCollection(): LayoutNode[] {
223
- let y = this.padding;
191
+ protected buildCollection(y = 0): LayoutNode[] {
224
192
  let skipped = 0;
225
193
  let nodes = [];
226
194
  for (let node of this.collection) {
227
- let rowHeight = (this.rowHeight ?? this.estimatedRowHeight);
195
+ let rowHeight = this.rowHeight ?? this.estimatedRowHeight;
228
196
 
229
197
  // Skip rows before the valid rectangle unless they are already cached.
230
198
  if (node.type === 'item' && y + rowHeight < this.requestedRect.y && !this.isValid(node, y)) {
@@ -243,25 +211,7 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
243
211
  }
244
212
  }
245
213
 
246
- if (this.isLoading) {
247
- let rect = new Rect(0, y, this.virtualizer.visibleRect.width,
248
- this.loaderHeight ?? this.virtualizer.visibleRect.height);
249
- let loader = new LayoutInfo('loader', 'loader', rect);
250
- this.layoutInfos.set('loader', loader);
251
- nodes.push({layoutInfo: loader});
252
- y = loader.rect.maxY;
253
- }
254
-
255
- if (nodes.length === 0 && this.enableEmptyState) {
256
- let rect = new Rect(0, y, this.virtualizer.visibleRect.width,
257
- this.placeholderHeight ?? this.virtualizer.visibleRect.height);
258
- let placeholder = new LayoutInfo('placeholder', 'placeholder', rect);
259
- this.layoutInfos.set('placeholder', placeholder);
260
- nodes.push({layoutInfo: placeholder});
261
- y = placeholder.rect.maxY;
262
- }
263
-
264
- this.contentSize = new Size(this.virtualizer.visibleRect.width, y + this.padding);
214
+ this.contentSize = new Size(this.virtualizer.visibleRect.width, y);
265
215
  return nodes;
266
216
  }
267
217
 
@@ -271,7 +221,7 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
271
221
  !this.invalidateEverything &&
272
222
  cached &&
273
223
  cached.node === node &&
274
- y === (cached.header || cached.layoutInfo).rect.y &&
224
+ y === cached.layoutInfo.rect.y &&
275
225
  cached.layoutInfo.rect.intersects(this.validRect) &&
276
226
  cached.validRect.containsRect(cached.layoutInfo.rect.intersection(this.requestedRect))
277
227
  );
@@ -286,11 +236,6 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
286
236
  layoutNode.node = node;
287
237
 
288
238
  layoutNode.layoutInfo.parentKey = parentKey ?? null;
289
- this.layoutInfos.set(layoutNode.layoutInfo.key, layoutNode.layoutInfo);
290
- if (layoutNode.header) {
291
- this.layoutInfos.set(layoutNode.header.key, layoutNode.header);
292
- }
293
-
294
239
  this.layoutNodes.set(node.key, layoutNode);
295
240
  return layoutNode;
296
241
  }
@@ -306,17 +251,8 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
306
251
  }
307
252
  }
308
253
 
309
- private buildSection(node: Node<T>, x: number, y: number): LayoutNode {
254
+ protected buildSection(node: Node<T>, x: number, y: number): LayoutNode {
310
255
  let width = this.virtualizer.visibleRect.width;
311
- let header = null;
312
- if (node.rendered || this.forceSectionHeaders) {
313
- let headerNode = this.buildSectionHeader(node, x, y);
314
- header = headerNode.layoutInfo;
315
- header.key += ':header';
316
- header.parentKey = node.key;
317
- y += header.rect.height;
318
- }
319
-
320
256
  let rect = new Rect(0, y, width, 0);
321
257
  let layoutInfo = new LayoutInfo(node.type, node.key, rect);
322
258
 
@@ -347,14 +283,13 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
347
283
  rect.height = y - startY;
348
284
 
349
285
  return {
350
- header,
351
286
  layoutInfo,
352
287
  children,
353
288
  validRect: layoutInfo.rect.intersection(this.requestedRect)
354
289
  };
355
290
  }
356
291
 
357
- private buildSectionHeader(node: Node<T>, x: number, y: number): LayoutNode {
292
+ protected buildSectionHeader(node: Node<T>, x: number, y: number): LayoutNode {
358
293
  let width = this.virtualizer.visibleRect.width;
359
294
  let rectHeight = this.headingHeight;
360
295
  let isEstimated = false;
@@ -365,7 +300,7 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
365
300
  // Mark as estimated if the size of the overall virtualizer changed,
366
301
  // or the content of the item changed.
367
302
  let previousLayoutNode = this.layoutNodes.get(node.key);
368
- let previousLayoutInfo = previousLayoutNode?.header || previousLayoutNode?.layoutInfo;
303
+ let previousLayoutInfo = previousLayoutNode?.layoutInfo;
369
304
  if (previousLayoutInfo) {
370
305
  let curNode = this.collection.getItem(node.key);
371
306
  let lastNode = this.lastCollection ? this.lastCollection.getItem(node.key) : null;
@@ -391,7 +326,7 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
391
326
  };
392
327
  }
393
328
 
394
- private buildItem(node: Node<T>, x: number, y: number): LayoutNode {
329
+ protected buildItem(node: Node<T>, x: number, y: number): LayoutNode {
395
330
  let width = this.virtualizer.visibleRect.width;
396
331
  let rectHeight = this.rowHeight;
397
332
  let isEstimated = false;
@@ -415,14 +350,8 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
415
350
  rectHeight = DEFAULT_HEIGHT;
416
351
  }
417
352
 
418
- if (typeof this.indentationForItem === 'function') {
419
- x += this.indentationForItem(this.collection, node.key) || 0;
420
- }
421
-
422
353
  let rect = new Rect(x, y, width - x, rectHeight);
423
354
  let layoutInfo = new LayoutInfo(node.type, node.key, rect);
424
- // allow overflow so the focus ring/selection ring can extend outside to overlap with the adjacent items borders
425
- layoutInfo.allowOverflow = true;
426
355
  layoutInfo.estimatedSize = isEstimated;
427
356
  return {
428
357
  layoutInfo,
@@ -431,18 +360,19 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
431
360
  }
432
361
 
433
362
  updateItemSize(key: Key, size: Size) {
434
- let layoutInfo = this.layoutInfos.get(key);
363
+ let layoutNode = this.layoutNodes.get(key);
435
364
  // If no layoutInfo, item has been deleted/removed.
436
- if (!layoutInfo) {
365
+ if (!layoutNode) {
437
366
  return false;
438
367
  }
439
368
 
369
+ let layoutInfo = layoutNode.layoutInfo;
440
370
  layoutInfo.estimatedSize = false;
441
371
  if (layoutInfo.rect.height !== size.height) {
442
372
  // Copy layout info rather than mutating so that later caches are invalidated.
443
373
  let newLayoutInfo = layoutInfo.copy();
444
374
  newLayoutInfo.rect.height = size.height;
445
- this.layoutInfos.set(key, newLayoutInfo);
375
+ layoutNode.layoutInfo = newLayoutInfo;
446
376
 
447
377
  // Items after this layoutInfo will need to be repositioned to account for the new height.
448
378
  // Adjust the validRect so that only items above remain valid.
@@ -473,9 +403,7 @@ export class ListLayout<T> extends Layout<Node<T>, ListLayoutProps> implements D
473
403
  n.validRect = n.validRect.intersection(this.validRect);
474
404
 
475
405
  // Replace layout info in LayoutNode
476
- if (n.header === oldLayoutInfo) {
477
- n.header = newLayoutInfo;
478
- } else if (n.layoutInfo === oldLayoutInfo) {
406
+ if (n.layoutInfo === oldLayoutInfo) {
479
407
  n.layoutInfo = newLayoutInfo;
480
408
  }
481
409
  }
@@ -14,34 +14,24 @@ import {DropTarget, Key} from '@react-types/shared';
14
14
  import {getChildNodes} from '@react-stately/collections';
15
15
  import {GridNode} from '@react-types/grid';
16
16
  import {InvalidationContext, LayoutInfo, Point, Rect, Size} from '@react-stately/virtualizer';
17
- import {LayoutNode, ListLayout, ListLayoutOptions, ListLayoutProps} from './ListLayout';
17
+ import {LayoutNode, ListLayout, ListLayoutOptions} from './ListLayout';
18
18
  import {TableCollection} from '@react-types/table';
19
19
  import {TableColumnLayout} from '@react-stately/table';
20
20
 
21
- export interface TableLayoutOptions<T> extends ListLayoutOptions<T> {
22
- scrollContainer?: 'table' | 'body'
23
- }
24
-
25
- export interface TableLayoutProps extends ListLayoutProps {
21
+ export interface TableLayoutProps {
26
22
  columnWidths?: Map<Key, number>
27
23
  }
28
24
 
29
- export class TableLayout<T> extends ListLayout<T> {
30
- collection: TableCollection<T>;
31
- lastCollection: TableCollection<T>;
32
- columnWidths: Map<Key, number>;
33
- stickyColumnIndices: number[];
34
- isLoading = false;
35
- lastPersistedKeys: Set<Key> = null;
36
- persistedIndices: Map<Key, number[]> = new Map();
37
- scrollContainer: 'table' | 'body';
38
- private disableSticky: boolean;
39
-
40
- constructor(options: TableLayoutOptions<T>) {
25
+ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> extends ListLayout<T, O> {
26
+ protected collection: TableCollection<T>;
27
+ private columnWidths: Map<Key, number>;
28
+ private stickyColumnIndices: number[];
29
+ private lastPersistedKeys: Set<Key> = null;
30
+ private persistedIndices: Map<Key, number[]> = new Map();
31
+
32
+ constructor(options: ListLayoutOptions) {
41
33
  super(options);
42
- this.scrollContainer = options.scrollContainer || 'table';
43
34
  this.stickyColumnIndices = [];
44
- this.disableSticky = this.checkChrome105();
45
35
  }
46
36
 
47
37
  private columnsChanged(newCollection: TableCollection<T>, oldCollection: TableCollection<T> | null) {
@@ -56,7 +46,7 @@ export class TableLayout<T> extends ListLayout<T> {
56
46
  );
57
47
  }
58
48
 
59
- validate(invalidationContext: InvalidationContext<TableLayoutProps>): void {
49
+ validate(invalidationContext: InvalidationContext<O>): void {
60
50
  let newCollection = this.virtualizer.collection as TableCollection<T>;
61
51
 
62
52
  // If columnWidths were provided via layoutOptions, update those.
@@ -76,21 +66,19 @@ export class TableLayout<T> extends ListLayout<T> {
76
66
  }
77
67
 
78
68
  protected buildCollection(): LayoutNode[] {
79
- // Track whether we were previously loading. This is used to adjust the animations of async loading vs inserts.
80
- let loadingState = this.collection.body.props.loadingState;
81
- this.isLoading = loadingState === 'loading' || loadingState === 'loadingMore';
82
69
  this.stickyColumnIndices = [];
83
70
 
84
71
  for (let column of this.collection.columns) {
85
72
  // The selection cell and any other sticky columns always need to be visible.
86
73
  // In addition, row headers need to be in the DOM for accessibility labeling.
87
- if (column.props.isDragButtonCell || column.props.isSelectionCell || this.collection.rowHeaderColumnKeys.has(column.key)) {
74
+ if (this.isStickyColumn(column) || this.collection.rowHeaderColumnKeys.has(column.key)) {
88
75
  this.stickyColumnIndices.push(column.index);
89
76
  }
90
77
  }
91
78
 
92
- let header = this.buildColumnHeader();
93
- let body = this.buildBody(this.scrollContainer === 'body' ? 0 : header.layoutInfo.rect.height);
79
+ let header = this.buildTableHeader();
80
+ this.layoutNodes.set(header.layoutInfo.key, header);
81
+ let body = this.buildBody(header.layoutInfo.rect.height);
94
82
  this.lastPersistedKeys = null;
95
83
 
96
84
  body.layoutInfo.rect.width = Math.max(header.layoutInfo.rect.width, body.layoutInfo.rect.width);
@@ -101,7 +89,7 @@ export class TableLayout<T> extends ListLayout<T> {
101
89
  ];
102
90
  }
103
91
 
104
- private buildColumnHeader(): LayoutNode {
92
+ protected buildTableHeader(): LayoutNode {
105
93
  let rect = new Rect(0, 0, 0, 0);
106
94
  let layoutInfo = new LayoutInfo('header', this.collection.head?.key ?? 'header', rect);
107
95
  layoutInfo.isSticky = true;
@@ -122,8 +110,6 @@ export class TableLayout<T> extends ListLayout<T> {
122
110
  rect.width = width;
123
111
  rect.height = y;
124
112
 
125
- this.layoutInfos.set(layoutInfo.key, layoutInfo);
126
-
127
113
  return {
128
114
  layoutInfo,
129
115
  children,
@@ -131,7 +117,7 @@ export class TableLayout<T> extends ListLayout<T> {
131
117
  };
132
118
  }
133
119
 
134
- private buildHeaderRow(headerRow: GridNode<T>, x: number, y: number): LayoutNode {
120
+ protected buildHeaderRow(headerRow: GridNode<T>, x: number, y: number): LayoutNode {
135
121
  let rect = new Rect(0, y, 0, 0);
136
122
  let row = new LayoutInfo('headerrow', headerRow.key, rect);
137
123
 
@@ -166,8 +152,6 @@ export class TableLayout<T> extends ListLayout<T> {
166
152
  if (child.layoutInfo.rect.height !== height) {
167
153
  // Need to copy the layout info before we mutate it.
168
154
  child.layoutInfo = child.layoutInfo.copy();
169
- this.layoutInfos.set(child.layoutInfo.key, child.layoutInfo);
170
-
171
155
  child.layoutInfo.rect.height = height;
172
156
  }
173
157
  }
@@ -209,15 +193,18 @@ export class TableLayout<T> extends ListLayout<T> {
209
193
  return {height, isEstimated};
210
194
  }
211
195
 
212
- private buildColumn(node: GridNode<T>, x: number, y: number): LayoutNode {
196
+ protected getEstimatedRowHeight(): number {
197
+ return this.rowHeight ?? this.estimatedRowHeight;
198
+ }
199
+
200
+ protected buildColumn(node: GridNode<T>, x: number, y: number): LayoutNode {
213
201
  let width = this.getRenderedColumnWidth(node);
214
202
  let {height, isEstimated} = this.getEstimatedHeight(node, width, this.headingHeight, this.estimatedHeadingHeight);
215
203
  let rect = new Rect(x, y, width, height);
216
204
  let layoutInfo = new LayoutInfo(node.type, node.key, rect);
217
- layoutInfo.isSticky = !this.disableSticky && (node.props?.isDragButtonCell || node.props?.isSelectionCell);
205
+ layoutInfo.isSticky = this.isStickyColumn(node);
218
206
  layoutInfo.zIndex = layoutInfo.isSticky ? 2 : 1;
219
207
  layoutInfo.estimatedSize = isEstimated;
220
- layoutInfo.allowOverflow = true;
221
208
 
222
209
  return {
223
210
  layoutInfo,
@@ -225,7 +212,13 @@ export class TableLayout<T> extends ListLayout<T> {
225
212
  };
226
213
  }
227
214
 
228
- private buildBody(y: number): LayoutNode {
215
+ // For subclasses.
216
+ // eslint-disable-next-line
217
+ protected isStickyColumn(node: GridNode<T>) {
218
+ return false;
219
+ }
220
+
221
+ protected buildBody(y: number): LayoutNode {
229
222
  let rect = new Rect(0, y, 0, 0);
230
223
  let layoutInfo = new LayoutInfo('rowgroup', this.collection.body.key, rect);
231
224
 
@@ -233,9 +226,8 @@ export class TableLayout<T> extends ListLayout<T> {
233
226
  let skipped = 0;
234
227
  let width = 0;
235
228
  let children: LayoutNode[] = [];
229
+ let rowHeight = this.getEstimatedRowHeight();
236
230
  for (let [i, node] of [...getChildNodes(this.collection.body, this.collection)].entries()) {
237
- let rowHeight = (this.rowHeight ?? this.estimatedRowHeight) + 1;
238
-
239
231
  // Skip rows before the valid rectangle unless they are already cached.
240
232
  if (y + rowHeight < this.requestedRect.y && !this.isValid(node, y)) {
241
233
  y += rowHeight;
@@ -257,36 +249,13 @@ export class TableLayout<T> extends ListLayout<T> {
257
249
  }
258
250
  }
259
251
 
260
- if (this.isLoading) {
261
- // Add some margin around the loader to ensure that scrollbars don't flicker in and out.
262
- let rect = new Rect(40, Math.max(y, 40), (width || this.virtualizer.visibleRect.width) - 80, children.length === 0 ? this.virtualizer.visibleRect.height - 80 : 60);
263
- let loader = new LayoutInfo('loader', 'loader', rect);
264
- loader.parentKey = layoutInfo.key;
265
- loader.isSticky = !this.disableSticky && children.length === 0;
266
- this.layoutInfos.set('loader', loader);
267
- children.push({layoutInfo: loader, validRect: loader.rect});
268
- y = loader.rect.maxY;
269
- width = Math.max(width, rect.width);
270
- } else if (children.length === 0) {
271
- if (this.enableEmptyState) {
272
- let rect = new Rect(40, Math.max(y, 40), this.virtualizer.visibleRect.width - 80, this.virtualizer.visibleRect.height - 80);
273
- let empty = new LayoutInfo('empty', 'empty', rect);
274
- empty.parentKey = layoutInfo.key;
275
- empty.isSticky = !this.disableSticky;
276
- this.layoutInfos.set('empty', empty);
277
- children.push({layoutInfo: empty, validRect: empty.rect});
278
- y = empty.rect.maxY;
279
- width = Math.max(width, rect.width);
280
- } else {
281
- y = this.virtualizer.visibleRect.maxY;
282
- }
252
+ if (children.length === 0) {
253
+ y = this.virtualizer.visibleRect.maxY;
283
254
  }
284
255
 
285
256
  rect.width = width;
286
257
  rect.height = y - startY;
287
258
 
288
- this.layoutInfos.set(layoutInfo.key, layoutInfo);
289
-
290
259
  return {
291
260
  layoutInfo,
292
261
  children,
@@ -310,7 +279,7 @@ export class TableLayout<T> extends ListLayout<T> {
310
279
  }
311
280
  }
312
281
 
313
- private buildRow(node: GridNode<T>, x: number, y: number): LayoutNode {
282
+ protected buildRow(node: GridNode<T>, x: number, y: number): LayoutNode {
314
283
  let rect = new Rect(x, y, 0, 0);
315
284
  let layoutInfo = new LayoutInfo('row', node.key, rect);
316
285
 
@@ -338,8 +307,8 @@ export class TableLayout<T> extends ListLayout<T> {
338
307
 
339
308
  this.setChildHeights(children, height);
340
309
 
341
- rect.width = this.layoutInfos.get(this.collection.head?.key ?? 'header').rect.width;
342
- rect.height = height + 1; // +1 for bottom border
310
+ rect.width = this.layoutNodes.get(this.collection.head?.key ?? 'header').layoutInfo.rect.width;
311
+ rect.height = height;
343
312
 
344
313
  return {
345
314
  layoutInfo,
@@ -348,12 +317,12 @@ export class TableLayout<T> extends ListLayout<T> {
348
317
  };
349
318
  }
350
319
 
351
- private buildCell(node: GridNode<T>, x: number, y: number): LayoutNode {
320
+ protected buildCell(node: GridNode<T>, x: number, y: number): LayoutNode {
352
321
  let width = this.getRenderedColumnWidth(node);
353
322
  let {height, isEstimated} = this.getEstimatedHeight(node, width, this.rowHeight, this.estimatedRowHeight);
354
323
  let rect = new Rect(x, y, width, height);
355
324
  let layoutInfo = new LayoutInfo(node.type, node.key, rect);
356
- layoutInfo.isSticky = !this.disableSticky && (node.props?.isDragButtonCell || node.props?.isSelectionCell);
325
+ layoutInfo.isSticky = this.isStickyColumn(node);
357
326
  layoutInfo.zIndex = layoutInfo.isSticky ? 2 : 1;
358
327
  layoutInfo.estimatedSize = isEstimated;
359
328
 
@@ -367,7 +336,7 @@ export class TableLayout<T> extends ListLayout<T> {
367
336
  // Adjust rect to keep number of visible rows consistent.
368
337
  // (only if height > 1 for getDropTargetFromPoint)
369
338
  if (rect.height > 1) {
370
- let rowHeight = (this.rowHeight ?? this.estimatedRowHeight) + 1; // +1 for border
339
+ let rowHeight = this.getEstimatedRowHeight();
371
340
  rect.y = Math.floor(rect.y / rowHeight) * rowHeight;
372
341
  rect.height = Math.ceil(rect.height / rowHeight) * rowHeight;
373
342
  }
@@ -508,7 +477,7 @@ export class TableLayout<T> extends ListLayout<T> {
508
477
 
509
478
  // Build a map of parentKey => indices of children to persist.
510
479
  for (let key of this.virtualizer.persistedKeys) {
511
- let layoutInfo = this.layoutInfos.get(key);
480
+ let layoutInfo = this.layoutNodes.get(key)?.layoutInfo;
512
481
 
513
482
  // Walk up ancestors so parents are also persisted if children are.
514
483
  while (layoutInfo && layoutInfo.parentKey) {
@@ -526,7 +495,7 @@ export class TableLayout<T> extends ListLayout<T> {
526
495
  indices.push(index);
527
496
  }
528
497
 
529
- layoutInfo = this.layoutInfos.get(layoutInfo.parentKey);
498
+ layoutInfo = this.layoutNodes.get(layoutInfo.parentKey)?.layoutInfo;
530
499
  }
531
500
  }
532
501
 
@@ -535,24 +504,6 @@ export class TableLayout<T> extends ListLayout<T> {
535
504
  }
536
505
  }
537
506
 
538
- // Checks if Chrome version is 105 or greater
539
- private checkChrome105() {
540
- if (typeof window === 'undefined' || window.navigator == null) {
541
- return false;
542
- }
543
-
544
- let isChrome105;
545
- if (window.navigator['userAgentData']) {
546
- isChrome105 = window.navigator['userAgentData']?.brands.some(b => b.brand === 'Chromium' && Number(b.version) === 105);
547
- } else {
548
- let regex = /Chrome\/(\d+)/;
549
- let matches = regex.exec(window.navigator.userAgent);
550
- isChrome105 = matches && matches.length >= 2 && Number(matches[1]) === 105;
551
- }
552
-
553
- return isChrome105;
554
- }
555
-
556
507
  getDropTargetFromPoint(x: number, y: number, isValidDropTarget: (target: DropTarget) => boolean): DropTarget {
557
508
  x += this.virtualizer.visibleRect.x;
558
509
  y += this.virtualizer.visibleRect.y;
package/src/index.ts CHANGED
@@ -9,7 +9,7 @@
9
9
  * OF ANY KIND, either express or implied. See the License for the specific language
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
- export type {ListLayoutOptions, ListLayoutProps, LayoutNode} from './ListLayout';
13
- export type {TableLayoutOptions, TableLayoutProps} from './TableLayout';
12
+ export type {ListLayoutOptions, LayoutNode} from './ListLayout';
13
+ export type {TableLayoutProps} from './TableLayout';
14
14
  export {ListLayout} from './ListLayout';
15
15
  export {TableLayout} from './TableLayout';