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,89 +1,192 @@
1
1
  import { Controller } from '@hotwired/stimulus';
2
2
 
3
- // src/controllers/rating_controller.ts
3
+ var FOCUS_EVENTS = /* @__PURE__ */ new Set(["blur", "focus", "focusin", "focusout"]);
4
+ var POINTER_EVENTS = /* @__PURE__ */ new Set(["mouseenter", "mouseleave", "pointerenter", "pointerleave"]);
5
+ function stateReasonFor(event) {
6
+ if (!event) return "api";
7
+ if (FOCUS_EVENTS.has(event.type)) return "focus";
8
+ if (POINTER_EVENTS.has(event.type)) return "pointer";
9
+ return "user";
10
+ }
11
+
12
+ function actionSource(source) {
13
+ if (source instanceof HTMLElement) {
14
+ return { event: null, host: source, origin: source, reason: "api" };
15
+ }
16
+ const event = source ?? null;
17
+ return {
18
+ event,
19
+ host: event?.currentTarget instanceof Element ? event.currentTarget : null,
20
+ origin: event?.target instanceof Element ? event.target : null,
21
+ reason: stateReasonFor(event)
22
+ };
23
+ }
4
24
 
5
- // src/utils/logical_scroll.ts
6
25
  function isRtl(element) {
7
26
  return window.getComputedStyle(element).direction === "rtl";
8
27
  }
9
28
 
10
- // src/utils/arrow_step.ts
11
29
  function isReservedArrowChord(event, allow = []) {
12
30
  if (!event.key.startsWith("Arrow")) return false;
13
31
  return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
14
32
  }
33
+ function hasModifierChord(event) {
34
+ return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
35
+ }
36
+
37
+ function setAttributeIfChanged(element, name, value, beforeWrite) {
38
+ if (element.getAttribute(name) === value) return false;
39
+ if (value === null) element.removeAttribute(name);
40
+ else element.setAttribute(name, value);
41
+ return true;
42
+ }
43
+
44
+ function sharedRegistry(key, create) {
45
+ const symbol = Symbol.for(key);
46
+ const scope = globalThis;
47
+ const existing = scope[symbol];
48
+ if (existing !== void 0) return existing;
49
+ const registry2 = create();
50
+ Object.defineProperty(globalThis, symbol, { value: registry2 });
51
+ return registry2;
52
+ }
15
53
 
