@@ -8,7 +8,103 @@ import type {
88import { resolveDimension } from '../../../../utils' ;
99import { getCrossIndex , getMainIndex } from './helpers' ;
1010
11- export const calculateLayout = ( {
11+ /**
12+ * Calculates masonry-style layout where items stack within each column.
13+ * Items maintain their sequential grid order (respecting columns). Vertical spacing
14+ * between items in a column is controlled by gaps.cross (rowGap when vertical).
15+ */
16+ const calculateMasonryLayout = ( {
17+ gaps,
18+ indexToKey,
19+ isVertical,
20+ itemHeights,
21+ itemWidths,
22+ numGroups,
23+ startCrossOffset
24+ } : GridLayoutProps ) : GridLayout | null => {
25+ 'worklet' ;
26+ const mainGroupSize = ( isVertical ? itemWidths : itemHeights ) as
27+ | null
28+ | number ;
29+
30+ if ( ! mainGroupSize ) {
31+ return null ;
32+ }
33+
34+ const itemPositions : Record < string , Vector > = { } ;
35+
36+ let mainCoordinate : Coordinate ;
37+ let crossCoordinate : Coordinate ;
38+ let crossItemSizes ;
39+
40+ if ( isVertical ) {
41+ // grid with specified number of columns (vertical orientation)
42+ mainCoordinate = 'x' ;
43+ crossCoordinate = 'y' ;
44+ crossItemSizes = itemHeights ;
45+ } else {
46+ // grid with specified number of rows (horizontal orientation)
47+ mainCoordinate = 'y' ;
48+ crossCoordinate = 'x' ;
49+ crossItemSizes = itemWidths ;
50+ }
51+
52+ // Track the current height/position of each column independently
53+ // Each column stacks its items, separated by the configured cross gap
54+ const columnHeights = new Array ( numGroups ) . fill ( startCrossOffset ?? 0 ) ;
55+
56+ for ( const [ itemIndex , itemKey ] of indexToKey . entries ( ) ) {
57+ const crossItemSize = resolveDimension ( crossItemSizes , itemKey ) ;
58+
59+ if ( crossItemSize === null ) {
60+ return null ;
61+ }
62+
63+ // Determine which column this item belongs to based on grid order
64+ const mainIndex = getMainIndex ( itemIndex , numGroups ) ;
65+ const crossAxisOffset = columnHeights [ mainIndex ] ! ;
66+
67+ // Update item position - place it at the current column height
68+ itemPositions [ itemKey ] = {
69+ [ crossCoordinate ] : crossAxisOffset ,
70+ [ mainCoordinate ] : mainIndex * ( mainGroupSize + gaps . main )
71+ } as Vector ;
72+
73+ // Update column height - advance by item size plus cross gap
74+ columnHeights [ mainIndex ] = crossAxisOffset + crossItemSize + gaps . cross ;
75+ }
76+
77+ // Container size is determined by the tallest column
78+ const rawMaxColumnHeight = Math . max ( ...columnHeights ) ;
79+ const baseCrossOffset = startCrossOffset ?? 0 ;
80+ // Remove the trailing cross gap from the tallest column if at least one item exists
81+ const maxColumnHeight =
82+ rawMaxColumnHeight > baseCrossOffset
83+ ? Math . max ( rawMaxColumnHeight - gaps . cross , baseCrossOffset )
84+ : rawMaxColumnHeight ;
85+ const mainSize = ( mainGroupSize + gaps . main ) * numGroups - gaps . main ;
86+
87+ return {
88+ containerCrossSize : maxColumnHeight ,
89+ contentBounds : [
90+ {
91+ [ crossCoordinate ] : startCrossOffset ?? 0 ,
92+ [ mainCoordinate ] : 0
93+ } as Vector ,
94+ {
95+ [ crossCoordinate ] : maxColumnHeight ,
96+ [ mainCoordinate ] : mainSize
97+ } as Vector
98+ ] ,
99+ crossAxisOffsets : columnHeights ,
100+ itemPositions
101+ } ;
102+ } ;
103+
104+ /**
105+ * Calculates standard grid layout where items in the same row align vertically
106+ */
107+ const calculateStandardLayout = ( {
12108 gaps,
13109 indexToKey,
14110 isVertical,
@@ -95,14 +191,59 @@ export const calculateLayout = ({
95191 } ;
96192} ;
97193
194+ export const calculateLayout = ( props : GridLayoutProps ) : GridLayout | null => {
195+ 'worklet' ;
196+ return props . masonry
197+ ? calculateMasonryLayout ( props )
198+ : calculateStandardLayout ( props ) ;
199+ } ;
200+
98201export const calculateItemCrossOffset = ( {
99202 crossGap,
100203 crossItemSizes,
101204 indexToKey,
102205 itemKey,
206+ masonry,
103207 numGroups
104208} : AutoOffsetAdjustmentProps ) : number => {
105209 'worklet' ;
210+
211+ if ( masonry ) {
212+ // Masonry layout: calculate offset within the same group (column for vertical, row for horizontal)
213+ // Find the target item's index and group
214+ let targetItemIndex = - 1 ;
215+ for ( let i = 0 ; i < indexToKey . length ; i ++ ) {
216+ if ( indexToKey [ i ] === itemKey ) {
217+ targetItemIndex = i ;
218+ break ;
219+ }
220+ }
221+
222+ if ( targetItemIndex === - 1 ) {
223+ return 0 ;
224+ }
225+
226+ const targetGroup = getMainIndex ( targetItemIndex , numGroups ) ;
227+ let offset = 0 ;
228+
229+ // Sum cross-axis sizes of all items in the same group that come before the target item
230+ // For vertical grids: sums heights of items in the same column
231+ // For horizontal grids: sums widths of items in the same row
232+ for ( let i = 0 ; i < targetItemIndex ; i ++ ) {
233+ const group = getMainIndex ( i , numGroups ) ;
234+ if ( group === targetGroup ) {
235+ const key = indexToKey [ i ] ! ;
236+ const itemSize = resolveDimension ( crossItemSizes , key ) ;
237+ if ( itemSize !== null ) {
238+ offset += itemSize + crossGap ;
239+ }
240+ }
241+ }
242+
243+ return offset ;
244+ }
245+
246+ // Standard grid layout: calculate offset using row-based logic
106247 let activeItemCrossOffset = 0 ;
107248 let currentGroupCrossSize = 0 ;
108249 let currentGroupCrossIndex = 0 ;
0 commit comments