@react-stately/layout 4.1.1 → 4.2.0

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
@@ -15,20 +15,41 @@ import {getChildNodes} from '@react-stately/collections';
15
15
  import {InvalidationContext, Layout, LayoutInfo, Point, Rect, Size} from '@react-stately/virtualizer';
16
16
 
17
17
  export interface ListLayoutOptions {
18
- /** The fixed height of a row in px. */
18
+ /**
19
+ * The fixed height of a row in px.
20
+ * @default 48
21
+ */
19
22
  rowHeight?: number,
20
23
  /** The estimated height of a row, when row heights are variable. */
21
24
  estimatedRowHeight?: number,
22
- /** The fixed height of a section header in px. */
25
+ /**
26
+ * The fixed height of a section header in px.
27
+ * @default 48
28
+ */
23
29
  headingHeight?: number,
24
30
  /** The estimated height of a section header, when the height is variable. */
25
31
  estimatedHeadingHeight?: number,
26
- /** The fixed height of a loader element in px. This loader is specifically for
32
+ /**
33
+ * The fixed height of a loader element in px. This loader is specifically for
27
34
  * "load more" elements rendered when loading more rows at the root level or inside nested row/sections.
35
+ * @default 48
28
36
  */
29
37
  loaderHeight?: number,
30
- /** The thickness of the drop indicator. */
31
- dropIndicatorThickness?: number
38
+ /**
39
+ * The thickness of the drop indicator.
40
+ * @default 2
41
+ */
42
+ dropIndicatorThickness?: number,
43
+ /**
44
+ * The gap between items.
45
+ * @default 0
46
+ */
47
+ gap?: number,
48
+ /**
49
+ * The padding around the list.
50
+ * @default 0
51
+ */
52
+ padding?: number
32
53
  }
33
54
 
34
55
  // A wrapper around LayoutInfo that supports hierarchy
