@react-stately/layout 4.3.1 → 4.5.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.
@@ -68,9 +68,12 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
68
68
  // If columnWidths were provided via layoutOptions, update those.
69
69
  // Otherwise, calculate column widths ourselves.
70
70
  if (invalidationContext.layoutOptions?.columnWidths) {
71
- if (invalidationContext.layoutOptions.columnWidths !== this.columnWidths) {
72
- this.columnWidths = invalidationContext.layoutOptions.columnWidths;
73
- invalidationContext.sizeChanged = true;
71
+ for (const [key, val] of invalidationContext.layoutOptions.columnWidths) {
72
+ if (this.columnWidths.get(key) !== val) {
73
+ this.columnWidths = invalidationContext.layoutOptions.columnWidths;
74
+ invalidationContext.sizeChanged = true;
75
+ break;
76
+ }
74
77
  }
75
78
  } else if (invalidationContext.sizeChanged || this.columnsChanged(newCollection, this.lastCollection)) {
76
79
  let columnLayout = new TableColumnLayout({});
@@ -274,9 +277,6 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
274
277
  if (y > this.requestedRect.maxY) {
275
278
  let rowsAfterRect = collection.size - (children.length + skipped);
276
279
  let lastNode = getLastItem(childNodes);
277
- if (lastNode?.type === 'loader') {
278
- rowsAfterRect--;
279
- }
280
280
 
281
281
  // Estimate the remaining height for rows that we don't need to layout right now.
282
282
  y += rowsAfterRect * rowHeight;
@@ -296,7 +296,7 @@ export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> exten
296
296
  }
297
297
 
298
298
  // Make sure that the table body gets a height if empty or performing initial load
299
- let isEmptyOrLoading = collection?.size === 0 || (collection.size === 1 && collection.getItem(collection.getFirstKey()!)!.type === 'loader');
299
+ let isEmptyOrLoading = collection?.size === 0;
300
300
  if (isEmptyOrLoading) {
301
301
  y = this.virtualizer!.visibleRect.maxY;
302
302
  } else {
@@ -29,6 +29,11 @@ export interface WaterfallLayoutOptions {
29
29
  * @default 18 x 18
30
30
  */
31
31
  minSpace?: Size,
32
+ /**
33
+ * The maximum allowed horizontal space between items.
34
+ * @default Infinity
35
+ */
36
+ maxHorizontalSpace?: number,
32
37
  /**
33
38
  * The maximum number of columns.
34
39
  * @default Infinity
@@ -55,6 +60,7 @@ const DEFAULT_OPTIONS = {
55
60
  minItemSize: new Size(200, 200),
56
61
  maxItemSize: new Size(Infinity, Infinity),
57
62
  minSpace: new Size(18, 18),
63
+ maxSpace: Infinity,
58
64
  maxColumns: Infinity,
59
65
  dropIndicatorThickness: 2
60
66
  };
@@ -64,13 +70,15 @@ export class WaterfallLayout<T extends object, O extends WaterfallLayoutOptions
64
70
  private layoutInfos: Map<Key, WaterfallLayoutInfo> = new Map();
65
71
  protected numColumns = 0;
66
72
  protected dropIndicatorThickness = 2;
73
+ private margin: number = 0;
67
74
 
68
75
  shouldInvalidateLayoutOptions(newOptions: O, oldOptions: O): boolean {
69
76
  return newOptions.maxColumns !== oldOptions.maxColumns
70
77
  || newOptions.dropIndicatorThickness !== oldOptions.dropIndicatorThickness
71
78
  || (!(newOptions.minItemSize || DEFAULT_OPTIONS.minItemSize).equals(oldOptions.minItemSize || DEFAULT_OPTIONS.minItemSize))
72
79
  || (!(newOptions.maxItemSize || DEFAULT_OPTIONS.maxItemSize).equals(oldOptions.maxItemSize || DEFAULT_OPTIONS.maxItemSize))
73
- || (!(newOptions.minSpace || DEFAULT_OPTIONS.minSpace).equals(oldOptions.minSpace || DEFAULT_OPTIONS.minSpace));
80
+ || (!(newOptions.minSpace || DEFAULT_OPTIONS.minSpace).equals(oldOptions.minSpace || DEFAULT_OPTIONS.minSpace))
81
+ || (newOptions.maxHorizontalSpace !== oldOptions.maxHorizontalSpace);
74
82
  }
75
83
 
76
84
  update(invalidationContext: InvalidationContext<O>): void {
@@ -78,6 +86,7 @@ export class WaterfallLayout<T extends object, O extends WaterfallLayoutOptions
78
86
  minItemSize = DEFAULT_OPTIONS.minItemSize,
79
87
  maxItemSize = DEFAULT_OPTIONS.maxItemSize,
80
88
  minSpace = DEFAULT_OPTIONS.minSpace,
89
+ maxHorizontalSpace = DEFAULT_OPTIONS.maxSpace,
81
90
  maxColumns = DEFAULT_OPTIONS.maxColumns,
82
91
  dropIndicatorThickness = DEFAULT_OPTIONS.dropIndicatorThickness
83
92
  } = invalidationContext.layoutOptions || {};
@@ -107,8 +116,9 @@ export class WaterfallLayout<T extends object, O extends WaterfallLayoutOptions
107
116
  let itemHeight = minItemSize.height + Math.floor((maxItemHeight - minItemSize.height) * t);
108
117
  itemHeight = Math.max(minItemSize.height, Math.min(maxItemHeight, itemHeight));
109
118
 
110
- // Compute the horizontal spacing and content height
111
- let horizontalSpacing = Math.floor((visibleWidth - numColumns * itemWidth) / (numColumns + 1));
119
+ // Compute the horizontal spacing, content height and horizontal margin
120
+ let horizontalSpacing = Math.min(Math.max(maxHorizontalSpace, minSpace.width), Math.floor((visibleWidth - numColumns * itemWidth) / (numColumns + 1)));
121
+ this.margin = Math.floor((visibleWidth - numColumns * itemWidth - horizontalSpacing * (numColumns + 1)) / 2);
112
122
 
113
123
  // Setup an array of column heights
114
124
  let columnHeights = Array(numColumns).fill(minSpace.height);
@@ -126,7 +136,7 @@ export class WaterfallLayout<T extends object, O extends WaterfallLayoutOptions
126
136
  // Preserve the previous column index so items don't jump around during resizing unless the number of columns changed.
127
137
  let prevColumn = numColumns === this.numColumns && oldLayoutInfo && oldLayoutInfo.rect.y < this.virtualizer!.visibleRect.maxY ? oldLayoutInfo.column : undefined;
128
138
  let column = prevColumn ?? columnHeights.reduce((minIndex, h, i) => h < columnHeights[minIndex] ? i : minIndex, 0);
129
- let x = horizontalSpacing + column * (itemWidth + horizontalSpacing);
139
+ let x = horizontalSpacing + column * (itemWidth + horizontalSpacing) + this.margin;
130
140
  let y = columnHeights[column];
131
141
 
132
142
  let rect = new Rect(x, y, itemWidth, height);
@@ -169,8 +179,9 @@ export class WaterfallLayout<T extends object, O extends WaterfallLayoutOptions
169
179
  newLayoutInfos.set(lastNode.key, layoutInfo);
170
180
  }
171
181
 
172
- // Reset all columns to the maximum for the next section. If loading, set to 0 so virtualizer doesn't render its body since there aren't items to render
173
- let isEmptyOrLoading = collection?.size === 0 || (collection.size === 1 && collection.getItem(collection.getFirstKey()!)!.type === 'loader');
182
+ // Reset all columns to the maximum for the next section. If loading, set to 0 so virtualizer doesn't render its body since there aren't items to render,
183
+ // except if we are performing skeleton loading
184
+ let isEmptyOrLoading = collection?.size === 0 && collection.getItem(collection.getFirstKey()!)?.type !== 'skeleton';
174
185
  let maxHeight = isEmptyOrLoading ? 0 : Math.max(...columnHeights);
175
186
  this.contentSize = new Size(this.virtualizer!.visibleRect.width, maxHeight);
176
187
  this.layoutInfos = newLayoutInfos;