stimeo-ui 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (337) hide show
  1. package/CHANGELOG.md +136 -0
  2. package/README.md +27 -11
  3. package/dist/cable/index.d.ts +88 -31
  4. package/dist/cable/index.js +384 -449
  5. package/dist/cable/index.js.map +1 -1
  6. package/dist/coerce-JtZVNG5h.d.ts +20 -0
  7. package/dist/controllers/accordion_controller.d.ts +4 -2
  8. package/dist/controllers/accordion_controller.js +174 -55
  9. package/dist/controllers/accordion_controller.js.map +1 -1
  10. package/dist/controllers/alert_dialog_controller.d.ts +12 -2
  11. package/dist/controllers/alert_dialog_controller.js +903 -235
  12. package/dist/controllers/alert_dialog_controller.js.map +1 -1
  13. package/dist/controllers/announcer_controller.d.ts +37 -5
  14. package/dist/controllers/announcer_controller.js +238 -205
  15. package/dist/controllers/announcer_controller.js.map +1 -1
  16. package/dist/controllers/aspect_ratio_controller.js +0 -10
  17. package/dist/controllers/aspect_ratio_controller.js.map +1 -1
  18. package/dist/controllers/auto_submit_controller.d.ts +30 -6
  19. package/dist/controllers/auto_submit_controller.js +414 -145
  20. package/dist/controllers/auto_submit_controller.js.map +1 -1
  21. package/dist/controllers/avatar_controller.js +185 -107
  22. package/dist/controllers/avatar_controller.js.map +1 -1
  23. package/dist/controllers/breadcrumb_controller.js +0 -130
  24. package/dist/controllers/breadcrumb_controller.js.map +1 -1
  25. package/dist/controllers/bulk_select_controller.d.ts +21 -3
  26. package/dist/controllers/bulk_select_controller.js +282 -58
  27. package/dist/controllers/bulk_select_controller.js.map +1 -1
  28. package/dist/controllers/calendar_controller.d.ts +38 -16
  29. package/dist/controllers/calendar_controller.js +228 -260
  30. package/dist/controllers/calendar_controller.js.map +1 -1
  31. package/dist/controllers/carousel_controller.d.ts +43 -12
  32. package/dist/controllers/carousel_controller.js +536 -436
  33. package/dist/controllers/carousel_controller.js.map +1 -1
  34. package/dist/controllers/character_counter_controller.d.ts +44 -7
  35. package/dist/controllers/character_counter_controller.js +493 -207
  36. package/dist/controllers/character_counter_controller.js.map +1 -1
  37. package/dist/controllers/checkbox_controller.d.ts +7 -2
  38. package/dist/controllers/checkbox_controller.js +38 -74
  39. package/dist/controllers/checkbox_controller.js.map +1 -1
  40. package/dist/controllers/clipboard_controller.d.ts +16 -5
  41. package/dist/controllers/clipboard_controller.js +261 -149
  42. package/dist/controllers/clipboard_controller.js.map +1 -1
  43. package/dist/controllers/collapsible_controller.d.ts +17 -5
  44. package/dist/controllers/collapsible_controller.js +304 -120
  45. package/dist/controllers/collapsible_controller.js.map +1 -1
  46. package/dist/controllers/color_picker_controller.d.ts +18 -6
  47. package/dist/controllers/color_picker_controller.js +178 -146
  48. package/dist/controllers/color_picker_controller.js.map +1 -1
  49. package/dist/controllers/combobox_controller.d.ts +24 -6
  50. package/dist/controllers/combobox_controller.js +522 -156
  51. package/dist/controllers/combobox_controller.js.map +1 -1
  52. package/dist/controllers/command_palette_controller.d.ts +51 -20
  53. package/dist/controllers/command_palette_controller.js +1156 -395
  54. package/dist/controllers/command_palette_controller.js.map +1 -1
  55. package/dist/controllers/conditional_fields_controller.d.ts +15 -2
  56. package/dist/controllers/conditional_fields_controller.js +300 -171
  57. package/dist/controllers/conditional_fields_controller.js.map +1 -1
  58. package/dist/controllers/confirm_controller.d.ts +49 -12
  59. package/dist/controllers/confirm_controller.js +980 -262
  60. package/dist/controllers/confirm_controller.js.map +1 -1
  61. package/dist/controllers/context_menu_controller.d.ts +80 -18
  62. package/dist/controllers/context_menu_controller.js +346 -153
  63. package/dist/controllers/context_menu_controller.js.map +1 -1
  64. package/dist/controllers/count_up_controller.d.ts +13 -1
  65. package/dist/controllers/count_up_controller.js +73 -21
  66. package/dist/controllers/count_up_controller.js.map +1 -1
  67. package/dist/controllers/countdown_controller.d.ts +47 -3
  68. package/dist/controllers/countdown_controller.js +281 -158
  69. package/dist/controllers/countdown_controller.js.map +1 -1
  70. package/dist/controllers/currency_input_controller.d.ts +93 -10
  71. package/dist/controllers/currency_input_controller.js +575 -225
  72. package/dist/controllers/currency_input_controller.js.map +1 -1
  73. package/dist/controllers/data_grid_controller.d.ts +25 -12
  74. package/dist/controllers/data_grid_controller.js +248 -214
  75. package/dist/controllers/data_grid_controller.js.map +1 -1
  76. package/dist/controllers/date_range_picker_controller.d.ts +42 -14
  77. package/dist/controllers/date_range_picker_controller.js +338 -235
  78. package/dist/controllers/date_range_picker_controller.js.map +1 -1
  79. package/dist/controllers/dialog_controller.d.ts +17 -6
  80. package/dist/controllers/dialog_controller.js +899 -237
  81. package/dist/controllers/dialog_controller.js.map +1 -1
  82. package/dist/controllers/direct_upload_controller.d.ts +25 -8
  83. package/dist/controllers/direct_upload_controller.js +97 -167
  84. package/dist/controllers/direct_upload_controller.js.map +1 -1
  85. package/dist/controllers/dirty_form_controller.d.ts +6 -1
  86. package/dist/controllers/dirty_form_controller.js +72 -69
  87. package/dist/controllers/dirty_form_controller.js.map +1 -1
  88. package/dist/controllers/dismissible_controller.d.ts +19 -0
  89. package/dist/controllers/dismissible_controller.js +196 -24
  90. package/dist/controllers/dismissible_controller.js.map +1 -1
  91. package/dist/controllers/drawer_controller.d.ts +39 -11
  92. package/dist/controllers/drawer_controller.js +974 -355
  93. package/dist/controllers/drawer_controller.js.map +1 -1
  94. package/dist/controllers/dropdown_controller.d.ts +31 -1
  95. package/dist/controllers/dropdown_controller.js +339 -96
  96. package/dist/controllers/dropdown_controller.js.map +1 -1
  97. package/dist/controllers/editable_controller.d.ts +12 -1
  98. package/dist/controllers/editable_controller.js +315 -88
  99. package/dist/controllers/editable_controller.js.map +1 -1
  100. package/dist/controllers/empty_state_controller.js +79 -58
  101. package/dist/controllers/empty_state_controller.js.map +1 -1
  102. package/dist/controllers/file_dropzone_controller.d.ts +63 -18
  103. package/dist/controllers/file_dropzone_controller.js +322 -211
  104. package/dist/controllers/file_dropzone_controller.js.map +1 -1
  105. package/dist/controllers/filter_controller.js +77 -42
  106. package/dist/controllers/filter_controller.js.map +1 -1
  107. package/dist/controllers/flash_controller.d.ts +35 -12
  108. package/dist/controllers/flash_controller.js +238 -347
  109. package/dist/controllers/flash_controller.js.map +1 -1
  110. package/dist/controllers/focus_controller.d.ts +15 -6
  111. package/dist/controllers/focus_controller.js +679 -240
  112. package/dist/controllers/focus_controller.js.map +1 -1
  113. package/dist/controllers/form_field_controller.js +156 -132
  114. package/dist/controllers/form_field_controller.js.map +1 -1
  115. package/dist/controllers/form_validation_controller.d.ts +3 -3
  116. package/dist/controllers/form_validation_controller.js +31 -97
  117. package/dist/controllers/form_validation_controller.js.map +1 -1
  118. package/dist/controllers/frame_loading_controller.d.ts +47 -14
  119. package/dist/controllers/frame_loading_controller.js +275 -217
  120. package/dist/controllers/frame_loading_controller.js.map +1 -1
  121. package/dist/controllers/highlight_controller.d.ts +27 -6
  122. package/dist/controllers/highlight_controller.js +107 -75
  123. package/dist/controllers/highlight_controller.js.map +1 -1
  124. package/dist/controllers/hover_card_controller.d.ts +60 -4
  125. package/dist/controllers/hover_card_controller.js +327 -130
  126. package/dist/controllers/hover_card_controller.js.map +1 -1
  127. package/dist/controllers/idle_controller.d.ts +48 -6
  128. package/dist/controllers/idle_controller.js +271 -101
  129. package/dist/controllers/idle_controller.js.map +1 -1
  130. package/dist/controllers/input_mask_controller.d.ts +30 -13
  131. package/dist/controllers/input_mask_controller.js +483 -97
  132. package/dist/controllers/input_mask_controller.js.map +1 -1
  133. package/dist/controllers/intersection_controller.d.ts +32 -13
  134. package/dist/controllers/intersection_controller.js +150 -106
  135. package/dist/controllers/intersection_controller.js.map +1 -1
  136. package/dist/controllers/lazy_frame_controller.d.ts +19 -2
  137. package/dist/controllers/lazy_frame_controller.js +24 -68
  138. package/dist/controllers/lazy_frame_controller.js.map +1 -1
  139. package/dist/controllers/listbox_controller.d.ts +44 -9
  140. package/dist/controllers/listbox_controller.js +523 -240
  141. package/dist/controllers/listbox_controller.js.map +1 -1
  142. package/dist/controllers/local_time_controller.d.ts +9 -7
  143. package/dist/controllers/local_time_controller.js +102 -77
  144. package/dist/controllers/local_time_controller.js.map +1 -1
  145. package/dist/controllers/masonry_controller.d.ts +11 -3
  146. package/dist/controllers/masonry_controller.js +80 -96
  147. package/dist/controllers/masonry_controller.js.map +1 -1
  148. package/dist/controllers/menu_controller.d.ts +55 -11
  149. package/dist/controllers/menu_controller.js +302 -188
  150. package/dist/controllers/menu_controller.js.map +1 -1
  151. package/dist/controllers/menubar_controller.d.ts +51 -9
  152. package/dist/controllers/menubar_controller.js +232 -345
  153. package/dist/controllers/menubar_controller.js.map +1 -1
  154. package/dist/controllers/meter_controller.d.ts +33 -20
  155. package/dist/controllers/meter_controller.js +417 -87
  156. package/dist/controllers/meter_controller.js.map +1 -1
  157. package/dist/controllers/multi_select_controller.d.ts +48 -18
  158. package/dist/controllers/multi_select_controller.js +785 -393
  159. package/dist/controllers/multi_select_controller.js.map +1 -1
  160. package/dist/controllers/navigation_menu_controller.d.ts +27 -6
  161. package/dist/controllers/navigation_menu_controller.js +190 -250
  162. package/dist/controllers/navigation_menu_controller.js.map +1 -1
  163. package/dist/controllers/nested_form_controller.d.ts +10 -0
  164. package/dist/controllers/nested_form_controller.js +396 -192
  165. package/dist/controllers/nested_form_controller.js.map +1 -1
  166. package/dist/controllers/network_status_controller.d.ts +23 -0
  167. package/dist/controllers/network_status_controller.js +268 -62
  168. package/dist/controllers/network_status_controller.js.map +1 -1
  169. package/dist/controllers/number_input_controller.d.ts +50 -8
  170. package/dist/controllers/number_input_controller.js +610 -303
  171. package/dist/controllers/number_input_controller.js.map +1 -1
  172. package/dist/controllers/optimistic_controller.d.ts +8 -5
  173. package/dist/controllers/optimistic_controller.js +45 -87
  174. package/dist/controllers/optimistic_controller.js.map +1 -1
  175. package/dist/controllers/otp_controller.d.ts +55 -8
  176. package/dist/controllers/otp_controller.js +658 -255
  177. package/dist/controllers/otp_controller.js.map +1 -1
  178. package/dist/controllers/overflow_indicator_controller.d.ts +5 -0
  179. package/dist/controllers/overflow_indicator_controller.js +122 -113
  180. package/dist/controllers/overflow_indicator_controller.js.map +1 -1
  181. package/dist/controllers/overflow_menu_controller.d.ts +58 -20
  182. package/dist/controllers/overflow_menu_controller.js +470 -295
  183. package/dist/controllers/overflow_menu_controller.js.map +1 -1
  184. package/dist/controllers/pagination_controller.d.ts +37 -7
  185. package/dist/controllers/pagination_controller.js +332 -123
  186. package/dist/controllers/pagination_controller.js.map +1 -1
  187. package/dist/controllers/password_reveal_controller.d.ts +46 -8
  188. package/dist/controllers/password_reveal_controller.js +392 -121
  189. package/dist/controllers/password_reveal_controller.js.map +1 -1
  190. package/dist/controllers/password_strength_controller.d.ts +15 -6
  191. package/dist/controllers/password_strength_controller.js +111 -158
  192. package/dist/controllers/password_strength_controller.js.map +1 -1
  193. package/dist/controllers/persist_controller.d.ts +11 -4
  194. package/dist/controllers/persist_controller.js +165 -111
  195. package/dist/controllers/persist_controller.js.map +1 -1
  196. package/dist/controllers/pointer_drag_controller.d.ts +35 -12
  197. package/dist/controllers/pointer_drag_controller.js +434 -188
  198. package/dist/controllers/pointer_drag_controller.js.map +1 -1
  199. package/dist/controllers/popover_controller.d.ts +41 -5
  200. package/dist/controllers/popover_controller.js +269 -113
  201. package/dist/controllers/popover_controller.js.map +1 -1
  202. package/dist/controllers/portal_controller.d.ts +44 -4
  203. package/dist/controllers/portal_controller.js +215 -76
  204. package/dist/controllers/portal_controller.js.map +1 -1
  205. package/dist/controllers/preview_guard_controller.d.ts +10 -4
  206. package/dist/controllers/preview_guard_controller.js +243 -116
  207. package/dist/controllers/preview_guard_controller.js.map +1 -1
  208. package/dist/controllers/progress_controller.d.ts +20 -19
  209. package/dist/controllers/progress_controller.js +391 -78
  210. package/dist/controllers/progress_controller.js.map +1 -1
  211. package/dist/controllers/radio_group_controller.d.ts +21 -9
  212. package/dist/controllers/radio_group_controller.js +278 -211
  213. package/dist/controllers/radio_group_controller.js.map +1 -1
  214. package/dist/controllers/range_slider_controller.d.ts +47 -6
  215. package/dist/controllers/range_slider_controller.js +220 -113
  216. package/dist/controllers/range_slider_controller.js.map +1 -1
  217. package/dist/controllers/rating_controller.d.ts +20 -4
  218. package/dist/controllers/rating_controller.js +458 -211
  219. package/dist/controllers/rating_controller.js.map +1 -1
  220. package/dist/controllers/read_more_controller.d.ts +2 -0
  221. package/dist/controllers/read_more_controller.js +138 -63
  222. package/dist/controllers/read_more_controller.js.map +1 -1
  223. package/dist/controllers/reading_progress_controller.d.ts +1 -1
  224. package/dist/controllers/reading_progress_controller.js +166 -137
  225. package/dist/controllers/reading_progress_controller.js.map +1 -1
  226. package/dist/controllers/relative_time_controller.d.ts +20 -6
  227. package/dist/controllers/relative_time_controller.js +211 -112
  228. package/dist/controllers/relative_time_controller.js.map +1 -1
  229. package/dist/controllers/reset_on_restore_controller.d.ts +85 -0
  230. package/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +103 -38
  231. package/dist/controllers/reset_on_restore_controller.js.map +1 -0
  232. package/dist/controllers/resizable_controller.d.ts +31 -2
  233. package/dist/controllers/resizable_controller.js +299 -150
  234. package/dist/controllers/resizable_controller.js.map +1 -1
  235. package/dist/controllers/roving_controller.d.ts +3 -3
  236. package/dist/controllers/roving_controller.js +49 -88
  237. package/dist/controllers/roving_controller.js.map +1 -1
  238. package/dist/controllers/scroll_area_controller.d.ts +7 -3
  239. package/dist/controllers/scroll_area_controller.js +490 -246
  240. package/dist/controllers/scroll_area_controller.js.map +1 -1
  241. package/dist/controllers/scroll_restore_controller.d.ts +3 -3
  242. package/dist/controllers/scroll_restore_controller.js +14 -55
  243. package/dist/controllers/scroll_restore_controller.js.map +1 -1
  244. package/dist/controllers/scroll_visibility_controller.d.ts +28 -12
  245. package/dist/controllers/scroll_visibility_controller.js +432 -149
  246. package/dist/controllers/scroll_visibility_controller.js.map +1 -1
  247. package/dist/controllers/scrollspy_controller.d.ts +6 -1
  248. package/dist/controllers/scrollspy_controller.js +193 -248
  249. package/dist/controllers/scrollspy_controller.js.map +1 -1
  250. package/dist/controllers/separator_controller.d.ts +20 -5
  251. package/dist/controllers/separator_controller.js +285 -142
  252. package/dist/controllers/separator_controller.js.map +1 -1
  253. package/dist/controllers/sidebar_controller.d.ts +65 -8
  254. package/dist/controllers/sidebar_controller.js +1069 -404
  255. package/dist/controllers/sidebar_controller.js.map +1 -1
  256. package/dist/controllers/skeleton_controller.d.ts +23 -0
  257. package/dist/controllers/skeleton_controller.js +259 -101
  258. package/dist/controllers/skeleton_controller.js.map +1 -1
  259. package/dist/controllers/slider_controller.d.ts +29 -2
  260. package/dist/controllers/slider_controller.js +187 -86
  261. package/dist/controllers/slider_controller.js.map +1 -1
  262. package/dist/controllers/smart_sticky_header_controller.d.ts +22 -4
  263. package/dist/controllers/smart_sticky_header_controller.js +173 -66
  264. package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
  265. package/dist/controllers/sortable_controller.d.ts +4 -4
  266. package/dist/controllers/sortable_controller.js +26 -125
  267. package/dist/controllers/sortable_controller.js.map +1 -1
  268. package/dist/controllers/spinner_controller.d.ts +39 -4
  269. package/dist/controllers/spinner_controller.js +288 -173
  270. package/dist/controllers/spinner_controller.js.map +1 -1
  271. package/dist/controllers/step_indicator_controller.d.ts +9 -4
  272. package/dist/controllers/step_indicator_controller.js +105 -46
  273. package/dist/controllers/step_indicator_controller.js.map +1 -1
  274. package/dist/controllers/stepper_controller.d.ts +14 -6
  275. package/dist/controllers/stepper_controller.js +120 -73
  276. package/dist/controllers/stepper_controller.js.map +1 -1
  277. package/dist/controllers/stick_to_bottom_controller.d.ts +9 -1
  278. package/dist/controllers/stick_to_bottom_controller.js +275 -140
  279. package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
  280. package/dist/controllers/sticky_observer_controller.d.ts +11 -0
  281. package/dist/controllers/sticky_observer_controller.js +220 -49
  282. package/dist/controllers/sticky_observer_controller.js.map +1 -1
  283. package/dist/controllers/submit_once_controller.d.ts +24 -8
  284. package/dist/controllers/submit_once_controller.js +376 -227
  285. package/dist/controllers/submit_once_controller.js.map +1 -1
  286. package/dist/controllers/switch_controller.d.ts +18 -4
  287. package/dist/controllers/switch_controller.js +201 -84
  288. package/dist/controllers/switch_controller.js.map +1 -1
  289. package/dist/controllers/tabs_controller.d.ts +9 -6
  290. package/dist/controllers/tabs_controller.js +41 -30
  291. package/dist/controllers/tabs_controller.js.map +1 -1
  292. package/dist/controllers/tags_input_controller.d.ts +16 -3
  293. package/dist/controllers/tags_input_controller.js +293 -173
  294. package/dist/controllers/tags_input_controller.js.map +1 -1
  295. package/dist/controllers/textarea_autosize_controller.d.ts +11 -1
  296. package/dist/controllers/textarea_autosize_controller.js +108 -74
  297. package/dist/controllers/textarea_autosize_controller.js.map +1 -1
  298. package/dist/controllers/theme_controller.d.ts +24 -11
  299. package/dist/controllers/theme_controller.js +140 -116
  300. package/dist/controllers/theme_controller.js.map +1 -1
  301. package/dist/controllers/time_picker_controller.d.ts +55 -5
  302. package/dist/controllers/time_picker_controller.js +331 -127
  303. package/dist/controllers/time_picker_controller.js.map +1 -1
  304. package/dist/controllers/toast_controller.d.ts +40 -16
  305. package/dist/controllers/toast_controller.js +244 -324
  306. package/dist/controllers/toast_controller.js.map +1 -1
  307. package/dist/controllers/toggle_group_controller.d.ts +14 -6
  308. package/dist/controllers/toggle_group_controller.js +252 -191
  309. package/dist/controllers/toggle_group_controller.js.map +1 -1
  310. package/dist/controllers/toolbar_controller.d.ts +8 -6
  311. package/dist/controllers/toolbar_controller.js +219 -120
  312. package/dist/controllers/toolbar_controller.js.map +1 -1
  313. package/dist/controllers/tooltip_controller.d.ts +49 -4
  314. package/dist/controllers/tooltip_controller.js +158 -124
  315. package/dist/controllers/tooltip_controller.js.map +1 -1
  316. package/dist/controllers/transition_controller.d.ts +22 -4
  317. package/dist/controllers/transition_controller.js +123 -119
  318. package/dist/controllers/transition_controller.js.map +1 -1
  319. package/dist/controllers/tree_view_controller.d.ts +22 -12
  320. package/dist/controllers/tree_view_controller.js +283 -369
  321. package/dist/controllers/tree_view_controller.js.map +1 -1
  322. package/dist/index.d.ts +10 -5
  323. package/dist/index.js +8573 -10410
  324. package/dist/index.js.map +1 -1
  325. package/dist/inspector/cli.d.ts +38 -14
  326. package/dist/inspector/cli.js +108 -40
  327. package/dist/inspector/cli.js.map +1 -1
  328. package/dist/inspector/cli_bin.js +145 -64
  329. package/dist/inspector/cli_bin.js.map +1 -1
  330. package/dist/inspector/examples.json +20 -20
  331. package/dist/inspector/manifest.json +802 -101
  332. package/dist/positioning/index.d.ts +15 -7
  333. package/dist/positioning/index.js +154 -47
  334. package/dist/positioning/index.js.map +1 -1
  335. package/package.json +11 -10
  336. package/dist/controllers/reset_before_cache_controller.d.ts +0 -76
  337. package/dist/controllers/reset_before_cache_controller.js.map +0 -1
