aidos-ui 4.0.0 → 4.0.2

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 (99) hide show
  1. package/.claude/settings.local.json +14 -1
  2. package/CLAUDE.md +72 -0
  3. package/dist/BaseButton.js.map +1 -1
  4. package/dist/BaseLink.js.map +1 -1
  5. package/dist/BaseListItem.d.ts +1 -1
  6. package/dist/Calendar.d.ts +2 -2
  7. package/dist/Card.js +1 -1
  8. package/dist/Card.js.map +1 -1
  9. package/dist/DarkMode.js.map +1 -1
  10. package/dist/DatePicker.js.map +1 -1
  11. package/dist/Dialog.d.ts +3 -3
  12. package/dist/Dialog.js +7 -1
  13. package/dist/Dialog.js.map +1 -1
  14. package/dist/Dropdown.js.map +1 -1
  15. package/dist/Icon.d.ts +3 -3
  16. package/dist/Icon.js +2 -21
  17. package/dist/Icon.js.map +1 -1
  18. package/dist/Interactable.js.map +1 -1
  19. package/dist/NextJSRouterState.js.map +1 -1
  20. package/dist/OnVisible.d.ts +1 -1
  21. package/dist/OnVisible.js.map +1 -1
  22. package/dist/Popover.d.ts +3 -3
  23. package/dist/Popover.js +3 -1
  24. package/dist/Popover.js.map +1 -1
  25. package/dist/Providers.d.ts +2 -2
  26. package/dist/RangeInput.d.ts +1 -1
  27. package/dist/RootView.js.map +1 -1
  28. package/dist/Select.d.ts +0 -1
  29. package/dist/Storage.d.ts +2 -1
  30. package/dist/Storage.js.map +1 -1
  31. package/dist/Stream.js.map +1 -1
  32. package/dist/Text.js.map +1 -1
  33. package/dist/TextInput.d.ts +3 -2
  34. package/dist/TextInput.js +13 -4
  35. package/dist/TextInput.js.map +1 -1
  36. package/dist/TextPairing.js.map +1 -1
  37. package/dist/Tooltip.d.ts +2 -2
  38. package/dist/Tooltip.js +3 -2
  39. package/dist/Tooltip.js.map +1 -1
  40. package/dist/aria.js.map +1 -1
  41. package/dist/hash.js.map +1 -1
  42. package/dist/jss.d.ts +11 -6
  43. package/dist/jss.js +47 -2
  44. package/dist/jss.js.map +1 -1
  45. package/dist/numberToBase.js.map +1 -1
  46. package/dist/stateMachineContextFactory.js.map +1 -1
  47. package/dist/useCookie.js.map +1 -1
  48. package/dist/useKeyboard.js.map +1 -1
  49. package/dist/useNavigation.js.map +1 -1
  50. package/dist/useRefEffect.js.map +1 -1
  51. package/next-env.d.ts +2 -1
  52. package/next.config.js +2 -1
  53. package/package.json +27 -20
  54. package/src/BaseListItem.tsx +1 -1
  55. package/src/Calendar.tsx +2 -2
  56. package/src/Card.tsx +1 -1
  57. package/src/Dialog.tsx +10 -4
  58. package/src/Icon.tsx +4 -30
  59. package/src/Popover.tsx +6 -4
  60. package/src/Providers.tsx +1 -1
  61. package/src/RangeInput.tsx +1 -1
  62. package/src/Storage.tsx +1 -1
  63. package/src/TextInput.tsx +17 -5
  64. package/src/Tooltip.tsx +5 -4
  65. package/src/docs/mdx.tsx +40 -0
  66. package/src/docs/searchIndex.ts +1 -1
  67. package/src/jss.tsx +55 -3
  68. package/src/pages/Badge.mdx +136 -14
  69. package/src/pages/Button.mdx +121 -63
  70. package/src/pages/Calendar.mdx +196 -2
  71. package/src/pages/Card.mdx +123 -47
  72. package/src/pages/Checkbox.mdx +103 -28
  73. package/src/pages/Dialog.mdx +172 -7
  74. package/src/pages/FlexLayout.mdx +185 -51
  75. package/src/pages/Hashing.mdx +199 -8
  76. package/src/pages/Icon.mdx +150 -42
  77. package/src/pages/IconButton.mdx +127 -116
  78. package/src/pages/Link.mdx +118 -47
  79. package/src/pages/List.mdx +167 -283
  80. package/src/pages/ListHeader.mdx +157 -27
  81. package/src/pages/Popover.mdx +177 -55
  82. package/src/pages/ProgressBar.mdx +186 -6
  83. package/src/pages/ProgressCircle.mdx +196 -13
  84. package/src/pages/RangeInput.mdx +189 -61
  85. package/src/pages/Tabs.mdx +202 -346
  86. package/src/pages/Text.mdx +102 -81
  87. package/src/pages/TextInput.mdx +169 -44
  88. package/src/pages/TextPairing.mdx +164 -115
  89. package/src/pages/TimeInput.mdx +142 -37
  90. package/src/pages/Tooltip.mdx +99 -12
  91. package/src/pages/_app.tsx +13 -3
  92. package/src/pages/css-in-js.mdx +196 -83
  93. package/src/pages/navigation.mdx +122 -20
  94. package/src/pages/useCookie.mdx +168 -7
  95. package/src/pages/useKeyboard.mdx +242 -5
  96. package/src/pages/useNavigation.mdx +244 -16
  97. package/src/pages/usePromise.mdx +271 -6
  98. package/src/pages/useRefEffect.mdx +230 -28
  99. package/tsconfig.json +13 -4
