stimeo-ui 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (328) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +8 -1
  3. package/dist/cable/index.d.ts +79 -23
  4. package/dist/cable/index.js +363 -436
  5. package/dist/cable/index.js.map +1 -1
  6. package/dist/controllers/accordion_controller.d.ts +4 -2
  7. package/dist/controllers/accordion_controller.js +166 -55
  8. package/dist/controllers/accordion_controller.js.map +1 -1
  9. package/dist/controllers/alert_dialog_controller.d.ts +12 -2
  10. package/dist/controllers/alert_dialog_controller.js +862 -221
  11. package/dist/controllers/alert_dialog_controller.js.map +1 -1
  12. package/dist/controllers/announcer_controller.d.ts +35 -5
  13. package/dist/controllers/announcer_controller.js +135 -198
  14. package/dist/controllers/announcer_controller.js.map +1 -1
  15. package/dist/controllers/aspect_ratio_controller.js +0 -10
  16. package/dist/controllers/aspect_ratio_controller.js.map +1 -1
  17. package/dist/controllers/auto_submit_controller.d.ts +30 -6
  18. package/dist/controllers/auto_submit_controller.js +271 -127
  19. package/dist/controllers/auto_submit_controller.js.map +1 -1
  20. package/dist/controllers/avatar_controller.js +171 -105
  21. package/dist/controllers/avatar_controller.js.map +1 -1
  22. package/dist/controllers/breadcrumb_controller.js +0 -130
  23. package/dist/controllers/breadcrumb_controller.js.map +1 -1
  24. package/dist/controllers/bulk_select_controller.d.ts +18 -0
  25. package/dist/controllers/bulk_select_controller.js +262 -52
  26. package/dist/controllers/bulk_select_controller.js.map +1 -1
  27. package/dist/controllers/calendar_controller.d.ts +24 -5
  28. package/dist/controllers/calendar_controller.js +175 -209
  29. package/dist/controllers/calendar_controller.js.map +1 -1
  30. package/dist/controllers/carousel_controller.d.ts +34 -7
  31. package/dist/controllers/carousel_controller.js +461 -399
  32. package/dist/controllers/carousel_controller.js.map +1 -1
  33. package/dist/controllers/character_counter_controller.d.ts +17 -1
  34. package/dist/controllers/character_counter_controller.js +246 -156
  35. package/dist/controllers/character_counter_controller.js.map +1 -1
  36. package/dist/controllers/checkbox_controller.d.ts +7 -2
  37. package/dist/controllers/checkbox_controller.js +32 -74
  38. package/dist/controllers/checkbox_controller.js.map +1 -1
  39. package/dist/controllers/clipboard_controller.d.ts +16 -5
  40. package/dist/controllers/clipboard_controller.js +168 -147
  41. package/dist/controllers/clipboard_controller.js.map +1 -1
  42. package/dist/controllers/collapsible_controller.d.ts +16 -4
  43. package/dist/controllers/collapsible_controller.js +301 -120
  44. package/dist/controllers/collapsible_controller.js.map +1 -1
  45. package/dist/controllers/color_picker_controller.d.ts +18 -6
  46. package/dist/controllers/color_picker_controller.js +149 -139
  47. package/dist/controllers/color_picker_controller.js.map +1 -1
  48. package/dist/controllers/combobox_controller.d.ts +23 -5
  49. package/dist/controllers/combobox_controller.js +360 -133
  50. package/dist/controllers/combobox_controller.js.map +1 -1
  51. package/dist/controllers/command_palette_controller.d.ts +45 -14
  52. package/dist/controllers/command_palette_controller.js +970 -358
  53. package/dist/controllers/command_palette_controller.js.map +1 -1
  54. package/dist/controllers/conditional_fields_controller.d.ts +15 -2
  55. package/dist/controllers/conditional_fields_controller.js +166 -124
  56. package/dist/controllers/conditional_fields_controller.js.map +1 -1
  57. package/dist/controllers/confirm_controller.d.ts +49 -12
  58. package/dist/controllers/confirm_controller.js +939 -248
  59. package/dist/controllers/confirm_controller.js.map +1 -1
  60. package/dist/controllers/context_menu_controller.d.ts +31 -4
  61. package/dist/controllers/context_menu_controller.js +256 -128
  62. package/dist/controllers/context_menu_controller.js.map +1 -1
  63. package/dist/controllers/count_up_controller.d.ts +13 -1
  64. package/dist/controllers/count_up_controller.js +73 -21
  65. package/dist/controllers/count_up_controller.js.map +1 -1
  66. package/dist/controllers/countdown_controller.d.ts +45 -1
  67. package/dist/controllers/countdown_controller.js +183 -148
  68. package/dist/controllers/countdown_controller.js.map +1 -1
  69. package/dist/controllers/currency_input_controller.d.ts +21 -2
  70. package/dist/controllers/currency_input_controller.js +134 -158
  71. package/dist/controllers/currency_input_controller.js.map +1 -1
  72. package/dist/controllers/data_grid_controller.d.ts +15 -4
  73. package/dist/controllers/data_grid_controller.js +114 -186
  74. package/dist/controllers/data_grid_controller.js.map +1 -1
  75. package/dist/controllers/date_range_picker_controller.d.ts +30 -5
  76. package/dist/controllers/date_range_picker_controller.js +288 -206
  77. package/dist/controllers/date_range_picker_controller.js.map +1 -1
  78. package/dist/controllers/dialog_controller.d.ts +13 -1
  79. package/dist/controllers/dialog_controller.js +858 -223
  80. package/dist/controllers/dialog_controller.js.map +1 -1
  81. package/dist/controllers/direct_upload_controller.d.ts +24 -7
  82. package/dist/controllers/direct_upload_controller.js +73 -153
  83. package/dist/controllers/direct_upload_controller.js.map +1 -1
  84. package/dist/controllers/dirty_form_controller.js +14 -48
  85. package/dist/controllers/dirty_form_controller.js.map +1 -1
  86. package/dist/controllers/dismissible_controller.d.ts +19 -0
  87. package/dist/controllers/dismissible_controller.js +175 -21
  88. package/dist/controllers/dismissible_controller.js.map +1 -1
  89. package/dist/controllers/drawer_controller.d.ts +39 -11
  90. package/dist/controllers/drawer_controller.js +933 -341
  91. package/dist/controllers/drawer_controller.js.map +1 -1
  92. package/dist/controllers/dropdown_controller.d.ts +31 -1
  93. package/dist/controllers/dropdown_controller.js +312 -82
  94. package/dist/controllers/dropdown_controller.js.map +1 -1
  95. package/dist/controllers/editable_controller.d.ts +12 -1
  96. package/dist/controllers/editable_controller.js +158 -68
  97. package/dist/controllers/editable_controller.js.map +1 -1
  98. package/dist/controllers/empty_state_controller.js +67 -53
  99. package/dist/controllers/empty_state_controller.js.map +1 -1
  100. package/dist/controllers/file_dropzone_controller.d.ts +60 -18
  101. package/dist/controllers/file_dropzone_controller.js +311 -207
  102. package/dist/controllers/file_dropzone_controller.js.map +1 -1
  103. package/dist/controllers/filter_controller.js +67 -39
  104. package/dist/controllers/filter_controller.js.map +1 -1
  105. package/dist/controllers/flash_controller.d.ts +31 -8
  106. package/dist/controllers/flash_controller.js +172 -314
  107. package/dist/controllers/flash_controller.js.map +1 -1
  108. package/dist/controllers/focus_controller.d.ts +12 -3
  109. package/dist/controllers/focus_controller.js +654 -240
  110. package/dist/controllers/focus_controller.js.map +1 -1
  111. package/dist/controllers/form_field_controller.js +147 -132
  112. package/dist/controllers/form_field_controller.js.map +1 -1
  113. package/dist/controllers/form_validation_controller.js +13 -86
  114. package/dist/controllers/form_validation_controller.js.map +1 -1
  115. package/dist/controllers/frame_loading_controller.d.ts +41 -9
  116. package/dist/controllers/frame_loading_controller.js +264 -213
  117. package/dist/controllers/frame_loading_controller.js.map +1 -1
  118. package/dist/controllers/highlight_controller.d.ts +22 -2
  119. package/dist/controllers/highlight_controller.js +107 -75
  120. package/dist/controllers/highlight_controller.js.map +1 -1
  121. package/dist/controllers/hover_card_controller.d.ts +56 -3
  122. package/dist/controllers/hover_card_controller.js +309 -118
  123. package/dist/controllers/hover_card_controller.js.map +1 -1
  124. package/dist/controllers/idle_controller.d.ts +42 -1
  125. package/dist/controllers/idle_controller.js +267 -101
  126. package/dist/controllers/idle_controller.js.map +1 -1
  127. package/dist/controllers/input_mask_controller.js +71 -72
  128. package/dist/controllers/input_mask_controller.js.map +1 -1
  129. package/dist/controllers/intersection_controller.d.ts +32 -13
  130. package/dist/controllers/intersection_controller.js +147 -106
  131. package/dist/controllers/intersection_controller.js.map +1 -1
  132. package/dist/controllers/lazy_frame_controller.d.ts +16 -0
  133. package/dist/controllers/lazy_frame_controller.js +9 -65
  134. package/dist/controllers/lazy_frame_controller.js.map +1 -1
  135. package/dist/controllers/listbox_controller.d.ts +34 -2
  136. package/dist/controllers/listbox_controller.js +306 -204
  137. package/dist/controllers/listbox_controller.js.map +1 -1
  138. package/dist/controllers/local_time_controller.d.ts +1 -1
  139. package/dist/controllers/local_time_controller.js +54 -64
  140. package/dist/controllers/local_time_controller.js.map +1 -1
  141. package/dist/controllers/masonry_controller.d.ts +11 -3
  142. package/dist/controllers/masonry_controller.js +70 -93
  143. package/dist/controllers/masonry_controller.js.map +1 -1
  144. package/dist/controllers/menu_controller.d.ts +32 -7
  145. package/dist/controllers/menu_controller.js +214 -156
  146. package/dist/controllers/menu_controller.js.map +1 -1
  147. package/dist/controllers/menubar_controller.d.ts +4 -4
  148. package/dist/controllers/menubar_controller.js +170 -329
  149. package/dist/controllers/menubar_controller.js.map +1 -1
  150. package/dist/controllers/meter_controller.d.ts +20 -0
  151. package/dist/controllers/meter_controller.js +151 -67
  152. package/dist/controllers/meter_controller.js.map +1 -1
  153. package/dist/controllers/multi_select_controller.d.ts +32 -4
  154. package/dist/controllers/multi_select_controller.js +593 -358
  155. package/dist/controllers/multi_select_controller.js.map +1 -1
  156. package/dist/controllers/navigation_menu_controller.d.ts +8 -1
  157. package/dist/controllers/navigation_menu_controller.js +124 -232
  158. package/dist/controllers/navigation_menu_controller.js.map +1 -1
  159. package/dist/controllers/nested_form_controller.d.ts +10 -0
  160. package/dist/controllers/nested_form_controller.js +352 -182
  161. package/dist/controllers/nested_form_controller.js.map +1 -1
  162. package/dist/controllers/network_status_controller.d.ts +23 -0
  163. package/dist/controllers/network_status_controller.js +242 -54
  164. package/dist/controllers/network_status_controller.js.map +1 -1
  165. package/dist/controllers/number_input_controller.d.ts +26 -2
  166. package/dist/controllers/number_input_controller.js +320 -263
  167. package/dist/controllers/number_input_controller.js.map +1 -1
  168. package/dist/controllers/optimistic_controller.js +16 -76
  169. package/dist/controllers/optimistic_controller.js.map +1 -1
  170. package/dist/controllers/otp_controller.d.ts +20 -4
  171. package/dist/controllers/otp_controller.js +198 -193
  172. package/dist/controllers/otp_controller.js.map +1 -1
  173. package/dist/controllers/overflow_indicator_controller.d.ts +5 -0
  174. package/dist/controllers/overflow_indicator_controller.js +116 -113
  175. package/dist/controllers/overflow_indicator_controller.js.map +1 -1
  176. package/dist/controllers/overflow_menu_controller.d.ts +48 -11
  177. package/dist/controllers/overflow_menu_controller.js +343 -283
  178. package/dist/controllers/overflow_menu_controller.js.map +1 -1
  179. package/dist/controllers/pagination_controller.d.ts +35 -6
  180. package/dist/controllers/pagination_controller.js +295 -119
  181. package/dist/controllers/pagination_controller.js.map +1 -1
  182. package/dist/controllers/password_reveal_controller.d.ts +45 -8
  183. package/dist/controllers/password_reveal_controller.js +389 -121
  184. package/dist/controllers/password_reveal_controller.js.map +1 -1
  185. package/dist/controllers/password_strength_controller.d.ts +14 -5
  186. package/dist/controllers/password_strength_controller.js +99 -153
  187. package/dist/controllers/password_strength_controller.js.map +1 -1
  188. package/dist/controllers/persist_controller.d.ts +11 -4
  189. package/dist/controllers/persist_controller.js +237 -103
  190. package/dist/controllers/persist_controller.js.map +1 -1
  191. package/dist/controllers/pointer_drag_controller.d.ts +19 -7
  192. package/dist/controllers/pointer_drag_controller.js +185 -165
  193. package/dist/controllers/pointer_drag_controller.js.map +1 -1
  194. package/dist/controllers/popover_controller.d.ts +38 -2
  195. package/dist/controllers/popover_controller.js +236 -97
  196. package/dist/controllers/popover_controller.js.map +1 -1
  197. package/dist/controllers/portal_controller.d.ts +44 -4
  198. package/dist/controllers/portal_controller.js +209 -76
  199. package/dist/controllers/portal_controller.js.map +1 -1
  200. package/dist/controllers/preview_guard_controller.d.ts +10 -4
  201. package/dist/controllers/preview_guard_controller.js +232 -116
  202. package/dist/controllers/preview_guard_controller.js.map +1 -1
  203. package/dist/controllers/progress_controller.d.ts +11 -0
  204. package/dist/controllers/progress_controller.js +125 -56
  205. package/dist/controllers/progress_controller.js.map +1 -1
  206. package/dist/controllers/radio_group_controller.d.ts +14 -5
  207. package/dist/controllers/radio_group_controller.js +117 -137
  208. package/dist/controllers/radio_group_controller.js.map +1 -1
  209. package/dist/controllers/range_slider_controller.d.ts +43 -3
  210. package/dist/controllers/range_slider_controller.js +192 -105
  211. package/dist/controllers/range_slider_controller.js.map +1 -1
  212. package/dist/controllers/rating_controller.d.ts +20 -4
  213. package/dist/controllers/rating_controller.js +418 -207
  214. package/dist/controllers/rating_controller.js.map +1 -1
  215. package/dist/controllers/read_more_controller.d.ts +2 -0
  216. package/dist/controllers/read_more_controller.js +134 -63
  217. package/dist/controllers/read_more_controller.js.map +1 -1
  218. package/dist/controllers/reading_progress_controller.d.ts +1 -1
  219. package/dist/controllers/reading_progress_controller.js +147 -135
  220. package/dist/controllers/reading_progress_controller.js.map +1 -1
  221. package/dist/controllers/relative_time_controller.d.ts +18 -4
  222. package/dist/controllers/relative_time_controller.js +145 -110
  223. package/dist/controllers/relative_time_controller.js.map +1 -1
  224. package/dist/controllers/reset_on_restore_controller.d.ts +85 -0
  225. package/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +97 -38
  226. package/dist/controllers/reset_on_restore_controller.js.map +1 -0
  227. package/dist/controllers/resizable_controller.d.ts +28 -2
  228. package/dist/controllers/resizable_controller.js +255 -139
  229. package/dist/controllers/resizable_controller.js.map +1 -1
  230. package/dist/controllers/roving_controller.js +39 -84
  231. package/dist/controllers/roving_controller.js.map +1 -1
  232. package/dist/controllers/scroll_area_controller.d.ts +7 -3
  233. package/dist/controllers/scroll_area_controller.js +428 -225
  234. package/dist/controllers/scroll_area_controller.js.map +1 -1
  235. package/dist/controllers/scroll_restore_controller.js +0 -53
  236. package/dist/controllers/scroll_restore_controller.js.map +1 -1
  237. package/dist/controllers/scroll_visibility_controller.d.ts +28 -12
  238. package/dist/controllers/scroll_visibility_controller.js +395 -145
  239. package/dist/controllers/scroll_visibility_controller.js.map +1 -1
  240. package/dist/controllers/scrollspy_controller.d.ts +6 -1
  241. package/dist/controllers/scrollspy_controller.js +181 -242
  242. package/dist/controllers/scrollspy_controller.js.map +1 -1
  243. package/dist/controllers/separator_controller.d.ts +19 -4
  244. package/dist/controllers/separator_controller.js +267 -140
  245. package/dist/controllers/separator_controller.js.map +1 -1
  246. package/dist/controllers/sidebar_controller.d.ts +64 -7
  247. package/dist/controllers/sidebar_controller.js +1023 -386
  248. package/dist/controllers/sidebar_controller.js.map +1 -1
  249. package/dist/controllers/skeleton_controller.d.ts +23 -0
  250. package/dist/controllers/skeleton_controller.js +248 -97
  251. package/dist/controllers/skeleton_controller.js.map +1 -1
  252. package/dist/controllers/slider_controller.d.ts +27 -1
  253. package/dist/controllers/slider_controller.js +159 -79
  254. package/dist/controllers/slider_controller.js.map +1 -1
  255. package/dist/controllers/smart_sticky_header_controller.d.ts +21 -3
  256. package/dist/controllers/smart_sticky_header_controller.js +162 -63
  257. package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
  258. package/dist/controllers/sortable_controller.js +0 -112
  259. package/dist/controllers/sortable_controller.js.map +1 -1
  260. package/dist/controllers/spinner_controller.d.ts +39 -4
  261. package/dist/controllers/spinner_controller.js +277 -169
  262. package/dist/controllers/spinner_controller.js.map +1 -1
  263. package/dist/controllers/step_indicator_controller.d.ts +5 -0
  264. package/dist/controllers/step_indicator_controller.js +99 -46
  265. package/dist/controllers/step_indicator_controller.js.map +1 -1
  266. package/dist/controllers/stepper_controller.d.ts +12 -3
  267. package/dist/controllers/stepper_controller.js +114 -73
  268. package/dist/controllers/stepper_controller.js.map +1 -1
  269. package/dist/controllers/stick_to_bottom_controller.d.ts +6 -0
  270. package/dist/controllers/stick_to_bottom_controller.js +262 -138
  271. package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
  272. package/dist/controllers/sticky_observer_controller.d.ts +11 -0
  273. package/dist/controllers/sticky_observer_controller.js +214 -49
  274. package/dist/controllers/sticky_observer_controller.js.map +1 -1
  275. package/dist/controllers/submit_once_controller.d.ts +23 -8
  276. package/dist/controllers/submit_once_controller.js +294 -205
  277. package/dist/controllers/submit_once_controller.js.map +1 -1
  278. package/dist/controllers/switch_controller.d.ts +12 -1
  279. package/dist/controllers/switch_controller.js +33 -60
  280. package/dist/controllers/switch_controller.js.map +1 -1
  281. package/dist/controllers/tabs_controller.d.ts +2 -2
  282. package/dist/controllers/tabs_controller.js +35 -29
  283. package/dist/controllers/tabs_controller.js.map +1 -1
  284. package/dist/controllers/tags_input_controller.d.ts +12 -0
  285. package/dist/controllers/tags_input_controller.js +136 -145
  286. package/dist/controllers/tags_input_controller.js.map +1 -1
  287. package/dist/controllers/textarea_autosize_controller.d.ts +10 -0
  288. package/dist/controllers/textarea_autosize_controller.js +71 -58
  289. package/dist/controllers/textarea_autosize_controller.js.map +1 -1
  290. package/dist/controllers/theme_controller.d.ts +21 -9
  291. package/dist/controllers/theme_controller.js +130 -112
  292. package/dist/controllers/theme_controller.js.map +1 -1
  293. package/dist/controllers/time_picker_controller.d.ts +18 -1
  294. package/dist/controllers/time_picker_controller.js +146 -80
  295. package/dist/controllers/time_picker_controller.js.map +1 -1
  296. package/dist/controllers/toast_controller.d.ts +36 -11
  297. package/dist/controllers/toast_controller.js +186 -297
  298. package/dist/controllers/toast_controller.js.map +1 -1
  299. package/dist/controllers/toggle_group_controller.d.ts +12 -3
  300. package/dist/controllers/toggle_group_controller.js +95 -136
  301. package/dist/controllers/toggle_group_controller.js.map +1 -1
  302. package/dist/controllers/toolbar_controller.js +68 -98
  303. package/dist/controllers/toolbar_controller.js.map +1 -1
  304. package/dist/controllers/tooltip_controller.d.ts +45 -3
  305. package/dist/controllers/tooltip_controller.js +158 -124
  306. package/dist/controllers/tooltip_controller.js.map +1 -1
  307. package/dist/controllers/transition_controller.d.ts +22 -4
  308. package/dist/controllers/transition_controller.js +123 -119
  309. package/dist/controllers/transition_controller.js.map +1 -1
  310. package/dist/controllers/tree_view_controller.d.ts +21 -11
  311. package/dist/controllers/tree_view_controller.js +150 -345
  312. package/dist/controllers/tree_view_controller.js.map +1 -1
  313. package/dist/index.d.ts +8 -5
  314. package/dist/index.js +6370 -9346
  315. package/dist/index.js.map +1 -1
  316. package/dist/inspector/cli.d.ts +23 -3
  317. package/dist/inspector/cli.js +92 -37
  318. package/dist/inspector/cli.js.map +1 -1
  319. package/dist/inspector/cli_bin.js +92 -56
  320. package/dist/inspector/cli_bin.js.map +1 -1
  321. package/dist/inspector/examples.json +8 -8
  322. package/dist/inspector/manifest.json +787 -100
  323. package/dist/positioning/index.d.ts +8 -0
  324. package/dist/positioning/index.js +148 -47
  325. package/dist/positioning/index.js.map +1 -1
  326. package/package.json +6 -6
  327. package/dist/controllers/reset_before_cache_controller.d.ts +0 -76
  328. package/dist/controllers/reset_before_cache_controller.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/announce.ts","../../src/utils/attribute_lease.ts","../../src/utils/before_cache_reset.ts","../../src/utils/detach_gate.ts","../../src/utils/listener_set.ts","../../src/utils/safe_timeout.ts","../../src/controllers/submit_once_controller.ts"],"names":[],"mappings":"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","file":"submit_once_controller.js","sourcesContent":["/**\n * Sends one message to the page's shared `stimeo--announcer`.\n *\n * A component that has to reach assistive tech does not carry a live region of its\n * own: a region only announces what changes *after* assistive tech already knows\n * about it, which a region that appears (or is un-hidden) with its message cannot\n * satisfy. The one region that can is the announcer sitting in the page from the\n * start, so state changes are handed to it as an event and it does the reading.\n *\n * The event goes to `window` because the announcer is usually a sibling high in the\n * document rather than an ancestor of the component dispatching it.\n *\n * Wording comes from the consumer — the library ships no English strings — so an\n * empty message is silently dropped and nothing is announced.\n *\n * @example\n * ```ts\n * announce(this.announceTextValue, { assertive: false });\n * ```\n */\nexport function announce(message: string, options: { assertive?: boolean } = {}): void {\n const text = message.trim();\n if (text.length === 0) return;\n window.dispatchEvent(\n new CustomEvent(\"stimeo--announcer:announce\", {\n detail: { message: text, assertive: options.assertive === true },\n }),\n );\n}\n\n/**\n * Fills `{name}` placeholders in an announcement template from `values`.\n *\n * The same substitution the value-text templates use, so a consumer writes\n * `\"{percent}% complete\"` in one attribute and gets the same rules everywhere. A\n * placeholder with no matching entry is left as authored rather than blanked, which\n * keeps a typo visible instead of silently swallowing the word.\n */\nexport function fillTemplate(template: string, values: Record<string, string | number>): string {\n return template.replace(/\\{([a-zA-Z][a-zA-Z0-9]*)\\}/g, (match, name: string) => {\n const replacement = values[name];\n return replacement === undefined ? match : String(replacement);\n });\n}\n","/** One temporarily controlled attribute value and the authored value it displaced. */\ninterface AttributeLeaseRecord {\n readonly original: string | null;\n written: string | null;\n}\n\n/**\n * Temporarily controls one attribute across a changing set of elements.\n *\n * The first write remembers the authored value, including the distinction between\n * an absent attribute and an authored empty string. Returning a lease restores that\n * value only while the attribute still matches the controller's last write. If a\n * consumer changed it in the meantime, the consumer owns the new value and teardown\n * leaves it alone.\n *\n * A `null` write deliberately removes the attribute while retaining the lease. This\n * is useful for derived ARIA whose valid absence is itself controller state, such as\n * an unbounded `aria-valuemin` or a blank spinbutton's `aria-valuenow`.\n *\n * The lease has no lifecycle of its own, so it never subscribes to document events.\n * A consumer that must not let a derived value become the authored baseline of a\n * cached page returns its leases from its own `turbo:before-cache` rewind, where the\n * matching `disconnect()` also releases the subscription.\n */\nexport class AttributeLease<T extends Element = Element> {\n readonly #attribute: string;\n readonly #records = new Map<T, AttributeLeaseRecord>();\n\n /** @param attribute - The attribute whose temporary values this lease owns. */\n constructor(attribute: string) {\n this.#attribute = attribute;\n }\n\n /** Writes or removes the leased attribute while preserving its authored value. */\n write(element: T, value: string | null): void {\n const existing = this.#records.get(element);\n if (existing) {\n existing.written = value;\n } else {\n this.#records.set(element, {\n original: element.getAttribute(this.#attribute),\n written: value,\n });\n }\n\n this.#reflect(element, value);\n }\n\n /** Returns one lease without overwriting a value subsequently authored by a consumer. */\n return(element: T): void {\n const record = this.#records.get(element);\n if (!record) return;\n this.#records.delete(element);\n const stillOwned = element.getAttribute(this.#attribute) === record.written;\n if (stillOwned) this.#reflect(element, record.original);\n }\n\n /** Reflects only a real value transition, avoiding self-triggered mutation work. */\n #reflect(element: T, value: string | null): void {\n if (element.getAttribute(this.#attribute) === value) return;\n if (value === null) element.removeAttribute(this.#attribute);\n else element.setAttribute(this.#attribute, value);\n }\n\n /** Returns every outstanding lease using the same ownership check as {@link return}. */\n returnAll(): void {\n for (const element of Array.from(this.#records.keys())) this.return(element);\n }\n}\n","/**\n * Runs a controller's \"return to the initial state\" pass just before Turbo\n * caches the page.\n *\n * **`disconnect()` cannot do this job, for two independent reasons.** Turbo\n * queues the clone from this event rather than taking it here, and the body swap\n * that runs the controller's `disconnect()` is queued separately — so which of\n * the two lands first is not something a controller can rely on, and a rewind\n * written in `disconnect()` may reach only the DOM being thrown away. In the\n * other direction, `disconnect()` also fires on an in-page move (Stimulus tears\n * down and reconnects the same element), where rewinding would wipe a\n * legitimately in-progress interaction — a spinner mid-load would vanish. One\n * timing is unreliable, the other is too eager; `turbo:before-cache` is the only\n * point that is exactly \"the page is about to be frozen\".\n *\n * Scope is the subscription only: registering on `activate()`, unregistering on\n * `deactivate()`, and one shared document listener no matter how many instances\n * are live. *What* to return to its initial state — which `data-state`, which\n * `hidden`, which `aria-busy` — stays in the controller, because no two\n * consumers answer it the same way.\n *\n * **Rewind state, not appearance.** The pass writes attributes the controller\n * itself owns; the visual result of those attributes is the consumer's CSS, and\n * a library that reached for style or class names would be guessing at markup\n * it does not own.\n *\n * Both entry points are idempotent, so the lifecycle hooks can call them\n * unconditionally: a second `activate()` does not double-subscribe and does not\n * make the callback run twice, and `deactivate()` on an instance that never\n * subscribed is a no-op.\n *\n * This file's own doc block is dropped from `dist`, but every member comment is\n * inlined into each consumer entry (`tsup` builds with `splitting: false`), so\n * rationale belongs here and only the contract belongs on the members.\n *\n * @example\n * ```ts\n * readonly #beforeCache = new BeforeCacheReset(() => this.#rewind());\n *\n * connect() { this.#beforeCache.activate(); }\n * disconnect() { this.#beforeCache.deactivate(); }\n * ```\n */\nexport class BeforeCacheReset {\n /** Every subscribed instance, iterated by the one shared document listener. */\n static readonly #subscribers = new Set<BeforeCacheReset>();\n\n /** The shared listener; installed while at least one instance is subscribed. */\n static readonly #onBeforeCache = (): void => {\n for (const subscriber of BeforeCacheReset.#subscribers) subscriber.#rewind();\n };\n\n readonly #rewind: () => void;\n\n /** @param rewind - the pass that returns this controller's state to its initial form. */\n constructor(rewind: () => void) {\n this.#rewind = rewind;\n }\n\n /** Subscribes to `turbo:before-cache`; call from `connect()`. Idempotent. */\n activate(): void {\n const first = BeforeCacheReset.#subscribers.size === 0;\n BeforeCacheReset.#subscribers.add(this);\n if (first) {\n document.addEventListener(\"turbo:before-cache\", BeforeCacheReset.#onBeforeCache);\n }\n }\n\n /** Unsubscribes; call from `disconnect()`. Safe when never subscribed. */\n deactivate(): void {\n BeforeCacheReset.#subscribers.delete(this);\n if (BeforeCacheReset.#subscribers.size > 0) return;\n document.removeEventListener(\"turbo:before-cache\", BeforeCacheReset.#onBeforeCache);\n }\n}\n","/**\n * Shared \"in-page move vs real detach\" discriminator for `disconnect()`.\n *\n * A mid-session `disconnect()` whose element is still in the document is\n * ambiguous. It can be:\n *\n * - an **in-page move** — a consumer (sortable, a teleport) re-inserted the\n * element; Stimulus reconnects the SAME instance in the same mutation batch\n * and the controller's runtime state must survive;\n * - a **detach that keeps the element** — `data-controller` no longer lists the\n * identifier (a Turbo 8 morph), or the element moved outside the observed\n * root (a scoped `Application.start(root)`, a shadow root); no reconnect will\n * ever come and the controller must tear down or it leaks its sessions,\n * document listeners, and moved nodes.\n *\n * Neither available signal decides this alone. A synchronous `data-controller`\n * token check catches the morph case but misses an observed-root exit (the\n * token stays, the reconnect never comes). Deferring teardown one microtask and\n * letting `connect()` cancel it catches the root exit, but leans on Stimulus\n * reconnecting moved elements within the same mutation batch — internal\n * behavior, not a public contract. {@link DetachGate} composes both: the token\n * check is the synchronous fast path (no probe, no reliance on batching), the\n * microtask probe is the fallback for the genuinely ambiguous remainder.\n *\n * Policy stays with the consumer; this util owns the discrimination only:\n *\n * - **Probe** ({@link disconnected}) when the teardown must eventually happen —\n * live drag sessions, document listeners, teleported *content*. Call\n * {@link cancel} from `connect()` (a reconnect means in-page move) and from\n * the head of any direct teardown path (see below).\n * - **Keep** (bare {@link DetachGate.isDetached}) when undoing the ambiguous\n * case would fight the controller's own effect: a controller that moves its\n * OWN element (portal's no-`content` form) exits a scoped observed root as\n * its normal job — a probe-driven teardown would restore the element into\n * the root, reconnect, re-teleport, and disconnect again, forever. Such\n * controllers keep their state on an ambiguous disconnect and tear down only\n * on {@link DetachGate.isDetached}.\n *\n * Event convention for probe consumers: teardown into a dead (detached) tree is\n * silent — consumers restore from their own `connect()`; a detach that keeps the\n * tree alive must end an in-flight session with a `cancel` event, or composing\n * consumers strand their session bookkeeping.\n */\n\n/** The slice of a Stimulus controller the gate inspects. */\nexport interface DetachGateHost {\n readonly element: Element;\n readonly identifier: string;\n}\n\n/**\n * Per-controller-instance gate deciding whether a `disconnect()` is a real\n * detach. Hold one per controller (`#gate = new DetachGate()`) — Stimulus\n * reuses the instance across reconnects, so the pending probe state carries\n * over exactly as the protocol needs.\n *\n * @example\n * ```ts\n * #gate = new DetachGate();\n *\n * connect(): void {\n * this.#gate.cancel(); // a reconnect: the element moved in-page\n * // …\n * }\n *\n * disconnect(): void {\n * this.#gate.disconnected(this, () => this.#teardown());\n * }\n *\n * #teardown(): void {\n * this.#gate.cancel(); // disarm a still-queued probe (double-run guard)\n * // …\n * }\n * ```\n */\nexport class DetachGate {\n /** Set while a probe is queued, waiting for a reconnect to cancel it. */\n #pending = false;\n\n /**\n * True while a probe is queued — the last disconnect was ambiguous and no\n * reconnect has cancelled it yet. Read it from `connect()` to tell the\n * reconnect half of an in-page move from a first connect: a controller whose\n * initialisation restarts a measurement (a min-duration floor, an elapsed\n * counter) must skip it for the move, where nothing actually restarted.\n */\n get pending(): boolean {\n return this.#pending;\n }\n\n /**\n * True when the disconnect is definitely a real detach — the element left\n * the document, or `data-controller` no longer lists the identifier. False\n * means ambiguous (in-page move or observed-root exit), NOT \"alive\".\n */\n static isDetached(host: DetachGateHost): boolean {\n if (!host.element.isConnected) return true;\n const tokens = (host.element.getAttribute(\"data-controller\") ?? \"\").split(/\\s+/);\n return !tokens.includes(host.identifier);\n }\n\n /**\n * Call from `disconnect()`: runs `teardown` synchronously on a definite\n * detach (fast path), otherwise defers it one microtask — a reconnect\n * ({@link cancel} from `connect()`) keeps the state, no reconnect runs it.\n * One microtask is the whole probe window: Stimulus reconnects a moved\n * element within the same mutation batch, before the checkpoint drains.\n */\n disconnected(host: DetachGateHost, teardown: () => void): void {\n if (DetachGate.isDetached(host)) {\n // Also disarms a probe a previous ambiguous disconnect left queued\n // (defer, element removed, disconnect again) — exactly one teardown.\n this.#pending = false;\n teardown();\n return;\n }\n this.#pending = true;\n queueMicrotask(() => {\n if (!this.#pending) return;\n this.#pending = false;\n teardown();\n });\n }\n\n /**\n * Disarms a pending probe. Call from `connect()` (the reconnect that proves\n * an in-page move) and from the head of any teardown path not routed through\n * {@link disconnected} (disabled-toggle, Escape), so an orphaned probe can\n * never run the teardown a second time.\n */\n cancel(): void {\n this.#pending = false;\n }\n}\n","/**\n * Owns every listener a controller attaches for one connected lifetime and\n * releases them together, so an event name is spelled once and the release\n * cannot drift from the registration.\n *\n * Detaching by hand repeats three things at the release site — the type, the\n * handler reference and the capture flag — and any one of them drifting leaves\n * a listener alive on an element the controller no longer drives. Aborting the\n * signal a registration was created with releases it whatever it was made of,\n * so the release site names nothing.\n *\n * One set per lifetime, held as a field and never rebuilt. {@link\n * ListenerSet.dispose} is the only thing that closes a generation, and it opens\n * the next one in the same step, so the lifecycle hooks can call `add` and\n * `dispose` in any order any number of times, and no `add` ever meets an\n * already aborted signal — a DOM that registers a listener on such a signal\n * would keep it forever. Rebuilding the set instead would leak:\n * `addEventListener` matches an existing registration on target, type, callback\n * and capture alone, so re-adding the same handler while the first registration\n * is live is discarded, and that live registration keeps the signal it was\n * attached with — a set built by a second `connect()` can never release what it\n * appears to own.\n *\n * Scope is the registration only. Timers, observers, leases and the order they\n * are unwound in stay with the controller, so `dispose()` belongs exactly where\n * the `removeEventListener` block stood. A receiver that changes at runtime (a\n * swapped target) is rebound by calling `dispose()` and then `add()` again from\n * the rebind, not by a second set.\n *\n * The set must own every registration of a tuple it holds: a listener the same\n * target already carries for the same type, callback and capture flag keeps the\n * right to remove it, and this set's `add` for that tuple is discarded. A\n * subscription shared between instances behind a participant count, or one that\n * lives for less than the connection, keeps its own pair of calls.\n *\n * This file's own doc block is dropped from `dist`, but every member comment is\n * inlined into each consumer entry (`tsup` builds with `splitting: false`), so\n * rationale belongs here and only the contract belongs on the members.\n *\n * @example\n * ```ts\n * readonly #listeners = new ListenerSet();\n *\n * connect(): void {\n * this.#listeners.add(this.element, \"submit\", this.#onNativeSubmit, { capture: true });\n * this.#listeners.add(this.element, \"turbo:submit-start\", this.#onSubmitStart);\n * this.#listeners.add(this.element, \"turbo:submit-end\", this.#onSubmitEnd);\n * }\n *\n * disconnect(): void {\n * this.#listeners.dispose();\n * }\n * ```\n */\nexport class ListenerSet {\n /** The generation every `add` joins until the next `dispose()`. */\n #abort = new AbortController();\n\n /**\n * Attaches `handler` to the open generation, exactly as the caller spelled it.\n *\n * The set supplies the signal, so `options` carries everything else the DOM\n * accepts — `capture` included, which has to match at release time and no\n * longer has a second place to drift from.\n */\n add(\n target: EventTarget,\n type: string,\n handler: EventListener,\n options?: Omit<AddEventListenerOptions, \"signal\">,\n ): void {\n target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });\n }\n\n /**\n * Releases every listener of the open generation, synchronously, and opens the\n * next one. Idempotent, and safe before anything has been added.\n */\n dispose(): void {\n this.#abort.abort();\n this.#abort = new AbortController();\n }\n}\n","/**\n * Self-cleaning timer registries shared by Stimeo controllers.\n *\n * Stimulus controllers frequently schedule `setTimeout` / `setInterval` work\n * (auto-dismiss, debouncing, polling). When the element leaves the DOM — a\n * Turbo Drive navigation, a Turbo Stream replacement, or any `disconnect()` —\n * orphaned timers keep firing against a detached controller, leaking memory and\n * mutating stale state. {@link SafeTimeout} and {@link SafeInterval} track every\n * timer they create so a single {@link TimerRegistry.clearAll | clearAll()} call\n * in `disconnect()` tears them all down.\n *\n * These are intentionally low-level primitives: they own *registration and\n * cleanup only*. Higher-level policy (pause/resume, remaining-time accounting)\n * stays in the individual controllers so per-widget semantics are not flattened\n * into a lowest-common-denominator helper.\n */\n\n/**\n * Largest delay a timer can hold: the platform stores it in a 32-bit signed\n * integer, and anything larger overflows to `1`, so a delay meant to be far in\n * the future fires almost immediately. A declared delay above this bound names\n * no delay at all, and a controller reading one falls back to its default.\n */\nexport const MAX_TIMER_DELAY_MS = 2_147_483_647;\n\n/**\n * Shared registry bookkeeping for the timeout/interval variants.\n *\n * Subclasses provide the scheduling primitive ({@link schedule}) and its matching\n * canceller ({@link cancel}); this base owns the set of live ids plus the\n * per-id and bulk teardown shared by both.\n */\nabstract class TimerRegistry {\n /** Live timer ids that have not yet been cleared (or, for timeouts, fired). */\n protected readonly ids = new Set<number>();\n\n /** Schedules the underlying platform timer and returns its id. */\n protected abstract schedule(callback: () => void, delay: number): number;\n\n /** Cancels the underlying platform timer for `id`. */\n protected abstract cancel(id: number): void;\n\n /**\n * Cancels a single tracked timer.\n *\n * No-ops if the id is unknown (already cleared, fired, or never owned by this\n * registry), so callers can clear defensively without guarding.\n */\n clear(id: number): void {\n if (this.ids.delete(id)) {\n this.cancel(id);\n }\n }\n\n /**\n * Cancels every tracked timer. Call this from a controller's `disconnect()`\n * to guarantee no timer outlives the element.\n */\n clearAll(): void {\n for (const id of this.ids) {\n this.cancel(id);\n }\n this.ids.clear();\n }\n\n /** Number of timers currently tracked (pending). */\n get size(): number {\n return this.ids.size;\n }\n}\n\n/**\n * `setTimeout` wrapper that auto-forgets each timer once it fires and supports\n * bulk teardown on disconnect.\n *\n * @example\n * ```ts\n * #timers = new SafeTimeout();\n *\n * connect() {\n * this.#timers.set(() => this.dismiss(), 5000);\n * }\n *\n * disconnect() {\n * this.#timers.clearAll();\n * }\n * ```\n */\nexport class SafeTimeout extends TimerRegistry {\n /**\n * Schedules `callback` after `delay` ms and returns the timer id.\n *\n * The id is removed from the registry automatically when the timeout fires,\n * so {@link TimerRegistry.size | size} reflects only still-pending timers.\n */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(() => {\n this.ids.delete(id);\n callback();\n }, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setTimeout(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearTimeout(id);\n }\n}\n\n/**\n * `setInterval` wrapper that tracks every interval for bulk teardown on\n * disconnect. Unlike {@link SafeTimeout}, intervals are retained until they are\n * explicitly cleared because they fire repeatedly.\n *\n * @example\n * ```ts\n * #intervals = new SafeInterval();\n *\n * connect() {\n * this.#intervals.set(() => this.tick(), 1000);\n * }\n *\n * disconnect() {\n * this.#intervals.clearAll();\n * }\n * ```\n */\nexport class SafeInterval extends TimerRegistry {\n /** Schedules a repeating `callback` every `delay` ms and returns the timer id. */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(callback, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setInterval(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearInterval(id);\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { announce } from \"../utils/announce\";\nimport { AttributeLease } from \"../utils/attribute_lease\";\nimport { BeforeCacheReset } from \"../utils/before_cache_reset\";\nimport { DetachGate } from \"../utils/detach_gate\";\nimport { ListenerSet } from \"../utils/listener_set\";\nimport { SafeTimeout } from \"../utils/safe_timeout\";\n\n/** Native controls that can submit a form. */\ntype SubmitControl = HTMLButtonElement | HTMLInputElement;\n\n/** Why an active submission returned to idle. */\ntype CompletionReason = \"canceled\" | \"manual\" | \"timeout\" | \"turbo\";\n\n/** A text/value write that cannot be represented by an `AttributeLease`. */\ntype LabelWrite =\n | {\n readonly channel: \"text\";\n readonly control: HTMLButtonElement;\n readonly original: string;\n readonly written: string;\n }\n | {\n readonly channel: \"value\";\n readonly control: HTMLInputElement;\n readonly original: string;\n readonly written: string;\n };\n\n/** One form's independent in-flight submission. */\ninterface SubmissionSession {\n readonly form: HTMLFormElement;\n readonly submitter: SubmitControl | null;\n readonly submitterHadFocus: boolean;\n readonly controls: Set<SubmitControl>;\n timeoutId: number | null;\n labelWrite: LabelWrite | null;\n idlePart: HTMLElement | null;\n busyPart: HTMLElement | null;\n}\n\n/** Per-button override for the controller's default busy label. */\nconst BUTTON_BUSY_LABEL = \"data-submit-once-busy-label\";\n\n/**\n * Headless form-scoped double-submit guard (identifier:\n * `stimeo--submit-once`). It disables every native submit control associated\n * with the submitting form, publishes busy state, and restores only the state\n * it still owns when the operation finishes.\n *\n * Turbo forms need no `data-action`: the controller observes\n * `turbo:submit-start` and `turbo:submit-end` itself.\n *\n * A non-Turbo async form owns its own lifecycle and declares each transition:\n * `submit->stimeo--submit-once#start` applies the busy state synchronously,\n * before the request is issued, and `#finish` / `#cancel` end it. Cancelling the\n * event's default is how such a form suppresses the native navigation, so it\n * never means the submission died — only `#cancel` does. A submit already\n * cancelled by an earlier listener never starts a session at all.\n *\n * ```html\n * <form data-controller=\"stimeo--submit-once\"\n * data-action=\"submit->stimeo--submit-once#start\n * save:complete->stimeo--submit-once#finish\n * save:error->stimeo--submit-once#cancel\">\n * <button type=\"submit\">Save</button>\n * </form>\n * ```\n *\n * A form whose submit a later listener vetoes needs `#cancel` wired to that\n * veto, or a non-zero `timeout`, to leave the busy state.\n *\n * Plain text buttons can use `busyLabel` (overridden by\n * `data-submit-once-busy-label`). A button with authored structure instead uses\n * an `idle` / `busy` target pair inside that button, so icons and descendants\n * are never replaced with `textContent`:\n *\n * ```html\n * <form data-controller=\"stimeo--submit-once\"\n * data-stimeo--submit-once-announce-text-value=\"Saving…\"\n * data-stimeo--submit-once-announce-ready-text-value=\"Saved.\">\n * <button type=\"submit\" data-stimeo--submit-once-target=\"submit\">\n * <span data-stimeo--submit-once-target=\"idle\">Save</span>\n * <span data-stimeo--submit-once-target=\"busy\" hidden>Saving…</span>\n * </button>\n * </form>\n * ```\n *\n * Events:\n * - `stimeo--submit-once:start` — `{ form, submitter }`.\n * - `stimeo--submit-once:reconcile` — `{ forms: HTMLFormElement[] }`, the forms\n * whose in-flight submission the Turbo cache rewind abandoned. `end` would\n * claim the submission resolved, so the rewind reports itself instead.\n * - `stimeo--submit-once:end` — `{ form, submitter, reason, success? }`, where\n * `reason` is `\"turbo\"`, `\"timeout\"`, `\"manual\"`, or `\"canceled\"`. Only\n * `\"canceled\"` skips the completion announcement, because no operation ran.\n *\n * @remarks\n * Behavior only: the consumer owns every visual, label, and announcement\n * string. `announceText` / `announceReadyText` are empty by default and speak\n * through the page's shared `stimeo--announcer` only on real state transitions.\n * Attribute writes use `AttributeLease`, so a consumer mutation made\n * while busy wins over restoration. `DetachGate` preserves an in-flight\n * session across an in-page move, while `BeforeCacheReset` rewinds the\n * snapshot before Turbo caches it. Cache/detach rewinds dispatch no `end`,\n * announce nothing and never move focus; a cache rewind that abandons a\n * submission reports it once as `reconcile`.\n */\nexport class SubmitOnceController extends Controller<HTMLElement> {\n static override targets = [\"submit\", \"idle\", \"busy\"];\n static override values = {\n announceText: { type: String, default: \"\" },\n announceReadyText: { type: String, default: \"\" },\n busyLabel: { type: String, default: \"\" },\n timeout: { type: Number, default: 0 },\n restoreFocus: { type: Boolean, default: false },\n };\n static actions = [\"cancel\", \"finish\", \"start\"] as const;\n static events = [\"start\", \"end\", \"reconcile\"] as const;\n\n declare readonly submitTargets: SubmitControl[];\n declare readonly idleTargets: HTMLElement[];\n declare readonly busyTargets: HTMLElement[];\n\n declare announceTextValue: string;\n declare announceReadyTextValue: string;\n declare busyLabelValue: string;\n declare timeoutValue: number;\n declare restoreFocusValue: boolean;\n\n readonly #timers = new SafeTimeout();\n readonly #gate = new DetachGate();\n readonly #listeners = new ListenerSet();\n readonly #beforeCache = new BeforeCacheReset(() => this.#rewindForCache());\n readonly #sessions = new Map<HTMLFormElement, SubmissionSession>();\n\n readonly #disabled = new AttributeLease<SubmitControl>(\"disabled\");\n readonly #controlBusy = new AttributeLease<SubmitControl>(\"aria-busy\");\n readonly #formBusy = new AttributeLease<HTMLFormElement>(\"aria-busy\");\n readonly #submitting = new AttributeLease<HTMLFormElement>(\"data-submitting\");\n readonly #ariaLabel = new AttributeLease<HTMLButtonElement>(\"aria-label\");\n readonly #hidden = new AttributeLease<HTMLElement>(\"hidden\");\n\n readonly #onSubmitStart = (event: Event): void => {\n this.start(event);\n };\n\n readonly #onSubmitEnd = (event: Event): void => {\n this.#complete(this.#eventForm(event), \"turbo\", this.#eventSuccess(event));\n };\n\n /** Blocks a later native submit while one for the same form is still active. */\n readonly #onNativeSubmit = (event: Event): void => {\n // The submit event's target is its form. This listener runs in capture so it\n // always observes that first event before a public `start` action creates the\n // session, while a later event sees the session and is canceled here.\n const session = this.#sessionFor(event.target);\n if (!session) return;\n this.#syncControls(session);\n event.preventDefault();\n };\n\n override connect(): void {\n this.#gate.cancel();\n this.#beforeCache.activate();\n this.#listeners.add(this.element, \"submit\", this.#onNativeSubmit, { capture: true });\n this.#listeners.add(this.element, \"turbo:submit-start\", this.#onSubmitStart);\n this.#listeners.add(this.element, \"turbo:submit-end\", this.#onSubmitEnd);\n }\n\n override disconnect(): void {\n this.#listeners.dispose();\n this.#beforeCache.deactivate();\n this.#gate.disconnected(this, () => this.#teardown());\n }\n\n /**\n * Begins the submission represented by `event`.\n *\n * Native `submit` events are intended for non-Turbo async forms. Turbo calls\n * this path through its auto-subscribed `turbo:submit-start` event. An event\n * whose default an earlier listener already cancelled carries no submission,\n * so it starts nothing.\n */\n start(event: Event): void {\n if (event.defaultPrevented) return;\n const form = this.#eventForm(event);\n if (!form || this.#sessions.has(form)) return;\n\n const controls = this.#submitControls(form);\n const submitter = this.#resolveSubmitter(event, controls);\n const session: SubmissionSession = {\n form,\n submitter,\n submitterHadFocus: submitter !== null && document.activeElement === submitter,\n controls: new Set(),\n timeoutId: null,\n labelWrite: null,\n idlePart: null,\n busyPart: null,\n };\n this.#sessions.set(form, session);\n\n this.#formBusy.write(form, \"true\");\n this.#submitting.write(form, \"true\");\n this.#syncControls(session, controls);\n this.#enterLabel(session);\n this.dispatch(\"start\", { detail: { form, submitter } });\n\n if (this.timeoutValue > 0) {\n session.timeoutId = this.#timers.set(\n () => this.#complete(form, \"timeout\", false),\n this.timeoutValue,\n );\n }\n\n announce(this.announceTextValue);\n }\n\n /**\n * Completes a non-Turbo async submission for the form associated with\n * `event`. When called directly with no event, it completes the controller's\n * sole active form (or the controller element itself when it is a form).\n */\n finish(event?: Event): void {\n const form = event ? this.#eventForm(event) : this.#defaultForm();\n this.#complete(form, \"manual\", event ? this.#eventSuccess(event) : undefined);\n }\n\n /**\n * Abandons the submission for the form associated with `event` because it\n * never reached the network — a veto from validation, or a request the\n * consumer gave up before sending. With no event it targets the controller's\n * sole active form, exactly like {@link finish}.\n *\n * The distinction from `finish` is observable: a request that ran and failed\n * is `finish` with `success: false` and still announces completion, while a\n * submission that never happened announces nothing.\n */\n cancel(event?: Event): void {\n const form = event ? this.#eventForm(event) : this.#defaultForm();\n this.#complete(form, \"canceled\", false);\n }\n\n /** Disables a newly connected explicit submit target during its form's session. */\n submitTargetConnected(target: SubmitControl): void {\n const session = this.#sessionFor(target.form);\n if (!session || !this.#isNativeSubmitControl(target)) return;\n this.#enterControl(session, target);\n }\n\n /**\n * Ends one session, restores owned state, and reports the observable completion.\n *\n * @stimeoRuntimeOnly `announceReadyText` words the one announcement of this finished submission\n * and `restoreFocus` decides where its focus returns.\n */\n #complete(form: HTMLFormElement | null, reason: CompletionReason, success?: boolean): void {\n const session = this.#sessionFor(form);\n if (!session) return;\n this.#sessions.delete(session.form);\n this.#returnSessionState(session);\n\n const detail =\n success === undefined\n ? { form: session.form, submitter: session.submitter, reason }\n : { form: session.form, submitter: session.submitter, reason, success };\n this.dispatch(\"end\", { detail });\n this.#restoreFocus(session);\n if (reason !== \"canceled\") announce(this.announceReadyTextValue);\n }\n\n /** Applies the busy state to every current native/explicit control exactly once. */\n #syncControls(session: SubmissionSession, controls = this.#submitControls(session.form)): void {\n for (const control of controls) this.#enterControl(session, control);\n }\n\n /** Temporarily disables one enabled submit control while retaining authored state. */\n #enterControl(session: SubmissionSession, control: SubmitControl): void {\n if (session.controls.has(control) || control.matches(\":disabled\")) return;\n session.controls.add(control);\n this.#disabled.write(control, \"\");\n this.#controlBusy.write(control, \"true\");\n }\n\n /**\n * Swaps only the triggering control's safe label channel.\n *\n * @stimeoRuntimeOnly `busyLabel` is the label this one submission shows until it ends; the idle\n * label comes back with the state it returns.\n */\n #enterLabel(session: SubmissionSession): void {\n const control = session.submitter;\n if (!control) return;\n\n const idlePart = this.#partInside(control, this.idleTargets);\n const busyPart = this.#partInside(control, this.busyTargets);\n if (idlePart && busyPart) {\n session.idlePart = idlePart;\n session.busyPart = busyPart;\n this.#hidden.write(idlePart, \"\");\n this.#hidden.write(busyPart, null);\n }\n\n const label = control.getAttribute(BUTTON_BUSY_LABEL) ?? this.busyLabelValue;\n if (label.length === 0) return;\n if (control instanceof HTMLInputElement) {\n // `input[type=image]#value` is submitted data, not a visible label.\n if (control.type !== \"submit\") return;\n session.labelWrite = {\n channel: \"value\",\n control,\n original: control.value,\n written: label,\n };\n control.value = label;\n return;\n }\n if (control.hasAttribute(\"aria-label\")) {\n this.#ariaLabel.write(control, label);\n return;\n }\n // A structured button needs the explicit idle/busy pair above. Replacing its\n // textContent would destroy icons, counters, and application-owned listeners.\n if (control.childElementCount > 0) return;\n session.labelWrite = {\n channel: \"text\",\n control,\n original: control.textContent ?? \"\",\n written: label,\n };\n control.textContent = label;\n }\n\n /** Returns labels, controls, and form hooks, respecting intervening consumer writes. */\n #returnSessionState(session: SubmissionSession): void {\n if (session.timeoutId !== null) {\n this.#timers.clear(session.timeoutId);\n session.timeoutId = null;\n }\n\n if (session.labelWrite?.channel === \"value\") {\n const { control, original, written } = session.labelWrite;\n if (control.value === written) control.value = original;\n } else if (session.labelWrite?.channel === \"text\") {\n const { control, original, written } = session.labelWrite;\n if (control.childElementCount === 0 && control.textContent === written) {\n control.textContent = original;\n }\n }\n session.labelWrite = null;\n\n if (session.submitter instanceof HTMLButtonElement) {\n this.#ariaLabel.return(session.submitter);\n }\n if (session.idlePart) this.#hidden.return(session.idlePart);\n if (session.busyPart) this.#hidden.return(session.busyPart);\n session.idlePart = null;\n session.busyPart = null;\n\n for (const control of session.controls) {\n this.#controlBusy.return(control);\n this.#disabled.return(control);\n }\n session.controls.clear();\n this.#submitting.return(session.form);\n this.#formBusy.return(session.form);\n }\n\n /** Restores focus only when disabling the focused submitter actually stranded it. */\n #restoreFocus(session: SubmissionSession): void {\n const submitter = session.submitter;\n if (!this.restoreFocusValue || !session.submitterHadFocus || !submitter?.isConnected) return;\n const active = document.activeElement;\n if (active === null || active === document.body || active === document.documentElement) {\n submitter.focus();\n }\n }\n\n /** Silently abandons every session before caching or on a real detach. */\n #abandonSessions(): void {\n this.#timers.clearAll();\n for (const session of this.#sessions.values()) {\n session.timeoutId = null;\n this.#returnSessionState(session);\n }\n this.#sessions.clear();\n }\n\n /**\n * Keeps Turbo's cached snapshot idle without `end`, announcements, or focus\n * moves; the abandoned forms are reported once as `reconcile`.\n */\n #rewindForCache(): void {\n const forms = [...this.#sessions.keys()];\n this.#abandonSessions();\n // A submission in flight dies with the navigation, so a consumer still\n // painting \"submitting\" from `start` would never be released.\n if (forms.length > 0) this.dispatch(\"reconcile\", { detail: { forms } });\n }\n\n /** Tears down a true detach; an in-page move is canceled by the next `connect()`. */\n #teardown(): void {\n this.#gate.cancel();\n this.#abandonSessions();\n }\n\n /** All native controls associated with `form`, plus valid explicit targets. */\n #submitControls(form: HTMLFormElement): SubmitControl[] {\n const controls = new Set<SubmitControl>();\n for (const element of Array.from(form.elements)) {\n if (this.#isNativeSubmitControl(element)) controls.add(element);\n }\n // Browsers omit input[type=image] from form.elements. Search\n // the form's tree root as well so descendant and external `form=` image\n // submitters receive the same guard without broadening into sibling forms.\n const root = form.getRootNode() as ParentNode;\n for (const image of root.querySelectorAll<HTMLInputElement>('input[type=\"image\"]')) {\n if (image.form === form) controls.add(image);\n }\n return Array.from(controls);\n }\n\n /** Resolves the real submitter, falling back to the first enabled submit control. */\n #resolveSubmitter(event: Event, controls: SubmitControl[]): SubmitControl | null {\n const submitter = this.#eventSubmitter(event);\n if (submitter && controls.includes(submitter)) return submitter;\n return controls.find((control) => !control.matches(\":disabled\")) ?? controls[0] ?? null;\n }\n\n /** Reads a native or Turbo submitter without assuming `SubmitEvent` exists. */\n #eventSubmitter(event: Event): SubmitControl | null {\n if (typeof SubmitEvent !== \"undefined\" && event instanceof SubmitEvent) {\n return this.#isNativeSubmitControl(event.submitter) ? event.submitter : null;\n }\n const detail = (event as CustomEvent<{ formSubmission?: { submitter?: HTMLElement | null } }>)\n .detail;\n const submitter = detail?.formSubmission?.submitter;\n return this.#isNativeSubmitControl(submitter) ? submitter : null;\n }\n\n /** Finds the form represented by an action/Turbo event and verifies this scope owns it. */\n #eventForm(event: Event): HTMLFormElement | null {\n const detail = (\n event as CustomEvent<{\n form?: HTMLFormElement;\n formSubmission?: { formElement?: HTMLFormElement; submitter?: HTMLElement | null };\n }>\n ).detail;\n const target = event.target;\n const candidate =\n (target instanceof HTMLFormElement ? target : null) ??\n (target instanceof Element ? target.closest(\"form\") : null) ??\n detail?.formSubmission?.formElement ??\n detail?.form ??\n this.#eventSubmitter(event)?.form ??\n (this.element instanceof HTMLFormElement ? this.element : null);\n return candidate && this.#ownsForm(candidate) ? candidate : null;\n }\n\n /** The only unambiguous form for a direct `finish()` call. */\n #defaultForm(): HTMLFormElement | null {\n if (this.element instanceof HTMLFormElement && this.#ownsForm(this.element))\n return this.element;\n if (this.#sessions.size === 1) return this.#sessions.keys().next().value ?? null;\n return null;\n }\n\n /** Prevents an ancestor controller from also taking over a nested instance's form. */\n #ownsForm(form: HTMLFormElement): boolean {\n let candidate: Element | null = form;\n while (candidate) {\n const identifiers = (candidate.getAttribute(\"data-controller\") ?? \"\").split(/\\s+/);\n if (identifiers.includes(this.identifier)) return candidate === this.element;\n candidate = candidate.parentElement;\n }\n return false;\n }\n\n /** Returns the active session for a form-shaped external event value. */\n #sessionFor(candidate: unknown): SubmissionSession | undefined {\n if (!(candidate instanceof HTMLFormElement)) return undefined;\n return this.#sessions.get(candidate);\n }\n\n /** Effective native submit controls, including implicit buttons and image inputs. */\n #isNativeSubmitControl(element: unknown): element is SubmitControl {\n if (element instanceof HTMLButtonElement) return element.type === \"submit\";\n return (\n element instanceof HTMLInputElement && (element.type === \"submit\" || element.type === \"image\")\n );\n }\n\n /** Finds a structured label part belonging to this button, not a nested control. */\n #partInside(control: SubmitControl, parts: HTMLElement[]): HTMLElement | null {\n return (\n parts.find(\n (part) =>\n control.contains(part) &&\n part.closest(\"button, input[type=submit], input[type=image]\") === control,\n ) ?? null\n );\n }\n\n /** Reads an optional boolean `success` from Turbo or a manual completion event. */\n #eventSuccess(event: Event): boolean | undefined {\n const success = (event as CustomEvent<{ success?: unknown }>).detail?.success;\n return typeof success === \"boolean\" ? success : undefined;\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/utils/announce.ts","../../src/utils/shared_registry.ts","../../src/utils/attribute_lease.ts","../../src/utils/detach_gate.ts","../../src/utils/listener_set.ts","../../src/utils/timer_bounds.ts","../../src/utils/number_bounds.ts","../../src/utils/coerce.ts","../../src/utils/number_value.ts","../../src/utils/safe_timeout.ts","../../src/controllers/submit_once_controller.ts"],"names":[],"mappings":"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","file":"submit_once_controller.js","sourcesContent":["/**\n * Sends one message to the page's shared `stimeo--announcer`.\n *\n * A component that has to reach assistive tech does not carry a live region of its\n * own: a region only announces what changes *after* assistive tech already knows\n * about it, which a region that appears (or is un-hidden) with its message cannot\n * satisfy. The one region that can is the announcer sitting in the page from the\n * start, so state changes are handed to it as an event and it does the reading.\n *\n * The event goes to `window` because the announcer is usually a sibling high in the\n * document rather than an ancestor of the component dispatching it.\n *\n * Wording comes from the consumer — the library ships no English strings — so an\n * empty message is silently dropped and nothing is announced.\n *\n * @example\n * ```ts\n * announce(this.announceTextValue, { assertive: false });\n * ```\n */\nexport function announce(message: string, options: { assertive?: boolean } = {}): void {\n const text = message.trim();\n if (text.length === 0) return;\n window.dispatchEvent(\n new CustomEvent(\"stimeo--announcer:announce\", {\n detail: { message: text, assertive: options.assertive === true },\n }),\n );\n}\n\n/**\n * Fills `{name}` placeholders in an announcement template from `values`.\n *\n * The same substitution the value-text templates use, so a consumer writes\n * `\"{percent}% complete\"` in one attribute and gets the same rules everywhere. A\n * placeholder with no matching entry is left as authored rather than blanked, which\n * keeps a typo visible instead of silently swallowing the word.\n */\nexport function fillTemplate(template: string, values: Record<string, string | number>): string {\n return template.replace(/\\{([a-zA-Z][a-zA-Z0-9]*)\\}/g, (match, name: string) => {\n const replacement = values[name];\n return replacement === undefined ? match : String(replacement);\n });\n}\n","/**\n * One registry per page for every copy of the library.\n *\n * Each `stimeo-ui/controllers/*` file, and each opt-in entry, is built on its own and carries\n * its own copy of every utility it uses, so a page that imports controllers one by one, or one\n * of them next to the barrel, evaluates a utility several times. A registry that coordinates\n * instances of different controllers — the stacks of leased values, the loans of a\n * `tabindex`, the Escape layers, the focus traps — must not be split by that: a dialog inside\n * a drawer, or a character counter and a form field on one input, coordinate whichever files\n * they came from. So such a registry lives once on `globalThis`, under a `Symbol.for` key the\n * copies agree on, and the first copy to ask for it creates it.\n *\n * The key names the registry and the version of its shape\n * (`stimeo-ui.<utility>.registry.v<n>`). Copies whose registries have the same shape share\n * one; a release that changes the shape changes the version, so a copy of another release\n * keeps a registry of its own instead of reading entries it does not understand — the two\n * then coordinate within each release only. An entry another copy put in a registry is read\n * through its fields alone, never through `#private` members, which belong to the class of\n * the copy that created it.\n */\n\n/**\n * The registry stored under `key`, created by `create` on first use.\n *\n * @param key - `stimeo-ui.<utility>.registry.v<n>`: the version is that of the shape `create`\n * returns.\n * @param create - Builds the empty registry.\n */\nexport function sharedRegistry<T extends object>(key: string, create: () => T): T {\n const symbol = Symbol.for(key);\n const scope = globalThis as unknown as Record<symbol, T | undefined>;\n const existing = scope[symbol];\n if (existing !== undefined) return existing;\n const registry = create();\n Object.defineProperty(globalThis, symbol, { value: registry });\n return registry;\n}\n","import { sharedRegistry } from \"./shared_registry\";\n\n/** What a lease's record says: the author's value, its last write, the leases beneath. */\ninterface AttributeLeaseRecord {\n readonly original: string | null;\n readonly written: string | null;\n /** Whether it hands back what the lease right beneath it wrote last. */\n readonly linked: boolean;\n /** The records of the other owners' leases beneath it, nearest first. */\n readonly beneath: readonly string[];\n}\n\n/** One lease's hold on one element's attribute, in the stack every lease of it shares. */\ninterface AttributeHold {\n /** The lease holding it; `null` for a hold read from a copy's record until its lease takes it. */\n lease: object | null;\n readonly record: string;\n written: string | null;\n /** What it hands back with nothing beneath it: the author's value. */\n readonly original: string | null;\n /** Whether it hands back what the hold beneath it wrote last instead. */\n linked: boolean;\n}\n\n/** The holds on each element by attribute, and every lease's record name by attribute. */\ninterface AttributeLeaseRegistry {\n readonly stacks: WeakMap<Element, Map<string, AttributeHold[]>>;\n readonly names: Map<string, Set<string>>;\n}\n\n/** Whether a parsed record entry is an attribute value: a string, or `null` for absence. */\nfunction isAttributeValue(value: unknown): value is string | null {\n return value === null || typeof value === \"string\";\n}\n\n/**\n * Reads the record a lease left on `element`: the author's value, the value the lease wrote\n * last, whether it hands back the value beneath it (`false` before the names when it does not)\n * and the records beneath it. `undefined` when the record is missing or holds anything else.\n */\nfunction readLeaseRecord(element: Element, record: string): AttributeLeaseRecord | undefined {\n try {\n const parsed: unknown = JSON.parse(element.getAttribute(record) ?? \"null\");\n if (!Array.isArray(parsed)) return undefined;\n const [original, written, ...rest] = parsed as unknown[];\n const linked = rest[0] !== false;\n const beneath = linked ? rest : rest.slice(1);\n if (!beneath.every((under): under is string => typeof under === \"string\")) return undefined;\n return isAttributeValue(original) && isAttributeValue(written)\n ? { original, written, linked, beneath }\n : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * The value `element`'s author wrote for `attribute`, as the record a lease of any owner leaves\n * on the element carries it; `undefined` while no lease leaves one, as when nothing has moved\n * the attribute off the author's value. A field a reveal turned from `password` into `text`\n * still reads `\"password\"` here.\n */\nexport function leasedAuthorValue(element: Element, attribute: string): string | null | undefined {\n const suffix = `-${attribute}-lease`;\n for (const name of element.getAttributeNames()) {\n if (!name.startsWith(\"data-\") || !name.endsWith(suffix)) continue;\n const record = readLeaseRecord(element, name);\n if (record) return record.original;\n }\n return undefined;\n}\n\n/**\n * Temporarily controls one attribute across a changing set of elements.\n *\n * The first write remembers the authored value, including the distinction between\n * an absent attribute and an authored empty string. Returning a lease restores that\n * value only while the attribute still matches the controller's last write. If a\n * consumer changed it in the meantime, the consumer owns the new value and teardown\n * leaves it alone.\n *\n * A `null` write deliberately removes the attribute while retaining the lease. This\n * is useful for derived ARIA whose valid absence is itself controller state, such as\n * an unbounded `aria-valuemin` or a blank spinbutton's `aria-valuenow`.\n *\n * **Every lease of an attribute shares one stack of holds per element**, whatever its owner\n * and whichever file of the package constructed it (the stacks are one per page): the holds\n * sit in the order their leases first wrote the element, each with the value it wrote last.\n * The element's value is the highest hold's that carries it, and the page's when none does.\n * A write reaches the element unless a hold above the writer carries the element's value: the\n * top hold always shows its writes, a hold beneath it shows nothing while a hold above keeps\n * its value on the element, and once the page has put a value there that no hold carries, the\n * next write of any hold replaces it. A return gives a value back only from the hold the\n * element's value is: the value the hold beneath it wrote last, or the author's once nothing\n * is beneath it; any other return only leaves the stack, and the page's value stays. Holds\n * that wrote the same value are told apart by the stack, never by the value: the higher one\n * keeps it. A first write over a top hold whose value the page has replaced takes the\n * element's value as the author's. Two instances of one controller on one element — an\n * element outside their scope — stack the same way.\n *\n * A **base lease** (`{ base: true }`) holds the element's own state rather than a value over\n * it — a form field's ARIA on its control is one — so its first write goes to the bottom of\n * the stack, beneath the leases of other owners already there: a temporary value another\n * owner leased stays shown and returns to the base lease's latest value.\n *\n * **The stack travels with the element.** While a hold shows a value other than the author's,\n * lies on another hold or is covered by one, the element carries its owner's record\n * `data-<owner>-<attribute>-lease`: JSON of the author's value and the value the hold wrote\n * last (each a string, or `null` for an absent attribute), `false` where the hold hands back\n * the author's value rather than the latest value of the hold beneath it, and the records of\n * every hold beneath it, nearest first. Returning the last lease of an owner removes its\n * record. A copy of the element — a page Turbo restores from its cache is one — therefore\n * carries the stack, and the first lease to touch the copy reads it back under the record\n * names of the leases constructed so far, which include every lease that wrote a record the\n * cache holds, and orders the holds by the records each one lists beneath it. Each lease takes\n * the hold its owner's record describes over in place. Complete records for distinct owners\n * preserve their order across a copy. The records describe holds by owner, not by instance:\n * one owner holding the element more than once leaves only its topmost hold's record, which\n * the first of its leases to write or return on the copy takes. Lower holds of that owner and\n * their positions are absent; mutually listed owners cannot express their full live order.\n * A hold that showed the author's value alone carries no record and is taken by its first\n * write on the copy. Missing or unreadable records also leave their holds unknown, so copying\n * the DOM does not promise identical decisions for every live stack. Without a record it can\n * read, a new stack takes the current value as the author's. A Turbo morph removes records\n * absent from the server HTML; existing holds remain in memory until their leases return.\n *\n * The lease has no lifecycle of its own, so it never subscribes to document events,\n * and nothing rewinds a live page before Turbo caches it: `turbo:before-cache` also\n * fires on pages that stay.\n */\nexport class AttributeLease<T extends Element = Element> {\n /** The holds on each element, bottom first, and the record names; one per page. */\n static readonly #registry = sharedRegistry(\n \"stimeo-ui.attribute-lease.registry.v1\",\n (): AttributeLeaseRegistry => ({ stacks: new WeakMap(), names: new Map() }),\n );\n readonly #attribute: string;\n readonly #record: string;\n readonly #base: boolean;\n readonly #held = new Set<T>();\n\n /**\n * @param attribute - The attribute whose temporary values this lease owns.\n * @param owner - Names the record on the element; the controller's identifier.\n * @param options - `base`: written beneath the other owners' leases.\n */\n constructor(attribute: string, owner: string, options: { readonly base?: boolean } = {}) {\n this.#attribute = attribute;\n this.#record = `data-${owner}-${attribute}-lease`;\n this.#base = options.base === true;\n const names = AttributeLease.#registry.names;\n names.set(attribute, (names.get(attribute) ?? new Set<string>()).add(this.#record));\n }\n\n /** Writes or removes the leased attribute while preserving its authored value. */\n write(element: T, value: string | null): void {\n const stack = this.#stack(element);\n const hold = this.#take(stack) ?? this.#acquire(element, stack);\n this.#held.add(element);\n const shows = !this.#covered(element, stack, hold);\n hold.written = value;\n this.#mark(element, stack);\n if (shows) this.#reflect(element, value);\n }\n\n /**\n * Returns one lease, also on a restored copy, to the value the hold beneath it wrote last or\n * the author's, without overwriting a value a consumer wrote since. Says whether there was a\n * lease to return: a hold this lease has, or the hold a copy's record describes while no live\n * lease of the owner has taken it — a copy of a page an earlier connection wrote carries one.\n */\n return(element: T): boolean {\n const held = this.#held.delete(element);\n const known = AttributeLease.#registry.stacks.get(element)?.has(this.#attribute);\n const stack = held || known || element.hasAttribute(this.#record) ? this.#stack(element) : [];\n const hold = this.#take(stack);\n if (!hold) return false;\n const carries = element.getAttribute(this.#attribute) === hold.written;\n const owns = carries && !this.#covered(element, stack, hold);\n const index = stack.indexOf(hold);\n stack.splice(index, 1);\n const above = stack[index];\n if (above) above.linked &&= hold.linked;\n this.#mark(element, stack);\n if (!owns) return true;\n const below = hold.linked ? stack[index - 1] : undefined;\n this.#reflect(element, below ? below.written : hold.original);\n return true;\n }\n\n /** Returns every lease it holds, with the same ownership check as {@link return}. */\n returnAll(): void {\n for (const element of Array.from(this.#held)) this.return(element);\n }\n\n /** The element's stack, read from its records the first time. */\n #stack(element: T): AttributeHold[] {\n const stacks = AttributeLease.#registry.stacks;\n const byAttribute = stacks.get(element) ?? new Map<string, AttributeHold[]>();\n stacks.set(element, byAttribute);\n const stack = byAttribute.get(this.#attribute) ?? this.#seed(element);\n byAttribute.set(this.#attribute, stack);\n return stack;\n }\n\n /** The holds a copy's records describe, each above every hold its record lists beneath. */\n #seed(element: T): AttributeHold[] {\n const found: (AttributeLeaseRecord & { readonly record: string })[] = [];\n for (const record of AttributeLease.#registry.names.get(this.#attribute) ?? []) {\n const read = readLeaseRecord(element, record);\n if (read) found.push({ ...read, record });\n }\n const depth = ({ beneath }: (typeof found)[number]) =>\n beneath.filter((name) => found.some((other) => other.record === name)).length;\n return found\n .sort((one, other) => depth(one) - depth(other))\n .map(({ record, written, original, linked }, index) => ({\n lease: null,\n record,\n written,\n original,\n linked: linked && index > 0,\n }));\n }\n\n /** Its hold in `stack`, taking over one read from its record. */\n #take(stack: AttributeHold[]): AttributeHold | undefined {\n const hold =\n stack.find((held) => held.lease === this) ??\n stack.find((held) => held.lease === null && held.record === this.#record);\n if (hold) hold.lease = this;\n return hold;\n }\n\n /** A new hold: on top, over the top hold while the element shows it; a base one at the bottom. */\n #acquire(element: T, stack: AttributeHold[]): AttributeHold {\n const current = element.getAttribute(this.#attribute);\n const top = stack.at(-1);\n const bottom = stack[0];\n const over = top?.written === current ? top : undefined;\n const from = this.#base ? bottom : over;\n const hold: AttributeHold = {\n lease: this,\n record: this.#record,\n written: current,\n original: from ? from.original : current,\n linked: !this.#base && over !== undefined,\n };\n if (this.#base && bottom) {\n bottom.linked = true;\n stack.unshift(hold);\n } else stack.push(hold);\n return hold;\n }\n\n /** Whether a hold above `hold` carries the element's value, which is then that hold's. */\n #covered(element: T, stack: readonly AttributeHold[], hold: AttributeHold): boolean {\n const current = element.getAttribute(this.#attribute);\n return stack.slice(stack.indexOf(hold) + 1).some((above) => above.written === current);\n }\n\n /** Leaves each owner's record of its topmost hold, or none where nothing needs one. */\n #mark(element: T, stack: readonly AttributeHold[]): void {\n const records = new Map<string, string | null>([[this.#record, null]]);\n stack.forEach((hold, index) => {\n const names = stack.slice(0, index).map((under) => under.record);\n const beneath = [...new Set(names.reverse())].filter((name) => name !== hold.record);\n const marker = hold.linked || beneath.length === 0 ? [] : [false];\n records.set(\n hold.record,\n hold.written === hold.original && beneath.length === 0 && index === stack.length - 1\n ? null\n : JSON.stringify([hold.original, hold.written, ...marker, ...beneath]),\n );\n });\n for (const [name, recorded] of records) {\n if (element.getAttribute(name) === recorded) continue;\n if (recorded === null) element.removeAttribute(name);\n else element.setAttribute(name, recorded);\n }\n }\n\n /** Reflects only a real value transition, avoiding self-triggered mutation work. */\n #reflect(element: T, value: string | null): void {\n if (element.getAttribute(this.#attribute) === value) return;\n if (value === null) element.removeAttribute(this.#attribute);\n else element.setAttribute(this.#attribute, value);\n }\n}\n","/**\n * Shared \"in-page move vs real detach\" discriminator for `disconnect()`.\n *\n * A mid-session `disconnect()` whose element is still in the document is\n * ambiguous. It can be:\n *\n * - an **in-page move** — a consumer (sortable, a teleport) re-inserted the\n * element; Stimulus reconnects the SAME instance in the same mutation batch\n * and the controller's runtime state must survive;\n * - a **detach that keeps the element** — `data-controller` no longer lists the\n * identifier (a Turbo 8 morph), or the element moved outside the observed\n * root (a scoped `Application.start(root)`, a shadow root); no reconnect will\n * ever come and the controller must tear down or it leaks its sessions,\n * document listeners, and moved nodes.\n *\n * Neither available signal decides this alone. A synchronous `data-controller`\n * token check catches the morph case but misses an observed-root exit (the\n * token stays, the reconnect never comes). Deferring teardown one microtask and\n * letting `connect()` cancel it catches the root exit, but leans on Stimulus\n * reconnecting moved elements within the same mutation batch — internal\n * behavior, not a public contract. {@link DetachGate} composes both: the token\n * check is the synchronous fast path (no probe, no reliance on batching), the\n * microtask probe is the fallback for the genuinely ambiguous remainder.\n *\n * An element out of the document is a real detach, with one exception: a\n * `data-turbo-permanent` element, or one inside it, that Turbo carries to the\n * next page. Turbo takes it out of the old `<body>`, which Stimulus answers with\n * a `disconnect()` while it is out of the document, and puts it into the new one\n * a few microtasks later in the same task, where the same instance connects\n * again. Such a disconnect is probed until the next task instead.\n *\n * Policy stays with the consumer; this util owns the discrimination only:\n *\n * - **Probe** ({@link disconnected}) when the teardown must eventually happen —\n * live drag sessions, document listeners, teleported *content*. Call\n * {@link cancel} from `connect()` (a reconnect means in-page move) and from\n * the head of any direct teardown path (see below).\n * - **Keep** (bare {@link DetachGate.isDetached}) when undoing the ambiguous\n * case would fight the controller's own effect: a controller that moves its\n * OWN element (portal's no-`content` form) exits a scoped observed root as\n * its normal job — a probe-driven teardown would restore the element into\n * the root, reconnect, re-teleport, and disconnect again, forever. Such\n * controllers keep their state on an ambiguous disconnect and tear down only\n * on {@link DetachGate.isDetached}.\n *\n * Event convention for probe consumers: teardown into a dead (detached) tree is\n * silent — consumers restore from their own `connect()`; a detach that keeps the\n * tree alive must end an in-flight session with a `cancel` event, or composing\n * consumers strand their session bookkeeping.\n */\n\n/** The slice of a Stimulus controller the gate inspects. */\nexport interface DetachGateHost {\n readonly element: Element;\n readonly identifier: string;\n}\n\n/**\n * Per-controller-instance gate deciding whether a `disconnect()` is a real\n * detach. Hold one per controller (`#gate = new DetachGate()`) — Stimulus\n * reuses the instance across reconnects, so the pending probe state carries\n * over exactly as the protocol needs.\n *\n * @example\n * ```ts\n * #gate = new DetachGate();\n *\n * connect(): void {\n * this.#gate.cancel(); // a reconnect: the element moved in-page\n * // …\n * }\n *\n * disconnect(): void {\n * this.#gate.disconnected(this, () => this.#teardown());\n * }\n *\n * dismiss(): void {\n * this.#gate.cancel(); // a direct path: disarm a probe a disconnect queued\n * this.#teardown();\n * }\n *\n * #teardown(): void {\n * // … (`disconnected` has already disarmed the probe on its own path)\n * }\n * ```\n */\nexport class DetachGate {\n /** Set while a probe is queued, waiting for a reconnect to cancel it. */\n #pending = false;\n\n /**\n * True while a probe is queued — the last disconnect was ambiguous and no\n * reconnect has cancelled it yet. Read it from `connect()` to tell the\n * reconnect half of an in-page move from a first connect: a controller whose\n * initialisation restarts a measurement (a min-duration floor, an elapsed\n * counter) must skip it for the move, where nothing actually restarted.\n */\n get pending(): boolean {\n return this.#pending;\n }\n\n /**\n * True when the disconnect is definitely a real detach — the element left\n * the document, or `data-controller` no longer lists the identifier. False\n * means ambiguous (in-page move or observed-root exit), NOT \"alive\".\n */\n static isDetached(host: DetachGateHost): boolean {\n return !host.element.isConnected || !DetachGate.#listed(host);\n }\n\n /**\n * Call from `disconnect()`: runs `teardown` synchronously on a definite\n * detach (fast path), otherwise defers it one microtask — a reconnect\n * ({@link cancel} from `connect()`) keeps the state, no reconnect runs it.\n * One microtask is the whole probe window: Stimulus reconnects a moved\n * element within the same mutation batch, before the checkpoint drains. An\n * element Turbo carries to the next page is probed until the next task, by\n * which time Turbo has put it back.\n */\n disconnected(host: DetachGateHost, teardown: () => void): void {\n const carried = DetachGate.#carried(host);\n if (!carried && DetachGate.isDetached(host)) {\n // Also disarms a probe a previous ambiguous disconnect left queued\n // (defer, element removed, disconnect again) — exactly one teardown.\n this.#pending = false;\n teardown();\n return;\n }\n this.#pending = true;\n const probe = (): void => {\n if (!this.#pending) return;\n this.#pending = false;\n teardown();\n };\n if (carried) setTimeout(probe, 0);\n else queueMicrotask(probe);\n }\n\n /**\n * Whether `host` is out of the document inside a `data-turbo-permanent` element, its\n * identifier still listed: where Turbo holds an element it carries to the next page.\n */\n static #carried(host: DetachGateHost): boolean {\n const element = host.element;\n return (\n !element.isConnected &&\n element.closest(\"[data-turbo-permanent]\") !== null &&\n DetachGate.#listed(host)\n );\n }\n\n /** Whether the element's `data-controller` still lists the identifier. */\n static #listed(host: DetachGateHost): boolean {\n const tokens = (host.element.getAttribute(\"data-controller\") ?? \"\").split(/\\s+/);\n return tokens.includes(host.identifier);\n }\n\n /**\n * Disarms a pending probe. Call from `connect()` (the reconnect that proves\n * an in-page move) and from the head of any teardown path not routed through\n * {@link disconnected} (disabled-toggle, Escape), so an orphaned probe can\n * never run the teardown a second time.\n */\n cancel(): void {\n this.#pending = false;\n }\n}\n","/**\n * Owns every listener a controller attaches for one connected lifetime and\n * releases them together, so an event name is spelled once and the release\n * cannot drift from the registration.\n *\n * Detaching by hand repeats three things at the release site — the type, the\n * handler reference and the capture flag — and any one of them drifting leaves\n * a listener alive on an element the controller no longer drives. Aborting the\n * signal a registration was created with releases it whatever it was made of,\n * so the release site names nothing.\n *\n * One set per lifetime, held as a field and never rebuilt. {@link\n * ListenerSet.dispose} is the only thing that closes a generation, and it opens\n * the next one in the same step, so the lifecycle hooks can call `add` and\n * `dispose` in any order any number of times, and no `add` ever meets an\n * already aborted signal — a DOM that registers a listener on such a signal\n * would keep it forever. Rebuilding the set instead would leak:\n * `addEventListener` matches an existing registration on target, type, callback\n * and capture alone, so re-adding the same handler while the first registration\n * is live is discarded, and that live registration keeps the signal it was\n * attached with — a set built by a second `connect()` can never release what it\n * appears to own.\n *\n * Scope is the registration only. Timers, observers, leases and the order they\n * are unwound in stay with the controller, which places `dispose()` in its\n * cleanup sequence. A receiver that changes at runtime (a\n * swapped target) is rebound by calling `dispose()` and then `add()` again from\n * the rebind, not by a second set.\n *\n * The set must own every registration of a tuple it holds: a listener the same\n * target already carries for the same type, callback and capture flag keeps the\n * right to remove it, and this set's `add` for that tuple is discarded. A\n * subscription shared between instances behind a participant count, or one that\n * lives for less than the connection, keeps its own pair of calls.\n *\n * @example\n * ```ts\n * readonly #listeners = new ListenerSet();\n *\n * connect(): void {\n * this.#listeners.add(this.element, \"submit\", this.#onNativeSubmit, { capture: true });\n * this.#listeners.add(this.element, \"turbo:submit-start\", this.#onSubmitStart);\n * this.#listeners.add(this.element, \"turbo:submit-end\", this.#onSubmitEnd);\n * }\n *\n * disconnect(): void {\n * this.#listeners.dispose();\n * }\n * ```\n */\nexport class ListenerSet {\n /** The generation every `add` joins until the next `dispose()`. */\n #abort = new AbortController();\n\n /**\n * Attaches `handler` to the open generation, exactly as the caller spelled it.\n *\n * The set supplies the signal, so `options` carries everything else the DOM\n * accepts — `capture` included, which has to match at release time and no\n * longer has a second place to drift from.\n */\n add(\n target: EventTarget,\n type: string,\n handler: EventListener,\n options?: Omit<AddEventListenerOptions, \"signal\">,\n ): void {\n target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });\n }\n\n /**\n * Releases every listener of the open generation, synchronously, and opens the\n * next one. Idempotent, and safe before anything has been added.\n */\n dispose(): void {\n this.#abort.abort();\n this.#abort = new AbortController();\n }\n}\n","/**\n * Largest delay a timer can hold: the platform stores it in a 32-bit signed\n * integer, and anything larger overflows to `1`, so a delay meant to be far in\n * the future fires almost immediately. A declared delay above this bound names\n * no delay at all, and a controller reading one falls back to its default.\n */\nexport const MAX_TIMER_DELAY_MS = 2_147_483_647;\n","import { MAX_TIMER_DELAY_MS } from \"./timer_bounds\";\n\n/** Semantic constraints on an already decoded number, independent of the DOM. */\nexport interface NumberBounds {\n /** Finite numbers are required, including when this field is omitted. */\n readonly finite?: true;\n /** Inclusive lower endpoint. */\n readonly min?: number;\n /** Inclusive upper endpoint. */\n readonly max?: number;\n /** Exclusive lower endpoint. */\n readonly exclusiveMin?: number;\n /** Require a number without a fractional part. */\n readonly integer?: boolean;\n /** Exact decoded numbers accepted by a discrete contract. */\n readonly allowedValues?: readonly number[];\n /** JSON-safe exceptions to finite-number checking for unbounded endpoints. */\n readonly allowInfinity?: \"negative\" | \"positive\" | \"both\";\n}\n\n/** One constraint for each Number Value, including shorthand declarations. */\nexport type NumberValueConstraints<Values> = {\n readonly [Key in keyof Values as Values[Key] extends\n | NumberConstructor\n | { type: NumberConstructor }\n ? Key\n : never]: NumberBounds;\n};\n\n/** Reusable semantic domains; fractional inputs remain fractional unless excluded. */\nexport const NUMBER_BOUNDS = {\n finite: { finite: true },\n nonNegative: { finite: true, min: 0 },\n positive: { finite: true, exclusiveMin: 0 },\n nonNegativeInteger: { finite: true, min: 0, integer: true },\n positiveInteger: { finite: true, exclusiveMin: 0, integer: true },\n lowerBound: { finite: true, allowInfinity: \"negative\" },\n upperBound: { finite: true, allowInfinity: \"positive\" },\n timer: { finite: true, min: 0, max: MAX_TIMER_DELAY_MS },\n positiveTimer: { finite: true, exclusiveMin: 0, max: MAX_TIMER_DELAY_MS },\n} as const satisfies Record<string, NumberBounds>;\n\n/**\n * Checks all declared bounds without rounding, clamping, or coercing the input.\n * Infinity exceptions relax only finite checking; every other bound still applies.\n */\nexport function matchesNumberBounds(value: number, bounds: NumberBounds): boolean {\n if (!Number.isFinite(value)) {\n const direction = value === Infinity ? \"positive\" : value === -Infinity ? \"negative\" : null;\n if (direction === null) return false;\n if (bounds.allowInfinity !== \"both\" && bounds.allowInfinity !== direction) return false;\n }\n if (bounds.min !== undefined && value < bounds.min) return false;\n if (bounds.max !== undefined && value > bounds.max) return false;\n if (bounds.exclusiveMin !== undefined && value <= bounds.exclusiveMin) return false;\n if (bounds.integer && !Number.isInteger(value)) return false;\n if (bounds.allowedValues !== undefined && !bounds.allowedValues.includes(value)) return false;\n return true;\n}\n\n/** Decodes a Number Value literal; action params use their own JSON decoder. */\nexport function decodeNumberValue(raw: string): number {\n return Number(raw.replace(/_/g, \"\"));\n}\n","/**\n * Numeric coercion for controllers that accept a number through a Value, an action\n * param, or an event detail.\n *\n * Stimulus decodes action params as JSON when possible and leaves other text\n * untouched. CustomEvent detail can also supply a number or numeric string.\n * The reader accepts those two types while rejecting blank and non-finite input.\n */\n\nimport { matchesNumberBounds, type NumberBounds } from \"./number_bounds\";\n\n/**\n * Reads a finite number or nonblank numeric string; other input returns `null`.\n * Blank strings cannot silently reset a value to zero.\n */\nexport function toFiniteNumber(raw: unknown): number | null {\n if (typeof raw !== \"number\" && typeof raw !== \"string\") return null;\n if (typeof raw === \"string\" && raw.trim().length === 0) return null;\n const value = Number(raw);\n return Number.isFinite(value) ? value : null;\n}\n\n/**\n * Returns an accepted decoded Number Value or the caller's fallback unchanged.\n * The caller owns the fallback contract; this reader does not parse or write a Value.\n */\nexport function readNumber(raw: number, fallback: number, bounds: NumberBounds): number {\n return matchesNumberBounds(raw, bounds) ? raw : fallback;\n}\n","import { readNumber } from \"./coerce\";\nimport { matchesNumberBounds, type NumberBounds } from \"./number_bounds\";\n\n/** The instance identity carried by a Number Value read. */\nexport interface NumberValueOwner {\n readonly identifier: string;\n readonly element: Element;\n}\n\n/** Reads current declarations at the point of use without rewriting their attributes. */\nexport class NumberValueReader {\n /** The last rejected literal per Value; no history grows for an unchanged instance. */\n readonly #lastRejected = new Map<string, string>();\n\n /** Resolves the declared number using its own fallback and shared class contract. */\n read(\n owner: NumberValueOwner,\n name: string,\n raw: number,\n fallback: number,\n bounds: NumberBounds,\n ): number {\n const resolved = readNumber(raw, fallback, bounds);\n if (matchesNumberBounds(raw, bounds)) {\n this.#lastRejected.delete(name);\n return resolved;\n }\n const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;\n const literal = owner.element.getAttribute(attribute);\n if (literal === null) {\n this.#lastRejected.delete(name);\n return resolved;\n }\n if (this.#lastRejected.get(name) !== literal) {\n this.#lastRejected.set(name, literal);\n console.warn(\n `Stimeo UI: \"${owner.identifier}\" has an invalid number Value \"${name}\" declaration ${JSON.stringify(literal)}; using ${fallback}.`,\n );\n }\n return resolved;\n }\n}\n","/**\n * Self-cleaning timer registries shared by Stimeo controllers.\n *\n * Stimulus controllers frequently schedule `setTimeout` / `setInterval` work\n * (auto-dismiss, debouncing, polling). When the element leaves the DOM — a\n * Turbo Drive navigation, a Turbo Stream replacement, or any `disconnect()` —\n * orphaned timers keep firing against a detached controller, leaking memory and\n * mutating stale state. {@link SafeTimeout} and {@link SafeInterval} track every\n * timer they create so a single {@link TimerRegistry.clearAll | clearAll()} call\n * in `disconnect()` tears them all down.\n *\n * These are intentionally low-level primitives: they own *registration and\n * cleanup only*. Higher-level policy (pause/resume, remaining-time accounting)\n * stays in the individual controllers so per-widget semantics are not flattened\n * into a lowest-common-denominator helper.\n */\n\nexport { MAX_TIMER_DELAY_MS } from \"./timer_bounds\";\n\n/**\n * Shared registry bookkeeping for the timeout/interval variants.\n *\n * Subclasses provide the scheduling primitive ({@link schedule}) and its matching\n * canceller ({@link cancel}); this base owns the set of live ids plus the\n * per-id and bulk teardown shared by both.\n */\nabstract class TimerRegistry {\n /** Live timer ids that have not yet been cleared (or, for timeouts, fired). */\n protected readonly ids = new Set<number>();\n\n /** Schedules the underlying platform timer and returns its id. */\n protected abstract schedule(callback: () => void, delay: number): number;\n\n /** Cancels the underlying platform timer for `id`. */\n protected abstract cancel(id: number): void;\n\n /**\n * Cancels a single tracked timer.\n *\n * No-ops if the id is unknown (already cleared, fired, or never owned by this\n * registry), so callers can clear defensively without guarding.\n */\n clear(id: number): void {\n if (this.ids.delete(id)) {\n this.cancel(id);\n }\n }\n\n /**\n * Cancels every tracked timer. Call this from a controller's `disconnect()`\n * to guarantee no timer outlives the element.\n */\n clearAll(): void {\n for (const id of this.ids) {\n this.cancel(id);\n }\n this.ids.clear();\n }\n\n /** Number of timers currently tracked (pending). */\n get size(): number {\n return this.ids.size;\n }\n}\n\n/**\n * `setTimeout` wrapper that auto-forgets each timer once it fires and supports\n * bulk teardown on disconnect.\n *\n * @example\n * ```ts\n * #timers = new SafeTimeout();\n *\n * connect() {\n * this.#timers.set(() => this.dismiss(), 5000);\n * }\n *\n * disconnect() {\n * this.#timers.clearAll();\n * }\n * ```\n */\nexport class SafeTimeout extends TimerRegistry {\n /**\n * Schedules `callback` after `delay` ms and returns the timer id.\n *\n * The id is removed from the registry automatically when the timeout fires,\n * so {@link TimerRegistry.size | size} reflects only still-pending timers.\n */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(() => {\n this.ids.delete(id);\n callback();\n }, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setTimeout(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearTimeout(id);\n }\n}\n\n/**\n * `setInterval` wrapper that tracks every interval for bulk teardown on\n * disconnect. Unlike {@link SafeTimeout}, intervals are retained until they are\n * explicitly cleared because they fire repeatedly.\n *\n * @example\n * ```ts\n * #intervals = new SafeInterval();\n *\n * connect() {\n * this.#intervals.set(() => this.tick(), 1000);\n * }\n *\n * disconnect() {\n * this.#intervals.clearAll();\n * }\n * ```\n */\nexport class SafeInterval extends TimerRegistry {\n /** Schedules a repeating `callback` every `delay` ms and returns the timer id. */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(callback, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setInterval(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearInterval(id);\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { announce } from \"../utils/announce\";\nimport { AttributeLease } from \"../utils/attribute_lease\";\nimport { DetachGate } from \"../utils/detach_gate\";\nimport { ListenerSet } from \"../utils/listener_set\";\nimport { NUMBER_BOUNDS, type NumberValueConstraints } from \"../utils/number_bounds\";\nimport { NumberValueReader } from \"../utils/number_value\";\nimport { SafeTimeout } from \"../utils/safe_timeout\";\n\n/** Native controls that can submit a form. */\ntype SubmitControl = HTMLButtonElement | HTMLInputElement;\n\n/** Why an active submission returned to idle. */\ntype CompletionReason = \"canceled\" | \"manual\" | \"timeout\" | \"turbo\";\n\n/** A text/value write that cannot be represented by an `AttributeLease`. */\ntype LabelWrite =\n | {\n readonly channel: \"text\";\n readonly control: HTMLButtonElement;\n readonly original: string;\n readonly written: string;\n }\n | {\n readonly channel: \"value\";\n readonly control: HTMLInputElement;\n readonly original: string;\n readonly written: string;\n };\n\n/** One form's independent in-flight submission. */\ninterface SubmissionSession {\n readonly form: HTMLFormElement;\n readonly submitter: SubmitControl | null;\n readonly submitterHadFocus: boolean;\n readonly controls: Set<SubmitControl>;\n timeoutId: number | null;\n labelWrite: LabelWrite | null;\n idlePart: HTMLElement | null;\n busyPart: HTMLElement | null;\n}\n\n/** Per-button override for the controller's default busy label. */\nconst BUTTON_BUSY_LABEL = \"data-submit-once-busy-label\";\n\n/** Suffix of the record of a label swapped through text or value; see the class remarks. */\nconst LABEL_RECORD = \"label\";\n\n/** Suffix of the mark on the control a running submission was sent from. */\nconst SUBMITTER_MARK = \"submitter\";\n\n/**\n * Headless form-scoped double-submit guard (identifier:\n * `stimeo--submit-once`). It disables every native submit control associated\n * with the submitting form, publishes busy state, and restores only the state\n * it still owns when the operation finishes.\n *\n * Turbo forms need no `data-action`: the controller observes\n * `turbo:submit-start` and `turbo:submit-end` itself.\n *\n * A non-Turbo async form owns its own lifecycle and declares each transition:\n * `submit->stimeo--submit-once#start` applies the busy state synchronously,\n * before the request is issued, and `#finish` / `#cancel` end it. Cancelling the\n * event's default is how such a form suppresses the native navigation, so it\n * never means the submission died — only `#cancel` does. A submit already\n * cancelled by an earlier listener never starts a session at all.\n *\n * ```html\n * <form data-controller=\"stimeo--submit-once\"\n * data-action=\"submit->stimeo--submit-once#start\n * save:complete->stimeo--submit-once#finish\n * save:error->stimeo--submit-once#cancel\">\n * <button type=\"submit\">Save</button>\n * </form>\n * ```\n *\n * A form whose submit a later listener vetoes needs `#cancel` wired to that\n * veto, or a non-zero `timeout`, to leave the busy state.\n *\n * Plain text buttons can use `busyLabel` (overridden by\n * `data-submit-once-busy-label`). A button with authored structure instead uses\n * an `idle` / `busy` target pair inside that button, so icons and descendants\n * are never replaced with `textContent`:\n *\n * ```html\n * <form data-controller=\"stimeo--submit-once\"\n * data-stimeo--submit-once-announce-text-value=\"Saving…\"\n * data-stimeo--submit-once-announce-ready-text-value=\"Saved.\">\n * <button type=\"submit\" data-stimeo--submit-once-target=\"submit\">\n * <span data-stimeo--submit-once-target=\"idle\">Save</span>\n * <span data-stimeo--submit-once-target=\"busy\" hidden>Saving…</span>\n * </button>\n * </form>\n * ```\n *\n * Events:\n * - `stimeo--submit-once:start` — `{ form, submitter }`.\n * - `stimeo--submit-once:reconcile` — `{ forms: HTMLFormElement[] }`, the forms a\n * connection found still marked `data-submitting` with no submission of its own\n * behind them: a page restored from the Turbo cache mid-submission, whose request\n * can no longer answer. `end` would claim the submission resolved.\n * - `stimeo--submit-once:end` — `{ form, submitter, reason, success? }`, where\n * `reason` is `\"turbo\"`, `\"timeout\"`, `\"manual\"`, or `\"canceled\"`. Only\n * `\"canceled\"` skips the completion announcement, because no operation ran.\n *\n * @remarks\n * Behavior only: the consumer owns every visual, label, and announcement\n * string. `announceText` / `announceReadyText` are empty by default and speak\n * through the page's shared `stimeo--announcer` only on real state transitions.\n * Attribute writes use `AttributeLease`, so a consumer mutation made\n * while busy wins over restoration, and the author's values travel with the elements.\n * A label swapped through the button's text or an input's value records the authored\n * and written labels on the control as `data-<identifier>-label`. A connection with no\n * submission of its own restores the authored label only while the written label remains,\n * so a restored copy loses busy wording without overwriting a consumer's change.\n * `DetachGate` preserves an in-flight session across an in-page move. A\n * submission is never abandoned on `turbo:before-cache`, which Turbo also dispatches on\n * pages that stay (a promoted frame navigation, a state-less `popstate`, a refresh of a\n * cached URL, a `data-turbo-permanent` element carried to the next page): the request\n * is still running there and the guard against a second submit has to hold until its\n * `turbo:submit-end`. Detach teardowns dispatch no `end`, announce nothing and never\n * move focus.\n */\nexport class SubmitOnceController extends Controller<HTMLElement> {\n /** Numeric read boundaries share one reader for this controller instance. */\n readonly #numbers = new NumberValueReader();\n\n static override targets = [\"submit\", \"idle\", \"busy\"];\n static override values = {\n announceText: { type: String, default: \"\" },\n announceReadyText: { type: String, default: \"\" },\n busyLabel: { type: String, default: \"\" },\n timeout: { type: Number, default: 0 },\n restoreFocus: { type: Boolean, default: false },\n };\n\n static valueConstraints = {\n timeout: NUMBER_BOUNDS.timer,\n } satisfies NumberValueConstraints<typeof SubmitOnceController.values>;\n static actions = [\"cancel\", \"finish\", \"start\"] as const;\n static events = [\"start\", \"end\", \"reconcile\"] as const;\n\n declare readonly submitTargets: SubmitControl[];\n declare readonly idleTargets: HTMLElement[];\n declare readonly busyTargets: HTMLElement[];\n\n declare announceTextValue: string;\n declare announceReadyTextValue: string;\n declare busyLabelValue: string;\n declare timeoutValue: number;\n declare restoreFocusValue: boolean;\n\n readonly #timers = new SafeTimeout();\n readonly #gate = new DetachGate();\n readonly #listeners = new ListenerSet();\n readonly #sessions = new Map<HTMLFormElement, SubmissionSession>();\n\n readonly #disabled = new AttributeLease<SubmitControl>(\"disabled\", this.identifier);\n readonly #controlBusy = new AttributeLease<SubmitControl>(\"aria-busy\", this.identifier);\n readonly #formBusy = new AttributeLease<HTMLFormElement>(\"aria-busy\", this.identifier);\n readonly #submitting = new AttributeLease<HTMLFormElement>(\"data-submitting\", this.identifier);\n readonly #ariaLabel = new AttributeLease<HTMLButtonElement>(\"aria-label\", this.identifier);\n readonly #hidden = new AttributeLease<HTMLElement>(\"hidden\", this.identifier);\n\n readonly #onSubmitStart = (event: Event): void => {\n this.start(event);\n };\n\n readonly #onSubmitEnd = (event: Event): void => {\n this.#complete(this.#eventForm(event), \"turbo\", this.#eventSuccess(event));\n };\n\n /** Blocks a later native submit while one for the same form is still active. */\n readonly #onNativeSubmit = (event: Event): void => {\n // The submit event's target is its form. This listener runs in capture so it\n // always observes that first event before a public `start` action creates the\n // session, while a later event sees the session and is canceled here.\n const session = this.#sessionFor(event.target);\n if (!session) return;\n this.#syncControls(session);\n event.preventDefault();\n };\n\n override connect(): void {\n this.#gate.cancel();\n this.#listeners.add(this.element, \"submit\", this.#onNativeSubmit, { capture: true });\n this.#listeners.add(this.element, \"turbo:submit-start\", this.#onSubmitStart);\n this.#listeners.add(this.element, \"turbo:submit-end\", this.#onSubmitEnd);\n if (this.#sessions.size === 0) this.#releaseInherited();\n }\n\n override disconnect(): void {\n this.#listeners.dispose();\n this.#gate.disconnected(this, () => this.#teardown());\n }\n\n /** The record of a label swapped through text or value, in this controller's namespace. */\n get #labelRecord(): string {\n return `data-${this.identifier}-${LABEL_RECORD}`;\n }\n\n /** The mark on the control a running submission was sent from. */\n get #submitterMark(): string {\n return `data-${this.identifier}-${SUBMITTER_MARK}`;\n }\n\n /**\n * Releases what a submission this instance is not running left behind: on a page\n * restored from Turbo's cache mid-submission, the labels it swapped come back from\n * their records, every leased attribute goes back to the author's value from its record,\n * and the forms that were still marked `data-submitting` are reported once as\n * `reconcile`.\n */\n #releaseInherited(): void {\n const forms = this.#ownedForms();\n const controls = new Set(\n this.element.querySelectorAll<SubmitControl>(`[${this.#labelRecord}]`),\n );\n for (const form of forms) {\n for (const control of this.#submitControls(form)) {\n if (control.hasAttribute(this.#labelRecord)) controls.add(control);\n }\n }\n for (const control of controls) {\n if (control.form && !this.#ownsForm(control.form)) continue;\n this.#restoreLabel(control);\n }\n const copied = forms.filter((form) => this.#returnCopied(form));\n if (copied.length > 0) this.dispatch(\"reconcile\", { detail: { forms: copied } });\n }\n\n /**\n * Gives back what a copy of `form` carries from a submission of an earlier instance, and\n * says whether it carried one. Turbo disables the submitter and marks the form\n * `aria-busy` before `turbo:submit-start`, and undoes both only when its request\n * finishes, which never happens on a copy: the control the submission marked as its\n * submitter is enabled again, in either of Turbo's submitter modes (`disabled`, or\n * `aria-disabled=\"true\"`), and the form's `aria-busy=\"true\"` goes.\n */\n #returnCopied(form: HTMLFormElement): boolean {\n let copied = this.#submitting.return(form);\n this.#formBusy.return(form);\n const parts = [...this.idleTargets, ...this.busyTargets];\n for (const control of this.#submitControls(form)) {\n this.#disabled.return(control);\n this.#controlBusy.return(control);\n if (control instanceof HTMLButtonElement) this.#ariaLabel.return(control);\n for (const part of parts) if (control.contains(part)) this.#hidden.return(part);\n if (!control.hasAttribute(this.#submitterMark)) continue;\n control.removeAttribute(this.#submitterMark);\n control.disabled = false;\n if (control.getAttribute(\"aria-disabled\") === \"true\")\n control.removeAttribute(\"aria-disabled\");\n copied = true;\n }\n if (copied && form.getAttribute(\"aria-busy\") === \"true\") form.removeAttribute(\"aria-busy\");\n return copied;\n }\n\n /** Returns a recorded label while its written value remains, and drops the record. */\n #restoreLabel(control: SubmitControl): void {\n const raw = control.getAttribute(this.#labelRecord);\n control.removeAttribute(this.#labelRecord);\n let record: unknown;\n try {\n record = JSON.parse(raw ?? \"\");\n } catch {\n return;\n }\n if (!Array.isArray(record) || record.length !== 2) return;\n const [original, written]: unknown[] = record;\n if (typeof original !== \"string\" || typeof written !== \"string\") return;\n if (control instanceof HTMLInputElement) {\n if (control.value === written) control.value = original;\n } else if (control.childElementCount === 0 && control.textContent === written) {\n control.textContent = original;\n }\n }\n\n /** The forms this controller owns: its own element, and the forms inside it. */\n #ownedForms(): HTMLFormElement[] {\n const forms = Array.from(this.element.querySelectorAll(\"form\"));\n if (this.element instanceof HTMLFormElement) forms.unshift(this.element);\n return forms.filter((form) => this.#ownsForm(form));\n }\n\n /**\n * Begins the submission represented by `event`.\n *\n * Native `submit` events are intended for non-Turbo async forms. Turbo calls\n * this path through its auto-subscribed `turbo:submit-start` event. An event\n * whose default an earlier listener already cancelled carries no submission,\n * so it starts nothing.\n */\n start(event: Event): void {\n if (event.defaultPrevented) return;\n const form = this.#eventForm(event);\n if (!form || this.#sessions.has(form)) return;\n\n const controls = this.#submitControls(form);\n const submitter = this.#resolveSubmitter(event, controls);\n const session: SubmissionSession = {\n form,\n submitter,\n submitterHadFocus: submitter !== null && document.activeElement === submitter,\n controls: new Set(),\n timeoutId: null,\n labelWrite: null,\n idlePart: null,\n busyPart: null,\n };\n this.#sessions.set(form, session);\n\n submitter?.setAttribute(this.#submitterMark, \"\");\n this.#formBusy.write(form, \"true\");\n this.#submitting.write(form, \"true\");\n this.#syncControls(session, controls);\n this.#enterLabel(session);\n this.dispatch(\"start\", { detail: { form, submitter } });\n\n if (this.#safeTimeout > 0) {\n session.timeoutId = this.#timers.set(\n () => this.#complete(form, \"timeout\", false),\n this.#safeTimeout,\n );\n }\n\n announce(this.announceTextValue);\n }\n\n /**\n * Completes a non-Turbo async submission for the form associated with\n * `event`. When called directly with no event, it completes the controller's\n * sole active form (or the controller element itself when it is a form).\n */\n finish(event?: Event): void {\n const form = event ? this.#eventForm(event) : this.#defaultForm();\n this.#complete(form, \"manual\", event ? this.#eventSuccess(event) : undefined);\n }\n\n /**\n * Abandons the submission for the form associated with `event` because it\n * never reached the network — a veto from validation, or a request the\n * consumer gave up before sending. With no event it targets the controller's\n * sole active form, exactly like {@link finish}.\n *\n * The distinction from `finish` is observable: a request that ran and failed\n * is `finish` with `success: false` and still announces completion, while a\n * submission that never happened announces nothing.\n */\n cancel(event?: Event): void {\n const form = event ? this.#eventForm(event) : this.#defaultForm();\n this.#complete(form, \"canceled\", false);\n }\n\n /** Disables a newly connected explicit submit target during its form's session. */\n submitTargetConnected(target: SubmitControl): void {\n const session = this.#sessionFor(target.form);\n if (!session || !this.#isNativeSubmitControl(target)) return;\n this.#enterControl(session, target);\n }\n\n /**\n * Ends one session, restores owned state, and reports the observable completion.\n *\n * @stimeoRuntimeOnly `announceReadyText` words the one announcement of this finished submission\n * and `restoreFocus` decides where its focus returns.\n */\n #complete(form: HTMLFormElement | null, reason: CompletionReason, success?: boolean): void {\n const session = this.#sessionFor(form);\n if (!session) return;\n this.#sessions.delete(session.form);\n this.#returnSessionState(session);\n\n const detail =\n success === undefined\n ? { form: session.form, submitter: session.submitter, reason }\n : { form: session.form, submitter: session.submitter, reason, success };\n this.dispatch(\"end\", { detail });\n this.#restoreFocus(session);\n if (reason !== \"canceled\") announce(this.announceReadyTextValue);\n }\n\n /** Applies the busy state to every current native/explicit control exactly once. */\n #syncControls(session: SubmissionSession, controls = this.#submitControls(session.form)): void {\n for (const control of controls) this.#enterControl(session, control);\n }\n\n /** Temporarily disables one enabled submit control while retaining authored state. */\n #enterControl(session: SubmissionSession, control: SubmitControl): void {\n if (session.controls.has(control) || control.matches(\":disabled\")) return;\n session.controls.add(control);\n this.#disabled.write(control, \"\");\n this.#controlBusy.write(control, \"true\");\n }\n\n /**\n * Swaps only the triggering control's safe label channel.\n *\n * @stimeoRuntimeOnly `busyLabel` is the label this one submission shows until it ends; the idle\n * label comes back with the state it returns.\n */\n #enterLabel(session: SubmissionSession): void {\n const control = session.submitter;\n if (!control) return;\n\n const idlePart = this.#partInside(control, this.idleTargets);\n const busyPart = this.#partInside(control, this.busyTargets);\n if (idlePart && busyPart) {\n session.idlePart = idlePart;\n session.busyPart = busyPart;\n this.#hidden.write(idlePart, \"\");\n this.#hidden.write(busyPart, null);\n }\n\n const label = control.getAttribute(BUTTON_BUSY_LABEL) ?? this.busyLabelValue;\n if (label.length === 0) return;\n if (control instanceof HTMLInputElement) {\n // `input[type=image]#value` is submitted data, not a visible label.\n if (control.type !== \"submit\") return;\n session.labelWrite = {\n channel: \"value\",\n control,\n original: control.value,\n written: label,\n };\n control.setAttribute(this.#labelRecord, JSON.stringify([control.value, label]));\n control.value = label;\n return;\n }\n if (control.hasAttribute(\"aria-label\")) {\n this.#ariaLabel.write(control, label);\n return;\n }\n // A structured button needs the explicit idle/busy pair above. Replacing its\n // textContent would destroy icons, counters, and application-owned listeners.\n if (control.childElementCount > 0) return;\n session.labelWrite = {\n channel: \"text\",\n control,\n original: control.textContent ?? \"\",\n written: label,\n };\n control.setAttribute(this.#labelRecord, JSON.stringify([control.textContent ?? \"\", label]));\n control.textContent = label;\n }\n\n /**\n * Returns labels, controls, and form hooks, respecting intervening consumer writes. The\n * submitter keeps its mark when `unfinished`: the submission is still running for Turbo,\n * which re-enables the submitter only when its request ends.\n */\n #returnSessionState(session: SubmissionSession, unfinished = false): void {\n if (session.timeoutId !== null) {\n this.#timers.clear(session.timeoutId);\n session.timeoutId = null;\n }\n\n if (session.labelWrite?.channel === \"value\") {\n const { control, original, written } = session.labelWrite;\n control.removeAttribute(this.#labelRecord);\n if (control.value === written) control.value = original;\n } else if (session.labelWrite?.channel === \"text\") {\n const { control, original, written } = session.labelWrite;\n control.removeAttribute(this.#labelRecord);\n if (control.childElementCount === 0 && control.textContent === written) {\n control.textContent = original;\n }\n }\n session.labelWrite = null;\n\n if (!unfinished) session.submitter?.removeAttribute(this.#submitterMark);\n if (session.submitter instanceof HTMLButtonElement) {\n this.#ariaLabel.return(session.submitter);\n }\n if (session.idlePart) this.#hidden.return(session.idlePart);\n if (session.busyPart) this.#hidden.return(session.busyPart);\n session.idlePart = null;\n session.busyPart = null;\n\n for (const control of session.controls) {\n this.#controlBusy.return(control);\n this.#disabled.return(control);\n }\n this.#submitting.return(session.form);\n this.#formBusy.return(session.form);\n }\n\n /** Restores focus only when disabling the focused submitter actually stranded it. */\n #restoreFocus(session: SubmissionSession): void {\n const submitter = session.submitter;\n if (!this.restoreFocusValue || !session.submitterHadFocus || !submitter?.isConnected) return;\n const active = document.activeElement;\n if (active === null || active === document.body || active === document.documentElement) {\n submitter.focus();\n }\n }\n\n /**\n * Silently abandons every session on a real detach. An element that left the document\n * leaves the submitter's mark in place, for the copy of the page Turbo may take after\n * this: its submission was never finished, and the connection that adopts the copy\n * enables what Turbo disabled for it.\n */\n #abandonSessions(): void {\n this.#timers.clearAll();\n const departed = !this.element.isConnected;\n for (const session of this.#sessions.values()) {\n session.timeoutId = null;\n this.#returnSessionState(session, departed);\n }\n this.#sessions.clear();\n }\n\n /** Tears down a true detach; an in-page move is canceled by the next `connect()`. */\n #teardown(): void {\n this.#abandonSessions();\n }\n\n /** All native controls associated with `form`, plus valid explicit targets. */\n #submitControls(form: HTMLFormElement): SubmitControl[] {\n const controls = new Set<SubmitControl>();\n for (const element of Array.from(form.elements)) {\n if (this.#isNativeSubmitControl(element)) controls.add(element);\n }\n // Browsers omit input[type=image] from form.elements. Search\n // the form's tree root as well so descendant and external `form=` image\n // submitters receive the same guard without broadening into sibling forms.\n const root = form.getRootNode() as ParentNode;\n for (const image of root.querySelectorAll<HTMLInputElement>('input[type=\"image\"]')) {\n if (image.form === form) controls.add(image);\n }\n return Array.from(controls);\n }\n\n /** Resolves the real submitter, falling back to the first enabled submit control. */\n #resolveSubmitter(event: Event, controls: SubmitControl[]): SubmitControl | null {\n const submitter = this.#eventSubmitter(event);\n if (submitter && controls.includes(submitter)) return submitter;\n return controls.find((control) => !control.matches(\":disabled\")) ?? controls[0] ?? null;\n }\n\n /** Reads a native or Turbo submitter without assuming `SubmitEvent` exists. */\n #eventSubmitter(event: Event): SubmitControl | null {\n if (typeof SubmitEvent !== \"undefined\" && event instanceof SubmitEvent) {\n return this.#isNativeSubmitControl(event.submitter) ? event.submitter : null;\n }\n const detail = (event as CustomEvent<{ formSubmission?: { submitter?: HTMLElement | null } }>)\n .detail;\n const submitter = detail?.formSubmission?.submitter;\n return this.#isNativeSubmitControl(submitter) ? submitter : null;\n }\n\n /** Finds the form represented by an action/Turbo event and verifies this scope owns it. */\n #eventForm(event: Event): HTMLFormElement | null {\n const detail = (\n event as CustomEvent<{\n form?: HTMLFormElement;\n formSubmission?: { formElement?: HTMLFormElement; submitter?: HTMLElement | null };\n }>\n ).detail;\n const target = event.target;\n const candidate =\n (target instanceof HTMLFormElement ? target : null) ??\n (target instanceof Element ? target.closest(\"form\") : null) ??\n detail?.formSubmission?.formElement ??\n detail?.form ??\n this.#eventSubmitter(event)?.form ??\n (this.element instanceof HTMLFormElement ? this.element : null);\n return candidate && this.#ownsForm(candidate) ? candidate : null;\n }\n\n /** The only unambiguous form for a direct `finish()` call. */\n #defaultForm(): HTMLFormElement | null {\n if (this.element instanceof HTMLFormElement && this.#ownsForm(this.element))\n return this.element;\n if (this.#sessions.size === 1) return this.#sessions.keys().next().value ?? null;\n return null;\n }\n\n /** Prevents an ancestor controller from also taking over a nested instance's form. */\n #ownsForm(form: HTMLFormElement): boolean {\n let candidate: Element | null = form;\n while (candidate) {\n const identifiers = (candidate.getAttribute(\"data-controller\") ?? \"\").split(/\\s+/);\n if (identifiers.includes(this.identifier)) return candidate === this.element;\n candidate = candidate.parentElement;\n }\n return false;\n }\n\n /** Returns the active session for a form-shaped external event value. */\n #sessionFor(candidate: unknown): SubmissionSession | undefined {\n if (!(candidate instanceof HTMLFormElement)) return undefined;\n return this.#sessions.get(candidate);\n }\n\n /** Effective native submit controls, including implicit buttons and image inputs. */\n #isNativeSubmitControl(element: unknown): element is SubmitControl {\n if (element instanceof HTMLButtonElement) return element.type === \"submit\";\n return (\n element instanceof HTMLInputElement && (element.type === \"submit\" || element.type === \"image\")\n );\n }\n\n /** Finds a structured label part belonging to this button, not a nested control. */\n #partInside(control: SubmitControl, parts: HTMLElement[]): HTMLElement | null {\n return (\n parts.find(\n (part) =>\n control.contains(part) &&\n part.closest(\"button, input[type=submit], input[type=image]\") === control,\n ) ?? null\n );\n }\n\n /** Reads an optional boolean `success` from Turbo or a manual completion event. */\n #eventSuccess(event: Event): boolean | undefined {\n const success = (event as CustomEvent<{ success?: unknown }>).detail?.success;\n return typeof success === \"boolean\" ? success : undefined;\n }\n /** Current `timeout` declaration resolved against its numeric contract. */\n get #safeTimeout(): number {\n return this.#numbers.read(\n this,\n \"timeout\",\n this.timeoutValue,\n SubmitOnceController.values.timeout.default,\n SubmitOnceController.valueConstraints.timeout,\n );\n }\n}\n"]}
@@ -15,6 +15,15 @@ import { Controller } from '@hotwired/stimulus';
15
15
  *
16
16
  * `change` and `reconcile` dispatch `{ checked: boolean }`.
17
17
  *
18
+ * User `change` reports compare the resulting state with the last published
19
+ * state. A pending page write handled in the same script joins that confirmation;
20
+ * a browser-delivered listener may settle it first as `reconcile`. Confirming
21
+ * the last published state reports nothing.
22
+ *
23
+ * A synchronous subscriber that confirms another state replaces reports still
24
+ * pending for the outer confirmation. Reading state or confirming it unchanged
25
+ * does not replace them. An event already being dispatched cannot be recalled.
26
+ *
18
27
  * @remarks
19
28
  * Behavior only — the consumer owns all styling (typically keyed off the
20
29
  * `[aria-checked="true"]` attribute). On a native `<button>` host, the browser
@@ -26,7 +35,7 @@ import { Controller } from '@hotwired/stimulus';
26
35
  *
27
36
  * Behavior provided:
28
37
  * - Click (or Space/Enter) toggles `aria-checked` between `"true"` and `"false"`.
29
- * - `stimeo--switch:change` is dispatched for every toggle the user makes; its
38
+ * - `stimeo--switch:change` reports a toggle that moves the published state; its
30
39
  * `detail.checked` carries the new boolean state.
31
40
  * - `stimeo--switch:reconcile` reports a checked state the page moved instead —
32
41
  * a retained-element morph that writes `aria-checked` or strips it (the state
@@ -55,6 +64,8 @@ declare class SwitchController extends Controller<HTMLElement> {
55
64
  connect(): void;
56
65
  /** Fills a form field inserted or replaced at runtime with the current state. */
57
66
  fieldTargetConnected(): void;
67
+ /** Brings the field that stays to the current state when an earlier one leaves. */
68
+ fieldTargetDisconnected(): void;
58
69
  /** Releases the click guard, the attribute observer, and a pass still queued. */
59
70
  disconnect(): void;
60
71
  /** Toggles the checked state. Bound via `data-action` (click). */