stimeo-ui 0.15.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (332) hide show
  1. package/CHANGELOG.md +209 -0
  2. package/README.md +128 -1
  3. package/dist/cable/index.d.ts +79 -23
  4. package/dist/cable/index.js +392 -417
  5. package/dist/cable/index.js.map +1 -1
  6. package/dist/controllers/accordion_controller.d.ts +34 -5
  7. package/dist/controllers/accordion_controller.js +228 -28
  8. package/dist/controllers/accordion_controller.js.map +1 -1
  9. package/dist/controllers/alert_dialog_controller.d.ts +20 -9
  10. package/dist/controllers/alert_dialog_controller.js +862 -221
  11. package/dist/controllers/alert_dialog_controller.js.map +1 -1
  12. package/dist/controllers/announcer_controller.d.ts +38 -7
  13. package/dist/controllers/announcer_controller.js +135 -191
  14. package/dist/controllers/announcer_controller.js.map +1 -1
  15. package/dist/controllers/aspect_ratio_controller.js +0 -10
  16. package/dist/controllers/aspect_ratio_controller.js.map +1 -1
  17. package/dist/controllers/auto_submit_controller.d.ts +30 -6
  18. package/dist/controllers/auto_submit_controller.js +295 -110
  19. package/dist/controllers/auto_submit_controller.js.map +1 -1
  20. package/dist/controllers/avatar_controller.js +172 -106
  21. package/dist/controllers/avatar_controller.js.map +1 -1
  22. package/dist/controllers/breadcrumb_controller.d.ts +10 -7
  23. package/dist/controllers/breadcrumb_controller.js +0 -129
  24. package/dist/controllers/breadcrumb_controller.js.map +1 -1
  25. package/dist/controllers/bulk_select_controller.d.ts +22 -4
  26. package/dist/controllers/bulk_select_controller.js +264 -53
  27. package/dist/controllers/bulk_select_controller.js.map +1 -1
  28. package/dist/controllers/calendar_controller.d.ts +109 -28
  29. package/dist/controllers/calendar_controller.js +346 -180
  30. package/dist/controllers/calendar_controller.js.map +1 -1
  31. package/dist/controllers/carousel_controller.d.ts +99 -27
  32. package/dist/controllers/carousel_controller.js +587 -300
  33. package/dist/controllers/carousel_controller.js.map +1 -1
  34. package/dist/controllers/character_counter_controller.d.ts +18 -2
  35. package/dist/controllers/character_counter_controller.js +271 -143
  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 +77 -75
  39. package/dist/controllers/checkbox_controller.js.map +1 -1
  40. package/dist/controllers/clipboard_controller.d.ts +44 -7
  41. package/dist/controllers/clipboard_controller.js +187 -108
  42. package/dist/controllers/clipboard_controller.js.map +1 -1
  43. package/dist/controllers/collapsible_controller.d.ts +41 -5
  44. package/dist/controllers/collapsible_controller.js +374 -108
  45. package/dist/controllers/collapsible_controller.js.map +1 -1
  46. package/dist/controllers/color_picker_controller.d.ts +47 -14
  47. package/dist/controllers/color_picker_controller.js +183 -127
  48. package/dist/controllers/color_picker_controller.js.map +1 -1
  49. package/dist/controllers/combobox_controller.d.ts +33 -6
  50. package/dist/controllers/combobox_controller.js +426 -109
  51. package/dist/controllers/combobox_controller.js.map +1 -1
  52. package/dist/controllers/command_palette_controller.d.ts +45 -14
  53. package/dist/controllers/command_palette_controller.js +994 -350
  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 +212 -125
  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 +939 -245
  60. package/dist/controllers/confirm_controller.js.map +1 -1
  61. package/dist/controllers/context_menu_controller.d.ts +36 -3
  62. package/dist/controllers/context_menu_controller.js +275 -127
  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 +75 -2
  68. package/dist/controllers/countdown_controller.js +235 -97
  69. package/dist/controllers/countdown_controller.js.map +1 -1
  70. package/dist/controllers/currency_input_controller.d.ts +93 -15
  71. package/dist/controllers/currency_input_controller.js +241 -118
  72. package/dist/controllers/currency_input_controller.js.map +1 -1
  73. package/dist/controllers/data_grid_controller.d.ts +78 -22
  74. package/dist/controllers/data_grid_controller.js +229 -135
  75. package/dist/controllers/data_grid_controller.js.map +1 -1
  76. package/dist/controllers/date_range_picker_controller.d.ts +64 -11
  77. package/dist/controllers/date_range_picker_controller.js +359 -165
  78. package/dist/controllers/date_range_picker_controller.js.map +1 -1
  79. package/dist/controllers/dialog_controller.d.ts +22 -3
  80. package/dist/controllers/dialog_controller.js +887 -225
  81. package/dist/controllers/dialog_controller.js.map +1 -1
  82. package/dist/controllers/direct_upload_controller.d.ts +24 -7
  83. package/dist/controllers/direct_upload_controller.js +80 -142
  84. package/dist/controllers/direct_upload_controller.js.map +1 -1
  85. package/dist/controllers/dirty_form_controller.d.ts +2 -2
  86. package/dist/controllers/dirty_form_controller.js +26 -47
  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 +175 -20
  90. package/dist/controllers/dismissible_controller.js.map +1 -1
  91. package/dist/controllers/drawer_controller.d.ts +52 -16
  92. package/dist/controllers/drawer_controller.js +963 -336
  93. package/dist/controllers/drawer_controller.js.map +1 -1
  94. package/dist/controllers/dropdown_controller.d.ts +39 -3
  95. package/dist/controllers/dropdown_controller.js +342 -85
  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 +182 -58
  99. package/dist/controllers/editable_controller.js.map +1 -1
  100. package/dist/controllers/empty_state_controller.js +67 -53
  101. package/dist/controllers/empty_state_controller.js.map +1 -1
  102. package/dist/controllers/file_dropzone_controller.d.ts +60 -18
  103. package/dist/controllers/file_dropzone_controller.js +426 -229
  104. package/dist/controllers/file_dropzone_controller.js.map +1 -1
  105. package/dist/controllers/filter_controller.d.ts +10 -4
  106. package/dist/controllers/filter_controller.js +78 -36
  107. package/dist/controllers/filter_controller.js.map +1 -1
  108. package/dist/controllers/flash_controller.d.ts +57 -14
  109. package/dist/controllers/flash_controller.js +328 -211
  110. package/dist/controllers/flash_controller.js.map +1 -1
  111. package/dist/controllers/focus_controller.d.ts +12 -3
  112. package/dist/controllers/focus_controller.js +654 -239
  113. package/dist/controllers/focus_controller.js.map +1 -1
  114. package/dist/controllers/form_field_controller.js +153 -136
  115. package/dist/controllers/form_field_controller.js.map +1 -1
  116. package/dist/controllers/form_validation_controller.js +29 -96
  117. package/dist/controllers/form_validation_controller.js.map +1 -1
  118. package/dist/controllers/frame_loading_controller.d.ts +41 -9
  119. package/dist/controllers/frame_loading_controller.js +263 -198
  120. package/dist/controllers/frame_loading_controller.js.map +1 -1
  121. package/dist/controllers/highlight_controller.d.ts +22 -2
  122. package/dist/controllers/highlight_controller.js +119 -70
  123. package/dist/controllers/highlight_controller.js.map +1 -1
  124. package/dist/controllers/hover_card_controller.d.ts +65 -4
  125. package/dist/controllers/hover_card_controller.js +335 -118
  126. package/dist/controllers/hover_card_controller.js.map +1 -1
  127. package/dist/controllers/idle_controller.d.ts +58 -4
  128. package/dist/controllers/idle_controller.js +316 -65
  129. package/dist/controllers/idle_controller.js.map +1 -1
  130. package/dist/controllers/input_mask_controller.d.ts +5 -2
  131. package/dist/controllers/input_mask_controller.js +116 -61
  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 +147 -103
  135. package/dist/controllers/intersection_controller.js.map +1 -1
  136. package/dist/controllers/lazy_frame_controller.d.ts +16 -0
  137. package/dist/controllers/lazy_frame_controller.js +9 -56
  138. package/dist/controllers/lazy_frame_controller.js.map +1 -1
  139. package/dist/controllers/listbox_controller.d.ts +84 -10
  140. package/dist/controllers/listbox_controller.js +413 -153
  141. package/dist/controllers/listbox_controller.js.map +1 -1
  142. package/dist/controllers/local_time_controller.d.ts +1 -1
  143. package/dist/controllers/local_time_controller.js +54 -64
  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 +70 -95
  147. package/dist/controllers/masonry_controller.js.map +1 -1
  148. package/dist/controllers/menu_controller.d.ts +40 -9
  149. package/dist/controllers/menu_controller.js +254 -167
  150. package/dist/controllers/menu_controller.js.map +1 -1
  151. package/dist/controllers/menubar_controller.d.ts +15 -4
  152. package/dist/controllers/menubar_controller.js +206 -331
  153. package/dist/controllers/menubar_controller.js.map +1 -1
  154. package/dist/controllers/meter_controller.d.ts +20 -0
  155. package/dist/controllers/meter_controller.js +159 -71
  156. package/dist/controllers/meter_controller.js.map +1 -1
  157. package/dist/controllers/multi_select_controller.d.ts +50 -7
  158. package/dist/controllers/multi_select_controller.js +797 -388
  159. package/dist/controllers/multi_select_controller.js.map +1 -1
  160. package/dist/controllers/navigation_menu_controller.d.ts +19 -1
  161. package/dist/controllers/navigation_menu_controller.js +156 -231
  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 +373 -174
  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 +241 -45
  168. package/dist/controllers/network_status_controller.js.map +1 -1
  169. package/dist/controllers/number_input_controller.d.ts +62 -10
  170. package/dist/controllers/number_input_controller.js +387 -227
  171. package/dist/controllers/number_input_controller.js.map +1 -1
  172. package/dist/controllers/optimistic_controller.js +28 -75
  173. package/dist/controllers/optimistic_controller.js.map +1 -1
  174. package/dist/controllers/otp_controller.d.ts +38 -7
  175. package/dist/controllers/otp_controller.js +344 -196
  176. package/dist/controllers/otp_controller.js.map +1 -1
  177. package/dist/controllers/overflow_indicator_controller.d.ts +13 -12
  178. package/dist/controllers/overflow_indicator_controller.js +167 -93
  179. package/dist/controllers/overflow_indicator_controller.js.map +1 -1
  180. package/dist/controllers/overflow_menu_controller.d.ts +66 -9
  181. package/dist/controllers/overflow_menu_controller.js +408 -277
  182. package/dist/controllers/overflow_menu_controller.js.map +1 -1
  183. package/dist/controllers/pagination_controller.d.ts +52 -15
  184. package/dist/controllers/pagination_controller.js +342 -120
  185. package/dist/controllers/pagination_controller.js.map +1 -1
  186. package/dist/controllers/password_reveal_controller.d.ts +60 -9
  187. package/dist/controllers/password_reveal_controller.js +424 -99
  188. package/dist/controllers/password_reveal_controller.js.map +1 -1
  189. package/dist/controllers/password_strength_controller.d.ts +14 -5
  190. package/dist/controllers/password_strength_controller.js +99 -153
  191. package/dist/controllers/password_strength_controller.js.map +1 -1
  192. package/dist/controllers/persist_controller.d.ts +11 -4
  193. package/dist/controllers/persist_controller.js +252 -96
  194. package/dist/controllers/persist_controller.js.map +1 -1
  195. package/dist/controllers/pointer_drag_controller.d.ts +19 -7
  196. package/dist/controllers/pointer_drag_controller.js +263 -164
  197. package/dist/controllers/pointer_drag_controller.js.map +1 -1
  198. package/dist/controllers/popover_controller.d.ts +46 -4
  199. package/dist/controllers/popover_controller.js +267 -94
  200. package/dist/controllers/popover_controller.js.map +1 -1
  201. package/dist/controllers/portal_controller.d.ts +44 -4
  202. package/dist/controllers/portal_controller.js +209 -72
  203. package/dist/controllers/portal_controller.js.map +1 -1
  204. package/dist/controllers/preview_guard_controller.d.ts +10 -4
  205. package/dist/controllers/preview_guard_controller.js +243 -112
  206. package/dist/controllers/preview_guard_controller.js.map +1 -1
  207. package/dist/controllers/progress_controller.d.ts +11 -0
  208. package/dist/controllers/progress_controller.js +132 -59
  209. package/dist/controllers/progress_controller.js.map +1 -1
  210. package/dist/controllers/radio_group_controller.d.ts +19 -8
  211. package/dist/controllers/radio_group_controller.js +138 -133
  212. package/dist/controllers/radio_group_controller.js.map +1 -1
  213. package/dist/controllers/range_slider_controller.d.ts +92 -4
  214. package/dist/controllers/range_slider_controller.js +245 -112
  215. package/dist/controllers/range_slider_controller.js.map +1 -1
  216. package/dist/controllers/rating_controller.d.ts +33 -6
  217. package/dist/controllers/rating_controller.js +429 -194
  218. package/dist/controllers/rating_controller.js.map +1 -1
  219. package/dist/controllers/read_more_controller.d.ts +26 -2
  220. package/dist/controllers/read_more_controller.js +185 -47
  221. package/dist/controllers/read_more_controller.js.map +1 -1
  222. package/dist/controllers/reading_progress_controller.d.ts +1 -1
  223. package/dist/controllers/reading_progress_controller.js +147 -135
  224. package/dist/controllers/reading_progress_controller.js.map +1 -1
  225. package/dist/controllers/relative_time_controller.d.ts +18 -4
  226. package/dist/controllers/relative_time_controller.js +145 -110
  227. package/dist/controllers/relative_time_controller.js.map +1 -1
  228. package/dist/controllers/reset_on_restore_controller.d.ts +85 -0
  229. package/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +97 -38
  230. package/dist/controllers/reset_on_restore_controller.js.map +1 -0
  231. package/dist/controllers/resizable_controller.d.ts +44 -4
  232. package/dist/controllers/resizable_controller.js +294 -118
  233. package/dist/controllers/resizable_controller.js.map +1 -1
  234. package/dist/controllers/roving_controller.js +39 -84
  235. package/dist/controllers/roving_controller.js.map +1 -1
  236. package/dist/controllers/scroll_area_controller.d.ts +7 -3
  237. package/dist/controllers/scroll_area_controller.js +428 -221
  238. package/dist/controllers/scroll_area_controller.js.map +1 -1
  239. package/dist/controllers/scroll_restore_controller.js +0 -53
  240. package/dist/controllers/scroll_restore_controller.js.map +1 -1
  241. package/dist/controllers/scroll_visibility_controller.d.ts +28 -12
  242. package/dist/controllers/scroll_visibility_controller.js +395 -146
  243. package/dist/controllers/scroll_visibility_controller.js.map +1 -1
  244. package/dist/controllers/scrollspy_controller.d.ts +9 -3
  245. package/dist/controllers/scrollspy_controller.js +181 -236
  246. package/dist/controllers/scrollspy_controller.js.map +1 -1
  247. package/dist/controllers/separator_controller.d.ts +59 -17
  248. package/dist/controllers/separator_controller.js +288 -132
  249. package/dist/controllers/separator_controller.js.map +1 -1
  250. package/dist/controllers/sidebar_controller.d.ts +81 -7
  251. package/dist/controllers/sidebar_controller.js +1071 -375
  252. package/dist/controllers/sidebar_controller.js.map +1 -1
  253. package/dist/controllers/skeleton_controller.d.ts +23 -0
  254. package/dist/controllers/skeleton_controller.js +248 -92
  255. package/dist/controllers/skeleton_controller.js.map +1 -1
  256. package/dist/controllers/slider_controller.d.ts +72 -8
  257. package/dist/controllers/slider_controller.js +211 -96
  258. package/dist/controllers/slider_controller.js.map +1 -1
  259. package/dist/controllers/smart_sticky_header_controller.d.ts +21 -3
  260. package/dist/controllers/smart_sticky_header_controller.js +163 -54
  261. package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
  262. package/dist/controllers/sortable_controller.js +13 -110
  263. package/dist/controllers/sortable_controller.js.map +1 -1
  264. package/dist/controllers/spinner_controller.d.ts +39 -4
  265. package/dist/controllers/spinner_controller.js +277 -161
  266. package/dist/controllers/spinner_controller.js.map +1 -1
  267. package/dist/controllers/step_indicator_controller.d.ts +24 -17
  268. package/dist/controllers/step_indicator_controller.js +107 -53
  269. package/dist/controllers/step_indicator_controller.js.map +1 -1
  270. package/dist/controllers/stepper_controller.d.ts +43 -9
  271. package/dist/controllers/stepper_controller.js +171 -48
  272. package/dist/controllers/stepper_controller.js.map +1 -1
  273. package/dist/controllers/stick_to_bottom_controller.d.ts +25 -0
  274. package/dist/controllers/stick_to_bottom_controller.js +310 -116
  275. package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
  276. package/dist/controllers/sticky_observer_controller.d.ts +11 -0
  277. package/dist/controllers/sticky_observer_controller.js +214 -49
  278. package/dist/controllers/sticky_observer_controller.js.map +1 -1
  279. package/dist/controllers/submit_once_controller.d.ts +22 -6
  280. package/dist/controllers/submit_once_controller.js +294 -192
  281. package/dist/controllers/submit_once_controller.js.map +1 -1
  282. package/dist/controllers/switch_controller.d.ts +40 -7
  283. package/dist/controllers/switch_controller.js +107 -43
  284. package/dist/controllers/switch_controller.js.map +1 -1
  285. package/dist/controllers/tabs_controller.d.ts +13 -1
  286. package/dist/controllers/tabs_controller.js +50 -25
  287. package/dist/controllers/tabs_controller.js.map +1 -1
  288. package/dist/controllers/tags_input_controller.d.ts +27 -3
  289. package/dist/controllers/tags_input_controller.js +290 -149
  290. package/dist/controllers/tags_input_controller.js.map +1 -1
  291. package/dist/controllers/textarea_autosize_controller.d.ts +10 -0
  292. package/dist/controllers/textarea_autosize_controller.js +71 -58
  293. package/dist/controllers/textarea_autosize_controller.js.map +1 -1
  294. package/dist/controllers/theme_controller.d.ts +37 -8
  295. package/dist/controllers/theme_controller.js +148 -80
  296. package/dist/controllers/theme_controller.js.map +1 -1
  297. package/dist/controllers/time_picker_controller.d.ts +18 -1
  298. package/dist/controllers/time_picker_controller.js +162 -81
  299. package/dist/controllers/time_picker_controller.js.map +1 -1
  300. package/dist/controllers/toast_controller.d.ts +85 -23
  301. package/dist/controllers/toast_controller.js +365 -193
  302. package/dist/controllers/toast_controller.js.map +1 -1
  303. package/dist/controllers/toggle_group_controller.d.ts +60 -9
  304. package/dist/controllers/toggle_group_controller.js +194 -99
  305. package/dist/controllers/toggle_group_controller.js.map +1 -1
  306. package/dist/controllers/toolbar_controller.js +92 -90
  307. package/dist/controllers/toolbar_controller.js.map +1 -1
  308. package/dist/controllers/tooltip_controller.d.ts +52 -2
  309. package/dist/controllers/tooltip_controller.js +185 -125
  310. package/dist/controllers/tooltip_controller.js.map +1 -1
  311. package/dist/controllers/transition_controller.d.ts +22 -4
  312. package/dist/controllers/transition_controller.js +123 -115
  313. package/dist/controllers/transition_controller.js.map +1 -1
  314. package/dist/controllers/tree_view_controller.d.ts +59 -18
  315. package/dist/controllers/tree_view_controller.js +248 -290
  316. package/dist/controllers/tree_view_controller.js.map +1 -1
  317. package/dist/index.d.ts +33 -3
  318. package/dist/index.js +8649 -8572
  319. package/dist/index.js.map +1 -1
  320. package/dist/inspector/cli.d.ts +95 -9
  321. package/dist/inspector/cli.js +343 -77
  322. package/dist/inspector/cli.js.map +1 -1
  323. package/dist/inspector/cli_bin.js +390 -96
  324. package/dist/inspector/cli_bin.js.map +1 -1
  325. package/dist/inspector/examples.json +45 -45
  326. package/dist/inspector/manifest.json +1308 -155
  327. package/dist/positioning/index.d.ts +8 -0
  328. package/dist/positioning/index.js +148 -45
  329. package/dist/positioning/index.js.map +1 -1
  330. package/package.json +7 -7
  331. package/dist/controllers/reset_before_cache_controller.d.ts +0 -76
  332. package/dist/controllers/reset_before_cache_controller.js.map +0 -1