@@ -1,425 +1,281 @@
1
1
  import { Column } from "../Column";
2
+ import { Row } from "../Row";
2
3
  import { Tabs } from "../Tabs";
3
4
  import { Badge } from "../Badge";
5
+ import { Span } from "../Text";
4
6
  import { cssVar } from "../jss";
5
7
 
6
8
  # Tabs
7
9
 
8
- The `Tabs` component is made of a horizontal container with the role `tablist`, which contains tabs. It can be controlled with the keyboard and it needs to be passed an ID for the content it controls.
10
+ A horizontal tab navigation component with full keyboard support and ARIA compliance. Tabs can render as links or buttons and support custom styling, add-ons, and label rendering.
11
+
12
+ ---
13
+
14
+ ## Props
15
+
16
+ ### Tabs
17
+
18
+ | Prop | Type | Default | Description |
19
+ |------|------|---------|-------------|
20
+ | `aria-controls` | `string` | Required | ID of the content panel this tab controls |
21
+ | `tabs` | `Tab[]` | Required | Array of tab configurations |
22
+ | `labelSize` | `Size` | `"large"` | Text size for tab labels |
23
+ | `tabPadding` | `Padding` | `"medium"` | Padding inside each tab |
24
+ | `gap` | `Gap` | `"none"` | Space between tabs |
25
+ | `jssTab` | `JSS \| (data) => JSS` | - | Custom styles for tabs (can be conditional on selected state) |
26
+ | `labelRenderer` | `(label: string) => ReactNode` | - | Custom label rendering function |
27
+
28
+ ### Tab object
29
+
30
+ | Property | Type | Default | Description |
31
+ |----------|------|---------|-------------|
32
+ | `label` | `string` | Required | Tab display text |
33
+ | `href` | `string` | - | Link URL (renders as link if provided) |
34
+ | `onClick` | `() => void` | - | Click handler (renders as button if no href) |
35
+ | `selected` | `boolean` | `false` | Whether this tab is currently selected |
36
+ | `addOn` | `ReactNode` | - | Additional element (e.g., badge, icon) |
37
+ | `addOnPosition` | `"start" \| "end"` | `"start"` | Position of add-on relative to label |
38
+
39
+ ---
40
+
41
+ ## Basic usage
9
42
 
10
43
  <Column gap="medium">
11
44
  <Tabs
