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,8 +1,5 @@
1
1
  import { Controller } from '@hotwired/stimulus';
2
2
 
3
- // src/controllers/frame_loading_controller.ts
4
-
5
- // src/utils/announce.ts
6
3
  function announce(message, options = {}) {
7
4
  const text = message.trim();
8
5
  if (text.length === 0) return;
@@ -19,141 +16,214 @@ function fillTemplate(template, values) {
19
16
  });
20
17
  }
21
18
 
22
- // src/utils/before_cache_reset.ts
23
- var BeforeCacheReset = class _BeforeCacheReset {
24
- /** Every subscribed instance, iterated by the one shared document listener. */
25
- static #subscribers = /* @__PURE__ */ new Set();
26
- /** The shared listener; installed while at least one instance is subscribed. */
27
- static #onBeforeCache = () => {
28
- for (const subscriber of _BeforeCacheReset.#subscribers) subscriber.#rewind();
29
- };
30
- #rewind;
31
- /** @param rewind - the pass that returns this controller's state to its initial form. */
32
- constructor(rewind) {
33
- this.#rewind = rewind;
34
- }
35
- /** Subscribes to `turbo:before-cache`; call from `connect()`. Idempotent. */
36
- activate() {
37
- const first = _BeforeCacheReset.#subscribers.size === 0;
38
- _BeforeCacheReset.#subscribers.add(this);
39
- if (first) {
40
- document.addEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
19
+ function sharedRegistry(key, create) {
20
+ const symbol = Symbol.for(key);
21
+ const scope = globalThis;
22
+ const existing = scope[symbol];
23
+ if (existing !== void 0) return existing;
24
+ const registry = create();
25
+ Object.defineProperty(globalThis, symbol, { value: registry });
26
+ return registry;
27
+ }
28
+
29
+ function isAttributeValue(value) {
30
+ return value === null || typeof value === "string";
31
+ }
32
+ function readLeaseRecord(element, record) {
33
+ try {
34
+ const parsed = JSON.parse(element.getAttribute(record) ?? "null");
35
+ if (!Array.isArray(parsed)) return void 0;
36
+ const [original, written, ...rest] = parsed;
37
+ const linked = rest[0] !== false;
38
+ const beneath = linked ? rest : rest.slice(1);
39
+ if (!beneath.every((under) => typeof under === "string")) return void 0;
40
+ return isAttributeValue(original) && isAttributeValue(written) ? { original, written, linked, beneath } : void 0;
41
+ } catch {
42
+ return void 0;
43
+ }
44
+ }
45
+ var AttributeLease = class _AttributeLease {
46
+ static #registry = sharedRegistry(
47
+ "stimeo-ui.attribute-lease.registry.v1",
48
+ () => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
49
+ );
50
+ #attribute;
51
+ #record;
52
+ #base;
53
+ #held = /* @__PURE__ */ new Set();
54
+ constructor(attribute, owner, options = {}) {
55
+ this.#attribute = attribute;
56
+ this.#record = `data-${owner}-${attribute}-lease`;
57
+ this.#base = options.base === true;
58
+ const names = _AttributeLease.#registry.names;
59
+ names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
60
+ }
61
+ write(element, value) {
62
+ const stack = this.#stack(element);
63
+ const hold = this.#take(stack) ?? this.#acquire(element, stack);
64
+ this.#held.add(element);
65
+ const shows = !this.#covered(element, stack, hold);
66
+ hold.written = value;
67
+ this.#mark(element, stack);
68
+ if (shows) this.#reflect(element, value);
69
+ }
70
+ return(element) {
71
+ const held = this.#held.delete(element);
72
+ const known = _AttributeLease.#registry.stacks.get(element)?.has(this.#attribute);
73
+ const stack = held || known || element.hasAttribute(this.#record) ? this.#stack(element) : [];
74
+ const hold = this.#take(stack);
75
+ if (!hold) return false;
76
+ const carries = element.getAttribute(this.#attribute) === hold.written;
77
+ const owns = carries && !this.#covered(element, stack, hold);
78
+ const index = stack.indexOf(hold);
79
+ stack.splice(index, 1);
80
+ const above = stack[index];
81
+ if (above) above.linked &&= hold.linked;
82
+ this.#mark(element, stack);
83
+ if (!owns) return true;
84
+ const below = hold.linked ? stack[index - 1] : void 0;
85
+ this.#reflect(element, below ? below.written : hold.original);
86
+ return true;
87
+ }
88
+ returnAll() {
89
+ for (const element of Array.from(this.#held)) this.return(element);
90
+ }
91
+ #stack(element) {
92
+ const stacks = _AttributeLease.#registry.stacks;
93
+ const byAttribute = stacks.get(element) ?? /* @__PURE__ */ new Map();
94
+ stacks.set(element, byAttribute);
95
+ const stack = byAttribute.get(this.#attribute) ?? this.#seed(element);
96
+ byAttribute.set(this.#attribute, stack);
97
+ return stack;
98
+ }
99
+ #seed(element) {
100
+ const found = [];
101
+ for (const record of _AttributeLease.#registry.names.get(this.#attribute) ?? []) {
102
+ const read = readLeaseRecord(element, record);
103
+ if (read) found.push({ ...read, record });
104
+ }
105
+ const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
106
+ return found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
107
+ lease: null,
108
+ record,
109
+ written,
110
+ original,
111
+ linked: linked && index > 0
112
+ }));
113
+ }
114
+ #take(stack) {
115
+ const hold = stack.find((held) => held.lease === this) ?? stack.find((held) => held.lease === null && held.record === this.#record);
116
+ if (hold) hold.lease = this;
117
+ return hold;
118
+ }
119
+ #acquire(element, stack) {
120
+ const current = element.getAttribute(this.#attribute);
121
+ const top = stack.at(-1);
122
+ const bottom = stack[0];
123
+ const over = top?.written === current ? top : void 0;
124
+ const from = this.#base ? bottom : over;
125
+ const hold = {
126
+ lease: this,
127
+ record: this.#record,
128
+ written: current,
129
+ original: from ? from.original : current,
130
+ linked: !this.#base && over !== void 0
131
+ };
132
+ if (this.#base && bottom) {
133
+ bottom.linked = true;
134
+ stack.unshift(hold);
135
+ } else stack.push(hold);
136
+ return hold;
137
+ }
138
+ #covered(element, stack, hold) {
139
+ const current = element.getAttribute(this.#attribute);
140
+ return stack.slice(stack.indexOf(hold) + 1).some((above) => above.written === current);
141
+ }
142
+ #mark(element, stack) {
143
+ const records = /* @__PURE__ */ new Map([[this.#record, null]]);
144
+ stack.forEach((hold, index) => {
145
+ const names = stack.slice(0, index).map((under) => under.record);
146
+ const beneath = [...new Set(names.reverse())].filter((name) => name !== hold.record);
147
+ const marker = hold.linked || beneath.length === 0 ? [] : [false];
148
+ records.set(
149
+ hold.record,
150
+ hold.written === hold.original && beneath.length === 0 && index === stack.length - 1 ? null : JSON.stringify([hold.original, hold.written, ...marker, ...beneath])
151
+ );
152
+ });
153
+ for (const [name, recorded] of records) {
154
+ if (element.getAttribute(name) === recorded) continue;
155
+ if (recorded === null) element.removeAttribute(name);
156
+ else element.setAttribute(name, recorded);
41
157
  }
42
158
  }
43
- /** Unsubscribes; call from `disconnect()`. Safe when never subscribed. */
44
- deactivate() {
45
- _BeforeCacheReset.#subscribers.delete(this);
46
- if (_BeforeCacheReset.#subscribers.size > 0) return;
47
- document.removeEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
159
+ #reflect(element, value) {
160
+ if (element.getAttribute(this.#attribute) === value) return;
161
+ if (value === null) element.removeAttribute(this.#attribute);
162
+ else element.setAttribute(this.#attribute, value);
48
163
  }
49
164
  };
50
165
 
51
- // src/utils/detach_gate.ts
52
166
  var DetachGate = class _DetachGate {
53
- /** Set while a probe is queued, waiting for a reconnect to cancel it. */
54
167
  #pending = false;
55
- /**
56
- * True while a probe is queued — the last disconnect was ambiguous and no
57
- * reconnect has cancelled it yet. Read it from `connect()` to tell the
58
- * reconnect half of an in-page move from a first connect: a controller whose
59
- * initialisation restarts a measurement (a min-duration floor, an elapsed
60
- * counter) must skip it for the move, where nothing actually restarted.
61
- */
62
168
  get pending() {
63
169
  return this.#pending;
64
170
  }
65
- /**
66
- * True when the disconnect is definitely a real detach — the element left
67
- * the document, or `data-controller` no longer lists the identifier. False
68
- * means ambiguous (in-page move or observed-root exit), NOT "alive".
69
- */
70
171
  static isDetached(host) {
71
- if (!host.element.isConnected) return true;
72
- const tokens = (host.element.getAttribute("data-controller") ?? "").split(/\s+/);
73
- return !tokens.includes(host.identifier);
74
- }
75
- /**
76
- * Call from `disconnect()`: runs `teardown` synchronously on a definite
77
- * detach (fast path), otherwise defers it one microtask — a reconnect
78
- * ({@link cancel} from `connect()`) keeps the state, no reconnect runs it.
79
- * One microtask is the whole probe window: Stimulus reconnects a moved
80
- * element within the same mutation batch, before the checkpoint drains.
81
- */
172
+ return !host.element.isConnected || !_DetachGate.#listed(host);
173
+ }
82
174
  disconnected(host, teardown) {
83
- if (_DetachGate.isDetached(host)) {
175
+ const carried = _DetachGate.#carried(host);
176
+ if (!carried && _DetachGate.isDetached(host)) {
84
177
  this.#pending = false;
85
178
  teardown();
86
179
  return;
87
180
  }
88
181
  this.#pending = true;
89
- queueMicrotask(() => {
182
+ const probe = () => {
90
183
  if (!this.#pending) return;
91
184
  this.#pending = false;
92
185
  teardown();
93
- });
186
+ };
187
+ if (carried) setTimeout(probe, 0);
188
+ else queueMicrotask(probe);
189
+ }
190
+ static #carried(host) {
191
+ const element = host.element;
192
+ return !element.isConnected && element.closest("[data-turbo-permanent]") !== null && _DetachGate.#listed(host);
193
+ }
194
+ static #listed(host) {
195
+ const tokens = (host.element.getAttribute("data-controller") ?? "").split(/\s+/);
196
+ return tokens.includes(host.identifier);
94
197
  }
95
- /**
96
- * Disarms a pending probe. Call from `connect()` (the reconnect that proves
97
- * an in-page move) and from the head of any teardown path not routed through
98
- * {@link disconnected} (disabled-toggle, Escape), so an orphaned probe can
99
- * never run the teardown a second time.
100
- */
101
198
  cancel() {
102
199
  this.#pending = false;
103
200
  }
104
201
  };
105
202
 
106
- // src/utils/listener_set.ts
107
203
  var ListenerSet = class {
108
- /** The generation every `add` joins until the next `dispose()`. */
109
204
  #abort = new AbortController();
110
- /**
111
- * Attaches `handler` to the open generation, exactly as the caller spelled it.
112
- *
113
- * The set supplies the signal, so `options` carries everything else the DOM
114
- * accepts — `capture` included, which has to match at release time and no
115
- * longer has a second place to drift from.
116
- */
117
205
  add(target, type, handler, options) {
118
206
  target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
119
207
  }
120
- /**
121
- * Releases every listener of the open generation, synchronously, and opens the
122
- * next one. Idempotent, and safe before anything has been added.
123
- */
124
208
  dispose() {
125
209
  this.#abort.abort();
126
210
  this.#abort = new AbortController();
127
211
  }
128
212
  };
129
213
 
130
- // src/utils/min_duration_floor.ts
131
214
  var MinDurationFloor = class {
132
215
  #timers;
133
- /** Pending finish timer id, or `null` when nothing is held back. */
134
216
  #timerId = null;
135
- /** Epoch ms the floor is measured from. */
136
217
  #since = 0;
137
- /** @param timers - the controller's registry; the floor schedules into it. */
138
218
  constructor(timers) {
139
219
  this.#timers = timers;
140
220
  }
141
- /** Starts the floor: call when the state being held becomes visible. */
142
221
  begin() {
143
222
  this.#since = Date.now();
144
223
  }
145
- /** True while a finish is held back waiting for the floor to elapse. */
146
224
  get pending() {
147
225
  return this.#timerId !== null;
148
226
  }
149
- /**
150
- * Runs `finish` once the floor has elapsed, immediately when it already has.
151
- *
152
- * A held-back finish is **replaced**, never stacked: only the most recently
153
- * queued id is cancellable, so a second timer would outlive every cancel and
154
- * end a state that has since restarted. Controllers that want the first signal
155
- * to win guard on {@link pending} before calling.
156
- */
157
227
  schedule(minDuration, finish) {
158
228
  this.cancel();
159
229
  const remaining = minDuration - (Date.now() - this.#since);
@@ -166,7 +236,6 @@ var MinDurationFloor = class {
166
236
  finish();
167
237
  }
168
238
  }
169
- /** Drops a held-back finish. Safe when none is queued, or after a bulk clear. */
170
239
  cancel() {
171
240
  if (this.#timerId !== null) {
172
241
  this.#timers.clear(this.#timerId);
@@ -175,43 +244,70 @@ var MinDurationFloor = class {
175
244
  }
176
245
  };
177
246
 
178
- // src/utils/safe_timeout.ts
247
+ var MAX_TIMER_DELAY_MS = 2147483647;
248
+
249
+ var NUMBER_BOUNDS = {
250
+ timer: { finite: true, min: 0, max: MAX_TIMER_DELAY_MS }};
251
+ function matchesNumberBounds(value, bounds) {
252
+ if (!Number.isFinite(value)) {
253
+ const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
254
+ if (direction === null) return false;
255
+ if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
256
+ }
257
+ if (bounds.min !== void 0 && value < bounds.min) return false;
258
+ if (bounds.max !== void 0 && value > bounds.max) return false;
259
+ if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
260
+ if (bounds.integer && !Number.isInteger(value)) return false;
261
+ if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
262
+ return true;
263
+ }
264
+
265
+ function readNumber(raw, fallback, bounds) {
266
+ return matchesNumberBounds(raw, bounds) ? raw : fallback;
267
+ }
268
+
269
+ var NumberValueReader = class {
270
+ #lastRejected = /* @__PURE__ */ new Map();
271
+ read(owner, name, raw, fallback, bounds) {
272
+ const resolved = readNumber(raw, fallback, bounds);
273
+ if (matchesNumberBounds(raw, bounds)) {
274
+ this.#lastRejected.delete(name);
275
+ return resolved;
276
+ }
277
+ const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
278
+ const literal = owner.element.getAttribute(attribute);
279
+ if (literal === null) {
280
+ this.#lastRejected.delete(name);
281
+ return resolved;
282
+ }
283
+ if (this.#lastRejected.get(name) !== literal) {
284
+ this.#lastRejected.set(name, literal);
285
+ console.warn(
286
+ `Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
287
+ );
288
+ }
289
+ return resolved;
290
+ }
291
+ };
292
+
179
293
  var TimerRegistry = class {
180
- /** Live timer ids that have not yet been cleared (or, for timeouts, fired). */
181
294
  ids = /* @__PURE__ */ new Set();
182
- /**
183
- * Cancels a single tracked timer.
184
- *
185
- * No-ops if the id is unknown (already cleared, fired, or never owned by this
186
- * registry), so callers can clear defensively without guarding.
187
- */
188
295
  clear(id) {
189
296
  if (this.ids.delete(id)) {
190
297
  this.cancel(id);
191
298
  }
192
299
  }
193
- /**
194
- * Cancels every tracked timer. Call this from a controller's `disconnect()`
195
- * to guarantee no timer outlives the element.
196
- */
197
300
  clearAll() {
198
301
  for (const id of this.ids) {
199
302
  this.cancel(id);
200
303
  }
201
304
  this.ids.clear();
202
305
  }
203
- /** Number of timers currently tracked (pending). */
204
306
  get size() {
205
307
  return this.ids.size;
206
308
  }
207
309
  };
208
310
  var SafeTimeout = class extends TimerRegistry {
209
- /**
210
- * Schedules `callback` after `delay` ms and returns the timer id.
211
- *
212
- * The id is removed from the registry automatically when the timeout fires,
213
- * so {@link TimerRegistry.size | size} reflects only still-pending timers.
214
- */
215
311
  set(callback, delay) {
216
312
  const id = this.schedule(() => {
217
313
  this.ids.delete(id);
@@ -228,8 +324,8 @@ var SafeTimeout = class extends TimerRegistry {
228
324
  }
229
325
  };
230
326
 
231
- // src/controllers/frame_loading_controller.ts
232
- var FrameLoadingController = class extends Controller {
327
+ var FrameLoadingController = class _FrameLoadingController extends Controller {
328
+ #numbers = new NumberValueReader();
233
329
  static targets = ["content", "skeleton", "overlay"];
234
330
  static values = {
235
331
  announceText: { type: String, default: "" },
@@ -237,147 +333,109 @@ var FrameLoadingController = class extends Controller {
237
333
  minDuration: { type: Number, default: 0 },
238
334
  restoreFocus: { type: Boolean, default: true }
239
335
  };
336
+ static valueConstraints = {
337
+ minDuration: NUMBER_BOUNDS.timer
338
+ };
240
339
  static events = ["start", "end", "reconcile"];
241
340
  #timeouts = new SafeTimeout();
242
341
  #floor = new MinDurationFloor(this.#timeouts);
243
342
  #gate = new DetachGate();
244
- #beforeCache = new BeforeCacheReset(() => this.#rewindForCache());
245
343
  #listeners = new ListenerSet();
344
+ #busy = new AttributeLease("aria-busy", this.identifier);
345
+ #loadingHook = new AttributeLease("data-frame-loading", this.identifier);
346
+ #hidden = new AttributeLease("hidden", this.identifier);
347
+ #inert = new AttributeLease("inert", this.identifier);
246
348
  #loading = false;
247
- /**
248
- * The optional targets this controller revealed, and the content it marked inert.
249
- * Held as references rather than re-resolved on the way out: a detach that keeps
250
- * the element takes the identifier off `data-controller` first, and a scope
251
- * without its identifier stops resolving targets — the elements to tidy would be
252
- * unreachable exactly when the tidying matters. They double as the ownership
253
- * marker, so a `hidden` or an `inert` the consumer wrote is never taken over.
254
- */
255
349
  #revealedSkeleton = null;
256
350
  #revealedOverlay = null;
257
351
  #inertTarget = null;
258
352
  #previousFocus = null;
259
- /** The id of the retreated element, used to re-find it if the load replaced it. */
260
353
  #previousFocusId = "";
261
354
  #onStart = () => {
262
355
  this.#floor.cancel();
263
356
  if (!this.#loading) this.#begin();
264
357
  };
265
- /**
266
- * @stimeoRuntimeOnly `minDuration` holds the loading state at least this long for the one load
267
- * that ended.
268
- */
269
358
  #onEnd = () => {
270
359
  if (!this.#loading) return;
271
- this.#floor.schedule(this.minDurationValue, () => this.#finish());
360
+ this.#floor.schedule(this.#safeMinDuration, () => this.#finish());
272
361
  };
273
362
  connect() {
274
363
  this.#gate.cancel();
275
- this.#beforeCache.activate();
276
364
  this.#listeners.add(this.element, "turbo:before-fetch-request", this.#onStart);
277
365
  this.#listeners.add(this.element, "turbo:frame-load", this.#onEnd);
278
366
  this.#listeners.add(this.element, "turbo:fetch-request-error", this.#onEnd);
367
+ if (this.#loading) return;
368
+ const stale = this.element.hasAttribute("data-frame-loading");
369
+ this.#returnCopied();
370
+ if (stale) this.dispatch("reconcile", { detail: {} });
371
+ }
372
+ #returnCopied() {
373
+ this.#busy.return(this.element);
374
+ this.#loadingHook.return(this.element);
375
+ for (const region of [...this.skeletonTargets, ...this.overlayTargets]) {
376
+ this.#hidden.return(region);
377
+ }
378
+ for (const content of this.contentTargets) this.#inert.return(content);
279
379
  }
280
380
  disconnect() {
281
381
  this.#listeners.dispose();
282
- this.#beforeCache.deactivate();
283
382
  this.#gate.disconnected(this, () => this.#teardown());
284
383
  }
285
- /**
286
- * Drops the held finish and the loading bookkeeping on a real detach, returning
287
- * the frame to its idle form. No reconnect is coming, so nothing is left that
288
- * could finish the load and clear the hooks — a detach that keeps the element
289
- * (a morph dropping the identifier, an exit from a scoped observed root) would
290
- * otherwise strand it busy and inert. Focus is left where it is: the element is
291
- * leaving this controller's care, and moving it now would be an unexplained jump.
292
- */
293
384
  #teardown() {
294
- this.#gate.cancel();
295
385
  this.#timeouts.clearAll();
296
- this.#floor.cancel();
297
386
  if (this.#loading) this.#rewindHooks();
298
387
  this.#loading = false;
299
388
  this.#previousFocus = null;
300
389
  }
301
- /**
302
- * Returns the frame to its idle form for the snapshot Turbo is about to take, so
303
- * a page reached with the Back button does not restore a frame that is busy and
304
- * inert with nothing left to finish it. State only — no `end` event and no focus
305
- * move, because the load did not actually complete.
306
- *
307
- * The load is abandoned rather than paused, so the flag and any finish the floor
308
- * still holds drop along with the hooks. A kept finish would surface after the
309
- * rewind as exactly the three things this pass exists to avoid — an `end`, a
310
- * completion announcement, and a focus move — and a kept flag would leave the
311
- * next fetch on a page that survives a cancelled visit skipping the loading
312
- * state, its idempotence guard already satisfied.
313
- */
314
- #rewindForCache() {
315
- if (!this.#loading) return;
316
- this.#loading = false;
317
- this.#floor.cancel();
318
- this.#rewindHooks();
319
- this.dispatch("reconcile", { detail: {} });
320
- }
321
- /**
322
- * Clears every hook the loading state writes. Shared by the three ways a load can
323
- * stop — completion, detach, snapshot — so none of them can drift into tidying
324
- * only part of it.
325
- */
326
390
  #rewindHooks() {
327
- this.element.removeAttribute("aria-busy");
328
- this.element.removeAttribute("data-frame-loading");
329
- if (this.#revealedSkeleton) this.#revealedSkeleton.hidden = true;
330
- if (this.#revealedOverlay) this.#revealedOverlay.hidden = true;
391
+ this.#busy.return(this.element);
392
+ this.#loadingHook.return(this.element);
393
+ if (this.#revealedSkeleton) this.#hidden.return(this.#revealedSkeleton);
394
+ if (this.#revealedOverlay) this.#hidden.return(this.#revealedOverlay);
331
395
  this.#revealedSkeleton = null;
332
396
  this.#revealedOverlay = null;
333
397
  this.#clearInert();
334
398
  }
335
- /**
336
- * Re-shows a `skeleton` that arrived mid-load. Turbo's frame renderer empties the
337
- * frame and re-inserts the response's children, so a response's authored (hidden)
338
- * skeleton can land while a later fetch is still running, and only the controller
339
- * knows the frame is still busy.
340
- */
341
399
  skeletonTargetConnected() {
342
400
  if (this.#loading) this.#revealSkeleton();
343
401
  }
344
- /** Re-shows an `overlay` that arrived mid-load — the same swap as the skeleton. */
402
+ skeletonTargetDisconnected(skeleton) {
403
+ if (this.#loading && !this.skeletonTargets.includes(skeleton)) this.#revealSkeleton();
404
+ }
345
405
  overlayTargetConnected() {
346
406
  if (this.#loading) this.#revealOverlay();
347
407
  }
348
- /**
349
- * Re-blocks a `content` that arrived mid-load, so the stale copy stays unusable.
350
- * The element that left is released first and ownership is then decided afresh, so
351
- * an `inert` the replacement authored stays the consumer's.
352
- */
408
+ overlayTargetDisconnected(overlay) {
409
+ if (this.#loading && !this.overlayTargets.includes(overlay)) this.#revealOverlay();
410
+ }
353
411
  contentTargetConnected() {
354
412
  if (!this.#loading) return;
355
413
  this.#clearInert();
356
414
  this.#applyInert();
357
415
  }
358
- /** Reveals the optional `skeleton`, noting it as this controller's to hide again. */
416
+ contentTargetDisconnected(content) {
417
+ if (!this.#loading || this.contentTargets.includes(content)) return;
418
+ this.#clearInert();
419
+ this.#applyInert();
420
+ }
359
421
  #revealSkeleton() {
360
- if (!this.hasSkeletonTarget) return;
361
- this.#revealedSkeleton = this.skeletonTarget;
362
- this.skeletonTarget.hidden = false;
422
+ const next = this.hasSkeletonTarget ? this.skeletonTarget : null;
423
+ this.#revealedSkeleton = this.#reveal(this.#revealedSkeleton, next);
363
424
  }
364
- /** Reveals the optional `overlay`, noting it as this controller's to hide again. */
365
425
  #revealOverlay() {
366
- if (!this.hasOverlayTarget) return;
367
- this.#revealedOverlay = this.overlayTarget;
368
- this.overlayTarget.hidden = false;
369
- }
370
- /**
371
- * Enters the loading state: hooks, skeleton/overlay, inert content, focus retreat.
372
- *
373
- * @stimeoRuntimeOnly `announceText` words the one announcement of this load and `restoreFocus`
374
- * decides whether focus leaves the frame it covers.
375
- */
426
+ const next = this.hasOverlayTarget ? this.overlayTarget : null;
427
+ this.#revealedOverlay = this.#reveal(this.#revealedOverlay, next);
428
+ }
429
+ #reveal(revealed, next) {
430
+ if (revealed && revealed !== next) this.#hidden.return(revealed);
431
+ if (next) this.#hidden.write(next, null);
432
+ return next;
433
+ }
376
434
  #begin() {
377
435
  this.#loading = true;
378
436
  this.#floor.begin();
379
- this.element.setAttribute("aria-busy", "true");
380
- this.element.setAttribute("data-frame-loading", "true");
437
+ this.#busy.write(this.element, "true");
438
+ this.#loadingHook.write(this.element, "true");
381
439
  this.#revealSkeleton();
382
440
  this.#revealOverlay();
383
441
  this.#applyInert();
@@ -385,12 +443,6 @@ var FrameLoadingController = class extends Controller {
385
443
  this.dispatch("start", { detail: {} });
386
444
  announce(fillTemplate(this.announceTextValue, {}));
387
445
  }
388
- /**
389
- * Leaves the loading state: restore hooks, hide skeleton/overlay, restore focus.
390
- *
391
- * @stimeoRuntimeOnly `announceReadyText` words the one announcement of this finished load and
392
- * `restoreFocus` decides whether focus goes back.
393
- */
394
446
  #finish() {
395
447
  this.#loading = false;
396
448
  this.#rewindHooks();
@@ -398,17 +450,15 @@ var FrameLoadingController = class extends Controller {
398
450
  this.dispatch("end", { detail: {} });
399
451
  announce(fillTemplate(this.announceReadyTextValue, {}));
400
452
  }
401
- /** Marks the content inert to block double-submits while stale (if we own it). */
402
453
  #applyInert() {
403
454
  if (!this.hasContentTarget || this.contentTarget.hasAttribute("inert")) return;
404
- this.contentTarget.setAttribute("inert", "");
455
+ this.#inert.write(this.contentTarget, "");
405
456
  this.#inertTarget = this.contentTarget;
406
457
  }
407
458
  #clearInert() {
408
- this.#inertTarget?.removeAttribute("inert");
459
+ if (this.#inertTarget) this.#inert.return(this.#inertTarget);
409
460
  this.#inertTarget = null;
410
461
  }
411
- /** Saves and blurs focus if it sits inside the frame about to go stale. */
412
462
  #retreatFocus() {
413
463
  this.#previousFocus = null;
414
464
  this.#previousFocusId = "";
@@ -420,13 +470,6 @@ var FrameLoadingController = class extends Controller {
420
470
  active.blur();
421
471
  }
422
472
  }
423
- /**
424
- * Restores focus after the load. The same node when it survived (e.g. a
425
- * non-replacing update), else the element re-rendered with the same id inside the
426
- * frame — Turbo frames typically re-emit the same controls. When neither is present
427
- * (an anonymous control was replaced) focus is left where the browser put it, to
428
- * avoid an unexpected jump (WCAG 3.2.x).
429
- */
430
473
  #restoreFocus() {
431
474
  const target = this.#previousFocus;
432
475
  const id = this.#previousFocusId;
@@ -442,8 +485,16 @@ var FrameLoadingController = class extends Controller {
442
485
  if (replacement && this.element.contains(replacement)) replacement.focus();
443
486
  }
444
487
  }
488
+ get #safeMinDuration() {
489
+ return this.#numbers.read(
490
+ this,
491
+ "minDuration",
492
+ this.minDurationValue,
493
+ _FrameLoadingController.values.minDuration.default,
494
+ _FrameLoadingController.valueConstraints.minDuration
495
+ );
496
+ }
445
497
  };
446
498
 
447
499
  export { FrameLoadingController };
448
- //# sourceMappingURL=frame_loading_controller.js.map
449
500
  //# sourceMappingURL=frame_loading_controller.js.map