16
- // src/utils/attribute_lease.ts
54
+ var registry = sharedRegistry(
55
+ "stimeo-ui.attribute-lease.registry.v1",
56
+ () => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
57
+ );
58
+ function isAttributeValue(value) {
59
+ return value === null || typeof value === "string";
60
+ }
61
+ function readLeaseRecord(element, record) {
62
+ try {
63
+ const parsed = JSON.parse(element.getAttribute(record) ?? "null");
64
+ if (!Array.isArray(parsed)) return void 0;
65
+ const [original, written, ...rest] = parsed;
66
+ const linked = rest[0] !== false;
67
+ const beneath = linked ? rest : rest.slice(1);
68
+ if (!beneath.every((under) => typeof under === "string")) return void 0;
69
+ return isAttributeValue(original) && isAttributeValue(written) ? { original, written, linked, beneath } : void 0;
70
+ } catch {
71
+ return void 0;
72
+ }
73
+ }
74
+ function attributeStack(element, attribute) {
75
+ const byAttribute = registry.stacks.get(element) ?? /* @__PURE__ */ new Map();
76
+ registry.stacks.set(element, byAttribute);
77
+ const known = byAttribute.get(attribute);
78
+ if (known) return known;
79
+ const found = [];
80
+ for (const record of registry.names.get(attribute) ?? []) {
81
+ const read = readLeaseRecord(element, record);
82
+ if (read) found.push({ ...read, record });
83
+ }
84
+ const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
85
+ const stack = found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
86
+ lease: null,
87
+ record,
88
+ written,
89
+ original,
90
+ linked: linked && index > 0
91
+ }));
92
+ byAttribute.set(attribute, stack);
93
+ return stack;
94
+ }
95
+ function findHold(stack, lease, record) {
96
+ return stack.find((held) => held.lease === lease) ?? stack.find((held) => held.lease === null && held.record === record);
97
+ }
17
98
  var AttributeLease = class {
18
99
  #attribute;
19
- #records = /* @__PURE__ */ new Map();
20
- /** @param attribute - The attribute whose temporary values this lease owns. */
21
- constructor(attribute) {
100
+ #record;
101
+ #base;
102
+ #preserveAuthor;
103
+ #held = /* @__PURE__ */ new Set();
104
+ constructor(attribute, owner, options = {}) {
22
105
  this.#attribute = attribute;
106
+ this.#record = `data-${owner}-${attribute}-lease`;
107
+ this.#base = options.base === true;
108
+ this.#preserveAuthor = options.preserveAuthor === true;
109
+ const names = registry.names;
110
+ names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
23
111
  }
24
- /** Writes or removes the leased attribute while preserving its authored value. */
25
112
  write(element, value) {
26
- const existing = this.#records.get(element);
27
- if (existing) {
28
- existing.written = value;
29
- } else {
30
- this.#records.set(element, {
31
- original: element.getAttribute(this.#attribute),
32
- written: value
33
- });
34
- }
35
- this.#reflect(element, value);
113
+ const stack = attributeStack(element, this.#attribute);
114
+ const hold = this.#take(stack) ?? this.#acquire(element, stack);
115
+ this.#held.add(element);
116
+ const shows = !this.#covered(element, stack, hold);
117
+ hold.written = value;
118
+ this.#mark(element, stack);
119
+ if (shows) this.#reflect(element, value);
36
120
  }
37
- /** Returns one lease without overwriting a value subsequently authored by a consumer. */
38
121
  return(element) {
39
- const record = this.#records.get(element);
40
- if (!record) return;
41
- this.#records.delete(element);
42
- const stillOwned = element.getAttribute(this.#attribute) === record.written;
43
- if (stillOwned) this.#reflect(element, record.original);
122
+ const held = this.#held.delete(element);
123
+ const known = registry.stacks.get(element)?.has(this.#attribute);
124
+ const stack = held || known || element.hasAttribute(this.#record) ? attributeStack(element, this.#attribute) : [];
125
+ const hold = this.#take(stack);
126
+ if (!hold) return false;
127
+ const carries = element.getAttribute(this.#attribute) === hold.written;
128
+ const owns = carries && !this.#covered(element, stack, hold);
129
+ const index = stack.indexOf(hold);
130
+ stack.splice(index, 1);
131
+ const above = stack[index];
132
+ if (above) above.linked &&= hold.linked;
133
+ this.#mark(element, stack);
134
+ if (!owns) return true;
135
+ const below = hold.linked ? stack[index - 1] : void 0;
136
+ this.#reflect(element, below ? below.written : hold.original);
137
+ return true;
44
138
  }
45
- /** Reflects only a real value transition, avoiding self-triggered mutation work. */
46
- #reflect(element, value) {
47
- if (element.getAttribute(this.#attribute) === value) return;
48
- if (value === null) element.removeAttribute(this.#attribute);
49
- else element.setAttribute(this.#attribute, value);
50
- }
51
- /** Returns every outstanding lease using the same ownership check as {@link return}. */
52
139
  returnAll() {
53
- for (const element of Array.from(this.#records.keys())) this.return(element);
54
- }
55
- };
56
-
57
- // src/utils/before_cache_reset.ts
58
- var BeforeCacheReset = class _BeforeCacheReset {
59
- /** Every subscribed instance, iterated by the one shared document listener. */
60
- static #subscribers = /* @__PURE__ */ new Set();
61
- /** The shared listener; installed while at least one instance is subscribed. */
62
- static #onBeforeCache = () => {
63
- for (const subscriber of _BeforeCacheReset.#subscribers) subscriber.#rewind();
64
- };
65
- #rewind;
66
- /** @param rewind - the pass that returns this controller's state to its initial form. */
67
- constructor(rewind) {
68
- this.#rewind = rewind;
69
- }
70
- /** Subscribes to `turbo:before-cache`; call from `connect()`. Idempotent. */
71
- activate() {
72
- const first = _BeforeCacheReset.#subscribers.size === 0;
73
- _BeforeCacheReset.#subscribers.add(this);
74
- if (first) {
75
- document.addEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
140
+ for (const element of Array.from(this.#held)) this.return(element);
141
+ }
142
+ #take(stack) {
143
+ const hold = findHold(stack, this, this.#record);
144
+ if (hold) hold.lease = this;
145
+ return hold;
146
+ }
147
+ #acquire(element, stack) {
148
+ const current = element.getAttribute(this.#attribute);
149
+ const top = stack.at(-1);
150
+ const bottom = stack[0];
151
+ const over = top?.written === current ? top : void 0;
152
+ const from = this.#base ? this.#preserveAuthor && !stack.some((held) => held.written === current) ? void 0 : bottom : over;
153
+ const hold = {
154
+ lease: this,
155
+ record: this.#record,
156
+ written: current,
157
+ original: from ? from.original : current,
158
+ linked: !this.#base && over !== void 0
159
+ };
160
+ if (this.#base && bottom) {
161
+ bottom.linked = true;
162
+ stack.unshift(hold);
163
+ } else stack.push(hold);
164
+ return hold;
165
+ }
166
+ #covered(element, stack, hold) {
167
+ const current = element.getAttribute(this.#attribute);
168
+ return stack.slice(stack.indexOf(hold) + 1).some((above) => above.written === current);
169
+ }
170
+ #mark(element, stack) {
171
+ const records = /* @__PURE__ */ new Map([[this.#record, null]]);
172
+ stack.forEach((hold, index) => {
173
+ const names = stack.slice(0, index).map((under) => under.record);
174
+ const beneath = [...new Set(names.reverse())].filter((name) => name !== hold.record);
175
+ const marker = hold.linked || beneath.length === 0 ? [] : [false];
176
+ records.set(
177
+ hold.record,
178
+ hold.written === hold.original && beneath.length === 0 && index === stack.length - 1 ? null : JSON.stringify([hold.original, hold.written, ...marker, ...beneath])
179
+ );
180
+ });
181
+ for (const [name, recorded] of records) {
182
+ setAttributeIfChanged(element, name, recorded);
76
183
  }
77
184
  }
78
- /** Unsubscribes; call from `disconnect()`. Safe when never subscribed. */
79
- deactivate() {
80
- _BeforeCacheReset.#subscribers.delete(this);
81
- if (_BeforeCacheReset.#subscribers.size > 0) return;
82
- document.removeEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
185
+ #reflect(element, value) {
186
+ setAttributeIfChanged(element, this.#attribute, value);
83
187
  }
84
188
  };
85
189
 
86
- // src/utils/field_mirror.ts
87
190
  function writeField(field, value) {
88
191
  if (field.value === value) return false;
89
192
  field.value = value;
@@ -93,27 +196,36 @@ function commitField(target) {
93
196
  target.dispatchEvent(new Event("change", { bubbles: true }));
94
197
  }
95
198
 
96
- // src/utils/microtask_coalescer.ts
199
+ var ListenerSet = class {
200
+ #abort = new AbortController();
201
+ add(target, type, handler, options) {
202
+ target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
203
+ }
204
+ dispose() {
205
+ this.#abort.abort();
206
+ this.#abort = new AbortController();
207
+ }
208
+ };
209
+
97
210
  var MicrotaskCoalescer = class {
98
211
  #run;
99
212
  #queued = false;
100
213
  #active = false;
101
214
  #generation = 0;
102
- /** @param run - the single reconciliation pass, invoked at most once per batch. */
103
215
  constructor(run) {
104
216
  this.#run = run;
105
217
  }
106
- /** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
107
218
  activate() {
108
219
  this.#active = true;
109
220
  }
110
- /** Closes the window and drops any pending pass; call from `disconnect()`. */
111
221
  cancel() {
112
222
  this.#active = false;
113
223
  this.#queued = false;
114
224
  this.#generation += 1;
115
225
  }
116
- /** Requests one pass after the batch settles. Idempotent; inert outside the window. */
226
+ get pending() {
227
+ return this.#queued;
228
+ }
117
229
  schedule() {
118
230
  if (!this.#active || this.#queued) return;
119
231
  this.#queued = true;
@@ -126,31 +238,97 @@ var MicrotaskCoalescer = class {
126
238
  }
127
239
  };
128
240
 
129
- // src/utils/roving_tabindex.ts
241
+ var MorphRenderWatcher = class {
242
+ #listeners = new ListenerSet();
243
+ #pass;
244
+ #element = null;
245
+ #onMorph = () => this.#pass.schedule();
246
+ constructor(run) {
247
+ this.#pass = new MicrotaskCoalescer(run);
248
+ }
249
+ observe(element) {
250
+ if (this.#element === element) return;
251
+ this.disconnect();
252
+ this.#element = element;
253
+ this.#pass.activate();
254
+ this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
255
+ }
256
+ schedule() {
257
+ this.#pass.schedule();
258
+ }
259
+ get pending() {
260
+ return this.#pass.pending;
261
+ }
262
+ disconnect() {
263
+ this.#listeners.dispose();
264
+ this.#pass.cancel();
265
+ this.#element = null;
266
+ }
267
+ };
268
+
269
+ var MoveCounter = class {
270
+ #sequence = 0;
271
+ record() {
272
+ this.#sequence += 1;
273
+ return this.#sequence;
274
+ }
275
+ isLatest(token) {
276
+ return token === this.#sequence;
277
+ }
278
+ };
279
+
280
+ var NUMBER_BOUNDS = {
281
+ finite: { finite: true }};
282
+ function matchesNumberBounds(value, bounds) {
283
+ if (!Number.isFinite(value)) {
284
+ const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
285
+ if (direction === null) return false;
286
+ if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
287
+ }
288
+ if (bounds.min !== void 0 && value < bounds.min) return false;
289
+ if (bounds.max !== void 0 && value > bounds.max) return false;
290
+ if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
291
+ if (bounds.integer && !Number.isInteger(value)) return false;
292
+ if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
293
+ return true;
294
+ }
295
+
296
+ function readNumber(raw, fallback, bounds) {
297
+ return matchesNumberBounds(raw, bounds) ? raw : fallback;
298
+ }
299
+
300
+ var NumberValueReader = class {
301
+ #lastRejected = /* @__PURE__ */ new Map();
302
+ read(owner, name, raw, fallback, bounds) {
303
+ const resolved = readNumber(raw, fallback, bounds);
304
+ if (matchesNumberBounds(raw, bounds)) {
305
+ this.#lastRejected.delete(name);
306
+ return resolved;
307
+ }
308
+ const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
309
+ const literal = owner.element.getAttribute(attribute);
310
+ if (literal === null) {
311
+ this.#lastRejected.delete(name);
312
+ return resolved;
313
+ }
314
+ if (this.#lastRejected.get(name) !== literal) {
315
+ this.#lastRejected.set(name, literal);
316
+ console.warn(
317
+ `Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
318
+ );
319
+ }
320
+ return resolved;
321
+ }
322
+ };
323
+
130
324
  var RovingTabindex = class {
131
- /** Returns the current ordered item elements; called on every operation. */
132
325
  #getItems;
133
- /**
134
- * @param getItems - Returns the current ordered item elements. Called on every
135
- * operation so the live target list is always used.
136
- */
137
326
  constructor(getItems) {
138
327
  this.#getItems = getItems;
139
328
  }
140
- /** Index of the currently tabbable item (`tabindex="0"`), or `-1` if none. */
141
329
  get activeIndex() {
142
330
  return this.#getItems().findIndex((item) => item.tabIndex === 0);
143
331
  }
144
- /**
145
- * Makes exactly the item at `index` tabbable (`tabindex="0"`) and removes every
146
- * other item from the Tab sequence (`tabindex="-1"`). An out-of-range `index`
147
- * (e.g. `-1`) leaves all items at `-1`, which a controller can use to express
148
- * "nothing is currently tabbable".
149
- *
150
- * @param index - Position of the item to make tabbable.
151
- * @param options - Pass `{ focus: true }` to also move DOM focus to that item,
152
- * and `items` to reuse an event-scoped collection snapshot.
153
- */
154
332
  setActive(index, options = {}) {
155
333
  const { focus = false } = options;
156
334
  const items = options.items ?? this.#getItems();
@@ -160,131 +338,234 @@ var RovingTabindex = class {
160
338
  if (focus) items[index]?.focus();
161
339
  }
162
340
  };
341
+ function deepActiveElement(ownerDocument) {
342
+ let active = ownerDocument.activeElement;
343
+ while (active?.shadowRoot?.activeElement) active = active.shadowRoot.activeElement;
344
+ return active;
345
+ }
163
346
 
164
- // src/utils/tabindex_loan.ts
165
- var TabindexLoan = class {
347
+ function queryOpenDescendants(root, selector) {
348
+ const matches = [];
349
+ const roots = [root];
350
+ for (const scope of roots) {
351
+ matches.push(...scope.querySelectorAll(selector));
352
+ if (scope.nodeType === Node.ELEMENT_NODE) {
353
+ const shadow = scope.shadowRoot;
354
+ if (shadow) roots.push(shadow);
355
+ }
356
+ for (const element of scope.querySelectorAll("*")) {
357
+ if (element.shadowRoot) roots.push(element.shadowRoot);
358
+ }
359
+ }
360
+ return matches;
361
+ }
362
+
363
+ function holdsFocus(element) {
364
+ return element.matches(":focus") || deepActiveElement(element.ownerDocument) === element;
365
+ }
366
+ var TabindexLoan = class _TabindexLoan {
367
+ static #registry = sharedRegistry(
368
+ "stimeo-ui.tabindex-loan.registry.v1",
369
+ () => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Set() })
370
+ );
166
371
  #value;
372
+ #record;
373
+ #preserveFocus;
167
374
  #lent = /* @__PURE__ */ new Set();
168
- /** Returns live loans before Turbo can copy them into its page snapshot. */
169
- #beforeCache = new BeforeCacheReset(() => this.returnAll());
170
- /**
171
- * @param value - the `tabindex` to lend. `"-1"` (the default) is
172
- * programmatically focusable but not a Tab stop; `"0"` is a real Tab stop,
173
- * which a scroll region with no focusable content of its own needs.
174
- */
175
- constructor(value = "-1") {
375
+ constructor(value, owner, options = {}) {
176
376
  this.#value = value;
177
- }
178
- /** Lends `element` the value; no-ops when it already carries a `tabindex`. */
179
- lend(element) {
180
- if (element.hasAttribute("tabindex")) return;
181
- element.setAttribute("tabindex", this.#value);
377
+ this.#record = `data-${owner}-tabindex-loan`;
378
+ this.#preserveFocus = options.preserveFocus === true;
379
+ _TabindexLoan.#registry.names.add(this.#record);
380
+ }
381
+ lend(element, options = {}) {
382
+ const stack = this.#stack(element);
383
+ const current = element.getAttribute("tabindex");
384
+ const held = stack.find((hold) => hold.loan === this) ?? stack.find(
385
+ (hold) => hold.loan === null && hold.record === this.#record && hold.value === this.#value
386
+ );
387
+ const linked = stack.at(-1)?.value === current;
388
+ if (!held && options.onlyIfUnfocusable && (current !== null || element.tabIndex >= 0)) return;
389
+ if (!held && current !== null && !linked) return;
182
390
  this.#lent.add(element);
183
- this.#beforeCache.activate();
391
+ if (held) held.loan = this;
392
+ else stack.push({ loan: this, record: this.#record, value: this.#value, linked });
393
+ this.#mark(element, stack);
394
+ if (current === null || !held && current !== this.#value) {
395
+ element.setAttribute("tabindex", this.#value);
396
+ }
397
+ }
398
+ return(element) {
399
+ const stack = this.#stack(element);
400
+ const hold = stack.find((held) => held.loan === this) ?? stack.find(
401
+ (held) => held.loan === null && held.record === this.#record && held.value === this.#value
402
+ );
403
+ if (!hold) return false;
404
+ if (!this.#release(element, stack, hold, this.#preserveFocus)) {
405
+ hold.loan = this;
406
+ this.#lent.add(element);
407
+ return false;
408
+ }
409
+ this.#lent.delete(element);
410
+ return true;
184
411
  }
185
- /** Takes back every loan whose value is still the one that was lent. */
186
412
  returnAll() {
187
- for (const element of this.#lent) {
188
- if (element.getAttribute("tabindex") === this.#value) element.removeAttribute("tabindex");
413
+ for (const element of Array.from(this.#lent)) this.return(element);
414
+ }
415
+ reclaim(element) {
416
+ if (element.getAttribute(this.#record) !== this.#value) return;
417
+ const stack = this.#stack(element);
418
+ const hold = stack.find((held) => held.loan === null && held.record === this.#record);
419
+ if (hold && !holdsFocus(element)) this.#release(element, stack, hold);
420
+ }
421
+ reclaimWithin(root) {
422
+ for (const element of queryOpenDescendants(root, `[${this.#record}]`)) {
423
+ this.reclaim(element);
424
+ }
425
+ }
426
+ #stack(element) {
427
+ const stacks = _TabindexLoan.#registry.stacks;
428
+ const stack = stacks.get(element) ?? this.#seed(element);
429
+ stacks.set(element, stack);
430
+ return stack;
431
+ }
432
+ #seed(element) {
433
+ const current = element.getAttribute("tabindex");
434
+ const holds = [];
435
+ for (const record of _TabindexLoan.#registry.names) {
436
+ const value = element.getAttribute(record);
437
+ if (value !== null) holds.push({ loan: null, record, value, linked: true });
438
+ }
439
+ holds.sort((one, other) => Number(one.value === current) - Number(other.value === current));
440
+ for (const [index, hold] of holds.entries()) hold.linked = index > 0;
441
+ return holds;
442
+ }
443
+ #release(element, stack, hold, preserveFocus = false) {
444
+ const current = element.getAttribute("tabindex");
445
+ const index = stack.indexOf(hold);
446
+ const owns = current === hold.value && !stack.slice(index + 1).some((above2) => above2.value === current);
447
+ if (owns && preserveFocus && holdsFocus(element)) return false;
448
+ stack.splice(index, 1);
449
+ const above = stack[index];
450
+ if (above) above.linked &&= hold.linked;
451
+ this.#mark(element, stack);
452
+ if (!owns) return true;
453
+ const below = hold.linked ? stack[index - 1] : void 0;
454
+ if (!below) element.removeAttribute("tabindex");
455
+ else if (below.value !== current) element.setAttribute("tabindex", below.value);
456
+ return true;
457
+ }
458
+ #mark(element, stack) {
459
+ const records = /* @__PURE__ */ new Map([[this.#record, null]]);
460
+ for (const hold of stack) records.set(hold.record, hold.value);
461
+ for (const [name, recorded] of records) {
462
+ setAttributeIfChanged(element, name, recorded);
189
463
  }
190
- this.#lent.clear();
191
- this.#beforeCache.deactivate();
192
464
  }
193
465
  };
194
466
 
195
- // src/controllers/rating_controller.ts
196
- var RatingController = class extends Controller {
467
+ function targetSelector(identifier, name) {
468
+ return `[data-${identifier}-target~="${name}"]`;
469
+ }
470
+
471
+ var RatingController = class _RatingController extends Controller {
472
+ #numbers = new NumberValueReader();
473
+ #moves = new MoveCounter();
474
+ #move = 0;
197
475
  static targets = ["symbol", "field"];
198
476
  static values = {
199
477
  value: { type: Number, default: 0 },
200
478
  clearable: { type: Boolean, default: true },
201
479
  readonly: { type: Boolean, default: false }
202
480
  };
481
+ static valueConstraints = {
482
+ value: NUMBER_BOUNDS.finite
483
+ };
203
484
  static actions = ["endPreview", "onKeydown", "preview", "select"];
204
485
  static events = ["change", "reconcile"];
205
486
  #roving = new RovingTabindex(() => this.symbolTargets);
206
- #rootRole = new AttributeLease("role");
207
- #symbolRole = new AttributeLease("role");
208
- #symbolAriaHidden = new AttributeLease("aria-hidden");
209
- #rootTabindex = new TabindexLoan();
210
- #repaint = new MicrotaskCoalescer(() => this.#reconcileScale());
211
- #beforeCache = new BeforeCacheReset(() => this.#rewindForCache());
487
+ #rootRole = new AttributeLease("role", this.identifier);
488
+ #symbolRole = new AttributeLease("role", this.identifier);
489
+ #symbolAriaHidden = new AttributeLease("aria-hidden", this.identifier);
490
+ #rootTabindex = new TabindexLoan("-1", this.identifier);
491
+ #repaint = new MorphRenderWatcher(() => this.#reconcileScale());
212
492
  #connected = false;
213
493
  #rescuedFocus = false;
214
- /** The rating shown last, which the next move is measured from. */
215
494
  #shown = 0;
216
- /** Reflects declarative state without announcing an initial user change. */
217
495
  connect() {
218
- this.#repaint.activate();
219
- this.#beforeCache.activate();
220
- this.#shown = this.#normalize(this.valueValue);
496
+ this.#rootTabindex.reclaim(this.element);
497
+ this.#repaint.observe(this.element);
498
+ this.#shown = this.#normalize(this.#safeValue);
221
499
  this.#apply(this.#shown, { focus: false });
222
500
  this.#connected = true;
223
501
  }
224
- /** Drops a queued reconciliation and hands every borrowed attribute back. */
225
502
  disconnect() {
226
503
  this.#connected = false;
227
- this.#repaint.cancel();
228
- this.#beforeCache.deactivate();
504
+ this.#repaint.disconnect();
229
505
  this.#releaseReadonly();
230
506
  }
231
- /** Removes a runtime-added symbol's authored Tab stop before the batch repaint. */
232
507
  symbolTargetConnected(symbol) {
233
508
  if (this.#connected === false) return;
234
509
  symbol.tabIndex = -1;
235
510
  this.#repaint.schedule();
236
511
  }
237
- /** Releases readonly ownership and reconciles the remaining DOM-ordered scale. */
238
512
  symbolTargetDisconnected(symbol) {
239
513
  this.#symbolRole.return(symbol);
240
514
  this.#symbolAriaHidden.return(symbol);
241
515
  this.#repaint.schedule();
242
516
  }
243
- /** Reflects into a hidden field added or replaced after connection. */
244
517
  fieldTargetConnected() {
245
518
  this.#repaint.schedule();
246
519
  }
247
- /** Reconciles after a hidden field is removed or replaced. */
248
520
  fieldTargetDisconnected() {
249
521
  this.#repaint.schedule();
250
522
  }
251
- /** Repaints when application code or a Turbo morph changes `value`. */
252
523
  valueValueChanged() {
253
524
  this.#repaint.schedule();
254
525
  }
255
- /** Repaints when application code changes whether value 0 is permitted. */
256
526
  clearableValueChanged() {
257
527
  this.#repaint.schedule();
258
528
  }
259
- /** Repaints when application code enters or leaves the readonly snapshot. */
260
529
  readonlyValueChanged() {
261
530
  this.#repaint.schedule();
262
531
  }
263
- /** Selects or clears the clicked symbol. Bound via `data-action` (click). */
264
- select(event) {
532
+ select(source) {
265
533
  if (this.readonlyValue) return;
266
- const ordinal = this.#symbolOrdinal(event.currentTarget);
534
+ const { event, host, origin, reason } = actionSource(source);
535
+ const symbol = host?.closest(targetSelector(this.identifier, "symbol"));
536
+ if (!symbol || !this.symbolTargets.includes(symbol)) return;
537
+ if (origin && origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
538
+ return;
539
+ const moveFocus = event !== null || this.element.contains(document.activeElement);
540
+ const ordinal = this.#symbolOrdinal(symbol);
267
541
  if (ordinal === null) return;
268
- const current = this.#normalize(this.valueValue);
269
- this.#commit(this.clearableValue && ordinal === current ? 0 : ordinal, {
270
- focus: ordinal === current
271
- });
272
- }
273
- /** Previews a fill range on hover or focus without committing it. */
274
- preview(event) {
542
+ const current = this.#normalize(this.#safeValue);
543
+ this.#commit(
544
+ this.clearableValue && ordinal === current ? 0 : ordinal,
545
+ {
546
+ focus: ordinal === current && moveFocus
547
+ },
548
+ reason
549
+ );
550
+ }
551
+ preview(source) {
275
552
  if (this.readonlyValue) return;
276
- const ordinal = this.#symbolOrdinal(event.currentTarget);
553
+ const { host, origin } = actionSource(source);
554
+ const symbol = host?.closest(targetSelector(this.identifier, "symbol"));
555
+ if (!symbol || !this.symbolTargets.includes(symbol)) return;
556
+ if (origin && origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
557
+ return;
558
+ const ordinal = this.#symbolOrdinal(symbol);
277
559
  if (ordinal !== null) this.#setFillRange(ordinal);
278
560
  }
279
- /** Restores the fill range after hover or focus leaves a symbol. */
280
561
  endPreview() {
281
562
  if (this.readonlyValue) return;
282
- this.#setFillRange(this.#normalize(this.valueValue));
563
+ this.#setFillRange(this.#normalize(this.#safeValue));
283
564
  }
284
- /** Arrow/Home/End/Space/Delete keyboard control, clamped without wrapping. */
285
565
  onKeydown(event) {
286
566
  if (event.defaultPrevented || isReservedArrowChord(event) || this.readonlyValue) return;
287
- const current = this.#normalize(this.valueValue);
567
+ if ((event.key === "Home" || event.key === "End") && hasModifierChord(event)) return;
568
+ const current = this.#normalize(this.#safeValue);
288
569
  let next = null;
289
570
  const rtl = isRtl(this.element);
290
571
  switch (event.key) {
@@ -318,69 +599,54 @@ var RatingController = class extends Controller {
318
599
  event.preventDefault();
319
600
  this.#commit(next, { focus: true });
320
601
  }
321
- /**
322
- * Repaints one settled target/Value mutation batch and reports a rating shown
323
- * that moved from the one shown before it.
324
- *
325
- * @stimeoRenderRoot
326
- */
327
602
  #reconcileScale() {
328
- const value = this.#normalize(this.valueValue);
603
+ const value = this.#normalize(this.#safeValue);
329
604
  const previous = this.#shown;
330
605
  this.#shown = value;
606
+ const move = this.#move;
331
607
  this.#apply(value, { focus: false });
608
+ if (!this.#moves.isLatest(move)) return;
332
609
  if (value !== previous) this.dispatch("reconcile", { detail: { value } });
333
610
  }
334
- /**
335
- * Applies one user operation and emits only when the rating shown changes.
336
- *
337
- * The user's value is written into `value` before anything reports it, so a
338
- * listener of the field's native `change` already reads it there, and the pass
339
- * that the write starts finds nothing left to report.
340
- */
341
- #commit(raw, { focus }) {
611
+ #commit(raw, { focus }, reason = "user") {
342
612
  const previous = this.#shown;
343
613
  const value = this.#normalize(raw);
344
614
  if (!Object.is(this.valueValue, value)) this.valueValue = value;
345
615
  this.#shown = value;
346
- this.#apply(value, { focus, notify: true });
347
- if (value !== previous) this.dispatch("change", { detail: { value } });
348
- }
349
- /** Synchronizes ARIA, roving focus, form state, and the visual fill hook. */
350
- #apply(value, { focus, notify = false }) {
616
+ if (value !== previous) this.#move = this.#moves.record();
617
+ const move = this.#move;
618
+ const field = this.#apply(value, { focus });
619
+ if (!this.#moves.isLatest(move)) return;
620
+ if (field) commitField(field);
621
+ if (!this.#moves.isLatest(move)) return;
622
+ if (value !== previous) this.dispatch("change", { detail: { value, reason } });
623
+ }
624
+ #apply(value, { focus }) {
625
+ const move = this.#move;
351
626
  this.symbolTargets.forEach((symbol, index) => {
352
627
  symbol.setAttribute("aria-checked", value > 0 && index + 1 === value ? "true" : "false");
353
628
  });
629
+ const field = this.hasFieldTarget ? this.fieldTarget : null;
630
+ const moved = field && writeField(field, String(value));
631
+ this.#setFillRange(value);
354
632
  if (this.readonlyValue) {
355
633
  this.#applyReadonly();
356
634
  } else {
357
635
  const returning = this.#releaseReadonly();
358
636
  this.#roving.setActive(value > 0 ? value - 1 : 0, { focus: focus || returning });
359
637
  }
360
- this.#mirrorField(value, notify);
361
- this.#setFillRange(value);
362
- }
363
- /** Mirrors `value` into the optional form field, reporting only a user's move. */
364
- #mirrorField(value, notify) {
365
- if (!this.hasFieldTarget) return;
366
- if (writeField(this.fieldTarget, String(value)) && notify) commitField(this.fieldTarget);
638
+ if (this.#moves.isLatest(move)) this.#setFillRange(value);
639
+ return moved ? field : null;
367
640
  }
368
- /** Marks the first `range` symbols with the consumer-owned fill hook. */
369
641
  #setFillRange(range) {
370
642
  this.symbolTargets.forEach((symbol, index) => {
371
643
  symbol.toggleAttribute("data-rating-hover", range > 0 && index < range);
372
644
  });
373
645
  }
374
- /**
375
- * Temporarily turns the radiogroup into a non-interactive image snapshot.
376
- *
377
- * Each lease is returned before it is taken again, so a value the consumer
378
- * wrote while readonly becomes the value the lease restores on release. The
379
- * return is a no-op on an attribute still holding this controller's own
380
- * write, which is the ordinary case.
381
- */
382
646
  #applyReadonly() {
647
+ const move = this.#move;
383
648
  this.#rescueFocus();
649
+ if (!this.#moves.isLatest(move)) return;
384
650
  this.#rootRole.return(this.element);
385
651
  this.#rootRole.write(this.element, "img");
386
652
  for (const symbol of this.symbolTargets) {
@@ -391,69 +657,50 @@ var RatingController = class extends Controller {
391
657
  }
392
658
  this.#roving.setActive(-1);
393
659
  }
394
- /**
395
- * Hands every readonly borrowing back before Turbo clones the page.
396
- *
397
- * The snapshot is taken while the controller is still connected, so an
398
- * element left as `role="img"` with hidden symbols is what a restored page
399
- * connects against — and that markup would be read as the authored one,
400
- * leaving no way back to the radiogroup. Rewinding first keeps the cached
401
- * copy identical to what the consumer wrote.
402
- */
403
- #rewindForCache() {
404
- this.#releaseReadonly();
405
- const value = this.#normalize(this.valueValue);
406
- this.#roving.setActive(value > 0 ? value - 1 : 0);
407
- }
408
- /**
409
- * Lands focus on the root before the symbols leave the accessibility tree.
410
- *
411
- * A symbol holding focus when readonly begins would keep it while losing its
412
- * role and gaining `aria-hidden`, stranding the user on a node no longer in
413
- * the tree. The root is the one element that survives the transition named:
414
- * it carries the consumer's accessible name under `role="img"`.
415
- */
416
660
  #rescueFocus() {
417
661
  const active = document.activeElement;
418
662
  if (!(active instanceof HTMLElement) || active === this.element) return;
419
663
  if (!this.element.contains(active)) return;
420
664
  this.#rootTabindex.lend(this.element);
421
- this.element.focus();
422
665
  this.#rescuedFocus = true;
666
+ this.element.focus();
423
667
  }
424
- /**
425
- * Restores authored roles and visibility after leaving readonly mode.
426
- *
427
- * @returns whether focus is standing on the root because {@link #rescueFocus}
428
- * put it there, and therefore belongs back on the Tab stop.
429
- */
430
668
  #releaseReadonly() {
431
669
  const returning = this.#rescuedFocus && document.activeElement === this.element;
432
670
  this.#rescuedFocus = false;
433
671
  this.#rootRole.return(this.element);
672
+ for (const symbol of this.symbolTargets) {
673
+ this.#symbolRole.return(symbol);
674
+ this.#symbolAriaHidden.return(symbol);
675
+ }
434
676
  this.#symbolRole.returnAll();
435
677
  this.#symbolAriaHidden.returnAll();
436
678
  this.#rootTabindex.returnAll();
437
679
  return returning;
438
680
  }
439
- /** Normalizes a raw value to an integer ordinal in the live DOM range. */
440
681
  #normalize(raw) {
441
682
  const maximum = this.symbolTargets.length;
442
- const ordinal = Number.isFinite(raw) ? Math.round(raw) : this.#minValue;
683
+ const ordinal = Math.round(raw);
443
684
  return Math.min(maximum, Math.max(this.#minValue, ordinal));
444
685
  }
445
- /** Lowest selectable value: 0 when clearable, otherwise 1. */
446
686
  get #minValue() {
447
687
  return this.clearableValue ? 0 : 1;
448
688
  }
449
- /** Returns a target's 1-based position, or null when the action host is invalid. */
450
689
  #symbolOrdinal(target) {
451
690
  const targets = this.symbolTargets;
452
691
  const index = targets.indexOf(target);
453
692
  return index < 0 ? null : index + 1;
454
693
  }
694
+ get #safeValue() {
695
+ return this.#numbers.read(
696
+ this,
697
+ "value",
698
+ this.valueValue,
699
+ _RatingController.values.value.default,
700
+ _RatingController.valueConstraints.value
701
+ );
702
+ }
455
703
  };
456
704
 
457
705
  export { RatingController };
458
- //# sourceMappingURL=rating_controller.js.map
459
706
  //# sourceMappingURL=rating_controller.js.map