12
- aria-controls="foo"
45
+ aria-controls="panel-1"
13
46
  tabs={[
14
- {
15
- href: "/Tabs",
16
- selected: true,
17
- label: "Tab 1",
18
- },
19
- {
20
- href: "/Tabs",
21
- selected: false,
22
- label: "Tab 2",
23
- },
24
- {
25
- href: "/Tabs",
26
- selected: false,
27
- label: "Tab 3",
28
- },
29
- ]}
30
- />
31
- <Tabs
32
- aria-controls="foo"
33
- tabs={[
34
- {
35
- href: "/Tabs",
36
- selected: false,
37
- label: "Tab 1",
38
- },
39
- {
40
- href: "/Tabs",
41
- selected: true,
42
- label: "Tab 2",
43
- },
44
- {
45
- href: "/Tabs",
46
- addOn: (
47
- <Badge
48
- jss={{ margin: cssVar("--spacing-m") }}
49
- color="highlight"
50
- size="medium"
51
- />
52
- ),
53
- selected: false,
54
- label: "Tab 3",
55
- },
56
- ]}
57
- />
58
- <Tabs
59
- aria-controls="foo"
60
- tabs={[
61
- {
62
- href: "/Tabs",
63
- selected: false,
64
- label: "Tab 1",
65
- },
66
- {
67
- href: "/Tabs",
68
- selected: false,
69
- label: "Tab 2",
70
- },
71
- {
72
- href: "/Tabs",
73
- addOn: (
74
- <Badge
75
- jss={{ margin: cssVar("--spacing-m") }}
76
- color="highlight"
77
- size="medium"
78
- />
79
- ),
80
- addOnPosition: "end",
81
- selected: true,
82
- label: "Tab 3",
83
- },
47
+ { href: "/Tabs", selected: true, label: "Tab 1" },
48
+ { href: "/Tabs", selected: false, label: "Tab 2" },
49
+ { href: "/Tabs", selected: false, label: "Tab 3" },
84
50
  ]}
85
51
  />
86
52
  </Column>
87
53
 
88
54
  ```js
55
+ <Tabs
56
+ aria-controls="panel-1"
57
+ tabs={[
58
+ { href: "/Tabs", selected: true, label: "Tab 1" },
59
+ { href: "/Tabs", selected: false, label: "Tab 2" },
60
+ { href: "/Tabs", selected: false, label: "Tab 3" },
61
+ ]}
62
+ />
63
+ ```
64
+
65
+ ---
66
+
67
+ ## With add-ons
68
+
69
+ Add badges, icons, or other elements to tabs:
70
+
89
71
  <Column gap="medium">
90
72
  <Tabs
91
- aria-controls="foo"
73
+ aria-controls="panel-2"
92
74
  tabs={[
75
+ { href: "/Tabs", selected: false, label: "Messages" },
93
76
  {
94
77
  href: "/Tabs",
95
78
  selected: true,
96
- label: "Tab 1",
97
- },
98
- {
99
- href: "/Tabs",
100
- selected: false,
101
- label: "Tab 2",
102
- },
103
- {
104
- href: "/Tabs",
105
- selected: false,
106
- label: "Tab 3",
79
+ label: "Notifications",
80
+ addOn: <Badge jss={{ margin: cssVar("--spacing-m") }} color="negative" size="small" />,
107
81
  },
82
+ { href: "/Tabs", selected: false, label: "Settings" },
108
83
  ]}
109
84
  />
110
85
  <Tabs