@@ -43,24 +64,22 @@ export interface LayoutNode {
43
64
  const DEFAULT_HEIGHT = 48;
44
65
 
45
66
  /**
46
- * The ListLayout class is an implementation of a virtualizer {@link Layout}.
47
- * To configure a ListLayout, you can use the properties to define the
48
- * layouts and/or use the method for defining indentation.
49
- * The {@link ListKeyboardDelegate} extends the existing virtualizer
50
- * delegate with an additional method to do this (it uses the same delegate object as
51
- * the virtualizer itself).
67
+ * ListLayout is a virtualizer Layout implementation
68
+ * that arranges its items in a vertical stack. It supports both fixed
69
+ * and variable height items.
52
70
  */
53
- export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTargetDelegate {
71
+ export class ListLayout<T, O extends ListLayoutOptions = ListLayoutOptions> extends Layout<Node<T>, O> implements DropTargetDelegate {
54
72
  protected rowHeight: number | null;
55
73
  protected estimatedRowHeight: number | null;
56
74
  protected headingHeight: number | null;
57
75
  protected estimatedHeadingHeight: number | null;
58
76
  protected loaderHeight: number | null;
59
77
  protected dropIndicatorThickness: number;
78
+ protected gap: number;
79
+ protected padding: number;
60
80
  protected layoutNodes: Map<Key, LayoutNode>;
61
81
  protected contentSize: Size;
62
82
  protected lastCollection: Collection<Node<T>> | null;
63
- private lastWidth: number;
64
83
  protected rootNodes: LayoutNode[];
65
84
  private invalidateEverything: boolean;
66
85
  /** The rectangle containing currently valid layout infos. */
@@ -80,9 +99,10 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
80
99
  this.estimatedHeadingHeight = options.estimatedHeadingHeight ?? null;
81
100
  this.loaderHeight = options.loaderHeight ?? null;
82
101
  this.dropIndicatorThickness = options.dropIndicatorThickness || 2;
102
+ this.gap = options.gap || 0;
103
+ this.padding = options.padding || 0;
83
104
  this.layoutNodes = new Map();
84
105
  this.rootNodes = [];
85
- this.lastWidth = 0;
86
106
  this.lastCollection = null;
87
107
  this.invalidateEverything = false;
88
108
  this.validRect = new Rect();
@@ -104,7 +124,7 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
104
124
  // Adjust rect to keep number of visible rows consistent.
105
125
  // (only if height > 1 for getDropTargetFromPoint)
106
126
  if (rect.height > 1) {
107
- let rowHeight = this.rowHeight ?? this.estimatedRowHeight ?? DEFAULT_HEIGHT;
127
+ let rowHeight = (this.rowHeight ?? this.estimatedRowHeight ?? DEFAULT_HEIGHT) + this.gap;
108
128
  rect.y = Math.floor(rect.y / rowHeight) * rowHeight;
109
129
  rect.height = Math.ceil(rect.height / rowHeight) * rowHeight;
110
130
  }
@@ -167,10 +187,28 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
167
187
  return node.layoutInfo.rect.intersects(rect) || node.layoutInfo.isSticky || node.layoutInfo.type === 'header' || this.virtualizer!.isPersistedKey(node.layoutInfo.key);
168
188
  }
169
189
 
170
- protected shouldInvalidateEverything(invalidationContext: InvalidationContext<O>) {
190
+ protected shouldInvalidateEverything(invalidationContext: InvalidationContext<O>): boolean {
171
191
  // Invalidate cache if the size of the collection changed.
172
192
  // In this case, we need to recalculate the entire layout.
173
- return invalidationContext.sizeChanged || false;
193
+ // Also invalidate if fixed sizes/gaps change.
194
+ let options = invalidationContext.layoutOptions;
195
+ return invalidationContext.sizeChanged
196
+ || this.rowHeight !== (options?.rowHeight ?? this.rowHeight)
197
+ || this.headingHeight !== (options?.headingHeight ?? this.headingHeight)
198
+ || this.loaderHeight !== (options?.loaderHeight ?? this.loaderHeight)
199
+ || this.gap !== (options?.gap ?? this.gap)
200
+ || this.padding !== (options?.padding ?? this.padding);
201
+ }
202
+
203
+ shouldInvalidateLayoutOptions(newOptions: O, oldOptions: O): boolean {
204
+ return newOptions.rowHeight !== oldOptions.rowHeight
205
+ || newOptions.estimatedRowHeight !== oldOptions.estimatedRowHeight
206
+ || newOptions.headingHeight !== oldOptions.headingHeight
207
+ || newOptions.estimatedHeadingHeight !== oldOptions.estimatedHeadingHeight
208
+ || newOptions.loaderHeight !== oldOptions.loaderHeight
209
+ || newOptions.dropIndicatorThickness !== oldOptions.dropIndicatorThickness
210
+ || newOptions.gap !== oldOptions.gap
211
+ || newOptions.padding !== oldOptions.padding;
174
212
  }
175
213
 
176
214
  update(invalidationContext: InvalidationContext<O>) {
@@ -184,6 +222,16 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
184
222
  this.layoutNodes.clear();
185
223
  }
186
224
 
225
+ let options = invalidationContext.layoutOptions;
226
+ this.rowHeight = options?.rowHeight ?? this.rowHeight;
227
+ this.estimatedRowHeight = options?.estimatedRowHeight ?? this.estimatedRowHeight;
228
+ this.headingHeight = options?.headingHeight ?? this.headingHeight;
229
+ this.estimatedHeadingHeight = options?.estimatedHeadingHeight ?? this.estimatedHeadingHeight;
230
+ this.loaderHeight = options?.loaderHeight ?? this.loaderHeight;
231
+ this.dropIndicatorThickness = options?.dropIndicatorThickness ?? this.dropIndicatorThickness;
232
+ this.gap = options?.gap ?? this.gap;
233
+ this.padding = options?.padding ?? this.padding;
234
+
187
235
  this.rootNodes = this.buildCollection();
188
236
 
189
237
  // Remove deleted layout nodes
@@ -198,18 +246,17 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
198
246
  }
199
247
  }
200
248
 
201
- this.lastWidth = this.virtualizer!.visibleRect.width;
202
249
  this.lastCollection = collection;
203
250
  this.invalidateEverything = false;
204
251
  this.validRect = this.requestedRect.copy();
205
252
  }
