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/character_counter_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,77 +16,153 @@ function fillTemplate(template, values) {
19
16
  });
20
17
  }
21
18
 
22
- // src/utils/attribute_lease.ts
23
- var AttributeLease = class {
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
+ );
24
50
  #attribute;
25
- #records = /* @__PURE__ */ new Map();
26
- /** @param attribute - The attribute whose temporary values this lease owns. */
27
- constructor(attribute) {
51
+ #record;
52
+ #base;
53
+ #held = /* @__PURE__ */ new Set();
54
+ constructor(attribute, owner, options = {}) {
28
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));
29
60
  }
30
- /** Writes or removes the leased attribute while preserving its authored value. */
31
61
  write(element, value) {
32
- const existing = this.#records.get(element);
33
- if (existing) {
34
- existing.written = value;
35
- } else {
36
- this.#records.set(element, {
37
- original: element.getAttribute(this.#attribute),
38
- written: value
39
- });
40
- }
41
- this.#reflect(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);
42
69
  }
43
- /** Returns one lease without overwriting a value subsequently authored by a consumer. */
44
70
  return(element) {
45
- const record = this.#records.get(element);
46
- if (!record) return;
47
- this.#records.delete(element);
48
- const stillOwned = element.getAttribute(this.#attribute) === record.written;
49
- if (stillOwned) this.#reflect(element, record.original);
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);
157
+ }
50
158
  }
51
- /** Reflects only a real value transition, avoiding self-triggered mutation work. */
52
159
  #reflect(element, value) {