111
- aria-controls="foo"
86
+ aria-controls="panel-3"
112
87
  tabs={[
113
- {
114
- href: "/Tabs",
115
- selected: false,
116
- label: "Tab 1",
117
- },
88
+ { href: "/Tabs", selected: false, label: "Overview" },
89
+ { href: "/Tabs", selected: false, label: "Analytics" },
118
90
  {
119
91
  href: "/Tabs",
120
92
  selected: true,
121
- label: "Tab 2",
122
- },
123
- {
124
- href: "/Tabs",
125
- addOn: (
126
- <Badge
127
- jss={{ margin: cssVar("--spacing-m") }}
128
- color="highlight"
129
- size="medium"
130
- />
131
- ),
132
- selected: false,
133
- label: "Tab 3",
134
- },
135
- ]}
136
- />
137
- <Tabs
138
- aria-controls="foo"
139
- tabs={[
140
- {
141
- href: "/Tabs",
142
- selected: false,
143
- label: "Tab 1",
144
- },
145
- {
146
- href: "/Tabs",
147
- selected: false,
148
- label: "Tab 2",
149
- },
150
- {
151
- href: "/Tabs",
152
- addOn: (
153
- <Badge
154
- jss={{ margin: cssVar("--spacing-m") }}
155
- color="highlight"
156
- size="medium"
157
- />
158
- ),
93
+ label: "New",
94
+ addOn: <Badge jss={{ margin: cssVar("--spacing-m") }} color="highlight" size="medium" />,
159
95
  addOnPosition: "end",
160
- selected: true,
161
- label: "Tab 3",
162
96
  },
163
97
  ]}
164
98
  />
165
99
  </Column>
100
+
101
+ ```js
102
+ // Badge before label (default)
103
+ <Tabs
104
+ aria-controls="panel"
105
+ tabs={[
106
+ { href: "/messages", selected: false, label: "Messages" },
107
+ {
108
+ href: "/notifications",
109
+ selected: true,
110
+ label: "Notifications",
111
+ addOn: <Badge color="negative" size="small" />,
112
+ },
113
+ ]}
114
+ />
115
+
116
+ // Badge after label
117
+ <Tabs
118
+ aria-controls="panel"
119
+ tabs={[
120
+ {
121
+ href: "/new",
122
+ selected: true,
123
+ label: "New",
124
+ addOn: <Badge color="highlight" size="medium" />,
125
+ addOnPosition: "end",
126
+ },
127
+ ]}
128
+ />
166
129
  ```
167
130
 
168
131
  ---
169
132
 
170
133
  ## Tabs as buttons
171
134
 
172
- You can also use the `Tabs` component with buttons instead of links by omitting the `href` property in the tabs.
135
+ Omit `href` to render tabs as buttons. Useful for in-page tab switching:
173
136
 
174
137
  <Column gap="medium">
175
138
  <Tabs
176
- aria-controls="foo"
177
- tabs={[
178
- {
179
- selected: true,
180
- label: "Tab 1",
181
- },
182
- {
183
- selected: false,
184
- label: "Tab 2",
185
- },
186
- {
187
- selected: false,
188
- label: "Tab 3",
189
- },
190
- ]}
191
- />
192
- <Tabs
193
- aria-controls="foo"
139
+ aria-controls="panel-4"
194
140
  tabs={[
195
- {
196
- selected: false,
197
- label: "Tab 1",
198
- },
199
- {
200
- selected: true,
201
- label: "Tab 2",
202
- },
203
- {
204
- addOn: (
205
- <Badge
206
- jss={{ margin: cssVar("--spacing-m") }}
207
- color="highlight"
208
- size="medium"
209
- />
210
- ),
211
- selected: false,
212
- label: "Tab 3",
213
- },
214
- ]}
215
- />
216
- <Tabs
217
- aria-controls="foo"
218
- tabs={[
219
- {
220
- selected: false,
221
- label: "Tab 1",
222
- },
223
- {
224
- selected: false,
225
- label: "Tab 2",
226
- },
227
- {
228
- addOn: (
229
- <Badge
230
- jss={{ margin: cssVar("--spacing-m") }}
231
- color="highlight"
232
- size="medium"
233
- />
234
- ),
235
- addOnPosition: "end",
236
- selected: true,
237
- label: "Tab 3",
238
- },
141
+ { selected: true, label: "Details" },
142
+ { selected: false, label: "Reviews" },
143
+ { selected: false, label: "Related" },
239
144
  ]}
240
145
  />
241
146
  </Column>
242
147
 