@@ -1,8 +1,27 @@
1
1
  import { Controller } from '@hotwired/stimulus';
2
2
 
3
- // src/controllers/flash_controller.ts
3
+ var FOCUS_EVENTS = /* @__PURE__ */ new Set(["blur", "focus", "focusin", "focusout"]);
4
+ var POINTER_EVENTS = /* @__PURE__ */ new Set(["mouseenter", "mouseleave", "pointerenter", "pointerleave"]);
5
+ function stateReasonFor(event) {
6
+ if (!event) return "api";
7
+ if (FOCUS_EVENTS.has(event.type)) return "focus";
8
+ if (POINTER_EVENTS.has(event.type)) return "pointer";
9
+ return "user";
10
+ }
11
+
12
+ function actionSource(source) {
13
+ if (source instanceof HTMLElement) {
14
+ return { event: null, host: source, origin: source, reason: "api" };
15
+ }
16
+ const event = source ?? null;
17
+ return {
18
+ event,
19
+ host: event?.currentTarget instanceof Element ? event.currentTarget : null,
20
+ origin: event?.target instanceof Element ? event.target : null,
21
+ reason: stateReasonFor(event)
22
+ };
23
+ }
4
24
 
5
- // src/utils/announce.ts
6
25
  function announce(message, options = {}) {
7
26
  const text = message.trim();
8
27
  if (text.length === 0) return;
@@ -13,36 +32,6 @@ function announce(message, options = {}) {
13
32
  );
14
33
  }
