stimeo-ui 0.16.0 → 0.18.0

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 (337) hide show
  1. package/CHANGELOG.md +136 -0
  2. package/README.md +27 -11
  3. package/dist/cable/index.d.ts +88 -31
  4. package/dist/cable/index.js +384 -449
  5. package/dist/cable/index.js.map +1 -1
  6. package/dist/coerce-JtZVNG5h.d.ts +20 -0
  7. package/dist/controllers/accordion_controller.d.ts +4 -2
  8. package/dist/controllers/accordion_controller.js +174 -55
  9. package/dist/controllers/accordion_controller.js.map +1 -1
  10. package/dist/controllers/alert_dialog_controller.d.ts +12 -2
  11. package/dist/controllers/alert_dialog_controller.js +903 -235
  12. package/dist/controllers/alert_dialog_controller.js.map +1 -1
  13. package/dist/controllers/announcer_controller.d.ts +37 -5
  14. package/dist/controllers/announcer_controller.js +238 -205
  15. package/dist/controllers/announcer_controller.js.map +1 -1
  16. package/dist/controllers/aspect_ratio_controller.js +0 -10
  17. package/dist/controllers/aspect_ratio_controller.js.map +1 -1
  18. package/dist/controllers/auto_submit_controller.d.ts +30 -6
  19. package/dist/controllers/auto_submit_controller.js +414 -145
  20. package/dist/controllers/auto_submit_controller.js.map +1 -1
  21. package/dist/controllers/avatar_controller.js +185 -107
  22. package/dist/controllers/avatar_controller.js.map +1 -1
  23. package/dist/controllers/breadcrumb_controller.js +0 -130
  24. package/dist/controllers/breadcrumb_controller.js.map +1 -1
  25. package/dist/controllers/bulk_select_controller.d.ts +21 -3
  26. package/dist/controllers/bulk_select_controller.js +282 -58
  27. package/dist/controllers/bulk_select_controller.js.map +1 -1
  28. package/dist/controllers/calendar_controller.d.ts +38 -16
  29. package/dist/controllers/calendar_controller.js +228 -260
  30. package/dist/controllers/calendar_controller.js.map +1 -1
  31. package/dist/controllers/carousel_controller.d.ts +43 -12
  32. package/dist/controllers/carousel_controller.js +536 -436
  33. package/dist/controllers/carousel_controller.js.map +1 -1
  34. package/dist/controllers/character_counter_controller.d.ts +44 -7
  35. package/dist/controllers/character_counter_controller.js +493 -207
  36. package/dist/controllers/character_counter_controller.js.map +1 -1
  37. package/dist/controllers/checkbox_controller.d.ts +7 -2
  38. package/dist/controllers/checkbox_controller.js +38 -74
  39. package/dist/controllers/checkbox_controller.js.map +1 -1
  40. package/dist/controllers/clipboard_controller.d.ts +16 -5
  41. package/dist/controllers/clipboard_controller.js +261 -149
  42. package/dist/controllers/clipboard_controller.js.map +1 -1
  43. package/dist/controllers/collapsible_controller.d.ts +17 -5
  44. package/dist/controllers/collapsible_controller.js +304 -120
  45. package/dist/controllers/collapsible_controller.js.map +1 -1
  46. package/dist/controllers/color_picker_controller.d.ts +18 -6
  47. package/dist/controllers/color_picker_controller.js +178 -146
  48. package/dist/controllers/color_picker_controller.js.map +1 -1
  49. package/dist/controllers/combobox_controller.d.ts +24 -6
  50. package/dist/controllers/combobox_controller.js +522 -156
  51. package/dist/controllers/combobox_controller.js.map +1 -1
  52. package/dist/controllers/command_palette_controller.d.ts +51 -20
  53. package/dist/controllers/command_palette_controller.js +1156 -395
  54. package/dist/controllers/command_palette_controller.js.map +1 -1
  55. package/dist/controllers/conditional_fields_controller.d.ts +15 -2
  56. package/dist/controllers/conditional_fields_controller.js +300 -171
  57. package/dist/controllers/conditional_fields_controller.js.map +1 -1
  58. package/dist/controllers/confirm_controller.d.ts +49 -12
  59. package/dist/controllers/confirm_controller.js +980 -262
  60. package/dist/controllers/confirm_controller.js.map +1 -1
  61. package/dist/controllers/context_menu_controller.d.ts +80 -18
  62. package/dist/controllers/context_menu_controller.js +346 -153
  63. package/dist/controllers/context_menu_controller.js.map +1 -1
  64. package/dist/controllers/count_up_controller.d.ts +13 -1
  65. package/dist/controllers/count_up_controller.js +73 -21
  66. package/dist/controllers/count_up_controller.js.map +1 -1
  67. package/dist/controllers/countdown_controller.d.ts +47 -3
  68. package/dist/controllers/countdown_controller.js +281 -158
  69. package/dist/controllers/countdown_controller.js.map +1 -1
  70. package/dist/controllers/currency_input_controller.d.ts +93 -10
  71. package/dist/controllers/currency_input_controller.js +575 -225
  72. package/dist/controllers/currency_input_controller.js.map +1 -1
  73. package/dist/controllers/data_grid_controller.d.ts +25 -12
  74. package/dist/controllers/data_grid_controller.js +248 -214
  75. package/dist/controllers/data_grid_controller.js.map +1 -1
  76. package/dist/controllers/date_range_picker_controller.d.ts +42 -14
  77. package/dist/controllers/date_range_picker_controller.js +338 -235
  78. package/dist/controllers/date_range_picker_controller.js.map +1 -1
  79. package/dist/controllers/dialog_controller.d.ts +17 -6
  80. package/dist/controllers/dialog_controller.js +899 -237
  81. package/dist/controllers/dialog_controller.js.map +1 -1
  82. package/dist/controllers/direct_upload_controller.d.ts +25 -8
  83. package/dist/controllers/direct_upload_controller.js +97 -167
  84. package/dist/controllers/direct_upload_controller.js.map +1 -1
  85. package/dist/controllers/dirty_form_controller.d.ts +6 -1
  86. package/dist/controllers/dirty_form_controller.js +72 -69
  87. package/dist/controllers/dirty_form_controller.js.map +1 -1
  88. package/dist/controllers/dismissible_controller.d.ts +19 -0
  89. package/dist/controllers/dismissible_controller.js +196 -24
  90. package/dist/controllers/dismissible_controller.js.map +1 -1
  91. package/dist/controllers/drawer_controller.d.ts +39 -11
  92. package/dist/controllers/drawer_controller.js +974 -355
  93. package/dist/controllers/drawer_controller.js.map +1 -1
  94. package/dist/controllers/dropdown_controller.d.ts +31 -1
  95. package/dist/controllers/dropdown_controller.js +339 -96
  96. package/dist/controllers/dropdown_controller.js.map +1 -1
  97. package/dist/controllers/editable_controller.d.ts +12 -1
  98. package/dist/controllers/editable_controller.js +315 -88
  99. package/dist/controllers/editable_controller.js.map +1 -1
  100. package/dist/controllers/empty_state_controller.js +79 -58
  101. package/dist/controllers/empty_state_controller.js.map +1 -1
  102. package/dist/controllers/file_dropzone_controller.d.ts +63 -18
  103. package/dist/controllers/file_dropzone_controller.js +322 -211
  104. package/dist/controllers/file_dropzone_controller.js.map +1 -1
  105. package/dist/controllers/filter_controller.js +77 -42
  106. package/dist/controllers/filter_controller.js.map +1 -1
  107. package/dist/controllers/flash_controller.d.ts +35 -12
  108. package/dist/controllers/flash_controller.js +238 -347
  109. package/dist/controllers/flash_controller.js.map +1 -1
  110. package/dist/controllers/focus_controller.d.ts +15 -6
  111. package/dist/controllers/focus_controller.js +679 -240
  112. package/dist/controllers/focus_controller.js.map +1 -1
  113. package/dist/controllers/form_field_controller.js +156 -132
  114. package/dist/controllers/form_field_controller.js.map +1 -1
  115. package/dist/controllers/form_validation_controller.d.ts +3 -3
  116. package/dist/controllers/form_validation_controller.js +31 -97
  117. package/dist/controllers/form_validation_controller.js.map +1 -1
  118. package/dist/controllers/frame_loading_controller.d.ts +47 -14
  119. package/dist/controllers/frame_loading_controller.js +275 -217
  120. package/dist/controllers/frame_loading_controller.js.map +1 -1
  121. package/dist/controllers/highlight_controller.d.ts +27 -6
  122. package/dist/controllers/highlight_controller.js +107 -75
  123. package/dist/controllers/highlight_controller.js.map +1 -1
  124. package/dist/controllers/hover_card_controller.d.ts +60 -4
  125. package/dist/controllers/hover_card_controller.js +327 -130
  126. package/dist/controllers/hover_card_controller.js.map +1 -1
  127. package/dist/controllers/idle_controller.d.ts +48 -6
  128. package/dist/controllers/idle_controller.js +271 -101
  129. package/dist/controllers/idle_controller.js.map +1 -1
  130. package/dist/controllers/input_mask_controller.d.ts +30 -13
  131. package/dist/controllers/input_mask_controller.js +483 -97
  132. package/dist/controllers/input_mask_controller.js.map +1 -1
  133. package/dist/controllers/intersection_controller.d.ts +32 -13
  134. package/dist/controllers/intersection_controller.js +150 -106
  135. package/dist/controllers/intersection_controller.js.map +1 -1
  136. package/dist/controllers/lazy_frame_controller.d.ts +19 -2
  137. package/dist/controllers/lazy_frame_controller.js +24 -68
  138. package/dist/controllers/lazy_frame_controller.js.map +1 -1
  139. package/dist/controllers/listbox_controller.d.ts +44 -9
  140. package/dist/controllers/listbox_controller.js +523 -240
  141. package/dist/controllers/listbox_controller.js.map +1 -1
  142. package/dist/controllers/local_time_controller.d.ts +9 -7
  143. package/dist/controllers/local_time_controller.js +102 -77
  144. package/dist/controllers/local_time_controller.js.map +1 -1
  145. package/dist/controllers/masonry_controller.d.ts +11 -3
  146. package/dist/controllers/masonry_controller.js +80 -96
  147. package/dist/controllers/masonry_controller.js.map +1 -1
  148. package/dist/controllers/menu_controller.d.ts +55 -11
  149. package/dist/controllers/menu_controller.js +302 -188
  150. package/dist/controllers/menu_controller.js.map +1 -1
  151. package/dist/controllers/menubar_controller.d.ts +51 -9
  152. package/dist/controllers/menubar_controller.js +232 -345
  153. package/dist/controllers/menubar_controller.js.map +1 -1
  154. package/dist/controllers/meter_controller.d.ts +33 -20
  155. package/dist/controllers/meter_controller.js +417 -87
  156. package/dist/controllers/meter_controller.js.map +1 -1
  157. package/dist/controllers/multi_select_controller.d.ts +48 -18
  158. package/dist/controllers/multi_select_controller.js +785 -393
  159. package/dist/controllers/multi_select_controller.js.map +1 -1
  160. package/dist/controllers/navigation_menu_controller.d.ts +27 -6
  161. package/dist/controllers/navigation_menu_controller.js +190 -250
  162. package/dist/controllers/navigation_menu_controller.js.map +1 -1
  163. package/dist/controllers/nested_form_controller.d.ts +10 -0
  164. package/dist/controllers/nested_form_controller.js +396 -192
  165. package/dist/controllers/nested_form_controller.js.map +1 -1
  166. package/dist/controllers/network_status_controller.d.ts +23 -0
  167. package/dist/controllers/network_status_controller.js +268 -62
  168. package/dist/controllers/network_status_controller.js.map +1 -1
  169. package/dist/controllers/number_input_controller.d.ts +50 -8
  170. package/dist/controllers/number_input_controller.js +610 -303
  171. package/dist/controllers/number_input_controller.js.map +1 -1
  172. package/dist/controllers/optimistic_controller.d.ts +8 -5
  173. package/dist/controllers/optimistic_controller.js +45 -87
  174. package/dist/controllers/optimistic_controller.js.map +1 -1
  175. package/dist/controllers/otp_controller.d.ts +55 -8
  176. package/dist/controllers/otp_controller.js +658 -255
  177. package/dist/controllers/otp_controller.js.map +1 -1
  178. package/dist/controllers/overflow_indicator_controller.d.ts +5 -0
  179. package/dist/controllers/overflow_indicator_controller.js +122 -113
  180. package/dist/controllers/overflow_indicator_controller.js.map +1 -1
  181. package/dist/controllers/overflow_menu_controller.d.ts +58 -20
  182. package/dist/controllers/overflow_menu_controller.js +470 -295
  183. package/dist/controllers/overflow_menu_controller.js.map +1 -1
  184. package/dist/controllers/pagination_controller.d.ts +37 -7
  185. package/dist/controllers/pagination_controller.js +332 -123
  186. package/dist/controllers/pagination_controller.js.map +1 -1
  187. package/dist/controllers/password_reveal_controller.d.ts +46 -8
  188. package/dist/controllers/password_reveal_controller.js +392 -121
  189. package/dist/controllers/password_reveal_controller.js.map +1 -1
  190. package/dist/controllers/password_strength_controller.d.ts +15 -6
  191. package/dist/controllers/password_strength_controller.js +111 -158
  192. package/dist/controllers/password_strength_controller.js.map +1 -1
  193. package/dist/controllers/persist_controller.d.ts +11 -4
  194. package/dist/controllers/persist_controller.js +165 -111
  195. package/dist/controllers/persist_controller.js.map +1 -1
  196. package/dist/controllers/pointer_drag_controller.d.ts +35 -12
  197. package/dist/controllers/pointer_drag_controller.js +434 -188
  198. package/dist/controllers/pointer_drag_controller.js.map +1 -1
  199. package/dist/controllers/popover_controller.d.ts +41 -5
  200. package/dist/controllers/popover_controller.js +269 -113
  201. package/dist/controllers/popover_controller.js.map +1 -1
  202. package/dist/controllers/portal_controller.d.ts +44 -4
  203. package/dist/controllers/portal_controller.js +215 -76
  204. package/dist/controllers/portal_controller.js.map +1 -1
  205. package/dist/controllers/preview_guard_controller.d.ts +10 -4
  206. package/dist/controllers/preview_guard_controller.js +243 -116
  207. package/dist/controllers/preview_guard_controller.js.map +1 -1
  208. package/dist/controllers/progress_controller.d.ts +20 -19
  209. package/dist/controllers/progress_controller.js +391 -78
  210. package/dist/controllers/progress_controller.js.map +1 -1
  211. package/dist/controllers/radio_group_controller.d.ts +21 -9
  212. package/dist/controllers/radio_group_controller.js +278 -211
  213. package/dist/controllers/radio_group_controller.js.map +1 -1
  214. package/dist/controllers/range_slider_controller.d.ts +47 -6
  215. package/dist/controllers/range_slider_controller.js +220 -113
  216. package/dist/controllers/range_slider_controller.js.map +1 -1
  217. package/dist/controllers/rating_controller.d.ts +20 -4
  218. package/dist/controllers/rating_controller.js +458 -211
  219. package/dist/controllers/rating_controller.js.map +1 -1
  220. package/dist/controllers/read_more_controller.d.ts +2 -0
  221. package/dist/controllers/read_more_controller.js +138 -63
  222. package/dist/controllers/read_more_controller.js.map +1 -1
  223. package/dist/controllers/reading_progress_controller.d.ts +1 -1
  224. package/dist/controllers/reading_progress_controller.js +166 -137
  225. package/dist/controllers/reading_progress_controller.js.map +1 -1
  226. package/dist/controllers/relative_time_controller.d.ts +20 -6
  227. package/dist/controllers/relative_time_controller.js +211 -112
  228. package/dist/controllers/relative_time_controller.js.map +1 -1
  229. package/dist/controllers/reset_on_restore_controller.d.ts +85 -0
  230. package/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +103 -38
  231. package/dist/controllers/reset_on_restore_controller.js.map +1 -0
  232. package/dist/controllers/resizable_controller.d.ts +31 -2
  233. package/dist/controllers/resizable_controller.js +299 -150
  234. package/dist/controllers/resizable_controller.js.map +1 -1
  235. package/dist/controllers/roving_controller.d.ts +3 -3
  236. package/dist/controllers/roving_controller.js +49 -88
  237. package/dist/controllers/roving_controller.js.map +1 -1
  238. package/dist/controllers/scroll_area_controller.d.ts +7 -3
  239. package/dist/controllers/scroll_area_controller.js +490 -246
  240. package/dist/controllers/scroll_area_controller.js.map +1 -1
  241. package/dist/controllers/scroll_restore_controller.d.ts +3 -3
  242. package/dist/controllers/scroll_restore_controller.js +14 -55
  243. package/dist/controllers/scroll_restore_controller.js.map +1 -1
  244. package/dist/controllers/scroll_visibility_controller.d.ts +28 -12
  245. package/dist/controllers/scroll_visibility_controller.js +432 -149
  246. package/dist/controllers/scroll_visibility_controller.js.map +1 -1
  247. package/dist/controllers/scrollspy_controller.d.ts +6 -1
  248. package/dist/controllers/scrollspy_controller.js +193 -248
  249. package/dist/controllers/scrollspy_controller.js.map +1 -1
  250. package/dist/controllers/separator_controller.d.ts +20 -5
  251. package/dist/controllers/separator_controller.js +285 -142
  252. package/dist/controllers/separator_controller.js.map +1 -1
  253. package/dist/controllers/sidebar_controller.d.ts +65 -8
  254. package/dist/controllers/sidebar_controller.js +1069 -404
  255. package/dist/controllers/sidebar_controller.js.map +1 -1
  256. package/dist/controllers/skeleton_controller.d.ts +23 -0
  257. package/dist/controllers/skeleton_controller.js +259 -101
  258. package/dist/controllers/skeleton_controller.js.map +1 -1
  259. package/dist/controllers/slider_controller.d.ts +29 -2
  260. package/dist/controllers/slider_controller.js +187 -86
  261. package/dist/controllers/slider_controller.js.map +1 -1
  262. package/dist/controllers/smart_sticky_header_controller.d.ts +22 -4
  263. package/dist/controllers/smart_sticky_header_controller.js +173 -66
  264. package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
  265. package/dist/controllers/sortable_controller.d.ts +4 -4
  266. package/dist/controllers/sortable_controller.js +26 -125
  267. package/dist/controllers/sortable_controller.js.map +1 -1
  268. package/dist/controllers/spinner_controller.d.ts +39 -4
  269. package/dist/controllers/spinner_controller.js +288 -173
  270. package/dist/controllers/spinner_controller.js.map +1 -1
  271. package/dist/controllers/step_indicator_controller.d.ts +9 -4
  272. package/dist/controllers/step_indicator_controller.js +105 -46
  273. package/dist/controllers/step_indicator_controller.js.map +1 -1
  274. package/dist/controllers/stepper_controller.d.ts +14 -6
  275. package/dist/controllers/stepper_controller.js +120 -73
  276. package/dist/controllers/stepper_controller.js.map +1 -1
  277. package/dist/controllers/stick_to_bottom_controller.d.ts +9 -1
  278. package/dist/controllers/stick_to_bottom_controller.js +275 -140
  279. package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
  280. package/dist/controllers/sticky_observer_controller.d.ts +11 -0
  281. package/dist/controllers/sticky_observer_controller.js +220 -49
  282. package/dist/controllers/sticky_observer_controller.js.map +1 -1
  283. package/dist/controllers/submit_once_controller.d.ts +24 -8
  284. package/dist/controllers/submit_once_controller.js +376 -227
  285. package/dist/controllers/submit_once_controller.js.map +1 -1
  286. package/dist/controllers/switch_controller.d.ts +18 -4
  287. package/dist/controllers/switch_controller.js +201 -84
  288. package/dist/controllers/switch_controller.js.map +1 -1
  289. package/dist/controllers/tabs_controller.d.ts +9 -6
  290. package/dist/controllers/tabs_controller.js +41 -30
  291. package/dist/controllers/tabs_controller.js.map +1 -1
  292. package/dist/controllers/tags_input_controller.d.ts +16 -3
  293. package/dist/controllers/tags_input_controller.js +293 -173
  294. package/dist/controllers/tags_input_controller.js.map +1 -1
  295. package/dist/controllers/textarea_autosize_controller.d.ts +11 -1
  296. package/dist/controllers/textarea_autosize_controller.js +108 -74
  297. package/dist/controllers/textarea_autosize_controller.js.map +1 -1
  298. package/dist/controllers/theme_controller.d.ts +24 -11
  299. package/dist/controllers/theme_controller.js +140 -116
  300. package/dist/controllers/theme_controller.js.map +1 -1
  301. package/dist/controllers/time_picker_controller.d.ts +55 -5
  302. package/dist/controllers/time_picker_controller.js +331 -127
  303. package/dist/controllers/time_picker_controller.js.map +1 -1
  304. package/dist/controllers/toast_controller.d.ts +40 -16
  305. package/dist/controllers/toast_controller.js +244 -324
  306. package/dist/controllers/toast_controller.js.map +1 -1
  307. package/dist/controllers/toggle_group_controller.d.ts +14 -6
  308. package/dist/controllers/toggle_group_controller.js +252 -191
  309. package/dist/controllers/toggle_group_controller.js.map +1 -1
  310. package/dist/controllers/toolbar_controller.d.ts +8 -6
  311. package/dist/controllers/toolbar_controller.js +219 -120
  312. package/dist/controllers/toolbar_controller.js.map +1 -1
  313. package/dist/controllers/tooltip_controller.d.ts +49 -4
  314. package/dist/controllers/tooltip_controller.js +158 -124
  315. package/dist/controllers/tooltip_controller.js.map +1 -1
  316. package/dist/controllers/transition_controller.d.ts +22 -4
  317. package/dist/controllers/transition_controller.js +123 -119
  318. package/dist/controllers/transition_controller.js.map +1 -1
  319. package/dist/controllers/tree_view_controller.d.ts +22 -12
  320. package/dist/controllers/tree_view_controller.js +283 -369
  321. package/dist/controllers/tree_view_controller.js.map +1 -1
  322. package/dist/index.d.ts +10 -5
  323. package/dist/index.js +8573 -10410
  324. package/dist/index.js.map +1 -1
  325. package/dist/inspector/cli.d.ts +38 -14
  326. package/dist/inspector/cli.js +108 -40
  327. package/dist/inspector/cli.js.map +1 -1
  328. package/dist/inspector/cli_bin.js +145 -64
  329. package/dist/inspector/cli_bin.js.map +1 -1
  330. package/dist/inspector/examples.json +20 -20
  331. package/dist/inspector/manifest.json +802 -101
  332. package/dist/positioning/index.d.ts +15 -7
  333. package/dist/positioning/index.js +154 -47
  334. package/dist/positioning/index.js.map +1 -1
  335. package/package.json +11 -10
  336. package/dist/controllers/reset_before_cache_controller.d.ts +0 -76
  337. package/dist/controllers/reset_before_cache_controller.js.map +0 -1
