@altinn/altinn-components 0.0.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.
Files changed (126) hide show
  1. package/.github/workflows/ci-cd-main.yml +44 -0
  2. package/.github/workflows/ci-cd-pull-request.yml +39 -0
  3. package/.node-version +1 -0
  4. package/.storybook/main.ts +22 -0
  5. package/.storybook/preview.ts +15 -0
  6. package/CHANGELOG.md +13 -0
  7. package/README.md +2 -0
  8. package/biome.jsonc +65 -0
  9. package/lib/components/Avatar/Avatar.tsx +91 -0
  10. package/lib/components/Avatar/AvatarGroup.stories.ts +67 -0
  11. package/lib/components/Avatar/AvatarGroup.tsx +42 -0
  12. package/lib/components/Avatar/avatar.module.css +59 -0
  13. package/lib/components/Avatar/avatar.stories.tsx +44 -0
  14. package/lib/components/Avatar/avatarGroup.module.css +78 -0
  15. package/lib/components/Avatar/color.ts +71 -0
  16. package/lib/components/Avatar/index.ts +2 -0
  17. package/lib/components/Badge/Badge.tsx +19 -0
  18. package/lib/components/Badge/badge.module.css +36 -0
  19. package/lib/components/Badge/index.tsx +1 -0
  20. package/lib/components/Button/Button.stories.ts +44 -0
  21. package/lib/components/Button/Button.tsx +39 -0
  22. package/lib/components/Button/ButtonBase.tsx +53 -0
  23. package/lib/components/Button/ComboButton.stories.ts +45 -0
  24. package/lib/components/Button/ComboButton.tsx +44 -0
  25. package/lib/components/Button/button.module.css +82 -0
  26. package/lib/components/Button/buttonBase.module.css +77 -0
  27. package/lib/components/Button/comboButton.module.css +83 -0
  28. package/lib/components/Button/index.ts +3 -0
  29. package/lib/components/Header/DigdirLogomark.tsx +23 -0
  30. package/lib/components/Header/GlobalMenu.stories.tsx +202 -0
  31. package/lib/components/Header/GlobalMenu.tsx +131 -0
  32. package/lib/components/Header/Header.stories.ts +85 -0
  33. package/lib/components/Header/Header.tsx +64 -0
  34. package/lib/components/Header/HeaderBase.tsx +10 -0
  35. package/lib/components/Header/HeaderButton.stories.ts +54 -0
  36. package/lib/components/Header/HeaderButton.tsx +55 -0
  37. package/lib/components/Header/HeaderLogo.stories.ts +17 -0
  38. package/lib/components/Header/HeaderLogo.tsx +22 -0
  39. package/lib/components/Header/HeaderSearch.stories.ts +20 -0
  40. package/lib/components/Header/HeaderSearch.tsx +44 -0
  41. package/lib/components/Header/globalMenu.module.css +28 -0
  42. package/lib/components/Header/header.module.css +39 -0
  43. package/lib/components/Header/headerButton.module.css +35 -0
  44. package/lib/components/Header/headerLogo.module.css +24 -0
  45. package/lib/components/Header/headerSearch.module.css +30 -0
  46. package/lib/components/Header/index.tsx +5 -0
  47. package/lib/components/Icon/CheckboxIcon.stories.ts +25 -0
  48. package/lib/components/Icon/CheckboxIcon.tsx +29 -0
  49. package/lib/components/Icon/Icon.stories.ts +24 -0
  50. package/lib/components/Icon/Icon.tsx +23 -0
  51. package/lib/components/Icon/RadioIcon.stories.ts +25 -0
  52. package/lib/components/Icon/RadioIcon.tsx +29 -0
  53. package/lib/components/Icon/SvgIcon.tsx +18 -0
  54. package/lib/components/Icon/__AkselIcon.tsx +37 -0
  55. package/lib/components/Icon/checkboxIcon.module.css +21 -0
  56. package/lib/components/Icon/icon.module.css +4 -0
  57. package/lib/components/Icon/iconsMap.tsx +2078 -0
  58. package/lib/components/Icon/index.ts +5 -0
  59. package/lib/components/Icon/radioIcon.module.css +21 -0
  60. package/lib/components/Layout/Layout.stories.ts +127 -0
  61. package/lib/components/Layout/Layout.tsx +40 -0
  62. package/lib/components/Layout/LayoutBase.stories.ts +17 -0
  63. package/lib/components/Layout/LayoutBase.tsx +30 -0
  64. package/lib/components/Layout/LayoutBody.stories.ts +17 -0
  65. package/lib/components/Layout/LayoutBody.tsx +16 -0
  66. package/lib/components/Layout/LayoutContent.stories.ts +17 -0
  67. package/lib/components/Layout/LayoutContent.tsx +15 -0
  68. package/lib/components/Layout/LayoutSidebar.stories.ts +17 -0
  69. package/lib/components/Layout/LayoutSidebar.tsx +16 -0
  70. package/lib/components/Layout/index.tsx +4 -0
  71. package/lib/components/Layout/layout.module.css +63 -0
  72. package/lib/components/Menu/Menu.stories.ts +495 -0
  73. package/lib/components/Menu/Menu.tsx +123 -0
  74. package/lib/components/Menu/MenuBase.tsx +17 -0
  75. package/lib/components/Menu/MenuGroup.tsx +18 -0
  76. package/lib/components/Menu/MenuHeader.tsx +13 -0
  77. package/lib/components/Menu/MenuItem.stories.ts +127 -0
  78. package/lib/components/Menu/MenuItem.tsx +58 -0
  79. package/lib/components/Menu/MenuItemBase.tsx +62 -0
  80. package/lib/components/Menu/MenuItemLabel.tsx +30 -0
  81. package/lib/components/Menu/MenuItemMedia.tsx +42 -0
  82. package/lib/components/Menu/MenuOption.stories.ts +50 -0
  83. package/lib/components/Menu/MenuOption.tsx +45 -0
  84. package/lib/components/Menu/MenuSearch.stories.ts +18 -0
  85. package/lib/components/Menu/MenuSearch.tsx +25 -0
  86. package/lib/components/Menu/index.ts +10 -0
  87. package/lib/components/Menu/menu.module.css +26 -0
  88. package/lib/components/Menu/menuHeader.module.css +12 -0
  89. package/lib/components/Menu/menuItem.module.css +136 -0
  90. package/lib/components/Menu/menuOption.module.css +29 -0
  91. package/lib/components/Menu/menuSearch.module.css +29 -0
  92. package/lib/components/Menu/useClickOutside.ts +21 -0
  93. package/lib/components/Menu/useEscapeKey.ts +16 -0
  94. package/lib/components/Toolbar/Toolbar.stories.tsx +188 -0
  95. package/lib/components/Toolbar/Toolbar.tsx +138 -0
  96. package/lib/components/Toolbar/ToolbarAdd.stories.ts +25 -0
  97. package/lib/components/Toolbar/ToolbarAdd.tsx +25 -0
  98. package/lib/components/Toolbar/ToolbarBase.tsx +27 -0
  99. package/lib/components/Toolbar/ToolbarButton.stories.ts +32 -0
  100. package/lib/components/Toolbar/ToolbarButton.tsx +65 -0
  101. package/lib/components/Toolbar/ToolbarFilter.stories.ts +66 -0
  102. package/lib/components/Toolbar/ToolbarFilter.tsx +70 -0
  103. package/lib/components/Toolbar/ToolbarMenu.stories.ts +37 -0
  104. package/lib/components/Toolbar/ToolbarMenu.tsx +28 -0
  105. package/lib/components/Toolbar/ToolbarOptions.stories.ts +108 -0
  106. package/lib/components/Toolbar/ToolbarOptions.tsx +61 -0
  107. package/lib/components/Toolbar/ToolbarSearch.stories.ts +19 -0
  108. package/lib/components/Toolbar/ToolbarSearch.tsx +24 -0
  109. package/lib/components/Toolbar/index.js +3 -0
  110. package/lib/components/Toolbar/toolbar.module.css +43 -0
  111. package/lib/components/Toolbar/toolbarButton.module.css +3 -0
  112. package/lib/components/Toolbar/toolbarSearch.module.css +28 -0
  113. package/lib/components/index.ts +1 -0
  114. package/lib/css/colors.css +113 -0
  115. package/lib/css/global.css +12 -0
  116. package/lib/css/theme-company.css +15 -0
  117. package/lib/css/theme-global.css +15 -0
  118. package/lib/css/theme-neutral.css +15 -0
  119. package/lib/css/theme-person.css +15 -0
  120. package/lib/css/theme.css +24 -0
  121. package/lib/index.ts +1 -0
  122. package/package.json +52 -0
  123. package/tsconfig.json +23 -0
  124. package/tsconfig.node.json +11 -0
  125. package/typings.d.ts +1 -0
  126. package/vite.config.ts +20 -0
