rizzo-css 0.0.11 → 0.0.13

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 (233) hide show
  1. package/.env.example +12 -0
  2. package/LICENSE +21 -0
  3. package/README.md +17 -3
  4. package/bin/rizzo-css.js +98 -42
  5. package/dist/rizzo.min.css +3 -2
  6. package/package.json +5 -3
  7. package/scaffold/astro-app/README.md +13 -2
  8. package/scaffold/astro-app/src/components/Accordion.astro +178 -0
  9. package/scaffold/astro-app/src/components/Alert.astro +131 -0
  10. package/scaffold/astro-app/src/components/Avatar.astro +59 -0
  11. package/scaffold/astro-app/src/components/Badge.astro +24 -0
  12. package/scaffold/astro-app/src/components/Breadcrumb.astro +61 -0
  13. package/scaffold/astro-app/src/components/Button.astro +3 -0
  14. package/scaffold/astro-app/src/components/Card.astro +18 -0
  15. package/scaffold/astro-app/src/components/Checkbox.astro +38 -0
  16. package/scaffold/astro-app/src/components/CodeBlock.astro +393 -0
  17. package/scaffold/astro-app/src/components/CopyToClipboard.astro +219 -0
  18. package/scaffold/astro-app/src/components/Divider.astro +37 -0
  19. package/scaffold/astro-app/src/components/Dropdown.astro +807 -0
  20. package/scaffold/astro-app/src/components/FormGroup.astro +59 -0
  21. package/scaffold/astro-app/src/components/FrameworkSwitcher.astro +72 -0
  22. package/scaffold/astro-app/src/components/Input.astro +59 -0
  23. package/scaffold/astro-app/src/components/Modal.astro +212 -0
  24. package/scaffold/astro-app/src/components/Navbar.astro +701 -0
  25. package/scaffold/astro-app/src/components/Pagination.astro +240 -0
  26. package/scaffold/astro-app/src/components/ProgressBar.astro +65 -0
  27. package/scaffold/astro-app/src/components/Radio.astro +38 -0
  28. package/scaffold/astro-app/src/components/Search.astro +1259 -0
  29. package/scaffold/astro-app/src/components/Select.astro +49 -0
  30. package/scaffold/astro-app/src/components/Settings.astro +382 -0
  31. package/scaffold/astro-app/src/components/Spinner.astro +30 -0
  32. package/scaffold/astro-app/src/components/Table.astro +181 -0
  33. package/scaffold/astro-app/src/components/Tabs.astro +223 -0
  34. package/scaffold/astro-app/src/components/Textarea.astro +58 -0
  35. package/scaffold/astro-app/src/components/ThemeSwitcher.astro +504 -0
  36. package/scaffold/astro-app/src/components/Toast.astro +30 -0
  37. package/scaffold/astro-app/src/components/Tooltip.astro +32 -0
  38. package/scaffold/astro-app/src/components/icons/Brush.astro +10 -0
  39. package/scaffold/astro-app/src/components/icons/Cake.astro +11 -0
  40. package/scaffold/astro-app/src/components/icons/Check.astro +29 -0
  41. package/scaffold/astro-app/src/components/icons/Cherry.astro +11 -0
  42. package/scaffold/astro-app/src/components/icons/ChevronDown.astro +29 -0
  43. package/scaffold/astro-app/src/components/icons/Circle.astro +29 -0
  44. package/scaffold/astro-app/src/components/icons/Close.astro +30 -0
  45. package/scaffold/astro-app/src/components/icons/Cmd.astro +26 -0
  46. package/scaffold/astro-app/src/components/icons/Copy.astro +30 -0
  47. package/scaffold/astro-app/src/components/icons/Eye.astro +30 -0
  48. package/scaffold/astro-app/src/components/icons/Filter.astro +29 -0
  49. package/scaffold/astro-app/src/components/icons/Flame.astro +28 -0
  50. package/scaffold/astro-app/src/components/icons/Flower.astro +11 -0
  51. package/scaffold/astro-app/src/components/icons/Gear.astro +30 -0
  52. package/scaffold/astro-app/src/components/icons/Heart.astro +28 -0
  53. package/scaffold/astro-app/src/components/icons/IceCream.astro +31 -0
  54. package/scaffold/astro-app/src/components/icons/Leaf.astro +29 -0
  55. package/scaffold/astro-app/src/components/icons/Lemon.astro +11 -0
  56. package/scaffold/astro-app/src/components/icons/Moon.astro +29 -0
  57. package/scaffold/astro-app/src/components/icons/Owl.astro +34 -0
  58. package/scaffold/astro-app/src/components/icons/Palette.astro +33 -0
  59. package/scaffold/astro-app/src/components/icons/Rainbow.astro +31 -0
  60. package/scaffold/astro-app/src/components/icons/Search.astro +30 -0
  61. package/scaffold/astro-app/src/components/icons/Shield.astro +28 -0
  62. package/scaffold/astro-app/src/components/icons/Snowflake.astro +34 -0
  63. package/scaffold/astro-app/src/components/icons/Sort.astro +30 -0
  64. package/scaffold/astro-app/src/components/icons/Sun.astro +29 -0
  65. package/scaffold/astro-app/src/components/icons/Sunset.astro +10 -0
  66. package/scaffold/astro-app/src/components/icons/Zap.astro +9 -0
  67. package/scaffold/astro-app/src/components/icons/devicons/Astro.astro +53 -0
  68. package/scaffold/astro-app/src/components/icons/devicons/Bash.astro +34 -0
  69. package/scaffold/astro-app/src/components/icons/devicons/Css3.astro +29 -0
  70. package/scaffold/astro-app/src/components/icons/devicons/Git.astro +24 -0
  71. package/scaffold/astro-app/src/components/icons/devicons/Html5.astro +27 -0
  72. package/scaffold/astro-app/src/components/icons/devicons/Javascript.astro +25 -0
  73. package/scaffold/astro-app/src/components/icons/devicons/Nodejs.astro +47 -0
  74. package/scaffold/astro-app/src/components/icons/devicons/Plaintext.astro +33 -0
  75. package/scaffold/astro-app/src/components/icons/devicons/React.astro +27 -0
  76. package/scaffold/astro-app/src/components/icons/devicons/Svelte.astro +25 -0
  77. package/scaffold/astro-app/src/components/icons/devicons/Vue.astro +26 -0
  78. package/scaffold/astro-app/src/config/frameworks.ts +26 -0
  79. package/scaffold/astro-app/src/config/themes.ts +54 -0
  80. package/scaffold/astro-app/src/layouts/DocsLayout.astro +204 -0
  81. package/scaffold/astro-app/src/layouts/Layout.astro +11 -2
  82. package/scaffold/astro-app/src/pages/components/accordion.astro +172 -0
  83. package/scaffold/astro-app/src/pages/components/alert.astro +250 -0
  84. package/scaffold/astro-app/src/pages/components/avatar.astro +102 -0
  85. package/scaffold/astro-app/src/pages/components/badge.astro +119 -0
  86. package/scaffold/astro-app/src/pages/components/breadcrumb.astro +124 -0
  87. package/scaffold/astro-app/src/pages/components/button.astro +74 -0
  88. package/scaffold/astro-app/src/pages/components/cards.astro +247 -0
  89. package/scaffold/astro-app/src/pages/components/copy-to-clipboard.astro +49 -0
  90. package/scaffold/astro-app/src/pages/components/divider.astro +74 -0
  91. package/scaffold/astro-app/src/pages/components/dropdown.astro +394 -0
  92. package/scaffold/astro-app/src/pages/components/forms.astro +367 -0
  93. package/scaffold/astro-app/src/pages/components/icons.astro +246 -0
  94. package/scaffold/astro-app/src/pages/components/modal.astro +152 -0
  95. package/scaffold/astro-app/src/pages/components/navbar.astro +80 -0
  96. package/scaffold/astro-app/src/pages/components/pagination.astro +126 -0
  97. package/scaffold/astro-app/src/pages/components/progress-bar.astro +94 -0
  98. package/scaffold/astro-app/src/pages/components/search.astro +155 -0
  99. package/scaffold/astro-app/src/pages/components/settings.astro +78 -0
  100. package/scaffold/astro-app/src/pages/components/spinner.astro +81 -0
  101. package/scaffold/astro-app/src/pages/components/table.astro +144 -0
  102. package/scaffold/astro-app/src/pages/components/tabs.astro +220 -0
  103. package/scaffold/astro-app/src/pages/components/theme-switcher.astro +67 -0
  104. package/scaffold/astro-app/src/pages/components/toast.astro +157 -0
  105. package/scaffold/astro-app/src/pages/components/tooltip.astro +209 -0
  106. package/scaffold/astro-app/src/pages/components.astro +290 -0
  107. package/scaffold/astro-app/src/pages/docs/accessibility.astro +9 -0
  108. package/scaffold/astro-app/src/pages/docs/colors.astro +9 -0
  109. package/scaffold/astro-app/src/pages/docs/design-system.astro +9 -0
  110. package/scaffold/astro-app/src/pages/docs/getting-started.astro +9 -0
  111. package/scaffold/astro-app/src/pages/docs/index.astro +15 -0
  112. package/scaffold/astro-app/src/pages/docs/themes/[theme].astro +14 -0
  113. package/scaffold/astro-app/src/pages/docs/theming.astro +10 -0
  114. package/scaffold/astro-app/src/pages/index.astro +5 -11
  115. package/scaffold/svelte/Table.svelte +6 -5
  116. package/scaffold/svelte/Tabs.svelte +3 -1
  117. package/scaffold/svelte-app/README.md +9 -2
  118. package/scaffold/svelte-app/src/app.html +1 -1
  119. package/scaffold/svelte-app/src/lib/rizzo/Accordion.svelte +128 -0
  120. package/scaffold/svelte-app/src/lib/rizzo/Alert.svelte +85 -0
  121. package/scaffold/svelte-app/src/lib/rizzo/Avatar.svelte +39 -0
  122. package/scaffold/svelte-app/src/lib/rizzo/Badge.svelte +31 -0
  123. package/scaffold/svelte-app/src/lib/rizzo/Breadcrumb.svelte +49 -0
  124. package/scaffold/svelte-app/src/lib/rizzo/Button.svelte +27 -0
  125. package/scaffold/svelte-app/src/lib/rizzo/Card.svelte +17 -0
  126. package/scaffold/svelte-app/src/lib/rizzo/Checkbox.svelte +37 -0
  127. package/scaffold/svelte-app/src/lib/rizzo/CopyToClipboard.svelte +79 -0
  128. package/scaffold/svelte-app/src/lib/rizzo/Divider.svelte +28 -0
  129. package/scaffold/svelte-app/src/lib/rizzo/Dropdown.svelte +254 -0
  130. package/scaffold/svelte-app/src/lib/rizzo/FormGroup.svelte +51 -0
  131. package/scaffold/svelte-app/src/lib/rizzo/Input.svelte +59 -0
  132. package/scaffold/svelte-app/src/lib/rizzo/Modal.svelte +157 -0
  133. package/scaffold/svelte-app/src/lib/rizzo/Pagination.svelte +93 -0
  134. package/scaffold/svelte-app/src/lib/rizzo/ProgressBar.svelte +58 -0
  135. package/scaffold/svelte-app/src/lib/rizzo/Radio.svelte +38 -0
  136. package/scaffold/svelte-app/src/lib/rizzo/Select.svelte +51 -0
  137. package/scaffold/svelte-app/src/lib/rizzo/Spinner.svelte +14 -0
  138. package/scaffold/svelte-app/src/lib/rizzo/Table.svelte +158 -0
  139. package/scaffold/svelte-app/src/lib/rizzo/Tabs.svelte +117 -0
  140. package/scaffold/svelte-app/src/lib/rizzo/Textarea.svelte +59 -0
  141. package/scaffold/svelte-app/src/lib/rizzo/ThemeSwitcher.svelte +315 -0
  142. package/scaffold/svelte-app/src/lib/rizzo/Toast.svelte +33 -0
  143. package/scaffold/svelte-app/src/lib/rizzo/Tooltip.svelte +19 -0
  144. package/scaffold/svelte-app/src/lib/rizzo/icons/Check.svelte +29 -0
  145. package/scaffold/svelte-app/src/lib/rizzo/icons/ChevronDown.svelte +29 -0
  146. package/scaffold/svelte-app/src/lib/rizzo/icons/Circle.svelte +29 -0
  147. package/scaffold/svelte-app/src/lib/rizzo/icons/Close.svelte +30 -0
  148. package/scaffold/svelte-app/src/lib/rizzo/icons/Cmd.svelte +27 -0
  149. package/scaffold/svelte-app/src/lib/rizzo/icons/Copy.svelte +30 -0
  150. package/scaffold/svelte-app/src/lib/rizzo/icons/Eye.svelte +30 -0
  151. package/scaffold/svelte-app/src/lib/rizzo/icons/Filter.svelte +29 -0
  152. package/scaffold/svelte-app/src/lib/rizzo/icons/Gear.svelte +30 -0
  153. package/scaffold/svelte-app/src/lib/rizzo/icons/IceCream.svelte +31 -0
  154. package/scaffold/svelte-app/src/lib/rizzo/icons/Moon.svelte +29 -0
  155. package/scaffold/svelte-app/src/lib/rizzo/icons/Owl.svelte +34 -0
  156. package/scaffold/svelte-app/src/lib/rizzo/icons/Palette.svelte +33 -0
  157. package/scaffold/svelte-app/src/lib/rizzo/icons/Rainbow.svelte +31 -0
  158. package/scaffold/svelte-app/src/lib/rizzo/icons/Search.svelte +30 -0
  159. package/scaffold/svelte-app/src/lib/rizzo/icons/Snowflake.svelte +34 -0
  160. package/scaffold/svelte-app/src/lib/rizzo/icons/Sort.svelte +30 -0
  161. package/scaffold/svelte-app/src/lib/rizzo/icons/devicons/Astro.svelte +45 -0
  162. package/scaffold/svelte-app/src/lib/rizzo/icons/devicons/Bash.svelte +28 -0
  163. package/scaffold/svelte-app/src/lib/rizzo/icons/devicons/Css3.svelte +23 -0
  164. package/scaffold/svelte-app/src/lib/rizzo/icons/devicons/Git.svelte +18 -0
  165. package/scaffold/svelte-app/src/lib/rizzo/icons/devicons/Html5.svelte +21 -0
  166. package/scaffold/svelte-app/src/lib/rizzo/icons/devicons/Javascript.svelte +19 -0
  167. package/scaffold/svelte-app/src/lib/rizzo/icons/devicons/Nodejs.svelte +44 -0
  168. package/scaffold/svelte-app/src/lib/rizzo/icons/devicons/Plaintext.svelte +24 -0
  169. package/scaffold/svelte-app/src/lib/rizzo/icons/devicons/React.svelte +21 -0
  170. package/scaffold/svelte-app/src/lib/rizzo/icons/devicons/SvelteIcon.svelte +19 -0
  171. package/scaffold/svelte-app/src/lib/rizzo/icons/devicons/Vue.svelte +20 -0
  172. package/scaffold/svelte-app/src/lib/rizzo/index.ts +33 -0
  173. package/scaffold/svelte-app/src/lib/rizzo-docs/CodeBlock.svelte +239 -0
  174. package/scaffold/svelte-app/src/lib/rizzo-docs/SvelteDocPage.svelte +99 -0
  175. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/AccordionDoc.svelte +53 -0
  176. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/AlertDoc.svelte +114 -0
  177. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/AvatarDoc.svelte +92 -0
  178. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/BadgeDoc.svelte +60 -0
  179. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/BreadcrumbDoc.svelte +55 -0
  180. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/ButtonDoc.svelte +55 -0
  181. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/CardsDoc.svelte +173 -0
  182. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/ComingSoon.svelte +12 -0
  183. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/ComponentsOverview.svelte +92 -0
  184. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/CopyToClipboardDoc.svelte +26 -0
  185. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/DividerDoc.svelte +105 -0
  186. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/DropdownDoc.svelte +161 -0
  187. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/FormsDoc.svelte +375 -0
  188. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/IconsDoc.svelte +246 -0
  189. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/Index.svelte +8 -0
  190. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/ModalDoc.svelte +50 -0
  191. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/NavbarDoc.svelte +79 -0
  192. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/PaginationDoc.svelte +44 -0
  193. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/ProgressBarDoc.svelte +95 -0
  194. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/SearchDoc.svelte +147 -0
  195. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/SettingsDoc.svelte +158 -0
  196. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/SpinnerDoc.svelte +41 -0
  197. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/TableDoc.svelte +116 -0
  198. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/TabsDoc.svelte +152 -0
  199. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/ThemeSwitcherDoc.svelte +181 -0
  200. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/Theming.svelte +6 -0
  201. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/ToastDoc.svelte +136 -0
  202. package/scaffold/svelte-app/src/lib/rizzo-docs/pages/TooltipDoc.svelte +57 -0
  203. package/scaffold/svelte-app/src/routes/+page.svelte +2 -2
  204. package/scaffold/svelte-app/src/routes/components/+page.svelte +4 -0
  205. package/scaffold/svelte-app/src/routes/components/[slug]/+page.svelte +7 -0
  206. package/scaffold/vanilla/README.md +20 -8
  207. package/scaffold/vanilla/components/accordion.html +187 -0
  208. package/scaffold/vanilla/components/alert.html +187 -0
  209. package/scaffold/vanilla/components/avatar.html +187 -0
  210. package/scaffold/vanilla/components/badge.html +187 -0
  211. package/scaffold/vanilla/components/breadcrumb.html +187 -0
  212. package/scaffold/vanilla/components/button.html +187 -0
  213. package/scaffold/vanilla/components/cards.html +187 -0
  214. package/scaffold/vanilla/components/copy-to-clipboard.html +187 -0
  215. package/scaffold/vanilla/components/divider.html +187 -0
  216. package/scaffold/vanilla/components/dropdown.html +187 -0
  217. package/scaffold/vanilla/components/forms.html +187 -0
  218. package/scaffold/vanilla/components/icons.html +187 -0
  219. package/scaffold/vanilla/components/index.html +212 -0
  220. package/scaffold/vanilla/components/modal.html +187 -0
  221. package/scaffold/vanilla/components/navbar.html +187 -0
  222. package/scaffold/vanilla/components/pagination.html +187 -0
  223. package/scaffold/vanilla/components/progress-bar.html +187 -0
  224. package/scaffold/vanilla/components/search.html +187 -0
  225. package/scaffold/vanilla/components/settings.html +187 -0
  226. package/scaffold/vanilla/components/spinner.html +187 -0
  227. package/scaffold/vanilla/components/table.html +187 -0
  228. package/scaffold/vanilla/components/tabs.html +187 -0
  229. package/scaffold/vanilla/components/theme-switcher.html +187 -0
  230. package/scaffold/vanilla/components/toast.html +187 -0
  231. package/scaffold/vanilla/components/tooltip.html +187 -0
  232. package/scaffold/vanilla/index.html +17 -283
  233. package/scaffold/vanilla/js/main.js +748 -0
