@@ -10,14 +10,14 @@ function App() {
1010 return (
1111 <div>
1212 <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.
1515 </p>
1616 <br />
1717 <br />
1818
1919 <h3>Lanes</h3>
20- <LanesGapVirtualizer />
20+ <LanesVirtualizer />
2121 <br />
2222 <br />
2323 <h3>Lanes Gaps</h3>
@@ -41,7 +41,7 @@ function App() {
4141 )
4242}
4343
44- function LanesGapVirtualizer () {
44+ function LanesVirtualizer () {
4545 const [numLanes, setNumLanes] = React.useState(4)
4646 const parentRef = React.useRef(null)
4747
@@ -55,18 +55,32 @@ function LanesGapVirtualizer() {
5555
5656 return (
5757 <>
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+ >
5965 <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+ />
6175 </div>
6276 <br />
6377 <div
6478 ref={parentRef}
6579 className="List"
6680 style={{
67- height: " 200px" ,
68- width: " 400px" ,
69- overflow: " auto" ,
81+ height: ' 200px' ,
82+ width: ' 400px' ,
83+ overflow: ' auto' ,
7084 }}
7185 >
7286 <div
@@ -115,23 +129,56 @@ function GapVirtualizer() {
115129
116130 return (
117131 <>
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+ >
119139 <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+ />
123160 <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+ />
125170 </div>
126171 <br />
127-
172+
128173 <div
129174 ref={parentRef}
130175 className="List"
131176 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',
135182 }}
136183 >
137184 <div
@@ -143,21 +190,26 @@ function GapVirtualizer() {
143190 >
144191 {rowVirtualizer.getVirtualItems().map((virtualRow) => {
145192 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>
161213 )
162214 })}
163215 </div>
@@ -183,82 +235,114 @@ function ResizeVirtualizer() {
183235 })
184236
185237 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+ }
205259 }, [rowVirtualizer])
206260
207-
208-
209261 return (
210262 <>
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- >
233263 <div
234264 style={{
235- height: `${rowVirtualizer.getTotalSize()}px` ,
236- width : '100% ',
237- position : 'relative ',
265+ display: 'grid' ,
266+ gridTemplateColumns : '80px 200px ',
267+ gap : '10px ',
238268 }}
239269 >
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>
260345 </div>
261- </div>
262346 </>
263347 )
264348}
0 commit comments