@@ -0,0 +1,495 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { Menu } from './Menu';
3
+
4
+ const meta = {
5
+ title: 'Menu/Menu',
6
+ component: Menu,
7
+ tags: ['autodocs'],
8
+ parameters: {},
9
+ args: {},
10
+ } satisfies Meta<typeof Menu>;
11
+
12
+ export default meta;
13
+ type Story = StoryObj<typeof meta>;
14
+
15
+ export const GlobalMenu: Story = {
16
+ args: {
17
+ theme: 'global',
18
+ groups: {
19
+ settings: {
20
+ defaultItemColor: 'default',
21
+ },
22
+ },
23
+ items: [
24
+ {
25
+ id: 'account',
26
+ group: 'account',
27
+ size: 'lg',
28
+ avatar: {
29
+ type: 'person',
30
+ name: 'Herman Friele',
31
+ },
32
+ title: 'Herman Friele',
33
+ description: 'Fødselsnr: XX.XX.XXXX',
34
+ },
35
+ {
36
+ id: 'inbox',
37
+ group: 'apps',
38
+ size: 'lg',
39
+ icon: 'inbox',
40
+ title: 'Innboks',
41
+ },
42
+ {
43
+ id: 'access',
44
+ group: 'apps',
45
+ size: 'lg',
46
+ icon: 'bookmark',
47
+ title: 'Tilganger',
48
+ },
49
+ {
50
+ id: 'access',
51
+ group: 'apps',
52
+ size: 'lg',
53
+ icon: 'menu-grid',
54
+ title: 'Alle skjema',
55
+ },
56
+ {
57
+ id: 'settings',
58
+ group: 'settings',
59
+ icon: 'cog',
60
+ title: 'Innstillinger',
61
+ },
62
+ ],
63
+ },
64
+ };
65
+
66
+ export const CollapsibleGlobalMenu: Story = {
67
+ args: {
68
+ theme: 'global',
69
+ groups: {
70
+ settings: {
71
+ defaultItemColor: 'default',
72
+ },
73
+ },
74
+ items: [
75
+ {
76
+ id: 'account',
77
+ group: 'account',
78
+ size: 'lg',
79
+ avatar: {
80
+ type: 'person',
81
+ name: 'Herman Friele',
82
+ },
83
+ title: 'Herman Friele',
84
+ description: 'Fødselsnr: XX.XX.XXXX',
85
+ },
86
+ {
87
+ id: 'innboks',
88
+ group: 'apps',
89
+ size: 'lg',
90
+ icon: 'inbox',
91
+ title: 'Innboks',
92
+ badge: '4',
93
+ collapsible: true,
94
+ items: [
95
+ {
96
+ id: 'utkast',
97
+ group: '1',
98
+ icon: 'doc-pencil',
99
+ title: 'Utkast',
100
+ },
101
+ {
102
+ id: 'sent',
103
+ group: '1',
104
+ icon: 'file-checkmark',
105
+ selected: true,
106
+ title: 'Sendt',
107
+ badge: 2,
108
+ },
109
+ {
110
+ id: 'bookmarks',
111
+ group: '3',
112
+ icon: 'bookmark',
113
+ title: 'Lagrede søk',
114
+ badge: 11,
115
+ },
116
+ {
117
+ id: 'arkiv',
118
+ group: '4',
119
+ icon: 'archive',
120
+ title: 'Arkivert',
121
+ },
122
+ {
123
+ id: 'trash',
124
+ group: '4',
125
+ icon: 'trash',
126
+ title: 'Papirkurv',
127
+ },
128
+ ],
129
+ },
130
+ {
131
+ id: 'tilganger',
132
+ group: 'apps',
133
+ size: 'lg',
134
+ icon: 'bookmark',
135
+ title: 'Tilganger',
136
+ },
137
+ {
138
+ id: 'skjema',
139
+ group: 'apps',
140
+ size: 'lg',
141
+ icon: 'menu-grid',
142
+ title: 'Alle skjema',
143
+ },
144
+ {
145
+ id: 'settings',
146
+ group: 'settings',
147
+ icon: 'cog',
148
+ title: 'Innstillinger',
149
+ },
150
+ ],
151
+ },
152
+ };
153
+
154
+ export const ExpandedGlobalMenu: Story = {
155
+ args: {
156
+ ...CollapsibleGlobalMenu.args,
157
+ items: [...(CollapsibleGlobalMenu?.args?.items ?? [])].map((item) => {
158
+ if (item.collapsible) {
159
+ return {
160
+ ...item,
161
+ group: item?.id === 'inbox' ? 'inbox' : '',
162
+ expanded: true,
163
+ };
164
+ }
165
+ return item;
166
+ }),
167
+ },
168
+ };
169
+
170
+ export const DrilldownMenu: Story = {
171
+ args: {
172
+ theme: 'global',
173
+ color: 'subtle',
174
+ groups: {
175
+ 'level-1': {
176
+ divider: true,
177
+ },
178
+ 'level-2': {
179
+ divider: true,
180
+ },
181
+ },
182
+ items: [
183
+ {
184
+ id: 'people',
185
+ group: 'level-1',
186
+ size: 'lg',
187
+ icon: 'menu-grid',
188
+ title: 'Alle skjema',
189
+ expanded: true,
190
+ items: [
191
+ {
192
+ group: 'level-2',
193
+ name: 'tema',
194
+ icon: 'teddy-bear',
195
+ title: 'Tema',
196
+ expanded: true,
197
+ items: [
198
+ {
199
+ group: 'level-3',
200
+ name: 'c1',
201
+ title: 'Kategori 1',
202
+ },
203
+ {
204
+ group: 'level-3',
205
+ name: 'c2',
206
+ title: 'Kategori 2',
207
+ },
208
+ {
209
+ group: 'level-3',
210
+ name: 'c3',
211
+ title: 'Kategori 3',
212
+ },
213
+ ],
214
+ },
215
+ ],
216
+ },
217
+ ],
218
+ },
219
+ };
220
+
221
+ export const InboxMenu: Story = {
222
+ args: {
223
+ theme: 'global',
224
+ groups: {},
225
+
226
+ items: [
227
+ {
228
+ id: 'innboks',
229
+ group: '1',
230
+ size: 'lg',
231
+ icon: 'inbox',
232
+ title: 'Innboks',
233
+ color: 'strong',
234
+ badge: 4,
235
+ },
236
+ {
237
+ id: 'utkast',
238
+ group: '2',
239
+ icon: 'doc-pencil',
240
+ title: 'Utkast',
241
+ },
242
+ {
243
+ id: 'sendt',
244
+ group: '2',
245
+ icon: 'file-checkmark',
246
+ selected: true,
247
+ title: 'Sendt',
248
+ badge: 2,
249
+ },
250
+ {
251
+ id: 'lagret',
252
+ group: '3',
253
+ icon: 'bookmark',
254
+ title: 'Lagrede søk',
255
+ badge: 11,
256
+ },
257
+ {
258
+ id: 'arkivert',
259
+ group: '4',
260
+ icon: 'archive',
261
+ title: 'Arkivert',
262
+ },
263
+ {
264
+ id: 'papirkurv',
265
+ group: '4',
266
+ disabled: true,
267
+ icon: 'trash',
268
+ title: 'Papirkurv',
269
+ },
270
+ ],
271
+
272
+ color: 'subtle',
273
+ },
274
+ };
275
+
276
+ export const InboxMenuWithShortcuts = {
277
+ args: {
278
+ theme: 'global',
279
+ groups: {
280
+ ...InboxMenu.args?.groups,
281
+ shortcuts: {
282
+ title: 'Snarveier',
283
+ defaultItemColor: 'default',
284
+ },
285
+ },
286
+ items: [
287
+ ...(InboxMenu.args?.items ?? []),
288
+ {
289
+ id: 'users',
290
+ group: 'shortcuts',
291
+ icon: 'person-group',
292
+ title: 'Brukere',
293
+ },
294
+ {
295
+ id: 'settings',
296
+ group: 'shortcuts',
297
+ icon: 'cog',
298
+ title: 'Innstillinger',
299
+ },
300
+ ],
301
+ },
302
+ };
303
+
304
+ export const PersonMenu: Story = {
305
+ args: {
306
+ theme: 'global',
307
+ groups: {},
308
+ items: [
309
+ {
310
+ id: 'person',
311
+ group: '1',
312
+ size: 'lg',
313
+ avatar: {
314
+ type: 'person',
315
+ name: 'Erik Huseklepp',
316
+ },
317
+ title: 'Erik Huseklepp',
318
+ },
319
+ {
320
+ id: 'profil',
321
+ group: '2',
322
+ icon: 'person-circle',
323
+ title: 'Kontaktinformasjon',
324
+ },
325
+ {
326
+ id: 'varslinger',
327
+ group: '2',
328
+ icon: 'bell',
329
+ title: 'Varslingsinnstillinger',
330
+ },
331
+ {
332
+ id: 'bookmarks',
333
+ group: '3',
334
+ icon: 'bookmark',
335
+ title: 'Favoritter',
336
+ },
337
+ {
338
+ id: 'grupper',
339
+ group: '3',
340
+ icon: 'hexagon-grid',
341
+ title: 'Grupper',
342
+ },
343
+ {
344
+ id: 'logg',
345
+ group: '4',
346
+ icon: 'clock-dashed',
347
+ title: 'Aktivitetslogg',
348
+ },
349
+ ],
350
+ },
351
+ };
352
+
353
+ export const CompanyMenu: Story = {
354
+ args: {
355
+ theme: 'global',
356
+ groups: {},
357
+ items: [
358
+ {
359
+ id: 'company',
360
+ group: '1',
361
+ size: 'lg',
362
+ avatar: {
363
+ type: 'company',
364
+ name: 'Bergen Bar',
365
+ },
366
+ title: 'Bergen Bar',
367
+ },
368
+ {
369
+ id: 'profil',
370
+ group: '2',
371
+ icon: 'buildings2',
372
+ title: 'Firmaprofil',
373
+ },
374
+ {
375
+ id: 'brukere',
376
+ group: '3',
377
+ icon: 'person-group',
378
+ title: 'Brukere',
379
+ },
380
+ {
381
+ id: 'grupper',
382
+ group: '3',
383
+ icon: 'hexagon-grid',
384
+ title: 'Grupper',
385
+ },
386
+ {
387
+ id: 'logg',
388
+ group: '4',
389
+ icon: 'clock-dashed',
390
+ title: 'Aktivitetslogg',
391
+ },
392
+ ],
393
+ },
394
+ };
395
+
396
+ export const AccountMenu: Story = {
397
+ args: {
398
+ theme: 'global',
399
+ groups: {
400
+ a1: {
401
+ title: 'Deg selv, favoritter og grupper',
402
+ },
403
+ b1: {
404
+ title: 'Andre kontoer',
405
+ },
406
+ },
407
+ items: [
408
+ {
409
+ id: '1',
410
+ group: 'a1',
411
+ avatar: {
412
+ type: 'person',
413
+ name: 'Dolly Duck',
414
+ },
415
+ title: 'Dolly Duck',
416
+ badge: '15',
417
+ },
418
+ {
419
+ id: '2',
420
+ group: 'a2',
421
+ avatar: {
422
+ type: 'company',
423
+ name: 'Bergen Bar',
424
+ },
425
+ title: 'Bergen Bar',
426
+ badge: 21,
427
+ },
428
+ {
429
+ id: '3',
430
+ group: 'a2',
431
+ avatar: {
432
+ type: 'company',
433
+ name: 'Sportsklubben Brann',
434
+ },
435
+ title: 'Sportsklubben Brann',
436
+ badge: '4',
437
+ },
438
+ {
439
+ id: '4',
440
+ group: 'a3',
441
+ avatarGroup: {
442
+ type: 'company',
443
+ items: [
444
+ {
445
+ name: 'Sportsklubben Brann',
446
+ },
447
+ {
448
+ name: 'Bergen Bar',
449
+ },
450
+ ],
451
+ },
452
+ title: 'Alle virksomheter',
453
+ },
454
+ {
455
+ id: '5',
456
+ group: 'b1',
457
+ avatar: {
458
+ type: 'company',
459
+ name: 'Jensens Laks',
460
+ },
461
+ title: 'Jensens laks',
462
+ },
463
+ {
464
+ id: '6',
465
+ group: 'b1',
466
+ avatar: {
467
+ type: 'company',
468
+ name: 'Haralds gym',
469
+ },
470
+ title: 'Haralds gym',
471
+ },
472
+ {
473
+ id: '7',
474
+ group: 'b1',
475
+ avatar: {
476
+ type: 'company',
477
+ name: 'Trim og tran',
478
+ },
479
+ title: 'Trim og tran',
480
+ },
481
+ ],
482
+ },
483
+ };
484
+
485
+ export const AccountMenuWithSearch: Story = {
486
+ args: {
487
+ ...AccountMenu.args,
488
+ search: {
489
+ placeholder: 'Søk i kontoer',
490
+ name: 'search',
491
+ value: '',
492
+ onChange: () => {},
493
+ },
494
+ },
495
+ };
@@ -0,0 +1,123 @@
1
+ import { MenuBase } from './MenuBase';
2
+ import { MenuGroup } from './MenuGroup';
3
+ import { MenuHeader } from './MenuHeader';
4
+ import { MenuSearch, type MenuSearchProps } from './MenuSearch';
5
+
6
+ import type { MenuItemColor, MenuItemSize } from './MenuItemBase';
7
+
8
+ import { MenuItem, type MenuItemProps } from './MenuItem';
9
+ import styles from './menu.module.css';
10
+
11
+ export type MenuTheme = 'global' | 'neutral' | 'company' | 'person';
12
+
13
+ interface MenuItemsGroupProps {
14
+ title?: string;
15
+ divider?: boolean;
16
+ defaultItemColor?: MenuItemColor;
17
+ defaultItemSize?: MenuItemSize;
18
+ }
19
+
20
+ export type MenuGroups = Record<string, MenuItemsGroupProps>;
21
+
22
+ export interface MenuProps {
23
+ theme?: MenuTheme;
24
+ defaultItemColor?: MenuItemColor;
25
+ defaultItemSize?: MenuItemSize;
26
+ groups?: MenuGroups;
27
+ items?: MenuItemProps[];
28
+ search?: MenuSearchProps;
29
+ }
30
+
31
+ const groupMenuItems = (items: MenuItemProps[]) => {
32
+ const groups: Record<string, MenuItemProps[]> = items?.reduce(
33
+ (acc: Record<string, MenuItemProps[]>, item: MenuItemProps) => {
34
+ const group = item?.group || '';
35
+ if (!acc[group]) {
36
+ acc[group] = [];
37
+ }
38
+ acc[group].push(item);
39
+ return acc;
40
+ },
41
+ {} as Record<string, MenuItemProps[]>,
42
+ );
43
+
44
+ return groups;
45
+ };
46
+
47
+ export const MenuItems = ({
48
+ defaultItemColor,
49
+ defaultItemSize,
50
+ groups = {},
51
+ items = [],
52
+ }: {
53
+ defaultItemColor: MenuItemColor;
54
+ defaultItemSize: MenuItemSize;
55
+ groups?: MenuGroups;
56
+ items: MenuItemProps[];
57
+ }) => {
58
+ const sections = groupMenuItems(items);
59
+
60
+ return Object.entries(sections)?.map(([key, options]) => {
61
+ const groupProps = groups?.[key];
62
+ return (
63
+ <MenuGroup as="ul" divider={groupProps?.divider} key={key}>
64
+ {groupProps?.title && (
65
+ <li className={styles.item}>
66
+ <MenuHeader title={groupProps.title} />
67
+ </li>
68
+ )}
69
+ {(options ?? [])
70
+ .filter((option) => !option.hidden)
71
+ .map((option, index) => {
72
+ if (option.expanded && option.items) {
73
+ return (
74
+ <li className={styles.item} aria-expanded="true" key={'item' + index}>
75
+ <MenuItem
76
+ {...option}
77
+ className={styles.menuItem}
78
+ color={option?.color || groupProps?.defaultItemColor || defaultItemColor}
79
+ size={option?.size || groupProps?.defaultItemSize || defaultItemSize}
80
+ key={'menu-item' + index}
81
+ />
82
+ <MenuItems
83
+ groups={groups}
84
+ items={option?.items}
85
+ defaultItemColor={defaultItemColor}
86
+ defaultItemSize={defaultItemSize}
87
+ />
88
+ </li>
89
+ );
90
+ }
91
+
92
+ return (
93
+ <li className={styles.item} key={'item' + index}>
94
+ <MenuItem
95
+ {...option}
96
+ className={styles.menuItem}
97
+ color={option?.color || groupProps?.defaultItemColor || defaultItemColor}
98
+ size={option?.size || groupProps?.defaultItemSize || defaultItemSize}
99
+ key={'menu-item' + index}
100
+ />
101
+ </li>
102
+ );
103
+ })}
104
+ </MenuGroup>
105
+ );
106
+ });
107
+ };
108
+
109
+ export const Menu = ({
110
+ theme,
111
+ defaultItemColor = 'subtle',
112
+ defaultItemSize = 'sm',
113
+ groups,
114
+ items = [],
115
+ search,
116
+ }: MenuProps) => {
117
+ return (
118
+ <MenuBase theme={theme}>
119
+ {search && <MenuSearch {...search} />}
120
+ <MenuItems items={items} groups={groups} defaultItemColor={defaultItemColor} defaultItemSize={defaultItemSize} />
121
+ </MenuBase>
122
+ );
123
+ };
@@ -0,0 +1,17 @@
1
+ import type { ElementType, ReactNode } from 'react';
2
+ import styles from './menu.module.css';
3
+
4
+ export interface MenuBaseProps {
5
+ as?: ElementType;
6
+ theme?: string;
7
+ children?: ReactNode;
8
+ }
9
+
10
+ export const MenuBase = ({ as = 'nav', theme, children }: MenuBaseProps) => {
11
+ const Component = as;
12
+ return (
13
+ <Component className={styles.menu} data-theme={theme}>
14
+ {children}
15
+ </Component>
16
+ );
17
+ };
@@ -0,0 +1,18 @@
1
+ import type { ElementType, ReactNode } from 'react';
2
+ import styles from './menu.module.css';
3
+
4
+ export interface MenuGroupProps {
5
+ as?: ElementType;
6
+ expanded?: boolean;
7
+ divider?: boolean;
8
+ children?: ReactNode;
9
+ }
10
+
11
+ export const MenuGroup = ({ as = 'ul', expanded, divider = true, children }: MenuGroupProps) => {
12
+ const Component = as;
13
+ return (
14
+ <Component aria-expanded={expanded} data-divider={divider} className={styles.group}>
15
+ {children}
16
+ </Component>
17
+ );
18
+ };
@@ -0,0 +1,13 @@
1
+ import styles from './menuHeader.module.css';
2
+
3
+ export interface MenuHeaderProps {
4
+ title: string;
5
+ }
6
+
7
+ export const MenuHeader = ({ title }: MenuHeaderProps) => {
8
+ return (
9
+ <header className={styles.header}>
10
+ <h2 className={styles.title}>{title}</h2>
11
+ </header>
12
+ );
13
+ };