stimeo-ui 0.17.0 → 0.18.1

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 (299) hide show
  1. package/CHANGELOG.md +136 -2
  2. package/README.md +19 -10
  3. package/dist/cable/index.d.ts +11 -9
  4. package/dist/cable/index.js +22 -14
  5. package/dist/cable/index.js.map +1 -1
  6. package/dist/coerce-JtZVNG5h.d.ts +20 -0
  7. package/dist/controllers/accordion_controller.js +14 -6
  8. package/dist/controllers/accordion_controller.js.map +1 -1
  9. package/dist/controllers/alert_dialog_controller.js +145 -118
  10. package/dist/controllers/alert_dialog_controller.js.map +1 -1
  11. package/dist/controllers/announcer_controller.d.ts +5 -3
  12. package/dist/controllers/announcer_controller.js +113 -17
  13. package/dist/controllers/announcer_controller.js.map +1 -1
  14. package/dist/controllers/auto_submit_controller.js +185 -60
  15. package/dist/controllers/auto_submit_controller.js.map +1 -1
  16. package/dist/controllers/avatar_controller.js +55 -43
  17. package/dist/controllers/avatar_controller.js.map +1 -1
  18. package/dist/controllers/breadcrumb_controller.js.map +1 -1
  19. package/dist/controllers/bulk_select_controller.d.ts +3 -3
  20. package/dist/controllers/bulk_select_controller.js +64 -50
  21. package/dist/controllers/bulk_select_controller.js.map +1 -1
  22. package/dist/controllers/calendar_controller.d.ts +15 -12
  23. package/dist/controllers/calendar_controller.js +55 -53
  24. package/dist/controllers/calendar_controller.js.map +1 -1
  25. package/dist/controllers/carousel_controller.d.ts +9 -5
  26. package/dist/controllers/carousel_controller.js +124 -86
  27. package/dist/controllers/carousel_controller.js.map +1 -1
  28. package/dist/controllers/character_counter_controller.d.ts +27 -6
  29. package/dist/controllers/character_counter_controller.js +291 -95
  30. package/dist/controllers/character_counter_controller.js.map +1 -1
  31. package/dist/controllers/checkbox_controller.js +6 -0
  32. package/dist/controllers/checkbox_controller.js.map +1 -1
  33. package/dist/controllers/clipboard_controller.js +118 -27
  34. package/dist/controllers/clipboard_controller.js.map +1 -1
  35. package/dist/controllers/collapsible_controller.d.ts +1 -1
  36. package/dist/controllers/collapsible_controller.js +52 -49
  37. package/dist/controllers/collapsible_controller.js.map +1 -1
  38. package/dist/controllers/color_picker_controller.js +29 -7
  39. package/dist/controllers/color_picker_controller.js.map +1 -1
  40. package/dist/controllers/combobox_controller.d.ts +1 -1
  41. package/dist/controllers/combobox_controller.js +210 -71
  42. package/dist/controllers/combobox_controller.js.map +1 -1
  43. package/dist/controllers/command_palette_controller.d.ts +9 -9
  44. package/dist/controllers/command_palette_controller.js +290 -140
  45. package/dist/controllers/command_palette_controller.js.map +1 -1
  46. package/dist/controllers/conditional_fields_controller.js +145 -58
  47. package/dist/controllers/conditional_fields_controller.js.map +1 -1
  48. package/dist/controllers/confirm_controller.js +145 -118
  49. package/dist/controllers/confirm_controller.js.map +1 -1
  50. package/dist/controllers/context_menu_controller.d.ts +49 -14
  51. package/dist/controllers/context_menu_controller.js +138 -73
  52. package/dist/controllers/context_menu_controller.js.map +1 -1
  53. package/dist/controllers/count_up_controller.js.map +1 -1
  54. package/dist/controllers/countdown_controller.d.ts +2 -2
  55. package/dist/controllers/countdown_controller.js +109 -21
  56. package/dist/controllers/countdown_controller.js.map +1 -1
  57. package/dist/controllers/currency_input_controller.d.ts +75 -11
  58. package/dist/controllers/currency_input_controller.js +446 -72
  59. package/dist/controllers/currency_input_controller.js.map +1 -1
  60. package/dist/controllers/data_grid_controller.d.ts +10 -8
  61. package/dist/controllers/data_grid_controller.js +134 -28
  62. package/dist/controllers/data_grid_controller.js.map +1 -1
  63. package/dist/controllers/date_range_picker_controller.d.ts +12 -9
  64. package/dist/controllers/date_range_picker_controller.js +93 -72
  65. package/dist/controllers/date_range_picker_controller.js.map +1 -1
  66. package/dist/controllers/dialog_controller.d.ts +4 -5
  67. package/dist/controllers/dialog_controller.js +145 -118
  68. package/dist/controllers/dialog_controller.js.map +1 -1
  69. package/dist/controllers/direct_upload_controller.d.ts +1 -1
  70. package/dist/controllers/direct_upload_controller.js +24 -14
  71. package/dist/controllers/direct_upload_controller.js.map +1 -1
  72. package/dist/controllers/dirty_form_controller.d.ts +6 -1
  73. package/dist/controllers/dirty_form_controller.js +68 -31
  74. package/dist/controllers/dirty_form_controller.js.map +1 -1
  75. package/dist/controllers/dismissible_controller.js +66 -46
  76. package/dist/controllers/dismissible_controller.js.map +1 -1
  77. package/dist/controllers/drawer_controller.js +145 -118
  78. package/dist/controllers/drawer_controller.js.map +1 -1
  79. package/dist/controllers/dropdown_controller.js +76 -63
  80. package/dist/controllers/dropdown_controller.js.map +1 -1
  81. package/dist/controllers/editable_controller.js +200 -62
  82. package/dist/controllers/editable_controller.js.map +1 -1
  83. package/dist/controllers/empty_state_controller.js +13 -6
  84. package/dist/controllers/empty_state_controller.js.map +1 -1
  85. package/dist/controllers/file_dropzone_controller.d.ts +3 -0
  86. package/dist/controllers/file_dropzone_controller.js +55 -48
  87. package/dist/controllers/file_dropzone_controller.js.map +1 -1
  88. package/dist/controllers/filter_controller.js +9 -2
  89. package/dist/controllers/filter_controller.js.map +1 -1
  90. package/dist/controllers/flash_controller.d.ts +4 -4
  91. package/dist/controllers/flash_controller.js +66 -33
  92. package/dist/controllers/flash_controller.js.map +1 -1
  93. package/dist/controllers/focus_controller.d.ts +3 -3
  94. package/dist/controllers/focus_controller.js +39 -14
  95. package/dist/controllers/focus_controller.js.map +1 -1
  96. package/dist/controllers/form_field_controller.d.ts +2 -1
  97. package/dist/controllers/form_field_controller.js +52 -43
  98. package/dist/controllers/form_field_controller.js.map +1 -1
  99. package/dist/controllers/form_validation_controller.d.ts +3 -3
  100. package/dist/controllers/form_validation_controller.js +18 -11
  101. package/dist/controllers/form_validation_controller.js.map +1 -1
  102. package/dist/controllers/frame_loading_controller.d.ts +13 -12
  103. package/dist/controllers/frame_loading_controller.js +55 -48
  104. package/dist/controllers/frame_loading_controller.js.map +1 -1
  105. package/dist/controllers/highlight_controller.d.ts +5 -4
  106. package/dist/controllers/highlight_controller.js.map +1 -1
  107. package/dist/controllers/hover_card_controller.d.ts +4 -1
  108. package/dist/controllers/hover_card_controller.js +67 -61
  109. package/dist/controllers/hover_card_controller.js.map +1 -1
  110. package/dist/controllers/idle_controller.d.ts +6 -5
  111. package/dist/controllers/idle_controller.js +10 -6
  112. package/dist/controllers/idle_controller.js.map +1 -1
  113. package/dist/controllers/input_mask_controller.d.ts +31 -13
  114. package/dist/controllers/input_mask_controller.js +434 -24
  115. package/dist/controllers/input_mask_controller.js.map +1 -1
  116. package/dist/controllers/intersection_controller.js +3 -0
  117. package/dist/controllers/intersection_controller.js.map +1 -1
  118. package/dist/controllers/lazy_frame_controller.d.ts +3 -2
  119. package/dist/controllers/lazy_frame_controller.js +15 -3
  120. package/dist/controllers/lazy_frame_controller.js.map +1 -1
  121. package/dist/controllers/listbox_controller.d.ts +10 -7
  122. package/dist/controllers/listbox_controller.js +260 -79
  123. package/dist/controllers/listbox_controller.js.map +1 -1
  124. package/dist/controllers/local_time_controller.d.ts +8 -6
  125. package/dist/controllers/local_time_controller.js +48 -13
  126. package/dist/controllers/local_time_controller.js.map +1 -1
  127. package/dist/controllers/masonry_controller.js +11 -4
  128. package/dist/controllers/masonry_controller.js.map +1 -1
  129. package/dist/controllers/menu_controller.d.ts +24 -5
  130. package/dist/controllers/menu_controller.js +138 -82
  131. package/dist/controllers/menu_controller.js.map +1 -1
  132. package/dist/controllers/menubar_controller.d.ts +49 -7
  133. package/dist/controllers/menubar_controller.js +65 -19
  134. package/dist/controllers/menubar_controller.js.map +1 -1
  135. package/dist/controllers/meter_controller.d.ts +13 -20
  136. package/dist/controllers/meter_controller.js +270 -24
  137. package/dist/controllers/meter_controller.js.map +1 -1
  138. package/dist/controllers/multi_select_controller.d.ts +19 -16
  139. package/dist/controllers/multi_select_controller.js +272 -95
  140. package/dist/controllers/multi_select_controller.js.map +1 -1
  141. package/dist/controllers/navigation_menu_controller.d.ts +19 -5
  142. package/dist/controllers/navigation_menu_controller.js +67 -19
  143. package/dist/controllers/navigation_menu_controller.js.map +1 -1
  144. package/dist/controllers/nested_form_controller.js +98 -64
  145. package/dist/controllers/nested_form_controller.js.map +1 -1
  146. package/dist/controllers/network_status_controller.js +70 -52
  147. package/dist/controllers/network_status_controller.js.map +1 -1
  148. package/dist/controllers/number_input_controller.d.ts +24 -6
  149. package/dist/controllers/number_input_controller.js +344 -94
  150. package/dist/controllers/number_input_controller.js.map +1 -1
  151. package/dist/controllers/optimistic_controller.d.ts +8 -5
  152. package/dist/controllers/optimistic_controller.js +29 -11
  153. package/dist/controllers/optimistic_controller.js.map +1 -1
  154. package/dist/controllers/otp_controller.d.ts +35 -4
  155. package/dist/controllers/otp_controller.js +502 -104
  156. package/dist/controllers/otp_controller.js.map +1 -1
  157. package/dist/controllers/overflow_indicator_controller.js +6 -0
  158. package/dist/controllers/overflow_indicator_controller.js.map +1 -1
  159. package/dist/controllers/overflow_menu_controller.d.ts +10 -9
  160. package/dist/controllers/overflow_menu_controller.js +136 -21
  161. package/dist/controllers/overflow_menu_controller.js.map +1 -1
  162. package/dist/controllers/pagination_controller.d.ts +3 -2
  163. package/dist/controllers/pagination_controller.js +47 -14
  164. package/dist/controllers/pagination_controller.js.map +1 -1
  165. package/dist/controllers/password_reveal_controller.d.ts +5 -4
  166. package/dist/controllers/password_reveal_controller.js +52 -49
  167. package/dist/controllers/password_reveal_controller.js.map +1 -1
  168. package/dist/controllers/password_strength_controller.d.ts +1 -1
  169. package/dist/controllers/password_strength_controller.js +13 -6
  170. package/dist/controllers/password_strength_controller.js.map +1 -1
  171. package/dist/controllers/persist_controller.js +48 -128
  172. package/dist/controllers/persist_controller.js.map +1 -1
  173. package/dist/controllers/pointer_drag_controller.d.ts +20 -8
  174. package/dist/controllers/pointer_drag_controller.js +255 -24
  175. package/dist/controllers/pointer_drag_controller.js.map +1 -1
  176. package/dist/controllers/popover_controller.d.ts +9 -5
  177. package/dist/controllers/popover_controller.js +91 -65
  178. package/dist/controllers/popover_controller.js.map +1 -1
  179. package/dist/controllers/portal_controller.js +6 -0
  180. package/dist/controllers/portal_controller.js.map +1 -1
  181. package/dist/controllers/preview_guard_controller.js +14 -3
  182. package/dist/controllers/preview_guard_controller.js.map +1 -1
  183. package/dist/controllers/progress_controller.d.ts +9 -19
  184. package/dist/controllers/progress_controller.js +271 -27
  185. package/dist/controllers/progress_controller.js.map +1 -1
  186. package/dist/controllers/radio_group_controller.d.ts +11 -8
  187. package/dist/controllers/radio_group_controller.js +167 -80
  188. package/dist/controllers/radio_group_controller.js.map +1 -1
  189. package/dist/controllers/range_slider_controller.d.ts +4 -3
  190. package/dist/controllers/range_slider_controller.js +27 -7
  191. package/dist/controllers/range_slider_controller.js.map +1 -1
  192. package/dist/controllers/rating_controller.js +93 -57
  193. package/dist/controllers/rating_controller.js.map +1 -1
  194. package/dist/controllers/read_more_controller.js +10 -6
  195. package/dist/controllers/read_more_controller.js.map +1 -1
  196. package/dist/controllers/reading_progress_controller.js +22 -5
  197. package/dist/controllers/reading_progress_controller.js.map +1 -1
  198. package/dist/controllers/relative_time_controller.d.ts +4 -4
  199. package/dist/controllers/relative_time_controller.js +75 -11
  200. package/dist/controllers/relative_time_controller.js.map +1 -1
  201. package/dist/controllers/reset_on_restore_controller.d.ts +3 -3
  202. package/dist/controllers/reset_on_restore_controller.js +6 -0
  203. package/dist/controllers/reset_on_restore_controller.js.map +1 -1
  204. package/dist/controllers/resizable_controller.d.ts +3 -0
  205. package/dist/controllers/resizable_controller.js +55 -22
  206. package/dist/controllers/resizable_controller.js.map +1 -1
  207. package/dist/controllers/roving_controller.d.ts +3 -3
  208. package/dist/controllers/roving_controller.js +10 -4
  209. package/dist/controllers/roving_controller.js.map +1 -1
  210. package/dist/controllers/scroll_area_controller.js +129 -88
  211. package/dist/controllers/scroll_area_controller.js.map +1 -1
  212. package/dist/controllers/scroll_restore_controller.d.ts +3 -3
  213. package/dist/controllers/scroll_restore_controller.js +14 -2
  214. package/dist/controllers/scroll_restore_controller.js.map +1 -1
  215. package/dist/controllers/scroll_visibility_controller.js +93 -60
  216. package/dist/controllers/scroll_visibility_controller.js.map +1 -1
  217. package/dist/controllers/scrollspy_controller.js +11 -5
  218. package/dist/controllers/scrollspy_controller.js.map +1 -1
  219. package/dist/controllers/separator_controller.d.ts +1 -1
  220. package/dist/controllers/separator_controller.js +59 -43
  221. package/dist/controllers/separator_controller.js.map +1 -1
  222. package/dist/controllers/sidebar_controller.d.ts +1 -1
  223. package/dist/controllers/sidebar_controller.js +150 -122
  224. package/dist/controllers/sidebar_controller.js.map +1 -1
  225. package/dist/controllers/skeleton_controller.js +55 -48
  226. package/dist/controllers/skeleton_controller.js.map +1 -1
  227. package/dist/controllers/slider_controller.d.ts +2 -1
  228. package/dist/controllers/slider_controller.js +27 -6
  229. package/dist/controllers/slider_controller.js.map +1 -1
  230. package/dist/controllers/smart_sticky_header_controller.d.ts +1 -1
  231. package/dist/controllers/smart_sticky_header_controller.js +13 -5
  232. package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
  233. package/dist/controllers/sortable_controller.d.ts +4 -4
  234. package/dist/controllers/sortable_controller.js +27 -14
  235. package/dist/controllers/sortable_controller.js.map +1 -1
  236. package/dist/controllers/spinner_controller.js +55 -48
  237. package/dist/controllers/spinner_controller.js.map +1 -1
  238. package/dist/controllers/step_indicator_controller.d.ts +4 -4
  239. package/dist/controllers/step_indicator_controller.js +6 -0
  240. package/dist/controllers/step_indicator_controller.js.map +1 -1
  241. package/dist/controllers/stepper_controller.d.ts +2 -3
  242. package/dist/controllers/stepper_controller.js +6 -0
  243. package/dist/controllers/stepper_controller.js.map +1 -1
  244. package/dist/controllers/stick_to_bottom_controller.d.ts +3 -1
  245. package/dist/controllers/stick_to_bottom_controller.js +19 -8
  246. package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
  247. package/dist/controllers/sticky_observer_controller.js +49 -43
  248. package/dist/controllers/sticky_observer_controller.js.map +1 -1
  249. package/dist/controllers/submit_once_controller.d.ts +2 -1
  250. package/dist/controllers/submit_once_controller.js +145 -85
  251. package/dist/controllers/submit_once_controller.js.map +1 -1
  252. package/dist/controllers/switch_controller.d.ts +7 -4
  253. package/dist/controllers/switch_controller.js +172 -28
  254. package/dist/controllers/switch_controller.js.map +1 -1
  255. package/dist/controllers/tabs_controller.d.ts +7 -4
  256. package/dist/controllers/tabs_controller.js +6 -1
  257. package/dist/controllers/tabs_controller.js.map +1 -1
  258. package/dist/controllers/tags_input_controller.d.ts +4 -3
  259. package/dist/controllers/tags_input_controller.js +157 -28
  260. package/dist/controllers/tags_input_controller.js.map +1 -1
  261. package/dist/controllers/textarea_autosize_controller.d.ts +1 -1
  262. package/dist/controllers/textarea_autosize_controller.js +40 -19
  263. package/dist/controllers/textarea_autosize_controller.js.map +1 -1
  264. package/dist/controllers/theme_controller.d.ts +3 -2
  265. package/dist/controllers/theme_controller.js +10 -4
  266. package/dist/controllers/theme_controller.js.map +1 -1
  267. package/dist/controllers/time_picker_controller.d.ts +38 -5
  268. package/dist/controllers/time_picker_controller.js +192 -54
  269. package/dist/controllers/time_picker_controller.js.map +1 -1
  270. package/dist/controllers/toast_controller.d.ts +4 -5
  271. package/dist/controllers/toast_controller.js +58 -27
  272. package/dist/controllers/toast_controller.js.map +1 -1
  273. package/dist/controllers/toggle_group_controller.d.ts +2 -3
  274. package/dist/controllers/toggle_group_controller.js +158 -56
  275. package/dist/controllers/toggle_group_controller.js.map +1 -1
  276. package/dist/controllers/toolbar_controller.d.ts +8 -6
  277. package/dist/controllers/toolbar_controller.js +150 -21
  278. package/dist/controllers/toolbar_controller.js.map +1 -1
  279. package/dist/controllers/tooltip_controller.d.ts +4 -1
  280. package/dist/controllers/tooltip_controller.js.map +1 -1
  281. package/dist/controllers/transition_controller.js.map +1 -1
  282. package/dist/controllers/tree_view_controller.d.ts +1 -1
  283. package/dist/controllers/tree_view_controller.js +134 -25
  284. package/dist/controllers/tree_view_controller.js.map +1 -1
  285. package/dist/index.d.ts +4 -1
  286. package/dist/index.js +2515 -1315
  287. package/dist/index.js.map +1 -1
  288. package/dist/inspector/cli.d.ts +25 -12
  289. package/dist/inspector/cli.js +21 -3
  290. package/dist/inspector/cli.js.map +1 -1
  291. package/dist/inspector/cli_bin.d.ts +1 -1
  292. package/dist/inspector/cli_bin.js +62 -8
  293. package/dist/inspector/cli_bin.js.map +1 -1
  294. package/dist/inspector/examples.json +13 -13
  295. package/dist/inspector/manifest.json +19 -5
  296. package/dist/positioning/index.d.ts +9 -8
  297. package/dist/positioning/index.js +6 -0
  298. package/dist/positioning/index.js.map +1 -1
  299. package/package.json +11 -10
