Skip to content

Commit df9eb32

Browse files
LFDanLudevongovett
andauthored
Fixing CardView Gallery layout card overlap and container resizing (#2739)
* replacing gallery card min width with 0 width of card should be determined by the gallery layout calculated divs. Min width matches v2. * fixing case where cards disappear when Gallery CardView width is resized to 0 turns out the row height calculatons are messed up because the total weight of the row becomes 0 giving up a NaN height from dividing by 0. This in turn breaks the visible height and y values propagated throughout the OverscanManager, Virtualizer, etc. This stops the Cards from properly reappearing when resizing the Gallery CardView back to widths greater than 0. Co-authored-by: Devon Govett <[email protected]>
1 parent 987e600 commit df9eb32

File tree

2 files changed

+88
-86
lines changed

2 files changed

+88
-86
lines changed

packages/@adobe/spectrum-css-temp/components/card/index.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -717,7 +717,7 @@ user-provided
717717
.spectrum-Card-grid:after {
718718
z-index: 2;
719719
}
720-
min-inline-size: var(--spectrum-card-quiet-min-size);
720+
min-inline-size: 0;
721721

722722
.spectrum-Card-grid {
723723
block-size: 100%;

packages/@react-spectrum/card/src/GalleryLayout.tsx

Lines changed: 87 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -138,105 +138,107 @@ export class GalleryLayout<T> extends BaseLayout<T> {
138138
let y = this.margin;
139139
let availableWidth = visibleWidth - this.margin * 2;
140140

141-
// Compute aspect ratios for all of the items, and the total width if all items were on in a single row.
142-
let ratios = [];
143-
let totalWidth = 0;
144-
let minRatio = this.minItemSize.width / this.minItemSize.height;
145-
let maxRatio = availableWidth / this.minItemSize.height;
146-
147-
for (let node of this.collection) {
148-
let ratio = node.props.width / node.props.height;
149-
if (ratio < minRatio) {
150-
ratio = minRatio;
151-
} else if (ratio > maxRatio && ratio !== minRatio) {
152-
ratio = maxRatio;
141+
// If avaliable width is not greater than 0, skip node layout calculations
142+
if (availableWidth > 0) {
143+
// Compute aspect ratios for all of the items, and the total width if all items were on in a single row.
144+
let ratios = [];
145+
let totalWidth = 0;
146+
let minRatio = this.minItemSize.width / this.minItemSize.height;
147+
let maxRatio = availableWidth / this.minItemSize.height;
148+
149+
for (let node of this.collection) {
150+
let ratio = node.props.width / node.props.height;
151+
if (ratio < minRatio) {
152+
ratio = minRatio;
153+
} else if (ratio > maxRatio && ratio !== minRatio) {
154+
ratio = maxRatio;
155+
}
156+
157+
let itemWidth = ratio * this.minItemSize.height;
158+
ratios.push(ratio);
159+
totalWidth += itemWidth;
153160
}
154161

155-
let itemWidth = ratio * this.minItemSize.height;
156-
ratios.push(ratio);
157-
totalWidth += itemWidth;
158-
}
162+
totalWidth += this.itemSpacing.width * (this.collection.size - 1);
159163

160-
totalWidth += this.itemSpacing.width * (this.collection.size - 1);
164+
// Determine how many rows we'll need, and partition the items into rows
165+
// using the aspect ratios as weights.
166+
let rows = Math.max(1, Math.ceil(totalWidth / availableWidth));
167+
// if the available width can't hold two items, then every item will get its own row
168+
// this leads to a faster run through linear partition and more dependable output for small row widths
169+
if (availableWidth <= (this.minItemSize.width * 2) + (this.itemPadding * 2)) {
170+
rows = this.collection.size;
171+
}
161172

162-
// Determine how many rows we'll need, and partition the items into rows
163-
// using the aspect ratios as weights.
164-
let rows = Math.max(1, Math.ceil(totalWidth / availableWidth));
165-
// if the available width can't hold two items, then every item will get its own row
166-
// this leads to a faster run through linear partition and more dependable output for small row widths
167-
if (availableWidth <= (this.minItemSize.width * 2) + (this.itemPadding * 2)) {
168-
rows = this.collection.size;
169-
}
173+
let weightedRatios = ratios.map(ratio => ratio < this.threshold ? ratio + (0.5 * (1 / ratio)) : ratio);
174+
let partition = linearPartition(weightedRatios, rows);
170175

171-
let weightedRatios = ratios.map(ratio => ratio < this.threshold ? ratio + (0.5 * (1 / ratio)) : ratio);
172-
let partition = linearPartition(weightedRatios, rows);
176+
let index = 0;
177+
for (let row of partition) {
178+
// Compute the total weight for this row
179+
let totalWeight = 0;
180+
for (let j = index; j < index + row.length; j++) {
181+
totalWeight += ratios[j];
182+
}
173183

174-
let index = 0;
175-
for (let row of partition) {
176-
// Compute the total weight for this row
177-
let totalWeight = 0;
178-
for (let j = index; j < index + row.length; j++) {
179-
totalWeight += ratios[j];
180-
}
184+
// Determine the row height based on the total available width and weight of this row.
185+
let bestRowHeight = (availableWidth - (row.length - 1) * this.itemSpacing.width) / totalWeight;
181186

182-
// Determine the row height based on the total available width and weight of this row.
183-
let bestRowHeight = (availableWidth - (row.length - 1) * this.itemSpacing.width) / totalWeight;
187+
// if this is the last row and the row height is >2x the ideal row height, then cap to the ideal height
188+
// probably doing this because if the last row has one extremely tall image, then the row becomes huge
189+
// though that can happen anywhere if a row has lots of tall images... so i'm not sure why this one matters
190+
if (row === partition[partition.length - 1] && bestRowHeight > this.idealRowHeight * 2) {
191+
bestRowHeight = this.idealRowHeight;
192+
}
193+
let itemHeight = Math.round(bestRowHeight) + this.itemPadding;
194+
let x = this.margin;
195+
196+
// if any items are going to end up too small, add a bit of width to them and subtract it from wider objects
197+
let widths = [];
198+
for (let j = index; j < index + row.length; j++) {
199+
let width = Math.round(bestRowHeight * ratios[j]);
200+
widths.push([j - index, width]);
201+
}
202+
this._distributeWidths(widths);
203+
204+
// Create items for this row.
205+
for (let j = index; j < index + row.length; j++) {
206+
let node = this.collection.rows[j];
207+
let itemWidth = Math.max(widths[j - index][1], this.minItemSize.width);
208+
let rect = new Rect(x, y, itemWidth, itemHeight);
209+
let layoutInfo = new LayoutInfo(node.type, node.key, rect);
210+
layoutInfo.allowOverflow = true;
211+
this.layoutInfos.set(node.key, layoutInfo);
212+
x += itemWidth + this.itemSpacing.width;
213+
}
184214

185-
// if this is the last row and the row height is >2x the ideal row height, then cap to the ideal height
186-
// probably doing this because if the last row has one extremely tall image, then the row becomes huge
187-
// though that can happen anywhere if a row has lots of tall images... so i'm not sure why this one matters
188-
if (row === partition[partition.length - 1] && bestRowHeight > this.idealRowHeight * 2) {
189-
bestRowHeight = this.idealRowHeight;
190-
}
191-
let itemHeight = Math.round(bestRowHeight) + this.itemPadding;
192-
let x = this.margin;
193-
194-
// if any items are going to end up too small, add a bit of width to them and subtract it from wider objects
195-
let widths = [];
196-
for (let j = index; j < index + row.length; j++) {
197-
let width = Math.round(bestRowHeight * ratios[j]);
198-
widths.push([j - index, width]);
199-
}
200-
this._distributeWidths(widths);
201-
202-
// Create items for this row.
203-
for (let j = index; j < index + row.length; j++) {
204-
let node = this.collection.rows[j];
205-
let itemWidth = Math.max(widths[j - index][1], this.minItemSize.width);
206-
let rect = new Rect(x, y, itemWidth, itemHeight);
207-
let layoutInfo = new LayoutInfo(node.type, node.key, rect);
208-
layoutInfo.allowOverflow = true;
209-
this.layoutInfos.set(node.key, layoutInfo);
210-
x += itemWidth + this.itemSpacing.width;
215+
y += itemHeight + this.itemSpacing.height;
216+
index += row.length;
211217
}
212218

213-
y += itemHeight + this.itemSpacing.height;
214-
index += row.length;
215-
}
219+
if (this.isLoading) {
220+
let loaderY = y;
221+
let loaderHeight = 60;
222+
// If there aren't any items, make loader take all avaliable room and remove margin from y calculation
223+
// so it doesn't scroll
224+
if (this.collection.size === 0) {
225+
loaderY = 0;
226+
loaderHeight = visibleHeight || 60;
227+
}
216228

217-
if (this.isLoading) {
218-
let loaderY = y;
219-
let loaderHeight = 60;
220-
// If there aren't any items, make loader take all avaliable room and remove margin from y calculation
221-
// so it doesn't scroll
222-
if (this.collection.size === 0) {
223-
loaderY = 0;
224-
loaderHeight = visibleHeight || 60;
229+
let rect = new Rect(0, loaderY, visibleWidth, loaderHeight);
230+
let loader = new LayoutInfo('loader', 'loader', rect);
231+
this.layoutInfos.set('loader', loader);
232+
y = loader.rect.maxY;
225233
}
226234

227-
let rect = new Rect(0, loaderY, visibleWidth, loaderHeight);
228-
let loader = new LayoutInfo('loader', 'loader', rect);
229-
this.layoutInfos.set('loader', loader);
230-
y = loader.rect.maxY;
231-
}
232-
233-
if (this.collection.size === 0 && !this.isLoading) {
234-
let rect = new Rect(0, 0, visibleWidth, visibleHeight);
235-
let placeholder = new LayoutInfo('placeholder', 'placeholder', rect);
236-
this.layoutInfos.set('placeholder', placeholder);
237-
y = placeholder.rect.maxY;
235+
if (this.collection.size === 0 && !this.isLoading) {
236+
let rect = new Rect(0, 0, visibleWidth, visibleHeight);
237+
let placeholder = new LayoutInfo('placeholder', 'placeholder', rect);
238+
this.layoutInfos.set('placeholder', placeholder);
239+
y = placeholder.rect.maxY;
240+
}
238241
}
239-
240242
this.contentSize = new Size(visibleWidth, y);
241243
}
242244
}

0 commit comments

Comments
 (0)