0
# Navigation and Interaction Components
1
2
Components that add keyboard navigation, scroll synchronization, and window scrolling capabilities.
3
4
## Capabilities
5
6
### ArrowKeyStepper Component
7
8
Higher-order component that adds arrow key navigation to virtualized components, enabling keyboard-driven scrolling.
9
10
```javascript { .api }
11
/**
12
* Adds arrow key navigation to virtualized components
13
* @param props - ArrowKeyStepper configuration
14
*/
15
function ArrowKeyStepper(props: {
16
/** Function that renders the navigable component */
17
children: (params: {
18
onSectionRendered: (params: {columnStartIndex: number, columnStopIndex: number, rowStartIndex: number, rowStopIndex: number}) => void,
19
scrollToColumn: number,
20
scrollToRow: number
21
}) => React.Node;
22
/** Optional CSS class name */
23
className?: string;
24
/** Total number of columns */
25
columnCount: number;
26
/** Whether arrow key navigation is disabled */
27
disabled?: boolean;
28
/** Whether the component is controlled externally */
29
isControlled?: boolean;
30
/** Navigation mode: 'cells' for cell-by-cell, 'edges' for visible edge navigation */
31
mode?: 'cells' | 'edges';
32
/** Callback when scroll position changes */
33
onScrollToChange?: (params: {scrollToColumn: number, scrollToRow: number}) => void;
34
/** Total number of rows */
35
rowCount: number;
36
/** Column index to scroll to */
37
scrollToColumn?: number;
38
/** Row index to scroll to */
39
scrollToRow?: number;
40
}): React.Component;
41
```
42
43
**Usage Examples:**
44
45
```javascript
46
import React, { useState } from 'react';
47
import { ArrowKeyStepper, Grid, AutoSizer } from 'react-virtualized';
48
49
// Basic arrow key navigation with Grid
50
function NavigableGrid({ data }) {
51
const cellRenderer = ({ columnIndex, key, rowIndex, style }) => (
52
<div key={key} style={style} className="grid-cell">
53
{data[rowIndex][columnIndex]}
54
</div>
55
);
56
57
return (
58
<div style={{ height: 400, width: 600 }}>
59
<ArrowKeyStepper
60
columnCount={data[0].length}
61
rowCount={data.length}
62
mode="cells"
63
>
64
{({ onSectionRendered, scrollToColumn, scrollToRow }) => (
65
<AutoSizer>
66
{({ height, width }) => (
67
<Grid
68
cellRenderer={cellRenderer}
69
columnCount={data[0].length}
70
columnWidth={100}
71
height={height}
72
onSectionRendered={onSectionRendered}
73
rowCount={data.length}
74
rowHeight={50}
75
scrollToColumn={scrollToColumn}
76
scrollToRow={scrollToRow}
77
width={width}
78
/>
79
)}
80
</AutoSizer>
81
)}
82
</ArrowKeyStepper>
83
</div>
84
);
85
}
86
87
// Controlled arrow key navigation with external state
88
function ControlledNavigableGrid({ data, onPositionChange }) {
89
const [scrollToColumn, setScrollToColumn] = useState(0);
90
const [scrollToRow, setScrollToRow] = useState(0);
91
92
const handleScrollToChange = ({ scrollToColumn, scrollToRow }) => {
93
setScrollToColumn(scrollToColumn);
94
setScrollToRow(scrollToRow);
95
onPositionChange?.(scrollToRow, scrollToColumn);
96
};
97
98
const cellRenderer = ({ columnIndex, key, rowIndex, style }) => {
99
const isSelected = columnIndex === scrollToColumn && rowIndex === scrollToRow;
100
101
return (
102
<div
103
key={key}
104
style={{
105
...style,
106
backgroundColor: isSelected ? '#e3f2fd' : 'white',
107
border: isSelected ? '2px solid #2196f3' : '1px solid #ddd'
108
}}
109
className="grid-cell"
110
>
111
{data[rowIndex][columnIndex]}
112
</div>
113
);
114
};
115
116
return (
117
<div>
118
<div className="position-indicator">
119
Current Position: Row {scrollToRow + 1}, Column {scrollToColumn + 1}
120
</div>
121
122
<div style={{ height: 400, width: 600 }}>
123
<ArrowKeyStepper
124
columnCount={data[0].length}
125
rowCount={data.length}
126
isControlled={true}
127
scrollToColumn={scrollToColumn}
128
scrollToRow={scrollToRow}
129
onScrollToChange={handleScrollToChange}
130
mode="cells"
131
>
132
{({ onSectionRendered }) => (
133
<Grid
134
cellRenderer={cellRenderer}
135
columnCount={data[0].length}
136
columnWidth={80}
137
height={400}
138
onSectionRendered={onSectionRendered}
139
rowCount={data.length}
140
rowHeight={40}
141
scrollToColumn={scrollToColumn}
142
scrollToRow={scrollToRow}
143
width={600}
144
/>
145
)}
146
</ArrowKeyStepper>
147
</div>
148
</div>
149
);
150
}
151
```
152
153
### ScrollSync Component
154
155
Synchronizes scrolling between multiple virtualized components, useful for creating interfaces with linked scrollable areas.
156
157
```javascript { .api }
158
/**
159
* Synchronizes scrolling between multiple components
160
* @param props - ScrollSync configuration
161
*/
162
function ScrollSync(props: {
163
/** Function that renders synchronized scrollable components */
164
children: (params: {
165
clientHeight: number,
166
clientWidth: number,
167
onScroll: (params: {clientHeight: number, clientWidth: number, scrollHeight: number, scrollLeft: number, scrollTop: number, scrollWidth: number}) => void,
168
scrollHeight: number,
169
scrollLeft: number,
170
scrollTop: number,
171
scrollWidth: number
172
}) => React.Node;
173
}): React.Component;
174
```
175
176
**Usage Examples:**
177
178
```javascript
179
import React from 'react';
180
import { ScrollSync, Grid, List, AutoSizer } from 'react-virtualized';
181
182
// Synchronized scrolling between header and data grid
183
function SynchronizedTable({ headers, data }) {
184
const headerRenderer = ({ columnIndex, key, style }) => (
185
<div key={key} style={style} className="header-cell">
186
{headers[columnIndex]}
187
</div>
188
);
189
190
const cellRenderer = ({ columnIndex, key, rowIndex, style }) => (
191
<div key={key} style={style} className="data-cell">
192
{data[rowIndex][columnIndex]}
193
</div>
194
);
195
196
return (
197
<div style={{ height: 400, width: 800 }}>
198
<ScrollSync>
199
{({ clientHeight, clientWidth, onScroll, scrollLeft, scrollTop }) => (
200
<div>
201
{/* Fixed header */}
202
<div style={{ height: 50, width: clientWidth }}>
203
<Grid
204
cellRenderer={headerRenderer}
205
columnCount={headers.length}
206
columnWidth={150}
207
height={50}
208
rowCount={1}
209
rowHeight={50}
210
scrollLeft={scrollLeft}
211
width={clientWidth}
212
/>
213
</div>
214
215
{/* Scrollable data */}
216
<div style={{ height: 350, width: clientWidth }}>
217
<Grid
218
cellRenderer={cellRenderer}
219
columnCount={headers.length}
220
columnWidth={150}
221
height={350}
222
onScroll={onScroll}
223
rowCount={data.length}
224
rowHeight={40}
225
width={clientWidth}
226
/>
227
</div>
228
</div>
229
)}
230
</ScrollSync>
231
</div>
232
);
233
}
234
235
// Multi-panel synchronized scrolling
236
function MultiPanelView({ leftData, rightData }) {
237
const leftCellRenderer = ({ key, rowIndex, style }) => (
238
<div key={key} style={style} className="left-cell">
239
{leftData[rowIndex]}
240
</div>
241
);
242
243
const rightCellRenderer = ({ key, rowIndex, style }) => (
244
<div key={key} style={style} className="right-cell">
245
{rightData[rowIndex]}
246
</div>
247
);
248
249
return (
250
<div style={{ height: 500, width: '100%' }}>
251
<ScrollSync>
252
{({ onScroll, scrollTop }) => (
253
<AutoSizer>
254
{({ height, width }) => (
255
<div style={{ display: 'flex', height, width }}>
256
{/* Left panel */}
257
<div style={{ width: width * 0.3 }}>
258
<List
259
height={height}
260
onScroll={onScroll}
261
rowCount={leftData.length}
262
rowHeight={60}
263
rowRenderer={leftCellRenderer}
264
scrollTop={scrollTop}
265
width={width * 0.3}
266
/>
267
</div>
268
269
{/* Right panel */}
270
<div style={{ width: width * 0.7 }}>
271
<List
272
height={height}
273
onScroll={onScroll}
274
rowCount={rightData.length}
275
rowHeight={60}
276
rowRenderer={rightCellRenderer}
277
scrollTop={scrollTop}
278
width={width * 0.7}
279
/>
280
</div>
281
</div>
282
)}
283
</AutoSizer>
284
)}
285
</ScrollSync>
286
</div>
287
);
288
}
289
```
290
291
### WindowScroller Component
292
293
Enables virtualized components to be scrolled by the browser window instead of an internal scrollable container.
294
295
```javascript { .api }
296
/**
297
* Enables window-based scrolling for components
298
* @param props - WindowScroller configuration
299
*/
300
function WindowScroller(props: {
301
/** Function that renders the window-scrollable component */
302
children: (params: {
303
height: number,
304
isScrolling: boolean,
305
onChildScroll: (params: {scrollTop: number}) => void,
306
scrollTop: number,
307
width: number
308
}) => React.Node;
309
/** Callback when window resizes */
310
onResize?: (params: {height: number, width: number}) => void;
311
/** Callback when window scrolls */
312
onScroll?: (params: {scrollTop: number}) => void;
313
/** Custom scroll element (defaults to window) */
314
scrollElement?: Element;
315
/** Server-side rendering flag */
316
serverHeight?: number;
317
/** Server-side rendering flag */
318
serverWidth?: number;
319
}): React.Component;
320
321
/** Default timeout for scroll detection */
322
const IS_SCROLLING_TIMEOUT: number;
323
```
324
325
**Usage Examples:**
326
327
```javascript
328
import React from 'react';
329
import { WindowScroller, List, AutoSizer } from 'react-virtualized';
330
331
// Basic window scrolling list
332
function WindowScrolledList({ items }) {
333
const rowRenderer = ({ index, key, style }) => (
334
<div key={key} style={style} className="list-item">
335
<h3>Item {index}</h3>
336
<p>{items[index]}</p>
337
</div>
338
);
339
340
return (
341
<WindowScroller>
342
{({ height, isScrolling, onChildScroll, scrollTop, width }) => (
343
<AutoSizer disableHeight>
344
{({ width: autoWidth }) => (
345
<List
346
autoHeight
347
height={height}
348
isScrolling={isScrolling}
349
onScroll={onChildScroll}
350
rowCount={items.length}
351
rowHeight={120}
352
rowRenderer={rowRenderer}
353
scrollTop={scrollTop}
354
width={autoWidth}
355
/>
356
)}
357
</AutoSizer>
358
)}
359
</WindowScroller>
360
);
361
}
362
363
// Window scrolling with header and footer
364
function FullPageList({ items, headerContent, footerContent }) {
365
const rowRenderer = ({ index, key, style }) => (
366
<div key={key} style={style} className="full-page-item">
367
<div className="item-content">
368
<h4>{items[index].title}</h4>
369
<p>{items[index].description}</p>
370
<div className="item-meta">
371
<span>{items[index].date}</span>
372
<span>{items[index].author}</span>
373
</div>
374
</div>
375
</div>
376
);
377
378
return (
379
<div>
380
{/* Page header */}
381
<header style={{ height: 80, padding: 20, backgroundColor: '#f5f5f5' }}>
382
{headerContent}
383
</header>
384
385
{/* Window-scrolled content */}
386
<WindowScroller>
387
{({ height, isScrolling, onChildScroll, scrollTop, width }) => (
388
<div style={{ width: '100%' }}>
389
<List
390
autoHeight
391
height={height}
392
isScrolling={isScrolling}
393
onScroll={onChildScroll}
394
rowCount={items.length}
395
rowHeight={150}
396
rowRenderer={rowRenderer}
397
scrollTop={scrollTop}
398
width={width}
399
style={{ outline: 'none' }}
400
/>
401
</div>
402
)}
403
</WindowScroller>
404
405
{/* Page footer */}
406
<footer style={{ height: 60, padding: 20, backgroundColor: '#f5f5f5' }}>
407
{footerContent}
408
</footer>
409
</div>
410
);
411
}
412
413
// Window scrolling with scroll monitoring
414
function MonitoredWindowList({ items, onScrollChange }) {
415
const handleScroll = ({ scrollTop }) => {
416
onScrollChange?.(scrollTop);
417
};
418
419
const handleResize = ({ height, width }) => {
420
console.log(`Window resized to ${width}x${height}`);
421
};
422
423
const rowRenderer = ({ index, key, style }) => (
424
<div key={key} style={style} className="monitored-item">
425
<div className="item-number">#{index + 1}</div>
426
<div className="item-body">
427
<h3>{items[index].title}</h3>
428
<p>{items[index].content}</p>
429
</div>
430
</div>
431
);
432
433
return (
434
<div className="page-container">
435
<div className="content-header">
436
<h1>Scrollable Content</h1>
437
<p>This list scrolls with the browser window</p>
438
</div>
439
440
<WindowScroller
441
onScroll={handleScroll}
442
onResize={handleResize}
443
>
444
{({ height, isScrolling, onChildScroll, scrollTop, width }) => (
445
<div style={{ maxWidth: 800, margin: '0 auto' }}>
446
<List
447
autoHeight
448
height={height}
449
isScrolling={isScrolling}
450
onScroll={onChildScroll}
451
rowCount={items.length}
452
rowHeight={100}
453
rowRenderer={rowRenderer}
454
scrollTop={scrollTop}
455
width={Math.min(width, 800)}
456
/>
457
</div>
458
)}
459
</WindowScroller>
460
</div>
461
);
462
}
463
```