@@ -1,13 +1,31 @@
1
1
  import { Controller } from '@hotwired/stimulus';
2
2
 
3
- // src/controllers/date_range_picker_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 logicalArrowKey(key, element) {
12
30
  if (key !== "ArrowRight" && key !== "ArrowLeft") return key;
13
31
  if (!isRtl(element)) return key;
@@ -18,36 +36,153 @@ function isReservedArrowChord(event, allow = []) {
18
36
  return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
19
37
  }
20
38
 
21
- // src/utils/before_cache_reset.ts
22
- var BeforeCacheReset = class _BeforeCacheReset {
23
- /** Every subscribed instance, iterated by the one shared document listener. */
24
- static #subscribers = /* @__PURE__ */ new Set();
25
- /** The shared listener; installed while at least one instance is subscribed. */
26
- static #onBeforeCache = () => {
27
- for (const subscriber of _BeforeCacheReset.#subscribers) subscriber.#rewind();
28
- };
29
- #rewind;
30
- /** @param rewind - the pass that returns this controller's state to its initial form. */
31
- constructor(rewind) {
32
- this.#rewind = rewind;
39
+ function sharedRegistry(key, create) {
40
+ const symbol = Symbol.for(key);
41
+ const scope = globalThis;
42
+ const existing = scope[symbol];
43
+ if (existing !== void 0) return existing;
44
+ const registry = create();
45
+ Object.defineProperty(globalThis, symbol, { value: registry });
46
+ return registry;
47
+ }
48
+
49
+ function isAttributeValue(value) {
50
+ return value === null || typeof value === "string";
51
+ }
52
+ function readLeaseRecord(element, record) {
53
+ try {
54
+ const parsed = JSON.parse(element.getAttribute(record) ?? "null");
55
+ if (!Array.isArray(parsed)) return void 0;
56
+ const [original, written, ...rest] = parsed;
57
+ const linked = rest[0] !== false;
58
+ const beneath = linked ? rest : rest.slice(1);
59
+ if (!beneath.every((under) => typeof under === "string")) return void 0;
60
+ return isAttributeValue(original) && isAttributeValue(written) ? { original, written, linked, beneath } : void 0;
61
+ } catch {
62
+ return void 0;
33
63
  }
34
- /** Subscribes to `turbo:before-cache`; call from `connect()`. Idempotent. */
35
- activate() {
36
- const first = _BeforeCacheReset.#subscribers.size === 0;
37
- _BeforeCacheReset.#subscribers.add(this);
38
- if (first) {
39
- document.addEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
64
+ }
65
+ var AttributeLease = class _AttributeLease {
66
+ static #registry = sharedRegistry(
67
+ "stimeo-ui.attribute-lease.registry.v1",
68
+ () => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
69
+ );
70
+ #attribute;
71
+ #record;
72
+ #base;
73
+ #held = /* @__PURE__ */ new Set();
74
+ constructor(attribute, owner, options = {}) {
75
+ this.#attribute = attribute;
76
+ this.#record = `data-${owner}-${attribute}-lease`;
77
+ this.#base = options.base === true;
78
+ const names = _AttributeLease.#registry.names;
79
+ names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
80
+ }
81
+ write(element, value) {
82
+ const stack = this.#stack(element);
83
+ const hold = this.#take(stack) ?? this.#acquire(element, stack);
84
+ this.#held.add(element);
85
+ const shows = !this.#covered(element, stack, hold);
86
+ hold.written = value;
87
+ this.#mark(element, stack);
88
+ if (shows) this.#reflect(element, value);
89
+ }
90
+ return(element) {
91
+ const held = this.#held.delete(element);
92
+ const known = _AttributeLease.#registry.stacks.get(element)?.has(this.#attribute);
93
+ const stack = held || known || element.hasAttribute(this.#record) ? this.#stack(element) : [];
94
+ const hold = this.#take(stack);
95
+ if (!hold) return false;
96
+ const carries = element.getAttribute(this.#attribute) === hold.written;
97
+ const owns = carries && !this.#covered(element, stack, hold);
98
+ const index = stack.indexOf(hold);
99
+ stack.splice(index, 1);
100
+ const above = stack[index];
101
+ if (above) above.linked &&= hold.linked;
102
+ this.#mark(element, stack);
103
+ if (!owns) return true;
104
+ const below = hold.linked ? stack[index - 1] : void 0;
105
+ this.#reflect(element, below ? below.written : hold.original);
106
+ return true;
107
+ }
108
+ returnAll() {
109
+ for (const element of Array.from(this.#held)) this.return(element);
110
+ }
111
+ #stack(element) {
112
+ const stacks = _AttributeLease.#registry.stacks;
113
+ const byAttribute = stacks.get(element) ?? /* @__PURE__ */ new Map();
114
+ stacks.set(element, byAttribute);
115
+ const stack = byAttribute.get(this.#attribute) ?? this.#seed(element);
116
+ byAttribute.set(this.#attribute, stack);
117
+ return stack;
118
+ }
119
+ #seed(element) {
120
+ const found = [];
121
+ for (const record of _AttributeLease.#registry.names.get(this.#attribute) ?? []) {
122
+ const read = readLeaseRecord(element, record);
123
+ if (read) found.push({ ...read, record });
124
+ }
125
+ const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
126
+ return found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
127
+ lease: null,
128
+ record,
129
+ written,
130
+ original,
131
+ linked: linked && index > 0
132
+ }));
133
+ }
134
+ #take(stack) {
135
+ const hold = stack.find((held) => held.lease === this) ?? stack.find((held) => held.lease === null && held.record === this.#record);
136
+ if (hold) hold.lease = this;
137
+ return hold;
138
+ }
139
+ #acquire(element, stack) {
140
+ const current = element.getAttribute(this.#attribute);
141
+ const top = stack.at(-1);
142
+ const bottom = stack[0];
143
+ const over = top?.written === current ? top : void 0;
144
+ const from = this.#base ? bottom : over;
145
+ const hold = {
146
+ lease: this,
147
+ record: this.#record,
148
+ written: current,
149
+ original: from ? from.original : current,
150
+ linked: !this.#base && over !== void 0
151
+ };
152
+ if (this.#base && bottom) {
153
+ bottom.linked = true;
154
+ stack.unshift(hold);
155
+ } else stack.push(hold);
156
+ return hold;
157
+ }
158
+ #covered(element, stack, hold) {
159
+ const current = element.getAttribute(this.#attribute);
160
+ return stack.slice(stack.indexOf(hold) + 1).some((above) => above.written === current);
161
+ }
162
+ #mark(element, stack) {
163
+ const records = /* @__PURE__ */ new Map([[this.#record, null]]);
164
+ stack.forEach((hold, index) => {
165
+ const names = stack.slice(0, index).map((under) => under.record);
166
+ const beneath = [...new Set(names.reverse())].filter((name) => name !== hold.record);
167
+ const marker = hold.linked || beneath.length === 0 ? [] : [false];
168
+ records.set(
169
+ hold.record,
170
+ hold.written === hold.original && beneath.length === 0 && index === stack.length - 1 ? null : JSON.stringify([hold.original, hold.written, ...marker, ...beneath])
171
+ );
172
+ });
173
+ for (const [name, recorded] of records) {
174
+ if (element.getAttribute(name) === recorded) continue;
175
+ if (recorded === null) element.removeAttribute(name);
176
+ else element.setAttribute(name, recorded);
40
177
  }
41
178
  }
42
- /** Unsubscribes; call from `disconnect()`. Safe when never subscribed. */
43
- deactivate() {
44
- _BeforeCacheReset.#subscribers.delete(this);
45
- if (_BeforeCacheReset.#subscribers.size > 0) return;
46
- document.removeEventListener("turbo:before-cache", _BeforeCacheReset.#onBeforeCache);
179
+ #reflect(element, value) {
180
+ if (element.getAttribute(this.#attribute) === value) return;
181
+ if (value === null) element.removeAttribute(this.#attribute);
182
+ else element.setAttribute(this.#attribute, value);
47
183
  }
48
184
  };
49
185
 
50
- // src/utils/intl_format.ts
51
186
  var formatters = /* @__PURE__ */ new WeakMap();
52
187
  function intlFormatter(factory, locale, options, fallbackLocale) {
53
188
  const cache = cacheFor(factory);
@@ -76,7 +211,6 @@ function construct(factory, locale, options, fallbackLocale) {
76
211
  }
77
212
  }
78
213
 
79
- // src/utils/dates.ts
80
214
  function toISODateString(date) {
81
215
  const year = date.getFullYear();
82
216
  const month = String(date.getMonth() + 1).padStart(2, "0");
@@ -114,32 +248,46 @@ function monthLabelFormatter(locale) {
114
248
  return intlFormatter(Intl.DateTimeFormat, locale, MONTH_LABEL_OPTIONS, "en");
115
249
  }
116
250
 
117
- // src/utils/locale.ts
251
+ function writeField(field, value) {
252
+ if (field.value === value) return false;
253
+ field.value = value;
254
+ return true;
255
+ }
256
+ function commitField(target) {
257
+ target.dispatchEvent(new Event("change", { bubbles: true }));
258
+ }
259
+
118
260
  function resolveLocale(element, declared = "") {
119
261
  return declared || element.closest("[lang]")?.getAttribute("lang") || void 0;
120
262
  }
121
263
 
122
- // src/utils/microtask_coalescer.ts
264
+ var ListenerSet = class {
265
+ #abort = new AbortController();
266
+ add(target, type, handler, options) {
267
+ target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
268
+ }
269
+ dispose() {
270
+ this.#abort.abort();
271
+ this.#abort = new AbortController();
272
+ }
273
+ };
274
+
123
275
  var MicrotaskCoalescer = class {
124
276
  #run;
125
277
  #queued = false;
126
278
  #active = false;
127
279
  #generation = 0;
128
- /** @param run - the single reconciliation pass, invoked at most once per batch. */
129
280
  constructor(run) {
130
281
  this.#run = run;
131
282
  }
132
- /** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
133
283
  activate() {
134
284
  this.#active = true;
135
285
  }
136
- /** Closes the window and drops any pending pass; call from `disconnect()`. */
137
286
  cancel() {
138
287
  this.#active = false;
139
288
  this.#queued = false;
140
289
  this.#generation += 1;
141
290
  }
142
- /** Requests one pass after the batch settles. Idempotent; inert outside the window. */
143
291
  schedule() {
144
292
  if (!this.#active || this.#queued) return;
145
293
  this.#queued = true;
@@ -152,43 +300,49 @@ var MicrotaskCoalescer = class {
152
300
  }
153
301
  };
154
302
 
155
- // src/utils/safe_timeout.ts
303
+ var MorphRenderWatcher = class {
304
+ #listeners = new ListenerSet();
305
+ #pass;
306
+ #element = null;
307
+ #onMorph = () => this.#pass.schedule();
308
+ constructor(run) {
309
+ this.#pass = new MicrotaskCoalescer(run);
310
+ }
311
+ observe(element) {
312
+ if (this.#element === element) return;
313
+ this.disconnect();
314
+ this.#element = element;
315
+ this.#pass.activate();
316
+ this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
317
+ }
318
+ schedule() {
319
+ this.#pass.schedule();
320
+ }
321
+ disconnect() {
322
+ this.#listeners.dispose();
323
+ this.#pass.cancel();
324
+ this.#element = null;
325
+ }
326
+ };
327
+
156
328
  var TimerRegistry = class {
157
- /** Live timer ids that have not yet been cleared (or, for timeouts, fired). */
158
329
  ids = /* @__PURE__ */ new Set();
159
- /**
160
- * Cancels a single tracked timer.
161
- *
162
- * No-ops if the id is unknown (already cleared, fired, or never owned by this
163
- * registry), so callers can clear defensively without guarding.
164
- */
165
330
  clear(id) {
166
331
  if (this.ids.delete(id)) {
167
332
  this.cancel(id);
168
333
  }
169
334
  }
170
- /**
171
- * Cancels every tracked timer. Call this from a controller's `disconnect()`
172
- * to guarantee no timer outlives the element.
173
- */
174
335
  clearAll() {
175
336
  for (const id of this.ids) {
176
337
  this.cancel(id);
177
338
  }
178
339
  this.ids.clear();
179
340
  }
180
- /** Number of timers currently tracked (pending). */
181
341
  get size() {
182
342
  return this.ids.size;
183
343
  }
184
344
  };
185
345
  var SafeTimeout = class extends TimerRegistry {
186
- /**
187
- * Schedules `callback` after `delay` ms and returns the timer id.
188
- *
189
- * The id is removed from the registry automatically when the timeout fires,
190
- * so {@link TimerRegistry.size | size} reflects only still-pending timers.
191
- */
192
346
  set(callback, delay) {
193
347
  const id = this.schedule(() => {
194
348
  this.ids.delete(id);
@@ -205,7 +359,6 @@ var SafeTimeout = class extends TimerRegistry {
205
359
  }
206
360
  };
207
361
 
208
- // src/utils/string_list.ts
209
362
  function parseStringList(raw, fallback = []) {
210
363
  const text = raw.trim();
211
364
  if (text.length === 0) return [...fallback];
@@ -219,113 +372,125 @@ function parseStringList(raw, fallback = []) {
219
372
  return parsed.filter((entry) => typeof entry === "string");
220
373
  }
221
374
 
222
- // src/controllers/date_range_picker_controller.ts
375
+ function targetSelector(identifier, name) {
376
+ return `[data-${identifier}-target~="${name}"]`;
377
+ }
378
+
223
379
  var GRID_SIZE = 42;
224
380
  var DateRangePickerController = class extends Controller {
381
+ #fieldWrites = /* @__PURE__ */ new WeakMap();
225
382
  static targets = ["grid", "monthLabel", "cell", "status", "startField", "endField"];
226
383
  static values = {
227
384
  min: { type: String, default: "" },
228
385
  max: { type: String, default: "" },
229
- // A JSON list read through `parseStringList` rather than Stimulus's `Array`
230
- // type: that reader throws out of the value observer before any callback
231
- // runs, so one malformed attribute would stop the picker from connecting.
232
386
  disabledDates: { type: String, default: "" },
233
387
  locale: { type: String, default: "" }
234
388
  };
235
389
  static actions = ["applyPreset", "next", "onKeydown", "prev", "previewTo", "selectDate"];
236
- static events = ["change", "monthchange"];
237
- /** The month currently rendered, as `YYYY-MM`. */
390
+ static events = ["change", "monthchange", "reconcile"];
238
391
  #viewMonth = "";
239
- /** The confirmed range endpoints (ISO), or "" when unset. */
240
392
  #startDate = "";
241
393
  #endDate = "";
242
- /** The first endpoint of an in-progress selection (ISO), or "" when idle. */
243
394
  #pendingStart = "";
244
- /** The hovered/focused date previewed while selecting (ISO), or "". */
245
395
  #previewDate = "";
246
- /** The roving-focus date in the grid (local time). */
247
396
  #focusedDate = /* @__PURE__ */ new Date();
248
- /** Deferred focus after an async month transition (cancelled on teardown). */
249
397
  #focusTimer = new SafeTimeout();
250
- /** The declared unavailable dates, indexed for the per-cell paint lookup. */
398
+ #rangeMoves = 0;
399
+ #transitions = 0;
251
400
  #disabledDates = /* @__PURE__ */ new Set();
252
- /** Rewinds an unfinished selection before Turbo snapshots the page. */
253
- #beforeCache = new BeforeCacheReset(() => this.#rewindForCache());
254
- /** Last painted month, or `null` until the initial paint has settled. */
255
401
  #announcedMonth = null;
256
- /**
257
- * Collapses a morph that swaps render inputs into one repaint, and refuses the
258
- * pass Stimulus delivers before `connect()`.
259
- */
260
- #repaint = new MicrotaskCoalescer(() => {
402
+ #multiselectable = new AttributeLease(
403
+ "aria-multiselectable",
404
+ this.identifier
405
+ );
406
+ #repaint = new MorphRenderWatcher(() => {
261
407
  this.#render();
262
408
  });
263
- /** Seeds and normalizes the range from optional hidden fields, then paints the grid. */
264
409
  connect() {
265
- this.#repaint.activate();
266
- this.#beforeCache.activate();
410
+ this.#repaint.observe(this.element);
267
411
  const authoredStart = this.hasStartFieldTarget ? normalizeISO(this.startFieldTarget.value) : "";
268
412
  const authoredEnd = this.hasEndFieldTarget ? normalizeISO(this.endFieldTarget.value) : "";
269
- [this.#startDate, this.#endDate] = orderRange(authoredStart, authoredEnd);
413
+ [this.#startDate, this.#endDate] = this.#selectableRange(
414
+ ...orderRange(authoredStart, authoredEnd)
415
+ );
270
416
  this.#pendingStart = "";
271
417
  this.#previewDate = "";
272
418
  this.#announcedMonth = null;
273
- this.#commitFields();
274
419
  const anchor = parseISODateString(this.#startDate) ?? this.#clampToBounds(/* @__PURE__ */ new Date()) ?? /* @__PURE__ */ new Date();
275
420
  this.#focusedDate = anchor;
276
421
  this.#viewMonth = toISOMonthString(anchor);
277
- if (this.hasGridTarget) this.gridTarget.setAttribute("aria-multiselectable", "true");
278
422
  this.#render();
279
423
  }
280
- /** Cancels any pending deferred focus so it never fires on a detached element. */
281
424
  disconnect() {
282
- this.#repaint.cancel();
283
- this.#beforeCache.deactivate();
425
+ this.#repaint.disconnect();
284
426
  this.#focusTimer.clearAll();
285
427
  }
286
- /** Repaints the label when application code changes `locale` at runtime. */
287
428
  localeValueChanged() {
288
429
  this.#repaint.schedule();
289
430
  }
290
- /** Repaints when application code (or a Turbo morph) changes `min` at runtime. */
291
431
  minValueChanged() {
292
432
  this.#repaint.schedule();
293
433
  }
294
- /** Repaints when application code (or a Turbo morph) changes `max` at runtime. */
295
434
  maxValueChanged() {
296
435
  this.#repaint.schedule();
297
436
  }
298
- /** Re-indexes the unavailable dates and repaints when the declared set changes. */
299
437
  disabledDatesValueChanged() {
300
438
  this.#disabledDates = new Set(parseStringList(this.disabledDatesValue));
301
439
  this.#repaint.schedule();
302
440
  }
303
- /** Navigates to the previous month. */
441
+ gridTargetConnected() {
442
+ this.#repaint.schedule();
443
+ }
444
+ gridTargetDisconnected(grid) {
445
+ if (!this.gridTargets.includes(grid)) this.#multiselectable.return(grid);
446
+ this.#repaint.schedule();
447
+ }
448
+ monthLabelTargetConnected() {
449
+ this.#repaint.schedule();
450
+ }
451
+ monthLabelTargetDisconnected() {
452
+ this.#repaint.schedule();
453
+ }
454
+ startFieldTargetConnected() {
455
+ this.#repaint.schedule();
456
+ }
457
+ startFieldTargetDisconnected() {
458
+ this.#repaint.schedule();
459
+ }
460
+ endFieldTargetConnected() {
461
+ this.#repaint.schedule();
462
+ }
463
+ endFieldTargetDisconnected() {
464
+ this.#repaint.schedule();
465
+ }
304
466
  prev(event) {
305
467
  event?.preventDefault();
306
468
  this.#shiftMonth(-1);
307
469
  }
308
- /** Navigates to the next month. */
309
470
  next(event) {
310
471
  event?.preventDefault();
311
472
  this.#shiftMonth(1);
312
473
  }
313
- /** Confirms a range endpoint from a clicked cell. */
314
- selectDate(event) {
315
- const cell = this.#cellFrom(event.target);
474
+ selectDate(source) {
475
+ const { origin, reason } = actionSource(source);
476
+ if (origin && origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
477
+ return;
478
+ const cell = this.#cellFrom(origin);
316
479
  if (!cell) return;
317
480
  const date = cell.getAttribute("data-date");
318
481
  if (!date || !this.#isSelectable(date)) return;
319
- this.#choose(date);
482
+ this.#choose(date, reason);
320
483
  }
321
- /** Previews the range up to a hovered/focused cell while selecting. */
322
- previewTo(event) {
323
- const cell = this.#cellFrom(event.target);
484
+ previewTo(source) {
485
+ const { event, origin } = actionSource(source);
486
+ if (origin && origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
487
+ return;
488
+ const cell = this.#cellFrom(origin);
324
489
  if (!cell) return;
325
490
  const date = cell.getAttribute("data-date");
326
491
  if (!date) return;
327
492
  let shouldRender = false;
328
- if (event.type.startsWith("focus")) {
493
+ if (event?.type.startsWith("focus")) {
329
494
  const parsed = parseISODateString(date);
330
495
  if (parsed) this.#focusedDate = parsed;
331
496
  shouldRender = cell.getAttribute("tabindex") !== "0";
@@ -336,27 +501,28 @@ var DateRangePickerController = class extends Controller {
336
501
  }
337
502
  if (shouldRender) this.#render();
338
503
  }
339
- /** Applies a named preset (`today` / `last7` / `last30` / `thisMonth`). */
340
- applyPreset(event) {
341
- const button = event.target?.closest("[data-range]");
504
+ applyPreset(source) {
505
+ const { event, origin, reason } = actionSource(source);
506
+ const button = origin?.closest("[data-range]");
507
+ if (!button || !this.element.contains(button)) return;
508
+ const focus = event !== null || this.element.contains(document.activeElement);
342
509
  const range = computePreset(button?.getAttribute("data-range") ?? "");
343
510
  if (!range) return;
344
511
  const intersection = this.#intersectRange(range);
345
512
  if (!intersection) return;
346
513
  const { start, end } = intersection;
347
514
  if (!this.#isSelectable(start) || !this.#isSelectable(end)) return;
348
- this.#startDate = start;
349
- this.#endDate = end;
350
- this.#pendingStart = "";
351
- this.#previewDate = "";
515
+ const move = this.#setConfirmed(start, end);
352
516
  const endDate = parseISODateString(end);
353
517
  if (endDate) this.#focusedDate = endDate;
354
- this.#commitFields();
355
- this.#transitionTo(toISOMonthString(parseISODateString(end) ?? /* @__PURE__ */ new Date()), end);
356
- this.#announce();
357
- this.dispatch("change", { detail: { start, end } });
518
+ this.#transitionTo(
519
+ toISOMonthString(parseISODateString(end) ?? /* @__PURE__ */ new Date()),
520
+ end,
521
+ true,
522
+ () => focus && (event !== null || this.element.contains(document.activeElement))
523
+ );
524
+ this.#reportConfirmed(move, reason);
358
525
  }
359
- /** Grid keyboard navigation, selection (Enter/Space), and Escape-to-cancel. */
360
526
  onKeydown(event) {
361
527
  if (event.defaultPrevented) return;
362
528
  if (isReservedArrowChord(event)) return;
@@ -411,9 +577,8 @@ var DateRangePickerController = class extends Controller {
411
577
  event.preventDefault();
412
578
  this.#moveFocusTo(next);
413
579
  }
414
- /** Records a chosen date as either the pending start or the confirmed end. */
415
- #choose(date) {
416
- if (!this.#pendingStart) {
580
+ #choose(date, reason = "user") {
581
+ if (!this.#pendingStart || !this.#isSelectable(this.#pendingStart)) {
417
582
  this.#pendingStart = date;
418
583
  this.#previewDate = date;
419
584
  const parsed = parseISODateString(date);
@@ -422,29 +587,40 @@ var DateRangePickerController = class extends Controller {
422
587
  return;
423
588
  }
424
589
  const [start, end] = date < this.#pendingStart ? [date, this.#pendingStart] : [this.#pendingStart, date];
590
+ const move = this.#setConfirmed(start, end);
591
+ this.#render(true);
592
+ this.#reportConfirmed(move, reason);
593
+ }
594
+ #setConfirmed(start, end) {
595
+ const changed = start !== this.#startDate || end !== this.#endDate;
425
596
  this.#startDate = start;
426
597
  this.#endDate = end;
427
598
  this.#pendingStart = "";
428
599
  this.#previewDate = "";
429
- this.#commitFields();
430
- this.#render();
431
- this.#announce();
432
- this.dispatch("change", { detail: { start, end } });
600
+ if (!changed) return null;
601
+ this.#rangeMoves += 1;
602
+ return this.#rangeMoves;
603
+ }
604
+ #reportConfirmed(move, reason) {
605
+ if (move === null || move !== this.#rangeMoves) return;
606
+ this.dispatch("change", { detail: { start: this.#startDate, end: this.#endDate, reason } });
433
607
  }
434
- /** Moves roving focus to `date`, transitioning the month when needed. */
435
608
  #moveFocusTo(date) {
436
609
  this.#focusedDate = date;
437
610
  const iso = toISODateString(date);
438
611
  if (this.#pendingStart && this.#isSelectable(iso)) this.#previewDate = iso;
439
612
  this.#transitionTo(toISOMonthString(date), iso);
440
613
  }
441
- /** Renders `month`, then focuses the cell for `dateStr` (deferred if async). */
442
- #transitionTo(month, dateStr) {
614
+ #transitionTo(month, dateStr, confirmed = false, canFocus = () => true) {
443
615
  const isTransition = month !== this.#viewMonth;
444
616
  this.#focusTimer.clearAll();
445
617
  this.#viewMonth = month;
446
- this.#render();
618
+ this.#transitions += 1;
619
+ const transition = this.#transitions;
620
+ this.#render(confirmed);
621
+ if (transition !== this.#transitions || !canFocus()) return;
447
622
  const focusCell = () => {
623
+ if (!canFocus()) return;
448
624
  this.cellTargets.find((c) => c.getAttribute("data-date") === dateStr)?.focus();
449
625
  };
450
626
  if (isTransition) {
@@ -453,7 +629,6 @@ var DateRangePickerController = class extends Controller {
453
629
  focusCell();
454
630
  }
455
631
  }
456
- /** Shifts the displayed month by `delta`, keeping focus within it. */
457
632
  #shiftMonth(delta) {
458
633
  const info = parseISOMonthString(this.#viewMonth);
459
634
  if (!info) return;
@@ -464,16 +639,17 @@ var DateRangePickerController = class extends Controller {
464
639
  this.#focusedDate = target;
465
640
  this.#render();
466
641
  }
467
- /**
468
- * Builds the six-week grid and binds range/roving/disabled state per cell.
469
- *
470
- * @stimeoRenderRoot
471
- */
472
- #render() {
642
+ #render(confirmed = false) {
643
+ if (this.hasGridTarget) this.#multiselectable.write(this.gridTarget, "true");
473
644
  const info = parseISOMonthString(this.#viewMonth);
474
645
  if (!info) return;
475
646
  const { year, month } = info;
647
+ if (this.#pendingStart && !this.#isSelectable(this.#pendingStart)) {
648
+ this.#pendingStart = "";
649
+ this.#previewDate = "";
650
+ }
476
651
  if (this.#previewDate && !this.#isSelectable(this.#previewDate)) this.#previewDate = "";
652
+ const narrowed = this.#narrowConfirmedRange();
477
653
  if (this.hasMonthLabelTarget) {
478
654
  const formatter = monthLabelFormatter(resolveLocale(this.element, this.localeValue));
479
655
  this.monthLabelTarget.textContent = formatter.format(new Date(year, month - 1, 1));
@@ -513,13 +689,44 @@ var DateRangePickerController = class extends Controller {
513
689
  const fallback = cells.find((cell) => cell.getAttribute("data-outside") === "false") ?? cells[0];
514
690
  fallback?.setAttribute("tabindex", "0");
515
691
  }
692
+ const move = this.#rangeMoves;
693
+ const moved = this.#mirrorFields();
694
+ if (confirmed) {
695
+ this.#announce();
696
+ for (const [field, value, write] of moved) {
697
+ if (field.value === value && this.#fieldWrites.get(field) === write) commitField(field);
698
+ }
699
+ }
516
700
  const previous = this.#announcedMonth;
517
701
  this.#announcedMonth = this.#viewMonth;
518
702
  if (previous !== null && previous !== this.#viewMonth) {
519
703
  this.dispatch("monthchange", { detail: { month: this.#viewMonth } });
520
704
  }
705
+ if (narrowed && move === this.#rangeMoves) {
706
+ this.dispatch("reconcile", { detail: { start: this.#startDate, end: this.#endDate } });
707
+ }
708
+ }
709
+ #narrowConfirmedRange() {
710
+ const [start, end] = this.#selectableRange(this.#startDate, this.#endDate);
711
+ if (start === this.#startDate && end === this.#endDate) return false;
712
+ this.#startDate = start;
713
+ this.#endDate = end;
714
+ this.#rangeMoves += 1;
715
+ return true;
716
+ }
717
+ #selectableRange(start, end) {
718
+ let from = parseISODateString(start || end);
719
+ let to = parseISODateString(end || start);
720
+ if (!from || !to) return [start, end];
721
+ while (from.getTime() <= to.getTime() && !this.#isSelectable(toISODateString(from))) {
722
+ from = addDays(from, 1);
723
+ }
724
+ while (to.getTime() >= from.getTime() && !this.#isSelectable(toISODateString(to))) {
725
+ to = addDays(to, -1);
726
+ }
727
+ if (from.getTime() > to.getTime()) return ["", ""];
728
+ return [start ? toISODateString(from) : "", end ? toISODateString(to) : ""];
521
729
  }
522
- /** The ordered [start, end] pair to paint: the preview while selecting, else confirmed. */
523
730
  #visualRange() {
524
731
  if (this.#pendingStart) {
525
732
  const other = this.#previewDate || this.#pendingStart;
@@ -527,71 +734,59 @@ var DateRangePickerController = class extends Controller {
527
734
  }
528
735
  return [this.#startDate, this.#endDate];
529
736
  }
530
- /** Writes the confirmed range to the hidden fields. */
531
- #commitFields() {
532
- if (this.hasStartFieldTarget) this.startFieldTarget.value = this.#startDate;
533
- if (this.hasEndFieldTarget) this.endFieldTarget.value = this.#endDate;
737
+ #writeField(field, value) {
738
+ if (!writeField(field, value)) return false;
739
+ this.#fieldWrites.set(field, (this.#fieldWrites.get(field) ?? 0) + 1);
740
+ return true;
741
+ }
742
+ #mirrorFields() {
743
+ const moved = [];
744
+ if (this.hasStartFieldTarget && this.#writeField(this.startFieldTarget, this.#startDate)) {
745
+ moved.push([
746
+ this.startFieldTarget,
747
+ this.#startDate,
748
+ this.#fieldWrites.get(this.startFieldTarget) ?? 0
749
+ ]);
750
+ }
751
+ if (this.hasEndFieldTarget && this.#writeField(this.endFieldTarget, this.#endDate)) {
752
+ moved.push([
753
+ this.endFieldTarget,
754
+ this.#endDate,
755
+ this.#fieldWrites.get(this.endFieldTarget) ?? 0
756
+ ]);
757
+ }
758
+ return moved;
534
759
  }
535
- /** Announces the confirmed range in the live status region. */
536
760
  #announce() {
537
761
  if (this.hasStatusTarget && this.#startDate && this.#endDate) {
538
762
  this.statusTarget.textContent = `${this.#startDate} \u2013 ${this.#endDate}`;
539
763
  }
540
764
  }
541
- /**
542
- * True when `iso` may be chosen as a range endpoint.
543
- *
544
- * The single availability question in the controller: the grid paint, the
545
- * preview, and both commit paths ask it, so a cell's `aria-disabled` and what
546
- * a click on that cell does are the same decision rather than two that have to
547
- * be kept in step.
548
- */
549
765
  #isSelectable(iso) {
550
766
  return !this.#outOfBounds(iso) && !this.#disabledDates.has(iso);
551
767
  }
552
- /** True when `iso` falls outside the `[min, max]` bounds. */
553
768
  #outOfBounds(iso) {
554
769
  if (this.minValue && iso < this.minValue) return true;
555
770
  if (this.maxValue && iso > this.maxValue) return true;
556
771
  return false;
557
772
  }
558
- /** Clamps a generated ISO date string into `[min, max]`. */
559
773
  #clampISO(iso) {
560
774
  if (this.minValue && iso < this.minValue) return this.minValue;
561
775
  if (this.maxValue && iso > this.maxValue) return this.maxValue;
562
776
  return iso;
563
777
  }
564
- /** Clamps a Date into `[min, max]`, returning null only when unparseable. */
565
778
  #clampToBounds(date) {
566
779
  const clamped = this.#clampISO(toISODateString(date));
567
780
  return parseISODateString(clamped);
568
781
  }
569
- /**
570
- * Intersects a preset with `[min, max]`, rejecting a disjoint interval.
571
- *
572
- * Only the bounds narrow a preset here: `disabledDates` excludes single days,
573
- * not sub-intervals, so it cannot shrink one to a still-contiguous range. A
574
- * day it excludes is still refused as an endpoint — the caller checks that —
575
- * but a preset that merely spans one keeps its span.
576
- */
577
782
  #intersectRange(range) {
578
783
  const start = this.minValue && range.start < this.minValue ? this.minValue : range.start;
579
784
  const end = this.maxValue && range.end > this.maxValue ? this.maxValue : range.end;
580
785
  return start <= end ? { start, end } : null;
581
786
  }
582
- /** Removes provisional range state before Turbo freezes a cached snapshot. */
583
- #rewindForCache() {
584
- this.#focusTimer.clearAll();
585
- if (!this.#pendingStart && !this.#previewDate) return;
586
- this.#pendingStart = "";
587
- this.#previewDate = "";
588
- this.#render();
589
- }
590
- /** Resolves the cell element from an event target, or null. */
591
787
  #cellFrom(target) {
592
- return target?.closest(
593
- "[data-stimeo--date-range-picker-target='cell']"
594
- ) ?? null;
788
+ const cell = target instanceof Element ? target.closest(targetSelector(this.identifier, "cell")) : null;
789
+ return cell && this.cellTargets.includes(cell) ? cell : null;
595
790
  }
596
791
  };
597
792
  function addDays(date, n) {
@@ -651,5 +846,4 @@ function computePreset(name) {
651
846
  }
652
847
 
653
848
  export { DateRangePickerController };
654
- //# sourceMappingURL=date_range_picker_controller.js.map
655
849
  //# sourceMappingURL=date_range_picker_controller.js.map