0
# Collections and Data Structures
1
2
Generic collection interfaces supporting lists, grids, trees, and other data structures with keyboard navigation, sorting, expansion, loading states, and layout management.
3
4
## Capabilities
5
6
### Core Collection Interface
7
8
The fundamental collection interface that provides iteration and key-based access to items.
9
10
```typescript { .api }
11
/**
12
* A generic interface to access a readonly sequential collection of unique keyed items
13
* @template T The type of items in the collection
14
*/
15
interface Collection<T> extends Iterable<T> {
16
/** The number of items in the collection */
17
readonly size: number;
18
19
/** Iterate over all keys in the collection */
20
getKeys(): Iterable<Key>;
21
22
/** Get an item by its key */
23
getItem(key: Key): T | null;
24
25
/** Get an item by the index of its key */
26
at(idx: number): T | null;
27
28
/** Get the key that comes before the given key in the collection */
29
getKeyBefore(key: Key): Key | null;
30
31
/** Get the key that comes after the given key in the collection */
32
getKeyAfter(key: Key): Key | null;
33
34
/** Get the first key in the collection */
35
getFirstKey(): Key | null;
36
37
/** Get the last key in the collection */
38
getLastKey(): Key | null;
39
40
/** Iterate over the child items of the given key */
41
getChildren?(key: Key): Iterable<T>;
42
43
/** Returns a string representation of the item's contents */
44
getTextValue?(key: Key): string;
45
46
/** Filters the collection using the given function */
47
filter?(filterFn: (nodeValue: string, node: T) => boolean): Collection<T>;
48
}
49
```
50
51
### Collection Nodes
52
53
Nodes represent individual items within a collection with metadata and hierarchy support.
54
55
```typescript { .api }
56
/**
57
* Represents a node in a collection
58
* @template T The type of the node's value
59
*/
60
interface Node<T> {
61
/** The type of item this node represents */
62
type: string;
63
/** A unique key for the node */
64
key: Key;
65
/** The object value the node was created from */
66
value: T | null;
67
/** The level of depth this node is at in the hierarchy */
68
level: number;
69
/** Whether this item has children, even if not loaded yet */
70
hasChildNodes: boolean;
71
/**
72
* The loaded children of this node
73
* @deprecated Use collection.getChildren(node.key) instead
74
*/
75
childNodes: Iterable<Node<T>>;
76
/** The rendered contents of this node (e.g. JSX) */
77
rendered: ReactNode;
78
/** A string value for this node, used for features like typeahead */
79
textValue: string;
80
/** An accessibility label for this node */
81
"aria-label"?: string;
82
/** The index of this node within its parent */
83
index: number;
84
/** A function that should be called to wrap the rendered node */
85
wrapper?: (element: ReactElement) => ReactElement;
86
/** The key of the parent node */
87
parentKey?: Key | null;
88
/** The key of the node before this node */
89
prevKey?: Key | null;
90
/** The key of the node after this node */
91
nextKey?: Key | null;
92
/** Additional properties specific to a particular node type */
93
props?: any;
94
/** @private */
95
shouldInvalidate?: (context: any) => boolean;
96
/** A function that renders this node to a React Element in the DOM */
97
render?: (node: Node<any>) => ReactElement;
98
}
99
```
100
101
### Items and Sections
102
103
Building blocks for creating collection content with React elements.
104
105
```typescript { .api }
106
/**
107
* Properties for collection items
108
* @template T The type of the item data
109
*/
110
interface ItemProps<T> extends LinkDOMProps {
111
/** Rendered contents of the item or child items */
112
children: ReactNode;
113
/** Rendered contents of the item if children contains child items */
114
title?: ReactNode;
115
/** A string representation of the item's contents, used for features like typeahead */
116
textValue?: string;
117
/** An accessibility label for this item */
118
"aria-label"?: string;
119
/** A list of child item objects. Used for dynamic collections */
120
childItems?: Iterable<T>;
121
/** Whether this item has children, even if not loaded yet */
122
hasChildItems?: boolean;
123
}
124
125
/**
126
* Properties for collection sections
127
* @template T The type of the item data
128
*/
129
interface SectionProps<T> {
130
/** Rendered contents of the section, e.g. a header */
131
title?: ReactNode;
132
/** An accessibility label for the section */
133
"aria-label"?: string;
134
/** Static child items or a function to render children */
135
children: ItemElement<T> | ItemElement<T>[] | ItemRenderer<T>;
136
/** Item objects in the section */
137
items?: Iterable<T>;
138
}
139
140
/** React element representing an item */
141
type ItemElement<T> = ReactElement<ItemProps<T>> | null;
142
143
/** Function that renders an item */
144
type ItemRenderer<T> = (item: T) => ItemElement<T>;
145
146
/** React element representing a section */
147
type SectionElement<T> = ReactElement<SectionProps<T>> | null;
148
149
/** Union of collection elements */
150
type CollectionElement<T> = SectionElement<T> | ItemElement<T>;
151
152
/** Children content for collections */
153
type CollectionChildren<T> = CollectionElement<T> | CollectionElement<T>[] | ((item: T) => CollectionElement<T>);
154
```
155
156
### Collection Base Properties
157
158
Base properties for collections with static and dynamic content support.
159
160
```typescript { .api }
161
/**
162
* Base properties for collections
163
* @template T The type of items in the collection
164
*/
165
interface CollectionBase<T> {
166
/** The contents of the collection */
167
children: CollectionChildren<T>;
168
/** Item objects in the collection */
169
items?: Iterable<T>;
170
/** The item keys that are disabled. These items cannot be selected, focused, or otherwise interacted with */
171
disabledKeys?: Iterable<Key>;
172
}
173
174
/**
175
* Base properties for collection state
176
* @template T The type of items in the collection
177
* @template C The type of the collection
178
*/
179
interface CollectionStateBase<T, C extends Collection<Node<T>> = Collection<Node<T>>> extends Partial<CollectionBase<T>> {
180
/** A pre-constructed collection to use instead of building one from items and children */
181
collection?: C;
182
}
183
```
184
185
### Loading States
186
187
Support for asynchronous collections with loading indicators.
188
189
```typescript { .api }
190
/** Loading states for collections */
191
type LoadingState = "loading" | "sorting" | "loadingMore" | "error" | "idle" | "filtering";
192
193
/**
194
* Properties for collections that support async loading
195
*/
196
interface AsyncLoadable {
197
/** Whether the items are currently loading */
198
isLoading?: boolean;
199
/** Handler that is called when more items should be loaded, e.g. while scrolling near the bottom */
200
onLoadMore?: () => any;
201
}
202
```
203
204
### Expandable Collections
205
206
Support for hierarchical collections with expand/collapse functionality.
207
208
```typescript { .api }
209
/**
210
* Properties for expandable collections (trees)
211
*/
212
interface Expandable {
213
/** The currently expanded keys in the collection (controlled) */
214
expandedKeys?: Iterable<Key>;
215
/** The initial expanded keys in the collection (uncontrolled) */
216
defaultExpandedKeys?: Iterable<Key>;
217
/** Handler that is called when items are expanded or collapsed */
218
onExpandedChange?: (keys: Set<Key>) => any;
219
}
220
```
221
222
### Sortable Collections
223
224
Support for collections with sorting functionality.
225
226
```typescript { .api }
227
/** Sort direction options */
228
type SortDirection = "ascending" | "descending";
229
230
/**
231
* Sort descriptor defining how to sort a collection
232
*/
233
interface SortDescriptor {
234
/** The key of the column to sort by */
235
column: Key;
236
/** The direction to sort by */
237
direction: SortDirection;
238
}
239
240
/**
241
* Properties for sortable collections
242
*/
243
interface Sortable {
244
/** The current sorted column and direction */
245
sortDescriptor?: SortDescriptor;
246
/** Handler that is called when the sorted column or direction changes */
247
onSortChange?: (descriptor: SortDescriptor) => any;
248
}
249
```
250
251
### Keyboard Navigation
252
253
Interfaces for custom keyboard navigation within collections.
254
255
```typescript { .api }
256
/**
257
* Keyboard navigation delegate for custom key handling
258
*/
259
interface KeyboardDelegate {
260
/** Returns the key visually below the given one, or null for none */
261
getKeyBelow?(key: Key): Key | null;
262
263
/** Returns the key visually above the given one, or null for none */
264
getKeyAbove?(key: Key): Key | null;
265
266
/** Returns the key visually to the left of the given one, or null for none */
267
getKeyLeftOf?(key: Key): Key | null;
268
269
/** Returns the key visually to the right of the given one, or null for none */
270
getKeyRightOf?(key: Key): Key | null;
271
272
/** Returns the key visually one page below the given one, or null for none */
273
getKeyPageBelow?(key: Key): Key | null;
274
275
/** Returns the key visually one page above the given one, or null for none */
276
getKeyPageAbove?(key: Key): Key | null;
277
278
/** Returns the first key, or null for none */
279
getFirstKey?(key?: Key | null, global?: boolean): Key | null;
280
281
/** Returns the last key, or null for none */
282
getLastKey?(key?: Key | null, global?: boolean): Key | null;
283
284
/** Returns the next key after fromKey that matches the given search string, or null for none */
285
getKeyForSearch?(search: string, fromKey?: Key | null): Key | null;
286
}
287
```
288
289
### Layout Management
290
291
Interfaces for managing collection layout and virtualization.
292
293
```typescript { .api }
294
/**
295
* Rectangle type for layout calculations
296
*/
297
interface Rect {
298
x: number;
299
y: number;
300
width: number;
301
height: number;
302
}
303
304
/**
305
* Size type for dimensions
306
*/
307
interface Size {
308
width: number;
309
height: number;
310
}
311
312
/**
313
* Layout delegate provides layout information for collection items
314
*/
315
interface LayoutDelegate {
316
/** Returns a rectangle for the item with the given key */
317
getItemRect(key: Key): Rect | null;
318
/** Returns the visible rectangle of the collection */
319
getVisibleRect(): Rect;
320
/** Returns the size of the scrollable content in the collection */
321
getContentSize(): Size;
322
/** Returns a list of keys between from and to */
323
getKeyRange?(from: Key, to: Key): Key[];
324
}
325
```
326
327
**Usage Examples:**
328
329
```typescript
330
import {
331
Collection,
332
Node,
333
CollectionBase,
334
ItemProps,
335
SectionProps,
336
Expandable,
337
Sortable,
338
AsyncLoadable,
339
SortDescriptor,
340
Key
341
} from "@react-types/shared";
342
343
// Basic list component with collection support
344
interface ListProps<T> extends CollectionBase<T>, AsyncLoadable {
345
onAction?: (key: Key) => void;
346
}
347
348
function List<T>({
349
children,
350
items,
351
disabledKeys,
352
isLoading,
353
onLoadMore,
354
onAction
355
}: ListProps<T>) {
356
// In a real implementation, you'd build a collection from children/items
357
// This is a simplified example showing the interface usage
358
359
return (
360
<div role="list">
361
{isLoading && <div>Loading...</div>}
362
{/* Render collection items */}
363
<div>Collection items would be rendered here</div>
364
{onLoadMore && (
365
<button onClick={onLoadMore}>Load More</button>
366
)}
367
</div>
368
);
369
}
370
371
// Tree component with expansion support
372
interface TreeProps<T> extends CollectionBase<T>, Expandable {
373
onAction?: (key: Key) => void;
374
}
375
376
function Tree<T>({
377
children,
378
items,
379
disabledKeys,
380
expandedKeys,
381
defaultExpandedKeys,
382
onExpandedChange,
383
onAction
384
}: TreeProps<T>) {
385
const [expanded, setExpanded] = useState<Set<Key>>(
386
new Set(defaultExpandedKeys || [])
387
);
388
389
const handleToggleExpanded = (key: Key) => {
390
const newExpanded = new Set(expandedKeys || expanded);
391
if (newExpanded.has(key)) {
392
newExpanded.delete(key);
393
} else {
394
newExpanded.add(key);
395
}
396
setExpanded(newExpanded);
397
onExpandedChange?.(newExpanded);
398
};
399
400
return (
401
<div role="tree">
402
{/* Tree implementation would render nodes with expand/collapse controls */}
403
<div>Tree nodes would be rendered here</div>
404
</div>
405
);
406
}
407
408
// Table component with sorting support
409
interface TableProps<T> extends CollectionBase<T>, Sortable {
410
columns: Array<{
411
key: Key;
412
name: string;
413
allowsSorting?: boolean;
414
}>;
415
}
416
417
function Table<T>({
418
children,
419
items,
420
columns,
421
sortDescriptor,
422
onSortChange
423
}: TableProps<T>) {
424
const handleSort = (column: Key) => {
425
const newDescriptor: SortDescriptor = {
426
column,
427
direction:
428
sortDescriptor?.column === column && sortDescriptor.direction === "ascending"
429
? "descending"
430
: "ascending"
431
};
432
onSortChange?.(newDescriptor);
433
};
434
435
return (
436
<table>
437
<thead>
438
<tr>
439
{columns.map(column => (
440
<th
441
key={column.key}
442
onClick={() => column.allowsSorting && handleSort(column.key)}
443
style={{
444
cursor: column.allowsSorting ? "pointer" : "default"
445
}}
446
>
447
{column.name}
448
{sortDescriptor?.column === column.key && (
449
<span>{sortDescriptor.direction === "ascending" ? " ↑" : " ↓"}</span>
450
)}
451
</th>
452
))}
453
</tr>
454
</thead>
455
<tbody>
456
{/* Table rows would be rendered here */}
457
</tbody>
458
</table>
459
);
460
}
461
462
// Usage with static content
463
function StaticListExample() {
464
return (
465
<List>
466
<Item key="1">First item</Item>
467
<Item key="2">Second item</Item>
468
<Section title="Group A">
469
<Item key="3">Third item</Item>
470
<Item key="4">Fourth item</Item>
471
</Section>
472
</List>
473
);
474
}
475
476
// Usage with dynamic content
477
function DynamicListExample() {
478
const items = [
479
{ id: 1, name: "Apple", category: "Fruit" },
480
{ id: 2, name: "Carrot", category: "Vegetable" },
481
{ id: 3, name: "Banana", category: "Fruit" }
482
];
483
484
return (
485
<List items={items} disabledKeys={[2]}>
486
{(item) => (
487
<Item key={item.id} textValue={item.name}>
488
{item.name} ({item.category})
489
</Item>
490
)}
491
</List>
492
);
493
}
494
495
// Tree with expansion
496
function TreeExample() {
497
const treeData = [
498
{
499
id: 1,
500
name: "Documents",
501
children: [
502
{ id: 2, name: "Resume.pdf" },
503
{ id: 3, name: "Cover Letter.doc" }
504
]
505
},
506
{
507
id: 4,
508
name: "Images",
509
children: [
510
{ id: 5, name: "Photo1.jpg" },
511
{ id: 6, name: "Photo2.png" }
512
]
513
}
514
];
515
516
return (
517
<Tree
518
items={treeData}
519
defaultExpandedKeys={[1]}
520
onExpandedChange={(keys) => console.log("Expanded keys:", keys)}
521
>
522
{(item) => (
523
<Item
524
key={item.id}
525
hasChildItems={!!item.children?.length}
526
childItems={item.children}
527
>
528
{item.name}
529
</Item>
530
)}
531
</Tree>
532
);
533
}
534
535
// Sortable table
536
function SortableTableExample() {
537
const data = [
538
{ id: 1, name: "Alice", age: 30, department: "Engineering" },
539
{ id: 2, name: "Bob", age: 25, department: "Design" },
540
{ id: 3, name: "Carol", age: 35, department: "Marketing" }
541
];
542
543
const columns = [
544
{ key: "name", name: "Name", allowsSorting: true },
545
{ key: "age", name: "Age", allowsSorting: true },
546
{ key: "department", name: "Department", allowsSorting: false }
547
];
548
549
const [sortDescriptor, setSortDescriptor] = useState<SortDescriptor>({
550
column: "name",
551
direction: "ascending"
552
});
553
554
return (
555
<Table
556
items={data}
557
columns={columns}
558
sortDescriptor={sortDescriptor}
559
onSortChange={setSortDescriptor}
560
>
561
{(item) => (
562
<Row key={item.id}>
563
<Cell>{item.name}</Cell>
564
<Cell>{item.age}</Cell>
565
<Cell>{item.department}</Cell>
566
</Row>
567
)}
568
</Table>
569
);
570
}
571
```