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/toast_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/event_owner.ts
6
25
  function ownerIndex(candidates, node) {
7
26
  if (!(node instanceof Node)) return -1;
8
27
  return candidates.findIndex((candidate) => candidate.contains(node));
@@ -11,51 +30,78 @@ function ownerOf(candidates, node) {
11
30
  return candidates[ownerIndex(candidates, node)] ?? null;
12
31
  }
13
32
 
14
- // src/utils/listener_set.ts
15
33
  var ListenerSet = class {
16
- /** The generation every `add` joins until the next `dispose()`. */
17
34
  #abort = new AbortController();
18
- /**
19
- * Attaches `handler` to the open generation, exactly as the caller spelled it.
20
- *
21
- * The set supplies the signal, so `options` carries everything else the DOM
22
- * accepts — `capture` included, which has to match at release time and no
23
- * longer has a second place to drift from.
24
- */
25
35
  add(target, type, handler, options) {
26
36
  target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
27
37
  }
28
- /**
29
- * Releases every listener of the open generation, synchronously, and opens the
30
- * next one. Idempotent, and safe before anything has been added.
31
- */
32
38
  dispose() {
33
39
  this.#abort.abort();
34
40
  this.#abort = new AbortController();
35
41
  }
36
42
  };
37
43
 
38
- // src/utils/microtask_coalescer.ts
44
+ var HoverRecheck = class {
45
+ #document;
46
+ #release;
47
+ #listeners = new ListenerSet();
48
+ #pending = /* @__PURE__ */ new Set();
49
+ #tickets = /* @__PURE__ */ new WeakMap();
50
+ constructor(document2, release) {
51
+ this.#document = document2;
52
+ this.#release = release;
53
+ }
54
+ wait(element) {
55
+ this.#pending.add(element);
56
+ this.#tickets.set(element, /* @__PURE__ */ Symbol());
57
+ this.#listeners.add(this.#document, "pointermove", this.#onPointerMove, {
58
+ capture: true,
59
+ passive: true
60
+ });
61
+ }
62
+ cancel(element) {
63
+ this.#tickets.delete(element);
64
+ this.#pending.delete(element);
65
+ if (this.#pending.size === 0) this.#listeners.dispose();
66
+ }
67
+ dispose() {
68
+ this.#listeners.dispose();
69
+ this.#pending.clear();
70
+ this.#tickets = /* @__PURE__ */ new WeakMap();
71
+ }
72
+ #onPointerMove = () => {
73
+ this.#listeners.dispose();
74
+ const waiting = [...this.#pending].map(
75
+ (element) => [element, this.#tickets.get(element)]
76
+ );
77
+ this.#pending.clear();
78
+ for (const [element, ticket] of waiting) {
79
+ if (this.#tickets.get(element) !== ticket) continue;
80
+ if (element.matches(":hover")) continue;
81
+ this.#release(element);
82
+ }
83
+ };
84
+ };
85
+
39
86
  var MicrotaskCoalescer = class {
40
87
  #run;
41
88
  #queued = false;
42
89
  #active = false;
43
90
  #generation = 0;
44
- /** @param run - the single reconciliation pass, invoked at most once per batch. */
45
91
  constructor(run) {
46
92
  this.#run = run;
47
93
  }
48
- /** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
49
94
  activate() {
50
95
  this.#active = true;
51
96
  }
52
- /** Closes the window and drops any pending pass; call from `disconnect()`. */
53
97
  cancel() {
54
98
  this.#active = false;
55
99
  this.#queued = false;
56
100
  this.#generation += 1;
57
101
  }
58
- /** Requests one pass after the batch settles. Idempotent; inert outside the window. */
102
+ get pending() {
103
+ return this.#queued;
104
+ }
59
105
  schedule() {
60
106
  if (!this.#active || this.#queued) return;
61
107
  this.#queued = true;
@@ -68,43 +114,99 @@ var MicrotaskCoalescer = class {
68
114
  }
69
115
  };
70
116
 
71
- // src/utils/safe_timeout.ts
117
+ var MorphRenderWatcher = class {
118
+ #listeners = new ListenerSet();
119
+ #pass;
120
+ #element = null;
121
+ #onMorph = () => this.#pass.schedule();
122
+ constructor(run) {
123
+ this.#pass = new MicrotaskCoalescer(run);
124
+ }
125
+ observe(element) {
126
+ if (this.#element === element) return;
127
+ this.disconnect();
128
+ this.#element = element;
129
+ this.#pass.activate();
130
+ this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
131
+ }
132
+ schedule() {
133
+ this.#pass.schedule();
134
+ }
135
+ get pending() {
136
+ return this.#pass.pending;
137
+ }
138
+ disconnect() {
139
+ this.#listeners.dispose();
140
+ this.#pass.cancel();
141
+ this.#element = null;
142
+ }
143
+ };
144
+
145
+ var MAX_TIMER_DELAY_MS = 2147483647;
146
+
147
+ var NUMBER_BOUNDS = {
148
+ finite: { finite: true },
149
+ timer: { finite: true, min: 0, max: MAX_TIMER_DELAY_MS }};
150
+ function matchesNumberBounds(value, bounds) {
151
+ if (!Number.isFinite(value)) {
152
+ const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
153
+ if (direction === null) return false;
154
+ if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
155
+ }
156
+ if (bounds.min !== void 0 && value < bounds.min) return false;
157
+ if (bounds.max !== void 0 && value > bounds.max) return false;
158
+ if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
159
+ if (bounds.integer && !Number.isInteger(value)) return false;
160
+ if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
161
+ return true;
162
+ }
163
+
164
+ function readNumber(raw, fallback, bounds) {
165
+ return matchesNumberBounds(raw, bounds) ? raw : fallback;
166
+ }
167
+
168
+ var NumberValueReader = class {
169
+ #lastRejected = /* @__PURE__ */ new Map();
170
+ read(owner, name, raw, fallback, bounds) {
171
+ const resolved = readNumber(raw, fallback, bounds);
172
+ if (matchesNumberBounds(raw, bounds)) {
173
+ this.#lastRejected.delete(name);
174
+ return resolved;
175
+ }
176
+ const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
177
+ const literal = owner.element.getAttribute(attribute);
178
+ if (literal === null) {
179
+ this.#lastRejected.delete(name);
180
+ return resolved;
181
+ }
182
+ if (this.#lastRejected.get(name) !== literal) {
183
+ this.#lastRejected.set(name, literal);
184
+ console.warn(
185
+ `Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
186
+ );
187
+ }
188
+ return resolved;
189
+ }
190
+ };
191
+
72
192
  var TimerRegistry = class {
73
- /** Live timer ids that have not yet been cleared (or, for timeouts, fired). */
74
193
  ids = /* @__PURE__ */ new Set();
75
- /**
76
- * Cancels a single tracked timer.
77
- *
78
- * No-ops if the id is unknown (already cleared, fired, or never owned by this
79
- * registry), so callers can clear defensively without guarding.
80
- */
81
194
  clear(id) {
82
195
  if (this.ids.delete(id)) {
83
196
  this.cancel(id);
84
197
  }
85
198
  }
86
- /**
87
- * Cancels every tracked timer. Call this from a controller's `disconnect()`
88
- * to guarantee no timer outlives the element.
89
- */
90
199
  clearAll() {
91
200
  for (const id of this.ids) {
92
201
  this.cancel(id);
93
202
  }
94
203
  this.ids.clear();
95
204
  }
96
- /** Number of timers currently tracked (pending). */
97
205
  get size() {
98
206
  return this.ids.size;
99
207
  }
100
208
  };
101
209
  var SafeTimeout = class extends TimerRegistry {
102
- /**
103
- * Schedules `callback` after `delay` ms and returns the timer id.
104
- *
105
- * The id is removed from the registry automatically when the timeout fires,
106
- * so {@link TimerRegistry.size | size} reflects only still-pending timers.
107
- */
108
210
  set(callback, delay) {
109
211
  const id = this.schedule(() => {
110
212
  this.ids.delete(id);
@@ -121,16 +223,9 @@ var SafeTimeout = class extends TimerRegistry {
121
223
  }
122
224
  };
123
225
 
124
- // src/utils/keyed_timers.ts
125
226
  var KeyedTimers = class {
126
227
  #timers = new SafeTimeout();
127
- /** The pending timer of each key; an entry lives exactly as long as its timer. */
128
228
  #pending = /* @__PURE__ */ new Map();
129
- /**
130
- * Arms `callback` after `delay` ms for `key`, cancelling the timer `key` had
131
- * pending. The entry is dropped before the callback runs, so the callback sees
132
- * the key unarmed and may arm it again for the next round.
133
- */
134
229
  set(key, callback, delay) {
135
230
  this.clear(key);
136
231
  const id = this.#timers.set(() => {
@@ -139,52 +234,28 @@ var KeyedTimers = class {
139
234
  }, delay);
140
235
  this.#pending.set(key, id);
141
236
  }
142
- /**
143
- * Cancels `key`'s pending timer, if it has one.
144
- *
145
- * A timer id is a positive integer, so `-1` stands for "nothing pending" and
146
- * the registry ignores an id it does not own — the unarmed case needs no
147
- * branch of its own, and no other key's timer can be reached from here.
148
- */
149
237
  clear(key) {
150
238
  this.#timers.clear(this.#pending.get(key) ?? -1);
151
239
  this.#pending.delete(key);
152
240
  }
153
- /**
154
- * Cancels every pending timer and forgets every key. Call this from a
155
- * controller's `disconnect()` so no timer, and no entry, outlives the element.
156
- */
157
241
  clearAll() {
158
242
  this.#timers.clearAll();
159
243
  this.#pending.clear();
160
244
  }
161
- /** Whether `key` has a timer pending. */
162
245
  has(key) {
163
246
  return this.#pending.has(key);
164
247
  }
165
248
  };
166
249
 
167
- // src/utils/pausable_timers.ts
168
250
  var PausableTimers = class {
169
251
  #timers = new KeyedTimers();
170
- /** Every key armed or held; an entry goes as its timer fires, or with the key. */
171
252
  #entries = /* @__PURE__ */ new Map();
172
- /**
173
- * Arms `callback` after `delay` ms for `key`, replacing whatever `key` had.
174
- * A held key stays held and banks `delay` for its resume.
175
- */
176
253
  set(key, callback, delay) {
177
254
  const reasons = this.#entries.get(key)?.reasons ?? /* @__PURE__ */ new Set();
178
255
  const entry = { callback, startedAt: 0, remaining: delay, reasons };
179
256
  this.#entries.set(key, entry);
180
257
  if (reasons.size === 0) this.#arm(key, entry, callback);
181
258
  }
182
- /**
183
- * Holds `key` for `reason` and banks the time left, with a floor of one
184
- * millisecond. A key with no timer is held all the same. Reports whether this
185
- * call is the one that stopped a running timer, so the caller can write its
186
- * held-state hook exactly once.
187
- */
188
259
  pause(key, reason) {
189
260
  let entry = this.#entries.get(key);
190
261
  if (entry === void 0) {
@@ -197,11 +268,6 @@ var PausableTimers = class {
197
268
  entry.remaining = Math.max(1, entry.remaining - (Date.now() - entry.startedAt));
198
269
  return true;
199
270
  }
200
- /**
201
- * Releases `reason` on `key`, arming the banked time again once no reason is
202
- * left; a key released with no timer is forgotten. Reports whether this call
203
- * is the one that started the timer again.
204
- */
205
271
  resume(key, reason) {
206
272
  const entry = this.#entries.get(key);
207
273
  if (entry === void 0) return false;
@@ -215,38 +281,20 @@ var PausableTimers = class {
215
281
  this.#arm(key, entry, callback);
216
282
  return true;
217
283
  }
218
- /** Whether any reason holds `key`, with or without a timer. */
219
284
  isHeld(key) {
220
285
  return (this.#entries.get(key)?.reasons.size ?? 0) > 0;
221
286
  }
222
- /** Whether `key` is armed or held — that is, whether this registry drives it at all. */
223
287
  tracks(key) {
224
288
  return this.#entries.has(key);
225
289
  }
226
- /**
227
- * Cancels `key`'s timer and keeps its hold, so a release arms nothing; a key
228
- * nothing holds is forgotten.
229
- */
230
- disarm(key) {
231
- this.#timers.clear(key);
232
- const entry = this.#entries.get(key);
233
- if (entry?.reasons.size) entry.callback = null;
234
- else this.#entries.delete(key);
235
- }
236
- /** Cancels `key`'s timer and drops its hold. */
237
290
  clear(key) {
238
291
  this.#timers.clear(key);
239
292
  this.#entries.delete(key);
240
293
  }
241
- /**
242
- * Cancels every timer and forgets every key. Call this from a controller's
243
- * `disconnect()` so neither a timer nor a hold outlives the element.
244
- */
245
294
  clearAll() {
246
295
  this.#timers.clearAll();
247
296
  this.#entries.clear();
248
297
  }
249
- /** Starts `entry`'s banked time running for `key`, and drops it as `callback` fires. */
250
298
  #arm(key, entry, callback) {
251
299
  entry.startedAt = Date.now();
252
300
  this.#timers.set(
@@ -260,18 +308,15 @@ var PausableTimers = class {
260
308
  }
261
309
  };
262
310
 
263
- // src/utils/target_selector.ts
264
311
  function targetSelector(identifier, name) {
265
312
  return `[data-${identifier}-target~="${name}"]`;
266
313
  }
267
314
 
268
- // src/utils/template_row.ts
269
315
  function cloneTemplateRoot(template) {
270
316
  const root = template.content.firstElementChild;
271
317
  return root instanceof HTMLElement ? root.cloneNode(true) : null;
272
318
  }
273
319
 
274
- // src/utils/transition_completion.ts
275
320
  function timeMs(value) {
276
321
  const trimmed = value.trim();
277
322
  const amount = Number.parseFloat(trimmed);
@@ -305,62 +350,52 @@ function maxTransitionTotalMs(style) {
305
350
  return maxTotalMs(transitionTimings(style));
306
351
  }
307
352
 
308
- // src/controllers/toast_controller.ts
309
353
  var DELEGATED_EVENTS = ["click", "focusin", "focusout", "keydown", "mouseover", "mouseout"];
310
- var ToastController = class extends Controller {
354
+ var ToastController = class _ToastController extends Controller {
355
+ #numbers = new NumberValueReader();
311
356
  static targets = ["list", "template", "item"];
312
357
  static values = {
313
358
  duration: { type: Number, default: 0 },
314
359
  max: { type: Number, default: 3 }
315
360
  };
361
+ static valueConstraints = {
362
+ duration: NUMBER_BOUNDS.timer,
363
+ max: NUMBER_BOUNDS.finite
364
+ };
316
365
  static actions = ["dismiss", "onKeydown", "pause", "resume", "show"];
317
366
  static events = ["dismiss", "show"];
318
- /** Removal timers for the leaving transition; the auto-dismiss ones live below. */
319
367
  #timers = new SafeTimeout();
320
- /** Per-toast auto-dismiss, held open while the toast is hovered or focused. */
321
368
  #dismiss = new PausableTimers();
322
- /** Toasts taken on and not let go; a move within the list keeps a toast here. */
323
369
  #taken = /* @__PURE__ */ new Set();
324
- /** Applies the `max` limit again once the event that released a hold has run its course. */
325
- #reapply = new MicrotaskCoalescer(() => this.#reapplyLimit());
326
- /** Toasts the pointer or focus moved into since the limit was last applied again. */
370
+ #outside = /* @__PURE__ */ new WeakSet();
371
+ #phases = /* @__PURE__ */ new WeakMap();
372
+ #reapply = new MorphRenderWatcher(() => this.#reapplyLimit());
327
373
  #spared = [];
328
- /** The next pointer movement, listened for while a hover hold waits on it. */
329
- #pointer = new ListenerSet();
330
- /** Toasts whose hover hold waits for the next pointer movement to be confirmed. */
331
- #awaitingPointer = /* @__PURE__ */ new Set();
332
- /**
333
- * Pending one-shot `requestAnimationFrame` handles (the entering→visible flip).
334
- * Tracked so {@link disconnect} can cancel any that have not fired, preventing a
335
- * detached element from being mutated after it leaves the DOM (Turbo).
336
- */
374
+ #pointer = new HoverRecheck(
375
+ this.element.ownerDocument,
376
+ (item) => this.#release(item, "hover", null)
377
+ );
337
378
  #rafHandles = /* @__PURE__ */ new Map();
338
- /** The stable list that owns delegated listeners for dynamically added items. */
339
379
  #delegatedList = null;
340
- /** Whether the controller is between `connect()` and `disconnect()`. */
380
+ #listListeners = new ListenerSet();
341
381
  #connected = false;
342
- /**
343
- * The `max` the list was last held to, or `null` before the first time. `connect()`
344
- * applies the limit only when `max` is not that one, so a reconnect of the same instance
345
- * leaves the list as it was unless `max` changed while it was away.
346
- */
382
+ #arrivals = /* @__PURE__ */ new Set();
347
383
  #appliedMax = null;
348
- /**
349
- * Stimulus reports every toast the list already holds before this runs, and each is
350
- * taken on then with its holds, meeting no limit. The limit is applied here once, over
351
- * all of them, unless the list was already held to the current `max`.
352
- */
353
384
  connect() {
354
385
  this.#connected = true;
355
- this.#reapply.activate();
386
+ const current = this.itemTargets;
387
+ for (const item of this.#arrivals) {
388
+ if (!current.includes(item)) this.#arrivals.delete(item);
389
+ }
390
+ this.#reapply.observe(this.element);
356
391
  this.#connectDelegatedEvents();
357
392
  for (const item of this.itemTargets) {
358
393
  if (!this.#dismiss.tracks(item) && item.dataset.state !== "leaving") {
359
394
  this.#startTimer(item);
360
395
  }
361
396
  }
362
- if (!Object.is(this.#appliedMax, this.maxValue)) {
363
- this.#appliedMax = this.maxValue;
397
+ if (!Object.is(this.#appliedMax, this.#safeMax)) {
398
+ this.#appliedMax = this.#safeMax;
364
399
  this.#enforceMax();
365
400
  }
366
401
  }
@@ -370,75 +405,52 @@ var ToastController = class extends Controller {
370
405
  this.#timers.clearAll();
371
406
  this.#dismiss.clearAll();
372
407
  this.#taken.clear();
373
- this.#reapply.cancel();
408
+ this.#reapply.disconnect();
374
409
  this.#spared = [];
375
410
  this.#pointer.dispose();
376
- this.#awaitingPointer.clear();
377
411
  for (const handle of this.#rafHandles.values()) {
378
412
  window.cancelAnimationFrame(handle);
379
413
  }
380
414
  this.#rafHandles.clear();
381
415
  }
382
- /** Rebinds delegated interaction when Turbo replaces the list target in place. */
383
- listTargetConnected(element) {
384
- if (this.#delegatedList !== element) this.#connectDelegatedEvents(element);
416
+ listTargetConnected() {
417
+ if (this.#delegatedList !== this.listTarget) this.#connectDelegatedEvents();
385
418
  }
386
- /** Releases delegation only when the removed target is its current owner. */
387
419
  listTargetDisconnected(element) {
388
420
  if (this.#delegatedList === element) this.#disconnectDelegatedEvents();
389
- }
390
- durationValueChanged() {
421
+ if (!this.#connected || !this.hasListTarget || this.listTarget === element) return;
422
+ if (this.#delegatedList !== this.listTarget) this.#connectDelegatedEvents();
391
423
  for (const item of this.itemTargets) {
392
- if (item.dataset.state === "leaving") continue;
393
- if (this.durationValue <= 0) {
394
- this.#dismiss.disarm(item);
395
- item.removeAttribute("data-paused");
396
- } else {
397
- this.#startTimer(item);
398
- }
424
+ if (this.#outside.has(item)) this.#takeIn(item);
399
425
  }
400
426
  }
401
- /**
402
- * Holds the list to a changed `max`. Stimulus can also call this ahead of `connect()` —
403
- * for an attribute that changed while the controller was away, and on every connect
404
- * for an undeclared `max`, with its default; `connect()` decides then.
405
- */
406
427
  maxValueChanged() {
407
428
  if (!this.#connected) return;
408
- this.#appliedMax = this.maxValue;
429
+ this.#appliedMax = this.#safeMax;
409
430
  this.enforceMaxLimit();
410
431
  }
411
- /**
412
- * Stimulus lifecycle callback triggered automatically when a new item target
413
- * enters the DOM, from a client-side injection or a Turbo Stream append alike, and for
414
- * every toast the list already holds ahead of `connect()`. An item already leaving,
415
- * parented outside `list`, or already taken on (a move within the list) is skipped; any
416
- * other is taken on with the holds it already has. On a connected controller the `max`
417
- * limit is then applied, never to the arrival itself; the toasts reported ahead of
418
- * `connect()` are left to it.
419
- */
420
432
  itemTargetConnected(element) {
421
- if (this.#taken.has(element)) return;
422
- if (element.dataset.state === "leaving" || element.parentNode !== this.listTarget) return;
433
+ if (this.#takeIn(element) && this.#connected) this.#enforceMax([element]);
434
+ }
435
+ #takeIn(element) {
436
+ if (this.#taken.has(element) || element.dataset.state === "leaving") return false;
437
+ if (element.parentNode !== this.listTarget) {
438
+ this.#outside.add(element);
439
+ return false;
440
+ }
423
441
  this.#taken.add(element);
442
+ this.#arrivals.add(element);
424
443
  this.#readHolds(element);
425
444
  this.#startTimer(element);
426
- element.setAttribute("data-state", "entering");
427
- this.#cancelAnimation(element);
445
+ this.#setPhase(element, "entering");
428
446
  const handle = window.requestAnimationFrame(() => {
429
447
  this.#rafHandles.delete(element);
430
448
  if (element.parentNode !== this.listTarget || element.dataset.state === "leaving") return;
431
- element.setAttribute("data-state", "visible");
449
+ this.#setPhase(element, "visible");
432
450
  });
433
451
  this.#rafHandles.set(element, handle);
434
- if (this.#connected) this.#enforceMax([element]);
435
- }
436
- /**
437
- * Lets go of a toast that left the list or stopped being an item: its timer, holds,
438
- * `data-paused` and pending frame. After a move within the list the node is already
439
- * back, still an item, when Stimulus reports it, so it keeps all of those, and only the
440
- * holds the move may have ended are read again.
441
- */
452
+ return true;
453
+ }
442
454
  itemTargetDisconnected(element) {
443
455
  const moved = this.#isShown(element) && element.matches(targetSelector(this.identifier, "item"));
444
456
  if (moved) {
@@ -446,26 +458,12 @@ var ToastController = class extends Controller {
446
458
  return;
447
459
  }
448
460
  this.#taken.delete(element);
461
+ this.#phases.delete(element);
462
+ if (this.#connected) this.#arrivals.delete(element);
449
463
  element.removeAttribute("data-paused");
450
464
  this.#letGo(element);
451
465
  this.#cancelAnimation(element);
452
466
  }
453
- /**
454
- * Shows a new toast. Accepts its content from either a Stimulus action param
455
- * (attribute-only trigger) or a programmatic `show` CustomEvent `detail`
456
- * (remote / Turbo trigger); the action param wins when both are present.
457
- *
458
- * <button data-action="click->stimeo--toast#show"
459
- * data-stimeo--toast-message-param="Saved"
460
- * data-stimeo--toast-type-param="status">Show</button>
461
- *
462
- * element.dispatchEvent(new CustomEvent("show", { detail: { message, type } }))
463
- *
464
- * Clones the template slot, interpolates the message, and appends to the list.
465
- * `message` is the wording key the rest of the library dispatches under, so a
466
- * part that already carries its own wording — `stimeo--clipboard:copy`,
467
- * `stimeo--auto-submit:done` — wires straight into this action.
468
- */
469
467
  show(event) {
470
468
  if (!this.hasTemplateTarget || !this.hasListTarget) return;
471
469
  const message = this.#readField(event, "message");
@@ -484,11 +482,6 @@ var ToastController = class extends Controller {
484
482
  this.listTarget.appendChild(item);
485
483
  this.dispatch("show", { detail: { item } });
486
484
  }
487
- /**
488
- * Reads a string field from a Stimulus action param or a CustomEvent `detail`,
489
- * preferring the action param. Returns null unless a non-empty string is found,
490
- * so untrusted runtime payloads cannot inject non-string values.
491
- */
492
485
  #readField(event, key) {
493
486
  const params = event.params;
494
487
  const fromParams = params?.[key];
@@ -500,153 +493,100 @@ var ToastController = class extends Controller {
500
493
  }
501
494
  return null;
502
495
  }
503
- /** Dismisses the toast that contained the trigger. */
504
- dismiss(event) {
505
- const item = this.#itemFromEvent(event);
496
+ dismiss(input) {
497
+ const source = actionSource(input);
498
+ const item = this.#itemFromSource(source);
506
499
  if (!item) return;
507
- this.#removeWithTransition(item, "user");
500
+ this.#removeWithTransition(item, source.reason);
508
501
  }
509
- /** Dismisses the focused toast when Escape is pressed. */
510
502
  onKeydown(event) {
511
503
  if (event.key === "Escape") {
512
504
  if (event.defaultPrevented || event.isComposing) return;
513
- const item = this.#itemFromEvent(event);
505
+ const item = this.#itemFromSource(actionSource(event));
514
506
  if (!item) return;
515
507
  event.preventDefault();
516
508
  this.#removeWithTransition(item, "user");
517
509
  }
518
510
  }
519
- /**
520
- * Pauses the auto-dismiss timer on mouse entry or keyboard focus.
521
- *
522
- * Hover and focus are tracked as independent reasons: the timer is only
523
- * snapshotted on the first active reason, and only resumed once *every*
524
- * reason has been released (see {@link resume}). This keeps a toast paused
525
- * while it is still hovered *or* focused, per WCAG 2.2.1. The hold is recorded
526
- * whether or not a timer runs, so the `max` limit passes over the toast either
527
- * way; `data-paused` marks a timer that stopped, and only that.
528
- */
529
- pause(event) {
530
- const item = this.#itemFromEvent(event);
531
- if (item && this.#dismiss.pause(item, this.#pauseReason(event))) {
511
+ pause(input) {
512
+ const source = actionSource(input);
513
+ const item = this.#itemFromSource(source);
514
+ if (item && this.#dismiss.pause(item, this.#pauseReason(source.event))) {
532
515
  item.setAttribute("data-paused", "true");
533
516
  }
534
517
  }
535
- /**
536
- * Resumes the auto-dismiss timer once both hover and focus have been released,
537
- * and then applies the `max` limit again, sparing the toast the pointer or focus is
538
- * moving into: its own hold only arrives after this event.
539
- */
540
- resume(event) {
541
- const item = this.#itemFromEvent(event);
518
+ resume(input) {
519
+ const source = actionSource(input);
520
+ const item = this.#itemFromSource(source);
542
521
  if (item) {
543
522
  this.#release(
544
523
  item,
545
- this.#pauseReason(event),
546
- ownerOf(this.itemTargets, event.relatedTarget)
524
+ this.#pauseReason(source.event),
525
+ ownerOf(this.itemTargets, source.event?.relatedTarget)
547
526
  );
548
527
  }
549
528
  }
550
- /** Releases one reason on `item`; releasing the last one applies the limit again. */
551
529
  #release(item, reason, spare) {
552
530
  const held = this.#dismiss.isHeld(item);
553
531
  if (this.#dismiss.resume(item, reason)) item.removeAttribute("data-paused");
554
532
  if (held && !this.#dismiss.isHeld(item)) this.#limitLater(spare);
555
533
  }
556
- /**
557
- * Applies the limit again once the current event is over, sparing `spare`. A hold can
558
- * end in a `focusout` the engine fires while it is itself taking the node out for a
559
- * script's move or removal, and taking the toast away inside that event would pull
560
- * the node from under the caller's own call.
561
- */
562
534
  #limitLater(spare) {
563
535
  if (spare) this.#spared.push(spare);
564
536
  this.#reapply.schedule();
565
537
  }
566
- /** Applies the limit again, sparing every toast gathered since the last pass. */
538
+ #setPhase(item, phase) {
539
+ this.#phases.set(item, phase);
540
+ item.setAttribute("data-state", phase);
541
+ }
567
542
  #reapplyLimit() {
543
+ for (const item of this.itemTargets) {
544
+ if (!this.#taken.has(item) || !this.hasListTarget || item.parentNode !== this.listTarget)
545
+ continue;
546
+ const phase = this.#phases.get(item);
547
+ if (item.dataset.state === "leaving") this.#phases.set(item, "leaving");
548
+ else if (phase) item.setAttribute("data-state", phase);
549
+ if (this.#dismiss.isHeld(item) && this.#dismiss.tracks(item))
550
+ item.setAttribute("data-paused", "true");
551
+ else item.removeAttribute("data-paused");
552
+ }
568
553
  const spared = this.#spared;
569
554
  this.#spared = [];
570
555
  this.#enforceMax(spared);
571
556
  }
572
- /**
573
- * Reads the holds of a toast that moved within the list. Focus is read at once: an
574
- * ordinary move ends it with a `focusout` and `moveBefore` keeps it. Where the pointer
575
- * is, nothing says until it moves again: no `mouseout` reaches a moved node, and
576
- * `:hover` can answer from before the move. A toast still held waits for that movement.
577
- */
578
557
  #afterMove(item) {
579
558
  if (!item.contains(document.activeElement)) this.#release(item, "focus", null);
580
- if (this.#dismiss.isHeld(item)) this.#awaitPointer(item);
559
+ if (this.#dismiss.isHeld(item)) this.#pointer.wait(item);
581
560
  }
582
- /**
583
- * Takes the holds a toast already has as it is taken on: focus inside it, and the pointer
584
- * over it by `:hover`. That answer can date from before the toast came here, so a hover
585
- * hold taken on it waits for the next pointer movement to be confirmed.
586
- */
587
561
  #readHolds(item) {
588
562
  if (item.contains(document.activeElement)) this.#dismiss.pause(item, "focus");
589
563
  if (!item.matches(":hover")) return;
590
564
  this.#dismiss.pause(item, "hover");
591
- this.#awaitPointer(item);
565
+ this.#pointer.wait(item);
592
566
  }
593
- /** Leaves `item`'s hover hold to be confirmed or let go on the next pointer movement. */
594
- #awaitPointer(item) {
595
- this.#awaitingPointer.add(item);
596
- this.#pointer.add(document, "pointermove", this.#onPointerMove, {
597
- capture: true,
598
- passive: true
599
- });
600
- }
601
- /**
602
- * Reads `:hover` once the pointer has moved, when the answer is current, and lets go of
603
- * the hover hold of every waiting toast the pointer is not over. Listens once.
604
- */
605
- #onPointerMove = () => {
606
- this.#pointer.dispose();
607
- const waiting = [...this.#awaitingPointer];
608
- this.#awaitingPointer.clear();
609
- for (const item of waiting) {
610
- if (!item.matches(":hover")) this.#release(item, "hover", null);
611
- }
612
- };
613
- /**
614
- * Drops a toast's timer and holds as it leaves, and stops waiting on the pointer for it.
615
- * A toast something still held counts as released: the limit is applied again.
616
- */
617
567
  #letGo(item) {
618
568
  if (this.#dismiss.isHeld(item)) this.#limitLater(null);
619
569
  this.#dismiss.clear(item);
620
- this.#awaitingPointer.delete(item);
621
- if (this.#awaitingPointer.size === 0) this.#pointer.dispose();
570
+ this.#pointer.cancel(item);
622
571
  }
623
- /** Resolves the toast item element a pause/resume event targets. */
624
- #itemFromEvent(event) {
625
- const target = event.target instanceof Element ? event.target : event.currentTarget;
626
- if (!(target instanceof Element) || !this.hasListTarget) return null;
572
+ #itemFromSource(source) {
573
+ const target = source.origin ?? source.host;
574
+ if (!target || !this.hasListTarget) return null;
575
+ if (target.closest(`[data-controller~="${this.identifier}"]`) !== this.element) return null;
627
576
  const item = target.closest(targetSelector(this.identifier, "item"));
628
- return item && this.listTarget.contains(item) ? item : null;
577
+ return item && this.itemTargets.includes(item) && this.listTarget.contains(item) ? item : null;
629
578
  }
630
- /** Classifies a pause/resume event as a hover or focus reason. */
631
579
  #pauseReason(event) {
632
- return event.type === "focusin" || event.type === "focusout" ? "focus" : "hover";
633
- }
634
- /**
635
- * Arms a toast's auto-dismiss; a non-positive `duration` means it never expires.
636
- * On a toast hover or focus already holds, the timer waits for the release.
637
- * `data-paused` is written from that state, so a mark the toast carried in with it,
638
- * from a restored page, does not outlive it.
639
- *
640
- * @stimeoRuntimeOnly `duration` is the delay of the one dismissal timer this call arms.
641
- */
580
+ return event?.type === "focusin" || event?.type === "focusout" ? "focus" : "hover";
581
+ }
642
582
  #startTimer(element) {
643
583
  if (element.dataset.state === "leaving" || element.parentNode !== this.listTarget) return;
644
- const armed = this.durationValue > 0;
584
+ const armed = this.#safeDuration > 0;
645
585
  if (armed) {
646
586
  this.#dismiss.set(
647
587
  element,
648
588
  () => this.#removeWithTransition(element, "timeout"),
649
- this.durationValue
589
+ this.#safeDuration
650
590
  );
651
591
  }
652
592
  if (armed && this.#dismiss.isHeld(element)) element.setAttribute("data-paused", "true");
@@ -656,7 +596,7 @@ var ToastController = class extends Controller {
656
596
  if (element.dataset.state === "leaving" || element.parentNode !== this.listTarget) return;
657
597
  this.#letGo(element);
658
598
  this.#cancelAnimation(element);
659
- element.setAttribute("data-state", "leaving");
599
+ this.#setPhase(element, "leaving");
660
600
  const finalize = () => {
661
601
  if (element.parentNode !== this.listTarget) return;
662
602
  this.listTarget.removeChild(element);
@@ -669,65 +609,36 @@ var ToastController = class extends Controller {
669
609
  finalize();
670
610
  }
671
611
  }
672
- /**
673
- * Removes the oldest toasts while the list shows more than `maxValue`, passing
674
- * over every toast hover or focus holds.
675
- *
676
- * Public (not `#private`) as a deterministic seam: enforcement normally runs
677
- * as each toast is taken on in `itemTargetConnected`, a Stimulus callback delivered
678
- * through a MutationObserver, which a DOM-only environment does not reliably fire —
679
- * so it can also be invoked directly. It counts only the toasts taken on. It is not a
680
- * user-wired action.
681
- */
682
612
  enforceMaxLimit() {
683
613
  this.#enforceMax();
684
614
  }
685
- /**
686
- * Removes the oldest toasts with reason `limit` while the list shows more than `max`
687
- * (`0` or less, or not a finite number, means no limit), passing over every toast hover
688
- * or focus holds and those in `spared` — the toast just taken on, or the ones the
689
- * pointer or focus moved into. Only toasts taken on count or go, so one Stimulus has
690
- * yet to report is neither; a toast already leaving does not count. With nothing else
691
- * left to go the list stays over the limit. The count is read again on every step,
692
- * because a `dismiss` listener may already have changed it.
693
- *
694
- * @stimeoRenderRoot
695
- */
696
615
  #enforceMax(spared = []) {
697
- if (!Number.isFinite(this.maxValue) || this.maxValue <= 0) return;
698
- for (const item of this.itemTargets) {
699
- if (this.#shownCount() <= this.maxValue) return;
616
+ if (this.#safeMax <= 0) return;
617
+ for (const item of [...this.#arrivals]) {
618
+ if (this.#shownCount() <= this.#safeMax) return;
700
619
  if (this.#isShown(item) && !spared.includes(item) && !this.#dismiss.isHeld(item)) {
701
620
  this.#removeWithTransition(item, "limit");
702
621
  }
703
622
  }
704
623
  }
705
- /** How many toasts the list shows: the ones taken on that are still shown. */
706
624
  #shownCount() {
707
625
  return this.itemTargets.filter((item) => this.#isShown(item)).length;
708
626
  }
709
- /** Whether the list shows `item`: a toast taken on, a child of `list`, not leaving. */
710
627
  #isShown(item) {
711
628
  return this.#taken.has(item) && this.hasListTarget && item.parentNode === this.listTarget && item.dataset.state !== "leaving";
712
629
  }
713
- /** Wires stable-container delegation so newly appended items work immediately. */
714
630
  #connectDelegatedEvents(list = this.hasListTarget ? this.listTarget : null) {
715
631
  this.#disconnectDelegatedEvents();
716
632
  if (!list) return;
717
633
  this.#delegatedList = list;
718
634
  for (const type of DELEGATED_EVENTS) {
719
- this.#delegatedList.addEventListener(type, this.#onListEvent);
635
+ this.#listListeners.add(list, type, this.#onListEvent);
720
636
  }
721
637
  }
722
- /** Releases every delegated listener from the exact list that owns it. */
723
638
  #disconnectDelegatedEvents() {
724
- if (!this.#delegatedList) return;
725
- for (const type of DELEGATED_EVENTS) {
726
- this.#delegatedList.removeEventListener(type, this.#onListEvent);
727
- }
639
+ this.#listListeners.dispose();
728
640
  this.#delegatedList = null;
729
641
  }
730
- /** Routes every delegated item interaction from the stable list. */
731
642
  #onListEvent = (event) => {
732
643
  if (event.type === "click") {
733
644
  const target = event.target instanceof Element ? event.target : null;
@@ -740,22 +651,31 @@ var ToastController = class extends Controller {
740
651
  else this.resume(event);
741
652
  }
742
653
  };
743
- /** Whether a bubbling focus/pointer event enters or leaves a toast boundary. */
744
654
  #crossesItemBoundary(event) {
745
- const item = this.#itemFromEvent(event);
655
+ const item = this.#itemFromSource(actionSource(event));
746
656
  if (!item) return false;
747
657
  const related = "relatedTarget" in event ? event.relatedTarget : null;
748
658
  return !(related instanceof Node && item.contains(related));
749
659
  }
750
- /** Cancels the pending entering-to-visible frame owned by one item. */
751
660
  #cancelAnimation(element) {
752
661
  const handle = this.#rafHandles.get(element);
753
662
  if (handle === void 0) return;
754
663
  window.cancelAnimationFrame(handle);
755
664
  this.#rafHandles.delete(element);
756
665
  }
666
+ get #safeDuration() {
667
+ return this.#numbers.read(
668
+ this,
669
+ "duration",
670
+ this.durationValue,
671
+ _ToastController.values.duration.default,
672
+ _ToastController.valueConstraints.duration
673
+ );
674
+ }
675
+ get #safeMax() {
676
+ return this.#numbers.read(this, "max", this.maxValue, 0, _ToastController.valueConstraints.max);
677
+ }
757
678
  };
758
679
 
759
680
  export { ToastController };
760
- //# sourceMappingURL=toast_controller.js.map
761
681
  //# sourceMappingURL=toast_controller.js.map