@@ -10,14 +10,14 @@ function App() {
10
10
return (
11
11
< div >
12
12
< p >
13
- < strong > Lanes</ strong > are useful when you are trying to draw a grid of items, where
14
- each row is split into multiple columns.
13
+ < strong > Lanes</ strong > are useful when you are trying to draw a grid of
14
+ items, where each row is split into multiple columns.
15
15
</ p >
16
16
< br />
17
17
< br />
18
18
19
19
< h3 > Lanes</ h3 >
20
- < LanesGapVirtualizer />
20
+ < LanesVirtualizer />
21
21
< br />
22
22
< br />
23
23
< h3 > Lanes Gaps</ h3 >
@@ -41,7 +41,7 @@ function App() {
41
41
)
42
42
}
43
43
44
- function LanesGapVirtualizer ( ) {
44
+ function LanesVirtualizer ( ) {
45
45
const [ numLanes , setNumLanes ] = React . useState ( 4 )
46
46
const parentRef = React . useRef ( null )
47
47
@@ -55,18 +55,32 @@ function LanesGapVirtualizer() {
55
55
56
56
return (
57
57
< >
58
- < div style = { { display : 'grid' , gridTemplateColumns : '80px 200px' , gap : '10px' } } >
58
+ < div
59
+ style = { {
60
+ display : 'grid' ,
61
+ gridTemplateColumns : '80px 200px' ,
62
+ gap : '10px' ,
63
+ } }
64
+ >
59
65
< label htmlFor = "numLanes1" > Num Lanes</ label >
60
- < input type = "number" id = "numLanes1" value = { numLanes } onChange = { ( e ) => { setNumLanes ( Number ( e . target . value ) ) ; rowVirtualizer . measure ( ) } } />
66
+ < input
67
+ type = "number"
68
+ id = "numLanes1"
69
+ value = { numLanes }
70
+ onChange = { ( e ) => {
71
+ setNumLanes ( Number ( e . target . value ) )
72
+ rowVirtualizer . measure ( )
73
+ } }
74
+ />
61
75
</ div >
62
76
< br />
63
77
< div
64
78
ref = { parentRef }
65
79
className = "List"
66
80
style = { {
67
- height : " 200px" ,
68
- width : " 400px" ,
69
- overflow : " auto" ,
81
+ height : ' 200px' ,
82
+ width : ' 400px' ,
83
+ overflow : ' auto' ,
70
84
} }
71
85
>
72
86
< div
@@ -115,23 +129,56 @@ function GapVirtualizer() {
115
129
116
130
return (
117
131
< >
118
- < div style = { { display : 'grid' , gridTemplateColumns : '80px 200px' , gap : '10px' } } >
132
+ < div
133
+ style = { {
134
+ display : 'grid' ,
135
+ gridTemplateColumns : '80px 200px' ,
136
+ gap : '10px' ,
137
+ } }
138
+ >
119
139
< label htmlFor = "numLanes2" > Num Lanes</ label >
120
- < input type = "number" id = "numLanes2" value = { numLanes } onChange = { ( e ) => { setNumLanes ( Number ( e . target . value ) ) ; rowVirtualizer . measure ( ) } } />
121
- < label htmlFor = "rowGap" > Row Gap</ label >
122
- < input type = "number" id = "rowGap" value = { rowGap } onChange = { ( e ) => { setRowGap ( Number ( e . target . value ) ) ; rowVirtualizer . measure ( ) } } />
140
+ < input
141
+ type = "number"
142
+ id = "numLanes2"
143
+ value = { numLanes }
144
+ readOnly
145
+ onChange = { ( e ) => {
146
+ setNumLanes ( Number ( e . target . value ) )
147
+ rowVirtualizer . measure ( )
148
+ } }
149
+ />
150
+ < label htmlFor = "rowGap" > Row Gap</ label >
151
+ < input
152
+ type = "number"
153
+ id = "rowGap"
154
+ value = { rowGap }
155
+ onChange = { ( e ) => {
156
+ setRowGap ( Number ( e . target . value ) )
157
+ rowVirtualizer . measure ( )
158
+ } }
159
+ />
123
160
< label htmlFor = "columnGap" > Column Gap</ label >
124
- < input type = "number" id = "columnGap" value = { columnGap } onChange = { ( e ) => { setColumnGap ( Number ( e . target . value ) ) ; rowVirtualizer . measure ( ) } } />
161
+ < input
162
+ type = "number"
163
+ id = "columnGap"
164
+ value = { columnGap }
165
+ onChange = { ( e ) => {
166
+ setColumnGap ( Number ( e . target . value ) )
167
+ rowVirtualizer . measure ( )
168
+ } }
169
+ />
125
170
</ div >
126
171
< br />
127
-
172
+
128
173
< div
129
174
ref = { parentRef }
130
175
className = "List"
131
176
style = { {
132
- height : "200px" ,
133
- width : "400px" ,
134
- overflow : "auto" ,
177
+ height : '200px' ,
178
+ width : '400px' ,
179
+ overflow : 'auto' ,
180
+ minWidth : CELL_WIDTH ,
181
+ minHeight : '35px' ,
135
182
} }
136
183
>
137
184
< div
@@ -143,21 +190,26 @@ function GapVirtualizer() {
143
190
>
144
191
{ rowVirtualizer . getVirtualItems ( ) . map ( ( virtualRow ) => {
145
192
return (
146
- < div
147
- key = { virtualRow . index }
148
- className = { virtualRow . index % 2 ? 'ListItemOdd' : 'ListItemEven' }
149
- style = { {
150
- position : 'absolute' ,
151
- top : 0 ,
152
- "--start-ratio" : `calc(mod(${ virtualRow . index } , ${ numLanes } ) / ${ numLanes } )` ,
153
- left : `calc((var(--start-ratio) * 100%) + (${ columnGap } px * var(--start-ratio)))` ,
154
- height : `${ virtualRow . size } px` ,
155
- transform : `translateY(${ virtualRow . start } px)` ,
156
- outline : '1px solid red' ,
157
- } as React . CSSProperties }
158
- >
159
- Cell { virtualRow . index }
160
- </ div >
193
+ < div
194
+ key = { virtualRow . index }
195
+ className = {
196
+ virtualRow . index % 2 ? 'ListItemOdd' : 'ListItemEven'
197
+ }
198
+ style = {
199
+ {
200
+ position : 'absolute' ,
201
+ top : 0 ,
202
+ '--start-ratio' : `calc(mod(${ virtualRow . index } , ${ numLanes } ) / ${ numLanes } )` ,
203
+ left : `calc((var(--start-ratio) * 100%) + (${ columnGap } px * var(--start-ratio)))` ,
204
+ width : `calc((100% / ${ numLanes } ) - (${ columnGap } px * (${ numLanes } - 1) / ${ numLanes } ))` ,
205
+ height : `${ virtualRow . size } px` ,
206
+ transform : `translateY(${ virtualRow . start } px)` ,
207
+ outline : '1px solid red' ,
208
+ } as React . CSSProperties
209
+ }
210
+ >
211
+ Cell { virtualRow . index }
212
+ </ div >
161
213
)
162
214
} ) }
163
215
</ div >
@@ -183,82 +235,114 @@ function ResizeVirtualizer() {
183
235
} )
184
236
185
237
React . useEffect ( ( ) => {
186
- if ( ! parentRef . current ) return
187
- // debounce not necessary
188
- const debouncedOnResize = debounce ( ( entries : Array < ResizeObserverEntry > ) => {
189
- const rect = entries . at ( 0 ) ?. contentRect
190
- if ( ! rect ) return
191
- const { width } = rect
192
- setNumLanes ( Math . floor ( width / CELL_WIDTH ) )
193
- rowVirtualizer . measure ( )
194
- } , {
195
- wait : 50 ,
196
-
197
- } )
198
- const resizeObserver = new ResizeObserver ( ( entries ) => {
199
- debouncedOnResize ( entries )
200
- } )
201
- resizeObserver . observe ( parentRef . current )
202
- return ( ) => {
203
- resizeObserver . disconnect ( )
204
- }
238
+ if ( ! parentRef . current ) return
239
+ // debounce not necessary
240
+ const debouncedOnResize = debounce (
241
+ ( entries : Array < ResizeObserverEntry > ) => {
242
+ const rect = entries . at ( 0 ) ?. contentRect
243
+ if ( ! rect ) return
244
+ const { width } = rect
245
+ setNumLanes ( Math . floor ( width / CELL_WIDTH ) )
246
+ rowVirtualizer . measure ( )
247
+ } ,
248
+ {
249
+ wait : 50 ,
250
+ } ,
251
+ )
252
+ const resizeObserver = new ResizeObserver ( ( entries ) => {
253
+ debouncedOnResize ( entries )
254
+ } )
255
+ resizeObserver . observe ( parentRef . current )
256
+ return ( ) => {
257
+ resizeObserver . disconnect ( )
258
+ }
205
259
} , [ rowVirtualizer ] )
206
260
207
-
208
-
209
261
return (
210
262
< >
211
- < div style = { { display : 'grid' , gridTemplateColumns : '80px 200px' , gap : '10px' } } >
212
- < label htmlFor = "numLanes2" > Num Lanes</ label >
213
- < input type = "number" id = "numLanes2" value = { numLanes } readOnly disabled />
214
- < label htmlFor = "rowGap" > Row Gap</ label >
215
- < input type = "number" id = "rowGap" value = { rowGap } onChange = { ( e ) => { setRowGap ( Number ( e . target . value ) ) ; rowVirtualizer . measure ( ) } } />
216
- < label htmlFor = "columnGap" > Column Gap</ label >
217
- < input type = "number" id = "columnGap" value = { columnGap } onChange = { ( e ) => { setColumnGap ( Number ( e . target . value ) ) ; rowVirtualizer . measure ( ) } } />
218
- </ div >
219
- < br />
220
-
221
- < div
222
- ref = { parentRef }
223
- className = "List"
224
- style = { {
225
- height : "200px" ,
226
- width : "400px" ,
227
- overflow : "auto" ,
228
- minWidth : CELL_WIDTH ,
229
- minHeight : "35px" ,
230
- resize : 'horizontal' ,
231
- } }
232
- >
233
263
< div
234
264
style = { {
235
- height : ` ${ rowVirtualizer . getTotalSize ( ) } px` ,
236
- width : '100% ' ,
237
- position : 'relative ' ,
265
+ display : 'grid' ,
266
+ gridTemplateColumns : '80px 200px ' ,
267
+ gap : '10px ' ,
238
268
} }
239
269
>
240
- { rowVirtualizer . getVirtualItems ( ) . map ( ( virtualRow ) => {
241
- return (
242
- < div
243
- key = { virtualRow . index }
244
- className = { virtualRow . index % 2 ? 'ListItemOdd' : 'ListItemEven' }
245
- style = { {
246
- position : 'absolute' ,
247
- top : 0 ,
248
- "--start-ratio" : `calc(mod(${ virtualRow . index } , ${ numLanes } ) / ${ numLanes } )` ,
249
- left : `calc((var(--start-ratio) * 100%) + (${ columnGap } px * var(--start-ratio)))` ,
250
- width : `calc((100% / ${ numLanes } ) - (${ columnGap } px * (${ numLanes } - 1) / ${ numLanes } ))` ,
251
- height : `${ virtualRow . size } px` ,
252
- transform : `translateY(${ virtualRow . start } px)` ,
253
- outline : '1px solid red' ,
254
- } as React . CSSProperties }
255
- >
256
- Cell { virtualRow . index }
257
- </ div >
258
- )
259
- } ) }
270
+ < label htmlFor = "numLanes2" > Num Lanes</ label >
271
+ < input
272
+ type = "number"
273
+ id = "numLanes2"
274
+ value = { numLanes }
275
+ readOnly
276
+ disabled
277
+ />
278
+ < label htmlFor = "rowGap" > Row Gap</ label >
279
+ < input
280
+ type = "number"
281
+ id = "rowGap"
282
+ value = { rowGap }
283
+ onChange = { ( e ) => {
284
+ setRowGap ( Number ( e . target . value ) )
285
+ rowVirtualizer . measure ( )
286
+ } }
287
+ />
288
+ < label htmlFor = "columnGap" > Column Gap</ label >
289
+ < input
290
+ type = "number"
291
+ id = "columnGap"
292
+ value = { columnGap }
293
+ onChange = { ( e ) => {
294
+ setColumnGap ( Number ( e . target . value ) )
295
+ rowVirtualizer . measure ( )
296
+ } }
297
+ />
298
+ </ div >
299
+ < br />
300
+
301
+ < div
302
+ ref = { parentRef }
303
+ className = "List"
304
+ style = { {
305
+ height : '200px' ,
306
+ width : '400px' ,
307
+ overflow : 'auto' ,
308
+ minWidth : CELL_WIDTH ,
309
+ minHeight : '35px' ,
310
+ resize : 'horizontal' ,
311
+ } }
312
+ >
313
+ < div
314
+ style = { {
315
+ height : `${ rowVirtualizer . getTotalSize ( ) } px` ,
316
+ width : '100%' ,
317
+ position : 'relative' ,
318
+ } }
319
+ >
320
+ { rowVirtualizer . getVirtualItems ( ) . map ( ( virtualRow ) => {
321
+ return (
322
+ < div
323
+ key = { virtualRow . index }
324
+ className = {
325
+ virtualRow . index % 2 ? 'ListItemOdd' : 'ListItemEven'
326
+ }
327
+ style = {
328
+ {
329
+ position : 'absolute' ,
330
+ top : 0 ,
331
+ '--start-ratio' : `calc(mod(${ virtualRow . index } , ${ numLanes } ) / ${ numLanes } )` ,
332
+ left : `calc((var(--start-ratio) * 100%) + (${ columnGap } px * var(--start-ratio)))` ,
333
+ width : `calc((100% / ${ numLanes } ) - (${ columnGap } px * (${ numLanes } - 1) / ${ numLanes } ))` ,
334
+ height : `${ virtualRow . size } px` ,
335
+ transform : `translateY(${ virtualRow . start } px)` ,
336
+ outline : '1px solid red' ,
337
+ } as React . CSSProperties
338
+ }
339
+ >
340
+ Cell { virtualRow . index }
341
+ </ div >
342
+ )
343
+ } ) }
344
+ </ div >
260
345
</ div >
261
- </ div >
262
346
</ >
263
347
)
264
348
}
0 commit comments