stimeo-ui 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (337) hide show
  1. package/CHANGELOG.md +136 -0
  2. package/README.md +27 -11
  3. package/dist/cable/index.d.ts +88 -31
  4. package/dist/cable/index.js +384 -449
  5. package/dist/cable/index.js.map +1 -1
  6. package/dist/coerce-JtZVNG5h.d.ts +20 -0
  7. package/dist/controllers/accordion_controller.d.ts +4 -2
  8. package/dist/controllers/accordion_controller.js +174 -55
  9. package/dist/controllers/accordion_controller.js.map +1 -1
  10. package/dist/controllers/alert_dialog_controller.d.ts +12 -2
  11. package/dist/controllers/alert_dialog_controller.js +903 -235
  12. package/dist/controllers/alert_dialog_controller.js.map +1 -1
  13. package/dist/controllers/announcer_controller.d.ts +37 -5
  14. package/dist/controllers/announcer_controller.js +238 -205
  15. package/dist/controllers/announcer_controller.js.map +1 -1
  16. package/dist/controllers/aspect_ratio_controller.js +0 -10
  17. package/dist/controllers/aspect_ratio_controller.js.map +1 -1
  18. package/dist/controllers/auto_submit_controller.d.ts +30 -6
  19. package/dist/controllers/auto_submit_controller.js +414 -145
  20. package/dist/controllers/auto_submit_controller.js.map +1 -1
  21. package/dist/controllers/avatar_controller.js +185 -107
  22. package/dist/controllers/avatar_controller.js.map +1 -1
  23. package/dist/controllers/breadcrumb_controller.js +0 -130
  24. package/dist/controllers/breadcrumb_controller.js.map +1 -1
  25. package/dist/controllers/bulk_select_controller.d.ts +21 -3
  26. package/dist/controllers/bulk_select_controller.js +282 -58
  27. package/dist/controllers/bulk_select_controller.js.map +1 -1
  28. package/dist/controllers/calendar_controller.d.ts +38 -16
  29. package/dist/controllers/calendar_controller.js +228 -260
  30. package/dist/controllers/calendar_controller.js.map +1 -1
  31. package/dist/controllers/carousel_controller.d.ts +43 -12
  32. package/dist/controllers/carousel_controller.js +536 -436
  33. package/dist/controllers/carousel_controller.js.map +1 -1
  34. package/dist/controllers/character_counter_controller.d.ts +44 -7
  35. package/dist/controllers/character_counter_controller.js +493 -207
  36. package/dist/controllers/character_counter_controller.js.map +1 -1
  37. package/dist/controllers/checkbox_controller.d.ts +7 -2
  38. package/dist/controllers/checkbox_controller.js +38 -74
  39. package/dist/controllers/checkbox_controller.js.map +1 -1
  40. package/dist/controllers/clipboard_controller.d.ts +16 -5
  41. package/dist/controllers/clipboard_controller.js +261 -149
  42. package/dist/controllers/clipboard_controller.js.map +1 -1
  43. package/dist/controllers/collapsible_controller.d.ts +17 -5
  44. package/dist/controllers/collapsible_controller.js +304 -120
  45. package/dist/controllers/collapsible_controller.js.map +1 -1
  46. package/dist/controllers/color_picker_controller.d.ts +18 -6
  47. package/dist/controllers/color_picker_controller.js +178 -146
  48. package/dist/controllers/color_picker_controller.js.map +1 -1
  49. package/dist/controllers/combobox_controller.d.ts +24 -6
  50. package/dist/controllers/combobox_controller.js +522 -156
  51. package/dist/controllers/combobox_controller.js.map +1 -1
  52. package/dist/controllers/command_palette_controller.d.ts +51 -20
  53. package/dist/controllers/command_palette_controller.js +1156 -395
  54. package/dist/controllers/command_palette_controller.js.map +1 -1
  55. package/dist/controllers/conditional_fields_controller.d.ts +15 -2
  56. package/dist/controllers/conditional_fields_controller.js +300 -171
  57. package/dist/controllers/conditional_fields_controller.js.map +1 -1
  58. package/dist/controllers/confirm_controller.d.ts +49 -12
  59. package/dist/controllers/confirm_controller.js +980 -262
  60. package/dist/controllers/confirm_controller.js.map +1 -1
  61. package/dist/controllers/context_menu_controller.d.ts +80 -18
  62. package/dist/controllers/context_menu_controller.js +346 -153
  63. package/dist/controllers/context_menu_controller.js.map +1 -1
  64. package/dist/controllers/count_up_controller.d.ts +13 -1
  65. package/dist/controllers/count_up_controller.js +73 -21
  66. package/dist/controllers/count_up_controller.js.map +1 -1
  67. package/dist/controllers/countdown_controller.d.ts +47 -3
  68. package/dist/controllers/countdown_controller.js +281 -158
  69. package/dist/controllers/countdown_controller.js.map +1 -1
  70. package/dist/controllers/currency_input_controller.d.ts +93 -10
  71. package/dist/controllers/currency_input_controller.js +575 -225
  72. package/dist/controllers/currency_input_controller.js.map +1 -1
  73. package/dist/controllers/data_grid_controller.d.ts +25 -12
  74. package/dist/controllers/data_grid_controller.js +248 -214
  75. package/dist/controllers/data_grid_controller.js.map +1 -1
  76. package/dist/controllers/date_range_picker_controller.d.ts +42 -14
  77. package/dist/controllers/date_range_picker_controller.js +338 -235
  78. package/dist/controllers/date_range_picker_controller.js.map +1 -1
  79. package/dist/controllers/dialog_controller.d.ts +17 -6
  80. package/dist/controllers/dialog_controller.js +899 -237
  81. package/dist/controllers/dialog_controller.js.map +1 -1
  82. package/dist/controllers/direct_upload_controller.d.ts +25 -8
  83. package/dist/controllers/direct_upload_controller.js +97 -167
  84. package/dist/controllers/direct_upload_controller.js.map +1 -1
  85. package/dist/controllers/dirty_form_controller.d.ts +6 -1
  86. package/dist/controllers/dirty_form_controller.js +72 -69
  87. package/dist/controllers/dirty_form_controller.js.map +1 -1
  88. package/dist/controllers/dismissible_controller.d.ts +19 -0
  89. package/dist/controllers/dismissible_controller.js +196 -24
  90. package/dist/controllers/dismissible_controller.js.map +1 -1
  91. package/dist/controllers/drawer_controller.d.ts +39 -11
  92. package/dist/controllers/drawer_controller.js +974 -355
  93. package/dist/controllers/drawer_controller.js.map +1 -1
  94. package/dist/controllers/dropdown_controller.d.ts +31 -1
  95. package/dist/controllers/dropdown_controller.js +339 -96
  96. package/dist/controllers/dropdown_controller.js.map +1 -1
  97. package/dist/controllers/editable_controller.d.ts +12 -1
  98. package/dist/controllers/editable_controller.js +315 -88
  99. package/dist/controllers/editable_controller.js.map +1 -1
  100. package/dist/controllers/empty_state_controller.js +79 -58
  101. package/dist/controllers/empty_state_controller.js.map +1 -1
  102. package/dist/controllers/file_dropzone_controller.d.ts +63 -18
  103. package/dist/controllers/file_dropzone_controller.js +322 -211
  104. package/dist/controllers/file_dropzone_controller.js.map +1 -1
  105. package/dist/controllers/filter_controller.js +77 -42
  106. package/dist/controllers/filter_controller.js.map +1 -1
  107. package/dist/controllers/flash_controller.d.ts +35 -12
  108. package/dist/controllers/flash_controller.js +238 -347
  109. package/dist/controllers/flash_controller.js.map +1 -1
  110. package/dist/controllers/focus_controller.d.ts +15 -6
  111. package/dist/controllers/focus_controller.js +679 -240
  112. package/dist/controllers/focus_controller.js.map +1 -1
  113. package/dist/controllers/form_field_controller.js +156 -132
  114. package/dist/controllers/form_field_controller.js.map +1 -1
  115. package/dist/controllers/form_validation_controller.d.ts +3 -3
  116. package/dist/controllers/form_validation_controller.js +31 -97
  117. package/dist/controllers/form_validation_controller.js.map +1 -1
  118. package/dist/controllers/frame_loading_controller.d.ts +47 -14
  119. package/dist/controllers/frame_loading_controller.js +275 -217
  120. package/dist/controllers/frame_loading_controller.js.map +1 -1
  121. package/dist/controllers/highlight_controller.d.ts +27 -6
  122. package/dist/controllers/highlight_controller.js +107 -75
  123. package/dist/controllers/highlight_controller.js.map +1 -1
  124. package/dist/controllers/hover_card_controller.d.ts +60 -4
  125. package/dist/controllers/hover_card_controller.js +327 -130
  126. package/dist/controllers/hover_card_controller.js.map +1 -1
  127. package/dist/controllers/idle_controller.d.ts +48 -6
  128. package/dist/controllers/idle_controller.js +271 -101
  129. package/dist/controllers/idle_controller.js.map +1 -1
  130. package/dist/controllers/input_mask_controller.d.ts +30 -13
  131. package/dist/controllers/input_mask_controller.js +483 -97
  132. package/dist/controllers/input_mask_controller.js.map +1 -1
  133. package/dist/controllers/intersection_controller.d.ts +32 -13
  134. package/dist/controllers/intersection_controller.js +150 -106
  135. package/dist/controllers/intersection_controller.js.map +1 -1
  136. package/dist/controllers/lazy_frame_controller.d.ts +19 -2
  137. package/dist/controllers/lazy_frame_controller.js +24 -68
  138. package/dist/controllers/lazy_frame_controller.js.map +1 -1
  139. package/dist/controllers/listbox_controller.d.ts +44 -9
  140. package/dist/controllers/listbox_controller.js +523 -240
  141. package/dist/controllers/listbox_controller.js.map +1 -1
  142. package/dist/controllers/local_time_controller.d.ts +9 -7
  143. package/dist/controllers/local_time_controller.js +102 -77
  144. package/dist/controllers/local_time_controller.js.map +1 -1
  145. package/dist/controllers/masonry_controller.d.ts +11 -3
  146. package/dist/controllers/masonry_controller.js +80 -96
  147. package/dist/controllers/masonry_controller.js.map +1 -1
  148. package/dist/controllers/menu_controller.d.ts +55 -11
  149. package/dist/controllers/menu_controller.js +302 -188
  150. package/dist/controllers/menu_controller.js.map +1 -1
  151. package/dist/controllers/menubar_controller.d.ts +51 -9
  152. package/dist/controllers/menubar_controller.js +232 -345
  153. package/dist/controllers/menubar_controller.js.map +1 -1
  154. package/dist/controllers/meter_controller.d.ts +33 -20
  155. package/dist/controllers/meter_controller.js +417 -87
  156. package/dist/controllers/meter_controller.js.map +1 -1
  157. package/dist/controllers/multi_select_controller.d.ts +48 -18
  158. package/dist/controllers/multi_select_controller.js +785 -393
  159. package/dist/controllers/multi_select_controller.js.map +1 -1
  160. package/dist/controllers/navigation_menu_controller.d.ts +27 -6
  161. package/dist/controllers/navigation_menu_controller.js +190 -250
  162. package/dist/controllers/navigation_menu_controller.js.map +1 -1
  163. package/dist/controllers/nested_form_controller.d.ts +10 -0
  164. package/dist/controllers/nested_form_controller.js +396 -192
  165. package/dist/controllers/nested_form_controller.js.map +1 -1
  166. package/dist/controllers/network_status_controller.d.ts +23 -0
  167. package/dist/controllers/network_status_controller.js +268 -62
  168. package/dist/controllers/network_status_controller.js.map +1 -1
  169. package/dist/controllers/number_input_controller.d.ts +50 -8
  170. package/dist/controllers/number_input_controller.js +610 -303
  171. package/dist/controllers/number_input_controller.js.map +1 -1
  172. package/dist/controllers/optimistic_controller.d.ts +8 -5
  173. package/dist/controllers/optimistic_controller.js +45 -87
  174. package/dist/controllers/optimistic_controller.js.map +1 -1
  175. package/dist/controllers/otp_controller.d.ts +55 -8
  176. package/dist/controllers/otp_controller.js +658 -255
  177. package/dist/controllers/otp_controller.js.map +1 -1
  178. package/dist/controllers/overflow_indicator_controller.d.ts +5 -0
  179. package/dist/controllers/overflow_indicator_controller.js +122 -113
  180. package/dist/controllers/overflow_indicator_controller.js.map +1 -1
  181. package/dist/controllers/overflow_menu_controller.d.ts +58 -20
  182. package/dist/controllers/overflow_menu_controller.js +470 -295
  183. package/dist/controllers/overflow_menu_controller.js.map +1 -1
  184. package/dist/controllers/pagination_controller.d.ts +37 -7
  185. package/dist/controllers/pagination_controller.js +332 -123
  186. package/dist/controllers/pagination_controller.js.map +1 -1
  187. package/dist/controllers/password_reveal_controller.d.ts +46 -8
  188. package/dist/controllers/password_reveal_controller.js +392 -121
  189. package/dist/controllers/password_reveal_controller.js.map +1 -1
  190. package/dist/controllers/password_strength_controller.d.ts +15 -6
  191. package/dist/controllers/password_strength_controller.js +111 -158
  192. package/dist/controllers/password_strength_controller.js.map +1 -1
  193. package/dist/controllers/persist_controller.d.ts +11 -4
  194. package/dist/controllers/persist_controller.js +165 -111
  195. package/dist/controllers/persist_controller.js.map +1 -1
  196. package/dist/controllers/pointer_drag_controller.d.ts +35 -12
  197. package/dist/controllers/pointer_drag_controller.js +434 -188
  198. package/dist/controllers/pointer_drag_controller.js.map +1 -1
  199. package/dist/controllers/popover_controller.d.ts +41 -5
  200. package/dist/controllers/popover_controller.js +269 -113
  201. package/dist/controllers/popover_controller.js.map +1 -1
  202. package/dist/controllers/portal_controller.d.ts +44 -4
  203. package/dist/controllers/portal_controller.js +215 -76
  204. package/dist/controllers/portal_controller.js.map +1 -1
  205. package/dist/controllers/preview_guard_controller.d.ts +10 -4
  206. package/dist/controllers/preview_guard_controller.js +243 -116
  207. package/dist/controllers/preview_guard_controller.js.map +1 -1
  208. package/dist/controllers/progress_controller.d.ts +20 -19
  209. package/dist/controllers/progress_controller.js +391 -78
  210. package/dist/controllers/progress_controller.js.map +1 -1
  211. package/dist/controllers/radio_group_controller.d.ts +21 -9
  212. package/dist/controllers/radio_group_controller.js +278 -211
  213. package/dist/controllers/radio_group_controller.js.map +1 -1
  214. package/dist/controllers/range_slider_controller.d.ts +47 -6
  215. package/dist/controllers/range_slider_controller.js +220 -113
  216. package/dist/controllers/range_slider_controller.js.map +1 -1
  217. package/dist/controllers/rating_controller.d.ts +20 -4
  218. package/dist/controllers/rating_controller.js +458 -211
  219. package/dist/controllers/rating_controller.js.map +1 -1
  220. package/dist/controllers/read_more_controller.d.ts +2 -0
  221. package/dist/controllers/read_more_controller.js +138 -63
  222. package/dist/controllers/read_more_controller.js.map +1 -1
  223. package/dist/controllers/reading_progress_controller.d.ts +1 -1
  224. package/dist/controllers/reading_progress_controller.js +166 -137
  225. package/dist/controllers/reading_progress_controller.js.map +1 -1
  226. package/dist/controllers/relative_time_controller.d.ts +20 -6
  227. package/dist/controllers/relative_time_controller.js +211 -112
  228. package/dist/controllers/relative_time_controller.js.map +1 -1
  229. package/dist/controllers/reset_on_restore_controller.d.ts +85 -0
  230. package/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +103 -38
  231. package/dist/controllers/reset_on_restore_controller.js.map +1 -0
  232. package/dist/controllers/resizable_controller.d.ts +31 -2
  233. package/dist/controllers/resizable_controller.js +299 -150
  234. package/dist/controllers/resizable_controller.js.map +1 -1
  235. package/dist/controllers/roving_controller.d.ts +3 -3
  236. package/dist/controllers/roving_controller.js +49 -88
  237. package/dist/controllers/roving_controller.js.map +1 -1
  238. package/dist/controllers/scroll_area_controller.d.ts +7 -3
  239. package/dist/controllers/scroll_area_controller.js +490 -246
  240. package/dist/controllers/scroll_area_controller.js.map +1 -1
  241. package/dist/controllers/scroll_restore_controller.d.ts +3 -3
  242. package/dist/controllers/scroll_restore_controller.js +14 -55
  243. package/dist/controllers/scroll_restore_controller.js.map +1 -1
  244. package/dist/controllers/scroll_visibility_controller.d.ts +28 -12
  245. package/dist/controllers/scroll_visibility_controller.js +432 -149
  246. package/dist/controllers/scroll_visibility_controller.js.map +1 -1
  247. package/dist/controllers/scrollspy_controller.d.ts +6 -1
  248. package/dist/controllers/scrollspy_controller.js +193 -248
  249. package/dist/controllers/scrollspy_controller.js.map +1 -1
  250. package/dist/controllers/separator_controller.d.ts +20 -5
  251. package/dist/controllers/separator_controller.js +285 -142
  252. package/dist/controllers/separator_controller.js.map +1 -1
  253. package/dist/controllers/sidebar_controller.d.ts +65 -8
  254. package/dist/controllers/sidebar_controller.js +1069 -404
  255. package/dist/controllers/sidebar_controller.js.map +1 -1
  256. package/dist/controllers/skeleton_controller.d.ts +23 -0
  257. package/dist/controllers/skeleton_controller.js +259 -101
  258. package/dist/controllers/skeleton_controller.js.map +1 -1
  259. package/dist/controllers/slider_controller.d.ts +29 -2
  260. package/dist/controllers/slider_controller.js +187 -86
  261. package/dist/controllers/slider_controller.js.map +1 -1
  262. package/dist/controllers/smart_sticky_header_controller.d.ts +22 -4
  263. package/dist/controllers/smart_sticky_header_controller.js +173 -66
  264. package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
  265. package/dist/controllers/sortable_controller.d.ts +4 -4
  266. package/dist/controllers/sortable_controller.js +26 -125
  267. package/dist/controllers/sortable_controller.js.map +1 -1
  268. package/dist/controllers/spinner_controller.d.ts +39 -4
  269. package/dist/controllers/spinner_controller.js +288 -173
  270. package/dist/controllers/spinner_controller.js.map +1 -1
  271. package/dist/controllers/step_indicator_controller.d.ts +9 -4
  272. package/dist/controllers/step_indicator_controller.js +105 -46
  273. package/dist/controllers/step_indicator_controller.js.map +1 -1
  274. package/dist/controllers/stepper_controller.d.ts +14 -6
  275. package/dist/controllers/stepper_controller.js +120 -73
  276. package/dist/controllers/stepper_controller.js.map +1 -1
  277. package/dist/controllers/stick_to_bottom_controller.d.ts +9 -1
  278. package/dist/controllers/stick_to_bottom_controller.js +275 -140
  279. package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
  280. package/dist/controllers/sticky_observer_controller.d.ts +11 -0
  281. package/dist/controllers/sticky_observer_controller.js +220 -49
  282. package/dist/controllers/sticky_observer_controller.js.map +1 -1
  283. package/dist/controllers/submit_once_controller.d.ts +24 -8
  284. package/dist/controllers/submit_once_controller.js +376 -227
  285. package/dist/controllers/submit_once_controller.js.map +1 -1
  286. package/dist/controllers/switch_controller.d.ts +18 -4
  287. package/dist/controllers/switch_controller.js +201 -84
  288. package/dist/controllers/switch_controller.js.map +1 -1
  289. package/dist/controllers/tabs_controller.d.ts +9 -6
  290. package/dist/controllers/tabs_controller.js +41 -30
  291. package/dist/controllers/tabs_controller.js.map +1 -1
  292. package/dist/controllers/tags_input_controller.d.ts +16 -3
  293. package/dist/controllers/tags_input_controller.js +293 -173
  294. package/dist/controllers/tags_input_controller.js.map +1 -1
  295. package/dist/controllers/textarea_autosize_controller.d.ts +11 -1
  296. package/dist/controllers/textarea_autosize_controller.js +108 -74
  297. package/dist/controllers/textarea_autosize_controller.js.map +1 -1
  298. package/dist/controllers/theme_controller.d.ts +24 -11
  299. package/dist/controllers/theme_controller.js +140 -116
  300. package/dist/controllers/theme_controller.js.map +1 -1
  301. package/dist/controllers/time_picker_controller.d.ts +55 -5
  302. package/dist/controllers/time_picker_controller.js +331 -127
  303. package/dist/controllers/time_picker_controller.js.map +1 -1
  304. package/dist/controllers/toast_controller.d.ts +40 -16
  305. package/dist/controllers/toast_controller.js +244 -324
  306. package/dist/controllers/toast_controller.js.map +1 -1
  307. package/dist/controllers/toggle_group_controller.d.ts +14 -6
  308. package/dist/controllers/toggle_group_controller.js +252 -191
  309. package/dist/controllers/toggle_group_controller.js.map +1 -1
  310. package/dist/controllers/toolbar_controller.d.ts +8 -6
  311. package/dist/controllers/toolbar_controller.js +219 -120
  312. package/dist/controllers/toolbar_controller.js.map +1 -1
  313. package/dist/controllers/tooltip_controller.d.ts +49 -4
  314. package/dist/controllers/tooltip_controller.js +158 -124
  315. package/dist/controllers/tooltip_controller.js.map +1 -1
  316. package/dist/controllers/transition_controller.d.ts +22 -4
  317. package/dist/controllers/transition_controller.js +123 -119
  318. package/dist/controllers/transition_controller.js.map +1 -1
  319. package/dist/controllers/tree_view_controller.d.ts +22 -12
  320. package/dist/controllers/tree_view_controller.js +283 -369
  321. package/dist/controllers/tree_view_controller.js.map +1 -1
  322. package/dist/index.d.ts +10 -5
  323. package/dist/index.js +8573 -10410
  324. package/dist/index.js.map +1 -1
  325. package/dist/inspector/cli.d.ts +38 -14
  326. package/dist/inspector/cli.js +108 -40
  327. package/dist/inspector/cli.js.map +1 -1
  328. package/dist/inspector/cli_bin.js +145 -64
  329. package/dist/inspector/cli_bin.js.map +1 -1
  330. package/dist/inspector/examples.json +20 -20
  331. package/dist/inspector/manifest.json +802 -101
  332. package/dist/positioning/index.d.ts +15 -7
  333. package/dist/positioning/index.js +154 -47
  334. package/dist/positioning/index.js.map +1 -1
  335. package/package.json +11 -10
  336. package/dist/controllers/reset_before_cache_controller.d.ts +0 -76
  337. package/dist/controllers/reset_before_cache_controller.js.map +0 -1