@@ -1,19 +1,65 @@
1
1
  import { Controller } from '@hotwired/stimulus';
2
2
 
3
- // src/controllers/navigation_menu_controller.ts
3
+ var FOCUS_EVENTS = /* @__PURE__ */ new Set(["blur", "focus", "focusin", "focusout"]);
4
+ var POINTER_EVENTS = /* @__PURE__ */ new Set(["mouseenter", "mouseleave", "pointerenter", "pointerleave"]);
5
+ function stateReasonFor(event) {
6
+ if (!event) return "api";
7
+ if (FOCUS_EVENTS.has(event.type)) return "focus";
8
+ if (POINTER_EVENTS.has(event.type)) return "pointer";
9
+ return "user";
10
+ }
11
+
12
+ function actionSource(source) {
13
+ if (source instanceof HTMLElement) {
14
+ return { event: null, host: source, origin: source, reason: "api" };
15
+ }
16
+ const event = source ?? null;
17
+ return {
18
+ event,
19
+ host: event?.currentTarget instanceof Element ? event.currentTarget : null,
20
+ origin: event?.target instanceof Element ? event.target : null,
21
+ reason: stateReasonFor(event)
22
+ };
23
+ }
24
+
25
+ function isAriaDisabled(element) {
26
+ for (let current = element; current; current = current.parentElement) {
27
+ if (current.getAttribute("aria-disabled") === "true") return true;
28
+ }
29
+ return false;
30
+ }
4
31
 
