1
- import throttle from "lodash/throttle" ;
1
+ /**
2
+ * Polyfill for CSS Grid masonry layout behavior
3
+ */
4
+
5
+ import throttle from 'lodash/throttle' ;
2
6
3
7
// Represents a parsed CSS grid column value
4
8
interface CSSValue {
@@ -13,7 +17,7 @@ interface GridTemplateColumns {
13
17
}
14
18
15
19
// Maps a CSS grid-template-columns value to its type
16
- const parseCSSValue = ( value : string ) : CSSValue [ 'type' ] => {
20
+ const parseCSSValue = ( value : string ) : CSSValue => {
17
21
const tests = [
18
22
{ test : / ^ \[ .* \] $ / , type : 'line-name' } ,
19
23
{ test : / ^ r e p e a t \( .+ \) $ / , type : 'repeat' } ,
@@ -25,7 +29,8 @@ const parseCSSValue = (value: string): CSSValue['type'] => {
25
29
{ test : / .* / , type : 'global' }
26
30
] as const ;
27
31
// Map CSS grid column value to a type (e.g., 'dimension', 'keyword')
28
- return tests . find ( ( { test } ) => test . test ( value ) ) ?. type || 'global' ;
32
+ const type = tests . find ( ( { test } ) => test . test ( value ) ) ?. type || 'global' ;
33
+ return { type, value }
29
34
} ;
30
35
31
36
// Parses the grid-template-columns property of an element
@@ -41,7 +46,7 @@ function parseGridTemplateColumns(grid: HTMLElement): GridTemplateColumns {
41
46
}
42
47
// Filters out line names to get actual column tracks
43
48
const parsed = columns . split ( / \s (? = (?: [ ^ ( ) ] * \( [ ^ ( ) ] * \) ) * [ ^ ( ) ] * $ ) / )
44
- . map ( value => ( { type : parseCSSValue ( value ) , value } ) )
49
+ . map ( value => parseCSSValue )
45
50
. filter ( col => 'line-name' !== col . type ) ;
46
51
return {
47
52
type : 'track-list' ,
@@ -50,7 +55,6 @@ function parseGridTemplateColumns(grid: HTMLElement): GridTemplateColumns {
50
55
}
51
56
52
57
export class Masonry {
53
- private readonly grid : HTMLElement ;
54
58
private items : HTMLElement [ ] = [ ] ;
55
59
private mutationObserver ?: MutationObserver ;
56
60
private resizeObserver ?: ResizeObserver ;
@@ -74,12 +78,13 @@ export class Masonry {
74
78
*/
75
79
create ( ) : void {
76
80
this . destroy ( ) ;
81
+ // Observe grid children for changes
77
82
this . mutationObserver ?. observe ( this . grid , {
78
83
childList : true ,
79
84
} ) ;
85
+ // Assumes children are HTMLElements, may need adjusting if SVG elements are used as grid items...
80
86
this . items = Array . from ( this . grid . children ) as HTMLElement [ ] ;
81
-
82
- this . items . forEach ( ( item ) => {
87
+ this . items . forEach ( item => {
83
88
this . resizeObserver ?. observe ( item ) ;
84
89
} ) ;
85
90
}
@@ -88,8 +93,8 @@ export class Masonry {
88
93
* Disconnects observers and resets the grid layout
89
94
*/
90
95
destroy ( ) : void {
91
- this . resizeObserver ?. disconnect ( ) ;
92
96
this . mutationObserver ?. disconnect ( ) ;
97
+ this . resizeObserver ?. disconnect ( ) ;
93
98
this . clean ( ) ;
94
99
this . items = [ ] ;
95
100
}
@@ -103,14 +108,14 @@ export class Masonry {
103
108
if ( ! style . getPropertyValue ( 'display' ) . includes ( 'grid' ) || 1 >= columns . length ) {
104
109
return this . clean ( ) ;
105
110
}
106
- const rowGap = parseFloat ( style . getPropertyValue ( 'row-gap' ) ) || 0 ;
111
+ const rowGap = Math . max ( 0 , parseFloat ( style . getPropertyValue ( 'row-gap' ) ) || 0 ) ;
107
112
this . adjustColumnSpacing ( rowGap , columns ) ;
108
113
}
109
114
110
115
/**
111
116
* Adjusts vertical spacing between grid items based on row gap
112
- * @param rowGap - The parsed row gap value in pixels
113
- * @param columns - The parsed grid-template-columns configuration
117
+ * @param rowGap - The vertical gap between rows in pixels (non-negative).
118
+ * @param columns - The parsed ` grid-template-columns` configuration.
114
119
*/
115
120
private adjustColumnSpacing ( rowGap : number , columns : CSSValue [ ] ) : void {
116
121
// Reset first items in each column
@@ -135,8 +140,8 @@ export class Masonry {
135
140
* Removes margin-top style property from all grid items
136
141
*/
137
142
private clean ( ) : void {
138
- this . items . forEach ( ( item ) => {
139
- item . style . removeProperty ( " margin-top" ) ;
143
+ this . items . forEach ( item => {
144
+ item ? .style . removeProperty ( ' margin-top' ) ;
140
145
} ) ;
141
146
}
142
147
}
0 commit comments