243
148
  ```js
244
- <Column gap="medium">
245
- <Tabs
246
- aria-controls="foo"
247
- tabs={[
248
- {
249
- selected: true,
250
- label: "Tab 1",
251
- },
252
- {
253
- selected: false,
254
- label: "Tab 2",
255
- },
256
- {
257
- selected: false,
258
- label: "Tab 3",
259
- },
260
- ]}
261
- />
262
- <Tabs
263
- aria-controls="foo"
264
- tabs={[
265
- {
266
- selected: false,
267
- label: "Tab 1",
268
- },
269
- {
270
- selected: true,
271
- label: "Tab 2",
272
- },
273
- {
274
- addOn: (
275
- <Badge
276
- jss={{ margin: cssVar("--spacing-m") }}
277
- color="highlight"
278
- size="medium"
279
- />
280
- ),
281
- selected: false,
282
- label: "Tab 3",
283
- },
284
- ]}
285
- />
286
- <Tabs
287
- aria-controls="foo"
288
- tabs={[
289
- {
290
- selected: false,
291
- label: "Tab 1",
292
- },
293
- {
294
- selected: false,
295
- label: "Tab 2",
296
- },
297
- {
298
- addOn: (
299
- <Badge
300
- jss={{ margin: cssVar("--spacing-m") }}
301
- color="highlight"
302
- size="medium"
303
- />
304
- ),
305
- addOnPosition: "end",
306
- selected: true,
307
- label: "Tab 3",
308
- },
309
- ]}
310
- />
311
- </Column>
149
+ const [activeTab, setActiveTab] = useState("details");
150
+
151
+ <Tabs
152
+ aria-controls="content-panel"
153
+ tabs={[
154
+ {
155
+ selected: activeTab === "details",
156
+ label: "Details",
157
+ onClick: () => setActiveTab("details"),
158
+ },
159
+ {
160
+ selected: activeTab === "reviews",
161
+ label: "Reviews",
162
+ onClick: () => setActiveTab("reviews"),
163
+ },
164
+ {
165
+ selected: activeTab === "related",
166
+ label: "Related",
167
+ onClick: () => setActiveTab("related"),
168
+ },
169
+ ]}
170
+ />
312
171
  ```
313
172
 
314
- ## Label pre renderers
173
+ ---
315
174
 
316
- You can customize how tab labels are rendered by providing a `labelRenderer` function that takes the label value and returns a ReactNode.
175
+ ## Custom label rendering
176
+
177
+ Use `labelRenderer` to customize how tab labels are displayed:
317
178
 
318
179
  <Column>
319
180
  <Tabs
320
- aria-controls="foo"
181
+ aria-controls="panel-5"
321
182
  labelRenderer={(value) => value.split(" ").map((word, index) => index % 2 === 0 ? <strong key={index}>{word} </strong> : word + " ")}
322
183
  tabs={[
323
- {
324
- href: "/Tabs",
325
- selected: true,
326
- label: "Bold Tab 1",
327
- },
328
- {
329
- href: "/Tabs",
330
- selected: false,
331
- label: "Bold Tab 2",
332
- },
333
- {
334
- href: "/Tabs",
335
- selected: false,
336
- label: "Bold Tab 3",
337
- },
184
+ { href: "/Tabs", selected: true, label: "Bold Tab 1" },
185
+ { href: "/Tabs", selected: false, label: "Bold Tab 2" },
186
+ { href: "/Tabs", selected: false, label: "Bold Tab 3" },
338
187
  ]}
339
188
  />
340
189
  </Column>
341
190
 
342
191
  ```js
343
- <Column>
344
- <Tabs
345
- aria-controls="foo"
346
- labelRenderer={(value) => value.split(" ").map((word, index) => index % 2 === 0 ? <strong key={index}>{word} </strong> : word + " ")}
347
- tabs={[
348
- {
349
- href: "/Tabs",
350
- selected: true,
351
- label: "Bold Tab 1",
352
- },
353
- {
354
- href: "/Tabs",
355
- selected: false,
356
- label: "Bold Tab 2",
357
- },
358
- {
359
- href: "/Tabs",
360
- selected: false,
361
- label: "Bold Tab 3",
362
- },
363
- ]}
364
- />
365
- </Column>
192
+ <Tabs
193
+ aria-controls="panel"
194
+ labelRenderer={(label) => (
195
+ <>
196
+ <Icon icon="fa-star" size="small" />
197
+ {label}
198
+ </>
199
+ )}
200
+ tabs={[
201
+ { href: "/featured", selected: true, label: "Featured" },
202
+ { href: "/popular", selected: false, label: "Popular" },
203
+ ]}
204
+ />
366
205
  ```