5
- // src/utils/logical_scroll.ts
6
32
  function isRtl(element) {
7
33
  return window.getComputedStyle(element).direction === "rtl";
8
34
  }
9
35
 
10
- // src/utils/arrow_step.ts
11
36
  function isReservedArrowChord(event, allow = []) {
12
37
  if (!event.key.startsWith("Arrow")) return false;
13
38
  return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
14
39
  }
15
40
 
16
- // src/utils/escape_layer.ts
41
+ var ClickVerdicts = class {
42
+ #verdicts = /* @__PURE__ */ new WeakMap();
43
+ record(event, entries, refused) {
44
+ this.#verdicts.set(event, { entries, refused });
45
+ }
46
+ for(event, entry) {
47
+ const verdict = event && this.#verdicts.get(event);
48
+ if (!verdict?.entries.includes(entry)) return void 0;
49
+ return verdict.refused;
50
+ }
51
+ };
52
+
53
+ function sharedRegistry(key, create) {
54
+ const symbol = Symbol.for(key);
55
+ const scope = globalThis;
56
+ const existing = scope[symbol];
57
+ if (existing !== void 0) return existing;
58
+ const registry = create();
59
+ Object.defineProperty(globalThis, symbol, { value: registry });
60
+ return registry;
61
+ }
62
+
17
63
  function claimsWhileFocusWithin(element) {
18
64
  return () => {
19
65
  const active = element.ownerDocument.activeElement;
@@ -21,17 +67,11 @@ function claimsWhileFocusWithin(element) {
21
67
  };
22
68
  }
23
69
  var EscapeLayer = class _EscapeLayer {
24
- static #registries = /* @__PURE__ */ new WeakMap();
25
- #ownerDocument = null;
26
- /** Dismissal callback while active; `null` when inactive. */
27
- #onDismiss = null;
28
- /** Live predicate deciding whether the layer claims a press; `null` = always. */
29
- #claims = null;
30
- /**
31
- * Activates this layer at the top of its document's Escape stack, installing
32
- * the document's shared resolver listener if this is its first layer.
33
- * Re-activating an already-active layer moves it to the top.
34
- */
70
+ static #registries = sharedRegistry(
71
+ "stimeo-ui.escape-layer.registry.v1",
72
+ () => /* @__PURE__ */ new WeakMap()
73
+ );
74
+ #active = null;
35
75
  activate(ownerDocument = document, options) {
36
76
  this.deactivate();
37
77
  let registry = _EscapeLayer.#registries.get(ownerDocument);
@@ -40,44 +80,31 @@ var EscapeLayer = class _EscapeLayer {
40
80
  _EscapeLayer.#registries.set(ownerDocument, registry);
41
81
  ownerDocument.addEventListener("keydown", registry.onKeydown);
42
82
  }
43
- registry.stack.push(this);
44
- this.#ownerDocument = ownerDocument;
45
- this.#onDismiss = options.onDismiss;
46
- this.#claims = options.claims ?? null;
47
- }
48
- /**
49
- * Removes this layer from its document's Escape stack, uninstalling the
50
- * shared listener when the stack empties. Safe to call when inactive.
51
- */
83
+ const entry = { onDismiss: options.onDismiss, claims: options.claims ?? null };
84
+ registry.stack.push(entry);
85
+ this.#active = { document: ownerDocument, entry };
86
+ }
52
87
  deactivate() {
53
- const ownerDocument = this.#ownerDocument;
54
- if (!ownerDocument) return;
55
- const registry = _EscapeLayer.#registries.get(ownerDocument);
88
+ const active = this.#active;
89
+ if (!active) return;
90
+ const registry = _EscapeLayer.#registries.get(active.document);
56
91
  if (registry) {
57
- const index = registry.stack.lastIndexOf(this);
92
+ const index = registry.stack.lastIndexOf(active.entry);
58
93
  if (index >= 0) registry.stack.splice(index, 1);
59
94
  if (registry.stack.length === 0) {
60
- ownerDocument.removeEventListener("keydown", registry.onKeydown);
61
- _EscapeLayer.#registries.delete(ownerDocument);
95
+ active.document.removeEventListener("keydown", registry.onKeydown);
96
+ _EscapeLayer.#registries.delete(active.document);
62
97
  }
63
98
  }
64
- this.#ownerDocument = null;
65
- this.#onDismiss = null;
66
- this.#claims = null;
67
- }
68
- /**
69
- * Whether this active layer would own a press right now: it is the topmost
70
- * layer whose {@link EscapeLayerOptions.claims} passes. Exposed for tests
71
- * and diagnostics — production dismissal goes through the shared listener.
72
- */
99
+ this.#active = null;
100
+ }
73
101
  get ownsEscape() {
74
- const ownerDocument = this.#ownerDocument;
75
- if (!ownerDocument) return false;
76
- const registry = _EscapeLayer.#registries.get(ownerDocument);
102
+ const active = this.#active;
103
+ if (!active) return false;
104
+ const registry = _EscapeLayer.#registries.get(active.document);
77
105
  if (!registry) return false;
78
- return _EscapeLayer.#resolveOwner(registry.stack) === this;
106
+ return _EscapeLayer.#resolveOwner(registry.stack) === active.entry;
79
107
  }
