@ceed/ads 1.23.3 → 1.23.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/data-display/Badge.md +71 -39
- package/dist/components/data-display/InfoSign.md +74 -98
- package/dist/components/data-display/Typography.md +310 -61
- package/dist/components/feedback/CircularProgress.md +257 -0
- package/dist/components/feedback/Skeleton.md +280 -0
- package/dist/components/feedback/llms.txt +2 -0
- package/dist/components/inputs/ButtonGroup.md +115 -106
- package/dist/components/inputs/Calendar.md +98 -459
- package/dist/components/inputs/CurrencyInput.md +181 -8
- package/dist/components/inputs/DatePicker.md +108 -436
- package/dist/components/inputs/DateRangePicker.md +130 -496
- package/dist/components/inputs/FilterMenu.md +169 -19
- package/dist/components/inputs/FilterableCheckboxGroup.md +119 -24
- package/dist/components/inputs/FormControl.md +368 -0
- package/dist/components/inputs/IconButton.md +137 -88
- package/dist/components/inputs/MonthPicker.md +95 -427
- package/dist/components/inputs/MonthRangePicker.md +89 -471
- package/dist/components/inputs/PercentageInput.md +183 -19
- package/dist/components/inputs/RadioButton.md +163 -35
- package/dist/components/inputs/RadioList.md +241 -0
- package/dist/components/inputs/RadioTileGroup.md +146 -62
- package/dist/components/inputs/Select.md +219 -328
- package/dist/components/inputs/Slider.md +334 -0
- package/dist/components/inputs/Switch.md +136 -376
- package/dist/components/inputs/Textarea.md +209 -11
- package/dist/components/inputs/Uploader/Uploader.md +145 -66
- package/dist/components/inputs/llms.txt +3 -0
- package/dist/components/navigation/Breadcrumbs.md +80 -322
- package/dist/components/navigation/Dropdown.md +92 -221
- package/dist/components/navigation/IconMenuButton.md +40 -502
- package/dist/components/navigation/InsetDrawer.md +68 -738
- package/dist/components/navigation/Link.md +39 -298
- package/dist/components/navigation/Menu.md +92 -285
- package/dist/components/navigation/MenuButton.md +55 -448
- package/dist/components/navigation/Pagination.md +47 -338
- package/dist/components/navigation/ProfileMenu.md +45 -268
- package/dist/components/navigation/Stepper.md +160 -28
- package/dist/components/navigation/Tabs.md +57 -316
- package/dist/components/surfaces/Sheet.md +150 -333
- package/dist/guides/ThemeProvider.md +116 -0
- package/dist/guides/llms.txt +9 -0
- package/dist/llms.txt +8 -0
- package/package.json +1 -1
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
## Introduction
|
|
4
4
|
|
|
5
|
-
Pagination
|
|
5
|
+
The Pagination component enables users to navigate through large datasets or content collections that are divided into discrete pages. It provides controls for moving forward and backward, jumping to specific pages, and understanding the current position within the total set.
|
|
6
|
+
|
|
7
|
+
Pagination ships in two variants -- **standard** (clickable page numbers) and **compact** (dropdown selector) -- making it suitable for different screen sizes and layout constraints. It supports both controlled and uncontrolled usage patterns to integrate smoothly with any state management approach.
|
|
6
8
|
|
|
7
9
|
```tsx
|
|
8
10
|
<Stack spacing={4}>
|
|
@@ -44,28 +46,11 @@ function DataList() {
|
|
|
44
46
|
}
|
|
45
47
|
```
|
|
46
48
|
|
|
47
|
-
##
|
|
48
|
-
|
|
49
|
-
### Playground
|
|
50
|
-
|
|
51
|
-
Both standard and compact variants displayed together.
|
|
52
|
-
|
|
53
|
-
```tsx
|
|
54
|
-
<Stack spacing={4}>
|
|
55
|
-
<Stack spacing={1}>
|
|
56
|
-
<div>Standard</div>
|
|
57
|
-
<Pagination {...args} variant="standard" />
|
|
58
|
-
</Stack>
|
|
59
|
-
<Stack spacing={1}>
|
|
60
|
-
<div>Compact</div>
|
|
61
|
-
<Pagination {...args} variant="compact" />
|
|
62
|
-
</Stack>
|
|
63
|
-
</Stack>
|
|
64
|
-
```
|
|
49
|
+
## Features
|
|
65
50
|
|
|
66
51
|
### Sizes
|
|
67
52
|
|
|
68
|
-
Pagination
|
|
53
|
+
Pagination is available in three sizes -- `sm`, `md`, and `lg` -- for both the standard and compact variants.
|
|
69
54
|
|
|
70
55
|
```tsx
|
|
71
56
|
<Stack spacing={4}>
|
|
@@ -84,9 +69,9 @@ Pagination supports three sizes for different layouts.
|
|
|
84
69
|
</Stack>
|
|
85
70
|
```
|
|
86
71
|
|
|
87
|
-
###
|
|
72
|
+
### Page Positions
|
|
88
73
|
|
|
89
|
-
|
|
74
|
+
The component correctly handles the visual presentation when the user is at the beginning, middle, or end of the page range, adjusting ellipsis buttons and disabled states accordingly.
|
|
90
75
|
|
|
91
76
|
```tsx
|
|
92
77
|
<Stack spacing={4}>
|
|
@@ -123,25 +108,25 @@ Navigation through different page positions.
|
|
|
123
108
|
</Stack>
|
|
124
109
|
```
|
|
125
110
|
|
|
126
|
-
### Uncontrolled
|
|
111
|
+
### Uncontrolled Mode
|
|
127
112
|
|
|
128
|
-
|
|
113
|
+
In uncontrolled mode the component manages its own page state internally. Pass `defaultPaginationModel` to set the initial state and use `onPageChange` to react to changes.
|
|
129
114
|
|
|
130
115
|
```tsx
|
|
131
116
|
<Pagination {...args} />
|
|
132
117
|
```
|
|
133
118
|
|
|
134
|
-
### Controlled
|
|
119
|
+
### Controlled Mode
|
|
135
120
|
|
|
136
|
-
|
|
121
|
+
In controlled mode the parent component owns the page state via `paginationModel` and updates it through `onPageChange`. This gives full control over navigation behavior.
|
|
137
122
|
|
|
138
123
|
```tsx
|
|
139
124
|
<Pagination {...args} paginationModel={paginationModel} onPageChange={handlePageChange} />
|
|
140
125
|
```
|
|
141
126
|
|
|
142
|
-
###
|
|
127
|
+
### Dynamic Row Count
|
|
143
128
|
|
|
144
|
-
Pagination automatically adjusts when total items
|
|
129
|
+
Pagination automatically adjusts when the total number of items changes. If the current page exceeds the new page count, the component clamps to the last available page.
|
|
145
130
|
|
|
146
131
|
```tsx
|
|
147
132
|
<Stack gap={2}>
|
|
@@ -153,40 +138,23 @@ Pagination automatically adjusts when total items change.
|
|
|
153
138
|
</Stack>
|
|
154
139
|
```
|
|
155
140
|
|
|
156
|
-
## When to Use
|
|
157
|
-
|
|
158
|
-
### ✅ Good Use Cases
|
|
159
|
-
|
|
160
|
-
- **Data tables**: Navigate through paginated table data
|
|
161
|
-
- **Search results**: Browse through multiple pages of results
|
|
162
|
-
- **Content listings**: Articles, products, or any list content
|
|
163
|
-
- **Admin panels**: Managing records across multiple pages
|
|
164
|
-
- **API data**: When server returns paginated responses
|
|
165
|
-
|
|
166
|
-
### ❌ When Not to Use
|
|
167
|
-
|
|
168
|
-
- **Infinite scroll**: When content loads continuously on scroll
|
|
169
|
-
- **Small datasets**: Less than 20 items don't need pagination
|
|
170
|
-
- **Single content**: Individual articles or detail views
|
|
171
|
-
- **Sequential flows**: Use Stepper for multi-step processes
|
|
172
|
-
- **Real-time data**: Frequently updating data may shift pages unpredictably
|
|
173
|
-
|
|
174
141
|
## Common Use Cases
|
|
175
142
|
|
|
176
143
|
### Data Table Pagination
|
|
177
144
|
|
|
178
145
|
```tsx
|
|
179
|
-
function DataTable({ columns
|
|
146
|
+
function DataTable({ columns }) {
|
|
180
147
|
const [page, setPage] = useState(1);
|
|
181
|
-
const [pageSize
|
|
148
|
+
const [pageSize] = useState(25);
|
|
182
149
|
const { data, totalCount, isLoading } = useFetchData(page, pageSize);
|
|
183
150
|
|
|
184
151
|
return (
|
|
185
152
|
<Box>
|
|
186
153
|
<Table columns={columns} data={data} loading={isLoading} />
|
|
187
|
-
<Stack direction="row" justifyContent="space-between" mt={2}>
|
|
154
|
+
<Stack direction="row" justifyContent="space-between" alignItems="center" mt={2}>
|
|
188
155
|
<Typography level="body-sm">
|
|
189
|
-
Showing {(page - 1) * pageSize + 1}
|
|
156
|
+
Showing {(page - 1) * pageSize + 1}--{Math.min(page * pageSize, totalCount)} of{' '}
|
|
157
|
+
{totalCount}
|
|
190
158
|
</Typography>
|
|
191
159
|
<Pagination
|
|
192
160
|
rowCount={totalCount}
|
|
@@ -199,14 +167,13 @@ function DataTable({ columns, fetchData }) {
|
|
|
199
167
|
}
|
|
200
168
|
```
|
|
201
169
|
|
|
202
|
-
### Search Results
|
|
170
|
+
### Search Results with Compact Variant
|
|
203
171
|
|
|
204
172
|
```tsx
|
|
205
173
|
function SearchResults({ query }) {
|
|
206
174
|
const [page, setPage] = useState(1);
|
|
207
175
|
const { results, totalCount } = useSearch(query, page);
|
|
208
176
|
|
|
209
|
-
// Reset to page 1 when query changes
|
|
210
177
|
useEffect(() => {
|
|
211
178
|
setPage(1);
|
|
212
179
|
}, [query]);
|
|
@@ -217,7 +184,7 @@ function SearchResults({ query }) {
|
|
|
217
184
|
<ResultList results={results} />
|
|
218
185
|
<Pagination
|
|
219
186
|
rowCount={totalCount}
|
|
220
|
-
|
|
187
|
+
paginationModel={{ page, pageSize: 10 }}
|
|
221
188
|
onPageChange={setPage}
|
|
222
189
|
variant="compact"
|
|
223
190
|
/>
|
|
@@ -226,322 +193,64 @@ function SearchResults({ query }) {
|
|
|
226
193
|
}
|
|
227
194
|
```
|
|
228
195
|
|
|
229
|
-
###
|
|
196
|
+
### URL-Synchronized Pagination
|
|
230
197
|
|
|
231
198
|
```tsx
|
|
232
|
-
function
|
|
233
|
-
const
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
const paginatedImages = useMemo(() => {
|
|
237
|
-
const start = (currentPage - 1) * pageSize;
|
|
238
|
-
return images.slice(start, start + pageSize);
|
|
239
|
-
}, [images, currentPage, pageSize]);
|
|
240
|
-
|
|
241
|
-
return (
|
|
242
|
-
<Box>
|
|
243
|
-
<Grid container spacing={2}>
|
|
244
|
-
{paginatedImages.map((image) => (
|
|
245
|
-
<Grid key={image.id} xs={4}>
|
|
246
|
-
<ImageCard image={image} />
|
|
247
|
-
</Grid>
|
|
248
|
-
))}
|
|
249
|
-
</Grid>
|
|
250
|
-
<Box display="flex" justifyContent="center" mt={3}>
|
|
251
|
-
<Pagination
|
|
252
|
-
rowCount={images.length}
|
|
253
|
-
paginationModel={{ page: currentPage, pageSize }}
|
|
254
|
-
onPageChange={setCurrentPage}
|
|
255
|
-
/>
|
|
256
|
-
</Box>
|
|
257
|
-
</Box>
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
### API Pagination
|
|
263
|
-
|
|
264
|
-
```tsx
|
|
265
|
-
function APIList() {
|
|
266
|
-
const [paginationModel, setPaginationModel] = useState({
|
|
267
|
-
page: 1,
|
|
268
|
-
pageSize: 20,
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
const { data } = useQuery({
|
|
272
|
-
queryKey: ['items', paginationModel],
|
|
273
|
-
queryFn: () => fetchItems({
|
|
274
|
-
page: paginationModel.page,
|
|
275
|
-
limit: paginationModel.pageSize,
|
|
276
|
-
}),
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
return (
|
|
280
|
-
<Box>
|
|
281
|
-
<ItemList items={data?.items || []} />
|
|
282
|
-
<Pagination
|
|
283
|
-
rowCount={data?.totalCount || 0}
|
|
284
|
-
paginationModel={paginationModel}
|
|
285
|
-
onPageChange={(newPage) =>
|
|
286
|
-
setPaginationModel((prev) => ({ ...prev, page: newPage }))
|
|
287
|
-
}
|
|
288
|
-
/>
|
|
289
|
-
</Box>
|
|
290
|
-
);
|
|
291
|
-
}
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
### Compact Pagination for Mobile
|
|
199
|
+
function URLPaginatedList() {
|
|
200
|
+
const [searchParams, setSearchParams] = useSearchParams();
|
|
201
|
+
const page = parseInt(searchParams.get('page') || '1', 10);
|
|
295
202
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
203
|
+
const handlePageChange = (newPage: number) => {
|
|
204
|
+
setSearchParams({ page: String(newPage) });
|
|
205
|
+
};
|
|
299
206
|
|
|
300
207
|
return (
|
|
301
208
|
<Pagination
|
|
302
|
-
rowCount={
|
|
303
|
-
paginationModel={{ page, pageSize:
|
|
304
|
-
onPageChange={
|
|
305
|
-
variant={isMobile ? 'compact' : 'standard'}
|
|
306
|
-
size={isMobile ? 'sm' : 'md'}
|
|
209
|
+
rowCount={totalCount}
|
|
210
|
+
paginationModel={{ page, pageSize: 25 }}
|
|
211
|
+
onPageChange={handlePageChange}
|
|
307
212
|
/>
|
|
308
213
|
);
|
|
309
214
|
}
|
|
310
215
|
```
|
|
311
216
|
|
|
312
|
-
## Props and Customization
|
|
313
|
-
|
|
314
|
-
### Key Props
|
|
315
|
-
|
|
316
|
-
| Prop | Type | Default | Description |
|
|
317
|
-
| ------------------------ | ------------------------------------ | --------------------------- | ---------------------------- |
|
|
318
|
-
| `rowCount` | `number` | - | Total number of items |
|
|
319
|
-
| `paginationModel` | `{ page: number; pageSize: number }` | - | Controlled pagination state |
|
|
320
|
-
| `defaultPaginationModel` | `{ page: number; pageSize: number }` | `{ page: 1, pageSize: 25 }` | Default state (uncontrolled) |
|
|
321
|
-
| `onPageChange` | `(newPage: number) => void` | - | Callback when page changes |
|
|
322
|
-
| `variant` | `'standard' \| 'compact'` | `'standard'` | Pagination style |
|
|
323
|
-
| `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Component size |
|
|
324
|
-
|
|
325
|
-
### Variant Comparison
|
|
326
|
-
|
|
327
|
-
| Feature | Standard | Compact |
|
|
328
|
-
| -------------------- | ------------ | ------------------- |
|
|
329
|
-
| Page numbers visible | Yes | No |
|
|
330
|
-
| Quick page jump | Click number | Dropdown select |
|
|
331
|
-
| Space required | More | Less |
|
|
332
|
-
| Best for | Desktop | Mobile/tight spaces |
|
|
333
|
-
|
|
334
|
-
### Controlled vs Uncontrolled
|
|
335
|
-
|
|
336
|
-
```tsx
|
|
337
|
-
// Uncontrolled - component manages state
|
|
338
|
-
<Pagination
|
|
339
|
-
rowCount={100}
|
|
340
|
-
defaultPaginationModel={{ page: 1, pageSize: 10 }}
|
|
341
|
-
onPageChange={(page) => console.log('Page:', page)}
|
|
342
|
-
/>
|
|
343
|
-
|
|
344
|
-
// Controlled - you manage state
|
|
345
|
-
const [page, setPage] = useState(1);
|
|
346
|
-
<Pagination
|
|
347
|
-
rowCount={100}
|
|
348
|
-
paginationModel={{ page, pageSize: 10 }}
|
|
349
|
-
onPageChange={setPage}
|
|
350
|
-
/>
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
### Page Calculation
|
|
354
|
-
|
|
355
|
-
The component automatically calculates total pages:
|
|
356
|
-
|
|
357
|
-
```tsx
|
|
358
|
-
// With 95 items and pageSize of 10:
|
|
359
|
-
// totalPages = Math.ceil(95 / 10) = 10 pages
|
|
360
|
-
|
|
361
|
-
<Pagination
|
|
362
|
-
rowCount={95}
|
|
363
|
-
paginationModel={{ page: 1, pageSize: 10 }}
|
|
364
|
-
onPageChange={handleChange}
|
|
365
|
-
/>
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
## Accessibility
|
|
369
|
-
|
|
370
|
-
Pagination includes built-in accessibility features:
|
|
371
|
-
|
|
372
|
-
### ARIA Attributes
|
|
373
|
-
|
|
374
|
-
- Current page marked with `aria-current="page"`
|
|
375
|
-
- Navigation buttons have descriptive `aria-label` attributes
|
|
376
|
-
- Disabled buttons properly marked with `disabled`
|
|
377
|
-
|
|
378
|
-
### Keyboard Navigation
|
|
379
|
-
|
|
380
|
-
- **Tab**: Move through pagination controls
|
|
381
|
-
- **Enter/Space**: Activate focused button
|
|
382
|
-
- **Arrow Keys**: Navigate within the compact dropdown
|
|
383
|
-
|
|
384
|
-
### Screen Reader Support
|
|
385
|
-
|
|
386
|
-
```tsx
|
|
387
|
-
// Buttons announce their purpose
|
|
388
|
-
<button aria-label="Previous page">←</button>
|
|
389
|
-
<button aria-label="Next page">→</button>
|
|
390
|
-
<button aria-label="More previous pages">...</button>
|
|
391
|
-
<button aria-current="page">5</button> // "Page 5, current"
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
### Status Announcements
|
|
395
|
-
|
|
396
|
-
Consider adding live region for page changes:
|
|
397
|
-
|
|
398
|
-
```tsx
|
|
399
|
-
function PaginatedList() {
|
|
400
|
-
const [page, setPage] = useState(1);
|
|
401
|
-
|
|
402
|
-
return (
|
|
403
|
-
<>
|
|
404
|
-
<div aria-live="polite" className="sr-only">
|
|
405
|
-
Page {page} of {totalPages}
|
|
406
|
-
</div>
|
|
407
|
-
<Pagination onPageChange={setPage} />
|
|
408
|
-
</>
|
|
409
|
-
);
|
|
410
|
-
}
|
|
411
|
-
```
|
|
412
|
-
|
|
413
217
|
## Best Practices
|
|
414
218
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
1. **Show context**: Display total items and current range
|
|
219
|
+
- **Always show context.** Display the total item count and current range (for example "Showing 1--25 of 250") alongside the pagination controls so users understand the scope of the data.
|
|
418
220
|
|
|
419
221
|
```tsx
|
|
420
|
-
//
|
|
222
|
+
// Good
|
|
421
223
|
<Stack direction="row" justifyContent="space-between">
|
|
422
|
-
<Typography>Showing 1
|
|
423
|
-
<Pagination rowCount={250} />
|
|
224
|
+
<Typography>Showing 1--25 of 250 items</Typography>
|
|
225
|
+
<Pagination rowCount={250} paginationModel={{ page: 1, pageSize: 25 }} onPageChange={setPage} />
|
|
424
226
|
</Stack>
|
|
425
227
|
```
|
|
426
228
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
```tsx
|
|
430
|
-
// ✅ Good: 1-based pagination
|
|
431
|
-
defaultPaginationModel={{ page: 1, pageSize: 25 }}
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
3. **Handle empty states**: Show appropriate UI when there's no data
|
|
435
|
-
|
|
436
|
-
```tsx
|
|
437
|
-
{totalCount > 0 ? (
|
|
438
|
-
<Pagination rowCount={totalCount} />
|
|
439
|
-
) : (
|
|
440
|
-
<Typography>No results found</Typography>
|
|
441
|
-
)}
|
|
442
|
-
```
|
|
443
|
-
|
|
444
|
-
4. **Reset on filter changes**: Return to page 1 when filters change
|
|
229
|
+
- **Reset to page 1 when filters change.** When the user applies a new filter or search query, always reset the page to 1 to avoid showing an empty or out-of-range page.
|
|
445
230
|
|
|
446
231
|
```tsx
|
|
447
232
|
useEffect(() => {
|
|
448
233
|
setPage(1);
|
|
449
|
-
}, [filters]);
|
|
234
|
+
}, [filters, searchQuery]);
|
|
450
235
|
```
|
|
451
236
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
1. **Don't use for very small datasets**: Pagination adds complexity
|
|
237
|
+
- **Do not paginate very small datasets.** If the total number of items fits comfortably on a single screen, display all items directly instead of adding pagination overhead.
|
|
455
238
|
|
|
456
239
|
```tsx
|
|
457
|
-
//
|
|
458
|
-
<Pagination rowCount={5} />
|
|
240
|
+
// Bad -- pagination for 5 items
|
|
241
|
+
<Pagination rowCount={5} paginationModel={{ page: 1, pageSize: 10 }} onPageChange={setPage} />
|
|
459
242
|
|
|
460
|
-
//
|
|
243
|
+
// Good -- just render the list
|
|
461
244
|
<ItemList items={items} />
|
|
462
245
|
```
|
|
463
246
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
3. **Don't use inconsistent page sizes**: Keep pageSize consistent in session
|
|
467
|
-
|
|
468
|
-
4. **Don't forget loading states**: Show loading while fetching page data
|
|
469
|
-
|
|
470
|
-
```tsx
|
|
471
|
-
// ✅ Good: Handle loading
|
|
472
|
-
<Pagination disabled={isLoading} />
|
|
473
|
-
```
|
|
474
|
-
|
|
475
|
-
## Performance Considerations
|
|
476
|
-
|
|
477
|
-
### Server-Side Pagination
|
|
478
|
-
|
|
479
|
-
For large datasets, paginate on the server:
|
|
480
|
-
|
|
481
|
-
```tsx
|
|
482
|
-
function ServerPaginatedList() {
|
|
483
|
-
const [page, setPage] = useState(1);
|
|
484
|
-
const pageSize = 25;
|
|
485
|
-
|
|
486
|
-
const { data, isLoading } = useQuery({
|
|
487
|
-
queryKey: ['items', page, pageSize],
|
|
488
|
-
queryFn: () => api.getItems({ page, pageSize }),
|
|
489
|
-
keepPreviousData: true, // Keep old data while loading
|
|
490
|
-
});
|
|
491
|
-
|
|
492
|
-
return (
|
|
493
|
-
<>
|
|
494
|
-
<ItemList items={data?.items} loading={isLoading} />
|
|
495
|
-
<Pagination
|
|
496
|
-
rowCount={data?.totalCount || 0}
|
|
497
|
-
paginationModel={{ page, pageSize }}
|
|
498
|
-
onPageChange={setPage}
|
|
499
|
-
/>
|
|
500
|
-
</>
|
|
501
|
-
);
|
|
502
|
-
}
|
|
503
|
-
```
|
|
504
|
-
|
|
505
|
-
### Memoize Handlers
|
|
506
|
-
|
|
507
|
-
When used in complex components, memoize the change handler:
|
|
508
|
-
|
|
509
|
-
```tsx
|
|
510
|
-
const handlePageChange = useCallback((newPage) => {
|
|
511
|
-
setPage(newPage);
|
|
512
|
-
}, []);
|
|
513
|
-
```
|
|
514
|
-
|
|
515
|
-
### Virtualization Alternative
|
|
516
|
-
|
|
517
|
-
For very large datasets, consider virtualization instead of pagination:
|
|
518
|
-
|
|
519
|
-
```tsx
|
|
520
|
-
// Instead of pagination for 10,000+ items:
|
|
521
|
-
<VirtualizedList items={items} itemHeight={50} />
|
|
522
|
-
```
|
|
523
|
-
|
|
524
|
-
### URL Sync
|
|
525
|
-
|
|
526
|
-
For shareable pagination state, sync with URL:
|
|
527
|
-
|
|
528
|
-
```tsx
|
|
529
|
-
function URLPaginatedList() {
|
|
530
|
-
const [searchParams, setSearchParams] = useSearchParams();
|
|
531
|
-
const page = parseInt(searchParams.get('page') || '1', 10);
|
|
247
|
+
- **Handle loading states.** Disable pagination controls while data is being fetched to prevent race conditions from rapid page switches.
|
|
532
248
|
|
|
533
|
-
|
|
534
|
-
setSearchParams({ page: String(newPage) });
|
|
535
|
-
};
|
|
249
|
+
- **Use the compact variant on small screens.** The standard variant with page numbers can overflow on narrow viewports. Switch to `variant="compact"` or reduce the `size` prop for responsive layouts.
|
|
536
250
|
|
|
537
|
-
|
|
538
|
-
<Pagination
|
|
539
|
-
rowCount={totalCount}
|
|
540
|
-
paginationModel={{ page, pageSize: 25 }}
|
|
541
|
-
onPageChange={handlePageChange}
|
|
542
|
-
/>
|
|
543
|
-
);
|
|
544
|
-
}
|
|
545
|
-
```
|
|
251
|
+
## Accessibility
|
|
546
252
|
|
|
547
|
-
|
|
253
|
+
- **ARIA attributes**: The current page button is marked with `aria-current="page"`. Previous and next buttons carry descriptive `aria-label` values such as "Previous page" and "Next page".
|
|
254
|
+
- **Keyboard navigation**: All pagination buttons are focusable with `Tab` and activated with `Enter` or `Space`. In the compact variant, arrow keys navigate the dropdown options.
|
|
255
|
+
- **Disabled states**: When the user is on the first or last page, the corresponding navigation button is properly disabled and announced as such to assistive technologies.
|
|
256
|
+
- **Live region consideration**: For dynamic content updates, consider wrapping a status message in an `aria-live="polite"` region to announce the current page to screen reader users.
|