@ceed/ads 1.20.2 → 1.22.0-next.1

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/README.md ADDED
@@ -0,0 +1,157 @@
1
+ # @ceed/ads
2
+
3
+ Admin Design System - A React component library for Ecube Labs admin applications.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @ceed/ads
9
+ # or
10
+ yarn add @ceed/ads
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ### With Bundler (Vite, Webpack, etc.)
16
+
17
+ ```tsx
18
+ import { Button, ThemeProvider } from '@ceed/ads';
19
+
20
+ function App() {
21
+ return (
22
+ <ThemeProvider>
23
+ <Button variant="solid">Click me</Button>
24
+ </ThemeProvider>
25
+ );
26
+ }
27
+ ```
28
+
29
+ ### Browser ESM (via CDN)
30
+
31
+ You can use `@ceed/ads` directly in the browser without a bundler via [esm.sh](https://esm.sh).
32
+
33
+ ```html
34
+ <!DOCTYPE html>
35
+ <html>
36
+ <head>
37
+ <meta charset="UTF-8">
38
+ <title>@ceed/ads Browser Example</title>
39
+ </head>
40
+ <body>
41
+ <div id="root"></div>
42
+
43
+ <script type="module">
44
+ import React from 'https://esm.sh/react@18';
45
+ import { createRoot } from 'https://esm.sh/react-dom@18/client';
46
+ import { Button, ThemeProvider } from 'https://esm.sh/@ceed/ads';
47
+
48
+ const App = () => (
49
+ React.createElement(ThemeProvider, null,
50
+ React.createElement(Button, { variant: 'solid' }, 'Hello from CDN!')
51
+ )
52
+ );
53
+
54
+ createRoot(document.getElementById('root')).render(
55
+ React.createElement(App)
56
+ );
57
+ </script>
58
+ </body>
59
+ </html>
60
+ ```
61
+
62
+ #### With Import Maps
63
+
64
+ For cleaner imports, use [Import Maps](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap):
65
+
66
+ ```html
67
+ <!DOCTYPE html>
68
+ <html>
69
+ <head>
70
+ <meta charset="UTF-8">
71
+ <title>@ceed/ads with Import Maps</title>
72
+ </head>
73
+ <body>
74
+ <div id="root"></div>
75
+
76
+ <script type="importmap">
77
+ {
78
+ "imports": {
79
+ "react": "https://esm.sh/react@18",
80
+ "react/jsx-runtime": "https://esm.sh/react@18/jsx-runtime",
81
+ "react-dom/client": "https://esm.sh/react-dom@18/client",
82
+ "@ceed/ads": "https://esm.sh/@ceed/ads"
83
+ }
84
+ }
85
+ </script>
86
+
87
+ <script type="module">
88
+ import React from 'react';
89
+ import { createRoot } from 'react-dom/client';
90
+ import { Button, ThemeProvider } from '@ceed/ads';
91
+
92
+ const App = () => (
93
+ React.createElement(ThemeProvider, null,
94
+ React.createElement(Button, { variant: 'solid' }, 'Hello from CDN!')
95
+ )
96
+ );
97
+
98
+ createRoot(document.getElementById('root')).render(
99
+ React.createElement(App)
100
+ );
101
+ </script>
102
+ </body>
103
+ </html>
104
+ ```
105
+
106
+ #### With JSX (using esm.sh)
107
+
108
+ esm.sh supports JSX transformation via the `?jsx-runtime=automatic` parameter:
109
+
110
+ ```html
111
+ <!DOCTYPE html>
112
+ <html>
113
+ <head>
114
+ <meta charset="UTF-8">
115
+ <title>@ceed/ads with JSX</title>
116
+ </head>
117
+ <body>
118
+ <div id="root"></div>
119
+
120
+ <script type="importmap">
121
+ {
122
+ "imports": {
123
+ "react": "https://esm.sh/react@18",
124
+ "react/jsx-runtime": "https://esm.sh/react@18/jsx-runtime",
125
+ "react-dom/client": "https://esm.sh/react-dom@18/client",
126
+ "@ceed/ads": "https://esm.sh/@ceed/ads"
127
+ }
128
+ }
129
+ </script>
130
+
131
+ <script type="module">
132
+ import { createRoot } from 'react-dom/client';
133
+ import { jsx } from 'react/jsx-runtime';
134
+ import { Button, Stack, ThemeProvider } from '@ceed/ads';
135
+
136
+ const App = () =>
137
+ jsx(ThemeProvider, {
138
+ children: jsx(Stack, {
139
+ spacing: 2,
140
+ children: [
141
+ jsx(Button, { variant: 'solid', children: 'Solid' }),
142
+ jsx(Button, { variant: 'outlined', children: 'Outlined' }),
143
+ jsx(Button, { variant: 'soft', children: 'Soft' }),
144
+ ],
145
+ }),
146
+ });
147
+
148
+ createRoot(document.getElementById('root')).render(jsx(App, {}));
149
+ </script>
150
+ </body>
151
+ </html>
152
+ ```
153
+
154
+ ## Documentation
155
+
156
+ - [Storybook](https://main--66da7aaaf87fb0b864d6afca.chromatic.com)
157
+ - [Confluence](https://ecubelabs.atlassian.net/wiki/spaces/HP/pages/3256123430/Design+System)
@@ -45,7 +45,7 @@ export declare function useDataTableRenderer<T extends Record<PropertyKey, unkno
45
45
  focusedRowId: InferredIdType<T, GetId> | null;
46
46
  onRowFocus: (rowId: InferredIdType<T, GetId>) => void;
47
47
  onAllCheckboxChange: () => void;
48
- onCheckboxChange: (event: any, selectedModel: InferredIdType<T, GetId>) => void;
48
+ onCheckboxChange: (event: React.MouseEvent | React.ChangeEvent | React.KeyboardEvent, selectedModel: InferredIdType<T, GetId>) => void;
49
49
  columns: ColumnDef<T, InferredIdType<T, GetId>>[];
50
50
  processedColumnGroups: {
51
51
  groups: import("./types").ProcessedColumnGroup[][];
@@ -53,4 +53,14 @@ export declare function useDataTableRenderer<T extends Record<PropertyKey, unkno
53
53
  fieldsInGroupingModel: Set<keyof T>;
54
54
  } | null;
55
55
  onTotalSelect: () => void;
56
+ selectionAnchor: {
57
+ rowId: InferredIdType<T, GetId>;
58
+ rowIndex: number;
59
+ wasSelected: boolean;
60
+ } | null;
61
+ setSelectionAnchor: import("react").Dispatch<import("react").SetStateAction<{
62
+ rowId: InferredIdType<T, GetId>;
63
+ rowIndex: number;
64
+ wasSelected: boolean;
65
+ } | null>>;
56
66
  };
@@ -26,7 +26,7 @@ interface BaseProfileMenuProps {
26
26
  src: string;
27
27
  alt: string;
28
28
  };
29
- caption?: string;
29
+ caption?: React.ReactNode;
30
30
  chip?: string;
31
31
  };
32
32
  menuItems: ({
@@ -377,12 +377,82 @@ const [selectionModel, setSelectionModel] = useState<string[]>([]);
377
377
  />;
378
378
  ```
379
379
 
380
+ ## Keyboard Accessibility
381
+
382
+ DataTable supports keyboard interactions for improved accessibility.
383
+
384
+ ### Shift+Click Range Selection
385
+
386
+ Hold the Shift key while clicking checkboxes to select or deselect a range of rows at once.
387
+ The selection state is applied based on the last clicked checkbox's resulting state.
388
+
389
+ ```tsx
390
+ <Stack spacing={2}>
391
+ <Typography level="body-sm">
392
+ Hold Shift and click checkboxes to select a range of rows. The selection state is applied based on the first
393
+ clicked checkbox&apos;s resulting state.
394
+ </Typography>
395
+ <DataTable {...args} selectionModel={selectionModel} onSelectionModelChange={setSelectionModel} />
396
+ <Typography level="body-xs">Selected IDs: {selectionModel.join(', ') || 'None'}</Typography>
397
+ </Stack>
398
+ ```
399
+
400
+ **How it works:**
401
+
402
+ 1. Click a checkbox to select/deselect a row (this becomes the "anchor")
403
+ 2. Hold Shift and click another checkbox
404
+ 3. All rows between the anchor and the clicked row will be set to the same state
405
+
406
+ **Edge cases handled:**
407
+
408
+ - Works seamlessly with virtual scrolling (rows don't need to be visible)
409
+ - Selection anchor resets when data changes or page changes
410
+ - Respects `isRowSelectable` - non-selectable rows are skipped in range
411
+
412
+ ### Keyboard Navigation
413
+
414
+ When a row has focus, use keyboard shortcuts to navigate and interact with rows.
415
+
416
+ ```tsx
417
+ <Stack spacing={2}>
418
+ <Typography level="body-sm">
419
+ Click a row to focus, then use keyboard to navigate. Arrow Up/Down to move, Space to toggle selection,
420
+ Home/End to jump to first/last row, PageUp/PageDown to move by 10 rows. Hold Shift with any navigation key to
421
+ extend selection.
422
+ </Typography>
423
+ <DataTable {...args} selectionModel={selectionModel} onSelectionModelChange={setSelectionModel} />
424
+ <Typography level="body-xs">Selected IDs: {selectionModel.join(', ') || 'None'}</Typography>
425
+ </Stack>
426
+ ```
427
+
428
+ **Supported keys:**
429
+
430
+ | Key | Action |
431
+ | ---------------------- | ------------------------------------------- |
432
+ | `↑` Arrow Up | Move focus to previous row |
433
+ | `↓` Arrow Down | Move focus to next row |
434
+ | `Space` | Toggle checkbox selection |
435
+ | `Home` | Move focus to first row |
436
+ | `End` | Move focus to last row |
437
+ | `Page Up` | Move focus up by 10 rows |
438
+ | `Page Down` | Move focus down by 10 rows |
439
+ | `Shift + ↑/↓` | Move focus and extend selection |
440
+ | `Shift + Home/End` | Jump to first/last row and extend selection |
441
+ | `Shift + Page Up/Down` | Move by 10 rows and extend selection |
442
+
443
+ **Notes:**
444
+
445
+ - Click any row first to establish focus
446
+ - Tab key skips checkboxes and moves directly between rows
447
+ - Works with virtual scrolling - target row is automatically scrolled into view
448
+ - Follows WAI-ARIA Grid pattern for accessibility
449
+
380
450
  ## Styles and Layout
381
451
 
382
452
  ### Back Office Style
383
453
 
384
454
  ```tsx
385
- <DataTable rows={args.rows} columns={args.columns} checkboxSelection={args.checkboxSelection} hoverRow={args.hoverRow} noWrap={args.noWrap} stripe={args.stripe} stickyHeader={args.stickyHeader} slots={args.slots} slotProps={args.slotProps} selectionModel={selectionModel} onSelectionModelChange={setSelectionModel} />
455
+ <DataTable rows={args.rows} columns={args.columns} checkboxSelection={args.checkboxSelection} hoverRow={args.hoverRow} noWrap={args.noWrap} stripe={args.stripe} stickyHeader={args.stickyHeader} slots={args.slots} slotProps={args.slotProps} selectionModel={selectionModel} onSelectionModelChange={setSelectionModel} pagination={args.pagination} />
386
456
  ```
387
457
 
388
458
  #### Commonly Used Table Options