80
- /** Builds a document's registry with its shared resolver listener. */
81
108
  static #createRegistry() {
82
109
  const registry = {
83
110
  stack: [],
@@ -86,66 +113,106 @@ var EscapeLayer = class _EscapeLayer {
86
113
  const owner = _EscapeLayer.#resolveOwner(registry.stack);
87
114
  if (!owner) return;
88
115
  event.preventDefault();
89
- owner.#onDismiss?.();
116
+ owner.onDismiss();
90
117
  }
91
118
  };
92
119
  return registry;
93
120
  }
94
- /** The topmost stack layer whose claims predicate passes, or `null`. */
95
121
  static #resolveOwner(stack) {
96
122
  for (let index = stack.length - 1; index >= 0; index--) {
97
123
  const layer = stack[index];
98
124
  if (!layer) continue;
99
- if (layer.#claims && !layer.#claims()) continue;
125
+ if (layer.claims && !layer.claims()) continue;
100
126
  return layer;
101
127
  }
102
128
  return null;
103
129
  }
104
130
  };
105
131
 
106
- // src/utils/event_owner.ts
107
132
  function ownerIndex(candidates, node) {
108
133
  if (!(node instanceof Node)) return -1;
109
134
  return candidates.findIndex((candidate) => candidate.contains(node));
110
135
  }
