mtrl 0.3.7 → 0.3.9

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 (691) hide show
  1. package/dist/LICENSE +21 -0
  2. package/dist/README.md +324 -0
  3. package/dist/components/badge/api.d.ts +48 -0
  4. package/{src/components/badge/badge.ts → dist/components/badge/badge.d.ts} +14 -57
  5. package/dist/components/badge/config.d.ts +79 -0
  6. package/dist/components/badge/constants.d.ts +35 -0
  7. package/dist/components/badge/features.d.ts +36 -0
  8. package/dist/components/badge/index.d.ts +8 -0
  9. package/dist/components/badge/types.d.ts +256 -0
  10. package/dist/components/bottom-app-bar/bottom-app-bar.d.ts +12 -0
  11. package/dist/components/bottom-app-bar/config.d.ts +16 -0
  12. package/dist/components/bottom-app-bar/constants.d.ts +22 -0
  13. package/{src/components/bottom-app-bar/index.ts → dist/components/bottom-app-bar/index.d.ts} +0 -9
  14. package/dist/components/bottom-app-bar/types.d.ts +96 -0
  15. package/dist/components/button/api.d.ts +47 -0
  16. package/dist/components/button/button.d.ts +25 -0
  17. package/dist/components/button/config.d.ts +59 -0
  18. package/dist/components/button/constants.d.ts +43 -0
  19. package/dist/components/button/index.d.ts +6 -0
  20. package/dist/components/button/types.d.ts +265 -0
  21. package/dist/components/card/api.d.ts +11 -0
  22. package/{src/components/card/card.ts → dist/components/card/card.d.ts} +10 -58
  23. package/dist/components/card/config.d.ts +105 -0
  24. package/dist/components/card/constants.d.ts +85 -0
  25. package/dist/components/card/content.d.ts +92 -0
  26. package/dist/components/card/features.d.ts +131 -0
  27. package/{src/components/card/index.ts → dist/components/card/index.d.ts} +13 -74
  28. package/dist/components/card/types.d.ts +471 -0
  29. package/dist/components/carousel/api.d.ts +33 -0
  30. package/dist/components/carousel/carousel.d.ts +75 -0
  31. package/dist/components/carousel/config.d.ts +45 -0
  32. package/dist/components/carousel/constants.d.ts +165 -0
  33. package/dist/components/carousel/features/drag.d.ts +8 -0
  34. package/{src/components/carousel/features/index.ts → dist/components/carousel/features/index.d.ts} +1 -4
  35. package/dist/components/carousel/features/slides.d.ts +8 -0
  36. package/{src/components/carousel/index.ts → dist/components/carousel/index.d.ts} +10 -29
  37. package/dist/components/carousel/types.d.ts +276 -0
  38. package/dist/components/checkbox/api.d.ts +7 -0
  39. package/dist/components/checkbox/checkbox.d.ts +65 -0
  40. package/dist/components/checkbox/config.d.ts +44 -0
  41. package/dist/components/checkbox/constants.d.ts +63 -0
  42. package/{src/components/checkbox/index.ts → dist/components/checkbox/index.d.ts} +8 -25
  43. package/dist/components/checkbox/types.d.ts +242 -0
  44. package/dist/components/chips/api.d.ts +49 -0
  45. package/dist/components/chips/chip/api.d.ts +23 -0
  46. package/dist/components/chips/chip/chip.d.ts +8 -0
  47. package/dist/components/chips/chip/config.d.ts +57 -0
  48. package/dist/components/chips/chip/constants.d.ts +38 -0
  49. package/dist/components/chips/chip/index.d.ts +2 -0
  50. package/dist/components/chips/chip/types.d.ts +11 -0
  51. package/dist/components/chips/chips.d.ts +17 -0
  52. package/dist/components/chips/config.d.ts +67 -0
  53. package/dist/components/chips/constants.d.ts +46 -0
  54. package/dist/components/chips/features/chip-items.d.ts +8 -0
  55. package/dist/components/chips/features/container.d.ts +8 -0
  56. package/dist/components/chips/features/controller.d.ts +9 -0
  57. package/{src/components/chips/features/index.ts → dist/components/chips/features/index.d.ts} +1 -2
  58. package/dist/components/chips/features/label.d.ts +8 -0
  59. package/dist/components/chips/index.d.ts +4 -0
  60. package/dist/components/chips/schema.d.ts +34 -0
  61. package/dist/components/chips/types.d.ts +398 -0
  62. package/dist/components/datepicker/api.d.ts +9 -0
  63. package/dist/components/datepicker/config.d.ts +79 -0
  64. package/dist/components/datepicker/constants.d.ts +97 -0
  65. package/dist/components/datepicker/datepicker.d.ts +8 -0
  66. package/dist/components/datepicker/index.d.ts +3 -0
  67. package/dist/components/datepicker/render.d.ts +43 -0
  68. package/dist/components/datepicker/types.d.ts +321 -0
  69. package/dist/components/datepicker/utils.d.ts +84 -0
  70. package/dist/components/dialog/api.d.ts +59 -0
  71. package/dist/components/dialog/config.d.ts +91 -0
  72. package/dist/components/dialog/constants.d.ts +116 -0
  73. package/{src/components/dialog/dialog.ts → dist/components/dialog/dialog.d.ts} +16 -69
  74. package/dist/components/dialog/features.d.ts +37 -0
  75. package/dist/components/dialog/index.d.ts +14 -0
  76. package/dist/components/dialog/types.d.ts +483 -0
  77. package/dist/components/divider/config.d.ts +143 -0
  78. package/dist/components/divider/constants.d.ts +45 -0
  79. package/{src/components/divider/divider.ts → dist/components/divider/divider.d.ts} +9 -37
  80. package/dist/components/divider/features.d.ts +50 -0
  81. package/{src/components/divider/index.ts → dist/components/divider/index.d.ts} +6 -12
  82. package/dist/components/divider/types.d.ts +124 -0
  83. package/dist/components/extended-fab/api.d.ts +83 -0
  84. package/dist/components/extended-fab/config.d.ts +75 -0
  85. package/dist/components/extended-fab/constants.d.ts +82 -0
  86. package/dist/components/extended-fab/extended-fab.d.ts +61 -0
  87. package/{src/components/extended-fab/index.ts → dist/components/extended-fab/index.d.ts} +6 -15
  88. package/dist/components/extended-fab/types.d.ts +703 -0
  89. package/dist/components/fab/api.d.ts +64 -0
  90. package/dist/components/fab/config.d.ts +71 -0
  91. package/dist/components/fab/constants.d.ts +75 -0
  92. package/{src/components/fab/fab.ts → dist/components/fab/fab.d.ts} +10 -46
  93. package/{src/components/fab/index.ts → dist/components/fab/index.d.ts} +6 -15
  94. package/dist/components/fab/types.d.ts +577 -0
  95. package/dist/components/index.d.ts +76 -0
  96. package/dist/components/list/api.d.ts +106 -0
  97. package/dist/components/list/config.d.ts +61 -0
  98. package/dist/components/list/constants.d.ts +76 -0
  99. package/dist/components/list/features/index.d.ts +2 -0
  100. package/dist/components/list/features/listmanager.d.ts +9 -0
  101. package/dist/components/list/features/selection.d.ts +9 -0
  102. package/dist/components/list/index.d.ts +12 -0
  103. package/dist/components/list/list.d.ts +11 -0
  104. package/dist/components/list/types.d.ts +294 -0
  105. package/dist/components/menu/api.d.ts +58 -0
  106. package/dist/components/menu/config.d.ts +78 -0
  107. package/dist/components/menu/constants.d.ts +106 -0
  108. package/dist/components/menu/features/controller.d.ts +10 -0
  109. package/dist/components/menu/features/index.d.ts +6 -0
  110. package/dist/components/menu/features/keyboard.d.ts +26 -0
  111. package/dist/components/menu/features/opener.d.ts +10 -0
  112. package/dist/components/menu/features/position.d.ts +18 -0
  113. package/dist/components/menu/features/submenu.d.ts +10 -0
  114. package/{src/components/menu/index.ts → dist/components/menu/index.d.ts} +11 -27
  115. package/dist/components/menu/menu.d.ts +29 -0
  116. package/dist/components/menu/types.d.ts +338 -0
  117. package/dist/components/navigation/api.d.ts +8 -0
  118. package/dist/components/navigation/config.d.ts +34 -0
  119. package/dist/components/navigation/constants.d.ts +137 -0
  120. package/dist/components/navigation/features/controller.d.ts +30 -0
  121. package/dist/components/navigation/features/items.d.ts +32 -0
  122. package/dist/components/navigation/index.d.ts +3 -0
  123. package/dist/components/navigation/nav-item.d.ts +30 -0
  124. package/dist/components/navigation/navigation.d.ts +8 -0
  125. package/dist/components/navigation/system/core.d.ts +72 -0
  126. package/dist/components/navigation/system/events.d.ts +35 -0
  127. package/dist/components/navigation/system/index.d.ts +10 -0
  128. package/dist/components/navigation/system/mobile.d.ts +52 -0
  129. package/dist/components/navigation/system/state.d.ts +22 -0
  130. package/dist/components/navigation/system/types.d.ts +305 -0
  131. package/dist/components/navigation/types.d.ts +216 -0
  132. package/dist/components/progress/api.d.ts +46 -0
  133. package/dist/components/progress/config.d.ts +63 -0
  134. package/dist/components/progress/constants.d.ts +58 -0
  135. package/dist/components/progress/index.d.ts +3 -0
  136. package/dist/components/progress/progress.d.ts +24 -0
  137. package/dist/components/progress/types.d.ts +199 -0
  138. package/dist/components/radios/api.d.ts +37 -0
  139. package/dist/components/radios/config.d.ts +42 -0
  140. package/dist/components/radios/constants.d.ts +114 -0
  141. package/dist/components/radios/index.d.ts +3 -0
  142. package/dist/components/radios/radio.d.ts +8 -0
  143. package/dist/components/radios/radios.d.ts +8 -0
  144. package/dist/components/radios/types.d.ts +189 -0
  145. package/dist/components/search/api.d.ts +55 -0
  146. package/dist/components/search/config.d.ts +73 -0
  147. package/dist/components/search/constants.d.ts +128 -0
  148. package/{src/components/search/features/index.ts → dist/components/search/features/index.d.ts} +1 -2
  149. package/dist/components/search/features/search.d.ts +7 -0
  150. package/dist/components/search/features/states.d.ts +8 -0
  151. package/dist/components/search/features/structure.d.ts +7 -0
  152. package/dist/components/search/index.d.ts +3 -0
  153. package/dist/components/search/search.d.ts +8 -0
  154. package/dist/components/search/types.d.ts +132 -0
  155. package/dist/components/segmented-button/config.d.ts +65 -0
  156. package/dist/components/segmented-button/constants.d.ts +85 -0
  157. package/dist/components/segmented-button/index.d.ts +4 -0
  158. package/dist/components/segmented-button/segment.d.ts +15 -0
  159. package/dist/components/segmented-button/segmented-button.d.ts +49 -0
  160. package/dist/components/segmented-button/types.d.ts +257 -0
  161. package/dist/components/select/api.d.ts +8 -0
  162. package/dist/components/select/config.d.ts +18 -0
  163. package/dist/components/select/constants.d.ts +113 -0
  164. package/dist/components/select/features.d.ts +13 -0
  165. package/{src/components/select/index.ts → dist/components/select/index.d.ts} +7 -20
  166. package/dist/components/select/select.d.ts +36 -0
  167. package/dist/components/select/types.d.ts +302 -0
  168. package/dist/components/sheet/api.d.ts +37 -0
  169. package/dist/components/sheet/config.d.ts +42 -0
  170. package/dist/components/sheet/constants.d.ts +136 -0
  171. package/dist/components/sheet/features/content.d.ts +6 -0
  172. package/dist/components/sheet/features/gestures.d.ts +6 -0
  173. package/{src/components/sheet/features/index.ts → dist/components/sheet/features/index.d.ts} +1 -2
  174. package/dist/components/sheet/features/position.d.ts +7 -0
  175. package/dist/components/sheet/features/state.d.ts +6 -0
  176. package/dist/components/sheet/features/title.d.ts +6 -0
  177. package/dist/components/sheet/index.d.ts +3 -0
  178. package/dist/components/sheet/sheet.d.ts +8 -0
  179. package/dist/components/sheet/types.d.ts +250 -0
  180. package/dist/components/slider/api.d.ts +57 -0
  181. package/dist/components/slider/config.d.ts +75 -0
  182. package/dist/components/slider/constants.d.ts +138 -0
  183. package/dist/components/slider/features/controller.d.ts +9 -0
  184. package/dist/components/slider/features/handlers.d.ts +25 -0
  185. package/dist/components/slider/features/index.d.ts +3 -0
  186. package/dist/components/slider/features/range.d.ts +8 -0
  187. package/dist/components/slider/features/states.d.ts +9 -0
  188. package/dist/components/slider/index.d.ts +3 -0
  189. package/dist/components/slider/schema.d.ts +108 -0
  190. package/dist/components/slider/slider.d.ts +18 -0
  191. package/dist/components/slider/types.d.ts +170 -0
  192. package/dist/components/snackbar/api.d.ts +7 -0
  193. package/dist/components/snackbar/config.d.ts +55 -0
  194. package/dist/components/snackbar/constants.d.ts +88 -0
  195. package/dist/components/snackbar/features.d.ts +13 -0
  196. package/dist/components/snackbar/index.d.ts +3 -0
  197. package/dist/components/snackbar/position.d.ts +15 -0
  198. package/dist/components/snackbar/queue.d.ts +7 -0
  199. package/dist/components/snackbar/snackbar.d.ts +8 -0
  200. package/dist/components/snackbar/types.d.ts +172 -0
  201. package/dist/components/switch/api.d.ts +7 -0
  202. package/dist/components/switch/config.d.ts +34 -0
  203. package/dist/components/switch/constants.d.ts +88 -0
  204. package/dist/components/switch/features.d.ts +59 -0
  205. package/dist/components/switch/index.d.ts +4 -0
  206. package/dist/components/switch/switch.d.ts +8 -0
  207. package/dist/components/switch/types.d.ts +131 -0
  208. package/dist/components/tabs/api.d.ts +52 -0
  209. package/dist/components/tabs/config.d.ts +39 -0
  210. package/dist/components/tabs/constants.d.ts +142 -0
  211. package/dist/components/tabs/features.d.ts +133 -0
  212. package/dist/components/tabs/index.d.ts +11 -0
  213. package/dist/components/tabs/indicator.d.ts +49 -0
  214. package/dist/components/tabs/responsive.d.ts +38 -0
  215. package/dist/components/tabs/scroll-indicators.d.ts +18 -0
  216. package/dist/components/tabs/state.d.ts +53 -0
  217. package/dist/components/tabs/tab-api.d.ts +43 -0
  218. package/dist/components/tabs/tab.d.ts +7 -0
  219. package/dist/components/tabs/tabs.d.ts +27 -0
  220. package/dist/components/tabs/types.d.ts +390 -0
  221. package/dist/components/tabs/utils.d.ts +17 -0
  222. package/dist/components/textfield/api.d.ts +8 -0
  223. package/dist/components/textfield/config.d.ts +34 -0
  224. package/dist/components/textfield/constants.d.ts +148 -0
  225. package/{src/components/textfield/features/index.ts → dist/components/textfield/features/index.d.ts} +1 -6
  226. package/dist/components/textfield/features/leading-icon.d.ts +55 -0
  227. package/dist/components/textfield/features/placement.d.ts +28 -0
  228. package/dist/components/textfield/features/prefix-text.d.ts +54 -0
  229. package/dist/components/textfield/features/suffix-text.d.ts +54 -0
  230. package/dist/components/textfield/features/supporting-text.d.ts +59 -0
  231. package/dist/components/textfield/features/trailing-icon.d.ts +55 -0
  232. package/dist/components/textfield/index.d.ts +3 -0
  233. package/dist/components/textfield/textfield.d.ts +36 -0
  234. package/dist/components/textfield/types.d.ts +217 -0
  235. package/dist/components/timepicker/api.d.ts +24 -0
  236. package/dist/components/timepicker/clockdial.d.ts +34 -0
  237. package/dist/components/timepicker/config.d.ts +75 -0
  238. package/dist/components/timepicker/constants.d.ts +266 -0
  239. package/dist/components/timepicker/index.d.ts +4 -0
  240. package/dist/components/timepicker/render.d.ts +9 -0
  241. package/dist/components/timepicker/timepicker.d.ts +8 -0
  242. package/dist/components/timepicker/types.d.ts +284 -0
  243. package/dist/components/timepicker/utils.d.ts +74 -0
  244. package/dist/components/tooltip/api.d.ts +18 -0
  245. package/dist/components/tooltip/config.d.ts +38 -0
  246. package/dist/components/tooltip/constants.d.ts +108 -0
  247. package/dist/components/tooltip/index.d.ts +3 -0
  248. package/dist/components/tooltip/tooltip.d.ts +8 -0
  249. package/dist/components/tooltip/types.d.ts +188 -0
  250. package/dist/components/top-app-bar/config.d.ts +16 -0
  251. package/dist/components/top-app-bar/constants.d.ts +74 -0
  252. package/{src/components/top-app-bar/index.ts → dist/components/top-app-bar/index.d.ts} +2 -4
  253. package/dist/components/top-app-bar/top-app-bar.d.ts +68 -0
  254. package/dist/components/top-app-bar/types.d.ts +118 -0
  255. package/dist/constants.d.ts +30 -0
  256. package/dist/core/collection/adapters/base.d.ts +47 -0
  257. package/dist/core/collection/adapters/route.d.ts +149 -0
  258. package/dist/core/collection/collection.d.ts +131 -0
  259. package/dist/core/collection/index.d.ts +10 -0
  260. package/dist/core/collection/list-manager/config.d.ts +29 -0
  261. package/dist/core/collection/list-manager/dom-elements.d.ts +30 -0
  262. package/dist/core/collection/list-manager/index.d.ts +61 -0
  263. package/dist/core/collection/list-manager/item-measurement.d.ts +91 -0
  264. package/dist/core/collection/list-manager/renderer.d.ts +31 -0
  265. package/dist/core/collection/list-manager/scroll-tracker.d.ts +20 -0
  266. package/dist/core/collection/list-manager/state.d.ts +60 -0
  267. package/dist/core/collection/list-manager/types.d.ts +361 -0
  268. package/dist/core/collection/list-manager/utils/recycling.d.ts +34 -0
  269. package/dist/core/collection/list-manager/utils/visibility.d.ts +45 -0
  270. package/dist/core/compose/base.d.ts +31 -0
  271. package/dist/core/compose/component.d.ts +61 -0
  272. package/dist/core/compose/features/badge.d.ts +43 -0
  273. package/dist/core/compose/features/checkable.d.ts +59 -0
  274. package/dist/core/compose/features/constants.d.ts +45 -0
  275. package/dist/core/compose/features/debounce.d.ts +84 -0
  276. package/dist/core/compose/features/disabled.d.ts +47 -0
  277. package/dist/core/compose/features/events.d.ts +37 -0
  278. package/dist/core/compose/features/gestures/longpress.d.ts +85 -0
  279. package/dist/core/compose/features/gestures/pan.d.ts +108 -0
  280. package/dist/core/compose/features/gestures/pinch.d.ts +111 -0
  281. package/dist/core/compose/features/gestures/rotate.d.ts +111 -0
  282. package/dist/core/compose/features/gestures/swipe.d.ts +149 -0
  283. package/dist/core/compose/features/gestures/tap.d.ts +79 -0
  284. package/dist/core/compose/features/gestures.d.ts +86 -0
  285. package/dist/core/compose/features/icon.d.ts +71 -0
  286. package/{src/core/compose/features/index.ts → dist/core/compose/features/index.d.ts} +7 -8
  287. package/dist/core/compose/features/input.d.ts +71 -0
  288. package/dist/core/compose/features/lifecycle.d.ts +61 -0
  289. package/dist/core/compose/features/position.d.ts +51 -0
  290. package/dist/core/compose/features/ripple.d.ts +61 -0
  291. package/dist/core/compose/features/size.d.ts +17 -0
  292. package/dist/core/compose/features/style.d.ts +16 -0
  293. package/dist/core/compose/features/text.d.ts +63 -0
  294. package/dist/core/compose/features/textinput.d.ts +93 -0
  295. package/dist/core/compose/features/textlabel.d.ts +57 -0
  296. package/dist/core/compose/features/throttle.d.ts +75 -0
  297. package/dist/core/compose/features/track.d.ts +42 -0
  298. package/dist/core/compose/features/variant.d.ts +17 -0
  299. package/dist/core/compose/features/withEvents.d.ts +45 -0
  300. package/dist/core/compose/index.d.ts +17 -0
  301. package/{src/core/compose/pipe.ts → dist/core/compose/pipe.d.ts} +4 -20
  302. package/dist/core/compose/utils/type-guards.d.ts +27 -0
  303. package/dist/core/composition/features/dom.d.ts +19 -0
  304. package/dist/core/composition/features/icon.d.ts +45 -0
  305. package/{src/core/composition/features/index.ts → dist/core/composition/features/index.d.ts} +1 -6
  306. package/dist/core/composition/features/label.d.ts +49 -0
  307. package/{src/core/composition/features/layout.ts → dist/core/composition/features/layout.d.ts} +8 -24
  308. package/{src/core/composition/index.ts → dist/core/composition/index.d.ts} +4 -14
  309. package/{src/core/config/component-config.ts → dist/core/config/component.d.ts} +34 -82
  310. package/dist/core/config.d.ts +130 -0
  311. package/{src/core/dom/attributes.ts → dist/core/dom/attributes.d.ts} +2 -13
  312. package/dist/core/dom/classes.d.ts +42 -0
  313. package/dist/core/dom/create.d.ts +124 -0
  314. package/dist/core/dom/events.d.ts +69 -0
  315. package/{src/core/dom/index.ts → dist/core/dom/index.d.ts} +1 -5
  316. package/dist/core/dom/utils.d.ts +42 -0
  317. package/dist/core/gestures/index.d.ts +12 -0
  318. package/dist/core/gestures/longpress.d.ts +23 -0
  319. package/dist/core/gestures/manager.d.ts +14 -0
  320. package/dist/core/gestures/pan.d.ts +12 -0
  321. package/dist/core/gestures/pinch.d.ts +14 -0
  322. package/dist/core/gestures/rotate.d.ts +14 -0
  323. package/dist/core/gestures/swipe.d.ts +20 -0
  324. package/dist/core/gestures/tap.d.ts +12 -0
  325. package/dist/core/gestures/types.d.ts +320 -0
  326. package/dist/core/gestures/utils.d.ts +57 -0
  327. package/dist/core/index.d.ts +29 -0
  328. package/dist/core/layout/array.d.ts +15 -0
  329. package/dist/core/layout/config.d.ts +32 -0
  330. package/dist/core/layout/create.d.ts +14 -0
  331. package/dist/core/layout/index.d.ts +13 -0
  332. package/dist/core/layout/jsx.d.ts +13 -0
  333. package/dist/core/layout/object.d.ts +14 -0
  334. package/dist/core/layout/processor.d.ts +28 -0
  335. package/dist/core/layout/result.d.ts +12 -0
  336. package/dist/core/layout/template.d.ts +12 -0
  337. package/dist/core/layout/types.d.ts +137 -0
  338. package/dist/core/layout/utils.d.ts +38 -0
  339. package/dist/core/state/disabled.d.ts +32 -0
  340. package/dist/core/state/emitter.d.ts +40 -0
  341. package/dist/core/state/events.d.ts +36 -0
  342. package/{src/core/state/index.ts → dist/core/state/index.d.ts} +1 -7
  343. package/dist/core/state/lifecycle.d.ts +57 -0
  344. package/dist/core/state/store.d.ts +82 -0
  345. package/dist/core/utils/background.d.ts +40 -0
  346. package/dist/core/utils/index.d.ts +23 -0
  347. package/dist/core/utils/mobile.d.ts +54 -0
  348. package/dist/core/utils/object.d.ts +13 -0
  349. package/dist/core/utils/performance.d.ts +79 -0
  350. package/dist/core/utils/validate.d.ts +73 -0
  351. package/dist/index.cjs +64 -0
  352. package/dist/index.cjs.map +285 -0
  353. package/dist/index.d.ts +15 -0
  354. package/dist/index.js +53 -14854
  355. package/dist/index.js.map +285 -0
  356. package/dist/package.json +39 -0
  357. package/dist/styles.css +7 -0
  358. package/package.json +24 -5
  359. package/.env +0 -15
  360. package/.typedocignore +0 -11
  361. package/CONTRIBUTING.md +0 -218
  362. package/DOCS.md +0 -153
  363. package/TESTING.md +0 -214
  364. package/git-user-stats.js +0 -545
  365. package/index.ts +0 -10
  366. package/src/components/badge/api.ts +0 -313
  367. package/src/components/badge/config.ts +0 -153
  368. package/src/components/badge/features.ts +0 -194
  369. package/src/components/badge/index.ts +0 -90
  370. package/src/components/badge/types.ts +0 -279
  371. package/src/components/bottom-app-bar/bottom-app-bar.ts +0 -154
  372. package/src/components/bottom-app-bar/config.ts +0 -29
  373. package/src/components/bottom-app-bar/types.ts +0 -114
  374. package/src/components/button/api.ts +0 -172
  375. package/src/components/button/button.ts +0 -112
  376. package/src/components/button/config.ts +0 -96
  377. package/src/components/button/index.ts +0 -37
  378. package/src/components/button/types.ts +0 -290
  379. package/src/components/card/api.ts +0 -222
  380. package/src/components/card/config.ts +0 -304
  381. package/src/components/card/content.ts +0 -343
  382. package/src/components/card/features.ts +0 -407
  383. package/src/components/card/types.ts +0 -497
  384. package/src/components/carousel/api.ts +0 -147
  385. package/src/components/carousel/carousel.ts +0 -242
  386. package/src/components/carousel/config.ts +0 -91
  387. package/src/components/carousel/constants.ts +0 -181
  388. package/src/components/carousel/features/drag.ts +0 -388
  389. package/src/components/carousel/features/slides.ts +0 -682
  390. package/src/components/carousel/types.ts +0 -327
  391. package/src/components/checkbox/api.ts +0 -82
  392. package/src/components/checkbox/checkbox.ts +0 -142
  393. package/src/components/checkbox/config.ts +0 -89
  394. package/src/components/checkbox/types.ts +0 -342
  395. package/src/components/chips/api.ts +0 -194
  396. package/src/components/chips/chip/api.ts +0 -233
  397. package/src/components/chips/chip/chip.ts +0 -131
  398. package/src/components/chips/chip/config.ts +0 -91
  399. package/src/components/chips/chip/index.ts +0 -3
  400. package/src/components/chips/chips.md +0 -481
  401. package/src/components/chips/chips.ts +0 -75
  402. package/src/components/chips/config.ts +0 -109
  403. package/src/components/chips/constants.ts +0 -61
  404. package/src/components/chips/features/chip-items.ts +0 -33
  405. package/src/components/chips/features/container.ts +0 -77
  406. package/src/components/chips/features/controller.ts +0 -448
  407. package/src/components/chips/features/label.ts +0 -108
  408. package/src/components/chips/index.ts +0 -11
  409. package/src/components/chips/schema.ts +0 -61
  410. package/src/components/chips/types.ts +0 -469
  411. package/src/components/datepicker/api.ts +0 -265
  412. package/src/components/datepicker/config.ts +0 -141
  413. package/src/components/datepicker/datepicker.ts +0 -341
  414. package/src/components/datepicker/index.ts +0 -12
  415. package/src/components/datepicker/render.ts +0 -450
  416. package/src/components/datepicker/types.ts +0 -397
  417. package/src/components/datepicker/utils.ts +0 -289
  418. package/src/components/dialog/api.ts +0 -317
  419. package/src/components/dialog/config.ts +0 -116
  420. package/src/components/dialog/features.ts +0 -907
  421. package/src/components/dialog/index.ts +0 -141
  422. package/src/components/dialog/types.ts +0 -553
  423. package/src/components/divider/config.ts +0 -165
  424. package/src/components/divider/features.ts +0 -233
  425. package/src/components/divider/types.ts +0 -132
  426. package/src/components/extended-fab/api.ts +0 -193
  427. package/src/components/extended-fab/config.ts +0 -140
  428. package/src/components/extended-fab/extended-fab.ts +0 -153
  429. package/src/components/extended-fab/types.ts +0 -749
  430. package/src/components/fab/api.ts +0 -137
  431. package/src/components/fab/config.ts +0 -121
  432. package/src/components/fab/types.ts +0 -615
  433. package/src/components/list/api.ts +0 -82
  434. package/src/components/list/config.ts +0 -63
  435. package/src/components/list/features.ts +0 -229
  436. package/src/components/list/index.ts +0 -67
  437. package/src/components/list/list-item.ts +0 -163
  438. package/src/components/list/list.ts +0 -108
  439. package/src/components/list/types.ts +0 -396
  440. package/src/components/list/utils.ts +0 -98
  441. package/src/components/menu/api.ts +0 -193
  442. package/src/components/menu/config.ts +0 -125
  443. package/src/components/menu/features/anchor.ts +0 -243
  444. package/src/components/menu/features/controller.ts +0 -1167
  445. package/src/components/menu/features/index.ts +0 -5
  446. package/src/components/menu/features/position.ts +0 -353
  447. package/src/components/menu/menu.ts +0 -119
  448. package/src/components/menu/types.ts +0 -379
  449. package/src/components/navigation/api.ts +0 -142
  450. package/src/components/navigation/config.ts +0 -73
  451. package/src/components/navigation/features/controller.ts +0 -273
  452. package/src/components/navigation/features/items.ts +0 -353
  453. package/src/components/navigation/index.ts +0 -11
  454. package/src/components/navigation/nav-item.ts +0 -196
  455. package/src/components/navigation/navigation.ts +0 -115
  456. package/src/components/navigation/system/core.ts +0 -302
  457. package/src/components/navigation/system/events.ts +0 -240
  458. package/src/components/navigation/system/index.ts +0 -184
  459. package/src/components/navigation/system/mobile.ts +0 -278
  460. package/src/components/navigation/system/state.ts +0 -77
  461. package/src/components/navigation/system/types.ts +0 -364
  462. package/src/components/navigation/types.ts +0 -292
  463. package/src/components/progress/api.ts +0 -178
  464. package/src/components/progress/config.ts +0 -122
  465. package/src/components/progress/index.ts +0 -4
  466. package/src/components/progress/progress.ts +0 -159
  467. package/src/components/progress/types.ts +0 -255
  468. package/src/components/radios/api.ts +0 -125
  469. package/src/components/radios/config.ts +0 -59
  470. package/src/components/radios/constants.ts +0 -19
  471. package/src/components/radios/index.ts +0 -3
  472. package/src/components/radios/radio.ts +0 -292
  473. package/src/components/radios/radios.ts +0 -43
  474. package/src/components/radios/types.ts +0 -219
  475. package/src/components/search/api.ts +0 -203
  476. package/src/components/search/config.ts +0 -86
  477. package/src/components/search/features/search.ts +0 -717
  478. package/src/components/search/features/states.ts +0 -169
  479. package/src/components/search/features/structure.ts +0 -197
  480. package/src/components/search/index.ts +0 -7
  481. package/src/components/search/search.ts +0 -52
  482. package/src/components/search/types.ts +0 -175
  483. package/src/components/segmented-button/config.ts +0 -119
  484. package/src/components/segmented-button/index.ts +0 -4
  485. package/src/components/segmented-button/segment.ts +0 -108
  486. package/src/components/segmented-button/segmented-button.ts +0 -361
  487. package/src/components/segmented-button/types.ts +0 -306
  488. package/src/components/select/api.ts +0 -78
  489. package/src/components/select/config.ts +0 -76
  490. package/src/components/select/features.ts +0 -317
  491. package/src/components/select/select.ts +0 -73
  492. package/src/components/select/types.ts +0 -355
  493. package/src/components/sheet/api.ts +0 -96
  494. package/src/components/sheet/config.ts +0 -65
  495. package/src/components/sheet/features/content.ts +0 -51
  496. package/src/components/sheet/features/gestures.ts +0 -177
  497. package/src/components/sheet/features/position.ts +0 -41
  498. package/src/components/sheet/features/state.ts +0 -116
  499. package/src/components/sheet/features/title.ts +0 -86
  500. package/src/components/sheet/index.ts +0 -12
  501. package/src/components/sheet/sheet.ts +0 -56
  502. package/src/components/sheet/types.ts +0 -294
  503. package/src/components/slider/accessibility.md +0 -59
  504. package/src/components/slider/api.ts +0 -192
  505. package/src/components/slider/config.ts +0 -118
  506. package/src/components/slider/features/controller.ts +0 -737
  507. package/src/components/slider/features/handlers.ts +0 -497
  508. package/src/components/slider/features/index.ts +0 -5
  509. package/src/components/slider/features/range.ts +0 -104
  510. package/src/components/slider/features/states.ts +0 -195
  511. package/src/components/slider/index.ts +0 -17
  512. package/src/components/slider/schema.ts +0 -141
  513. package/src/components/slider/slider.ts +0 -76
  514. package/src/components/slider/types.ts +0 -223
  515. package/src/components/snackbar/api.ts +0 -162
  516. package/src/components/snackbar/config.ts +0 -61
  517. package/src/components/snackbar/features.ts +0 -76
  518. package/src/components/snackbar/index.ts +0 -9
  519. package/src/components/snackbar/position.ts +0 -79
  520. package/src/components/snackbar/queue.ts +0 -76
  521. package/src/components/snackbar/snackbar.ts +0 -60
  522. package/src/components/snackbar/types.ts +0 -159
  523. package/src/components/switch/api.ts +0 -93
  524. package/src/components/switch/config.ts +0 -56
  525. package/src/components/switch/features.ts +0 -198
  526. package/src/components/switch/index.ts +0 -8
  527. package/src/components/switch/switch.ts +0 -52
  528. package/src/components/switch/types.ts +0 -168
  529. package/src/components/tabs/api.ts +0 -221
  530. package/src/components/tabs/config.ts +0 -73
  531. package/src/components/tabs/features.ts +0 -403
  532. package/src/components/tabs/index.ts +0 -46
  533. package/src/components/tabs/indicator.ts +0 -285
  534. package/src/components/tabs/responsive.ts +0 -144
  535. package/src/components/tabs/scroll-indicators.ts +0 -149
  536. package/src/components/tabs/state.ts +0 -186
  537. package/src/components/tabs/tab-api.ts +0 -266
  538. package/src/components/tabs/tab.ts +0 -267
  539. package/src/components/tabs/tabs.ts +0 -71
  540. package/src/components/tabs/types.ts +0 -461
  541. package/src/components/tabs/utils.ts +0 -107
  542. package/src/components/textfield/api.ts +0 -197
  543. package/src/components/textfield/config.ts +0 -52
  544. package/src/components/textfield/features/leading-icon.ts +0 -127
  545. package/src/components/textfield/features/placement.ts +0 -149
  546. package/src/components/textfield/features/prefix-text.ts +0 -107
  547. package/src/components/textfield/features/suffix-text.ts +0 -100
  548. package/src/components/textfield/features/supporting-text.ts +0 -113
  549. package/src/components/textfield/features/trailing-icon.ts +0 -108
  550. package/src/components/textfield/index.ts +0 -9
  551. package/src/components/textfield/textfield.ts +0 -92
  552. package/src/components/textfield/types.ts +0 -265
  553. package/src/components/timepicker/README.md +0 -277
  554. package/src/components/timepicker/api.ts +0 -632
  555. package/src/components/timepicker/clockdial.ts +0 -479
  556. package/src/components/timepicker/config.ts +0 -228
  557. package/src/components/timepicker/index.ts +0 -3
  558. package/src/components/timepicker/render.ts +0 -613
  559. package/src/components/timepicker/timepicker.ts +0 -117
  560. package/src/components/timepicker/types.ts +0 -336
  561. package/src/components/timepicker/utils.ts +0 -241
  562. package/src/components/tooltip/api.ts +0 -415
  563. package/src/components/tooltip/config.ts +0 -80
  564. package/src/components/tooltip/index.ts +0 -12
  565. package/src/components/tooltip/tooltip.ts +0 -60
  566. package/src/components/tooltip/types.ts +0 -223
  567. package/src/components/top-app-bar/config.ts +0 -83
  568. package/src/components/top-app-bar/top-app-bar.ts +0 -316
  569. package/src/components/top-app-bar/types.ts +0 -140
  570. package/src/core/build/constants.ts +0 -48
  571. package/src/core/build/icon.ts +0 -137
  572. package/src/core/build/ripple.ts +0 -193
  573. package/src/core/build/text.ts +0 -91
  574. package/src/core/collection/adapters/base.ts +0 -62
  575. package/src/core/collection/adapters/route.ts +0 -201
  576. package/src/core/collection/collection.ts +0 -300
  577. package/src/core/collection/index.ts +0 -57
  578. package/src/core/collection/list-manager.ts +0 -333
  579. package/src/core/compose/base.ts +0 -43
  580. package/src/core/compose/component.ts +0 -255
  581. package/src/core/compose/features/badge.ts +0 -79
  582. package/src/core/compose/features/checkable.ts +0 -155
  583. package/src/core/compose/features/disabled.ts +0 -116
  584. package/src/core/compose/features/events.ts +0 -65
  585. package/src/core/compose/features/icon.ts +0 -71
  586. package/src/core/compose/features/input.ts +0 -174
  587. package/src/core/compose/features/lifecycle.ts +0 -139
  588. package/src/core/compose/features/position.ts +0 -94
  589. package/src/core/compose/features/ripple.ts +0 -58
  590. package/src/core/compose/features/size.ts +0 -29
  591. package/src/core/compose/features/style.ts +0 -31
  592. package/src/core/compose/features/text.ts +0 -44
  593. package/src/core/compose/features/textinput.ts +0 -238
  594. package/src/core/compose/features/textlabel.ts +0 -113
  595. package/src/core/compose/features/track.ts +0 -84
  596. package/src/core/compose/features/variant.ts +0 -29
  597. package/src/core/compose/features/withEvents.ts +0 -137
  598. package/src/core/compose/index.ts +0 -54
  599. package/src/core/composition/features/dom.ts +0 -45
  600. package/src/core/composition/features/icon.ts +0 -131
  601. package/src/core/composition/features/label.ts +0 -155
  602. package/src/core/config.ts +0 -211
  603. package/src/core/dom/classes.ts +0 -132
  604. package/src/core/dom/create.ts +0 -273
  605. package/src/core/dom/events.ts +0 -209
  606. package/src/core/dom/utils.ts +0 -97
  607. package/src/core/index.ts +0 -111
  608. package/src/core/layout/README.md +0 -715
  609. package/src/core/layout/array.ts +0 -180
  610. package/src/core/layout/config.ts +0 -193
  611. package/src/core/layout/create.ts +0 -54
  612. package/src/core/layout/index.ts +0 -36
  613. package/src/core/layout/object.ts +0 -123
  614. package/src/core/layout/processor.ts +0 -106
  615. package/src/core/layout/result.ts +0 -84
  616. package/src/core/layout/types.ts +0 -180
  617. package/src/core/layout/utils.ts +0 -144
  618. package/src/core/state/disabled.ts +0 -81
  619. package/src/core/state/emitter.ts +0 -94
  620. package/src/core/state/events.ts +0 -88
  621. package/src/core/state/lifecycle.ts +0 -131
  622. package/src/core/state/store.ts +0 -197
  623. package/src/core/utils/index.ts +0 -45
  624. package/src/core/utils/mobile.ts +0 -98
  625. package/src/core/utils/object.ts +0 -41
  626. package/src/core/utils/validate.ts +0 -234
  627. package/src/index.ts +0 -134
  628. package/src/styles/abstract/_base.scss +0 -2
  629. package/src/styles/abstract/_config.scss +0 -28
  630. package/src/styles/abstract/_functions.scss +0 -124
  631. package/src/styles/abstract/_mixins.scss +0 -352
  632. package/src/styles/abstract/_theme.scss +0 -269
  633. package/src/styles/abstract/_variables.scss +0 -305
  634. package/src/styles/base/_reset.scss +0 -86
  635. package/src/styles/base/_typography.scss +0 -155
  636. package/src/styles/components/_badge.scss +0 -182
  637. package/src/styles/components/_bottom-app-bar.scss +0 -103
  638. package/src/styles/components/_button.scss +0 -224
  639. package/src/styles/components/_card.scss +0 -401
  640. package/src/styles/components/_carousel.scss +0 -645
  641. package/src/styles/components/_checkbox.scss +0 -231
  642. package/src/styles/components/_chips.scss +0 -638
  643. package/src/styles/components/_datepicker.scss +0 -358
  644. package/src/styles/components/_dialog.scss +0 -259
  645. package/src/styles/components/_divider.scss +0 -57
  646. package/src/styles/components/_extended-fab.scss +0 -267
  647. package/src/styles/components/_fab.scss +0 -225
  648. package/src/styles/components/_list.scss +0 -248
  649. package/src/styles/components/_menu.scss +0 -248
  650. package/src/styles/components/_navigation-mobile.scss +0 -244
  651. package/src/styles/components/_navigation-system.scss +0 -151
  652. package/src/styles/components/_navigation.scss +0 -407
  653. package/src/styles/components/_progress.scss +0 -151
  654. package/src/styles/components/_radios.scss +0 -187
  655. package/src/styles/components/_search.scss +0 -306
  656. package/src/styles/components/_segmented-button.scss +0 -227
  657. package/src/styles/components/_select.scss +0 -265
  658. package/src/styles/components/_sheet.scss +0 -236
  659. package/src/styles/components/_slider.scss +0 -489
  660. package/src/styles/components/_snackbar.scss +0 -211
  661. package/src/styles/components/_switch.scss +0 -298
  662. package/src/styles/components/_tabs.scss +0 -416
  663. package/src/styles/components/_textfield.scss +0 -773
  664. package/src/styles/components/_timepicker.scss +0 -451
  665. package/src/styles/components/_tooltip.scss +0 -241
  666. package/src/styles/components/_top-app-bar.scss +0 -225
  667. package/src/styles/main.scss +0 -175
  668. package/src/styles/themes/_autumn.scss +0 -105
  669. package/src/styles/themes/_base-theme.scss +0 -85
  670. package/src/styles/themes/_baseline.scss +0 -173
  671. package/src/styles/themes/_bluekhaki.scss +0 -125
  672. package/src/styles/themes/_brownbeige.scss +0 -125
  673. package/src/styles/themes/_browngreen.scss +0 -125
  674. package/src/styles/themes/_forest.scss +0 -77
  675. package/src/styles/themes/_greenbeige.scss +0 -125
  676. package/src/styles/themes/_index.scss +0 -6
  677. package/src/styles/themes/_material.scss +0 -125
  678. package/src/styles/themes/_ocean.scss +0 -77
  679. package/src/styles/themes/_sageivory.scss +0 -125
  680. package/src/styles/themes/_spring.scss +0 -77
  681. package/src/styles/themes/_summer.scss +0 -87
  682. package/src/styles/themes/_sunset.scss +0 -60
  683. package/src/styles/themes/_tealcaramel.scss +0 -125
  684. package/src/styles/themes/_winter.scss +0 -77
  685. package/src/styles/utilities/_colors.scss +0 -154
  686. package/src/styles/utilities/_flexbox.scss +0 -194
  687. package/src/styles/utilities/_layout.scss +0 -665
  688. package/src/styles/utilities/_ripple.scss +0 -79
  689. package/src/styles/utilities/_spacing.scss +0 -139
  690. package/src/styles/utilities/_typography.scss +0 -178
  691. package/src/styles/utilities/_visibility.scss +0 -142