@@ -157,6 +157,9 @@ var MicrotaskCoalescer = class {
157
157
  this.#queued = false;
158
158
  this.#generation += 1;
159
159
  }
160
+ get pending() {
161
+ return this.#queued;
162
+ }
160
163
  schedule() {
161
164
  if (!this.#active || this.#queued) return;
162
165
  this.#queued = true;
@@ -187,6 +190,9 @@ var MorphRenderWatcher = class {
187
190
  schedule() {
188
191
  this.#pass.schedule();
189
192
  }
193
+ get pending() {
194
+ return this.#pass.pending;
195
+ }
190
196
  disconnect() {
191
197
  this.#listeners.dispose();
192
198
  this.#pass.cancel();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/blur_deferral.ts","../../src/utils/frame_coalescer.ts","../../src/utils/layout_observer.ts","../../src/utils/listener_set.ts","../../src/utils/logical_scroll.ts","../../src/utils/microtask_coalescer.ts","../../src/utils/morph_render_watcher.ts","../../src/utils/number_bounds.ts","../../src/utils/coerce.ts","../../src/utils/number_value.ts","../../src/utils/reduced_motion.ts","../../src/controllers/overflow_indicator_controller.ts"],"names":[],"mappings":"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","file":"overflow_indicator_controller.js","sourcesContent":["/**\n * Shared bookkeeping for updates that must wait until an element loses focus.\n *\n * A control that becomes redundant while the user is standing on it cannot be\n * removed from the page immediately: hiding a focused element (or turning it\n * into a native `disabled` one) drops focus to `<body>`, stranding a keyboard\n * user mid-widget. The fix every affected controller reached for is the same —\n * keep the element reachable, hold the destructive update, listen for `blur`,\n * and apply it then.\n *\n * {@link BlurDeferral} owns *only* the registry part of that: which elements are\n * currently holding an update back, attaching and detaching the `blur` listener,\n * and guaranteeing teardown. It is deliberately **policy-free** — what the\n * interim state looks like (`aria-disabled`, an ownership marker, nothing at\n * all), and what the deferred update actually is, stay in the controller, which\n * receives the element back through its release callback.\n *\n * Three contracts are worth stating up front, because each one is a hazard the\n * consumers would otherwise hit:\n *\n * - **The release callback fires only on a real `blur`.** `release()` /\n * `releaseAll()` *cancel* a deferral; they do not complete it. Conflating the\n * two makes `overflow-indicator` disable a button that just left the boundary.\n * - **`elements` is a snapshot.** Iterating the live key set while releasing\n * inside the loop is the failure this registry exists to prevent.\n * - **The entry is detached before the callback runs**, so a controller that\n * decides the update is still unsafe can simply defer again.\n *\n * @example\n * ```ts\n * readonly #deferred = new BlurDeferral<HTMLElement>((trigger) => {\n * if (this.#connected) this.#evaluate();\n * });\n *\n * #hide(trigger: HTMLElement) {\n * if (document.activeElement === trigger) {\n * this.#deferred.deferOnly(trigger); // stays visible until blur\n * return;\n * }\n * this.#deferred.releaseAll();\n * trigger.hidden = true;\n * }\n *\n * disconnect() {\n * this.#deferred.releaseAll();\n * }\n * ```\n */\nexport class BlurDeferral<T extends HTMLElement = HTMLElement> {\n /** Elements currently holding an update back, mapped to their `blur` listener. */\n readonly #pending = new Map<T, () => void>();\n /** Called after a pending element blurs and has been detached. */\n readonly #onRelease: (element: T) => void;\n\n /** @param onRelease - Invoked once `element` actually blurs; never on `release`. */\n constructor(onRelease: (element: T) => void) {\n this.#onRelease = onRelease;\n }\n\n /** Number of elements currently holding an update back. */\n get size(): number {\n return this.#pending.size;\n }\n\n /** Snapshot of the pending elements, safe to iterate while releasing them. */\n get elements(): T[] {\n return [...this.#pending.keys()];\n }\n\n /** Whether `element` is currently holding an update back. */\n has(element: T): boolean {\n return this.#pending.has(element);\n }\n\n /** Holds an update back until `element` blurs. Idempotent (no stacked listeners). */\n defer(element: T): void {\n if (this.#pending.has(element)) return;\n const onBlur = (): void => {\n this.#detach(element);\n this.#onRelease(element);\n };\n this.#pending.set(element, onBlur);\n element.addEventListener(\"blur\", onBlur);\n }\n\n /** Defers `element` as the only pending entry, cancelling any others. */\n deferOnly(element: T): void {\n for (const pending of this.elements) {\n if (pending !== element) this.#detach(pending);\n }\n this.defer(element);\n }\n\n /** Cancels `element`'s deferral without completing it; no-ops when not pending. */\n release(element: T): void {\n this.#detach(element);\n }\n\n /** Cancels every deferral without completing any of them. */\n releaseAll(): void {\n for (const element of this.elements) this.#detach(element);\n }\n\n /** Removes the `blur` listener for `element` and forgets it. */\n #detach(element: T): void {\n const onBlur = this.#pending.get(element);\n if (onBlur) element.removeEventListener(\"blur\", onBlur);\n this.#pending.delete(element);\n }\n}\n","/**\n * Runs one piece of scroll-driven work per animation frame.\n *\n * A `scroll` listener fires many times between two paints, and each firing would\n * measure the same layout. Requesting a frame on the first firing and ignoring\n * the rest until it runs keeps the reads to one per frame; running on the frame\n * — rather than on a timer — keeps the measurement in step with what the reader\n * sees.\n *\n * **The first request of a burst wins.** Work handed in while a frame is already\n * pending is dropped, so a caller that queues a special first pass and then\n * receives scrolls gets that first pass, not the last scroll's.\n *\n * Cancelling drops the pending frame outright, and does nothing at all when none\n * is pending. A frame really is cancelled, so nothing needs a generation to tell\n * a stale callback from a fresh one, and nothing has to be refused before the\n * caller starts listening.\n *\n * Scope is the scheduling only. What to measure stays with the caller.\n *\n * @example\n * ```ts\n * readonly #frames = new FrameCoalescer();\n *\n * readonly #onScroll = (): void => this.#frames.schedule(() => this.#measure());\n *\n * disconnect(): void {\n * this.#frames.cancel();\n * }\n * ```\n */\nexport class FrameCoalescer {\n #frame: number | null = null;\n\n /**\n * Runs `run` on the next frame, unless a frame is already pending — the first\n * request of a burst wins and the rest are dropped. The pending frame is\n * released before `run`, so `run` may request the next one.\n */\n schedule(run: () => void): void {\n if (this.#frame !== null) return;\n this.#frame = requestAnimationFrame(() => {\n this.#frame = null;\n run();\n });\n }\n\n /**\n * Drops the pending frame, and reaches the platform only when there is one.\n *\n * There is no handle value that stands for \"nothing pending\":\n * `cancelAnimationFrame` takes an `unsigned long`, so a negative placeholder\n * arrives as a large positive number that the same allocator can hand out, and\n * an idle cancel would then drop a frame belonging to someone else.\n */\n cancel(): void {\n if (this.#frame === null) return;\n cancelAnimationFrame(this.#frame);\n this.#frame = null;\n }\n}\n","/**\n * Unified layout observation for Stimeo controllers.\n *\n * Widgets whose output is measured — an overflow boundary, a masonry column count,\n * an autosized textarea — have three sources that move the layout under them: their\n * *own* box changing — via {@link ResizeObserver} — the *viewport* changing — via the\n * `window` `resize` event — and a descendant *resource settling*, because an image or\n * a frame reports a height of zero until it has loaded. Wiring those three by hand in\n * every controller risks leaked listeners on `disconnect()`. {@link LayoutObserver}\n * owns all three behind one callback and one\n * {@link LayoutObserver.disconnect | disconnect()} that releases everything.\n *\n * Behavior only: the helper reports *that* layout changed; it never reads or\n * writes styles. Consumers decide what to recompute.\n */\n\n/** Invoked whenever an observed element or the viewport changes size. */\nexport type LayoutCallback = () => void;\n\n/** Constructs a {@link ResizeObserver}; injectable so tests stay deterministic. */\nexport type ResizeObserverFactory = (callback: ResizeObserverCallback) => ResizeObserver;\n\n/** Options for {@link LayoutObserver}. */\nexport interface LayoutObserverOptions {\n /**\n * Factory for the {@link ResizeObserver} used by {@link LayoutObserver.observe}.\n * Defaults to the global constructor; override it in tests, or to no-op in\n * environments where `ResizeObserver` is unavailable.\n */\n resizeObserverFactory?: ResizeObserverFactory;\n}\n\n/**\n * Observes element resizes and/or viewport resizes through a single callback,\n * with guaranteed teardown.\n *\n * @example\n * ```ts\n * #layout = new LayoutObserver(() => this.#reposition());\n *\n * connect() {\n * this.#layout.observe(this.panelTarget);\n * this.#layout.observeViewport();\n * this.#layout.observeDescendantLoads(this.panelTarget);\n * }\n *\n * disconnect() {\n * this.#layout.disconnect();\n * }\n * ```\n */\nexport class LayoutObserver {\n readonly #callback: LayoutCallback;\n readonly #resizeObserverFactory: ResizeObserverFactory | null;\n #resizeObserver: ResizeObserver | null = null;\n #observingViewport = false;\n #loadContainer: Element | null = null;\n\n /** Stable bound handler so add/removeEventListener target the same reference. */\n readonly #handleViewportResize = (): void => {\n this.#callback();\n };\n\n /** Stable bound handler for the capture-phase `load`; see {@link observeDescendantLoads}. */\n readonly #handleDescendantLoad = (): void => {\n this.#callback();\n };\n\n constructor(callback: LayoutCallback, options: LayoutObserverOptions = {}) {\n this.#callback = callback;\n this.#resizeObserverFactory =\n options.resizeObserverFactory ??\n (typeof ResizeObserver === \"undefined\" ? null : (cb) => new ResizeObserver(cb));\n }\n\n /**\n * Starts observing an element's size. Repeated calls observe additional\n * elements through the same shared observer. No-ops when no\n * `ResizeObserver` implementation is available.\n */\n observe(element: Element): void {\n if (!this.#resizeObserverFactory) return;\n if (!this.#resizeObserver) {\n this.#resizeObserver = this.#resizeObserverFactory(() => {\n this.#callback();\n });\n }\n this.#resizeObserver.observe(element);\n }\n\n /** Stops observing a single element while leaving any others in place. */\n unobserve(element: Element): void {\n this.#resizeObserver?.unobserve(element);\n }\n\n /** Starts observing viewport resizes. Idempotent: the listener is added once. */\n observeViewport(): void {\n if (this.#observingViewport) return;\n this.#observingViewport = true;\n window.addEventListener(\"resize\", this.#handleViewportResize);\n }\n\n /** Stops observing viewport resizes without affecting element observation. */\n unobserveViewport(): void {\n if (!this.#observingViewport) return;\n this.#observingViewport = false;\n window.removeEventListener(\"resize\", this.#handleViewportResize);\n }\n\n /**\n * Starts reporting a `load` from anywhere inside `container` — an image or a\n * frame settling changes the box it sits in, and it measures as zero high until\n * then. `load` does not bubble, so the subscription is a capture-phase listener\n * on the container itself and nothing the caller spells.\n *\n * **One container at a time.** A further call moves the observation, so a widget\n * whose content element is swapped at runtime releases the element it let go by\n * naming the new one — there is no second place for the release to drift from.\n */\n observeDescendantLoads(container: Element): void {\n this.unobserveDescendantLoads();\n this.#loadContainer = container;\n container.addEventListener(\"load\", this.#handleDescendantLoad, true);\n }\n\n /** Stops reporting descendant loads without affecting element or viewport observation. */\n unobserveDescendantLoads(): void {\n this.#loadContainer?.removeEventListener(\"load\", this.#handleDescendantLoad, true);\n this.#loadContainer = null;\n }\n\n /**\n * Releases every observation: disconnects the {@link ResizeObserver} and removes\n * the viewport and descendant-load listeners. Safe to call multiple times. Call\n * this from a controller's `disconnect()`.\n */\n disconnect(): void {\n this.#resizeObserver?.disconnect();\n this.#resizeObserver = null;\n this.unobserveViewport();\n this.unobserveDescendantLoads();\n }\n}\n","/**\n * Owns every listener a controller attaches for one connected lifetime and\n * releases them together, so an event name is spelled once and the release\n * cannot drift from the registration.\n *\n * Detaching by hand repeats three things at the release site — the type, the\n * handler reference and the capture flag — and any one of them drifting leaves\n * a listener alive on an element the controller no longer drives. Aborting the\n * signal a registration was created with releases it whatever it was made of,\n * so the release site names nothing.\n *\n * One set per lifetime, held as a field and never rebuilt. {@link\n * ListenerSet.dispose} is the only thing that closes a generation, and it opens\n * the next one in the same step, so the lifecycle hooks can call `add` and\n * `dispose` in any order any number of times, and no `add` ever meets an\n * already aborted signal — a DOM that registers a listener on such a signal\n * would keep it forever. Rebuilding the set instead would leak:\n * `addEventListener` matches an existing registration on target, type, callback\n * and capture alone, so re-adding the same handler while the first registration\n * is live is discarded, and that live registration keeps the signal it was\n * attached with — a set built by a second `connect()` can never release what it\n * appears to own.\n *\n * Scope is the registration only. Timers, observers, leases and the order they\n * are unwound in stay with the controller, which places `dispose()` in its\n * cleanup sequence. A receiver that changes at runtime (a\n * swapped target) is rebound by calling `dispose()` and then `add()` again from\n * the rebind, not by a second set.\n *\n * The set must own every registration of a tuple it holds: a listener the same\n * target already carries for the same type, callback and capture flag keeps the\n * right to remove it, and this set's `add` for that tuple is discarded. A\n * subscription shared between instances behind a participant count, or one that\n * lives for less than the connection, keeps its own pair of calls.\n *\n * @example\n * ```ts\n * readonly #listeners = new ListenerSet();\n *\n * connect(): void {\n * this.#listeners.add(this.element, \"submit\", this.#onNativeSubmit, { capture: true });\n * this.#listeners.add(this.element, \"turbo:submit-start\", this.#onSubmitStart);\n * this.#listeners.add(this.element, \"turbo:submit-end\", this.#onSubmitEnd);\n * }\n *\n * disconnect(): void {\n * this.#listeners.dispose();\n * }\n * ```\n */\nexport class ListenerSet {\n /** The generation every `add` joins until the next `dispose()`. */\n #abort = new AbortController();\n\n /**\n * Attaches `handler` to the open generation, exactly as the caller spelled it.\n *\n * The set supplies the signal, so `options` carries everything else the DOM\n * accepts — `capture` included, which has to match at release time and no\n * longer has a second place to drift from.\n */\n add(\n target: EventTarget,\n type: string,\n handler: EventListener,\n options?: Omit<AddEventListenerOptions, \"signal\">,\n ): void {\n target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });\n }\n\n /**\n * Releases every listener of the open generation, synchronously, and opens the\n * next one. Idempotent, and safe before anything has been added.\n */\n dispose(): void {\n this.#abort.abort();\n this.#abort = new AbortController();\n }\n}\n","/** Normalized scroll position and maximum distance on one logical axis. */\nexport interface LogicalScrollMetrics {\n position: number;\n max: number;\n}\n\n/**\n * Whether horizontal scrolling on `element` follows right-to-left inline flow.\n *\n * Resolved from the **computed** `direction`, so the authoring contract is the\n * usual `dir=\"rtl\"` (or a stylesheet) on the element or any ancestor.\n *\n * Scope: horizontal writing modes. A vertical writing mode (`writing-mode:\n * vertical-rl`) also inverts the horizontal axis, which this check does not\n * model — vertical writing modes are out of scope for the scroll utilities\n * (their consumers describe axes as horizontal/vertical, not inline/block).\n */\nexport function isRtl(element: Element): boolean {\n return window.getComputedStyle(element).direction === \"rtl\";\n}\n\n/**\n * Returns scroll distance from the logical start edge.\n *\n * CSSOM View exposes standards-mode RTL horizontal offsets as `0` at the inline\n * start (right) and increasingly negative values toward the inline end (left).\n * The normalized position is always clamped to `[0, max]`, which also absorbs\n * Safari's elastic overscroll values.\n */\nexport function logicalScrollMetrics(\n element: HTMLElement,\n horizontal: boolean,\n): LogicalScrollMetrics {\n const max = Math.max(\n 0,\n horizontal\n ? element.scrollWidth - element.clientWidth\n : element.scrollHeight - element.clientHeight,\n );\n const raw = horizontal ? element.scrollLeft : element.scrollTop;\n const position = horizontal && isRtl(element) ? -raw : raw;\n return { position: Math.min(max, Math.max(0, position)), max };\n}\n\n/**\n * Converts a logical start/end delta to the physical value accepted by\n * `Element.scrollBy`.\n */\nexport function physicalScrollDelta(\n element: HTMLElement,\n horizontal: boolean,\n logicalDelta: number,\n): number {\n return horizontal && isRtl(element) ? -logicalDelta : logicalDelta;\n}\n","/**\n * Collapses many Stimulus lifecycle callbacks from one DOM mutation into a\n * single pass.\n *\n * Stimulus fires `<name>TargetConnected` / `Disconnected` once per element and\n * `<name>ValueChanged` once per changed attribute. Replacing a list of N options\n * or morphing several render Values therefore delivers N callbacks — but the\n * useful unit of work is \"reconcile against the resulting declarative input\",\n * once, after the batch has settled. Every controller with reconcilable targets\n * or render Values needs the same shape: a `queued` flag plus `queueMicrotask`.\n *\n * **A microtask is the right horizon, and the reason is specific.** Stimulus\n * drives these callbacks from a `MutationObserver`, whose own callback already\n * runs as a microtask with the whole batch in hand; scheduling one more lands\n * after the last sibling callback of that batch and still before paint or any\n * event handler. A timer would be later than it needs to be, and reconciling\n * synchronously would run once per element against a half-applied DOM.\n *\n * **The two guards are not the same guard.** Scheduling is refused before the\n * controller connects, and running is refused after it disconnects:\n *\n * - **Before `connect()`** — Stimulus delivers initial target and Value callbacks\n * ahead of `connect()`. Reconciling there would compute output against a\n * controller whose own state has not been initialised, and `connect()` is\n * about to do a full pass anyway.\n * - **After `disconnect()`** — Stimulus fires a callback for **every** target\n * during teardown, and a microtask queued just before it would otherwise run\n * against a detached tree. {@link MicrotaskCoalescer.cancel} exists for the\n * teardown path to drop the pending pass outright.\n *\n * Both guards are part of one contract here rather than something each consumer\n * has to remember separately.\n *\n * Scope is the scheduling only. *What* to reconcile — keep the surviving active\n * option, fall back to the next / previous / first visible one, rebuild derived\n * chips or hidden fields — stays in the controller, because no two consumers\n * answer it the same way.\n *\n * @example\n * ```ts\n * readonly #reconcile = new MicrotaskCoalescer(() => this.#reconcileOptions());\n *\n * connect() { this.#reconcile.activate(); }\n * disconnect() { this.#reconcile.cancel(); }\n *\n * optionTargetConnected() { this.#reconcile.schedule(); }\n * optionTargetDisconnected() { this.#reconcile.schedule(); }\n * ```\n */\nexport class MicrotaskCoalescer {\n readonly #run: () => void;\n #queued = false;\n #active = false;\n #generation = 0;\n\n /** @param run - the single reconciliation pass, invoked at most once per batch. */\n constructor(run: () => void) {\n this.#run = run;\n }\n\n /** Opens the window in which {@link schedule} is honoured; call from `connect()`. */\n activate(): void {\n this.#active = true;\n }\n\n /** Closes the window and drops any pending pass; call from `disconnect()`. */\n cancel(): void {\n this.#active = false;\n this.#queued = false;\n this.#generation += 1;\n }\n\n /** Requests one pass after the batch settles. Idempotent; inert outside the window. */\n schedule(): void {\n if (!this.#active || this.#queued) return;\n this.#queued = true;\n const generation = this.#generation;\n queueMicrotask(() => {\n // A cancelled callback must not consume a pass queued after reconnect.\n if (generation !== this.#generation || !this.#queued || !this.#active) return;\n this.#queued = false;\n this.#run();\n });\n }\n}\n","import { ListenerSet } from \"./listener_set\";\nimport { MicrotaskCoalescer } from \"./microtask_coalescer\";\n\n/**\n * Coalesces retained-element morphs and explicit lifecycle requests into one pass.\n *\n * Observe the controller's element after its initial state is settled. Morphs on\n * that element and bubbling morphs on its descendants share the same microtask\n * as {@link schedule}. The callback repairs derived output; the controller owns\n * any logical transition reporting. {@link disconnect} releases the listener and\n * cancels pending work, including work from an earlier connected lifetime.\n */\nexport class MorphRenderWatcher {\n readonly #listeners = new ListenerSet();\n readonly #pass: MicrotaskCoalescer;\n #element: Element | null = null;\n readonly #onMorph = (): void => this.#pass.schedule();\n\n /** @param run - the single pass that repairs the settled controller's output. */\n constructor(run: () => void) {\n this.#pass = new MicrotaskCoalescer(run);\n }\n\n /** Opens observation; observing the same element again preserves queued work. */\n observe(element: Element): void {\n if (this.#element === element) return;\n this.disconnect();\n this.#element = element;\n this.#pass.activate();\n this.#listeners.add(element, \"turbo:morph-element\", this.#onMorph);\n }\n\n /** Requests one pass; requests outside an observed lifetime are ignored. */\n schedule(): void {\n this.#pass.schedule();\n }\n\n /** Releases observation and pending work synchronously; safe to repeat. */\n disconnect(): void {\n this.#listeners.dispose();\n this.#pass.cancel();\n this.#element = null;\n }\n}\n","import { MAX_TIMER_DELAY_MS } from \"./timer_bounds\";\n\n/** Semantic constraints on an already decoded number, independent of the DOM. */\nexport interface NumberBounds {\n /** Finite numbers are required, including when this field is omitted. */\n readonly finite?: true;\n /** Inclusive lower endpoint. */\n readonly min?: number;\n /** Inclusive upper endpoint. */\n readonly max?: number;\n /** Exclusive lower endpoint. */\n readonly exclusiveMin?: number;\n /** Require a number without a fractional part. */\n readonly integer?: boolean;\n /** Exact decoded numbers accepted by a discrete contract. */\n readonly allowedValues?: readonly number[];\n /** JSON-safe exceptions to finite-number checking for unbounded endpoints. */\n readonly allowInfinity?: \"negative\" | \"positive\" | \"both\";\n}\n\n/** One constraint for each Number Value, including shorthand declarations. */\nexport type NumberValueConstraints<Values> = {\n readonly [Key in keyof Values as Values[Key] extends\n | NumberConstructor\n | { type: NumberConstructor }\n ? Key\n : never]: NumberBounds;\n};\n\n/** Reusable semantic domains; fractional inputs remain fractional unless excluded. */\nexport const NUMBER_BOUNDS = {\n finite: { finite: true },\n nonNegative: { finite: true, min: 0 },\n positive: { finite: true, exclusiveMin: 0 },\n nonNegativeInteger: { finite: true, min: 0, integer: true },\n positiveInteger: { finite: true, exclusiveMin: 0, integer: true },\n lowerBound: { finite: true, allowInfinity: \"negative\" },\n upperBound: { finite: true, allowInfinity: \"positive\" },\n timer: { finite: true, min: 0, max: MAX_TIMER_DELAY_MS },\n positiveTimer: { finite: true, exclusiveMin: 0, max: MAX_TIMER_DELAY_MS },\n} as const satisfies Record<string, NumberBounds>;\n\n/**\n * Checks all declared bounds without rounding, clamping, or coercing the input.\n * Infinity exceptions relax only finite checking; every other bound still applies.\n */\nexport function matchesNumberBounds(value: number, bounds: NumberBounds): boolean {\n if (!Number.isFinite(value)) {\n const direction = value === Infinity ? \"positive\" : value === -Infinity ? \"negative\" : null;\n if (direction === null) return false;\n if (bounds.allowInfinity !== \"both\" && bounds.allowInfinity !== direction) return false;\n }\n if (bounds.min !== undefined && value < bounds.min) return false;\n if (bounds.max !== undefined && value > bounds.max) return false;\n if (bounds.exclusiveMin !== undefined && value <= bounds.exclusiveMin) return false;\n if (bounds.integer && !Number.isInteger(value)) return false;\n if (bounds.allowedValues !== undefined && !bounds.allowedValues.includes(value)) return false;\n return true;\n}\n\n/** Decodes a Number Value literal; action params use their own JSON decoder. */\nexport function decodeNumberValue(raw: string): number {\n return Number(raw.replace(/_/g, \"\"));\n}\n","/**\n * Numeric coercion for controllers that accept a number through a Value, an action\n * param, or an event detail.\n *\n * Stimulus decodes action params as JSON when possible and leaves other text\n * untouched. CustomEvent detail can also supply a number or numeric string.\n * The reader accepts those two types while rejecting blank and non-finite input.\n */\n\nimport { matchesNumberBounds, type NumberBounds } from \"./number_bounds\";\n\n/**\n * Reads a finite number or nonblank numeric string; other input returns `null`.\n * Blank strings cannot silently reset a value to zero.\n */\nexport function toFiniteNumber(raw: unknown): number | null {\n if (typeof raw !== \"number\" && typeof raw !== \"string\") return null;\n if (typeof raw === \"string\" && raw.trim().length === 0) return null;\n const value = Number(raw);\n return Number.isFinite(value) ? value : null;\n}\n\n/**\n * Returns an accepted decoded Number Value or the caller's fallback unchanged.\n * The caller owns the fallback contract; this reader does not parse or write a Value.\n */\nexport function readNumber(raw: number, fallback: number, bounds: NumberBounds): number {\n return matchesNumberBounds(raw, bounds) ? raw : fallback;\n}\n","import { readNumber } from \"./coerce\";\nimport { matchesNumberBounds, type NumberBounds } from \"./number_bounds\";\n\n/** The instance identity carried by a Number Value read. */\nexport interface NumberValueOwner {\n readonly identifier: string;\n readonly element: Element;\n}\n\n/** Reads current declarations at the point of use without rewriting their attributes. */\nexport class NumberValueReader {\n /** The last rejected literal per Value; no history grows for an unchanged instance. */\n readonly #lastRejected = new Map<string, string>();\n\n /** Resolves the declared number using its own fallback and shared class contract. */\n read(\n owner: NumberValueOwner,\n name: string,\n raw: number,\n fallback: number,\n bounds: NumberBounds,\n ): number {\n const resolved = readNumber(raw, fallback, bounds);\n if (matchesNumberBounds(raw, bounds)) {\n this.#lastRejected.delete(name);\n return resolved;\n }\n const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;\n const literal = owner.element.getAttribute(attribute);\n if (literal === null) {\n this.#lastRejected.delete(name);\n return resolved;\n }\n if (this.#lastRejected.get(name) !== literal) {\n this.#lastRejected.set(name, literal);\n console.warn(\n `Stimeo UI: \"${owner.identifier}\" has an invalid number Value \"${name}\" declaration ${JSON.stringify(literal)}; using ${fallback}.`,\n );\n }\n return resolved;\n }\n}\n","/**\n * Shared `prefers-reduced-motion` lookup for the motion-aware controllers.\n *\n * This one-liner keeps the media query string and the environment guard\n * single-sourced across them. The preference is intentionally re-read on every\n * call — the controllers check it at each animation/scroll start (WCAG 2.2\n * **2.3.3**), so flipping the OS setting takes effect immediately without any\n * listener or cache bookkeeping here.\n */\n\n/**\n * Whether the user currently requests reduced motion.\n *\n * @returns `true` when `(prefers-reduced-motion: reduce)` matches; `false`\n * otherwise, including environments without `window.matchMedia` (treated as\n * \"no preference\").\n */\nexport function prefersReducedMotion(): boolean {\n return (\n typeof window.matchMedia === \"function\" &&\n window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n );\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { BlurDeferral } from \"../utils/blur_deferral\";\nimport { FrameCoalescer } from \"../utils/frame_coalescer\";\nimport { LayoutObserver } from \"../utils/layout_observer\";\nimport { ListenerSet } from \"../utils/listener_set\";\nimport { logicalScrollMetrics, physicalScrollDelta } from \"../utils/logical_scroll\";\nimport { MorphRenderWatcher } from \"../utils/morph_render_watcher\";\nimport { NUMBER_BOUNDS, type NumberValueConstraints } from \"../utils/number_bounds\";\nimport { NumberValueReader } from \"../utils/number_value\";\nimport { prefersReducedMotion } from \"../utils/reduced_motion\";\n\nconst DIRECTION_PARAM = \"direction-param\";\n\n/**\n * Headless **Overflow Indicator**: detects whether a scroll container can still\n * scroll toward its start and/or end and publishes it as `data-overflow-start` /\n * `data-overflow-end`. No APG widget — a state-detection utility. Consumers draw\n * edge shadows or arrows in CSS to signal \"more content this way\".\n *\n * Markup contract (identifier: `stimeo--overflow-indicator`):\n * <div data-controller=\"stimeo--overflow-indicator\"\n * data-stimeo--overflow-indicator-orientation-value=\"horizontal\">\n * <button type=\"button\" aria-label=\"Prev\"\n * data-stimeo--overflow-indicator-direction-param=\"start\"\n * data-action=\"click->stimeo--overflow-indicator#scrollByPage\">‹</button>\n * <div data-stimeo--overflow-indicator-target=\"viewport\"\n * tabindex=\"0\" role=\"region\" aria-label=\"Products\"\n * style=\"overflow-x: auto;\"><!-- items --></div>\n * <button type=\"button\" aria-label=\"Next\"\n * data-stimeo--overflow-indicator-direction-param=\"end\"\n * data-action=\"click->stimeo--overflow-indicator#scrollByPage\">›</button>\n * </div>\n *\n * The viewport's scroll position and size are watched by the controller itself:\n * a passive `scroll` listener on the viewport, coalesced to one measurement per\n * animation frame, plus `LayoutObserver` for viewport/content resize, a\n * {@link MutationObserver}, and descendant load events. Nothing about the\n * measurement is the consumer's to wire. Optional page buttons scroll one logical\n * viewport page at a time (including RTL) and have their disabled state synced to\n * the matching direction's remaining room.\n *\n * `change` dispatches `{ start: boolean, end: boolean }`.\n *\n * @remarks\n * Behavior only — shadows, arrows, and gradients are the consumer's CSS;\n * `data-overflow-*` carry no ARIA semantics. All observers/listeners are released\n * on `disconnect()` (Turbo navigation included). `scrollByPage` scrolls smoothly\n * by default and forces an instant jump under `prefers-reduced-motion`, regardless\n * of the consumer's CSS `scroll-behavior`.\n */\nexport class OverflowIndicatorController extends Controller<HTMLElement> {\n /** Numeric read boundaries share one reader for this controller instance. */\n readonly #numbers = new NumberValueReader();\n\n /** The direction param above, in the namespace this controller is registered under. */\n get #directionParam(): string {\n return `data-${this.identifier}-${DIRECTION_PARAM}`;\n }\n\n /** Selects every button carrying that param. */\n get #directionButtonSelector(): string {\n return `[${this.#directionParam}]`;\n }\n\n static override targets = [\"viewport\"];\n static override values = {\n orientation: { type: String, default: \"horizontal\" },\n threshold: { type: Number, default: 1 },\n };\n\n static valueConstraints = {\n threshold: NUMBER_BOUNDS.finite,\n } satisfies NumberValueConstraints<typeof OverflowIndicatorController.values>;\n static actions = [\"scrollByPage\"] as const;\n static events = [\"change\"] as const;\n\n declare readonly viewportTarget: HTMLElement;\n declare readonly hasViewportTarget: boolean;\n\n declare orientationValue: string;\n declare thresholdValue: number;\n\n readonly #layout = new LayoutObserver(() => {\n if (this.#connected) this.#refresh();\n });\n /** Folds a burst of scrolls into one measurement per frame. */\n readonly #frames = new FrameCoalescer();\n /** Holds the viewport's scroll listener for exactly as long as that viewport. */\n readonly #viewportListeners = new ListenerSet();\n readonly #onScroll = (): void => this.#frames.schedule(() => this.#refresh());\n #connected = false;\n #observedViewport: HTMLElement | null = null;\n readonly #observedContent = new Set<Element>();\n #mutationObserver: MutationObserver | null = null;\n /**\n * Boundary buttons whose native `disabled` is held back while they hold focus.\n *\n * Completing the deferral is what actually disables the button, so the release\n * callback — unlike {@link #cancelPendingButtonDisable} — applies it.\n */\n readonly #pendingButtonDisables = new BlurDeferral<HTMLButtonElement>((button) => {\n this.#restorePendingMarkers(button);\n // If the author disabled it while focus was pending, do not claim ownership.\n if (!button.disabled) this.#disableButton(button);\n });\n /** Last reported room, so `change` fires only on transitions. */\n #state: { start: boolean; end: boolean } | null = null;\n\n readonly #morphRender = new MorphRenderWatcher(() => this.#refresh());\n\n override connect(): void {\n this.#morphRender.observe(this.element);\n this.#connected = true;\n this.#syncViewport();\n }\n\n override disconnect(): void {\n this.#morphRender.disconnect();\n this.#connected = false;\n this.#stopObservingViewport();\n this.#layout.disconnect();\n this.#clearPendingButtonDisables();\n this.#state = null;\n }\n\n viewportTargetConnected(): void {\n this.#syncViewport();\n }\n\n viewportTargetDisconnected(viewport: HTMLElement): void {\n if (this.#observedViewport === viewport) this.#stopObservingViewport();\n this.#syncViewport();\n }\n\n orientationValueChanged(): void {\n if (this.#connected) this.#refresh();\n }\n\n thresholdValueChanged(): void {\n if (this.#connected) this.#refresh();\n }\n\n /**\n * Re-measures remaining scroll room and reflects the state hooks.\n *\n * @stimeoRenderRoot\n */\n #refresh(): void {\n if (!this.hasViewportTarget) return;\n const vp = this.viewportTarget;\n const horizontal = this.orientationValue !== \"vertical\";\n const t = this.#threshold;\n const { position: scrollPos, max: maxScroll } = logicalScrollMetrics(vp, horizontal);\n\n const start = scrollPos > t;\n const end = scrollPos < maxScroll - t;\n\n vp.setAttribute(\"data-overflow-start\", start ? \"true\" : \"false\");\n vp.setAttribute(\"data-overflow-end\", end ? \"true\" : \"false\");\n this.#syncButtons(start, end);\n\n if (!this.#state || this.#state.start !== start || this.#state.end !== end) {\n this.#state = { start, end };\n this.dispatch(\"change\", { detail: { start, end } });\n }\n }\n\n /** Scrolls one viewport page toward the `direction` param (`start`/`end`). */\n scrollByPage(event: Event): void {\n if (!this.hasViewportTarget) return;\n const direction = this.#directionFromEvent(event);\n if (!direction) return;\n if (this.#state && !this.#state[direction]) return;\n\n const vp = this.viewportTarget;\n const horizontal = this.orientationValue !== \"vertical\";\n const page = horizontal ? vp.clientWidth : vp.clientHeight;\n const logicalDelta = direction === \"start\" ? -page : page;\n const delta = physicalScrollDelta(vp, horizontal, logicalDelta);\n const behavior: ScrollBehavior = prefersReducedMotion() ? \"instant\" : \"smooth\";\n\n if (horizontal) {\n vp.scrollBy({ left: delta, behavior });\n } else {\n vp.scrollBy({ top: delta, behavior });\n }\n }\n\n /** Mirrors remaining room onto any direction buttons by toggling `disabled`. */\n #syncButtons(start: boolean, end: boolean): void {\n for (const button of this.#pendingButtonDisables.elements) {\n if (\n !button.isConnected ||\n button.closest(`[data-controller~='${this.identifier}']`) !== this.element\n ) {\n this.#cancelPendingButtonDisable(button);\n }\n }\n const buttons = this.element.querySelectorAll<HTMLButtonElement>(this.#directionButtonSelector);\n for (const button of buttons) {\n if (button.closest(`[data-controller~='${this.identifier}']`) !== this.element) {\n continue;\n }\n const direction = button.getAttribute(this.#directionParam);\n if (direction === \"start\") this.#toggleButton(button, start);\n else if (direction === \"end\") this.#toggleButton(button, end);\n }\n }\n\n /**\n * Reflects the remaining room onto a page button's `disabled`, owning only the\n * `disabled` it sets itself via a marker (`data-overflow-indicator-disabled`,\n * like `number-input`/`conditional-fields`). An author-disabled button (e.g. the\n * whole control disabled) is therefore never blindly re-enabled.\n */\n #toggleButton(button: HTMLButtonElement, hasRoom: boolean): void {\n // A restored Turbo snapshot can carry pending markers from the previous visit\n // that this instance never created; release them from the DOM record before\n // deciding, or the displaced `aria-disabled` would never be given back.\n if (\n !this.#pendingButtonDisables.has(button) &&\n button.hasAttribute(\"data-overflow-indicator-pending-disabled\")\n ) {\n this.#cancelPendingButtonDisable(button);\n }\n if (hasRoom) {\n this.#cancelPendingButtonDisable(button);\n if (button.hasAttribute(\"data-overflow-indicator-disabled\")) {\n button.disabled = false;\n button.removeAttribute(\"data-overflow-indicator-disabled\");\n }\n return;\n }\n if (button.disabled) return; // already disabled (possibly by the author) — leave it\n if (document.activeElement === button) {\n this.#deferButtonDisable(button);\n } else {\n this.#disableButton(button);\n }\n }\n\n /**\n * Keeps a boundary button focusable until native blur, exposing its temporary\n * inoperability with `aria-disabled` and making its action a no-op meanwhile.\n */\n #deferButtonDisable(button: HTMLButtonElement): void {\n if (this.#pendingButtonDisables.has(button)) return;\n button.setAttribute(\"data-overflow-indicator-pending-disabled\", \"\");\n // The ownership marker carries the value we displace, so the DOM alone is\n // enough to undo it. A Turbo snapshot is cloned before `disconnect()` runs,\n // so a restored page arrives with these markers while the new instance has\n // no in-memory record of them — an empty marker means \"there was none\".\n button.setAttribute(\n \"data-overflow-indicator-aria-disabled\",\n button.getAttribute(\"aria-disabled\") ?? \"\",\n );\n button.setAttribute(\"aria-disabled\", \"true\");\n this.#pendingButtonDisables.defer(button);\n }\n\n #disableButton(button: HTMLButtonElement): void {\n button.disabled = true;\n button.setAttribute(\"data-overflow-indicator-disabled\", \"\");\n }\n\n /** Cancels a pending disable without applying it, restoring the displaced state. */\n #cancelPendingButtonDisable(button: HTMLButtonElement): void {\n this.#pendingButtonDisables.release(button);\n this.#restorePendingMarkers(button);\n }\n\n /**\n * Drops the pending marker and gives back the `aria-disabled` it displaced.\n *\n * Reads the displaced value from the DOM rather than memory: a Turbo snapshot is\n * cloned before `disconnect()` runs, so a restored page can arrive with markers\n * this instance never wrote.\n */\n #restorePendingMarkers(button: HTMLButtonElement): void {\n button.removeAttribute(\"data-overflow-indicator-pending-disabled\");\n const displaced = button.getAttribute(\"data-overflow-indicator-aria-disabled\");\n if (displaced !== null) {\n button.removeAttribute(\"data-overflow-indicator-aria-disabled\");\n if (button.getAttribute(\"aria-disabled\") === \"true\") {\n if (displaced === \"\") button.removeAttribute(\"aria-disabled\");\n else button.setAttribute(\"aria-disabled\", displaced);\n }\n }\n }\n\n #clearPendingButtonDisables(): void {\n for (const button of this.#pendingButtonDisables.elements) {\n this.#cancelPendingButtonDisable(button);\n }\n }\n\n /** Moves resize/mutation/load observation to the current viewport target. */\n #syncViewport(): void {\n if (!this.#connected) return;\n const next = this.hasViewportTarget ? this.viewportTarget : null;\n if (next === this.#observedViewport) return;\n this.#stopObservingViewport();\n if (!next) return;\n\n this.#observedViewport = next;\n this.#viewportListeners.add(next, \"scroll\", this.#onScroll, { passive: true });\n this.#layout.observe(next);\n this.#layout.observeViewport();\n this.#layout.observeDescendantLoads(next);\n this.#syncContentObservation();\n\n if (typeof MutationObserver !== \"undefined\") {\n this.#mutationObserver = new MutationObserver(() => {\n if (!this.#connected || this.#observedViewport !== next) return;\n this.#syncContentObservation();\n this.#refresh();\n });\n this.#mutationObserver.observe(this.element, {\n childList: true,\n subtree: true,\n characterData: true,\n attributes: true,\n attributeFilter: [\"class\", \"style\", \"hidden\"],\n });\n }\n this.#state = null;\n this.#refresh();\n }\n\n /** Observes direct content boxes whose resize can change the viewport's scroll extent. */\n #syncContentObservation(): void {\n const next = new Set<Element>(this.#observedViewport?.children ?? []);\n for (const content of this.#observedContent) {\n if (!next.has(content)) {\n this.#layout.unobserve(content);\n this.#observedContent.delete(content);\n }\n }\n for (const content of next) {\n if (!this.#observedContent.has(content)) {\n this.#observedContent.add(content);\n this.#layout.observe(content);\n }\n }\n }\n\n #stopObservingViewport(): void {\n this.#frames.cancel();\n this.#viewportListeners.dispose();\n this.#mutationObserver?.disconnect();\n this.#mutationObserver = null;\n this.#layout.unobserveDescendantLoads();\n if (this.#observedViewport) this.#layout.unobserve(this.#observedViewport);\n for (const content of this.#observedContent) this.#layout.unobserve(content);\n this.#observedContent.clear();\n this.#observedViewport = null;\n this.#layout.unobserveViewport();\n }\n\n get #threshold(): number {\n const value = this.#safeThreshold;\n return Math.max(0, value);\n }\n\n #directionFromEvent(event: Event): \"start\" | \"end\" | null {\n const params = (event as Event & { params?: { direction?: unknown } }).params;\n const direction = params?.direction;\n return direction === \"start\" || direction === \"end\" ? direction : null;\n }\n /** Current `threshold` declaration resolved against its numeric contract. */\n get #safeThreshold(): number {\n return this.#numbers.read(\n this,\n \"threshold\",\n this.thresholdValue,\n OverflowIndicatorController.values.threshold.default,\n OverflowIndicatorController.valueConstraints.threshold,\n );\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/utils/blur_deferral.ts","../../src/utils/frame_coalescer.ts","../../src/utils/layout_observer.ts","../../src/utils/listener_set.ts","../../src/utils/logical_scroll.ts","../../src/utils/microtask_coalescer.ts","../../src/utils/morph_render_watcher.ts","../../src/utils/number_bounds.ts","../../src/utils/coerce.ts","../../src/utils/number_value.ts","../../src/utils/reduced_motion.ts","../../src/controllers/overflow_indicator_controller.ts"],"names":[],"mappings":"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","file":"overflow_indicator_controller.js","sourcesContent":["/**\n * Shared bookkeeping for updates that must wait until an element loses focus.\n *\n * A control that becomes redundant while the user is standing on it cannot be\n * removed from the page immediately: hiding a focused element (or turning it\n * into a native `disabled` one) drops focus to `<body>`, stranding a keyboard\n * user mid-widget. Every affected controller handles it the same way — keep the\n * element reachable, hold the destructive update, listen for `blur`, and apply\n * it then.\n *\n * {@link BlurDeferral} owns *only* the registry part of that: which elements are\n * currently holding an update back, attaching and detaching the `blur` listener,\n * and guaranteeing teardown. It is deliberately **policy-free** — what the\n * interim state looks like (`aria-disabled`, an ownership marker, nothing at\n * all), and what the deferred update actually is, stay in the controller, which\n * receives the element back through its release callback.\n *\n * Three contracts are worth stating up front, because each one is a hazard the\n * consumers would otherwise hit:\n *\n * - **The release callback fires only on a real `blur`.** `release()` /\n * `releaseAll()` *cancel* a deferral; they do not complete it. Conflating the\n * two makes `overflow-indicator` disable a button that just left the boundary.\n * - **`elements` is a snapshot.** Iterating the live key set while releasing\n * inside the loop is the failure this registry exists to prevent.\n * - **The entry is detached before the callback runs**, so a controller that\n * decides the update is still unsafe can simply defer again.\n *\n * @example\n * ```ts\n * readonly #deferred = new BlurDeferral<HTMLElement>((trigger) => {\n * if (this.#connected) this.#evaluate();\n * });\n *\n * #hide(trigger: HTMLElement) {\n * if (document.activeElement === trigger) {\n * this.#deferred.deferOnly(trigger); // stays visible until blur\n * return;\n * }\n * this.#deferred.releaseAll();\n * trigger.hidden = true;\n * }\n *\n * disconnect() {\n * this.#deferred.releaseAll();\n * }\n * ```\n */\nexport class BlurDeferral<T extends HTMLElement = HTMLElement> {\n /** Elements currently holding an update back, mapped to their `blur` listener. */\n readonly #pending = new Map<T, () => void>();\n /** Called after a pending element blurs and has been detached. */\n readonly #onRelease: (element: T) => void;\n\n /** @param onRelease - Invoked once `element` actually blurs; never on `release`. */\n constructor(onRelease: (element: T) => void) {\n this.#onRelease = onRelease;\n }\n\n /** Number of elements currently holding an update back. */\n get size(): number {\n return this.#pending.size;\n }\n\n /** Snapshot of the pending elements, safe to iterate while releasing them. */\n get elements(): T[] {\n return [...this.#pending.keys()];\n }\n\n /** Whether `element` is currently holding an update back. */\n has(element: T): boolean {\n return this.#pending.has(element);\n }\n\n /** Holds an update back until `element` blurs. Idempotent (no stacked listeners). */\n defer(element: T): void {\n if (this.#pending.has(element)) return;\n const onBlur = (): void => {\n this.#detach(element);\n this.#onRelease(element);\n };\n this.#pending.set(element, onBlur);\n element.addEventListener(\"blur\", onBlur);\n }\n\n /** Defers `element` as the only pending entry, cancelling any others. */\n deferOnly(element: T): void {\n for (const pending of this.elements) {\n if (pending !== element) this.#detach(pending);\n }\n this.defer(element);\n }\n\n /** Cancels `element`'s deferral without completing it; no-ops when not pending. */\n release(element: T): void {\n this.#detach(element);\n }\n\n /** Cancels every deferral without completing any of them. */\n releaseAll(): void {\n for (const element of this.elements) this.#detach(element);\n }\n\n /** Removes the `blur` listener for `element` and forgets it. */\n #detach(element: T): void {\n const onBlur = this.#pending.get(element);\n if (onBlur) element.removeEventListener(\"blur\", onBlur);\n this.#pending.delete(element);\n }\n}\n","/**\n * Runs one piece of scroll-driven work per animation frame.\n *\n * A `scroll` listener fires many times between two paints, and each firing would\n * measure the same layout. Requesting a frame on the first firing and ignoring\n * the rest until it runs keeps the reads to one per frame; running on the frame\n * — rather than on a timer — keeps the measurement in step with what the reader\n * sees.\n *\n * **The first request of a burst wins.** Work handed in while a frame is already\n * pending is dropped, so a caller that queues a special first pass and then\n * receives scrolls gets that first pass, not the last scroll's.\n *\n * Cancelling drops the pending frame outright, and does nothing at all when none\n * is pending. A frame really is cancelled, so nothing needs a generation to tell\n * a stale callback from a fresh one, and nothing has to be refused before the\n * caller starts listening.\n *\n * Scope is the scheduling only. What to measure stays with the caller.\n *\n * @example\n * ```ts\n * readonly #frames = new FrameCoalescer();\n *\n * readonly #onScroll = (): void => this.#frames.schedule(() => this.#measure());\n *\n * disconnect(): void {\n * this.#frames.cancel();\n * }\n * ```\n */\nexport class FrameCoalescer {\n #frame: number | null = null;\n\n /**\n * Runs `run` on the next frame, unless a frame is already pending — the first\n * request of a burst wins and the rest are dropped. The pending frame is\n * released before `run`, so `run` may request the next one.\n */\n schedule(run: () => void): void {\n if (this.#frame !== null) return;\n this.#frame = requestAnimationFrame(() => {\n this.#frame = null;\n run();\n });\n }\n\n /**\n * Drops the pending frame, and reaches the platform only when there is one.\n *\n * There is no handle value that stands for \"nothing pending\":\n * `cancelAnimationFrame` takes an `unsigned long`, so a negative placeholder\n * arrives as a large positive number that the same allocator can hand out, and\n * an idle cancel would then drop a frame belonging to someone else.\n */\n cancel(): void {\n if (this.#frame === null) return;\n cancelAnimationFrame(this.#frame);\n this.#frame = null;\n }\n}\n","/**\n * Unified layout observation for Stimeo controllers.\n *\n * Widgets whose output is measured — an overflow boundary, a masonry column count,\n * an autosized textarea — have three sources that move the layout under them: their\n * *own* box changing — via {@link ResizeObserver} — the *viewport* changing — via the\n * `window` `resize` event — and a descendant *resource settling*, because an image or\n * a frame reports a height of zero until it has loaded. Wiring those three by hand in\n * every controller risks leaked listeners on `disconnect()`. {@link LayoutObserver}\n * owns all three behind one callback and one\n * {@link LayoutObserver.disconnect | disconnect()} that releases everything.\n *\n * Behavior only: the helper reports *that* layout changed; it never reads or\n * writes styles. Consumers decide what to recompute.\n */\n\n/** Invoked whenever an observed element or the viewport changes size. */\nexport type LayoutCallback = () => void;\n\n/** Constructs a {@link ResizeObserver}; injectable so tests stay deterministic. */\nexport type ResizeObserverFactory = (callback: ResizeObserverCallback) => ResizeObserver;\n\n/** Options for {@link LayoutObserver}. */\nexport interface LayoutObserverOptions {\n /**\n * Factory for the {@link ResizeObserver} used by {@link LayoutObserver.observe}.\n * Defaults to the global constructor; override it in tests, or to no-op in\n * environments where `ResizeObserver` is unavailable.\n */\n resizeObserverFactory?: ResizeObserverFactory;\n}\n\n/**\n * Observes element resizes and/or viewport resizes through a single callback,\n * with guaranteed teardown.\n *\n * @example\n * ```ts\n * #layout = new LayoutObserver(() => this.#reposition());\n *\n * connect() {\n * this.#layout.observe(this.panelTarget);\n * this.#layout.observeViewport();\n * this.#layout.observeDescendantLoads(this.panelTarget);\n * }\n *\n * disconnect() {\n * this.#layout.disconnect();\n * }\n * ```\n */\nexport class LayoutObserver {\n readonly #callback: LayoutCallback;\n readonly #resizeObserverFactory: ResizeObserverFactory | null;\n #resizeObserver: ResizeObserver | null = null;\n #observingViewport = false;\n #loadContainer: Element | null = null;\n\n /** Stable bound handler so add/removeEventListener target the same reference. */\n readonly #handleViewportResize = (): void => {\n this.#callback();\n };\n\n /** Stable bound handler for the capture-phase `load`; see {@link observeDescendantLoads}. */\n readonly #handleDescendantLoad = (): void => {\n this.#callback();\n };\n\n constructor(callback: LayoutCallback, options: LayoutObserverOptions = {}) {\n this.#callback = callback;\n this.#resizeObserverFactory =\n options.resizeObserverFactory ??\n (typeof ResizeObserver === \"undefined\" ? null : (cb) => new ResizeObserver(cb));\n }\n\n /**\n * Starts observing an element's size. Repeated calls observe additional\n * elements through the same shared observer. No-ops when no\n * `ResizeObserver` implementation is available.\n */\n observe(element: Element): void {\n if (!this.#resizeObserverFactory) return;\n if (!this.#resizeObserver) {\n this.#resizeObserver = this.#resizeObserverFactory(() => {\n this.#callback();\n });\n }\n this.#resizeObserver.observe(element);\n }\n\n /** Stops observing a single element while leaving any others in place. */\n unobserve(element: Element): void {\n this.#resizeObserver?.unobserve(element);\n }\n\n /** Starts observing viewport resizes. Idempotent: the listener is added once. */\n observeViewport(): void {\n if (this.#observingViewport) return;\n this.#observingViewport = true;\n window.addEventListener(\"resize\", this.#handleViewportResize);\n }\n\n /** Stops observing viewport resizes without affecting element observation. */\n unobserveViewport(): void {\n if (!this.#observingViewport) return;\n this.#observingViewport = false;\n window.removeEventListener(\"resize\", this.#handleViewportResize);\n }\n\n /**\n * Starts reporting a `load` from anywhere inside `container` — an image or a\n * frame settling changes the box it sits in, and it measures as zero high until\n * then. `load` does not bubble, so the subscription is a capture-phase listener\n * on the container itself and nothing the caller spells.\n *\n * **One container at a time.** A further call moves the observation, so a widget\n * whose content element is swapped at runtime releases the element it let go by\n * naming the new one — there is no second place for the release to drift from.\n */\n observeDescendantLoads(container: Element): void {\n this.unobserveDescendantLoads();\n this.#loadContainer = container;\n container.addEventListener(\"load\", this.#handleDescendantLoad, true);\n }\n\n /** Stops reporting descendant loads without affecting element or viewport observation. */\n unobserveDescendantLoads(): void {\n this.#loadContainer?.removeEventListener(\"load\", this.#handleDescendantLoad, true);\n this.#loadContainer = null;\n }\n\n /**\n * Releases every observation: disconnects the {@link ResizeObserver} and removes\n * the viewport and descendant-load listeners. Safe to call multiple times. Call\n * this from a controller's `disconnect()`.\n */\n disconnect(): void {\n this.#resizeObserver?.disconnect();\n this.#resizeObserver = null;\n this.unobserveViewport();\n this.unobserveDescendantLoads();\n }\n}\n","/** What {@link ListenerSet.add} passes on besides the signal it supplies itself. */\nexport type ListenerOptions = Omit<AddEventListenerOptions, \"signal\">;\n\n/**\n * The event map a target's own `addEventListener` types its handlers with: the\n * window's, a document's, an HTML or SVG element's, any other element's, or a\n * media query list's. Other targets name none.\n */\ntype EventMapOf<T extends EventTarget> = T extends Window\n ? WindowEventMap\n : T extends Document\n ? DocumentEventMap\n : T extends HTMLElement\n ? HTMLElementEventMap\n : T extends SVGElement\n ? SVGElementEventMap\n : T extends Element\n ? ElementEventMap\n : T extends MediaQueryList\n ? MediaQueryListEventMap\n : Record<never, never>;\n\n/**\n * Owns every listener a controller attaches for one connected lifetime and\n * releases them together, so an event name is spelled once and the release\n * cannot drift from the registration.\n *\n * Detaching by hand repeats three things at the release site — the type, the\n * handler reference and the capture flag — and any one of them drifting leaves\n * a listener alive on an element the controller no longer drives. Aborting the\n * signal a registration was created with releases it whatever it was made of,\n * so the release site names nothing.\n *\n * One set per lifetime, held as a field and never rebuilt.\n * {@link ListenerSet.dispose} is the only thing that closes a generation, and it opens\n * the next one in the same step, so the lifecycle hooks can call `add` and\n * `dispose` in any order any number of times, and no `add` ever meets an\n * already aborted signal — a DOM that registers a listener on such a signal\n * would keep it forever. Rebuilding the set instead would leak:\n * `addEventListener` matches an existing registration on target, type, callback\n * and capture alone, so re-adding the same handler while the first registration\n * is live is discarded, and that live registration keeps the signal it was\n * attached with — a set built by a second `connect()` can never release what it\n * appears to own.\n *\n * Scope is the registration only. Timers, observers, leases and the order they\n * are unwound in stay with the controller, which places `dispose()` in its\n * cleanup sequence. A receiver that changes at runtime (a\n * swapped target) is rebound by calling `dispose()` and then `add()` again from\n * the rebind, not by a second set.\n *\n * A lifetime shorter than the connection — while a Value holds, until a first\n * load, while a subscription is open — gets a set of its own, which the end of\n * that lifetime disposes. A controller therefore holds one set per lifetime: the\n * connection's, and one for each swapped receiver or shorter span.\n *\n * The set must own every registration of a tuple it holds: a listener the same\n * target already carries for the same type, callback and capture flag keeps the\n * right to remove it, and this set's `add` for that tuple is discarded. A\n * subscription shared between instances behind a participant count, listeners of\n * several elements that come and go one at a time, and those one gesture holds\n * keep their own pair of calls or a signal of their own.\n *\n * @example\n * ```ts\n * readonly #listeners = new ListenerSet();\n *\n * connect(): void {\n * this.#listeners.add(this.element, \"submit\", this.#onNativeSubmit, { capture: true });\n * this.#listeners.add(this.element, \"turbo:submit-start\", this.#onSubmitStart);\n * this.#listeners.add(this.element, \"turbo:submit-end\", this.#onSubmitEnd);\n * }\n *\n * disconnect(): void {\n * this.#listeners.dispose();\n * }\n * ```\n */\nexport class ListenerSet {\n /** The generation every `add` joins until the next `dispose()`. */\n #abort = new AbortController();\n\n /**\n * Attaches `handler` to the open generation, exactly as the caller spelled it.\n *\n * The set supplies the signal, so `options` carries everything else the DOM\n * accepts — `capture` included. A removal by hand would have to repeat that\n * flag; aborting the signal releases the listener without it.\n *\n * A `type` the target's own event map names types `handler` with that event,\n * as the target's `addEventListener` does, so a handler written for a\n * `KeyboardEvent` is passed as it is; any other `type` takes an\n * `EventListener`.\n */\n add<T extends EventTarget, K extends keyof EventMapOf<T> & string>(\n target: T,\n type: K,\n handler: (event: EventMapOf<T>[K]) => void,\n options?: ListenerOptions,\n ): void;\n add(target: EventTarget, type: string, handler: EventListener, options?: ListenerOptions): void;\n add(target: EventTarget, type: string, handler: EventListener, options?: ListenerOptions): void {\n target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });\n }\n\n /**\n * Releases every listener of the open generation, synchronously, and opens the\n * next one. Idempotent, and safe before anything has been added.\n */\n dispose(): void {\n this.#abort.abort();\n this.#abort = new AbortController();\n }\n}\n","/** Normalized scroll position and maximum distance on one logical axis. */\nexport interface LogicalScrollMetrics {\n position: number;\n max: number;\n}\n\n/**\n * Whether horizontal scrolling on `element` follows right-to-left inline flow.\n *\n * Resolved from the **computed** `direction`, so the authoring contract is the\n * usual `dir=\"rtl\"` (or a stylesheet) on the element or any ancestor.\n *\n * Scope: horizontal writing modes. A vertical writing mode (`writing-mode:\n * vertical-rl`) also inverts the horizontal axis, which this check does not\n * model — vertical writing modes are out of scope for the scroll utilities\n * (their consumers describe axes as horizontal/vertical, not inline/block).\n */\nexport function isRtl(element: Element): boolean {\n return window.getComputedStyle(element).direction === \"rtl\";\n}\n\n/**\n * Returns scroll distance from the logical start edge.\n *\n * CSSOM View exposes standards-mode RTL horizontal offsets as `0` at the inline\n * start (right) and increasingly negative values toward the inline end (left).\n * The normalized position is always clamped to `[0, max]`, which also absorbs\n * Safari's elastic overscroll values.\n */\nexport function logicalScrollMetrics(\n element: HTMLElement,\n horizontal: boolean,\n): LogicalScrollMetrics {\n const max = Math.max(\n 0,\n horizontal\n ? element.scrollWidth - element.clientWidth\n : element.scrollHeight - element.clientHeight,\n );\n const raw = horizontal ? element.scrollLeft : element.scrollTop;\n const position = horizontal && isRtl(element) ? -raw : raw;\n return { position: Math.min(max, Math.max(0, position)), max };\n}\n\n/**\n * Converts a logical start/end delta to the physical value accepted by\n * `Element.scrollBy`.\n */\nexport function physicalScrollDelta(\n element: HTMLElement,\n horizontal: boolean,\n logicalDelta: number,\n): number {\n return horizontal && isRtl(element) ? -logicalDelta : logicalDelta;\n}\n","/**\n * Collapses many Stimulus lifecycle callbacks from one DOM mutation into a\n * single pass.\n *\n * Stimulus fires `<name>TargetConnected` / `Disconnected` once per element and\n * `<name>ValueChanged` once per changed attribute. Replacing a list of N options\n * or morphing several render Values therefore delivers N callbacks — but the\n * useful unit of work is \"reconcile against the resulting declarative input\",\n * once, after the batch has settled. Every controller with reconcilable targets\n * or render Values needs the same shape: a `queued` flag plus `queueMicrotask`.\n *\n * **A microtask is the right horizon, and the reason is specific.** Stimulus\n * drives these callbacks from a `MutationObserver`, whose own callback already\n * runs as a microtask with the whole batch in hand; scheduling one more lands\n * after the last sibling callback of that batch and still before paint or any\n * event handler. A timer would be later than it needs to be, and reconciling\n * synchronously would run once per element against a half-applied DOM.\n *\n * **The two guards are not the same guard.** Scheduling is refused before the\n * controller connects, and running is refused after it disconnects:\n *\n * - **Before `connect()`** — Stimulus delivers initial target and Value callbacks\n * ahead of `connect()`. Reconciling there would compute output against a\n * controller whose own state has not been initialised, and `connect()` is\n * about to do a full pass anyway.\n * - **After `disconnect()`** — Stimulus fires a callback for **every** target\n * during teardown, and a microtask queued just before it would otherwise run\n * against a detached tree. {@link MicrotaskCoalescer.cancel} exists for the\n * teardown path to drop the pending pass outright.\n *\n * Both guards are part of one contract here rather than something each consumer\n * has to remember separately.\n *\n * Scope is the scheduling only. *What* to reconcile — keep the surviving active\n * option, fall back to the next / previous / first visible one, rebuild derived\n * chips or hidden fields — stays in the controller, because no two consumers\n * answer it the same way.\n *\n * @example\n * ```ts\n * readonly #reconcile = new MicrotaskCoalescer(() => this.#reconcileOptions());\n *\n * connect() { this.#reconcile.activate(); }\n * disconnect() { this.#reconcile.cancel(); }\n *\n * optionTargetConnected() { this.#reconcile.schedule(); }\n * optionTargetDisconnected() { this.#reconcile.schedule(); }\n * ```\n */\nexport class MicrotaskCoalescer {\n readonly #run: () => void;\n #queued = false;\n #active = false;\n #generation = 0;\n\n /** @param run - the single reconciliation pass, invoked at most once per batch. */\n constructor(run: () => void) {\n this.#run = run;\n }\n\n /** Opens the window in which {@link schedule} is honoured; call from `connect()`. */\n activate(): void {\n this.#active = true;\n }\n\n /** Closes the window and drops any pending pass; call from `disconnect()`. */\n cancel(): void {\n this.#active = false;\n this.#queued = false;\n this.#generation += 1;\n }\n\n /** Whether a requested pass has yet to run. */\n get pending(): boolean {\n return this.#queued;\n }\n\n /** Requests one pass after the batch settles. Idempotent; inert outside the window. */\n schedule(): void {\n if (!this.#active || this.#queued) return;\n this.#queued = true;\n const generation = this.#generation;\n queueMicrotask(() => {\n // A cancelled callback must not consume a pass queued after reconnect.\n if (generation !== this.#generation || !this.#queued || !this.#active) return;\n this.#queued = false;\n this.#run();\n });\n }\n}\n","import { ListenerSet } from \"./listener_set\";\nimport { MicrotaskCoalescer } from \"./microtask_coalescer\";\n\n/**\n * Coalesces retained-element morphs and explicit lifecycle requests into one pass.\n *\n * Observe the controller's element after its initial state is settled. Morphs on\n * that element and bubbling morphs on its descendants share the same microtask\n * as {@link schedule}. The callback repairs derived output; the controller owns\n * any logical transition reporting. {@link disconnect} releases the listener and\n * cancels pending work, including work from an earlier connected lifetime.\n */\nexport class MorphRenderWatcher {\n readonly #listeners = new ListenerSet();\n readonly #pass: MicrotaskCoalescer;\n #element: Element | null = null;\n readonly #onMorph = (): void => this.#pass.schedule();\n\n /** @param run - the single pass that repairs the settled controller's output. */\n constructor(run: () => void) {\n this.#pass = new MicrotaskCoalescer(run);\n }\n\n /** Opens observation; observing the same element again preserves queued work. */\n observe(element: Element): void {\n if (this.#element === element) return;\n this.disconnect();\n this.#element = element;\n this.#pass.activate();\n this.#listeners.add(element, \"turbo:morph-element\", this.#onMorph);\n }\n\n /** Requests one pass; requests outside an observed lifetime are ignored. */\n schedule(): void {\n this.#pass.schedule();\n }\n\n /** Whether a pass requested by a morph or {@link schedule} has yet to run. */\n get pending(): boolean {\n return this.#pass.pending;\n }\n\n /** Releases observation and pending work synchronously; safe to repeat. */\n disconnect(): void {\n this.#listeners.dispose();\n this.#pass.cancel();\n this.#element = null;\n }\n}\n","import { MAX_TIMER_DELAY_MS } from \"./timer_bounds\";\n\n/** Semantic constraints on an already decoded number, independent of the DOM. */\nexport interface NumberBounds {\n /** Finite numbers are required, including when this field is omitted. */\n readonly finite?: true;\n /** Inclusive lower endpoint. */\n readonly min?: number;\n /** Inclusive upper endpoint. */\n readonly max?: number;\n /** Exclusive lower endpoint. */\n readonly exclusiveMin?: number;\n /** Require a number without a fractional part. */\n readonly integer?: boolean;\n /** Exact decoded numbers accepted by a discrete contract. */\n readonly allowedValues?: readonly number[];\n /** JSON-safe exceptions to finite-number checking for unbounded endpoints. */\n readonly allowInfinity?: \"negative\" | \"positive\" | \"both\";\n}\n\n/** One constraint for each Number Value, including shorthand declarations. */\nexport type NumberValueConstraints<Values> = {\n readonly [Key in keyof Values as Values[Key] extends\n | NumberConstructor\n | { type: NumberConstructor }\n ? Key\n : never]: NumberBounds;\n};\n\n/** Reusable semantic domains; fractional inputs remain fractional unless excluded. */\nexport const NUMBER_BOUNDS = {\n finite: { finite: true },\n nonNegative: { finite: true, min: 0 },\n positive: { finite: true, exclusiveMin: 0 },\n nonNegativeInteger: { finite: true, min: 0, integer: true },\n positiveInteger: { finite: true, exclusiveMin: 0, integer: true },\n lowerBound: { finite: true, allowInfinity: \"negative\" },\n upperBound: { finite: true, allowInfinity: \"positive\" },\n timer: { finite: true, min: 0, max: MAX_TIMER_DELAY_MS },\n positiveTimer: { finite: true, exclusiveMin: 0, max: MAX_TIMER_DELAY_MS },\n} as const satisfies Record<string, NumberBounds>;\n\n/**\n * Checks all declared bounds without rounding, clamping, or coercing the input.\n * Infinity exceptions relax only finite checking; every other bound still applies.\n */\nexport function matchesNumberBounds(value: number, bounds: NumberBounds): boolean {\n if (!Number.isFinite(value)) {\n const direction = value === Infinity ? \"positive\" : value === -Infinity ? \"negative\" : null;\n if (direction === null) return false;\n if (bounds.allowInfinity !== \"both\" && bounds.allowInfinity !== direction) return false;\n }\n if (bounds.min !== undefined && value < bounds.min) return false;\n if (bounds.max !== undefined && value > bounds.max) return false;\n if (bounds.exclusiveMin !== undefined && value <= bounds.exclusiveMin) return false;\n if (bounds.integer && !Number.isInteger(value)) return false;\n if (bounds.allowedValues !== undefined && !bounds.allowedValues.includes(value)) return false;\n return true;\n}\n\n/** Decodes a Number Value literal; action params use their own JSON decoder. */\nexport function decodeNumberValue(raw: string): number {\n return Number(raw.replace(/_/g, \"\"));\n}\n","/**\n * Numeric coercion for controllers that accept a number through a Value, an action\n * param, or an event detail.\n *\n * Stimulus decodes action params as JSON when possible and leaves other text\n * untouched. CustomEvent detail can also supply a number or numeric string.\n * The reader accepts those two types while rejecting blank and non-finite input.\n */\n\nimport { matchesNumberBounds, type NumberBounds } from \"./number_bounds\";\n\n/** Numeric update input, accepting decoded action params before CustomEvent detail. */\nexport type NumericValueEvent = Event & {\n params?: { amount?: unknown };\n detail?: { value?: unknown };\n};\n\n/** Reads the nullish action-param/detail precedence without falling back from invalid input. */\nexport function eventNumber(event: NumericValueEvent): number | null {\n return toFiniteNumber(event.params?.amount ?? event.detail?.value);\n}\n\n/**\n * Reads a finite number or nonblank numeric string; other input returns `null`.\n * Blank strings cannot silently reset a value to zero.\n */\nexport function toFiniteNumber(raw: unknown): number | null {\n if (typeof raw !== \"number\" && typeof raw !== \"string\") return null;\n if (typeof raw === \"string\" && raw.trim().length === 0) return null;\n const value = Number(raw);\n return Number.isFinite(value) ? value : null;\n}\n\n/**\n * Returns an accepted decoded Number Value or the caller's fallback unchanged.\n * The caller owns the fallback contract; this reader does not parse or write a Value.\n */\nexport function readNumber(raw: number, fallback: number, bounds: NumberBounds): number {\n return matchesNumberBounds(raw, bounds) ? raw : fallback;\n}\n","import { readNumber } from \"./coerce\";\nimport { matchesNumberBounds, type NumberBounds } from \"./number_bounds\";\n\n/** The instance identity carried by a Number Value read. */\nexport interface NumberValueOwner {\n readonly identifier: string;\n readonly element: Element;\n}\n\n/** Reads current declarations at the point of use without rewriting their attributes. */\nexport class NumberValueReader {\n /** The last rejected literal per Value; no history grows for an unchanged instance. */\n readonly #lastRejected = new Map<string, string>();\n\n /** Resolves the declared number using its own fallback and shared class contract. */\n read(\n owner: NumberValueOwner,\n name: string,\n raw: number,\n fallback: number,\n bounds: NumberBounds,\n ): number {\n const resolved = readNumber(raw, fallback, bounds);\n if (matchesNumberBounds(raw, bounds)) {\n this.#lastRejected.delete(name);\n return resolved;\n }\n const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;\n const literal = owner.element.getAttribute(attribute);\n if (literal === null) {\n this.#lastRejected.delete(name);\n return resolved;\n }\n if (this.#lastRejected.get(name) !== literal) {\n this.#lastRejected.set(name, literal);\n console.warn(\n `Stimeo UI: \"${owner.identifier}\" has an invalid number Value \"${name}\" declaration ${JSON.stringify(literal)}; using ${fallback}.`,\n );\n }\n return resolved;\n }\n}\n","/**\n * Shared `prefers-reduced-motion` lookup for the motion-aware controllers.\n *\n * This one-liner keeps the media query string and the environment guard\n * single-sourced across them. The preference is intentionally re-read on every\n * call — the controllers check it at each animation/scroll start (WCAG 2.2\n * **2.3.3**), so flipping the OS setting takes effect immediately without any\n * listener or cache bookkeeping here.\n */\n\n/**\n * Whether the user currently requests reduced motion.\n *\n * @returns `true` when `(prefers-reduced-motion: reduce)` matches; `false`\n * otherwise, including environments without `window.matchMedia` (treated as\n * \"no preference\").\n */\nexport function prefersReducedMotion(): boolean {\n return (\n typeof window.matchMedia === \"function\" &&\n window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n );\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { BlurDeferral } from \"../utils/blur_deferral\";\nimport { FrameCoalescer } from \"../utils/frame_coalescer\";\nimport { LayoutObserver } from \"../utils/layout_observer\";\nimport { ListenerSet } from \"../utils/listener_set\";\nimport { logicalScrollMetrics, physicalScrollDelta } from \"../utils/logical_scroll\";\nimport { MorphRenderWatcher } from \"../utils/morph_render_watcher\";\nimport { NUMBER_BOUNDS, type NumberValueConstraints } from \"../utils/number_bounds\";\nimport { NumberValueReader } from \"../utils/number_value\";\nimport { prefersReducedMotion } from \"../utils/reduced_motion\";\n\nconst DIRECTION_PARAM = \"direction-param\";\n\n/**\n * Headless **Overflow Indicator**: detects whether a scroll container can still\n * scroll toward its start and/or end and publishes it as `data-overflow-start` /\n * `data-overflow-end`. No APG widget — a state-detection utility. Consumers draw\n * edge shadows or arrows in CSS to signal \"more content this way\".\n *\n * Markup contract (identifier: `stimeo--overflow-indicator`):\n * <div data-controller=\"stimeo--overflow-indicator\"\n * data-stimeo--overflow-indicator-orientation-value=\"horizontal\">\n * <button type=\"button\" aria-label=\"Prev\"\n * data-stimeo--overflow-indicator-direction-param=\"start\"\n * data-action=\"click->stimeo--overflow-indicator#scrollByPage\">‹</button>\n * <div data-stimeo--overflow-indicator-target=\"viewport\"\n * tabindex=\"0\" role=\"region\" aria-label=\"Products\"\n * style=\"overflow-x: auto;\"><!-- items --></div>\n * <button type=\"button\" aria-label=\"Next\"\n * data-stimeo--overflow-indicator-direction-param=\"end\"\n * data-action=\"click->stimeo--overflow-indicator#scrollByPage\">›</button>\n * </div>\n *\n * The viewport's scroll position and size are watched by the controller itself:\n * a passive `scroll` listener on the viewport, coalesced to one measurement per\n * animation frame, plus `LayoutObserver` for viewport/content resize, a\n * {@link MutationObserver}, and descendant load events. Nothing about the\n * measurement is the consumer's to wire. Optional page buttons scroll one logical\n * viewport page at a time (including RTL) and have their disabled state synced to\n * the matching direction's remaining room.\n *\n * `change` dispatches `{ start: boolean, end: boolean }`.\n *\n * @remarks\n * Behavior only — shadows, arrows, and gradients are the consumer's CSS;\n * `data-overflow-*` carry no ARIA semantics. All observers/listeners are released\n * on `disconnect()` (Turbo navigation included). `scrollByPage` scrolls smoothly\n * by default and forces an instant jump under `prefers-reduced-motion`, regardless\n * of the consumer's CSS `scroll-behavior`.\n */\nexport class OverflowIndicatorController extends Controller<HTMLElement> {\n /** Numeric read boundaries share one reader for this controller instance. */\n readonly #numbers = new NumberValueReader();\n\n /** The direction param above, in the namespace this controller is registered under. */\n get #directionParam(): string {\n return `data-${this.identifier}-${DIRECTION_PARAM}`;\n }\n\n /** Selects every button carrying that param. */\n get #directionButtonSelector(): string {\n return `[${this.#directionParam}]`;\n }\n\n static override targets = [\"viewport\"];\n static override values = {\n orientation: { type: String, default: \"horizontal\" },\n threshold: { type: Number, default: 1 },\n };\n\n static valueConstraints = {\n threshold: NUMBER_BOUNDS.finite,\n } satisfies NumberValueConstraints<typeof OverflowIndicatorController.values>;\n static actions = [\"scrollByPage\"] as const;\n static events = [\"change\"] as const;\n\n declare readonly viewportTarget: HTMLElement;\n declare readonly hasViewportTarget: boolean;\n\n declare orientationValue: string;\n declare thresholdValue: number;\n\n readonly #layout = new LayoutObserver(() => {\n if (this.#connected) this.#refresh();\n });\n /** Folds a burst of scrolls into one measurement per frame. */\n readonly #frames = new FrameCoalescer();\n /** Holds the viewport's scroll listener for exactly as long as that viewport. */\n readonly #viewportListeners = new ListenerSet();\n readonly #onScroll = (): void => this.#frames.schedule(() => this.#refresh());\n #connected = false;\n #observedViewport: HTMLElement | null = null;\n readonly #observedContent = new Set<Element>();\n #mutationObserver: MutationObserver | null = null;\n /**\n * Boundary buttons whose native `disabled` is held back while they hold focus.\n *\n * Completing the deferral is what actually disables the button, so the release\n * callback — unlike {@link #cancelPendingButtonDisable} — applies it.\n */\n readonly #pendingButtonDisables = new BlurDeferral<HTMLButtonElement>((button) => {\n this.#restorePendingMarkers(button);\n // If the author disabled it while focus was pending, do not claim ownership.\n if (!button.disabled) this.#disableButton(button);\n });\n /** Last reported room, so `change` fires only on transitions. */\n #state: { start: boolean; end: boolean } | null = null;\n\n readonly #morphRender = new MorphRenderWatcher(() => this.#refresh());\n\n override connect(): void {\n this.#morphRender.observe(this.element);\n this.#connected = true;\n this.#syncViewport();\n }\n\n override disconnect(): void {\n this.#morphRender.disconnect();\n this.#connected = false;\n this.#stopObservingViewport();\n this.#layout.disconnect();\n this.#clearPendingButtonDisables();\n this.#state = null;\n }\n\n viewportTargetConnected(): void {\n this.#syncViewport();\n }\n\n viewportTargetDisconnected(viewport: HTMLElement): void {\n if (this.#observedViewport === viewport) this.#stopObservingViewport();\n this.#syncViewport();\n }\n\n orientationValueChanged(): void {\n if (this.#connected) this.#refresh();\n }\n\n thresholdValueChanged(): void {\n if (this.#connected) this.#refresh();\n }\n\n /**\n * Re-measures remaining scroll room and reflects the state hooks.\n *\n * @stimeoRenderRoot\n */\n #refresh(): void {\n if (!this.hasViewportTarget) return;\n const vp = this.viewportTarget;\n const horizontal = this.orientationValue !== \"vertical\";\n const t = this.#threshold;\n const { position: scrollPos, max: maxScroll } = logicalScrollMetrics(vp, horizontal);\n\n const start = scrollPos > t;\n const end = scrollPos < maxScroll - t;\n\n vp.setAttribute(\"data-overflow-start\", start ? \"true\" : \"false\");\n vp.setAttribute(\"data-overflow-end\", end ? \"true\" : \"false\");\n this.#syncButtons(start, end);\n\n if (!this.#state || this.#state.start !== start || this.#state.end !== end) {\n this.#state = { start, end };\n this.dispatch(\"change\", { detail: { start, end } });\n }\n }\n\n /** Scrolls one viewport page toward the `direction` param (`start`/`end`). */\n scrollByPage(event: Event): void {\n if (!this.hasViewportTarget) return;\n const direction = this.#directionFromEvent(event);\n if (!direction) return;\n if (this.#state && !this.#state[direction]) return;\n\n const vp = this.viewportTarget;\n const horizontal = this.orientationValue !== \"vertical\";\n const page = horizontal ? vp.clientWidth : vp.clientHeight;\n const logicalDelta = direction === \"start\" ? -page : page;\n const delta = physicalScrollDelta(vp, horizontal, logicalDelta);\n const behavior: ScrollBehavior = prefersReducedMotion() ? \"instant\" : \"smooth\";\n\n if (horizontal) {\n vp.scrollBy({ left: delta, behavior });\n } else {\n vp.scrollBy({ top: delta, behavior });\n }\n }\n\n /** Mirrors remaining room onto any direction buttons by toggling `disabled`. */\n #syncButtons(start: boolean, end: boolean): void {\n for (const button of this.#pendingButtonDisables.elements) {\n if (\n !button.isConnected ||\n button.closest(`[data-controller~='${this.identifier}']`) !== this.element\n ) {\n this.#cancelPendingButtonDisable(button);\n }\n }\n const buttons = this.element.querySelectorAll<HTMLButtonElement>(this.#directionButtonSelector);\n for (const button of buttons) {\n if (button.closest(`[data-controller~='${this.identifier}']`) !== this.element) {\n continue;\n }\n const direction = button.getAttribute(this.#directionParam);\n if (direction === \"start\") this.#toggleButton(button, start);\n else if (direction === \"end\") this.#toggleButton(button, end);\n }\n }\n\n /**\n * Reflects the remaining room onto a page button's `disabled`, owning only the\n * `disabled` it sets itself via a marker (`data-overflow-indicator-disabled`,\n * like `number-input`/`conditional-fields`). An author-disabled button (e.g. the\n * whole control disabled) is therefore never blindly re-enabled.\n */\n #toggleButton(button: HTMLButtonElement, hasRoom: boolean): void {\n // A restored Turbo snapshot can carry pending markers from the previous visit\n // that this instance never created; release them from the DOM record before\n // deciding, or the displaced `aria-disabled` would never be given back.\n if (\n !this.#pendingButtonDisables.has(button) &&\n button.hasAttribute(\"data-overflow-indicator-pending-disabled\")\n ) {\n this.#cancelPendingButtonDisable(button);\n }\n if (hasRoom) {\n this.#cancelPendingButtonDisable(button);\n if (button.hasAttribute(\"data-overflow-indicator-disabled\")) {\n button.disabled = false;\n button.removeAttribute(\"data-overflow-indicator-disabled\");\n }\n return;\n }\n if (button.disabled) return; // already disabled (possibly by the author) — leave it\n if (document.activeElement === button) {\n this.#deferButtonDisable(button);\n } else {\n this.#disableButton(button);\n }\n }\n\n /**\n * Keeps a boundary button focusable until native blur, exposing its temporary\n * inoperability with `aria-disabled` and making its action a no-op meanwhile.\n */\n #deferButtonDisable(button: HTMLButtonElement): void {\n if (this.#pendingButtonDisables.has(button)) return;\n button.setAttribute(\"data-overflow-indicator-pending-disabled\", \"\");\n // The ownership marker carries the value we displace, so the DOM alone is\n // enough to undo it. A snapshot Turbo takes while the controller is still\n // connected carries these markers, and a restored visit brings them back while\n // the new instance has no in-memory record of them — an empty marker means\n // \"there was none\".\n button.setAttribute(\n \"data-overflow-indicator-aria-disabled\",\n button.getAttribute(\"aria-disabled\") ?? \"\",\n );\n button.setAttribute(\"aria-disabled\", \"true\");\n this.#pendingButtonDisables.defer(button);\n }\n\n #disableButton(button: HTMLButtonElement): void {\n button.disabled = true;\n button.setAttribute(\"data-overflow-indicator-disabled\", \"\");\n }\n\n /** Cancels a pending disable without applying it, restoring the displaced state. */\n #cancelPendingButtonDisable(button: HTMLButtonElement): void {\n this.#pendingButtonDisables.release(button);\n this.#restorePendingMarkers(button);\n }\n\n /**\n * Drops the pending marker and gives back the `aria-disabled` it displaced.\n *\n * Reads the displaced value from the DOM rather than memory: a snapshot Turbo takes\n * while the controller is still connected carries the markers, so a restored page\n * can arrive with markers this instance never wrote.\n */\n #restorePendingMarkers(button: HTMLButtonElement): void {\n button.removeAttribute(\"data-overflow-indicator-pending-disabled\");\n const displaced = button.getAttribute(\"data-overflow-indicator-aria-disabled\");\n if (displaced !== null) {\n button.removeAttribute(\"data-overflow-indicator-aria-disabled\");\n if (button.getAttribute(\"aria-disabled\") === \"true\") {\n if (displaced === \"\") button.removeAttribute(\"aria-disabled\");\n else button.setAttribute(\"aria-disabled\", displaced);\n }\n }\n }\n\n #clearPendingButtonDisables(): void {\n for (const button of this.#pendingButtonDisables.elements) {\n this.#cancelPendingButtonDisable(button);\n }\n }\n\n /** Moves resize/mutation/load observation to the current viewport target. */\n #syncViewport(): void {\n if (!this.#connected) return;\n const next = this.hasViewportTarget ? this.viewportTarget : null;\n if (next === this.#observedViewport) return;\n this.#stopObservingViewport();\n if (!next) return;\n\n this.#observedViewport = next;\n this.#viewportListeners.add(next, \"scroll\", this.#onScroll, { passive: true });\n this.#layout.observe(next);\n this.#layout.observeViewport();\n this.#layout.observeDescendantLoads(next);\n this.#syncContentObservation();\n\n if (typeof MutationObserver !== \"undefined\") {\n this.#mutationObserver = new MutationObserver(() => {\n if (!this.#connected || this.#observedViewport !== next) return;\n this.#syncContentObservation();\n this.#refresh();\n });\n this.#mutationObserver.observe(this.element, {\n childList: true,\n subtree: true,\n characterData: true,\n attributes: true,\n attributeFilter: [\"class\", \"style\", \"hidden\"],\n });\n }\n this.#state = null;\n this.#refresh();\n }\n\n /** Observes direct content boxes whose resize can change the viewport's scroll extent. */\n #syncContentObservation(): void {\n const next = new Set<Element>(this.#observedViewport?.children ?? []);\n for (const content of this.#observedContent) {\n if (!next.has(content)) {\n this.#layout.unobserve(content);\n this.#observedContent.delete(content);\n }\n }\n for (const content of next) {\n if (!this.#observedContent.has(content)) {\n this.#observedContent.add(content);\n this.#layout.observe(content);\n }\n }\n }\n\n #stopObservingViewport(): void {\n this.#frames.cancel();\n this.#viewportListeners.dispose();\n this.#mutationObserver?.disconnect();\n this.#mutationObserver = null;\n this.#layout.unobserveDescendantLoads();\n if (this.#observedViewport) this.#layout.unobserve(this.#observedViewport);\n for (const content of this.#observedContent) this.#layout.unobserve(content);\n this.#observedContent.clear();\n this.#observedViewport = null;\n this.#layout.unobserveViewport();\n }\n\n get #threshold(): number {\n const value = this.#safeThreshold;\n return Math.max(0, value);\n }\n\n #directionFromEvent(event: Event): \"start\" | \"end\" | null {\n const params = (event as Event & { params?: { direction?: unknown } }).params;\n const direction = params?.direction;\n return direction === \"start\" || direction === \"end\" ? direction : null;\n }\n /** Current `threshold` declaration resolved against its numeric contract. */\n get #safeThreshold(): number {\n return this.#numbers.read(\n this,\n \"threshold\",\n this.thresholdValue,\n OverflowIndicatorController.values.threshold.default,\n OverflowIndicatorController.valueConstraints.threshold,\n );\n }\n}\n"]}
@@ -4,7 +4,8 @@ import { Controller } from '@hotwired/stimulus';
4
4
  * Headless **overflow / priority menu**: items that no longer fit their container are
5
5
  * moved into a "More" dropdown (lowest `data-priority` first) and moved back as space
6
6
  * returns, watched with a {@link ResizeObserver} (no dedicated APG pattern — a
7
- * composition; the menu's a11y is delegated to Menu, so it is not re-implemented).
7
+ * composition; the menu's a11y is delegated to `stimeo--menu`, so it is not
8
+ * re-implemented).
8
9
  *
9
10
  * Markup contract (identifier: `stimeo--overflow-menu`):
10
11
  * <nav data-controller="stimeo--overflow-menu" aria-label="Actions">
@@ -25,7 +26,7 @@ import { Controller } from '@hotwired/stimulus';
25
26
  * This controller does not own the root's role. It ships none: a navigation bar is
26
27
  * a named `<nav>` whose items stay in the normal Tab order. A consumer may add
27
28
  * `role="toolbar"`, but that alone is not an APG Toolbar — the single-tab-stop
28
- * roving is {@link ToolbarController}'s behavior, not this one's — and a named
29
+ * roving is `stimeo--toolbar`'s behavior, not this one's — and a named
29
30
  * role still needs `aria-label` / `aria-labelledby`.
30
31
  *
31
32
  * Two parts of that contract are load-bearing and easy to drop. The banked-items
@@ -36,15 +37,15 @@ import { Controller } from '@hotwired/stimulus';
36
37
  * by keyboard at all (`Enter` / `Space` / `ArrowDown`). It is spelled out above rather
37
38
  * than referenced, because the keyboard half of this contract lives *only* in the
38
39
  * consumer's markup — nothing in this controller's source would reveal its absence.
39
- * Items need no bindings of their own — Menu delegates their handling from its own
40
- * element, so a control that only becomes a menu item at runtime is operable the
40
+ * Items need no bindings of their own — `stimeo--menu` delegates their handling from its
41
+ * own element, so a control that only becomes a menu item at runtime is operable the
41
42
  * moment it lands.
42
43
  *
43
44
  * A bare More trigger — no text, no element child, no `aria-label` or
44
45
  * `aria-labelledby` — gets `moreLabel` as its text on every pass, before the pass
45
- * measures it, marked with `data-<identifier>-owns-label` whose value is that
46
- * label. The trigger stays this controller's only while it holds that label and
47
- * nothing else — the text still matches the marker, and it has no element child,
46
+ * measures it, recording `["text", original, written]` in `data-<identifier>-owns-label`.
47
+ * The trigger stays this controller's only while it holds that label and
48
+ * nothing else — the text still matches the written string, and it has no element child,
48
49
  * `aria-label` or `aria-labelledby` — and only then does a `moreLabel` swapped at
49
50
  * runtime rewrite it, or an empty one hand it back bare. Any other trigger content
50
51
  * is the author's and is never written: a trigger the consumer has rewritten, or
@@ -55,8 +56,8 @@ import { Controller } from '@hotwired/stimulus';
55
56
  * On connect (and every debounced resize) it measures the items against the container's
56
57
  * content width and, when they overflow, banks the lowest-priority ones into the More
57
58
  * menu — giving each `role="menuitem"` / `tabindex="-1"` and the menu's `item` target so
58
- * Menu drives it — until the rest fit beside the More button. When nothing overflows the
59
- * More wrapper (button *and* menu) is `hidden`. The controller element carries
59
+ * `stimeo--menu` drives it — until the rest fit beside the More button. When nothing
60
+ * overflows the More wrapper (button *and* menu) is `hidden`. The controller element carries
60
61
  * `data-overflowing` / `data-overflow-count`, and a `change` event fires on connect and
61
62
  * on later update or resize transitions; retained-element morphs report `reconcile`.
62
63
  *