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 +1 @@
1
- {"version":3,"sources":["../../src/utils/logical_scroll.ts","../../src/utils/arrow_step.ts","../../src/utils/escape_layer.ts","../../src/utils/event_owner.ts","../../src/utils/safe_timeout.ts","../../src/utils/state_reason.ts","../../src/controllers/navigation_menu_controller.ts"],"names":[],"mappings":";;;;;AAiBO,SAAS,MAAM,OAAA,EAA2B;AAC/C,EAAA,OAAO,MAAA,CAAO,gBAAA,CAAiB,OAAO,CAAA,CAAE,SAAA,KAAc,KAAA;AACxD;;;AC4EO,SAAS,oBAAA,CACd,KAAA,EACA,KAAA,GAAkC,EAAC,EAC1B;AACT,EAAA,IAAI,CAAC,KAAA,CAAM,GAAA,CAAI,UAAA,CAAW,OAAO,GAAG,OAAO,KAAA;AAC3C,EAAA,OACG,KAAA,CAAM,MAAA,IAAU,CAAC,KAAA,CAAM,QAAA,CAAS,KAAK,CAAA,IACrC,KAAA,CAAM,OAAA,IAAW,CAAC,KAAA,CAAM,QAAA,CAAS,MAAM,KACvC,KAAA,CAAM,OAAA,IAAW,CAAC,KAAA,CAAM,QAAA,CAAS,MAAM,CAAA,IACvC,KAAA,CAAM,QAAA,IAAY,CAAC,KAAA,CAAM,QAAA,CAAS,OAAO,CAAA;AAE9C;;;AC1CO,SAAS,uBAAuB,OAAA,EAAiC;AACtE,EAAA,OAAO,MAAM;AACX,IAAA,MAAM,MAAA,GAAS,QAAQ,aAAA,CAAc,aAAA;AACrC,IAAA,OAAO,MAAA,KAAW,QAAQ,MAAA,KAAW,OAAA,CAAQ,cAAc,IAAA,IAAQ,OAAA,CAAQ,SAAS,MAAM,CAAA;AAAA,EAC5F,CAAA;AACF;AAEO,IAAM,WAAA,GAAN,MAAM,YAAA,CAAY;AAAA,EACvB,OAAgB,WAAA,mBAAc,IAAI,OAAA,EAAuC;AAAA,EAEzE,cAAA,GAAkC,IAAA;AAAA;AAAA,EAElC,UAAA,GAAkC,IAAA;AAAA;AAAA,EAElC,OAAA,GAAkC,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOlC,QAAA,CAAS,aAAA,GAA0B,QAAA,EAAU,OAAA,EAAmC;AAC9E,IAAA,IAAA,CAAK,UAAA,EAAW;AAChB,IAAA,IAAI,QAAA,GAAW,YAAA,CAAY,WAAA,CAAY,GAAA,CAAI,aAAa,CAAA;AACxD,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,QAAA,GAAW,aAAY,eAAA,EAAgB;AACvC,MAAA,YAAA,CAAY,WAAA,CAAY,GAAA,CAAI,aAAA,EAAe,QAAQ,CAAA;AACnD,MAAA,aAAA,CAAc,gBAAA,CAAiB,SAAA,EAAW,QAAA,CAAS,SAAS,CAAA;AAAA,IAC9D;AACA,IAAA,QAAA,CAAS,KAAA,CAAM,KAAK,IAAI,CAAA;AACxB,IAAA,IAAA,CAAK,cAAA,GAAiB,aAAA;AACtB,IAAA,IAAA,CAAK,aAAa,OAAA,CAAQ,SAAA;AAC1B,IAAA,IAAA,CAAK,OAAA,GAAU,QAAQ,MAAA,IAAU,IAAA;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAA,GAAmB;AACjB,IAAA,MAAM,gBAAgB,IAAA,CAAK,cAAA;AAC3B,IAAA,IAAI,CAAC,aAAA,EAAe;AAEpB,IAAA,MAAM,QAAA,GAAW,YAAA,CAAY,WAAA,CAAY,GAAA,CAAI,aAAa,CAAA;AAC1D,IAAA,IAAI,QAAA,EAAU;AACZ,MAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,KAAA,CAAM,WAAA,CAAY,IAAI,CAAA;AAC7C,MAAA,IAAI,SAAS,CAAA,EAAG,QAAA,CAAS,KAAA,CAAM,MAAA,CAAO,OAAO,CAAC,CAAA;AAC9C,MAAA,IAAI,QAAA,CAAS,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG;AAC/B,QAAA,aAAA,CAAc,mBAAA,CAAoB,SAAA,EAAW,QAAA,CAAS,SAAS,CAAA;AAC/D,QAAA,YAAA,CAAY,WAAA,CAAY,OAAO,aAAa,CAAA;AAAA,MAC9C;AAAA,IACF;AACA,IAAA,IAAA,CAAK,cAAA,GAAiB,IAAA;AACtB,IAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAClB,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,UAAA,GAAsB;AACxB,IAAA,MAAM,gBAAgB,IAAA,CAAK,cAAA;AAC3B,IAAA,IAAI,CAAC,eAAe,OAAO,KAAA;AAC3B,IAAA,MAAM,QAAA,GAAW,YAAA,CAAY,WAAA,CAAY,GAAA,CAAI,aAAa,CAAA;AAC1D,IAAA,IAAI,CAAC,UAAU,OAAO,KAAA;AACtB,IAAA,OAAO,YAAA,CAAY,aAAA,CAAc,QAAA,CAAS,KAAK,CAAA,KAAM,IAAA;AAAA,EACvD;AAAA;AAAA,EAGA,OAAO,eAAA,GAAuC;AAC5C,IAAA,MAAM,QAAA,GAAgC;AAAA,MACpC,OAAO,EAAC;AAAA,MACR,SAAA,EAAW,CAAC,KAAA,KAA+B;AACzC,QAAA,IAAI,MAAM,GAAA,KAAQ,QAAA,IAAY,KAAA,CAAM,gBAAA,IAAoB,MAAM,WAAA,EAAa;AAC3E,QAAA,MAAM,KAAA,GAAQ,YAAA,CAAY,aAAA,CAAc,QAAA,CAAS,KAAK,CAAA;AACtD,QAAA,IAAI,CAAC,KAAA,EAAO;AACZ,QAAA,KAAA,CAAM,cAAA,EAAe;AACrB,QAAA,KAAA,CAAM,UAAA,IAAa;AAAA,MACrB;AAAA,KACF;AACA,IAAA,OAAO,QAAA;AAAA,EACT;AAAA;AAAA,EAGA,OAAO,cAAc,KAAA,EAA0C;AAC7D,IAAA,KAAA,IAAS,QAAQ,KAAA,CAAM,MAAA,GAAS,CAAA,EAAG,KAAA,IAAS,GAAG,KAAA,EAAA,EAAS;AACtD,MAAA,MAAM,KAAA,GAAQ,MAAM,KAAK,CAAA;AACzB,MAAA,IAAI,CAAC,KAAA,EAAO;AACZ,MAAA,IAAI,KAAA,CAAM,OAAA,IAAW,CAAC,KAAA,CAAM,SAAQ,EAAG;AACvC,MAAA,OAAO,KAAA;AAAA,IACT;AACA,IAAA,OAAO,IAAA;AAAA,EACT;AACF,CAAA;;;AC/HO,SAAS,UAAA,CACd,YACA,IAAA,EACQ;AACR,EAAA,IAAI,EAAE,IAAA,YAAgB,IAAA,CAAA,EAAO,OAAO,EAAA;AACpC,EAAA,OAAO,WAAW,SAAA,CAAU,CAAC,cAAc,SAAA,CAAU,QAAA,CAAS,IAAI,CAAC,CAAA;AACrE;;;ACNA,IAAe,gBAAf,MAA6B;AAAA;AAAA,EAER,GAAA,uBAAU,GAAA,EAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAczC,MAAM,EAAA,EAAkB;AACtB,IAAA,IAAI,IAAA,CAAK,GAAA,CAAI,MAAA,CAAO,EAAE,CAAA,EAAG;AACvB,MAAA,IAAA,CAAK,OAAO,EAAE,CAAA;AAAA,IAChB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,QAAA,GAAiB;AACf,IAAA,KAAA,MAAW,EAAA,IAAM,KAAK,GAAA,EAAK;AACzB,MAAA,IAAA,CAAK,OAAO,EAAE,CAAA;AAAA,IAChB;AACA,IAAA,IAAA,CAAK,IAAI,KAAA,EAAM;AAAA,EACjB;AAAA;AAAA,EAGA,IAAI,IAAA,GAAe;AACjB,IAAA,OAAO,KAAK,GAAA,CAAI,IAAA;AAAA,EAClB;AACF,CAAA;AAmBO,IAAM,WAAA,GAAN,cAA0B,aAAA,CAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO7C,GAAA,CAAI,UAAsB,KAAA,EAAuB;AAC/C,IAAA,MAAM,EAAA,GAAK,IAAA,CAAK,QAAA,CAAS,MAAM;AAC7B,MAAA,IAAA,CAAK,GAAA,CAAI,OAAO,EAAE,CAAA;AAClB,MAAA,QAAA,EAAS;AAAA,IACX,GAAG,KAAK,CAAA;AACR,IAAA,IAAA,CAAK,GAAA,CAAI,IAAI,EAAE,CAAA;AACf,IAAA,OAAO,EAAA;AAAA,EACT;AAAA,EAEU,QAAA,CAAS,UAAsB,KAAA,EAAuB;AAC9D,IAAA,OAAO,MAAA,CAAO,UAAA,CAAW,QAAA,EAAU,KAAK,CAAA;AAAA,EAC1C;AAAA,EAEU,OAAO,EAAA,EAAkB;AACjC,IAAA,MAAA,CAAO,aAAa,EAAE,CAAA;AAAA,EACxB;AACF,CAAA;;;AC3EA,IAAM,YAAA,uBAAmB,GAAA,CAAI,CAAC,QAAQ,OAAA,EAAS,SAAA,EAAW,UAAU,CAAC,CAAA;AAGrE,IAAM,cAAA,uBAAqB,GAAA,CAAI,CAAC,cAAc,YAAA,EAAc,cAAA,EAAgB,cAAc,CAAC,CAAA;AA0BpF,SAAS,eAAe,KAAA,EAA4D;AACzF,EAAA,IAAI,CAAC,OAAO,OAAO,KAAA;AACnB,EAAA,IAAI,YAAA,CAAa,GAAA,CAAI,KAAA,CAAM,IAAI,GAAG,OAAO,OAAA;AACzC,EAAA,IAAI,cAAA,CAAe,GAAA,CAAI,KAAA,CAAM,IAAI,GAAG,OAAO,SAAA;AAC3C,EAAA,OAAO,MAAA;AACT;;;ACsBO,IAAM,wBAAA,GAAN,cAAuC,UAAA,CAAwB;AAAA,EACpE,OAAgB,OAAA,GAAU,CAAC,SAAA,EAAW,SAAS,WAAW,CAAA;AAAA,EAC1D,OAAgB,MAAA,GAAS;AAAA,IACvB,WAAA,EAAa,EAAE,IAAA,EAAM,OAAA,EAAS,SAAS,KAAA,EAAM;AAAA,IAC7C,UAAA,EAAY,EAAE,IAAA,EAAM,MAAA,EAAQ,SAAS,GAAA;AAAI,GAC3C;AAAA,EACA,OAAO,OAAA,GAAU,CAAC,kBAAA,EAAoB,QAAQ,CAAA;AAAA,EAC9C,OAAO,MAAA,GAAS,CAAC,OAAA,EAAS,MAAM,CAAA;AAAA;AAAA,EAUvB,YAAA,GAAe,IAAI,WAAA,EAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO/B,WAAA,uBAAkB,GAAA,EAAiB;AAAA;AAAA;AAAA;AAAA;AAAA,EAM5C,eAAA,GAAsC,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQtC,UAAA,GAAa,KAAA;AAAA;AAAA,EAGb,UAAA,GAAa,KAAA;AAAA;AAAA,EAGJ,YAAA,GAAe,IAAI,WAAA,EAAY;AAAA;AAAA,EAG/B,OAAA,GAAgB;AACvB,IAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAClB,IAAA,IAAA,CAAK,UAAU,KAAK,CAAA;AACpB,IAAA,QAAA,CAAS,gBAAA,CAAiB,OAAA,EAAS,IAAA,CAAK,eAAA,EAAiB,IAAI,CAAA;AAC7D,IAAA,IAAA,CAAK,OAAA,CAAQ,gBAAA,CAAiB,UAAA,EAAY,IAAA,CAAK,WAAW,CAAA;AAC1D,IAAA,IAAI,IAAA,CAAK,gBAAA,EAAkB,IAAA,CAAK,kBAAA,EAAmB;AACnD,IAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAAA,EACpB;AAAA;AAAA,EAGS,UAAA,GAAmB;AAC1B,IAAA,IAAA,CAAK,UAAA,GAAa,KAAA;AAClB,IAAA,IAAA,CAAK,UAAA,GAAa,KAAA;AAClB,IAAA,IAAA,CAAK,aAAa,UAAA,EAAW;AAC7B,IAAA,QAAA,CAAS,mBAAA,CAAoB,OAAA,EAAS,IAAA,CAAK,eAAA,EAAiB,IAAI,CAAA;AAChE,IAAA,IAAA,CAAK,OAAA,CAAQ,mBAAA,CAAoB,UAAA,EAAY,IAAA,CAAK,WAAW,CAAA;AAC7D,IAAA,IAAA,CAAK,qBAAA,EAAsB;AAC3B,IAAA,IAAA,CAAK,aAAa,QAAA,EAAS;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,uBAAA,GAAgC;AAC9B,IAAA,IAAI,CAAC,KAAK,UAAA,EAAY;AACtB,IAAA,IAAI,IAAA,CAAK,gBAAA,EAAkB,IAAA,CAAK,kBAAA,EAAmB;AAAA,cACzC,qBAAA,EAAsB;AAAA,EAClC;AAAA;AAAA,EAGA,sBAAA,GAA+B;AAC7B,IAAA,IAAA,CAAK,qBAAA,EAAsB;AAAA,EAC7B;AAAA;AAAA,EAGA,yBAAA,GAAkC;AAChC,IAAA,IAAA,CAAK,qBAAA,EAAsB;AAAA,EAC7B;AAAA;AAAA,EAGA,oBAAA,GAA6B;AAC3B,IAAA,IAAA,CAAK,qBAAA,EAAsB;AAAA,EAC7B;AAAA;AAAA,EAGA,uBAAA,GAAgC;AAC9B,IAAA,IAAA,CAAK,qBAAA,EAAsB;AAAA,EAC7B;AAAA;AAAA,EAGA,wBAAA,GAAiC;AAC/B,IAAA,IAAA,CAAK,qBAAA,EAAsB;AAAA,EAC7B;AAAA;AAAA,EAGA,2BAAA,GAAoC;AAClC,IAAA,IAAA,CAAK,qBAAA,EAAsB;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,OAAO,KAAA,EAAoB;AACzB,IAAA,MAAM,UAAU,KAAA,CAAM,aAAA;AACtB,IAAA,MAAM,MAAA,GAAS,eAAe,KAAK,CAAA;AACnC,IAAA,IAAA,CAAK,aAAa,QAAA,EAAS;AAC3B,IAAA,IAAI,CAAC,IAAA,CAAK,WAAA,CAAY,OAAO,CAAA,EAAG;AAC9B,MAAA,IAAA,CAAK,UAAA,CAAW,SAAS,MAAM,CAAA;AAC/B,MAAA;AAAA,IACF;AACA,IAAA,IAAI,IAAA,CAAK,gBAAA,IAAoB,IAAA,CAAK,eAAA,KAAoB,OAAA,EAAS;AAC/D,IAAA,IAAA,CAAK,WAAA,CAAY,SAAS,MAAM,CAAA;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,iBAAiB,KAAA,EAA4B;AAK3C,IAAA,IAAI,MAAM,gBAAA,EAAkB;AAC5B,IAAA,IAAI,KAAA,CAAM,GAAA,KAAQ,YAAA,IAAgB,KAAA,CAAM,QAAQ,WAAA,EAAa;AAC7D,IAAA,IAAI,oBAAA,CAAqB,KAAK,CAAA,EAAG;AAEjC,IAAA,MAAM,UAAU,KAAA,CAAM,aAAA;AAGtB,IAAA,MAAM,QAAA,GAAW,KAAK,cAAA,CAAe,MAAA;AAAA,MACnC,CAAC,OAAA,KAAY,OAAA,KAAY,OAAA,IAAW,IAAA,CAAK,aAAa,OAAO;AAAA,KAC/D;AACA,IAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,OAAA,CAAQ,OAAO,CAAA;AACtC,IAAA,IAAI,KAAA,KAAU,EAAA,IAAM,QAAA,CAAS,MAAA,GAAS,CAAA,EAAG;AAEzC,IAAA,KAAA,CAAM,cAAA,EAAe;AAIrB,IAAA,MAAM,OAAA,GAAU,KAAA,CAAM,IAAA,CAAK,OAAO,IAAI,WAAA,GAAc,YAAA;AACpD,IAAA,MAAM,IAAA,GAAO,KAAA,CAAM,GAAA,KAAQ,OAAA,GAAU,CAAA,GAAI,EAAA;AACzC,IAAA,QAAA,CAAA,CAAU,QAAQ,IAAA,GAAO,QAAA,CAAS,UAAU,QAAA,CAAS,MAAM,GAAG,KAAA,EAAM;AAAA,EACtE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,UAAA,CAAW,SAAsB,MAAA,EAA2B;AAK1D,IAAA,IAAI,OAAA,CAAQ,YAAA,CAAa,eAAe,CAAA,KAAM,MAAA,EAAQ;AACtD,IAAA,IAAI,IAAA,CAAK,WAAA,CAAY,OAAO,CAAA,EAAG;AAC7B,MAAA,IAAA,CAAK,gBAAA,EAAiB;AACtB,MAAA;AAAA,IACF;AACA,IAAA,IAAA,CAAK,UAAU,MAAM,CAAA;AACrB,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,SAAA,CAAU,OAAO,CAAA;AACpC,IAAA,IAAI,CAAC,KAAA,EAAO;AACZ,IAAA,KAAA,CAAM,MAAA,GAAS,KAAA;AACf,IAAA,OAAA,CAAQ,YAAA,CAAa,iBAAiB,MAAM,CAAA;AAC5C,IAAA,IAAA,CAAK,OAAA,CAAQ,MAAA,EAAQ,OAAA,EAAS,KAAA,EAAO,MAAM,CAAA;AAC3C,IAAA,IAAA,CAAK,gBAAA,EAAiB;AAAA,EACxB;AAAA;AAAA,EAGA,WAAA,CAAY,SAAsB,MAAA,EAA2B;AAC3D,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,WAAA,CAAY,OAAO,CAAA;AACpC,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,SAAA,CAAU,OAAO,CAAA;AACpC,IAAA,IAAI,KAAA,QAAa,MAAA,GAAS,IAAA;AAC1B,IAAA,OAAA,CAAQ,YAAA,CAAa,iBAAiB,OAAO,CAAA;AAC7C,IAAA,IAAI,OAAO,KAAA,EAAO,IAAA,CAAK,QAAQ,OAAA,EAAS,OAAA,EAAS,OAAO,MAAM,CAAA;AAC9D,IAAA,IAAA,CAAK,gBAAA,EAAiB;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,UAAU,MAAA,EAA2B;AACnC,IAAA,KAAA,MAAW,WAAW,IAAA,CAAK,cAAA,EAAgB,IAAA,CAAK,WAAA,CAAY,SAAS,MAAM,CAAA;AAAA,EAC7E;AAAA;AAAA,EAGA,OAAA,CACE,IAAA,EACA,OAAA,EACA,KAAA,EACA,MAAA,EACM;AACN,IAAA,IAAI,CAAC,KAAK,UAAA,EAAY;AACtB,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,cAAA,CAAe,OAAA,CAAQ,OAAO,CAAA;AACjD,IAAA,MAAM,MAAA,GAAS,EAAE,MAAA,EAAQ,KAAA,EAAO,KAAA,EAAM;AACtC,IAAA,IAAI,IAAA,KAAS,QAAQ,IAAA,CAAK,QAAA,CAAS,QAAQ,EAAE,MAAA,EAAQ,UAAA,EAAY,KAAA,EAAO,CAAA;AAAA,cAC9D,QAAA,CAAS,OAAA,EAAS,EAAE,MAAA,EAAQ,UAAA,EAAY,OAAO,CAAA;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,aAAa,OAAA,EAA+B;AAC1C,IAAA,IAAI,OAAA,CAAQ,YAAA,CAAa,QAAQ,CAAA,EAAG,OAAO,KAAA;AAC3C,IAAA,OAAO,CAAE,OAAA,CAAiD,QAAA;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,gBAAA,GAAyB;AACvB,IAAA,IAAI,KAAK,UAAA,EAAY;AACnB,MAAA,IAAA,CAAK,YAAA,CAAa,SAAS,QAAA,EAAU;AAAA,QACnC,SAAA,EAAW,MAAM,IAAA,CAAK,gBAAA,EAAiB;AAAA,QACvC,MAAA,EAAQ,sBAAA,CAAuB,IAAA,CAAK,OAAO;AAAA,OAC5C,CAAA;AAAA,IACH,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,aAAa,UAAA,EAAW;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,gBAAA,GAAyB;AACvB,IAAA,MAAM,OAAO,IAAA,CAAK,YAAA;AAClB,IAAA,IAAI,CAAC,IAAA,EAAM;AACX,IAAA,IAAA,CAAK,aAAa,QAAA,EAAS;AAC3B,IAAA,IAAA,CAAK,WAAA,CAAY,MAAM,QAAQ,CAAA;AAC/B,IAAA,IAAA,CAAK,KAAA,EAAM;AAAA,EACb;AAAA;AAAA,EAGS,eAAA,GAAkB,CAAC,KAAA,KAA4B;AACtD,IAAA,IAAI,IAAA,CAAK,UAAA,IAAc,CAAC,IAAA,CAAK,OAAA,CAAQ,QAAA,CAAS,KAAA,CAAM,MAAc,CAAA,EAAG,IAAA,CAAK,SAAA,CAAU,SAAS,CAAA;AAAA,EAC/F,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUS,WAAA,GAAc,CAAC,KAAA,KAA4B;AAClD,IAAA,MAAM,OAAO,KAAA,CAAM,aAAA;AACnB,IAAA,IAAI,EAAE,IAAA,YAAgB,IAAA,CAAA,IAAS,KAAK,OAAA,CAAQ,QAAA,CAAS,IAAI,CAAA,EAAG;AAC5D,IAAA,IAAA,CAAK,UAAU,OAAO,CAAA;AAAA,EACxB,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOS,eAAA,GAAkB,CAAC,KAAA,KAAuB;AACjD,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,gBAAA,CAAiB,KAAA,CAAM,aAA4B,CAAA;AACxE,IAAA,IAAI,CAAC,OAAA,EAAS;AACd,IAAA,IAAA,CAAK,eAAA,GAAkB,OAAA;AACvB,IAAA,IAAA,CAAK,aAAa,QAAA,EAAS;AAC3B,IAAA,IAAA,CAAK,YAAA,CAAa,IAAI,MAAM,IAAA,CAAK,eAAe,OAAO,CAAA,EAAG,KAAK,eAAe,CAAA;AAAA,EAChF,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUS,eAAA,GAAkB,CAAC,KAAA,KAAuB;AACjD,IAAA,MAAM,IAAA,GAAO,KAAA,YAAiB,UAAA,GAAa,KAAA,CAAM,aAAA,GAAgB,IAAA;AACjE,IAAA,IAAI,UAAA,CAAW,IAAA,CAAK,cAAA,EAAgB,IAAI,MAAM,EAAA,EAAI;AAClD,IAAA,IAAA,CAAK,eAAA,GAAkB,IAAA;AACvB,IAAA,IAAA,CAAK,aAAa,QAAA,EAAS;AAC3B,IAAA,IAAA,CAAK,aAAa,GAAA,CAAI,MAAM,KAAK,eAAA,EAAgB,EAAG,KAAK,eAAe,CAAA;AAAA,EAC1E,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,eAAe,OAAA,EAA4B;AAKzC,IAAA,IAAI,OAAA,CAAQ,YAAA,CAAa,eAAe,CAAA,KAAM,MAAA,EAAQ;AACtD,IAAA,MAAM,OAAO,IAAA,CAAK,YAAA;AAClB,IAAA,IAAI,IAAA,IAAQ,IAAA,KAAS,OAAA,IAAW,IAAA,CAAK,WAAA,CAAY,IAAA,CAAK,SAAA,CAAU,IAAI,CAAC,CAAA,EAAG,IAAA,CAAK,KAAA,EAAM;AACnF,IAAA,IAAA,CAAK,UAAA,CAAW,SAAS,SAAS,CAAA;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,eAAA,GAAwB;AACtB,IAAA,MAAM,OAAO,IAAA,CAAK,YAAA;AAClB,IAAA,IAAI,QAAQ,IAAA,CAAK,WAAA,CAAY,KAAK,SAAA,CAAU,IAAI,CAAC,CAAA,EAAG;AACpD,IAAA,IAAA,CAAK,UAAU,SAAS,CAAA;AAAA,EAC1B;AAAA;AAAA,EAGA,YAAY,KAAA,EAAoC;AAC9C,IAAA,OAAO,KAAA,EAAO,QAAA,CAAS,QAAA,CAAS,aAAa,CAAA,IAAK,KAAA;AAAA,EACpD;AAAA;AAAA,EAGA,kBAAA,GAA2B;AACzB,IAAA,KAAA,MAAW,OAAA,IAAW,KAAK,cAAA,EAAgB;AACzC,MAAA,OAAA,CAAQ,gBAAA,CAAiB,YAAA,EAAc,IAAA,CAAK,eAAe,CAAA;AAC3D,MAAA,OAAA,CAAQ,gBAAA,CAAiB,YAAA,EAAc,IAAA,CAAK,eAAe,CAAA;AAC3D,MAAA,IAAA,CAAK,WAAA,CAAY,IAAI,OAAO,CAAA;AAAA,IAC9B;AAAA,EACF;AAAA;AAAA,EAGA,qBAAA,GAA8B;AAC5B,IAAA,KAAA,MAAW,OAAA,IAAW,KAAK,WAAA,EAAa;AACtC,MAAA,OAAA,CAAQ,mBAAA,CAAoB,YAAA,EAAc,IAAA,CAAK,eAAe,CAAA;AAC9D,MAAA,OAAA,CAAQ,mBAAA,CAAoB,YAAA,EAAc,IAAA,CAAK,eAAe,CAAA;AAAA,IAChE;AACA,IAAA,IAAA,CAAK,YAAY,KAAA,EAAM;AACvB,IAAA,IAAA,CAAK,eAAA,GAAkB,IAAA;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,qBAAA,GAA8B;AAC5B,IAAA,IAAI,CAAC,IAAA,CAAK,UAAA,IAAc,CAAC,KAAK,gBAAA,EAAkB;AAChD,IAAA,IAAA,CAAK,qBAAA,EAAsB;AAC3B,IAAA,IAAA,CAAK,kBAAA,EAAmB;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,IAAI,cAAA,GAAgC;AAClC,IAAA,MAAM,QAAQ,IAAA,CAAK,gBAAA;AACnB,IAAA,MAAM,UAAU,CAAC,OAAA,KAAkC,UAAA,CAAW,KAAA,EAAO,OAAO,CAAA,KAAM,EAAA;AAClF,IAAA,OAAO;AAAA,MACL,GAAG,KAAA;AAAA,MACH,GAAG,KAAK,cAAA,CAAe,MAAA,CAAO,CAAC,OAAA,KAAY,CAAC,OAAA,CAAQ,OAAO,CAAC,CAAA;AAAA,MAC5D,GAAG,KAAK,YAAA,CAAa,MAAA,CAAO,CAAC,KAAA,KAAU,CAAC,OAAA,CAAQ,KAAK,CAAC;AAAA,KACxD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBAAiB,OAAA,EAA0C;AACzD,IAAA,IAAI,IAAA,CAAK,cAAA,CAAe,QAAA,CAAS,OAAO,GAAG,OAAO,OAAA;AAClD,IAAA,IAAI,IAAA,CAAK,YAAA,CAAa,QAAA,CAAS,OAAO,CAAA,EAAG;AACvC,MAAA,OAAO,IAAA,CAAK,cAAA,CAAe,IAAA,CAAK,CAAC,OAAA,KAAY,KAAK,SAAA,CAAU,OAAO,CAAA,KAAM,OAAO,CAAA,IAAK,IAAA;AAAA,IACvF;AACA,IAAA,IAAI,IAAA,CAAK,gBAAA,CAAiB,QAAA,CAAS,OAAO,CAAA,EAAG;AAC3C,MAAA,OAAO,IAAA,CAAK,eAAe,IAAA,CAAK,CAAC,YAAY,OAAA,CAAQ,QAAA,CAAS,OAAO,CAAC,CAAA,IAAK,IAAA;AAAA,IAC7E;AACA,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA,EAGA,UAAU,OAAA,EAA0C;AAClD,IAAA,MAAM,EAAA,GAAK,OAAA,CAAQ,YAAA,CAAa,eAAe,CAAA;AAG/C,IAAA,OAAO,EAAA,GAAM,IAAA,CAAK,YAAA,CAAa,IAAA,CAAK,CAAC,UAAU,KAAA,CAAM,EAAA,KAAO,EAAE,CAAA,IAAK,IAAA,GAAQ,IAAA;AAAA,EAC7E;AAAA;AAAA,EAGA,YAAY,OAAA,EAA+B;AACzC,IAAA,OAAO,OAAA,CAAQ,YAAA,CAAa,eAAe,CAAA,KAAM,MAAA;AAAA,EACnD;AAAA;AAAA,EAGA,IAAI,YAAA,GAAmC;AACrC,IAAA,OAAO,IAAA,CAAK,eAAe,IAAA,CAAK,CAAC,YAAY,IAAA,CAAK,WAAA,CAAY,OAAO,CAAC,CAAA,IAAK,IAAA;AAAA,EAC7E;AAAA;AAAA,EAGA,IAAI,UAAA,GAAsB;AACxB,IAAA,OAAO,KAAK,YAAA,KAAiB,IAAA;AAAA,EAC/B;AACF","file":"navigation_menu_controller.js","sourcesContent":["/** Normalized scroll position and maximum distance on one logical axis. */\nexport interface LogicalScrollMetrics {\n position: number;\n max: number;\n}\n\n/**\n * Whether horizontal scrolling on `element` follows right-to-left inline flow.\n *\n * Resolved from the **computed** `direction`, so the authoring contract is the\n * usual `dir=\"rtl\"` (or a stylesheet) on the element or any ancestor.\n *\n * Scope: horizontal writing modes. A vertical writing mode (`writing-mode:\n * vertical-rl`) also inverts the horizontal axis, which this check does not\n * model — vertical writing modes are out of scope for the scroll utilities\n * (their consumers describe axes as horizontal/vertical, not inline/block).\n */\nexport function isRtl(element: Element): boolean {\n return window.getComputedStyle(element).direction === \"rtl\";\n}\n\n/**\n * Returns scroll distance from the logical start edge.\n *\n * CSSOM View exposes standards-mode RTL horizontal offsets as `0` at the inline\n * start (right) and increasingly negative values toward the inline end (left).\n * The normalized position is always clamped to `[0, max]`, which also absorbs\n * Safari's elastic overscroll values.\n */\nexport function logicalScrollMetrics(\n element: HTMLElement,\n horizontal: boolean,\n): LogicalScrollMetrics {\n const max = Math.max(\n 0,\n horizontal\n ? element.scrollWidth - element.clientWidth\n : element.scrollHeight - element.clientHeight,\n );\n const raw = horizontal ? element.scrollLeft : element.scrollTop;\n const position = horizontal && isRtl(element) ? -raw : raw;\n return { position: Math.min(max, Math.max(0, position)), max };\n}\n\n/**\n * Converts a logical start/end delta to the physical value accepted by\n * `Element.scrollBy`.\n */\nexport function physicalScrollDelta(\n element: HTMLElement,\n horizontal: boolean,\n logicalDelta: number,\n): number {\n return horizontal && isRtl(element) ? -logicalDelta : logicalDelta;\n}\n","import { isRtl } from \"./logical_scroll\";\n\n/**\n * Turns an arrow key into a **logical** step: `+1` for \"next\", `-1` for\n * \"previous\", `0` when the key names neither.\n *\n * APG defines the horizontal pair as *next / previous* and says a vertical\n * arrangement swaps in Down/Up for the same meaning — so the pair is one axis's\n * spelling of an order, and the order reverses with the writing direction. Only\n * the horizontal pair reverses. Down/Up name an axis the writing direction does\n * not mirror, and returning them unchanged is the point: many controllers fold\n * both pairs into one branch, where swapping the branches under RTL would flip\n * the vertical axis too — a bug that reads as \"the arrows work\" until someone\n * presses Down.\n *\n * **Direction is read from the element the caller passes, which should be the\n * container that lays the items out** — not the focused child. A child may carry\n * its own `dir` (an LTR input inside an RTL form is ordinary authoring), and\n * probing per handler makes two handlers disagree at the boundary between them.\n *\n * This decides direction only. Whether the axis is even active (an\n * `orientation=\"horizontal\"` widget ignoring Down/Up), how far the step lands,\n * and what wrapping does all stay with the caller.\n *\n * **It encodes the list-order convention: `ArrowDown` is *next*.** Widgets that\n * pair the arrows by *value* instead — `ArrowUp` meaning \"more\", as a rating or a\n * slider does — must not use this, or their vertical axis inverts. Reverse the\n * horizontal pair on its own there.\n *\n * @example\n * ```ts\n * const step = logicalArrowStep(event.key, this.element);\n * if (step === 0) return;\n * this.#roving.setActive(rovingMove(current, length, step, \"wrap\"), { focus: true });\n * ```\n */\nexport function logicalArrowStep(key: string, element: Element): 1 | -1 | 0 {\n if (key === \"ArrowDown\") return 1;\n if (key === \"ArrowUp\") return -1;\n if (key !== \"ArrowRight\" && key !== \"ArrowLeft\") return 0;\n const forward = isRtl(element) ? \"ArrowLeft\" : \"ArrowRight\";\n return key === forward ? 1 : -1;\n}\n\n/**\n * Rewrites `key` so an existing LTR-shaped branch keeps working under RTL:\n * `ArrowRight` and `ArrowLeft` trade places, everything else passes through.\n *\n * The alternative — negating a delta — silently breaks handlers whose two\n * horizontal branches are **not mirror images**. A grid that clamps one edge but\n * not the other, or a segmented field guarding `index > 0` on one side and\n * `index < length - 1` on the other, ends up applying the wrong guard to the\n * wrong direction. Swapping the key leaves each branch, guards and all, exactly\n * where its author put it.\n *\n * Same rule as {@link logicalArrowStep} about which element to read: pass the\n * container that lays the items out, not the focused child.\n *\n * @example\n * ```ts\n * switch (logicalArrowKey(event.key, this.element)) {\n * case \"ArrowLeft\": // \"previous\" — whatever direction that is on screen\n * ```\n */\nexport function logicalArrowKey(key: string, element: Element): string {\n if (key !== \"ArrowRight\" && key !== \"ArrowLeft\") return key;\n if (!isRtl(element)) return key;\n return key === \"ArrowRight\" ? \"ArrowLeft\" : \"ArrowRight\";\n}\n\n/** Modifiers a widget may claim on an arrow key, named for the `allow` list. */\nexport type ArrowModifier = \"alt\" | \"ctrl\" | \"meta\" | \"shift\";\n\n/**\n * True when an arrow key arrived carrying a modifier the widget must leave to\n * the browser: return without calling `preventDefault()` and without moving any\n * state.\n *\n * A bare arrow belongs to the widget; a chorded one usually does not.\n * `Alt`/`Meta` plus a horizontal arrow is history back/forward on every desktop\n * browser, and a widget that swallows it makes the shortcut work or not\n * depending on where focus happens to sit — a coin-flip the user cannot see.\n *\n * `allow` is for the combinations APG assigns to a pattern **and the widget\n * actually implements** — today only Combobox's optional `Alt+Down`/`Alt+Up`.\n * Listing one the widget does not implement defeats the point: the chord then\n * runs the plain-arrow branch, which is exactly what this guard exists to stop.\n * Non-arrow keys return `false`, so chorded letters and\n * `Control+Home`/`Control+End` are untouched.\n *\n * @example\n * ```ts\n * if (isReservedArrowChord(event)) return;\n * ```\n */\nexport function isReservedArrowChord(\n event: KeyboardEvent,\n allow: readonly ArrowModifier[] = [],\n): boolean {\n if (!event.key.startsWith(\"Arrow\")) return false;\n return (\n (event.altKey && !allow.includes(\"alt\")) ||\n (event.ctrlKey && !allow.includes(\"ctrl\")) ||\n (event.metaKey && !allow.includes(\"meta\")) ||\n (event.shiftKey && !allow.includes(\"shift\"))\n );\n}\n\n/**\n * True when a press arrived carrying any modifier, for the keys the browser and\n * the OS own outright: return without calling `preventDefault()` and without\n * moving any state.\n *\n * `Control+Home` and `Control+End` jump the document to its ends, and a widget\n * that swallows them makes the shortcut work or not depending on where focus\n * happens to sit. {@link isReservedArrowChord} answers the same question for the\n * arrows, but returns `false` for every other key so that this one can decide.\n *\n * There is no `allow` list here on purpose. APG assigns no modifier chord to\n * `Home`/`End`, so a widget that wanted one would be claiming a combination the\n * pattern never gave it.\n *\n * @example\n * ```ts\n * case \"Home\":\n * case \"End\":\n * if (hasModifierChord(event)) return;\n * ```\n */\nexport function hasModifierChord(event: KeyboardEvent): boolean {\n return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;\n}\n","/**\n * Single resolver for layered Escape dismissal.\n *\n * Every Escape-dismissable overlay layer (modal focus traps, disclosure\n * overlays like dropdown/popover/menu, and hover-triggered transient layers)\n * registers here while it is open. One document-level listener per document\n * resolves each press to exactly one owner and invokes that layer's\n * {@link EscapeLayerOptions.onDismiss} — controllers never listen for a\n * dismissing Escape themselves.\n *\n * @remarks\n * Each document owns an activation-ordered stack. The owner of a press is the\n * **topmost layer whose {@link EscapeLayerOptions.claims} passes**; a layer\n * that declines is transparent, so a background overlay opened behind a modal\n * never blocks it. Because a layer opened from within another is necessarily\n * activated later, LIFO order is also inner-first for nested layers — no DOM\n * inspection is needed.\n *\n * The shared listener runs in the document bubble phase and honors\n * `event.defaultPrevented`, so an element-level widget handler that consumes\n * Escape first (an editor cancelling its edit, a combobox closing its list)\n * always wins over every registered layer — the deepest handler resolves the\n * press. A keydown that is part of an IME composition (`event.isComposing`)\n * cancels the composition, never a layer, and is ignored here for every layer\n * at once.\n *\n * A WeakMap keeps documents collectible; the listener is installed only while\n * a document's stack is non-empty, and controller lifecycle hooks guarantee\n * that disconnected layers never remain registered.\n */\n\n/** Behavior a layer registers when it activates. */\nexport interface EscapeLayerOptions {\n /**\n * Dismisses the layer. Called by the shared resolver when this layer owns a\n * press; the resolver has already consumed the event (`preventDefault()`),\n * so the callback only needs to close and place focus per the widget's\n * contract.\n */\n onDismiss: () => void;\n /**\n * Whether the layer claims the current press. Evaluated per press, so it can\n * depend on live state (e.g. \"focus is inside me or fell to the body\"). A\n * declining layer is skipped and the next layer down is consulted; omitting\n * it means the layer always claims while active.\n */\n claims?: () => boolean;\n}\n\n/** A document's stack plus the one shared listener bound to it. */\ninterface EscapeLayerRegistry {\n stack: EscapeLayer[];\n onKeydown: (event: KeyboardEvent) => void;\n}\n\n/**\n * Claims predicate shared by the click-opened disclosure overlays (dropdown /\n * popover / navigation-menu / menu / context-menu / menubar): the layer claims\n * a press while focus is inside `element`, or after focus fell to the body —\n * a click on non-focusable overlay content blurs to `<body>`, and Escape must\n * still close the overlay (the \"body-focus rescue\"). A press made after focus\n * moved to another interactive element is declined, so closing never yanks\n * focus away from where the user deliberately went.\n */\nexport function claimsWhileFocusWithin(element: Element): () => boolean {\n return () => {\n const active = element.ownerDocument.activeElement;\n return active === null || active === element.ownerDocument.body || element.contains(active);\n };\n}\n\nexport class EscapeLayer {\n static readonly #registries = new WeakMap<Document, EscapeLayerRegistry>();\n\n #ownerDocument: Document | null = null;\n /** Dismissal callback while active; `null` when inactive. */\n #onDismiss: (() => void) | null = null;\n /** Live predicate deciding whether the layer claims a press; `null` = always. */\n #claims: (() => boolean) | null = null;\n\n /**\n * Activates this layer at the top of its document's Escape stack, installing\n * the document's shared resolver listener if this is its first layer.\n * Re-activating an already-active layer moves it to the top.\n */\n activate(ownerDocument: Document = document, options: EscapeLayerOptions): void {\n this.deactivate();\n let registry = EscapeLayer.#registries.get(ownerDocument);\n if (!registry) {\n registry = EscapeLayer.#createRegistry();\n EscapeLayer.#registries.set(ownerDocument, registry);\n ownerDocument.addEventListener(\"keydown\", registry.onKeydown);\n }\n registry.stack.push(this);\n this.#ownerDocument = ownerDocument;\n this.#onDismiss = options.onDismiss;\n this.#claims = options.claims ?? null;\n }\n\n /**\n * Removes this layer from its document's Escape stack, uninstalling the\n * shared listener when the stack empties. Safe to call when inactive.\n */\n deactivate(): void {\n const ownerDocument = this.#ownerDocument;\n if (!ownerDocument) return;\n\n const registry = EscapeLayer.#registries.get(ownerDocument);\n if (registry) {\n const index = registry.stack.lastIndexOf(this);\n if (index >= 0) registry.stack.splice(index, 1);\n if (registry.stack.length === 0) {\n ownerDocument.removeEventListener(\"keydown\", registry.onKeydown);\n EscapeLayer.#registries.delete(ownerDocument);\n }\n }\n this.#ownerDocument = null;\n this.#onDismiss = null;\n this.#claims = null;\n }\n\n /**\n * Whether this active layer would own a press right now: it is the topmost\n * layer whose {@link EscapeLayerOptions.claims} passes. Exposed for tests\n * and diagnostics — production dismissal goes through the shared listener.\n */\n get ownsEscape(): boolean {\n const ownerDocument = this.#ownerDocument;\n if (!ownerDocument) return false;\n const registry = EscapeLayer.#registries.get(ownerDocument);\n if (!registry) return false;\n return EscapeLayer.#resolveOwner(registry.stack) === this;\n }\n\n /** Builds a document's registry with its shared resolver listener. */\n static #createRegistry(): EscapeLayerRegistry {\n const registry: EscapeLayerRegistry = {\n stack: [],\n onKeydown: (event: KeyboardEvent): void => {\n if (event.key !== \"Escape\" || event.defaultPrevented || event.isComposing) return;\n const owner = EscapeLayer.#resolveOwner(registry.stack);\n if (!owner) return;\n event.preventDefault();\n owner.#onDismiss?.();\n },\n };\n return registry;\n }\n\n /** The topmost stack layer whose claims predicate passes, or `null`. */\n static #resolveOwner(stack: EscapeLayer[]): EscapeLayer | null {\n for (let index = stack.length - 1; index >= 0; index--) {\n const layer = stack[index];\n if (!layer) continue;\n if (layer.#claims && !layer.#claims()) continue;\n return layer;\n }\n return null;\n }\n}\n","/**\n * Resolves which of a controller's elements owns a node.\n *\n * A delegated listener hears events from a whole subtree, so the handler's first\n * job is almost always the same question: which item, handle, or control does\n * this `event.target` belong to? The same question comes up for a\n * `MutationRecord.target`, for `document.activeElement`, and for a\n * `relatedTarget` on the way out of a hover region.\n *\n * The answer is `Node.contains()`, which is **inclusive** — an element contains\n * itself — so testing the candidate for identity as well would be redundant.\n *\n * **The guard is what makes this safe to call with a raw event target.**\n * `event.target` is typed `EventTarget | null`, and `contains()` takes a `Node?`:\n * browsers throw `TypeError` for anything else, and `window` — the everyday\n * `EventTarget` that is not a `Node` — is what an event dispatched at it carries.\n * Narrowing here means a caller never has to cast, and the rule cannot drift\n * between the places that ask the question.\n *\n * Scope stays with the caller. These helpers say *which candidate owns the node*,\n * not *whether the node belongs to this controller at all* — a component with\n * nested instances of itself decides that first (by comparing the closest\n * annotated ancestor) and passes the candidates it owns.\n */\n\n/**\n * Index in `candidates` of the first one that is, or contains, `node`.\n *\n * `-1` when none does, when `node` is absent, and when it is an `EventTarget`\n * that is not a `Node`. Candidates are tested in array order, so a nested pair\n * resolves to whichever the caller listed first.\n */\nexport function ownerIndex<T extends Element>(\n candidates: readonly T[],\n node: EventTarget | null | undefined,\n): number {\n if (!(node instanceof Node)) return -1;\n return candidates.findIndex((candidate) => candidate.contains(node));\n}\n\n/**\n * The first candidate that is, or contains, `node`; `null` when none does.\n *\n * A miss indexes the array at `-1`, which reads as `undefined` and lands on the\n * same `null` the absent cases produce.\n */\nexport function ownerOf<T extends Element>(\n candidates: readonly T[],\n node: EventTarget | null | undefined,\n): T | null {\n return candidates[ownerIndex(candidates, node)] ?? null;\n}\n","/**\n * Self-cleaning timer registries shared by Stimeo controllers.\n *\n * Stimulus controllers frequently schedule `setTimeout` / `setInterval` work\n * (auto-dismiss, debouncing, polling). When the element leaves the DOM — a\n * Turbo Drive navigation, a Turbo Stream replacement, or any `disconnect()` —\n * orphaned timers keep firing against a detached controller, leaking memory and\n * mutating stale state. {@link SafeTimeout} and {@link SafeInterval} track every\n * timer they create so a single {@link TimerRegistry.clearAll | clearAll()} call\n * in `disconnect()` tears them all down.\n *\n * These are intentionally low-level primitives: they own *registration and\n * cleanup only*. Higher-level policy (pause/resume, remaining-time accounting)\n * stays in the individual controllers so per-widget semantics are not flattened\n * into a lowest-common-denominator helper.\n */\n\n/**\n * Largest delay a timer can hold: the platform stores it in a 32-bit signed\n * integer, and anything larger overflows to `1`, so a delay meant to be far in\n * the future fires almost immediately. A declared delay above this bound names\n * no delay at all, and a controller reading one falls back to its default.\n */\nexport const MAX_TIMER_DELAY_MS = 2_147_483_647;\n\n/**\n * Shared registry bookkeeping for the timeout/interval variants.\n *\n * Subclasses provide the scheduling primitive ({@link schedule}) and its matching\n * canceller ({@link cancel}); this base owns the set of live ids plus the\n * per-id and bulk teardown shared by both.\n */\nabstract class TimerRegistry {\n /** Live timer ids that have not yet been cleared (or, for timeouts, fired). */\n protected readonly ids = new Set<number>();\n\n /** Schedules the underlying platform timer and returns its id. */\n protected abstract schedule(callback: () => void, delay: number): number;\n\n /** Cancels the underlying platform timer for `id`. */\n protected abstract cancel(id: number): void;\n\n /**\n * Cancels a single tracked timer.\n *\n * No-ops if the id is unknown (already cleared, fired, or never owned by this\n * registry), so callers can clear defensively without guarding.\n */\n clear(id: number): void {\n if (this.ids.delete(id)) {\n this.cancel(id);\n }\n }\n\n /**\n * Cancels every tracked timer. Call this from a controller's `disconnect()`\n * to guarantee no timer outlives the element.\n */\n clearAll(): void {\n for (const id of this.ids) {\n this.cancel(id);\n }\n this.ids.clear();\n }\n\n /** Number of timers currently tracked (pending). */\n get size(): number {\n return this.ids.size;\n }\n}\n\n/**\n * `setTimeout` wrapper that auto-forgets each timer once it fires and supports\n * bulk teardown on disconnect.\n *\n * @example\n * ```ts\n * #timers = new SafeTimeout();\n *\n * connect() {\n * this.#timers.set(() => this.dismiss(), 5000);\n * }\n *\n * disconnect() {\n * this.#timers.clearAll();\n * }\n * ```\n */\nexport class SafeTimeout extends TimerRegistry {\n /**\n * Schedules `callback` after `delay` ms and returns the timer id.\n *\n * The id is removed from the registry automatically when the timeout fires,\n * so {@link TimerRegistry.size | size} reflects only still-pending timers.\n */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(() => {\n this.ids.delete(id);\n callback();\n }, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setTimeout(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearTimeout(id);\n }\n}\n\n/**\n * `setInterval` wrapper that tracks every interval for bulk teardown on\n * disconnect. Unlike {@link SafeTimeout}, intervals are retained until they are\n * explicitly cleared because they fire repeatedly.\n *\n * @example\n * ```ts\n * #intervals = new SafeInterval();\n *\n * connect() {\n * this.#intervals.set(() => this.tick(), 1000);\n * }\n *\n * disconnect() {\n * this.#intervals.clearAll();\n * }\n * ```\n */\nexport class SafeInterval extends TimerRegistry {\n /** Schedules a repeating `callback` every `delay` ms and returns the timer id. */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(callback, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setInterval(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearInterval(id);\n }\n}\n","/**\n * Why a component's public open/closed state moved, carried as `detail.reason`\n * on the state event that reports the move.\n *\n * The attribute a component publishes (`aria-expanded`, `hidden`, `data-state`)\n * only says *what* the state is now. A subscriber that mirrors the state\n * elsewhere, or reports it, needs *why*: closing on `Escape` and closing because\n * the consumer called the action are the same attribute write and different\n * events to the page around it.\n *\n * `\"api\"` is the one every subscriber has to look at. A close the consumer asked\n * for arrives back at their own listener, so a listener that closes something\n * else on `close` loops unless it ignores its own calls.\n *\n * | Reason | The state moved because |\n * | --- | --- |\n * | `\"user\"` | a control of this component was operated |\n * | `\"select\"` | an item inside it was activated |\n * | `\"escape\"` | `Escape` was pressed and this layer owned it |\n * | `\"outside\"` | a pointer landed outside it, or on its backdrop |\n * | `\"focus\"` | focus entered or left it |\n * | `\"pointer\"` | the pointer entered or left it |\n * | `\"scroll\"` | a tracked scroll container scrolled |\n * | `\"api\"` | a public action was called with no DOM event |\n */\nexport type StateReason =\n | \"user\"\n | \"select\"\n | \"escape\"\n | \"outside\"\n | \"focus\"\n | \"pointer\"\n | \"scroll\"\n | \"api\";\n\n/** Focus modality: the events a focus move delivers to an action. */\nconst FOCUS_EVENTS = new Set([\"blur\", \"focus\", \"focusin\", \"focusout\"]);\n\n/** Pointer modality: the crossing events an action is bound to for hover. */\nconst POINTER_EVENTS = new Set([\"mouseenter\", \"mouseleave\", \"pointerenter\", \"pointerleave\"]);\n\n/**\n * Reads the interaction behind a **public action** from the DOM event it was\n * handed.\n *\n * Stimulus always passes the event to an action it invoked from `data-action`,\n * and a consumer calling the method themselves passes nothing — which is what\n * separates `\"api\"` from the rest.\n *\n * Only an action entry may resolve a reason this way. Inside the component the\n * event type no longer identifies the interaction: one `click` is the trigger\n * being pressed, the page outside being pressed, and an item being activated,\n * and those paths pass their own reason instead.\n *\n * @param event - The event the action received, if any.\n * @returns `\"api\"` with no event, else the modality the event type names, else\n * `\"user\"`.\n *\n * @example\n * ```ts\n * toggle(event?: Event): void {\n * this.#apply(!this.#isOpen, stateReasonFor(event));\n * }\n * ```\n */\nexport function stateReasonFor(event?: Event | null): \"user\" | \"focus\" | \"pointer\" | \"api\" {\n if (!event) return \"api\";\n if (FOCUS_EVENTS.has(event.type)) return \"focus\";\n if (POINTER_EVENTS.has(event.type)) return \"pointer\";\n return \"user\";\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { isReservedArrowChord } from \"../utils/arrow_step\";\nimport { claimsWhileFocusWithin, EscapeLayer } from \"../utils/escape_layer\";\nimport { ownerIndex } from \"../utils/event_owner\";\nimport { isRtl } from \"../utils/logical_scroll\";\nimport { SafeTimeout } from \"../utils/safe_timeout\";\nimport { type StateReason, stateReasonFor } from \"../utils/state_reason\";\n\n/**\n * Headless, accessible **navigation menu** behavior (disclosure navigation).\n *\n * Markup contract (identifier: `stimeo--navigation-menu`):\n * <nav data-controller=\"stimeo--navigation-menu\" aria-label=\"Main\">\n * <ul>\n * <li>\n * <button data-stimeo--navigation-menu-target=\"trigger\"\n * aria-expanded=\"false\" aria-controls=\"nav-products\"\n * data-action=\"click->stimeo--navigation-menu#toggle\n * keydown->stimeo--navigation-menu#onTriggerKeydown\">\n * Products\n * </button>\n * <div id=\"nav-products\" data-stimeo--navigation-menu-target=\"panel\" hidden>\n * <a href=\"/a\">Product A</a><a href=\"/b\">Product B</a>\n * </div>\n * </li>\n * </ul>\n * </nav>\n *\n * Implements the WAI-ARIA APG **Disclosure** navigation pattern: each top-level\n * button toggles its sub-panel (`aria-expanded` + `hidden` synced), only one panel\n * is open at a time, and the panel content is a plain set of links (not a\n * `role=\"menu\"`). Focus is **not** trapped — `Tab` moves through the links\n * naturally. `Escape` closes the open panel and returns focus to its trigger.\n * While a panel is open the nav is a layer on the shared `EscapeLayer`\n * stack; it claims a press only while focus is inside the nav or fell to the\n * body (a click on non-focusable panel content), so one keypress closes exactly\n * one layer. An outside click, or focus leaving the nav for a known external\n * destination, closes it. `ArrowLeft`/`ArrowRight` move focus between the\n * *navigable* triggers without rewriting `tabindex` (they keep their natural Tab\n * order); a modified press (`Alt`/`Ctrl`/`Meta`/`Shift`, e.g. the browser's\n * `Alt+←` history back) is left to the browser. Hover open/close is opt-in via\n * `openOnHover`.\n *\n * Every `panel` must be a **descendant of the nav element**: Stimulus resolves\n * targets within the controller's own scope, so a panel moved outside it (a\n * portal) never becomes a `panel` target and its trigger then opens nothing.\n *\n * By default the hover region is each trigger and its panel. An optional\n * `hoverArea` target widens it: mark a wrapper that contains **exactly one**\n * trigger (e.g. the `<li>` holding a top-level *link*, its disclosure button, and\n * its panel — the APG \"Disclosure Navigation with Top-Level Links\" arrangement)\n * and hovering anywhere over that wrapper opens the contained trigger's panel.\n * Wrapping several triggers at once (marking the whole `<ul>`, say) is not\n * supported: every hover over the area then resolves to the *first* contained\n * trigger. A trigger or panel inside a `hoverArea` defers to the wrapper (its own\n * edges stop scheduling open/close), so pointer movement within the area never\n * flickers the panel. Keep a trigger's panel inside its `hoverArea`: a panel left\n * outside simply falls back to the default two-region behavior — moving straight\n * across a shared edge never schedules a close (the leave handler checks\n * `relatedTarget`), and `hoverDelay` bridges the pointer's travel across an\n * actual gap. Targets added or removed **while connected** (e.g. a Turbo Stream\n * append) re-wire the hover listeners via Stimulus target callbacks; the same\n * callbacks also run during Stimulus teardown, so they no-op once disconnected.\n * Flipping `openOnHover` at runtime wires or unwires the listeners in place.\n * `hoverArea` has no effect unless `openOnHover` is enabled.\n *\n * Pointer-driven closing never hides focused content — hiding the panel would\n * take the focused link with it and drop focus to the body. So a hover *leave*\n * keeps the panel open while focus is inside it, and a hover *switch* to another\n * trigger returns focus to the outgoing trigger before hiding its panel. For the\n * same reason, while `openOnHover` is on, a click on a trigger whose hover region\n * the pointer currently occupies is a no-op: closing it would leave it unopenable\n * until the pointer left and re-entered (`mouseenter` does not re-fire in place).\n * `Escape` and outside clicks dismiss regardless.\n *\n * @remarks\n * Behavior only. Panel layout, mega-menu styling, and animation are the\n * consumer's CSS. Static placement is CSS; viewport-edge collision avoidance is\n * delegated to the opt-in `stimeo-ui/positioning` module (never imported here, so\n * the core stays zero-dependency). For an app command menu with arrow roving and\n * `role=\"menu\"`, use `stimeo--menubar` instead.\n *\n * Each move of a panel's open state is reported: `stimeo--navigation-menu:open`\n * and `stimeo--navigation-menu:close` dispatch\n * `{ reason: StateReason, index: number, panel: HTMLElement }` — `index` is the\n * trigger's position in `triggerTargets` — after the state attributes are\n * written. Both are informational, so neither is cancelable. Switching panels\n * reports the outgoing `close` before the incoming `open`, both with the reason\n * that drove the switch. A call that leaves a panel where it already was, the\n * normalization in {@link connect}, target churn, and {@link disconnect} are all\n * silent.\n */\nexport class NavigationMenuController extends Controller<HTMLElement> {\n static override targets = [\"trigger\", \"panel\", \"hoverArea\"];\n static override values = {\n openOnHover: { type: Boolean, default: false },\n hoverDelay: { type: Number, default: 150 },\n };\n static actions = [\"onTriggerKeydown\", \"toggle\"] as const;\n static events = [\"close\", \"open\"] as const;\n\n declare readonly triggerTargets: HTMLElement[];\n declare readonly panelTargets: HTMLElement[];\n declare readonly hoverAreaTargets: HTMLElement[];\n\n declare openOnHoverValue: boolean;\n declare hoverDelayValue: number;\n\n /** Open/close delay timers for hover mode; cleared together on disconnect. */\n readonly #hoverTimers = new SafeTimeout();\n\n /**\n * Elements currently carrying hover listeners. Removal always mirrors this\n * set (not a recomputed target snapshot), so a rebuild driven by target churn\n * can never strand a listener on an element that stopped being a target.\n */\n readonly #hoverWired = new Set<HTMLElement>();\n\n /**\n * The trigger whose hover region the pointer currently occupies, if any. Only\n * the click semantics under `openOnHover` read it (see {@link toggle}).\n */\n #hoveredTrigger: HTMLElement | null = null;\n\n /**\n * Whether the controller is between `connect()` and `disconnect()`. Stimulus\n * disconnects the controller **before** disconnecting its targets, so the\n * target callbacks below also fire during teardown; without this flag their\n * rebuild would re-wire hover listeners onto an already-disconnected nav.\n */\n #connected = false;\n\n /** Whether state moves are reported: set once `connect()` settled the baseline. */\n #reporting = false;\n\n /** Escape-stack membership while any panel is open; the shared resolver dismisses via it. */\n readonly #escapeLayer = new EscapeLayer();\n\n /** Establishes the closed baseline and the dismissal listeners. */\n override connect(): void {\n this.#connected = true;\n this.#closeAll(\"api\");\n document.addEventListener(\"click\", this.#onOutsideClick, true);\n this.element.addEventListener(\"focusout\", this.#onFocusOut);\n if (this.openOnHoverValue) this.#addHoverListeners();\n this.#reporting = true;\n }\n\n /** Removes every listener, pending hover timer, and stack membership taken while connected. */\n override disconnect(): void {\n this.#connected = false;\n this.#reporting = false;\n this.#escapeLayer.deactivate();\n document.removeEventListener(\"click\", this.#onOutsideClick, true);\n this.element.removeEventListener(\"focusout\", this.#onFocusOut);\n this.#removeHoverListeners();\n this.#hoverTimers.clearAll();\n }\n\n /**\n * Follows a runtime flip of `openOnHover` (a morph or a scripted attribute\n * change) by wiring or unwiring the hover listeners in place. Stimulus also\n * fires this once before `connect()`, which the `#connected` guard skips —\n * `connect()` owns the initial wiring.\n */\n openOnHoverValueChanged(): void {\n if (!this.#connected) return;\n if (this.openOnHoverValue) this.#addHoverListeners();\n else this.#removeHoverListeners();\n }\n\n /** Re-wires hover listeners when a target is added after connect (Turbo Streams etc.). */\n triggerTargetConnected(): void {\n this.#rewireHoverListeners();\n }\n\n /** Re-wires hover listeners when a target is removed while connected. */\n triggerTargetDisconnected(): void {\n this.#rewireHoverListeners();\n }\n\n /** See {@link NavigationMenuController.triggerTargetConnected}. */\n panelTargetConnected(): void {\n this.#rewireHoverListeners();\n }\n\n /** See {@link NavigationMenuController.triggerTargetDisconnected}. */\n panelTargetDisconnected(): void {\n this.#rewireHoverListeners();\n }\n\n /** See {@link NavigationMenuController.triggerTargetConnected}. */\n hoverAreaTargetConnected(): void {\n this.#rewireHoverListeners();\n }\n\n /** See {@link NavigationMenuController.triggerTargetDisconnected}. */\n hoverAreaTargetDisconnected(): void {\n this.#rewireHoverListeners();\n }\n\n /**\n * Toggles a trigger's panel (single-open). Bound via `data-action` (click).\n *\n * An explicit activation always wins over a scheduled hover open/close, so any\n * pending hover timer is dropped first — otherwise a close scheduled just\n * before the click would fire into the freshly opened panel.\n *\n * Under `openOnHover`, closing a panel the pointer itself is holding open is\n * refused: the pointer stays put, `mouseenter` does not re-fire, and the panel\n * would be unopenable until the pointer left and came back. Keyboard\n * activation (pointer elsewhere) still toggles it closed, and `Escape` and\n * outside clicks dismiss either way.\n */\n toggle(event: Event): void {\n const trigger = event.currentTarget as HTMLElement;\n const reason = stateReasonFor(event);\n this.#hoverTimers.clearAll();\n if (!this.#isExpanded(trigger)) {\n this.#openPanel(trigger, reason);\n return;\n }\n if (this.openOnHoverValue && this.#hoveredTrigger === trigger) return;\n this.#closePanel(trigger, reason);\n }\n\n /**\n * `ArrowLeft`/`ArrowRight` move focus between triggers (keeping Tab order).\n *\n * Only navigable triggers are destinations, so a `hidden` or disabled item\n * never swallows the press. A modified arrow belongs to the browser or OS\n * (`Alt+←`/`Alt+→` is history back/forward), and with no destination at all\n * (a single-trigger nav) the press is left to the page — `preventDefault()` is\n * called only when focus actually moves, keeping the scroll-suppression\n * contract honest.\n */\n onTriggerKeydown(event: KeyboardEvent): void {\n // A descendant widget that already claimed the key must not ALSO move the\n // trigger focus — composition depends on this yield. It runs ahead of the\n // modifier check, so a claimed press is yielded whether or not it carries a\n // modifier.\n if (event.defaultPrevented) return;\n if (event.key !== \"ArrowRight\" && event.key !== \"ArrowLeft\") return;\n if (isReservedArrowChord(event)) return;\n\n const current = event.currentTarget as HTMLElement;\n // The focused trigger stays in the ring even when it is not navigable\n // itself, so arrows still lead *out* of a trigger disabled while focused.\n const triggers = this.triggerTargets.filter(\n (trigger) => trigger === current || this.#isNavigable(trigger),\n );\n const index = triggers.indexOf(current);\n if (index === -1 || triggers.length < 2) return;\n\n event.preventDefault();\n // Logical, not physical: the triggers are an ordered row, so which one is\n // \"next\" follows the writing direction. Read from the controller element —\n // the nav lays the triggers out, and a trigger may carry its own `dir`.\n const forward = isRtl(this.element) ? \"ArrowLeft\" : \"ArrowRight\";\n const step = event.key === forward ? 1 : -1;\n triggers[(index + step + triggers.length) % triggers.length]?.focus();\n }\n\n /**\n * Opens `trigger`'s panel, closing any other open panel first. Re-opening an\n * already-open panel only re-asserts the Escape layer: the close/open\n * round-trip would rewrite `aria-expanded` and `hidden` for no state change.\n */\n #openPanel(trigger: HTMLElement, reason: StateReason): void {\n // Guarded here rather than in `toggle`, because hover reaches this method\n // without going through it. Reaching an `aria-disabled` trigger is what the\n // attribute asks for — opening its panel is the activation it forbids, and\n // opening a popup counts as activation.\n if (trigger.getAttribute(\"aria-disabled\") === \"true\") return;\n if (this.#isExpanded(trigger)) {\n this.#syncEscapeLayer();\n return;\n }\n this.#closeAll(reason);\n const panel = this.#panelFor(trigger);\n if (!panel) return;\n panel.hidden = false;\n trigger.setAttribute(\"aria-expanded\", \"true\");\n this.#report(\"open\", trigger, panel, reason);\n this.#syncEscapeLayer();\n }\n\n /** Closes `trigger`'s panel, reflects the collapsed state, and reports a move. */\n #closePanel(trigger: HTMLElement, reason: StateReason): void {\n const was = this.#isExpanded(trigger);\n const panel = this.#panelFor(trigger);\n if (panel) panel.hidden = true;\n trigger.setAttribute(\"aria-expanded\", \"false\");\n if (was && panel) this.#report(\"close\", trigger, panel, reason);\n this.#syncEscapeLayer();\n }\n\n /**\n * Closes every open panel and normalizes `aria-expanded=\"false\"` on **all**\n * triggers, so a trigger whose panel was hidden by other means cannot keep\n * advertising an open panel.\n */\n #closeAll(reason: StateReason): void {\n for (const trigger of this.triggerTargets) this.#closePanel(trigger, reason);\n }\n\n /** Names the panel that moved, so a subscriber does not have to re-derive it. */\n #report(\n name: \"close\" | \"open\",\n trigger: HTMLElement,\n panel: HTMLElement,\n reason: StateReason,\n ): void {\n if (!this.#reporting) return;\n const index = this.triggerTargets.indexOf(trigger);\n const detail = { reason, index, panel };\n if (name === \"open\") this.dispatch(\"open\", { detail, cancelable: false });\n else this.dispatch(\"close\", { detail, cancelable: false });\n }\n\n /**\n * Whether a trigger can receive arrow-key focus: not `hidden`, not a natively\n * `disabled` control (mirrors `toolbar`). CSS-only visibility cannot be\n * detected headlessly and stays the consumer's responsibility.\n *\n * **`aria-disabled=\"true\"` stays reachable.** APG separates the attributes by\n * intent: `disabled` is for controls a neighbour makes inferable, while\n * `aria-disabled` marks one that must remain *discoverable* — and a nav\n * section the user cannot even arrow to is a section they cannot learn exists.\n * Opening is suppressed separately, so the trigger announces itself and does\n * nothing.\n */\n #isNavigable(trigger: HTMLElement): boolean {\n if (trigger.hasAttribute(\"hidden\")) return false;\n return !(trigger as HTMLButtonElement | HTMLInputElement).disabled;\n }\n\n /**\n * Aligns Escape-stack membership with the open state: joins (or re-asserts to\n * the top) while a panel is open, leaves once none is. Re-asserting when the\n * user switches panels is deliberate — the nav is again the newest layer.\n */\n #syncEscapeLayer(): void {\n if (this.#isAnyOpen) {\n this.#escapeLayer.activate(document, {\n onDismiss: () => this.#closeAndRestore(),\n claims: claimsWhileFocusWithin(this.element),\n });\n } else {\n this.#escapeLayer.deactivate();\n }\n }\n\n /**\n * Closes any open panel and returns focus to its trigger (Escape path). A\n * pending hover open would otherwise re-open the panel the user just\n * dismissed, so the reservation is dropped with it.\n */\n #closeAndRestore(): void {\n const open = this.#openTrigger;\n if (!open) return;\n this.#hoverTimers.clearAll();\n this.#closePanel(open, \"escape\");\n open.focus();\n }\n\n /** Closes panels when a click lands outside the nav element. */\n readonly #onOutsideClick = (event: MouseEvent): void => {\n if (this.#isAnyOpen && !this.element.contains(event.target as Node)) this.#closeAll(\"outside\");\n };\n\n /**\n * Closes (without restoring focus) when focus leaves the nav for a known\n * external destination. A null/non-Node destination is indeterminate:\n * browsers use it for clicks on non-focusable content and for window\n * deactivation, so those never close the nav here (matching the popover\n * convention) — the outside-click handler decides pointer dismissal, and the\n * Escape stack's body-focus claim keeps keyboard dismissal working.\n */\n readonly #onFocusOut = (event: FocusEvent): void => {\n const next = event.relatedTarget;\n if (!(next instanceof Node) || this.element.contains(next)) return;\n this.#closeAll(\"focus\");\n };\n\n /**\n * Opens a trigger's panel after the hover delay (hover mode).\n *\n * @stimeoRuntimeOnly `hoverDelay` is the delay of the one hover timer this entry arms.\n */\n readonly #onPointerEnter = (event: Event): void => {\n const trigger = this.#triggerForHover(event.currentTarget as HTMLElement);\n if (!trigger) return;\n this.#hoveredTrigger = trigger;\n this.#hoverTimers.clearAll();\n this.#hoverTimers.set(() => this.#openFromHover(trigger), this.hoverDelayValue);\n };\n\n /**\n * Closes the open panel after the hover delay (hover mode) — unless the\n * pointer moved directly into another part of the hover region (an adjacent\n * trigger, panel, or hoverArea): crossing a shared edge must not schedule a\n * spurious close, e.g. a hoverArea whose panel sits outside it as a sibling.\n *\n * @stimeoRuntimeOnly `hoverDelay` is the delay of the one hover timer this exit arms.\n */\n readonly #onPointerLeave = (event: Event): void => {\n const next = event instanceof MouseEvent ? event.relatedTarget : null;\n if (ownerIndex(this.#hoverElements, next) !== -1) return;\n this.#hoveredTrigger = null;\n this.#hoverTimers.clearAll();\n this.#hoverTimers.set(() => this.#closeFromHover(), this.hoverDelayValue);\n };\n\n /**\n * Hover-driven open. Switching panels would hide the outgoing one, so focus\n * standing inside it is handed back to its trigger first: pointer movement\n * must never send focus to the body (the keyboard user would lose their\n * place). Focus outside the closing panel is left alone.\n */\n #openFromHover(trigger: HTMLElement): void {\n // Checked before the rescue below, not only inside `#openPanel`. The rescue\n // exists to get focus out of a panel that is about to be hidden; when the\n // destination refuses to open, nothing is hidden and moving focus buys\n // nothing — it just walks the caret out of a panel that stays open.\n if (trigger.getAttribute(\"aria-disabled\") === \"true\") return;\n const open = this.#openTrigger;\n if (open && open !== trigger && this.#holdsFocus(this.#panelFor(open))) open.focus();\n this.#openPanel(trigger, \"pointer\");\n }\n\n /**\n * Hover-driven close: skipped while focus sits inside the panel it would hide,\n * for the same no-focus-loss reason (cf. `hover_card`'s delayed close). The\n * panel then stays open until `Escape`, an outside click, or focus leaving the\n * nav dismisses it — all of which restore or keep focus deliberately.\n */\n #closeFromHover(): void {\n const open = this.#openTrigger;\n if (open && this.#holdsFocus(this.#panelFor(open))) return;\n this.#closeAll(\"pointer\");\n }\n\n /** Whether `panel` exists and currently contains the focused element. */\n #holdsFocus(panel: HTMLElement | null): boolean {\n return panel?.contains(document.activeElement) ?? false;\n }\n\n /** Wires hover open/close on each hover element (opt-in), tracking what was wired. */\n #addHoverListeners(): void {\n for (const element of this.#hoverElements) {\n element.addEventListener(\"mouseenter\", this.#onPointerEnter);\n element.addEventListener(\"mouseleave\", this.#onPointerLeave);\n this.#hoverWired.add(element);\n }\n }\n\n /** Removes the hover listeners from exactly the elements that were wired. */\n #removeHoverListeners(): void {\n for (const element of this.#hoverWired) {\n element.removeEventListener(\"mouseenter\", this.#onPointerEnter);\n element.removeEventListener(\"mouseleave\", this.#onPointerLeave);\n }\n this.#hoverWired.clear();\n this.#hoveredTrigger = null;\n }\n\n /**\n * Rebuilds the hover wiring from the current targets. Target callbacks call\n * this so items added or removed after connect (e.g. a Turbo Stream append)\n * participate in hover; the wired-set removal keeps the rebuild symmetric.\n * Stimulus fires those callbacks during teardown too, hence the `#connected`\n * guard — a rebuild after `disconnect()` would resurrect the listeners.\n */\n #rewireHoverListeners(): void {\n if (!this.#connected || !this.openOnHoverValue) return;\n this.#removeHoverListeners();\n this.#addHoverListeners();\n }\n\n /**\n * Elements that participate in hover: each `hoverArea`, plus every trigger and\n * panel **not** wrapped by one. A wrapped trigger/panel must defer to its\n * wrapper — its own mouseleave would otherwise schedule a close while the\n * pointer is still inside the area (mouseenter does not re-fire on the wrapper\n * when moving among its descendants), flickering the panel shut.\n */\n get #hoverElements(): HTMLElement[] {\n const areas = this.hoverAreaTargets;\n const covered = (element: HTMLElement): boolean => ownerIndex(areas, element) !== -1;\n return [\n ...areas,\n ...this.triggerTargets.filter((trigger) => !covered(trigger)),\n ...this.panelTargets.filter((panel) => !covered(panel)),\n ];\n }\n\n /**\n * Resolves the trigger for a hovered element: the trigger itself, the trigger\n * controlling a hovered panel, or the first trigger contained in a hovered\n * `hoverArea` wrapper.\n */\n #triggerForHover(element: HTMLElement): HTMLElement | null {\n if (this.triggerTargets.includes(element)) return element;\n if (this.panelTargets.includes(element)) {\n return this.triggerTargets.find((trigger) => this.#panelFor(trigger) === element) ?? null;\n }\n if (this.hoverAreaTargets.includes(element)) {\n return this.triggerTargets.find((trigger) => element.contains(trigger)) ?? null;\n }\n return null;\n }\n\n /** The panel controlled by `trigger` (matched by `aria-controls`/`id`). */\n #panelFor(trigger: HTMLElement): HTMLElement | null {\n const id = trigger.getAttribute(\"aria-controls\");\n // Resolve against this controller's own panel targets (not a global id\n // lookup) so it stays scoped to this nav instance.\n return id ? (this.panelTargets.find((panel) => panel.id === id) ?? null) : null;\n }\n\n /** Whether `trigger`'s panel is currently expanded. */\n #isExpanded(trigger: HTMLElement): boolean {\n return trigger.getAttribute(\"aria-expanded\") === \"true\";\n }\n\n /** The trigger whose panel is currently open, if any. */\n get #openTrigger(): HTMLElement | null {\n return this.triggerTargets.find((trigger) => this.#isExpanded(trigger)) ?? null;\n }\n\n /** Whether any panel is currently open. */\n get #isAnyOpen(): boolean {\n return this.#openTrigger !== null;\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/utils/state_reason.ts","../../src/utils/action_source.ts","../../src/utils/aria_disabled.ts","../../src/utils/logical_scroll.ts","../../src/utils/arrow_step.ts","../../src/utils/click_verdicts.ts","../../src/utils/shared_registry.ts","../../src/utils/escape_layer.ts","../../src/utils/event_owner.ts","../../src/utils/listener_set.ts","../../src/utils/timer_bounds.ts","../../src/utils/number_bounds.ts","../../src/utils/coerce.ts","../../src/utils/number_value.ts","../../src/utils/safe_timeout.ts","../../src/utils/target_selector.ts","../../src/controllers/navigation_menu_controller.ts"],"names":[],"mappings":"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","file":"navigation_menu_controller.js","sourcesContent":["/**\n * Why a component's public state moved, carried as `detail.reason`\n * on the state event that reports the move.\n *\n * The attribute a component publishes (`aria-expanded`, `hidden`, `data-state`)\n * only says *what* the state is now. A subscriber that mirrors the state\n * elsewhere, or reports it, needs *why*: closing on `Escape` and closing because\n * the consumer called the action are the same attribute write and different\n * events to the page around it.\n *\n * `\"api\"` is the one every subscriber has to look at. A close the consumer asked\n * for arrives back at their own listener, so a listener that closes something\n * else on `close` loops unless it ignores its own calls.\n *\n * | Reason | The state moved because |\n * | --- | --- |\n * | `\"user\"` | a control of this component was operated |\n * | `\"select\"` | an item inside it was activated |\n * | `\"escape\"` | `Escape` was pressed and this layer owned it |\n * | `\"outside\"` | a pointer landed outside it, or on its backdrop |\n * | `\"focus\"` | focus entered or left it |\n * | `\"pointer\"` | the pointer entered or left it |\n * | `\"scroll\"` | a tracked scroll container scrolled |\n * | `\"api\"` | a public action was called with no DOM event |\n * | `\"timeout\"` | an automatic timer advanced or dismissed the state |\n * | `\"limit\"` | a capacity limit dismissed an item |\n * | `\"media\"` | an operating-system media preference changed |\n */\nexport type StateReason =\n | \"user\"\n | \"select\"\n | \"escape\"\n | \"outside\"\n | \"focus\"\n | \"pointer\"\n | \"scroll\"\n | \"api\"\n | \"timeout\"\n | \"limit\"\n | \"media\";\n\n/** Focus modality: the events a focus move delivers to an action. */\nconst FOCUS_EVENTS = new Set([\"blur\", \"focus\", \"focusin\", \"focusout\"]);\n\n/** Pointer modality: the crossing events an action is bound to for hover. */\nconst POINTER_EVENTS = new Set([\"mouseenter\", \"mouseleave\", \"pointerenter\", \"pointerleave\"]);\n\n/**\n * Reads the interaction behind a **public action** from the DOM event it was\n * handed.\n *\n * Stimulus always passes the event to an action it invoked from `data-action`,\n * and a consumer calling the method themselves passes nothing — which is what\n * separates `\"api\"` from the rest.\n *\n * Only an action entry may resolve a reason this way. Inside the component the\n * event type no longer identifies the interaction: one `click` is the trigger\n * being pressed, the page outside being pressed, and an item being activated,\n * and those paths pass their own reason instead.\n *\n * @param event - The event the action received, if any.\n * @returns `\"api\"` with no event, else the modality the event type names, else\n * `\"user\"`.\n *\n * @example\n * ```ts\n * toggle(event?: Event): void {\n * this.#apply(!this.#isOpen, stateReasonFor(event));\n * }\n * ```\n */\nexport function stateReasonFor(event?: Event | null): \"user\" | \"focus\" | \"pointer\" | \"api\" {\n if (!event) return \"api\";\n if (FOCUS_EVENTS.has(event.type)) return \"focus\";\n if (POINTER_EVENTS.has(event.type)) return \"pointer\";\n return \"user\";\n}\n","import { stateReasonFor } from \"./state_reason\";\n\n/** The DOM binding and origin of a public action, or its explicit API element. */\nexport interface ActionSource {\n readonly event: Event | null;\n readonly host: Element | null;\n readonly origin: Element | null;\n readonly reason: ReturnType<typeof stateReasonFor>;\n}\n\n/**\n * Normalizes an action's DOM event or explicit element without choosing an owned target.\n *\n * Event hosts and origins stay separate so consumers can yield to nested controls.\n * An explicit element supplies both, with no DOM event and an `\"api\"` reason.\n * Consumers resolve current targets and enforce their own scope and interaction rules.\n *\n * @param source - The DOM event, explicit element, or absent optional argument.\n * @returns The source elements, original event and public interaction reason.\n */\nexport function actionSource(source?: Event | HTMLElement): ActionSource {\n if (source instanceof HTMLElement) {\n return { event: null, host: source, origin: source, reason: \"api\" };\n }\n const event = source ?? null;\n return {\n event,\n host: event?.currentTarget instanceof Element ? event.currentTarget : null,\n origin: event?.target instanceof Element ? event.target : null,\n reason: stateReasonFor(event),\n };\n}\n","/**\n * Whether `element` or one of its ancestors carries `aria-disabled=\"true\"`.\n *\n * WAI-ARIA defines the state for an element and all of its focusable descendants\n * (https://www.w3.org/TR/wai-aria-1.2/#aria-disabled), so an element inside one\n * that carries it is disabled whatever it carries itself — `aria-disabled=\"false\"`\n * on the element does not lift it. Only the value `\"true\"` disables. The walk\n * follows parent elements to the top of the element's tree, the document element\n * for an element in a document.\n *\n * `aria-disabled` keeps an element focusable and reachable, so this answers whether\n * it may be *activated*, not whether it may take focus.\n */\nexport function isAriaDisabled(element: Element): boolean {\n for (let current: Element | null = element; current; current = current.parentElement) {\n if (current.getAttribute(\"aria-disabled\") === \"true\") return true;\n }\n return false;\n}\n","/** Normalized scroll position and maximum distance on one logical axis. */\nexport interface LogicalScrollMetrics {\n position: number;\n max: number;\n}\n\n/**\n * Whether horizontal scrolling on `element` follows right-to-left inline flow.\n *\n * Resolved from the **computed** `direction`, so the authoring contract is the\n * usual `dir=\"rtl\"` (or a stylesheet) on the element or any ancestor.\n *\n * Scope: horizontal writing modes. A vertical writing mode (`writing-mode:\n * vertical-rl`) also inverts the horizontal axis, which this check does not\n * model — vertical writing modes are out of scope for the scroll utilities\n * (their consumers describe axes as horizontal/vertical, not inline/block).\n */\nexport function isRtl(element: Element): boolean {\n return window.getComputedStyle(element).direction === \"rtl\";\n}\n\n/**\n * Returns scroll distance from the logical start edge.\n *\n * CSSOM View exposes standards-mode RTL horizontal offsets as `0` at the inline\n * start (right) and increasingly negative values toward the inline end (left).\n * The normalized position is always clamped to `[0, max]`, which also absorbs\n * Safari's elastic overscroll values.\n */\nexport function logicalScrollMetrics(\n element: HTMLElement,\n horizontal: boolean,\n): LogicalScrollMetrics {\n const max = Math.max(\n 0,\n horizontal\n ? element.scrollWidth - element.clientWidth\n : element.scrollHeight - element.clientHeight,\n );\n const raw = horizontal ? element.scrollLeft : element.scrollTop;\n const position = horizontal && isRtl(element) ? -raw : raw;\n return { position: Math.min(max, Math.max(0, position)), max };\n}\n\n/**\n * Converts a logical start/end delta to the physical value accepted by\n * `Element.scrollBy`.\n */\nexport function physicalScrollDelta(\n element: HTMLElement,\n horizontal: boolean,\n logicalDelta: number,\n): number {\n return horizontal && isRtl(element) ? -logicalDelta : logicalDelta;\n}\n","import { isRtl } from \"./logical_scroll\";\n\n/**\n * Turns an arrow key into a **logical** step: `+1` for \"next\", `-1` for\n * \"previous\", `0` when the key names neither.\n *\n * APG defines the horizontal pair as *next / previous* and says a vertical\n * arrangement swaps in Down/Up for the same meaning — so the pair is one axis's\n * spelling of an order, and the order reverses with the writing direction. Only\n * the horizontal pair reverses. Down/Up name an axis the writing direction does\n * not mirror, and returning them unchanged is the point: many controllers fold\n * both pairs into one branch, where swapping the branches under RTL would flip\n * the vertical axis too — a bug that reads as \"the arrows work\" until someone\n * presses Down.\n *\n * **Direction is read from the element the caller passes, which should be the\n * container that lays the items out** — not the focused child. A child may carry\n * its own `dir` (an LTR input inside an RTL form is ordinary authoring), and\n * probing per handler makes two handlers disagree at the boundary between them.\n *\n * This decides direction only. Whether the axis is even active (an\n * `orientation=\"horizontal\"` widget ignoring Down/Up), how far the step lands,\n * and what wrapping does all stay with the caller.\n *\n * **It encodes the list-order convention: `ArrowDown` is *next*.** Widgets that\n * pair the arrows by *value* instead — `ArrowUp` meaning \"more\", as a rating or a\n * slider does — must not use this, or their vertical axis inverts. Reverse the\n * horizontal pair on its own there.\n *\n * @example\n * ```ts\n * const step = logicalArrowStep(event.key, this.element);\n * if (step === 0) return;\n * this.#roving.setActive(rovingMove(current, length, step, \"wrap\"), { focus: true });\n * ```\n */\nexport function logicalArrowStep(key: string, element: Element): 1 | -1 | 0 {\n if (key === \"ArrowDown\") return 1;\n if (key === \"ArrowUp\") return -1;\n if (key !== \"ArrowRight\" && key !== \"ArrowLeft\") return 0;\n const forward = isRtl(element) ? \"ArrowLeft\" : \"ArrowRight\";\n return key === forward ? 1 : -1;\n}\n\n/**\n * Rewrites `key` so an existing LTR-shaped branch keeps working under RTL:\n * `ArrowRight` and `ArrowLeft` trade places, everything else passes through.\n *\n * The alternative — negating a delta — silently breaks handlers whose two\n * horizontal branches are **not mirror images**. A grid that clamps one edge but\n * not the other, or a segmented field guarding `index > 0` on one side and\n * `index < length - 1` on the other, ends up applying the wrong guard to the\n * wrong direction. Swapping the key leaves each branch, guards and all, exactly\n * where its author put it.\n *\n * Same rule as {@link logicalArrowStep} about which element to read: pass the\n * container that lays the items out, not the focused child.\n *\n * @example\n * ```ts\n * switch (logicalArrowKey(event.key, this.element)) {\n * case \"ArrowLeft\": // \"previous\" — whatever direction that is on screen\n * ```\n */\nexport function logicalArrowKey(key: string, element: Element): string {\n if (key !== \"ArrowRight\" && key !== \"ArrowLeft\") return key;\n if (!isRtl(element)) return key;\n return key === \"ArrowRight\" ? \"ArrowLeft\" : \"ArrowRight\";\n}\n\n/**\n * The element whose writing direction orders a grid's columns: the nearest\n * `<table>` holding `grid` inside `root`, else `grid` itself. Table columns follow\n * the table's direction, so a direction declared only on a row group such as\n * `<tbody>` does not reorder them and must not reverse the arrows either.\n */\nexport function gridDirectionElement(grid: Element, root: Element): Element {\n const table = grid.closest(\"table\");\n return table !== null && root.contains(table) ? table : grid;\n}\n\n/** Modifiers a widget may claim on an arrow key, named for the `allow` list. */\nexport type ArrowModifier = \"alt\" | \"ctrl\" | \"meta\" | \"shift\";\n\n/**\n * True when an arrow key arrived carrying a modifier the widget must leave to\n * the browser: return without calling `preventDefault()` and without moving any\n * state.\n *\n * A bare arrow belongs to the widget; a chorded one usually does not.\n * `Alt`/`Meta` plus a horizontal arrow is history back/forward on every desktop\n * browser, and a widget that swallows it makes the shortcut work or not\n * depending on where focus happens to sit — a coin-flip the user cannot see.\n *\n * `allow` is for the combinations APG assigns to a pattern **and the widget\n * actually implements** — for example `Alt+Down`/`Alt+Up`, which the APG Combobox\n * pattern makes optional and `stimeo--combobox` implements.\n * Listing one the widget does not implement defeats the point: the chord then\n * runs the plain-arrow branch, which is exactly what this guard exists to stop.\n * Non-arrow keys return `false`, so chorded letters and\n * `Control+Home`/`Control+End` are untouched.\n *\n * @example\n * ```ts\n * if (isReservedArrowChord(event)) return;\n * ```\n */\nexport function isReservedArrowChord(\n event: KeyboardEvent,\n allow: readonly ArrowModifier[] = [],\n): boolean {\n if (!event.key.startsWith(\"Arrow\")) return false;\n return (\n (event.altKey && !allow.includes(\"alt\")) ||\n (event.ctrlKey && !allow.includes(\"ctrl\")) ||\n (event.metaKey && !allow.includes(\"meta\")) ||\n (event.shiftKey && !allow.includes(\"shift\"))\n );\n}\n\n/**\n * True when a press carries Alt, Control, Meta, or Shift, regardless of its key.\n *\n * This predicate reports modifier presence only; key ownership stays with the\n * caller. Check the widget's implemented key/modifier bindings and their state\n * conditions before using this result to yield a chord. An unimplemented chord\n * must not fall through to the bare-key behavior.\n *\n * Dedicated bindings, such as a grid's Control+Home/End navigation, belong in\n * the caller. Unlike {@link isReservedArrowChord}, this function examines every\n * key and has no arrow-specific reservation or `allow` list.\n *\n * @example\n * ```ts\n * // A Home/End handler with no modifier bindings yields every modified press.\n * case \"Home\":\n * case \"End\":\n * if (hasModifierChord(event)) return;\n * ```\n */\nexport function hasModifierChord(event: KeyboardEvent): boolean {\n return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;\n}\n","/**\n * The activation verdict a click took as it started, kept for every action that\n * click goes on to reach.\n *\n * A consumer handler on an entry can run before the action bound there, and may\n * change the entry while the click runs — mark it `disabled` or `aria-disabled` while\n * the command it starts is in flight. Judging the entry again when the action runs\n * would then take back the activation the click started. So a controller judges a\n * click once, in a capture listener on its own element that runs before the handlers\n * inside that element, records the verdict here with every entry that held\n * the click's target, and each action that click reaches reads the verdict back for\n * the entry it resolved.\n *\n * A verdict is read back only for an entry it was recorded with. An action that\n * resolves another entry — a handler moved the target into one — reads `undefined`\n * and judges that entry when it runs, as it does every event that is not a click.\n * Records are keyed by the event and are released with it.\n */\nexport class ClickVerdicts {\n readonly #verdicts = new WeakMap<\n Event,\n { readonly entries: readonly Element[]; readonly refused: boolean }\n >();\n\n /**\n * Records whether `event` is refused, with the `entries` that held its target as it\n * started. A later record of the same event replaces this one, and a record with no\n * entries matches no entry.\n */\n record(event: Event, entries: readonly Element[], refused: boolean): void {\n this.#verdicts.set(event, { entries, refused });\n }\n\n /**\n * The verdict recorded for `event` when `entry` is one of the entries it was\n * recorded with; `undefined` when there is no event, the event was never recorded,\n * or it was recorded without `entry`.\n */\n for(event: Event | null | undefined, entry: Element): boolean | undefined {\n const verdict = event && this.#verdicts.get(event);\n if (!verdict?.entries.includes(entry)) return undefined;\n return verdict.refused;\n }\n}\n","/**\n * One registry per page for every copy of the library.\n *\n * Each `stimeo-ui/controllers/*` file, and each opt-in entry, is built on its own and carries\n * its own copy of every utility it uses, so a page that imports controllers one by one, or one\n * of them next to the barrel, evaluates a utility several times. A registry that coordinates\n * instances of different controllers — the stacks of leased values, the loans of a\n * `tabindex`, the Escape layers, the focus traps — must not be split by that: a dialog inside\n * a drawer, or a character counter and a form field on one input, coordinate whichever files\n * they came from. So such a registry lives once on `globalThis`, under a `Symbol.for` key the\n * copies agree on, and the first copy to ask for it creates it.\n *\n * The key names the registry and the version of its shape\n * (`stimeo-ui.<utility>.registry.v<n>`). Copies whose registries have the same shape share\n * one; a release that changes the shape changes the version, so a copy of another release\n * keeps a registry of its own instead of reading entries it does not understand — the two\n * then coordinate within each release only. An entry another copy put in a registry is read\n * through its fields alone, never through `#private` members, which belong to the class of\n * the copy that created it.\n */\n\n/**\n * The registry stored under `key`, created by `create` on first use.\n *\n * @param key - `stimeo-ui.<utility>.registry.v<n>`: the version is that of the shape `create`\n * returns.\n * @param create - Builds the empty registry.\n */\nexport function sharedRegistry<T extends object>(key: string, create: () => T): T {\n const symbol = Symbol.for(key);\n const scope = globalThis as unknown as Record<symbol, T | undefined>;\n const existing = scope[symbol];\n if (existing !== undefined) return existing;\n const registry = create();\n Object.defineProperty(globalThis, symbol, { value: registry });\n return registry;\n}\n","/**\n * Single resolver for layered Escape dismissal.\n *\n * Every Escape-dismissable overlay layer (modal focus traps, disclosure\n * overlays like dropdown/popover/menu, and hover-triggered transient layers)\n * registers here while it is open. One document-level listener per document\n * resolves each press to exactly one owner and invokes that layer's\n * {@link EscapeLayerOptions.onDismiss} — controllers never listen for a\n * dismissing Escape themselves.\n *\n * @remarks\n * Each document owns an activation-ordered stack. The owner of a press is the\n * **topmost layer whose {@link EscapeLayerOptions.claims} passes**; a layer\n * that declines is transparent, so a background overlay opened behind a modal\n * never blocks it. Because a layer opened from within another is necessarily\n * activated later, LIFO order is also inner-first for nested layers — no DOM\n * inspection is needed.\n *\n * The shared listener runs in the document bubble phase and honors\n * `event.defaultPrevented`, so an element-level widget handler that consumes\n * Escape first (an editor cancelling its edit, a combobox closing its list)\n * always wins over every registered layer — the deepest handler resolves the\n * press. A keydown that is part of an IME composition (`event.isComposing`)\n * cancels the composition, never a layer, and is ignored here for every layer\n * at once.\n *\n * A WeakMap keeps documents collectible; the listener is installed only while\n * a document's stack is non-empty, and controller lifecycle hooks guarantee\n * that disconnected layers never remain registered. The map is one per page\n * (`sharedRegistry`), so the layers of controllers imported from different\n * files share one stack and one listener.\n */\n\nimport { sharedRegistry } from \"./shared_registry\";\n\n/** Behavior a layer registers when it activates. */\nexport interface EscapeLayerOptions {\n /**\n * Dismisses the layer. Called by the shared resolver when this layer owns a\n * press; the resolver has already consumed the event (`preventDefault()`),\n * so the callback only needs to close and place focus per the widget's\n * contract.\n */\n onDismiss: () => void;\n /**\n * Whether the layer claims the current press. Evaluated per press, so it can\n * depend on live state (e.g. \"focus is inside me or fell to the body\"). A\n * declining layer is skipped and the next layer down is consulted; omitting\n * it means the layer always claims while active.\n */\n claims?: () => boolean;\n}\n\n/** An active layer on its document's stack: what the resolver reads, from any copy. */\ninterface LayerEntry {\n readonly onDismiss: () => void;\n readonly claims: (() => boolean) | null;\n}\n\n/** A document's stack plus the one shared listener bound to it. */\ninterface EscapeLayerRegistry {\n readonly stack: LayerEntry[];\n readonly onKeydown: (event: KeyboardEvent) => void;\n}\n\n/**\n * Claims predicate shared by the click-opened disclosure overlays (dropdown /\n * popover / navigation-menu / menu / context-menu / menubar): the layer claims\n * a press while focus is inside `element`, or after focus fell to the body —\n * a click on non-focusable overlay content blurs to `<body>`, and Escape must\n * still close the overlay (the \"body-focus rescue\"). A press made after focus\n * moved to another interactive element is declined, so closing never yanks\n * focus away from where the user deliberately went.\n */\nexport function claimsWhileFocusWithin(element: Element): () => boolean {\n return () => {\n const active = element.ownerDocument.activeElement;\n return active === null || active === element.ownerDocument.body || element.contains(active);\n };\n}\n\nexport class EscapeLayer {\n static readonly #registries = sharedRegistry(\n \"stimeo-ui.escape-layer.registry.v1\",\n () => new WeakMap<Document, EscapeLayerRegistry>(),\n );\n\n /** The document and the entry of this layer while it is active. */\n #active: { readonly document: Document; readonly entry: LayerEntry } | null = null;\n\n /**\n * Activates this layer at the top of its document's Escape stack, installing\n * the document's shared resolver listener if this is its first layer.\n * Re-activating an already-active layer moves it to the top.\n */\n activate(ownerDocument: Document = document, options: EscapeLayerOptions): void {\n this.deactivate();\n let registry = EscapeLayer.#registries.get(ownerDocument);\n if (!registry) {\n registry = EscapeLayer.#createRegistry();\n EscapeLayer.#registries.set(ownerDocument, registry);\n ownerDocument.addEventListener(\"keydown\", registry.onKeydown);\n }\n const entry = { onDismiss: options.onDismiss, claims: options.claims ?? null };\n registry.stack.push(entry);\n this.#active = { document: ownerDocument, entry };\n }\n\n /**\n * Removes this layer from its document's Escape stack, uninstalling the\n * shared listener when the stack empties. Safe to call when inactive.\n */\n deactivate(): void {\n const active = this.#active;\n if (!active) return;\n\n const registry = EscapeLayer.#registries.get(active.document);\n if (registry) {\n const index = registry.stack.lastIndexOf(active.entry);\n if (index >= 0) registry.stack.splice(index, 1);\n if (registry.stack.length === 0) {\n active.document.removeEventListener(\"keydown\", registry.onKeydown);\n EscapeLayer.#registries.delete(active.document);\n }\n }\n this.#active = null;\n }\n\n /**\n * Whether this active layer would own a press right now: it is the topmost\n * layer whose {@link EscapeLayerOptions.claims} passes. Exposed for tests\n * and diagnostics — production dismissal goes through the shared listener.\n */\n get ownsEscape(): boolean {\n const active = this.#active;\n if (!active) return false;\n const registry = EscapeLayer.#registries.get(active.document);\n if (!registry) return false;\n return EscapeLayer.#resolveOwner(registry.stack) === active.entry;\n }\n\n /** Builds a document's registry with its shared resolver listener. */\n static #createRegistry(): EscapeLayerRegistry {\n const registry: EscapeLayerRegistry = {\n stack: [],\n onKeydown: (event: KeyboardEvent): void => {\n if (event.key !== \"Escape\" || event.defaultPrevented || event.isComposing) return;\n const owner = EscapeLayer.#resolveOwner(registry.stack);\n if (!owner) return;\n event.preventDefault();\n owner.onDismiss();\n },\n };\n return registry;\n }\n\n /** The topmost stack layer whose claims predicate passes, or `null`. */\n static #resolveOwner(stack: readonly LayerEntry[]): LayerEntry | null {\n for (let index = stack.length - 1; index >= 0; index--) {\n const layer = stack[index];\n if (!layer) continue;\n if (layer.claims && !layer.claims()) continue;\n return layer;\n }\n return null;\n }\n}\n","/**\n * Resolves which of a controller's elements owns a node.\n *\n * A delegated listener hears events from a whole subtree, so the handler's first\n * job is almost always the same question: which item, handle, or control does\n * this `event.target` belong to? The same question comes up for a\n * `MutationRecord.target`, for `document.activeElement`, and for a\n * `relatedTarget` on the way out of a hover region.\n *\n * The answer is `Node.contains()`, which is **inclusive** — an element contains\n * itself — so testing the candidate for identity as well would be redundant.\n *\n * **The guard is what makes this safe to call with a raw event target.**\n * `event.target` is typed `EventTarget | null`, and `contains()` takes a `Node?`:\n * browsers throw `TypeError` for anything else, and `window` — the everyday\n * `EventTarget` that is not a `Node` — is what an event dispatched at it carries.\n * Narrowing here means a caller never has to cast, and the rule cannot drift\n * between the places that ask the question.\n *\n * Scope stays with the caller. These helpers say *which candidate owns the node*,\n * not *whether the node belongs to this controller at all* — a component with\n * nested instances of itself decides that first (by comparing the closest\n * annotated ancestor) and passes the candidates it owns.\n *\n * Candidates can nest, and then more than one holds the node: {@link ownerIndex}\n * and {@link ownerOf} answer the first one listed, {@link ownersOf} all of them.\n */\n\n/**\n * Index in `candidates` of the first one that is, or contains, `node`.\n *\n * `-1` when none does, when `node` is absent, and when it is an `EventTarget`\n * that is not a `Node`. Candidates are tested in array order, so a nested pair\n * resolves to whichever the caller listed first.\n */\nexport function ownerIndex<T extends Element>(\n candidates: readonly T[],\n node: EventTarget | null | undefined,\n): number {\n if (!(node instanceof Node)) return -1;\n return candidates.findIndex((candidate) => candidate.contains(node));\n}\n\n/**\n * The first candidate that is, or contains, `node`; `null` when none does.\n *\n * A miss indexes the array at `-1`, which reads as `undefined` and lands on the\n * same `null` the absent cases produce.\n */\nexport function ownerOf<T extends Element>(\n candidates: readonly T[],\n node: EventTarget | null | undefined,\n): T | null {\n return candidates[ownerIndex(candidates, node)] ?? null;\n}\n\n/**\n * Every candidate that is, or contains, `node`, in array order.\n *\n * Empty when none does, when `node` is absent, and when it is an `EventTarget`\n * that is not a `Node`. A caller asking whether any owner of a node refuses\n * something reads the whole list, since an owner nested in another one holds the\n * node too.\n */\nexport function ownersOf<T extends Element>(\n candidates: readonly T[],\n node: EventTarget | null | undefined,\n): T[] {\n if (!(node instanceof Node)) return [];\n return candidates.filter((candidate) => candidate.contains(node));\n}\n","/** What {@link ListenerSet.add} passes on besides the signal it supplies itself. */\nexport type ListenerOptions = Omit<AddEventListenerOptions, \"signal\">;\n\n/**\n * The event map a target's own `addEventListener` types its handlers with: the\n * window's, a document's, an HTML or SVG element's, any other element's, or a\n * media query list's. Other targets name none.\n */\ntype EventMapOf<T extends EventTarget> = T extends Window\n ? WindowEventMap\n : T extends Document\n ? DocumentEventMap\n : T extends HTMLElement\n ? HTMLElementEventMap\n : T extends SVGElement\n ? SVGElementEventMap\n : T extends Element\n ? ElementEventMap\n : T extends MediaQueryList\n ? MediaQueryListEventMap\n : Record<never, never>;\n\n/**\n * Owns every listener a controller attaches for one connected lifetime and\n * releases them together, so an event name is spelled once and the release\n * cannot drift from the registration.\n *\n * Detaching by hand repeats three things at the release site — the type, the\n * handler reference and the capture flag — and any one of them drifting leaves\n * a listener alive on an element the controller no longer drives. Aborting the\n * signal a registration was created with releases it whatever it was made of,\n * so the release site names nothing.\n *\n * One set per lifetime, held as a field and never rebuilt.\n * {@link ListenerSet.dispose} is the only thing that closes a generation, and it opens\n * the next one in the same step, so the lifecycle hooks can call `add` and\n * `dispose` in any order any number of times, and no `add` ever meets an\n * already aborted signal — a DOM that registers a listener on such a signal\n * would keep it forever. Rebuilding the set instead would leak:\n * `addEventListener` matches an existing registration on target, type, callback\n * and capture alone, so re-adding the same handler while the first registration\n * is live is discarded, and that live registration keeps the signal it was\n * attached with — a set built by a second `connect()` can never release what it\n * appears to own.\n *\n * Scope is the registration only. Timers, observers, leases and the order they\n * are unwound in stay with the controller, which places `dispose()` in its\n * cleanup sequence. A receiver that changes at runtime (a\n * swapped target) is rebound by calling `dispose()` and then `add()` again from\n * the rebind, not by a second set.\n *\n * A lifetime shorter than the connection — while a Value holds, until a first\n * load, while a subscription is open — gets a set of its own, which the end of\n * that lifetime disposes. A controller therefore holds one set per lifetime: the\n * connection's, and one for each swapped receiver or shorter span.\n *\n * The set must own every registration of a tuple it holds: a listener the same\n * target already carries for the same type, callback and capture flag keeps the\n * right to remove it, and this set's `add` for that tuple is discarded. A\n * subscription shared between instances behind a participant count, listeners of\n * several elements that come and go one at a time, and those one gesture holds\n * keep their own pair of calls or a signal of their own.\n *\n * @example\n * ```ts\n * readonly #listeners = new ListenerSet();\n *\n * connect(): void {\n * this.#listeners.add(this.element, \"submit\", this.#onNativeSubmit, { capture: true });\n * this.#listeners.add(this.element, \"turbo:submit-start\", this.#onSubmitStart);\n * this.#listeners.add(this.element, \"turbo:submit-end\", this.#onSubmitEnd);\n * }\n *\n * disconnect(): void {\n * this.#listeners.dispose();\n * }\n * ```\n */\nexport class ListenerSet {\n /** The generation every `add` joins until the next `dispose()`. */\n #abort = new AbortController();\n\n /**\n * Attaches `handler` to the open generation, exactly as the caller spelled it.\n *\n * The set supplies the signal, so `options` carries everything else the DOM\n * accepts — `capture` included. A removal by hand would have to repeat that\n * flag; aborting the signal releases the listener without it.\n *\n * A `type` the target's own event map names types `handler` with that event,\n * as the target's `addEventListener` does, so a handler written for a\n * `KeyboardEvent` is passed as it is; any other `type` takes an\n * `EventListener`.\n */\n add<T extends EventTarget, K extends keyof EventMapOf<T> & string>(\n target: T,\n type: K,\n handler: (event: EventMapOf<T>[K]) => void,\n options?: ListenerOptions,\n ): void;\n add(target: EventTarget, type: string, handler: EventListener, options?: ListenerOptions): void;\n add(target: EventTarget, type: string, handler: EventListener, options?: ListenerOptions): void {\n target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });\n }\n\n /**\n * Releases every listener of the open generation, synchronously, and opens the\n * next one. Idempotent, and safe before anything has been added.\n */\n dispose(): void {\n this.#abort.abort();\n this.#abort = new AbortController();\n }\n}\n","/**\n * Largest delay a timer can hold: the platform stores it in a 32-bit signed\n * integer, and anything larger wraps around, so a delay meant to be far in the\n * future can fire almost immediately. A declared delay above this bound names\n * no delay at all, and a controller reading one falls back to its default.\n */\nexport const MAX_TIMER_DELAY_MS = 2_147_483_647;\n","import { MAX_TIMER_DELAY_MS } from \"./timer_bounds\";\n\n/** Semantic constraints on an already decoded number, independent of the DOM. */\nexport interface NumberBounds {\n /** Finite numbers are required, including when this field is omitted. */\n readonly finite?: true;\n /** Inclusive lower endpoint. */\n readonly min?: number;\n /** Inclusive upper endpoint. */\n readonly max?: number;\n /** Exclusive lower endpoint. */\n readonly exclusiveMin?: number;\n /** Require a number without a fractional part. */\n readonly integer?: boolean;\n /** Exact decoded numbers accepted by a discrete contract. */\n readonly allowedValues?: readonly number[];\n /** JSON-safe exceptions to finite-number checking for unbounded endpoints. */\n readonly allowInfinity?: \"negative\" | \"positive\" | \"both\";\n}\n\n/** One constraint for each Number Value, including shorthand declarations. */\nexport type NumberValueConstraints<Values> = {\n readonly [Key in keyof Values as Values[Key] extends\n | NumberConstructor\n | { type: NumberConstructor }\n ? Key\n : never]: NumberBounds;\n};\n\n/** Reusable semantic domains; fractional inputs remain fractional unless excluded. */\nexport const NUMBER_BOUNDS = {\n finite: { finite: true },\n nonNegative: { finite: true, min: 0 },\n positive: { finite: true, exclusiveMin: 0 },\n nonNegativeInteger: { finite: true, min: 0, integer: true },\n positiveInteger: { finite: true, exclusiveMin: 0, integer: true },\n lowerBound: { finite: true, allowInfinity: \"negative\" },\n upperBound: { finite: true, allowInfinity: \"positive\" },\n timer: { finite: true, min: 0, max: MAX_TIMER_DELAY_MS },\n positiveTimer: { finite: true, exclusiveMin: 0, max: MAX_TIMER_DELAY_MS },\n} as const satisfies Record<string, NumberBounds>;\n\n/**\n * Checks all declared bounds without rounding, clamping, or coercing the input.\n * Infinity exceptions relax only finite checking; every other bound still applies.\n */\nexport function matchesNumberBounds(value: number, bounds: NumberBounds): boolean {\n if (!Number.isFinite(value)) {\n const direction = value === Infinity ? \"positive\" : value === -Infinity ? \"negative\" : null;\n if (direction === null) return false;\n if (bounds.allowInfinity !== \"both\" && bounds.allowInfinity !== direction) return false;\n }\n if (bounds.min !== undefined && value < bounds.min) return false;\n if (bounds.max !== undefined && value > bounds.max) return false;\n if (bounds.exclusiveMin !== undefined && value <= bounds.exclusiveMin) return false;\n if (bounds.integer && !Number.isInteger(value)) return false;\n if (bounds.allowedValues !== undefined && !bounds.allowedValues.includes(value)) return false;\n return true;\n}\n\n/** Decodes a Number Value literal; action params use their own JSON decoder. */\nexport function decodeNumberValue(raw: string): number {\n return Number(raw.replace(/_/g, \"\"));\n}\n","/**\n * Numeric coercion for controllers that accept a number through a Value, an action\n * param, or an event detail.\n *\n * Stimulus decodes action params as JSON when possible and leaves other text\n * untouched. CustomEvent detail can also supply a number or numeric string.\n * The reader accepts those two types while rejecting blank and non-finite input.\n */\n\nimport { matchesNumberBounds, type NumberBounds } from \"./number_bounds\";\n\n/** Numeric update input, accepting decoded action params before CustomEvent detail. */\nexport type NumericValueEvent = Event & {\n params?: { amount?: unknown };\n detail?: { value?: unknown };\n};\n\n/** Reads the nullish action-param/detail precedence without falling back from invalid input. */\nexport function eventNumber(event: NumericValueEvent): number | null {\n return toFiniteNumber(event.params?.amount ?? event.detail?.value);\n}\n\n/**\n * Reads a finite number or nonblank numeric string; other input returns `null`.\n * Blank strings cannot silently reset a value to zero.\n */\nexport function toFiniteNumber(raw: unknown): number | null {\n if (typeof raw !== \"number\" && typeof raw !== \"string\") return null;\n if (typeof raw === \"string\" && raw.trim().length === 0) return null;\n const value = Number(raw);\n return Number.isFinite(value) ? value : null;\n}\n\n/**\n * Returns an accepted decoded Number Value or the caller's fallback unchanged.\n * The caller owns the fallback contract; this reader does not parse or write a Value.\n */\nexport function readNumber(raw: number, fallback: number, bounds: NumberBounds): number {\n return matchesNumberBounds(raw, bounds) ? raw : fallback;\n}\n","import { readNumber } from \"./coerce\";\nimport { matchesNumberBounds, type NumberBounds } from \"./number_bounds\";\n\n/** The instance identity carried by a Number Value read. */\nexport interface NumberValueOwner {\n readonly identifier: string;\n readonly element: Element;\n}\n\n/** Reads current declarations at the point of use without rewriting their attributes. */\nexport class NumberValueReader {\n /** The last rejected literal per Value; no history grows for an unchanged instance. */\n readonly #lastRejected = new Map<string, string>();\n\n /** Resolves the declared number using its own fallback and shared class contract. */\n read(\n owner: NumberValueOwner,\n name: string,\n raw: number,\n fallback: number,\n bounds: NumberBounds,\n ): number {\n const resolved = readNumber(raw, fallback, bounds);\n if (matchesNumberBounds(raw, bounds)) {\n this.#lastRejected.delete(name);\n return resolved;\n }\n const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;\n const literal = owner.element.getAttribute(attribute);\n if (literal === null) {\n this.#lastRejected.delete(name);\n return resolved;\n }\n if (this.#lastRejected.get(name) !== literal) {\n this.#lastRejected.set(name, literal);\n console.warn(\n `Stimeo UI: \"${owner.identifier}\" has an invalid number Value \"${name}\" declaration ${JSON.stringify(literal)}; using ${fallback}.`,\n );\n }\n return resolved;\n }\n}\n","/**\n * Self-cleaning timer registries shared by Stimeo controllers.\n *\n * Stimulus controllers frequently schedule `setTimeout` / `setInterval` work\n * (auto-dismiss, debouncing, polling). When the element leaves the DOM — a\n * Turbo Drive navigation, a Turbo Stream replacement, or any `disconnect()` —\n * orphaned timers keep firing against a detached controller, leaking memory and\n * mutating stale state. {@link SafeTimeout} and {@link SafeInterval} track every\n * timer they create so a single {@link TimerRegistry.clearAll | clearAll()} call\n * in `disconnect()` tears them all down.\n *\n * These are intentionally low-level primitives: they own *registration and\n * cleanup only*. One pending timer per key is `KeyedTimers`\n * (`keyed_timers.ts`), and a timer a person can hold open, with its remaining-time\n * accounting, is `PausableTimers` (`pausable_timers.ts`).\n */\n\nexport { MAX_TIMER_DELAY_MS } from \"./timer_bounds\";\n\n/**\n * Shared registry bookkeeping for the timeout/interval variants.\n *\n * Subclasses provide the scheduling primitive ({@link schedule}) and its matching\n * canceller ({@link cancel}); this base owns the set of live ids plus the\n * per-id and bulk teardown shared by both.\n */\nabstract class TimerRegistry {\n /** Live timer ids that have not yet been cleared (or, for timeouts, fired). */\n protected readonly ids = new Set<number>();\n\n /** Schedules the underlying platform timer and returns its id. */\n protected abstract schedule(callback: () => void, delay: number): number;\n\n /** Cancels the underlying platform timer for `id`. */\n protected abstract cancel(id: number): void;\n\n /**\n * Cancels a single tracked timer.\n *\n * No-ops if the id is unknown (already cleared, fired, or never owned by this\n * registry), so callers can clear defensively without guarding.\n */\n clear(id: number): void {\n if (this.ids.delete(id)) {\n this.cancel(id);\n }\n }\n\n /**\n * Cancels every tracked timer. Call this from a controller's `disconnect()`\n * to guarantee no timer outlives the element.\n */\n clearAll(): void {\n for (const id of this.ids) {\n this.cancel(id);\n }\n this.ids.clear();\n }\n\n /** Number of timers currently tracked (pending). */\n get size(): number {\n return this.ids.size;\n }\n}\n\n/**\n * `setTimeout` wrapper that auto-forgets each timer once it fires and supports\n * bulk teardown on disconnect.\n *\n * @example\n * ```ts\n * #timers = new SafeTimeout();\n *\n * connect() {\n * this.#timers.set(() => this.dismiss(), 5000);\n * }\n *\n * disconnect() {\n * this.#timers.clearAll();\n * }\n * ```\n */\nexport class SafeTimeout extends TimerRegistry {\n /**\n * Schedules `callback` after `delay` ms and returns the timer id.\n *\n * The id is removed from the registry automatically when the timeout fires,\n * so {@link TimerRegistry.size | size} reflects only still-pending timers.\n */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(() => {\n this.ids.delete(id);\n callback();\n }, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setTimeout(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearTimeout(id);\n }\n}\n\n/**\n * `setInterval` wrapper that tracks every interval for bulk teardown on\n * disconnect. Unlike {@link SafeTimeout}, intervals are retained until they are\n * explicitly cleared because they fire repeatedly.\n *\n * @example\n * ```ts\n * #intervals = new SafeInterval();\n *\n * connect() {\n * this.#intervals.set(() => this.tick(), 1000);\n * }\n *\n * disconnect() {\n * this.#intervals.clearAll();\n * }\n * ```\n */\nexport class SafeInterval extends TimerRegistry {\n /** Schedules a repeating `callback` every `delay` ms and returns the timer id. */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(callback, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setInterval(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearInterval(id);\n }\n}\n","/**\n * The attribute selector for one declared target, in a controller's own namespace.\n *\n * Stimulus reads `data-<identifier>-target` as a **space-separated token list**, so a\n * single element can carry several target names at once. Matching the attribute value\n * whole would reach only the elements that declare exactly one name, which is why the\n * name is paired with `~=`.\n *\n * `identifier` is the name the controller is registered under, so the selector reaches\n * the markup that registration renders rather than one fixed spelling.\n */\nexport function targetSelector(identifier: string, name: string): string {\n return `[data-${identifier}-target~=\"${name}\"]`;\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { actionSource } from \"../utils/action_source\";\nimport { isAriaDisabled } from \"../utils/aria_disabled\";\nimport { isReservedArrowChord } from \"../utils/arrow_step\";\nimport { ClickVerdicts } from \"../utils/click_verdicts\";\nimport { claimsWhileFocusWithin, EscapeLayer } from \"../utils/escape_layer\";\nimport { ownerIndex, ownersOf } from \"../utils/event_owner\";\nimport { ListenerSet } from \"../utils/listener_set\";\nimport { isRtl } from \"../utils/logical_scroll\";\nimport { NUMBER_BOUNDS, type NumberValueConstraints } from \"../utils/number_bounds\";\nimport { NumberValueReader } from \"../utils/number_value\";\nimport { SafeTimeout } from \"../utils/safe_timeout\";\nimport type { StateReason } from \"../utils/state_reason\";\nimport { targetSelector } from \"../utils/target_selector\";\n\n/**\n * Headless, accessible **navigation menu** behavior (disclosure navigation).\n *\n * Markup contract (identifier: `stimeo--navigation-menu`):\n * <nav data-controller=\"stimeo--navigation-menu\" aria-label=\"Main\">\n * <ul data-stimeo--navigation-menu-target=\"list\">\n * <li>\n * <button data-stimeo--navigation-menu-target=\"trigger\"\n * aria-expanded=\"false\" aria-controls=\"nav-products\"\n * data-action=\"click->stimeo--navigation-menu#toggle\n * keydown->stimeo--navigation-menu#onTriggerKeydown\">\n * Products\n * </button>\n * <div id=\"nav-products\" data-stimeo--navigation-menu-target=\"panel\" hidden>\n * <a href=\"/a\">Product A</a><a href=\"/b\">Product B</a>\n * </div>\n * </li>\n * </ul>\n * </nav>\n *\n * Implements the WAI-ARIA APG **Disclosure** navigation pattern: each top-level\n * button toggles its sub-panel (`aria-expanded` + `hidden` synced), only one panel\n * is open at a time, and the panel content is a plain set of links (not a\n * `role=\"menu\"`). Focus is **not** trapped — `Tab` moves through the links\n * naturally. `Escape` closes the open panel and returns focus to its trigger.\n * While a panel is open the nav is a layer on the shared `EscapeLayer`\n * stack; it claims a press only while focus is inside the nav or fell to the\n * body (a click on non-focusable panel content), so one keypress closes exactly\n * one layer. An outside click, or focus leaving the nav for a known external\n * destination, closes it. `ArrowLeft`/`ArrowRight` move focus between the\n * *navigable* triggers without rewriting `tabindex` (they keep their natural Tab\n * order); a modified press (`Alt`/`Ctrl`/`Meta`/`Shift`, e.g. the browser's\n * `Alt+←` history back) is left to the browser. `ArrowLeft`/`ArrowRight` follow\n * the writing direction of the optional `list` target, or of the root while there\n * is none. Hover open/close is opt-in via `openOnHover`.\n *\n * Every `panel` must be a **descendant of the nav element**: Stimulus resolves\n * targets within the controller's own scope, so a panel moved outside it (a\n * portal) never becomes a `panel` target and its trigger then opens nothing.\n *\n * By default the hover region is each trigger and its panel. An optional\n * `hoverArea` target widens it: mark a wrapper that contains **exactly one**\n * trigger (e.g. the `<li>` holding a top-level *link*, its disclosure button, and\n * its panel — the arrangement of the APG example\n * \"Disclosure (Show/Hide) Navigation Menu with Top-Level Links\") and hovering\n * anywhere over that wrapper opens the contained trigger's panel.\n * Wrapping several triggers at once (marking the whole `<ul>`, say) is not\n * supported: every hover over the area then resolves to the *first* contained\n * trigger. A trigger or panel inside a `hoverArea` defers to the wrapper (its own\n * edges stop scheduling open/close), so pointer movement within the area never\n * flickers the panel. Keep a trigger's panel inside its `hoverArea`: a panel left\n * outside simply falls back to the default two-region behavior — moving straight\n * across a shared edge never schedules a close (the leave handler checks\n * `relatedTarget`), and `hoverDelay` bridges the pointer's travel across an\n * actual gap. Targets added or removed **while connected** (e.g. a Turbo Stream\n * append) re-wire the hover listeners via Stimulus target callbacks; the same\n * callbacks also run during Stimulus teardown, so they no-op once disconnected.\n * Flipping `openOnHover` at runtime wires or unwires the listeners in place.\n * `hoverArea` has no effect unless `openOnHover` is enabled.\n *\n * Pointer-driven closing never hides focused content — hiding the panel would\n * take the focused link with it and drop focus to the body. So a hover *leave*\n * keeps the panel open while focus is inside it, and a hover *switch* to another\n * trigger returns focus to the outgoing trigger before hiding its panel. For the\n * same reason, while `openOnHover` is on, a click on a trigger whose hover region\n * the pointer currently occupies is a no-op: closing it would leave it unopenable\n * until the pointer left and re-entered (`mouseenter` does not re-fire in place).\n * `Escape` and outside clicks dismiss regardless.\n *\n * @remarks\n * Behavior only. Panel layout, mega-menu styling, and animation are the\n * consumer's CSS. Static placement is CSS; viewport-edge collision avoidance is\n * delegated to the opt-in `stimeo-ui/positioning` module (never imported here, so\n * the core stays zero-dependency). For an app command menu with arrow roving and\n * `role=\"menu\"`, use `stimeo--menubar` instead.\n *\n * Each move of a panel's open state is reported: `stimeo--navigation-menu:open`\n * and `stimeo--navigation-menu:close` dispatch\n * `{ reason: StateReason, index: number, panel: HTMLElement }` — `index` is the\n * trigger's position in `triggerTargets` — after the state attributes are\n * written. Both are informational, so neither is cancelable. Switching panels\n * reports the outgoing `close` before the incoming `open`, both with the reason\n * that drove the switch. A call that leaves a panel where it already was, the\n * normalization in {@link connect}, target churn, and {@link disconnect} are all\n * silent.\n */\nexport class NavigationMenuController extends Controller<HTMLElement> {\n /** Numeric read boundaries share one reader for this controller instance. */\n readonly #numbers = new NumberValueReader();\n\n static override targets = [\"trigger\", \"panel\", \"hoverArea\", \"list\"];\n static override values = {\n openOnHover: { type: Boolean, default: false },\n hoverDelay: { type: Number, default: 150 },\n };\n\n static valueConstraints = {\n hoverDelay: NUMBER_BOUNDS.timer,\n } satisfies NumberValueConstraints<typeof NavigationMenuController.values>;\n static actions = [\"onTriggerKeydown\", \"toggle\"] as const;\n static events = [\"close\", \"open\"] as const;\n\n declare readonly triggerTargets: HTMLElement[];\n declare readonly panelTargets: HTMLElement[];\n declare readonly hoverAreaTargets: HTMLElement[];\n declare readonly listTarget: HTMLElement;\n declare readonly hasListTarget: boolean;\n\n declare openOnHoverValue: boolean;\n declare hoverDelayValue: number;\n\n /** Open/close delay timers for hover mode; cleared together on disconnect. */\n readonly #hoverTimers = new SafeTimeout();\n\n /**\n * The hover listeners on the elements that were wired. Releasing the set reaches\n * exactly those elements (not a recomputed target snapshot), so a rebuild driven by\n * target churn can never strand a listener on an element that stopped being a target.\n */\n readonly #hoverListeners = new ListenerSet();\n\n /**\n * The trigger whose hover region the pointer currently occupies, if any. Only\n * the click semantics under `openOnHover` read it (see {@link toggle}).\n */\n #hoveredTrigger: HTMLElement | null = null;\n\n /**\n * Whether the controller is between `connect()` and `disconnect()`. Stimulus\n * disconnects the controller **before** disconnecting its targets, so the\n * target callbacks below also fire during teardown; without this flag their\n * rebuild would re-wire hover listeners onto an already-disconnected nav.\n */\n #connected = false;\n\n /** Whether state moves are reported: set once `connect()` settled the baseline. */\n #reporting = false;\n\n /** Escape-stack membership while any panel is open; the shared resolver dismisses via it. */\n readonly #escapeLayer = new EscapeLayer();\n\n /** The listeners of one connection, released together on `disconnect()`. */\n readonly #listeners = new ListenerSet();\n\n /** The activation verdict the capture pass took for a click, with the triggers it judged. */\n readonly #clickVerdicts = new ClickVerdicts();\n\n /** Establishes the closed baseline and the dismissal listeners. */\n override connect(): void {\n this.#connected = true;\n this.#closeAll(\"api\");\n this.#listeners.add(this.element, \"click\", this.#onClickCapture, { capture: true });\n this.#listeners.add(document, \"click\", this.#onOutsideClick, { capture: true });\n this.#listeners.add(this.element, \"focusout\", this.#onFocusOut);\n if (this.openOnHoverValue) this.#addHoverListeners();\n this.#reporting = true;\n }\n\n /** Removes every listener, pending hover timer, and stack membership taken while connected. */\n override disconnect(): void {\n this.#connected = false;\n this.#reporting = false;\n this.#escapeLayer.deactivate();\n this.#listeners.dispose();\n this.#removeHoverListeners();\n this.#hoverTimers.clearAll();\n }\n\n /**\n * Follows a runtime flip of `openOnHover` (a morph or a scripted attribute\n * change) by wiring or unwiring the hover listeners in place. Stimulus also\n * fires this once before `connect()`, which the `#connected` guard skips —\n * `connect()` owns the initial wiring.\n */\n openOnHoverValueChanged(): void {\n if (!this.#connected) return;\n if (this.openOnHoverValue) this.#addHoverListeners();\n else this.#removeHoverListeners();\n }\n\n /** Re-wires hover listeners when a target is added after connect (Turbo Streams etc.). */\n triggerTargetConnected(): void {\n this.#rewireHoverListeners();\n }\n\n /** Re-wires hover listeners when a target is removed while connected. */\n triggerTargetDisconnected(): void {\n this.#rewireHoverListeners();\n }\n\n /** See {@link NavigationMenuController.triggerTargetConnected}. */\n panelTargetConnected(): void {\n this.#rewireHoverListeners();\n }\n\n /** See {@link NavigationMenuController.triggerTargetDisconnected}. */\n panelTargetDisconnected(): void {\n this.#rewireHoverListeners();\n }\n\n /** See {@link NavigationMenuController.triggerTargetConnected}. */\n hoverAreaTargetConnected(): void {\n this.#rewireHoverListeners();\n }\n\n /** See {@link NavigationMenuController.triggerTargetDisconnected}. */\n hoverAreaTargetDisconnected(): void {\n this.#rewireHoverListeners();\n }\n\n /**\n * Toggles a trigger's panel (single-open). Bound via `data-action` (click).\n *\n * An explicit activation always wins over a scheduled hover open/close, so any\n * pending hover timer is dropped first — otherwise a close scheduled just\n * before the click would fire into the freshly opened panel.\n *\n * Under `openOnHover`, closing a panel the pointer itself is holding open is\n * refused: the pointer stays put, `mouseenter` does not re-fire, and the panel\n * would be unopenable until the pointer left and came back. Keyboard\n * activation (pointer elsewhere) still toggles it closed, and `Escape` and\n * outside clicks dismiss either way.\n *\n * A trigger that is `hidden`, natively `disabled` or `aria-disabled` (itself or\n * through an ancestor) never opens, whether this receives a DOM event of any\n * type or an explicit element: the refusal leaves an open panel open and reports\n * nothing, and only the pending hover timer is dropped, as for every explicit\n * activation. Nor does a trigger whose target lies inside a nested trigger that\n * refuses. A click keeps the verdict taken as it started, before the handlers\n * inside the nav element ran, when the trigger held its target then; a trigger that\n * verdict was not taken for, and anything else, is judged now. A panel already open\n * behind such a trigger still closes.\n */\n toggle(source: Event | HTMLElement): void {\n const { event, host, origin, reason } = actionSource(source);\n const trigger = host?.closest<HTMLElement>(targetSelector(this.identifier, \"trigger\"));\n if (!trigger || !this.triggerTargets.includes(trigger)) return;\n if (origin && origin.closest(`[data-controller~=\"${this.identifier}\"]`) !== this.element)\n return;\n this.#hoverTimers.clearAll();\n if (!this.#isExpanded(trigger)) {\n if (!this.#refuses(trigger, event, origin)) this.#openPanel(trigger, reason);\n return;\n }\n if (this.openOnHoverValue && this.#hoveredTrigger === trigger) return;\n this.#closePanel(trigger, reason);\n }\n\n /**\n * `ArrowLeft`/`ArrowRight` move focus between triggers (keeping Tab order).\n *\n * Only navigable triggers are destinations, so a `hidden` or disabled item\n * never swallows the press. A modified arrow belongs to the browser or OS\n * (`Alt+←`/`Alt+→` is history back/forward), and with no destination at all\n * (a single-trigger nav) the press is left to the page — `preventDefault()` is\n * called only when focus actually moves, keeping the scroll-suppression\n * contract honest.\n */\n onTriggerKeydown(event: KeyboardEvent): void {\n // A descendant widget that already claimed the key must not ALSO move the\n // trigger focus — composition depends on this yield. It runs ahead of the\n // modifier check, so a claimed press is yielded whether or not it carries a\n // modifier.\n if (event.defaultPrevented) return;\n if (event.key !== \"ArrowRight\" && event.key !== \"ArrowLeft\") return;\n if (isReservedArrowChord(event)) return;\n\n const current = event.currentTarget as HTMLElement;\n // The focused trigger stays in the ring even when it is not navigable\n // itself, so arrows still lead *out* of a trigger disabled while focused.\n const triggers = this.triggerTargets.filter(\n (trigger) => trigger === current || this.#isNavigable(trigger),\n );\n const index = triggers.indexOf(current);\n if (index === -1 || triggers.length < 2) return;\n\n event.preventDefault();\n // The trigger row owns direction; each trigger may carry its own independent dir.\n const forward = isRtl(this.hasListTarget ? this.listTarget : this.element)\n ? \"ArrowLeft\"\n : \"ArrowRight\";\n const step = event.key === forward ? 1 : -1;\n triggers[(index + step + triggers.length) % triggers.length]?.focus();\n }\n\n /**\n * Opens `trigger`'s panel, closing any other open panel first. Re-opening an\n * already-open panel only re-asserts the Escape layer: the close/open\n * round-trip would rewrite `aria-expanded` and `hidden` for no state change.\n *\n * Both callers have already judged the trigger: `toggle` — by a click's verdict from\n * before the handlers inside the nav element ran when that verdict was taken\n * for the trigger, and now otherwise — and the hover open, as its timer fires.\n */\n #openPanel(trigger: HTMLElement, reason: StateReason): void {\n if (this.#isExpanded(trigger)) {\n this.#syncEscapeLayer();\n return;\n }\n this.#closeAll(reason);\n const panel = this.#panelFor(trigger);\n if (!panel) return;\n panel.hidden = false;\n trigger.setAttribute(\"aria-expanded\", \"true\");\n this.#report(\"open\", trigger, panel, reason);\n this.#syncEscapeLayer();\n }\n\n /** Closes `trigger`'s panel, reflects the collapsed state, and reports a move. */\n #closePanel(trigger: HTMLElement, reason: StateReason): void {\n const was = this.#isExpanded(trigger);\n const panel = this.#panelFor(trigger);\n if (panel) panel.hidden = true;\n trigger.setAttribute(\"aria-expanded\", \"false\");\n if (was && panel) this.#report(\"close\", trigger, panel, reason);\n this.#syncEscapeLayer();\n }\n\n /**\n * Closes every open panel and normalizes `aria-expanded=\"false\"` on **all**\n * triggers, so a trigger whose panel was hidden by other means cannot keep\n * advertising an open panel.\n */\n #closeAll(reason: StateReason): void {\n for (const trigger of this.triggerTargets) this.#closePanel(trigger, reason);\n }\n\n /** Names the panel that moved, so a subscriber does not have to re-derive it. */\n #report(\n name: \"close\" | \"open\",\n trigger: HTMLElement,\n panel: HTMLElement,\n reason: StateReason,\n ): void {\n if (!this.#reporting) return;\n const index = this.triggerTargets.indexOf(trigger);\n const detail = { reason, index, panel };\n if (name === \"open\") this.dispatch(\"open\", { detail, cancelable: false });\n else this.dispatch(\"close\", { detail, cancelable: false });\n }\n\n /**\n * Whether a trigger can receive arrow-key focus: not `hidden`, not a natively\n * `disabled` control (mirrors `toolbar`). CSS-only visibility cannot be\n * detected headlessly and stays the consumer's responsibility.\n *\n * **`aria-disabled=\"true\"` stays reachable.** APG separates the attributes by\n * intent: `disabled` is for controls a neighbour makes inferable, while\n * `aria-disabled` marks one that must remain *discoverable* — and a nav\n * section the user cannot even arrow to is a section they cannot learn exists.\n * Opening is suppressed separately, so the trigger announces itself and does\n * nothing.\n */\n #isNavigable(trigger: HTMLElement): boolean {\n if (trigger.hasAttribute(\"hidden\")) return false;\n return !(trigger as HTMLButtonElement | HTMLInputElement).disabled;\n }\n\n /**\n * Whether `trigger` refuses activation: it is out of reach (its own `hidden` or\n * native `disabled`) or `aria-disabled`, itself or through an ancestor, which\n * keeps it reachable but never opened.\n */\n #isActivationDisabled(trigger: HTMLElement): boolean {\n return !this.#isNavigable(trigger) || isAriaDisabled(trigger);\n }\n\n /**\n * Whether opening `trigger`'s panel is refused for `event`, or for `origin` — the\n * explicit element, or the trigger a hover landed on — when there is no event. A\n * click keeps the verdict its capture pass took, before the handlers inside\n * the nav element could change it, when `trigger` was among the triggers holding\n * its target. Otherwise — any other event, an explicit element, a hover, or a click\n * recorded without `trigger` — `trigger` itself and the triggers holding the event's\n * target, or `origin`, are judged now, and any one that refuses activation refuses\n * `trigger`. `trigger` is judged on its own because a handler can move the target\n * out of it before `toggle` runs.\n */\n #refuses(trigger: HTMLElement, event: Event | null, origin: Element | null): boolean {\n const recorded = this.#clickVerdicts.for(event, trigger);\n if (recorded !== undefined) return recorded;\n if (this.#isActivationDisabled(trigger)) return true;\n const target = event ? event.target : origin;\n return ownersOf(this.triggerTargets, target).some((owner) => this.#isActivationDisabled(owner));\n }\n\n /**\n * Aligns Escape-stack membership with the open state: joins (or re-asserts to\n * the top) while a panel is open, leaves once none is. Re-asserting when the\n * user switches panels is deliberate — the nav is again the newest layer.\n */\n #syncEscapeLayer(): void {\n if (this.#isAnyOpen) {\n this.#escapeLayer.activate(document, {\n onDismiss: () => this.#closeAndRestore(),\n claims: claimsWhileFocusWithin(this.element),\n });\n } else {\n this.#escapeLayer.deactivate();\n }\n }\n\n /**\n * Closes any open panel and returns focus to its trigger (Escape path). A\n * pending hover open would otherwise re-open the panel the user just\n * dismissed, so the reservation is dropped with it.\n */\n #closeAndRestore(): void {\n const open = this.#openTrigger;\n if (!open) return;\n this.#hoverTimers.clearAll();\n this.#closePanel(open, \"escape\");\n open.focus();\n }\n\n /**\n * Judges a click on a trigger before the handlers inside the nav element\n * run, and records the verdict for the `toggle` the click goes on to reach: refused\n * when any trigger holding the target refuses activation, recorded with all of\n * those triggers. The click itself is left to run its course: a refused one opens\n * nothing, and nothing else is stopped.\n */\n readonly #onClickCapture = (event: MouseEvent): void => {\n const triggers = ownersOf(this.triggerTargets, event.target);\n this.#clickVerdicts.record(\n event,\n triggers,\n triggers.some((trigger) => this.#isActivationDisabled(trigger)),\n );\n };\n\n /** Closes panels when a click lands outside the nav element. */\n readonly #onOutsideClick = (event: MouseEvent): void => {\n if (this.#isAnyOpen && !this.element.contains(event.target as Node)) this.#closeAll(\"outside\");\n };\n\n /**\n * Closes (without restoring focus) when focus leaves the nav for a known\n * external destination. A null/non-Node destination is indeterminate:\n * browsers use it for clicks on non-focusable content and for window\n * deactivation, so those never close the nav here (matching the popover\n * convention) — the outside-click handler decides pointer dismissal, and the\n * Escape stack's body-focus claim keeps keyboard dismissal working.\n */\n readonly #onFocusOut = (event: FocusEvent): void => {\n const next = event.relatedTarget;\n if (!(next instanceof Node) || this.element.contains(next)) return;\n this.#closeAll(\"focus\");\n };\n\n /**\n * Opens a trigger's panel after the hover delay (hover mode).\n *\n * @stimeoRuntimeOnly `hoverDelay` is the delay of the one hover timer this entry arms.\n */\n readonly #onPointerEnter = (event: Event): void => {\n const trigger = this.#triggerForHover(event.currentTarget as HTMLElement);\n if (!trigger) return;\n this.#hoveredTrigger = trigger;\n this.#hoverTimers.clearAll();\n this.#hoverTimers.set(() => this.#openFromHover(trigger), this.#safeHoverDelay);\n };\n\n /**\n * Closes the open panel after the hover delay (hover mode) — unless the\n * pointer moved directly into another part of the hover region (an adjacent\n * trigger, panel, or hoverArea): crossing a shared edge must not schedule a\n * spurious close, e.g. a hoverArea whose panel sits outside it as a sibling.\n *\n * @stimeoRuntimeOnly `hoverDelay` is the delay of the one hover timer this exit arms.\n */\n readonly #onPointerLeave = (event: Event): void => {\n const next = event instanceof MouseEvent ? event.relatedTarget : null;\n if (ownerIndex(this.#hoverElements, next) !== -1) return;\n this.#hoveredTrigger = null;\n this.#hoverTimers.clearAll();\n this.#hoverTimers.set(() => this.#closeFromHover(), this.#safeHoverDelay);\n };\n\n /**\n * Hover-driven open. Switching panels would hide the outgoing one, so focus\n * standing inside it is handed back to its trigger first: pointer movement\n * must never send focus to the body (the keyboard user would lose their\n * place). Focus outside the closing panel is left alone.\n */\n #openFromHover(trigger: HTMLElement): void {\n // Checked here, before the rescue below; `#openPanel` does not check again.\n // The rescue exists to get focus out of a panel that is about to be hidden;\n // when the destination refuses to open, nothing is hidden and moving focus\n // buys nothing — it just walks the caret out of a panel that stays open.\n // Reaching an `aria-disabled` trigger is what the attribute asks for, while\n // opening its panel is the activation it forbids. A hover lands on `trigger`,\n // so every trigger holding it is judged, an outer one included.\n if (this.#refuses(trigger, null, trigger)) return;\n const open = this.#openTrigger;\n if (open && open !== trigger && this.#holdsFocus(this.#panelFor(open))) open.focus();\n this.#openPanel(trigger, \"pointer\");\n }\n\n /**\n * Hover-driven close: skipped while focus sits inside the panel it would hide,\n * for the same no-focus-loss reason (cf. `hover_card`'s delayed close). The\n * panel then stays open until `Escape`, an outside click, or focus leaving the\n * nav dismisses it — all of which restore or keep focus deliberately.\n */\n #closeFromHover(): void {\n const open = this.#openTrigger;\n if (open && this.#holdsFocus(this.#panelFor(open))) return;\n this.#closeAll(\"pointer\");\n }\n\n /** Whether `panel` exists and currently contains the focused element. */\n #holdsFocus(panel: HTMLElement | null): boolean {\n return panel?.contains(document.activeElement) ?? false;\n }\n\n /** Wires hover open/close on each hover element (opt-in). */\n #addHoverListeners(): void {\n for (const element of this.#hoverElements) {\n this.#hoverListeners.add(element, \"mouseenter\", this.#onPointerEnter);\n this.#hoverListeners.add(element, \"mouseleave\", this.#onPointerLeave);\n }\n }\n\n /** Releases the hover listeners from exactly the elements that were wired. */\n #removeHoverListeners(): void {\n this.#hoverListeners.dispose();\n this.#hoveredTrigger = null;\n }\n\n /**\n * Rebuilds the hover wiring from the current targets. Target callbacks call\n * this so items added or removed after connect (e.g. a Turbo Stream append)\n * participate in hover; releasing the wired set keeps the rebuild symmetric.\n * Stimulus fires those callbacks during teardown too, hence the `#connected`\n * guard — a rebuild after `disconnect()` would resurrect the listeners.\n */\n #rewireHoverListeners(): void {\n if (!this.#connected || !this.openOnHoverValue) return;\n this.#removeHoverListeners();\n this.#addHoverListeners();\n }\n\n /**\n * Elements that participate in hover: each `hoverArea`, plus every trigger and\n * panel **not** wrapped by one. A wrapped trigger/panel must defer to its\n * wrapper — its own mouseleave would otherwise schedule a close while the\n * pointer is still inside the area (mouseenter does not re-fire on the wrapper\n * when moving among its descendants), flickering the panel shut.\n */\n get #hoverElements(): HTMLElement[] {\n const areas = this.hoverAreaTargets;\n const covered = (element: HTMLElement): boolean => ownerIndex(areas, element) !== -1;\n return [\n ...areas,\n ...this.triggerTargets.filter((trigger) => !covered(trigger)),\n ...this.panelTargets.filter((panel) => !covered(panel)),\n ];\n }\n\n /**\n * Resolves the trigger for a hovered element: the trigger itself, the trigger\n * controlling a hovered panel, or the first trigger contained in a hovered\n * `hoverArea` wrapper.\n */\n #triggerForHover(element: HTMLElement): HTMLElement | null {\n if (this.triggerTargets.includes(element)) return element;\n if (this.panelTargets.includes(element)) {\n return this.triggerTargets.find((trigger) => this.#panelFor(trigger) === element) ?? null;\n }\n if (this.hoverAreaTargets.includes(element)) {\n return this.triggerTargets.find((trigger) => element.contains(trigger)) ?? null;\n }\n return null;\n }\n\n /** The panel controlled by `trigger` (matched by `aria-controls`/`id`). */\n #panelFor(trigger: HTMLElement): HTMLElement | null {\n const id = trigger.getAttribute(\"aria-controls\");\n // Resolve against this controller's own panel targets (not a global id\n // lookup) so it stays scoped to this nav instance.\n return id ? (this.panelTargets.find((panel) => panel.id === id) ?? null) : null;\n }\n\n /** Whether `trigger`'s panel is currently expanded. */\n #isExpanded(trigger: HTMLElement): boolean {\n return trigger.getAttribute(\"aria-expanded\") === \"true\";\n }\n\n /** The trigger whose panel is currently open, if any. */\n get #openTrigger(): HTMLElement | null {\n return this.triggerTargets.find((trigger) => this.#isExpanded(trigger)) ?? null;\n }\n\n /** Whether any panel is currently open. */\n get #isAnyOpen(): boolean {\n return this.#openTrigger !== null;\n }\n /** Current `hoverDelay` declaration resolved against its numeric contract. */\n get #safeHoverDelay(): number {\n return this.#numbers.read(\n this,\n \"hoverDelay\",\n this.hoverDelayValue,\n NavigationMenuController.values.hoverDelay.default,\n NavigationMenuController.valueConstraints.hoverDelay,\n );\n }\n}\n"]}
@@ -84,6 +84,16 @@ declare class NestedFormController extends Controller<HTMLElement> {
84
84
  default: string;
85
85
  };
86
86
  };
87
+ static valueConstraints: {
88
+ min: {
89
+ readonly finite: true;
90
+ readonly min: 0;
91
+ };
92
+ max: {
93
+ readonly finite: true;
94
+ readonly min: 0;
95
+ };
96
+ };
87
97
  static actions: readonly ["add"];
88
98
  static events: readonly ["add", "remove", "reconcile"];
89
99
  readonly listTarget: HTMLElement;