@@ -1,1167 +0,0 @@
1
- // src/components/menu/features/controller.ts
2
-
3
- import { MenuConfig, MenuContent, MenuItem, MenuDivider, MenuEvent, MenuSelectEvent } from '../types';
4
- import { createPositioner } from './position';
5
-
6
- /**
7
- * Adds controller functionality to the menu component
8
- * Manages state, rendering, positioning, and event handling
9
- *
10
- * @param config - Menu configuration
11
- * @returns Component enhancer with menu controller functionality
12
- */
13
- export const withController = (config: MenuConfig) => component => {
14
- if (!component.element) {
15
- console.warn('Cannot initialize menu controller: missing element');
16
- return component;
17
- }
18
-
19
- // Initialize state
20
- const state = {
21
- visible: config.visible || false,
22
- items: config.items || [],
23
- position: config.position,
24
- activeSubmenu: null as HTMLElement,
25
- activeSubmenuItem: null as HTMLElement,
26
- activeItemIndex: -1,
27
- submenuLevel: 0, // Track nesting level of submenus
28
- activeSubmenus: [] as Array<{
29
- element: HTMLElement,
30
- menuItem: HTMLElement,
31
- level: number,
32
- isOpening: boolean // Track if submenu is in opening transition
33
- }>,
34
- submenuTimer: null,
35
- hoverIntent: {
36
- timer: null,
37
- activeItem: null
38
- },
39
- component
40
- };
41
-
42
- // Create positioner
43
- const positioner = createPositioner(component, config);
44
-
45
- // Create event helpers
46
- const eventHelpers = {
47
- triggerEvent(eventName: string, data: any = {}, originalEvent?: Event) {
48
- const eventData = {
49
- menu: state.component,
50
- ...data,
51
- originalEvent,
52
- preventDefault: () => { eventData.defaultPrevented = true; },
53
- defaultPrevented: false
54
- };
55
-
56
- component.emit(eventName, eventData);
57
- return eventData;
58
- }
59
- };
60
-
61
- /**
62
- * Gets the anchor element from config
63
- */
64
- const getAnchorElement = (): HTMLElement => {
65
- // First try to get the resolved anchor from the anchor feature
66
- if (component.anchor && typeof component.anchor.getAnchor === 'function') {
67
- return component.anchor.getAnchor();
68
- }
69
-
70
- // Fall back to config anchor for initial positioning
71
- const { anchor } = config;
72
-
73
- if (typeof anchor === 'string') {
74
- const element = document.querySelector(anchor);
75
- if (!element) {
76
- console.warn(`Menu anchor not found: ${anchor}`);
77
- return null;
78
- }
79
- return element as HTMLElement;
80
- }
81
-
82
- // Handle component with element property
83
- if (typeof anchor === 'object' && anchor !== null && 'element' in anchor) {
84
- return anchor.element;
85
- }
86
-
87
- // Handle direct HTML element
88
- return anchor as HTMLElement;
89
- };
90
-
91
- /**
92
- * Creates a DOM element for a menu item
93
- */
94
- const createMenuItem = (item: MenuItem, index: number): HTMLElement => {
95
- const itemElement = document.createElement('li');
96
- const itemClass = `${component.getClass('menu-item')}`;
97
-
98
- itemElement.className = itemClass;
99
- itemElement.setAttribute('role', 'menuitem');
100
- itemElement.setAttribute('tabindex', '-1'); // Set to -1 by default, will update when needed
101
- itemElement.setAttribute('data-id', item.id);
102
- itemElement.setAttribute('data-index', index.toString());
103
-
104
- if (item.disabled) {
105
- itemElement.classList.add(`${itemClass}--disabled`);
106
- itemElement.setAttribute('aria-disabled', 'true');
107
- } else {
108
- itemElement.setAttribute('aria-disabled', 'false');
109
- }
110
-
111
- if (item.hasSubmenu) {
112
- itemElement.classList.add(`${itemClass}--submenu`);
113
- itemElement.setAttribute('aria-haspopup', 'true');
114
- itemElement.setAttribute('aria-expanded', 'false');
115
- }
116
-
117
- // Create content container for flexible layout
118
- const contentContainer = document.createElement('span');
119
- contentContainer.className = `${component.getClass('menu-item-content')}`;
120
-
121
- // Add icon if provided
122
- if (item.icon) {
123
- const iconElement = document.createElement('span');
124
- iconElement.className = `${component.getClass('menu-item-icon')}`;
125
- iconElement.innerHTML = item.icon;
126
- contentContainer.appendChild(iconElement);
127
- }
128
-
129
- // Add text
130
- const textElement = document.createElement('span');
131
- textElement.className = `${component.getClass('menu-item-text')}`;
132
- textElement.textContent = item.text;
133
- contentContainer.appendChild(textElement);
134
-
135
- // Add shortcut if provided
136
- if (item.shortcut) {
137
- const shortcutElement = document.createElement('span');
138
- shortcutElement.className = `${component.getClass('menu-item-shortcut')}`;
139
- shortcutElement.textContent = item.shortcut;
140
- contentContainer.appendChild(shortcutElement);
141
- }
142
-
143
- itemElement.appendChild(contentContainer);
144
-
145
- // Add event listeners
146
- if (!item.disabled) {
147
- // Mouse events
148
- itemElement.addEventListener('click', (e) => handleItemClick(e, item, index));
149
-
150
- // Focus and blur events for proper focus styling
151
- itemElement.addEventListener('focus', () => {
152
- state.activeItemIndex = index;
153
- });
154
-
155
- // Additional keyboard event handler for accessibility
156
- itemElement.addEventListener('keydown', (e) => {
157
- if (e.key === 'Enter' || e.key === ' ') {
158
- e.preventDefault();
159
- handleItemClick(e, item, index);
160
- }
161
- });
162
-
163
- if (item.hasSubmenu && config.openSubmenuOnHover) {
164
- itemElement.addEventListener('mouseenter', () => handleSubmenuHover(item, index, itemElement));
165
- itemElement.addEventListener('mouseleave', handleSubmenuLeave);
166
- }
167
- }
168
-
169
- return itemElement;
170
- };
171
-
172
- /**
173
- * Creates a DOM element for a menu divider
174
- */
175
- const createDivider = (divider: MenuDivider, index: number): HTMLElement => {
176
- const dividerElement = document.createElement('li');
177
- dividerElement.className = `${component.getClass('menu-divider')}`;
178
- dividerElement.setAttribute('role', 'separator');
179
- dividerElement.setAttribute('data-index', index.toString());
180
-
181
- if (divider.id) {
182
- dividerElement.setAttribute('id', divider.id);
183
- }
184
-
185
- return dividerElement;
186
- };
187
-
188
- /**
189
- * Renders the menu items
190
- */
191
- const renderMenuItems = (): void => {
192
- const menuList = document.createElement('ul');
193
- menuList.className = `${component.getClass('menu-list')}`;
194
- menuList.setAttribute('role', 'menu');
195
-
196
- // Create items
197
- state.items.forEach((item, index) => {
198
- if ('type' in item && item.type === 'divider') {
199
- menuList.appendChild(createDivider(item, index));
200
- } else {
201
- menuList.appendChild(createMenuItem(item as MenuItem, index));
202
- }
203
- });
204
-
205
- // Clear and append
206
- component.element.innerHTML = '';
207
- component.element.appendChild(menuList);
208
- };
209
-
210
- /**
211
- * Clean up hover intent timer
212
- */
213
- const clearHoverIntent = () => {
214
- if (state.hoverIntent.timer) {
215
- clearTimeout(state.hoverIntent.timer);
216
- state.hoverIntent.timer = null;
217
- state.hoverIntent.activeItem = null;
218
- }
219
- };
220
-
221
- /**
222
- * Sets focus appropriately based on interaction type
223
- * For keyboard interactions, focuses the first item
224
- * For mouse interactions, makes the menu container focusable but doesn't auto-focus
225
- *
226
- * @param {'keyboard'|'mouse'} interactionType - Type of interaction that opened the menu
227
- */
228
- const handleFocus = (interactionType: 'keyboard' | 'mouse'): void => {
229
- // Reset active item index
230
- state.activeItemIndex = -1;
231
-
232
- if (interactionType === 'keyboard') {
233
- // Find all focusable items
234
- const items = Array.from(
235
- component.element.querySelectorAll(`.${component.getClass('menu-item')}:not(.${component.getClass('menu-item--disabled')})`)
236
- ) as HTMLElement[];
237
-
238
- if (items.length > 0) {
239
- // Focus the first item for keyboard navigation
240
- items[0].setAttribute('tabindex', '0');
241
- items[0].focus();
242
- state.activeItemIndex = 0;
243
- } else {
244
- // If no items, focus the menu itself
245
- component.element.setAttribute('tabindex', '0');
246
- component.element.focus();
247
- }
248
- } else {
249
- // For mouse interaction, make the menu focusable but don't auto-focus
250
- component.element.setAttribute('tabindex', '-1');
251
- }
252
- };
253
-
254
- /**
255
- * Handles click on a menu item
256
- */
257
- const handleItemClick = (e: MouseEvent, item: MenuItem, index: number): void => {
258
- e.preventDefault();
259
- e.stopPropagation();
260
-
261
- // Don't process if disabled
262
- if (item.disabled) return;
263
-
264
- if (item.hasSubmenu) {
265
- handleSubmenuClick(item, index, e.currentTarget as HTMLElement);
266
- return;
267
- }
268
-
269
- // Trigger select event
270
- const selectEvent = eventHelpers.triggerEvent('select', {
271
- item,
272
- itemId: item.id,
273
- itemData: item.data
274
- }, e) as MenuSelectEvent;
275
-
276
- // Close menu if needed
277
- if (config.closeOnSelect && !selectEvent.defaultPrevented) {
278
- closeMenu(e);
279
- }
280
- };
281
-
282
- /**
283
- * Handles click on a submenu item
284
- */
285
- const handleSubmenuClick = (item: MenuItem, index: number, itemElement: HTMLElement, viaKeyboard = false): void => {
286
- if (!item.submenu || !item.hasSubmenu) return;
287
-
288
- // Check if the submenu is already open
289
- const isOpen = itemElement.getAttribute('aria-expanded') === 'true';
290
-
291
- // Find if any submenu is currently in opening transition
292
- const anySubmenuTransitioning = state.activeSubmenus.some(s => s.isOpening);
293
-
294
- // Completely ignore clicks during any submenu transition
295
- if (anySubmenuTransitioning) {
296
- return;
297
- }
298
-
299
- if (isOpen) {
300
- // Close submenu - only if fully open
301
- // Find the closest submenu level
302
- const currentLevel = parseInt(
303
- itemElement.closest(`.${component.getClass('menu--submenu')}`)?.getAttribute('data-level') || '0',
304
- 10
305
- );
306
-
307
- // Close this level + 1 and deeper
308
- closeSubmenuAtLevel(currentLevel + 1);
309
-
310
- // Reset expanded state
311
- itemElement.setAttribute('aria-expanded', 'false');
312
- } else {
313
- // Open new submenu
314
- openSubmenu(item, index, itemElement, viaKeyboard);
315
- }
316
- };
317
-
318
- /**
319
- * Handles hover on a submenu item
320
- */
321
- const handleSubmenuHover = (item: MenuItem, index: number, itemElement: HTMLElement): void => {
322
- if (!config.openSubmenuOnHover || !item.hasSubmenu) return;
323
-
324
- // Clear any existing timers
325
- clearHoverIntent();
326
- clearSubmenuTimer();
327
-
328
- // Set hover intent
329
- state.hoverIntent.activeItem = itemElement;
330
- state.hoverIntent.timer = setTimeout(() => {
331
- const isCurrentlyHovered = itemElement.matches(':hover');
332
- if (isCurrentlyHovered) {
333
- // Only close and reopen if this is a different submenu item
334
- if (state.activeSubmenuItem !== itemElement) {
335
- openSubmenu(item, index, itemElement);
336
- }
337
- }
338
- state.hoverIntent.timer = null;
339
- }, 100);
340
- };
341
-
342
- /**
343
- * Handles mouse leave from submenu
344
- */
345
- const handleSubmenuLeave = (e: MouseEvent): void => {
346
- // Clear hover intent
347
- clearHoverIntent();
348
-
349
- // Don't close immediately to allow moving to submenu
350
- clearSubmenuTimer();
351
-
352
- // Set a timer to close the submenu if not re-entered
353
- state.submenuTimer = setTimeout(() => {
354
- // Check if mouse is over the submenu or the parent menu item
355
- const submenuElement = state.activeSubmenu;
356
- const menuItemElement = state.activeSubmenuItem;
357
-
358
- if (submenuElement && menuItemElement) {
359
- const overSubmenu = submenuElement.matches(':hover');
360
- const overMenuItem = menuItemElement.matches(':hover');
361
-
362
- if (!overSubmenu && !overMenuItem) {
363
- closeSubmenuAtLevel(state.submenuLevel);
364
- }
365
- }
366
-
367
- state.submenuTimer = null;
368
- }, 300);
369
- };
370
-
371
- /**
372
- * Opens a submenu with proper animation and positioning
373
- */
374
- const openSubmenu = (item: MenuItem, index: number, itemElement: HTMLElement, viaKeyboard = false): void => {
375
- if (!item.submenu || !item.hasSubmenu) return;
376
-
377
- // Get current level of the submenu we're opening
378
- const currentLevel = itemElement.closest(`.${component.getClass('menu--submenu')}`)
379
- ? parseInt(itemElement.closest(`.${component.getClass('menu--submenu')}`).getAttribute('data-level') || '0', 10) + 1
380
- : 1;
381
-
382
- // Close any deeper level submenus first, preserving the current level
383
- closeSubmenuAtLevel(currentLevel);
384
-
385
- // Check if this submenu is already in opening state - if so, do nothing
386
- const existingSubmenuIndex = state.activeSubmenus.findIndex(
387
- s => s.menuItem === itemElement && s.isOpening
388
- );
389
- if (existingSubmenuIndex >= 0) {
390
- return; // Already opening this submenu, don't restart the process
391
- }
392
-
393
- // Set expanded state
394
- itemElement.setAttribute('aria-expanded', 'true');
395
-
396
- // Create submenu element with proper classes and attributes
397
- const submenuElement = document.createElement('div');
398
- submenuElement.className = `${component.getClass('menu')} ${component.getClass('menu--submenu')}`;
399
- submenuElement.setAttribute('role', 'menu');
400
- submenuElement.setAttribute('tabindex', '-1');
401
- submenuElement.setAttribute('data-level', currentLevel.toString());
402
- submenuElement.setAttribute('data-parent-item', item.id);
403
-
404
- // Increase z-index for each level of submenu
405
- submenuElement.style.zIndex = `${1000 + (currentLevel * 10)}`;
406
-
407
- // Create submenu list
408
- const submenuList = document.createElement('ul');
409
- submenuList.className = `${component.getClass('menu-list')}`;
410
-
411
- // Create submenu items
412
- const submenuItems = [];
413
- item.submenu.forEach((subitem, subindex) => {
414
- if ('type' in subitem && subitem.type === 'divider') {
415
- submenuList.appendChild(createDivider(subitem, subindex));
416
- } else {
417
- const subitemElement = createMenuItem(subitem as MenuItem, subindex);
418
- submenuList.appendChild(subitemElement);
419
- if (!(subitem as MenuItem).disabled) {
420
- submenuItems.push(subitemElement);
421
- }
422
- }
423
- });
424
-
425
- submenuElement.appendChild(submenuList);
426
-
427
- // Add to DOM to enable measurement and transitions
428
- document.body.appendChild(submenuElement);
429
-
430
- // Position the submenu using our positioner with the current nesting level
431
- positioner.positionSubmenu(submenuElement, itemElement, currentLevel);
432
-
433
- // Setup keyboard navigation for submenu
434
- submenuElement.addEventListener('keydown', handleMenuKeydown);
435
-
436
- // Add mouseenter event to prevent closing
437
- submenuElement.addEventListener('mouseenter', () => {
438
- clearSubmenuTimer();
439
- });
440
-
441
- // Add mouseleave event to handle closing
442
- submenuElement.addEventListener('mouseleave', (e) => {
443
- handleSubmenuLeave(e);
444
- });
445
-
446
- // Setup submenu event handlers for nested submenus
447
- const setupNestedSubmenuHandlers = (parent: HTMLElement) => {
448
- const submenuItems = parent.querySelectorAll(`.${component.getClass('menu-item--submenu')}`) as NodeListOf<HTMLElement>;
449
-
450
- submenuItems.forEach((menuItem) => {
451
- const itemIndex = parseInt(menuItem.getAttribute('data-index'), 10);
452
- const menuItemData = item.submenu[itemIndex] as MenuItem;
453
-
454
- if (menuItemData && menuItemData.hasSubmenu) {
455
- // Add hover handler for nested submenus
456
- if (config.openSubmenuOnHover) {
457
- menuItem.addEventListener('mouseenter', () => {
458
- handleNestedSubmenuHover(menuItemData, itemIndex, menuItem);
459
- });
460
- menuItem.addEventListener('mouseleave', handleSubmenuLeave);
461
- }
462
-
463
- // Add click handler for nested submenus
464
- menuItem.addEventListener('click', (e) => {
465
- e.preventDefault();
466
- e.stopPropagation();
467
- handleNestedSubmenuClick(menuItemData, itemIndex, menuItem, false);
468
- });
469
- }
470
- });
471
- };
472
-
473
- // Setup handlers for any nested submenus
474
- setupNestedSubmenuHandlers(submenuElement);
475
-
476
- // Update state with active submenu
477
- state.activeSubmenu = submenuElement;
478
- state.activeSubmenuItem = itemElement;
479
-
480
- // Add to active submenus array to maintain hierarchy
481
- state.activeSubmenus.push({
482
- element: submenuElement,
483
- menuItem: itemElement,
484
- level: currentLevel,
485
- isOpening: true // Mark as in opening transition
486
- });
487
-
488
- // Update submenu level
489
- state.submenuLevel = currentLevel;
490
-
491
- // Add document events for this submenu
492
- document.addEventListener('click', handleDocumentClickForSubmenu);
493
- window.addEventListener('resize', handleWindowResizeForSubmenu, { passive: true });
494
- window.addEventListener('scroll', handleWindowScrollForSubmenu, { passive: true });
495
-
496
- // Make visible with animation
497
- requestAnimationFrame(() => {
498
- submenuElement.classList.add(`${component.getClass('menu--visible')}`);
499
-
500
- // Wait for transition to complete before marking as fully opened
501
- // This should match your CSS transition duration
502
- setTimeout(() => {
503
- // Find this submenu in the active submenus array and update its state
504
- const index = state.activeSubmenus.findIndex(s => s.element === submenuElement);
505
- if (index !== -1) {
506
- state.activeSubmenus[index].isOpening = false;
507
- }
508
- }, 300); // Adjust to match your transition duration
509
-
510
- // If opened via keyboard, focus the first item in the submenu
511
- if (viaKeyboard && submenuItems.length > 0) {
512
- setTimeout(() => {
513
- submenuItems[0].focus();
514
- }, 50);
515
- }
516
- });
517
- };
518
-
519
- /**
520
- * Handles hover on a nested submenu item
521
- */
522
- const handleNestedSubmenuHover = (item: MenuItem, index: number, itemElement: HTMLElement): void => {
523
- if (!config.openSubmenuOnHover || !item.hasSubmenu) return;
524
-
525
- // Clear any existing timers
526
- clearHoverIntent();
527
- clearSubmenuTimer();
528
-
529
- // Set hover intent with a slightly longer delay for nested menus
530
- state.hoverIntent.activeItem = itemElement;
531
- state.hoverIntent.timer = setTimeout(() => {
532
- const isCurrentlyHovered = itemElement.matches(':hover');
533
- if (isCurrentlyHovered) {
534
- // Find the closest submenu level of this item
535
- const currentLevel = parseInt(
536
- itemElement.closest(`.${component.getClass('menu--submenu')}`)?.getAttribute('data-level') || '1',
537
- 10
538
- );
539
-
540
- // Open the nested submenu (will handle closing deeper levels properly)
541
- handleNestedSubmenuClick(item, index, itemElement, false);
542
- }
543
- state.hoverIntent.timer = null;
544
- }, 120); // Slightly longer delay for nested submenus
545
- };
546
-
547
- /**
548
- * Handles click on a nested submenu item
549
- */
550
- const handleNestedSubmenuClick = (item: MenuItem, index: number, itemElement: HTMLElement, viaKeyboard = false): void => {
551
- if (!item.submenu || !item.hasSubmenu) return;
552
-
553
- // Check if the submenu is already open
554
- const isOpen = itemElement.getAttribute('aria-expanded') === 'true';
555
-
556
- // Find if any submenu is currently in opening transition
557
- const anySubmenuTransitioning = state.activeSubmenus.some(s => s.isOpening);
558
-
559
- // Completely ignore clicks during any submenu transition
560
- if (anySubmenuTransitioning) {
561
- return;
562
- }
563
-
564
- if (isOpen) {
565
- // Find the closest submenu level
566
- const currentLevel = parseInt(
567
- itemElement.closest(`.${component.getClass('menu--submenu')}`)?.getAttribute('data-level') || '1',
568
- 10
569
- );
570
-
571
- // Close submenus at and deeper than the next level
572
- closeSubmenuAtLevel(currentLevel + 1);
573
- } else {
574
- // Open the nested submenu
575
- openSubmenu(item, index, itemElement, viaKeyboard);
576
- }
577
- };
578
-
579
- /**
580
- * Clear submenu close timer
581
- */
582
- const clearSubmenuTimer = () => {
583
- if (state.submenuTimer) {
584
- clearTimeout(state.submenuTimer);
585
- state.submenuTimer = null;
586
- }
587
- };
588
-
589
- /**
590
- * Closes submenus at or deeper than the specified level
591
- * @param level - The level to start closing from
592
- */
593
- const closeSubmenuAtLevel = (level: number): void => {
594
- // Clear any hover intent or submenu timers
595
- clearHoverIntent();
596
- clearSubmenuTimer();
597
-
598
- // Find submenus at or deeper than the specified level
599
- const submenusCopy = [...state.activeSubmenus];
600
- const submenuIndicesToRemove = [];
601
-
602
- // Identify which submenus to remove, working from deepest level first
603
- for (let i = submenusCopy.length - 1; i >= 0; i--) {
604
- if (submenusCopy[i].level >= level) {
605
- const submenuToClose = submenusCopy[i];
606
-
607
- // Set aria-expanded attribute to false on the parent menu item
608
- if (submenuToClose.menuItem) {
609
- submenuToClose.menuItem.setAttribute('aria-expanded', 'false');
610
- }
611
-
612
- // Hide with animation
613
- submenuToClose.element.classList.remove(`${component.getClass('menu--visible')}`);
614
-
615
- // Schedule for removal
616
- setTimeout(() => {
617
- if (submenuToClose.element.parentNode) {
618
- submenuToClose.element.parentNode.removeChild(submenuToClose.element);
619
- }
620
- }, 200);
621
-
622
- // Mark for removal from state
623
- submenuIndicesToRemove.push(i);
624
- }
625
- }
626
-
627
- // Remove the closed submenus from state
628
- submenuIndicesToRemove.forEach(index => {
629
- state.activeSubmenus.splice(index, 1);
630
- });
631
-
632
- // Update active submenu references based on what's left
633
- if (state.activeSubmenus.length > 0) {
634
- const deepestRemaining = state.activeSubmenus[state.activeSubmenus.length - 1];
635
- state.activeSubmenu = deepestRemaining.element;
636
- state.activeSubmenuItem = deepestRemaining.menuItem;
637
- state.submenuLevel = deepestRemaining.level;
638
- } else {
639
- state.activeSubmenu = null;
640
- state.activeSubmenuItem = null;
641
- state.submenuLevel = 0;
642
- }
643
- };
644
-
645
- /**
646
- * Closes all submenus
647
- */
648
- const closeSubmenu = (): void => {
649
- // Clear timers
650
- clearHoverIntent();
651
- clearSubmenuTimer();
652
-
653
- if (state.activeSubmenus.length === 0) return;
654
-
655
- // Close all active submenus
656
- [...state.activeSubmenus].forEach(submenu => {
657
- // Remove expanded state from parent item
658
- if (submenu.menuItem) {
659
- submenu.menuItem.setAttribute('aria-expanded', 'false');
660
- }
661
-
662
- // Remove submenu element with animation
663
- submenu.element.classList.remove(`${component.getClass('menu--visible')}`);
664
-
665
- // Remove after animation
666
- setTimeout(() => {
667
- if (submenu.element.parentNode) {
668
- submenu.element.parentNode.removeChild(submenu.element);
669
- }
670
- }, 200);
671
- });
672
-
673
- // Clear state
674
- state.activeSubmenu = null;
675
- state.activeSubmenuItem = null;
676
- state.activeSubmenus = [];
677
- state.submenuLevel = 0;
678
-
679
- // Remove document events
680
- document.removeEventListener('click', handleDocumentClickForSubmenu);
681
- window.removeEventListener('resize', handleWindowResizeForSubmenu);
682
- window.removeEventListener('scroll', handleWindowScrollForSubmenu);
683
- };
684
-
685
- /**
686
- * Handles document click for submenu
687
- */
688
- const handleDocumentClickForSubmenu = (e: MouseEvent): void => {
689
- if (!state.activeSubmenu) return;
690
-
691
- const submenuElement = state.activeSubmenu;
692
- const menuItemElement = state.activeSubmenuItem;
693
-
694
- // Check if click was inside submenu or parent menu item
695
- if (submenuElement.contains(e.target as Node) ||
696
- (menuItemElement && menuItemElement.contains(e.target as Node))) {
697
- return;
698
- }
699
-
700
- // Close submenu if clicked outside
701
- closeSubmenu();
702
- };
703
-
704
- /**
705
- * Handles window resize for submenu
706
- */
707
- const handleWindowResizeForSubmenu = (): void => {
708
- // Reposition open submenu on resize
709
- if (state.activeSubmenu && state.activeSubmenuItem) {
710
- positioner.positionSubmenu(state.activeSubmenu, state.activeSubmenuItem, state.submenuLevel);
711
- }
712
- };
713
-
714
- /**
715
- * Handles window scroll for submenu
716
- * Repositions the submenu to stay attached to its parent during scrolling
717
- */
718
- const handleWindowScrollForSubmenu = (): void => {
719
- // Use requestAnimationFrame to optimize scroll performance
720
- window.requestAnimationFrame(() => {
721
- // Only reposition if we have an active submenu
722
- if (state.activeSubmenu && state.activeSubmenuItem) {
723
- positioner.positionSubmenu(state.activeSubmenu, state.activeSubmenuItem, state.submenuLevel);
724
- }
725
- });
726
- };
727
-
728
- /**
729
- * Opens the menu
730
- * @param {Event} [event] - Optional event that triggered the open
731
- * @param {'mouse'|'keyboard'} [interactionType='mouse'] - Type of interaction that triggered the open
732
- */
733
- const openMenu = (event?: Event, interactionType: 'mouse' | 'keyboard' = 'mouse'): void => {
734
- if (state.visible) return;
735
-
736
- // Update state
737
- state.visible = true;
738
-
739
- // Step 1: Add the menu to the DOM if it's not already there with initial hidden state
740
- if (!component.element.parentNode) {
741
- // Apply explicit initial styling to ensure it doesn't flash
742
- component.element.classList.remove(`${component.getClass('menu--visible')}`);
743
- component.element.setAttribute('aria-hidden', 'true');
744
- component.element.style.transform = 'scaleY(0)';
745
- component.element.style.opacity = '0';
746
-
747
- // Add to DOM
748
- document.body.appendChild(component.element);
749
- }
750
-
751
- // Step 2: Position the menu (will be invisible)
752
- const anchorElement = getAnchorElement();
753
- if (anchorElement) {
754
- positioner.positionMenu(anchorElement);
755
- }
756
-
757
- // Step 3: Use a small delay to ensure DOM operations are complete
758
- setTimeout(() => {
759
- // Set attributes for accessibility
760
- component.element.setAttribute('aria-hidden', 'false');
761
-
762
- // Remove the inline styles we added
763
- component.element.style.transform = '';
764
- component.element.style.opacity = '';
765
-
766
- // Force a reflow before adding the visible class
767
- void component.element.getBoundingClientRect();
768
-
769
- // Add visible class to start the CSS transition
770
- component.element.classList.add(`${component.getClass('menu--visible')}`);
771
-
772
- // Step 4: Focus based on interaction type (after animation starts)
773
- setTimeout(() => {
774
- handleFocus(interactionType);
775
- }, 100);
776
- }, 20); // Short delay for browser to process
777
-
778
- // Add document events
779
- if (config.closeOnClickOutside) {
780
- document.addEventListener('click', handleDocumentClick);
781
- }
782
- if (config.closeOnEscape) {
783
- document.addEventListener('keydown', handleDocumentKeydown);
784
- }
785
- window.addEventListener('resize', handleWindowResize, { passive: true });
786
- window.addEventListener('scroll', handleWindowScroll, { passive: true });
787
-
788
- // Trigger event
789
- eventHelpers.triggerEvent('open', {}, event);
790
- };
791
-
792
- /**
793
- * Closes the menu
794
- */
795
- const closeMenu = (event?: Event): void => {
796
- if (!state.visible) return;
797
-
798
- // Close any open submenu first
799
- closeSubmenu();
800
-
801
- // Update state
802
- state.visible = false;
803
-
804
- // Set attributes
805
- component.element.setAttribute('aria-hidden', 'true');
806
- component.element.classList.remove(`${component.getClass('menu--visible')}`);
807
-
808
- // Remove document events
809
- document.removeEventListener('click', handleDocumentClick);
810
- document.removeEventListener('keydown', handleDocumentKeydown);
811
- window.removeEventListener('resize', handleWindowResize);
812
- window.removeEventListener('scroll', handleWindowScroll);
813
-
814
- // Trigger event
815
- eventHelpers.triggerEvent('close', {}, event);
816
-
817
- // Remove from DOM after animation completes
818
- setTimeout(() => {
819
- if (component.element.parentNode && !state.visible) {
820
- component.element.parentNode.removeChild(component.element);
821
- }
822
- }, 300); // Match the animation duration in CSS
823
- };
824
-
825
- /**
826
- * Toggles the menu
827
- */
828
- const toggleMenu = (event?: Event): void => {
829
- if (state.visible) {
830
- closeMenu(event);
831
- } else {
832
- openMenu(event);
833
- }
834
- };
835
-
836
- /**
837
- * Handles document click
838
- */
839
- const handleDocumentClick = (e: MouseEvent): void => {
840
- // Don't close if clicked inside menu
841
- if (component.element.contains(e.target as Node)) {
842
- return;
843
- }
844
-
845
- // Check if clicked on anchor element
846
- const anchor = getAnchorElement();
847
- if (anchor && anchor.contains(e.target as Node)) {
848
- return;
849
- }
850
-
851
- // Close menu
852
- closeMenu(e);
853
- };
854
-
855
- /**
856
- * Handles document keydown
857
- */
858
- const handleDocumentKeydown = (e: KeyboardEvent): void => {
859
- if (e.key === 'Escape') {
860
- closeMenu(e);
861
- }
862
- };
863
-
864
- /**
865
- * Handles window resize
866
- */
867
- const handleWindowResize = (): void => {
868
- if (state.visible) {
869
- const anchorElement = getAnchorElement();
870
- if (anchorElement) {
871
- positioner.positionMenu(anchorElement);
872
- }
873
- }
874
- };
875
-
876
- /**
877
- * Handles window scroll
878
- * Repositions the menu to stay attached to its anchor during scrolling
879
- */
880
- const handleWindowScroll = (): void => {
881
- if (state.visible) {
882
- // Use requestAnimationFrame to optimize scroll performance
883
- window.requestAnimationFrame(() => {
884
- // Reposition the main menu to stay attached to anchor when scrolling
885
- const anchorElement = getAnchorElement();
886
- if (anchorElement) {
887
- positioner.positionMenu(anchorElement);
888
- }
889
-
890
- // Also reposition any open submenu relative to its parent menu item
891
- if (state.activeSubmenu && state.activeSubmenuItem) {
892
- positioner.positionSubmenu(state.activeSubmenu, state.activeSubmenuItem, state.submenuLevel);
893
- }
894
- });
895
- }
896
- };
897
-
898
- /**
899
- * Handles keydown events on the menu or submenu
900
- */
901
- const handleMenuKeydown = (e: KeyboardEvent): void => {
902
- // Determine if this event is from the main menu or a submenu
903
- const isSubmenu = state.activeSubmenu && state.activeSubmenu.contains(e.target as Node);
904
-
905
- // Get the appropriate menu element
906
- const menuElement = isSubmenu ? state.activeSubmenu : component.element;
907
-
908
- // Get all non-disabled menu items from the current menu
909
- const items = Array.from(menuElement.querySelectorAll(
910
- `.${component.getClass('menu-item')}:not(.${component.getClass('menu-item--disabled')})`
911
- )) as HTMLElement[];
912
-
913
- if (items.length === 0) return;
914
-
915
- // Get the currently focused item index
916
- let focusedItemIndex = -1;
917
- const focusedElement = menuElement.querySelector(':focus') as HTMLElement;
918
- if (focusedElement && focusedElement.classList.contains(component.getClass('menu-item'))) {
919
- focusedItemIndex = items.indexOf(focusedElement);
920
- }
921
-
922
- switch (e.key) {
923
- case 'ArrowDown':
924
- case 'Down':
925
- e.preventDefault();
926
- // If no item is active, select the first one
927
- if (focusedItemIndex < 0) {
928
- items[0].focus();
929
- } else if (focusedItemIndex < items.length - 1) {
930
- items[focusedItemIndex + 1].focus();
931
- } else {
932
- items[0].focus();
933
- }
934
- break;
935
-
936
- case 'ArrowUp':
937
- case 'Up':
938
- e.preventDefault();
939
- // If no item is active, select the last one
940
- if (focusedItemIndex < 0) {
941
- items[items.length - 1].focus();
942
- } else if (focusedItemIndex > 0) {
943
- items[focusedItemIndex - 1].focus();
944
- } else {
945
- items[items.length - 1].focus();
946
- }
947
- break;
948
-
949
- case 'Home':
950
- e.preventDefault();
951
- items[0].focus();
952
- break;
953
-
954
- case 'End':
955
- e.preventDefault();
956
- items[items.length - 1].focus();
957
- break;
958
-
959
- case 'Enter':
960
- case ' ':
961
- e.preventDefault();
962
- // If an item is focused, click it
963
- if (focusedItemIndex >= 0) {
964
- items[focusedItemIndex].click();
965
- }
966
- break;
967
-
968
- case 'ArrowRight':
969
- case 'Right':
970
- e.preventDefault();
971
- // Handle right arrow in different contexts
972
- if (isSubmenu) {
973
- // In a submenu, right arrow opens nested submenus
974
- if (focusedItemIndex >= 0 && items[focusedItemIndex].classList.contains(`${component.getClass('menu-item--submenu')}`)) {
975
- items[focusedItemIndex].click();
976
- }
977
- } else {
978
- // In main menu, right arrow opens a submenu
979
- if (focusedItemIndex >= 0 && items[focusedItemIndex].classList.contains(`${component.getClass('menu-item--submenu')}`)) {
980
- // Get the correct menu item data
981
- const itemElement = items[focusedItemIndex];
982
- const itemIndex = parseInt(itemElement.getAttribute('data-index'), 10);
983
- const itemData = state.items[itemIndex] as MenuItem;
984
-
985
- // Open submenu via keyboard
986
- handleSubmenuClick(itemData, itemIndex, itemElement, true);
987
- }
988
- }
989
- break;
990
-
991
- case 'ArrowLeft':
992
- case 'Left':
993
- e.preventDefault();
994
- // Handle left arrow in different contexts
995
- if (isSubmenu) {
996
- // In a submenu, left arrow returns to the parent menu
997
- if (state.activeSubmenuItem) {
998
- // Store the reference to the parent item before closing the submenu
999
- const parentItem = state.activeSubmenuItem;
1000
-
1001
- // Get the current level
1002
- const currentLevel = parseInt(
1003
- menuElement.getAttribute('data-level') || '1',
1004
- 10
1005
- );
1006
-
1007
- // Close this level of submenu
1008
- closeSubmenuAtLevel(currentLevel);
1009
-
1010
- // Focus the parent item after closing
1011
- parentItem.focus();
1012
- } else {
1013
- closeSubmenu();
1014
- }
1015
- }
1016
- break;
1017
-
1018
- case 'Escape':
1019
- e.preventDefault();
1020
- if (isSubmenu) {
1021
- // In a submenu, Escape closes just the submenu
1022
- if (state.activeSubmenuItem) {
1023
- // Store the reference to the parent item before closing the submenu
1024
- const parentItem = state.activeSubmenuItem;
1025
-
1026
- // Get the current level
1027
- const currentLevel = parseInt(
1028
- menuElement.getAttribute('data-level') || '1',
1029
- 10
1030
- );
1031
-
1032
- // Close this level of submenu
1033
- closeSubmenuAtLevel(currentLevel);
1034
-
1035
- // Focus the parent item after closing
1036
- parentItem.focus();
1037
- } else {
1038
- closeSubmenu();
1039
- }
1040
- } else {
1041
- // In main menu, Escape closes the entire menu
1042
- closeMenu(e);
1043
- }
1044
- break;
1045
-
1046
- case 'Tab':
1047
- // Close the menu when tabbing out
1048
- if (!isSubmenu) {
1049
- closeMenu();
1050
- }
1051
- break;
1052
- }
1053
- };
1054
-
1055
- /**
1056
- * Sets up the menu
1057
- */
1058
- const initMenu = () => {
1059
- // Set up menu structure
1060
- renderMenuItems();
1061
-
1062
- // Set up keyboard navigation
1063
- component.element.addEventListener('keydown', handleMenuKeydown);
1064
-
1065
- // Position if visible
1066
- if (state.visible) {
1067
- const anchorElement = getAnchorElement();
1068
- if (anchorElement) {
1069
- positioner.positionMenu(anchorElement);
1070
- }
1071
-
1072
- // Show immediately
1073
- component.element.classList.add(`${component.getClass('menu--visible')}`);
1074
-
1075
- // Set up document events
1076
- if (config.closeOnClickOutside) {
1077
- document.addEventListener('click', handleDocumentClick);
1078
- }
1079
- if (config.closeOnEscape) {
1080
- document.addEventListener('keydown', handleDocumentKeydown);
1081
- }
1082
- window.addEventListener('resize', handleWindowResize);
1083
- window.addEventListener('scroll', handleWindowScroll);
1084
- }
1085
- };
1086
-
1087
- // Initialize after DOM is ready
1088
- setTimeout(initMenu, 0);
1089
-
1090
- // Register with lifecycle if available
1091
- if (component.lifecycle) {
1092
- const originalDestroy = component.lifecycle.destroy || (() => {});
1093
- component.lifecycle.destroy = () => {
1094
- // Clean up timers
1095
- clearHoverIntent();
1096
- clearSubmenuTimer();
1097
-
1098
- // Clean up document events
1099
- document.removeEventListener('click', handleDocumentClick);
1100
- document.removeEventListener('keydown', handleDocumentKeydown);
1101
- window.removeEventListener('resize', handleWindowResize);
1102
- window.removeEventListener('scroll', handleWindowScroll);
1103
-
1104
- // Clean up submenu events
1105
- document.removeEventListener('click', handleDocumentClickForSubmenu);
1106
- window.removeEventListener('resize', handleWindowResizeForSubmenu);
1107
- window.removeEventListener('scroll', handleWindowScrollForSubmenu);
1108
-
1109
- // Clean up submenu element
1110
- if (state.activeSubmenus.length > 0) {
1111
- state.activeSubmenus.forEach(submenu => {
1112
- if (submenu.element.parentNode) {
1113
- submenu.element.parentNode.removeChild(submenu.element);
1114
- }
1115
- });
1116
- }
1117
-
1118
- originalDestroy();
1119
- };
1120
- }
1121
-
1122
- // Return enhanced component
1123
- return {
1124
- ...component,
1125
- menu: {
1126
- open: (event) => {
1127
- openMenu(event);
1128
- return component;
1129
- },
1130
-
1131
- close: (event) => {
1132
- closeMenu(event);
1133
- return component;
1134
- },
1135
-
1136
- toggle: (event) => {
1137
- toggleMenu(event);
1138
- return component;
1139
- },
1140
-
1141
- isOpen: () => state.visible,
1142
-
1143
- setItems: (items) => {
1144
- state.items = items;
1145
- renderMenuItems();
1146
- return component;
1147
- },
1148
-
1149
- getItems: () => state.items,
1150
-
1151
- setPosition: (position) => {
1152
- state.position = position;
1153
- if (state.visible) {
1154
- const anchorElement = getAnchorElement();
1155
- if (anchorElement) {
1156
- positioner.positionMenu(anchorElement);
1157
- }
1158
- }
1159
- return component;
1160
- },
1161
-
1162
- getPosition: () => state.position
1163
- }
1164
- };
1165
- };
1166
-
1167
- export default withController;