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,5 @@
1
1
  import { Controller } from '@hotwired/stimulus';
2
2
 
3
- // src/controllers/auto_submit_controller.ts
4
-
5
- // src/utils/announce.ts
6
3
  function announce(message, options = {}) {
7
4
  const text = message.trim();
8
5
  if (text.length === 0) return;
@@ -13,175 +10,456 @@ function announce(message, options = {}) {
13
10
  );
14
11
  }
15
12
 
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);
13
+ function setAttributeIfChanged(element, name, value, beforeWrite) {
14
+ if (element.getAttribute(name) === value) return false;
15
+ if (value === null) element.removeAttribute(name);
16
+ else element.setAttribute(name, value);
17
+ return true;
18
+ }
19
+
20
+ function sharedRegistry(key, create) {
21
+ const symbol = Symbol.for(key);
22
+ const scope = globalThis;
23
+ const existing = scope[symbol];
24
+ if (existing !== void 0) return existing;
25
+ const registry2 = create();
26
+ Object.defineProperty(globalThis, symbol, { value: registry2 });
27
+ return registry2;
28
+ }
29
+
30
+ var registry = sharedRegistry(
31
+ "stimeo-ui.attribute-lease.registry.v1",
32
+ () => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
33
+ );
34
+ function isAttributeValue(value) {
35
+ return value === null || typeof value === "string";
36
+ }
37
+ function readLeaseRecord(element, record) {
38
+ try {
39
+ const parsed = JSON.parse(element.getAttribute(record) ?? "null");
40
+ if (!Array.isArray(parsed)) return void 0;
41
+ const [original, written, ...rest] = parsed;
42
+ const linked = rest[0] !== false;
43
+ const beneath = linked ? rest : rest.slice(1);
44
+ if (!beneath.every((under) => typeof under === "string")) return void 0;
45
+ return isAttributeValue(original) && isAttributeValue(written) ? { original, written, linked, beneath } : void 0;
46
+ } catch {
47
+ return void 0;
48
+ }
49
+ }
50
+ function attributeStack(element, attribute) {
51
+ const byAttribute = registry.stacks.get(element) ?? /* @__PURE__ */ new Map();
52
+ registry.stacks.set(element, byAttribute);
53
+ const known = byAttribute.get(attribute);
54
+ if (known) return known;
55
+ const found = [];
56
+ for (const record of registry.names.get(attribute) ?? []) {
57
+ const read = readLeaseRecord(element, record);
58
+ if (read) found.push({ ...read, record });
59
+ }
60
+ const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
61
+ const stack = found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
62
+ lease: null,
63
+ record,
64
+ written,
65
+ original,
66
+ linked: linked && index > 0
67
+ }));
68
+ byAttribute.set(attribute, stack);
69
+ return stack;
70
+ }
71
+ function findHold(stack, lease, record) {
72
+ return stack.find((held) => held.lease === lease) ?? stack.find((held) => held.lease === null && held.record === record);
73
+ }
74
+ var AttributeLease = class {
75
+ #attribute;
76
+ #record;
77
+ #base;
78
+ #preserveAuthor;
79
+ #held = /* @__PURE__ */ new Set();
80
+ constructor(attribute, owner, options = {}) {
81
+ this.#attribute = attribute;
82
+ this.#record = `data-${owner}-${attribute}-lease`;
83
+ this.#base = options.base === true;
84
+ this.#preserveAuthor = options.preserveAuthor === true;
85
+ const names = registry.names;
86
+ names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
87
+ }
88
+ write(element, value) {
89
+ const stack = attributeStack(element, this.#attribute);
90
+ const hold = this.#take(stack) ?? this.#acquire(element, stack);
91
+ this.#held.add(element);
92
+ const shows = !this.#covered(element, stack, hold);
93
+ hold.written = value;
94
+ this.#mark(element, stack);
95
+ if (shows) this.#reflect(element, value);
96
+ }
97
+ return(element) {
98
+ const held = this.#held.delete(element);
99
+ const known = registry.stacks.get(element)?.has(this.#attribute);
100
+ const stack = held || known || element.hasAttribute(this.#record) ? attributeStack(element, this.#attribute) : [];
101
+ const hold = this.#take(stack);
102
+ if (!hold) return false;
103
+ const carries = element.getAttribute(this.#attribute) === hold.written;
104
+ const owns = carries && !this.#covered(element, stack, hold);
105
+ const index = stack.indexOf(hold);
106
+ stack.splice(index, 1);
107
+ const above = stack[index];
108
+ if (above) above.linked &&= hold.linked;
109
+ this.#mark(element, stack);
110
+ if (!owns) return true;
111
+ const below = hold.linked ? stack[index - 1] : void 0;
112
+ this.#reflect(element, below ? below.written : hold.original);
113
+ return true;
114
+ }
115
+ returnAll() {
116
+ for (const element of Array.from(this.#held)) this.return(element);
117
+ }
118
+ #take(stack) {
119
+ const hold = findHold(stack, this, this.#record);
120
+ if (hold) hold.lease = this;
121
+ return hold;
122
+ }
123
+ #acquire(element, stack) {
124
+ const current = element.getAttribute(this.#attribute);
125
+ const top = stack.at(-1);
126
+ const bottom = stack[0];
127
+ const over = top?.written === current ? top : void 0;
128
+ const from = this.#base ? this.#preserveAuthor && !stack.some((held) => held.written === current) ? void 0 : bottom : over;
129
+ const hold = {
130
+ lease: this,
131
+ record: this.#record,
132
+ written: current,
133
+ original: from ? from.original : current,
134
+ linked: !this.#base && over !== void 0
135
+ };
136
+ if (this.#base && bottom) {
137
+ bottom.linked = true;
138
+ stack.unshift(hold);
139
+ } else stack.push(hold);
140
+ return hold;
141
+ }
142
+ #covered(element, stack, hold) {
143
+ const current = element.getAttribute(this.#attribute);
144
+ return stack.slice(stack.indexOf(hold) + 1).some((above) => above.written === current);
145
+ }
146
+ #mark(element, stack) {
147
+ const records = /* @__PURE__ */ new Map([[this.#record, null]]);
148
+ stack.forEach((hold, index) => {
149
+ const names = stack.slice(0, index).map((under) => under.record);
150
+ const beneath = [...new Set(names.reverse())].filter((name) => name !== hold.record);
151
+ const marker = hold.linked || beneath.length === 0 ? [] : [false];
152
+ records.set(
153
+ hold.record,
154
+ hold.written === hold.original && beneath.length === 0 && index === stack.length - 1 ? null : JSON.stringify([hold.original, hold.written, ...marker, ...beneath])
155
+ );
156
+ });
157
+ for (const [name, recorded] of records) {
158
+ setAttributeIfChanged(element, name, recorded);
35
159
  }
36
160
  }
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);
161
+ #reflect(element, value) {
162
+ setAttributeIfChanged(element, this.#attribute, value);
42
163
  }
43
164
  };
44
165
 
45
- // src/utils/composition_tracker.ts
46
166
  var COMPOSITION_INPUT_TYPES = /* @__PURE__ */ new Set([
47
167
  "insertCompositionText",
48
168
  "insertFromComposition",
49
169
  "deleteCompositionText",
50
170
  "deleteByComposition"
51
171
  ]);
172
+ function isCompositionInputType(inputType) {
173
+ return inputType !== void 0 && COMPOSITION_INPUT_TYPES.has(inputType);
174
+ }
175
+ var BEFORE_MORPH_ATTRIBUTE = "turbo:before-morph-attribute";
176
+ var textOf = (element) => element.value;
177
+ var holdsFocus = (element) => element instanceof Node && element.getRootNode().activeElement === element;
52
178
  var CompositionTracker = class {
53
179
  #observedTargets = /* @__PURE__ */ new Set();
54
- #activeTargets = /* @__PURE__ */ new Set();
180
+ #composition = null;
55
181
  #onStart;
56
182
  #onEnd;
57
- /** The field whose confirming `input` is still owed, while the window is open. */
183
+ #onAbandon;
58
184
  #confirmedTarget = null;
185
+ #keyComposing = false;
186
+ #connected = false;
187
+ #abandoned = null;
59
188
  constructor(options = {}) {
60
189
  this.#onStart = options.onStart;
61
190
  this.#onEnd = options.onEnd;
191
+ this.#onAbandon = options.onAbandon;
62
192
  }
63
- /** Starts lifecycle tracking for `target`; repeated calls are idempotent. */
64
193
  observe(target) {
194
+ this.#connected = true;
65
195
  if (this.#observedTargets.has(target)) return;
66
196
  target.addEventListener("compositionstart", this.#handleStart);
67
197
  target.addEventListener("compositionend", this.#handleEnd);
68
- target.addEventListener("keydown", this.#handleKeydown);
198
+ target.addEventListener("keydown", this.#handleKeydown, true);
199
+ target.addEventListener("beforeinput", this.#handleBeforeInput, true);
200
+ target.addEventListener("input", this.#handleInput, true);
201
+ target.addEventListener("blur", this.#handleBlur, true);
202
+ target.addEventListener(BEFORE_MORPH_ATTRIBUTE, this.#handleBeforeMorphAttribute);
69
203
  this.#observedTargets.add(target);
70
204
  }
71
- /** Stops tracking one target and clears any active composition it owned. */
72
205
  unobserve(target) {
73
206
  if (!this.#observedTargets.delete(target)) return;
74
- target.removeEventListener("compositionstart", this.#handleStart);
75
- target.removeEventListener("compositionend", this.#handleEnd);
76
- target.removeEventListener("keydown", this.#handleKeydown);
77
- this.#activeTargets.delete(target);
207
+ this.#release(target);
208
+ if (this.#composition?.target === target) this.#letGo();
78
209
  if (this.#confirmedTarget === target) this.#confirmedTarget = null;
79
210
  }
80
- /** Releases every listener and clears state so reconnect starts cleanly. */
81
211
  disconnect() {
82
- for (const target of this.#observedTargets) {
83
- target.removeEventListener("compositionstart", this.#handleStart);
84
- target.removeEventListener("compositionend", this.#handleEnd);
85
- target.removeEventListener("keydown", this.#handleKeydown);
86
- }
212
+ this.#connected = false;
213
+ for (const target of this.#observedTargets) this.#release(target);
87
214
  this.#observedTargets.clear();
88
- this.#activeTargets.clear();
215
+ this.#letGo();
89
216
  this.#confirmedTarget = null;
217
+ this.#keyComposing = false;
218
+ this.#abandoned = null;
90
219
  }
91
- /** True when lifecycle tracking or the current event reports composition. */
92
220
  isComposing(event) {
93
- return this.#activeTargets.size > 0 || event?.isComposing === true;
94
- }
95
- /**
96
- * Whether `event` is the `input` echoing the composition just confirmed.
97
- *
98
- * Asking closes the window either way, so one confirmation is folded at most
99
- * once and a consumer asks once per `input`.
100
- */
221
+ this.#settle();
222
+ return event?.isComposing === true || this.#composition !== null;
223
+ }
224
+ composingElement() {
225
+ this.#settle();
226
+ return this.#composition?.element ?? null;
227
+ }
228
+ owns(event) {
229
+ const element = this.composingElement();
230
+ return event.isComposing === true || element !== null && element === event.target;
231
+ }
101
232
  consumesConfirmedInput(event) {
102
233
  const confirmed = this.#confirmedTarget;
103
234
  this.#confirmedTarget = null;
104
235
  if (confirmed === null || confirmed !== event.target) return false;
105
236
  const inputType = event.inputType;
106
- return !inputType || COMPOSITION_INPUT_TYPES.has(inputType);
237
+ return !inputType || isCompositionInputType(inputType);
238
+ }
239
+ #release(target) {
240
+ target.removeEventListener("compositionstart", this.#handleStart);
241
+ target.removeEventListener("compositionend", this.#handleEnd);
242
+ target.removeEventListener("keydown", this.#handleKeydown, true);
243
+ target.removeEventListener("beforeinput", this.#handleBeforeInput, true);
244
+ target.removeEventListener("input", this.#handleInput, true);
245
+ target.removeEventListener("blur", this.#handleBlur, true);
246
+ target.removeEventListener(BEFORE_MORPH_ATTRIBUTE, this.#handleBeforeMorphAttribute);
107
247
  }
248
+ #letGo() {
249
+ const composition = this.#composition;
250
+ if (composition === null) return;
251
+ if (!holdsFocus(composition.element)) {
252
+ this.#composition = null;
253
+ return;
254
+ }
255
+ queueMicrotask(() => {
256
+ if (this.#composition !== composition || this.#observedTargets.has(composition.target)) {
257
+ return;
258
+ }
259
+ this.#composition = null;
260
+ if (!this.#connected) return;
261
+ this.#abandoned = { element: composition.element, inPlace: true };
262
+ this.#runAbandon();
263
+ });
264
+ }
265
+ #settle() {
266
+ const composition = this.#composition;
267
+ if (composition === null) return;
268
+ const { target, element, text } = composition;
269
+ if (target instanceof Node && element instanceof Node && !target.contains(element)) {
270
+ this.#composition = null;
271
+ } else if (textOf(element) !== text) {
272
+ this.#abandon(false);
273
+ }
274
+ }
275
+ #abandon(inPlace) {
276
+ this.#owe(inPlace);
277
+ this.#composition = null;
278
+ queueMicrotask(this.#runAbandon);
279
+ }
280
+ #owe(inPlace) {
281
+ const composition = this.#composition;
282
+ if (composition !== null) this.#abandoned = { element: composition.element, inPlace };
283
+ }
284
+ #runAbandon = () => {
285
+ const abandoned = this.#abandoned;
286
+ if (abandoned === null) return;
287
+ this.#abandoned = null;
288
+ this.#onAbandon?.(abandoned.element, abandoned.inPlace);
289
+ };
108
290
  #handleStart = (event) => {
109
291
  this.#confirmedTarget = null;
110
- if (event.currentTarget) this.#activeTargets.add(event.currentTarget);
292
+ this.#settle();
293
+ this.#owe(true);
294
+ const target = event.currentTarget;
295
+ const element = event.target;
296
+ this.#composition = { target, element, text: textOf(element) };
297
+ this.#runAbandon();
111
298
  this.#onStart?.(event);
112
299
  };
113
300
  #handleEnd = (event) => {
114
- if (event.currentTarget) this.#activeTargets.delete(event.currentTarget);
301
+ this.#runAbandon();
302
+ if (this.#composition?.target === event.currentTarget) this.#composition = null;
115
303
  this.#confirmedTarget = event.target;
116
304
  this.#onEnd?.(event);
117
305
  };
118
- /** A key on an observed field opens an edit of its own, so no echo is owed. */
119
- #handleKeydown = () => {
306
+ #handleKeydown = (event) => {
307
+ this.#settle();
308
+ this.#runAbandon();
120
309
  this.#confirmedTarget = null;
310
+ this.#keyComposing = event.isComposing === true;
311
+ };
312
+ #handleBeforeInput = (event) => {
313
+ const keyComposing = this.#keyComposing;
314
+ this.#keyComposing = false;
315
+ this.#settle();
316
+ const { isComposing, inputType } = event;
317
+ if (this.#composition !== null && !keyComposing && !isComposing && inputType && !isCompositionInputType(inputType)) {
318
+ this.#abandon(true);
319
+ }
320
+ this.#runAbandon();
321
+ };
322
+ #handleInput = (event) => {
323
+ const composition = this.#composition;
324
+ const { isTrusted, isComposing, inputType } = event;
325
+ if (composition !== null && event.target === composition.element && (isTrusted || isComposing || isCompositionInputType(inputType))) {
326
+ composition.text = textOf(composition.element);
327
+ } else {
328
+ this.#settle();
329
+ }
330
+ this.#runAbandon();
331
+ };
332
+ #handleBlur = (event) => {
333
+ this.#settle();
334
+ this.#runAbandon();
335
+ const composition = this.#composition;
336
+ if (composition === null || composition.target === composition.element) return;
337
+ const { element } = composition;
338
+ if (!(element instanceof Node) || event.target !== element) return;
339
+ const root = element.getRootNode();
340
+ if (root.activeElement !== element) this.#composition = null;
341
+ };
342
+ #handleBeforeMorphAttribute = (event) => {
343
+ this.#settle();
344
+ const composition = this.#composition;
345
+ if (composition?.target !== event.currentTarget || event.target !== composition.element) return;
346
+ const detail = event.detail;
347
+ if (detail?.attributeName === "value") event.preventDefault();
121
348
  };
122
349
  };
123
350
 
124
- // src/utils/listener_set.ts
351
+ var DetachGate = class _DetachGate {
352
+ #pending = false;
353
+ get pending() {
354
+ return this.#pending;
355
+ }
356
+ static isDetached(host) {
357
+ return !host.element.isConnected || !_DetachGate.#listed(host);
358
+ }
359
+ disconnected(host, teardown) {
360
+ const carried = _DetachGate.#carried(host);
361
+ if (!carried && _DetachGate.isDetached(host)) {
362
+ this.#pending = false;
363
+ teardown();
364
+ return;
365
+ }
366
+ this.#pending = true;
367
+ const probe = () => {
368
+ if (!this.#pending) return;
369
+ this.#pending = false;
370
+ teardown();
371
+ };
372
+ if (carried) setTimeout(probe, 0);
373
+ else queueMicrotask(probe);
374
+ }
375
+ static #carried(host) {
376
+ const element = host.element;
377
+ return !element.isConnected && element.closest("[data-turbo-permanent]") !== null && _DetachGate.#listed(host);
378
+ }
379
+ static #listed(host) {
380
+ const tokens = (host.element.getAttribute("data-controller") ?? "").split(/\s+/);
381
+ return tokens.includes(host.identifier);
382
+ }
383
+ cancel() {
384
+ this.#pending = false;
385
+ }
386
+ };
387
+
125
388
  var ListenerSet = class {
126
- /** The generation every `add` joins until the next `dispose()`. */
127
389
  #abort = new AbortController();
128
- /**
129
- * Attaches `handler` to the open generation, exactly as the caller spelled it.
130
- *
131
- * The set supplies the signal, so `options` carries everything else the DOM
132
- * accepts — `capture` included, which has to match at release time and no
133
- * longer has a second place to drift from.
134
- */
135
390
  add(target, type, handler, options) {
136
391
  target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
137
392
  }
138
- /**
139
- * Releases every listener of the open generation, synchronously, and opens the
140
- * next one. Idempotent, and safe before anything has been added.
141
- */
142
393
  dispose() {
143
394
  this.#abort.abort();
144
395
  this.#abort = new AbortController();
145
396
  }
146
397
  };
147
398
 
148
- // src/utils/safe_timeout.ts
399
+ var MAX_TIMER_DELAY_MS = 2147483647;
400
+
401
+ var NUMBER_BOUNDS = {
402
+ timer: { finite: true, min: 0, max: MAX_TIMER_DELAY_MS }};
403
+ function matchesNumberBounds(value, bounds) {
404
+ if (!Number.isFinite(value)) {
405
+ const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
406
+ if (direction === null) return false;
407
+ if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
408
+ }
409
+ if (bounds.min !== void 0 && value < bounds.min) return false;
410
+ if (bounds.max !== void 0 && value > bounds.max) return false;
411
+ if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
412
+ if (bounds.integer && !Number.isInteger(value)) return false;
413
+ if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
414
+ return true;
415
+ }
416
+
417
+ function readNumber(raw, fallback, bounds) {
418
+ return matchesNumberBounds(raw, bounds) ? raw : fallback;
419
+ }
420
+
421
+ var NumberValueReader = class {
422
+ #lastRejected = /* @__PURE__ */ new Map();
423
+ read(owner, name, raw, fallback, bounds) {
424
+ const resolved = readNumber(raw, fallback, bounds);
425
+ if (matchesNumberBounds(raw, bounds)) {
426
+ this.#lastRejected.delete(name);
427
+ return resolved;
428
+ }
429
+ const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
430
+ const literal = owner.element.getAttribute(attribute);
431
+ if (literal === null) {
432
+ this.#lastRejected.delete(name);
433
+ return resolved;
434
+ }
435
+ if (this.#lastRejected.get(name) !== literal) {
436
+ this.#lastRejected.set(name, literal);
437
+ console.warn(
438
+ `Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
439
+ );
440
+ }
441
+ return resolved;
442
+ }
443
+ };
444
+
149
445
  var TimerRegistry = class {
150
- /** Live timer ids that have not yet been cleared (or, for timeouts, fired). */
151
446
  ids = /* @__PURE__ */ new Set();
152
- /**
153
- * Cancels a single tracked timer.
154
- *
155
- * No-ops if the id is unknown (already cleared, fired, or never owned by this
156
- * registry), so callers can clear defensively without guarding.
157
- */
158
447
  clear(id) {
159
448
  if (this.ids.delete(id)) {
160
449
  this.cancel(id);
161
450
  }
162
451
  }
163
- /**
164
- * Cancels every tracked timer. Call this from a controller's `disconnect()`
165
- * to guarantee no timer outlives the element.
166
- */
167
452
  clearAll() {
168
453
  for (const id of this.ids) {
169
454
  this.cancel(id);
170
455
  }
171
456
  this.ids.clear();
172
457
  }
173
- /** Number of timers currently tracked (pending). */
174
458
  get size() {
175
459
  return this.ids.size;
176
460
  }
177
461
  };
178
462
  var SafeTimeout = class extends TimerRegistry {
179
- /**
180
- * Schedules `callback` after `delay` ms and returns the timer id.
181
- *
182
- * The id is removed from the registry automatically when the timeout fires,
183
- * so {@link TimerRegistry.size | size} reflects only still-pending timers.
184
- */
185
463
  set(callback, delay) {
186
464
  const id = this.schedule(() => {
187
465
  this.ids.delete(id);
@@ -198,8 +476,19 @@ var SafeTimeout = class extends TimerRegistry {
198
476
  }
199
477
  };
200
478
 
201
- // src/controllers/auto_submit_controller.ts
202
- var AutoSubmitController = class extends Controller {
479
+ var TransientHooks = class {
480
+ #attributes;
481
+ constructor(options) {
482
+ this.#attributes = options.attributes;
483
+ }
484
+ reset(element) {
485
+ for (const attribute of this.#attributes) element.removeAttribute(attribute);
486
+ }
487
+ };
488
+
489
+ var PENDING = new TransientHooks({ attributes: ["data-auto-submit-pending"] });
490
+ var AutoSubmitController = class _AutoSubmitController extends Controller {
491
+ #numbers = new NumberValueReader();
203
492
  static targets = ["form"];
204
493
  static values = {
205
494
  debounce: { type: Number, default: 300 },
@@ -207,70 +496,60 @@ var AutoSubmitController = class extends Controller {
207
496
  announce: { type: Boolean, default: false },
208
497
  message: { type: String, default: "" }
209
498
  };
499
+ static valueConstraints = {
500
+ debounce: NUMBER_BOUNDS.timer
501
+ };
210
502
  static actions = ["submit"];
211
503
  static events = ["submit", "done", "reconcile"];
212
- /** Debounce timer registry; one `clearAll()` in disconnect tears it down. */
213
504
  #timers = new SafeTimeout();
214
- /** Id of the pending debounce timer, so a new keystroke can reset it. */
215
505
  #pendingId = 0;
216
- /** Listeners that live with the bound form rather than with the connection. */
217
506
  #formListeners = new ListenerSet();
218
- /** The form the listeners are attached to; target callbacks rebind it. */
219
507
  #boundForm = null;
220
- /** Rewinds the transient state hooks just before Turbo snapshots the page. */
221
- #beforeCache = new BeforeCacheReset(() => this.#rewindForCache());
222
- /**
223
- * Clears `aria-busy` and emits completion once Turbo finishes the submit.
224
- *
225
- * @stimeoRuntimeOnly `message` and `announce` shape the event and the announcement of one
226
- * finished submit; the busy flag it clears does not depend on them.
227
- */
508
+ #busy = new AttributeLease("aria-busy", this.identifier);
509
+ #gate = new DetachGate();
228
510
  #onSubmitEnd = () => {
229
- this.#boundForm?.removeAttribute("aria-busy");
511
+ if (this.#boundForm) this.#busy.return(this.#boundForm);
230
512
  const message = this.messageValue;
231
513
  this.dispatch("done", { detail: { message: message || void 0 } });
232
514
  if (this.announceValue) announce(message);
233
515
  };
234
- /** Owns delegated IME lifecycle state and submits confirmed input text. */
235
516
  #composition = new CompositionTracker({
236
517
  onEnd: (event) => {
237
518
  if (this.#triggers("input")) this.#schedule(event.target ?? null);
238
519
  }
239
520
  });
240
521
  connect() {
241
- this.#beforeCache.activate();
242
- this.#bindForm(this.#resolveForm());
522
+ const moved = this.#gate.pending;
523
+ this.#gate.cancel();
524
+ const form = this.#resolveForm();
525
+ if (moved && form && form === this.#boundForm) {
526
+ this.#composition.observe(form);
527
+ return;
528
+ }
529
+ const stale = form?.hasAttribute("data-auto-submit-pending") === true;
530
+ if (form) PENDING.reset(form);
531
+ const busy = form !== null && this.#busy.return(form);
532
+ this.#bindForm(form);
533
+ if (stale || busy) this.dispatch("reconcile", { detail: {} });
243
534
  }
244
535
  disconnect() {
245
- this.#beforeCache.deactivate();
246
- this.#bindForm(null);
247
536
  this.#composition.disconnect();
248
- this.#timers.clearAll();
249
- this.#pendingId = 0;
537
+ this.#gate.disconnected(this, () => {
538
+ this.#bindForm(null);
539
+ this.#pendingId = 0;
540
+ });
250
541
  }
251
- /** Follows a `form` target added (or swapped in) at runtime. */
252
542
  formTargetConnected() {
253
543
  this.#bindForm(this.#resolveForm());
254
544
  }
255
- /** Follows a `form` target removed (or swapped out) at runtime. */
256
545
  formTargetDisconnected() {
257
546
  this.#bindForm(this.#resolveForm());
258
547
  }
259
- /**
260
- * Schedules a debounced submit. Wired to `input`/`change`; the `on` value is an
261
- * allowlist so a configured subset (e.g. only `change`) is honored even when both
262
- * are bound in markup. Coalesces rapid events into a single `requestSubmit`.
263
- */
264
548
  submit(event) {
265
549
  if (!this.#triggers(event.type)) return;
266
550
  if (event.type === "input" && this.#composition.isComposing(event)) return;
267
551
  this.#schedule(event.target ?? null);
268
552
  }
269
- /**
270
- * Schedules (and coalesces) the debounced submit for the given trigger.
271
- *
272
- * @stimeoRuntimeOnly `debounce` is the delay of the one submit timer this call arms.
273
- */
274
553
  #schedule(trigger) {
275
554
  const form = this.#boundForm;
276
555
  if (!form) return;
@@ -281,23 +560,15 @@ var AutoSubmitController = class extends Controller {
281
560
  form.removeAttribute("data-auto-submit-pending");
282
561
  this.dispatch("submit", { detail: { trigger } });
283
562
  if (form.checkValidity()) {
284
- form.setAttribute("aria-busy", "true");
563
+ this.#busy.write(form, "true");
285
564
  }
286
565
  form.requestSubmit();
287
- }, this.debounceValue);
566
+ }, this.#safeDebounce);
288
567
  }
289
- /** Cancels the pending debounced submit, if any (`clear` no-ops on unknown ids). */
290
568
  #cancelPending() {
291
569
  this.#timers.clear(this.#pendingId);
292
570
  this.#pendingId = 0;
293
571
  }
294
- /**
295
- * Replaces the subscribed form symmetrically. The outgoing form loses the
296
- * `turbo:submit-end`/composition listeners, its pending hook, and any pending
297
- * debounced submit (which described the outgoing form). An in-flight `aria-busy`
298
- * is left for `turbo:submit-end` or the pre-cache rewind — removing it here
299
- * would wipe a legitimately in-progress submission.
300
- */
301
572
  #bindForm(form) {
302
573
  if (form === this.#boundForm) return;
303
574
  const previous = this.#boundForm;
@@ -312,25 +583,23 @@ var AutoSubmitController = class extends Controller {
312
583
  this.#formListeners.add(form, "turbo:submit-end", this.#onSubmitEnd);
313
584
  this.#composition.observe(form);
314
585
  }
315
- /** Resolves the form: the explicit `form` target, else a `<form>` root, else null. */
316
586
  #resolveForm() {
317
587
  if (this.hasFormTarget) return this.formTarget;
318
588
  return this.element instanceof HTMLFormElement ? this.element : null;
319
589
  }
320
- /** Returns the transient state hooks to their initial (absent) state. */
321
- #rewindForCache() {
322
- const inProgress = this.#pendingId !== 0 || this.#boundForm?.hasAttribute("aria-busy") === true;
323
- this.#cancelPending();
324
- this.#boundForm?.removeAttribute("data-auto-submit-pending");
325
- this.#boundForm?.removeAttribute("aria-busy");
326
- if (inProgress) this.dispatch("reconcile", { detail: {} });
327
- }
328
- /** Whether `type` is one of the whitespace-separated event types in `on`. */
329
590
  #triggers(type) {
330
591
  return this.onValue.split(/\s+/).filter(Boolean).includes(type);
331
592
  }
593
+ get #safeDebounce() {
594
+ return this.#numbers.read(
595
+ this,
596
+ "debounce",
597
+ this.debounceValue,
598
+ _AutoSubmitController.values.debounce.default,
599
+ _AutoSubmitController.valueConstraints.debounce
600
+ );
601
+ }
332
602
  };
333
603
 
334
604
  export { AutoSubmitController };
335
- //# sourceMappingURL=auto_submit_controller.js.map
336
605
  //# sourceMappingURL=auto_submit_controller.js.map