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/meter_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,41 +16,66 @@ 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
- });
19
+
20
+ var NUMBER_BOUNDS = {
21
+ finite: { finite: true }};
22
+ function matchesNumberBounds(value, bounds) {
23
+ if (!Number.isFinite(value)) {
24
+ const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
25
+ if (direction === null) return false;
26
+ if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
27
+ }
28
+ if (bounds.min !== void 0 && value < bounds.min) return false;
29
+ if (bounds.max !== void 0 && value > bounds.max) return false;
30
+ if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
31
+ if (bounds.integer && !Number.isInteger(value)) return false;
32
+ if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
33
+ return true;
20
34
  }
21
35
 
22
- // src/utils/coerce.ts
36
+ function eventNumber(event) {
37
+ return toFiniteNumber(event.params?.amount ?? event.detail?.value);
38
+ }
23
39
  function toFiniteNumber(raw) {
24
- if (raw === null || raw === void 0 || raw === "") return null;
25
- const value = typeof raw === "number" ? raw : Number(raw);
40
+ if (typeof raw !== "number" && typeof raw !== "string") return null;
41
+ if (typeof raw === "string" && raw.trim().length === 0) return null;
42
+ const value = Number(raw);
26
43
  return Number.isFinite(value) ? value : null;
27
44
  }
45
+ function readNumber(raw, fallback, bounds) {
46
+ return matchesNumberBounds(raw, bounds) ? raw : fallback;
47
+ }
48
+
49
+ var ListenerSet = class {
50
+ #abort = new AbortController();
51
+ add(target, type, handler, options) {
52
+ target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
53
+ }
54
+ dispose() {
55
+ this.#abort.abort();
56
+ this.#abort = new AbortController();
57
+ }
58
+ };
28
59
 
