diff --git a/packages/@react-stately/layout/src/GridLayout.ts b/packages/@react-stately/layout/src/GridLayout.ts index 14e3ae7588d..c61e54782a5 100644 --- a/packages/@react-stately/layout/src/GridLayout.ts +++ b/packages/@react-stately/layout/src/GridLayout.ts @@ -126,7 +126,7 @@ export class GridLayout exte itemHeight = Math.max(minItemSize.height, Math.min(maxItemHeight, itemHeight)); // Compute the horizontal spacing, content height and horizontal margin - let horizontalSpacing = Math.min(maxHorizontalSpace, Math.floor((visibleWidth - numColumns * itemWidth) / (numColumns + 1))); + let horizontalSpacing = Math.min(Math.max(maxHorizontalSpace, minSpace.width), Math.floor((visibleWidth - numColumns * itemWidth) / (numColumns + 1))); this.gap = new Size(horizontalSpacing, minSpace.height); this.margin = Math.floor((visibleWidth - numColumns * itemWidth - horizontalSpacing * (numColumns + 1)) / 2); diff --git a/packages/@react-stately/layout/src/WaterfallLayout.ts b/packages/@react-stately/layout/src/WaterfallLayout.ts index 4c91fb971b1..437bf9e1c73 100644 --- a/packages/@react-stately/layout/src/WaterfallLayout.ts +++ b/packages/@react-stately/layout/src/WaterfallLayout.ts @@ -29,6 +29,11 @@ export interface WaterfallLayoutOptions { * @default 18 x 18 */ minSpace?: Size, + /** + * The maximum allowed horizontal space between items. + * @default Infinity + */ + maxHorizontalSpace?: number, /** * The maximum number of columns. * @default Infinity @@ -55,6 +60,7 @@ const DEFAULT_OPTIONS = { minItemSize: new Size(200, 200), maxItemSize: new Size(Infinity, Infinity), minSpace: new Size(18, 18), + maxSpace: Infinity, maxColumns: Infinity, dropIndicatorThickness: 2 }; @@ -64,13 +70,15 @@ export class WaterfallLayout = new Map(); protected numColumns = 0; protected dropIndicatorThickness = 2; + private margin: number = 0; shouldInvalidateLayoutOptions(newOptions: O, oldOptions: O): boolean { return newOptions.maxColumns !== oldOptions.maxColumns || newOptions.dropIndicatorThickness !== oldOptions.dropIndicatorThickness || (!(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)); + || (!(newOptions.minSpace || DEFAULT_OPTIONS.minSpace).equals(oldOptions.minSpace || DEFAULT_OPTIONS.minSpace)) + || (newOptions.maxHorizontalSpace !== oldOptions.maxHorizontalSpace); } update(invalidationContext: InvalidationContext): void { @@ -78,6 +86,7 @@ export class WaterfallLayout h < columnHeights[minIndex] ? i : minIndex, 0); - let x = horizontalSpacing + column * (itemWidth + horizontalSpacing); + let x = horizontalSpacing + column * (itemWidth + horizontalSpacing) + this.margin; let y = columnHeights[column]; let rect = new Rect(x, y, itemWidth, height); diff --git a/packages/react-aria-components/stories/GridList.stories.tsx b/packages/react-aria-components/stories/GridList.stories.tsx index 569b2e497d1..b1203c66ba9 100644 --- a/packages/react-aria-components/stories/GridList.stories.tsx +++ b/packages/react-aria-components/stories/GridList.stories.tsx @@ -201,6 +201,7 @@ export const VirtualizedGridList: StoryObj = { }; interface VirtualizedGridListGridProps { + minItemSizeWidth?: number, maxItemSizeWidth?: number, maxColumns?: number, minHorizontalSpace?: number, @@ -209,6 +210,7 @@ interface VirtualizedGridListGridProps { export let VirtualizedGridListGrid: StoryFn = (args) => { const { + minItemSizeWidth = 40, maxItemSizeWidth = 65, maxColumns = Infinity, minHorizontalSpace = 0, @@ -223,7 +225,7 @@ export let VirtualizedGridListGrid: StoryFn = (arg = (arg VirtualizedGridListGrid.story = { args: { + minItemSizeWidth: 40, maxItemSizeWidth: 65, maxColumns: undefined, minHorizontalSpace: 0, maxHorizontalSpace: undefined }, argTypes: { + minItemSizeWidth: { + control: 'number', + description: 'The minimum width of each item in the grid list', + defaultValue: 40 + }, maxItemSizeWidth: { control: 'number', description: 'Maximum width of each item in the grid list.', diff --git a/packages/react-aria-components/stories/ListBox.stories.tsx b/packages/react-aria-components/stories/ListBox.stories.tsx index d3ffaf1a281..6564ffc9a52 100644 --- a/packages/react-aria-components/stories/ListBox.stories.tsx +++ b/packages/react-aria-components/stories/ListBox.stories.tsx @@ -514,7 +514,7 @@ export const VirtualizedListBoxGrid: StoryObj = { + render: (args) => { + return ; + }, + args: { + minSize: 40, + maxSize: 65, + maxColumns: undefined, + minSpace: undefined, + maxSpace: undefined + }, + argTypes: { + minSize: { + control: 'number', + description: 'The minimum width of each item in the grid list', + defaultValue: 40 + }, + maxSize: { + control: 'number', + description: 'Maximum width of each item in the grid list.', + defaultValue: 65 + }, + maxColumns: { + control: 'number', + description: 'Maximum number of columns in the grid list.', + defaultValue: undefined + }, + minSpace: { + control: 'number', + description: 'Minimum horizontal space between grid items.', + defaultValue: undefined + }, + maxSpace: { + control: 'number', + description: 'Maximum horizontal space between grid items.', + defaultValue: undefined + } + } +}; + + let renderEmptyState = ({isLoading}) => { return (