stimeo-ui 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (337) hide show
  1. package/CHANGELOG.md +136 -0
  2. package/README.md +27 -11
  3. package/dist/cable/index.d.ts +88 -31
  4. package/dist/cable/index.js +384 -449
  5. package/dist/cable/index.js.map +1 -1
  6. package/dist/coerce-JtZVNG5h.d.ts +20 -0
  7. package/dist/controllers/accordion_controller.d.ts +4 -2
  8. package/dist/controllers/accordion_controller.js +174 -55
  9. package/dist/controllers/accordion_controller.js.map +1 -1
  10. package/dist/controllers/alert_dialog_controller.d.ts +12 -2
  11. package/dist/controllers/alert_dialog_controller.js +903 -235
  12. package/dist/controllers/alert_dialog_controller.js.map +1 -1
  13. package/dist/controllers/announcer_controller.d.ts +37 -5
  14. package/dist/controllers/announcer_controller.js +238 -205
  15. package/dist/controllers/announcer_controller.js.map +1 -1
  16. package/dist/controllers/aspect_ratio_controller.js +0 -10
  17. package/dist/controllers/aspect_ratio_controller.js.map +1 -1
  18. package/dist/controllers/auto_submit_controller.d.ts +30 -6
  19. package/dist/controllers/auto_submit_controller.js +414 -145
  20. package/dist/controllers/auto_submit_controller.js.map +1 -1
  21. package/dist/controllers/avatar_controller.js +185 -107
  22. package/dist/controllers/avatar_controller.js.map +1 -1
  23. package/dist/controllers/breadcrumb_controller.js +0 -130
  24. package/dist/controllers/breadcrumb_controller.js.map +1 -1
  25. package/dist/controllers/bulk_select_controller.d.ts +21 -3
  26. package/dist/controllers/bulk_select_controller.js +282 -58
  27. package/dist/controllers/bulk_select_controller.js.map +1 -1
  28. package/dist/controllers/calendar_controller.d.ts +38 -16
  29. package/dist/controllers/calendar_controller.js +228 -260
  30. package/dist/controllers/calendar_controller.js.map +1 -1
  31. package/dist/controllers/carousel_controller.d.ts +43 -12
  32. package/dist/controllers/carousel_controller.js +536 -436
  33. package/dist/controllers/carousel_controller.js.map +1 -1
  34. package/dist/controllers/character_counter_controller.d.ts +44 -7
  35. package/dist/controllers/character_counter_controller.js +493 -207
  36. package/dist/controllers/character_counter_controller.js.map +1 -1
  37. package/dist/controllers/checkbox_controller.d.ts +7 -2
  38. package/dist/controllers/checkbox_controller.js +38 -74
  39. package/dist/controllers/checkbox_controller.js.map +1 -1
  40. package/dist/controllers/clipboard_controller.d.ts +16 -5
  41. package/dist/controllers/clipboard_controller.js +261 -149
  42. package/dist/controllers/clipboard_controller.js.map +1 -1
  43. package/dist/controllers/collapsible_controller.d.ts +17 -5
  44. package/dist/controllers/collapsible_controller.js +304 -120
  45. package/dist/controllers/collapsible_controller.js.map +1 -1
  46. package/dist/controllers/color_picker_controller.d.ts +18 -6
  47. package/dist/controllers/color_picker_controller.js +178 -146
  48. package/dist/controllers/color_picker_controller.js.map +1 -1
  49. package/dist/controllers/combobox_controller.d.ts +24 -6
  50. package/dist/controllers/combobox_controller.js +522 -156
  51. package/dist/controllers/combobox_controller.js.map +1 -1
  52. package/dist/controllers/command_palette_controller.d.ts +51 -20
  53. package/dist/controllers/command_palette_controller.js +1156 -395
  54. package/dist/controllers/command_palette_controller.js.map +1 -1
  55. package/dist/controllers/conditional_fields_controller.d.ts +15 -2
  56. package/dist/controllers/conditional_fields_controller.js +300 -171
  57. package/dist/controllers/conditional_fields_controller.js.map +1 -1
  58. package/dist/controllers/confirm_controller.d.ts +49 -12
  59. package/dist/controllers/confirm_controller.js +980 -262
  60. package/dist/controllers/confirm_controller.js.map +1 -1
  61. package/dist/controllers/context_menu_controller.d.ts +80 -18
  62. package/dist/controllers/context_menu_controller.js +346 -153
  63. package/dist/controllers/context_menu_controller.js.map +1 -1
  64. package/dist/controllers/count_up_controller.d.ts +13 -1
  65. package/dist/controllers/count_up_controller.js +73 -21
  66. package/dist/controllers/count_up_controller.js.map +1 -1
  67. package/dist/controllers/countdown_controller.d.ts +47 -3
  68. package/dist/controllers/countdown_controller.js +281 -158
  69. package/dist/controllers/countdown_controller.js.map +1 -1
  70. package/dist/controllers/currency_input_controller.d.ts +93 -10
  71. package/dist/controllers/currency_input_controller.js +575 -225
  72. package/dist/controllers/currency_input_controller.js.map +1 -1
  73. package/dist/controllers/data_grid_controller.d.ts +25 -12
  74. package/dist/controllers/data_grid_controller.js +248 -214
  75. package/dist/controllers/data_grid_controller.js.map +1 -1
  76. package/dist/controllers/date_range_picker_controller.d.ts +42 -14
  77. package/dist/controllers/date_range_picker_controller.js +338 -235
  78. package/dist/controllers/date_range_picker_controller.js.map +1 -1
  79. package/dist/controllers/dialog_controller.d.ts +17 -6
  80. package/dist/controllers/dialog_controller.js +899 -237
  81. package/dist/controllers/dialog_controller.js.map +1 -1
  82. package/dist/controllers/direct_upload_controller.d.ts +25 -8
  83. package/dist/controllers/direct_upload_controller.js +97 -167
  84. package/dist/controllers/direct_upload_controller.js.map +1 -1
  85. package/dist/controllers/dirty_form_controller.d.ts +6 -1
  86. package/dist/controllers/dirty_form_controller.js +72 -69
  87. package/dist/controllers/dirty_form_controller.js.map +1 -1
  88. package/dist/controllers/dismissible_controller.d.ts +19 -0
  89. package/dist/controllers/dismissible_controller.js +196 -24
  90. package/dist/controllers/dismissible_controller.js.map +1 -1
  91. package/dist/controllers/drawer_controller.d.ts +39 -11
  92. package/dist/controllers/drawer_controller.js +974 -355
  93. package/dist/controllers/drawer_controller.js.map +1 -1
  94. package/dist/controllers/dropdown_controller.d.ts +31 -1
  95. package/dist/controllers/dropdown_controller.js +339 -96
  96. package/dist/controllers/dropdown_controller.js.map +1 -1
  97. package/dist/controllers/editable_controller.d.ts +12 -1
  98. package/dist/controllers/editable_controller.js +315 -88
  99. package/dist/controllers/editable_controller.js.map +1 -1
  100. package/dist/controllers/empty_state_controller.js +79 -58
  101. package/dist/controllers/empty_state_controller.js.map +1 -1
  102. package/dist/controllers/file_dropzone_controller.d.ts +63 -18
  103. package/dist/controllers/file_dropzone_controller.js +322 -211
  104. package/dist/controllers/file_dropzone_controller.js.map +1 -1
  105. package/dist/controllers/filter_controller.js +77 -42
  106. package/dist/controllers/filter_controller.js.map +1 -1
  107. package/dist/controllers/flash_controller.d.ts +35 -12
  108. package/dist/controllers/flash_controller.js +238 -347
  109. package/dist/controllers/flash_controller.js.map +1 -1
  110. package/dist/controllers/focus_controller.d.ts +15 -6
  111. package/dist/controllers/focus_controller.js +679 -240
  112. package/dist/controllers/focus_controller.js.map +1 -1
  113. package/dist/controllers/form_field_controller.js +156 -132
  114. package/dist/controllers/form_field_controller.js.map +1 -1
  115. package/dist/controllers/form_validation_controller.d.ts +3 -3
  116. package/dist/controllers/form_validation_controller.js +31 -97
  117. package/dist/controllers/form_validation_controller.js.map +1 -1
  118. package/dist/controllers/frame_loading_controller.d.ts +47 -14
  119. package/dist/controllers/frame_loading_controller.js +275 -217
  120. package/dist/controllers/frame_loading_controller.js.map +1 -1
  121. package/dist/controllers/highlight_controller.d.ts +27 -6
  122. package/dist/controllers/highlight_controller.js +107 -75
  123. package/dist/controllers/highlight_controller.js.map +1 -1
  124. package/dist/controllers/hover_card_controller.d.ts +60 -4
  125. package/dist/controllers/hover_card_controller.js +327 -130
  126. package/dist/controllers/hover_card_controller.js.map +1 -1
  127. package/dist/controllers/idle_controller.d.ts +48 -6
  128. package/dist/controllers/idle_controller.js +271 -101
  129. package/dist/controllers/idle_controller.js.map +1 -1
  130. package/dist/controllers/input_mask_controller.d.ts +30 -13
  131. package/dist/controllers/input_mask_controller.js +483 -97
  132. package/dist/controllers/input_mask_controller.js.map +1 -1
  133. package/dist/controllers/intersection_controller.d.ts +32 -13
  134. package/dist/controllers/intersection_controller.js +150 -106
  135. package/dist/controllers/intersection_controller.js.map +1 -1
  136. package/dist/controllers/lazy_frame_controller.d.ts +19 -2
  137. package/dist/controllers/lazy_frame_controller.js +24 -68
  138. package/dist/controllers/lazy_frame_controller.js.map +1 -1
  139. package/dist/controllers/listbox_controller.d.ts +44 -9
  140. package/dist/controllers/listbox_controller.js +523 -240
  141. package/dist/controllers/listbox_controller.js.map +1 -1
  142. package/dist/controllers/local_time_controller.d.ts +9 -7
  143. package/dist/controllers/local_time_controller.js +102 -77
  144. package/dist/controllers/local_time_controller.js.map +1 -1
  145. package/dist/controllers/masonry_controller.d.ts +11 -3
  146. package/dist/controllers/masonry_controller.js +80 -96
  147. package/dist/controllers/masonry_controller.js.map +1 -1
  148. package/dist/controllers/menu_controller.d.ts +55 -11
  149. package/dist/controllers/menu_controller.js +302 -188
  150. package/dist/controllers/menu_controller.js.map +1 -1
  151. package/dist/controllers/menubar_controller.d.ts +51 -9
  152. package/dist/controllers/menubar_controller.js +232 -345
  153. package/dist/controllers/menubar_controller.js.map +1 -1
  154. package/dist/controllers/meter_controller.d.ts +33 -20
  155. package/dist/controllers/meter_controller.js +417 -87
  156. package/dist/controllers/meter_controller.js.map +1 -1
  157. package/dist/controllers/multi_select_controller.d.ts +48 -18
  158. package/dist/controllers/multi_select_controller.js +785 -393
  159. package/dist/controllers/multi_select_controller.js.map +1 -1
  160. package/dist/controllers/navigation_menu_controller.d.ts +27 -6
  161. package/dist/controllers/navigation_menu_controller.js +190 -250
  162. package/dist/controllers/navigation_menu_controller.js.map +1 -1
  163. package/dist/controllers/nested_form_controller.d.ts +10 -0
  164. package/dist/controllers/nested_form_controller.js +396 -192
  165. package/dist/controllers/nested_form_controller.js.map +1 -1
  166. package/dist/controllers/network_status_controller.d.ts +23 -0
  167. package/dist/controllers/network_status_controller.js +268 -62
  168. package/dist/controllers/network_status_controller.js.map +1 -1
  169. package/dist/controllers/number_input_controller.d.ts +50 -8
  170. package/dist/controllers/number_input_controller.js +610 -303
  171. package/dist/controllers/number_input_controller.js.map +1 -1
  172. package/dist/controllers/optimistic_controller.d.ts +8 -5
  173. package/dist/controllers/optimistic_controller.js +45 -87
  174. package/dist/controllers/optimistic_controller.js.map +1 -1
  175. package/dist/controllers/otp_controller.d.ts +55 -8
  176. package/dist/controllers/otp_controller.js +658 -255
  177. package/dist/controllers/otp_controller.js.map +1 -1
  178. package/dist/controllers/overflow_indicator_controller.d.ts +5 -0
  179. package/dist/controllers/overflow_indicator_controller.js +122 -113
  180. package/dist/controllers/overflow_indicator_controller.js.map +1 -1
  181. package/dist/controllers/overflow_menu_controller.d.ts +58 -20
  182. package/dist/controllers/overflow_menu_controller.js +470 -295
  183. package/dist/controllers/overflow_menu_controller.js.map +1 -1
  184. package/dist/controllers/pagination_controller.d.ts +37 -7
  185. package/dist/controllers/pagination_controller.js +332 -123
  186. package/dist/controllers/pagination_controller.js.map +1 -1
  187. package/dist/controllers/password_reveal_controller.d.ts +46 -8
  188. package/dist/controllers/password_reveal_controller.js +392 -121
  189. package/dist/controllers/password_reveal_controller.js.map +1 -1
  190. package/dist/controllers/password_strength_controller.d.ts +15 -6
  191. package/dist/controllers/password_strength_controller.js +111 -158
  192. package/dist/controllers/password_strength_controller.js.map +1 -1
  193. package/dist/controllers/persist_controller.d.ts +11 -4
  194. package/dist/controllers/persist_controller.js +165 -111
  195. package/dist/controllers/persist_controller.js.map +1 -1
  196. package/dist/controllers/pointer_drag_controller.d.ts +35 -12
  197. package/dist/controllers/pointer_drag_controller.js +434 -188
  198. package/dist/controllers/pointer_drag_controller.js.map +1 -1
  199. package/dist/controllers/popover_controller.d.ts +41 -5
  200. package/dist/controllers/popover_controller.js +269 -113
  201. package/dist/controllers/popover_controller.js.map +1 -1
  202. package/dist/controllers/portal_controller.d.ts +44 -4
  203. package/dist/controllers/portal_controller.js +215 -76
  204. package/dist/controllers/portal_controller.js.map +1 -1
  205. package/dist/controllers/preview_guard_controller.d.ts +10 -4
  206. package/dist/controllers/preview_guard_controller.js +243 -116
  207. package/dist/controllers/preview_guard_controller.js.map +1 -1
  208. package/dist/controllers/progress_controller.d.ts +20 -19
  209. package/dist/controllers/progress_controller.js +391 -78
  210. package/dist/controllers/progress_controller.js.map +1 -1
  211. package/dist/controllers/radio_group_controller.d.ts +21 -9
  212. package/dist/controllers/radio_group_controller.js +278 -211
  213. package/dist/controllers/radio_group_controller.js.map +1 -1
  214. package/dist/controllers/range_slider_controller.d.ts +47 -6
  215. package/dist/controllers/range_slider_controller.js +220 -113
  216. package/dist/controllers/range_slider_controller.js.map +1 -1
  217. package/dist/controllers/rating_controller.d.ts +20 -4
  218. package/dist/controllers/rating_controller.js +458 -211
  219. package/dist/controllers/rating_controller.js.map +1 -1
  220. package/dist/controllers/read_more_controller.d.ts +2 -0
  221. package/dist/controllers/read_more_controller.js +138 -63
  222. package/dist/controllers/read_more_controller.js.map +1 -1
  223. package/dist/controllers/reading_progress_controller.d.ts +1 -1
  224. package/dist/controllers/reading_progress_controller.js +166 -137
  225. package/dist/controllers/reading_progress_controller.js.map +1 -1
  226. package/dist/controllers/relative_time_controller.d.ts +20 -6
  227. package/dist/controllers/relative_time_controller.js +211 -112
  228. package/dist/controllers/relative_time_controller.js.map +1 -1
  229. package/dist/controllers/reset_on_restore_controller.d.ts +85 -0
  230. package/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +103 -38
  231. package/dist/controllers/reset_on_restore_controller.js.map +1 -0
  232. package/dist/controllers/resizable_controller.d.ts +31 -2
  233. package/dist/controllers/resizable_controller.js +299 -150
  234. package/dist/controllers/resizable_controller.js.map +1 -1
  235. package/dist/controllers/roving_controller.d.ts +3 -3
  236. package/dist/controllers/roving_controller.js +49 -88
  237. package/dist/controllers/roving_controller.js.map +1 -1
  238. package/dist/controllers/scroll_area_controller.d.ts +7 -3
  239. package/dist/controllers/scroll_area_controller.js +490 -246
  240. package/dist/controllers/scroll_area_controller.js.map +1 -1
  241. package/dist/controllers/scroll_restore_controller.d.ts +3 -3
  242. package/dist/controllers/scroll_restore_controller.js +14 -55
  243. package/dist/controllers/scroll_restore_controller.js.map +1 -1
  244. package/dist/controllers/scroll_visibility_controller.d.ts +28 -12
  245. package/dist/controllers/scroll_visibility_controller.js +432 -149
  246. package/dist/controllers/scroll_visibility_controller.js.map +1 -1
  247. package/dist/controllers/scrollspy_controller.d.ts +6 -1
  248. package/dist/controllers/scrollspy_controller.js +193 -248
  249. package/dist/controllers/scrollspy_controller.js.map +1 -1
  250. package/dist/controllers/separator_controller.d.ts +20 -5
  251. package/dist/controllers/separator_controller.js +285 -142
  252. package/dist/controllers/separator_controller.js.map +1 -1
  253. package/dist/controllers/sidebar_controller.d.ts +65 -8
  254. package/dist/controllers/sidebar_controller.js +1069 -404
  255. package/dist/controllers/sidebar_controller.js.map +1 -1
  256. package/dist/controllers/skeleton_controller.d.ts +23 -0
  257. package/dist/controllers/skeleton_controller.js +259 -101
  258. package/dist/controllers/skeleton_controller.js.map +1 -1
  259. package/dist/controllers/slider_controller.d.ts +29 -2
  260. package/dist/controllers/slider_controller.js +187 -86
  261. package/dist/controllers/slider_controller.js.map +1 -1
  262. package/dist/controllers/smart_sticky_header_controller.d.ts +22 -4
  263. package/dist/controllers/smart_sticky_header_controller.js +173 -66
  264. package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
  265. package/dist/controllers/sortable_controller.d.ts +4 -4
  266. package/dist/controllers/sortable_controller.js +26 -125
  267. package/dist/controllers/sortable_controller.js.map +1 -1
  268. package/dist/controllers/spinner_controller.d.ts +39 -4
  269. package/dist/controllers/spinner_controller.js +288 -173
  270. package/dist/controllers/spinner_controller.js.map +1 -1
  271. package/dist/controllers/step_indicator_controller.d.ts +9 -4
  272. package/dist/controllers/step_indicator_controller.js +105 -46
  273. package/dist/controllers/step_indicator_controller.js.map +1 -1
  274. package/dist/controllers/stepper_controller.d.ts +14 -6
  275. package/dist/controllers/stepper_controller.js +120 -73
  276. package/dist/controllers/stepper_controller.js.map +1 -1
  277. package/dist/controllers/stick_to_bottom_controller.d.ts +9 -1
  278. package/dist/controllers/stick_to_bottom_controller.js +275 -140
  279. package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
  280. package/dist/controllers/sticky_observer_controller.d.ts +11 -0
  281. package/dist/controllers/sticky_observer_controller.js +220 -49
  282. package/dist/controllers/sticky_observer_controller.js.map +1 -1
  283. package/dist/controllers/submit_once_controller.d.ts +24 -8
  284. package/dist/controllers/submit_once_controller.js +376 -227
  285. package/dist/controllers/submit_once_controller.js.map +1 -1
  286. package/dist/controllers/switch_controller.d.ts +18 -4
  287. package/dist/controllers/switch_controller.js +201 -84
  288. package/dist/controllers/switch_controller.js.map +1 -1
  289. package/dist/controllers/tabs_controller.d.ts +9 -6
  290. package/dist/controllers/tabs_controller.js +41 -30
  291. package/dist/controllers/tabs_controller.js.map +1 -1
  292. package/dist/controllers/tags_input_controller.d.ts +16 -3
  293. package/dist/controllers/tags_input_controller.js +293 -173
  294. package/dist/controllers/tags_input_controller.js.map +1 -1
  295. package/dist/controllers/textarea_autosize_controller.d.ts +11 -1
  296. package/dist/controllers/textarea_autosize_controller.js +108 -74
  297. package/dist/controllers/textarea_autosize_controller.js.map +1 -1
  298. package/dist/controllers/theme_controller.d.ts +24 -11
  299. package/dist/controllers/theme_controller.js +140 -116
  300. package/dist/controllers/theme_controller.js.map +1 -1
  301. package/dist/controllers/time_picker_controller.d.ts +55 -5
  302. package/dist/controllers/time_picker_controller.js +331 -127
  303. package/dist/controllers/time_picker_controller.js.map +1 -1
  304. package/dist/controllers/toast_controller.d.ts +40 -16
  305. package/dist/controllers/toast_controller.js +244 -324
  306. package/dist/controllers/toast_controller.js.map +1 -1
  307. package/dist/controllers/toggle_group_controller.d.ts +14 -6
  308. package/dist/controllers/toggle_group_controller.js +252 -191
  309. package/dist/controllers/toggle_group_controller.js.map +1 -1
  310. package/dist/controllers/toolbar_controller.d.ts +8 -6
  311. package/dist/controllers/toolbar_controller.js +219 -120
  312. package/dist/controllers/toolbar_controller.js.map +1 -1
  313. package/dist/controllers/tooltip_controller.d.ts +49 -4
  314. package/dist/controllers/tooltip_controller.js +158 -124
  315. package/dist/controllers/tooltip_controller.js.map +1 -1
  316. package/dist/controllers/transition_controller.d.ts +22 -4
  317. package/dist/controllers/transition_controller.js +123 -119
  318. package/dist/controllers/transition_controller.js.map +1 -1
  319. package/dist/controllers/tree_view_controller.d.ts +22 -12
  320. package/dist/controllers/tree_view_controller.js +283 -369
  321. package/dist/controllers/tree_view_controller.js.map +1 -1
  322. package/dist/index.d.ts +10 -5
  323. package/dist/index.js +8573 -10410
  324. package/dist/index.js.map +1 -1
  325. package/dist/inspector/cli.d.ts +38 -14
  326. package/dist/inspector/cli.js +108 -40
  327. package/dist/inspector/cli.js.map +1 -1
  328. package/dist/inspector/cli_bin.js +145 -64
  329. package/dist/inspector/cli_bin.js.map +1 -1
  330. package/dist/inspector/examples.json +20 -20
  331. package/dist/inspector/manifest.json +802 -101
  332. package/dist/positioning/index.d.ts +15 -7
  333. package/dist/positioning/index.js +154 -47
  334. package/dist/positioning/index.js.map +1 -1
  335. package/package.json +11 -10
  336. package/dist/controllers/reset_before_cache_controller.d.ts +0 -76
  337. package/dist/controllers/reset_before_cache_controller.js.map +0 -1