136
+ function ownersOf(candidates, node) {
137
+ if (!(node instanceof Node)) return [];
138
+ return candidates.filter((candidate) => candidate.contains(node));
139
+ }
140
+
141
+ var ListenerSet = class {
142
+ #abort = new AbortController();
143
+ add(target, type, handler, options) {
144
+ target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
145
+ }
146
+ dispose() {
147
+ this.#abort.abort();
148
+ this.#abort = new AbortController();
149
+ }
150
+ };
151
+
152
+ var MAX_TIMER_DELAY_MS = 2147483647;
153
+
154
+ var NUMBER_BOUNDS = {
155
+ timer: { finite: true, min: 0, max: MAX_TIMER_DELAY_MS }};
156
+ function matchesNumberBounds(value, bounds) {
157
+ if (!Number.isFinite(value)) {
158
+ const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
159
+ if (direction === null) return false;
160
+ if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
161
+ }
162
+ if (bounds.min !== void 0 && value < bounds.min) return false;
163
+ if (bounds.max !== void 0 && value > bounds.max) return false;
164
+ if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
165
+ if (bounds.integer && !Number.isInteger(value)) return false;
166
+ if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
167
+ return true;
168
+ }
169
+
170
+ function readNumber(raw, fallback, bounds) {
171
+ return matchesNumberBounds(raw, bounds) ? raw : fallback;
172
+ }
173
+
174
+ var NumberValueReader = class {
175
+ #lastRejected = /* @__PURE__ */ new Map();
176
+ read(owner, name, raw, fallback, bounds) {
177
+ const resolved = readNumber(raw, fallback, bounds);
178
+ if (matchesNumberBounds(raw, bounds)) {
179
+ this.#lastRejected.delete(name);
180
+ return resolved;
181
+ }
182
+ const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
183
+ const literal = owner.element.getAttribute(attribute);
184
+ if (literal === null) {
185
+ this.#lastRejected.delete(name);
186
+ return resolved;
187
+ }
188
+ if (this.#lastRejected.get(name) !== literal) {
189
+ this.#lastRejected.set(name, literal);
190
+ console.warn(
191
+ `Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
192
+ );
193
+ }
194
+ return resolved;
195
+ }
196
+ };
111
197
 
112
- // src/utils/safe_timeout.ts
113
198
  var TimerRegistry = class {
114
- /** Live timer ids that have not yet been cleared (or, for timeouts, fired). */
115
199
  ids = /* @__PURE__ */ new Set();
116
- /**
117
- * Cancels a single tracked timer.
118
- *
119
- * No-ops if the id is unknown (already cleared, fired, or never owned by this
120
- * registry), so callers can clear defensively without guarding.
121
- */
122
200
  clear(id) {
123
201
  if (this.ids.delete(id)) {
124
202
  this.cancel(id);
125
203
  }
126
204
  }
127
- /**
128
- * Cancels every tracked timer. Call this from a controller's `disconnect()`
129
- * to guarantee no timer outlives the element.
130
- */
131
205
  clearAll() {
132
206
  for (const id of this.ids) {
133
207
  this.cancel(id);
134
208
  }
135
209
  this.ids.clear();
136
210
  }
137
- /** Number of timers currently tracked (pending). */
138
211
  get size() {
139
212
  return this.ids.size;
140
213
  }
141
214
  };
142
215
  var SafeTimeout = class extends TimerRegistry {
143
- /**
144
- * Schedules `callback` after `delay` ms and returns the timer id.
145
- *
146
- * The id is removed from the registry automatically when the timeout fires,
147
- * so {@link TimerRegistry.size | size} reflects only still-pending timers.
148
- */
149
216
  set(callback, delay) {
150
217
  const id = this.schedule(() => {
151
218
  this.ids.delete(id);
@@ -162,137 +229,84 @@ var SafeTimeout = class extends TimerRegistry {
162
229
  }
163
230
  };
164
231
 
165
- // src/utils/state_reason.ts
166
- var FOCUS_EVENTS = /* @__PURE__ */ new Set(["blur", "focus", "focusin", "focusout"]);
167
- var POINTER_EVENTS = /* @__PURE__ */ new Set(["mouseenter", "mouseleave", "pointerenter", "pointerleave"]);
168
- function stateReasonFor(event) {
169
- if (!event) return "api";
170
- if (FOCUS_EVENTS.has(event.type)) return "focus";
171
- if (POINTER_EVENTS.has(event.type)) return "pointer";
172
- return "user";
232
+ function targetSelector(identifier, name) {
233
+ return `[data-${identifier}-target~="${name}"]`;
173
234
  }
174
235
 
175
- // src/controllers/navigation_menu_controller.ts
176
- var NavigationMenuController = class extends Controller {
177
- static targets = ["trigger", "panel", "hoverArea"];
236
+ var NavigationMenuController = class _NavigationMenuController extends Controller {
237
+ #numbers = new NumberValueReader();
238
+ static targets = ["trigger", "panel", "hoverArea", "list"];
178
239
  static values = {
179
240
  openOnHover: { type: Boolean, default: false },
180
241
  hoverDelay: { type: Number, default: 150 }
181
242
  };
243
+ static valueConstraints = {
244
+ hoverDelay: NUMBER_BOUNDS.timer
245
+ };
182
246
  static actions = ["onTriggerKeydown", "toggle"];
183
247
  static events = ["close", "open"];
184
- /** Open/close delay timers for hover mode; cleared together on disconnect. */
185
248
  #hoverTimers = new SafeTimeout();
186
- /**
187
- * Elements currently carrying hover listeners. Removal always mirrors this
188
- * set (not a recomputed target snapshot), so a rebuild driven by target churn
189
- * can never strand a listener on an element that stopped being a target.
190
- */
191
- #hoverWired = /* @__PURE__ */ new Set();
192
- /**
193
- * The trigger whose hover region the pointer currently occupies, if any. Only
194
- * the click semantics under `openOnHover` read it (see {@link toggle}).
195
- */
249
+ #hoverListeners = new ListenerSet();
196
250
  #hoveredTrigger = null;
197
- /**
198
- * Whether the controller is between `connect()` and `disconnect()`. Stimulus
199
- * disconnects the controller **before** disconnecting its targets, so the
200
- * target callbacks below also fire during teardown; without this flag their
201
- * rebuild would re-wire hover listeners onto an already-disconnected nav.
202
- */
203
251
  #connected = false;
204
- /** Whether state moves are reported: set once `connect()` settled the baseline. */
205
252
  #reporting = false;
206
- /** Escape-stack membership while any panel is open; the shared resolver dismisses via it. */
207
253
  #escapeLayer = new EscapeLayer();
208
- /** Establishes the closed baseline and the dismissal listeners. */
254
+ #listeners = new ListenerSet();
255
+ #clickVerdicts = new ClickVerdicts();
209
256
  connect() {
210
257
  this.#connected = true;
211
258
  this.#closeAll("api");
212
- document.addEventListener("click", this.#onOutsideClick, true);
213
- this.element.addEventListener("focusout", this.#onFocusOut);
259
+ this.#listeners.add(this.element, "click", this.#onClickCapture, { capture: true });
260
+ this.#listeners.add(document, "click", this.#onOutsideClick, { capture: true });
261
+ this.#listeners.add(this.element, "focusout", this.#onFocusOut);
214
262
  if (this.openOnHoverValue) this.#addHoverListeners();
215
263
  this.#reporting = true;
216
264
  }
217
- /** Removes every listener, pending hover timer, and stack membership taken while connected. */
218
265
  disconnect() {
219
266
  this.#connected = false;
220
267
  this.#reporting = false;
221
268
  this.#escapeLayer.deactivate();
222
- document.removeEventListener("click", this.#onOutsideClick, true);
223
- this.element.removeEventListener("focusout", this.#onFocusOut);
269
+ this.#listeners.dispose();
224
270
  this.#removeHoverListeners();
225
271
  this.#hoverTimers.clearAll();
226
272
  }
227
- /**
228
- * Follows a runtime flip of `openOnHover` (a morph or a scripted attribute
229
- * change) by wiring or unwiring the hover listeners in place. Stimulus also
230
- * fires this once before `connect()`, which the `#connected` guard skips —
231
- * `connect()` owns the initial wiring.
232
- */
233
273
  openOnHoverValueChanged() {
234
274
  if (!this.#connected) return;
235
275
  if (this.openOnHoverValue) this.#addHoverListeners();
236
276
  else this.#removeHoverListeners();
237
277
  }
238
- /** Re-wires hover listeners when a target is added after connect (Turbo Streams etc.). */
239
278
  triggerTargetConnected() {
240
279
  this.#rewireHoverListeners();
241
280
  }
242
- /** Re-wires hover listeners when a target is removed while connected. */
243
281
  triggerTargetDisconnected() {
244
282
  this.#rewireHoverListeners();
245
283
  }
246
- /** See {@link NavigationMenuController.triggerTargetConnected}. */
247
284
  panelTargetConnected() {
248
285
  this.#rewireHoverListeners();
249
286
  }
250
- /** See {@link NavigationMenuController.triggerTargetDisconnected}. */
251
287
  panelTargetDisconnected() {
252
288
  this.#rewireHoverListeners();
253
289
  }
254
- /** See {@link NavigationMenuController.triggerTargetConnected}. */
255
290
  hoverAreaTargetConnected() {
256
291
  this.#rewireHoverListeners();
257
292
  }
258
- /** See {@link NavigationMenuController.triggerTargetDisconnected}. */
259
293
  hoverAreaTargetDisconnected() {
260
294
  this.#rewireHoverListeners();
261
295
  }
262
- /**
263
- * Toggles a trigger's panel (single-open). Bound via `data-action` (click).
264
- *
265
- * An explicit activation always wins over a scheduled hover open/close, so any
266
- * pending hover timer is dropped first — otherwise a close scheduled just
267
- * before the click would fire into the freshly opened panel.
268
- *
269
- * Under `openOnHover`, closing a panel the pointer itself is holding open is
270
- * refused: the pointer stays put, `mouseenter` does not re-fire, and the panel
271
- * would be unopenable until the pointer left and came back. Keyboard
272
- * activation (pointer elsewhere) still toggles it closed, and `Escape` and
273
- * outside clicks dismiss either way.
274
- */
275
- toggle(event) {
276
- const trigger = event.currentTarget;
277
- const reason = stateReasonFor(event);
296
+ toggle(source) {
297
+ const { event, host, origin, reason } = actionSource(source);
298
+ const trigger = host?.closest(targetSelector(this.identifier, "trigger"));
299
+ if (!trigger || !this.triggerTargets.includes(trigger)) return;
300
+ if (origin && origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
301
+ return;
278
302
  this.#hoverTimers.clearAll();
279
303
  if (!this.#isExpanded(trigger)) {
280
- this.#openPanel(trigger, reason);
304
+ if (!this.#refuses(trigger, event, origin)) this.#openPanel(trigger, reason);
281
305
  return;
282
306
  }
283
307
  if (this.openOnHoverValue && this.#hoveredTrigger === trigger) return;
284
308
  this.#closePanel(trigger, reason);
285
309
  }
286
- /**
287
- * `ArrowLeft`/`ArrowRight` move focus between triggers (keeping Tab order).
288
- *
289
- * Only navigable triggers are destinations, so a `hidden` or disabled item
290
- * never swallows the press. A modified arrow belongs to the browser or OS
291
- * (`Alt+←`/`Alt+→` is history back/forward), and with no destination at all
292
- * (a single-trigger nav) the press is left to the page — `preventDefault()` is
293
- * called only when focus actually moves, keeping the scroll-suppression
294
- * contract honest.
295
- */
296
310
  onTriggerKeydown(event) {
297
311
  if (event.defaultPrevented) return;
298
312
  if (event.key !== "ArrowRight" && event.key !== "ArrowLeft") return;
@@ -304,17 +318,11 @@ var NavigationMenuController = class extends Controller {
304
318
  const index = triggers.indexOf(current);
305
319
  if (index === -1 || triggers.length < 2) return;
306
320
  event.preventDefault();
307
- const forward = isRtl(this.element) ? "ArrowLeft" : "ArrowRight";
321
+ const forward = isRtl(this.hasListTarget ? this.listTarget : this.element) ? "ArrowLeft" : "ArrowRight";
308
322
  const step = event.key === forward ? 1 : -1;
309
323
  triggers[(index + step + triggers.length) % triggers.length]?.focus();
310
324
  }
311
- /**
312
- * Opens `trigger`'s panel, closing any other open panel first. Re-opening an
313
- * already-open panel only re-asserts the Escape layer: the close/open
314
- * round-trip would rewrite `aria-expanded` and `hidden` for no state change.
315
- */
316
325
  #openPanel(trigger, reason) {
317
- if (trigger.getAttribute("aria-disabled") === "true") return;
318
326
  if (this.#isExpanded(trigger)) {
319
327
  this.#syncEscapeLayer();
320
328
  return;
@@ -327,7 +335,6 @@ var NavigationMenuController = class extends Controller {
327
335
  this.#report("open", trigger, panel, reason);
328
336
  this.#syncEscapeLayer();
329
337
  }
330
- /** Closes `trigger`'s panel, reflects the collapsed state, and reports a move. */
331
338
  #closePanel(trigger, reason) {
332
339
  const was = this.#isExpanded(trigger);
333
340
  const panel = this.#panelFor(trigger);
@@ -336,15 +343,9 @@ var NavigationMenuController = class extends Controller {
336
343
  if (was && panel) this.#report("close", trigger, panel, reason);
337
344
  this.#syncEscapeLayer();
338
345
  }
339
- /**
340
- * Closes every open panel and normalizes `aria-expanded="false"` on **all**
341
- * triggers, so a trigger whose panel was hidden by other means cannot keep
342
- * advertising an open panel.
343
- */
344
346
  #closeAll(reason) {
345
347
  for (const trigger of this.triggerTargets) this.#closePanel(trigger, reason);
346
348
  }
347
- /** Names the panel that moved, so a subscriber does not have to re-derive it. */
348
349
  #report(name, trigger, panel, reason) {
349
350
  if (!this.#reporting) return;
350
351
  const index = this.triggerTargets.indexOf(trigger);
@@ -352,27 +353,20 @@ var NavigationMenuController = class extends Controller {
352
353
  if (name === "open") this.dispatch("open", { detail, cancelable: false });
353
354
  else this.dispatch("close", { detail, cancelable: false });
354
355
  }
355
- /**
356
- * Whether a trigger can receive arrow-key focus: not `hidden`, not a natively
357
- * `disabled` control (mirrors `toolbar`). CSS-only visibility cannot be
358
- * detected headlessly and stays the consumer's responsibility.
359
- *
360
- * **`aria-disabled="true"` stays reachable.** APG separates the attributes by
361
- * intent: `disabled` is for controls a neighbour makes inferable, while
362
- * `aria-disabled` marks one that must remain *discoverable* — and a nav
363
- * section the user cannot even arrow to is a section they cannot learn exists.
364
- * Opening is suppressed separately, so the trigger announces itself and does
365
- * nothing.
366
- */
367
356
  #isNavigable(trigger) {
368
357
  if (trigger.hasAttribute("hidden")) return false;
369
358
  return !trigger.disabled;
370
359
  }
371
- /**
372
- * Aligns Escape-stack membership with the open state: joins (or re-asserts to
373
- * the top) while a panel is open, leaves once none is. Re-asserting when the
374
- * user switches panels is deliberate — the nav is again the newest layer.
375
- */
360
+ #isActivationDisabled(trigger) {
361
+ return !this.#isNavigable(trigger) || isAriaDisabled(trigger);
362
+ }
363
+ #refuses(trigger, event, origin) {
364
+ const recorded = this.#clickVerdicts.for(event, trigger);
365
+ if (recorded !== void 0) return recorded;
366
+ if (this.#isActivationDisabled(trigger)) return true;
367
+ const target = event ? event.target : origin;
368
+ return ownersOf(this.triggerTargets, target).some((owner) => this.#isActivationDisabled(owner));
369
+ }
376
370
  #syncEscapeLayer() {
377
371
  if (this.#isAnyOpen) {
378
372
  this.#escapeLayer.activate(document, {
@@ -383,11 +377,6 @@ var NavigationMenuController = class extends Controller {
383
377
  this.#escapeLayer.deactivate();
384
378
  }
385
379
  }
386
- /**
387
- * Closes any open panel and returns focus to its trigger (Escape path). A
388
- * pending hover open would otherwise re-open the panel the user just
389
- * dismissed, so the reservation is dropped with it.
390
- */
391
380
  #closeAndRestore() {
392
381
  const open = this.#openTrigger;
393
382
  if (!open) return;
@@ -395,113 +384,65 @@ var NavigationMenuController = class extends Controller {
395
384
  this.#closePanel(open, "escape");
396
385
  open.focus();
397
386
  }
398
- /** Closes panels when a click lands outside the nav element. */
387
+ #onClickCapture = (event) => {
388
+ const triggers = ownersOf(this.triggerTargets, event.target);
389
+ this.#clickVerdicts.record(
390
+ event,
391
+ triggers,
392
+ triggers.some((trigger) => this.#isActivationDisabled(trigger))
393
+ );
394
+ };
399
395
  #onOutsideClick = (event) => {
400
396
  if (this.#isAnyOpen && !this.element.contains(event.target)) this.#closeAll("outside");
401
397
  };
402
- /**
403
- * Closes (without restoring focus) when focus leaves the nav for a known
404
- * external destination. A null/non-Node destination is indeterminate:
405
- * browsers use it for clicks on non-focusable content and for window
406
- * deactivation, so those never close the nav here (matching the popover
407
- * convention) — the outside-click handler decides pointer dismissal, and the
408
- * Escape stack's body-focus claim keeps keyboard dismissal working.
409
- */
410
398
  #onFocusOut = (event) => {
411
399
  const next = event.relatedTarget;
412
400
  if (!(next instanceof Node) || this.element.contains(next)) return;
413
401
  this.#closeAll("focus");
414
402
  };
415
- /**
416
- * Opens a trigger's panel after the hover delay (hover mode).
417
- *
418
- * @stimeoRuntimeOnly `hoverDelay` is the delay of the one hover timer this entry arms.
419
- */
420
403
  #onPointerEnter = (event) => {
421
404
  const trigger = this.#triggerForHover(event.currentTarget);
422
405
  if (!trigger) return;
423
406
  this.#hoveredTrigger = trigger;
424
407
  this.#hoverTimers.clearAll();
425
- this.#hoverTimers.set(() => this.#openFromHover(trigger), this.hoverDelayValue);
408
+ this.#hoverTimers.set(() => this.#openFromHover(trigger), this.#safeHoverDelay);
426
409
  };
427
- /**
428
- * Closes the open panel after the hover delay (hover mode) — unless the
429
- * pointer moved directly into another part of the hover region (an adjacent
430
- * trigger, panel, or hoverArea): crossing a shared edge must not schedule a
431
- * spurious close, e.g. a hoverArea whose panel sits outside it as a sibling.
432
- *
433
- * @stimeoRuntimeOnly `hoverDelay` is the delay of the one hover timer this exit arms.
434
- */
435
410
  #onPointerLeave = (event) => {
436
411
  const next = event instanceof MouseEvent ? event.relatedTarget : null;
437
412
  if (ownerIndex(this.#hoverElements, next) !== -1) return;
438
413
  this.#hoveredTrigger = null;
439
414
  this.#hoverTimers.clearAll();
440
- this.#hoverTimers.set(() => this.#closeFromHover(), this.hoverDelayValue);
415
+ this.#hoverTimers.set(() => this.#closeFromHover(), this.#safeHoverDelay);
441
416
  };
442
- /**
443
- * Hover-driven open. Switching panels would hide the outgoing one, so focus
444
- * standing inside it is handed back to its trigger first: pointer movement
445
- * must never send focus to the body (the keyboard user would lose their
446
- * place). Focus outside the closing panel is left alone.
447
- */
448
417
  #openFromHover(trigger) {
449
- if (trigger.getAttribute("aria-disabled") === "true") return;
418
+ if (this.#refuses(trigger, null, trigger)) return;
450
419
  const open = this.#openTrigger;
451
420
  if (open && open !== trigger && this.#holdsFocus(this.#panelFor(open))) open.focus();
452
421
  this.#openPanel(trigger, "pointer");
453
422
  }
454
- /**
455
- * Hover-driven close: skipped while focus sits inside the panel it would hide,
456
- * for the same no-focus-loss reason (cf. `hover_card`'s delayed close). The
457
- * panel then stays open until `Escape`, an outside click, or focus leaving the
458
- * nav dismisses it — all of which restore or keep focus deliberately.
459
- */
460
423
  #closeFromHover() {
461
424
  const open = this.#openTrigger;
462
425
  if (open && this.#holdsFocus(this.#panelFor(open))) return;
463
426
  this.#closeAll("pointer");
464
427
  }
465
- /** Whether `panel` exists and currently contains the focused element. */
466
428
  #holdsFocus(panel) {
467
429
  return panel?.contains(document.activeElement) ?? false;
468
430
  }
469
- /** Wires hover open/close on each hover element (opt-in), tracking what was wired. */
470
431
  #addHoverListeners() {
471
432
  for (const element of this.#hoverElements) {
472
- element.addEventListener("mouseenter", this.#onPointerEnter);
473
- element.addEventListener("mouseleave", this.#onPointerLeave);
474
- this.#hoverWired.add(element);
433
+ this.#hoverListeners.add(element, "mouseenter", this.#onPointerEnter);
434
+ this.#hoverListeners.add(element, "mouseleave", this.#onPointerLeave);
475
435
  }
476
436
  }
477
- /** Removes the hover listeners from exactly the elements that were wired. */
478
437
  #removeHoverListeners() {
479
- for (const element of this.#hoverWired) {
480
- element.removeEventListener("mouseenter", this.#onPointerEnter);
481
- element.removeEventListener("mouseleave", this.#onPointerLeave);
482
- }
483
- this.#hoverWired.clear();
438
+ this.#hoverListeners.dispose();
484
439
  this.#hoveredTrigger = null;
485
440
  }
486
- /**
487
- * Rebuilds the hover wiring from the current targets. Target callbacks call
488
- * this so items added or removed after connect (e.g. a Turbo Stream append)
489
- * participate in hover; the wired-set removal keeps the rebuild symmetric.
490
- * Stimulus fires those callbacks during teardown too, hence the `#connected`
491
- * guard — a rebuild after `disconnect()` would resurrect the listeners.
492
- */
493
441
  #rewireHoverListeners() {
494
442
  if (!this.#connected || !this.openOnHoverValue) return;
495
443
  this.#removeHoverListeners();
496
444
  this.#addHoverListeners();
497
445
  }
498
- /**
499
- * Elements that participate in hover: each `hoverArea`, plus every trigger and
500
- * panel **not** wrapped by one. A wrapped trigger/panel must defer to its
501
- * wrapper — its own mouseleave would otherwise schedule a close while the
502
- * pointer is still inside the area (mouseenter does not re-fire on the wrapper
503
- * when moving among its descendants), flickering the panel shut.
504
- */
505
446
  get #hoverElements() {
506
447
  const areas = this.hoverAreaTargets;
507
448
  const covered = (element) => ownerIndex(areas, element) !== -1;
@@ -511,11 +452,6 @@ var NavigationMenuController = class extends Controller {
511
452
  ...this.panelTargets.filter((panel) => !covered(panel))
512
453
  ];
513
454
  }
514
- /**
515
- * Resolves the trigger for a hovered element: the trigger itself, the trigger
516
- * controlling a hovered panel, or the first trigger contained in a hovered
517
- * `hoverArea` wrapper.
518
- */
519
455
  #triggerForHover(element) {
520
456
  if (this.triggerTargets.includes(element)) return element;
521
457
  if (this.panelTargets.includes(element)) {
@@ -526,25 +462,29 @@ var NavigationMenuController = class extends Controller {
526
462
  }
527
463
  return null;
528
464
  }
529
- /** The panel controlled by `trigger` (matched by `aria-controls`/`id`). */
530
465
  #panelFor(trigger) {
531
466
  const id = trigger.getAttribute("aria-controls");
532
467
  return id ? this.panelTargets.find((panel) => panel.id === id) ?? null : null;
533
468
  }
534
- /** Whether `trigger`'s panel is currently expanded. */
535
469
  #isExpanded(trigger) {
536
470
  return trigger.getAttribute("aria-expanded") === "true";
537
471
  }
538
- /** The trigger whose panel is currently open, if any. */
539
472
  get #openTrigger() {
540
473
  return this.triggerTargets.find((trigger) => this.#isExpanded(trigger)) ?? null;
541
474
  }
542
- /** Whether any panel is currently open. */
543
475
  get #isAnyOpen() {
544
476
  return this.#openTrigger !== null;
545
477
  }
478
+ get #safeHoverDelay() {
479
+ return this.#numbers.read(
480
+ this,
481
+ "hoverDelay",
482
+ this.hoverDelayValue,
483
+ _NavigationMenuController.values.hoverDelay.default,
484
+ _NavigationMenuController.valueConstraints.hoverDelay
485
+ );
486
+ }
546
487
  };
547
488
 
548
489
  export { NavigationMenuController };
549
- //# sourceMappingURL=navigation_menu_controller.js.map
550
490
  //# sourceMappingURL=navigation_menu_controller.js.map