53
160
  if (element.getAttribute(this.#attribute) === value) return;
54
161
  if (value === null) element.removeAttribute(this.#attribute);
55
162
  else element.setAttribute(this.#attribute, value);
56
163
  }
57
- /** Returns every outstanding lease using the same ownership check as {@link return}. */
58
- returnAll() {
59
- for (const element of Array.from(this.#records.keys())) this.return(element);
60
- }
61
164
  };
62
165
 
63
- // src/utils/before_cache_reset.ts
64
- var BeforeCacheReset = class _BeforeCacheReset {
65
- /** Every subscribed instance, iterated by the one shared document listener. */
66
- static #subscribers = /* @__PURE__ */ new Set();
67
- /** The shared listener; installed while at least one instance is subscribed. */
68
- static #onBeforeCache = () => {
69
- for (const subscriber of _BeforeCacheReset.#subscribers) subscriber.#rewind();
70
- };
71
- #rewind;
72
- /** @param rewind - the pass that returns this controller's state to its initial form. */
73
- constructor(rewind) {
74
- this.#rewind = rewind;
75
- }
76
- /** Subscribes to `turbo:before-cache`; call from `connect()`. Idempotent. */
77
- activate() {
78
- const first = _BeforeCacheReset.#subscribers.size === 0;
79
- _BeforeCacheReset.#subscribers.add(this);
80
- if (first) {
81
- document.addEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
82
- }
83
- }
84
- /** Unsubscribes; call from `disconnect()`. Safe when never subscribed. */
85
- deactivate() {
86
- _BeforeCacheReset.#subscribers.delete(this);
87
- if (_BeforeCacheReset.#subscribers.size > 0) return;
88
- document.removeEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
89
- }
90
- };
91
-
92
- // src/utils/composition_tracker.ts
93
166
  var COMPOSITION_INPUT_TYPES = /* @__PURE__ */ new Set([
94
167
  "insertCompositionText",
95
168
  "insertFromComposition",
@@ -101,13 +174,11 @@ var CompositionTracker = class {
101
174
  #activeTargets = /* @__PURE__ */ new Set();
102
175
  #onStart;
103
176
  #onEnd;
104
- /** The field whose confirming `input` is still owed, while the window is open. */
105
177
  #confirmedTarget = null;
106
178
  constructor(options = {}) {
107
179
  this.#onStart = options.onStart;
108
180
  this.#onEnd = options.onEnd;
109
181
  }
110
- /** Starts lifecycle tracking for `target`; repeated calls are idempotent. */
111
182
  observe(target) {
112
183
  if (this.#observedTargets.has(target)) return;
113
184
  target.addEventListener("compositionstart", this.#handleStart);
@@ -115,7 +186,6 @@ var CompositionTracker = class {
115
186
  target.addEventListener("keydown", this.#handleKeydown);
116
187
  this.#observedTargets.add(target);
117
188
  }
118
- /** Stops tracking one target and clears any active composition it owned. */
119
189
  unobserve(target) {
120
190
  if (!this.#observedTargets.delete(target)) return;
121
191
  target.removeEventListener("compositionstart", this.#handleStart);
@@ -124,7 +194,6 @@ var CompositionTracker = class {
124
194
  this.#activeTargets.delete(target);
125
195
  if (this.#confirmedTarget === target) this.#confirmedTarget = null;
126
196
  }
127
- /** Releases every listener and clears state so reconnect starts cleanly. */
128
197
  disconnect() {
129
198
  for (const target of this.#observedTargets) {
130
199
  target.removeEventListener("compositionstart", this.#handleStart);
@@ -135,16 +204,9 @@ var CompositionTracker = class {
135
204
  this.#activeTargets.clear();
136
205
  this.#confirmedTarget = null;
137
206
  }
138
- /** True when lifecycle tracking or the current event reports composition. */
139
207
  isComposing(event) {
140
208
  return this.#activeTargets.size > 0 || event?.isComposing === true;
141
209
  }
142
- /**
143
- * Whether `event` is the `input` echoing the composition just confirmed.
144
- *
145
- * Asking closes the window either way, so one confirmation is folded at most
146
- * once and a consumer asks once per `input`.
147
- */
148
210
  consumesConfirmedInput(event) {
149
211
  const confirmed = this.#confirmedTarget;
150
212
  this.#confirmedTarget = null;
@@ -162,33 +224,38 @@ var CompositionTracker = class {
162
224
  this.#confirmedTarget = event.target;
163
225
  this.#onEnd?.(event);
164
226
  };
165
- /** A key on an observed field opens an edit of its own, so no echo is owed. */
166
227
  #handleKeydown = () => {
167
228
  this.#confirmedTarget = null;
168
229
  };
169
230
  };
170
231
 
171
- // src/utils/microtask_coalescer.ts
232
+ var ListenerSet = class {
233
+ #abort = new AbortController();
234
+ add(target, type, handler, options) {
235
+ target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
236
+ }
237
+ dispose() {
238
+ this.#abort.abort();
239
+ this.#abort = new AbortController();
240
+ }
241
+ };
242
+
172
243
  var MicrotaskCoalescer = class {
173
244
  #run;
174
245
  #queued = false;
175
246
  #active = false;
176
247
  #generation = 0;
177
- /** @param run - the single reconciliation pass, invoked at most once per batch. */
178
248
  constructor(run) {
179
249
  this.#run = run;
180
250
  }
181
- /** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
182
251
  activate() {
183
252
  this.#active = true;
184
253
  }
185
- /** Closes the window and drops any pending pass; call from `disconnect()`. */
186
254
  cancel() {
187
255
  this.#active = false;
188
256
  this.#queued = false;
189
257
  this.#generation += 1;
190
258
  }
191
- /** Requests one pass after the batch settles. Idempotent; inert outside the window. */
192
259
  schedule() {
193
260
  if (!this.#active || this.#queued) return;
194
261
  this.#queued = true;
@@ -201,43 +268,93 @@ var MicrotaskCoalescer = class {
201
268
  }
202
269
  };
203
270
 
204
- // src/utils/safe_timeout.ts
271
+ var MorphRenderWatcher = class {
272
+ #listeners = new ListenerSet();
273
+ #pass;
274
+ #element = null;
275
+ #onMorph = () => this.#pass.schedule();
276
+ constructor(run) {
277
+ this.#pass = new MicrotaskCoalescer(run);
278
+ }
279
+ observe(element) {
280
+ if (this.#element === element) return;
281
+ this.disconnect();
282
+ this.#element = element;
283
+ this.#pass.activate();
284
+ this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
285
+ }
286
+ schedule() {
287
+ this.#pass.schedule();
288
+ }
289
+ disconnect() {
290
+ this.#listeners.dispose();
291
+ this.#pass.cancel();
292
+ this.#element = null;
293
+ }
294
+ };
295
+
296
+ var NUMBER_BOUNDS = {
297
+ nonNegative: { finite: true, min: 0 }};
298
+ function matchesNumberBounds(value, bounds) {
299
+ if (!Number.isFinite(value)) {
300
+ const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
301
+ if (direction === null) return false;
302
+ if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
303
+ }
304
+ if (bounds.min !== void 0 && value < bounds.min) return false;
305
+ if (bounds.max !== void 0 && value > bounds.max) return false;
306
+ if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
307
+ if (bounds.integer && !Number.isInteger(value)) return false;
308
+ if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
309
+ return true;
310
+ }
311
+
312
+ function readNumber(raw, fallback, bounds) {
313
+ return matchesNumberBounds(raw, bounds) ? raw : fallback;
314
+ }
315
+
316
+ var NumberValueReader = class {
317
+ #lastRejected = /* @__PURE__ */ new Map();
318
+ read(owner, name, raw, fallback, bounds) {
319
+ const resolved = readNumber(raw, fallback, bounds);
320
+ if (matchesNumberBounds(raw, bounds)) {
321
+ this.#lastRejected.delete(name);
322
+ return resolved;
323
+ }
324
+ const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
325
+ const literal = owner.element.getAttribute(attribute);
326
+ if (literal === null) {
327
+ this.#lastRejected.delete(name);
328
+ return resolved;
329
+ }
330
+ if (this.#lastRejected.get(name) !== literal) {
331
+ this.#lastRejected.set(name, literal);
332
+ console.warn(
333
+ `Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
334
+ );
335
+ }
336
+ return resolved;
337
+ }
338
+ };
339
+
205
340
  var TimerRegistry = class {
206
- /** Live timer ids that have not yet been cleared (or, for timeouts, fired). */
207
341
  ids = /* @__PURE__ */ new Set();
208
- /**
209
- * Cancels a single tracked timer.
210
- *
211
- * No-ops if the id is unknown (already cleared, fired, or never owned by this
212
- * registry), so callers can clear defensively without guarding.
213
- */
214
342
  clear(id) {
215
343
  if (this.ids.delete(id)) {
216
344
  this.cancel(id);
217
345
  }
218
346
  }
219
- /**
220
- * Cancels every tracked timer. Call this from a controller's `disconnect()`
221
- * to guarantee no timer outlives the element.
222
- */
223
347
  clearAll() {
224
348
  for (const id of this.ids) {
225
349
  this.cancel(id);
226
350
  }
227
351
  this.ids.clear();
228
352
  }
229
- /** Number of timers currently tracked (pending). */
230
353
  get size() {
231
354
  return this.ids.size;
232
355
  }
233
356
  };
234
357
  var SafeTimeout = class extends TimerRegistry {
235
- /**
236
- * Schedules `callback` after `delay` ms and returns the timer id.
237
- *
238
- * The id is removed from the registry automatically when the timeout fires,
239
- * so {@link TimerRegistry.size | size} reflects only still-pending timers.
240
- */
241
358
  set(callback, delay) {
242
359
  const id = this.schedule(() => {
243
360
  this.ids.delete(id);
@@ -254,8 +371,8 @@ var SafeTimeout = class extends TimerRegistry {
254
371
  }
255
372
  };
256
373
 
257
- // src/controllers/character_counter_controller.ts
258
374
  var CharacterCounterController = class _CharacterCounterController extends Controller {
375
+ #numbers = new NumberValueReader();
259
376
  static targets = ["input", "output"];
260
377
  static values = {
261
378
  max: { type: Number, default: 0 },
@@ -263,13 +380,18 @@ var CharacterCounterController = class _CharacterCounterController extends Contr
263
380
  mode: { type: String, default: "remaining" },
264
381
  announceText: { type: String, default: "" }
265
382
  };
383
+ static valueConstraints = {
384
+ max: NUMBER_BOUNDS.nonNegative,
385
+ warnAt: NUMBER_BOUNDS.nonNegative
386
+ };
266
387
  static events = ["change", "reconcile"];
267
- /** Delay (ms) before one settled count is sent to the shared announcer. */
268
388
  static #announceDelay = 200;
269
389
  #timeouts = new SafeTimeout();
270
- #ariaInvalid = new AttributeLease("aria-invalid");
271
- #beforeCache = new BeforeCacheReset(() => this.#rewindForCache());
272
- #repaint = new MicrotaskCoalescer(() => this.#reconcile(true));
390
+ #ariaInvalid = new AttributeLease(
391
+ "aria-invalid",
392
+ this.identifier
393
+ );
394
+ #repaint = new MorphRenderWatcher(() => this.#reconcile(true));
273
395
  #composition = new CompositionTracker({
274
396
  onEnd: (event) => this.#commitFrom(event.currentTarget)
275
397
  });
@@ -285,56 +407,42 @@ var CharacterCounterController = class _CharacterCounterController extends Contr
285
407
  if (this.#composition.isComposing(event)) return;
286
408
  this.#commit(field);
287
409
  };
288
- /** Reflects the current DOM state and opens the mutation-reconciliation window. */
289
410
  connect() {
290
- this.#repaint.activate();
291
- this.#beforeCache.activate();
411
+ this.#repaint.observe(this.element);
292
412
  this.#reconcile(false);
293
413
  }
294
- /** Releases listeners, timers, borrowed ARIA, and controller-owned state hooks. */
295
414
  disconnect() {
296
- this.#repaint.cancel();
297
- this.#beforeCache.deactivate();
415
+ this.#repaint.disconnect();
298
416
  this.#cancelAnnouncement();
299
417
  this.#bindField(null);
300
- this.#composition.disconnect();
301
418
  this.#clearStateHooks();
302
419
  this.#lastLength = null;
303
420
  this.#lastDetail = null;
304
421
  }
305
- /** Rebinds and repaints after an input target is added or replaced at runtime. */
306
422
  inputTargetConnected() {
307
423
  this.#repaint.schedule();
308
424
  }
309
- /** Releases and repaints after an input target is removed or replaced at runtime. */
310
425
  inputTargetDisconnected() {
311
426
  this.#repaint.schedule();
312
427
  }
313
- /** Initializes a display target inserted after the controller connected. */
314
428
  outputTargetConnected() {
315
429
  this.#repaint.schedule();
316
430
  }
317
- /** Reconciles the optional display target set after a removal or replacement. */
318
431
  outputTargetDisconnected() {
319
432
  this.#repaint.schedule();
320
433
  }
321
- /** Repaints when application code or a Turbo morph changes `max`. */
322
434
  maxValueChanged() {
323
435
  this.#repaint.schedule();
324
436
  }
325
- /** Repaints when application code or a Turbo morph changes `warnAt`. */
326
437
  warnAtValueChanged() {
327
438
  this.#repaint.schedule();
328
439
  }
329
- /** Repaints when application code or a Turbo morph changes `mode`. */
330
440
  modeValueChanged() {
331
441
  this.#repaint.schedule();
332
442
  }
333
- /** Rewords a pending announcement with the template the consumer changed it to. */
334
443
  announceTextValueChanged() {
335
444
  this.#repaint.schedule();
336
445
  }
337
- /** Rebinds one mutation batch and reports a changed controller-derived state. */
338
446
  #reconcile(report) {
339
447
  const previous = this.#lastDetail;
340
448
  const owedField = this.#announceId === null ? null : this.#boundField;
@@ -357,13 +465,11 @@ var CharacterCounterController = class _CharacterCounterController extends Contr
357
465
  this.dispatch("reconcile", { detail });
358
466
  }
359
467
  }
360
- /** Commits a composition only when it came from the field still owned here. */
361
468
  #commitFrom(target) {
362
469
  const field = this.#boundField;
363
470
  if (!field || target !== field || this.#field !== field) return;
364
471
  this.#commit(field);
365
472
  }
366
- /** Reflects one confirmed user input and reports only a real length transition. */
367
473
  #commit(field) {
368
474
  const reading = this.#render(field);
369
475
  const previousLength = this.#lastLength;
@@ -380,7 +486,6 @@ var CharacterCounterController = class _CharacterCounterController extends Contr
380
486
  this.dispatch("change", { detail });
381
487
  this.#scheduleAnnouncement(reading);
382
488
  }
383
- /** Selects the public event state from the richer internal reading. */
384
489
  #detail(reading) {
385
490
  return {
386
491
  length: reading.length,
@@ -388,19 +493,13 @@ var CharacterCounterController = class _CharacterCounterController extends Contr
388
493
  over: reading.over
389
494
  };
390
495
  }
391
- /** Compares exactly the state carried by `change` and `reconcile`. */
392
496
  #detailsDiffer(left, right) {
393
497
  return left.length !== right.length || left.remaining !== right.remaining || left.over !== right.over;
394
498
  }
395
- /**
396
- * Synchronizes visible count, state hooks, and temporary validation ARIA.
397
- *
398
- * @stimeoRenderRoot
399
- */
400
499
  #render(field) {
401
500
  const length = field.value.length;
402
- const max = this.#normalizeCount(this.maxValue);
403
- const warnAt = this.#normalizeCount(this.warnAtValue);
501
+ const max = this.#normalizeCount(this.#safeMax);
502
+ const warnAt = this.#normalizeCount(this.#safeWarnAt);
404
503
  const remaining = max > 0 ? max - length : null;
405
504
  const over = remaining !== null && remaining < 0;
406
505
  const near = remaining !== null && warnAt > 0 && !over && remaining <= warnAt;
@@ -411,14 +510,11 @@ var CharacterCounterController = class _CharacterCounterController extends Contr
411
510
  this.#writeOutput(text);
412
511
  return { length, remaining, over, max, text };
413
512
  }
414
- /** Clears derived output when the declarative input set has no usable field. */
415
513
  #renderEmpty() {
416
- this.#ariaInvalid.returnAll();
417
514
  this.#reflectedOver = null;
418
515
  this.#clearStateHooks();
419
516
  this.#writeOutput("");
420
517
  }
421
- /** Replaces the observed field symmetrically, returning state from the old one. */
422
518
  #bindField(field) {
423
519
  if (field === this.#boundField) return;
424
520
  const previous = this.#boundField;
@@ -434,14 +530,12 @@ var CharacterCounterController = class _CharacterCounterController extends Contr
434
530
  field.addEventListener("input", this.#onInput);
435
531
  this.#composition.observe(field);
436
532
  }
437
- /** Leases `aria-invalid` only on the edge into over-limit, then returns it. */
438
533
  #reflectInvalid(field, over) {
439
- if (this.#reflectedOver === over) return;
534
+ if (this.#reflectedOver === over && (!over || field.hasAttribute("aria-invalid"))) return;
440
535
  this.#reflectedOver = over;
441
536
  if (over) this.#ariaInvalid.write(field, "true");
442
537
  else this.#ariaInvalid.return(field);
443
538
  }
444
- /** Builds the visible count for the normalized display mode. */
445
539
  #format(length, remaining, max) {
446
540
  if (remaining === null) return String(length);
447
541
  switch (this.#mode) {
@@ -453,12 +547,6 @@ var CharacterCounterController = class _CharacterCounterController extends Contr
453
547
  return String(remaining);
454
548
  }
455
549
  }
456
- /**
457
- * Debounces one i18n-neutral message into the shared polite announcer.
458
- *
459
- * @stimeoRuntimeOnly `announceText` is the template of the one announcement this call schedules;
460
- * the count is painted by the marked renderer.
461
- */
462
550
  #scheduleAnnouncement(reading) {
463
551
  this.#cancelAnnouncement();
464
552
  const message = fillTemplate(this.announceTextValue, {
@@ -474,17 +562,14 @@ var CharacterCounterController = class _CharacterCounterController extends Contr
474
562
  this.#announceId = null;
475
563
  }, _CharacterCounterController.#announceDelay);
476
564
  }
477
- /** Cancels the one outstanding announcement without touching visible output. */
478
565
  #cancelAnnouncement() {
479
566
  if (this.#announceId !== null) this.#timeouts.clear(this.#announceId);
480
567
  this.#announceId = null;
481
568
  }
482
- /** Writes the optional display only when its text actually changed. */
483
569
  #writeOutput(text) {
484
570
  if (!this.hasOutputTarget || this.outputTarget.textContent === text) return;
485
571
  this.outputTarget.textContent = text;
486
572
  }
487
- /** Reflects a presence-style state hook without redundant attribute writes. */
488
573
  #toggle(name, on) {
489
574
  if (on) {
490
575
  if (this.element.getAttribute(name) !== "true") this.element.setAttribute(name, "true");
@@ -492,29 +577,17 @@ var CharacterCounterController = class _CharacterCounterController extends Contr
492
577
  this.element.removeAttribute(name);
493
578
  }
494
579
  }
495
- /** Removes both state hooks owned by this controller. */
496
580
  #clearStateHooks() {
497
581
  this.#toggle("data-over-limit", false);
498
582
  this.#toggle("data-near-limit", false);
499
583
  }
500
- /** Returns borrowed ARIA and transient hooks before Turbo snapshots the page. */
501
- #rewindForCache() {
502
- this.#cancelAnnouncement();
503
- this.#ariaInvalid.returnAll();
504
- this.#reflectedOver = null;
505
- this.#clearStateHooks();
506
- }
507
- /** Converts a public count Value into a finite non-negative integer. */
508
584
  #normalizeCount(raw) {
509
- if (!Number.isFinite(raw)) return 0;
510
585
  return Math.max(0, Math.trunc(raw));
511
586
  }
512
- /** Falls back to `remaining` when an authored display mode is unknown. */
513
587
  get #mode() {
514
588
  if (this.modeValue === "used" || this.modeValue === "both") return this.modeValue;
515
589
  return "remaining";
516
590
  }
517
- /** The watched field: the first target, or a directly controlled form field. */
518
591
  get #field() {
519
592
  if (this.hasInputTarget) return this.inputTarget;
520
593
  const element = this.element;
@@ -522,8 +595,25 @@ var CharacterCounterController = class _CharacterCounterController extends Contr
522
595
  return element;
523
596
  return null;
524
597
  }
598
+ get #safeMax() {
599
+ return this.#numbers.read(
600
+ this,
601
+ "max",
602
+ this.maxValue,
603
+ _CharacterCounterController.values.max.default,
604
+ _CharacterCounterController.valueConstraints.max
605
+ );
606
+ }
607
+ get #safeWarnAt() {
608
+ return this.#numbers.read(
609
+ this,
610
+ "warnAt",
611
+ this.warnAtValue,
612
+ _CharacterCounterController.values.warnAt.default,
613
+ _CharacterCounterController.valueConstraints.warnAt
614
+ );
615
+ }
525
616
  };
526
617
 
527
618
  export { CharacterCounterController };
528
- //# sourceMappingURL=character_counter_controller.js.map
529
619
  //# sourceMappingURL=character_counter_controller.js.map