@@ -1,8 +1,12 @@
1
1
  import { Controller } from '@hotwired/stimulus';
2
2
 
3
- // src/controllers/countdown_controller.ts
3
+ function fillTemplate(template, values) {
4
+ return template.replace(/\{([a-zA-Z][a-zA-Z0-9]*)\}/g, (match, name) => {
5
+ const replacement = Object.hasOwn(values, name) ? values[name] : void 0;
6
+ return replacement === void 0 ? match : String(replacement);
7
+ });
8
+ }
4
9
 
5
- // src/utils/announce.ts
6
10
  function announce(message, options = {}) {
7
11
  const text = message.trim();
8
12
  if (text.length === 0) return;
@@ -12,34 +16,37 @@ function announce(message, options = {}) {
12
16
  })
13
17
  );
14
18
  }
15
- function fillTemplate(template, values) {
16
- return template.replace(/\{([a-zA-Z][a-zA-Z0-9]*)\}/g, (match, name) => {
17
- const replacement = values[name];
18
- return replacement === void 0 ? match : String(replacement);
19
- });
20
- }
21
19
 
22
- // src/utils/microtask_coalescer.ts
20
+ var ListenerSet = class {
21
+ #abort = new AbortController();
22
+ add(target, type, handler, options) {
23
+ target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
24
+ }
25
+ dispose() {
26
+ this.#abort.abort();
27
+ this.#abort = new AbortController();
28
+ }
29
+ };
30
+
23
31
  var MicrotaskCoalescer = class {
24
32
  #run;
25
33
  #queued = false;
26
34
  #active = false;
27
35
  #generation = 0;
28
- /** @param run - the single reconciliation pass, invoked at most once per batch. */
29
36
  constructor(run) {
30
37
  this.#run = run;
31
38
  }
32
- /** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
33
39
  activate() {
34
40
  this.#active = true;
35
41
  }
36
- /** Closes the window and drops any pending pass; call from `disconnect()`. */
37
42
  cancel() {
38
43
  this.#active = false;
39
44
  this.#queued = false;
40
45
  this.#generation += 1;
41
46
  }
42
- /** Requests one pass after the batch settles. Idempotent; inert outside the window. */
47
+ get pending() {
48
+ return this.#queued;
49
+ }
43
50
  schedule() {
44
51
  if (!this.#active || this.#queued) return;
45
52
  this.#queued = true;
@@ -52,38 +59,187 @@ var MicrotaskCoalescer = class {
52
59
  }
53
60
  };
54
61
 
55
- // src/utils/safe_timeout.ts
62
+ var MorphRenderWatcher = class {
63
+ #listeners = new ListenerSet();
64
+ #pass;
65
+ #element = null;
66
+ #onMorph = () => this.#pass.schedule();
67
+ constructor(run) {
68
+ this.#pass = new MicrotaskCoalescer(run);
69
+ }
70
+ observe(element) {
71
+ if (this.#element === element) return;
72
+ this.disconnect();
73
+ this.#element = element;
74
+ this.#pass.activate();
75
+ this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
76
+ }
77
+ schedule() {
78
+ this.#pass.schedule();
79
+ }
80
+ get pending() {
81
+ return this.#pass.pending;
82
+ }
83
+ disconnect() {
84
+ this.#listeners.dispose();
85
+ this.#pass.cancel();
86
+ this.#element = null;
87
+ }
88
+ };
89
+
90
+ var MAX_TIMER_DELAY_MS = 2147483647;
91
+
92
+ var NUMBER_BOUNDS = {
93
+ positiveTimer: { finite: true, exclusiveMin: 0, max: MAX_TIMER_DELAY_MS }
94
+ };
95
+ function matchesNumberBounds(value, bounds) {
96
+ if (!Number.isFinite(value)) {
97
+ const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
98
+ if (direction === null) return false;
99
+ if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
100
+ }
101
+ if (bounds.min !== void 0 && value < bounds.min) return false;
102
+ if (bounds.max !== void 0 && value > bounds.max) return false;
103
+ if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
104
+ if (bounds.integer && !Number.isInteger(value)) return false;
105
+ if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
106
+ return true;
107
+ }
108
+
109
+ function readNumber(raw, fallback, bounds) {
110
+ return matchesNumberBounds(raw, bounds) ? raw : fallback;
111
+ }
112
+
113
+ var NumberValueReader = class {
114
+ #lastRejected = /* @__PURE__ */ new Map();
115
+ read(owner, name, raw, fallback, bounds) {
116
+ const resolved = readNumber(raw, fallback, bounds);
117
+ if (matchesNumberBounds(raw, bounds)) {
118
+ this.#lastRejected.delete(name);
119
+ return resolved;
120
+ }
121
+ const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
122
+ const literal = owner.element.getAttribute(attribute);
123
+ if (literal === null) {
124
+ this.#lastRejected.delete(name);
125
+ return resolved;
126
+ }
127
+ if (this.#lastRejected.get(name) !== literal) {
128
+ this.#lastRejected.set(name, literal);
129
+ console.warn(
130
+ `Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
131
+ );
132
+ }
133
+ return resolved;
134
+ }
135
+ };
136
+
137
+ function setAttributeIfChanged(element, name, value, beforeWrite) {
138
+ if (element.getAttribute(name) === value) return false;
139
+ if (value === null) element.removeAttribute(name);
140
+ else element.setAttribute(name, value);
141
+ return true;
142
+ }
143
+
144
+ var OwnedText = class {
145
+ #attribute;
146
+ #channel;
147
+ #guard;
148
+ #writes = /* @__PURE__ */ new WeakMap();
149
+ constructor(attribute, channel = "text", guard = () => true) {
150
+ this.#attribute = attribute;
151
+ this.#channel = channel;
152
+ this.#guard = guard;
153
+ }
154
+ hasRecord(element) {
155
+ return element.hasAttribute(this.#attribute);
156
+ }
157
+ read(element, evidence = {}) {
158
+ if (evidence.source === "live") return this.#writes.get(element) ?? null;
159
+ const raw = element.getAttribute(this.#attribute);
160
+ if (raw === null) return null;
161
+ try {
162
+ const record = JSON.parse(raw);
163
+ if (!Array.isArray(record) || record.length !== 3 || record[0] !== this.#channel || typeof record[1] !== "string" || typeof record[2] !== "string")
164
+ return null;
165
+ return { original: record[1], written: record[2] };
166
+ } catch {
167
+ return null;
168
+ }
169
+ }
170
+ owns(element, evidence = {}) {
171
+ return this.#ownedRecord(element, evidence) !== null;
172
+ }
173
+ #ownedRecord(element, evidence) {
174
+ const record = this.read(element, evidence);
175
+ if (record === null || !this.#guard(element)) return null;
176
+ if (this.#channel === "text" && element.firstElementChild !== null) return null;
177
+ const current = this.#current(element);
178
+ return current === record.written || evidence.source === "live" && evidence.allowOriginal === true && current === record.original ? record : null;
179
+ }
180
+ write(element, written, original, force = false) {
181
+ const current = this.#current(element);
182
+ if (current === null) return;
183
+ const before = original ?? this.#writes.get(element)?.original ?? this.read(element)?.original ?? current;
184
+ if (force || current !== written) this.#put(element, written);
185
+ this.remember(element, written, before);
186
+ }
187
+ remember(element, written, original) {
188
+ const current = this.#current(element);
189
+ if (current === null) return;
190
+ const before = original ?? this.#writes.get(element)?.original ?? this.read(element)?.original ?? current;
191
+ this.#writes.set(element, { original: before, written });
192
+ setAttributeIfChanged(
193
+ element,
194
+ this.#attribute,
195
+ JSON.stringify([this.#channel, before, written])
196
+ );
197
+ }
198
+ clear(element, evidence = {}) {
199
+ if (!this.owns(element, evidence)) return false;
200
+ if (this.#current(element) !== "") this.#put(element, "");
201
+ this.forget(element);
202
+ return true;
203
+ }
204
+ restore(element, evidence = {}) {
205
+ const record = this.#ownedRecord(element, evidence);
206
+ if (record === null) return false;
207
+ if (this.#current(element) !== record.original) this.#put(element, record.original);
208
+ this.forget(element);
209
+ return true;
210
+ }
211
+ forget(element) {
212
+ setAttributeIfChanged(element, this.#attribute, null);
213
+ this.#writes.delete(element);
214
+ }
215
+ #current(element) {
216
+ if (this.#channel === "text") return element.textContent ?? "";
217
+ return element instanceof HTMLInputElement ? element.value : null;
218
+ }
219
+ #put(element, text) {
220
+ if (this.#channel === "text") element.textContent = text;
221
+ else if (element instanceof HTMLInputElement) element.value = text;
222
+ }
223
+ };
224
+
56
225
  var TimerRegistry = class {
57
- /** Live timer ids that have not yet been cleared (or, for timeouts, fired). */
58
226
  ids = /* @__PURE__ */ new Set();
59
- /**
60
- * Cancels a single tracked timer.
61
- *
62
- * No-ops if the id is unknown (already cleared, fired, or never owned by this
63
- * registry), so callers can clear defensively without guarding.
64
- */
65
227
  clear(id) {
66
228
  if (this.ids.delete(id)) {
67
229
  this.cancel(id);
68
230
  }
69
231
  }
70
- /**
71
- * Cancels every tracked timer. Call this from a controller's `disconnect()`
72
- * to guarantee no timer outlives the element.
73
- */
74
232
  clearAll() {
75
233
  for (const id of this.ids) {
76
234
  this.cancel(id);
77
235
  }
78
236
  this.ids.clear();
79
237
  }
80
- /** Number of timers currently tracked (pending). */
81
238
  get size() {
82
239
  return this.ids.size;
83
240
  }
84
241
  };
85
242
  var SafeInterval = class extends TimerRegistry {
86
- /** Schedules a repeating `callback` every `delay` ms and returns the timer id. */
87
243
  set(callback, delay) {
88
244
  const id = this.schedule(callback, delay);
89
245
  this.ids.add(id);
@@ -97,11 +253,10 @@ var SafeInterval = class extends TimerRegistry {
97
253
  }
98
254
  };
99
255
 
100
- // src/controllers/countdown_controller.ts
101
256
  var SECOND_MS = 1e3;
102
257
  var OWNED_STATUS = "owns-status";
103
- var CountdownController = class extends Controller {
104
- /** The status marker, in the namespace this controller is registered under. */
258
+ var CountdownController = class _CountdownController extends Controller {
259
+ #numbers = new NumberValueReader();
105
260
  get #ownedStatus() {
106
261
  return `data-${this.identifier}-${OWNED_STATUS}`;
107
262
  }
@@ -114,114 +269,121 @@ var CountdownController = class extends Controller {
114
269
  completeLabel: { type: String, default: "" },
115
270
  announceText: { type: String, default: "" }
116
271
  };
272
+ static valueConstraints = {
273
+ interval: NUMBER_BOUNDS.positiveTimer
274
+ };
117
275
  static actions = ["pause", "reset", "resume", "start"];
118
276
  static events = ["complete", "tick"];
119
277
  #intervals = new SafeInterval();
120
278
  #intervalId = null;
121
- /** Collapses a morph that swaps several render inputs at once into one re-derive. */
122
- #resync = new MicrotaskCoalescer(() => this.#resyncToValues());
123
- /**
124
- * Whether `connect()` has run for this connection. Stimulus delivers Value
125
- * callbacks ahead of it, while the run state is still the markup's own and not
126
- * yet settled; `connect()` brings the status up to date once it is.
127
- */
279
+ #armedPeriod = 0;
280
+ #resync = new MorphRenderWatcher(() => this.#resyncToValues());
128
281
  #connected = false;
129
- /** Epoch-ms anchor: the deadline (down) or the count-up origin (up). */
130
282
  #reference = 0;
131
- /** Amount (ms) captured at pause, so resume can restore the same display. */
132
283
  #pausedAmount = 0;
133
- /**
134
- * The amount the slots are currently showing, floored to the second they render.
135
- * It lags {@link #currentAmount} by up to one tick, and it — not the live reading —
136
- * is what a pause has to preserve: storing the fraction behind the display instead
137
- * makes the first tick after a resume step by two units.
138
- */
139
284
  #renderedAmount = 0;
285
+ #phase = "paused";
286
+ #valuesDirty = false;
287
+ #statusText = new OwnedText(this.#ownedStatus);
140
288
  connect() {
141
289
  this.#connected = true;
142
- this.#resync.activate();
290
+ this.#resync.observe(this.element);
291
+ this.#valuesDirty = false;
143
292
  this.#initReference();
144
293
  const amount = this.#currentAmount();
145
294
  this.#render(amount);
146
295
  this.#pausedAmount = this.#renderedAmount;
147
296
  const authored = this.element.getAttribute("data-state");
297
+ this.#phase = authored ?? "paused";
148
298
  const settled = authored === "complete" && this.#isSettled(amount);
149
299
  if (authored === null) {
150
300
  if (this.autostartValue && this.#isValidDeadline) this.start();
151
- else this.element.setAttribute("data-state", "paused");
301
+ else this.#setPhase("paused");
152
302
  } else if (!settled) {
153
- this.element.setAttribute("data-state", "paused");
303
+ this.#setPhase("paused");
154
304
  if (authored === "running") this.start();
155
305
  }
156
306
  this.#syncStatus();
157
307
  }
158
308
  disconnect() {
159
309
  this.#connected = false;
160
- this.#resync.cancel();
310
+ this.#resync.disconnect();
161
311
  this.#intervals.clearAll();
162
312
  this.#intervalId = null;
163
313
  }
164
- /** Re-derives the display when a morph swaps the deadline in place. */
314
+ daysTargetConnected() {
315
+ this.#resync.schedule();
316
+ }
317
+ daysTargetDisconnected() {
318
+ this.#resync.schedule();
319
+ }
320
+ hoursTargetConnected() {
321
+ this.#resync.schedule();
322
+ }
323
+ hoursTargetDisconnected() {
324
+ this.#resync.schedule();
325
+ }
326
+ minutesTargetConnected() {
327
+ this.#resync.schedule();
328
+ }
329
+ minutesTargetDisconnected() {
330
+ this.#resync.schedule();
331
+ }
332
+ secondsTargetConnected() {
333
+ this.#resync.schedule();
334
+ }
335
+ secondsTargetDisconnected() {
336
+ this.#resync.schedule();
337
+ }
338
+ statusTargetConnected() {
339
+ this.#resync.schedule();
340
+ }
341
+ statusTargetDisconnected(status) {
342
+ if (!this.statusTargets.includes(status) && this.#ownsStatus(status)) {
343
+ this.#statusText.forget(status);
344
+ }
345
+ this.#resync.schedule();
346
+ }
165
347
  deadlineValueChanged() {
348
+ this.#valuesDirty = true;
166
349
  this.#resync.schedule();
167
350
  }
168
- /** Re-derives the display when a morph flips the counting direction in place. */
169
351
  directionValueChanged() {
352
+ this.#valuesDirty = true;
170
353
  this.#resync.schedule();
171
354
  }
172
- /**
173
- * Follows a completion label swapped in place by a morph.
174
- *
175
- * Render only: `complete` is neither dispatched nor announced again. The status
176
- * changes only after the countdown has completed, and only while it still shows
177
- * the text this controller wrote there and nothing else, so text or an element
178
- * the consumer put in the slot stays. A change delivered ahead of `connect()` is
179
- * left to `connect()`.
180
- */
181
355
  completeLabelValueChanged() {
182
356
  if (this.#connected) this.#syncStatus();
183
357
  }
184
- /**
185
- * Keeps a status that still shows this controller's text, and nothing else, on
186
- * what the run state calls for: the label in force while complete; otherwise the
187
- * text is taken back, marker and all. A status holding anything else is the
188
- * consumer's and is left alone, marker included.
189
- *
190
- * @stimeoRenderRoot
191
- */
358
+ intervalValueChanged() {
359
+ if (this.#intervalId === null || this.#armedPeriod === this.#safeInterval) return;
360
+ this.#teardownInterval();
361
+ this.#armTick();
362
+ }
192
363
  #syncStatus() {
193
364
  if (!this.hasStatusTarget || !this.#ownsStatus(this.statusTarget)) return;
194
365
  if (this.#state === "complete") this.#writeStatus(this.statusTarget, this.completeLabelValue);
195
366
  else this.#releaseStatus(this.statusTarget);
196
367
  }
197
- /**
198
- * Points the anchor at the current `deadline` / `direction` and repaints.
199
- *
200
- * Render only: it starts no interval and emits no event, so a morph cannot make a
201
- * paused timer run or replay a milestone. A running one needs no restart either —
202
- * every tick reads the anchor, so moving it is enough. While paused the stored
203
- * amount follows the new reading, or resume would continue from the old deadline.
204
- * A completion the new reading no longer settles (a deadline moved forward, or a
205
- * count turned `up`) is handed back paused, as `connect()` hands back a restored
206
- * one, so `resume()` counts again; the completion text this controller wrote goes
207
- * with it, from a status slot that still shows it and nothing else.
208
- *
209
- * @stimeoRenderRoot
210
- */
211
368
  #resyncToValues() {
369
+ this.#setPhase(this.#state);
370
+ if (!this.#valuesDirty) {
371
+ this.#render(this.#renderedAmount);
372
+ this.#syncStatus();
373
+ return;
374
+ }
375
+ this.#valuesDirty = false;
212
376
  this.#initReference();
213
377
  const amount = this.#currentAmount();
214
378
  this.#render(amount);
215
- if (this.#state === "complete" && !this.#isSettled(amount)) {
216
- this.element.setAttribute("data-state", "paused");
217
- this.#syncStatus();
379
+ if (this.#state === "running" && !this.#isValidDeadline) {
380
+ this.#teardownInterval();
381
+ this.#setPhase("paused");
218
382
  }
383
+ if (this.#state === "complete" && !this.#isSettled(amount)) this.#setPhase("paused");
219
384
  if (this.#state !== "running") this.#pausedAmount = this.#renderedAmount;
385
+ this.#syncStatus();
220
386
  }
221
- /**
222
- * Starts (or restarts after pause) ticking toward the deadline. Counting on from a
223
- * completion takes this controller's completion text back out of the status.
224
- */
225
387
  start() {
226
388
  if (this.#state === "running" || !this.#isValidDeadline) return;
227
389
  if (this.#isDown && this.#currentAmount() <= 0) {
@@ -230,36 +392,25 @@ var CountdownController = class extends Controller {
230
392
  }
231
393
  this.#runInterval();
232
394
  }
233
- /** Pauses ticking, preserving the currently displayed amount. */
234
395
  pause() {
235
396
  if (this.#state !== "running") return;
236
397
  this.#pausedAmount = this.#renderedAmount;
237
398
  this.#teardownInterval();
238
- this.element.setAttribute("data-state", "paused");
399
+ this.#setPhase("paused");
239
400
  }
240
- /** Resumes from a pause, continuing from the preserved amount. */
241
401
  resume() {
242
402
  if (this.#state !== "paused" || !this.#isValidDeadline) return;
243
403
  const now = Date.now();
244
404
  this.#reference = this.#isDown ? now + this.#pausedAmount : now - this.#pausedAmount;
245
405
  this.start();
246
406
  }
247
- /**
248
- * Re-syncs to the deadline and clears any pause offset, **preserving the current
249
- * run state**: a running timer keeps counting down from the reset amount, while a
250
- * paused (or completed) one resets the displayed amount but stays paused until the
251
- * user resumes — it never silently restarts. The run state is read from the DOM,
252
- * not re-derived from the declarative `autostart` Value (which governs only markup
253
- * that states no run state at all); re-deriving it would override a user's pause —
254
- * the DOM, not a re-run of declarative config, is the source of truth.
255
- */
256
407
  reset() {
257
408
  const wasRunning = this.#state === "running";
258
409
  this.#teardownInterval();
259
410
  this.#initReference();
260
411
  const amount = this.#currentAmount();
261
412
  this.#render(amount);
262
- this.element.setAttribute("data-state", "paused");
413
+ this.#setPhase("paused");
263
414
  this.#syncStatus();
264
415
  if (wasRunning && this.#isValidDeadline) {
265
416
  this.#pausedAmount = 0;
@@ -268,29 +419,21 @@ var CountdownController = class extends Controller {
268
419
  this.#pausedAmount = this.#renderedAmount;
269
420
  }
270
421
  }
271
- /**
272
- * Schedules the repeating tick and marks the timer running. This is the one way
273
- * into `running`, and a running timer keeps none of this controller's completion
274
- * text in the status, so the status is settled here as on every other way out of
275
- * `complete`: `start()` leaves a completion directly when `direction` flips to
276
- * `up` and `start()` runs in the same task, ahead of the direction callback.
277
- *
278
- * @stimeoRuntimeOnly `interval` is the period of the one timer this call arms; the running state
279
- * it writes does not depend on a Value.
280
- */
281
422
  #runInterval() {
282
- this.element.setAttribute("data-state", "running");
423
+ this.#setPhase("running");
283
424
  this.#syncStatus();
284
- this.#intervalId = this.#intervals.set(() => this.#tick(), this.intervalValue);
425
+ this.#armTick();
426
+ }
427
+ #armTick() {
428
+ this.#armedPeriod = this.#safeInterval;
429
+ this.#intervalId = this.#intervals.set(() => this.#tick(), this.#armedPeriod);
285
430
  }
286
- /** Cancels the repeating tick, if any. */
287
431
  #teardownInterval() {
288
432
  if (this.#intervalId !== null) {
289
433
  this.#intervals.clear(this.#intervalId);
290
434
  this.#intervalId = null;
291
435
  }
292
436
  }
293
- /** Recomputes, renders, emits `tick`, and completes when a countdown hits 0. */
294
437
  #tick() {
295
438
  const amount = this.#currentAmount();
296
439
  this.#render(amount);
@@ -301,26 +444,14 @@ var CountdownController = class extends Controller {
301
444
  this.#complete();
302
445
  }
303
446
  }
304
- /**
305
- * Stops at zero, marks completion, writes the completion label, emits `complete`,
306
- * and announces it.
307
- *
308
- * @stimeoRenderRoot
309
- */
310
447
  #complete() {
311
448
  this.#teardownInterval();
312
449
  this.#render(0);
313
- this.element.setAttribute("data-state", "complete");
450
+ this.#setPhase("complete");
314
451
  this.#claimStatus();
315
452
  this.dispatch("complete", { detail: {} });
316
453
  this.#announceCompletion();
317
454
  }
318
- /**
319
- * Writes the label in force into the status slot as this controller's text. An
320
- * empty label puts no text there: it claims a slot that is already empty — no
321
- * text and no element — empties one that is still this controller's, and leaves
322
- * any other slot, the consumer's, alone.
323
- */
324
455
  #claimStatus() {
325
456
  if (!this.hasStatusTarget) return;
326
457
  const status = this.statusTarget;
@@ -328,54 +459,36 @@ var CountdownController = class extends Controller {
328
459
  if (label === "" && !this.#isEmpty(status) && !this.#ownsStatus(status)) return;
329
460
  this.#writeStatus(status, label);
330
461
  }
331
- /**
332
- * Whether the status slot shows the text this controller wrote there and nothing
333
- * else: the text matches the marker, and the slot holds no element. Writing the
334
- * slot's text replaces every child node, so an element the consumer puts in the
335
- * slot makes it theirs even while the text still matches. An empty marker
336
- * matches the slot whenever it is empty.
337
- */
338
462
  #ownsStatus(status) {
339
- return status.getAttribute(this.#ownedStatus) === status.textContent && status.firstElementChild === null;
463
+ return this.#statusText.owns(status) || !this.#statusText.hasRecord(status) && this.#statusText.owns(status, { source: "live", allowOriginal: true });
340
464
  }
341
- /** Whether the status slot holds nothing at all: no text and no element. */
342
465
  #isEmpty(status) {
343
466
  return status.textContent === "" && status.firstElementChild === null;
344
467
  }
345
- /** Puts `text` into the status slot and records it as this controller's. */
346
468
  #writeStatus(status, text) {
347
- if (status.textContent !== text) status.textContent = text;
348
- status.setAttribute(this.#ownedStatus, text);
469
+ this.#statusText.write(status, text);
349
470
  }
350
- /** Takes this controller's text out of the status slot, marker and all. */
351
471
  #releaseStatus(status) {
352
- status.textContent = "";
353
- status.removeAttribute(this.#ownedStatus);
354
- }
355
- /**
356
- * Reads the completion out through the shared announcer.
357
- *
358
- * @stimeoRuntimeOnly `announceText` words the one announcement a completion makes.
359
- */
472
+ this.#statusText.clear(
473
+ status,
474
+ this.#statusText.hasRecord(status) ? {} : { source: "live", allowOriginal: true }
475
+ );
476
+ }
360
477
  #announceCompletion() {
361
478
  announce(fillTemplate(this.announceTextValue, {}));
362
479
  }
363
- /** Sets the time anchor from the `deadline` value. */
364
480
  #initReference() {
365
481
  this.#reference = Date.parse(this.deadlineValue);
366
482
  }
367
- /** Whether a reading of `amount` leaves nothing to count: only a countdown at zero. */
368
483
  #isSettled(amount) {
369
484
  return this.#isDown && amount <= 0;
370
485
  }
371
- /** Remaining (down) or elapsed (up) ms, never negative. */
372
486
  #currentAmount() {
373
487
  if (!this.#isValidDeadline) return 0;
374
488
  const now = Date.now();
375
489
  const raw = this.#isDown ? this.#reference - now : now - this.#reference;
376
490
  return Math.max(0, raw);
377
491
  }
378
- /** Writes the amount into the day/hour/minute/second slots. */
379
492
  #render(amount) {
380
493
  const totalSeconds = Math.floor(amount / SECOND_MS);
381
494
  this.#renderedAmount = totalSeconds * SECOND_MS;
@@ -388,7 +501,6 @@ var CountdownController = class extends Controller {
388
501
  if (this.hasMinutesTarget) this.minutesTarget.textContent = this.#pad(minutes);
389
502
  if (this.hasSecondsTarget) this.secondsTarget.textContent = this.#pad(seconds);
390
503
  }
391
- /** Zero-pads a unit to two digits. */
392
504
  #pad(unit) {
393
505
  return String(unit).padStart(2, "0");
394
506
  }
@@ -398,12 +510,23 @@ var CountdownController = class extends Controller {
398
510
  get #isValidDeadline() {
399
511
  return !Number.isNaN(this.#reference);
400
512
  }
401
- /** Current lifecycle phase as reflected on `data-state`. */
513
+ #setPhase(phase) {
514
+ this.#phase = phase;
515
+ this.element.setAttribute("data-state", phase);
516
+ }
402
517
  get #state() {
403
- return this.element.getAttribute("data-state") ?? "paused";
518
+ return this.element.getAttribute("data-state") ?? this.#phase;
519
+ }
520
+ get #safeInterval() {
521
+ return this.#numbers.read(
522
+ this,
523
+ "interval",
524
+ this.intervalValue,
525
+ _CountdownController.values.interval.default,
526
+ _CountdownController.valueConstraints.interval
527
+ );
404
528
  }
405
529
  };
406
530
 
407
531
  export { CountdownController };
408
- //# sourceMappingURL=countdown_controller.js.map
409
532
  //# sourceMappingURL=countdown_controller.js.map