29
- // src/utils/microtask_coalescer.ts
30
60
  var MicrotaskCoalescer = class {
31
61
  #run;
32
62
  #queued = false;
33
63
  #active = false;
34
64
  #generation = 0;
35
- /** @param run - the single reconciliation pass, invoked at most once per batch. */
36
65
  constructor(run) {
37
66
  this.#run = run;
38
67
  }
39
- /** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
40
68
  activate() {
41
69
  this.#active = true;
42
70
  }
43
- /** Closes the window and drops any pending pass; call from `disconnect()`. */
44
71
  cancel() {
45
72
  this.#active = false;
46
73
  this.#queued = false;
47
74
  this.#generation += 1;
48
75
  }
49
- /** Requests one pass after the batch settles. Idempotent; inert outside the window. */
76
+ get pending() {
77
+ return this.#queued;
78
+ }
50
79
  schedule() {
51
80
  if (!this.#active || this.#queued) return;
52
81
  this.#queued = true;
@@ -59,11 +88,62 @@ var MicrotaskCoalescer = class {
59
88
  }
60
89
  };
61
90
 
62
- // src/utils/range.ts
91
+ var MorphRenderWatcher = class {
92
+ #listeners = new ListenerSet();
93
+ #pass;
94
+ #element = null;
95
+ #onMorph = () => this.#pass.schedule();
96
+ constructor(run) {
97
+ this.#pass = new MicrotaskCoalescer(run);
98
+ }
99
+ observe(element) {
100
+ if (this.#element === element) return;
101
+ this.disconnect();
102
+ this.#element = element;
103
+ this.#pass.activate();
104
+ this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
105
+ }
106
+ schedule() {
107
+ this.#pass.schedule();
108
+ }
109
+ get pending() {
110
+ return this.#pass.pending;
111
+ }
112
+ disconnect() {
113
+ this.#listeners.dispose();
114
+ this.#pass.cancel();
115
+ this.#element = null;
116
+ }
117
+ };
118
+
119
+ var NumberValueReader = class {
120
+ #lastRejected = /* @__PURE__ */ new Map();
121
+ read(owner, name, raw, fallback, bounds) {
122
+ const resolved = readNumber(raw, fallback, bounds);
123
+ if (matchesNumberBounds(raw, bounds)) {
124
+ this.#lastRejected.delete(name);
125
+ return resolved;
126
+ }
127
+ const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
128
+ const literal = owner.element.getAttribute(attribute);
129
+ if (literal === null) {
130
+ this.#lastRejected.delete(name);
131
+ return resolved;
132
+ }
133
+ if (this.#lastRejected.get(name) !== literal) {
134
+ this.#lastRejected.set(name, literal);
135
+ console.warn(
136
+ `Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
137
+ );
138
+ }
139
+ return resolved;
140
+ }
141
+ };
142
+
63
143
  function rangeFraction(value, min, max) {
64
144
  const span = max - min;
65
145
  if (!(span > 0)) return 0;
66
- const clamped = Math.min(max, Math.max(min, value));
146
+ const clamped = clampRange(value, min, max);
67
147
  let fraction;
68
148
  if (Number.isFinite(span)) {
69
149
  fraction = (clamped - min) / span;
@@ -74,14 +154,254 @@ function rangeFraction(value, min, max) {
74
154
  if (!Number.isFinite(fraction)) return 0;
75
155
  return Math.min(1, Math.max(0, fraction));
76
156
  }
157
+ function clampRange(value, min, max) {
158
+ return Math.min(max, Math.max(min, value));
159
+ }
77
160
 
78
- // src/controllers/meter_controller.ts
79
- var OWNED_VALUE_TEXT = "owns-valuetext";
80
- var MeterController = class extends Controller {
81
- /** The marker above, in the namespace this controller is registered under. */
82
- get #ownedValueText() {
83
- return `data-${this.identifier}-${OWNED_VALUE_TEXT}`;
161
+ function setAttributeIfChanged(element, name, value, beforeWrite) {
162
+ if (element.getAttribute(name) === value) return false;
163
+ if (value === null) element.removeAttribute(name);
164
+ else element.setAttribute(name, value);
165
+ return true;
166
+ }
167
+
168
+ function sharedRegistry(key, create) {
169
+ const symbol = Symbol.for(key);
170
+ const scope = globalThis;
171
+ const existing = scope[symbol];
172
+ if (existing !== void 0) return existing;
173
+ const registry2 = create();
174
+ Object.defineProperty(globalThis, symbol, { value: registry2 });
175
+ return registry2;
176
+ }
177
+
178
+ var registry = sharedRegistry(
179
+ "stimeo-ui.attribute-lease.registry.v1",
180
+ () => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
181
+ );
182
+ function isAttributeValue(value) {
183
+ return value === null || typeof value === "string";
184
+ }
185
+ function readLeaseRecord(element, record) {
186
+ try {
187
+ const parsed = JSON.parse(element.getAttribute(record) ?? "null");
188
+ if (!Array.isArray(parsed)) return void 0;
189
+ const [original, written, ...rest] = parsed;
190
+ const linked = rest[0] !== false;
191
+ const beneath = linked ? rest : rest.slice(1);
192
+ if (!beneath.every((under) => typeof under === "string")) return void 0;
193
+ return isAttributeValue(original) && isAttributeValue(written) ? { original, written, linked, beneath } : void 0;
194
+ } catch {
195
+ return void 0;
84
196
  }
197
+ }
198
+ function attributeStack(element, attribute) {
199
+ const byAttribute = registry.stacks.get(element) ?? /* @__PURE__ */ new Map();
200
+ registry.stacks.set(element, byAttribute);
201
+ const known = byAttribute.get(attribute);
202
+ if (known) return known;
203
+ const found = [];
204
+ for (const record of registry.names.get(attribute) ?? []) {
205
+ const read = readLeaseRecord(element, record);
206
+ if (read) found.push({ ...read, record });
207
+ }
208
+ const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
209
+ const stack = found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
210
+ lease: null,
211
+ record,
212
+ written,
213
+ original,
214
+ linked: linked && index > 0
215
+ }));
216
+ byAttribute.set(attribute, stack);
217
+ return stack;
218
+ }
219
+ function findHold(stack, lease, record) {
220
+ return stack.find((held) => held.lease === lease) ?? stack.find((held) => held.lease === null && held.record === record);
221
+ }
222
+ function readAttributeLease(element, attribute, owner, lease, expected) {
223
+ const stack = attributeStack(element, attribute);
224
+ const hold = findHold(stack, lease, `data-${owner}-${attribute}-lease`);
225
+ const current = element.getAttribute(attribute);
226
+ const covering = [...stack].reverse().find((other) => other.written === current);
227
+ const original = (hold ?? covering)?.original;
228
+ const authored = original === void 0 ? current : original;
229
+ return {
230
+ original: authored,
231
+ allowed: hold ? current === hold.written || stack.slice(stack.indexOf(hold) + 1).some((above) => above.written === current) : authored === expected
232
+ };
233
+ }
234
+ var AttributeLease = class {
235
+ #attribute;
236
+ #record;
237
+ #base;
238
+ #preserveAuthor;
239
+ #held = /* @__PURE__ */ new Set();
240
+ constructor(attribute, owner, options = {}) {
241
+ this.#attribute = attribute;
242
+ this.#record = `data-${owner}-${attribute}-lease`;
243
+ this.#base = options.base === true;
244
+ this.#preserveAuthor = options.preserveAuthor === true;
245
+ const names = registry.names;
246
+ names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
247
+ }
248
+ write(element, value) {
249
+ const stack = attributeStack(element, this.#attribute);
250
+ const hold = this.#take(stack) ?? this.#acquire(element, stack);
251
+ this.#held.add(element);
252
+ const shows = !this.#covered(element, stack, hold);
253
+ hold.written = value;
254
+ this.#mark(element, stack);
255
+ if (shows) this.#reflect(element, value);
256
+ }
257
+ return(element) {
258
+ const held = this.#held.delete(element);
259
+ const known = registry.stacks.get(element)?.has(this.#attribute);
260
+ const stack = held || known || element.hasAttribute(this.#record) ? attributeStack(element, this.#attribute) : [];
261
+ const hold = this.#take(stack);
262
+ if (!hold) return false;
263
+ const carries = element.getAttribute(this.#attribute) === hold.written;
264
+ const owns = carries && !this.#covered(element, stack, hold);
265
+ const index = stack.indexOf(hold);
266
+ stack.splice(index, 1);
267
+ const above = stack[index];
268
+ if (above) above.linked &&= hold.linked;
269
+ this.#mark(element, stack);
270
+ if (!owns) return true;
271
+ const below = hold.linked ? stack[index - 1] : void 0;
272
+ this.#reflect(element, below ? below.written : hold.original);
273
+ return true;
274
+ }
275
+ returnAll() {
276
+ for (const element of Array.from(this.#held)) this.return(element);
277
+ }
278
+ #take(stack) {
279
+ const hold = findHold(stack, this, this.#record);
280
+ if (hold) hold.lease = this;
281
+ return hold;
282
+ }
283
+ #acquire(element, stack) {
284
+ const current = element.getAttribute(this.#attribute);
285
+ const top = stack.at(-1);
286
+ const bottom = stack[0];
287
+ const over = top?.written === current ? top : void 0;
288
+ const from = this.#base ? this.#preserveAuthor && !stack.some((held) => held.written === current) ? void 0 : bottom : over;
289
+ const hold = {
290
+ lease: this,
291
+ record: this.#record,
292
+ written: current,
293
+ original: from ? from.original : current,
294
+ linked: !this.#base && over !== void 0
295
+ };
296
+ if (this.#base && bottom) {
297
+ bottom.linked = true;
298
+ stack.unshift(hold);
299
+ } else stack.push(hold);
300
+ return hold;
301
+ }
302
+ #covered(element, stack, hold) {
303
+ const current = element.getAttribute(this.#attribute);
304
+ return stack.slice(stack.indexOf(hold) + 1).some((above) => above.written === current);
305
+ }
306
+ #mark(element, stack) {
307
+ const records = /* @__PURE__ */ new Map([[this.#record, null]]);
308
+ stack.forEach((hold, index) => {
309
+ const names = stack.slice(0, index).map((under) => under.record);
310
+ const beneath = [...new Set(names.reverse())].filter((name) => name !== hold.record);
311
+ const marker = hold.linked || beneath.length === 0 ? [] : [false];
312
+ records.set(
313
+ hold.record,
314
+ hold.written === hold.original && beneath.length === 0 && index === stack.length - 1 ? null : JSON.stringify([hold.original, hold.written, ...marker, ...beneath])
315
+ );
316
+ });
317
+ for (const [name, recorded] of records) {
318
+ setAttributeIfChanged(element, name, recorded);
319
+ }
320
+ }
321
+ #reflect(element, value) {
322
+ setAttributeIfChanged(element, this.#attribute, value);
323
+ }
324
+ };
325
+
326
+ var TemplateAttributeLease = class {
327
+ #lease;
328
+ #record;
329
+ #attribute;
330
+ #owner;
331
+ #states = /* @__PURE__ */ new WeakMap();
332
+ constructor(attribute, owner) {
333
+ this.#attribute = attribute;
334
+ this.#owner = owner;
335
+ this.#lease = new AttributeLease(attribute, owner, { base: true, preserveAuthor: true });
336
+ this.#record = `data-${owner}-${attribute}-template`;
337
+ }
338
+ render(element, source, value) {
339
+ let state = this.#read(element);
340
+ if (!state && source.length === 0) {
341
+ this.#lease.return(element);
342
+ return;
343
+ }
344
+ if (!state || state.source !== source) {
345
+ this.#lease.return(element);
346
+ state = { source, expected: this.#view(element, null).original, yielded: false };
347
+ }
348
+ if (source.length === 0 || value === null) {
349
+ this.#states.set(element, state);
350
+ this.release(element);
351
+ return;
352
+ }
353
+ if (!state.yielded && this.#continues(element, state)) {
354
+ this.#lease.write(element, value);
355
+ state.expected = value;
356
+ } else {
357
+ state.yielded = true;
358
+ this.#lease.return(element);
359
+ }
360
+ this.#mark(element, state);
361
+ }
362
+ release(element) {
363
+ const state = this.#read(element);
364
+ if (state && !state.yielded) {
365
+ state.yielded = !this.#continues(element, state);
366
+ if (!state.yielded) state.expected = this.#view(element, null).original;
367
+ }
368
+ this.#lease.return(element);
369
+ if (state) this.#mark(element, state);
370
+ }
371
+ #continues(element, state) {
372
+ const view = this.#view(element, state.expected);
373
+ return view.allowed || !element.hasAttribute(this.#record) && element.getAttribute(this.#attribute) === view.original;
374
+ }
375
+ #view(element, expected) {
376
+ return readAttributeLease(element, this.#attribute, this.#owner, this.#lease, expected);
377
+ }
378
+ #read(element) {
379
+ const live = this.#states.get(element);
380
+ if (live) return live;
381
+ try {
382
+ const parsed = JSON.parse(element.getAttribute(this.#record) ?? "null");
383
+ if (!Array.isArray(parsed) || parsed.length !== 3) return void 0;
384
+ const [source, expected, yielded] = parsed;
385
+ if (typeof source !== "string" || expected !== null && typeof expected !== "string" || typeof yielded !== "boolean")
386
+ return void 0;
387
+ return { source, expected, yielded };
388
+ } catch {
389
+ return void 0;
390
+ }
391
+ }
392
+ #mark(element, state) {
393
+ this.#states.set(element, state);
394
+ setAttributeIfChanged(
395
+ element,
396
+ this.#record,
397
+ JSON.stringify([state.source, state.expected, state.yielded])
398
+ );
399
+ }
400
+ };
401
+
402
+ var MeterController = class _MeterController extends Controller {
403
+ #numbers = new NumberValueReader();
404
+ #valueText = new TemplateAttributeLease("aria-valuetext", this.identifier);
85
405
  static targets = ["bar"];
86
406
  static values = {
87
407
  announceText: { type: String, default: "" },
@@ -93,129 +413,139 @@ var MeterController = class extends Controller {
93
413
  optimum: { type: Number, default: 0 },
94
414
  valueText: { type: String, default: "" }
95
415
  };
416
+ static valueConstraints = {
417
+ value: NUMBER_BOUNDS.finite,
418
+ min: NUMBER_BOUNDS.finite,
419
+ max: NUMBER_BOUNDS.finite,
420
+ low: NUMBER_BOUNDS.finite,
421
+ high: NUMBER_BOUNDS.finite,
422
+ optimum: NUMBER_BOUNDS.finite
423
+ };
96
424
  static actions = ["setValue"];
97
425
  static events = ["change"];
98
- /**
99
- * Collapses a morph that swaps several render inputs at once into one repaint.
100
- * A single update usually rewrites the whole set, and each Value would otherwise
101
- * repaint on its own.
102
- */
103
- #repaint = new MicrotaskCoalescer(() => {
426
+ #repaint = new MorphRenderWatcher(() => {
104
427
  this.#render();
105
428
  });
106
- /** The segment last announced, so only a change is read out. */
107
- #announcedState = null;
429
+ #shownState = null;
108
430
  connect() {
109
- this.#repaint.activate();
431
+ this.#repaint.observe(this.element);
110
432
  this.#render();
111
433
  }
112
- /** Closes the window in which a queued repaint may still run. */
113
434
  disconnect() {
114
- this.#repaint.cancel();
435
+ this.#repaint.disconnect();
436
+ this.#valueText.release(this.element);
115
437
  }
116
- /**
117
- * Updates the measured value from an action param (`amount`) or a
118
- * `detail.value` CustomEvent, syncs ARIA and `data-state`, and dispatches
119
- * `change` with the value, ratio, and computed segment.
120
- */
121
438
  setValue(event) {
122
- const next = toFiniteNumber(event.params?.amount ?? event.detail?.value);
439
+ const next = eventNumber(event);
123
440
  if (next === null) return;
124
441
  this.valueValue = this.#clamp(next);
442
+ const shown = this.#shownState;
125
443
  const reading = this.#render();
126
444
  this.dispatch("change", { detail: reading });
127
- if (reading.state !== this.#announcedState) {
128
- this.#announcedState = reading.state;
445
+ if (reading.state !== shown) {
129
446
  announce(
130
447
  fillTemplate(this.announceTextValue, { state: reading.state, value: reading.value })
131
448
  );
132
449
  }
133
450
  }
134
- /** Repaints when application code (or a Turbo morph) changes `value` at runtime. */
135
451
  valueValueChanged() {
136
452
  this.#repaint.schedule();
137
453
  }
138
- /** Repaints when application code (or a Turbo morph) changes `min` at runtime. */
139
454
  minValueChanged() {
140
455
  this.#repaint.schedule();
141
456
  }
142
- /** Repaints when application code (or a Turbo morph) changes `max` at runtime. */
143
457
  maxValueChanged() {
144
458
  this.#repaint.schedule();
145
459
  }
146
- /** Repaints when application code (or a Turbo morph) changes `low` at runtime. */
147
460
  lowValueChanged() {
148
461
  this.#repaint.schedule();
149
462
  }
150
- /** Repaints when application code (or a Turbo morph) changes `high` at runtime. */
151
463
  highValueChanged() {
152
464
  this.#repaint.schedule();
153
465
  }
154
- /** Repaints when application code (or a Turbo morph) changes `valueText` at runtime. */
155
466
  valueTextValueChanged() {
156
467
  this.#repaint.schedule();
157
468
  }
158
- /** Clamps `raw` into the configured `[min, max]` range. */
159
469
  #clamp(raw) {
160
- return Math.min(this.maxValue, Math.max(this.minValue, raw));
470
+ return clampRange(raw, this.#safeMin, this.#safeMax);
161
471
  }
162
- /** Whether a threshold attribute is present (absent = no threshold). */
163
472
  #hasThreshold(name) {
164
473
  return this.element.hasAttribute(`data-${this.identifier}-${name}-value`);
165
474
  }
166
- /**
167
- * Classifies `value` into a `low`/`medium`/`high` segment. Values at or below
168
- * `low` are `low`; at or above `high` are `high`; otherwise `medium`. With
169
- * neither threshold present, everything is `medium`.
170
- */
171
475
  #stateOf(value) {
172
- if (this.#hasThreshold("low") && value <= this.lowValue) return "low";
173
- if (this.#hasThreshold("high") && value >= this.highValue) return "high";
476
+ if (this.#hasThreshold("low") && value <= this.#safeLow) return "low";
477
+ if (this.#hasThreshold("high") && value >= this.#safeHigh) return "high";
174
478
  return "medium";
175
479
  }
176
- /**
177
- * Reflects value/range onto ARIA, the segment onto `data-state`, and the ratio.
178
- * The reading is derived once and returned, so the `change` detail reports the
179
- * same numbers the DOM just received.
180
- *
181
- * @stimeoRenderRoot
182
- */
183
480
  #render() {
184
- const value = this.#clamp(this.valueValue);
481
+ const value = this.#clamp(this.#safeValue);
185
482
  const reading = {
186
483
  value,
187
- ratio: rangeFraction(value, this.minValue, this.maxValue),
484
+ ratio: rangeFraction(value, this.#safeMin, this.#safeMax),
188
485
  state: this.#stateOf(value)
189
486
  };
190
- this.element.setAttribute("aria-valuemin", String(this.minValue));
191
- this.element.setAttribute("aria-valuemax", String(this.maxValue));
487
+ this.element.setAttribute("aria-valuemin", String(this.#safeMin));
488
+ this.element.setAttribute("aria-valuemax", String(this.#safeMax));
192
489
  this.element.setAttribute("aria-valuenow", String(reading.value));
193
490
  this.element.style.setProperty("--stimeo--meter-ratio", String(reading.ratio));
194
491
  this.element.setAttribute("data-state", reading.state);
195
492
  this.#applyValueText(reading);
493
+ this.#shownState = reading.state;
196
494
  return reading;
197
495
  }
198
- /**
199
- * Sets `aria-valuetext` from the consumer-provided template, substituting
200
- * `{value}`, `{percent}`, and `{state}`. Kept i18n-neutral in the library.
201
- * With no template the attribute belongs to the consumer, so only a text this
202
- * controller wrote is taken back ({@link OWNED_VALUE_TEXT}).
203
- */
204
496
  #applyValueText({ value, ratio, state }) {
205
- if (this.valueTextValue.length === 0) {
206
- if (this.element.hasAttribute(this.#ownedValueText)) {
207
- this.element.removeAttribute("aria-valuetext");
208
- this.element.removeAttribute(this.#ownedValueText);
209
- }
210
- return;
211
- }
212
- const percent = Math.round(ratio * 100);
213
- const text = this.valueTextValue.replaceAll("{value}", String(value)).replaceAll("{percent}", String(percent)).replaceAll("{state}", state);
214
- this.element.setAttribute("aria-valuetext", text);
215
- this.element.setAttribute(this.#ownedValueText, "");
497
+ this.#valueText.render(
498
+ this.element,
499
+ this.valueTextValue,
500
+ fillTemplate(this.valueTextValue, { value, percent: Math.round(ratio * 100), state })
501
+ );
502
+ }
503
+ get #safeValue() {
504
+ return this.#numbers.read(
505
+ this,
506
+ "value",
507
+ this.valueValue,
508
+ _MeterController.values.value.default,
509
+ _MeterController.valueConstraints.value
510
+ );
511
+ }
512
+ get #safeMin() {
513
+ return this.#numbers.read(
514
+ this,
515
+ "min",
516
+ this.minValue,
517
+ _MeterController.values.min.default,
518
+ _MeterController.valueConstraints.min
519
+ );
520
+ }
521
+ get #safeMax() {
522
+ return this.#numbers.read(
523
+ this,
524
+ "max",
525
+ this.maxValue,
526
+ _MeterController.values.max.default,
527
+ _MeterController.valueConstraints.max
528
+ );
529
+ }
530
+ get #safeLow() {
531
+ return this.#numbers.read(
532
+ this,
533
+ "low",
534
+ this.lowValue,
535
+ _MeterController.values.low.default,
536
+ _MeterController.valueConstraints.low
537
+ );
538
+ }
539
+ get #safeHigh() {
540
+ return this.#numbers.read(
541
+ this,
542
+ "high",
543
+ this.highValue,
544
+ _MeterController.values.high.default,
545
+ _MeterController.valueConstraints.high
546
+ );
216
547
  }
217
548
  };
218
549
 
219
550
  export { MeterController };
220
- //# sourceMappingURL=meter_controller.js.map
221
551
  //# sourceMappingURL=meter_controller.js.map