15
34
 
16
- // src/utils/before_cache_reset.ts
17
- var BeforeCacheReset = class _BeforeCacheReset {
18
- /** Every subscribed instance, iterated by the one shared document listener. */
19
- static #subscribers = /* @__PURE__ */ new Set();
20
- /** The shared listener; installed while at least one instance is subscribed. */
21
- static #onBeforeCache = () => {
22
- for (const subscriber of _BeforeCacheReset.#subscribers) subscriber.#rewind();
23
- };
24
- #rewind;
25
- /** @param rewind - the pass that returns this controller's state to its initial form. */
26
- constructor(rewind) {
27
- this.#rewind = rewind;
28
- }
29
- /** Subscribes to `turbo:before-cache`; call from `connect()`. Idempotent. */
30
- activate() {
31
- const first = _BeforeCacheReset.#subscribers.size === 0;
32
- _BeforeCacheReset.#subscribers.add(this);
33
- if (first) {
34
- document.addEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
35
- }
36
- }
37
- /** Unsubscribes; call from `disconnect()`. Safe when never subscribed. */
38
- deactivate() {
39
- _BeforeCacheReset.#subscribers.delete(this);
40
- if (_BeforeCacheReset.#subscribers.size > 0) return;
41
- document.removeEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
42
- }
43
- };
44
-
45
- // src/utils/event_owner.ts
46
35
  function ownerIndex(candidates, node) {
47
36
  if (!(node instanceof Node)) return -1;
48
37
  return candidates.findIndex((candidate) => candidate.contains(node));
@@ -51,43 +40,79 @@ function ownerOf(candidates, node) {
51
40
  return candidates[ownerIndex(candidates, node)] ?? null;
52
41
  }
53
42
 
54
- // src/utils/safe_timeout.ts
43
+ var ListenerSet = class {
44
+ #abort = new AbortController();
45
+ add(target, type, handler, options) {
46
+ target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
47
+ }
48
+ dispose() {
49
+ this.#abort.abort();
50
+ this.#abort = new AbortController();
51
+ }
52
+ };
53
+
54
+ var HoverRecheck = class {
55
+ #document;
56
+ #release;
57
+ #listeners = new ListenerSet();
58
+ #pending = /* @__PURE__ */ new Set();
59
+ #tickets = /* @__PURE__ */ new WeakMap();
60
+ constructor(document2, release) {
61
+ this.#document = document2;
62
+ this.#release = release;
63
+ }
64
+ wait(element) {
65
+ this.#pending.add(element);
66
+ this.#tickets.set(element, /* @__PURE__ */ Symbol());
67
+ this.#listeners.add(this.#document, "pointermove", this.#onPointerMove, {
68
+ capture: true,
69
+ passive: true
70
+ });
71
+ }
72
+ cancel(element) {
73
+ this.#tickets.delete(element);
74
+ this.#pending.delete(element);
75
+ if (this.#pending.size === 0) this.#listeners.dispose();
76
+ }
77
+ dispose() {
78
+ this.#listeners.dispose();
79
+ this.#pending.clear();
80
+ this.#tickets = /* @__PURE__ */ new WeakMap();
81
+ }
82
+ #onPointerMove = () => {
83
+ this.#listeners.dispose();
84
+ const waiting = [...this.#pending].map(
85
+ (element) => [element, this.#tickets.get(element)]
86
+ );
87
+ this.#pending.clear();
88
+ for (const [element, ticket] of waiting) {
89
+ if (this.#tickets.get(element) !== ticket) continue;
90
+ if (element.matches(":hover")) continue;
91
+ this.#release(element);
92
+ }
93
+ };
94
+ };
95
+
96
+ var MAX_TIMER_DELAY_MS = 2147483647;
97
+
55
98
  var TimerRegistry = class {
56
- /** Live timer ids that have not yet been cleared (or, for timeouts, fired). */
57
99
  ids = /* @__PURE__ */ new Set();
58
- /**
59
- * Cancels a single tracked timer.
60
- *
61
- * No-ops if the id is unknown (already cleared, fired, or never owned by this
62
- * registry), so callers can clear defensively without guarding.
63
- */
64
100
  clear(id) {
65
101
  if (this.ids.delete(id)) {
66
102
  this.cancel(id);
67
103
  }
68
104
  }
69
- /**
70
- * Cancels every tracked timer. Call this from a controller's `disconnect()`
71
- * to guarantee no timer outlives the element.
72
- */
73
105
  clearAll() {
74
106
  for (const id of this.ids) {
75
107
  this.cancel(id);
76
108
  }
77
109
  this.ids.clear();
78
110
  }
79
- /** Number of timers currently tracked (pending). */
80
111
  get size() {
81
112
  return this.ids.size;
82
113
  }
83
114
  };
84
115
  var SafeTimeout = class extends TimerRegistry {
85
- /**
86
- * Schedules `callback` after `delay` ms and returns the timer id.
87
- *
88
- * The id is removed from the registry automatically when the timeout fires,
89
- * so {@link TimerRegistry.size | size} reflects only still-pending timers.
90
- */
91
116
  set(callback, delay) {
92
117
  const id = this.schedule(() => {
93
118
  this.ids.delete(id);
@@ -104,16 +129,9 @@ var SafeTimeout = class extends TimerRegistry {
104
129
  }
105
130
  };
106
131
 
107
- // src/utils/keyed_timers.ts
108
132
  var KeyedTimers = class {
109
133
  #timers = new SafeTimeout();
110
- /** The pending timer of each key; an entry lives exactly as long as its timer. */
111
134
  #pending = /* @__PURE__ */ new Map();
112
- /**
113
- * Arms `callback` after `delay` ms for `key`, cancelling the timer `key` had
114
- * pending. The entry is dropped before the callback runs, so the callback sees
115
- * the key unarmed and may arm it again for the next round.
116
- */
117
135
  set(key, callback, delay) {
118
136
  this.clear(key);
119
137
  const id = this.#timers.set(() => {
@@ -122,76 +140,38 @@ var KeyedTimers = class {
122
140
  }, delay);
123
141
  this.#pending.set(key, id);
124
142
  }
125
- /**
126
- * Cancels `key`'s pending timer, if it has one.
127
- *
128
- * A timer id is a positive integer, so `-1` stands for "nothing pending" and
129
- * the registry ignores an id it does not own — the unarmed case needs no
130
- * branch of its own, and no other key's timer can be reached from here.
131
- */
132
143
  clear(key) {
133
144
  this.#timers.clear(this.#pending.get(key) ?? -1);
134
145
  this.#pending.delete(key);
135
146
  }
136
- /**
137
- * Cancels every pending timer and forgets every key. Call this from a
138
- * controller's `disconnect()` so no timer, and no entry, outlives the element.
139
- */
140
147
  clearAll() {
141
148
  this.#timers.clearAll();
142
149
  this.#pending.clear();
143
150
  }
144
- /** Whether `key` has a timer pending. */
145
151
  has(key) {
146
152
  return this.#pending.has(key);
147
153
  }
148
154
  };
149
155
 
150
- // src/utils/listener_set.ts
151
- var ListenerSet = class {
152
- /** The generation every `add` joins until the next `dispose()`. */
153
- #abort = new AbortController();
154
- /**
155
- * Attaches `handler` to the open generation, exactly as the caller spelled it.
156
- *
157
- * The set supplies the signal, so `options` carries everything else the DOM
158
- * accepts — `capture` included, which has to match at release time and no
159
- * longer has a second place to drift from.
160
- */
161
- add(target, type, handler, options) {
162
- target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
163
- }
164
- /**
165
- * Releases every listener of the open generation, synchronously, and opens the
166
- * next one. Idempotent, and safe before anything has been added.
167
- */
168
- dispose() {
169
- this.#abort.abort();
170
- this.#abort = new AbortController();
171
- }
172
- };
173
-
174
- // src/utils/microtask_coalescer.ts
175
156
  var MicrotaskCoalescer = class {
176
157
  #run;
177
158
  #queued = false;
178
159
  #active = false;
179
160
  #generation = 0;
180
- /** @param run - the single reconciliation pass, invoked at most once per batch. */
181
161
  constructor(run) {
182
162
  this.#run = run;
183
163
  }
184
- /** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
185
164
  activate() {
186
165
  this.#active = true;
187
166
  }
188
- /** Closes the window and drops any pending pass; call from `disconnect()`. */
189
167
  cancel() {
190
168
  this.#active = false;
191
169
  this.#queued = false;
192
170
  this.#generation += 1;
193
171
  }
194
- /** Requests one pass after the batch settles. Idempotent; inert outside the window. */
172
+ get pending() {
173
+ return this.#queued;
174
+ }
195
175
  schedule() {
196
176
  if (!this.#active || this.#queued) return;
197
177
  this.#queued = true;
@@ -204,27 +184,88 @@ var MicrotaskCoalescer = class {
204
184
  }
205
185
  };
206
186
 
207
- // src/utils/pausable_timers.ts
187
+ var MorphRenderWatcher = class {
188
+ #listeners = new ListenerSet();
189
+ #pass;
190
+ #element = null;
191
+ #onMorph = () => this.#pass.schedule();
192
+ constructor(run) {
193
+ this.#pass = new MicrotaskCoalescer(run);
194
+ }
195
+ observe(element) {
196
+ if (this.#element === element) return;
197
+ this.disconnect();
198
+ this.#element = element;
199
+ this.#pass.activate();
200
+ this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
201
+ }
202
+ schedule() {
203
+ this.#pass.schedule();
204
+ }
205
+ get pending() {
206
+ return this.#pass.pending;
207
+ }
208
+ disconnect() {
209
+ this.#listeners.dispose();
210
+ this.#pass.cancel();
211
+ this.#element = null;
212
+ }
213
+ };
214
+
215
+ var NUMBER_BOUNDS = {
216
+ finite: { finite: true },
217
+ timer: { finite: true, min: 0, max: MAX_TIMER_DELAY_MS }};
218
+ function matchesNumberBounds(value, bounds) {
219
+ if (!Number.isFinite(value)) {
220
+ const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
221
+ if (direction === null) return false;
222
+ if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
223
+ }
224
+ if (bounds.min !== void 0 && value < bounds.min) return false;
225
+ if (bounds.max !== void 0 && value > bounds.max) return false;
226
+ if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
227
+ if (bounds.integer && !Number.isInteger(value)) return false;
228
+ if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
229
+ return true;
230
+ }
231
+
232
+ function readNumber(raw, fallback, bounds) {
233
+ return matchesNumberBounds(raw, bounds) ? raw : fallback;
234
+ }
235
+
236
+ var NumberValueReader = class {
237
+ #lastRejected = /* @__PURE__ */ new Map();
238
+ read(owner, name, raw, fallback, bounds) {
239
+ const resolved = readNumber(raw, fallback, bounds);
240
+ if (matchesNumberBounds(raw, bounds)) {
241
+ this.#lastRejected.delete(name);
242
+ return resolved;
243
+ }
244
+ const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
245
+ const literal = owner.element.getAttribute(attribute);
246
+ if (literal === null) {
247
+ this.#lastRejected.delete(name);
248
+ return resolved;
249
+ }
250
+ if (this.#lastRejected.get(name) !== literal) {
251
+ this.#lastRejected.set(name, literal);
252
+ console.warn(
253
+ `Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
254
+ );
255
+ }
256
+ return resolved;
257
+ }
258
+ };
259
+
208
260
  var PausableTimers = class {
209
261
  #timers = new KeyedTimers();
210
- /** Every key armed or held; an entry goes as its timer fires, or with the key. */
211
262
  #entries = /* @__PURE__ */ new Map();
212
- /**
213
- * Arms `callback` after `delay` ms for `key`, replacing whatever `key` had.
214
- * A held key stays held and banks `delay` for its resume.
215
- */
216
263
  set(key, callback, delay) {
217
264
  const reasons = this.#entries.get(key)?.reasons ?? /* @__PURE__ */ new Set();
218
265
  const entry = { callback, startedAt: 0, remaining: delay, reasons };
219
266
  this.#entries.set(key, entry);
220
267
  if (reasons.size === 0) this.#arm(key, entry, callback);
221
268
  }
222
- /**
223
- * Holds `key` for `reason` and banks the time left, with a floor of one
224
- * millisecond. A key with no timer is held all the same. Reports whether this
225
- * call is the one that stopped a running timer, so the caller can write its
226
- * held-state hook exactly once.
227
- */
228
269
  pause(key, reason) {
229
270
  let entry = this.#entries.get(key);
230
271
  if (entry === void 0) {
@@ -237,11 +278,6 @@ var PausableTimers = class {
237
278
  entry.remaining = Math.max(1, entry.remaining - (Date.now() - entry.startedAt));
238
279
  return true;
239
280
  }
240
- /**
241
- * Releases `reason` on `key`, arming the banked time again once no reason is
242
- * left; a key released with no timer is forgotten. Reports whether this call
243
- * is the one that started the timer again.
244
- */
245
281
  resume(key, reason) {
246
282
  const entry = this.#entries.get(key);
247
283
  if (entry === void 0) return false;
@@ -255,38 +291,20 @@ var PausableTimers = class {
255
291
  this.#arm(key, entry, callback);
256
292
  return true;
257
293
  }
258
- /** Whether any reason holds `key`, with or without a timer. */
259
294
  isHeld(key) {
260
295
  return (this.#entries.get(key)?.reasons.size ?? 0) > 0;
261
296
  }
262
- /** Whether `key` is armed or held — that is, whether this registry drives it at all. */
263
297
  tracks(key) {
264
298
  return this.#entries.has(key);
265
299
  }
266
- /**
267
- * Cancels `key`'s timer and keeps its hold, so a release arms nothing; a key
268
- * nothing holds is forgotten.
269
- */
270
- disarm(key) {
271
- this.#timers.clear(key);
272
- const entry = this.#entries.get(key);
273
- if (entry?.reasons.size) entry.callback = null;
274
- else this.#entries.delete(key);
275
- }
276
- /** Cancels `key`'s timer and drops its hold. */
277
300
  clear(key) {
278
301
  this.#timers.clear(key);
279
302
  this.#entries.delete(key);
280
303
  }
281
- /**
282
- * Cancels every timer and forgets every key. Call this from a controller's
283
- * `disconnect()` so neither a timer nor a hold outlives the element.
284
- */
285
304
  clearAll() {
286
305
  this.#timers.clearAll();
287
306
  this.#entries.clear();
288
307
  }
289
- /** Starts `entry`'s banked time running for `key`, and drops it as `callback` fires. */
290
308
  #arm(key, entry, callback) {
291
309
  entry.startedAt = Date.now();
292
310
  this.#timers.set(
@@ -300,12 +318,10 @@ var PausableTimers = class {
300
318
  }
301
319
  };
302
320
 
303
- // src/utils/target_selector.ts
304
321
  function targetSelector(identifier, name) {
305
322
  return `[data-${identifier}-target~="${name}"]`;
306
323
  }
307
324
 
308
- // src/utils/transition_completion.ts
309
325
  function timeMs(value) {
310
326
  const trimmed = value.trim();
311
327
  const amount = Number.parseFloat(trimmed);
@@ -339,11 +355,11 @@ function maxTransitionTotalMs(style) {
339
355
  return maxTotalMs(transitionTimings(style));
340
356
  }
341
357
 
342
- // src/controllers/flash_controller.ts
343
358
  var ASSERTIVE_TYPES = /* @__PURE__ */ new Set(["alert", "error"]);
344
359
  var MESSAGE_PART = "message";
345
- var FlashController = class extends Controller {
346
- /** Selects the message parts, in the namespace this controller is registered under. */
360
+ var SHOWN_ATTRIBUTE = "shown";
361
+ var FlashController = class _FlashController extends Controller {
362
+ #numbers = new NumberValueReader();
347
363
  get #messageSelector() {
348
364
  return targetSelector(this.identifier, MESSAGE_PART);
349
365
  }
@@ -353,54 +369,31 @@ var FlashController = class extends Controller {
353
369
  pauseOnHover: { type: Boolean, default: true },
354
370
  max: { type: Number, default: 0 }
355
371
  };
372
+ static valueConstraints = {
373
+ duration: NUMBER_BOUNDS.timer,
374
+ max: NUMBER_BOUNDS.finite
375
+ };
356
376
  static actions = ["dismiss"];
357
377
  static events = ["show", "dismiss", "reconcile"];
358
- /** Removal timers for the leaving transition; the auto-dismiss ones live below. */
359
378
  #timers = new SafeTimeout();
360
- /**
361
- * Per-message auto-dismiss held open while the message is hovered or focused, and
362
- * the one record of which messages hover or focus holds, which the cap reads.
363
- */
364
379
  #dismiss = new PausableTimers();
365
- /**
366
- * Auto-dismiss of the messages taken on while `pauseOnHover` is off. It runs on
367
- * whatever holds the message; the hold itself still keeps the cap away.
368
- */
369
380
  #fixedDismiss = new KeyedTimers();
370
- /** Applies the cap again once the event that released a hold has run its course. */
371
- #reapply = new MicrotaskCoalescer(() => this.#reapplyCap());
372
- /** Messages the pointer or focus moved into since the cap was last applied again. */
381
+ #reapply = new MorphRenderWatcher(() => this.#reapplyCap());
373
382
  #spared = [];
374
- /** The next pointer movement, listened for while a hover hold waits on it. */
375
- #pointer = new ListenerSet();
376
- /** Messages whose hover hold waits for the next pointer movement to be confirmed. */
377
- #awaitingPointer = /* @__PURE__ */ new Set();
383
+ #pointer = new HoverRecheck(
384
+ this.element.ownerDocument,
385
+ (message) => this.#resume(message, "hover", null)
386
+ );
378
387
  #observer = null;
379
- /** Whether the controller is between `connect()` and `disconnect()`. */
380
388
  #connected = false;
381
- /**
382
- * The `max` the stack was last held to, or `null` before the first time. `connect()`
383
- * applies the cap only when `max` is not that one, so a reconnect of the same instance
384
- * leaves the stack as it was unless `max` changed while it was away.
385
- */
389
+ #arrivals = /* @__PURE__ */ new Set();
386
390
  #appliedMax = null;
387
- /** Messages already processed, in insertion order, to enforce `max` and avoid double work. */
388
391
  #order = [];
389
- /**
390
- * Messages between `leaving` and their removal. {@link FlashController.#beginDismiss}
391
- * releases the bookkeeping above *before* the transition wait, so for that window the
392
- * element is in the DOM but in neither collection — without this set a re-scan would
393
- * read it as a brand-new flash and show it a second time.
394
- */
395
392
  #leaving = /* @__PURE__ */ new Set();
396
- #beforeCache = new BeforeCacheReset(() => this.#rewindForCache());
393
+ get #shownAttribute() {
394
+ return `data-${this.identifier}-${SHOWN_ATTRIBUTE}`;
395
+ }
397
396
  #onEnter = (event) => this.#pause(event.currentTarget, event.type === "focusin" ? "focus" : "hover");
398
- /**
399
- * Releases hover or focus on a message. Focus moving between two of the message's
400
- * own controls is no release, since `focusout` names a control still inside it; and
401
- * the message the pointer or focus is moving into is spared when the release
402
- * applies the cap again, because its own hold only arrives after this event.
403
- */
404
397
  #onLeave = (event) => {
405
398
  const message = event.currentTarget;
406
399
  const next = ownerOf(this.#order, event.relatedTarget);
@@ -409,92 +402,58 @@ var FlashController = class extends Controller {
409
402
  };
410
403
  connect() {
411
404
  this.#connected = true;
412
- this.#reapply.activate();
405
+ const current = this.messageTargets;
406
+ for (const item of this.#arrivals) {
407
+ if (!current.includes(item)) this.#arrivals.delete(item);
408
+ }
409
+ this.#discardInherited();
410
+ this.#reapply.observe(this.element);
413
411
  for (const message of this.messageTargets) {
414
412
  if (this.#owns(message)) this.#process(message, true);
415
413
  }
416
- if (!Object.is(this.#appliedMax, this.maxValue)) {
417
- this.#appliedMax = this.maxValue;
414
+ if (!Object.is(this.#appliedMax, this.#safeMax)) {
415
+ this.#appliedMax = this.#safeMax;
418
416
  this.#enforceMax();
419
417
  }
420
418
  this.#syncObservation();
421
- this.#beforeCache.activate();
422
419
  }
423
420
  disconnect() {
424
421
  this.#connected = false;
425
- this.#beforeCache.deactivate();
426
422
  this.#stopObserving();
427
423
  this.#timers.clearAll();
428
424
  this.#dismiss.clearAll();
429
425
  this.#fixedDismiss.clearAll();
430
- this.#reapply.cancel();
426
+ this.#reapply.disconnect();
431
427
  this.#spared = [];
432
428
  this.#pointer.dispose();
433
- this.#awaitingPointer.clear();
434
429
  for (const message of this.#order) this.#unbindPause(message);
435
430
  this.#order.length = 0;
436
431
  this.#leaving.clear();
437
432
  }
438
- /**
439
- * Takes the managed flashes out of the page just before Turbo freezes it, so a
440
- * restored snapshot carries no notification the visitor has already received: the
441
- * fresh `connect()` there reads a leftover flash as a brand-new one and announces it
442
- * a second time. A message that never auto-dismisses (`duration: 0`) is one of these
443
- * too — that value governs the timer, not what belongs in a cached page. Removal
444
- * only: `dismiss` reports a dismissal, and freezing the page is not one.
445
- */
446
- #rewindForCache() {
447
- const removed = this.#order.length;
448
- for (const message of [...this.#order]) {
433
+ #discardInherited() {
434
+ let removed = 0;
435
+ for (const message of this.messageTargets) {
436
+ if (!message.hasAttribute(this.#shownAttribute) || this.#arrivals.has(message)) continue;
437
+ if (!this.#owns(message)) continue;
449
438
  message.remove();
450
- this.#forget(message);
439
+ removed += 1;
451
440
  }
452
- for (const message of this.#leaving) message.remove();
453
- this.#leaving.clear();
454
441
  if (removed > 0) this.dispatch("reconcile", { detail: { removed } });
455
442
  }
456
- /** Follows a `region` element swapped in — or arriving — at runtime (Turbo Stream). */
457
443
  regionTargetConnected() {
458
444
  this.#resync();
459
445
  }
460
- /** Releases the observation when the `region` element leaves the target set. */
461
446
  regionTargetDisconnected() {
462
447
  this.#resync();
463
448
  }
464
- /**
465
- * Holds the stack to a changed `max`: lowering it below the count dismisses the
466
- * oldest messages hover and focus do not hold, with reason `limit`, as an arrival
467
- * past the cap does, while raising it or setting `0` dismisses nothing. Stimulus can
468
- * also call this ahead of `connect()` — for an attribute that changed while the
469
- * controller was away, and on every connect for an undeclared `max`, with its default;
470
- * `connect()` decides then.
471
- */
472
449
  maxValueChanged() {
473
450
  if (!this.#connected) return;
474
- this.#appliedMax = this.maxValue;
451
+ this.#appliedMax = this.#safeMax;
475
452
  this.#enforceMax();
476
453
  }
477
- /**
478
- * Whether this controller owns `message`. Ownership is the current `region`'s
479
- * subtree: a message target anywhere else in the controller's scope is the
480
- * consumer's, and so is one in a region that has gone away. The initial scan, a
481
- * re-scan after a `region` swap, and a departure from the target set all resolve
482
- * ownership through this one test; the observation gets it structurally, by watching
483
- * that subtree and nothing else.
484
- */
485
454
  #owns(message) {
486
455
  return this.hasRegionTarget && this.regionTarget.contains(message);
487
456
  }
488
- /**
489
- * Releases a message that left the target set (a Turbo Stream `remove`, the consumer
490
- * detaching the node, or a morph that rewrote the target attribute in place): it
491
- * stops occupying a `max` slot, and both its pending auto-dismiss and an already
492
- * scheduled removal are cancelled. A move *within* the region keeps all of them —
493
- * which is why the element must still be a message to be treated as one: ownership
494
- * alone reads an in-place attribute rewrite as a move, and a node outside the target
495
- * set belongs to the consumer, so nothing here may dismiss it. A move may still have
496
- * ended a hold, so those are read again.
497
- */
498
457
  messageTargetDisconnected(message) {
499
458
  if (!this.#connected) return;
500
459
  const moved = this.#owns(message) && message.matches(this.#messageSelector);
@@ -503,15 +462,9 @@ var FlashController = class extends Controller {
503
462
  return;
504
463
  }
505
464
  this.#forget(message);
465
+ this.#arrivals.delete(message);
506
466
  this.#leaving.delete(message);
507
467
  }
508
- /**
509
- * Re-points the observation after a `region` swap and picks up the messages the
510
- * new element brought with it (dynamic inserts, so their own `role` announces
511
- * them). The `#connected` guard is load-bearing: Stimulus runs target callbacks
512
- * for the initial markup *before* `connect()` and again during teardown *after*
513
- * `disconnect()`, and re-observing there would outlive the controller.
514
- */
515
468
  #resync() {
516
469
  if (!this.#connected) return;
517
470
  this.#syncObservation();
@@ -519,11 +472,6 @@ var FlashController = class extends Controller {
519
472
  if (this.#owns(message)) this.#process(message, false);
520
473
  }
521
474
  }
522
- /**
523
- * Points the mutation observation at the current `region` target, re-resolved on
524
- * every sync rather than captured at connect, so an element swapped in at runtime
525
- * is observed instead of the detached original.
526
- */
527
475
  #syncObservation() {
528
476
  this.#stopObserving();
529
477
  if (!this.hasRegionTarget || typeof MutationObserver === "undefined") return;
@@ -534,11 +482,6 @@ var FlashController = class extends Controller {
534
482
  this.#observer?.disconnect();
535
483
  this.#observer = null;
536
484
  }
537
- /**
538
- * Hover and focus listeners, bound and unbound as a pair so the two sides stay in
539
- * sync. Every message gets them, whatever `pauseOnHover` says, because the cap reads
540
- * the holds they record; unbinding is idempotent (a no-op when nothing was bound).
541
- */
542
485
  #bindPause(message) {
543
486
  message.addEventListener("mouseenter", this.#onEnter);
544
487
  message.addEventListener("mouseleave", this.#onLeave);
@@ -551,13 +494,14 @@ var FlashController = class extends Controller {
551
494
  message.removeEventListener("focusin", this.#onEnter);
552
495
  message.removeEventListener("focusout", this.#onLeave);
553
496
  }
554
- /** Dismisses the flash whose close control fired the event. */
555
- dismiss(event) {
556
- const target = event.currentTarget || event.target;
557
- const message = target?.closest(this.#messageSelector);
558
- if (message) this.#beginDismiss(message, "user");
497
+ dismiss(input) {
498
+ const source = actionSource(input);
499
+ if (source.origin && source.origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
500
+ return;
501
+ const message = (source.origin ?? source.host)?.closest(this.#messageSelector);
502
+ if (message && this.messageTargets.includes(message))
503
+ this.#beginDismiss(message, source.reason);
559
504
  }
560
- /** Processes messages added after connect (Turbo Stream); their own role announces them. */
561
505
  #onMutations(mutations) {
562
506
  for (const mutation of mutations) {
563
507
  for (const node of mutation.addedNodes) {
@@ -569,14 +513,6 @@ var FlashController = class extends Controller {
569
513
  }
570
514
  }
571
515
  }
572
- /**
573
- * Applies role/state, wires pause listeners, takes the holds the message already has,
574
- * and schedules auto-dismiss. A message `connect()` takes on (`atConnect`) is bridged
575
- * to the Announcer and meets no cap here, `connect()` holding the stack to it; a later
576
- * insert announces through its own role and meets the cap as it arrives. Idempotent
577
- * per message, and a node that has left the region by the time its insertion is
578
- * reported is not taken on.
579
- */
580
516
  #process(message, atConnect) {
581
517
  if (this.#order.includes(message) || this.#leaving.has(message) || !this.#owns(message)) {
582
518
  return;
@@ -587,134 +523,74 @@ var FlashController = class extends Controller {
587
523
  message.setAttribute("role", assertive ? "alert" : "status");
588
524
  }
589
525
  message.setAttribute("data-flash-state", "visible");
526
+ message.setAttribute(this.#shownAttribute, "");
527
+ const known = this.#arrivals.has(message);
590
528
  this.#order.push(message);
529
+ this.#arrivals.add(message);
591
530
  this.#bindPause(message);
592
531
  this.#readHolds(message);
593
- const text = message.textContent?.trim() ?? "";
594
- this.dispatch("show", { target: message, detail: { type, message: text } });
595
- if (atConnect) announce(text, { assertive });
532
+ if (!known) {
533
+ const text = message.textContent?.trim() ?? "";
534
+ this.dispatch("show", { target: message, detail: { type, message: text } });
535
+ if (atConnect) announce(text, { assertive });
536
+ }
596
537
  this.#startTimer(message);
597
538
  if (!atConnect) this.#enforceMax([message]);
598
539
  }
599
- /**
600
- * Dismisses the oldest messages with reason `limit` while more than `max` are shown
601
- * (0 or less, or not a finite number, means no cap), passing over every message hover
602
- * or focus holds and those in `spared` — the message just taken on, or the ones the
603
- * pointer or focus moved into. Only messages still in the region count or go: one a
604
- * script has just taken out stays on the stack until its removal is reported. With
605
- * nothing else left to go the stack stays over the cap. The loop walks a copy and
606
- * re-reads the count on every step, because a `dismiss` listener may already have
607
- * changed both.
608
- *
609
- * @stimeoRenderRoot
610
- */
611
540
  #enforceMax(spared = []) {
612
- if (!Number.isFinite(this.maxValue) || this.maxValue <= 0) return;
613
- for (const message of [...this.#order]) {
614
- if (this.#shownCount() <= this.maxValue) return;
541
+ if (this.#safeMax <= 0) return;
542
+ for (const message of [...this.#arrivals]) {
543
+ if (this.#shownCount() <= this.#safeMax) return;
615
544
  if (this.#owns(message) && !spared.includes(message) && !this.#dismiss.isHeld(message)) {
616
545
  this.#beginDismiss(message, "limit");
617
546
  }
618
547
  }
619
548
  }
620
- /** How many messages the stack shows: the ones taken on that are still in the region. */
621
549
  #shownCount() {
622
550
  return this.#order.filter((message) => this.#owns(message)).length;
623
551
  }
624
- /**
625
- * Arms a message's auto-dismiss; a non-positive `duration` means it never expires.
626
- * The timer waits out hover and focus when `pauseOnHover` is on, and runs on
627
- * regardless when it is off.
628
- *
629
- * @stimeoRuntimeOnly `duration` is the delay of the one dismissal timer this call arms,
630
- * and `pauseOnHover` whether hover and focus hold that timer.
631
- */
632
552
  #startTimer(message) {
633
- if (this.durationValue <= 0) return;
553
+ if (this.#safeDuration <= 0) return;
634
554
  const dismiss = () => this.#beginDismiss(message, "timeout");
635
- if (this.pauseOnHoverValue) this.#dismiss.set(message, dismiss, this.durationValue);
636
- else this.#fixedDismiss.set(message, dismiss, this.durationValue);
637
- }
638
- /**
639
- * Records hover or focus on a message. Hover and focus are independent reasons:
640
- * the registry banks the time left on the first of them and waits for the last
641
- * (WCAG 2.2 2.2.1), and the cap passes over the message while either is held.
642
- */
555
+ if (this.pauseOnHoverValue) this.#dismiss.set(message, dismiss, this.#safeDuration);
556
+ else this.#fixedDismiss.set(message, dismiss, this.#safeDuration);
557
+ }
643
558
  #pause(message, reason) {
644
559
  this.#dismiss.pause(message, reason);
645
560
  }
646
- /**
647
- * Releases one reason, resuming the banked time once no reason is left. Releasing
648
- * the last one applies the cap again, sparing `spare`.
649
- */
650
561
  #resume(message, reason, spare) {
651
562
  const held = this.#dismiss.isHeld(message);
652
563
  this.#dismiss.resume(message, reason);
653
564
  if (held && !this.#dismiss.isHeld(message)) this.#capLater(spare);
654
565
  }
655
- /**
656
- * Applies the cap again once the current event is over, sparing `spare`. A hold can
657
- * end in a `focusout` the engine fires while it is itself taking the node out for a
658
- * script's move or removal, and dismissing the message inside that event would pull
659
- * the node from under the caller's own call.
660
- */
661
566
  #capLater(spare) {
662
567
  if (spare) this.#spared.push(spare);
663
568
  this.#reapply.schedule();
664
569
  }
665
- /** Applies the cap again, sparing every message gathered since the last pass. */
666
570
  #reapplyCap() {
571
+ for (const message of [...this.#order, ...this.#leaving]) {
572
+ if (!this.#owns(message)) continue;
573
+ message.setAttribute("data-flash-state", this.#leaving.has(message) ? "leaving" : "visible");
574
+ message.setAttribute(this.#shownAttribute, "");
575
+ if (!message.hasAttribute("role")) {
576
+ const assertive = ASSERTIVE_TYPES.has(message.getAttribute("data-flash-type") ?? "");
577
+ message.setAttribute("role", assertive ? "alert" : "status");
578
+ }
579
+ }
667
580
  const spared = this.#spared;
668
581
  this.#spared = [];
669
582
  this.#enforceMax(spared);
670
583
  }
671
- /**
672
- * Reads the holds of a message that moved within the region. Focus is read at once:
673
- * an ordinary move ends it with a `focusout` and `moveBefore` keeps it. Where the
674
- * pointer is, nothing says until it moves again: no `mouseleave` reaches a moved node,
675
- * and `:hover` can answer from before the move. A message still held waits for that
676
- * movement.
677
- */
678
584
  #afterMove(message) {
679
585
  if (!message.contains(document.activeElement)) this.#resume(message, "focus", null);
680
- if (this.#dismiss.isHeld(message)) this.#awaitPointer(message);
586
+ if (this.#dismiss.isHeld(message)) this.#pointer.wait(message);
681
587
  }
682
- /**
683
- * Takes the holds a message already has as it is taken on: focus inside it, and the
684
- * pointer over it by `:hover`. That answer can date from before the message came here,
685
- * so a hover hold taken on it waits for the next pointer movement to be confirmed.
686
- */
687
588
  #readHolds(message) {
688
589
  if (message.contains(document.activeElement)) this.#pause(message, "focus");
689
590
  if (!message.matches(":hover")) return;
690
591
  this.#pause(message, "hover");
691
- this.#awaitPointer(message);
592
+ this.#pointer.wait(message);
692
593
  }
693
- /** Leaves `message`'s hover hold to be confirmed or let go on the next pointer movement. */
694
- #awaitPointer(message) {
695
- this.#awaitingPointer.add(message);
696
- this.#pointer.add(document, "pointermove", this.#onPointerMove, {
697
- capture: true,
698
- passive: true
699
- });
700
- }
701
- /**
702
- * Reads `:hover` once the pointer has moved, when the answer is current, and lets go of
703
- * the hover hold of every waiting message the pointer is not over. Listens once.
704
- */
705
- #onPointerMove = () => {
706
- this.#pointer.dispose();
707
- const waiting = [...this.#awaitingPointer];
708
- this.#awaitingPointer.clear();
709
- for (const message of waiting) {
710
- if (!message.matches(":hover")) this.#resume(message, "hover", null);
711
- }
712
- };
713
- /**
714
- * Releases every per-message resource: timers, hold, stacking slot, pause listeners,
715
- * and the wait on the pointer. A message something still held counts as released: the
716
- * cap is applied again.
717
- */
718
594
  #forget(message) {
719
595
  if (this.#dismiss.isHeld(message)) this.#capLater(null);
720
596
  this.#dismiss.clear(message);
@@ -722,10 +598,8 @@ var FlashController = class extends Controller {
722
598
  const index = this.#order.indexOf(message);
723
599
  if (index !== -1) this.#order.splice(index, 1);
724
600
  this.#unbindPause(message);
725
- this.#awaitingPointer.delete(message);
726
- if (this.#awaitingPointer.size === 0) this.#pointer.dispose();
601
+ this.#pointer.cancel(message);
727
602
  }
728
- /** Marks a message leaving, then removes it after its CSS transition and emits dismiss. */
729
603
  #beginDismiss(message, reason) {
730
604
  if (!this.#order.includes(message)) return;
731
605
  this.#forget(message);
@@ -734,6 +608,7 @@ var FlashController = class extends Controller {
734
608
  const finalize = () => {
735
609
  if (!this.#leaving.delete(message)) return;
736
610
  message.remove();
611
+ this.#arrivals.delete(message);
737
612
  this.dispatch("dismiss", { detail: { element: message, reason } });
738
613
  };
739
614
  const transition = this.#transitionMs(message);
@@ -743,13 +618,29 @@ var FlashController = class extends Controller {
743
618
  finalize();
744
619
  }
745
620
  }
746
- /** Maximum transition total (duration + delay) of `el` in ms (0 when none / unsupported). */
747
621
  #transitionMs(el) {
748
622
  if (typeof window.getComputedStyle !== "function") return 0;
749
623
  return maxTransitionTotalMs(window.getComputedStyle(el));
750
624
  }
625
+ get #safeDuration() {
626
+ return this.#numbers.read(
627
+ this,
628
+ "duration",
629
+ this.durationValue,
630
+ _FlashController.values.duration.default,
631
+ _FlashController.valueConstraints.duration
632
+ );
633
+ }
634
+ get #safeMax() {
635
+ return this.#numbers.read(
636
+ this,
637
+ "max",
638
+ this.maxValue,
639
+ _FlashController.values.max.default,
640
+ _FlashController.valueConstraints.max
641
+ );
642
+ }
751
643
  };
752
644
 
753
645
  export { FlashController };
754
- //# sourceMappingURL=flash_controller.js.map
755
646
  //# sourceMappingURL=flash_controller.js.map