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,69 +1,50 @@
1
1
  import { Controller } from '@hotwired/stimulus';
2
2
 
3
- // src/controllers/pointer_drag_controller.ts
4
-
5
- // src/utils/arrow_step.ts
6
3
  function isReservedArrowChord(event, allow = []) {
7
4
  if (!event.key.startsWith("Arrow")) return false;
8
5
  return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
9
6
  }
7
+ function hasModifierChord(event) {
8
+ return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
9
+ }
10
10
 
11
- // src/utils/detach_gate.ts
12
11
  var DetachGate = class _DetachGate {
13
- /** Set while a probe is queued, waiting for a reconnect to cancel it. */
14
12
  #pending = false;
15
- /**
16
- * True while a probe is queued — the last disconnect was ambiguous and no
17
- * reconnect has cancelled it yet. Read it from `connect()` to tell the
18
- * reconnect half of an in-page move from a first connect: a controller whose
19
- * initialisation restarts a measurement (a min-duration floor, an elapsed
20
- * counter) must skip it for the move, where nothing actually restarted.
21
- */
22
13
  get pending() {
23
14
  return this.#pending;
24
15
  }
25
- /**
26
- * True when the disconnect is definitely a real detach — the element left
27
- * the document, or `data-controller` no longer lists the identifier. False
28
- * means ambiguous (in-page move or observed-root exit), NOT "alive".
29
- */
30
16
  static isDetached(host) {
31
- if (!host.element.isConnected) return true;
32
- const tokens = (host.element.getAttribute("data-controller") ?? "").split(/\s+/);
33
- return !tokens.includes(host.identifier);
34
- }
35
- /**
36
- * Call from `disconnect()`: runs `teardown` synchronously on a definite
37
- * detach (fast path), otherwise defers it one microtask — a reconnect
38
- * ({@link cancel} from `connect()`) keeps the state, no reconnect runs it.
39
- * One microtask is the whole probe window: Stimulus reconnects a moved
40
- * element within the same mutation batch, before the checkpoint drains.
41
- */
17
+ return !host.element.isConnected || !_DetachGate.#listed(host);
18
+ }
42
19
  disconnected(host, teardown) {
43
- if (_DetachGate.isDetached(host)) {
20
+ const carried = _DetachGate.#carried(host);
21
+ if (!carried && _DetachGate.isDetached(host)) {
44
22
  this.#pending = false;
45
23
  teardown();
46
24
  return;
47
25
  }
48
26
  this.#pending = true;
49
- queueMicrotask(() => {
27
+ const probe = () => {
50
28
  if (!this.#pending) return;
51
29
  this.#pending = false;
52
30
  teardown();
53
- });
31
+ };
32
+ if (carried) setTimeout(probe, 0);
33
+ else queueMicrotask(probe);
34
+ }
35
+ static #carried(host) {
36
+ const element = host.element;
37
+ return !element.isConnected && element.closest("[data-turbo-permanent]") !== null && _DetachGate.#listed(host);
38
+ }
39
+ static #listed(host) {
40
+ const tokens = (host.element.getAttribute("data-controller") ?? "").split(/\s+/);
41
+ return tokens.includes(host.identifier);
54
42
  }
55
- /**
56
- * Disarms a pending probe. Call from `connect()` (the reconnect that proves
57
- * an in-page move) and from the head of any teardown path not routed through
58
- * {@link disconnected} (disabled-toggle, Escape), so an orphaned probe can
59
- * never run the teardown a second time.
60
- */
61
43
  cancel() {
62
44
  this.#pending = false;
63
45
  }
64
46
  };
65
47
 
