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,53 +1,201 @@
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;
14
32
  return key === "ArrowRight" ? "ArrowLeft" : "ArrowRight";
15
33
  }
34
+ function gridDirectionElement(grid, root) {
35
+ const table = grid.closest("table");
36
+ return table !== null && root.contains(table) ? table : grid;
37
+ }
16
38
  function isReservedArrowChord(event, allow = []) {
17
39
  if (!event.key.startsWith("Arrow")) return false;
18
40
  return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
19
41
  }
42
+ function hasModifierChord(event) {
43
+ return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
44
+ }
20
45
 
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;
46
+ function setAttributeIfChanged(element, name, value, beforeWrite) {
47
+ if (element.getAttribute(name) === value) return false;
48
+ if (value === null) element.removeAttribute(name);
49
+ else element.setAttribute(name, value);
50
+ return true;
51
+ }
52
+
53
+ function sharedRegistry(key, create) {
54
+ const symbol = Symbol.for(key);
55
+ const scope = globalThis;
56
+ const existing = scope[symbol];
57
+ if (existing !== void 0) return existing;
58
+ const registry2 = create();
59
+ Object.defineProperty(globalThis, symbol, { value: registry2 });
60
+ return registry2;
61
+ }
62
+
63
+ var registry = sharedRegistry(
64
+ "stimeo-ui.attribute-lease.registry.v1",
65
+ () => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
66
+ );
67
+ function isAttributeValue(value) {
68
+ return value === null || typeof value === "string";
69
+ }
70
+ function readLeaseRecord(element, record) {
71
+ try {
72
+ const parsed = JSON.parse(element.getAttribute(record) ?? "null");
73
+ if (!Array.isArray(parsed)) return void 0;
74
+ const [original, written, ...rest] = parsed;
75
+ const linked = rest[0] !== false;
76
+ const beneath = linked ? rest : rest.slice(1);
77
+ if (!beneath.every((under) => typeof under === "string")) return void 0;
78
+ return isAttributeValue(original) && isAttributeValue(written) ? { original, written, linked, beneath } : void 0;
79
+ } catch {
80
+ return void 0;
33
81
  }
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);
82
+ }
83
+ function attributeStack(element, attribute) {
84
+ const byAttribute = registry.stacks.get(element) ?? /* @__PURE__ */ new Map();
85
+ registry.stacks.set(element, byAttribute);
86
+ const known = byAttribute.get(attribute);
87
+ if (known) return known;
88
+ const found = [];
89
+ for (const record of registry.names.get(attribute) ?? []) {
90
+ const read = readLeaseRecord(element, record);
91
+ if (read) found.push({ ...read, record });
92
+ }
93
+ const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
94
+ const stack = found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
95
+ lease: null,
96
+ record,
97
+ written,
98
+ original,
99
+ linked: linked && index > 0
100
+ }));
101
+ byAttribute.set(attribute, stack);
102
+ return stack;
103
+ }
104
+ function findHold(stack, lease, record) {
105
+ return stack.find((held) => held.lease === lease) ?? stack.find((held) => held.lease === null && held.record === record);
106
+ }
107
+ var AttributeLease = class {
108
+ #attribute;
109
+ #record;
110
+ #base;
111
+ #preserveAuthor;
112
+ #held = /* @__PURE__ */ new Set();
113
+ constructor(attribute, owner, options = {}) {
114
+ this.#attribute = attribute;
115
+ this.#record = `data-${owner}-${attribute}-lease`;
116
+ this.#base = options.base === true;
117
+ this.#preserveAuthor = options.preserveAuthor === true;
118
+ const names = registry.names;
119
+ names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
120
+ }
121
+ write(element, value) {
122
+ const stack = attributeStack(element, this.#attribute);
123
+ const hold = this.#take(stack) ?? this.#acquire(element, stack);
124
+ this.#held.add(element);
125
+ const shows = !this.#covered(element, stack, hold);
126
+ hold.written = value;
127
+ this.#mark(element, stack);
128
+ if (shows) this.#reflect(element, value);
129
+ }
130
+ return(element) {
131
+ const held = this.#held.delete(element);
132
+ const known = registry.stacks.get(element)?.has(this.#attribute);
133
+ const stack = held || known || element.hasAttribute(this.#record) ? attributeStack(element, this.#attribute) : [];
134
+ const hold = this.#take(stack);
135
+ if (!hold) return false;
136
+ const carries = element.getAttribute(this.#attribute) === hold.written;
137
+ const owns = carries && !this.#covered(element, stack, hold);
138
+ const index = stack.indexOf(hold);
139
+ stack.splice(index, 1);
140
+ const above = stack[index];
141
+ if (above) above.linked &&= hold.linked;
142
+ this.#mark(element, stack);
143
+ if (!owns) return true;
144
+ const below = hold.linked ? stack[index - 1] : void 0;
145
+ this.#reflect(element, below ? below.written : hold.original);
146
+ return true;
147
+ }
148
+ returnAll() {
149
+ for (const element of Array.from(this.#held)) this.return(element);
150
+ }
151
+ #take(stack) {
152
+ const hold = findHold(stack, this, this.#record);
153
+ if (hold) hold.lease = this;
154
+ return hold;
155
+ }
156
+ #acquire(element, stack) {
157
+ const current = element.getAttribute(this.#attribute);
158
+ const top = stack.at(-1);
159
+ const bottom = stack[0];
160
+ const over = top?.written === current ? top : void 0;
161
+ const from = this.#base ? this.#preserveAuthor && !stack.some((held) => held.written === current) ? void 0 : bottom : over;
162
+ const hold = {
163
+ lease: this,
164
+ record: this.#record,
165
+ written: current,
166
+ original: from ? from.original : current,
167
+ linked: !this.#base && over !== void 0
168
+ };
169
+ if (this.#base && bottom) {
170
+ bottom.linked = true;
171
+ stack.unshift(hold);
172
+ } else stack.push(hold);
173
+ return hold;
174
+ }
175
+ #covered(element, stack, hold) {
176
+ const current = element.getAttribute(this.#attribute);
177
+ return stack.slice(stack.indexOf(hold) + 1).some((above) => above.written === current);
178
+ }
179
+ #mark(element, stack) {
180
+ const records = /* @__PURE__ */ new Map([[this.#record, null]]);
181
+ stack.forEach((hold, index) => {
182
+ const names = stack.slice(0, index).map((under) => under.record);
183
+ const beneath = [...new Set(names.reverse())].filter((name) => name !== hold.record);
184
+ const marker = hold.linked || beneath.length === 0 ? [] : [false];
185
+ records.set(
186
+ hold.record,
187
+ hold.written === hold.original && beneath.length === 0 && index === stack.length - 1 ? null : JSON.stringify([hold.original, hold.written, ...marker, ...beneath])
188
+ );
189
+ });
190
+ for (const [name, recorded] of records) {
191
+ setAttributeIfChanged(element, name, recorded);
40
192
  }
41
193
  }
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);
194
+ #reflect(element, value) {
195
+ setAttributeIfChanged(element, this.#attribute, value);
47
196
  }
48
197
  };
49
198
 
50
- // src/utils/intl_format.ts
51
199
  var formatters = /* @__PURE__ */ new WeakMap();
52
200
  function intlFormatter(factory, locale, options, fallbackLocale) {
53
201
  const cache = cacheFor(factory);
@@ -76,7 +224,6 @@ function construct(factory, locale, options, fallbackLocale) {
76
224
  }
77
225
  }
78
226
 
79
- // src/utils/dates.ts
80
227
  function toISODateString(date) {
81
228
  const year = date.getFullYear();
82
229
  const month = String(date.getMonth() + 1).padStart(2, "0");
@@ -109,12 +256,35 @@ function parseISOMonthString(monthStr) {
109
256
  function toISOMonthString(date) {
110
257
  return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}`;
111
258
  }
259
+ function monthGridDays(year, month, weekStart) {
260
+ const first = new Date(year, month - 1, 1);
261
+ const offset = (first.getDay() - weekStart + 7) % 7;
262
+ const current = new Date(first);
263
+ current.setDate(first.getDate() - offset);
264
+ const days = [];
265
+ for (let i = 0; i < 42; i++) {
266
+ days.push(new Date(current));
267
+ current.setDate(current.getDate() + 1);
268
+ }
269
+ return days;
270
+ }
271
+ function dateInMonthClamped(date, year, month) {
272
+ const target = new Date(year, month - 1, 1);
273
+ const lastDay = new Date(target.getFullYear(), target.getMonth() + 1, 0).getDate();
274
+ target.setDate(Math.min(date.getDate(), lastDay));
275
+ return target;
276
+ }
277
+ function shiftMonthClamped(date, delta) {
278
+ return dateInMonthClamped(date, date.getFullYear(), date.getMonth() + 1 + delta);
279
+ }
280
+ function shiftYearClamped(date, delta) {
281
+ return dateInMonthClamped(date, date.getFullYear() + delta, date.getMonth() + 1);
282
+ }
112
283
  var MONTH_LABEL_OPTIONS = { month: "long", year: "numeric" };
113
284
  function monthLabelFormatter(locale) {
114
285
  return intlFormatter(Intl.DateTimeFormat, locale, MONTH_LABEL_OPTIONS, "en");
115
286
  }
116
287
 
117
- // src/utils/field_mirror.ts
118
288
  function writeField(field, value) {
119
289
  if (field.value === value) return false;
120
290
  field.value = value;
@@ -124,32 +294,40 @@ function commitField(target) {
124
294
  target.dispatchEvent(new Event("change", { bubbles: true }));
125
295
  }
126
296
 
127
- // src/utils/locale.ts
128
297
  function resolveLocale(element, declared = "") {
129
298
  return declared || element.closest("[lang]")?.getAttribute("lang") || void 0;
130
299
  }
131
300
 
132
- // src/utils/microtask_coalescer.ts
301
+ var ListenerSet = class {
302
+ #abort = new AbortController();
303
+ add(target, type, handler, options) {
304
+ target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
305
+ }
306
+ dispose() {
307
+ this.#abort.abort();
308
+ this.#abort = new AbortController();
309
+ }
310
+ };
311
+
133
312
  var MicrotaskCoalescer = class {
134
313
  #run;
135
314
  #queued = false;
136
315
  #active = false;
137
316
  #generation = 0;
138
- /** @param run - the single reconciliation pass, invoked at most once per batch. */
139
317
  constructor(run) {
140
318
  this.#run = run;
141
319
  }
142
- /** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
143
320
  activate() {
144
321
  this.#active = true;
145
322
  }
146
- /** Closes the window and drops any pending pass; call from `disconnect()`. */
147
323
  cancel() {
148
324
  this.#active = false;
149
325
  this.#queued = false;
150
326
  this.#generation += 1;
151
327
  }
152
- /** Requests one pass after the batch settles. Idempotent; inert outside the window. */
328
+ get pending() {
329
+ return this.#queued;
330
+ }
153
331
  schedule() {
154
332
  if (!this.#active || this.#queued) return;
155
333
  this.#queued = true;
@@ -162,43 +340,52 @@ var MicrotaskCoalescer = class {
162
340
  }
163
341
  };
164
342
 
165
- // src/utils/safe_timeout.ts
343
+ var MorphRenderWatcher = class {
344
+ #listeners = new ListenerSet();
345
+ #pass;
346
+ #element = null;
347
+ #onMorph = () => this.#pass.schedule();
348
+ constructor(run) {
349
+ this.#pass = new MicrotaskCoalescer(run);
350
+ }
351
+ observe(element) {
352
+ if (this.#element === element) return;
353
+ this.disconnect();
354
+ this.#element = element;
355
+ this.#pass.activate();
356
+ this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
357
+ }
358
+ schedule() {
359
+ this.#pass.schedule();
360
+ }
361
+ get pending() {
362
+ return this.#pass.pending;
363
+ }
364
+ disconnect() {
365
+ this.#listeners.dispose();
366
+ this.#pass.cancel();
367
+ this.#element = null;
368
+ }
369
+ };
370
+
166
371
  var TimerRegistry = class {
167
- /** Live timer ids that have not yet been cleared (or, for timeouts, fired). */
168
372
  ids = /* @__PURE__ */ new Set();
169
- /**
170
- * Cancels a single tracked timer.
171
- *
172
- * No-ops if the id is unknown (already cleared, fired, or never owned by this
173
- * registry), so callers can clear defensively without guarding.
174
- */
175
373
  clear(id) {
176
374
  if (this.ids.delete(id)) {
177
375
  this.cancel(id);
178
376
  }
179
377
  }
180
- /**
181
- * Cancels every tracked timer. Call this from a controller's `disconnect()`
182
- * to guarantee no timer outlives the element.
183
- */
184
378
  clearAll() {
185
379
  for (const id of this.ids) {
186
380
  this.cancel(id);
187
381
  }
188
382
  this.ids.clear();
189
383
  }
190
- /** Number of timers currently tracked (pending). */
191
384
  get size() {
192
385
  return this.ids.size;
193
386
  }
194
387
  };
195
388
  var SafeTimeout = class extends TimerRegistry {
196
- /**
197
- * Schedules `callback` after `delay` ms and returns the timer id.
198
- *
199
- * The id is removed from the registry automatically when the timeout fires,
200
- * so {@link TimerRegistry.size | size} reflects only still-pending timers.
201
- */
202
389
  set(callback, delay) {
203
390
  const id = this.schedule(() => {
204
391
  this.ids.delete(id);
@@ -215,7 +402,6 @@ var SafeTimeout = class extends TimerRegistry {
215
402
  }
216
403
  };
217
404
 
218
- // src/utils/string_list.ts
219
405
  function parseStringList(raw, fallback = []) {
220
406
  const text = raw.trim();
221
407
  if (text.length === 0) return [...fallback];
@@ -229,67 +415,42 @@ function parseStringList(raw, fallback = []) {
229
415
  return parsed.filter((entry) => typeof entry === "string");
230
416
  }
231
417
 
232
- // src/utils/target_selector.ts
233
418
  function targetSelector(identifier, name) {
234
419
  return `[data-${identifier}-target~="${name}"]`;
235
420
  }
236
421
 
237
- // src/controllers/date_range_picker_controller.ts
238
422
  var GRID_SIZE = 42;
239
423
  var DateRangePickerController = class extends Controller {
424
+ #fieldWrites = /* @__PURE__ */ new WeakMap();
240
425
  static targets = ["grid", "monthLabel", "cell", "status", "startField", "endField"];
241
426
  static values = {
242
427
  min: { type: String, default: "" },
243
428
  max: { type: String, default: "" },
244
- // A JSON list read through `parseStringList` rather than Stimulus's `Array`
245
- // type: that reader throws out of the value observer before any callback
246
- // runs, so one malformed attribute would stop the picker from connecting.
247
429
  disabledDates: { type: String, default: "" },
248
430
  locale: { type: String, default: "" }
249
431
  };
250
432
  static actions = ["applyPreset", "next", "onKeydown", "prev", "previewTo", "selectDate"];
251
433
  static events = ["change", "monthchange", "reconcile"];
252
- /** The month currently rendered, as `YYYY-MM`. */
253
434
  #viewMonth = "";
254
- /** The confirmed range endpoints (ISO), or "" when unset. */
255
435
  #startDate = "";
256
436
  #endDate = "";
257
- /** The first endpoint of an in-progress selection (ISO), or "" when idle. */
258
437
  #pendingStart = "";
259
- /** The hovered/focused date previewed while selecting (ISO), or "". */
260
438
  #previewDate = "";
261
- /** The roving-focus date in the grid (local time). */
262
439
  #focusedDate = /* @__PURE__ */ new Date();
263
- /** Deferred focus after an async month transition (cancelled on teardown). */
264
440
  #focusTimer = new SafeTimeout();
265
- /**
266
- * How many times the confirmed range has moved — a commit or a narrowing — so
267
- * a report still pending for a range a listener has since replaced can tell.
268
- */
269
441
  #rangeMoves = 0;
270
- /** How many month transitions have started, so one a listener starts mid-paint keeps its focus. */
271
442
  #transitions = 0;
272
- /** The declared unavailable dates, indexed for the per-cell paint lookup. */
273
443
  #disabledDates = /* @__PURE__ */ new Set();
274
- /** Rewinds an unfinished selection before Turbo snapshots the page. */
275
- #beforeCache = new BeforeCacheReset(() => this.#rewindForCache());
276
- /** Last painted month, or `null` until the initial paint has settled. */
277
444
  #announcedMonth = null;
278
- /**
279
- * Collapses a morph that swaps render inputs into one repaint, and refuses the
280
- * pass Stimulus delivers before `connect()`.
281
- */
282
- #repaint = new MicrotaskCoalescer(() => {
445
+ #multiselectable = new AttributeLease(
446
+ "aria-multiselectable",
447
+ this.identifier
448
+ );
449
+ #repaint = new MorphRenderWatcher(() => {
283
450
  this.#render();
284
451
  });
285
- /**
286
- * Seeds the range from optional hidden fields — ordered, and narrowed to the
287
- * days that can be chosen — then paints the grid, which writes the range back
288
- * to the fields silently.
289
- */
290
452
  connect() {
291
- this.#repaint.activate();
292
- this.#beforeCache.activate();
453
+ this.#repaint.observe(this.element);
293
454
  const authoredStart = this.hasStartFieldTarget ? normalizeISO(this.startFieldTarget.value) : "";
294
455
  const authoredEnd = this.hasEndFieldTarget ? normalizeISO(this.endFieldTarget.value) : "";
295
456
  [this.#startDate, this.#endDate] = this.#selectableRange(
@@ -301,58 +462,78 @@ var DateRangePickerController = class extends Controller {
301
462
  const anchor = parseISODateString(this.#startDate) ?? this.#clampToBounds(/* @__PURE__ */ new Date()) ?? /* @__PURE__ */ new Date();
302
463
  this.#focusedDate = anchor;
303
464
  this.#viewMonth = toISOMonthString(anchor);
304
- if (this.hasGridTarget) this.gridTarget.setAttribute("aria-multiselectable", "true");
305
465
  this.#render();
306
466
  }
307
- /** Cancels any pending deferred focus so it never fires on a detached element. */
308
467
  disconnect() {
309
- this.#repaint.cancel();
310
- this.#beforeCache.deactivate();
468
+ this.#repaint.disconnect();
311
469
  this.#focusTimer.clearAll();
312
470
  }
313
- /** Repaints the label when application code changes `locale` at runtime. */
314
471
  localeValueChanged() {
315
472
  this.#repaint.schedule();
316
473
  }
317
- /** Repaints when application code (or a Turbo morph) changes `min` at runtime. */
318
474
  minValueChanged() {
319
475
  this.#repaint.schedule();
320
476
  }
321
- /** Repaints when application code (or a Turbo morph) changes `max` at runtime. */
322
477
  maxValueChanged() {
323
478
  this.#repaint.schedule();
324
479
  }
325
- /** Re-indexes the unavailable dates and repaints when the declared set changes. */
326
480
  disabledDatesValueChanged() {
327
481
  this.#disabledDates = new Set(parseStringList(this.disabledDatesValue));
328
482
  this.#repaint.schedule();
329
483
  }
330
- /** Navigates to the previous month. */
484
+ gridTargetConnected() {
485
+ this.#repaint.schedule();
486
+ }
487
+ gridTargetDisconnected(grid) {
488
+ if (!this.gridTargets.includes(grid)) this.#multiselectable.return(grid);
489
+ this.#repaint.schedule();
490
+ }
491
+ monthLabelTargetConnected() {
492
+ this.#repaint.schedule();
493
+ }
494
+ monthLabelTargetDisconnected() {
495
+ this.#repaint.schedule();
496
+ }
497
+ startFieldTargetConnected() {
498
+ this.#repaint.schedule();
499
+ }
500
+ startFieldTargetDisconnected() {
501
+ this.#repaint.schedule();
502
+ }
503
+ endFieldTargetConnected() {
504
+ this.#repaint.schedule();
505
+ }
506
+ endFieldTargetDisconnected() {
507
+ this.#repaint.schedule();
508
+ }
331
509
  prev(event) {
332
510
  event?.preventDefault();
333
511
  this.#shiftMonth(-1);
334
512
  }
335
- /** Navigates to the next month. */
336
513
  next(event) {
337
514
  event?.preventDefault();
338
515
  this.#shiftMonth(1);
339
516
  }
340
- /** Confirms a range endpoint from a clicked cell. */
341
- selectDate(event) {
342
- const cell = this.#cellFrom(event.target);
517
+ selectDate(source) {
518
+ const { origin, reason } = actionSource(source);
519
+ if (origin && origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
520
+ return;
521
+ const cell = this.#cellFrom(origin);
343
522
  if (!cell) return;
344
523
  const date = cell.getAttribute("data-date");
345
524
  if (!date || !this.#isSelectable(date)) return;
346
- this.#choose(date);
525
+ this.#choose(date, reason);
347
526
  }
348
- /** Previews the range up to a hovered/focused cell while selecting. */
349
- previewTo(event) {
350
- const cell = this.#cellFrom(event.target);
527
+ previewTo(source) {
528
+ const { event, origin } = actionSource(source);
529
+ if (origin && origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
530
+ return;
531
+ const cell = this.#cellFrom(origin);
351
532
  if (!cell) return;
352
533
  const date = cell.getAttribute("data-date");
353
534
  if (!date) return;
354
535
  let shouldRender = false;
355
- if (event.type.startsWith("focus")) {
536
+ if (event?.type.startsWith("focus")) {
356
537
  const parsed = parseISODateString(date);
357
538
  if (parsed) this.#focusedDate = parsed;
358
539
  shouldRender = cell.getAttribute("tabindex") !== "0";
@@ -363,9 +544,11 @@ var DateRangePickerController = class extends Controller {
363
544
  }
364
545
  if (shouldRender) this.#render();
365
546
  }
366
- /** Applies a named preset (`today` / `last7` / `last30` / `thisMonth`). */
367
- applyPreset(event) {
368
- const button = event.target?.closest("[data-range]");
547
+ applyPreset(source) {
548
+ const { event, origin, reason } = actionSource(source);
549
+ const button = origin?.closest("[data-range]");
550
+ if (!button || !this.element.contains(button)) return;
551
+ const focus = event !== null || this.element.contains(document.activeElement);
369
552
  const range = computePreset(button?.getAttribute("data-range") ?? "");
370
553
  if (!range) return;
371
554
  const intersection = this.#intersectRange(range);
@@ -375,13 +558,20 @@ var DateRangePickerController = class extends Controller {
375
558
  const move = this.#setConfirmed(start, end);
376
559
  const endDate = parseISODateString(end);
377
560
  if (endDate) this.#focusedDate = endDate;
378
- this.#transitionTo(toISOMonthString(parseISODateString(end) ?? /* @__PURE__ */ new Date()), end, true);
379
- this.#reportConfirmed(move);
561
+ this.#transitionTo(
562
+ toISOMonthString(parseISODateString(end) ?? /* @__PURE__ */ new Date()),
563
+ end,
564
+ true,
565
+ () => focus && (event !== null || this.element.contains(document.activeElement))
566
+ );
567
+ this.#reportConfirmed(move, reason);
380
568
  }
381
- /** Grid keyboard navigation, selection (Enter/Space), and Escape-to-cancel. */
382
569
  onKeydown(event) {
383
570
  if (event.defaultPrevented) return;
384
571
  if (isReservedArrowChord(event)) return;
572
+ if ((event.key === "Home" || event.key === "End") && hasModifierChord(event)) return;
573
+ if ((event.key === "PageUp" || event.key === "PageDown") && (event.ctrlKey || event.altKey || event.metaKey))
574
+ return;
385
575
  const cell = this.#cellFrom(event.target);
386
576
  if (!cell) return;
387
577
  const dateStr = cell.getAttribute("data-date") ?? "";
@@ -402,7 +592,8 @@ var DateRangePickerController = class extends Controller {
402
592
  return;
403
593
  }
404
594
  let next = null;
405
- switch (logicalArrowKey(event.key, this.element)) {
595
+ const directionElement = this.hasGridTarget ? gridDirectionElement(this.gridTarget, this.element) : this.element;
596
+ switch (logicalArrowKey(event.key, directionElement)) {
406
597
  case "ArrowLeft":
407
598
  next = addDays(date, -1);
408
599
  break;
@@ -433,8 +624,7 @@ var DateRangePickerController = class extends Controller {
433
624
  event.preventDefault();
434
625
  this.#moveFocusTo(next);
435
626
  }
436
- /** Records a chosen date as either the pending start or the confirmed end. */
437
- #choose(date) {
627
+ #choose(date, reason = "user") {
438
628
  if (!this.#pendingStart || !this.#isSelectable(this.#pendingStart)) {
439
629
  this.#pendingStart = date;
440
630
  this.#previewDate = date;
@@ -446,54 +636,38 @@ var DateRangePickerController = class extends Controller {
446
636
  const [start, end] = date < this.#pendingStart ? [date, this.#pendingStart] : [this.#pendingStart, date];
447
637
  const move = this.#setConfirmed(start, end);
448
638
  this.#render(true);
449
- this.#reportConfirmed(move);
450
- }
451
- /**
452
- * Makes `start`–`end` the confirmed range, ending any selection in progress,
453
- * ahead of the paint that shows it.
454
- *
455
- * @returns The move count the range is now at, which its `change` checks.
456
- */
639
+ this.#reportConfirmed(move, reason);
640
+ }
457
641
  #setConfirmed(start, end) {
642
+ const changed = start !== this.#startDate || end !== this.#endDate;
458
643
  this.#startDate = start;
459
644
  this.#endDate = end;
460
645
  this.#pendingStart = "";
461
646
  this.#previewDate = "";
647
+ if (!changed) return null;
462
648
  this.#rangeMoves += 1;
463
649
  return this.#rangeMoves;
464
650
  }
465
- /**
466
- * Dispatches `change` for the range confirmed at `move`, once its paint has
467
- * reported it through the fields. A listener of those reports that moved the
468
- * range on has had the range it moved to reported instead.
469
- */
470
- #reportConfirmed(move) {
471
- if (move !== this.#rangeMoves) return;
472
- this.dispatch("change", { detail: { start: this.#startDate, end: this.#endDate } });
651
+ #reportConfirmed(move, reason) {
652
+ if (move === null || move !== this.#rangeMoves) return;
653
+ this.dispatch("change", { detail: { start: this.#startDate, end: this.#endDate, reason } });
473
654
  }
474
- /** Moves roving focus to `date`, transitioning the month when needed. */
475
655
  #moveFocusTo(date) {
476
656
  this.#focusedDate = date;
477
657
  const iso = toISODateString(date);
478
658
  if (this.#pendingStart && this.#isSelectable(iso)) this.#previewDate = iso;
479
659
  this.#transitionTo(toISOMonthString(date), iso);
480
660
  }
481
- /**
482
- * Renders `month`, then focuses the cell for `dateStr` (deferred if async).
483
- * A listener of the paint's reports that started another move has placed
484
- * focus itself, so this one leaves it there.
485
- *
486
- * @param confirmed - Whether the paint shows a range the user has just confirmed.
487
- */
488
- #transitionTo(month, dateStr, confirmed = false) {
661
+ #transitionTo(month, dateStr, confirmed = false, canFocus = () => true) {
489
662
  const isTransition = month !== this.#viewMonth;
490
663
  this.#focusTimer.clearAll();
491
664
  this.#viewMonth = month;
492
665
  this.#transitions += 1;
493
666
  const transition = this.#transitions;
494
667
  this.#render(confirmed);
495
- if (transition !== this.#transitions) return;
668
+ if (transition !== this.#transitions || !canFocus()) return;
496
669
  const focusCell = () => {
670
+ if (!canFocus()) return;
497
671
  this.cellTargets.find((c) => c.getAttribute("data-date") === dateStr)?.focus();
498
672
  };
499
673
  if (isTransition) {
@@ -502,28 +676,16 @@ var DateRangePickerController = class extends Controller {
502
676
  focusCell();
503
677
  }
504
678
  }
505
- /** Shifts the displayed month by `delta`, keeping focus within it. */
506
679
  #shiftMonth(delta) {
507
680
  const info = parseISOMonthString(this.#viewMonth);
508
681
  if (!info) return;
509
- const target = new Date(info.year, info.month - 1 + delta, 1);
682
+ const target = dateInMonthClamped(this.#focusedDate, info.year, info.month + delta);
510
683
  this.#viewMonth = toISOMonthString(target);
511
- const lastDay = new Date(target.getFullYear(), target.getMonth() + 1, 0).getDate();
512
- target.setDate(Math.min(this.#focusedDate.getDate(), lastDay));
513
684
  this.#focusedDate = target;
514
685
  this.#render();
515
686
  }
516
- /**
517
- * Builds the six-week grid and binds range/roving/disabled state per cell,
518
- * mirrors the confirmed range to the hidden fields, then reports what the
519
- * paint moved.
520
- *
521
- * @param confirmed - Whether the paint shows a range the user has just
522
- * confirmed, which the status then announces and each moved field reports.
523
- *
524
- * @stimeoRenderRoot
525
- */
526
687
  #render(confirmed = false) {
688
+ if (this.hasGridTarget) this.#multiselectable.write(this.gridTarget, "true");
527
689
  const info = parseISOMonthString(this.#viewMonth);
528
690
  if (!info) return;
529
691
  const { year, month } = info;
@@ -538,7 +700,7 @@ var DateRangePickerController = class extends Controller {
538
700
  this.monthLabelTarget.textContent = formatter.format(new Date(year, month - 1, 1));
539
701
  }
540
702
  const [rangeStart, rangeEnd] = this.#visualRange();
541
- const days = gridDays(year, month);
703
+ const days = monthGridDays(year, month, 0);
542
704
  const focusedStr = toISODateString(this.#focusedDate);
543
705
  const todayStr = toISODateString(/* @__PURE__ */ new Date());
544
706
  const cells = this.cellTargets;
@@ -576,8 +738,8 @@ var DateRangePickerController = class extends Controller {
576
738
  const moved = this.#mirrorFields();
577
739
  if (confirmed) {
578
740
  this.#announce();
579
- for (const [field, value] of moved) {
580
- if (field.value === value) commitField(field);
741
+ for (const [field, value, write] of moved) {
742
+ if (field.value === value && this.#fieldWrites.get(field) === write) commitField(field);
581
743
  }
582
744
  }
583
745
  const previous = this.#announcedMonth;
@@ -589,12 +751,6 @@ var DateRangePickerController = class extends Controller {
589
751
  this.dispatch("reconcile", { detail: { start: this.#startDate, end: this.#endDate } });
590
752
  }
591
753
  }
592
- /**
593
- * Narrows the confirmed range to days that can still be chosen. The paint
594
- * mirrors it to the fields without reporting a commit.
595
- *
596
- * @returns Whether the range moved.
597
- */
598
754
  #narrowConfirmedRange() {
599
755
  const [start, end] = this.#selectableRange(this.#startDate, this.#endDate);
600
756
  if (start === this.#startDate && end === this.#endDate) return false;
@@ -603,11 +759,6 @@ var DateRangePickerController = class extends Controller {
603
759
  this.#rangeMoves += 1;
604
760
  return true;
605
761
  }
606
- /**
607
- * The range `start`–`end` with each end moved inward to the nearest day that
608
- * can be chosen, or two empty strings when none of its days can be. A lone
609
- * endpoint is the one-day range it names, and stays lone.
610
- */
611
762
  #selectableRange(start, end) {
612
763
  let from = parseISODateString(start || end);
613
764
  let to = parseISODateString(end || start);
@@ -621,7 +772,6 @@ var DateRangePickerController = class extends Controller {
621
772
  if (from.getTime() > to.getTime()) return ["", ""];
622
773
  return [start ? toISODateString(from) : "", end ? toISODateString(to) : ""];
623
774
  }
624
- /** The ordered [start, end] pair to paint: the preview while selecting, else confirmed. */
625
775
  #visualRange() {
626
776
  if (this.#pendingStart) {
627
777
  const other = this.#previewDate || this.#pendingStart;
@@ -629,81 +779,59 @@ var DateRangePickerController = class extends Controller {
629
779
  }
630
780
  return [this.#startDate, this.#endDate];
631
781
  }
632
- /**
633
- * Writes the confirmed range to the hidden fields.
634
- *
635
- * @returns Each field whose value moved, with the value written to it.
636
- */
782
+ #writeField(field, value) {
783
+ if (!writeField(field, value)) return false;
784
+ this.#fieldWrites.set(field, (this.#fieldWrites.get(field) ?? 0) + 1);
785
+ return true;
786
+ }
637
787
  #mirrorFields() {
638
788
  const moved = [];
639
- if (this.hasStartFieldTarget && writeField(this.startFieldTarget, this.#startDate)) {
640
- moved.push([this.startFieldTarget, this.#startDate]);
789
+ if (this.hasStartFieldTarget && this.#writeField(this.startFieldTarget, this.#startDate)) {
790
+ moved.push([
791
+ this.startFieldTarget,
792
+ this.#startDate,
793
+ this.#fieldWrites.get(this.startFieldTarget) ?? 0
794
+ ]);
641
795
  }
642
- if (this.hasEndFieldTarget && writeField(this.endFieldTarget, this.#endDate)) {
643
- moved.push([this.endFieldTarget, this.#endDate]);
796
+ if (this.hasEndFieldTarget && this.#writeField(this.endFieldTarget, this.#endDate)) {
797
+ moved.push([
798
+ this.endFieldTarget,
799
+ this.#endDate,
800
+ this.#fieldWrites.get(this.endFieldTarget) ?? 0
801
+ ]);
644
802
  }
645
803
  return moved;
646
804
  }
647
- /** Announces the confirmed range in the live status region. */
648
805
  #announce() {
649
806
  if (this.hasStatusTarget && this.#startDate && this.#endDate) {
650
807
  this.statusTarget.textContent = `${this.#startDate} \u2013 ${this.#endDate}`;
651
808
  }
652
809
  }
653
- /**
654
- * True when `iso` may be chosen as a range endpoint.
655
- *
656
- * The single availability question in the controller: the grid paint, the
657
- * preview, and both commit paths ask it, so a cell's `aria-disabled` and what
658
- * a click on that cell does are the same decision rather than two that have to
659
- * be kept in step.
660
- */
661
810
  #isSelectable(iso) {
662
811
  return !this.#outOfBounds(iso) && !this.#disabledDates.has(iso);
663
812
  }
664
- /** True when `iso` falls outside the `[min, max]` bounds. */
665
813
  #outOfBounds(iso) {
666
814
  if (this.minValue && iso < this.minValue) return true;
667
815
  if (this.maxValue && iso > this.maxValue) return true;
668
816
  return false;
669
817
  }
670
- /** Clamps a generated ISO date string into `[min, max]`. */
671
818
  #clampISO(iso) {
672
819
  if (this.minValue && iso < this.minValue) return this.minValue;
673
820
  if (this.maxValue && iso > this.maxValue) return this.maxValue;
674
821
  return iso;
675
822
  }
676
- /** Clamps a Date into `[min, max]`, returning null only when unparseable. */
677
823
  #clampToBounds(date) {
678
824
  const clamped = this.#clampISO(toISODateString(date));
679
825
  return parseISODateString(clamped);
680
826
  }
681
- /**
682
- * Intersects a preset with `[min, max]`, rejecting a disjoint interval.
683
- *
684
- * Only the bounds narrow a preset here: `disabledDates` excludes single days,
685
- * not sub-intervals, so it cannot shrink one to a still-contiguous range. A
686
- * day it excludes is still refused as an endpoint — the caller checks that —
687
- * but a preset that merely spans one keeps its span.
688
- */
689
827
  #intersectRange(range) {
690
828
  const start = this.minValue && range.start < this.minValue ? this.minValue : range.start;
691
829
  const end = this.maxValue && range.end > this.maxValue ? this.maxValue : range.end;
692
830
  return start <= end ? { start, end } : null;
693
831
  }
694
- /** Removes provisional range state before Turbo freezes a cached snapshot. */
695
- #rewindForCache() {
696
- this.#focusTimer.clearAll();
697
- if (!this.#pendingStart && !this.#previewDate) return;
698
- this.#pendingStart = "";
699
- this.#previewDate = "";
700
- this.#render();
701
- }
702
- /** Resolves the cell element from an event target, or null. */
703
832
  #cellFrom(target) {
704
- return target?.closest(
705
- targetSelector(this.identifier, "cell")
706
- ) ?? null;
833
+ const cell = target instanceof Element ? target.closest(targetSelector(this.identifier, "cell")) : null;
834
+ return cell && this.cellTargets.includes(cell) ? cell : null;
707
835
  }
708
836
  };
709
837
  function addDays(date, n) {
@@ -711,30 +839,6 @@ function addDays(date, n) {
711
839
  next.setDate(next.getDate() + n);
712
840
  return next;
713
841
  }
714
- function shiftMonthClamped(date, delta) {
715
- const target = new Date(date.getFullYear(), date.getMonth() + delta, 1);
716
- const lastDay = new Date(target.getFullYear(), target.getMonth() + 1, 0).getDate();
717
- target.setDate(Math.min(date.getDate(), lastDay));
718
- return target;
719
- }
720
- function shiftYearClamped(date, delta) {
721
- const target = new Date(date.getFullYear() + delta, date.getMonth(), 1);
722
- const lastDay = new Date(target.getFullYear(), target.getMonth() + 1, 0).getDate();
723
- target.setDate(Math.min(date.getDate(), lastDay));
724
- return target;
725
- }
726
- function gridDays(year, month) {
727
- const first = new Date(year, month - 1, 1);
728
- const start = new Date(first);
729
- start.setDate(first.getDate() - first.getDay());
730
- const days = [];
731
- const current = new Date(start);
732
- for (let i = 0; i < GRID_SIZE; i++) {
733
- days.push(new Date(current));
734
- current.setDate(current.getDate() + 1);
735
- }
736
- return days;
737
- }
738
842
  function normalizeISO(value) {
739
843
  const date = parseISODateString(value.trim());
740
844
  return date ? toISODateString(date) : "";
@@ -763,5 +867,4 @@ function computePreset(name) {
763
867
  }
764
868
 
765
869
  export { DateRangePickerController };
766
- //# sourceMappingURL=date_range_picker_controller.js.map
767
870
  //# sourceMappingURL=date_range_picker_controller.js.map