stimeo-ui 0.15.0 → 0.17.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 (332) hide show
  1. package/CHANGELOG.md +209 -0
  2. package/README.md +128 -1
  3. package/dist/cable/index.d.ts +79 -23
  4. package/dist/cable/index.js +392 -417
  5. package/dist/cable/index.js.map +1 -1
  6. package/dist/controllers/accordion_controller.d.ts +34 -5
  7. package/dist/controllers/accordion_controller.js +228 -28
  8. package/dist/controllers/accordion_controller.js.map +1 -1
  9. package/dist/controllers/alert_dialog_controller.d.ts +20 -9
  10. package/dist/controllers/alert_dialog_controller.js +862 -221
  11. package/dist/controllers/alert_dialog_controller.js.map +1 -1
  12. package/dist/controllers/announcer_controller.d.ts +38 -7
  13. package/dist/controllers/announcer_controller.js +135 -191
  14. package/dist/controllers/announcer_controller.js.map +1 -1
  15. package/dist/controllers/aspect_ratio_controller.js +0 -10
  16. package/dist/controllers/aspect_ratio_controller.js.map +1 -1
  17. package/dist/controllers/auto_submit_controller.d.ts +30 -6
  18. package/dist/controllers/auto_submit_controller.js +295 -110
  19. package/dist/controllers/auto_submit_controller.js.map +1 -1
  20. package/dist/controllers/avatar_controller.js +172 -106
  21. package/dist/controllers/avatar_controller.js.map +1 -1
  22. package/dist/controllers/breadcrumb_controller.d.ts +10 -7
  23. package/dist/controllers/breadcrumb_controller.js +0 -129
  24. package/dist/controllers/breadcrumb_controller.js.map +1 -1
  25. package/dist/controllers/bulk_select_controller.d.ts +22 -4
  26. package/dist/controllers/bulk_select_controller.js +264 -53
  27. package/dist/controllers/bulk_select_controller.js.map +1 -1
  28. package/dist/controllers/calendar_controller.d.ts +109 -28
  29. package/dist/controllers/calendar_controller.js +346 -180
  30. package/dist/controllers/calendar_controller.js.map +1 -1
  31. package/dist/controllers/carousel_controller.d.ts +99 -27
  32. package/dist/controllers/carousel_controller.js +587 -300
  33. package/dist/controllers/carousel_controller.js.map +1 -1
  34. package/dist/controllers/character_counter_controller.d.ts +18 -2
  35. package/dist/controllers/character_counter_controller.js +271 -143
  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 +77 -75
  39. package/dist/controllers/checkbox_controller.js.map +1 -1
  40. package/dist/controllers/clipboard_controller.d.ts +44 -7
  41. package/dist/controllers/clipboard_controller.js +187 -108
  42. package/dist/controllers/clipboard_controller.js.map +1 -1
  43. package/dist/controllers/collapsible_controller.d.ts +41 -5
  44. package/dist/controllers/collapsible_controller.js +374 -108
  45. package/dist/controllers/collapsible_controller.js.map +1 -1
  46. package/dist/controllers/color_picker_controller.d.ts +47 -14
  47. package/dist/controllers/color_picker_controller.js +183 -127
  48. package/dist/controllers/color_picker_controller.js.map +1 -1
  49. package/dist/controllers/combobox_controller.d.ts +33 -6
  50. package/dist/controllers/combobox_controller.js +426 -109
  51. package/dist/controllers/combobox_controller.js.map +1 -1
  52. package/dist/controllers/command_palette_controller.d.ts +45 -14
  53. package/dist/controllers/command_palette_controller.js +994 -350
  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 +212 -125
  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 +939 -245
  60. package/dist/controllers/confirm_controller.js.map +1 -1
  61. package/dist/controllers/context_menu_controller.d.ts +36 -3
  62. package/dist/controllers/context_menu_controller.js +275 -127
  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 +75 -2
  68. package/dist/controllers/countdown_controller.js +235 -97
  69. package/dist/controllers/countdown_controller.js.map +1 -1
  70. package/dist/controllers/currency_input_controller.d.ts +93 -15
  71. package/dist/controllers/currency_input_controller.js +241 -118
  72. package/dist/controllers/currency_input_controller.js.map +1 -1
  73. package/dist/controllers/data_grid_controller.d.ts +78 -22
  74. package/dist/controllers/data_grid_controller.js +229 -135
  75. package/dist/controllers/data_grid_controller.js.map +1 -1
  76. package/dist/controllers/date_range_picker_controller.d.ts +64 -11
  77. package/dist/controllers/date_range_picker_controller.js +359 -165
  78. package/dist/controllers/date_range_picker_controller.js.map +1 -1
  79. package/dist/controllers/dialog_controller.d.ts +22 -3
  80. package/dist/controllers/dialog_controller.js +887 -225
  81. package/dist/controllers/dialog_controller.js.map +1 -1
  82. package/dist/controllers/direct_upload_controller.d.ts +24 -7
  83. package/dist/controllers/direct_upload_controller.js +80 -142
  84. package/dist/controllers/direct_upload_controller.js.map +1 -1
  85. package/dist/controllers/dirty_form_controller.d.ts +2 -2
  86. package/dist/controllers/dirty_form_controller.js +26 -47
  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 +175 -20
  90. package/dist/controllers/dismissible_controller.js.map +1 -1
  91. package/dist/controllers/drawer_controller.d.ts +52 -16
  92. package/dist/controllers/drawer_controller.js +963 -336
  93. package/dist/controllers/drawer_controller.js.map +1 -1
  94. package/dist/controllers/dropdown_controller.d.ts +39 -3
  95. package/dist/controllers/dropdown_controller.js +342 -85
  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 +182 -58
  99. package/dist/controllers/editable_controller.js.map +1 -1
  100. package/dist/controllers/empty_state_controller.js +67 -53
  101. package/dist/controllers/empty_state_controller.js.map +1 -1
  102. package/dist/controllers/file_dropzone_controller.d.ts +60 -18
  103. package/dist/controllers/file_dropzone_controller.js +426 -229
  104. package/dist/controllers/file_dropzone_controller.js.map +1 -1
  105. package/dist/controllers/filter_controller.d.ts +10 -4
  106. package/dist/controllers/filter_controller.js +78 -36
  107. package/dist/controllers/filter_controller.js.map +1 -1
  108. package/dist/controllers/flash_controller.d.ts +57 -14
  109. package/dist/controllers/flash_controller.js +328 -211
  110. package/dist/controllers/flash_controller.js.map +1 -1
  111. package/dist/controllers/focus_controller.d.ts +12 -3
  112. package/dist/controllers/focus_controller.js +654 -239
  113. package/dist/controllers/focus_controller.js.map +1 -1
  114. package/dist/controllers/form_field_controller.js +153 -136
  115. package/dist/controllers/form_field_controller.js.map +1 -1
  116. package/dist/controllers/form_validation_controller.js +29 -96
  117. package/dist/controllers/form_validation_controller.js.map +1 -1
  118. package/dist/controllers/frame_loading_controller.d.ts +41 -9
  119. package/dist/controllers/frame_loading_controller.js +263 -198
  120. package/dist/controllers/frame_loading_controller.js.map +1 -1
  121. package/dist/controllers/highlight_controller.d.ts +22 -2
  122. package/dist/controllers/highlight_controller.js +119 -70
  123. package/dist/controllers/highlight_controller.js.map +1 -1
  124. package/dist/controllers/hover_card_controller.d.ts +65 -4
  125. package/dist/controllers/hover_card_controller.js +335 -118
  126. package/dist/controllers/hover_card_controller.js.map +1 -1
  127. package/dist/controllers/idle_controller.d.ts +58 -4
  128. package/dist/controllers/idle_controller.js +316 -65
  129. package/dist/controllers/idle_controller.js.map +1 -1
  130. package/dist/controllers/input_mask_controller.d.ts +5 -2
  131. package/dist/controllers/input_mask_controller.js +116 -61
  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 +147 -103
  135. package/dist/controllers/intersection_controller.js.map +1 -1
  136. package/dist/controllers/lazy_frame_controller.d.ts +16 -0
  137. package/dist/controllers/lazy_frame_controller.js +9 -56
  138. package/dist/controllers/lazy_frame_controller.js.map +1 -1
  139. package/dist/controllers/listbox_controller.d.ts +84 -10
  140. package/dist/controllers/listbox_controller.js +413 -153
  141. package/dist/controllers/listbox_controller.js.map +1 -1
  142. package/dist/controllers/local_time_controller.d.ts +1 -1
  143. package/dist/controllers/local_time_controller.js +54 -64
  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 +70 -95
  147. package/dist/controllers/masonry_controller.js.map +1 -1
  148. package/dist/controllers/menu_controller.d.ts +40 -9
  149. package/dist/controllers/menu_controller.js +254 -167
  150. package/dist/controllers/menu_controller.js.map +1 -1
  151. package/dist/controllers/menubar_controller.d.ts +15 -4
  152. package/dist/controllers/menubar_controller.js +206 -331
  153. package/dist/controllers/menubar_controller.js.map +1 -1
  154. package/dist/controllers/meter_controller.d.ts +20 -0
  155. package/dist/controllers/meter_controller.js +159 -71
  156. package/dist/controllers/meter_controller.js.map +1 -1
  157. package/dist/controllers/multi_select_controller.d.ts +50 -7
  158. package/dist/controllers/multi_select_controller.js +797 -388
  159. package/dist/controllers/multi_select_controller.js.map +1 -1
  160. package/dist/controllers/navigation_menu_controller.d.ts +19 -1
  161. package/dist/controllers/navigation_menu_controller.js +156 -231
  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 +373 -174
  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 +241 -45
  168. package/dist/controllers/network_status_controller.js.map +1 -1
  169. package/dist/controllers/number_input_controller.d.ts +62 -10
  170. package/dist/controllers/number_input_controller.js +387 -227
  171. package/dist/controllers/number_input_controller.js.map +1 -1
  172. package/dist/controllers/optimistic_controller.js +28 -75
  173. package/dist/controllers/optimistic_controller.js.map +1 -1
  174. package/dist/controllers/otp_controller.d.ts +38 -7
  175. package/dist/controllers/otp_controller.js +344 -196
  176. package/dist/controllers/otp_controller.js.map +1 -1
  177. package/dist/controllers/overflow_indicator_controller.d.ts +13 -12
  178. package/dist/controllers/overflow_indicator_controller.js +167 -93
  179. package/dist/controllers/overflow_indicator_controller.js.map +1 -1
  180. package/dist/controllers/overflow_menu_controller.d.ts +66 -9
  181. package/dist/controllers/overflow_menu_controller.js +408 -277
  182. package/dist/controllers/overflow_menu_controller.js.map +1 -1
  183. package/dist/controllers/pagination_controller.d.ts +52 -15
  184. package/dist/controllers/pagination_controller.js +342 -120
  185. package/dist/controllers/pagination_controller.js.map +1 -1
  186. package/dist/controllers/password_reveal_controller.d.ts +60 -9
  187. package/dist/controllers/password_reveal_controller.js +424 -99
  188. package/dist/controllers/password_reveal_controller.js.map +1 -1
  189. package/dist/controllers/password_strength_controller.d.ts +14 -5
  190. package/dist/controllers/password_strength_controller.js +99 -153
  191. package/dist/controllers/password_strength_controller.js.map +1 -1
  192. package/dist/controllers/persist_controller.d.ts +11 -4
  193. package/dist/controllers/persist_controller.js +252 -96
  194. package/dist/controllers/persist_controller.js.map +1 -1
  195. package/dist/controllers/pointer_drag_controller.d.ts +19 -7
  196. package/dist/controllers/pointer_drag_controller.js +263 -164
  197. package/dist/controllers/pointer_drag_controller.js.map +1 -1
  198. package/dist/controllers/popover_controller.d.ts +46 -4
  199. package/dist/controllers/popover_controller.js +267 -94
  200. package/dist/controllers/popover_controller.js.map +1 -1
  201. package/dist/controllers/portal_controller.d.ts +44 -4
  202. package/dist/controllers/portal_controller.js +209 -72
  203. package/dist/controllers/portal_controller.js.map +1 -1
  204. package/dist/controllers/preview_guard_controller.d.ts +10 -4
  205. package/dist/controllers/preview_guard_controller.js +243 -112
  206. package/dist/controllers/preview_guard_controller.js.map +1 -1
  207. package/dist/controllers/progress_controller.d.ts +11 -0
  208. package/dist/controllers/progress_controller.js +132 -59
  209. package/dist/controllers/progress_controller.js.map +1 -1
  210. package/dist/controllers/radio_group_controller.d.ts +19 -8
  211. package/dist/controllers/radio_group_controller.js +138 -133
  212. package/dist/controllers/radio_group_controller.js.map +1 -1
  213. package/dist/controllers/range_slider_controller.d.ts +92 -4
  214. package/dist/controllers/range_slider_controller.js +245 -112
  215. package/dist/controllers/range_slider_controller.js.map +1 -1
  216. package/dist/controllers/rating_controller.d.ts +33 -6
  217. package/dist/controllers/rating_controller.js +429 -194
  218. package/dist/controllers/rating_controller.js.map +1 -1
  219. package/dist/controllers/read_more_controller.d.ts +26 -2
  220. package/dist/controllers/read_more_controller.js +185 -47
  221. package/dist/controllers/read_more_controller.js.map +1 -1
  222. package/dist/controllers/reading_progress_controller.d.ts +1 -1
  223. package/dist/controllers/reading_progress_controller.js +147 -135
  224. package/dist/controllers/reading_progress_controller.js.map +1 -1
  225. package/dist/controllers/relative_time_controller.d.ts +18 -4
  226. package/dist/controllers/relative_time_controller.js +145 -110
  227. package/dist/controllers/relative_time_controller.js.map +1 -1
  228. package/dist/controllers/reset_on_restore_controller.d.ts +85 -0
  229. package/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +97 -38
  230. package/dist/controllers/reset_on_restore_controller.js.map +1 -0
  231. package/dist/controllers/resizable_controller.d.ts +44 -4
  232. package/dist/controllers/resizable_controller.js +294 -118
  233. package/dist/controllers/resizable_controller.js.map +1 -1
  234. package/dist/controllers/roving_controller.js +39 -84
  235. package/dist/controllers/roving_controller.js.map +1 -1
  236. package/dist/controllers/scroll_area_controller.d.ts +7 -3
  237. package/dist/controllers/scroll_area_controller.js +428 -221
  238. package/dist/controllers/scroll_area_controller.js.map +1 -1
  239. package/dist/controllers/scroll_restore_controller.js +0 -53
  240. package/dist/controllers/scroll_restore_controller.js.map +1 -1
  241. package/dist/controllers/scroll_visibility_controller.d.ts +28 -12
  242. package/dist/controllers/scroll_visibility_controller.js +395 -146
  243. package/dist/controllers/scroll_visibility_controller.js.map +1 -1
  244. package/dist/controllers/scrollspy_controller.d.ts +9 -3
  245. package/dist/controllers/scrollspy_controller.js +181 -236
  246. package/dist/controllers/scrollspy_controller.js.map +1 -1
  247. package/dist/controllers/separator_controller.d.ts +59 -17
  248. package/dist/controllers/separator_controller.js +288 -132
  249. package/dist/controllers/separator_controller.js.map +1 -1
  250. package/dist/controllers/sidebar_controller.d.ts +81 -7
  251. package/dist/controllers/sidebar_controller.js +1071 -375
  252. package/dist/controllers/sidebar_controller.js.map +1 -1
  253. package/dist/controllers/skeleton_controller.d.ts +23 -0
  254. package/dist/controllers/skeleton_controller.js +248 -92
  255. package/dist/controllers/skeleton_controller.js.map +1 -1
  256. package/dist/controllers/slider_controller.d.ts +72 -8
  257. package/dist/controllers/slider_controller.js +211 -96
  258. package/dist/controllers/slider_controller.js.map +1 -1
  259. package/dist/controllers/smart_sticky_header_controller.d.ts +21 -3
  260. package/dist/controllers/smart_sticky_header_controller.js +163 -54
  261. package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
  262. package/dist/controllers/sortable_controller.js +13 -110
  263. package/dist/controllers/sortable_controller.js.map +1 -1
  264. package/dist/controllers/spinner_controller.d.ts +39 -4
  265. package/dist/controllers/spinner_controller.js +277 -161
  266. package/dist/controllers/spinner_controller.js.map +1 -1
  267. package/dist/controllers/step_indicator_controller.d.ts +24 -17
  268. package/dist/controllers/step_indicator_controller.js +107 -53
  269. package/dist/controllers/step_indicator_controller.js.map +1 -1
  270. package/dist/controllers/stepper_controller.d.ts +43 -9
  271. package/dist/controllers/stepper_controller.js +171 -48
  272. package/dist/controllers/stepper_controller.js.map +1 -1
  273. package/dist/controllers/stick_to_bottom_controller.d.ts +25 -0
  274. package/dist/controllers/stick_to_bottom_controller.js +310 -116
  275. package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
  276. package/dist/controllers/sticky_observer_controller.d.ts +11 -0
  277. package/dist/controllers/sticky_observer_controller.js +214 -49
  278. package/dist/controllers/sticky_observer_controller.js.map +1 -1
  279. package/dist/controllers/submit_once_controller.d.ts +22 -6
  280. package/dist/controllers/submit_once_controller.js +294 -192
  281. package/dist/controllers/submit_once_controller.js.map +1 -1
  282. package/dist/controllers/switch_controller.d.ts +40 -7
  283. package/dist/controllers/switch_controller.js +107 -43
  284. package/dist/controllers/switch_controller.js.map +1 -1
  285. package/dist/controllers/tabs_controller.d.ts +13 -1
  286. package/dist/controllers/tabs_controller.js +50 -25
  287. package/dist/controllers/tabs_controller.js.map +1 -1
  288. package/dist/controllers/tags_input_controller.d.ts +27 -3
  289. package/dist/controllers/tags_input_controller.js +290 -149
  290. package/dist/controllers/tags_input_controller.js.map +1 -1
  291. package/dist/controllers/textarea_autosize_controller.d.ts +10 -0
  292. package/dist/controllers/textarea_autosize_controller.js +71 -58
  293. package/dist/controllers/textarea_autosize_controller.js.map +1 -1
  294. package/dist/controllers/theme_controller.d.ts +37 -8
  295. package/dist/controllers/theme_controller.js +148 -80
  296. package/dist/controllers/theme_controller.js.map +1 -1
  297. package/dist/controllers/time_picker_controller.d.ts +18 -1
  298. package/dist/controllers/time_picker_controller.js +162 -81
  299. package/dist/controllers/time_picker_controller.js.map +1 -1
  300. package/dist/controllers/toast_controller.d.ts +85 -23
  301. package/dist/controllers/toast_controller.js +365 -193
  302. package/dist/controllers/toast_controller.js.map +1 -1
  303. package/dist/controllers/toggle_group_controller.d.ts +60 -9
  304. package/dist/controllers/toggle_group_controller.js +194 -99
  305. package/dist/controllers/toggle_group_controller.js.map +1 -1
  306. package/dist/controllers/toolbar_controller.js +92 -90
  307. package/dist/controllers/toolbar_controller.js.map +1 -1
  308. package/dist/controllers/tooltip_controller.d.ts +52 -2
  309. package/dist/controllers/tooltip_controller.js +185 -125
  310. package/dist/controllers/tooltip_controller.js.map +1 -1
  311. package/dist/controllers/transition_controller.d.ts +22 -4
  312. package/dist/controllers/transition_controller.js +123 -115
  313. package/dist/controllers/transition_controller.js.map +1 -1
  314. package/dist/controllers/tree_view_controller.d.ts +59 -18
  315. package/dist/controllers/tree_view_controller.js +248 -290
  316. package/dist/controllers/tree_view_controller.js.map +1 -1
  317. package/dist/index.d.ts +33 -3
  318. package/dist/index.js +8649 -8572
  319. package/dist/index.js.map +1 -1
  320. package/dist/inspector/cli.d.ts +95 -9
  321. package/dist/inspector/cli.js +343 -77
  322. package/dist/inspector/cli.js.map +1 -1
  323. package/dist/inspector/cli_bin.js +390 -96
  324. package/dist/inspector/cli_bin.js.map +1 -1
  325. package/dist/inspector/examples.json +45 -45
  326. package/dist/inspector/manifest.json +1308 -155
  327. package/dist/positioning/index.d.ts +8 -0
  328. package/dist/positioning/index.js +148 -45
  329. package/dist/positioning/index.js.map +1 -1
  330. package/package.json +7 -7
  331. package/dist/controllers/reset_before_cache_controller.d.ts +0 -76
  332. 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/roving_tabindex.ts","../../src/utils/safe_timeout.ts","../../src/utils/typeahead.ts","../../src/controllers/menubar_controller.ts"],"names":["top"],"mappings":"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","file":"menubar_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 * Low-level roving-tabindex primitive shared by composite-widget controllers.\n *\n * The APG roving-tabindex pattern keeps a composite widget a single Tab stop:\n * exactly one item is in the Tab sequence (`tabindex=\"0\"`) while the rest are\n * removed from it (`tabindex=\"-1\"`), and the arrow keys move both DOM focus and\n * that single tabbable position together. {@link RovingTabindex} owns *only* that\n * mechanical bookkeeping — \"which one item is tabbable, and move focus there\".\n *\n * It is intentionally **policy-free**. Orientation, wrapping vs. clamping,\n * selection-follows-focus, typeahead, and `Home`/`End` semantics differ per APG\n * pattern (Radio Group, Toolbar, Rating, …); folding them into one helper would\n * flatten those widgets to a lowest common denominator and lose each pattern's\n * correctness. Those decisions therefore stay in each controller, which calls\n * {@link RovingTabindex.setActive} with an index it computed itself (optionally\n * via the pure {@link rovingMove} helper).\n *\n * @remarks\n * Items are read lazily through a getter so a controller can add or remove\n * targets (Stimulus re-scans the DOM) without re-wiring this helper. A caller\n * that already captured one event-scoped snapshot may pass it to\n * {@link RovingTabindex.setActive}, avoiding a second live-target scan while\n * preserving one coherent collection for the whole operation.\n */\nexport class RovingTabindex {\n /** Returns the current ordered item elements; called on every operation. */\n readonly #getItems: () => HTMLElement[];\n\n /**\n * @param getItems - Returns the current ordered item elements. Called on every\n * operation so the live target list is always used.\n */\n constructor(getItems: () => HTMLElement[]) {\n this.#getItems = getItems;\n }\n\n /** Index of the currently tabbable item (`tabindex=\"0\"`), or `-1` if none. */\n get activeIndex(): number {\n return this.#getItems().findIndex((item) => item.tabIndex === 0);\n }\n\n /**\n * Makes exactly the item at `index` tabbable (`tabindex=\"0\"`) and removes every\n * other item from the Tab sequence (`tabindex=\"-1\"`). An out-of-range `index`\n * (e.g. `-1`) leaves all items at `-1`, which a controller can use to express\n * \"nothing is currently tabbable\".\n *\n * @param index - Position of the item to make tabbable.\n * @param options - Pass `{ focus: true }` to also move DOM focus to that item,\n * and `items` to reuse an event-scoped collection snapshot.\n */\n setActive(\n index: number,\n options: { focus?: boolean; items?: readonly HTMLElement[] } = {},\n ): void {\n const { focus = false } = options;\n const items = options.items ?? this.#getItems();\n items.forEach((item, i) => {\n item.tabIndex = i === index ? 0 : -1;\n });\n if (focus) items[index]?.focus();\n }\n}\n\n/** Edge behavior for {@link rovingMove}: cycle past the ends, or stop at them. */\nexport type RovingWrap = \"wrap\" | \"clamp\";\n\n/**\n * Pure helper that resolves the target index for a one-step directional move.\n *\n * Keyboard/orientation mapping stays in the caller: it decides that a key means\n * `delta` `+1` (next) or `-1` (previous) and whether the widget should `\"wrap\"`\n * (Radio Group, Toolbar) or `\"clamp\"` at the ends.\n *\n * @param current - The index focus is moving from.\n * @param length - Number of items in the set.\n * @param delta - `+1` to move to the next item, `-1` for the previous.\n * @param wrap - `\"wrap\"` cycles around the ends; `\"clamp\"` stops at them.\n * @returns The resolved index, or `-1` when there are no items.\n */\nexport function rovingMove(\n current: number,\n length: number,\n delta: number,\n wrap: RovingWrap,\n): number {\n if (length === 0) return -1;\n const next = current + delta;\n if (wrap === \"wrap\") return (next + length) % length;\n return Math.min(length - 1, Math.max(0, next));\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 * Shared type-ahead (first-letter navigation) primitive for composite widgets.\n *\n * The APG asks list-like widgets to move focus by typed characters: one\n * character jumps to the next item starting with it, and several characters\n * typed in quick succession narrow the match to that prefix. Getting that right\n * needs three pieces of bookkeeping — accumulating the query, dropping it after\n * an idle window, and resolving the label each candidate is matched against —\n * and single-sourcing them here keeps every widget on the same rules instead of\n * letting them drift apart.\n *\n * The helpers here own *only* that mechanical part. They are deliberately\n * **policy-free** in the same sense as `RovingTabindex`: which elements are\n * candidates, where the search starts, and what happens on a match (real DOM\n * focus, a roving tab stop, or a virtual `aria-activedescendant`) differ per APG\n * pattern, so each controller keeps those decisions and calls\n * {@link findTypeaheadMatch} with a candidate list it assembled itself.\n *\n * Three decisions are worth stating up front:\n *\n * - **A repeated character collapses the stored query**, rather than deriving a\n * shorter one at search time. That is what lets a follow-up character resume\n * narrowing: `s`, `s`, `e` searches `\"se\"`, not the dead `\"sse\"` that no label\n * can match. Growing the query instead leaves the widget frozen on exactly\n * that sequence, which is why the choice is made here and not per widget.\n * - **`aria-label` wins over text**, so type-ahead matches what a screen reader\n * announces — but only when it contributes a name. A blank (empty or\n * whitespace-only) one is skipped, because accname skips it too: honoring it\n * would leave an element that AT announces by its text unreachable by that\n * text. This mirrors the accessible-name computation rather than the raw text.\n * - **`Space` and composition input are never type-ahead.** `Space` natively\n * activates a `<button>`-based item, and characters still being composed are\n * not a committed query.\n *\n * This file's own doc block is dropped from `dist`, but every member comment is\n * inlined into each consumer entry (`tsup` builds with `splitting: false`), so\n * rationale belongs here and contracts belong on the members.\n */\n\nimport { SafeTimeout } from \"./safe_timeout\";\n\n/** Idle window after which an accumulated query is dropped, in milliseconds. */\nexport const TYPEAHEAD_RESET_MS = 500;\n\n/** Construction options for {@link Typeahead}. */\nexport interface TypeaheadOptions {\n /** Idle window before the query resets. Defaults to {@link TYPEAHEAD_RESET_MS}. */\n resetMs?: number;\n}\n\n/**\n * Accumulates typed characters into a search query and drops it when idle.\n *\n * @remarks\n * Owns a {@link SafeTimeout} of its own, so a consumer's `#timers.clearAll()` does\n * **not** reach the pending reset — call {@link Typeahead.reset} from `disconnect()`.\n */\nexport class Typeahead {\n /** Timer registry for the pending idle reset; private so `reset()` is the only exit. */\n readonly #timers = new SafeTimeout();\n /** Idle window before the query resets, in milliseconds. */\n readonly #resetMs: number;\n /** The accumulated lowercase query, empty when idle. */\n #query = \"\";\n /** Id of the pending reset timer, `0` when none is scheduled. */\n #timerId = 0;\n\n /** @param options - Overrides for the idle window. */\n constructor({ resetMs = TYPEAHEAD_RESET_MS }: TypeaheadOptions = {}) {\n this.#resetMs = resetMs;\n }\n\n /** The query a search would currently run with; empty while idle. */\n get query(): string {\n return this.#query;\n }\n\n /**\n * Folds `key` into the query, restarts the idle window, and returns the query to\n * search with. A repeated character collapses the query to that one character.\n */\n push(key: string): string {\n const char = key.toLowerCase();\n const repeated = this.#query.length > 0 && [...this.#query].every((c) => c === char);\n this.#query = repeated ? char : this.#query + char;\n this.#timers.clear(this.#timerId);\n this.#timerId = this.#timers.set(() => this.reset(), this.#resetMs);\n return this.#query;\n }\n\n /** Clears the query and cancels the pending idle reset. */\n reset(): void {\n this.#query = \"\";\n this.#timers.clear(this.#timerId);\n this.#timerId = 0;\n }\n}\n\n/** Whether `event` is a bare printable character usable for type-ahead. */\nexport function isTypeaheadKey(event: KeyboardEvent): boolean {\n return (\n event.key.length === 1 &&\n event.key !== \" \" &&\n !event.ctrlKey &&\n !event.metaKey &&\n !event.altKey &&\n !event.isComposing\n );\n}\n\n/**\n * The name `element` is matched under, normalized for comparison.\n *\n * @param fallbackText - Produces the name when `aria-label` contributes none.\n * Defaults to the element's `textContent`; pass a narrower source when part of\n * the subtree is not part of the name (a tree item's nested child group, say).\n * Called lazily, so an `aria-label` costs no subtree walk.\n */\nexport function typeaheadLabel(element: HTMLElement, fallbackText?: () => string): string {\n // Blank means absent, matching accname: a whitespace-only `aria-label` is\n // skipped there too, so honoring it here would make an element that a screen\n // reader announces by its text unreachable by that text.\n const label = element.getAttribute(\"aria-label\")?.trim();\n if (label) return label.toLowerCase();\n const text = fallbackText ? fallbackText() : (element.textContent ?? \"\");\n return text.trim().toLowerCase();\n}\n\n/**\n * Index of the first item after `from` whose label starts with `query`, wrapping\n * and evaluating `from` itself last, or `-1` when nothing matches.\n *\n * @param items - Candidates in navigation order, already filtered by the caller.\n * @param from - Index the search moves on from; `-1` starts at the first item. Any\n * out-of-range value is folded back in, so `indexOf` results pass straight through.\n * @param query - Lowercase query, normally the return of {@link Typeahead.push}.\n * @param label - Resolves a candidate's name. Defaults to {@link typeaheadLabel}.\n */\nexport function findTypeaheadMatch(\n items: readonly HTMLElement[],\n from: number,\n query: string,\n label: (item: HTMLElement) => string = (item) => typeaheadLabel(item),\n): number {\n // An empty query would prefix-match everything; an empty list never enters the\n // loop, so it needs no guard of its own.\n if (query === \"\") return -1;\n const count = items.length;\n for (let step = 1; step <= count; step += 1) {\n // Folded twice so a negative `from` still lands on a real slot: `%` keeps the\n // sign of its left operand, and a negative index would read `undefined`.\n const index = (((from + step) % count) + count) % count;\n const candidate = items[index];\n if (candidate && label(candidate).startsWith(query)) return index;\n }\n return -1;\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { isReservedArrowChord } from \"../utils/arrow_step\";\nimport { claimsWhileFocusWithin, EscapeLayer } from \"../utils/escape_layer\";\nimport { ownerOf } from \"../utils/event_owner\";\nimport { isRtl } from \"../utils/logical_scroll\";\nimport { RovingTabindex } from \"../utils/roving_tabindex\";\nimport { SafeTimeout } from \"../utils/safe_timeout\";\nimport { findTypeaheadMatch, isTypeaheadKey, Typeahead } from \"../utils/typeahead\";\n\n/** Where to land focus when a menu opens. */\ntype OpenFocus = \"first\" | \"last\";\n\n/**\n * Attributes whose runtime change flips a top item's navigability, watched so the\n * single Tab stop survives a consumer disabling or hiding the item that held it.\n *\n * `tabindex` is deliberately absent — this controller writes it, and observing it\n * would feed back into the observer. `aria-disabled` is absent for a different\n * reason: per the APG it keeps an item focusable and only blocks activation, so\n * it never changes *which* top can hold the Tab stop.\n */\nconst STATE_ATTRIBUTES = [\"disabled\", \"hidden\"];\n\n/**\n * Headless, accessible **menubar** behavior.\n *\n * Markup contract (identifier: `stimeo--menubar`):\n * <div data-controller=\"stimeo--menubar\" role=\"menubar\" aria-label=\"Main\">\n * <button role=\"menuitem\" aria-haspopup=\"menu\" aria-expanded=\"false\"\n * aria-controls=\"m-file\" data-stimeo--menubar-target=\"top\"\n * data-action=\"click->stimeo--menubar#toggle\n * keydown->stimeo--menubar#onTopKeydown\">File</button>\n * <ul id=\"m-file\" role=\"menu\" data-stimeo--menubar-target=\"menu\" hidden>\n * <li role=\"none\">\n * <button role=\"menuitem\" tabindex=\"-1\" data-stimeo--menubar-target=\"item\"\n * data-action=\"click->stimeo--menubar#activate\n * keydown->stimeo--menubar#onItemKeydown\">New</button>\n * </li>\n * </ul>\n * <!-- more top items + menus -->\n * </div>\n *\n * Implements the WAI-ARIA APG **Menubar** pattern (single level): the top items\n * form one Tab stop via roving tabindex and the arrow keys move between them;\n * `ArrowDown`/`Enter`/`Space` open a menu (`ArrowUp` opens it at the last item),\n * the arrow keys then move within the menu, and pressing `ArrowLeft`/`ArrowRight`\n * while a menu is open jumps to the adjacent top menu. `Escape` closes and\n * returns focus to the owning top item — while a menu is open the menubar is a\n * layer on the shared `EscapeLayer` stack, claiming a press only while\n * focus is inside the controller or fell to the body, so one keypress closes\n * exactly one layer. `Tab` and an outside click close.\n *\n * @remarks\n * Behavior only. Each top item↔menu pair is linked by `aria-controls`/`id` (not by\n * position), so *pairing* does not depend on the markup order. Where the order does\n * show is native `Tab`/`Shift+Tab`: the browser walks the DOM, so `Shift+Tab` out of\n * a menu lands on the owning top item only while the menu follows it — write the\n * conventional owner-then-menu order to get the destinations described here. Menu\n * placement and viewport-edge collision are out of scope — static placement is the\n * consumer's CSS, and dynamic placement is delegated to the opt-in\n * `stimeo-ui/positioning` module (never imported here, keeping the core\n * zero-dependency). Roving mechanics across the top items are delegated to\n * `RovingTabindex`.\n *\n * Focus-restoration contract: closing via `Escape` or an item activation returns\n * focus to the owning top item; `Tab` and an outside click deliberately do **not**\n * — they close where the user is already heading, and yanking focus back would\n * fight the pointer/Tab move.\n *\n * Disabledness follows the APG's two-attribute split, which is the consumer's way\n * of saying which one they mean:\n * - **`hidden` / natively `disabled`** — removed from the roving set entirely, so\n * the lone Tab stop never lands on an unfocusable top item (which would drop the\n * whole menubar out of the Tab order) and the arrow keys never stall on it.\n * - **`aria-disabled=\"true\"`** — still reached by the arrow keys (\"disabled menu\n * items are focusable but cannot be activated\"), so a screen-reader user can\n * discover it; only *activation* is suppressed. Clicks on it — including the one\n * a browser synthesizes for Enter/Space — are swallowed before consumer handlers\n * run, and an `aria-disabled` top never opens its menu, since opening the popup\n * is activation. Moving onto it still closes whatever menu was open, which the\n * APG makes unconditional for a horizontal move.\n *\n * Arrow directions are logical: `ArrowRight` moves to the next top item under LTR\n * and to the previous one under RTL, on the bar and from inside an open menu. A top\n * item may also be a **plain command** with no `aria-controls` at all: moving to it\n * behaves the same way — the open menu closes and it takes the roving focus. A top\n * item whose `aria-controls` names no menu target is broken markup instead, and the\n * open/closed state is left alone rather than acted on by guesswork.\n *\n * A menu with no navigable items still opens (its `aria-expanded` flips and focus\n * stays on the top item) so a consumer that fills a menu asynchronously keeps\n * working; `Escape` or a second click closes it. While such a menu is\n * structurally empty the consumer **must** carry `aria-busy=\"true\"` on it and drop\n * that once the items land — `role=\"menu\"` requires owned `menuitem`s, and this\n * controller cannot tell \"still loading\" from \"nothing to show\", so it never\n * infers the state. A menu whose items exist but are all inert is not busy.\n *\n * Runtime DOM edits (Turbo Streams, a consumer swapping menus in) are reconciled\n * from the live targets: expanded flags, menu visibility, the single Tab stop, and\n * Escape-stack membership are re-derived whenever a top/menu target is added or\n * removed, and whenever `disabled`/`hidden` changes under the menubar.\n */\nexport class MenubarController extends Controller<HTMLElement> {\n static override targets = [\"top\", \"menu\", \"item\"];\n static actions = [\"activate\", \"onItemKeydown\", \"onTopKeydown\", \"toggle\"] as const;\n\n declare readonly topTargets: HTMLButtonElement[];\n declare readonly menuTargets: HTMLElement[];\n declare readonly itemTargets: HTMLButtonElement[];\n\n /** Roving tabindex across the top-level menuitems (one Tab stop). */\n readonly #roving = new RovingTabindex(() => this.topTargets);\n /** Typeahead query and its idle-reset timer (scoped to the open menu). */\n readonly #typeahead = new Typeahead();\n readonly #timers = new SafeTimeout();\n\n /** Escape-stack membership while a menu is open; the shared resolver dismisses via it. */\n readonly #escapeLayer = new EscapeLayer();\n /** Whether {@link #escapeLayer} is currently on the stack (see {@link #syncEscapeLayer}). */\n #layerActive = false;\n\n /** Watches `disabled`/`hidden` under the menubar; `null` while disconnected. */\n #observer: MutationObserver | null = null;\n /**\n * Live between `connect()` and `disconnect()`. Stimulus reports the *initial*\n * targets before `connect()` and re-reports them as disconnected afterwards;\n * gating on this keeps the target callbacks from clobbering the authored Tab\n * stop on mount and from resurrecting one after teardown.\n */\n #connected = false;\n\n /** The element inside the menubar that last took DOM focus, if any. */\n #focused: HTMLElement | null = null;\n\n /**\n * Records where DOM focus sits inside the menubar. `focusin` bubbles, so one\n * listener covers the top items and everything nested in their menus. The\n * record is needed because a top or menu becomes unreachable *before* this\n * controller hears about it, by which point the browser has already reset\n * `document.activeElement`.\n */\n readonly #onFocusIn = (event: FocusEvent): void => {\n this.#focused = event.target as HTMLElement | null;\n };\n\n /**\n * Forgets the record once focus genuinely lands on something else, so a later\n * mutation cannot pull focus back in. A `relatedTarget` of `null` is the case\n * that must be kept: it is what \"focus went nowhere\" looks like, which is\n * exactly the state the rescue exists for.\n */\n readonly #onFocusOut = (event: FocusEvent): void => {\n const next = event.relatedTarget as HTMLElement | null;\n if (next && !this.element.contains(next)) this.#focused = null;\n };\n\n /**\n * Establishes the closed baseline and the single tab stop: keep an existing tab\n * stop when it is still navigable (so a Turbo cache restore preserves the user's\n * position), else fall back to the first navigable top item.\n *\n * The outside-click listener is registered in the **capture** phase: in the\n * bubble phase an inside handler that removes its own click target detaches the\n * node before `contains()` runs, so an *inside* click would read as outside and\n * close the menu.\n */\n override connect(): void {\n this.#closeAllMenus();\n this.#reconcile();\n this.element.addEventListener(\"click\", this.#onDisabledClickCapture, true);\n this.element.addEventListener(\"focusin\", this.#onFocusIn);\n this.element.addEventListener(\"focusout\", this.#onFocusOut);\n document.addEventListener(\"click\", this.#onOutsideClick, true);\n if (typeof MutationObserver !== \"undefined\") {\n this.#observer = new MutationObserver(() => this.#reconcile());\n this.#observer.observe(this.element, {\n subtree: true,\n childList: true,\n attributes: true,\n attributeFilter: STATE_ATTRIBUTES,\n });\n }\n this.#connected = true;\n }\n\n /** Removes the listeners, stack membership, and any pending timer (typeahead / Tab close). */\n override disconnect(): void {\n this.#connected = false;\n this.#escapeLayer.deactivate();\n this.#layerActive = false;\n this.element.removeEventListener(\"click\", this.#onDisabledClickCapture, true);\n this.element.removeEventListener(\"focusin\", this.#onFocusIn);\n this.element.removeEventListener(\"focusout\", this.#onFocusOut);\n document.removeEventListener(\"click\", this.#onOutsideClick, true);\n this.#focused = null;\n this.#observer?.disconnect();\n this.#observer = null;\n // The typeahead owns its own timer registry, so `#timers.clearAll()` does not\n // reach its pending reset — drop it explicitly or it outlives the element.\n this.#typeahead.reset();\n this.#timers.clearAll();\n }\n\n /**\n * A top item added at runtime is dropped out of the Tab sequence first — a fresh\n * `<button>` is tabbable by default, which would leave the menubar with two Tab\n * stops — before the lone stop is re-established.\n */\n topTargetConnected(top: HTMLButtonElement): void {\n if (!this.#connected) return;\n top.tabIndex = -1;\n this.#reconcile();\n }\n\n /**\n * Removing a top item can strand two things: the Tab stop it held, and the menu\n * it owned (which nothing could close afterwards).\n *\n * The departing element is handed back in a neutral state first. A target can\n * leave without leaving the document — a morph that only drops the\n * `data-*-target` token keeps the node — and it would then sit in the page\n * carrying this controller's `tabindex=\"0\"` (a second Tab stop next to the one\n * re-established below) and an `aria-expanded=\"true\"` nothing can collapse.\n * When the node really is gone these writes are harmless no-ops.\n */\n topTargetDisconnected(top: HTMLButtonElement): void {\n if (!this.#connected) return;\n top.tabIndex = -1;\n if (this.#isExpanded(top)) top.setAttribute(\"aria-expanded\", \"false\");\n this.#reconcile();\n }\n\n /** See {@link MenubarController.menuTargetDisconnected}. */\n menuTargetConnected(): void {\n if (!this.#connected) return;\n this.#reconcile();\n }\n\n /**\n * A menu removed while open leaves its owner claiming `aria-expanded=\"true\"` for\n * a popup that no longer exists, and the menubar holding an Escape layer that\n * would swallow presses meant for something else.\n *\n * As with {@link MenubarController.topTargetDisconnected}, the departing menu is\n * closed first: a token-only removal leaves a visible popup in the page that no\n * key and no click can dismiss any more.\n */\n menuTargetDisconnected(menu: HTMLElement): void {\n if (!this.#connected) return;\n if (!menu.hidden) menu.hidden = true;\n this.#reconcile();\n }\n\n /** Toggles a top item's menu. Bound via `data-action` (click on the top item). */\n toggle(event: Event): void {\n const top = event.currentTarget as HTMLButtonElement;\n if (this.#isExpanded(top)) {\n this.#closeMenu(top);\n } else {\n this.#openMenu(top, \"first\");\n }\n }\n\n /** Keyboard handling while focus is on a top item. */\n onTopKeydown(event: KeyboardEvent): void {\n // A descendant widget that already claimed the key (a grabbed drag handle, an\n // inline editor) must not ALSO move the roving focus — composition depends on\n // this yield.\n if (event.defaultPrevented) return;\n if (isReservedArrowChord(event)) return;\n const tops = this.#navigableTops;\n const index = tops.indexOf(event.currentTarget as HTMLButtonElement);\n if (index === -1) return;\n const length = tops.length;\n // Whether a menu is currently open governs whether horizontal moves *open*\n // the adjacent menu or merely move the roving focus between top items.\n const anyOpen = this.#isAnyOpen;\n\n // Logical, not physical. APG defines these as \"next / previous control\", and\n // says a vertical arrangement swaps in Down/Up for the same meaning — so the\n // pair is one axis's spelling of an order, and the order reverses with the\n // writing direction. Read from the controller element: the container is what\n // lays the items out, and a child may carry its own `dir` (an LTR input inside\n // an RTL form is ordinary authoring).\n const step = isRtl(this.element) ? -1 : 1;\n\n switch (event.key) {\n case \"ArrowRight\":\n event.preventDefault();\n this.#gotoTop(tops[(index + step + length) % length], anyOpen);\n break;\n case \"ArrowLeft\":\n event.preventDefault();\n this.#gotoTop(tops[(index - step + length) % length], anyOpen);\n break;\n case \"ArrowDown\":\n event.preventDefault();\n this.#openMenu(tops[index], \"first\");\n break;\n case \"ArrowUp\":\n event.preventDefault();\n this.#openMenu(tops[index], \"last\");\n break;\n case \"Home\":\n event.preventDefault();\n this.#gotoTop(tops[0], anyOpen);\n break;\n case \"End\":\n event.preventDefault();\n this.#gotoTop(tops[length - 1], anyOpen);\n break;\n case \"Tab\":\n // Focus is leaving the menubar: close, but let the browser's own Tab move\n // run first (see #closeMenusSoon).\n this.#closeMenusSoon();\n break;\n default:\n break;\n }\n // Enter/Space are intentionally left to the native button click, which runs\n // toggle(): it opens the menu at its first item, or closes it when that menu\n // is already open. Handling them here would double-fire.\n // Escape is owned by the shared EscapeLayer resolver (see #dismissOpenMenu).\n }\n\n /** Keyboard handling while focus is on a menu item. */\n onItemKeydown(event: KeyboardEvent): void {\n // See onTopKeydown: a key a descendant already consumed is not ours.\n if (event.defaultPrevented) return;\n if (isReservedArrowChord(event)) return;\n const item = event.currentTarget as HTMLButtonElement;\n const menu = item.closest<HTMLElement>(\"[role='menu']\");\n if (!menu) return;\n const items = this.#itemsIn(menu);\n // -1 when the key came from an item outside this menu's navigable set:\n // ArrowDown then lands on the first item, ArrowUp on the second-to-last.\n const index = items.indexOf(item);\n const length = items.length;\n\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault();\n this.#focusAt(items, (index + 1) % length);\n break;\n case \"ArrowUp\":\n event.preventDefault();\n this.#focusAt(items, (index - 1 + length) % length);\n break;\n case \"Home\":\n event.preventDefault();\n this.#focusAt(items, 0);\n break;\n case \"End\":\n event.preventDefault();\n this.#focusAt(items, length - 1);\n break;\n case \"ArrowRight\":\n event.preventDefault();\n this.#moveToAdjacentMenu(menu, isRtl(this.element) ? -1 : 1);\n break;\n case \"ArrowLeft\":\n event.preventDefault();\n this.#moveToAdjacentMenu(menu, isRtl(this.element) ? 1 : -1);\n break;\n case \"Tab\":\n // Per APG, Tab closes the menubar but lets focus move on naturally.\n this.#closeMenusSoon();\n break;\n default:\n if (isTypeaheadKey(event)) {\n event.preventDefault();\n this.#typeaheadTo(items, index, event.key);\n }\n break;\n }\n // Enter/Space activate the item via its native button click → activate().\n }\n\n /** Closes the owning menu after an item is activated and refocuses its top. */\n activate(event: Event): void {\n const item = event.currentTarget as HTMLElement;\n const menu = item.closest<HTMLElement>(\"[role='menu']\");\n const top = this.#topFor(menu);\n this.#closeAllMenus();\n this.#focusTop(top);\n }\n\n /** Moves the roving focus to `top`, opening its menu when one was open. */\n #gotoTop(top: HTMLButtonElement | undefined, reopen: boolean): void {\n if (!top) return;\n if (reopen) {\n this.#openMenu(top, \"first\");\n } else {\n // The roving index is resolved against the full target list, since that is\n // what carries the tabindex bookkeeping.\n this.#roving.setActive(this.topTargets.indexOf(top), { focus: true });\n }\n }\n\n /**\n * Opens `top`'s menu (closing others) and focuses its first/last item.\n *\n * Three kinds of top item never open a menu:\n * - **`aria-disabled`** — focusable but never activated, and opening a popup is\n * activation.\n * - **plain command** (no `aria-controls` at all) — a legitimate top item that\n * simply has no popup.\n * - **dangling `aria-controls`** (names a menu that is not a target) — broken\n * markup.\n *\n * The first two still take the roving focus, and any open menu closes: the APG\n * makes closing unconditional for the horizontal move (\"closes the submenu,\n * moves focus to the next menubar item, and *optionally* opens that item's\n * submenu\"), so only the opening half is skipped. Leaving the old menu open\n * would strand a popup that no longer contains focus.\n *\n * The dangling case instead leaves the open/closed state and focus untouched\n * rather than closing everything and dropping focus to the body — but only on\n * this \"open a menu\" path. A dangling top is still an ordinary roving\n * destination while nothing is open.\n */\n #openMenu(top: HTMLButtonElement | null | undefined, focus: OpenFocus): void {\n if (!top) return;\n // Resolve the menu *before* touching any state, so the dangling case below\n // can bail out without having changed anything.\n const menu = this.#menuFor(top);\n if (!menu || this.#isActivationBlocked(top)) {\n // Broken markup (an `aria-controls` that resolves to nothing) is the one\n // case that must not act on a guess.\n if (menu || !top.hasAttribute(\"aria-controls\")) {\n this.#closeAllMenus();\n this.#focusTop(top);\n }\n return;\n }\n // A reopen must discard a pending Tab close, or that stale task would slam\n // the freshly opened menu shut on the next tick.\n this.#timers.clearAll();\n this.#closeAllMenus();\n menu.hidden = false;\n top.setAttribute(\"aria-expanded\", \"true\");\n this.#escapeLayer.activate(document, {\n onDismiss: () => this.#dismissOpenMenu(),\n claims: claimsWhileFocusWithin(this.element),\n });\n // Opening always re-registers, which puts this layer back on top of the\n // stack — the menubar is the innermost thing the user just interacted with.\n this.#layerActive = true;\n this.#roving.setActive(this.topTargets.indexOf(top));\n const items = this.#itemsIn(menu);\n this.#focusAt(items, focus === \"first\" ? 0 : items.length - 1);\n }\n\n /**\n * Hides `top`'s menu and reflects the collapsed state. A top item that controls\n * no menu (a plain command in an otherwise popup-bearing menubar) is left alone\n * — stamping `aria-expanded=\"false\"` on it would announce a popup it lacks.\n */\n #closeMenu(top: HTMLButtonElement | null): void {\n if (!top) return;\n const menu = this.#menuFor(top);\n if (!menu) return;\n menu.hidden = true;\n top.setAttribute(\"aria-expanded\", \"false\");\n this.#syncEscapeLayer();\n }\n\n /** Closes every menu and resets the typeahead query. */\n #closeAllMenus(): void {\n for (const top of this.topTargets) this.#closeMenu(top);\n this.#typeahead.reset();\n }\n\n /**\n * Closes every menu on the next task instead of synchronously.\n *\n * Used by `Tab`: closing right away removes the focused element before the\n * browser performs its own Tab move, which can restart traversal at the\n * document head and lose the user's place in the Tab order.\n */\n #closeMenusSoon(): void {\n if (!this.#isAnyOpen) return;\n this.#timers.clearAll();\n this.#timers.set(() => this.#closeAllMenus(), 0);\n }\n\n /**\n * Escape path, invoked by the shared resolver: pressed inside an open menu it\n * closes that menu and returns focus to its top item (the APG behavior);\n * pressed while focus is on a top item (or fell to the body) it closes every\n * menu without moving focus.\n */\n #dismissOpenMenu(): void {\n const menu = ownerOf(this.menuTargets, document.activeElement);\n if (menu) {\n const top = this.#topFor(menu);\n this.#closeMenu(top);\n this.#focusTop(top);\n return;\n }\n this.#closeAllMenus();\n }\n\n /** Opens the menu of the navigable top item `delta` steps from `menu`'s owner. */\n #moveToAdjacentMenu(menu: HTMLElement, delta: number): void {\n const top = this.#topFor(menu);\n if (!top) return;\n const tops = this.#navigableTops;\n const current = tops.indexOf(top);\n if (current === -1) return;\n const next = (current + delta + tops.length) % tops.length;\n this.#openMenu(tops[next], \"first\");\n }\n\n /**\n * Closes when a click lands outside the controller's element. Focus is left on\n * whatever the user clicked — see the focus-restoration contract in the class\n * docs.\n */\n readonly #onOutsideClick = (event: MouseEvent): void => {\n if (this.#isAnyOpen && !this.element.contains(event.target as Node)) this.#closeAllMenus();\n };\n\n /**\n * Captures clicks so `aria-disabled` top items and commands cannot reach\n * consumer handlers (nor `toggle`/`activate`). Native Enter/Space activation\n * also synthesizes a click and is blocked here; natively `disabled` buttons\n * dispatch no click at all.\n */\n readonly #onDisabledClickCapture = (event: MouseEvent): void => {\n const target = event.target;\n if (!(target instanceof Node)) return;\n const blocked = [...this.topTargets, ...this.itemTargets].some(\n (element) => this.#isActivationBlocked(element) && element.contains(target),\n );\n if (!blocked) return;\n event.preventDefault();\n event.stopImmediatePropagation();\n };\n\n /**\n * Re-derives every piece of state this controller owns from the live targets:\n * expanded flags, menu visibility, the single Tab stop, and Escape-stack\n * membership. Runtime DOM edits can leave those out of step with each other — a\n * top can claim `aria-expanded=\"true\"` after its menu target was removed or after\n * being hidden itself, a menu can stay visible after its owning top was removed,\n * an open pair can appear from a morph with no layer registered for it, and the\n * Tab stop can end up on a now-inert top, on a runtime-added one, or on none at\n * all. The only state carried between calls is the tracked focus record and which side\n * of the Escape stack this layer is on (which the stack itself does not expose), so the outcome is the\n * same whichever mutation arrived and calling it more often than needed is free.\n */\n #reconcile(): void {\n for (const top of this.topTargets) {\n // A top is expanded only while its own menu resolves *and* is visible.\n // Both halves matter: the menu target can be removed outright, and a Turbo\n // Stream `replace` swaps it for a fresh (contract-`hidden`) one — which\n // still resolves, so a \"did it disappear?\" test alone would leave the top\n // claiming a popup nobody can see, and the Escape layer registered with it.\n // Only tops that already declare the attribute are touched: stamping\n // `aria-expanded` on a plain command would announce a popup it lacks.\n // The owner must also still be reachable: a consumer that `hidden`s or\n // natively disables an expanded top would otherwise leave its popup on\n // screen with no way back to the element that owns it.\n const own = this.#menuFor(top);\n if (this.#isExpanded(top) && (!own || own.hidden || !this.#isNavigable(top))) {\n top.setAttribute(\"aria-expanded\", \"false\");\n }\n }\n for (const menu of this.menuTargets) {\n if (menu.hidden) continue;\n // A menu with no expanded owner can never be closed by the user again.\n const owner = this.#topFor(menu);\n if (!owner || !this.#isExpanded(owner)) menu.hidden = true;\n }\n this.#ensureTabStop();\n this.#rescueFocus();\n this.#syncEscapeLayer();\n }\n\n /**\n * Returns DOM focus to the menubar when the element holding it became\n * unreachable — hidden, natively disabled, or removed — and the document had\n * nowhere to put it. The destination is the top item that now owns the Tab\n * stop, which is where `Escape` from a closed menu would have left the user.\n *\n * Focus is only *restored*, never *stolen*: it moves solely when the tracked\n * element can no longer take it **and** focus is either already gone or still\n * sitting on that element. The second half is what makes a `hidden` ancestor\n * work — a browser blurs the element it hides on its next style pass, not when\n * the attribute is written, so waiting to observe `<body>` here would always\n * come too early. Removal is the other side of the same condition: the browser\n * has already fallen back to the body, or left a detached `activeElement`.\n */\n #rescueFocus(): void {\n const focused = this.#focused;\n if (!focused) return;\n // An ordinary close — Escape, Tab, an outside click — also leaves the focused\n // menu item inside a hidden subtree, but nothing went away and the user is\n // already on their way somewhere. Only an owner that can no longer be reached\n // is a reason to move focus, so the owner decides, not the item.\n const owner = this.topTargets.find(\n (top) => top === focused || this.#menuFor(top)?.contains(focused),\n );\n if (focused.isConnected && owner && this.#isNavigable(owner)) return;\n const doc = this.element.ownerDocument;\n const active = doc.activeElement;\n const lost =\n active === null ||\n active === doc.body ||\n active === doc.documentElement ||\n !active.isConnected ||\n active === focused ||\n focused.contains(active);\n if (!lost) return;\n const index = this.#roving.activeIndex;\n const top = index === -1 ? undefined : this.topTargets[index];\n if (top && top !== focused && !focused.contains(top)) top.focus();\n }\n\n /**\n * Brings Escape-stack membership back in line with what the DOM now says is\n * open, in both directions.\n *\n * The deactivate half is the common one (something closed). The activate half\n * covers a menu that became open *without* going through {@link #openMenu} —\n * a Turbo morph that patches `aria-expanded` and the menu's `hidden` in place,\n * which is legitimate here because the DOM is this controller's only source of\n * truth. Without it the popup is visible but `Escape` does nothing, since no\n * layer is registered to claim the press.\n *\n * `#layerActive` exists because re-activating an already-active layer moves it\n * to the top of the stack, which would reshuffle nested layers on every\n * unrelated mutation. Registering only on the false→true edge keeps activation\n * ordered by when each layer actually opened.\n */\n #syncEscapeLayer(): void {\n const open = this.#isAnyOpen;\n if (open === this.#layerActive) return;\n if (open) {\n this.#escapeLayer.activate(document, {\n onDismiss: () => this.#dismissOpenMenu(),\n claims: claimsWhileFocusWithin(this.element),\n });\n } else {\n this.#escapeLayer.deactivate();\n }\n this.#layerActive = open;\n }\n\n /**\n * Re-establishes the single Tab stop: keep the current one while it is still\n * navigable, else hand it to the first navigable top item. Every other top is\n * explicitly removed from the Tab sequence, so a top that arrived tabbable\n * cannot leave two stops behind. No Tab stop at all (`-1`) happens only when\n * every top is inert, and is recovered from as soon as one becomes navigable.\n */\n #ensureTabStop(): void {\n const active = this.#roving.activeIndex;\n const activeTop = active === -1 ? undefined : this.topTargets[active];\n if (activeTop && this.#isNavigable(activeTop)) {\n this.#roving.setActive(active);\n return;\n }\n const first = this.#navigableTops[0];\n this.#roving.setActive(first ? this.topTargets.indexOf(first) : -1);\n }\n\n /** The menu element controlled by `top` (matched by `aria-controls`/`id`). */\n #menuFor(top: HTMLElement): HTMLElement | null {\n const id = top.getAttribute(\"aria-controls\");\n // Resolve against this controller's own menu targets (not a global id lookup)\n // so it stays scoped to this menubar instance.\n return id ? (this.menuTargets.find((menu) => menu.id === id) ?? null) : null;\n }\n\n /** The top item that controls `menu` (reverse of `#menuFor`). */\n #topFor(menu: HTMLElement | null): HTMLButtonElement | null {\n if (!menu) return null;\n return this.topTargets.find((top) => top.getAttribute(\"aria-controls\") === menu.id) ?? null;\n }\n\n /** The navigable item targets that live inside `menu`, in DOM order. */\n #itemsIn(menu: HTMLElement): HTMLButtonElement[] {\n return this.itemTargets.filter((item) => menu.contains(item) && this.#isNavigable(item));\n }\n\n /** Top items eligible for the roving tab stop and the arrow keys. */\n get #navigableTops(): HTMLButtonElement[] {\n return this.topTargets.filter((top) => this.#isNavigable(top));\n }\n\n /**\n * Whether `element` can take roving focus: `hidden` and natively `disabled`\n * controls are out of reach and are skipped. `aria-disabled` is deliberately\n * **not** checked — the APG keeps such items focusable so they stay\n * discoverable, and {@link MenubarController.#isActivationBlocked} is what\n * suppresses acting on them. CSS-only visibility is not detectable headlessly\n * and stays the consumer's responsibility.\n */\n #isNavigable(element: HTMLButtonElement): boolean {\n if (element.hasAttribute(\"hidden\")) return false;\n return !element.disabled;\n }\n\n /** Whether `element` is `aria-disabled`: reachable, but never activated. */\n #isActivationBlocked(element: Element): boolean {\n return element.getAttribute(\"aria-disabled\") === \"true\";\n }\n\n /** Moves DOM focus to the item at `index` (no-op if out of range). */\n #focusAt(items: HTMLElement[], index: number): void {\n items[index]?.focus();\n }\n\n /** Returns roving focus to a top item (and makes it the single tab stop). */\n #focusTop(top: HTMLButtonElement | null): void {\n if (!top) return;\n this.#roving.setActive(this.topTargets.indexOf(top), { focus: true });\n }\n\n /** Whether `top`'s menu is currently expanded. */\n #isExpanded(top: HTMLElement): boolean {\n return top.getAttribute(\"aria-expanded\") === \"true\";\n }\n\n /** Whether any top item's menu is currently open. */\n get #isAnyOpen(): boolean {\n return this.topTargets.some((top) => this.#isExpanded(top));\n }\n\n /** Advances focus to the next item in `items` matching the accumulated query. */\n #typeaheadTo(items: HTMLElement[], current: number, key: string): void {\n const index = findTypeaheadMatch(items, current, this.#typeahead.push(key));\n if (index !== -1) items[index]?.focus();\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/utils/state_reason.ts","../../src/utils/action_source.ts","../../src/utils/logical_scroll.ts","../../src/utils/arrow_step.ts","../../src/utils/shared_registry.ts","../../src/utils/escape_layer.ts","../../src/utils/event_owner.ts","../../src/utils/listener_set.ts","../../src/utils/microtask_coalescer.ts","../../src/utils/morph_render_watcher.ts","../../src/utils/roving_tabindex.ts","../../src/utils/safe_timeout.ts","../../src/utils/target_selector.ts","../../src/utils/typeahead.ts","../../src/controllers/menubar_controller.ts"],"names":["top"],"mappings":"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","file":"menubar_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","/** 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 * 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\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 * 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. {@link\n * 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 * 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, or one that\n * lives for less than the connection, keeps its own pair of calls.\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, which has to match at release time and no\n * longer has a second place to drift from.\n */\n add(\n target: EventTarget,\n type: string,\n handler: EventListener,\n options?: Omit<AddEventListenerOptions, \"signal\">,\n ): 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 * Collapses many Stimulus lifecycle callbacks from one DOM mutation into a\n * single pass.\n *\n * Stimulus fires `<name>TargetConnected` / `Disconnected` once per element and\n * `<name>ValueChanged` once per changed attribute. Replacing a list of N options\n * or morphing several render Values therefore delivers N callbacks — but the\n * useful unit of work is \"reconcile against the resulting declarative input\",\n * once, after the batch has settled. Every controller with reconcilable targets\n * or render Values needs the same shape: a `queued` flag plus `queueMicrotask`.\n *\n * **A microtask is the right horizon, and the reason is specific.** Stimulus\n * drives these callbacks from a `MutationObserver`, whose own callback already\n * runs as a microtask with the whole batch in hand; scheduling one more lands\n * after the last sibling callback of that batch and still before paint or any\n * event handler. A timer would be later than it needs to be, and reconciling\n * synchronously would run once per element against a half-applied DOM.\n *\n * **The two guards are not the same guard.** Scheduling is refused before the\n * controller connects, and running is refused after it disconnects:\n *\n * - **Before `connect()`** — Stimulus delivers initial target and Value callbacks\n * ahead of `connect()`. Reconciling there would compute output against a\n * controller whose own state has not been initialised, and `connect()` is\n * about to do a full pass anyway.\n * - **After `disconnect()`** — Stimulus fires a callback for **every** target\n * during teardown, and a microtask queued just before it would otherwise run\n * against a detached tree. {@link MicrotaskCoalescer.cancel} exists for the\n * teardown path to drop the pending pass outright.\n *\n * Both guards are part of one contract here rather than something each consumer\n * has to remember separately.\n *\n * Scope is the scheduling only. *What* to reconcile — keep the surviving active\n * option, fall back to the next / previous / first visible one, rebuild derived\n * chips or hidden fields — stays in the controller, because no two consumers\n * answer it the same way.\n *\n * @example\n * ```ts\n * readonly #reconcile = new MicrotaskCoalescer(() => this.#reconcileOptions());\n *\n * connect() { this.#reconcile.activate(); }\n * disconnect() { this.#reconcile.cancel(); }\n *\n * optionTargetConnected() { this.#reconcile.schedule(); }\n * optionTargetDisconnected() { this.#reconcile.schedule(); }\n * ```\n */\nexport class MicrotaskCoalescer {\n readonly #run: () => void;\n #queued = false;\n #active = false;\n #generation = 0;\n\n /** @param run - the single reconciliation pass, invoked at most once per batch. */\n constructor(run: () => void) {\n this.#run = run;\n }\n\n /** Opens the window in which {@link schedule} is honoured; call from `connect()`. */\n activate(): void {\n this.#active = true;\n }\n\n /** Closes the window and drops any pending pass; call from `disconnect()`. */\n cancel(): void {\n this.#active = false;\n this.#queued = false;\n this.#generation += 1;\n }\n\n /** Requests one pass after the batch settles. Idempotent; inert outside the window. */\n schedule(): void {\n if (!this.#active || this.#queued) return;\n this.#queued = true;\n const generation = this.#generation;\n queueMicrotask(() => {\n // A cancelled callback must not consume a pass queued after reconnect.\n if (generation !== this.#generation || !this.#queued || !this.#active) return;\n this.#queued = false;\n this.#run();\n });\n }\n}\n","import { ListenerSet } from \"./listener_set\";\nimport { MicrotaskCoalescer } from \"./microtask_coalescer\";\n\n/**\n * Coalesces retained-element morphs and explicit lifecycle requests into one pass.\n *\n * Observe the controller's element after its initial state is settled. Morphs on\n * that element and bubbling morphs on its descendants share the same microtask\n * as {@link schedule}. The callback repairs derived output; the controller owns\n * any logical transition reporting. {@link disconnect} releases the listener and\n * cancels pending work, including work from an earlier connected lifetime.\n */\nexport class MorphRenderWatcher {\n readonly #listeners = new ListenerSet();\n readonly #pass: MicrotaskCoalescer;\n #element: Element | null = null;\n readonly #onMorph = (): void => this.#pass.schedule();\n\n /** @param run - the single pass that repairs the settled controller's output. */\n constructor(run: () => void) {\n this.#pass = new MicrotaskCoalescer(run);\n }\n\n /** Opens observation; observing the same element again preserves queued work. */\n observe(element: Element): void {\n if (this.#element === element) return;\n this.disconnect();\n this.#element = element;\n this.#pass.activate();\n this.#listeners.add(element, \"turbo:morph-element\", this.#onMorph);\n }\n\n /** Requests one pass; requests outside an observed lifetime are ignored. */\n schedule(): void {\n this.#pass.schedule();\n }\n\n /** Releases observation and pending work synchronously; safe to repeat. */\n disconnect(): void {\n this.#listeners.dispose();\n this.#pass.cancel();\n this.#element = null;\n }\n}\n","/**\n * Low-level roving-tabindex primitive shared by composite-widget controllers.\n *\n * The APG roving-tabindex pattern keeps a composite widget a single Tab stop:\n * exactly one item is in the Tab sequence (`tabindex=\"0\"`) while the rest are\n * removed from it (`tabindex=\"-1\"`), and the arrow keys move both DOM focus and\n * that single tabbable position together. {@link RovingTabindex} owns *only* that\n * mechanical bookkeeping — \"which one item is tabbable, and move focus there\".\n *\n * It is intentionally **policy-free**. Orientation, wrapping vs. clamping,\n * selection-follows-focus, typeahead, and `Home`/`End` semantics differ per APG\n * pattern (Radio Group, Toolbar, Rating, …); folding them into one helper would\n * flatten those widgets to a lowest common denominator and lose each pattern's\n * correctness. Those decisions therefore stay in each controller, which calls\n * {@link RovingTabindex.setActive} with an index it computed itself (optionally\n * via the pure {@link rovingMove} helper).\n *\n * @remarks\n * Items are read lazily through a getter so a controller can add or remove\n * targets (Stimulus re-scans the DOM) without re-wiring this helper. A caller\n * that already captured one event-scoped snapshot may pass it to\n * {@link RovingTabindex.setActive}, avoiding a second live-target scan while\n * preserving one coherent collection for the whole operation.\n */\nexport class RovingTabindex {\n /** Returns the current ordered item elements; called on every operation. */\n readonly #getItems: () => HTMLElement[];\n\n /**\n * @param getItems - Returns the current ordered item elements. Called on every\n * operation so the live target list is always used.\n */\n constructor(getItems: () => HTMLElement[]) {\n this.#getItems = getItems;\n }\n\n /** Index of the currently tabbable item (`tabindex=\"0\"`), or `-1` if none. */\n get activeIndex(): number {\n return this.#getItems().findIndex((item) => item.tabIndex === 0);\n }\n\n /**\n * Makes exactly the item at `index` tabbable (`tabindex=\"0\"`) and removes every\n * other item from the Tab sequence (`tabindex=\"-1\"`). An out-of-range `index`\n * (e.g. `-1`) leaves all items at `-1`, which a controller can use to express\n * \"nothing is currently tabbable\".\n *\n * @param index - Position of the item to make tabbable.\n * @param options - Pass `{ focus: true }` to also move DOM focus to that item,\n * and `items` to reuse an event-scoped collection snapshot.\n */\n setActive(\n index: number,\n options: { focus?: boolean; items?: readonly HTMLElement[] } = {},\n ): void {\n const { focus = false } = options;\n const items = options.items ?? this.#getItems();\n items.forEach((item, i) => {\n item.tabIndex = i === index ? 0 : -1;\n });\n if (focus) items[index]?.focus();\n }\n}\n\n/** Edge behavior for {@link rovingMove}: cycle past the ends, or stop at them. */\nexport type RovingWrap = \"wrap\" | \"clamp\";\n\n/**\n * Pure helper that resolves the target index for a one-step directional move.\n *\n * Keyboard/orientation mapping stays in the caller: it decides that a key means\n * `delta` `+1` (next) or `-1` (previous) and whether the widget should `\"wrap\"`\n * (Radio Group, Toolbar) or `\"clamp\"` at the ends.\n *\n * @param current - The index focus is moving from.\n * @param length - Number of items in the set.\n * @param delta - `+1` to move to the next item, `-1` for the previous.\n * @param wrap - `\"wrap\"` cycles around the ends; `\"clamp\"` stops at them.\n * @returns The resolved index, or `-1` when there are no items.\n */\nexport function rovingMove(\n current: number,\n length: number,\n delta: number,\n wrap: RovingWrap,\n): number {\n if (length === 0) return -1;\n const next = current + delta;\n if (wrap === \"wrap\") return (next + length) % length;\n return Math.min(length - 1, Math.max(0, next));\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\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","/**\n * Shared type-ahead (first-letter navigation) primitive for composite widgets.\n *\n * The APG asks list-like widgets to move focus by typed characters: one\n * character jumps to the next item starting with it, and several characters\n * typed in quick succession narrow the match to that prefix. Getting that right\n * needs three pieces of bookkeeping — accumulating the query, dropping it after\n * an idle window, and resolving the label each candidate is matched against —\n * and single-sourcing them here keeps every widget on the same rules instead of\n * letting them drift apart.\n *\n * The helpers here own *only* that mechanical part. They are deliberately\n * **policy-free** in the same sense as `RovingTabindex`: which elements are\n * candidates, where the search starts, and what happens on a match (real DOM\n * focus, a roving tab stop, or a virtual `aria-activedescendant`) differ per APG\n * pattern, so each controller keeps those decisions and calls\n * {@link findTypeaheadMatch} with a candidate list it assembled itself.\n *\n * Three decisions are worth stating up front:\n *\n * - **A repeated character collapses the stored query**, rather than deriving a\n * shorter one at search time. That is what lets a follow-up character resume\n * narrowing: `s`, `s`, `e` searches `\"se\"`, not the dead `\"sse\"` that no label\n * can match. Growing the query instead leaves the widget frozen on exactly\n * that sequence, which is why the choice is made here and not per widget.\n * - **`aria-label` wins over text**, so type-ahead matches what a screen reader\n * announces — but only when it contributes a name. A blank (empty or\n * whitespace-only) one is skipped, because accname skips it too: honoring it\n * would leave an element that AT announces by its text unreachable by that\n * text. This mirrors the accessible-name computation rather than the raw text.\n * - **`Space` and composition input are never type-ahead.** `Space` natively\n * activates a `<button>`-based item, and characters still being composed are\n * not a committed query.\n */\n\nimport { SafeTimeout } from \"./safe_timeout\";\n\n/** Idle window after which an accumulated query is dropped, in milliseconds. */\nexport const TYPEAHEAD_RESET_MS = 500;\n\n/** Construction options for {@link Typeahead}. */\nexport interface TypeaheadOptions {\n /** Idle window before the query resets. Defaults to {@link TYPEAHEAD_RESET_MS}. */\n resetMs?: number;\n}\n\n/**\n * Accumulates typed characters into a search query and drops it when idle.\n *\n * @remarks\n * Owns a {@link SafeTimeout} of its own, so a consumer's `#timers.clearAll()` does\n * **not** reach the pending reset — call {@link Typeahead.reset} from `disconnect()`.\n */\nexport class Typeahead {\n /** Timer registry for the pending idle reset; private so `reset()` is the only exit. */\n readonly #timers = new SafeTimeout();\n /** Idle window before the query resets, in milliseconds. */\n readonly #resetMs: number;\n /** The accumulated lowercase query, empty when idle. */\n #query = \"\";\n /** Id of the pending reset timer, `0` when none is scheduled. */\n #timerId = 0;\n\n /** @param options - Overrides for the idle window. */\n constructor({ resetMs = TYPEAHEAD_RESET_MS }: TypeaheadOptions = {}) {\n this.#resetMs = resetMs;\n }\n\n /** The query a search would currently run with; empty while idle. */\n get query(): string {\n return this.#query;\n }\n\n /**\n * Folds `key` into the query, restarts the idle window, and returns the query to\n * search with. A repeated character collapses the query to that one character.\n */\n push(key: string): string {\n const char = key.toLowerCase();\n const repeated = this.#query.length > 0 && [...this.#query].every((c) => c === char);\n this.#query = repeated ? char : this.#query + char;\n this.#timers.clear(this.#timerId);\n this.#timerId = this.#timers.set(() => this.reset(), this.#resetMs);\n return this.#query;\n }\n\n /** Clears the query and cancels the pending idle reset. */\n reset(): void {\n this.#query = \"\";\n this.#timers.clear(this.#timerId);\n this.#timerId = 0;\n }\n}\n\n/** Whether `event` is a bare printable character usable for type-ahead. */\nexport function isTypeaheadKey(event: KeyboardEvent): boolean {\n return (\n event.key.length === 1 &&\n event.key !== \" \" &&\n !event.ctrlKey &&\n !event.metaKey &&\n !event.altKey &&\n !event.isComposing\n );\n}\n\n/**\n * The name `element` is matched under, normalized for comparison.\n *\n * @param fallbackText - Produces the name when `aria-label` contributes none.\n * Defaults to the element's `textContent`; pass a narrower source when part of\n * the subtree is not part of the name (a tree item's nested child group, say).\n * Called lazily, so an `aria-label` costs no subtree walk.\n */\nexport function typeaheadLabel(element: HTMLElement, fallbackText?: () => string): string {\n // Blank means absent, matching accname: a whitespace-only `aria-label` is\n // skipped there too, so honoring it here would make an element that a screen\n // reader announces by its text unreachable by that text.\n const label = element.getAttribute(\"aria-label\")?.trim();\n if (label) return label.toLowerCase();\n const text = fallbackText ? fallbackText() : (element.textContent ?? \"\");\n return text.trim().toLowerCase();\n}\n\n/**\n * Index of the first item after `from` whose label starts with `query`, wrapping\n * and evaluating `from` itself last, or `-1` when nothing matches.\n *\n * @param items - Candidates in navigation order, already filtered by the caller.\n * @param from - Index the search moves on from; `-1` starts at the first item. Any\n * out-of-range value is folded back in, so `indexOf` results pass straight through.\n * @param query - Lowercase query, normally the return of {@link Typeahead.push}.\n * @param label - Resolves a candidate's name. Defaults to {@link typeaheadLabel}.\n */\nexport function findTypeaheadMatch(\n items: readonly HTMLElement[],\n from: number,\n query: string,\n label: (item: HTMLElement) => string = (item) => typeaheadLabel(item),\n): number {\n // An empty query would prefix-match everything; an empty list never enters the\n // loop, so it needs no guard of its own.\n if (query === \"\") return -1;\n const count = items.length;\n for (let step = 1; step <= count; step += 1) {\n // Folded twice so a negative `from` still lands on a real slot: `%` keeps the\n // sign of its left operand, and a negative index would read `undefined`.\n const index = (((from + step) % count) + count) % count;\n const candidate = items[index];\n if (candidate && label(candidate).startsWith(query)) return index;\n }\n return -1;\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { actionSource } from \"../utils/action_source\";\nimport { isReservedArrowChord } from \"../utils/arrow_step\";\nimport { claimsWhileFocusWithin, EscapeLayer } from \"../utils/escape_layer\";\nimport { ownerOf } from \"../utils/event_owner\";\nimport { isRtl } from \"../utils/logical_scroll\";\nimport { MorphRenderWatcher } from \"../utils/morph_render_watcher\";\nimport { RovingTabindex } from \"../utils/roving_tabindex\";\nimport { SafeTimeout } from \"../utils/safe_timeout\";\nimport type { StateReason } from \"../utils/state_reason\";\nimport { targetSelector } from \"../utils/target_selector\";\nimport { findTypeaheadMatch, isTypeaheadKey, Typeahead } from \"../utils/typeahead\";\n\n/** Where to land focus when a menu opens. */\ntype OpenFocus = \"first\" | \"last\";\n\n/**\n * Attributes whose runtime change flips a top item's navigability, watched so the\n * single Tab stop survives a consumer disabling or hiding the item that held it.\n *\n * `tabindex` is deliberately absent — this controller writes it, and observing it\n * would feed back into the observer. `aria-disabled` is absent for a different\n * reason: per the APG it keeps an item focusable and only blocks activation, so\n * it never changes *which* top can hold the Tab stop.\n */\nconst STATE_ATTRIBUTES = [\"disabled\", \"hidden\"];\n\n/**\n * Headless, accessible **menubar** behavior.\n *\n * Markup contract (identifier: `stimeo--menubar`):\n * <div data-controller=\"stimeo--menubar\" role=\"menubar\" aria-label=\"Main\">\n * <button role=\"menuitem\" aria-haspopup=\"menu\" aria-expanded=\"false\"\n * aria-controls=\"m-file\" data-stimeo--menubar-target=\"top\"\n * data-action=\"click->stimeo--menubar#toggle\n * keydown->stimeo--menubar#onTopKeydown\">File</button>\n * <ul id=\"m-file\" role=\"menu\" data-stimeo--menubar-target=\"menu\" hidden>\n * <li role=\"none\">\n * <button role=\"menuitem\" tabindex=\"-1\" data-stimeo--menubar-target=\"item\"\n * data-action=\"click->stimeo--menubar#activate\n * keydown->stimeo--menubar#onItemKeydown\">New</button>\n * </li>\n * </ul>\n * <!-- more top items + menus -->\n * </div>\n *\n * Implements the WAI-ARIA APG **Menubar** pattern (single level): the top items\n * form one Tab stop via roving tabindex and the arrow keys move between them;\n * `ArrowDown`/`Enter`/`Space` open a menu (`ArrowUp` opens it at the last item),\n * the arrow keys then move within the menu, and pressing `ArrowLeft`/`ArrowRight`\n * while a menu is open jumps to the adjacent top menu. `Escape` closes and\n * returns focus to the owning top item — while a menu is open the menubar is a\n * layer on the shared `EscapeLayer` stack, claiming a press only while\n * focus is inside the controller or fell to the body, so one keypress closes\n * exactly one layer. `Tab` and an outside click close.\n *\n * @remarks\n * Behavior only. Each top item↔menu pair is linked by `aria-controls`/`id` (not by\n * position), so *pairing* does not depend on the markup order. Where the order does\n * show is native `Tab`/`Shift+Tab`: the browser walks the DOM, so `Shift+Tab` out of\n * a menu lands on the owning top item only while the menu follows it — write the\n * conventional owner-then-menu order to get the destinations described here. Menu\n * placement and viewport-edge collision are out of scope — static placement is the\n * consumer's CSS, and dynamic placement is delegated to the opt-in\n * `stimeo-ui/positioning` module (never imported here, keeping the core\n * zero-dependency). Roving mechanics across the top items are delegated to\n * `RovingTabindex`.\n *\n * Focus-restoration contract: closing via `Escape` or an item activation returns\n * focus to the owning top item; `Tab` and an outside click deliberately do **not**\n * — they close where the user is already heading, and yanking focus back would\n * fight the pointer/Tab move.\n *\n * Disabledness follows the APG's two-attribute split, which is the consumer's way\n * of saying which one they mean:\n * - **`hidden` / natively `disabled`** — removed from the roving set entirely, so\n * the lone Tab stop never lands on an unfocusable top item (which would drop the\n * whole menubar out of the Tab order) and the arrow keys never stall on it.\n * - **`aria-disabled=\"true\"`** — still reached by the arrow keys (\"disabled menu\n * items are focusable but cannot be activated\"), so a screen-reader user can\n * discover it; only *activation* is suppressed. Clicks on it — including the one\n * a browser synthesizes for Enter/Space — are swallowed before consumer handlers\n * run, and an `aria-disabled` top never opens its menu, since opening the popup\n * is activation. Moving onto it still closes whatever menu was open, which the\n * APG makes unconditional for a horizontal move.\n *\n * Arrow directions are logical: `ArrowRight` moves to the next top item under LTR\n * and to the previous one under RTL, on the bar and from inside an open menu. A top\n * item may also be a **plain command** with no `aria-controls` at all: moving to it\n * behaves the same way — the open menu closes and it takes the roving focus. A top\n * item whose `aria-controls` names no menu target is broken markup instead, and the\n * open/closed state is left alone rather than acted on by guesswork.\n *\n * A menu with no navigable items still opens (its `aria-expanded` flips and focus\n * stays on the top item) so a consumer that fills a menu asynchronously keeps\n * working; `Escape` or a second click closes it. While such a menu is\n * structurally empty the consumer **must** carry `aria-busy=\"true\"` on it and drop\n * that once the items land — `role=\"menu\"` requires owned `menuitem`s, and this\n * controller cannot tell \"still loading\" from \"nothing to show\", so it never\n * infers the state. A menu whose items exist but are all inert is not busy.\n *\n * Runtime DOM edits (Turbo Streams, a consumer swapping menus in) are reconciled\n * from the live targets: expanded flags, menu visibility, the single Tab stop, and\n * Escape-stack membership are re-derived whenever a top/menu target is added or\n * removed, and whenever `disabled`/`hidden` changes under the menubar.\n *\n * Each move of a menu's open state is reported: `stimeo--menubar:open` and\n * `stimeo--menubar:close` dispatch\n * `{ reason: StateReason, index: number, menu: HTMLElement }` — `index` is the\n * top item's position in `topTargets` — after the state attributes are written.\n * Both are informational, so neither is cancelable. Moving along the menubar\n * with a menu open reports the outgoing `close` before the incoming `open`. A\n * call that leaves a menu where it already was, the normalization in\n * {@link connect}, the reconciliation that follows target churn, and\n * {@link disconnect} are all silent.\n */\nexport class MenubarController extends Controller<HTMLElement> {\n static override targets = [\"top\", \"menu\", \"item\"];\n static actions = [\"activate\", \"onItemKeydown\", \"onTopKeydown\", \"toggle\"] as const;\n static events = [\"close\", \"open\"] as const;\n\n declare readonly topTargets: HTMLButtonElement[];\n declare readonly menuTargets: HTMLElement[];\n declare readonly itemTargets: HTMLButtonElement[];\n\n /** Roving tabindex across the top-level menuitems (one Tab stop). */\n readonly #roving = new RovingTabindex(() => this.topTargets);\n /** Typeahead query and its idle-reset timer (scoped to the open menu). */\n readonly #typeahead = new Typeahead();\n readonly #timers = new SafeTimeout();\n\n /** Escape-stack membership while a menu is open; the shared resolver dismisses via it. */\n readonly #escapeLayer = new EscapeLayer();\n /** Whether {@link #escapeLayer} is currently on the stack (see {@link #syncEscapeLayer}). */\n #layerActive = false;\n\n /** Watches `disabled`/`hidden` under the menubar; `null` while disconnected. */\n #observer: MutationObserver | null = null;\n /**\n * Live between `connect()` and `disconnect()`. Stimulus reports the *initial*\n * targets before `connect()` and re-reports them as disconnected afterwards;\n * gating on this keeps the target callbacks from clobbering the authored Tab\n * stop on mount and from resurrecting one after teardown.\n */\n #connected = false;\n\n /** Whether state moves are reported: set once `connect()` settled the baseline. */\n #reporting = false;\n\n /** The element inside the menubar that last took DOM focus, if any. */\n #focused: HTMLElement | null = null;\n\n /**\n * Records where DOM focus sits inside the menubar. `focusin` bubbles, so one\n * listener covers the top items and everything nested in their menus. The\n * record is needed because a top or menu becomes unreachable *before* this\n * controller hears about it, by which point the browser has already reset\n * `document.activeElement`.\n */\n readonly #onFocusIn = (event: FocusEvent): void => {\n this.#focused = event.target as HTMLElement | null;\n };\n\n /**\n * Forgets the record once focus genuinely lands on something else, so a later\n * mutation cannot pull focus back in. A `relatedTarget` of `null` is the case\n * that must be kept: it is what \"focus went nowhere\" looks like, which is\n * exactly the state the rescue exists for.\n */\n readonly #onFocusOut = (event: FocusEvent): void => {\n const next = event.relatedTarget as HTMLElement | null;\n if (next && !this.element.contains(next)) this.#focused = null;\n };\n\n readonly #morph = new MorphRenderWatcher(() => this.#reconcile());\n\n /**\n * Establishes the closed baseline and the single tab stop: keep an existing tab\n * stop when it is still navigable (so a Turbo cache restore preserves the user's\n * position), else fall back to the first navigable top item.\n *\n * The outside-click listener is registered in the **capture** phase: in the\n * bubble phase an inside handler that removes its own click target detaches the\n * node before `contains()` runs, so an *inside* click would read as outside and\n * close the menu.\n */\n override connect(): void {\n this.#morph.observe(this.element);\n this.#closeAllMenus(\"api\");\n this.#reconcile();\n this.element.addEventListener(\"click\", this.#onDisabledClickCapture, true);\n this.element.addEventListener(\"focusin\", this.#onFocusIn);\n this.element.addEventListener(\"focusout\", this.#onFocusOut);\n document.addEventListener(\"click\", this.#onOutsideClick, true);\n if (typeof MutationObserver !== \"undefined\") {\n this.#observer = new MutationObserver(() => this.#morph.schedule());\n this.#observer.observe(this.element, {\n subtree: true,\n childList: true,\n attributes: true,\n attributeFilter: STATE_ATTRIBUTES,\n });\n }\n this.#connected = true;\n this.#reporting = true;\n }\n\n /** Removes the listeners, stack membership, and any pending timer (typeahead / Tab close). */\n override disconnect(): void {\n this.#morph.disconnect();\n this.#connected = false;\n this.#reporting = false;\n this.#escapeLayer.deactivate();\n this.#layerActive = false;\n this.element.removeEventListener(\"click\", this.#onDisabledClickCapture, true);\n this.element.removeEventListener(\"focusin\", this.#onFocusIn);\n this.element.removeEventListener(\"focusout\", this.#onFocusOut);\n document.removeEventListener(\"click\", this.#onOutsideClick, true);\n this.#focused = null;\n this.#observer?.disconnect();\n this.#observer = null;\n // The typeahead owns its own timer registry, so `#timers.clearAll()` does not\n // reach its pending reset — drop it explicitly or it outlives the element.\n this.#typeahead.reset();\n this.#timers.clearAll();\n }\n\n /**\n * A top item added at runtime is dropped out of the Tab sequence first — a fresh\n * `<button>` is tabbable by default, which would leave the menubar with two Tab\n * stops — before the lone stop is re-established.\n */\n topTargetConnected(top: HTMLButtonElement): void {\n if (!this.#connected) return;\n top.tabIndex = -1;\n this.#reconcile();\n }\n\n /**\n * Removing a top item can strand two things: the Tab stop it held, and the menu\n * it owned (which nothing could close afterwards).\n *\n * The departing element is handed back in a neutral state first. A target can\n * leave without leaving the document — a morph that only drops the\n * `data-*-target` token keeps the node — and it would then sit in the page\n * carrying this controller's `tabindex=\"0\"` (a second Tab stop next to the one\n * re-established below) and an `aria-expanded=\"true\"` nothing can collapse.\n * When the node really is gone these writes are harmless no-ops.\n */\n topTargetDisconnected(top: HTMLButtonElement): void {\n if (!this.#connected) return;\n top.tabIndex = -1;\n if (this.#isExpanded(top)) top.setAttribute(\"aria-expanded\", \"false\");\n this.#reconcile();\n }\n\n /** See {@link MenubarController.menuTargetDisconnected}. */\n menuTargetConnected(): void {\n if (!this.#connected) return;\n this.#reconcile();\n }\n\n /**\n * A menu removed while open leaves its owner claiming `aria-expanded=\"true\"` for\n * a popup that no longer exists, and the menubar holding an Escape layer that\n * would swallow presses meant for something else.\n *\n * As with {@link MenubarController.topTargetDisconnected}, the departing menu is\n * closed first: a token-only removal leaves a visible popup in the page that no\n * key and no click can dismiss any more.\n */\n menuTargetDisconnected(menu: HTMLElement): void {\n if (!this.#connected) return;\n if (!menu.hidden) menu.hidden = true;\n this.#reconcile();\n }\n\n /** Toggles an owned top item's menu from an action or explicit element. */\n toggle(source: Event | HTMLElement): void {\n const { event, host, origin, reason } = actionSource(source);\n const top = host?.closest<HTMLButtonElement>(targetSelector(this.identifier, \"top\"));\n if (!top || !this.topTargets.includes(top)) return;\n if (origin && origin.closest(`[data-controller~=\"${this.identifier}\"]`) !== this.element)\n return;\n const moveFocus = event !== null || this.element.contains(document.activeElement);\n if (this.#isExpanded(top)) {\n this.#closeMenu(top, reason);\n } else {\n this.#openMenu(top, \"first\", reason, moveFocus);\n }\n }\n\n /** Keyboard handling while focus is on a top item. */\n onTopKeydown(event: KeyboardEvent): void {\n // A descendant widget that already claimed the key (a grabbed drag handle, an\n // inline editor) must not ALSO move the roving focus — composition depends on\n // this yield.\n if (event.defaultPrevented) return;\n if (isReservedArrowChord(event)) return;\n const tops = this.#navigableTops;\n const index = tops.indexOf(event.currentTarget as HTMLButtonElement);\n if (index === -1) return;\n const length = tops.length;\n // Whether a menu is currently open governs whether horizontal moves *open*\n // the adjacent menu or merely move the roving focus between top items.\n const anyOpen = this.#isAnyOpen;\n\n // Logical, not physical. APG defines these as \"next / previous control\", and\n // says a vertical arrangement swaps in Down/Up for the same meaning — so the\n // pair is one axis's spelling of an order, and the order reverses with the\n // writing direction. Read from the controller element: the container is what\n // lays the items out, and a child may carry its own `dir` (an LTR input inside\n // an RTL form is ordinary authoring).\n const step = isRtl(this.element) ? -1 : 1;\n\n switch (event.key) {\n case \"ArrowRight\":\n event.preventDefault();\n this.#gotoTop(tops[(index + step + length) % length], anyOpen, \"user\");\n break;\n case \"ArrowLeft\":\n event.preventDefault();\n this.#gotoTop(tops[(index - step + length) % length], anyOpen, \"user\");\n break;\n case \"ArrowDown\":\n event.preventDefault();\n this.#openMenu(tops[index], \"first\", \"user\");\n break;\n case \"ArrowUp\":\n event.preventDefault();\n this.#openMenu(tops[index], \"last\", \"user\");\n break;\n case \"Home\":\n event.preventDefault();\n this.#gotoTop(tops[0], anyOpen, \"user\");\n break;\n case \"End\":\n event.preventDefault();\n this.#gotoTop(tops[length - 1], anyOpen, \"user\");\n break;\n case \"Tab\":\n // Focus is leaving the menubar: close, but let the browser's own Tab move\n // run first (see #closeMenusSoon).\n this.#closeMenusSoon();\n break;\n default:\n break;\n }\n // Enter/Space are intentionally left to the native button click, which runs\n // toggle(): it opens the menu at its first item, or closes it when that menu\n // is already open. Handling them here would double-fire.\n // Escape is owned by the shared EscapeLayer resolver (see #dismissOpenMenu).\n }\n\n /** Keyboard handling while focus is on a menu item. */\n onItemKeydown(event: KeyboardEvent): void {\n // See onTopKeydown: a key a descendant already consumed is not ours.\n if (event.defaultPrevented) return;\n if (isReservedArrowChord(event)) return;\n const item = event.currentTarget as HTMLButtonElement;\n const menu = item.closest<HTMLElement>(\"[role='menu']\");\n if (!menu) return;\n const items = this.#itemsIn(menu);\n // -1 when the key came from an item outside this menu's navigable set:\n // ArrowDown then lands on the first item, ArrowUp on the second-to-last.\n const index = items.indexOf(item);\n const length = items.length;\n\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault();\n this.#focusAt(items, (index + 1) % length);\n break;\n case \"ArrowUp\":\n event.preventDefault();\n this.#focusAt(items, (index - 1 + length) % length);\n break;\n case \"Home\":\n event.preventDefault();\n this.#focusAt(items, 0);\n break;\n case \"End\":\n event.preventDefault();\n this.#focusAt(items, length - 1);\n break;\n case \"ArrowRight\":\n event.preventDefault();\n this.#moveToAdjacentMenu(menu, isRtl(this.element) ? -1 : 1);\n break;\n case \"ArrowLeft\":\n event.preventDefault();\n this.#moveToAdjacentMenu(menu, isRtl(this.element) ? 1 : -1);\n break;\n case \"Tab\":\n // Per APG, Tab closes the menubar but lets focus move on naturally.\n this.#closeMenusSoon();\n break;\n default:\n if (isTypeaheadKey(event)) {\n event.preventDefault();\n this.#typeaheadTo(items, index, event.key);\n }\n break;\n }\n // Enter/Space activate the item via its native button click → activate().\n }\n\n /** Closes an activated item's menu, returning focus only from inside on API calls. */\n activate(source: Event | HTMLElement): void {\n const { event, host, origin } = actionSource(source);\n const item = host?.closest<HTMLButtonElement>(targetSelector(this.identifier, \"item\"));\n if (!item || !this.itemTargets.includes(item)) return;\n if (origin && origin.closest(`[data-controller~=\"${this.identifier}\"]`) !== this.element)\n return;\n const moveFocus = event !== null || this.element.contains(document.activeElement);\n const menu = item.closest<HTMLElement>(\"[role='menu']\");\n const top = this.#topFor(menu);\n this.#closeAllMenus(\"select\");\n this.#focusTop(\n top,\n moveFocus && (event !== null || this.element.contains(document.activeElement)),\n );\n }\n\n /** Moves the roving focus to `destination`, opening its menu when one was open. */\n #gotoTop(destination: HTMLButtonElement | undefined, reopen: boolean, reason: StateReason): void {\n if (!destination) return;\n if (reopen) {\n this.#openMenu(destination, \"first\", reason);\n } else {\n // The roving index is resolved against the full target list, since that is\n // what carries the tabindex bookkeeping.\n this.#roving.setActive(this.topTargets.indexOf(destination), { focus: true });\n }\n }\n\n /**\n * Opens `opener`'s menu (closing others) and focuses its first/last item.\n *\n * Three kinds of top item never open a menu:\n * - **`aria-disabled`** — focusable but never activated, and opening a popup is\n * activation.\n * - **plain command** (no `aria-controls` at all) — a legitimate top item that\n * simply has no popup.\n * - **dangling `aria-controls`** (names a menu that is not a target) — broken\n * markup.\n *\n * The first two still take the roving focus, and any open menu closes: the APG\n * makes closing unconditional for the horizontal move (\"closes the submenu,\n * moves focus to the next menubar item, and *optionally* opens that item's\n * submenu\"), so only the opening half is skipped. Leaving the old menu open\n * would strand a popup that no longer contains focus.\n *\n * The dangling case instead leaves the open/closed state and focus untouched\n * rather than closing everything and dropping focus to the body — but only on\n * this \"open a menu\" path. A dangling top is still an ordinary roving\n * destination while nothing is open.\n */\n #openMenu(\n opener: HTMLButtonElement | null | undefined,\n focus: OpenFocus,\n reason: StateReason,\n moveFocus = true,\n ): void {\n if (!opener) return;\n // Resolve the menu *before* touching any state, so the dangling case below\n // can bail out without having changed anything.\n const menu = this.#menuFor(opener);\n if (!menu || this.#isActivationBlocked(opener)) {\n // Broken markup (an `aria-controls` that resolves to nothing) is the one\n // case that must not act on a guess.\n if (menu || !opener.hasAttribute(\"aria-controls\")) {\n this.#closeAllMenus(reason);\n this.#focusTop(\n opener,\n moveFocus && (reason !== \"api\" || this.element.contains(document.activeElement)),\n );\n }\n return;\n }\n // A reopen must discard a pending Tab close, or that stale task would slam\n // the freshly opened menu shut on the next tick.\n this.#timers.clearAll();\n const was = this.#isExpanded(opener);\n // The blanket close also rewrites this top's own attributes, so its move is\n // reported from here instead — a reopen of the menu that is already open\n // leaves the state where it was and says nothing.\n this.#closeAllMenus(reason, opener);\n menu.hidden = false;\n opener.setAttribute(\"aria-expanded\", \"true\");\n if (!was) this.#report(\"open\", opener, menu, reason);\n // A subscriber may close it again from the handler above. The layer and the\n // focus move below belong to a menu that is open; against a hidden one they\n // strand the Escape layer and put focus where nothing is visible.\n if (!this.#isExpanded(opener)) return;\n this.#escapeLayer.activate(document, {\n onDismiss: () => this.#dismissOpenMenu(),\n claims: claimsWhileFocusWithin(this.element),\n });\n // Opening always re-registers, which puts this layer back on top of the\n // stack — the menubar is the innermost thing the user just interacted with.\n this.#layerActive = true;\n this.#roving.setActive(this.topTargets.indexOf(opener));\n const items = this.#itemsIn(menu);\n if (moveFocus && (reason !== \"api\" || this.element.contains(document.activeElement))) {\n this.#focusAt(items, focus === \"first\" ? 0 : items.length - 1);\n }\n }\n\n /**\n * Hides `top`'s menu and reflects the collapsed state. A top item that controls\n * no menu (a plain command in an otherwise popup-bearing menubar) is left alone\n * — stamping `aria-expanded=\"false\"` on it would announce a popup it lacks.\n */\n #closeMenu(top: HTMLButtonElement | null, reason: StateReason, silent = false): void {\n if (!top) return;\n const menu = this.#menuFor(top);\n if (!menu) return;\n const was = this.#isExpanded(top);\n menu.hidden = true;\n top.setAttribute(\"aria-expanded\", \"false\");\n if (was && !silent) this.#report(\"close\", top, menu, reason);\n this.#syncEscapeLayer();\n }\n\n /**\n * Closes every menu and resets the typeahead query. `except` still has its\n * attributes rewritten — the caller is about to reopen it — but its move is\n * left for that caller to report.\n */\n #closeAllMenus(reason: StateReason, except: HTMLButtonElement | null = null): void {\n for (const top of this.topTargets) this.#closeMenu(top, reason, top === except);\n this.#typeahead.reset();\n }\n\n /** Names the menu that moved, so a subscriber does not have to re-derive it. */\n #report(\n name: \"close\" | \"open\",\n top: HTMLButtonElement,\n menu: HTMLElement,\n reason: StateReason,\n ): void {\n if (!this.#reporting) return;\n const detail = { reason, index: this.topTargets.indexOf(top), menu };\n if (name === \"open\") this.dispatch(\"open\", { detail, cancelable: false });\n else this.dispatch(\"close\", { detail, cancelable: false });\n }\n\n /**\n * Closes every menu on the next task instead of synchronously.\n *\n * Used by `Tab`: closing right away removes the focused element before the\n * browser performs its own Tab move, which can restart traversal at the\n * document head and lose the user's place in the Tab order.\n */\n #closeMenusSoon(): void {\n if (!this.#isAnyOpen) return;\n this.#timers.clearAll();\n this.#timers.set(() => this.#closeAllMenus(\"focus\"), 0);\n }\n\n /**\n * Escape path, invoked by the shared resolver: pressed inside an open menu it\n * closes that menu and returns focus to its top item (the APG behavior);\n * pressed while focus is on a top item (or fell to the body) it closes every\n * menu without moving focus.\n */\n #dismissOpenMenu(): void {\n const menu = ownerOf(this.menuTargets, document.activeElement);\n if (menu) {\n const top = this.#topFor(menu);\n this.#closeMenu(top, \"escape\");\n this.#focusTop(top);\n return;\n }\n this.#closeAllMenus(\"escape\");\n }\n\n /** Opens the menu of the navigable top item `delta` steps from `menu`'s owner. */\n #moveToAdjacentMenu(menu: HTMLElement, delta: number): void {\n const ownerTop = this.#topFor(menu);\n if (!ownerTop) return;\n const tops = this.#navigableTops;\n const current = tops.indexOf(ownerTop);\n if (current === -1) return;\n const next = (current + delta + tops.length) % tops.length;\n this.#openMenu(tops[next], \"first\", \"user\");\n }\n\n /**\n * Closes when a click lands outside the controller's element. Focus is left on\n * whatever the user clicked — see the focus-restoration contract in the class\n * docs.\n */\n readonly #onOutsideClick = (event: MouseEvent): void => {\n if (this.#isAnyOpen && !this.element.contains(event.target as Node)) {\n this.#closeAllMenus(\"outside\");\n }\n };\n\n /**\n * Captures clicks so `aria-disabled` top items and commands cannot reach\n * consumer handlers (nor `toggle`/`activate`). Native Enter/Space activation\n * also synthesizes a click and is blocked here; natively `disabled` buttons\n * dispatch no click at all.\n */\n readonly #onDisabledClickCapture = (event: MouseEvent): void => {\n const target = event.target;\n if (!(target instanceof Node)) return;\n const blocked = [...this.topTargets, ...this.itemTargets].some(\n (element) => this.#isActivationBlocked(element) && element.contains(target),\n );\n if (!blocked) return;\n event.preventDefault();\n event.stopImmediatePropagation();\n };\n\n /**\n * Re-derives every piece of state this controller owns from the live targets:\n * expanded flags, menu visibility, the single Tab stop, and Escape-stack\n * membership. Runtime DOM edits can leave those out of step with each other — a\n * top can claim `aria-expanded=\"true\"` after its menu target was removed or after\n * being hidden itself, a menu can stay visible after its owning top was removed,\n * an open pair can appear from a morph with no layer registered for it, and the\n * Tab stop can end up on a now-inert top, on a runtime-added one, or on none at\n * all. The only state carried between calls is the tracked focus record and which side\n * of the Escape stack this layer is on (which the stack itself does not expose), so the outcome is the\n * same whichever mutation arrived and calling it more often than needed is free.\n */\n #reconcile(): void {\n for (const top of this.topTargets) {\n // A top is expanded only while its own menu resolves *and* is visible.\n // Both halves matter: the menu target can be removed outright, and a Turbo\n // Stream `replace` swaps it for a fresh (contract-`hidden`) one — which\n // still resolves, so a \"did it disappear?\" test alone would leave the top\n // claiming a popup nobody can see, and the Escape layer registered with it.\n // Only tops that already declare the attribute are touched: stamping\n // `aria-expanded` on a plain command would announce a popup it lacks.\n // The owner must also still be reachable: a consumer that `hidden`s or\n // natively disables an expanded top would otherwise leave its popup on\n // screen with no way back to the element that owns it.\n const own = this.#menuFor(top);\n if (this.#isExpanded(top) && (!own || own.hidden || !this.#isNavigable(top))) {\n top.setAttribute(\"aria-expanded\", \"false\");\n }\n }\n for (const menu of this.menuTargets) {\n if (menu.hidden) continue;\n // A menu with no expanded owner can never be closed by the user again.\n const owner = this.#topFor(menu);\n if (!owner || !this.#isExpanded(owner)) menu.hidden = true;\n }\n this.#ensureTabStop();\n this.#rescueFocus();\n this.#syncEscapeLayer();\n }\n\n /**\n * Returns DOM focus to the menubar when the element holding it became\n * unreachable — hidden, natively disabled, or removed — and the document had\n * nowhere to put it. The destination is the top item that now owns the Tab\n * stop, which is where `Escape` from a closed menu would have left the user.\n *\n * Focus is only *restored*, never *stolen*: it moves solely when the tracked\n * element can no longer take it **and** focus is either already gone or still\n * sitting on that element. The second half is what makes a `hidden` ancestor\n * work — a browser blurs the element it hides on its next style pass, not when\n * the attribute is written, so waiting to observe `<body>` here would always\n * come too early. Removal is the other side of the same condition: the browser\n * has already fallen back to the body, or left a detached `activeElement`.\n */\n #rescueFocus(): void {\n const focused = this.#focused;\n if (!focused) return;\n // An ordinary close — Escape, Tab, an outside click — also leaves the focused\n // menu item inside a hidden subtree, but nothing went away and the user is\n // already on their way somewhere. Only an owner that can no longer be reached\n // is a reason to move focus, so the owner decides, not the item.\n const owner = this.topTargets.find(\n (top) => top === focused || this.#menuFor(top)?.contains(focused),\n );\n if (focused.isConnected && owner && this.#isNavigable(owner)) return;\n const doc = this.element.ownerDocument;\n const active = doc.activeElement;\n const lost =\n active === null ||\n active === doc.body ||\n active === doc.documentElement ||\n !active.isConnected ||\n active === focused ||\n focused.contains(active);\n if (!lost) return;\n const index = this.#roving.activeIndex;\n const top = index === -1 ? undefined : this.topTargets[index];\n if (top && top !== focused && !focused.contains(top)) top.focus();\n }\n\n /**\n * Brings Escape-stack membership back in line with what the DOM now says is\n * open, in both directions.\n *\n * The deactivate half is the common one (something closed). The activate half\n * covers a menu that became open *without* going through {@link #openMenu} —\n * a Turbo morph that patches `aria-expanded` and the menu's `hidden` in place,\n * which is legitimate here because the DOM is this controller's only source of\n * truth. Without it the popup is visible but `Escape` does nothing, since no\n * layer is registered to claim the press.\n *\n * `#layerActive` exists because re-activating an already-active layer moves it\n * to the top of the stack, which would reshuffle nested layers on every\n * unrelated mutation. Registering only on the false→true edge keeps activation\n * ordered by when each layer actually opened.\n */\n #syncEscapeLayer(): void {\n const open = this.#isAnyOpen;\n if (open === this.#layerActive) return;\n if (open) {\n this.#escapeLayer.activate(document, {\n onDismiss: () => this.#dismissOpenMenu(),\n claims: claimsWhileFocusWithin(this.element),\n });\n } else {\n this.#escapeLayer.deactivate();\n }\n this.#layerActive = open;\n }\n\n /**\n * Re-establishes the single Tab stop: keep the current one while it is still\n * navigable, else hand it to the first navigable top item. Every other top is\n * explicitly removed from the Tab sequence, so a top that arrived tabbable\n * cannot leave two stops behind. No Tab stop at all (`-1`) happens only when\n * every top is inert, and is recovered from as soon as one becomes navigable.\n */\n #ensureTabStop(): void {\n const active = this.#roving.activeIndex;\n const activeTop = active === -1 ? undefined : this.topTargets[active];\n if (activeTop && this.#isNavigable(activeTop)) {\n this.#roving.setActive(active);\n return;\n }\n const first = this.#navigableTops[0];\n this.#roving.setActive(first ? this.topTargets.indexOf(first) : -1);\n }\n\n /** The menu element controlled by `top` (matched by `aria-controls`/`id`). */\n #menuFor(top: HTMLElement): HTMLElement | null {\n const id = top.getAttribute(\"aria-controls\");\n // Resolve against this controller's own menu targets (not a global id lookup)\n // so it stays scoped to this menubar instance.\n return id ? (this.menuTargets.find((menu) => menu.id === id) ?? null) : null;\n }\n\n /** The top item that controls `menu` (reverse of `#menuFor`). */\n #topFor(menu: HTMLElement | null): HTMLButtonElement | null {\n if (!menu) return null;\n return this.topTargets.find((top) => top.getAttribute(\"aria-controls\") === menu.id) ?? null;\n }\n\n /** The navigable item targets that live inside `menu`, in DOM order. */\n #itemsIn(menu: HTMLElement): HTMLButtonElement[] {\n return this.itemTargets.filter((item) => menu.contains(item) && this.#isNavigable(item));\n }\n\n /** Top items eligible for the roving tab stop and the arrow keys. */\n get #navigableTops(): HTMLButtonElement[] {\n return this.topTargets.filter((top) => this.#isNavigable(top));\n }\n\n /**\n * Whether `element` can take roving focus: `hidden` and natively `disabled`\n * controls are out of reach and are skipped. `aria-disabled` is deliberately\n * **not** checked — the APG keeps such items focusable so they stay\n * discoverable, and {@link MenubarController.#isActivationBlocked} is what\n * suppresses acting on them. CSS-only visibility is not detectable headlessly\n * and stays the consumer's responsibility.\n */\n #isNavigable(element: HTMLButtonElement): boolean {\n if (element.hasAttribute(\"hidden\")) return false;\n return !element.disabled;\n }\n\n /** Whether `element` is `aria-disabled`: reachable, but never activated. */\n #isActivationBlocked(element: Element): boolean {\n return element.getAttribute(\"aria-disabled\") === \"true\";\n }\n\n /** Moves DOM focus to the item at `index` (no-op if out of range). */\n #focusAt(items: HTMLElement[], index: number): void {\n items[index]?.focus();\n }\n\n /** Returns roving focus to a top item (and makes it the single tab stop). */\n #focusTop(top: HTMLButtonElement | null, focus = true): void {\n if (!top) return;\n this.#roving.setActive(this.topTargets.indexOf(top), { focus });\n }\n\n /** Whether `top`'s menu is currently expanded. */\n #isExpanded(top: HTMLElement): boolean {\n return top.getAttribute(\"aria-expanded\") === \"true\";\n }\n\n /** Whether any top item's menu is currently open. */\n get #isAnyOpen(): boolean {\n return this.topTargets.some((top) => this.#isExpanded(top));\n }\n\n /** Advances focus to the next item in `items` matching the accumulated query. */\n #typeaheadTo(items: HTMLElement[], current: number, key: string): void {\n const index = findTypeaheadMatch(items, current, this.#typeahead.push(key));\n if (index !== -1) items[index]?.focus();\n }\n}\n"]}
@@ -77,6 +77,26 @@ declare class MeterController extends Controller<HTMLElement> {
77
77
  default: string;
78
78
  };
79
79
  };
80
+ static valueConstraints: {
81
+ value: {
82
+ readonly finite: true;
83
+ };
84
+ min: {
85
+ readonly finite: true;
86
+ };
87
+ max: {
88
+ readonly finite: true;
89
+ };
90
+ low: {
91
+ readonly finite: true;
92
+ };
93
+ high: {
94
+ readonly finite: true;
95
+ };
96
+ optimum: {
97
+ readonly finite: true;
98
+ };
99
+ };
80
100
  static actions: readonly ["setValue"];
81
101
  static events: readonly ["change"];
82
102
  readonly barTarget: HTMLElement;