206
253
 
207
- protected buildCollection(y = 0): LayoutNode[] {
254
+ protected buildCollection(y = this.padding): LayoutNode[] {
208
255
  let collection = this.virtualizer!.collection;
209
256
  let skipped = 0;
210
257
  let nodes: LayoutNode[] = [];
211
258
  for (let node of collection) {
212
- let rowHeight = this.rowHeight ?? this.estimatedRowHeight ?? DEFAULT_HEIGHT;
259
+ let rowHeight = (this.rowHeight ?? this.estimatedRowHeight ?? DEFAULT_HEIGHT) + this.gap;
213
260
 
214
261
  // Skip rows before the valid rectangle unless they are already cached.
215
262
  if (node.type === 'item' && y + rowHeight < this.requestedRect.y && !this.isValid(node, y)) {
@@ -218,8 +265,8 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
218
265
  continue;
219
266
  }
220
267
 
221
- let layoutNode = this.buildChild(node, 0, y, null);
222
- y = layoutNode.layoutInfo.rect.maxY;
268
+ let layoutNode = this.buildChild(node, this.padding, y, null);
269
+ y = layoutNode.layoutInfo.rect.maxY + this.gap;
223
270
  nodes.push(layoutNode);
224
271
 
225
272
  if (node.type === 'item' && y > this.requestedRect.maxY) {
@@ -228,6 +275,8 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
228
275
  }
229
276
  }
230
277
 
278
+ y -= this.gap;
279
+ y += this.padding;
231
280
  this.contentSize = new Size(this.virtualizer!.visibleRect.width, y);
232
281
  return nodes;
233
282
  }
@@ -272,9 +321,9 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
272
321
  }
273
322
 