66
- // src/utils/event_owner.ts
67
48
  function ownerIndex(candidates, node) {
68
49
  if (!(node instanceof Node)) return -1;
69
50
  return candidates.findIndex((candidate) => candidate.contains(node));
@@ -72,7 +53,148 @@ function ownerOf(candidates, node) {
72
53
  return candidates[ownerIndex(candidates, node)] ?? null;
73
54
  }
74
55
 
75
- // src/utils/owned_pointer_session.ts
56
+ function unslotted(element) {
57
+ return element.assignedSlot === null && element.parentElement?.shadowRoot != null;
58
+ }
59
+ function flatTreeParent(element) {
60
+ if (unslotted(element)) return null;
61
+ const parent = element.assignedSlot ?? element.parentNode;
62
+ if (parent instanceof ShadowRoot) return parent.host;
63
+ return parent instanceof Element ? parent : null;
64
+ }
65
+ function flatTreeContains(ancestor, element) {
66
+ for (let node = element; node; node = flatTreeParent(node)) {
67
+ if (node === ancestor) return true;
68
+ }
69
+ return false;
70
+ }
71
+ function closestInFlatTree(element, selector) {
72
+ for (let node = element; node; node = flatTreeParent(node)) {
73
+ if (node.matches(selector)) return node;
74
+ }
75
+ return null;
76
+ }
77
+ function deepActiveElement(ownerDocument) {
78
+ let active = ownerDocument.activeElement;
79
+ while (active?.shadowRoot?.activeElement) active = active.shadowRoot.activeElement;
80
+ return active;
81
+ }
82
+
83
+ var ListenerSet = class {
84
+ #abort = new AbortController();
85
+ add(target, type, handler, options) {
86
+ target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
87
+ }
88
+ dispose() {
89
+ this.#abort.abort();
90
+ this.#abort = new AbortController();
91
+ }
92
+ };
93
+
94
+ var MicrotaskCoalescer = class {
95
+ #run;
96
+ #queued = false;
97
+ #active = false;
98
+ #generation = 0;
99
+ constructor(run) {
100
+ this.#run = run;
101
+ }
102
+ activate() {
103
+ this.#active = true;
104
+ }
105
+ cancel() {
106
+ this.#active = false;
107
+ this.#queued = false;
108
+ this.#generation += 1;
109
+ }
110
+ get pending() {
111
+ return this.#queued;
112
+ }
113
+ schedule() {
114
+ if (!this.#active || this.#queued) return;
115
+ this.#queued = true;
116
+ const generation = this.#generation;
117
+ queueMicrotask(() => {
118
+ if (generation !== this.#generation || !this.#queued || !this.#active) return;
119
+ this.#queued = false;
120
+ this.#run();
121
+ });
122
+ }
123
+ };
124
+
125
+ var MorphRenderWatcher = class {
126
+ #listeners = new ListenerSet();
127
+ #pass;
128
+ #element = null;
129
+ #onMorph = () => this.#pass.schedule();
130
+ constructor(run) {
131
+ this.#pass = new MicrotaskCoalescer(run);
132
+ }
133
+ observe(element) {
134
+ if (this.#element === element) return;
135
+ this.disconnect();
136
+ this.#element = element;
137
+ this.#pass.activate();
138
+ this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
139
+ }
140
+ schedule() {
141
+ this.#pass.schedule();
142
+ }
143
+ get pending() {
144
+ return this.#pass.pending;
145
+ }
146
+ disconnect() {
147
+ this.#listeners.dispose();
148
+ this.#pass.cancel();
149
+ this.#element = null;
150
+ }
151
+ };
152
+
153
+ var NUMBER_BOUNDS = {
154
+ nonNegative: { finite: true, min: 0 },
155
+ positive: { finite: true, exclusiveMin: 0 }};
156
+ function matchesNumberBounds(value, bounds) {
157
+ if (!Number.isFinite(value)) {
158
+ const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
159
+ if (direction === null) return false;
160
+ if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
161
+ }
162
+ if (bounds.min !== void 0 && value < bounds.min) return false;
163
+ if (bounds.max !== void 0 && value > bounds.max) return false;
164
+ if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
165
+ if (bounds.integer && !Number.isInteger(value)) return false;
166
+ if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
167
+ return true;
168
+ }
169
+
170
+ function readNumber(raw, fallback, bounds) {
171
+ return matchesNumberBounds(raw, bounds) ? raw : fallback;
172
+ }
173
+
174
+ var NumberValueReader = class {
175
+ #lastRejected = /* @__PURE__ */ new Map();
176
+ read(owner, name, raw, fallback, bounds) {
177
+ const resolved = readNumber(raw, fallback, bounds);
178
+ if (matchesNumberBounds(raw, bounds)) {
179
+ this.#lastRejected.delete(name);
180
+ return resolved;
181
+ }
182
+ const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
183
+ const literal = owner.element.getAttribute(attribute);
184
+ if (literal === null) {
185
+ this.#lastRejected.delete(name);
186
+ return resolved;
187
+ }
188
+ if (this.#lastRejected.get(name) !== literal) {
189
+ this.#lastRejected.set(name, literal);
190
+ console.warn(
191
+ `Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
192
+ );
193
+ }
194
+ return resolved;
195
+ }
196
+ };
197
+
76
198
  var OwnedPointerSession = class {
77
199
  #pointerId;
78
200
  #owner;
@@ -92,7 +214,6 @@ var OwnedPointerSession = class {
92
214
  } catch {
93
215
  }
94
216
  }
95
- /** Releases capture and listeners, then reports the end exactly once. */
96
217
  end() {
97
218
  this.#finish("teardown");
98
219
  }
@@ -117,23 +238,158 @@ var OwnedPointerSession = class {
117
238
  };
118
239
  };
119
240
 
120
- // src/utils/transient_hooks.ts
241
+ function setAttributeIfChanged(element, name, value, beforeWrite) {
242
+ if (element.getAttribute(name) === value) return false;
243
+ if (value === null) element.removeAttribute(name);
244
+ else element.setAttribute(name, value);
245
+ return true;
246
+ }
247
+
248
+ function queryOpenDescendants(root, selector) {
249
+ const matches = [];
250
+ const roots = [root];
251
+ for (const scope of roots) {
252
+ matches.push(...scope.querySelectorAll(selector));
253
+ if (scope.nodeType === Node.ELEMENT_NODE) {
254
+ const shadow = scope.shadowRoot;
255
+ if (shadow) roots.push(shadow);
256
+ }
257
+ for (const element of scope.querySelectorAll("*")) {
258
+ if (element.shadowRoot) roots.push(element.shadowRoot);
259
+ }
260
+ }
261
+ return matches;
262
+ }
263
+
264
+ function sharedRegistry(key, create) {
265
+ const symbol = Symbol.for(key);
266
+ const scope = globalThis;
267
+ const existing = scope[symbol];
268
+ if (existing !== void 0) return existing;
269
+ const registry = create();
270
+ Object.defineProperty(globalThis, symbol, { value: registry });
271
+ return registry;
272
+ }
273
+
274
+ function holdsFocus(element) {
275
+ return element.matches(":focus") || deepActiveElement(element.ownerDocument) === element;
276
+ }
277
+ var TabindexLoan = class _TabindexLoan {
278
+ static #registry = sharedRegistry(
279
+ "stimeo-ui.tabindex-loan.registry.v1",
280
+ () => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Set() })
281
+ );
282
+ #value;
283
+ #record;
284
+ #preserveFocus;
285
+ #lent = /* @__PURE__ */ new Set();
286
+ constructor(value, owner, options = {}) {
287
+ this.#value = value;
288
+ this.#record = `data-${owner}-tabindex-loan`;
289
+ this.#preserveFocus = options.preserveFocus === true;
290
+ _TabindexLoan.#registry.names.add(this.#record);
291
+ }
292
+ lend(element, options = {}) {
293
+ const stack = this.#stack(element);
294
+ const current = element.getAttribute("tabindex");
295
+ const held = stack.find((hold) => hold.loan === this) ?? stack.find(
296
+ (hold) => hold.loan === null && hold.record === this.#record && hold.value === this.#value
297
+ );
298
+ const linked = stack.at(-1)?.value === current;
299
+ if (!held && options.onlyIfUnfocusable && (current !== null || element.tabIndex >= 0)) return;
300
+ if (!held && current !== null && !linked) return;
301
+ this.#lent.add(element);
302
+ if (held) held.loan = this;
303
+ else stack.push({ loan: this, record: this.#record, value: this.#value, linked });
304
+ this.#mark(element, stack);
305
+ if (current === null || !held && current !== this.#value) {
306
+ element.setAttribute("tabindex", this.#value);
307
+ }
308
+ }
309
+ return(element) {
310
+ const stack = this.#stack(element);
311
+ const hold = stack.find((held) => held.loan === this) ?? stack.find(
312
+ (held) => held.loan === null && held.record === this.#record && held.value === this.#value
313
+ );
314
+ if (!hold) return false;
315
+ if (!this.#release(element, stack, hold, this.#preserveFocus)) {
316
+ hold.loan = this;
317
+ this.#lent.add(element);
318
+ return false;
319
+ }
320
+ this.#lent.delete(element);
321
+ return true;
322
+ }
323
+ returnAll() {
324
+ for (const element of Array.from(this.#lent)) this.return(element);
325
+ }
326
+ reclaim(element) {
327
+ if (element.getAttribute(this.#record) !== this.#value) return;
328
+ const stack = this.#stack(element);
329
+ const hold = stack.find((held) => held.loan === null && held.record === this.#record);
330
+ if (hold && !holdsFocus(element)) this.#release(element, stack, hold);
331
+ }
332
+ reclaimWithin(root) {
333
+ for (const element of queryOpenDescendants(root, `[${this.#record}]`)) {
334
+ this.reclaim(element);
335
+ }
336
+ }
337
+ #stack(element) {
338
+ const stacks = _TabindexLoan.#registry.stacks;
339
+ const stack = stacks.get(element) ?? this.#seed(element);
340
+ stacks.set(element, stack);
341
+ return stack;
342
+ }
343
+ #seed(element) {
344
+ const current = element.getAttribute("tabindex");
345
+ const holds = [];
346
+ for (const record of _TabindexLoan.#registry.names) {
347
+ const value = element.getAttribute(record);
348
+ if (value !== null) holds.push({ loan: null, record, value, linked: true });
349
+ }
350
+ holds.sort((one, other) => Number(one.value === current) - Number(other.value === current));
351
+ for (const [index, hold] of holds.entries()) hold.linked = index > 0;
352
+ return holds;
353
+ }
354
+ #release(element, stack, hold, preserveFocus = false) {
355
+ const current = element.getAttribute("tabindex");
356
+ const index = stack.indexOf(hold);
357
+ const owns = current === hold.value && !stack.slice(index + 1).some((above2) => above2.value === current);
358
+ if (owns && preserveFocus && holdsFocus(element)) return false;
359
+ stack.splice(index, 1);
360
+ const above = stack[index];
361
+ if (above) above.linked &&= hold.linked;
362
+ this.#mark(element, stack);
363
+ if (!owns) return true;
364
+ const below = hold.linked ? stack[index - 1] : void 0;
365
+ if (!below) element.removeAttribute("tabindex");
366
+ else if (below.value !== current) element.setAttribute("tabindex", below.value);
367
+ return true;
368
+ }
369
+ #mark(element, stack) {
370
+ const records = /* @__PURE__ */ new Map([[this.#record, null]]);
371
+ for (const hold of stack) records.set(hold.record, hold.value);
372
+ for (const [name, recorded] of records) {
373
+ setAttributeIfChanged(element, name, recorded);
374
+ }
375
+ }
376
+ };
377
+
121
378
  var TransientHooks = class {
122
379
  #attributes;
123
380
  constructor(options) {
124
381
  this.#attributes = options.attributes;
125
382
  }
126
- /** Drops the declared hooks from `element`; what it carries besides stays. */
127
383
  reset(element) {
128
384
  for (const attribute of this.#attributes) element.removeAttribute(attribute);
129
385
  }
130
386
  };
131
387
 
132
- // src/controllers/pointer_drag_controller.ts
133
388
  var TRANSIENT_DRAG = new TransientHooks({ attributes: ["data-dragging"] });
134
389
  var TRANSIENT_GRAB = new TransientHooks({ attributes: ["data-grabbed"] });
135
390
  var NATIVE_KEY_OWNERS = "input, textarea, select, button, a[href], summary, [contenteditable]";
136
391
  var PointerDragController = class _PointerDragController extends Controller {
392
+ #numbers = new NumberValueReader();
137
393
  static targets = ["handle"];
138
394
  static values = {
139
395
  axis: { type: String, default: "both" },
@@ -142,52 +398,63 @@ var PointerDragController = class _PointerDragController extends Controller {
142
398
  disabled: { type: Boolean, default: false },
143
399
  follow: { type: Boolean, default: false }
144
400
  };
401
+ static valueConstraints = {
402
+ threshold: NUMBER_BOUNDS.nonNegative,
403
+ keyboardStep: NUMBER_BOUNDS.positive
404
+ };
145
405
  static actions = ["reset"];
146
406
  static events = ["start", "move", "end", "cancel"];
147
- /** Marker attribute recording that this controller set the touch-action. */
148
407
  static #TOUCH_ACTION_MARKER = "data-pointer-drag-touch-action";
149
- /** Marker attribute recording that this controller set `tabindex`. */
150
- static #TABINDEX_MARKER = "data-pointer-drag-tabindex";
408
+ #tabindex = new TabindexLoan("0", this.identifier, { preserveFocus: true });
151
409
  #pointer = null;
152
410
  #keyboard = null;
153
- /** Committed follow offset from past drops; a new drag's deltas add onto it. */
411
+ #keyboardFocusSession = null;
412
+ #listeners = new ListenerSet();
413
+ #shadowFocus = new ListenerSet();
414
+ #keyboardFocus = new MicrotaskCoalescer(() => this.#settleKeyboardFocus());
154
415
  #followBase = { x: 0, y: 0 };
155
- /** Decides whether a mid-session disconnect() is an in-page move or a detach. */
416
+ #gestureFollows = false;
417
+ #followOutput = null;
156
418
  #gate = new DetachGate();
419
+ #morphRender = new MorphRenderWatcher(() => {
420
+ for (const handle of this.#handles()) this.#prepareHandle(handle);
421
+ this.#reflectSession();
422
+ });
157
423
  connect() {
424
+ this.#morphRender.observe(this.element);
425
+ if (!this.#pointer && !this.#keyboard) this.#followOutput = null;
158
426
  this.#gate.cancel();
159
427
  if (!this.#pointer) TRANSIENT_DRAG.reset(this.element);
160
428
  if (!this.#keyboard) TRANSIENT_GRAB.reset(this.element);
161
- if (this.followValue && !this.#pointer && !this.#keyboard) {
162
- this.#followBase = this.#parseFollowBase();
163
- }
164
- this.element.addEventListener("pointerdown", this.#onPointerDown);
165
- this.element.addEventListener("keydown", this.#onKeydown);
429
+ this.#listeners.add(this.element, "pointerdown", this.#onPointerDown);
430
+ this.#listeners.add(this.element, "keydown", this.#onKeydown);
431
+ this.#listeners.add(this.element, "focusout", this.#onFocusout);
432
+ this.#listeners.add(this.element, "focusin", this.#onFocusin);
433
+ this.#keyboardFocus.activate();
166
434
  if (!this.hasHandleTarget) this.#prepareHandle(this.element);
435
+ if (this.#keyboard) {
436
+ this.#watchShadowFocus(this.#keyboard.handle);
437
+ this.#checkKeyboardFocus(this.#keyboard);
438
+ }
167
439
  }
168
440
  disconnect() {
169
- this.element.removeEventListener("pointerdown", this.#onPointerDown);
170
- this.element.removeEventListener("keydown", this.#onKeydown);
441
+ this.#morphRender.disconnect();
442
+ this.#listeners.dispose();
443
+ this.#shadowFocus.dispose();
444
+ this.#keyboardFocus.cancel();
445
+ this.#keyboardFocusSession = null;
171
446
  if (this.#pointer || this.#keyboard) {
172
447
  this.#gate.disconnected(this, () => this.#teardown());
173
448
  return;
174
449
  }
175
450
  this.#teardown();
176
451
  }
177
- /**
178
- * Returns the element to its origin: drops the committed follow offset and the
179
- * inline `translate` that carries it.
180
- *
181
- * In follow mode the inline `translate` belongs to this controller, and the
182
- * committed offset lives in a field the DOM cannot reach — so a consumer that
183
- * wants the element back at the start, or that has written a position of its
184
- * own, needs this to make the two agree again. An in-flight drag is cancelled
185
- * first, or its deltas would land on top of the offset just cleared.
186
- */
187
452
  reset() {
453
+ const follows = this.#pointer || this.#keyboard ? this.#gestureFollows : this.followValue;
188
454
  const live = this.#pointer?.started ? this.#pointer.pointerType : this.#keyboard ? "keyboard" : null;
189
455
  if (this.#pointer || this.#keyboard) this.#teardownSessions();
190
456
  if (live) this.#dispatchCancel(live);
457
+ if (!follows) return;
191
458
  this.#followBase = { x: 0, y: 0 };
192
459
  this.#applyFollow(0, 0);
193
460
  }
@@ -204,13 +471,6 @@ var PointerDragController = class _PointerDragController extends Controller {
204
471
  if (!this.hasHandleTarget) this.#prepareHandle(this.element);
205
472
  if (interrupted) this.#dispatchCancel(interrupted);
206
473
  }
207
- /**
208
- * Re-derives the handles' touch-action when the axis changes.
209
- *
210
- * Two layers, like the `tabindex` loan: the marker says the value was once
211
- * ours, and the current inline value says it still is. A consumer that wrote
212
- * its own `touch-action` after connect keeps it.
213
- */
214
474
  axisValueChanged(_value, previousValue) {
215
475
  const lent = this.#touchActionFor(previousValue ?? this.axisValue);
216
476
  for (const handle of this.#handles()) {
@@ -219,7 +479,6 @@ var PointerDragController = class _PointerDragController extends Controller {
219
479
  handle.style.touchAction = this.#touchActionForAxis();
220
480
  }
221
481
  }
222
- /** Cancels any in-flight session when the controller is disabled mid-drag. */
223
482
  disabledValueChanged() {
224
483
  if (!this.disabledValue) return;
225
484
  if (this.#pointer?.started || this.#keyboard) {
@@ -227,17 +486,14 @@ var PointerDragController = class _PointerDragController extends Controller {
227
486
  }
228
487
  this.#teardownSessions();
229
488
  }
230
- /**
231
- * Arms a pointer drag on a handle; `start` waits for the threshold.
232
- *
233
- * @stimeoRuntimeOnly `disabled` decides whether this one pointer press starts a gesture.
234
- */
235
- #onPointerDown = (event) => {
489
+ #onPointerDown = (rawEvent) => {
490
+ const event = rawEvent;
236
491
  if (this.disabledValue || this.#pointer || this.#keyboard || event.button !== 0) return;
237
492
  const handle = this.#handleFor(event.target);
238
493
  if (!handle) return;
239
494
  event.preventDefault();
240
495
  handle.focus();
496
+ this.#beginGesture();
241
497
  const session = {
242
498
  pointer: null,
243
499
  handle,
@@ -254,18 +510,13 @@ var PointerDragController = class _PointerDragController extends Controller {
254
510
  });
255
511
  this.#pointer = session;
256
512
  };
257
- /**
258
- * Applies one tracked move: threshold, hooks, follow offset, events.
259
- *
260
- * @stimeoRuntimeOnly `threshold` decides when this one pointer move starts the drag.
261
- */
262
513
  #trackMove(session, event) {
263
514
  const [dx, dy] = this.#filterAxis(
264
515
  event.clientX - session.originX,
265
516
  event.clientY - session.originY
266
517
  );
267
518
  if (!session.started) {
268
- if (Math.hypot(dx, dy) < this.thresholdValue) return;
519
+ if (Math.hypot(dx, dy) < this.#safeThreshold) return;
269
520
  session.started = true;
270
521
  this.element.setAttribute("data-dragging", "true");
271
522
  this.dispatch("start", {
@@ -279,13 +530,6 @@ var PointerDragController = class _PointerDragController extends Controller {
279
530
  detail: { dx, dy, x: event.clientX, y: event.clientY, pointerType: session.pointerType }
280
531
  });
281
532
  }
282
- /**
283
- * Closes the session and reports only the outcome the caller does not own.
284
- *
285
- * A teardown (detach, Escape, mid-drag disable, `reset`) is announced by the
286
- * path that asked for it, so this one stays silent; `pointerup` below the
287
- * threshold was a plain click, and `pointercancel` is an OS gesture takeover.
288
- */
289
533
  #finishPointer(session, kind) {
290
534
  const { started, dx, dy, pointerType } = session;
291
535
  this.#pointer = null;
@@ -298,19 +542,14 @@ var PointerDragController = class _PointerDragController extends Controller {
298
542
  }
299
543
  this.#dispatchCancel(pointerType);
300
544
  }
301
- /**
302
- * Keyboard alternative: Space/Enter grab & drop, arrows move, Escape cancels.
303
- *
304
- * @stimeoRuntimeOnly `disabled` decides whether this one key is taken and `keyboardStep` is the
305
- * distance of one keyboard move.
306
- */
307
- #onKeydown = (event) => {
545
+ #onKeydown = (rawEvent) => {
546
+ const event = rawEvent;
308
547
  if (this.disabledValue) return;
309
548
  const handle = this.#handleFor(event.target);
310
549
  if (!handle) return;
311
550
  if (event.defaultPrevented) return;
312
551
  if (event.isComposing) return;
313
- if (this.#ownsNativeKeys(event.target, handle)) return;
552
+ if (this.#ownsNativeKeys(event.composedPath()[0] ?? event.target, handle)) return;
314
553
  if (isReservedArrowChord(event)) return;
315
554
  if (event.key === "Escape") {
316
555
  if (this.#pointer?.started) {
@@ -334,6 +573,7 @@ var PointerDragController = class _PointerDragController extends Controller {
334
573
  }
335
574
  if (!this.#keyboard) return;
336
575
  if (event.key === "Home" || event.key === "End") {
576
+ if (hasModifierChord(event)) return;
337
577
  event.preventDefault();
338
578
  return;
339
579
  }
@@ -355,9 +595,50 @@ var PointerDragController = class _PointerDragController extends Controller {
355
595
  }
356
596
  });
357
597
  };
358
- /** Enters the grabbed mode: `data-grabbed` on element + handle, then `start`. */
598
+ #onFocusin = () => {
599
+ const session = this.#keyboard;
600
+ if (session) {
601
+ this.#watchShadowFocus(session.handle);
602
+ this.#checkKeyboardFocus(session);
603
+ }
604
+ };
605
+ #watchShadowFocus(handle) {
606
+ const tree = handle.getRootNode();
607
+ let active = tree.activeElement;
608
+ while (active?.shadowRoot) {
609
+ const shadow = active.shadowRoot;
610
+ this.#shadowFocus.add(shadow, "focusin", this.#onFocusin);
611
+ active = shadow.activeElement;
612
+ }
613
+ }
614
+ #onFocusout = (rawEvent) => {
615
+ const destination = rawEvent.relatedTarget;
616
+ const session = this.#keyboard;
617
+ if (session && this.#handleFor(destination) === session.handle && !this.#ownsNativeKeys(destination, session.handle)) {
618
+ return;
619
+ }
620
+ this.#checkKeyboardFocus(this.#keyboard);
621
+ };
622
+ #checkKeyboardFocus(session) {
623
+ this.#keyboardFocusSession = session;
624
+ this.#keyboardFocus.schedule();
625
+ }
626
+ #settleKeyboardFocus() {
627
+ const session = this.#keyboardFocusSession;
628
+ this.#keyboardFocusSession = null;
629
+ if (!session || this.#keyboard !== session || !this.element.isConnected) return;
630
+ const tree = session.handle.getRootNode();
631
+ const active = tree.activeElement ?? null;
632
+ if (this.#handleFor(active) === session.handle && !this.#ownsNativeKeys(active, session.handle)) {
633
+ return;
634
+ }
635
+ this.#clearKeyboardSession();
636
+ this.#dispatchCancel("keyboard");
637
+ }
359
638
  #grabKeyboard(handle) {
639
+ this.#beginGesture();
360
640
  this.#keyboard = { handle, dx: 0, dy: 0 };
641
+ this.#watchShadowFocus(handle);
361
642
  this.element.setAttribute("data-grabbed", "true");
362
643
  handle.setAttribute("data-grabbed", "true");
363
644
  this.dispatch("start", { detail: { x: 0, y: 0, pointerType: "keyboard" } });
@@ -369,9 +650,8 @@ var PointerDragController = class _PointerDragController extends Controller {
369
650
  this.#followCommit(session.dx, session.dy);
370
651
  this.dispatch("end", { detail: { dx: session.dx, dy: session.dy, pointerType: "keyboard" } });
371
652
  }
372
- /** Maps an arrow key to a raw (unfiltered) `keyboardStep` delta. */
373
653
  #keyboardDelta(key) {
374
- const step = this.keyboardStepValue;
654
+ const step = this.#safeKeyboardStep;
375
655
  switch (key) {
376
656
  case "ArrowRight":
377
657
  return [step, 0];
@@ -385,47 +665,29 @@ var PointerDragController = class _PointerDragController extends Controller {
385
665
  return null;
386
666
  }
387
667
  }
388
- /**
389
- * Zeroes the delta on the locked axis (`axis` = x | y | both).
390
- *
391
- * @stimeoRuntimeOnly `axis` filters the offset of one move to the allowed direction.
392
- */
393
668
  #filterAxis(dx, dy) {
394
669
  if (this.axisValue === "x") return [dx, 0];
395
670
  if (this.axisValue === "y") return [0, dy];
396
671
  return [dx, dy];
397
672
  }
398
- /** Silently closes a live pointer session; the caller announces the outcome. */
399
673
  #endPointerSession() {
400
674
  this.#pointer?.pointer?.end();
401
675
  }
402
676
  #clearKeyboardSession() {
677
+ this.#shadowFocus.dispose();
403
678
  this.#keyboard?.handle.removeAttribute("data-grabbed");
404
679
  this.#keyboard = null;
680
+ this.#keyboardFocusSession = null;
405
681
  this.element.removeAttribute("data-grabbed");
406
682
  }
407
- /**
408
- * Full teardown for a controller whose element is going away or lost its
409
- * controller. Into a dead (detached) tree it is silent — consumers restore
410
- * from their own `connect()`, not from a cancel event. But a detach that
411
- * KEEPS the element (Turbo morph, `data-controller` removed) leaves the
412
- * consumers alive and listening: an in-flight session must end in `cancel`
413
- * there, or a composer like sortable strands its one-at-a-time session
414
- * bookkeeping forever.
415
- */
416
683
  #teardown() {
417
- this.#gate.cancel();
418
684
  const interrupted = this.#pointer?.started ? this.#pointer.pointerType : this.#keyboard ? "keyboard" : null;
419
685
  this.#teardownSessions();
420
686
  for (const handle of this.#handles()) this.#restoreHandle(handle);
421
- this.#followReset();
687
+ this.#tabindex.returnAll();
688
+ if (interrupted) this.#followReset();
422
689
  if (interrupted && this.element.isConnected) this.#dispatchCancel(interrupted);
423
690
  }
424
- /**
425
- * Silently tears down whatever session is live (disconnect / disabled /
426
- * Escape). Composed from the two single-session teardowns so pointer and
427
- * keyboard cleanup cannot diverge as either path evolves.
428
- */
429
691
  #teardownSessions() {
430
692
  this.#endPointerSession();
431
693
  this.#clearKeyboardSession();
@@ -434,63 +696,52 @@ var PointerDragController = class _PointerDragController extends Controller {
434
696
  this.#followReset();
435
697
  this.dispatch("cancel", { detail: { pointerType } });
436
698
  }
437
- /**
438
- * Applies the in-flight offset to the element's `translate` (follow only).
439
- *
440
- * @stimeoRuntimeOnly `follow` decides whether this one move writes the offset as a translate; the
441
- * offset itself comes from the gesture.
442
- */
699
+ #beginGesture() {
700
+ this.#gestureFollows = this.followValue;
701
+ if (this.#gestureFollows) this.#followBase = this.#parseFollowBase();
702
+ }
443
703
  #followMove(dx, dy) {
444
- if (!this.followValue) return;
704
+ if (!this.#gestureFollows) return;
445
705
  this.#applyFollow(this.#followBase.x + dx, this.#followBase.y + dy);
446
706
  }
447
- /**
448
- * Folds a drop's deltas into the committed base offset (follow only).
449
- *
450
- * @stimeoRuntimeOnly `follow` decides whether the end of this one gesture keeps the offset as a
451
- * translate.
452
- */
453
707
  #followCommit(dx, dy) {
454
- if (!this.followValue) return;
708
+ if (!this.#gestureFollows) return;
455
709
  this.#followBase = { x: this.#followBase.x + dx, y: this.#followBase.y + dy };
456
710
  this.#applyFollow(this.#followBase.x, this.#followBase.y);
457
711
  }
458
- /**
459
- * Snaps back to the committed position (follow only) — the cancel contract.
460
- *
461
- * @stimeoRuntimeOnly `follow` decides whether cancelling this one gesture clears the translate it
462
- * wrote.
463
- */
464
712
  #followReset() {
465
- if (!this.followValue) return;
713
+ if (!this.#gestureFollows) return;
466
714
  this.#applyFollow(this.#followBase.x, this.#followBase.y);
467
715
  }
468
- /**
469
- * Writes the offset as the standalone CSS `translate` property — independent
470
- * of `transform`, so an authored transform (scale, rotate…) is never
471
- * clobbered. At the origin the property is removed to keep the DOM clean.
472
- */
716
+ #reflectSession() {
717
+ if (this.#pointer?.started) this.element.setAttribute("data-dragging", "true");
718
+ else this.element.removeAttribute("data-dragging");
719
+ if (this.#keyboard) {
720
+ this.element.setAttribute("data-grabbed", "true");
721
+ this.#keyboard.handle.setAttribute("data-grabbed", "true");
722
+ } else {
723
+ this.element.removeAttribute("data-grabbed");
724
+ for (const handle of this.#handles()) handle.removeAttribute("data-grabbed");
725
+ }
726
+ const follows = this.#pointer || this.#keyboard ? this.#gestureFollows : this.followValue;
727
+ if (follows && this.#followOutput) {
728
+ this.#applyFollow(this.#followOutput.x, this.#followOutput.y);
729
+ }
730
+ }
473
731
  #applyFollow(x, y) {
732
+ this.#followOutput = { x, y };
474
733
  if (x === 0 && y === 0) this.element.style.removeProperty("translate");
475
734
  else this.element.style.setProperty("translate", `${x}px ${y}px`);
476
735
  }
477
- /**
478
- * Reads a committed follow offset back from the inline `translate`. Only the
479
- * controller's own `<x>px <y>px` shape is recognized; anything else (an
480
- * authored percentage, `none`, an empty string) starts the base at 0 — follow
481
- * mode owns this property, per the class contract.
482
- */
483
736
  #parseFollowBase() {
484
737
  const raw = this.element.style.getPropertyValue("translate").trim();
485
738
  const match = /^(-?\d+(?:\.\d+)?)px(?:\s+(-?\d+(?:\.\d+)?)px)?$/.exec(raw);
486
739
  if (!match) return { x: 0, y: 0 };
487
740
  return { x: Number(match[1]), y: Number(match[2] ?? "0") };
488
741
  }
489
- /** The active handles: the `handle` targets, else the element itself. */
490
742
  #handles() {
491
743
  return this.hasHandleTarget ? this.handleTargets : [this.element];
492
744
  }
493
- /** Resolves the handle owning an event target (the handle or a descendant). */
494
745
  #handleFor(target) {
495
746
  return ownerOf(this.#handles(), target);
496
747
  }
@@ -499,25 +750,14 @@ var PointerDragController = class _PointerDragController extends Controller {
499
750
  if (type === "touch" || type === "pen") return type;
500
751
  return "mouse";
501
752
  }
502
- /** `touch-action` that lets the page keep panning on the locked axis only. */
503
753
  #touchActionForAxis() {
504
754
  return this.#touchActionFor(this.axisValue);
505
755
  }
506
- /** The `touch-action` an `axis` declaration lends, for any axis value. */
507
756
  #touchActionFor(axis) {
508
757
  if (axis === "x") return "pan-y";
509
758
  if (axis === "y") return "pan-x";
510
759
  return "none";
511
760
  }
512
- /**
513
- * Establishes the handle contract: a `touch-action` derived from `axis`
514
- * (marker-guarded so an authored inline value wins and reconnects stay
515
- * idempotent) and focusability for the keyboard path (`tabindex="0"` only when
516
- * the author supplied none — prefer a real `<button>` handle). Both additions
517
- * are marker-owned so `#restoreHandle` reverts them symmetrically on teardown.
518
- *
519
- * @stimeoRenderRoot
520
- */
521
761
  #prepareHandle(handle) {
522
762
  const lent = this.#touchActionForAxis();
523
763
  const marked = handle.hasAttribute(_PointerDragController.#TOUCH_ACTION_MARKER);
@@ -525,17 +765,14 @@ var PointerDragController = class _PointerDragController extends Controller {
525
765
  handle.style.touchAction = lent;
526
766
  handle.setAttribute(_PointerDragController.#TOUCH_ACTION_MARKER, "true");
527
767
  }
528
- if (handle.tabIndex < 0 && !handle.hasAttribute("tabindex")) {
529
- handle.setAttribute("tabindex", "0");
530
- handle.setAttribute(_PointerDragController.#TABINDEX_MARKER, "true");
531
- }
768
+ this.#tabindex.lend(handle, { onlyIfUnfocusable: true });
532
769
  }
533
- /** Whether the key belongs to a native control or editing surface in the handle. */
534
770
  #ownsNativeKeys(target, handle) {
535
- const owner = target?.closest(NATIVE_KEY_OWNERS) ?? null;
536
- return owner !== null && owner !== handle && handle.contains(owner);
771
+ let element = target;
772
+ while (element.shadowRoot?.activeElement) element = element.shadowRoot.activeElement;
773
+ const owner = closestInFlatTree(element, NATIVE_KEY_OWNERS);
774
+ return owner !== null && owner !== handle && flatTreeContains(handle, owner);
537
775
  }
538
- /** Reverts the marker-owned touch-action + tabindex (authored values untouched). */
539
776
  #restoreHandle(handle) {
540
777
  if (handle.hasAttribute(_PointerDragController.#TOUCH_ACTION_MARKER)) {
541
778
  if (handle.style.touchAction === this.#touchActionForAxis()) {
@@ -543,18 +780,27 @@ var PointerDragController = class _PointerDragController extends Controller {
543
780
  }
544
781
  handle.removeAttribute(_PointerDragController.#TOUCH_ACTION_MARKER);
545
782
  }
546
- if (handle.hasAttribute(_PointerDragController.#TABINDEX_MARKER)) {
547
- const ours = handle.getAttribute("tabindex") === "0";
548
- if (ours && document.activeElement !== handle) {
549
- handle.removeAttribute("tabindex");
550
- handle.removeAttribute(_PointerDragController.#TABINDEX_MARKER);
551
- return;
552
- }
553
- if (!ours) handle.removeAttribute(_PointerDragController.#TABINDEX_MARKER);
554
- }
783
+ this.#tabindex.return(handle);
784
+ }
785
+ get #safeThreshold() {
786
+ return this.#numbers.read(
787
+ this,
788
+ "threshold",
789
+ this.thresholdValue,
790
+ _PointerDragController.values.threshold.default,
791
+ _PointerDragController.valueConstraints.threshold
792
+ );
793
+ }
794
+ get #safeKeyboardStep() {
795
+ return this.#numbers.read(
796
+ this,
797
+ "keyboardStep",
798
+ this.keyboardStepValue,
799
+ _PointerDragController.values.keyboardStep.default,
800
+ _PointerDragController.valueConstraints.keyboardStep
801
+ );
555
802
  }
556
803
  };
557
804
 
558
805
  export { PointerDragController };
559
- //# sourceMappingURL=pointer_drag_controller.js.map
560
806
  //# sourceMappingURL=pointer_drag_controller.js.map