@@ -0,0 +1,807 @@
1
+ ---
2
+ import ChevronDown from './icons/ChevronDown.astro';
3
+
4
+ interface MenuItem {
5
+ label: string;
6
+ value?: string;
7
+ href?: string;
8
+ onClick?: string;
9
+ disabled?: boolean;
10
+ separator?: boolean;
11
+ submenu?: MenuItem[];
12
+ }
13
+
14
+ interface Props {
15
+ trigger: string;
16
+ items: MenuItem[];
17
+ id?: string;
18
+ class?: string;
19
+ position?: 'left' | 'right';
20
+ align?: 'start' | 'end';
21
+ }
22
+
23
+ const {
24
+ trigger,
25
+ items,
26
+ id,
27
+ class: className = '',
28
+ position = 'left',
29
+ align = 'start',
30
+ } = Astro.props;
31
+
32
+ const dropdownId = id || `dropdown-${Math.random().toString(36).substr(2, 9)}`;
33
+ const triggerId = `${dropdownId}-trigger`;
34
+ const menuId = `${dropdownId}-menu`;
35
+ ---
36
+
37
+ <div class={`dropdown ${className}`.trim()} data-dropdown={dropdownId}>
38
+ <button
39
+ type="button"
40
+ class="dropdown__trigger"
41
+ id={triggerId}
42
+ aria-expanded="false"
43
+ aria-haspopup="true"
44
+ aria-controls={menuId}
45
+ aria-label={trigger}
46
+ >
47
+ <span class="dropdown__trigger-text">{trigger}</span>
48
+ <ChevronDown class="dropdown__icon" width={16} height={16} />
49
+ </button>
50
+
51
+ <div
52
+ class={`dropdown__menu dropdown__menu--${position} dropdown__menu--${align}`}
53
+ id={menuId}
54
+ role="menu"
55
+ aria-labelledby={triggerId}
56
+ aria-label={`${trigger} menu`}
57
+ aria-orientation="vertical"
58
+ aria-hidden="true"
59
+ tabindex="-1"
60
+ >
61
+ {items.map((item, index) => {
62
+ if (item.separator) {
63
+ return <div class="dropdown__separator" role="separator" />;
64
+ }
65
+
66
+ const itemId = `${dropdownId}-item-${index}`;
67
+ const hasSubmenu = item.submenu && item.submenu.length > 0;
68
+ const submenuId = hasSubmenu ? `${dropdownId}-submenu-${index}` : undefined;
69
+
70
+ if (item.href) {
71
+ return (
72
+ <div class={`dropdown__item-wrapper ${hasSubmenu ? 'dropdown__item-wrapper--has-submenu' : ''}`}>
73
+ {hasSubmenu ? (
74
+ <div
75
+ class={`dropdown__item ${item.disabled ? 'dropdown__item--disabled' : ''} dropdown__item--has-submenu`}
76
+ role="menuitem"
77
+ id={itemId}
78
+ tabindex="-1"
79
+ aria-label={item.label}
80
+ aria-disabled={item.disabled ? 'true' : 'false'}
81
+ aria-expanded="false"
82
+ aria-haspopup="true"
83
+ aria-controls={submenuId}
84
+ data-dropdown-has-submenu="true"
85
+ >
86
+ <span>{item.label}</span>
87
+ <ChevronDown class="dropdown__item-arrow" width={12} height={12} />
88
+ </div>
89
+ ) : (
90
+ <div
91
+ class={`dropdown__item ${item.disabled ? 'dropdown__item--disabled' : ''}`}
92
+ role="menuitem"
93
+ id={itemId}
94
+ tabindex="-1"
95
+ aria-label={item.label}
96
+ aria-disabled={item.disabled ? 'true' : 'false'}
97
+ data-dropdown-href={item.href}
98
+ >
99
+ <span>{item.label}</span>
100
+ </div>
101
+ )}
102
+ {hasSubmenu && item.submenu && (
103
+ <div
104
+ class="dropdown__submenu"
105
+ id={submenuId}
106
+ role="menu"
107
+ aria-label={`${item.label} submenu`}
108
+ aria-hidden="true"
109
+ >
110
+ {item.submenu.map((subItem, subIndex) => {
111
+ if (subItem.separator) {
112
+ return <div class="dropdown__separator" role="separator" />;
113
+ }
114
+
115
+ const subItemId = `${submenuId}-item-${subIndex}`;
116
+ const hasNestedSubmenu = subItem.submenu && subItem.submenu.length > 0;
117
+ const nestedSubmenuId = hasNestedSubmenu ? `${subItemId}-submenu` : undefined;
118
+
119
+ if (subItem.href) {
120
+ return (
121
+ <div class={`dropdown__item-wrapper ${hasNestedSubmenu ? 'dropdown__item-wrapper--has-submenu' : ''}`}>
122
+ {hasNestedSubmenu ? (
123
+ <div
124
+ class={`dropdown__item dropdown__submenu-item ${subItem.disabled ? 'dropdown__item--disabled' : ''} dropdown__item--has-submenu`}
125
+ role="menuitem"
126
+ id={subItemId}
127
+ tabindex="-1"
128
+ aria-label={subItem.label}
129
+ aria-disabled={subItem.disabled ? 'true' : 'false'}
130
+ aria-expanded="false"
131
+ aria-haspopup="true"
132
+ aria-controls={nestedSubmenuId}
133
+ data-dropdown-has-submenu="true"
134
+ >
135
+ <span>{subItem.label}</span>
136
+ <ChevronDown class="dropdown__item-arrow" width={12} height={12} />
137
+ </div>
138
+ ) : (
139
+ <div
140
+ class={`dropdown__item dropdown__submenu-item ${subItem.disabled ? 'dropdown__item--disabled' : ''}`}
141
+ role="menuitem"
142
+ id={subItemId}
143
+ tabindex="-1"
144
+ aria-label={subItem.label}
145
+ aria-disabled={subItem.disabled ? 'true' : 'false'}
146
+ data-dropdown-href={subItem.href}
147
+ >
148
+ <span>{subItem.label}</span>
149
+ </div>
150
+ )}
151
+ {hasNestedSubmenu && subItem.submenu && (
152
+ <div
153
+ class="dropdown__submenu"
154
+ id={nestedSubmenuId}
155
+ role="menu"
156
+ aria-label={`${subItem.label} submenu`}
157
+ aria-hidden="true"
158
+ >
159
+ {subItem.submenu.map((nestedItem, nestedIndex) => {
160
+ if (nestedItem.separator) {
161
+ return <div class="dropdown__separator" role="separator" />;
162
+ }
163
+
164
+ const nestedItemId = `${nestedSubmenuId}-item-${nestedIndex}`;
165
+
166
+ if (nestedItem.href) {
167
+ return (
168
+ <div
169
+ class={`dropdown__item dropdown__submenu-item ${nestedItem.disabled ? 'dropdown__item--disabled' : ''}`}
170
+ role="menuitem"
171
+ id={nestedItemId}
172
+ tabindex="-1"
173
+ aria-disabled={nestedItem.disabled ? 'true' : 'false'}
174
+ data-dropdown-href={nestedItem.href}
175
+ >
176
+ <span>{nestedItem.label}</span>
177
+ </div>
178
+ );
179
+ }
180
+
181
+ return (
182
+ <div
183
+ class={`dropdown__item dropdown__submenu-item ${nestedItem.disabled ? 'dropdown__item--disabled' : ''}`}
184
+ role="menuitem"
185
+ id={nestedItemId}
186
+ tabindex="-1"
187
+ aria-label={nestedItem.label}
188
+ aria-disabled={nestedItem.disabled ? 'true' : 'false'}
189
+ data-dropdown-value={nestedItem.value || nestedItem.label}
190
+ data-dropdown-onclick={nestedItem.onClick || ''}
191
+ >
192
+ <span>{nestedItem.label}</span>
193
+ </div>
194
+ );
195
+ })}
196
+ </div>
197
+ )}
198
+ </div>
199
+ );
200
+ }
201
+
202
+ return (
203
+ <div class={`dropdown__item-wrapper ${hasNestedSubmenu ? 'dropdown__item-wrapper--has-submenu' : ''}`}>
204
+ <div
205
+ class={`dropdown__item dropdown__submenu-item ${subItem.disabled ? 'dropdown__item--disabled' : ''} ${hasNestedSubmenu ? 'dropdown__item--has-submenu' : ''}`}
206
+ role="menuitem"
207
+ id={subItemId}
208
+ tabindex="-1"
209
+ aria-label={subItem.label}
210
+ aria-disabled={subItem.disabled ? 'true' : 'false'}
211
+ aria-expanded={hasNestedSubmenu ? 'false' : undefined}
212
+ aria-haspopup={hasNestedSubmenu ? 'true' : undefined}
213
+ aria-controls={nestedSubmenuId}
214
+ data-dropdown-value={subItem.value || subItem.label}
215
+ data-dropdown-onclick={subItem.onClick || ''}
216
+ data-dropdown-has-submenu={hasNestedSubmenu ? 'true' : undefined}
217
+ >
218
+ <span>{subItem.label}</span>
219
+ {hasNestedSubmenu && <ChevronDown class="dropdown__item-arrow" width={12} height={12} />}
220
+ </div>
221
+ {hasNestedSubmenu && subItem.submenu && (
222
+ <div
223
+ class="dropdown__submenu"
224
+ id={nestedSubmenuId}
225
+ role="menu"
226
+ aria-label={`${subItem.label} submenu`}
227
+ aria-hidden="true"
228
+ >
229
+ {subItem.submenu.map((nestedItem, nestedIndex) => {
230
+ if (nestedItem.separator) {
231
+ return <div class="dropdown__separator" role="separator" />;
232
+ }
233
+
234
+ const nestedItemId = `${nestedSubmenuId}-item-${nestedIndex}`;
235
+
236
+ if (nestedItem.href) {
237
+ return (
238
+ <div
239
+ class={`dropdown__item dropdown__submenu-item ${nestedItem.disabled ? 'dropdown__item--disabled' : ''}`}
240
+ role="menuitem"
241
+ id={nestedItemId}
242
+ tabindex="-1"
243
+ aria-label={nestedItem.label}
244
+ aria-disabled={nestedItem.disabled ? 'true' : 'false'}
245
+ data-dropdown-href={nestedItem.href}
246
+ >
247
+ <span>{nestedItem.label}</span>
248
+ </div>
249
+ );
250
+ }
251
+
252
+ return (
253
+ <div
254
+ class={`dropdown__item dropdown__submenu-item ${nestedItem.disabled ? 'dropdown__item--disabled' : ''}`}
255
+ role="menuitem"
256
+ id={nestedItemId}
257
+ tabindex="-1"
258
+ aria-disabled={nestedItem.disabled ? 'true' : 'false'}
259
+ data-dropdown-value={nestedItem.value || nestedItem.label}
260
+ data-dropdown-onclick={nestedItem.onClick || ''}
261
+ >
262
+ <span>{nestedItem.label}</span>
263
+ </div>
264
+ );
265
+ })}
266
+ </div>
267
+ )}
268
+ </div>
269
+ );
270
+ })}
271
+ </div>
272
+ )}
273
+ </div>
274
+ );
275
+ }
276
+
277
+ return (
278
+ <div class={`dropdown__item-wrapper ${hasSubmenu ? 'dropdown__item-wrapper--has-submenu' : ''}`}>
279
+ <div
280
+ class={`dropdown__item ${item.disabled ? 'dropdown__item--disabled' : ''} ${hasSubmenu ? 'dropdown__item--has-submenu' : ''}`}
281
+ role="menuitem"
282
+ id={itemId}
283
+ tabindex="-1"
284
+ aria-disabled={item.disabled ? 'true' : 'false'}
285
+ aria-expanded={hasSubmenu ? 'false' : undefined}
286
+ aria-haspopup={hasSubmenu ? 'true' : undefined}
287
+ aria-controls={submenuId}
288
+ data-dropdown-value={item.value || item.label}
289
+ data-dropdown-onclick={item.onClick || ''}
290
+ data-dropdown-has-submenu={hasSubmenu ? 'true' : undefined}
291
+ >
292
+ <span>{item.label}</span>
293
+ {hasSubmenu && <ChevronDown class="dropdown__item-arrow" width={12} height={12} />}
294
+ </div>
295
+ {hasSubmenu && item.submenu && (
296
+ <div
297
+ class="dropdown__submenu"
298
+ id={submenuId}
299
+ role="menu"
300
+ aria-label={`${item.label} submenu`}
301
+ aria-hidden="true"
302
+ >
303
+ {item.submenu.map((subItem, subIndex) => {
304
+ if (subItem.separator) {
305
+ return <div class="dropdown__separator" role="separator" />;
306
+ }
307
+
308
+ const subItemId = `${submenuId}-item-${subIndex}`;
309
+ const hasNestedSubmenu = subItem.submenu && subItem.submenu.length > 0;
310
+ const nestedSubmenuId = hasNestedSubmenu ? `${subItemId}-submenu` : undefined;
311
+
312
+ if (subItem.href) {
313
+ return (
314
+ <div class={`dropdown__item-wrapper ${hasNestedSubmenu ? 'dropdown__item-wrapper--has-submenu' : ''}`}>
315
+ {hasNestedSubmenu ? (
316
+ <div
317
+ class={`dropdown__item dropdown__submenu-item ${subItem.disabled ? 'dropdown__item--disabled' : ''} dropdown__item--has-submenu`}
318
+ role="menuitem"
319
+ id={subItemId}
320
+ tabindex="-1"
321
+ aria-disabled={subItem.disabled ? 'true' : 'false'}
322
+ aria-expanded="false"
323
+ aria-haspopup="true"
324
+ aria-controls={nestedSubmenuId}
325
+ data-dropdown-has-submenu="true"
326
+ >
327
+ <span>{subItem.label}</span>
328
+ <ChevronDown class="dropdown__item-arrow" width={12} height={12} />
329
+ </div>
330
+ ) : (
331
+ <div
332
+ class={`dropdown__item dropdown__submenu-item ${subItem.disabled ? 'dropdown__item--disabled' : ''}`}
333
+ role="menuitem"
334
+ id={subItemId}
335
+ tabindex="-1"
336
+ aria-disabled={subItem.disabled ? 'true' : 'false'}
337
+ data-dropdown-href={subItem.href}
338
+ >
339
+ <span>{subItem.label}</span>
340
+ </div>
341
+ )}
342
+ {hasNestedSubmenu && subItem.submenu && (
343
+ <div
344
+ class="dropdown__submenu"
345
+ id={nestedSubmenuId}
346
+ role="menu"
347
+ aria-label={`${subItem.label} submenu`}
348
+ aria-hidden="true"
349
+ >
350
+ {subItem.submenu.map((nestedItem, nestedIndex) => {
351
+ if (nestedItem.separator) {
352
+ return <div class="dropdown__separator" role="separator" />;
353
+ }
354
+
355
+ const nestedItemId = `${nestedSubmenuId}-item-${nestedIndex}`;
356
+
357
+ if (nestedItem.href) {
358
+ return (
359
+ <div
360
+ class={`dropdown__item dropdown__submenu-item ${nestedItem.disabled ? 'dropdown__item--disabled' : ''}`}
361
+ role="menuitem"
362
+ id={nestedItemId}
363
+ tabindex="-1"
364
+ aria-label={nestedItem.label}
365
+ aria-disabled={nestedItem.disabled ? 'true' : 'false'}
366
+ data-dropdown-href={nestedItem.href}
367
+ >
368
+ <span>{nestedItem.label}</span>
369
+ </div>
370
+ );
371
+ }
372
+
373
+ return (
374
+ <div
375
+ class={`dropdown__item dropdown__submenu-item ${nestedItem.disabled ? 'dropdown__item--disabled' : ''}`}
376
+ role="menuitem"
377
+ id={nestedItemId}
378
+ tabindex="-1"
379
+ aria-disabled={nestedItem.disabled ? 'true' : 'false'}
380
+ data-dropdown-value={nestedItem.value || nestedItem.label}
381
+ data-dropdown-onclick={nestedItem.onClick || ''}
382
+ >
383
+ <span>{nestedItem.label}</span>
384
+ </div>
385
+ );
386
+ })}
387
+ </div>
388
+ )}
389
+ </div>
390
+ );
391
+ }
392
+
393
+ return (
394
+ <div class={`dropdown__item-wrapper ${hasNestedSubmenu ? 'dropdown__item-wrapper--has-submenu' : ''}`}>
395
+ <div
396
+ class={`dropdown__item dropdown__submenu-item ${subItem.disabled ? 'dropdown__item--disabled' : ''} ${hasNestedSubmenu ? 'dropdown__item--has-submenu' : ''}`}
397
+ role="menuitem"
398
+ id={subItemId}
399
+ tabindex="-1"
400
+ aria-label={subItem.label}
401
+ aria-disabled={subItem.disabled ? 'true' : 'false'}
402
+ aria-expanded={hasNestedSubmenu ? 'false' : undefined}
403
+ aria-haspopup={hasNestedSubmenu ? 'true' : undefined}
404
+ aria-controls={nestedSubmenuId}
405
+ data-dropdown-value={subItem.value || subItem.label}
406
+ data-dropdown-onclick={subItem.onClick || ''}
407
+ data-dropdown-has-submenu={hasNestedSubmenu ? 'true' : undefined}
408
+ >
409
+ <span>{subItem.label}</span>
410
+ {hasNestedSubmenu && <ChevronDown class="dropdown__item-arrow" width={12} height={12} />}
411
+ </div>
412
+ {hasNestedSubmenu && subItem.submenu && (
413
+ <div
414
+ class="dropdown__submenu"
415
+ id={nestedSubmenuId}
416
+ role="menu"
417
+ aria-label={`${subItem.label} submenu`}
418
+ aria-hidden="true"
419
+ >
420
+ {subItem.submenu.map((nestedItem, nestedIndex) => {
421
+ if (nestedItem.separator) {
422
+ return <div class="dropdown__separator" role="separator" />;
423
+ }
424
+
425
+ const nestedItemId = `${nestedSubmenuId}-item-${nestedIndex}`;
426
+
427
+ if (nestedItem.href) {
428
+ return (
429
+ <div
430
+ class={`dropdown__item dropdown__submenu-item ${nestedItem.disabled ? 'dropdown__item--disabled' : ''}`}
431
+ role="menuitem"
432
+ id={nestedItemId}
433
+ tabindex="-1"
434
+ aria-disabled={nestedItem.disabled ? 'true' : 'false'}
435
+ data-dropdown-href={nestedItem.href}
436
+ >
437
+ <span>{nestedItem.label}</span>
438
+ </div>
439
+ );
440
+ }
441
+
442
+ return (
443
+ <div
444
+ class={`dropdown__item dropdown__submenu-item ${nestedItem.disabled ? 'dropdown__item--disabled' : ''}`}
445
+ role="menuitem"
446
+ id={nestedItemId}
447
+ tabindex="-1"
448
+ aria-disabled={nestedItem.disabled ? 'true' : 'false'}
449
+ data-dropdown-value={nestedItem.value || nestedItem.label}
450
+ data-dropdown-onclick={nestedItem.onClick || ''}
451
+ >
452
+ <span>{nestedItem.label}</span>
453
+ </div>
454
+ );
455
+ })}
456
+ </div>
457
+ )}
458
+ </div>
459
+ );
460
+ })}
461
+ </div>
462
+ )}
463
+ </div>
464
+ );
465
+ })}
466
+ </div>
467
+ </div>
468
+
469
+ <script is:inline>
470
+ (function initDropdown() {
471
+ const init = () => {
472
+ const dropdowns = document.querySelectorAll('[data-dropdown]');
473
+
474
+ dropdowns.forEach((dropdown) => {
475
+ const dropdownId = dropdown.getAttribute('data-dropdown');
476
+ if (!dropdownId) return;
477
+
478
+ if (dropdown.hasAttribute('data-dropdown-initialized')) return;
479
+ dropdown.setAttribute('data-dropdown-initialized', 'true');
480
+
481
+ const trigger = dropdown.querySelector('.dropdown__trigger');
482
+ const menu = dropdown.querySelector('.dropdown__menu');
483
+ const items = dropdown.querySelectorAll('.dropdown__item');
484
+ const itemWrappers = dropdown.querySelectorAll('.dropdown__item-wrapper');
485
+
486
+ if (!trigger || !menu) return;
487
+
488
+ let currentIndex = -1;
489
+
490
+ const getVisibleItems = () => {
491
+ return Array.from(items).filter(item => {
492
+ const isDisabled = item.getAttribute('aria-disabled') === 'true';
493
+ const wrapper = item.closest('.dropdown__item-wrapper');
494
+ const submenu = wrapper ? wrapper.querySelector('.dropdown__submenu') : null;
495
+ // Don't include submenu items in main navigation
496
+ if (submenu && item.closest('.dropdown__submenu')) {
497
+ return false;
498
+ }
499
+ return !isDisabled && (item.offsetParent !== null || menu.classList.contains('dropdown__menu--open'));
500
+ });
501
+ };
502
+
503
+ // Close all submenus except the one being opened
504
+ const closeAllSubmenus = (exceptWrapper = null) => {
505
+ itemWrappers.forEach((wrapper) => {
506
+ if (wrapper === exceptWrapper) return; // Don't close the one we're opening
507
+ const submenu = wrapper.querySelector('.dropdown__submenu');
508
+ const item = wrapper.querySelector('.dropdown__item');
509
+ if (submenu && item) {
510
+ submenu.classList.remove('dropdown__submenu--open');
511
+ submenu.setAttribute('aria-hidden', 'true');
512
+ item.setAttribute('aria-expanded', 'false');
513
+ }
514
+ });
515
+ };
516
+
517
+ // Toggle submenu
518
+ const toggleSubmenu = (wrapper, open) => {
519
+ const submenu = wrapper.querySelector('.dropdown__submenu');
520
+ const item = wrapper.querySelector('.dropdown__item');
521
+ if (!submenu || !item) return;
522
+
523
+ const isOpen = open !== undefined ? open : submenu.classList.contains('dropdown__submenu--open');
524
+ const willBeOpen = !isOpen;
525
+
526
+ // Close other submenus at the same level, but keep parent submenus open
527
+ if (willBeOpen) {
528
+ // Find all submenus at the same level (siblings of the current wrapper's parent)
529
+ const parentSubmenu = wrapper.closest('.dropdown__submenu');
530
+ if (parentSubmenu) {
531
+ // Close siblings within the same parent submenu
532
+ const siblingWrappers = parentSubmenu.querySelectorAll('.dropdown__item-wrapper');
533
+ siblingWrappers.forEach((siblingWrapper) => {
534
+ if (siblingWrapper === wrapper) return; // Don't close the one we're opening
535
+ const siblingSubmenu = siblingWrapper.querySelector('.dropdown__submenu');
536
+ const siblingItem = siblingWrapper.querySelector('.dropdown__item');
537
+ if (siblingSubmenu && siblingItem) {
538
+ siblingSubmenu.classList.remove('dropdown__submenu--open');
539
+ siblingSubmenu.setAttribute('aria-hidden', 'true');
540
+ siblingItem.setAttribute('aria-expanded', 'false');
541
+ }
542
+ });
543
+ } else {
544
+ // Main menu level - close all other main menu submenus
545
+ closeAllSubmenus(wrapper);
546
+ }
547
+ }
548
+
549
+ // Toggle the open class
550
+ if (willBeOpen) {
551
+ submenu.classList.add('dropdown__submenu--open');
552
+ submenu.setAttribute('aria-hidden', 'false');
553
+ item.setAttribute('aria-expanded', 'true');
554
+ } else {
555
+ submenu.classList.remove('dropdown__submenu--open');
556
+ submenu.setAttribute('aria-hidden', 'true');
557
+ item.setAttribute('aria-expanded', 'false');
558
+ }
559
+
560
+ // Position submenu - now positioned directly under the item
561
+ if (willBeOpen) {
562
+ // Submenu is positioned directly under via CSS (top: 100%, left: 0, right: 0)
563
+ // No JavaScript positioning needed
564
+ } else {
565
+ // Reset any inline styles when closing
566
+ submenu.style.left = '';
567
+ submenu.style.right = '';
568
+ submenu.style.top = '';
569
+ submenu.style.marginLeft = '';
570
+ submenu.style.marginRight = '';
571
+ submenu.style.marginTop = '';
572
+ submenu.style.maxWidth = '';
573
+ submenu.style.width = '';
574
+ }
575
+ };
576
+
577
+ const toggleMenu = (open) => {
578
+ const isOpen = open !== undefined ? open : menu.classList.contains('dropdown__menu--open');
579
+ const willBeOpen = !isOpen;
580
+
581
+ menu.classList.toggle('dropdown__menu--open', willBeOpen);
582
+ trigger.setAttribute('aria-expanded', willBeOpen.toString());
583
+ menu.setAttribute('aria-hidden', (!willBeOpen).toString());
584
+
585
+ items.forEach((item) => {
586
+ item.setAttribute('tabindex', willBeOpen ? '0' : '-1');
587
+ });
588
+
589
+ if (!willBeOpen) {
590
+ trigger.focus();
591
+ currentIndex = -1;
592
+ closeAllSubmenus();
593
+ } else {
594
+ const visibleItems = getVisibleItems();
595
+ if (visibleItems.length > 0) {
596
+ currentIndex = 0;
597
+ setTimeout(() => visibleItems[0].focus(), 0);
598
+ }
599
+ }
600
+ };
601
+
602
+ const closeMenu = () => {
603
+ menu.classList.remove('dropdown__menu--open');
604
+ trigger.setAttribute('aria-expanded', 'false');
605
+ menu.setAttribute('aria-hidden', 'true');
606
+ items.forEach((item) => {
607
+ item.setAttribute('tabindex', '-1');
608
+ });
609
+ closeAllSubmenus();
610
+ currentIndex = -1;
611
+ };
612
+
613
+ // Handle outside click
614
+ const handleOutsideClick = (e) => {
615
+ if (e && e.target && !dropdown.contains(e.target)) {
616
+ closeMenu();
617
+ document.removeEventListener('click', handleOutsideClick);
618
+ }
619
+ };
620
+
621
+ const wrappedToggleMenu = (open) => {
622
+ const wasOpen = menu.classList.contains('dropdown__menu--open');
623
+ toggleMenu(open);
624
+ const isNowOpen = menu.classList.contains('dropdown__menu--open');
625
+
626
+ if (isNowOpen && !wasOpen) {
627
+ setTimeout(() => document.addEventListener('click', handleOutsideClick), 0);
628
+ } else if (!isNowOpen && wasOpen) {
629
+ document.removeEventListener('click', handleOutsideClick);
630
+ }
631
+ };
632
+
633
+ // Trigger click
634
+ trigger.addEventListener('click', () => wrappedToggleMenu(undefined));
635
+
636
+ // Menu item click handler - handles all clicks including nested submenus
637
+ menu.addEventListener('click', (e) => {
638
+ const target = e.target;
639
+ // Find the item - could be the button/a itself, or a child element (span, arrow icon, etc.)
640
+ const item = target.closest('.dropdown__item');
641
+ if (!item) return;
642
+
643
+ // Stop propagation to prevent outside click handler from firing
644
+ e.stopPropagation();
645
+
646
+ const isDisabled = item.getAttribute('aria-disabled') === 'true';
647
+ if (isDisabled) {
648
+ e.preventDefault();
649
+ return;
650
+ }
651
+
652
+ // FIRST: Check if item has submenu by finding wrapper and checking for submenu
653
+ // This must be checked FIRST, regardless of whether item is in a submenu or not
654
+ const wrapper = item.closest('.dropdown__item-wrapper');
655
+
656
+ if (wrapper) {
657
+ const submenu = wrapper.querySelector('.dropdown__submenu');
658
+ if (submenu) {
659
+ // Item has submenu - toggle it (works for main menu items AND nested submenu items)
660
+ e.preventDefault();
661
+ e.stopImmediatePropagation();
662
+ toggleSubmenu(wrapper, undefined);
663
+ return;
664
+ }
665
+ }
666
+
667
+ // Item does NOT have a submenu - handle as regular item
668
+ const href = item.getAttribute('data-dropdown-href');
669
+ if (href) {
670
+ window.location.href = href;
671
+ }
672
+ const onClick = item.getAttribute('data-dropdown-onclick');
673
+ if (onClick && typeof window[onClick] === 'function') {
674
+ const value = item.getAttribute('data-dropdown-value') || item.textContent?.trim() || '';
675
+ window[onClick](value);
676
+ }
677
+
678
+ // Close menu for final items (items without submenus)
679
+ closeMenu();
680
+ });
681
+
682
+ // Keyboard navigation on trigger
683
+ trigger.addEventListener('keydown', (e) => {
684
+ if (e && 'key' in e) {
685
+ const keyEvent = e;
686
+ if (keyEvent.key === 'Enter' || keyEvent.key === ' ') {
687
+ keyEvent.preventDefault();
688
+ wrappedToggleMenu(undefined);
689
+ } else if (keyEvent.key === 'ArrowDown') {
690
+ keyEvent.preventDefault();
691
+ const visibleItems = getVisibleItems();
692
+ if (visibleItems.length > 0) {
693
+ currentIndex = 0;
694
+ wrappedToggleMenu(true);
695
+ setTimeout(() => visibleItems[0].focus(), 0);
696
+ }
697
+ } else if (keyEvent.key === 'ArrowUp') {
698
+ keyEvent.preventDefault();
699
+ const visibleItems = getVisibleItems();
700
+ if (visibleItems.length > 0) {
701
+ currentIndex = visibleItems.length - 1;
702
+ wrappedToggleMenu(true);
703
+ setTimeout(() => visibleItems[currentIndex].focus(), 0);
704
+ }
705
+ } else if (keyEvent.key === 'Escape') {
706
+ keyEvent.preventDefault();
707
+ closeMenu();
708
+ }
709
+ }
710
+ });
711
+
712
+ // Keyboard navigation in menu
713
+ menu.addEventListener('keydown', (e) => {
714
+ if (e && 'key' in e) {
715
+ const keyEvent = e;
716
+ const visibleItems = getVisibleItems();
717
+
718
+ if (keyEvent.key === 'Escape') {
719
+ keyEvent.preventDefault();
720
+ closeMenu();
721
+ trigger.focus();
722
+ } else if (keyEvent.key === 'ArrowDown') {
723
+ keyEvent.preventDefault();
724
+ currentIndex = (currentIndex + 1) % visibleItems.length;
725
+ visibleItems[currentIndex].focus();
726
+ } else if (keyEvent.key === 'ArrowUp') {
727
+ keyEvent.preventDefault();
728
+ currentIndex = currentIndex <= 0 ? visibleItems.length - 1 : currentIndex - 1;
729
+ visibleItems[currentIndex].focus();
730
+ } else if (keyEvent.key === 'Home') {
731
+ keyEvent.preventDefault();
732
+ currentIndex = 0;
733
+ visibleItems[0].focus();
734
+ } else if (keyEvent.key === 'End') {
735
+ keyEvent.preventDefault();
736
+ currentIndex = visibleItems.length - 1;
737
+ visibleItems[currentIndex].focus();
738
+ } else if (keyEvent.key === 'ArrowRight') {
739
+ // Open submenu if available
740
+ const target = keyEvent.target;
741
+ if (target && target instanceof HTMLElement) {
742
+ const wrapper = target.closest('.dropdown__item-wrapper');
743
+ if (wrapper) {
744
+ const submenu = wrapper.querySelector('.dropdown__submenu');
745
+ if (submenu) {
746
+ keyEvent.preventDefault();
747
+ toggleSubmenu(wrapper, true);
748
+ const firstSubItem = submenu.querySelector('.dropdown__item');
749
+ if (firstSubItem) {
750
+ setTimeout(() => firstSubItem.focus(), 0);
751
+ }
752
+ }
753
+ }
754
+ }
755
+ } else if (keyEvent.key === 'ArrowLeft') {
756
+ // Close submenu if in one
757
+ const target = keyEvent.target;
758
+ if (target && target instanceof HTMLElement) {
759
+ const submenu = target.closest('.dropdown__submenu');
760
+ if (submenu) {
761
+ keyEvent.preventDefault();
762
+ const wrapper = submenu.closest('.dropdown__item-wrapper');
763
+ if (wrapper) {
764
+ toggleSubmenu(wrapper, false);
765
+ const parentItem = wrapper.querySelector('.dropdown__item');
766
+ if (parentItem) {
767
+ parentItem.focus();
768
+ }
769
+ }
770
+ }
771
+ }
772
+ } else if (keyEvent.key === 'Enter' || keyEvent.key === ' ') {
773
+ keyEvent.preventDefault();
774
+ const target = keyEvent.target;
775
+ if (target && target instanceof HTMLElement) {
776
+ const isDisabled = target.getAttribute('aria-disabled') === 'true';
777
+ if (!isDisabled) {
778
+ // Check if item has submenu
779
+ const wrapper = target.closest('.dropdown__item-wrapper');
780
+ const submenu = wrapper ? wrapper.querySelector('.dropdown__submenu') : null;
781
+ if (submenu) {
782
+ toggleSubmenu(wrapper, undefined);
783
+ } else {
784
+ if (target.tagName === 'A') {
785
+ target.click();
786
+ } else if (target.tagName === 'BUTTON') {
787
+ target.click();
788
+ }
789
+ closeMenu();
790
+ }
791
+ }
792
+ }
793
+ } else if (keyEvent.key === 'Tab') {
794
+ closeMenu();
795
+ }
796
+ }
797
+ });
798
+ });
799
+ };
800
+
801
+ if (document.readyState === 'loading') {
802
+ document.addEventListener('DOMContentLoaded', init);
803
+ } else {
804
+ init();
805
+ }
806
+ })();
807
+ </script>