@react-stately/layout
Advanced tools
Comparing version 3.0.0-nightly-f90799b33-241206 to 3.0.0-nightly-f90f4899f-250227
@@ -20,70 +20,107 @@ var $7Tzdl$reactstatelyvirtualizer = require("@react-stately/virtualizer"); | ||
*/ | ||
const $1f7773ceb2a3b9a6$var$DEFAULT_OPTIONS = { | ||
minItemSize: new (0, $7Tzdl$reactstatelyvirtualizer.Size)(200, 200), | ||
maxItemSize: new (0, $7Tzdl$reactstatelyvirtualizer.Size)(Infinity, Infinity), | ||
preserveAspectRatio: false, | ||
minSpace: new (0, $7Tzdl$reactstatelyvirtualizer.Size)(18, 18), | ||
maxColumns: Infinity, | ||
dropIndicatorThickness: 2 | ||
}; | ||
class $1f7773ceb2a3b9a6$export$7d2b12578154a735 extends (0, $7Tzdl$reactstatelyvirtualizer.Layout) { | ||
update() { | ||
shouldInvalidateLayoutOptions(newOptions, oldOptions) { | ||
return newOptions.maxColumns !== oldOptions.maxColumns || newOptions.dropIndicatorThickness !== oldOptions.dropIndicatorThickness || newOptions.preserveAspectRatio !== oldOptions.preserveAspectRatio || !(newOptions.minItemSize || $1f7773ceb2a3b9a6$var$DEFAULT_OPTIONS.minItemSize).equals(oldOptions.minItemSize || $1f7773ceb2a3b9a6$var$DEFAULT_OPTIONS.minItemSize) || !(newOptions.maxItemSize || $1f7773ceb2a3b9a6$var$DEFAULT_OPTIONS.maxItemSize).equals(oldOptions.maxItemSize || $1f7773ceb2a3b9a6$var$DEFAULT_OPTIONS.maxItemSize) || !(newOptions.minSpace || $1f7773ceb2a3b9a6$var$DEFAULT_OPTIONS.minSpace).equals(oldOptions.minSpace || $1f7773ceb2a3b9a6$var$DEFAULT_OPTIONS.minSpace); | ||
} | ||
update(invalidationContext) { | ||
let { minItemSize: minItemSize = $1f7773ceb2a3b9a6$var$DEFAULT_OPTIONS.minItemSize, maxItemSize: maxItemSize = $1f7773ceb2a3b9a6$var$DEFAULT_OPTIONS.maxItemSize, preserveAspectRatio: preserveAspectRatio = $1f7773ceb2a3b9a6$var$DEFAULT_OPTIONS.preserveAspectRatio, minSpace: minSpace = $1f7773ceb2a3b9a6$var$DEFAULT_OPTIONS.minSpace, maxColumns: maxColumns = $1f7773ceb2a3b9a6$var$DEFAULT_OPTIONS.maxColumns, dropIndicatorThickness: dropIndicatorThickness = $1f7773ceb2a3b9a6$var$DEFAULT_OPTIONS.dropIndicatorThickness } = invalidationContext.layoutOptions || {}; | ||
this.dropIndicatorThickness = dropIndicatorThickness; | ||
let visibleWidth = this.virtualizer.visibleRect.width; | ||
// The max item width is always the entire viewport. | ||
// If the max item height is infinity, scale in proportion to the max width. | ||
let maxItemWidth = Math.min(this.maxItemSize.width, visibleWidth); | ||
let maxItemHeight = Number.isFinite(this.maxItemSize.height) ? this.maxItemSize.height : Math.floor(this.minItemSize.height / this.minItemSize.width * maxItemWidth); | ||
let maxItemWidth = Math.min(maxItemSize.width, visibleWidth); | ||
let maxItemHeight = Number.isFinite(maxItemSize.height) ? maxItemSize.height : Math.floor(minItemSize.height / minItemSize.width * maxItemWidth); | ||
// Compute the number of rows and columns needed to display the content | ||
let columns = Math.floor(visibleWidth / (this.minItemSize.width + this.minSpace.width)); | ||
this.numColumns = Math.max(1, Math.min(this.maxColumns, columns)); | ||
let columns = Math.floor(visibleWidth / (minItemSize.width + minSpace.width)); | ||
let numColumns = Math.max(1, Math.min(maxColumns, columns)); | ||
// Compute the available width (minus the space between items) | ||
let width = visibleWidth - this.minSpace.width * Math.max(0, this.numColumns); | ||
let width = visibleWidth - minSpace.width * Math.max(0, numColumns); | ||
// Compute the item width based on the space available | ||
let itemWidth = Math.floor(width / this.numColumns); | ||
itemWidth = Math.max(this.minItemSize.width, Math.min(maxItemWidth, itemWidth)); | ||
let itemWidth = Math.floor(width / numColumns); | ||
itemWidth = Math.max(minItemSize.width, Math.min(maxItemWidth, itemWidth)); | ||
// Compute the item height, which is proportional to the item width | ||
let t = (itemWidth - this.minItemSize.width) / Math.max(1, maxItemWidth - this.minItemSize.width); | ||
let itemHeight = this.minItemSize.height + Math.floor((maxItemHeight - this.minItemSize.height) * t); | ||
itemHeight = Math.max(this.minItemSize.height, Math.min(maxItemHeight, itemHeight)); | ||
this.itemSize = new (0, $7Tzdl$reactstatelyvirtualizer.Size)(itemWidth, itemHeight); | ||
let t = (itemWidth - minItemSize.width) / Math.max(1, maxItemWidth - minItemSize.width); | ||
let itemHeight = minItemSize.height + Math.floor((maxItemHeight - minItemSize.height) * t); | ||
itemHeight = Math.max(minItemSize.height, Math.min(maxItemHeight, itemHeight)); | ||
// Compute the horizontal spacing and content height | ||
this.horizontalSpacing = Math.floor((visibleWidth - this.numColumns * this.itemSize.width) / (this.numColumns + 1)); | ||
this.layoutInfos = []; | ||
for (let node of this.virtualizer.collection)this.layoutInfos.push(this.getLayoutInfoForNode(node)); | ||
} | ||
getVisibleLayoutInfos(rect) { | ||
let firstVisibleItem = this.getIndexAtPoint(rect.x, rect.y); | ||
let lastVisibleItem = this.getIndexAtPoint(rect.maxX, rect.maxY); | ||
let result = this.layoutInfos.slice(firstVisibleItem, lastVisibleItem + 1); | ||
let persistedIndices = []; | ||
for (let key of this.virtualizer.persistedKeys){ | ||
let item = this.virtualizer.collection.getItem(key); | ||
if ((item === null || item === void 0 ? void 0 : item.index) != null) persistedIndices.push(item.index); | ||
let horizontalSpacing = Math.floor((visibleWidth - numColumns * itemWidth) / (numColumns + 1)); | ||
this.gap = new (0, $7Tzdl$reactstatelyvirtualizer.Size)(horizontalSpacing, minSpace.height); | ||
let rows = Math.ceil(this.virtualizer.collection.size / numColumns); | ||
let iterator = this.virtualizer.collection[Symbol.iterator](); | ||
let y = rows > 0 ? minSpace.height : 0; | ||
let newLayoutInfos = new Map(); | ||
let skeleton = null; | ||
let skeletonCount = 0; | ||
for(let row = 0; row < rows; row++){ | ||
let maxHeight = 0; | ||
let rowLayoutInfos = []; | ||
for(let col = 0; col < numColumns; col++){ | ||
// Repeat skeleton until the end of the current row. | ||
let node = skeleton || iterator.next().value; | ||
if (!node) break; | ||
if (node.type === 'skeleton') skeleton = node; | ||
let key = skeleton ? `${skeleton.key}-${skeletonCount++}` : node.key; | ||
let oldLayoutInfo = this.layoutInfos.get(key); | ||
let content = node; | ||
if (skeleton) content = oldLayoutInfo && oldLayoutInfo.content.key === key ? oldLayoutInfo.content : { | ||
...skeleton, | ||
key: key | ||
}; | ||
let x = horizontalSpacing + col * (itemWidth + horizontalSpacing); | ||
let height = itemHeight; | ||
let estimatedSize = !preserveAspectRatio; | ||
if (oldLayoutInfo && estimatedSize) { | ||
height = oldLayoutInfo.rect.height; | ||
estimatedSize = invalidationContext.layoutOptionsChanged || invalidationContext.sizeChanged || oldLayoutInfo.estimatedSize || oldLayoutInfo.content !== content; | ||
} | ||
let rect = new (0, $7Tzdl$reactstatelyvirtualizer.Rect)(x, y, itemWidth, height); | ||
let layoutInfo = new (0, $7Tzdl$reactstatelyvirtualizer.LayoutInfo)(node.type, key, rect); | ||
layoutInfo.estimatedSize = estimatedSize; | ||
layoutInfo.allowOverflow = true; | ||
layoutInfo.content = content; | ||
newLayoutInfos.set(key, layoutInfo); | ||
rowLayoutInfos.push(layoutInfo); | ||
maxHeight = Math.max(maxHeight, layoutInfo.rect.height); | ||
} | ||
for (let layoutInfo of rowLayoutInfos)layoutInfo.rect.height = maxHeight; | ||
y += maxHeight + minSpace.height; | ||
// Keep adding skeleton rows until we fill the viewport | ||
if (skeleton && row === rows - 1 && y < this.virtualizer.visibleRect.height) rows++; | ||
} | ||
persistedIndices.sort((a, b)=>a - b); | ||
let persistedBefore = []; | ||
for (let index of persistedIndices){ | ||
if (index < firstVisibleItem) persistedBefore.push(this.layoutInfos[index]); | ||
else if (index > lastVisibleItem) result.push(this.layoutInfos[index]); | ||
} | ||
result.unshift(...persistedBefore); | ||
return result; | ||
this.layoutInfos = newLayoutInfos; | ||
this.contentSize = new (0, $7Tzdl$reactstatelyvirtualizer.Size)(this.virtualizer.visibleRect.width, y); | ||
} | ||
getIndexAtPoint(x, y) { | ||
let itemHeight = this.itemSize.height + this.minSpace.height; | ||
let itemWidth = this.itemSize.width + this.horizontalSpacing; | ||
return Math.max(0, Math.min(this.virtualizer.collection.size - 1, Math.floor(y / itemHeight) * this.numColumns + Math.floor((x - this.horizontalSpacing) / itemWidth))); | ||
} | ||
getLayoutInfo(key) { | ||
let node = this.virtualizer.collection.getItem(key); | ||
return node ? this.layoutInfos[node.index] : null; | ||
return this.layoutInfos.get(key); | ||
} | ||
getLayoutInfoForNode(node) { | ||
let idx = node.index; | ||
let row = Math.floor(idx / this.numColumns); | ||
let column = idx % this.numColumns; | ||
let x = this.horizontalSpacing + column * (this.itemSize.width + this.horizontalSpacing); | ||
let y = this.minSpace.height + row * (this.itemSize.height + this.minSpace.height); | ||
let rect = new (0, $7Tzdl$reactstatelyvirtualizer.Rect)(x, y, this.itemSize.width, this.itemSize.height); | ||
return new (0, $7Tzdl$reactstatelyvirtualizer.LayoutInfo)(node.type, node.key, rect); | ||
} | ||
getContentSize() { | ||
let numRows = Math.ceil(this.virtualizer.collection.size / this.numColumns); | ||
let contentHeight = this.minSpace.height + numRows * (this.itemSize.height + this.minSpace.height); | ||
return new (0, $7Tzdl$reactstatelyvirtualizer.Size)(this.virtualizer.visibleRect.width, contentHeight); | ||
return this.contentSize; | ||
} | ||
getVisibleLayoutInfos(rect) { | ||
let layoutInfos = []; | ||
for (let layoutInfo of this.layoutInfos.values())if (layoutInfo.rect.intersects(rect) || this.virtualizer.isPersistedKey(layoutInfo.key)) layoutInfos.push(layoutInfo); | ||
return layoutInfos; | ||
} | ||
updateItemSize(key, size) { | ||
let layoutInfo = this.layoutInfos.get(key); | ||
if (!size || !layoutInfo) return false; | ||
if (size.height !== layoutInfo.rect.height) { | ||
let newLayoutInfo = layoutInfo.copy(); | ||
newLayoutInfo.rect.height = size.height; | ||
newLayoutInfo.estimatedSize = false; | ||
this.layoutInfos.set(key, newLayoutInfo); | ||
return true; | ||
} | ||
return false; | ||
} | ||
getDropTargetFromPoint(x, y, isValidDropTarget) { | ||
if (this.layoutInfos.length === 0) return { | ||
if (this.layoutInfos.size === 0) return { | ||
type: 'root' | ||
@@ -93,4 +130,7 @@ }; | ||
y += this.virtualizer.visibleRect.y; | ||
let index = this.getIndexAtPoint(x, y); | ||
let layoutInfo = this.layoutInfos[index]; | ||
let key = this.virtualizer.keyAtPoint(new (0, $7Tzdl$reactstatelyvirtualizer.Point)(x, y)); | ||
let layoutInfo = key != null ? this.getLayoutInfo(key) : null; | ||
if (!layoutInfo) return { | ||
type: 'root' | ||
}; | ||
let target = { | ||
@@ -127,13 +167,8 @@ type: 'item', | ||
if (this.numColumns === 1) // Flip from vertical to horizontal if only one column is visible. | ||
rect = new (0, $7Tzdl$reactstatelyvirtualizer.Rect)(layoutInfo.rect.x, target.dropPosition === 'before' ? layoutInfo.rect.y - this.minSpace.height / 2 - this.dropIndicatorThickness / 2 : layoutInfo.rect.maxY + this.minSpace.height / 2 - this.dropIndicatorThickness / 2, layoutInfo.rect.width, this.dropIndicatorThickness); | ||
else rect = new (0, $7Tzdl$reactstatelyvirtualizer.Rect)(target.dropPosition === 'before' ? layoutInfo.rect.x - this.horizontalSpacing / 2 - this.dropIndicatorThickness / 2 : layoutInfo.rect.maxX + this.horizontalSpacing / 2 - this.dropIndicatorThickness / 2, layoutInfo.rect.y, this.dropIndicatorThickness, layoutInfo.rect.height); | ||
rect = new (0, $7Tzdl$reactstatelyvirtualizer.Rect)(layoutInfo.rect.x, target.dropPosition === 'before' ? layoutInfo.rect.y - this.gap.height / 2 - this.dropIndicatorThickness / 2 : layoutInfo.rect.maxY + this.gap.height / 2 - this.dropIndicatorThickness / 2, layoutInfo.rect.width, this.dropIndicatorThickness); | ||
else rect = new (0, $7Tzdl$reactstatelyvirtualizer.Rect)(target.dropPosition === 'before' ? layoutInfo.rect.x - this.gap.width / 2 - this.dropIndicatorThickness / 2 : layoutInfo.rect.maxX + this.gap.width / 2 - this.dropIndicatorThickness / 2, layoutInfo.rect.y, this.dropIndicatorThickness, layoutInfo.rect.height); | ||
return new (0, $7Tzdl$reactstatelyvirtualizer.LayoutInfo)('dropIndicator', target.key + ':' + target.dropPosition, rect); | ||
} | ||
constructor(options){ | ||
super(), this.itemSize = new (0, $7Tzdl$reactstatelyvirtualizer.Size)(), this.numColumns = 0, this.horizontalSpacing = 0, this.layoutInfos = []; | ||
this.minItemSize = options.minItemSize || new (0, $7Tzdl$reactstatelyvirtualizer.Size)(200, 200); | ||
this.maxItemSize = options.maxItemSize || new (0, $7Tzdl$reactstatelyvirtualizer.Size)(Infinity, Infinity); | ||
this.minSpace = options.minSpace || new (0, $7Tzdl$reactstatelyvirtualizer.Size)(18, 18); | ||
this.maxColumns = options.maxColumns || Infinity; | ||
this.dropIndicatorThickness = options.dropIndicatorThickness || 2; | ||
constructor(...args){ | ||
super(...args), this.gap = $1f7773ceb2a3b9a6$var$DEFAULT_OPTIONS.minSpace, this.dropIndicatorThickness = 2, this.numColumns = 0, this.contentSize = new (0, $7Tzdl$reactstatelyvirtualizer.Size)(), this.layoutInfos = new Map(); | ||
} | ||
@@ -140,0 +175,0 @@ } |
@@ -1,2 +0,2 @@ | ||
import {Size as $ipgKF$Size, Rect as $ipgKF$Rect, LayoutInfo as $ipgKF$LayoutInfo, Layout as $ipgKF$Layout} from "@react-stately/virtualizer"; | ||
import {Size as $ipgKF$Size, Rect as $ipgKF$Rect, LayoutInfo as $ipgKF$LayoutInfo, Point as $ipgKF$Point, Layout as $ipgKF$Layout} from "@react-stately/virtualizer"; | ||
@@ -14,70 +14,107 @@ /* | ||
*/ | ||
const $a58592d295a170a4$var$DEFAULT_OPTIONS = { | ||
minItemSize: new (0, $ipgKF$Size)(200, 200), | ||
maxItemSize: new (0, $ipgKF$Size)(Infinity, Infinity), | ||
preserveAspectRatio: false, | ||
minSpace: new (0, $ipgKF$Size)(18, 18), | ||
maxColumns: Infinity, | ||
dropIndicatorThickness: 2 | ||
}; | ||
class $a58592d295a170a4$export$7d2b12578154a735 extends (0, $ipgKF$Layout) { | ||
update() { | ||
shouldInvalidateLayoutOptions(newOptions, oldOptions) { | ||
return newOptions.maxColumns !== oldOptions.maxColumns || newOptions.dropIndicatorThickness !== oldOptions.dropIndicatorThickness || newOptions.preserveAspectRatio !== oldOptions.preserveAspectRatio || !(newOptions.minItemSize || $a58592d295a170a4$var$DEFAULT_OPTIONS.minItemSize).equals(oldOptions.minItemSize || $a58592d295a170a4$var$DEFAULT_OPTIONS.minItemSize) || !(newOptions.maxItemSize || $a58592d295a170a4$var$DEFAULT_OPTIONS.maxItemSize).equals(oldOptions.maxItemSize || $a58592d295a170a4$var$DEFAULT_OPTIONS.maxItemSize) || !(newOptions.minSpace || $a58592d295a170a4$var$DEFAULT_OPTIONS.minSpace).equals(oldOptions.minSpace || $a58592d295a170a4$var$DEFAULT_OPTIONS.minSpace); | ||
} | ||
update(invalidationContext) { | ||
let { minItemSize: minItemSize = $a58592d295a170a4$var$DEFAULT_OPTIONS.minItemSize, maxItemSize: maxItemSize = $a58592d295a170a4$var$DEFAULT_OPTIONS.maxItemSize, preserveAspectRatio: preserveAspectRatio = $a58592d295a170a4$var$DEFAULT_OPTIONS.preserveAspectRatio, minSpace: minSpace = $a58592d295a170a4$var$DEFAULT_OPTIONS.minSpace, maxColumns: maxColumns = $a58592d295a170a4$var$DEFAULT_OPTIONS.maxColumns, dropIndicatorThickness: dropIndicatorThickness = $a58592d295a170a4$var$DEFAULT_OPTIONS.dropIndicatorThickness } = invalidationContext.layoutOptions || {}; | ||
this.dropIndicatorThickness = dropIndicatorThickness; | ||
let visibleWidth = this.virtualizer.visibleRect.width; | ||
// The max item width is always the entire viewport. | ||
// If the max item height is infinity, scale in proportion to the max width. | ||
let maxItemWidth = Math.min(this.maxItemSize.width, visibleWidth); | ||
let maxItemHeight = Number.isFinite(this.maxItemSize.height) ? this.maxItemSize.height : Math.floor(this.minItemSize.height / this.minItemSize.width * maxItemWidth); | ||
let maxItemWidth = Math.min(maxItemSize.width, visibleWidth); | ||
let maxItemHeight = Number.isFinite(maxItemSize.height) ? maxItemSize.height : Math.floor(minItemSize.height / minItemSize.width * maxItemWidth); | ||
// Compute the number of rows and columns needed to display the content | ||
let columns = Math.floor(visibleWidth / (this.minItemSize.width + this.minSpace.width)); | ||
this.numColumns = Math.max(1, Math.min(this.maxColumns, columns)); | ||
let columns = Math.floor(visibleWidth / (minItemSize.width + minSpace.width)); | ||
let numColumns = Math.max(1, Math.min(maxColumns, columns)); | ||
// Compute the available width (minus the space between items) | ||
let width = visibleWidth - this.minSpace.width * Math.max(0, this.numColumns); | ||
let width = visibleWidth - minSpace.width * Math.max(0, numColumns); | ||
// Compute the item width based on the space available | ||
let itemWidth = Math.floor(width / this.numColumns); | ||
itemWidth = Math.max(this.minItemSize.width, Math.min(maxItemWidth, itemWidth)); | ||
let itemWidth = Math.floor(width / numColumns); | ||
itemWidth = Math.max(minItemSize.width, Math.min(maxItemWidth, itemWidth)); | ||
// Compute the item height, which is proportional to the item width | ||
let t = (itemWidth - this.minItemSize.width) / Math.max(1, maxItemWidth - this.minItemSize.width); | ||
let itemHeight = this.minItemSize.height + Math.floor((maxItemHeight - this.minItemSize.height) * t); | ||
itemHeight = Math.max(this.minItemSize.height, Math.min(maxItemHeight, itemHeight)); | ||
this.itemSize = new (0, $ipgKF$Size)(itemWidth, itemHeight); | ||
let t = (itemWidth - minItemSize.width) / Math.max(1, maxItemWidth - minItemSize.width); | ||
let itemHeight = minItemSize.height + Math.floor((maxItemHeight - minItemSize.height) * t); | ||
itemHeight = Math.max(minItemSize.height, Math.min(maxItemHeight, itemHeight)); | ||
// Compute the horizontal spacing and content height | ||
this.horizontalSpacing = Math.floor((visibleWidth - this.numColumns * this.itemSize.width) / (this.numColumns + 1)); | ||
this.layoutInfos = []; | ||
for (let node of this.virtualizer.collection)this.layoutInfos.push(this.getLayoutInfoForNode(node)); | ||
} | ||
getVisibleLayoutInfos(rect) { | ||
let firstVisibleItem = this.getIndexAtPoint(rect.x, rect.y); | ||
let lastVisibleItem = this.getIndexAtPoint(rect.maxX, rect.maxY); | ||
let result = this.layoutInfos.slice(firstVisibleItem, lastVisibleItem + 1); | ||
let persistedIndices = []; | ||
for (let key of this.virtualizer.persistedKeys){ | ||
let item = this.virtualizer.collection.getItem(key); | ||
if ((item === null || item === void 0 ? void 0 : item.index) != null) persistedIndices.push(item.index); | ||
let horizontalSpacing = Math.floor((visibleWidth - numColumns * itemWidth) / (numColumns + 1)); | ||
this.gap = new (0, $ipgKF$Size)(horizontalSpacing, minSpace.height); | ||
let rows = Math.ceil(this.virtualizer.collection.size / numColumns); | ||
let iterator = this.virtualizer.collection[Symbol.iterator](); | ||
let y = rows > 0 ? minSpace.height : 0; | ||
let newLayoutInfos = new Map(); | ||
let skeleton = null; | ||
let skeletonCount = 0; | ||
for(let row = 0; row < rows; row++){ | ||
let maxHeight = 0; | ||
let rowLayoutInfos = []; | ||
for(let col = 0; col < numColumns; col++){ | ||
// Repeat skeleton until the end of the current row. | ||
let node = skeleton || iterator.next().value; | ||
if (!node) break; | ||
if (node.type === 'skeleton') skeleton = node; | ||
let key = skeleton ? `${skeleton.key}-${skeletonCount++}` : node.key; | ||
let oldLayoutInfo = this.layoutInfos.get(key); | ||
let content = node; | ||
if (skeleton) content = oldLayoutInfo && oldLayoutInfo.content.key === key ? oldLayoutInfo.content : { | ||
...skeleton, | ||
key: key | ||
}; | ||
let x = horizontalSpacing + col * (itemWidth + horizontalSpacing); | ||
let height = itemHeight; | ||
let estimatedSize = !preserveAspectRatio; | ||
if (oldLayoutInfo && estimatedSize) { | ||
height = oldLayoutInfo.rect.height; | ||
estimatedSize = invalidationContext.layoutOptionsChanged || invalidationContext.sizeChanged || oldLayoutInfo.estimatedSize || oldLayoutInfo.content !== content; | ||
} | ||
let rect = new (0, $ipgKF$Rect)(x, y, itemWidth, height); | ||
let layoutInfo = new (0, $ipgKF$LayoutInfo)(node.type, key, rect); | ||
layoutInfo.estimatedSize = estimatedSize; | ||
layoutInfo.allowOverflow = true; | ||
layoutInfo.content = content; | ||
newLayoutInfos.set(key, layoutInfo); | ||
rowLayoutInfos.push(layoutInfo); | ||
maxHeight = Math.max(maxHeight, layoutInfo.rect.height); | ||
} | ||
for (let layoutInfo of rowLayoutInfos)layoutInfo.rect.height = maxHeight; | ||
y += maxHeight + minSpace.height; | ||
// Keep adding skeleton rows until we fill the viewport | ||
if (skeleton && row === rows - 1 && y < this.virtualizer.visibleRect.height) rows++; | ||
} | ||
persistedIndices.sort((a, b)=>a - b); | ||
let persistedBefore = []; | ||
for (let index of persistedIndices){ | ||
if (index < firstVisibleItem) persistedBefore.push(this.layoutInfos[index]); | ||
else if (index > lastVisibleItem) result.push(this.layoutInfos[index]); | ||
} | ||
result.unshift(...persistedBefore); | ||
return result; | ||
this.layoutInfos = newLayoutInfos; | ||
this.contentSize = new (0, $ipgKF$Size)(this.virtualizer.visibleRect.width, y); | ||
} | ||
getIndexAtPoint(x, y) { | ||
let itemHeight = this.itemSize.height + this.minSpace.height; | ||
let itemWidth = this.itemSize.width + this.horizontalSpacing; | ||
return Math.max(0, Math.min(this.virtualizer.collection.size - 1, Math.floor(y / itemHeight) * this.numColumns + Math.floor((x - this.horizontalSpacing) / itemWidth))); | ||
} | ||
getLayoutInfo(key) { | ||
let node = this.virtualizer.collection.getItem(key); | ||
return node ? this.layoutInfos[node.index] : null; | ||
return this.layoutInfos.get(key); | ||
} | ||
getLayoutInfoForNode(node) { | ||
let idx = node.index; | ||
let row = Math.floor(idx / this.numColumns); | ||
let column = idx % this.numColumns; | ||
let x = this.horizontalSpacing + column * (this.itemSize.width + this.horizontalSpacing); | ||
let y = this.minSpace.height + row * (this.itemSize.height + this.minSpace.height); | ||
let rect = new (0, $ipgKF$Rect)(x, y, this.itemSize.width, this.itemSize.height); | ||
return new (0, $ipgKF$LayoutInfo)(node.type, node.key, rect); | ||
} | ||
getContentSize() { | ||
let numRows = Math.ceil(this.virtualizer.collection.size / this.numColumns); | ||
let contentHeight = this.minSpace.height + numRows * (this.itemSize.height + this.minSpace.height); | ||
return new (0, $ipgKF$Size)(this.virtualizer.visibleRect.width, contentHeight); | ||
return this.contentSize; | ||
} | ||
getVisibleLayoutInfos(rect) { | ||
let layoutInfos = []; | ||
for (let layoutInfo of this.layoutInfos.values())if (layoutInfo.rect.intersects(rect) || this.virtualizer.isPersistedKey(layoutInfo.key)) layoutInfos.push(layoutInfo); | ||
return layoutInfos; | ||
} | ||
updateItemSize(key, size) { | ||
let layoutInfo = this.layoutInfos.get(key); | ||
if (!size || !layoutInfo) return false; | ||
if (size.height !== layoutInfo.rect.height) { | ||
let newLayoutInfo = layoutInfo.copy(); | ||
newLayoutInfo.rect.height = size.height; | ||
newLayoutInfo.estimatedSize = false; | ||
this.layoutInfos.set(key, newLayoutInfo); | ||
return true; | ||
} | ||
return false; | ||
} | ||
getDropTargetFromPoint(x, y, isValidDropTarget) { | ||
if (this.layoutInfos.length === 0) return { | ||
if (this.layoutInfos.size === 0) return { | ||
type: 'root' | ||
@@ -87,4 +124,7 @@ }; | ||
y += this.virtualizer.visibleRect.y; | ||
let index = this.getIndexAtPoint(x, y); | ||
let layoutInfo = this.layoutInfos[index]; | ||
let key = this.virtualizer.keyAtPoint(new (0, $ipgKF$Point)(x, y)); | ||
let layoutInfo = key != null ? this.getLayoutInfo(key) : null; | ||
if (!layoutInfo) return { | ||
type: 'root' | ||
}; | ||
let target = { | ||
@@ -121,13 +161,8 @@ type: 'item', | ||
if (this.numColumns === 1) // Flip from vertical to horizontal if only one column is visible. | ||
rect = new (0, $ipgKF$Rect)(layoutInfo.rect.x, target.dropPosition === 'before' ? layoutInfo.rect.y - this.minSpace.height / 2 - this.dropIndicatorThickness / 2 : layoutInfo.rect.maxY + this.minSpace.height / 2 - this.dropIndicatorThickness / 2, layoutInfo.rect.width, this.dropIndicatorThickness); | ||
else rect = new (0, $ipgKF$Rect)(target.dropPosition === 'before' ? layoutInfo.rect.x - this.horizontalSpacing / 2 - this.dropIndicatorThickness / 2 : layoutInfo.rect.maxX + this.horizontalSpacing / 2 - this.dropIndicatorThickness / 2, layoutInfo.rect.y, this.dropIndicatorThickness, layoutInfo.rect.height); | ||
rect = new (0, $ipgKF$Rect)(layoutInfo.rect.x, target.dropPosition === 'before' ? layoutInfo.rect.y - this.gap.height / 2 - this.dropIndicatorThickness / 2 : layoutInfo.rect.maxY + this.gap.height / 2 - this.dropIndicatorThickness / 2, layoutInfo.rect.width, this.dropIndicatorThickness); | ||
else rect = new (0, $ipgKF$Rect)(target.dropPosition === 'before' ? layoutInfo.rect.x - this.gap.width / 2 - this.dropIndicatorThickness / 2 : layoutInfo.rect.maxX + this.gap.width / 2 - this.dropIndicatorThickness / 2, layoutInfo.rect.y, this.dropIndicatorThickness, layoutInfo.rect.height); | ||
return new (0, $ipgKF$LayoutInfo)('dropIndicator', target.key + ':' + target.dropPosition, rect); | ||
} | ||
constructor(options){ | ||
super(), this.itemSize = new (0, $ipgKF$Size)(), this.numColumns = 0, this.horizontalSpacing = 0, this.layoutInfos = []; | ||
this.minItemSize = options.minItemSize || new (0, $ipgKF$Size)(200, 200); | ||
this.maxItemSize = options.maxItemSize || new (0, $ipgKF$Size)(Infinity, Infinity); | ||
this.minSpace = options.minSpace || new (0, $ipgKF$Size)(18, 18); | ||
this.maxColumns = options.maxColumns || Infinity; | ||
this.dropIndicatorThickness = options.dropIndicatorThickness || 2; | ||
constructor(...args){ | ||
super(...args), this.gap = $a58592d295a170a4$var$DEFAULT_OPTIONS.minSpace, this.dropIndicatorThickness = 2, this.numColumns = 0, this.contentSize = new (0, $ipgKF$Size)(), this.layoutInfos = new Map(); | ||
} | ||
@@ -134,0 +169,0 @@ } |
@@ -38,3 +38,3 @@ var $iId4j$reactstatelycollections = require("@react-stately/collections"); | ||
var _this_rowHeight, _ref; | ||
let rowHeight = (_ref = (_this_rowHeight = this.rowHeight) !== null && _this_rowHeight !== void 0 ? _this_rowHeight : this.estimatedRowHeight) !== null && _ref !== void 0 ? _ref : $fe69e47e38ed0ac4$var$DEFAULT_HEIGHT; | ||
let rowHeight = ((_ref = (_this_rowHeight = this.rowHeight) !== null && _this_rowHeight !== void 0 ? _this_rowHeight : this.estimatedRowHeight) !== null && _ref !== void 0 ? _ref : $fe69e47e38ed0ac4$var$DEFAULT_HEIGHT) + this.gap; | ||
rect.y = Math.floor(rect.y / rowHeight) * rowHeight; | ||
@@ -85,4 +85,10 @@ rect.height = Math.ceil(rect.height / rowHeight) * rowHeight; | ||
// In this case, we need to recalculate the entire layout. | ||
return invalidationContext.sizeChanged || false; | ||
// Also invalidate if fixed sizes/gaps change. | ||
let options = invalidationContext.layoutOptions; | ||
var _options_rowHeight, _options_headingHeight, _options_loaderHeight, _options_gap, _options_padding; | ||
return invalidationContext.sizeChanged || this.rowHeight !== ((_options_rowHeight = options === null || options === void 0 ? void 0 : options.rowHeight) !== null && _options_rowHeight !== void 0 ? _options_rowHeight : this.rowHeight) || this.headingHeight !== ((_options_headingHeight = options === null || options === void 0 ? void 0 : options.headingHeight) !== null && _options_headingHeight !== void 0 ? _options_headingHeight : this.headingHeight) || this.loaderHeight !== ((_options_loaderHeight = options === null || options === void 0 ? void 0 : options.loaderHeight) !== null && _options_loaderHeight !== void 0 ? _options_loaderHeight : this.loaderHeight) || this.gap !== ((_options_gap = options === null || options === void 0 ? void 0 : options.gap) !== null && _options_gap !== void 0 ? _options_gap : this.gap) || this.padding !== ((_options_padding = options === null || options === void 0 ? void 0 : options.padding) !== null && _options_padding !== void 0 ? _options_padding : this.padding); | ||
} | ||
shouldInvalidateLayoutOptions(newOptions, oldOptions) { | ||
return newOptions.rowHeight !== oldOptions.rowHeight || newOptions.estimatedRowHeight !== oldOptions.estimatedRowHeight || newOptions.headingHeight !== oldOptions.headingHeight || newOptions.estimatedHeadingHeight !== oldOptions.estimatedHeadingHeight || newOptions.loaderHeight !== oldOptions.loaderHeight || newOptions.dropIndicatorThickness !== oldOptions.dropIndicatorThickness || newOptions.gap !== oldOptions.gap || newOptions.padding !== oldOptions.padding; | ||
} | ||
update(invalidationContext) { | ||
@@ -97,2 +103,19 @@ let collection = this.virtualizer.collection; | ||
} | ||
let options = invalidationContext.layoutOptions; | ||
var _options_rowHeight; | ||
this.rowHeight = (_options_rowHeight = options === null || options === void 0 ? void 0 : options.rowHeight) !== null && _options_rowHeight !== void 0 ? _options_rowHeight : this.rowHeight; | ||
var _options_estimatedRowHeight; | ||
this.estimatedRowHeight = (_options_estimatedRowHeight = options === null || options === void 0 ? void 0 : options.estimatedRowHeight) !== null && _options_estimatedRowHeight !== void 0 ? _options_estimatedRowHeight : this.estimatedRowHeight; | ||
var _options_headingHeight; | ||
this.headingHeight = (_options_headingHeight = options === null || options === void 0 ? void 0 : options.headingHeight) !== null && _options_headingHeight !== void 0 ? _options_headingHeight : this.headingHeight; | ||
var _options_estimatedHeadingHeight; | ||
this.estimatedHeadingHeight = (_options_estimatedHeadingHeight = options === null || options === void 0 ? void 0 : options.estimatedHeadingHeight) !== null && _options_estimatedHeadingHeight !== void 0 ? _options_estimatedHeadingHeight : this.estimatedHeadingHeight; | ||
var _options_loaderHeight; | ||
this.loaderHeight = (_options_loaderHeight = options === null || options === void 0 ? void 0 : options.loaderHeight) !== null && _options_loaderHeight !== void 0 ? _options_loaderHeight : this.loaderHeight; | ||
var _options_dropIndicatorThickness; | ||
this.dropIndicatorThickness = (_options_dropIndicatorThickness = options === null || options === void 0 ? void 0 : options.dropIndicatorThickness) !== null && _options_dropIndicatorThickness !== void 0 ? _options_dropIndicatorThickness : this.dropIndicatorThickness; | ||
var _options_gap; | ||
this.gap = (_options_gap = options === null || options === void 0 ? void 0 : options.gap) !== null && _options_gap !== void 0 ? _options_gap : this.gap; | ||
var _options_padding; | ||
this.padding = (_options_padding = options === null || options === void 0 ? void 0 : options.padding) !== null && _options_padding !== void 0 ? _options_padding : this.padding; | ||
this.rootNodes = this.buildCollection(); | ||
@@ -106,3 +129,2 @@ // Remove deleted layout nodes | ||
} | ||
this.lastWidth = this.virtualizer.visibleRect.width; | ||
this.lastCollection = collection; | ||
@@ -112,3 +134,3 @@ this.invalidateEverything = false; | ||
} | ||
buildCollection(y = 0) { | ||
buildCollection(y = this.padding) { | ||
let collection = this.virtualizer.collection; | ||
@@ -119,3 +141,3 @@ let skipped = 0; | ||
var _this_rowHeight, _ref; | ||
let rowHeight = (_ref = (_this_rowHeight = this.rowHeight) !== null && _this_rowHeight !== void 0 ? _this_rowHeight : this.estimatedRowHeight) !== null && _ref !== void 0 ? _ref : $fe69e47e38ed0ac4$var$DEFAULT_HEIGHT; | ||
let rowHeight = ((_ref = (_this_rowHeight = this.rowHeight) !== null && _this_rowHeight !== void 0 ? _this_rowHeight : this.estimatedRowHeight) !== null && _ref !== void 0 ? _ref : $fe69e47e38ed0ac4$var$DEFAULT_HEIGHT) + this.gap; | ||
// Skip rows before the valid rectangle unless they are already cached. | ||
@@ -127,4 +149,4 @@ if (node.type === 'item' && y + rowHeight < this.requestedRect.y && !this.isValid(node, y)) { | ||
} | ||
let layoutNode = this.buildChild(node, 0, y, null); | ||
y = layoutNode.layoutInfo.rect.maxY; | ||
let layoutNode = this.buildChild(node, this.padding, y, null); | ||
y = layoutNode.layoutInfo.rect.maxY + this.gap; | ||
nodes.push(layoutNode); | ||
@@ -136,2 +158,4 @@ if (node.type === 'item' && y > this.requestedRect.maxY) { | ||
} | ||
y -= this.gap; | ||
y += this.padding; | ||
this.contentSize = new (0, $iId4j$reactstatelyvirtualizer.Size)(this.virtualizer.visibleRect.width, y); | ||
@@ -166,5 +190,5 @@ return nodes; | ||
buildLoader(node, x, y) { | ||
let rect = new (0, $iId4j$reactstatelyvirtualizer.Rect)(x, y, 0, 0); | ||
let rect = new (0, $iId4j$reactstatelyvirtualizer.Rect)(x, y, this.padding, 0); | ||
let layoutInfo = new (0, $iId4j$reactstatelyvirtualizer.LayoutInfo)('loader', node.key, rect); | ||
rect.width = this.virtualizer.contentSize.width; | ||
rect.width = this.virtualizer.contentSize.width - this.padding - x; | ||
rect.height = this.loaderHeight || this.rowHeight || this.estimatedRowHeight || $fe69e47e38ed0ac4$var$DEFAULT_HEIGHT; | ||
@@ -178,4 +202,4 @@ return { | ||
let collection = this.virtualizer.collection; | ||
let width = this.virtualizer.visibleRect.width; | ||
let rect = new (0, $iId4j$reactstatelyvirtualizer.Rect)(0, y, width, 0); | ||
let width = this.virtualizer.visibleRect.width - this.padding; | ||
let rect = new (0, $iId4j$reactstatelyvirtualizer.Rect)(x, y, width - x, 0); | ||
let layoutInfo = new (0, $iId4j$reactstatelyvirtualizer.LayoutInfo)(node.type, node.key, rect); | ||
@@ -187,3 +211,3 @@ let startY = y; | ||
var _this_rowHeight, _ref; | ||
let rowHeight = (_ref = (_this_rowHeight = this.rowHeight) !== null && _this_rowHeight !== void 0 ? _this_rowHeight : this.estimatedRowHeight) !== null && _ref !== void 0 ? _ref : $fe69e47e38ed0ac4$var$DEFAULT_HEIGHT; | ||
let rowHeight = ((_ref = (_this_rowHeight = this.rowHeight) !== null && _this_rowHeight !== void 0 ? _this_rowHeight : this.estimatedRowHeight) !== null && _ref !== void 0 ? _ref : $fe69e47e38ed0ac4$var$DEFAULT_HEIGHT) + this.gap; | ||
// Skip rows before the valid rectangle unless they are already cached. | ||
@@ -196,3 +220,3 @@ if (y + rowHeight < this.requestedRect.y && !this.isValid(node, y)) { | ||
let layoutNode = this.buildChild(child, x, y, layoutInfo.key); | ||
y = layoutNode.layoutInfo.rect.maxY; | ||
y = layoutNode.layoutInfo.rect.maxY + this.gap; | ||
children.push(layoutNode); | ||
@@ -207,2 +231,3 @@ if (y > this.requestedRect.maxY) { | ||
} | ||
y -= this.gap; | ||
rect.height = y - startY; | ||
@@ -217,3 +242,3 @@ return { | ||
buildSectionHeader(node, x, y) { | ||
let width = this.virtualizer.visibleRect.width; | ||
let width = this.virtualizer.visibleRect.width - this.padding; | ||
let rectHeight = this.headingHeight; | ||
@@ -232,3 +257,3 @@ let isEstimated = false; | ||
rectHeight = previousLayoutInfo.rect.height; | ||
isEstimated = width !== this.lastWidth || curNode !== lastNode || previousLayoutInfo.estimatedSize; | ||
isEstimated = width !== previousLayoutInfo.rect.width || curNode !== lastNode || previousLayoutInfo.estimatedSize; | ||
} else { | ||
@@ -240,3 +265,3 @@ rectHeight = node.rendered ? this.estimatedHeadingHeight : 0; | ||
if (rectHeight == null) rectHeight = $fe69e47e38ed0ac4$var$DEFAULT_HEIGHT; | ||
let headerRect = new (0, $iId4j$reactstatelyvirtualizer.Rect)(0, y, width, rectHeight); | ||
let headerRect = new (0, $iId4j$reactstatelyvirtualizer.Rect)(x, y, width - x, rectHeight); | ||
let header = new (0, $iId4j$reactstatelyvirtualizer.LayoutInfo)('header', node.key, headerRect); | ||
@@ -252,3 +277,3 @@ header.estimatedSize = isEstimated; | ||
buildItem(node, x, y) { | ||
let width = this.virtualizer.visibleRect.width; | ||
let width = this.virtualizer.visibleRect.width - this.padding - x; | ||
let rectHeight = this.rowHeight; | ||
@@ -264,3 +289,3 @@ let isEstimated = false; | ||
rectHeight = previousLayoutNode.layoutInfo.rect.height; | ||
isEstimated = width !== this.lastWidth || node !== previousLayoutNode.node || previousLayoutNode.layoutInfo.estimatedSize; | ||
isEstimated = width !== previousLayoutNode.layoutInfo.rect.width || node !== previousLayoutNode.node || previousLayoutNode.layoutInfo.estimatedSize; | ||
} else { | ||
@@ -272,3 +297,3 @@ rectHeight = this.estimatedRowHeight; | ||
if (rectHeight == null) rectHeight = $fe69e47e38ed0ac4$var$DEFAULT_HEIGHT; | ||
let rect = new (0, $iId4j$reactstatelyvirtualizer.Rect)(x, y, width - x, rectHeight); | ||
let rect = new (0, $iId4j$reactstatelyvirtualizer.Rect)(x, y, width, rectHeight); | ||
let layoutInfo = new (0, $iId4j$reactstatelyvirtualizer.LayoutInfo)(node.type, node.key, rect); | ||
@@ -384,5 +409,6 @@ layoutInfo.estimatedSize = isEstimated; | ||
this.dropIndicatorThickness = options.dropIndicatorThickness || 2; | ||
this.gap = options.gap || 0; | ||
this.padding = options.padding || 0; | ||
this.layoutNodes = new Map(); | ||
this.rootNodes = []; | ||
this.lastWidth = 0; | ||
this.lastCollection = null; | ||
@@ -389,0 +415,0 @@ this.invalidateEverything = false; |
@@ -32,3 +32,3 @@ import {getChildNodes as $img26$getChildNodes} from "@react-stately/collections"; | ||
var _this_rowHeight, _ref; | ||
let rowHeight = (_ref = (_this_rowHeight = this.rowHeight) !== null && _this_rowHeight !== void 0 ? _this_rowHeight : this.estimatedRowHeight) !== null && _ref !== void 0 ? _ref : $61ef60fc9b1041f4$var$DEFAULT_HEIGHT; | ||
let rowHeight = ((_ref = (_this_rowHeight = this.rowHeight) !== null && _this_rowHeight !== void 0 ? _this_rowHeight : this.estimatedRowHeight) !== null && _ref !== void 0 ? _ref : $61ef60fc9b1041f4$var$DEFAULT_HEIGHT) + this.gap; | ||
rect.y = Math.floor(rect.y / rowHeight) * rowHeight; | ||
@@ -79,4 +79,10 @@ rect.height = Math.ceil(rect.height / rowHeight) * rowHeight; | ||
// In this case, we need to recalculate the entire layout. | ||
return invalidationContext.sizeChanged || false; | ||
// Also invalidate if fixed sizes/gaps change. | ||
let options = invalidationContext.layoutOptions; | ||
var _options_rowHeight, _options_headingHeight, _options_loaderHeight, _options_gap, _options_padding; | ||
return invalidationContext.sizeChanged || this.rowHeight !== ((_options_rowHeight = options === null || options === void 0 ? void 0 : options.rowHeight) !== null && _options_rowHeight !== void 0 ? _options_rowHeight : this.rowHeight) || this.headingHeight !== ((_options_headingHeight = options === null || options === void 0 ? void 0 : options.headingHeight) !== null && _options_headingHeight !== void 0 ? _options_headingHeight : this.headingHeight) || this.loaderHeight !== ((_options_loaderHeight = options === null || options === void 0 ? void 0 : options.loaderHeight) !== null && _options_loaderHeight !== void 0 ? _options_loaderHeight : this.loaderHeight) || this.gap !== ((_options_gap = options === null || options === void 0 ? void 0 : options.gap) !== null && _options_gap !== void 0 ? _options_gap : this.gap) || this.padding !== ((_options_padding = options === null || options === void 0 ? void 0 : options.padding) !== null && _options_padding !== void 0 ? _options_padding : this.padding); | ||
} | ||
shouldInvalidateLayoutOptions(newOptions, oldOptions) { | ||
return newOptions.rowHeight !== oldOptions.rowHeight || newOptions.estimatedRowHeight !== oldOptions.estimatedRowHeight || newOptions.headingHeight !== oldOptions.headingHeight || newOptions.estimatedHeadingHeight !== oldOptions.estimatedHeadingHeight || newOptions.loaderHeight !== oldOptions.loaderHeight || newOptions.dropIndicatorThickness !== oldOptions.dropIndicatorThickness || newOptions.gap !== oldOptions.gap || newOptions.padding !== oldOptions.padding; | ||
} | ||
update(invalidationContext) { | ||
@@ -91,2 +97,19 @@ let collection = this.virtualizer.collection; | ||
} | ||
let options = invalidationContext.layoutOptions; | ||
var _options_rowHeight; | ||
this.rowHeight = (_options_rowHeight = options === null || options === void 0 ? void 0 : options.rowHeight) !== null && _options_rowHeight !== void 0 ? _options_rowHeight : this.rowHeight; | ||
var _options_estimatedRowHeight; | ||
this.estimatedRowHeight = (_options_estimatedRowHeight = options === null || options === void 0 ? void 0 : options.estimatedRowHeight) !== null && _options_estimatedRowHeight !== void 0 ? _options_estimatedRowHeight : this.estimatedRowHeight; | ||
var _options_headingHeight; | ||
this.headingHeight = (_options_headingHeight = options === null || options === void 0 ? void 0 : options.headingHeight) !== null && _options_headingHeight !== void 0 ? _options_headingHeight : this.headingHeight; | ||
var _options_estimatedHeadingHeight; | ||
this.estimatedHeadingHeight = (_options_estimatedHeadingHeight = options === null || options === void 0 ? void 0 : options.estimatedHeadingHeight) !== null && _options_estimatedHeadingHeight !== void 0 ? _options_estimatedHeadingHeight : this.estimatedHeadingHeight; | ||
var _options_loaderHeight; | ||
this.loaderHeight = (_options_loaderHeight = options === null || options === void 0 ? void 0 : options.loaderHeight) !== null && _options_loaderHeight !== void 0 ? _options_loaderHeight : this.loaderHeight; | ||
var _options_dropIndicatorThickness; | ||
this.dropIndicatorThickness = (_options_dropIndicatorThickness = options === null || options === void 0 ? void 0 : options.dropIndicatorThickness) !== null && _options_dropIndicatorThickness !== void 0 ? _options_dropIndicatorThickness : this.dropIndicatorThickness; | ||
var _options_gap; | ||
this.gap = (_options_gap = options === null || options === void 0 ? void 0 : options.gap) !== null && _options_gap !== void 0 ? _options_gap : this.gap; | ||
var _options_padding; | ||
this.padding = (_options_padding = options === null || options === void 0 ? void 0 : options.padding) !== null && _options_padding !== void 0 ? _options_padding : this.padding; | ||
this.rootNodes = this.buildCollection(); | ||
@@ -100,3 +123,2 @@ // Remove deleted layout nodes | ||
} | ||
this.lastWidth = this.virtualizer.visibleRect.width; | ||
this.lastCollection = collection; | ||
@@ -106,3 +128,3 @@ this.invalidateEverything = false; | ||
} | ||
buildCollection(y = 0) { | ||
buildCollection(y = this.padding) { | ||
let collection = this.virtualizer.collection; | ||
@@ -113,3 +135,3 @@ let skipped = 0; | ||
var _this_rowHeight, _ref; | ||
let rowHeight = (_ref = (_this_rowHeight = this.rowHeight) !== null && _this_rowHeight !== void 0 ? _this_rowHeight : this.estimatedRowHeight) !== null && _ref !== void 0 ? _ref : $61ef60fc9b1041f4$var$DEFAULT_HEIGHT; | ||
let rowHeight = ((_ref = (_this_rowHeight = this.rowHeight) !== null && _this_rowHeight !== void 0 ? _this_rowHeight : this.estimatedRowHeight) !== null && _ref !== void 0 ? _ref : $61ef60fc9b1041f4$var$DEFAULT_HEIGHT) + this.gap; | ||
// Skip rows before the valid rectangle unless they are already cached. | ||
@@ -121,4 +143,4 @@ if (node.type === 'item' && y + rowHeight < this.requestedRect.y && !this.isValid(node, y)) { | ||
} | ||
let layoutNode = this.buildChild(node, 0, y, null); | ||
y = layoutNode.layoutInfo.rect.maxY; | ||
let layoutNode = this.buildChild(node, this.padding, y, null); | ||
y = layoutNode.layoutInfo.rect.maxY + this.gap; | ||
nodes.push(layoutNode); | ||
@@ -130,2 +152,4 @@ if (node.type === 'item' && y > this.requestedRect.maxY) { | ||
} | ||
y -= this.gap; | ||
y += this.padding; | ||
this.contentSize = new (0, $img26$Size)(this.virtualizer.visibleRect.width, y); | ||
@@ -160,5 +184,5 @@ return nodes; | ||
buildLoader(node, x, y) { | ||
let rect = new (0, $img26$Rect)(x, y, 0, 0); | ||
let rect = new (0, $img26$Rect)(x, y, this.padding, 0); | ||
let layoutInfo = new (0, $img26$LayoutInfo)('loader', node.key, rect); | ||
rect.width = this.virtualizer.contentSize.width; | ||
rect.width = this.virtualizer.contentSize.width - this.padding - x; | ||
rect.height = this.loaderHeight || this.rowHeight || this.estimatedRowHeight || $61ef60fc9b1041f4$var$DEFAULT_HEIGHT; | ||
@@ -172,4 +196,4 @@ return { | ||
let collection = this.virtualizer.collection; | ||
let width = this.virtualizer.visibleRect.width; | ||
let rect = new (0, $img26$Rect)(0, y, width, 0); | ||
let width = this.virtualizer.visibleRect.width - this.padding; | ||
let rect = new (0, $img26$Rect)(x, y, width - x, 0); | ||
let layoutInfo = new (0, $img26$LayoutInfo)(node.type, node.key, rect); | ||
@@ -181,3 +205,3 @@ let startY = y; | ||
var _this_rowHeight, _ref; | ||
let rowHeight = (_ref = (_this_rowHeight = this.rowHeight) !== null && _this_rowHeight !== void 0 ? _this_rowHeight : this.estimatedRowHeight) !== null && _ref !== void 0 ? _ref : $61ef60fc9b1041f4$var$DEFAULT_HEIGHT; | ||
let rowHeight = ((_ref = (_this_rowHeight = this.rowHeight) !== null && _this_rowHeight !== void 0 ? _this_rowHeight : this.estimatedRowHeight) !== null && _ref !== void 0 ? _ref : $61ef60fc9b1041f4$var$DEFAULT_HEIGHT) + this.gap; | ||
// Skip rows before the valid rectangle unless they are already cached. | ||
@@ -190,3 +214,3 @@ if (y + rowHeight < this.requestedRect.y && !this.isValid(node, y)) { | ||
let layoutNode = this.buildChild(child, x, y, layoutInfo.key); | ||
y = layoutNode.layoutInfo.rect.maxY; | ||
y = layoutNode.layoutInfo.rect.maxY + this.gap; | ||
children.push(layoutNode); | ||
@@ -201,2 +225,3 @@ if (y > this.requestedRect.maxY) { | ||
} | ||
y -= this.gap; | ||
rect.height = y - startY; | ||
@@ -211,3 +236,3 @@ return { | ||
buildSectionHeader(node, x, y) { | ||
let width = this.virtualizer.visibleRect.width; | ||
let width = this.virtualizer.visibleRect.width - this.padding; | ||
let rectHeight = this.headingHeight; | ||
@@ -226,3 +251,3 @@ let isEstimated = false; | ||
rectHeight = previousLayoutInfo.rect.height; | ||
isEstimated = width !== this.lastWidth || curNode !== lastNode || previousLayoutInfo.estimatedSize; | ||
isEstimated = width !== previousLayoutInfo.rect.width || curNode !== lastNode || previousLayoutInfo.estimatedSize; | ||
} else { | ||
@@ -234,3 +259,3 @@ rectHeight = node.rendered ? this.estimatedHeadingHeight : 0; | ||
if (rectHeight == null) rectHeight = $61ef60fc9b1041f4$var$DEFAULT_HEIGHT; | ||
let headerRect = new (0, $img26$Rect)(0, y, width, rectHeight); | ||
let headerRect = new (0, $img26$Rect)(x, y, width - x, rectHeight); | ||
let header = new (0, $img26$LayoutInfo)('header', node.key, headerRect); | ||
@@ -246,3 +271,3 @@ header.estimatedSize = isEstimated; | ||
buildItem(node, x, y) { | ||
let width = this.virtualizer.visibleRect.width; | ||
let width = this.virtualizer.visibleRect.width - this.padding - x; | ||
let rectHeight = this.rowHeight; | ||
@@ -258,3 +283,3 @@ let isEstimated = false; | ||
rectHeight = previousLayoutNode.layoutInfo.rect.height; | ||
isEstimated = width !== this.lastWidth || node !== previousLayoutNode.node || previousLayoutNode.layoutInfo.estimatedSize; | ||
isEstimated = width !== previousLayoutNode.layoutInfo.rect.width || node !== previousLayoutNode.node || previousLayoutNode.layoutInfo.estimatedSize; | ||
} else { | ||
@@ -266,3 +291,3 @@ rectHeight = this.estimatedRowHeight; | ||
if (rectHeight == null) rectHeight = $61ef60fc9b1041f4$var$DEFAULT_HEIGHT; | ||
let rect = new (0, $img26$Rect)(x, y, width - x, rectHeight); | ||
let rect = new (0, $img26$Rect)(x, y, width, rectHeight); | ||
let layoutInfo = new (0, $img26$LayoutInfo)(node.type, node.key, rect); | ||
@@ -378,5 +403,6 @@ layoutInfo.estimatedSize = isEstimated; | ||
this.dropIndicatorThickness = options.dropIndicatorThickness || 2; | ||
this.gap = options.gap || 0; | ||
this.padding = options.padding || 0; | ||
this.layoutNodes = new Map(); | ||
this.rootNodes = []; | ||
this.lastWidth = 0; | ||
this.lastCollection = null; | ||
@@ -383,0 +409,0 @@ this.invalidateEverything = false; |
var $1f7773ceb2a3b9a6$exports = require("./GridLayout.main.js"); | ||
var $fe69e47e38ed0ac4$exports = require("./ListLayout.main.js"); | ||
var $67c493497dcda343$exports = require("./TableLayout.main.js"); | ||
var $f2cd6b90da09fa37$exports = require("./WaterfallLayout.main.js"); | ||
@@ -13,2 +14,3 @@ | ||
$parcel$export(module.exports, "TableLayout", () => $67c493497dcda343$exports.TableLayout); | ||
$parcel$export(module.exports, "WaterfallLayout", () => $f2cd6b90da09fa37$exports.WaterfallLayout); | ||
/* | ||
@@ -29,2 +31,3 @@ * Copyright 2020 Adobe. All rights reserved. | ||
//# sourceMappingURL=main.js.map |
import {GridLayout as $a58592d295a170a4$export$7d2b12578154a735} from "./GridLayout.module.js"; | ||
import {ListLayout as $61ef60fc9b1041f4$export$cacbb3924155d68e} from "./ListLayout.module.js"; | ||
import {TableLayout as $a152112e902709bf$export$62444c3c724b1b20} from "./TableLayout.module.js"; | ||
import {WaterfallLayout as $f483179558aa907f$export$e9f7cda058ba8df8} from "./WaterfallLayout.module.js"; | ||
@@ -20,3 +21,4 @@ /* | ||
export {$a58592d295a170a4$export$7d2b12578154a735 as GridLayout, $61ef60fc9b1041f4$export$cacbb3924155d68e as ListLayout, $a152112e902709bf$export$62444c3c724b1b20 as TableLayout}; | ||
export {$a58592d295a170a4$export$7d2b12578154a735 as GridLayout, $61ef60fc9b1041f4$export$cacbb3924155d68e as ListLayout, $a152112e902709bf$export$62444c3c724b1b20 as TableLayout, $f483179558aa907f$export$e9f7cda058ba8df8 as WaterfallLayout}; | ||
//# sourceMappingURL=module.js.map |
@@ -35,2 +35,5 @@ var $fe69e47e38ed0ac4$exports = require("./ListLayout.main.js"); | ||
} | ||
shouldInvalidateLayoutOptions(newOptions, oldOptions) { | ||
return newOptions.columnWidths !== oldOptions.columnWidths || super.shouldInvalidateLayoutOptions(newOptions, oldOptions); | ||
} | ||
update(invalidationContext) { | ||
@@ -48,3 +51,3 @@ var _invalidationContext_layoutOptions; | ||
let columnLayout = new (0, $9lycG$reactstatelytable.TableColumnLayout)({}); | ||
this.columnWidths = columnLayout.buildColumnWidths(this.virtualizer.visibleRect.width, newCollection, new Map()); | ||
this.columnWidths = columnLayout.buildColumnWidths(this.virtualizer.visibleRect.width - this.padding * 2, newCollection, new Map()); | ||
invalidationContext.sizeChanged = true; | ||
@@ -62,6 +65,6 @@ } | ||
this.layoutNodes.set(header.layoutInfo.key, header); | ||
let body = this.buildBody(header.layoutInfo.rect.height); | ||
let body = this.buildBody(header.layoutInfo.rect.maxY + this.gap); | ||
this.lastPersistedKeys = null; | ||
body.layoutInfo.rect.width = Math.max(header.layoutInfo.rect.width, body.layoutInfo.rect.width); | ||
this.contentSize = new (0, $9lycG$reactstatelyvirtualizer.Size)(body.layoutInfo.rect.width, body.layoutInfo.rect.maxY); | ||
this.contentSize = new (0, $9lycG$reactstatelyvirtualizer.Size)(body.layoutInfo.rect.width + this.padding * 2, body.layoutInfo.rect.maxY + this.padding); | ||
return [ | ||
@@ -75,3 +78,3 @@ header, | ||
let collection = this.virtualizer.collection; | ||
let rect = new (0, $9lycG$reactstatelyvirtualizer.Rect)(0, 0, 0, 0); | ||
let rect = new (0, $9lycG$reactstatelyvirtualizer.Rect)(this.padding, this.padding, 0, 0); | ||
var _collection_head_key; | ||
@@ -81,7 +84,7 @@ let layoutInfo = new (0, $9lycG$reactstatelyvirtualizer.LayoutInfo)('header', (_collection_head_key = (_collection_head = collection.head) === null || _collection_head === void 0 ? void 0 : _collection_head.key) !== null && _collection_head_key !== void 0 ? _collection_head_key : 'header', rect); | ||
layoutInfo.zIndex = 1; | ||
let y = 0; | ||
let y = this.padding; | ||
let width = 0; | ||
let children = []; | ||
for (let headerRow of collection.headerRows){ | ||
let layoutNode = this.buildChild(headerRow, 0, y, layoutInfo.key); | ||
let layoutNode = this.buildChild(headerRow, this.padding, y, layoutInfo.key); | ||
layoutNode.layoutInfo.parentKey = layoutInfo.key; | ||
@@ -94,3 +97,3 @@ y = layoutNode.layoutInfo.rect.maxY; | ||
rect.width = width; | ||
rect.height = y; | ||
rect.height = y - this.padding; | ||
return { | ||
@@ -104,3 +107,3 @@ layoutInfo: layoutInfo, | ||
buildHeaderRow(headerRow, x, y) { | ||
let rect = new (0, $9lycG$reactstatelyvirtualizer.Rect)(0, y, 0, 0); | ||
let rect = new (0, $9lycG$reactstatelyvirtualizer.Rect)(x, y, 0, 0); | ||
let row = new (0, $9lycG$reactstatelyvirtualizer.LayoutInfo)('headerrow', headerRow.key, rect); | ||
@@ -120,3 +123,3 @@ let height = 0; | ||
rect.height = height; | ||
rect.width = x; | ||
rect.width = x - rect.x; | ||
return { | ||
@@ -139,8 +142,8 @@ layoutInfo: row, | ||
let collection = this.virtualizer.collection; | ||
var _node_colspan; | ||
let colspan = (_node_colspan = node.colspan) !== null && _node_colspan !== void 0 ? _node_colspan : 1; | ||
var _node_colSpan; | ||
let colSpan = (_node_colSpan = node.colSpan) !== null && _node_colSpan !== void 0 ? _node_colSpan : 1; | ||
var _node_colIndex; | ||
let colIndex = (_node_colIndex = node.colIndex) !== null && _node_colIndex !== void 0 ? _node_colIndex : node.index; | ||
let width = 0; | ||
for(let i = colIndex; i < colIndex + colspan; i++){ | ||
for(let i = colIndex; i < colIndex + colSpan; i++){ | ||
let column = collection.columns[i]; | ||
@@ -200,3 +203,3 @@ var _this_columnWidths_get; | ||
let collection = this.virtualizer.collection; | ||
let rect = new (0, $9lycG$reactstatelyvirtualizer.Rect)(0, y, 0, 0); | ||
let rect = new (0, $9lycG$reactstatelyvirtualizer.Rect)(this.padding, y, 0, 0); | ||
let layoutInfo = new (0, $9lycG$reactstatelyvirtualizer.LayoutInfo)('rowgroup', collection.body.key, rect); | ||
@@ -207,3 +210,3 @@ let startY = y; | ||
let children = []; | ||
let rowHeight = this.getEstimatedRowHeight(); | ||
let rowHeight = this.getEstimatedRowHeight() + this.gap; | ||
for (let node of (0, $9lycG$reactstatelycollections.getChildNodes)(collection.body, collection)){ | ||
@@ -216,6 +219,6 @@ // Skip rows before the valid rectangle unless they are already cached. | ||
} | ||
let layoutNode = this.buildChild(node, 0, y, layoutInfo.key); | ||
let layoutNode = this.buildChild(node, this.padding, y, layoutInfo.key); | ||
layoutNode.layoutInfo.parentKey = layoutInfo.key; | ||
layoutNode.index = children.length; | ||
y = layoutNode.layoutInfo.rect.maxY; | ||
y = layoutNode.layoutInfo.rect.maxY + this.gap; | ||
width = Math.max(width, layoutNode.layoutInfo.rect.width); | ||
@@ -230,2 +233,3 @@ children.push(layoutNode); | ||
if (children.length === 0) y = this.virtualizer.visibleRect.maxY; | ||
else y -= this.gap; | ||
rect.width = width; | ||
@@ -232,0 +236,0 @@ rect.height = y - startY; |
@@ -29,2 +29,5 @@ import {ListLayout as $61ef60fc9b1041f4$export$cacbb3924155d68e} from "./ListLayout.module.js"; | ||
} | ||
shouldInvalidateLayoutOptions(newOptions, oldOptions) { | ||
return newOptions.columnWidths !== oldOptions.columnWidths || super.shouldInvalidateLayoutOptions(newOptions, oldOptions); | ||
} | ||
update(invalidationContext) { | ||
@@ -42,3 +45,3 @@ var _invalidationContext_layoutOptions; | ||
let columnLayout = new (0, $bmsJv$TableColumnLayout)({}); | ||
this.columnWidths = columnLayout.buildColumnWidths(this.virtualizer.visibleRect.width, newCollection, new Map()); | ||
this.columnWidths = columnLayout.buildColumnWidths(this.virtualizer.visibleRect.width - this.padding * 2, newCollection, new Map()); | ||
invalidationContext.sizeChanged = true; | ||
@@ -56,6 +59,6 @@ } | ||
this.layoutNodes.set(header.layoutInfo.key, header); | ||
let body = this.buildBody(header.layoutInfo.rect.height); | ||
let body = this.buildBody(header.layoutInfo.rect.maxY + this.gap); | ||
this.lastPersistedKeys = null; | ||
body.layoutInfo.rect.width = Math.max(header.layoutInfo.rect.width, body.layoutInfo.rect.width); | ||
this.contentSize = new (0, $bmsJv$Size)(body.layoutInfo.rect.width, body.layoutInfo.rect.maxY); | ||
this.contentSize = new (0, $bmsJv$Size)(body.layoutInfo.rect.width + this.padding * 2, body.layoutInfo.rect.maxY + this.padding); | ||
return [ | ||
@@ -69,3 +72,3 @@ header, | ||
let collection = this.virtualizer.collection; | ||
let rect = new (0, $bmsJv$Rect)(0, 0, 0, 0); | ||
let rect = new (0, $bmsJv$Rect)(this.padding, this.padding, 0, 0); | ||
var _collection_head_key; | ||
@@ -75,7 +78,7 @@ let layoutInfo = new (0, $bmsJv$LayoutInfo)('header', (_collection_head_key = (_collection_head = collection.head) === null || _collection_head === void 0 ? void 0 : _collection_head.key) !== null && _collection_head_key !== void 0 ? _collection_head_key : 'header', rect); | ||
layoutInfo.zIndex = 1; | ||
let y = 0; | ||
let y = this.padding; | ||
let width = 0; | ||
let children = []; | ||
for (let headerRow of collection.headerRows){ | ||
let layoutNode = this.buildChild(headerRow, 0, y, layoutInfo.key); | ||
let layoutNode = this.buildChild(headerRow, this.padding, y, layoutInfo.key); | ||
layoutNode.layoutInfo.parentKey = layoutInfo.key; | ||
@@ -88,3 +91,3 @@ y = layoutNode.layoutInfo.rect.maxY; | ||
rect.width = width; | ||
rect.height = y; | ||
rect.height = y - this.padding; | ||
return { | ||
@@ -98,3 +101,3 @@ layoutInfo: layoutInfo, | ||
buildHeaderRow(headerRow, x, y) { | ||
let rect = new (0, $bmsJv$Rect)(0, y, 0, 0); | ||
let rect = new (0, $bmsJv$Rect)(x, y, 0, 0); | ||
let row = new (0, $bmsJv$LayoutInfo)('headerrow', headerRow.key, rect); | ||
@@ -114,3 +117,3 @@ let height = 0; | ||
rect.height = height; | ||
rect.width = x; | ||
rect.width = x - rect.x; | ||
return { | ||
@@ -133,8 +136,8 @@ layoutInfo: row, | ||
let collection = this.virtualizer.collection; | ||
var _node_colspan; | ||
let colspan = (_node_colspan = node.colspan) !== null && _node_colspan !== void 0 ? _node_colspan : 1; | ||
var _node_colSpan; | ||
let colSpan = (_node_colSpan = node.colSpan) !== null && _node_colSpan !== void 0 ? _node_colSpan : 1; | ||
var _node_colIndex; | ||
let colIndex = (_node_colIndex = node.colIndex) !== null && _node_colIndex !== void 0 ? _node_colIndex : node.index; | ||
let width = 0; | ||
for(let i = colIndex; i < colIndex + colspan; i++){ | ||
for(let i = colIndex; i < colIndex + colSpan; i++){ | ||
let column = collection.columns[i]; | ||
@@ -194,3 +197,3 @@ var _this_columnWidths_get; | ||
let collection = this.virtualizer.collection; | ||
let rect = new (0, $bmsJv$Rect)(0, y, 0, 0); | ||
let rect = new (0, $bmsJv$Rect)(this.padding, y, 0, 0); | ||
let layoutInfo = new (0, $bmsJv$LayoutInfo)('rowgroup', collection.body.key, rect); | ||
@@ -201,3 +204,3 @@ let startY = y; | ||
let children = []; | ||
let rowHeight = this.getEstimatedRowHeight(); | ||
let rowHeight = this.getEstimatedRowHeight() + this.gap; | ||
for (let node of (0, $bmsJv$getChildNodes)(collection.body, collection)){ | ||
@@ -210,6 +213,6 @@ // Skip rows before the valid rectangle unless they are already cached. | ||
} | ||
let layoutNode = this.buildChild(node, 0, y, layoutInfo.key); | ||
let layoutNode = this.buildChild(node, this.padding, y, layoutInfo.key); | ||
layoutNode.layoutInfo.parentKey = layoutInfo.key; | ||
layoutNode.index = children.length; | ||
y = layoutNode.layoutInfo.rect.maxY; | ||
y = layoutNode.layoutInfo.rect.maxY + this.gap; | ||
width = Math.max(width, layoutNode.layoutInfo.rect.width); | ||
@@ -224,2 +227,3 @@ children.push(layoutNode); | ||
if (children.length === 0) y = this.virtualizer.visibleRect.maxY; | ||
else y -= this.gap; | ||
rect.width = width; | ||
@@ -226,0 +230,0 @@ rect.height = y - startY; |
@@ -1,3 +0,3 @@ | ||
import { DropTarget, DropTargetDelegate, ItemDropTarget, Key, Node, Collection } from "@react-types/shared"; | ||
import { Layout, LayoutInfo, Rect, Size, InvalidationContext } from "@react-stately/virtualizer"; | ||
import { DropTarget, DropTargetDelegate, ItemDropTarget, Key, Node, Collection, LayoutDelegate } from "@react-types/shared"; | ||
import { InvalidationContext, Layout, LayoutInfo, Rect, Size } from "@react-stately/virtualizer"; | ||
import { GridNode } from "@react-types/grid"; | ||
@@ -17,2 +17,9 @@ import { TableCollection } from "@react-types/table"; | ||
/** | ||
* Whether to preserve the aspect ratio of the `minItemSize`. | ||
* By default, grid rows may have variable heights. When `preserveAspectRatio` | ||
* is true, all rows will have equal heights. | ||
* @default false | ||
*/ | ||
preserveAspectRatio?: boolean; | ||
/** | ||
* The minimum space required between items. | ||
@@ -33,19 +40,18 @@ * @default 18 x 18 | ||
} | ||
export class GridLayout<T, O = any> extends Layout<Node<T>, O> implements DropTargetDelegate { | ||
protected minItemSize: Size; | ||
protected maxItemSize: Size; | ||
protected minSpace: Size; | ||
protected maxColumns: number; | ||
/** | ||
* GridLayout is a virtualizer Layout implementation | ||
* that arranges its items in a grid. | ||
* The items are sized between a minimum and maximum size | ||
* depending on the width of the container. | ||
*/ | ||
export class GridLayout<T, O extends GridLayoutOptions = GridLayoutOptions> extends Layout<Node<T>, O> implements DropTargetDelegate { | ||
protected gap: Size; | ||
protected dropIndicatorThickness: number; | ||
protected itemSize: Size; | ||
protected numColumns: number; | ||
protected horizontalSpacing: number; | ||
protected layoutInfos: LayoutInfo[]; | ||
constructor(options: GridLayoutOptions); | ||
update(): void; | ||
shouldInvalidateLayoutOptions(newOptions: O, oldOptions: O): boolean; | ||
update(invalidationContext: InvalidationContext<O>): void; | ||
getLayoutInfo(key: Key): LayoutInfo; | ||
getContentSize(): Size; | ||
getVisibleLayoutInfos(rect: Rect): LayoutInfo[]; | ||
protected getIndexAtPoint(x: number, y: number): number; | ||
getLayoutInfo(key: Key): LayoutInfo | null; | ||
protected getLayoutInfoForNode(node: Node<T>): LayoutInfo; | ||
getContentSize(): Size; | ||
updateItemSize(key: Key, size: Size): boolean; | ||
getDropTargetFromPoint(x: number, y: number, isValidDropTarget: (target: DropTarget) => boolean): DropTarget; | ||
@@ -55,16 +61,37 @@ getDropTargetLayoutInfo(target: ItemDropTarget): LayoutInfo; | ||
export interface ListLayoutOptions { | ||
/** The fixed height of a row in px. */ | ||
/** | ||
* The fixed height of a row in px. | ||
* @default 48 | ||
*/ | ||
rowHeight?: number; | ||
/** The estimated height of a row, when row heights are variable. */ | ||
estimatedRowHeight?: number; | ||
/** The fixed height of a section header in px. */ | ||
/** | ||
* The fixed height of a section header in px. | ||
* @default 48 | ||
*/ | ||
headingHeight?: number; | ||
/** The estimated height of a section header, when the height is variable. */ | ||
estimatedHeadingHeight?: number; | ||
/** The fixed height of a loader element in px. This loader is specifically for | ||
/** | ||
* The fixed height of a loader element in px. This loader is specifically for | ||
* "load more" elements rendered when loading more rows at the root level or inside nested row/sections. | ||
* @default 48 | ||
*/ | ||
loaderHeight?: number; | ||
/** The thickness of the drop indicator. */ | ||
/** | ||
* The thickness of the drop indicator. | ||
* @default 2 | ||
*/ | ||
dropIndicatorThickness?: number; | ||
/** | ||
* The gap between items. | ||
* @default 0 | ||
*/ | ||
gap?: number; | ||
/** | ||
* The padding around the list. | ||
* @default 0 | ||
*/ | ||
padding?: number; | ||
} | ||
@@ -79,10 +106,7 @@ export interface LayoutNode { | ||
/** | ||
* The ListLayout class is an implementation of a virtualizer {@link Layout}. | ||
* To configure a ListLayout, you can use the properties to define the | ||
* layouts and/or use the method for defining indentation. | ||
* The {@link ListKeyboardDelegate} extends the existing virtualizer | ||
* delegate with an additional method to do this (it uses the same delegate object as | ||
* the virtualizer itself). | ||
* ListLayout is a virtualizer Layout implementation | ||
* that arranges its items in a vertical stack. It supports both fixed | ||
* and variable height items. | ||
*/ | ||
export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTargetDelegate { | ||
export class ListLayout<T, O extends ListLayoutOptions = ListLayoutOptions> extends Layout<Node<T>, O> implements DropTargetDelegate { | ||
protected rowHeight: number | null; | ||
@@ -94,2 +118,4 @@ protected estimatedRowHeight: number | null; | ||
protected dropIndicatorThickness: number; | ||
protected gap: number; | ||
protected padding: number; | ||
protected layoutNodes: Map<Key, LayoutNode>; | ||
@@ -114,2 +140,3 @@ protected contentSize: Size; | ||
protected shouldInvalidateEverything(invalidationContext: InvalidationContext<O>): boolean; | ||
shouldInvalidateLayoutOptions(newOptions: O, oldOptions: O): boolean; | ||
update(invalidationContext: InvalidationContext<O>): void; | ||
@@ -129,9 +156,14 @@ protected buildCollection(y?: number): LayoutNode[]; | ||
} | ||
export interface TableLayoutProps { | ||
export interface TableLayoutProps extends ListLayoutOptions { | ||
columnWidths?: Map<Key, number>; | ||
} | ||
/** | ||
* TableLayout is a virtualizer Layout implementation that arranges | ||
* items in rows and columns. | ||
*/ | ||
export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> extends ListLayout<T, O> { | ||
protected lastCollection: TableCollection<T> | null; | ||
constructor(options: ListLayoutOptions); | ||
constructor(options?: ListLayoutOptions); | ||
protected get collection(): TableCollection<T>; | ||
shouldInvalidateLayoutOptions(newOptions: O, oldOptions: O): boolean; | ||
update(invalidationContext: InvalidationContext<O>): void; | ||
@@ -152,3 +184,44 @@ protected buildCollection(): LayoutNode[]; | ||
} | ||
export interface WaterfallLayoutOptions { | ||
/** | ||
* The minimum item size. | ||
* @default 200 x 200 | ||
*/ | ||
minItemSize?: Size; | ||
/** | ||
* The maximum item size. | ||
* @default Infinity | ||
*/ | ||
maxItemSize?: Size; | ||
/** | ||
* The minimum space required between items. | ||
* @default 18 x 18 | ||
*/ | ||
minSpace?: Size; | ||
/** | ||
* The maximum number of columns. | ||
* @default Infinity | ||
*/ | ||
maxColumns?: number; | ||
/** | ||
* The thickness of the drop indicator. | ||
* @default 2 | ||
*/ | ||
dropIndicatorThickness?: number; | ||
} | ||
export class WaterfallLayout<T extends object, O extends WaterfallLayoutOptions = WaterfallLayoutOptions> extends Layout<Node<T>, O> implements LayoutDelegate, DropTargetDelegate { | ||
protected numColumns: number; | ||
protected dropIndicatorThickness: number; | ||
shouldInvalidateLayoutOptions(newOptions: O, oldOptions: O): boolean; | ||
update(invalidationContext: InvalidationContext<O>): void; | ||
getLayoutInfo(key: Key): LayoutInfo; | ||
getContentSize(): Size; | ||
getVisibleLayoutInfos(rect: Rect): LayoutInfo[]; | ||
updateItemSize(key: Key, size: Size): boolean; | ||
getKeyRightOf(key: Key): Key | null; | ||
getKeyLeftOf(key: Key): Key | null; | ||
getKeyRange(from: Key, to: Key): Key[]; | ||
getDropTargetFromPoint(x: number, y: number): DropTarget; | ||
} | ||
//# sourceMappingURL=types.d.ts.map |
{ | ||
"name": "@react-stately/layout", | ||
"version": "3.0.0-nightly-f90799b33-241206", | ||
"version": "3.0.0-nightly-f90f4899f-250227", | ||
"description": "Spectrum UI components in React", | ||
@@ -25,17 +25,17 @@ "license": "Apache-2.0", | ||
"dependencies": { | ||
"@react-stately/collections": "^3.0.0-nightly-f90799b33-241206", | ||
"@react-stately/table": "^3.0.0-nightly-f90799b33-241206", | ||
"@react-stately/virtualizer": "^3.0.0-nightly-f90799b33-241206", | ||
"@react-types/grid": "^3.0.0-nightly-f90799b33-241206", | ||
"@react-types/shared": "^3.0.0-nightly-f90799b33-241206", | ||
"@react-types/table": "^3.0.0-nightly-f90799b33-241206", | ||
"@react-stately/collections": "3.0.0-nightly-f90f4899f-250227", | ||
"@react-stately/table": "3.0.0-nightly-f90f4899f-250227", | ||
"@react-stately/virtualizer": "3.0.0-nightly-f90f4899f-250227", | ||
"@react-types/grid": "3.0.0-nightly-f90f4899f-250227", | ||
"@react-types/shared": "3.0.0-nightly-f90f4899f-250227", | ||
"@react-types/table": "3.0.0-nightly-f90f4899f-250227", | ||
"@swc/helpers": "^0.5.0" | ||
}, | ||
"peerDependencies": { | ||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" | ||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", | ||
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"stableVersion": "4.1.0" | ||
} | ||
} |
@@ -14,3 +14,3 @@ /* | ||
import {DropTarget, DropTargetDelegate, ItemDropTarget, Key, Node} from '@react-types/shared'; | ||
import {Layout, LayoutInfo, Rect, Size} from '@react-stately/virtualizer'; | ||
import {InvalidationContext, Layout, LayoutInfo, Point, Rect, Size} from '@react-stately/virtualizer'; | ||
@@ -29,2 +29,9 @@ export interface GridLayoutOptions { | ||
/** | ||
* Whether to preserve the aspect ratio of the `minItemSize`. | ||
* By default, grid rows may have variable heights. When `preserveAspectRatio` | ||
* is true, all rows will have equal heights. | ||
* @default false | ||
*/ | ||
preserveAspectRatio?: boolean, | ||
/** | ||
* The minimum space required between items. | ||
@@ -46,23 +53,44 @@ * @default 18 x 18 | ||
export class GridLayout<T, O = any> extends Layout<Node<T>, O> implements DropTargetDelegate { | ||
protected minItemSize: Size; | ||
protected maxItemSize: Size; | ||
protected minSpace: Size; | ||
protected maxColumns: number; | ||
protected dropIndicatorThickness: number; | ||
protected itemSize: Size = new Size(); | ||
const DEFAULT_OPTIONS = { | ||
minItemSize: new Size(200, 200), | ||
maxItemSize: new Size(Infinity, Infinity), | ||
preserveAspectRatio: false, | ||
minSpace: new Size(18, 18), | ||
maxColumns: Infinity, | ||
dropIndicatorThickness: 2 | ||
}; | ||
/** | ||
* GridLayout is a virtualizer Layout implementation | ||
* that arranges its items in a grid. | ||
* The items are sized between a minimum and maximum size | ||
* depending on the width of the container. | ||
*/ | ||
export class GridLayout<T, O extends GridLayoutOptions = GridLayoutOptions> extends Layout<Node<T>, O> implements DropTargetDelegate { | ||
protected gap: Size = DEFAULT_OPTIONS.minSpace; | ||
protected dropIndicatorThickness = 2; | ||
protected numColumns: number = 0; | ||
protected horizontalSpacing: number = 0; | ||
protected layoutInfos: LayoutInfo[] = []; | ||
private contentSize: Size = new Size(); | ||
private layoutInfos: Map<Key, LayoutInfo> = new Map(); | ||
constructor(options: GridLayoutOptions) { | ||
super(); | ||
this.minItemSize = options.minItemSize || new Size(200, 200); | ||
this.maxItemSize = options.maxItemSize || new Size(Infinity, Infinity); | ||
this.minSpace = options.minSpace || new Size(18, 18); | ||
this.maxColumns = options.maxColumns || Infinity; | ||
this.dropIndicatorThickness = options.dropIndicatorThickness || 2; | ||
shouldInvalidateLayoutOptions(newOptions: O, oldOptions: O): boolean { | ||
return newOptions.maxColumns !== oldOptions.maxColumns | ||
|| newOptions.dropIndicatorThickness !== oldOptions.dropIndicatorThickness | ||
|| newOptions.preserveAspectRatio !== oldOptions.preserveAspectRatio | ||
|| (!(newOptions.minItemSize || DEFAULT_OPTIONS.minItemSize).equals(oldOptions.minItemSize || DEFAULT_OPTIONS.minItemSize)) | ||
|| (!(newOptions.maxItemSize || DEFAULT_OPTIONS.maxItemSize).equals(oldOptions.maxItemSize || DEFAULT_OPTIONS.maxItemSize)) | ||
|| (!(newOptions.minSpace || DEFAULT_OPTIONS.minSpace).equals(oldOptions.minSpace || DEFAULT_OPTIONS.minSpace)); | ||
} | ||
update(): void { | ||
update(invalidationContext: InvalidationContext<O>): void { | ||
let { | ||
minItemSize = DEFAULT_OPTIONS.minItemSize, | ||
maxItemSize = DEFAULT_OPTIONS.maxItemSize, | ||
preserveAspectRatio = DEFAULT_OPTIONS.preserveAspectRatio, | ||
minSpace = DEFAULT_OPTIONS.minSpace, | ||
maxColumns = DEFAULT_OPTIONS.maxColumns, | ||
dropIndicatorThickness = DEFAULT_OPTIONS.dropIndicatorThickness | ||
} = invalidationContext.layoutOptions || {}; | ||
this.dropIndicatorThickness = dropIndicatorThickness; | ||
let visibleWidth = this.virtualizer!.visibleRect.width; | ||
@@ -72,92 +100,125 @@ | ||
// If the max item height is infinity, scale in proportion to the max width. | ||
let maxItemWidth = Math.min(this.maxItemSize.width, visibleWidth); | ||
let maxItemHeight = Number.isFinite(this.maxItemSize.height) | ||
? this.maxItemSize.height | ||
: Math.floor((this.minItemSize.height / this.minItemSize.width) * maxItemWidth); | ||
let maxItemWidth = Math.min(maxItemSize.width, visibleWidth); | ||
let maxItemHeight = Number.isFinite(maxItemSize.height) | ||
? maxItemSize.height | ||
: Math.floor((minItemSize.height / minItemSize.width) * maxItemWidth); | ||
// Compute the number of rows and columns needed to display the content | ||
let columns = Math.floor(visibleWidth / (this.minItemSize.width + this.minSpace.width)); | ||
this.numColumns = Math.max(1, Math.min(this.maxColumns, columns)); | ||
let columns = Math.floor(visibleWidth / (minItemSize.width + minSpace.width)); | ||
let numColumns = Math.max(1, Math.min(maxColumns, columns)); | ||
// Compute the available width (minus the space between items) | ||
let width = visibleWidth - (this.minSpace.width * Math.max(0, this.numColumns)); | ||
let width = visibleWidth - (minSpace.width * Math.max(0, numColumns)); | ||
// Compute the item width based on the space available | ||
let itemWidth = Math.floor(width / this.numColumns); | ||
itemWidth = Math.max(this.minItemSize.width, Math.min(maxItemWidth, itemWidth)); | ||
let itemWidth = Math.floor(width / numColumns); | ||
itemWidth = Math.max(minItemSize.width, Math.min(maxItemWidth, itemWidth)); | ||
// Compute the item height, which is proportional to the item width | ||
let t = ((itemWidth - this.minItemSize.width) / Math.max(1, maxItemWidth - this.minItemSize.width)); | ||
let itemHeight = this.minItemSize.height + Math.floor((maxItemHeight - this.minItemSize.height) * t); | ||
itemHeight = Math.max(this.minItemSize.height, Math.min(maxItemHeight, itemHeight)); | ||
this.itemSize = new Size(itemWidth, itemHeight); | ||
let t = ((itemWidth - minItemSize.width) / Math.max(1, maxItemWidth - minItemSize.width)); | ||
let itemHeight = minItemSize.height + Math.floor((maxItemHeight - minItemSize.height) * t); | ||
itemHeight = Math.max(minItemSize.height, Math.min(maxItemHeight, itemHeight)); | ||
// Compute the horizontal spacing and content height | ||
this.horizontalSpacing = Math.floor((visibleWidth - this.numColumns * this.itemSize.width) / (this.numColumns + 1)); | ||
let horizontalSpacing = Math.floor((visibleWidth - numColumns * itemWidth) / (numColumns + 1)); | ||
this.gap = new Size(horizontalSpacing, minSpace.height); | ||
this.layoutInfos = []; | ||
for (let node of this.virtualizer!.collection) { | ||
this.layoutInfos.push(this.getLayoutInfoForNode(node)); | ||
} | ||
} | ||
let rows = Math.ceil(this.virtualizer!.collection.size / numColumns); | ||
let iterator = this.virtualizer!.collection[Symbol.iterator](); | ||
let y = rows > 0 ? minSpace.height : 0; | ||
let newLayoutInfos = new Map(); | ||
let skeleton: Node<T> | null = null; | ||
let skeletonCount = 0; | ||
for (let row = 0; row < rows; row++) { | ||
let maxHeight = 0; | ||
let rowLayoutInfos: LayoutInfo[] = []; | ||
for (let col = 0; col < numColumns; col++) { | ||
// Repeat skeleton until the end of the current row. | ||
let node = skeleton || iterator.next().value; | ||
if (!node) { | ||
break; | ||
} | ||
getVisibleLayoutInfos(rect: Rect): LayoutInfo[] { | ||
let firstVisibleItem = this.getIndexAtPoint(rect.x, rect.y); | ||
let lastVisibleItem = this.getIndexAtPoint(rect.maxX, rect.maxY); | ||
let result = this.layoutInfos.slice(firstVisibleItem, lastVisibleItem + 1); | ||
let persistedIndices: number[] = []; | ||
for (let key of this.virtualizer!.persistedKeys) { | ||
let item = this.virtualizer!.collection.getItem(key); | ||
if (item?.index != null) { | ||
persistedIndices.push(item.index); | ||
if (node.type === 'skeleton') { | ||
skeleton = node; | ||
} | ||
let key = skeleton ? `${skeleton.key}-${skeletonCount++}` : node.key; | ||
let oldLayoutInfo = this.layoutInfos.get(key); | ||
let content = node; | ||
if (skeleton) { | ||
content = oldLayoutInfo && oldLayoutInfo.content.key === key ? oldLayoutInfo.content : {...skeleton, key}; | ||
} | ||
let x = horizontalSpacing + col * (itemWidth + horizontalSpacing); | ||
let height = itemHeight; | ||
let estimatedSize = !preserveAspectRatio; | ||
if (oldLayoutInfo && estimatedSize) { | ||
height = oldLayoutInfo.rect.height; | ||
estimatedSize = invalidationContext.layoutOptionsChanged || invalidationContext.sizeChanged || oldLayoutInfo.estimatedSize || (oldLayoutInfo.content !== content); | ||
} | ||
let rect = new Rect(x, y, itemWidth, height); | ||
let layoutInfo = new LayoutInfo(node.type, key, rect); | ||
layoutInfo.estimatedSize = estimatedSize; | ||
layoutInfo.allowOverflow = true; | ||
layoutInfo.content = content; | ||
newLayoutInfos.set(key, layoutInfo); | ||
rowLayoutInfos.push(layoutInfo); | ||
maxHeight = Math.max(maxHeight, layoutInfo.rect.height); | ||
} | ||
} | ||
persistedIndices.sort((a, b) => a - b); | ||
let persistedBefore: LayoutInfo[] = []; | ||
for (let index of persistedIndices) { | ||
if (index < firstVisibleItem) { | ||
persistedBefore.push(this.layoutInfos[index]); | ||
} else if (index > lastVisibleItem) { | ||
result.push(this.layoutInfos[index]); | ||
for (let layoutInfo of rowLayoutInfos) { | ||
layoutInfo.rect.height = maxHeight; | ||
} | ||
y += maxHeight + minSpace.height; | ||
// Keep adding skeleton rows until we fill the viewport | ||
if (skeleton && row === rows - 1 && y < this.virtualizer!.visibleRect.height) { | ||
rows++; | ||
} | ||
} | ||
result.unshift(...persistedBefore); | ||
return result; | ||
this.layoutInfos = newLayoutInfos; | ||
this.contentSize = new Size(this.virtualizer!.visibleRect.width, y); | ||
} | ||
protected getIndexAtPoint(x: number, y: number) { | ||
let itemHeight = this.itemSize.height + this.minSpace.height; | ||
let itemWidth = this.itemSize.width + this.horizontalSpacing; | ||
return Math.max(0, | ||
Math.min( | ||
this.virtualizer!.collection.size - 1, | ||
Math.floor(y / itemHeight) * this.numColumns + Math.floor((x - this.horizontalSpacing) / itemWidth) | ||
) | ||
); | ||
getLayoutInfo(key: Key): LayoutInfo { | ||
return this.layoutInfos.get(key)!; | ||
} | ||
getLayoutInfo(key: Key): LayoutInfo | null { | ||
let node = this.virtualizer!.collection.getItem(key); | ||
return node ? this.layoutInfos[node.index] : null; | ||
getContentSize(): Size { | ||
return this.contentSize; | ||
} | ||
protected getLayoutInfoForNode(node: Node<T>): LayoutInfo { | ||
let idx = node.index; | ||
let row = Math.floor(idx / this.numColumns); | ||
let column = idx % this.numColumns; | ||
let x = this.horizontalSpacing + column * (this.itemSize.width + this.horizontalSpacing); | ||
let y = this.minSpace.height + row * (this.itemSize.height + this.minSpace.height); | ||
let rect = new Rect(x, y, this.itemSize.width, this.itemSize.height); | ||
return new LayoutInfo(node.type, node.key, rect); | ||
getVisibleLayoutInfos(rect: Rect): LayoutInfo[] { | ||
let layoutInfos: LayoutInfo[] = []; | ||
for (let layoutInfo of this.layoutInfos.values()) { | ||
if (layoutInfo.rect.intersects(rect) || this.virtualizer!.isPersistedKey(layoutInfo.key)) { | ||
layoutInfos.push(layoutInfo); | ||
} | ||
} | ||
return layoutInfos; | ||
} | ||
getContentSize(): Size { | ||
let numRows = Math.ceil(this.virtualizer!.collection.size / this.numColumns); | ||
let contentHeight = this.minSpace.height + numRows * (this.itemSize.height + this.minSpace.height); | ||
return new Size(this.virtualizer!.visibleRect.width, contentHeight); | ||
updateItemSize(key: Key, size: Size) { | ||
let layoutInfo = this.layoutInfos.get(key); | ||
if (!size || !layoutInfo) { | ||
return false; | ||
} | ||
if (size.height !== layoutInfo.rect.height) { | ||
let newLayoutInfo = layoutInfo.copy(); | ||
newLayoutInfo.rect.height = size.height; | ||
newLayoutInfo.estimatedSize = false; | ||
this.layoutInfos.set(key, newLayoutInfo); | ||
return true; | ||
} | ||
return false; | ||
} | ||
getDropTargetFromPoint(x: number, y: number, isValidDropTarget: (target: DropTarget) => boolean): DropTarget { | ||
if (this.layoutInfos.length === 0) { | ||
if (this.layoutInfos.size === 0) { | ||
return {type: 'root'}; | ||
@@ -168,5 +229,9 @@ } | ||
y += this.virtualizer!.visibleRect.y; | ||
let index = this.getIndexAtPoint(x, y); | ||
let layoutInfo = this.layoutInfos[index]; | ||
let key = this.virtualizer!.keyAtPoint(new Point(x, y)); | ||
let layoutInfo = key != null ? this.getLayoutInfo(key) : null; | ||
if (!layoutInfo) { | ||
return {type: 'root'}; | ||
} | ||
let target: DropTarget = { | ||
@@ -210,4 +275,4 @@ type: 'item', | ||
target.dropPosition === 'before' | ||
? layoutInfo.rect.y - this.minSpace.height / 2 - this.dropIndicatorThickness / 2 | ||
: layoutInfo.rect.maxY + this.minSpace.height / 2 - this.dropIndicatorThickness / 2, | ||
? layoutInfo.rect.y - this.gap.height / 2 - this.dropIndicatorThickness / 2 | ||
: layoutInfo.rect.maxY + this.gap.height / 2 - this.dropIndicatorThickness / 2, | ||
layoutInfo.rect.width, | ||
@@ -219,4 +284,4 @@ this.dropIndicatorThickness | ||
target.dropPosition === 'before' | ||
? layoutInfo.rect.x - this.horizontalSpacing / 2 - this.dropIndicatorThickness / 2 | ||
: layoutInfo.rect.maxX + this.horizontalSpacing / 2 - this.dropIndicatorThickness / 2, | ||
? layoutInfo.rect.x - this.gap.width / 2 - this.dropIndicatorThickness / 2 | ||
: layoutInfo.rect.maxX + this.gap.width / 2 - this.dropIndicatorThickness / 2, | ||
layoutInfo.rect.y, | ||
@@ -223,0 +288,0 @@ this.dropIndicatorThickness, |
@@ -15,4 +15,6 @@ /* | ||
export type {TableLayoutProps} from './TableLayout'; | ||
export type {WaterfallLayoutOptions} from './WaterfallLayout'; | ||
export {GridLayout} from './GridLayout'; | ||
export {ListLayout} from './ListLayout'; | ||
export {TableLayout} from './TableLayout'; | ||
export {WaterfallLayout} from './WaterfallLayout'; |
@@ -18,16 +18,37 @@ /* | ||
export interface ListLayoutOptions { | ||
/** The fixed height of a row in px. */ | ||
/** | ||
* The fixed height of a row in px. | ||
* @default 48 | ||
*/ | ||
rowHeight?: number, | ||
/** The estimated height of a row, when row heights are variable. */ | ||
estimatedRowHeight?: number, | ||
/** The fixed height of a section header in px. */ | ||
/** | ||
* The fixed height of a section header in px. | ||
* @default 48 | ||
*/ | ||
headingHeight?: number, | ||
/** The estimated height of a section header, when the height is variable. */ | ||
estimatedHeadingHeight?: number, | ||
/** The fixed height of a loader element in px. This loader is specifically for | ||
/** | ||
* The fixed height of a loader element in px. This loader is specifically for | ||
* "load more" elements rendered when loading more rows at the root level or inside nested row/sections. | ||
* @default 48 | ||
*/ | ||
loaderHeight?: number, | ||
/** The thickness of the drop indicator. */ | ||
dropIndicatorThickness?: number | ||
/** | ||
* The thickness of the drop indicator. | ||
* @default 2 | ||
*/ | ||
dropIndicatorThickness?: number, | ||
/** | ||
* The gap between items. | ||
* @default 0 | ||
*/ | ||
gap?: number, | ||
/** | ||
* The padding around the list. | ||
* @default 0 | ||
*/ | ||
padding?: number | ||
} | ||
@@ -47,10 +68,7 @@ | ||
/** | ||
* The ListLayout class is an implementation of a virtualizer {@link Layout}. | ||
* To configure a ListLayout, you can use the properties to define the | ||
* layouts and/or use the method for defining indentation. | ||
* The {@link ListKeyboardDelegate} extends the existing virtualizer | ||
* delegate with an additional method to do this (it uses the same delegate object as | ||
* the virtualizer itself). | ||
* ListLayout is a virtualizer Layout implementation | ||
* that arranges its items in a vertical stack. It supports both fixed | ||
* and variable height items. | ||
*/ | ||
export class ListLayout<T, O = any> extends Layout<Node<T>, O> implements DropTargetDelegate { | ||
export class ListLayout<T, O extends ListLayoutOptions = ListLayoutOptions> extends Layout<Node<T>, O> implements DropTargetDelegate { | ||
protected rowHeight: number | null; | ||
@@ -62,6 +80,7 @@ protected estimatedRowHeight: number | null; | ||
protected dropIndicatorThickness: number; | ||
protected gap: number; | ||
protected padding: number; | ||
protected layoutNodes: Map<Key, LayoutNode>; | ||
protected contentSize: Size; | ||
protected lastCollection: Collection<Node<T>> | null; | ||
private lastWidth: number; | ||
protected rootNodes: LayoutNode[]; | ||
@@ -86,5 +105,6 @@ private invalidateEverything: boolean; | ||
this.dropIndicatorThickness = options.dropIndicatorThickness || 2; | ||
this.gap = options.gap || 0; | ||
this.padding = options.padding || 0; | ||
this.layoutNodes = new Map(); | ||
this.rootNodes = []; | ||
this.lastWidth = 0; | ||
this.lastCollection = null; | ||
@@ -111,3 +131,3 @@ this.invalidateEverything = false; | ||
if (rect.height > 1) { | ||
let rowHeight = this.rowHeight ?? this.estimatedRowHeight ?? DEFAULT_HEIGHT; | ||
let rowHeight = (this.rowHeight ?? this.estimatedRowHeight ?? DEFAULT_HEIGHT) + this.gap; | ||
rect.y = Math.floor(rect.y / rowHeight) * rowHeight; | ||
@@ -175,8 +195,26 @@ rect.height = Math.ceil(rect.height / rowHeight) * rowHeight; | ||
protected shouldInvalidateEverything(invalidationContext: InvalidationContext<O>) { | ||
protected shouldInvalidateEverything(invalidationContext: InvalidationContext<O>): boolean { | ||
// Invalidate cache if the size of the collection changed. | ||
// In this case, we need to recalculate the entire layout. | ||
return invalidationContext.sizeChanged || false; | ||
// Also invalidate if fixed sizes/gaps change. | ||
let options = invalidationContext.layoutOptions; | ||
return invalidationContext.sizeChanged | ||
|| this.rowHeight !== (options?.rowHeight ?? this.rowHeight) | ||
|| this.headingHeight !== (options?.headingHeight ?? this.headingHeight) | ||
|| this.loaderHeight !== (options?.loaderHeight ?? this.loaderHeight) | ||
|| this.gap !== (options?.gap ?? this.gap) | ||
|| this.padding !== (options?.padding ?? this.padding); | ||
} | ||
shouldInvalidateLayoutOptions(newOptions: O, oldOptions: O): boolean { | ||
return newOptions.rowHeight !== oldOptions.rowHeight | ||
|| newOptions.estimatedRowHeight !== oldOptions.estimatedRowHeight | ||
|| newOptions.headingHeight !== oldOptions.headingHeight | ||
|| newOptions.estimatedHeadingHeight !== oldOptions.estimatedHeadingHeight | ||
|| newOptions.loaderHeight !== oldOptions.loaderHeight | ||
|| newOptions.dropIndicatorThickness !== oldOptions.dropIndicatorThickness | ||
|| newOptions.gap !== oldOptions.gap | ||
|| newOptions.padding !== oldOptions.padding; | ||
} | ||
update(invalidationContext: InvalidationContext<O>) { | ||
@@ -193,2 +231,12 @@ let collection = this.virtualizer!.collection; | ||
let options = invalidationContext.layoutOptions; | ||
this.rowHeight = options?.rowHeight ?? this.rowHeight; | ||
this.estimatedRowHeight = options?.estimatedRowHeight ?? this.estimatedRowHeight; | ||
this.headingHeight = options?.headingHeight ?? this.headingHeight; | ||
this.estimatedHeadingHeight = options?.estimatedHeadingHeight ?? this.estimatedHeadingHeight; | ||
this.loaderHeight = options?.loaderHeight ?? this.loaderHeight; | ||
this.dropIndicatorThickness = options?.dropIndicatorThickness ?? this.dropIndicatorThickness; | ||
this.gap = options?.gap ?? this.gap; | ||
this.padding = options?.padding ?? this.padding; | ||
this.rootNodes = this.buildCollection(); | ||
@@ -208,3 +256,2 @@ | ||
this.lastWidth = this.virtualizer!.visibleRect.width; | ||
this.lastCollection = collection; | ||
@@ -215,3 +262,3 @@ this.invalidateEverything = false; | ||
protected buildCollection(y = 0): LayoutNode[] { | ||
protected buildCollection(y = this.padding): LayoutNode[] { | ||
let collection = this.virtualizer!.collection; | ||
@@ -221,3 +268,3 @@ let skipped = 0; | ||
for (let node of collection) { | ||
let rowHeight = this.rowHeight ?? this.estimatedRowHeight ?? DEFAULT_HEIGHT; | ||
let rowHeight = (this.rowHeight ?? this.estimatedRowHeight ?? DEFAULT_HEIGHT) + this.gap; | ||
@@ -231,4 +278,4 @@ // Skip rows before the valid rectangle unless they are already cached. | ||
let layoutNode = this.buildChild(node, 0, y, null); | ||
y = layoutNode.layoutInfo.rect.maxY; | ||
let layoutNode = this.buildChild(node, this.padding, y, null); | ||
y = layoutNode.layoutInfo.rect.maxY + this.gap; | ||
nodes.push(layoutNode); | ||
@@ -242,2 +289,4 @@ | ||
y -= this.gap; | ||
y += this.padding; | ||
this.contentSize = new Size(this.virtualizer!.visibleRect.width, y); | ||
@@ -287,5 +336,5 @@ return nodes; | ||
protected buildLoader(node: Node<T>, x: number, y: number): LayoutNode { | ||
let rect = new Rect(x, y, 0, 0); | ||
let rect = new Rect(x, y, this.padding, 0); | ||
let layoutInfo = new LayoutInfo('loader', node.key, rect); | ||
rect.width = this.virtualizer!.contentSize.width; | ||
rect.width = this.virtualizer!.contentSize.width - this.padding - x; | ||
rect.height = this.loaderHeight || this.rowHeight || this.estimatedRowHeight || DEFAULT_HEIGHT; | ||
@@ -301,4 +350,4 @@ | ||
let collection = this.virtualizer!.collection; | ||
let width = this.virtualizer!.visibleRect.width; | ||
let rect = new Rect(0, y, width, 0); | ||
let width = this.virtualizer!.visibleRect.width - this.padding; | ||
let rect = new Rect(x, y, width - x, 0); | ||
let layoutInfo = new LayoutInfo(node.type, node.key, rect); | ||
@@ -310,3 +359,3 @@ | ||
for (let child of getChildNodes(node, collection)) { | ||
let rowHeight = (this.rowHeight ?? this.estimatedRowHeight ?? DEFAULT_HEIGHT); | ||
let rowHeight = (this.rowHeight ?? this.estimatedRowHeight ?? DEFAULT_HEIGHT) + this.gap; | ||
@@ -321,3 +370,3 @@ // Skip rows before the valid rectangle unless they are already cached. | ||
let layoutNode = this.buildChild(child, x, y, layoutInfo.key); | ||
y = layoutNode.layoutInfo.rect.maxY; | ||
y = layoutNode.layoutInfo.rect.maxY + this.gap; | ||
children.push(layoutNode); | ||
@@ -332,2 +381,3 @@ | ||
y -= this.gap; | ||
rect.height = y - startY; | ||
@@ -344,3 +394,3 @@ | ||
protected buildSectionHeader(node: Node<T>, x: number, y: number): LayoutNode { | ||
let width = this.virtualizer!.visibleRect.width; | ||
let width = this.virtualizer!.visibleRect.width - this.padding; | ||
let rectHeight = this.headingHeight; | ||
@@ -360,3 +410,3 @@ let isEstimated = false; | ||
rectHeight = previousLayoutInfo.rect.height; | ||
isEstimated = width !== this.lastWidth || curNode !== lastNode || previousLayoutInfo.estimatedSize; | ||
isEstimated = width !== previousLayoutInfo.rect.width || curNode !== lastNode || previousLayoutInfo.estimatedSize; | ||
} else { | ||
@@ -372,3 +422,3 @@ rectHeight = (node.rendered ? this.estimatedHeadingHeight : 0); | ||
let headerRect = new Rect(0, y, width, rectHeight); | ||
let headerRect = new Rect(x, y, width - x, rectHeight); | ||
let header = new LayoutInfo('header', node.key, headerRect); | ||
@@ -385,3 +435,3 @@ header.estimatedSize = isEstimated; | ||
protected buildItem(node: Node<T>, x: number, y: number): LayoutNode { | ||
let width = this.virtualizer!.visibleRect.width; | ||
let width = this.virtualizer!.visibleRect.width - this.padding - x; | ||
let rectHeight = this.rowHeight; | ||
@@ -398,3 +448,3 @@ let isEstimated = false; | ||
rectHeight = previousLayoutNode.layoutInfo.rect.height; | ||
isEstimated = width !== this.lastWidth || node !== previousLayoutNode.node || previousLayoutNode.layoutInfo.estimatedSize; | ||
isEstimated = width !== previousLayoutNode.layoutInfo.rect.width || node !== previousLayoutNode.node || previousLayoutNode.layoutInfo.estimatedSize; | ||
} else { | ||
@@ -410,3 +460,3 @@ rectHeight = this.estimatedRowHeight; | ||
let rect = new Rect(x, y, width - x, rectHeight); | ||
let rect = new Rect(x, y, width, rectHeight); | ||
let layoutInfo = new LayoutInfo(node.type, node.key, rect); | ||
@@ -413,0 +463,0 @@ layoutInfo.estimatedSize = isEstimated; |
@@ -21,3 +21,3 @@ /* | ||
export interface TableLayoutProps { | ||
export interface TableLayoutProps extends ListLayoutOptions { | ||
columnWidths?: Map<Key, number> | ||
@@ -28,2 +28,6 @@ } | ||
/** | ||
* TableLayout is a virtualizer Layout implementation that arranges | ||
* items in rows and columns. | ||
*/ | ||
export class TableLayout<T, O extends TableLayoutProps = TableLayoutProps> extends ListLayout<T, O> { | ||
@@ -36,3 +40,3 @@ protected lastCollection: TableCollection<T> | null = null; | ||
constructor(options: ListLayoutOptions) { | ||
constructor(options?: ListLayoutOptions) { | ||
super(options); | ||
@@ -59,2 +63,7 @@ this.stickyColumnIndices = []; | ||
shouldInvalidateLayoutOptions(newOptions: O, oldOptions: O): boolean { | ||
return newOptions.columnWidths !== oldOptions.columnWidths | ||
|| super.shouldInvalidateLayoutOptions(newOptions, oldOptions); | ||
} | ||
update(invalidationContext: InvalidationContext<O>): void { | ||
@@ -72,3 +81,3 @@ let newCollection = this.virtualizer!.collection as TableCollection<T>; | ||
let columnLayout = new TableColumnLayout({}); | ||
this.columnWidths = columnLayout.buildColumnWidths(this.virtualizer!.visibleRect.width, newCollection, new Map()); | ||
this.columnWidths = columnLayout.buildColumnWidths(this.virtualizer!.visibleRect.width - this.padding * 2, newCollection, new Map()); | ||
invalidationContext.sizeChanged = true; | ||
@@ -94,7 +103,7 @@ } | ||
this.layoutNodes.set(header.layoutInfo.key, header); | ||
let body = this.buildBody(header.layoutInfo.rect.height); | ||
let body = this.buildBody(header.layoutInfo.rect.maxY + this.gap); | ||
this.lastPersistedKeys = null; | ||
body.layoutInfo.rect.width = Math.max(header.layoutInfo.rect.width, body.layoutInfo.rect.width); | ||
this.contentSize = new Size(body.layoutInfo.rect.width, body.layoutInfo.rect.maxY); | ||
this.contentSize = new Size(body.layoutInfo.rect.width + this.padding * 2, body.layoutInfo.rect.maxY + this.padding); | ||
return [ | ||
@@ -108,3 +117,3 @@ header, | ||
let collection = this.virtualizer!.collection as TableCollection<T>; | ||
let rect = new Rect(0, 0, 0, 0); | ||
let rect = new Rect(this.padding, this.padding, 0, 0); | ||
let layoutInfo = new LayoutInfo('header', collection.head?.key ?? 'header', rect); | ||
@@ -114,7 +123,7 @@ layoutInfo.isSticky = true; | ||
let y = 0; | ||
let y = this.padding; | ||
let width = 0; | ||
let children: LayoutNode[] = []; | ||
for (let headerRow of collection.headerRows) { | ||
let layoutNode = this.buildChild(headerRow, 0, y, layoutInfo.key); | ||
let layoutNode = this.buildChild(headerRow, this.padding, y, layoutInfo.key); | ||
layoutNode.layoutInfo.parentKey = layoutInfo.key; | ||
@@ -128,3 +137,3 @@ y = layoutNode.layoutInfo.rect.maxY; | ||
rect.width = width; | ||
rect.height = y; | ||
rect.height = y - this.padding; | ||
@@ -140,3 +149,3 @@ return { | ||
protected buildHeaderRow(headerRow: GridNode<T>, x: number, y: number): LayoutNode { | ||
let rect = new Rect(0, y, 0, 0); | ||
let rect = new Rect(x, y, 0, 0); | ||
let row = new LayoutInfo('headerrow', headerRow.key, rect); | ||
@@ -161,3 +170,3 @@ | ||
rect.height = height; | ||
rect.width = x; | ||
rect.width = x - rect.x; | ||
@@ -185,6 +194,6 @@ return { | ||
let collection = this.virtualizer!.collection as TableCollection<T>; | ||
let colspan = node.colspan ?? 1; | ||
let colSpan = node.colSpan ?? 1; | ||
let colIndex = node.colIndex ?? node.index; | ||
let width = 0; | ||
for (let i = colIndex; i < colIndex + colspan; i++) { | ||
for (let i = colIndex; i < colIndex + colSpan; i++) { | ||
let column = collection.columns[i]; | ||
@@ -249,3 +258,3 @@ if (column?.key != null) { | ||
let collection = this.virtualizer!.collection as TableCollection<T>; | ||
let rect = new Rect(0, y, 0, 0); | ||
let rect = new Rect(this.padding, y, 0, 0); | ||
let layoutInfo = new LayoutInfo('rowgroup', collection.body.key, rect); | ||
@@ -257,3 +266,3 @@ | ||
let children: LayoutNode[] = []; | ||
let rowHeight = this.getEstimatedRowHeight(); | ||
let rowHeight = this.getEstimatedRowHeight() + this.gap; | ||
for (let node of getChildNodes(collection.body, collection)) { | ||
@@ -267,6 +276,6 @@ // Skip rows before the valid rectangle unless they are already cached. | ||
let layoutNode = this.buildChild(node, 0, y, layoutInfo.key); | ||
let layoutNode = this.buildChild(node, this.padding, y, layoutInfo.key); | ||
layoutNode.layoutInfo.parentKey = layoutInfo.key; | ||
layoutNode.index = children.length; | ||
y = layoutNode.layoutInfo.rect.maxY; | ||
y = layoutNode.layoutInfo.rect.maxY + this.gap; | ||
width = Math.max(width, layoutNode.layoutInfo.rect.width); | ||
@@ -284,2 +293,4 @@ children.push(layoutNode); | ||
y = this.virtualizer!.visibleRect.maxY; | ||
} else { | ||
y -= this.gap; | ||
} | ||
@@ -286,0 +297,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
515385
34
5569
9
+ Added@react-aria/ssr@3.0.0-nightly-f90f4899f-250227(transitive)
+ Added@react-aria/utils@3.0.0-nightly-f90f4899f-250227(transitive)
+ Added@react-stately/collections@3.0.0-nightly-f90f4899f-250227(transitive)
+ Added@react-stately/flags@3.0.0-nightly-f90f4899f-250227(transitive)
+ Added@react-stately/grid@3.0.0-nightly-f90f4899f-250227(transitive)
+ Added@react-stately/selection@3.0.0-nightly-f90f4899f-250227(transitive)
+ Added@react-stately/table@3.0.0-nightly-f90f4899f-250227(transitive)
+ Added@react-stately/utils@3.0.0-nightly-f90f4899f-250227(transitive)
+ Added@react-stately/virtualizer@3.0.0-nightly-f90f4899f-250227(transitive)
+ Added@react-types/grid@3.0.0-nightly-f90f4899f-250227(transitive)
+ Added@react-types/shared@3.0.0-nightly-f90f4899f-250227(transitive)
+ Added@react-types/table@3.0.0-nightly-f90f4899f-250227(transitive)
- Removed@react-aria/ssr@3.9.7(transitive)
- Removed@react-aria/utils@3.27.0(transitive)
- Removed@react-stately/collections@3.12.1(transitive)
- Removed@react-stately/flags@3.0.5(transitive)
- Removed@react-stately/grid@3.10.1(transitive)
- Removed@react-stately/selection@3.19.0(transitive)
- Removed@react-stately/table@3.13.1(transitive)
- Removed@react-stately/utils@3.10.5(transitive)
- Removed@react-stately/virtualizer@3.7.1(transitive)
- Removed@react-types/grid@3.2.11(transitive)
- Removed@react-types/shared@3.27.0(transitive)
- Removed@react-types/table@3.10.4(transitive)
- Removedjs-tokens@4.0.0(transitive)
- Removedloose-envify@1.4.0(transitive)
- Removedreact@18.3.1(transitive)
Updated@react-stately/collections@3.0.0-nightly-f90f4899f-250227
Updated@react-stately/virtualizer@3.0.0-nightly-f90f4899f-250227