274
323
  protected buildLoader(node: Node<T>, x: number, y: number): LayoutNode {
275
- let rect = new Rect(x, y, 0, 0);
324
+ let rect = new Rect(x, y, this.padding, 0);
276
325
  let layoutInfo = new LayoutInfo('loader', node.key, rect);
277
- rect.width = this.virtualizer!.contentSize.width;
326
+ rect.width = this.virtualizer!.contentSize.width - this.padding - x;
278
327
  rect.height = this.loaderHeight || this.rowHeight || this.estimatedRowHeight || DEFAULT_HEIGHT;
279
328
 
280
329
  return {
@@ -285,15 +334,15 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
285
334
 
286
335
  protected buildSection(node: Node<T>, x: number, y: number): LayoutNode {
287
336
  let collection = this.virtualizer!.collection;
288
- let width = this.virtualizer!.visibleRect.width;
289
- let rect = new Rect(0, y, width, 0);
337
+ let width = this.virtualizer!.visibleRect.width - this.padding;
338
+ let rect = new Rect(x, y, width - x, 0);
290
339
  let layoutInfo = new LayoutInfo(node.type, node.key, rect);
291
340
 
292
341
  let startY = y;
293
342
  let skipped = 0;
294
343
  let children: LayoutNode[] = [];
295
344
  for (let child of getChildNodes(node, collection)) {
296
- let rowHeight = (this.rowHeight ?? this.estimatedRowHeight ?? DEFAULT_HEIGHT);
345
+ let rowHeight = (this.rowHeight ?? this.estimatedRowHeight ?? DEFAULT_HEIGHT) + this.gap;
297
346
 
298
347
  // Skip rows before the valid rectangle unless they are already cached.
299
348
  if (y + rowHeight < this.requestedRect.y && !this.isValid(node, y)) {
@@ -303,7 +352,7 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
303
352
  }
304
353
 
305
354
  let layoutNode = this.buildChild(child, x, y, layoutInfo.key);
306
- y = layoutNode.layoutInfo.rect.maxY;
355
+ y = layoutNode.layoutInfo.rect.maxY + this.gap;
307
356
  children.push(layoutNode);
308
357
 
309
358
  if (y > this.requestedRect.maxY) {
@@ -313,6 +362,7 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
313
362
  }
314
363
  }
315
364
 
365
+ y -= this.gap;
316
366
  rect.height = y - startY;
317
367
 
318
368
  return {
@@ -324,7 +374,7 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
324
374
  }
325
375
 
326
376
  protected buildSectionHeader(node: Node<T>, x: number, y: number): LayoutNode {
327
- let width = this.virtualizer!.visibleRect.width;
377
+ let width = this.virtualizer!.visibleRect.width - this.padding;
328
378
  let rectHeight = this.headingHeight;
329
379
  let isEstimated = false;
330
380
 
@@ -339,7 +389,7 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
339
389
  let curNode = this.virtualizer!.collection.getItem(node.key);
340
390
  let lastNode = this.lastCollection ? this.lastCollection.getItem(node.key) : null;
341
391
  rectHeight = previousLayoutInfo.rect.height;
342
- isEstimated = width !== this.lastWidth || curNode !== lastNode || previousLayoutInfo.estimatedSize;
392
+ isEstimated = width !== previousLayoutInfo.rect.width || curNode !== lastNode || previousLayoutInfo.estimatedSize;
343
393
  } else {
344
394
  rectHeight = (node.rendered ? this.estimatedHeadingHeight : 0);
345
395
  isEstimated = true;
@@ -350,7 +400,7 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
350
400
  rectHeight = DEFAULT_HEIGHT;
351
401
  }
352
402
 
353
- let headerRect = new Rect(0, y, width, rectHeight);
403
+ let headerRect = new Rect(x, y, width - x, rectHeight);
354
404
  let header = new LayoutInfo('header', node.key, headerRect);
355
405
  header.estimatedSize = isEstimated;
356
406
  return {
@@ -362,7 +412,7 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
362
412
  }
363
413
 
364
414
  protected buildItem(node: Node<T>, x: number, y: number): LayoutNode {
365
- let width = this.virtualizer!.visibleRect.width;
415
+ let width = this.virtualizer!.visibleRect.width - this.padding - x;
366
416
  let rectHeight = this.rowHeight;
367
417
  let isEstimated = false;
368
418
 
@@ -374,7 +424,7 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
374
424
  let previousLayoutNode = this.layoutNodes.get(node.key);
375
425
  if (previousLayoutNode) {
376
426
  rectHeight = previousLayoutNode.layoutInfo.rect.height;
377
- isEstimated = width !== this.lastWidth || node !== previousLayoutNode.node || previousLayoutNode.layoutInfo.estimatedSize;
427
+ isEstimated = width !== previousLayoutNode.layoutInfo.rect.width || node !== previousLayoutNode.node || previousLayoutNode.layoutInfo.estimatedSize;
378
428
  } else {
379
429
  rectHeight = this.estimatedRowHeight;
380
430
  isEstimated = true;
@@ -385,7 +435,7 @@ export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTa
385
435
  rectHeight = DEFAULT_HEIGHT;
386
436
  }
387
437
 
388
- let rect = new Rect(x, y, width - x, rectHeight);
438
+ let rect = new Rect(x, y, width, rectHeight);
389
439
  let layoutInfo = new LayoutInfo(node.type, node.key, rect);
390
440
  layoutInfo.estimatedSize = isEstimated;
391
441
  return {
@@ -18,12 +18,16 @@ 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 TableLayoutProps {
21
+ export interface TableLayoutProps extends ListLayoutOptions {
22
22
  columnWidths?: Map<Key, number>
23
23
  }
24
24
 
25
25
  const DEFAULT_ROW_HEIGHT = 48;
26
26
 
27
+ /**
28
+ * TableLayout is a virtualizer Layout implementation that arranges
29
+ * items in rows and columns.
30
+ */
27
31
  export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> extends ListLayout<T, O> {
28
32
  protected lastCollection: TableCollection<T> | null = null;
29
33
  private columnWidths: Map<Key, number> = new Map();
@@ -31,7 +35,7 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
31
35
  private lastPersistedKeys: Set<Key> | null = null;
32
36
  private persistedIndices: Map<Key, number[]> = new Map();
33
37
 
34
- constructor(options: ListLayoutOptions) {
38
+ constructor(options?: ListLayoutOptions) {
35
39
  super(options);
36
40
  this.stickyColumnIndices = [];
37
41
  }
@@ -53,6 +57,11 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
53
57
  );
54
58
  }
55
59
 
60
+ shouldInvalidateLayoutOptions(newOptions: O, oldOptions: O): boolean {
61
+ return newOptions.columnWidths !== oldOptions.columnWidths
62
+ || super.shouldInvalidateLayoutOptions(newOptions, oldOptions);
63
+ }
64
+
56
65
  update(invalidationContext: InvalidationContext<O>): void {
57
66
  let newCollection = this.virtualizer!.collection as TableCollection<T>;
58
67
 
@@ -65,7 +74,7 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
65
74
  }
66
75
  } else if (invalidationContext.sizeChanged || this.columnsChanged(newCollection, this.lastCollection)) {
67
76
  let columnLayout = new TableColumnLayout({});
68
- this.columnWidths = columnLayout.buildColumnWidths(this.virtualizer!.visibleRect.width, newCollection, new Map());
77
+ this.columnWidths = columnLayout.buildColumnWidths(this.virtualizer!.visibleRect.width - this.padding * 2, newCollection, new Map());
69
78
  invalidationContext.sizeChanged = true;
70
79
  }
71
80
 
@@ -86,11 +95,11 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
86
95
 
87
96
  let header = this.buildTableHeader();
88
97
  this.layoutNodes.set(header.layoutInfo.key, header);
89
- let body = this.buildBody(header.layoutInfo.rect.height);
98
+ let body = this.buildBody(header.layoutInfo.rect.maxY + this.gap);
90
99
  this.lastPersistedKeys = null;
91
100
 
92
101
  body.layoutInfo.rect.width = Math.max(header.layoutInfo.rect.width, body.layoutInfo.rect.width);
93
- this.contentSize = new Size(body.layoutInfo.rect.width, body.layoutInfo.rect.maxY);
102
+ this.contentSize = new Size(body.layoutInfo.rect.width + this.padding * 2, body.layoutInfo.rect.maxY + this.padding);
94
103
  return [
95
104
  header,
96
105
  body
@@ -99,16 +108,16 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
99
108
 
100
109
  protected buildTableHeader(): LayoutNode {
101
110
  let collection = this.virtualizer!.collection as TableCollection<T>;
102
- let rect = new Rect(0, 0, 0, 0);
111
+ let rect = new Rect(this.padding, this.padding, 0, 0);
103
112
  let layoutInfo = new LayoutInfo('header', collection.head?.key ?? 'header', rect);
104
113
  layoutInfo.isSticky = true;
105
114
  layoutInfo.zIndex = 1;
106
115
 
107
- let y = 0;
116
+ let y = this.padding;
108
117
  let width = 0;
109
118
  let children: LayoutNode[] = [];
110
119
  for (let headerRow of collection.headerRows) {
111
- let layoutNode = this.buildChild(headerRow, 0, y, layoutInfo.key);
120
+ let layoutNode = this.buildChild(headerRow, this.padding, y, layoutInfo.key);
112
121
  layoutNode.layoutInfo.parentKey = layoutInfo.key;
113
122
  y = layoutNode.layoutInfo.rect.maxY;
114
123
  width = Math.max(width, layoutNode.layoutInfo.rect.width);
@@ -117,7 +126,7 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
117
126
  }
118
127
 
119
128
  rect.width = width;
120
- rect.height = y;
129
+ rect.height = y - this.padding;
121
130
 
122
131
  return {
123
132
  layoutInfo,
@@ -128,7 +137,7 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
128
137
  }
129
138
 
130
139
  protected buildHeaderRow(headerRow: GridNode<T>, x: number, y: number): LayoutNode {
131
- let rect = new Rect(0, y, 0, 0);
140
+ let rect = new Rect(x, y, 0, 0);
132
141
  let row = new LayoutInfo('headerrow', headerRow.key, rect);
133
142
 
134
143
  let height = 0;
@@ -148,7 +157,7 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
148
157
  this.setChildHeights(columns, height);
149
158
 
150
159
  rect.height = height;
151
- rect.width = x;
160
+ rect.width = x - rect.x;
152
161
 
153
162
  return {
154
163
  layoutInfo: row,
@@ -171,10 +180,10 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
171
180
  // used to get the column widths when rendering to the DOM
172
181
  private getRenderedColumnWidth(node: GridNode<T>) {
173
182
  let collection = this.virtualizer!.collection as TableCollection<T>;
174
- let colspan = node.colspan ?? 1;
183
+ let colSpan = node.colSpan ?? 1;
175
184
  let colIndex = node.colIndex ?? node.index;
176
185
  let width = 0;
177
- for (let i = colIndex; i < colIndex + colspan; i++) {
186
+ for (let i = colIndex; i < colIndex + colSpan; i++) {
178
187
  let column = collection.columns[i];
179
188
  if (column?.key != null) {
180
189
  width += this.columnWidths.get(column.key) ?? 0;
@@ -234,14 +243,14 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
234
243
 
235
244
  protected buildBody(y: number): LayoutNode {
236
245
  let collection = this.virtualizer!.collection as TableCollection<T>;
237
- let rect = new Rect(0, y, 0, 0);
246
+ let rect = new Rect(this.padding, y, 0, 0);
238
247
  let layoutInfo = new LayoutInfo('rowgroup', collection.body.key, rect);
239
248
 
240
249
  let startY = y;
241
250
  let skipped = 0;
242
251
  let width = 0;
243
252
  let children: LayoutNode[] = [];
244
- let rowHeight = this.getEstimatedRowHeight();
253
+ let rowHeight = this.getEstimatedRowHeight() + this.gap;
245
254
  for (let node of getChildNodes(collection.body, collection)) {
246
255
  // Skip rows before the valid rectangle unless they are already cached.
247
256
  if (y + rowHeight < this.requestedRect.y && !this.isValid(node, y)) {
@@ -250,10 +259,10 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
250
259
  continue;
251
260
  }
252
261
 
253
- let layoutNode = this.buildChild(node, 0, y, layoutInfo.key);
262
+ let layoutNode = this.buildChild(node, this.padding, y, layoutInfo.key);
254
263
  layoutNode.layoutInfo.parentKey = layoutInfo.key;
255
264
  layoutNode.index = children.length;
256
- y = layoutNode.layoutInfo.rect.maxY;
265
+ y = layoutNode.layoutInfo.rect.maxY + this.gap;
257
266
  width = Math.max(width, layoutNode.layoutInfo.rect.width);
258
267
  children.push(layoutNode);
259
268
 
@@ -266,6 +275,8 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
266
275
 
267
276
  if (children.length === 0) {
268
277
  y = this.virtualizer!.visibleRect.maxY;
278
+ } else {
279
+ y -= this.gap;
269
280
  }
270
281
 
271
282
  rect.width = width;