@@ -1,8 +1,27 @@
1
1
  import { Controller } from '@hotwired/stimulus';
2
2
 
3
- // src/controllers/scrollspy_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/declared_value.ts
6
25
  function parseDeclared(raw, parse, fallback) {
7
26
  try {
8
27
  return parse(raw);
@@ -22,14 +41,8 @@ function validSelector(element, raw, fallback) {
22
41
  );
23
42
  }
24
43
 
25
- // src/utils/frame_coalescer.ts
26
44
  var FrameCoalescer = class {
27
45
  #frame = null;
28
- /**
29
- * Runs `run` on the next frame, unless a frame is already pending — the first
30
- * request of a burst wins and the rest are dropped. The pending frame is
31
- * released before `run`, so `run` may request the next one.
32
- */
33
46
  schedule(run) {
34
47
  if (this.#frame !== null) return;
35
48
  this.#frame = requestAnimationFrame(() => {
@@ -37,14 +50,6 @@ var FrameCoalescer = class {
37
50
  run();
38
51
  });
39
52
  }
40
- /**
41
- * Drops the pending frame, and reaches the platform only when there is one.
42
- *
43
- * There is no handle value that stands for "nothing pending":
44
- * `cancelAnimationFrame` takes an `unsigned long`, so a negative placeholder
45
- * arrives as a large positive number that the same allocator can hand out, and
46
- * an idle cancel would then drop a frame belonging to someone else.
47
- */
48
53
  cancel() {
49
54
  if (this.#frame === null) return;
50
55
  cancelAnimationFrame(this.#frame);
@@ -52,7 +57,6 @@ var FrameCoalescer = class {
52
57
  }
53
58
  };
54
59
 
55
- // src/utils/intersection_watcher.ts
56
60
  function queryRoot(selector) {
57
61
  if (!selector) return null;
58
62
  return parseDeclared(selector, (raw) => document.querySelector(raw), null);
@@ -65,30 +69,12 @@ var IntersectionWatcher = class {
65
69
  constructor(onEntries) {
66
70
  this.#onEntries = onEntries;
67
71
  }
68
- /** Whether an observer is live (started, `IntersectionObserver` supported). */
69
72
  get active() {
70
73
  return this.#active;
71
74
  }
72
- /** Whether the live observer discarded configured options after construction failed. */
73
75
  get usingPlatformDefaults() {
74
76
  return this.#usingPlatformDefaults;
75
77
  }
76
- /**
77
- * (Re)creates the observer and observes `targets`. Returns `false` — leaving
78
- * the watcher inert — without `IntersectionObserver` support (very old
79
- * browsers; the caller's no-JS fallback stays in charge) or with no targets.
80
- * If initial construction with the configured options fails, the watcher
81
- * warns and retries once with the same root and platform defaults. A
82
- * `rootSelector` that does not parse resolves to the viewport (see
83
- * {@link IntersectionWatchOptions.rootSelector}), so a typo never fails the
84
- * call.
85
- *
86
- * @throws The fallback constructor error if both construction attempts fail,
87
- * or whatever the platform throws from `observe()`. The exception is passed
88
- * through unchanged, but the watcher rolls back first: every target observed
89
- * so far is released and `active` stays `false`, so a caller that retries
90
- * starts from a clean slate.
91
- */
92
78
  start(targets, options = {}) {
93
79
  this.stop();
94
80
  if (typeof IntersectionObserver === "undefined") return false;
@@ -126,14 +112,6 @@ var IntersectionWatcher = class {
126
112
  throw error;
127
113
  }
128
114
  }
129
- /**
130
- * Re-delivers `target`'s CURRENT intersection state: `IntersectionObserver`
131
- * only reports *changes*, but `observe()` always reports the present state,
132
- * so unobserve→observe turns "still intersecting" into a fresh callback.
133
- *
134
- * @throws Whatever `unobserve()`/`observe()` throws. The watcher is stopped
135
- * first, so it never stays live with a half-rearmed target.
136
- */
137
115
  rearm(target) {
138
116
  if (!this.#observer) return;
139
117
  try {
@@ -144,7 +122,6 @@ var IntersectionWatcher = class {
144
122
  throw error;
145
123
  }
146
124
  }
147
- /** Severs the observer; late queued callbacks become no-ops via the guard. */
148
125
  stop() {
149
126
  this.#active = false;
150
127
  this.#observer?.disconnect();
@@ -153,12 +130,124 @@ var IntersectionWatcher = class {
153
130
  }
154
131
  };
155
132
 
156
- // src/utils/reduced_motion.ts
133
+ var ListenerSet = class {
134
+ #abort = new AbortController();
135
+ add(target, type, handler, options) {
136
+ target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
137
+ }
138
+ dispose() {
139
+ this.#abort.abort();
140
+ this.#abort = new AbortController();
141
+ }
142
+ };
143
+
144
+ var MicrotaskCoalescer = class {
145
+ #run;
146
+ #queued = false;
147
+ #active = false;
148
+ #generation = 0;
149
+ constructor(run) {
150
+ this.#run = run;
151
+ }
152
+ activate() {
153
+ this.#active = true;
154
+ }
155
+ cancel() {
156
+ this.#active = false;
157
+ this.#queued = false;
158
+ this.#generation += 1;
159
+ }
160
+ get pending() {
161
+ return this.#queued;
162
+ }
163
+ schedule() {
164
+ if (!this.#active || this.#queued) return;
165
+ this.#queued = true;
166
+ const generation = this.#generation;
167
+ queueMicrotask(() => {
168
+ if (generation !== this.#generation || !this.#queued || !this.#active) return;
169
+ this.#queued = false;
170
+ this.#run();
171
+ });
172
+ }
173
+ };
174
+
175
+ var MorphRenderWatcher = class {
176
+ #listeners = new ListenerSet();
177
+ #pass;
178
+ #element = null;
179
+ #onMorph = () => this.#pass.schedule();
180
+ constructor(run) {
181
+ this.#pass = new MicrotaskCoalescer(run);
182
+ }
183
+ observe(element) {
184
+ if (this.#element === element) return;
185
+ this.disconnect();
186
+ this.#element = element;
187
+ this.#pass.activate();
188
+ this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
189
+ }
190
+ schedule() {
191
+ this.#pass.schedule();
192
+ }
193
+ get pending() {
194
+ return this.#pass.pending;
195
+ }
196
+ disconnect() {
197
+ this.#listeners.dispose();
198
+ this.#pass.cancel();
199
+ this.#element = null;
200
+ }
201
+ };
202
+
203
+ var NUMBER_BOUNDS = {
204
+ finite: { finite: true }};
205
+ function matchesNumberBounds(value, bounds) {
206
+ if (!Number.isFinite(value)) {
207
+ const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
208
+ if (direction === null) return false;
209
+ if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
210
+ }
211
+ if (bounds.min !== void 0 && value < bounds.min) return false;
212
+ if (bounds.max !== void 0 && value > bounds.max) return false;
213
+ if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
214
+ if (bounds.integer && !Number.isInteger(value)) return false;
215
+ if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
216
+ return true;
217
+ }
218
+
219
+ function readNumber(raw, fallback, bounds) {
220
+ return matchesNumberBounds(raw, bounds) ? raw : fallback;
221
+ }
222
+
223
+ var NumberValueReader = class {
224
+ #lastRejected = /* @__PURE__ */ new Map();
225
+ read(owner, name, raw, fallback, bounds) {
226
+ const resolved = readNumber(raw, fallback, bounds);
227
+ if (matchesNumberBounds(raw, bounds)) {
228
+ this.#lastRejected.delete(name);
229
+ return resolved;
230
+ }
231
+ const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
232
+ const literal = owner.element.getAttribute(attribute);
233
+ if (literal === null) {
234
+ this.#lastRejected.delete(name);
235
+ return resolved;
236
+ }
237
+ if (this.#lastRejected.get(name) !== literal) {
238
+ this.#lastRejected.set(name, literal);
239
+ console.warn(
240
+ `Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
241
+ );
242
+ }
243
+ return resolved;
244
+ }
245
+ };
246
+
157
247
  function prefersReducedMotion() {
158
248
  return typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
159
249
  }
160
250
 
161
- // src/utils/scroll_source.ts
162
251
  function resolveScrollContainer(selector) {
163
252
  const match = selector ? document.querySelector(selector) : null;
164
253
  return match instanceof HTMLElement ? match : null;
@@ -167,9 +256,13 @@ function scrollOffset(source) {
167
256
  return source === window ? window.scrollY ?? window.pageYOffset ?? 0 : source.scrollTop;
168
257
  }
169
258
 
170
- // src/controllers/scrollspy_controller.ts
259
+ function targetSelector(identifier, name) {
260
+ return `[data-${identifier}-target~="${name}"]`;
261
+ }
262
+
171
263
  var ANCHOR_ATTRIBUTES = ["href", "data-href"];
172
- var ScrollspyController = class extends Controller {
264
+ var ScrollspyController = class _ScrollspyController extends Controller {
265
+ #numbers = new NumberValueReader();
173
266
  static targets = ["link"];
174
267
  static values = {
175
268
  offset: { type: Number, default: 0 },
@@ -177,70 +270,48 @@ var ScrollspyController = class extends Controller {
177
270
  rootSelector: { type: String, default: "" },
178
271
  focusSection: { type: Boolean, default: false }
179
272
  };
273
+ static valueConstraints = {
274
+ offset: NUMBER_BOUNDS.finite
275
+ };
180
276
  static actions = ["scrollTo"];
181
277
  static events = ["change"];
182
- /**
183
- * The one offset shared by observation, active-section selection, and
184
- * scrolling. Stimulus parses a malformed Number Value as `NaN`; degrading it
185
- * to the declared default keeps every path aligned instead of only repairing
186
- * the observer margin while selection and scrolling still receive `NaN`.
187
- */
188
278
  get #offset() {
189
- return Number.isFinite(this.offsetValue) ? this.offsetValue : 0;
279
+ return this.#safeOffset;
190
280
  }
191
- /** Shared IO plumbing (support guard, active guard, teardown). */
192
281
  #watcher = new IntersectionWatcher((entries) => this.#onIntersection(entries));
193
282
  #isConnected = false;
194
- /**
195
- * Sections currently tracked, by id. Only the *latest reported* intersection
196
- * flag is stored — never a coordinate. Positions are re-measured when the
197
- * active section is evaluated, so a section observed several batches ago is
198
- * never compared against a trigger line computed now.
199
- */
200
283
  #intersectionStates = /* @__PURE__ */ new Map();
201
- /** Current active section ID; comparing it suppresses duplicate `change` events. */
202
284
  #activeSectionId = "";
203
- /**
204
- * Scroll root resolved when the observer was (re)built; `null` = viewport.
205
- * Cached so a click and every intersection batch reuse the element the
206
- * observer is actually watching instead of re-querying the document.
207
- */
208
285
  #rootElement = null;
209
- /**
210
- * The source the `scroll` listener is currently attached to — the resolved
211
- * root, or the window while the viewport is spied. `null` means "nothing
212
- * attached", so teardown always detaches from the source it attached to
213
- * rather than from whatever the selector resolves to now.
214
- */
215
- #scrollSource = null;
216
- /** Coalesces a scroll burst into one measurement per frame. */
286
+ #scrollListeners = new ListenerSet();
217
287
  #frames = new FrameCoalescer();
218
- /** The validated `rootSelector`, or `""` when the declaration cannot be parsed. */
219
288
  #rootSelector = "";
220
- /** Watches the link targets' anchor attributes for an in-place morph rewrite. */
221
289
  #anchorObserver = null;
222
- /** True while a coalesced observation rebuild is queued; see {@link #scheduleResync}. */
223
- #resyncQueued = false;
290
+ #resyncInputsChanged = false;
291
+ #resync = new MorphRenderWatcher(() => {
292
+ const retainStates = !this.#resyncInputsChanged;
293
+ this.#resyncInputsChanged = false;
294
+ this.#initializeObserver(retainStates);
295
+ this.#evaluateActiveSection();
296
+ });
224
297
  connect() {
298
+ this.#resync.observe(this.element);
225
299
  this.#isConnected = true;
226
300
  this.#observeAnchorAttributes();
227
301
  this.#initializeObserver();
228
302
  }
229
303
  disconnect() {
230
304
  this.#isConnected = false;
231
- this.#resyncQueued = false;
305
+ this.#resync.disconnect();
306
+ this.#resyncInputsChanged = false;
232
307
  this.#watcher.stop();
233
308
  this.#anchorObserver?.disconnect();
234
309
  this.#anchorObserver = null;
235
310
  this.#detachScrollListener();
236
311
  this.#frames.cancel();
237
- this.#intersectionStates.clear();
238
312
  this.#activeSectionId = "";
239
313
  this.#rootElement = null;
240
314
  }
241
- /**
242
- * Re-initializes the observer if the offset or rootMargin values change dynamically.
243
- */
244
315
  offsetValueChanged() {
245
316
  if (!this.#isConnected) return;
246
317
  this.#initializeObserver();
@@ -254,50 +325,16 @@ var ScrollspyController = class extends Controller {
254
325
  if (!this.#isConnected) return;
255
326
  this.#initializeObserver();
256
327
  }
257
- /**
258
- * Re-syncs the observation set when a Turbo Stream/morph swaps the table of
259
- * contents. Stimulus fires these before `connect()` for the links already in
260
- * the markup, hence the guard: the initial observer is built exactly once, by
261
- * `connect()`.
262
- */
263
328
  linkTargetConnected() {
264
329
  if (this.#isConnected) this.#scheduleResync();
265
330
  }
266
331
  linkTargetDisconnected() {
267
332
  if (this.#isConnected) this.#scheduleResync();
268
333
  }
269
- /**
270
- * Queues **one** observation rebuild for the current mutation batch.
271
- *
272
- * A single Turbo morph can append one link, drop another, and rewrite a
273
- * third's `href`, and those arrive through two independent channels —
274
- * Stimulus's target callbacks and {@link #anchorObserver}. Each channel just
275
- * raises the flag and queues a drain; the first drain to run does the work and
276
- * clears it, so every later drain in the same batch finds nothing to do and
277
- * the set is rebuilt once instead of three times. `disconnect()` clears the
278
- * same flag, which is how a queued rebuild is dropped rather than run against
279
- * a detached controller.
280
- */
281
334
  #scheduleResync() {
282
- this.#resyncQueued = true;
283
- queueMicrotask(this.#drainResync);
335
+ this.#resyncInputsChanged = true;
336
+ this.#resync.schedule();
284
337
  }
285
- #drainResync = () => {
286
- if (!this.#resyncQueued) return;
287
- this.#resyncQueued = false;
288
- this.#initializeObserver();
289
- };
290
- /**
291
- * Watches the link targets' anchor attributes so an in-place rewrite re-syncs.
292
- *
293
- * A Turbo 8 morph keeps the element **and** its `data-*-target` marker and
294
- * only rewrites attributes, so Stimulus fires no target callback: without
295
- * this, a link re-pointed from `#intro` to `#faq` would keep the controller
296
- * observing `#intro` for the rest of the page's life. The filter is exactly
297
- * `href` and `data-href`; guarding on `MutationObserver` keeps the
298
- * controller usable where the API is absent, matching `IntersectionWatcher`'s
299
- * own support guard.
300
- */
301
338
  #observeAnchorAttributes() {
302
339
  if (typeof MutationObserver !== "undefined") {
303
340
  this.#anchorObserver = new MutationObserver(this.#onAnchorMutation);
@@ -308,12 +345,6 @@ var ScrollspyController = class extends Controller {
308
345
  });
309
346
  }
310
347
  }
311
- /**
312
- * Rebuilds only for a rewrite on a *current* link target. The observer is
313
- * scoped to this controller's element, but that subtree also holds links the
314
- * author never marked as targets (a "back to top" anchor, a nested nav), and
315
- * those anchor nothing here.
316
- */
317
348
  #onAnchorMutation = (records) => {
318
349
  const links = this.linkTargets;
319
350
  for (const record of records) {
@@ -322,20 +353,16 @@ var ScrollspyController = class extends Controller {
322
353
  return;
323
354
  }
324
355
  };
325
- /**
326
- * Scrolls to the section the clicked link anchors, honoring `offset` and any
327
- * nested scroll container (a plain fragment jump would scroll the window).
328
- *
329
- * Honors `prefers-reduced-motion` (WCAG 2.2 **2.3.3**) by forcing an instant
330
- * jump independently of the consumer's CSS `scroll-behavior`. With
331
- * `focusSection` enabled it also moves the sequential focus starting point
332
- * into the destination; the URL fragment is deliberately not touched.
333
- */
334
- scrollTo(event) {
335
- const link = event.currentTarget;
356
+ scrollTo(source) {
357
+ const { event, host, origin } = actionSource(source);
358
+ const link = host?.closest(targetSelector(this.identifier, "link"));
359
+ if (!link || !this.linkTargets.includes(link)) return;
360
+ if (origin && origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
361
+ return;
362
+ const moveFocus = event !== null || this.element.contains(document.activeElement);
336
363
  const id = this.#getAnchorId(link);
337
364
  if (!id) return;
338
- event.preventDefault();
365
+ event?.preventDefault();
339
366
  const targetElement = document.getElementById(id);
340
367
  if (!targetElement) return;
341
368
  const behavior = prefersReducedMotion() ? "instant" : "smooth";
@@ -350,85 +377,36 @@ var ScrollspyController = class extends Controller {
350
377
  const scrollPosition = scrollOffset(window) + targetRect.top - offset;
351
378
  window.scrollTo({ top: scrollPosition, behavior });
352
379
  }
353
- if (this.focusSectionValue) this.#focusSection(targetElement);
354
- }
355
- /**
356
- * Moves the sequential focus starting point into the section `scrollTo` just
357
- * jumped to, so the next Tab continues *inside* the destination instead of
358
- * resuming in the table of contents (`preventDefault()` alone would leave the
359
- * starting point on the link). Opt-in through `focusSection`, because moving
360
- * focus is a decision only the consuming page can make.
361
- *
362
- * `tabindex="-1"` is established only when the section is not already
363
- * focusable and is never removed, so an author-owned tabindex is left alone.
364
- * `preventScroll` keeps the focus call from cancelling the smooth scroll
365
- * started just above.
366
- */
380
+ if (this.focusSectionValue && moveFocus) this.#focusSection(targetElement);
381
+ }
367
382
  #focusSection(section) {
368
383
  if (!section.hasAttribute("tabindex")) section.setAttribute("tabindex", "-1");
369
384
  section.focus({ preventScroll: true });
370
385
  }
371
- /**
372
- * The cached scroll root, re-resolved when the cached element has left the
373
- * document (a Turbo morph replaced the container) so `scrollTo` never
374
- * scrolls a detached node.
375
- */
376
386
  #scrollRoot() {
377
387
  if (this.#rootElement && !this.#rootElement.isConnected) {
378
388
  this.#rootElement = this.#queryRootElement();
379
389
  }
380
390
  return this.#rootElement;
381
391
  }
382
- /**
383
- * Resolves `rootSelector` to a scrollable element.
384
- *
385
- * @returns The container, or `null` meaning "spy the viewport" when the
386
- * declaration is empty, matches nothing, or matches a non-HTML element. A typo
387
- * reads as empty: the declaration is validated once when it changes, so a
388
- * selector that cannot be parsed degrades to viewport spying rather than
389
- * leaving the controller inert.
390
- */
391
392
  #queryRootElement() {
392
393
  return resolveScrollContainer(this.#rootSelector);
393
394
  }
394
- /**
395
- * Re-evaluates once per frame while the reader scrolls.
396
- *
397
- * `IntersectionObserver` reports **threshold crossings**, not positions, so a
398
- * reader moving inside one long section — or across a gap wider than the
399
- * observation band — produces no batch at all and the highlight would stay
400
- * frozen at whatever the last crossing decided. That would make the current
401
- * location depend on the *route* to a position instead of the position: a
402
- * stepwise scroll crosses thresholds an instant jump to the same offset never
403
- * does. Listening to the scroll source closes the gap, and because both paths
404
- * end in {@link #evaluateActiveSection} — which measures section rects and the
405
- * root's top edge at that instant — they converge on the same answer.
406
- */
407
395
  #onScroll = () => this.#frames.schedule(() => this.#evaluateActiveSection());
408
- /**
409
- * Points the `scroll` listener at whatever the reader actually scrolls: the
410
- * resolved root, or the window when the viewport is spied.
411
- *
412
- * The detach is unconditional, so rebuilding the observation set (a Value
413
- * change, a morph) **moves** the listener rather than stacking a second one on
414
- * a container the reader has stopped scrolling.
415
- */
416
396
  #syncScrollListener() {
417
397
  this.#detachScrollListener();
418
- this.#scrollSource = this.#rootElement ?? window;
419
- this.#scrollSource.addEventListener("scroll", this.#onScroll, { passive: true });
398
+ this.#scrollListeners.add(this.#rootElement ?? window, "scroll", this.#onScroll, {
399
+ passive: true
400
+ });
420
401
  }
421
402
  #detachScrollListener() {
422
- this.#scrollSource?.removeEventListener("scroll", this.#onScroll);
423
- this.#scrollSource = null;
424
- }
425
- /**
426
- * @stimeoRuntimeOnly `rootMargin` wires the observer this call installs; the active section it
427
- * republishes is the one already recorded.
428
- */
429
- #initializeObserver() {
403
+ this.#scrollListeners.dispose();
404
+ }
405
+ #initializeObserver(retainStates = false) {
430
406
  this.#watcher.stop();
431
- this.#intersectionStates.clear();
407
+ const previousStates = retainStates ? this.#intersectionStates : null;
408
+ const previousRoot = this.#rootElement;
409
+ this.#intersectionStates = /* @__PURE__ */ new Map();
432
410
  this.#activeSectionId = this.#activeSectionId || this.#activeIdFromDom();
433
411
  this.#rootElement = this.#queryRootElement();
434
412
  this.#syncScrollListener();
@@ -439,13 +417,18 @@ var ScrollspyController = class extends Controller {
439
417
  const id = this.#getAnchorId(link);
440
418
  if (!id) continue;
441
419
  const section = document.getElementById(id);
442
- if (section && !sections.includes(section)) sections.push(section);
420
+ if (section && !sections.includes(section)) {
421
+ sections.push(section);
422
+ const previous = previousStates?.get(id);
423
+ if (previous?.element === section && previousRoot === this.#rootElement) {
424
+ this.#intersectionStates.set(id, previous);
425
+ }
426
+ }
443
427
  }
444
428
  this.#watcher.start(sections, {
445
429
  root: this.#rootElement,
446
430
  rootMargin: margin,
447
431
  threshold: [0, 0.2, 0.4, 0.6, 0.8, 1]
448
- // Multiple thresholds handle large sections safely
449
432
  });
450
433
  this.#syncActiveStates(false);
451
434
  }
@@ -462,18 +445,6 @@ var ScrollspyController = class extends Controller {
462
445
  }
463
446
  this.#evaluateActiveSection();
464
447
  };
465
- /**
466
- * Picks the section closest to the trigger line and publishes it.
467
- *
468
- * Every coordinate is read **now**: the section rects *and* the root's top
469
- * edge share one measurement instant. Comparing an entry's recorded
470
- * `boundingClientRect.top` (captured whenever that section last crossed a
471
- * threshold) against a freshly computed trigger line mixes two moments in
472
- * time, which would make the result depend on how the reader arrived at a
473
- * position — a smooth scroll and an instant jump to the same offset disagree.
474
- *
475
- * @stimeoRenderRoot
476
- */
477
448
  #evaluateActiveSection() {
478
449
  const rootEl = this.#scrollRoot();
479
450
  const triggerLine = (rootEl ? rootEl.getBoundingClientRect().top : 0) + this.#offset;
@@ -500,19 +471,6 @@ var ScrollspyController = class extends Controller {
500
471
  this.#syncActiveStates(true);
501
472
  }
502
473
  }
503
- /**
504
- * Writes `aria-current` across the current link set, optionally announcing.
505
- *
506
- * The two halves are separate because they answer different questions.
507
- * *Attributes* must be idempotently re-established whenever the link set
508
- * changes, even though the reader has not moved — otherwise a link that
509
- * appears (or is re-anchored) while its section is already current never gets
510
- * marked. *The `change` event* announces that the reader moved, so it fires
511
- * only from the evaluation path; re-publishing over a new link set is not
512
- * news, and dispatching there would make a rebuild look like navigation.
513
- *
514
- * @param announce Whether this sync represents a change of current section.
515
- */
516
474
  #syncActiveStates(announce) {
517
475
  const activeLinks = this.linkTargets.filter(
518
476
  (link) => this.#getAnchorId(link) === this.#activeSectionId
@@ -530,11 +488,6 @@ var ScrollspyController = class extends Controller {
530
488
  this.dispatch("change", { detail: { id: this.#activeSectionId, link: primaryLink } });
531
489
  }
532
490
  }
533
- /**
534
- * The current location already encoded in the DOM, i.e. the section anchored
535
- * by the first link carrying this controller's `aria-current="location"`.
536
- * Empty when no link claims it (a genuinely fresh render).
537
- */
538
491
  #activeIdFromDom() {
539
492
  for (const link of this.linkTargets) {
540
493
  if (link.getAttribute("aria-current") !== "location") continue;
@@ -543,31 +496,23 @@ var ScrollspyController = class extends Controller {
543
496
  }
544
497
  return "";
545
498
  }
546
- /**
547
- * The section id a link anchors, resolved in a fixed order:
548
- *
549
- * 1. `href`, when it is a non-empty same-document fragment — the contract.
550
- * 2. otherwise `data-href`'s fragment — the fallback for a link whose `href`
551
- * must stay a real URL (a server-rendered permalink) or that is not an
552
- * `<a>` at all.
553
- * 3. otherwise `null`: the link anchors nothing here and is not observed.
554
- *
555
- * Step 2 is reached whenever `href` yields nothing usable — absent, `"#"`, or
556
- * a real URL — which is the whole point of the fallback: `href="/guide/usage"`
557
- * with `data-href="#usage"` is a permalink that also spies, and picking the
558
- * first *present* attribute instead of the first *usable* one would silently
559
- * exclude exactly that markup. When both are valid fragments `href` wins.
560
- */
561
499
  #getAnchorId(link) {
562
500
  return this.#fragmentId(link.getAttribute("href")) ?? this.#fragmentId(link.getAttribute("data-href"));
563
501
  }
564
- /** The id in a `#fragment` value; `null` for absent, empty, or non-fragment. */
565
502
  #fragmentId(value) {
566
503
  if (!value?.startsWith("#")) return null;
567
504
  return value.substring(1) || null;
568
505
  }
506
+ get #safeOffset() {
507
+ return this.#numbers.read(
508
+ this,
509
+ "offset",
510
+ this.offsetValue,
511
+ _ScrollspyController.values.offset.default,
512
+ _ScrollspyController.valueConstraints.offset
513
+ );
514
+ }
569
515
  };
570
516
 
571
517
  export { ScrollspyController };
572
- //# sourceMappingURL=scrollspy_controller.js.map
573
518
  //# sourceMappingURL=scrollspy_controller.js.map