367
206
 
207
+ ---
368
208
 
369
209
  ## Custom tab styles
370
210
 
371
- You can provide custom styles to individual tabs using the `tabJSStyle` prop, which can either be a style object or a function that receives the tab's selected state.
211
+ Use `jssTab` for custom styling. It can be a static style object or a function that receives the tab's selected state:
372
212
 
373
213
  <Column>
374
214
  <Tabs
375
- aria-controls="foo"
376
- tabJSStyle={(data) => ({
215
+ aria-controls="panel-6"
216
+ jssTab={(data) => ({
377
217
  borderBottom: data.selected ? "2px solid cyan" : "2px solid magenta",
378
218
  })}
379
219
  tabs={[
380
- {
381
- href: "/Tabs",
382
- selected: true,
383
- label: "Styled Tab 1",
384
- },
385
- {
386
- href: "/Tabs",
387
- selected: false,
388
- label: "Styled Tab 2",
389
- },
390
- {
391
- href: "/Tabs",
392
- selected: false,
393
- label: "Styled Tab 3",
394
- },
220
+ { href: "/Tabs", selected: true, label: "Styled Tab 1" },
221
+ { href: "/Tabs", selected: false, label: "Styled Tab 2" },
222
+ { href: "/Tabs", selected: false, label: "Styled Tab 3" },
395
223
  ]}
396
224
  />
397
225
  </Column>
398
226
 
399
227
  ```js
400
- <Column>
401
- <Tabs
402
- aria-controls="foo"
403
- tabJSStyle={(data) => ({
404
- borderBottom: data.selected ? "2px solid cyan" : "2px solid magenta",
405
- })}
406
- tabs={[
407
- {
408
- href: "/Tabs",
409
- selected: true,
410
- label: "Styled Tab 1",
411
- },
412
- {
413
- href: "/Tabs",
414
- selected: false,
415
- label: "Styled Tab 2",
416
- },
417
- {
418
- href: "/Tabs",
419
- selected: false,
420
- label: "Styled Tab 3",
421
- },
422
- ]}
423
- />
424
- </Column>
425
- ```
228
+ // Conditional styling based on selection
229
+ <Tabs
230
+ aria-controls="panel"
231
+ jssTab={(data) => ({
232
+ borderBottom: data.selected
233
+ ? "3px solid var(--highlight)"
234
+ : "3px solid transparent",
235
+ backgroundColor: data.selected
236
+ ? "var(--light-highlight)"
237
+ : "transparent",
238
+ })}
239
+ tabs={tabs}
240
+ />
241
+
242
+ // Static styling for all tabs
243
+ <Tabs
244
+ aria-controls="panel"
245
+ jssTab={{ borderRadius: 8 }}
246
+ tabs={tabs}
247
+ />
248
+ ```
249
+
250
+ ---
251
+
252
+ ## Keyboard navigation
253
+
254
+ Tabs support full keyboard navigation:
255
+
256
+ - **Arrow Left/Right**: Move focus between tabs
257
+ - **Enter/Space**: Activate the focused tab
258
+ - **Home**: Move to first tab
259
+ - **End**: Move to last tab
260
+
261
+ ---
262
+
263
+ ## Accessibility
264
+
265
+ The Tabs component follows WAI-ARIA tab patterns:
266
+
267
+ - Container has `role="tablist"`
268
+ - Each tab has `role="tab"` and `aria-controls` pointing to its panel
269
+ - Selected tab has `aria-selected="true"`
270
+ - Content panel should have `role="tabpanel"` and matching `id`
271
+
272
+ ```js
273
+ <Tabs
274
+ aria-controls="settings-panel"
275
+ tabs={tabs}
276
+ />
277
+
278
+ <div id="settings-panel" role="tabpanel">
279
+ {/* Panel content */}
280
+ </div>
281
+ ```