@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.
Files changed (43) hide show
  1. package/dist/components/data-display/Badge.md +71 -39
  2. package/dist/components/data-display/InfoSign.md +74 -98
  3. package/dist/components/data-display/Typography.md +310 -61
  4. package/dist/components/feedback/CircularProgress.md +257 -0
  5. package/dist/components/feedback/Skeleton.md +280 -0
  6. package/dist/components/feedback/llms.txt +2 -0
  7. package/dist/components/inputs/ButtonGroup.md +115 -106
  8. package/dist/components/inputs/Calendar.md +98 -459
  9. package/dist/components/inputs/CurrencyInput.md +181 -8
  10. package/dist/components/inputs/DatePicker.md +108 -436
  11. package/dist/components/inputs/DateRangePicker.md +130 -496
  12. package/dist/components/inputs/FilterMenu.md +169 -19
  13. package/dist/components/inputs/FilterableCheckboxGroup.md +119 -24
  14. package/dist/components/inputs/FormControl.md +368 -0
  15. package/dist/components/inputs/IconButton.md +137 -88
  16. package/dist/components/inputs/MonthPicker.md +95 -427
  17. package/dist/components/inputs/MonthRangePicker.md +89 -471
  18. package/dist/components/inputs/PercentageInput.md +183 -19
  19. package/dist/components/inputs/RadioButton.md +163 -35
  20. package/dist/components/inputs/RadioList.md +241 -0
  21. package/dist/components/inputs/RadioTileGroup.md +146 -62
  22. package/dist/components/inputs/Select.md +219 -328
  23. package/dist/components/inputs/Slider.md +334 -0
  24. package/dist/components/inputs/Switch.md +136 -376
  25. package/dist/components/inputs/Textarea.md +209 -11
  26. package/dist/components/inputs/Uploader/Uploader.md +145 -66
  27. package/dist/components/inputs/llms.txt +3 -0
  28. package/dist/components/navigation/Breadcrumbs.md +80 -322
  29. package/dist/components/navigation/Dropdown.md +92 -221
  30. package/dist/components/navigation/IconMenuButton.md +40 -502
  31. package/dist/components/navigation/InsetDrawer.md +68 -738
  32. package/dist/components/navigation/Link.md +39 -298
  33. package/dist/components/navigation/Menu.md +92 -285
  34. package/dist/components/navigation/MenuButton.md +55 -448
  35. package/dist/components/navigation/Pagination.md +47 -338
  36. package/dist/components/navigation/ProfileMenu.md +45 -268
  37. package/dist/components/navigation/Stepper.md +160 -28
  38. package/dist/components/navigation/Tabs.md +57 -316
  39. package/dist/components/surfaces/Sheet.md +150 -333
  40. package/dist/guides/ThemeProvider.md +116 -0
  41. package/dist/guides/llms.txt +9 -0
  42. package/dist/llms.txt +8 -0
  43. package/package.json +1 -1
@@ -2,7 +2,9 @@
2
2
 
3
3
  ## Introduction
4
4
 
5
- Pagination allows users to navigate through large datasets or content collections by dividing them into discrete pages. It provides intuitive controls for moving between pages, jumping to specific pages, and understanding the current position within the total dataset. Pagination comes in two variants: standard (page numbers) and compact (dropdown selector), making it suitable for different space constraints and use cases.
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
- ## Examples
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 supports three sizes for different layouts.
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
- ### Pages
72
+ ### Page Positions
88
73
 
89
- Navigation through different page positions.
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
- Component manages its own page state.
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
- Parent component controls the page state.
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
- ### Change Row Count
127
+ ### Dynamic Row Count
143
128
 
144
- Pagination automatically adjusts when total items change.
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, fetchData }) {
146
+ function DataTable({ columns }) {
180
147
  const [page, setPage] = useState(1);
181
- const [pageSize, setPageSize] = useState(25);
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} - {Math.min(page * pageSize, totalCount)} of {totalCount}
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
- defaultPaginationModel={{ page: 1, pageSize: 10 }}
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
- ### Gallery with Pagination
196
+ ### URL-Synchronized Pagination
230
197
 
231
198
  ```tsx
232
- function Gallery({ images }) {
233
- const pageSize = 12;
234
- const [currentPage, setCurrentPage] = useState(1);
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
- ```tsx
297
- function ResponsivePagination({ rowCount, page, onPageChange }) {
298
- const isMobile = useMediaQuery('(max-width: 600px)');
203
+ const handlePageChange = (newPage: number) => {
204
+ setSearchParams({ page: String(newPage) });
205
+ };
299
206
 
300
207
  return (
301
208
  <Pagination
302
- rowCount={rowCount}
303
- paginationModel={{ page, pageSize: 10 }}
304
- onPageChange={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
- ### Do
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
- // Good: Show pagination context
222
+ // Good
421
223
  <Stack direction="row" justifyContent="space-between">
422
- <Typography>Showing 1-25 of 250 items</Typography>
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
- 2. **Start from page 1**: Users expect 1-based pagination
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
- ### Don't
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
- // Bad: Pagination for 5 items
458
- <Pagination rowCount={5} />
240
+ // Bad -- pagination for 5 items
241
+ <Pagination rowCount={5} paginationModel={{ page: 1, pageSize: 10 }} onPageChange={setPage} />
459
242
 
460
- // Good: Just show all items
243
+ // Good -- just render the list
461
244
  <ItemList items={items} />
462
245
  ```
463
246
 
464
- 2. **Don't hide the page count**: Users need to know how many pages exist
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
- const handlePageChange = (newPage) => {
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
- return (
538
- <Pagination
539
- rowCount={totalCount}
540
- paginationModel={{ page, pageSize: 25 }}
541
- onPageChange={handlePageChange}
542
- />
543
- );
544
- }
545
- ```
251
+ ## Accessibility
546
252
 
547
- Pagination is essential for managing large datasets in admin interfaces. Choose between standard and compact variants based on available space and user needs, ensuring a smooth navigation experience through your data.
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.