stimeo-ui 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (279) hide show
  1. package/CHANGELOG.md +212 -0
  2. package/README.md +120 -0
  3. package/dist/cable/index.js +123 -29
  4. package/dist/cable/index.js.map +1 -1
  5. package/dist/controllers/accordion_controller.d.ts +30 -3
  6. package/dist/controllers/accordion_controller.js +98 -9
  7. package/dist/controllers/accordion_controller.js.map +1 -1
  8. package/dist/controllers/alert_dialog_controller.d.ts +8 -7
  9. package/dist/controllers/alert_dialog_controller.js.map +1 -1
  10. package/dist/controllers/announcer_controller.d.ts +3 -2
  11. package/dist/controllers/announcer_controller.js +96 -62
  12. package/dist/controllers/announcer_controller.js.map +1 -1
  13. package/dist/controllers/auto_submit_controller.js +83 -7
  14. package/dist/controllers/auto_submit_controller.js.map +1 -1
  15. package/dist/controllers/avatar_controller.js +1 -1
  16. package/dist/controllers/avatar_controller.js.map +1 -1
  17. package/dist/controllers/breadcrumb_controller.d.ts +10 -7
  18. package/dist/controllers/breadcrumb_controller.js +38 -11
  19. package/dist/controllers/breadcrumb_controller.js.map +1 -1
  20. package/dist/controllers/bulk_select_controller.d.ts +4 -4
  21. package/dist/controllers/bulk_select_controller.js +7 -6
  22. package/dist/controllers/bulk_select_controller.js.map +1 -1
  23. package/dist/controllers/calendar_controller.d.ts +101 -23
  24. package/dist/controllers/calendar_controller.js +340 -123
  25. package/dist/controllers/calendar_controller.js.map +1 -1
  26. package/dist/controllers/carousel_controller.d.ts +67 -22
  27. package/dist/controllers/carousel_controller.js +263 -38
  28. package/dist/controllers/carousel_controller.js.map +1 -1
  29. package/dist/controllers/character_counter_controller.d.ts +1 -1
  30. package/dist/controllers/character_counter_controller.js +40 -2
  31. package/dist/controllers/character_counter_controller.js.map +1 -1
  32. package/dist/controllers/checkbox_controller.js +81 -12
  33. package/dist/controllers/checkbox_controller.js.map +1 -1
  34. package/dist/controllers/clipboard_controller.d.ts +28 -2
  35. package/dist/controllers/clipboard_controller.js +63 -5
  36. package/dist/controllers/clipboard_controller.js.map +1 -1
  37. package/dist/controllers/collapsible_controller.d.ts +25 -1
  38. package/dist/controllers/collapsible_controller.js +99 -14
  39. package/dist/controllers/collapsible_controller.js.map +1 -1
  40. package/dist/controllers/color_picker_controller.d.ts +29 -8
  41. package/dist/controllers/color_picker_controller.js +80 -34
  42. package/dist/controllers/color_picker_controller.js.map +1 -1
  43. package/dist/controllers/combobox_controller.d.ts +10 -1
  44. package/dist/controllers/combobox_controller.js +106 -16
  45. package/dist/controllers/combobox_controller.js.map +1 -1
  46. package/dist/controllers/command_palette_controller.d.ts +3 -3
  47. package/dist/controllers/command_palette_controller.js +35 -3
  48. package/dist/controllers/command_palette_controller.js.map +1 -1
  49. package/dist/controllers/conditional_fields_controller.js +85 -17
  50. package/dist/controllers/conditional_fields_controller.js.map +1 -1
  51. package/dist/controllers/confirm_controller.js +3 -0
  52. package/dist/controllers/confirm_controller.js.map +1 -1
  53. package/dist/controllers/context_menu_controller.d.ts +6 -0
  54. package/dist/controllers/context_menu_controller.js +32 -12
  55. package/dist/controllers/context_menu_controller.js.map +1 -1
  56. package/dist/controllers/count_up_controller.js.map +1 -1
  57. package/dist/controllers/countdown_controller.d.ts +30 -1
  58. package/dist/controllers/countdown_controller.js +129 -26
  59. package/dist/controllers/countdown_controller.js.map +1 -1
  60. package/dist/controllers/currency_input_controller.d.ts +77 -16
  61. package/dist/controllers/currency_input_controller.js +221 -67
  62. package/dist/controllers/currency_input_controller.js.map +1 -1
  63. package/dist/controllers/data_grid_controller.d.ts +63 -18
  64. package/dist/controllers/data_grid_controller.js +195 -29
  65. package/dist/controllers/data_grid_controller.js.map +1 -1
  66. package/dist/controllers/date_range_picker_controller.d.ts +41 -6
  67. package/dist/controllers/date_range_picker_controller.js +151 -30
  68. package/dist/controllers/date_range_picker_controller.js.map +1 -1
  69. package/dist/controllers/dialog_controller.d.ts +10 -3
  70. package/dist/controllers/dialog_controller.js +35 -8
  71. package/dist/controllers/dialog_controller.js.map +1 -1
  72. package/dist/controllers/direct_upload_controller.js +22 -4
  73. package/dist/controllers/direct_upload_controller.js.map +1 -1
  74. package/dist/controllers/dirty_form_controller.d.ts +2 -2
  75. package/dist/controllers/dirty_form_controller.js +46 -13
  76. package/dist/controllers/dirty_form_controller.js.map +1 -1
  77. package/dist/controllers/dismissible_controller.js +1 -0
  78. package/dist/controllers/dismissible_controller.js.map +1 -1
  79. package/dist/controllers/drawer_controller.d.ts +18 -10
  80. package/dist/controllers/drawer_controller.js +54 -19
  81. package/dist/controllers/drawer_controller.js.map +1 -1
  82. package/dist/controllers/dropdown_controller.d.ts +9 -3
  83. package/dist/controllers/dropdown_controller.js +36 -9
  84. package/dist/controllers/dropdown_controller.js.map +1 -1
  85. package/dist/controllers/editable_controller.js +34 -0
  86. package/dist/controllers/editable_controller.js.map +1 -1
  87. package/dist/controllers/file_dropzone_controller.js +144 -51
  88. package/dist/controllers/file_dropzone_controller.js.map +1 -1
  89. package/dist/controllers/filter_controller.d.ts +10 -4
  90. package/dist/controllers/filter_controller.js +20 -6
  91. package/dist/controllers/filter_controller.js.map +1 -1
  92. package/dist/controllers/flash_controller.d.ts +26 -6
  93. package/dist/controllers/flash_controller.js +432 -71
  94. package/dist/controllers/flash_controller.js.map +1 -1
  95. package/dist/controllers/focus_controller.js +1 -0
  96. package/dist/controllers/focus_controller.js.map +1 -1
  97. package/dist/controllers/form_field_controller.js +7 -5
  98. package/dist/controllers/form_field_controller.js.map +1 -1
  99. package/dist/controllers/form_validation_controller.js +19 -13
  100. package/dist/controllers/form_validation_controller.js.map +1 -1
  101. package/dist/controllers/frame_loading_controller.js +45 -8
  102. package/dist/controllers/frame_loading_controller.js.map +1 -1
  103. package/dist/controllers/highlight_controller.js +82 -25
  104. package/dist/controllers/highlight_controller.js.map +1 -1
  105. package/dist/controllers/hover_card_controller.d.ts +10 -2
  106. package/dist/controllers/hover_card_controller.js +40 -14
  107. package/dist/controllers/hover_card_controller.js.map +1 -1
  108. package/dist/controllers/idle_controller.d.ts +16 -3
  109. package/dist/controllers/idle_controller.js +90 -5
  110. package/dist/controllers/idle_controller.js.map +1 -1
  111. package/dist/controllers/input_mask_controller.d.ts +5 -2
  112. package/dist/controllers/input_mask_controller.js +65 -9
  113. package/dist/controllers/input_mask_controller.js.map +1 -1
  114. package/dist/controllers/intersection_controller.js +3 -0
  115. package/dist/controllers/intersection_controller.js.map +1 -1
  116. package/dist/controllers/lazy_frame_controller.js +11 -2
  117. package/dist/controllers/lazy_frame_controller.js.map +1 -1
  118. package/dist/controllers/listbox_controller.d.ts +50 -8
  119. package/dist/controllers/listbox_controller.js +203 -45
  120. package/dist/controllers/listbox_controller.js.map +1 -1
  121. package/dist/controllers/local_time_controller.js +10 -5
  122. package/dist/controllers/local_time_controller.js.map +1 -1
  123. package/dist/controllers/masonry_controller.d.ts +3 -3
  124. package/dist/controllers/masonry_controller.js +31 -15
  125. package/dist/controllers/masonry_controller.js.map +1 -1
  126. package/dist/controllers/menu_controller.d.ts +9 -3
  127. package/dist/controllers/menu_controller.js +45 -16
  128. package/dist/controllers/menu_controller.js.map +1 -1
  129. package/dist/controllers/menubar_controller.d.ts +11 -0
  130. package/dist/controllers/menubar_controller.js +58 -24
  131. package/dist/controllers/menubar_controller.js.map +1 -1
  132. package/dist/controllers/meter_controller.js +9 -5
  133. package/dist/controllers/meter_controller.js.map +1 -1
  134. package/dist/controllers/multi_select_controller.d.ts +18 -3
  135. package/dist/controllers/multi_select_controller.js +278 -104
  136. package/dist/controllers/multi_select_controller.js.map +1 -1
  137. package/dist/controllers/navigation_menu_controller.d.ts +11 -0
  138. package/dist/controllers/navigation_menu_controller.js +48 -15
  139. package/dist/controllers/navigation_menu_controller.js.map +1 -1
  140. package/dist/controllers/nested_form_controller.js +37 -8
  141. package/dist/controllers/nested_form_controller.js.map +1 -1
  142. package/dist/controllers/network_status_controller.js +9 -1
  143. package/dist/controllers/network_status_controller.js.map +1 -1
  144. package/dist/controllers/number_input_controller.d.ts +36 -8
  145. package/dist/controllers/number_input_controller.js +124 -21
  146. package/dist/controllers/number_input_controller.js.map +1 -1
  147. package/dist/controllers/optimistic_controller.js +42 -5
  148. package/dist/controllers/optimistic_controller.js.map +1 -1
  149. package/dist/controllers/otp_controller.d.ts +22 -7
  150. package/dist/controllers/otp_controller.js +198 -55
  151. package/dist/controllers/otp_controller.js.map +1 -1
  152. package/dist/controllers/overflow_indicator_controller.d.ts +8 -12
  153. package/dist/controllers/overflow_indicator_controller.js +115 -21
  154. package/dist/controllers/overflow_indicator_controller.js.map +1 -1
  155. package/dist/controllers/overflow_menu_controller.d.ts +26 -6
  156. package/dist/controllers/overflow_menu_controller.js +141 -44
  157. package/dist/controllers/overflow_menu_controller.js.map +1 -1
  158. package/dist/controllers/pagination_controller.d.ts +19 -11
  159. package/dist/controllers/pagination_controller.js +74 -28
  160. package/dist/controllers/pagination_controller.js.map +1 -1
  161. package/dist/controllers/password_reveal_controller.d.ts +15 -1
  162. package/dist/controllers/password_reveal_controller.js +59 -2
  163. package/dist/controllers/password_reveal_controller.js.map +1 -1
  164. package/dist/controllers/persist_controller.js +30 -8
  165. package/dist/controllers/persist_controller.js.map +1 -1
  166. package/dist/controllers/pointer_drag_controller.js +131 -52
  167. package/dist/controllers/pointer_drag_controller.js.map +1 -1
  168. package/dist/controllers/popover_controller.d.ts +9 -3
  169. package/dist/controllers/popover_controller.js +45 -11
  170. package/dist/controllers/popover_controller.js.map +1 -1
  171. package/dist/controllers/portal_controller.d.ts +1 -1
  172. package/dist/controllers/portal_controller.js +6 -2
  173. package/dist/controllers/portal_controller.js.map +1 -1
  174. package/dist/controllers/preview_guard_controller.js +16 -1
  175. package/dist/controllers/preview_guard_controller.js.map +1 -1
  176. package/dist/controllers/progress_controller.js +8 -4
  177. package/dist/controllers/progress_controller.js.map +1 -1
  178. package/dist/controllers/radio_group_controller.d.ts +6 -4
  179. package/dist/controllers/radio_group_controller.js +42 -17
  180. package/dist/controllers/radio_group_controller.js.map +1 -1
  181. package/dist/controllers/range_slider_controller.d.ts +49 -1
  182. package/dist/controllers/range_slider_controller.js +88 -42
  183. package/dist/controllers/range_slider_controller.js.map +1 -1
  184. package/dist/controllers/rating_controller.d.ts +14 -3
  185. package/dist/controllers/rating_controller.js +39 -15
  186. package/dist/controllers/rating_controller.js.map +1 -1
  187. package/dist/controllers/read_more_controller.d.ts +24 -2
  188. package/dist/controllers/read_more_controller.js +100 -7
  189. package/dist/controllers/read_more_controller.js.map +1 -1
  190. package/dist/controllers/reading_progress_controller.js +65 -19
  191. package/dist/controllers/reading_progress_controller.js.map +1 -1
  192. package/dist/controllers/relative_time_controller.js +10 -5
  193. package/dist/controllers/relative_time_controller.js.map +1 -1
  194. package/dist/controllers/resizable_controller.d.ts +16 -2
  195. package/dist/controllers/resizable_controller.js +82 -22
  196. package/dist/controllers/resizable_controller.js.map +1 -1
  197. package/dist/controllers/scroll_area_controller.js +75 -27
  198. package/dist/controllers/scroll_area_controller.js.map +1 -1
  199. package/dist/controllers/scroll_restore_controller.js +37 -16
  200. package/dist/controllers/scroll_restore_controller.js.map +1 -1
  201. package/dist/controllers/scroll_visibility_controller.js +49 -30
  202. package/dist/controllers/scroll_visibility_controller.js.map +1 -1
  203. package/dist/controllers/scrollspy_controller.d.ts +3 -2
  204. package/dist/controllers/scrollspy_controller.js +71 -26
  205. package/dist/controllers/scrollspy_controller.js.map +1 -1
  206. package/dist/controllers/separator_controller.d.ts +41 -14
  207. package/dist/controllers/separator_controller.js +66 -37
  208. package/dist/controllers/separator_controller.js.map +1 -1
  209. package/dist/controllers/sidebar_controller.d.ts +20 -3
  210. package/dist/controllers/sidebar_controller.js +77 -18
  211. package/dist/controllers/sidebar_controller.js.map +1 -1
  212. package/dist/controllers/skeleton_controller.js +6 -1
  213. package/dist/controllers/skeleton_controller.js.map +1 -1
  214. package/dist/controllers/slider_controller.d.ts +45 -7
  215. package/dist/controllers/slider_controller.js +82 -47
  216. package/dist/controllers/slider_controller.js.map +1 -1
  217. package/dist/controllers/smart_sticky_header_controller.js +60 -26
  218. package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
  219. package/dist/controllers/sortable_controller.js +17 -2
  220. package/dist/controllers/sortable_controller.js.map +1 -1
  221. package/dist/controllers/spinner_controller.js +10 -2
  222. package/dist/controllers/spinner_controller.js.map +1 -1
  223. package/dist/controllers/step_indicator_controller.d.ts +19 -17
  224. package/dist/controllers/step_indicator_controller.js +18 -17
  225. package/dist/controllers/step_indicator_controller.js.map +1 -1
  226. package/dist/controllers/stepper_controller.d.ts +34 -9
  227. package/dist/controllers/stepper_controller.js +101 -19
  228. package/dist/controllers/stepper_controller.js.map +1 -1
  229. package/dist/controllers/stick_to_bottom_controller.d.ts +19 -0
  230. package/dist/controllers/stick_to_bottom_controller.js +104 -8
  231. package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
  232. package/dist/controllers/submit_once_controller.d.ts +3 -2
  233. package/dist/controllers/submit_once_controller.js +45 -9
  234. package/dist/controllers/submit_once_controller.js.map +1 -1
  235. package/dist/controllers/switch_controller.d.ts +29 -7
  236. package/dist/controllers/switch_controller.js +101 -10
  237. package/dist/controllers/switch_controller.js.map +1 -1
  238. package/dist/controllers/tabs_controller.d.ts +12 -0
  239. package/dist/controllers/tabs_controller.js +21 -2
  240. package/dist/controllers/tabs_controller.js.map +1 -1
  241. package/dist/controllers/tags_input_controller.d.ts +15 -3
  242. package/dist/controllers/tags_input_controller.js +209 -59
  243. package/dist/controllers/tags_input_controller.js.map +1 -1
  244. package/dist/controllers/textarea_autosize_controller.js +29 -3
  245. package/dist/controllers/textarea_autosize_controller.js.map +1 -1
  246. package/dist/controllers/theme_controller.d.ts +20 -3
  247. package/dist/controllers/theme_controller.js +64 -14
  248. package/dist/controllers/theme_controller.js.map +1 -1
  249. package/dist/controllers/time_picker_controller.js +23 -8
  250. package/dist/controllers/time_picker_controller.js.map +1 -1
  251. package/dist/controllers/toast_controller.d.ts +55 -15
  252. package/dist/controllers/toast_controller.js +451 -105
  253. package/dist/controllers/toast_controller.js.map +1 -1
  254. package/dist/controllers/toggle_group_controller.d.ts +49 -7
  255. package/dist/controllers/toggle_group_controller.js +159 -23
  256. package/dist/controllers/toggle_group_controller.js.map +1 -1
  257. package/dist/controllers/toolbar_controller.js +32 -0
  258. package/dist/controllers/toolbar_controller.js.map +1 -1
  259. package/dist/controllers/tooltip_controller.d.ts +8 -0
  260. package/dist/controllers/tooltip_controller.js +39 -13
  261. package/dist/controllers/tooltip_controller.js.map +1 -1
  262. package/dist/controllers/transition_controller.js +4 -0
  263. package/dist/controllers/transition_controller.js.map +1 -1
  264. package/dist/controllers/tree_view_controller.d.ts +39 -8
  265. package/dist/controllers/tree_view_controller.js +169 -16
  266. package/dist/controllers/tree_view_controller.js.map +1 -1
  267. package/dist/index.d.ts +28 -1
  268. package/dist/index.js +5002 -1911
  269. package/dist/index.js.map +1 -1
  270. package/dist/inspector/cli.d.ts +72 -6
  271. package/dist/inspector/cli.js +262 -51
  272. package/dist/inspector/cli.js.map +1 -1
  273. package/dist/inspector/cli_bin.js +309 -51
  274. package/dist/inspector/cli_bin.js.map +1 -1
  275. package/dist/inspector/examples.json +40 -40
  276. package/dist/inspector/manifest.json +529 -61
  277. package/dist/positioning/index.js +2 -0
  278. package/dist/positioning/index.js.map +1 -1
  279. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/logical_scroll.ts","../../src/utils/arrow_step.ts","../../src/utils/focus_candidate.ts","../../src/utils/interactive_host.ts","../../src/utils/roving_tabindex.ts","../../src/utils/safe_timeout.ts","../../src/utils/typeahead.ts","../../src/controllers/tree_view_controller.ts"],"names":[],"mappings":"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","file":"tree_view_controller.js","sourcesContent":["/** 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","import { isRtl } from \"./logical_scroll\";\n\n/**\n * Turns an arrow key into a **logical** step: `+1` for \"next\", `-1` for\n * \"previous\", `0` when the key names neither.\n *\n * APG defines the horizontal pair as *next / previous* and says a vertical\n * arrangement swaps in Down/Up for the same meaning — so the pair is one axis's\n * spelling of an order, and the order reverses with the writing direction. Only\n * the horizontal pair reverses. Down/Up name an axis the writing direction does\n * not mirror, and returning them unchanged is the point: many controllers fold\n * both pairs into one branch, where swapping the branches under RTL would flip\n * the vertical axis too — a bug that reads as \"the arrows work\" until someone\n * presses Down.\n *\n * **Direction is read from the element the caller passes, which should be the\n * container that lays the items out** — not the focused child. A child may carry\n * its own `dir` (an LTR input inside an RTL form is ordinary authoring), and\n * probing per handler makes two handlers disagree at the boundary between them.\n *\n * This decides direction only. Whether the axis is even active (an\n * `orientation=\"horizontal\"` widget ignoring Down/Up), how far the step lands,\n * and what wrapping does all stay with the caller.\n *\n * **It encodes the list-order convention: `ArrowDown` is *next*.** Widgets that\n * pair the arrows by *value* instead — `ArrowUp` meaning \"more\", as a rating or a\n * slider does — must not use this, or their vertical axis inverts. Reverse the\n * horizontal pair on its own there.\n *\n * @example\n * ```ts\n * const step = logicalArrowStep(event.key, this.element);\n * if (step === 0) return;\n * this.#roving.setActive(rovingMove(current, length, step, \"wrap\"), { focus: true });\n * ```\n */\nexport function logicalArrowStep(key: string, element: Element): 1 | -1 | 0 {\n if (key === \"ArrowDown\") return 1;\n if (key === \"ArrowUp\") return -1;\n if (key !== \"ArrowRight\" && key !== \"ArrowLeft\") return 0;\n const forward = isRtl(element) ? \"ArrowLeft\" : \"ArrowRight\";\n return key === forward ? 1 : -1;\n}\n\n/**\n * Rewrites `key` so an existing LTR-shaped branch keeps working under RTL:\n * `ArrowRight` and `ArrowLeft` trade places, everything else passes through.\n *\n * The alternative — negating a delta — silently breaks handlers whose two\n * horizontal branches are **not mirror images**. A grid that clamps one edge but\n * not the other, or a segmented field guarding `index > 0` on one side and\n * `index < length - 1` on the other, ends up applying the wrong guard to the\n * wrong direction. Swapping the key leaves each branch, guards and all, exactly\n * where its author put it.\n *\n * Same rule as {@link logicalArrowStep} about which element to read: pass the\n * container that lays the items out, not the focused child.\n *\n * @example\n * ```ts\n * switch (logicalArrowKey(event.key, this.element)) {\n * case \"ArrowLeft\": // \"previous\" — whatever direction that is on screen\n * ```\n */\nexport function logicalArrowKey(key: string, element: Element): string {\n if (key !== \"ArrowRight\" && key !== \"ArrowLeft\") return key;\n if (!isRtl(element)) return key;\n return key === \"ArrowRight\" ? \"ArrowLeft\" : \"ArrowRight\";\n}\n\n/** Modifiers a widget may claim on an arrow key, named for the `allow` list. */\nexport type ArrowModifier = \"alt\" | \"ctrl\" | \"meta\" | \"shift\";\n\n/**\n * True when an arrow key arrived carrying a modifier the widget must leave to\n * the browser: return without calling `preventDefault()` and without moving any\n * state.\n *\n * A bare arrow belongs to the widget; a chorded one usually does not.\n * `Alt`/`Meta` plus a horizontal arrow is history back/forward on every desktop\n * browser, and a widget that swallows it makes the shortcut work or not\n * depending on where focus happens to sit — a coin-flip the user cannot see.\n *\n * `allow` is for the combinations APG assigns to a pattern **and the widget\n * actually implements** — today only Combobox's optional `Alt+Down`/`Alt+Up`.\n * Listing one the widget does not implement defeats the point: the chord then\n * runs the plain-arrow branch, which is exactly what this guard exists to stop.\n * Non-arrow keys return `false`, so chorded letters and\n * `Control+Home`/`Control+End` are untouched.\n *\n * @example\n * ```ts\n * if (isReservedArrowChord(event)) return;\n * ```\n */\nexport function isReservedArrowChord(\n event: KeyboardEvent,\n allow: readonly ArrowModifier[] = [],\n): boolean {\n if (!event.key.startsWith(\"Arrow\")) return false;\n return (\n (event.altKey && !allow.includes(\"alt\")) ||\n (event.ctrlKey && !allow.includes(\"ctrl\")) ||\n (event.metaKey && !allow.includes(\"meta\")) ||\n (event.shiftKey && !allow.includes(\"shift\"))\n );\n}\n\n/**\n * True when a press arrived carrying any modifier, for the keys the browser and\n * the OS own outright: return without calling `preventDefault()` and without\n * moving any state.\n *\n * `Control+Home` and `Control+End` jump the document to its ends, and a widget\n * that swallows them makes the shortcut work or not depending on where focus\n * happens to sit. {@link isReservedArrowChord} answers the same question for the\n * arrows, but returns `false` for every other key so that this one can decide.\n *\n * There is no `allow` list here on purpose. APG assigns no modifier chord to\n * `Home`/`End`, so a widget that wanted one would be claiming a combination the\n * pattern never gave it.\n *\n * @example\n * ```ts\n * case \"Home\":\n * case \"End\":\n * if (hasModifierChord(event)) return;\n * ```\n */\nexport function hasModifierChord(event: KeyboardEvent): boolean {\n return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;\n}\n","/**\n * Whether a disabled `<fieldset>` ancestor actually reaches `control`.\n *\n * HTML exempts the contents of a fieldset's **first direct-child `<legend>`**, and\n * the exemption is per fieldset — a control legal in one legend can still be\n * disabled by a second, outer fieldset, so the walk continues upward.\n *\n * Exported on its own for callers that need the disabled-inheritance rule without\n * the rest of {@link canTakeFocus} — a control's availability check, or a `hidden`\n * walk bounded at a widget root, which is a different rule to compose with.\n */\nexport function inheritsFieldsetDisabled(control: HTMLElement): boolean {\n let fieldset: Element | null = control.closest(\"fieldset[disabled]\");\n while (fieldset) {\n const legend = Array.from(fieldset.children).find((child) => child.tagName === \"LEGEND\");\n if (!legend?.contains(control)) return true;\n fieldset = fieldset.parentElement?.closest(\"fieldset[disabled]\") ?? null;\n }\n return false;\n}\n\n/**\n * Whether an element can actually take focus, checked **before** `focus()` runs.\n *\n * A controller that must move focus off a control it is about to disable or hide\n * picks a destination and calls `focus()` on it. If that destination cannot take\n * focus, the call **fails silently**: `hidden` and natively `disabled` elements\n * swallow it, so the caret stays in the subtree that is disappearing and lands on\n * `<body>` a frame later — the exact outcome the rescue exists to prevent, minus\n * any signal that it happened.\n *\n * Testing after the fact is the obvious alternative and is deliberately not the\n * rule. Reading `document.activeElement` back only works in a real browser, and\n * looping over candidates that way performs a real focus move per failure —\n * observable to assistive technology. Checking first costs nothing and catches\n * the cases that actually occur.\n *\n * **`aria-disabled` is not disqualifying.** It is the attribute an author uses\n * for a control that must stay *discoverable*, and the roving contract keeps\n * such items reachable. Only the four conditions that make the platform refuse\n * focus are checked: `hidden` / `inert` (an ancestor's counts),\n * `input[type=\"hidden\"]`, the native `disabled` property, and `disabled`\n * inherited from an ancestor `fieldset`. CSS-only invisibility is handled by\n * {@link isRenderedForFocus} when a consumer needs sequential-focus semantics.\n *\n * Reading `:disabled` instead of walking the fieldset chain would be shorter, but\n * that pseudo-class is not evaluated consistently outside real browsers and this\n * has to be right headlessly too. happy-dom in particular focuses a `<button>`\n * inside a disabled fieldset where a real engine refuses, so the inheritance is\n * spelled out rather than delegated.\n *\n * What a consumer does when nothing survives is its own call: some fall back to\n * their landmark, while a widget whose caret already sits somewhere legitimate\n * refuses the move outright rather than relocating it.\n *\n * @example\n * ```ts\n * const target = candidates.find(canTakeFocus);\n * if (target) target.focus();\n * else {\n * this.#tabindex.lend(this.element); // nothing left: fall back to the landmark\n * this.element.focus();\n * }\n * ```\n *\n * @param element - the candidate destination\n */\nexport function canTakeFocus(element: HTMLElement): boolean {\n if (element.closest(\"[hidden], [inert]\")) return false;\n if (element instanceof HTMLInputElement && element.type === \"hidden\") return false;\n if (!(\"disabled\" in element)) return true;\n if ((element as HTMLElement & { disabled: boolean }).disabled) return false;\n return !inheritsFieldsetDisabled(element);\n}\n\n/** Elements whose semantics or authored attributes can place them in sequential focus order. */\nexport const TAB_STOP_CANDIDATE_SELECTOR = [\n \"a[href]\",\n \"area[href]\",\n \"button\",\n \"input\",\n \"select\",\n \"textarea\",\n \"summary\",\n \"iframe\",\n \"audio[controls]\",\n \"video[controls]\",\n \"[tabindex]\",\n \"[contenteditable]\",\n].join(\",\");\n\n/** Optional browser visibility API used to exclude CSS-hidden candidates. */\ninterface VisibilityCheckable {\n checkVisibility?: (options?: { visibilityProperty?: boolean }) => boolean;\n}\n\n/** Whether CSS visibility allows an otherwise eligible element to participate in focus order. */\nexport function isRenderedForFocus(element: HTMLElement): boolean {\n const check = (element as HTMLElement & VisibilityCheckable).checkVisibility;\n return typeof check === \"function\" ? check.call(element, { visibilityProperty: true }) : true;\n}\n\n/** Parses an authored `tabindex`; invalid syntax has no explicit focus-order meaning. */\nfunction authoredTabindex(element: HTMLElement): number | null {\n const value = element.getAttribute(\"tabindex\");\n if (value === null || !/^[+-]?\\d+$/.test(value.trim())) return null;\n return Number(value);\n}\n\n/** Whether the element's native semantics place it in sequential focus order. */\nfunction hasNativeTabStop(element: HTMLElement): boolean {\n if (element instanceof HTMLAnchorElement || element instanceof HTMLAreaElement) {\n return element.hasAttribute(\"href\");\n }\n if (\n element instanceof HTMLButtonElement ||\n element instanceof HTMLSelectElement ||\n element instanceof HTMLTextAreaElement\n ) {\n return true;\n }\n if (element instanceof HTMLInputElement) return element.type !== \"hidden\";\n if (element instanceof HTMLIFrameElement) return true;\n if (element.tagName === \"AUDIO\" || element.tagName === \"VIDEO\") {\n return element.hasAttribute(\"controls\");\n }\n if (element instanceof HTMLElement && element.tagName === \"SUMMARY\") {\n const details = element.parentElement;\n return (\n details instanceof HTMLDetailsElement &&\n Array.from(details.children).find((child) => child.tagName === \"SUMMARY\") === element\n );\n }\n return false;\n}\n\n/** Whether an explicit `contenteditable` value creates an editable tab stop. */\nfunction hasEditableTabStop(element: HTMLElement): boolean {\n const value = element.getAttribute(\"contenteditable\")?.toLowerCase();\n return value === \"\" || value === \"true\" || value === \"plaintext-only\";\n}\n\n/**\n * Whether an element is a usable sequential Tab stop right now.\n *\n * Native semantics, authored `tabindex`, editable hosts, inherited disabled state,\n * HTML `hidden`/`inert`, and CSS visibility are evaluated together. `aria-disabled`\n * remains focusable because it communicates unavailability without removing the\n * control from discovery order.\n */\nexport function isTabStop(element: HTMLElement): boolean {\n if (!canTakeFocus(element) || !isRenderedForFocus(element)) return false;\n\n const tabindex = authoredTabindex(element);\n if (tabindex !== null) return tabindex >= 0;\n return hasNativeTabStop(element) || hasEditableTabStop(element);\n}\n\n/** Returns every usable sequential Tab stop below `root` in document order. */\nexport function tabStopsWithin(root: ParentNode): HTMLElement[] {\n return Array.from(root.querySelectorAll<HTMLElement>(TAB_STOP_CANDIDATE_SELECTOR)).filter(\n isTabStop,\n );\n}\n\n/** Returns the first usable sequential Tab stop below `root`, if one exists. */\nexport function firstTabStop(root: ParentNode): HTMLElement | null {\n for (const candidate of root.querySelectorAll<HTMLElement>(TAB_STOP_CANDIDATE_SELECTOR)) {\n if (isTabStop(candidate)) return candidate;\n }\n return null;\n}\n\n/** Whether `root` contains at least one usable sequential Tab stop. */\nexport function hasTabStop(root: ParentNode): boolean {\n return firstTabStop(root) !== null;\n}\n","/**\n * Native interactive elements whose built-in activation must not be replaced\n * wholesale by a controller. Deliberately excludes `[tabindex]`: a generic\n * element made focusable is the supported host for several headless patterns.\n * Contenteditable is handled by {@link isInteractiveHost} because its state is\n * inherited and its keywords are ASCII-case-insensitive.\n */\nexport const INTERACTIVE_HOST_SELECTOR =\n \"button, input, select, textarea, label, a[href], area[href], summary, details, audio[controls], video[controls], iframe, object, embed\";\n\n/**\n * Whether an element owns native activation or an inherited editing surface.\n *\n * The `contenteditable` missing and invalid states inherit. Walking explicitly\n * also keeps `contenteditable=\"false\"` as a real boundary inside an editable\n * ancestor instead of treating any ancestor attribute as decisive.\n */\nexport function isInteractiveHost(element: HTMLElement): boolean {\n if (element.matches(INTERACTIVE_HOST_SELECTOR)) return true;\n\n let current: HTMLElement | null = element;\n while (current) {\n const raw = current.getAttribute(\"contenteditable\");\n if (raw !== null) {\n const value = raw.trim().toLowerCase();\n if (value === \"false\") return false;\n if (value === \"\" || value === \"true\" || value === \"plaintext-only\") return true;\n }\n current = current.parentElement;\n }\n return false;\n}\n","/**\n * Low-level roving-tabindex primitive shared by composite-widget controllers.\n *\n * The APG roving-tabindex pattern keeps a composite widget a single Tab stop:\n * exactly one item is in the Tab sequence (`tabindex=\"0\"`) while the rest are\n * removed from it (`tabindex=\"-1\"`), and the arrow keys move both DOM focus and\n * that single tabbable position together. {@link RovingTabindex} owns *only* that\n * mechanical bookkeeping — \"which one item is tabbable, and move focus there\".\n *\n * It is intentionally **policy-free**. Orientation, wrapping vs. clamping,\n * selection-follows-focus, typeahead, and `Home`/`End` semantics differ per APG\n * pattern (Radio Group, Toolbar, Rating, …); folding them into one helper would\n * flatten those widgets to a lowest common denominator and lose each pattern's\n * correctness. Those decisions therefore stay in each controller, which calls\n * {@link RovingTabindex.setActive} with an index it computed itself (optionally\n * via the pure {@link rovingMove} helper).\n *\n * @remarks\n * Items are read lazily through a getter so a controller can add or remove\n * targets (Stimulus re-scans the DOM) without re-wiring this helper. A caller\n * that already captured one event-scoped snapshot may pass it to\n * {@link RovingTabindex.setActive}, avoiding a second live-target scan while\n * preserving one coherent collection for the whole operation.\n */\nexport class RovingTabindex {\n /** Returns the current ordered item elements; called on every operation. */\n readonly #getItems: () => HTMLElement[];\n\n /**\n * @param getItems - Returns the current ordered item elements. Called on every\n * operation so the live target list is always used.\n */\n constructor(getItems: () => HTMLElement[]) {\n this.#getItems = getItems;\n }\n\n /** Index of the currently tabbable item (`tabindex=\"0\"`), or `-1` if none. */\n get activeIndex(): number {\n return this.#getItems().findIndex((item) => item.tabIndex === 0);\n }\n\n /**\n * Makes exactly the item at `index` tabbable (`tabindex=\"0\"`) and removes every\n * other item from the Tab sequence (`tabindex=\"-1\"`). An out-of-range `index`\n * (e.g. `-1`) leaves all items at `-1`, which a controller can use to express\n * \"nothing is currently tabbable\".\n *\n * @param index - Position of the item to make tabbable.\n * @param options - Pass `{ focus: true }` to also move DOM focus to that item,\n * and `items` to reuse an event-scoped collection snapshot.\n */\n setActive(\n index: number,\n options: { focus?: boolean; items?: readonly HTMLElement[] } = {},\n ): void {\n const { focus = false } = options;\n const items = options.items ?? this.#getItems();\n items.forEach((item, i) => {\n item.tabIndex = i === index ? 0 : -1;\n });\n if (focus) items[index]?.focus();\n }\n}\n\n/** Edge behavior for {@link rovingMove}: cycle past the ends, or stop at them. */\nexport type RovingWrap = \"wrap\" | \"clamp\";\n\n/**\n * Pure helper that resolves the target index for a one-step directional move.\n *\n * Keyboard/orientation mapping stays in the caller: it decides that a key means\n * `delta` `+1` (next) or `-1` (previous) and whether the widget should `\"wrap\"`\n * (Radio Group, Toolbar) or `\"clamp\"` at the ends.\n *\n * @param current - The index focus is moving from.\n * @param length - Number of items in the set.\n * @param delta - `+1` to move to the next item, `-1` for the previous.\n * @param wrap - `\"wrap\"` cycles around the ends; `\"clamp\"` stops at them.\n * @returns The resolved index, or `-1` when there are no items.\n */\nexport function rovingMove(\n current: number,\n length: number,\n delta: number,\n wrap: RovingWrap,\n): number {\n if (length === 0) return -1;\n const next = current + delta;\n if (wrap === \"wrap\") return (next + length) % length;\n return Math.min(length - 1, Math.max(0, next));\n}\n","/**\n * Self-cleaning timer registries shared by Stimeo controllers.\n *\n * Stimulus controllers frequently schedule `setTimeout` / `setInterval` work\n * (auto-dismiss, debouncing, polling). When the element leaves the DOM — a\n * Turbo Drive navigation, a Turbo Stream replacement, or any `disconnect()` —\n * orphaned timers keep firing against a detached controller, leaking memory and\n * mutating stale state. {@link SafeTimeout} and {@link SafeInterval} track every\n * timer they create so a single {@link TimerRegistry.clearAll | clearAll()} call\n * in `disconnect()` tears them all down.\n *\n * These are intentionally low-level primitives: they own *registration and\n * cleanup only*. Higher-level policy (pause/resume, remaining-time accounting)\n * stays in the individual controllers so per-widget semantics are not flattened\n * into a lowest-common-denominator helper.\n */\n\n/**\n * Largest delay a timer can hold: the platform stores it in a 32-bit signed\n * integer, and anything larger overflows to `1`, so a delay meant to be far in\n * the future fires almost immediately. A declared delay above this bound names\n * no delay at all, and a controller reading one falls back to its default.\n */\nexport const MAX_TIMER_DELAY_MS = 2_147_483_647;\n\n/**\n * Shared registry bookkeeping for the timeout/interval variants.\n *\n * Subclasses provide the scheduling primitive ({@link schedule}) and its matching\n * canceller ({@link cancel}); this base owns the set of live ids plus the\n * per-id and bulk teardown shared by both.\n */\nabstract class TimerRegistry {\n /** Live timer ids that have not yet been cleared (or, for timeouts, fired). */\n protected readonly ids = new Set<number>();\n\n /** Schedules the underlying platform timer and returns its id. */\n protected abstract schedule(callback: () => void, delay: number): number;\n\n /** Cancels the underlying platform timer for `id`. */\n protected abstract cancel(id: number): void;\n\n /**\n * Cancels a single tracked timer.\n *\n * No-ops if the id is unknown (already cleared, fired, or never owned by this\n * registry), so callers can clear defensively without guarding.\n */\n clear(id: number): void {\n if (this.ids.delete(id)) {\n this.cancel(id);\n }\n }\n\n /**\n * Cancels every tracked timer. Call this from a controller's `disconnect()`\n * to guarantee no timer outlives the element.\n */\n clearAll(): void {\n for (const id of this.ids) {\n this.cancel(id);\n }\n this.ids.clear();\n }\n\n /** Number of timers currently tracked (pending). */\n get size(): number {\n return this.ids.size;\n }\n}\n\n/**\n * `setTimeout` wrapper that auto-forgets each timer once it fires and supports\n * bulk teardown on disconnect.\n *\n * @example\n * ```ts\n * #timers = new SafeTimeout();\n *\n * connect() {\n * this.#timers.set(() => this.dismiss(), 5000);\n * }\n *\n * disconnect() {\n * this.#timers.clearAll();\n * }\n * ```\n */\nexport class SafeTimeout extends TimerRegistry {\n /**\n * Schedules `callback` after `delay` ms and returns the timer id.\n *\n * The id is removed from the registry automatically when the timeout fires,\n * so {@link TimerRegistry.size | size} reflects only still-pending timers.\n */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(() => {\n this.ids.delete(id);\n callback();\n }, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setTimeout(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearTimeout(id);\n }\n}\n\n/**\n * `setInterval` wrapper that tracks every interval for bulk teardown on\n * disconnect. Unlike {@link SafeTimeout}, intervals are retained until they are\n * explicitly cleared because they fire repeatedly.\n *\n * @example\n * ```ts\n * #intervals = new SafeInterval();\n *\n * connect() {\n * this.#intervals.set(() => this.tick(), 1000);\n * }\n *\n * disconnect() {\n * this.#intervals.clearAll();\n * }\n * ```\n */\nexport class SafeInterval extends TimerRegistry {\n /** Schedules a repeating `callback` every `delay` ms and returns the timer id. */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(callback, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setInterval(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearInterval(id);\n }\n}\n","/**\n * Shared type-ahead (first-letter navigation) primitive for composite widgets.\n *\n * The APG asks list-like widgets to move focus by typed characters: one\n * character jumps to the next item starting with it, and several characters\n * typed in quick succession narrow the match to that prefix. Getting that right\n * needs three pieces of bookkeeping — accumulating the query, dropping it after\n * an idle window, and resolving the label each candidate is matched against —\n * and single-sourcing them here keeps every widget on the same rules instead of\n * letting them drift apart.\n *\n * The helpers here own *only* that mechanical part. They are deliberately\n * **policy-free** in the same sense as `RovingTabindex`: which elements are\n * candidates, where the search starts, and what happens on a match (real DOM\n * focus, a roving tab stop, or a virtual `aria-activedescendant`) differ per APG\n * pattern, so each controller keeps those decisions and calls\n * {@link findTypeaheadMatch} with a candidate list it assembled itself.\n *\n * Three decisions are worth stating up front:\n *\n * - **A repeated character collapses the stored query**, rather than deriving a\n * shorter one at search time. That is what lets a follow-up character resume\n * narrowing: `s`, `s`, `e` searches `\"se\"`, not the dead `\"sse\"` that no label\n * can match. Growing the query instead leaves the widget frozen on exactly\n * that sequence, which is why the choice is made here and not per widget.\n * - **`aria-label` wins over text**, so type-ahead matches what a screen reader\n * announces — but only when it contributes a name. A blank (empty or\n * whitespace-only) one is skipped, because accname skips it too: honoring it\n * would leave an element that AT announces by its text unreachable by that\n * text. This mirrors the accessible-name computation rather than the raw text.\n * - **`Space` and composition input are never type-ahead.** `Space` natively\n * activates a `<button>`-based item, and characters still being composed are\n * not a committed query.\n *\n * This file's own doc block is dropped from `dist`, but every member comment is\n * inlined into each consumer entry (`tsup` builds with `splitting: false`), so\n * rationale belongs here and contracts belong on the members.\n */\n\nimport { SafeTimeout } from \"./safe_timeout\";\n\n/** Idle window after which an accumulated query is dropped, in milliseconds. */\nexport const TYPEAHEAD_RESET_MS = 500;\n\n/** Construction options for {@link Typeahead}. */\nexport interface TypeaheadOptions {\n /** Idle window before the query resets. Defaults to {@link TYPEAHEAD_RESET_MS}. */\n resetMs?: number;\n}\n\n/**\n * Accumulates typed characters into a search query and drops it when idle.\n *\n * @remarks\n * Owns a {@link SafeTimeout} of its own, so a consumer's `#timers.clearAll()` does\n * **not** reach the pending reset — call {@link Typeahead.reset} from `disconnect()`.\n */\nexport class Typeahead {\n /** Timer registry for the pending idle reset; private so `reset()` is the only exit. */\n readonly #timers = new SafeTimeout();\n /** Idle window before the query resets, in milliseconds. */\n readonly #resetMs: number;\n /** The accumulated lowercase query, empty when idle. */\n #query = \"\";\n /** Id of the pending reset timer, `0` when none is scheduled. */\n #timerId = 0;\n\n /** @param options - Overrides for the idle window. */\n constructor({ resetMs = TYPEAHEAD_RESET_MS }: TypeaheadOptions = {}) {\n this.#resetMs = resetMs;\n }\n\n /** The query a search would currently run with; empty while idle. */\n get query(): string {\n return this.#query;\n }\n\n /**\n * Folds `key` into the query, restarts the idle window, and returns the query to\n * search with. A repeated character collapses the query to that one character.\n */\n push(key: string): string {\n const char = key.toLowerCase();\n const repeated = this.#query.length > 0 && [...this.#query].every((c) => c === char);\n this.#query = repeated ? char : this.#query + char;\n this.#timers.clear(this.#timerId);\n this.#timerId = this.#timers.set(() => this.reset(), this.#resetMs);\n return this.#query;\n }\n\n /** Clears the query and cancels the pending idle reset. */\n reset(): void {\n this.#query = \"\";\n this.#timers.clear(this.#timerId);\n this.#timerId = 0;\n }\n}\n\n/** Whether `event` is a bare printable character usable for type-ahead. */\nexport function isTypeaheadKey(event: KeyboardEvent): boolean {\n return (\n event.key.length === 1 &&\n event.key !== \" \" &&\n !event.ctrlKey &&\n !event.metaKey &&\n !event.altKey &&\n !event.isComposing\n );\n}\n\n/**\n * The name `element` is matched under, normalized for comparison.\n *\n * @param fallbackText - Produces the name when `aria-label` contributes none.\n * Defaults to the element's `textContent`; pass a narrower source when part of\n * the subtree is not part of the name (a tree item's nested child group, say).\n * Called lazily, so an `aria-label` costs no subtree walk.\n */\nexport function typeaheadLabel(element: HTMLElement, fallbackText?: () => string): string {\n // Blank means absent, matching accname: a whitespace-only `aria-label` is\n // skipped there too, so honoring it here would make an element that a screen\n // reader announces by its text unreachable by that text.\n const label = element.getAttribute(\"aria-label\")?.trim();\n if (label) return label.toLowerCase();\n const text = fallbackText ? fallbackText() : (element.textContent ?? \"\");\n return text.trim().toLowerCase();\n}\n\n/**\n * Index of the first item after `from` whose label starts with `query`, wrapping\n * and evaluating `from` itself last, or `-1` when nothing matches.\n *\n * @param items - Candidates in navigation order, already filtered by the caller.\n * @param from - Index the search moves on from; `-1` starts at the first item. Any\n * out-of-range value is folded back in, so `indexOf` results pass straight through.\n * @param query - Lowercase query, normally the return of {@link Typeahead.push}.\n * @param label - Resolves a candidate's name. Defaults to {@link typeaheadLabel}.\n */\nexport function findTypeaheadMatch(\n items: readonly HTMLElement[],\n from: number,\n query: string,\n label: (item: HTMLElement) => string = (item) => typeaheadLabel(item),\n): number {\n // An empty query would prefix-match everything; an empty list never enters the\n // loop, so it needs no guard of its own.\n if (query === \"\") return -1;\n const count = items.length;\n for (let step = 1; step <= count; step += 1) {\n // Folded twice so a negative `from` still lands on a real slot: `%` keeps the\n // sign of its left operand, and a negative index would read `undefined`.\n const index = (((from + step) % count) + count) % count;\n const candidate = items[index];\n if (candidate && label(candidate).startsWith(query)) return index;\n }\n return -1;\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { isReservedArrowChord } from \"../utils/arrow_step\";\nimport { canTakeFocus } from \"../utils/focus_candidate\";\nimport { INTERACTIVE_HOST_SELECTOR, isInteractiveHost } from \"../utils/interactive_host\";\nimport { isRtl } from \"../utils/logical_scroll\";\nimport { RovingTabindex } from \"../utils/roving_tabindex\";\nimport { SafeTimeout } from \"../utils/safe_timeout\";\nimport { findTypeaheadMatch, isTypeaheadKey, Typeahead, typeaheadLabel } from \"../utils/typeahead\";\n\n/**\n * Interactive descendants a `treeitem` may legitimately contain (inline rename\n * field, link, chevron). Keys and clicks raised inside one belong to it, not to\n * the tree, so the tree handlers step aside.\n *\n * The selector is matched with `closest()`, which also matches the item host\n * itself — deliberately. An item host that *is* one of these elements is out of\n * contract (see the class TSDoc), and the tree then stands down completely\n * rather than responding to some keys and not others.\n */\nconst NESTED_INTERACTIVE = INTERACTIVE_HOST_SELECTOR;\n\n/**\n * Headless, accessible single-select tree view.\n *\n * Markup contract (identifier: `stimeo--tree-view`):\n * <ul data-controller=\"stimeo--tree-view\" role=\"tree\" aria-label=\"Files\">\n * <li role=\"treeitem\" aria-expanded=\"false\" aria-selected=\"false\" tabindex=\"0\"\n * data-stimeo--tree-view-target=\"item\"\n * data-action=\"keydown->stimeo--tree-view#onKeydown\n * click->stimeo--tree-view#onClick\">\n * <span>src</span>\n * <button type=\"button\" tabindex=\"-1\" aria-hidden=\"true\"\n * data-action=\"click->stimeo--tree-view#toggle\"></button>\n * <ul role=\"group\" data-stimeo--tree-view-target=\"group\" hidden>\n * <li role=\"treeitem\" aria-selected=\"false\" tabindex=\"-1\"\n * data-stimeo--tree-view-target=\"item\" data-action=\"…\">…</li>\n * </ul>\n * </li>\n * </ul>\n *\n * Implements the WAI-ARIA APG **Tree View** (single-select) pattern. Parent/child\n * structure is read from the DOM nesting (`treeitem` → child `group`). The whole\n * tree is one Tab stop (roving tabindex); arrows navigate visible items, expand /\n * collapse, and move between parent and child.\n *\n * Behavior provided:\n * - `ArrowDown`/`ArrowUp` move between visible items; `Home`/`End` jump to the\n * first / last visible item; printable characters typeahead by label prefix\n * (`aria-label` first, else the item's own text), with a repeated character\n * cycling through the items that start with it.\n * - `ArrowRight` expands a collapsed parent or steps into its first child;\n * `ArrowLeft` collapses an expanded parent or steps to the parent item.\n * - `Enter`/`Space`/click select the item (single selection via `aria-selected`);\n * an `aria-disabled=\"true\"` item stays focusable but cannot be selected.\n * - `aria-expanded` and each child `group`'s `hidden` stay in sync, dispatching\n * `stimeo--tree-view:toggle`; selection dispatches `stimeo--tree-view:select`.\n * - The optional `toggle` action expands / collapses the nearest item, so a\n * chevron gives pointer users the reach that `ArrowRight` gives the keyboard.\n *\n * Contract notes:\n * - A child container is resolved by `role=\"group\"`, not by the target: the\n * `group` target is what `stimeo check`'s accessibility rule anchors on, which\n * is why the contract still declares it.\n * - Nested interactive controls (natively interactive elements such as `button`,\n * `input`, `select`, `textarea`, `label`, `a[href]`, `summary`) and IME composition\n * keys are left alone; a click on a child `group`'s own box (its indentation\n * band) selects nothing rather than the parent row.\n * - A key a descendant already consumed (`event.defaultPrevented`) is yielded,\n * so a composed widget inside a row never causes a double move.\n *\n * Consumer contract — the item host:\n * - **Host `role=\"treeitem\"` on a non-interactive element** (`li`, `div`), never\n * on a natively interactive element. APG's *Navigation Treeview* does host it on a\n * link, but that variant activates on `Enter`, which this pattern has already\n * spent on selection; a link host is therefore out of contract and would need\n * `Enter` / click / `select` redefined first.\n * - An out-of-contract host makes `onKeydown` and `onClick` stand down\n * **entirely** — no movement, no selection, no `select` — rather than respond\n * partially. `stimeo check` reports the unsupported host before runtime.\n *\n * Consumer contract — controls nested inside a row:\n * - Keep them out of the Tab sequence (`tabindex=\"-1\"`); the tree is one Tab\n * stop and writes `tabindex` on nothing but its own item targets.\n * - Moving focus *into* a control (to start an inline rename) is the consumer's\n * call — the tree never focuses one.\n * - When the edit or action ends, focus the `treeitem` itself. The roving\n * position never moved, so that alone restores the keyboard path.\n * - The tree consumes none of the control's events, by two mechanisms:\n * the nested-interactive selector names controls by *element shape*, the\n * `defaultPrevented` yield covers widgets no selector can name.\n * `select` dispatches `{ item: HTMLElement }`.\n * `toggle` dispatches `{ item: HTMLElement, expanded: boolean }`.\n */\nexport class TreeViewController extends Controller<HTMLElement> {\n static override targets = [\"item\", \"group\"];\n static actions = [\"onClick\", \"onKeydown\", \"toggle\"] as const;\n static events = [\"select\", \"toggle\"] as const;\n\n declare readonly itemTargets: HTMLElement[];\n\n readonly #roving = new RovingTabindex(() => this.itemTargets);\n readonly #typeahead = new Typeahead();\n readonly #timers = new SafeTimeout();\n #connected = false;\n /**\n * Item targets in DOM order as of the last connect / target change. A removed\n * item is gone from `itemTargets` by the time Stimulus reports it, so this is\n * the only record of where in the order the gap opened.\n */\n #order: HTMLElement[] = [];\n /** The `treeitem` that last took DOM focus inside this tree, if any. */\n #focused: HTMLElement | null = null;\n\n /**\n * Records which row owns DOM focus. `focusin` bubbles, so one listener covers\n * rows and anything nested in them; the tree needs this because a removal is\n * only reported *after* the fact, when the browser has already reset\n * `document.activeElement`.\n */\n readonly #onFocusIn = (event: FocusEvent): void => {\n const target = event.target as HTMLElement | null;\n this.#focused = target?.closest<HTMLElement>('[role=\"treeitem\"]') ?? null;\n };\n\n /**\n * Forgets the tracked row when focus genuinely leaves the tree, so a later\n * removal of that row cannot be mistaken for \"focus died with it\" and pull\n * focus back in.\n *\n * A *removed* focused row must not clear the record — that is exactly the case\n * {@link TreeViewController.itemTargetDisconnected} needs it for. The two are\n * indistinguishable while the event is dispatching: Chromium fires `focusout`\n * for a removed row *before* the detach lands — `isConnected` still reads\n * `true` — with `document.activeElement` already on `<body>`, exactly like a\n * plain `blur()`. Deciding on a microtask separates them — a row that is gone\n * by then was removed. Focus moving between two rows needs no handling:\n * `focusin` overwrites the record right after.\n */\n readonly #onFocusOut = (event: FocusEvent): void => {\n const target = event.target as HTMLElement | null;\n const row = target?.closest<HTMLElement>('[role=\"treeitem\"]') ?? null;\n if (!row || row !== this.#focused) return;\n const next = event.relatedTarget;\n if (next instanceof Node && this.element.contains(next)) return;\n queueMicrotask(() => {\n if (this.#focused === row && row.isConnected) this.#focused = null;\n });\n };\n\n /**\n * Reconciles authored expansion state and establishes the single tab stop.\n * Idempotent, so a Turbo cache restore / morph re-runs it safely (re-adding\n * the same listener reference is a no-op per the DOM spec).\n */\n override connect(): void {\n this.element.addEventListener(\"focusin\", this.#onFocusIn);\n this.element.addEventListener(\"focusout\", this.#onFocusOut);\n this.#seedFocused();\n this.#reconcileExpansion();\n this.#normalizeSelection();\n this.#normalizeTabStop();\n this.#order = [...this.itemTargets];\n this.#connected = true;\n }\n\n /** Detaches the focus trackers, drops the typeahead buffer and its timer. */\n override disconnect(): void {\n this.element.removeEventListener(\"focusin\", this.#onFocusIn);\n this.element.removeEventListener(\"focusout\", this.#onFocusOut);\n this.#connected = false;\n this.#typeahead.reset();\n this.#timers.clearAll();\n this.#order = [];\n this.#focused = null;\n }\n\n /**\n * Seeds the focus record from the live document. A row can already hold focus\n * when the controller connects (a Turbo restore, or `data-controller` added to\n * a tree the user was already in), and the `focusin` listener alone would\n * never learn about it.\n */\n #seedFocused(): void {\n const active = this.element.ownerDocument.activeElement;\n this.#focused =\n active instanceof HTMLElement && this.element.contains(active)\n ? active.closest<HTMLElement>('[role=\"treeitem\"]')\n : null;\n }\n\n /**\n * Re-normalizes the tab stop when a `treeitem` is added at runtime, so an\n * appended item carrying `tabindex=\"0\"` cannot turn the tree into two Tab\n * stops. Skipped before `connect()`: Stimulus registers the initial targets\n * first, and `connect()` owns the initial tab-stop policy.\n */\n itemTargetConnected(): void {\n if (!this.#connected) return;\n this.#normalizeSelection();\n this.#normalizeTabStop();\n this.#trackOrder();\n }\n\n /**\n * Brings authored `aria-selected` to the shape the APG requires, without\n * changing which item the author chose.\n *\n * Every item gets an explicit value — an absent `aria-selected` means \"not\n * selectable\" in ARIA, so a forgotten attribute hides a selectable row — and a\n * single-select tree keeps at most one `true`, first in DOM order. `connect()`\n * reconciles authored expansion the same way. The scan is the `item` target\n * set: a `role=\"treeitem\"` without the target is outside the contract and is\n * neither counted nor written.\n */\n #normalizeSelection(): void {\n const items = this.itemTargets;\n const selected = items.find((item) => item.getAttribute(\"aria-selected\") === \"true\");\n for (const item of items) {\n item.setAttribute(\"aria-selected\", item === selected ? \"true\" : \"false\");\n }\n }\n\n /**\n * Restores the roving invariants when a `treeitem` leaves at runtime — a Turbo\n * Stream `remove`, a morph, or plain DOM surgery. The mirror of\n * {@link TreeViewController.itemTargetConnected}: without it, removing the row\n * that held `tabindex=\"0\"` leaves every survivor at `-1`, i.e. a tree with\n * **no** Tab stop, and drops DOM focus to `<body>` when the removed subtree\n * held it.\n *\n * The replacement is the nearest surviving *visible* item in the pre-removal\n * order — the one after the gap, else the one before it — which is the row\n * `ArrowDown` would have reached. Removing a non-active item changes nothing.\n * When no visible item survives, a tree with zero Tab stops is the correct end\n * state.\n *\n * Focus is only *restored*, never *stolen*: DOM focus moves solely when it was\n * inside the removed subtree and the document has nowhere left to put it.\n */\n itemTargetDisconnected(item: HTMLElement): void {\n // Stimulus drains the target callbacks for a removed *tree* before calling\n // `disconnect()`; recovering a roving position for a widget that is already\n // gone would move focus for nothing.\n if (!this.#connected || !this.element.isConnected) return;\n const stranded = this.#focusDiedWith(item);\n if (stranded) this.#focused = null;\n if (stranded || this.#roving.activeIndex === -1) {\n const next = this.#neighborOf(item);\n if (next) this.#roving.setActive(this.itemTargets.indexOf(next), { focus: stranded });\n }\n // Drop only the item just reported. One DOM mutation batch arrives as\n // several callbacks, so rebuilding from `itemTargets` here would also erase\n // the recorded position of every sibling whose callback has yet to run.\n this.#order = this.#order.filter((tracked) => tracked !== item);\n }\n\n /**\n * Folds newly connected targets into the DOM-order snapshot, keeping the slot\n * of any item whose disconnect callback has not arrived yet — a morph that\n * both adds and removes rows reports one batch as several callbacks, and\n * {@link TreeViewController.#neighborOf} needs those slots.\n */\n #trackOrder(): void {\n const live = this.itemTargets;\n const pending = this.#order.filter((tracked) => !live.includes(tracked));\n if (pending.length === 0) {\n this.#order = live;\n return;\n }\n // `pending` is in ascending recorded order, so re-inserting each at the\n // index it held rebuilds the pre-mutation sequence.\n const merged = [...live];\n for (const tracked of pending) {\n merged.splice(Math.min(this.#order.indexOf(tracked), merged.length), 0, tracked);\n }\n this.#order = merged;\n }\n\n /**\n * Routes tree keyboard interaction. Because `treeitem`s nest, only the handler\n * on the nearest item to the event target acts; the same keydown bubbling to an\n * ancestor item's handler is ignored to avoid double moves / selections. Keys\n * from a nested interactive control (or an IME composition) are left untouched.\n */\n onKeydown(event: KeyboardEvent): void {\n // A descendant widget that already claimed the key (a grabbed drag handle, a\n // nested custom control) must not ALSO move the roving focus — composition\n // depends on this yield. `#ownerItem` covers the controls a selector can\n // name; this covers the ones it cannot.\n if (event.defaultPrevented) return;\n if (isReservedArrowChord(event)) return;\n if (event.isComposing) return;\n const item = this.#ownerItem(event);\n if (!item) return;\n\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault();\n this.#moveBy(item, 1);\n break;\n case \"ArrowUp\":\n event.preventDefault();\n this.#moveBy(item, -1);\n break;\n // Logical, not physical: APG describes these as \"to the child level\" and\n // \"to the parent level\" — a spatial move — so they follow the writing\n // direction, exactly as horizontal roving does. `isRtl()` is the shared\n // detector; never introduce a second one.\n //\n // Direction comes from the tree, not from `item`: the tree is what lays\n // the rows out, and a row may carry its own `dir` (an LTR path inside an\n // RTL browser is ordinary bidi authoring). Probing the focused row instead\n // would let one row's arrows mean the opposite of its sibling's.\n case \"ArrowRight\":\n event.preventDefault();\n if (isRtl(this.element)) this.#collapseOrLeave(item);\n else this.#expandOrEnter(item);\n break;\n case \"ArrowLeft\":\n event.preventDefault();\n if (isRtl(this.element)) this.#expandOrEnter(item);\n else this.#collapseOrLeave(item);\n break;\n case \"Home\":\n event.preventDefault();\n this.#focusEdge(0);\n break;\n case \"End\":\n event.preventDefault();\n this.#focusEdge(-1);\n break;\n case \"Enter\":\n case \" \":\n event.preventDefault();\n this.#select(item);\n break;\n default:\n if (isTypeaheadKey(event)) {\n event.preventDefault();\n this.#typeaheadTo(item, event.key);\n }\n break;\n }\n }\n\n /** Selects the clicked item (nearest to the target only). */\n onClick(event: Event): void {\n const item = this.#ownerItem(event);\n if (!item) return;\n // The indentation band of a child group is the group's own box, not the\n // parent row: a click that lands there must select nothing.\n const group = this.#childGroup(item);\n if (group?.contains(event.target as Node)) return;\n this.#focusItem(item);\n this.#select(item);\n }\n\n /**\n * Expands or collapses the `treeitem` nearest to the event target. Optional:\n * wire it on a chevron control so pointer users can reach child nodes, which\n * otherwise only `ArrowRight` / `ArrowLeft` can do. A leaf item is a no-op.\n *\n * The toggled row then takes focus and the tab stop, matching what the keyboard\n * path guarantees. Without it, a browser that focuses buttons on mousedown leaves\n * focus on the clicked chevron: arrows would stop working until focus returns to a\n * row, and a decorative `aria-hidden` chevron would hold focus. The synchronous\n * `stimeo--tree-view:toggle` event is observed before this final focus hand-off.\n */\n toggle(event: Event): void {\n const item = (event.target as HTMLElement).closest<HTMLElement>('[role=\"treeitem\"]');\n if (!item || !this.element.contains(item)) return;\n if (!this.#childGroup(item)) return;\n this.#setExpanded(item, !this.#isExpanded(item));\n this.#focusItem(item);\n }\n\n /**\n * The item a raw event belongs to, or `null` when the tree must not act: the\n * event came from a nested `treeitem` (it bubbled to an ancestor's handler) or\n * from an interactive control inside the item.\n */\n #ownerItem(event: Event): HTMLElement | null {\n const item = event.currentTarget as HTMLElement;\n const target = event.target as HTMLElement;\n if (target.closest('[role=\"treeitem\"]') !== item) return null;\n const control = target.closest<HTMLElement>(NESTED_INTERACTIVE);\n if (control && item.contains(control)) return null;\n if (isInteractiveHost(target)) return null;\n return item;\n }\n\n /**\n * The surviving visible item that inherits `removed`'s position: the next one\n * in the pre-removal order, else the previous one. Falls back to the first\n * visible item when `removed` is not in that order — a target that arrived and\n * left between two callbacks — and to `undefined` when nothing visible is left.\n */\n #neighborOf(removed: HTMLElement): HTMLElement | undefined {\n const visible = this.#visibleItems;\n const index = this.#order.indexOf(removed);\n for (let at = index + 1; at < this.#order.length; at += 1) {\n const candidate = this.#order[at];\n if (candidate && visible.includes(candidate)) return candidate;\n }\n for (let at = index - 1; at >= 0; at -= 1) {\n const candidate = this.#order[at];\n if (candidate && visible.includes(candidate)) return candidate;\n }\n return visible[0];\n }\n\n /**\n * Whether DOM focus went down with `removed`: it sat on that row (or inside\n * it) and the document now has nowhere to put focus. A browser falls back to\n * `<body>`; a still-referenced but detached `activeElement` is the same\n * condition seen from the other side. Focus that landed on a real element —\n * inside this tree or anywhere else on the page — is left alone, so a removal\n * can never steal it.\n */\n #focusDiedWith(removed: HTMLElement): boolean {\n const focused = this.#focused;\n if (!focused || (focused !== removed && !removed.contains(focused))) return false;\n const doc = this.element.ownerDocument;\n const active = doc.activeElement;\n if (active === null || active === doc.body || active === doc.documentElement) return true;\n return !active.isConnected;\n }\n\n /** Moves focus to the next (`delta=1`) or previous visible item, if any. */\n #moveBy(item: HTMLElement, delta: number): void {\n const visible = this.#visibleItems;\n const current = visible.indexOf(item);\n // An item outside the visible set has no neighbors; without this guard a\n // `-1 + 1` index would warp focus to the top of the tree.\n if (current === -1) return;\n const next = visible[current + delta];\n if (next) this.#focusItem(next);\n }\n\n /**\n * Expands a collapsed parent, else steps into the first child.\n *\n * Bound to `ArrowRight` under LTR and `ArrowLeft` under RTL — the caller picks;\n * this method only knows \"toward the child level\".\n */\n #expandOrEnter(item: HTMLElement): void {\n const group = this.#childGroup(item);\n if (!group) return;\n if (!this.#isExpanded(item)) {\n this.#setExpanded(item, true);\n return;\n }\n // Never move the only tab stop into a subtree a consumer hid directly.\n if (group.hidden) return;\n // The first child that is not itself hidden. Matched on the element rather\n // than through `#visibleItems`, so a `role=\"treeitem\"` without the item\n // target still receives focus (the fallback in `#focusItem`).\n const firstChild = Array.from(\n group.querySelectorAll<HTMLElement>(':scope > [role=\"treeitem\"]'),\n ).find((child) => !child.hidden);\n if (firstChild) this.#focusItem(firstChild);\n }\n\n /** `ArrowLeft`: collapse an expanded parent, else step out to the parent item. */\n #collapseOrLeave(item: HTMLElement): void {\n if (this.#childGroup(item) && this.#isExpanded(item)) {\n this.#setExpanded(item, false);\n return;\n }\n const parent = this.#parentItem(item);\n if (parent) this.#focusItem(parent);\n }\n\n /** Focuses the first (`0`) or last (`-1`) visible item. */\n #focusEdge(index: number): void {\n const visible = this.#visibleItems;\n const target = index < 0 ? visible[visible.length - 1] : visible[index];\n if (target) this.#focusItem(target);\n }\n\n /**\n * Whether the item is disabled — **its own attribute or an ancestor's**.\n *\n * ARIA is explicit that the state carries down: \"The state of being disabled\n * applies to the current element *and all focusable descendant elements* of the\n * element on which the `aria-disabled` attribute is applied.\" A tree is the only\n * pattern here whose items nest, so it is the only place the inheritance is\n * observable — but the rule is the shared one.\n *\n * The walk stops at the controller element: an `aria-disabled` on the tree root\n * would otherwise disable every row, and a disabled *tree* is the consumer's\n * call to make with `inert`, not something this controller infers.\n */\n #isDisabled(item: HTMLElement): boolean {\n let current: HTMLElement | null = item;\n while (current && current !== this.element) {\n if (current.getAttribute(\"aria-disabled\") === \"true\") return true;\n current = current.parentElement;\n }\n return false;\n }\n\n /**\n * Applies single selection and dispatches `select`. An `aria-disabled=\"true\"`\n * item stays focusable (APG keeps disabled nodes reachable) but is never\n * activated, so consumers see no `select` for it.\n */\n #select(item: HTMLElement): void {\n if (this.#isDisabled(item)) return;\n for (const candidate of this.itemTargets) {\n candidate.setAttribute(\"aria-selected\", candidate === item ? \"true\" : \"false\");\n }\n this.dispatch(\"select\", { detail: { item } });\n }\n\n /** Updates expansion, reconciles a collapsed subtree, then synchronously dispatches `toggle`. */\n #setExpanded(item: HTMLElement, expanded: boolean): void {\n const group = this.#childGroup(item);\n if (!group) return;\n item.setAttribute(\"aria-expanded\", String(expanded));\n group.hidden = !expanded;\n if (!expanded) this.#escapeCollapsedSubtree(item, group);\n this.dispatch(\"toggle\", { detail: { item, expanded } });\n }\n\n /**\n * Reconciles focus and the roving tab stop before a synchronous `toggle` event\n * observes a newly collapsed group. If DOM focus remains inside the group,\n * both move to the parent. Otherwise only a stranded tab stop moves, preserving\n * DOM focus. A native chevron click takes this path when mousedown has already\n * focused the button. {@link TreeViewController.toggle} completes its focus\n * hand-off after this reconciliation and event dispatch.\n */\n #escapeCollapsedSubtree(item: HTMLElement, group: HTMLElement): void {\n const active = document.activeElement;\n if (active instanceof HTMLElement && group.contains(active)) {\n this.#focusItem(item);\n return;\n }\n // Focus is elsewhere on the page: relocate the tab stop without stealing it.\n const activeIndex = this.#roving.activeIndex;\n const tabbable = activeIndex === -1 ? undefined : this.itemTargets[activeIndex];\n if (!tabbable || !group.contains(tabbable)) return;\n const index = this.itemTargets.indexOf(item);\n if (index !== -1) this.#roving.setActive(index);\n }\n\n /** Makes `item` the single tab stop and moves DOM focus to it. */\n #focusItem(item: HTMLElement): void {\n const index = this.itemTargets.indexOf(item);\n if (index !== -1) {\n this.#roving.setActive(index, { focus: true });\n return;\n }\n // A `role=\"treeitem\"` without the item target is outside roving bookkeeping,\n // yet DOM structure still reaches it as a parent / first child. Focus it so\n // the tree never silently stops responding; `tabindex=\"-1\"` keeps the single\n // Tab stop on a real target.\n //\n // Checked first: `hidden` and natively disabled elements swallow `focus()`,\n // and moving toward one would leave the caret where it was with no signal.\n // Refusing is the honest outcome here — there is no better destination to\n // fall back to, since roving already declined this node.\n if (!canTakeFocus(item)) return;\n item.tabIndex = -1;\n item.focus();\n }\n\n /** Advances the typeahead query and focuses the next matching visible item. */\n #typeaheadTo(item: HTMLElement, char: string): void {\n const query = this.#typeahead.push(char);\n // Only visible rows are candidates: an item inside a collapsed group is not\n // reachable by arrow keys either, so typeahead must not tunnel into one.\n const visible = this.#visibleItems;\n const index = findTypeaheadMatch(visible, visible.indexOf(item), query, (candidate) =>\n this.#label(candidate),\n );\n // A miss reads back as `undefined` (`visible[-1]`), so the guard below is the\n // only branch — spelling the `-1` out again would add an unreachable one.\n const match = visible[index];\n if (match) this.#focusItem(match);\n }\n\n /**\n * Aligns each parent's `aria-expanded` with its child `group`'s `hidden`.\n * `aria-expanded` wins when authored (it is what assistive tech reads); a\n * parent without it derives one from `hidden` (establish-only-when-absent, so\n * a restored snapshot is never clobbered). Disagreeing markup would otherwise\n * strand the only tab stop on an invisible item.\n */\n #reconcileExpansion(): void {\n for (const item of this.itemTargets) {\n const group = this.#childGroup(item);\n if (!group) continue;\n if (item.hasAttribute(\"aria-expanded\")) {\n group.hidden = !this.#isExpanded(item);\n } else {\n item.setAttribute(\"aria-expanded\", String(!group.hidden));\n }\n }\n }\n\n /**\n * Leaves exactly one item tabbable. An existing, still-visible tab stop wins\n * (the DOM is the source of truth after a Turbo restore, so the user's roving\n * position survives); otherwise the selected item takes it (APG: a single-select\n * tree puts initial focus on its selected node), else the first visible item.\n */\n #normalizeTabStop(): void {\n const items = this.itemTargets;\n const visible = this.#visibleItems;\n const activeIndex = this.#roving.activeIndex;\n const active = activeIndex === -1 ? undefined : items[activeIndex];\n if (active && visible.includes(active)) {\n this.#roving.setActive(activeIndex);\n return;\n }\n const selected = visible.find((item) => item.getAttribute(\"aria-selected\") === \"true\");\n const next = selected ?? visible[0];\n this.#roving.setActive(next ? items.indexOf(next) : -1);\n }\n\n /**\n * The visible items: those with no `hidden` ancestor up to the tree, and not\n * `hidden` themselves. A hidden row is out of the move set — it can hold\n * neither DOM focus nor the only Tab stop — and neither can anything nested\n * under one, which is why the walk tests every ancestor rather than only the\n * collapsed `role=\"group\"` containers.\n */\n get #visibleItems(): HTMLElement[] {\n return this.itemTargets.filter((item) => {\n let node: HTMLElement | null = item;\n while (node && node !== this.element) {\n if (node.hidden) return false;\n node = node.parentElement;\n }\n return true;\n });\n }\n\n /** The child `group` owned directly by `item`, or `null` for a leaf. */\n #childGroup(item: HTMLElement): HTMLElement | null {\n return item.querySelector<HTMLElement>(':scope > [role=\"group\"]');\n }\n\n /** The nearest ancestor `treeitem`, or `null` at the root level. */\n #parentItem(item: HTMLElement): HTMLElement | null {\n return item.parentElement?.closest<HTMLElement>('[role=\"treeitem\"]') ?? null;\n }\n\n /** Whether a parent item is currently expanded. */\n #isExpanded(item: HTMLElement): boolean {\n return item.getAttribute(\"aria-expanded\") === \"true\";\n }\n\n /**\n * `item`'s name for typeahead. The fallback is the item's *own* text: a parent\n * row's nested child group is rendered inside it, and folding a whole subtree\n * into one name would let \"readme\" match the folder containing it.\n */\n #label(item: HTMLElement): string {\n return typeaheadLabel(item, () => {\n let text = \"\";\n for (const node of Array.from(item.childNodes)) {\n if (node.nodeType === Node.ELEMENT_NODE && (node as Element).matches('[role=\"group\"]')) {\n continue;\n }\n text += node.textContent ?? \"\";\n }\n return text;\n });\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/utils/logical_scroll.ts","../../src/utils/arrow_step.ts","../../src/utils/field_mirror.ts","../../src/utils/focus_candidate.ts","../../src/utils/interactive_host.ts","../../src/utils/microtask_coalescer.ts","../../src/utils/roving_tabindex.ts","../../src/utils/safe_timeout.ts","../../src/utils/typeahead.ts","../../src/controllers/tree_view_controller.ts"],"names":[],"mappings":"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","file":"tree_view_controller.js","sourcesContent":["/** 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","import { isRtl } from \"./logical_scroll\";\n\n/**\n * Turns an arrow key into a **logical** step: `+1` for \"next\", `-1` for\n * \"previous\", `0` when the key names neither.\n *\n * APG defines the horizontal pair as *next / previous* and says a vertical\n * arrangement swaps in Down/Up for the same meaning — so the pair is one axis's\n * spelling of an order, and the order reverses with the writing direction. Only\n * the horizontal pair reverses. Down/Up name an axis the writing direction does\n * not mirror, and returning them unchanged is the point: many controllers fold\n * both pairs into one branch, where swapping the branches under RTL would flip\n * the vertical axis too — a bug that reads as \"the arrows work\" until someone\n * presses Down.\n *\n * **Direction is read from the element the caller passes, which should be the\n * container that lays the items out** — not the focused child. A child may carry\n * its own `dir` (an LTR input inside an RTL form is ordinary authoring), and\n * probing per handler makes two handlers disagree at the boundary between them.\n *\n * This decides direction only. Whether the axis is even active (an\n * `orientation=\"horizontal\"` widget ignoring Down/Up), how far the step lands,\n * and what wrapping does all stay with the caller.\n *\n * **It encodes the list-order convention: `ArrowDown` is *next*.** Widgets that\n * pair the arrows by *value* instead — `ArrowUp` meaning \"more\", as a rating or a\n * slider does — must not use this, or their vertical axis inverts. Reverse the\n * horizontal pair on its own there.\n *\n * @example\n * ```ts\n * const step = logicalArrowStep(event.key, this.element);\n * if (step === 0) return;\n * this.#roving.setActive(rovingMove(current, length, step, \"wrap\"), { focus: true });\n * ```\n */\nexport function logicalArrowStep(key: string, element: Element): 1 | -1 | 0 {\n if (key === \"ArrowDown\") return 1;\n if (key === \"ArrowUp\") return -1;\n if (key !== \"ArrowRight\" && key !== \"ArrowLeft\") return 0;\n const forward = isRtl(element) ? \"ArrowLeft\" : \"ArrowRight\";\n return key === forward ? 1 : -1;\n}\n\n/**\n * Rewrites `key` so an existing LTR-shaped branch keeps working under RTL:\n * `ArrowRight` and `ArrowLeft` trade places, everything else passes through.\n *\n * The alternative — negating a delta — silently breaks handlers whose two\n * horizontal branches are **not mirror images**. A grid that clamps one edge but\n * not the other, or a segmented field guarding `index > 0` on one side and\n * `index < length - 1` on the other, ends up applying the wrong guard to the\n * wrong direction. Swapping the key leaves each branch, guards and all, exactly\n * where its author put it.\n *\n * Same rule as {@link logicalArrowStep} about which element to read: pass the\n * container that lays the items out, not the focused child.\n *\n * @example\n * ```ts\n * switch (logicalArrowKey(event.key, this.element)) {\n * case \"ArrowLeft\": // \"previous\" — whatever direction that is on screen\n * ```\n */\nexport function logicalArrowKey(key: string, element: Element): string {\n if (key !== \"ArrowRight\" && key !== \"ArrowLeft\") return key;\n if (!isRtl(element)) return key;\n return key === \"ArrowRight\" ? \"ArrowLeft\" : \"ArrowRight\";\n}\n\n/** Modifiers a widget may claim on an arrow key, named for the `allow` list. */\nexport type ArrowModifier = \"alt\" | \"ctrl\" | \"meta\" | \"shift\";\n\n/**\n * True when an arrow key arrived carrying a modifier the widget must leave to\n * the browser: return without calling `preventDefault()` and without moving any\n * state.\n *\n * A bare arrow belongs to the widget; a chorded one usually does not.\n * `Alt`/`Meta` plus a horizontal arrow is history back/forward on every desktop\n * browser, and a widget that swallows it makes the shortcut work or not\n * depending on where focus happens to sit — a coin-flip the user cannot see.\n *\n * `allow` is for the combinations APG assigns to a pattern **and the widget\n * actually implements** — today only Combobox's optional `Alt+Down`/`Alt+Up`.\n * Listing one the widget does not implement defeats the point: the chord then\n * runs the plain-arrow branch, which is exactly what this guard exists to stop.\n * Non-arrow keys return `false`, so chorded letters and\n * `Control+Home`/`Control+End` are untouched.\n *\n * @example\n * ```ts\n * if (isReservedArrowChord(event)) return;\n * ```\n */\nexport function isReservedArrowChord(\n event: KeyboardEvent,\n allow: readonly ArrowModifier[] = [],\n): boolean {\n if (!event.key.startsWith(\"Arrow\")) return false;\n return (\n (event.altKey && !allow.includes(\"alt\")) ||\n (event.ctrlKey && !allow.includes(\"ctrl\")) ||\n (event.metaKey && !allow.includes(\"meta\")) ||\n (event.shiftKey && !allow.includes(\"shift\"))\n );\n}\n\n/**\n * True when a press arrived carrying any modifier, for the keys the browser and\n * the OS own outright: return without calling `preventDefault()` and without\n * moving any state.\n *\n * `Control+Home` and `Control+End` jump the document to its ends, and a widget\n * that swallows them makes the shortcut work or not depending on where focus\n * happens to sit. {@link isReservedArrowChord} answers the same question for the\n * arrows, but returns `false` for every other key so that this one can decide.\n *\n * There is no `allow` list here on purpose. APG assigns no modifier chord to\n * `Home`/`End`, so a widget that wanted one would be claiming a combination the\n * pattern never gave it.\n *\n * @example\n * ```ts\n * case \"Home\":\n * case \"End\":\n * if (hasModifierChord(event)) return;\n * ```\n */\nexport function hasModifierChord(event: KeyboardEvent): boolean {\n return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;\n}\n","/**\n * The hidden form fields a widget keeps in step with the state it owns.\n *\n * A widget whose value lives in ARIA attributes submits nothing on its own, so\n * it mirrors that value into `<input type=\"hidden\">` — one field for a single\n * value, a container of generated fields for a set. The mirror is one-way: the\n * widget's own state stays the source of truth and the field is what the form\n * reads.\n *\n * The write and the report are separate calls because they answer to different\n * rules. A mirror is refreshed on connect, after a morph, and whenever the\n * widget repairs itself — none of which a form should treat as an edit. Only\n * the user's own commit reports one, which is why {@link commitField} is never\n * folded into the writes.\n */\n\n/** How a widget's generated fields identify themselves to the form. */\nexport interface GeneratedFieldOptions {\n /** The `name` every generated input submits under. */\n readonly name: string;\n /** The `form` id, for a widget that sits outside the form it submits to. */\n readonly form?: string;\n}\n\n/**\n * Mirrors one value into a hidden field.\n *\n * @returns Whether the field's value moved.\n *\n * @example\n * ```ts\n * if (writeField(this.fieldTarget, String(value)) && userDriven) {\n * commitField(this.fieldTarget);\n * }\n * ```\n */\nexport function writeField(field: HTMLInputElement, value: string): boolean {\n if (field.value === value) return false;\n field.value = value;\n return true;\n}\n\n/**\n * Mirrors a set of values into `container` as generated hidden fields, in the\n * order given.\n *\n * A container that already submits exactly this set is left untouched, children\n * and all — the mirror is refreshed far more often than it moves (every connect,\n * morph and repair), and a rebuild that changes nothing is still a DOM mutation\n * a consumer's observer and a `:empty` rule can see.\n *\n * @returns Whether the submitted set moved — its values, their order, the\n * `name`, or the `form`.\n */\nexport function writeFields(\n container: HTMLElement,\n values: readonly string[],\n { name, form = \"\" }: GeneratedFieldOptions,\n): boolean {\n const current = [...container.children];\n const submits =\n current.length === values.length &&\n current.every(\n (child, index) =>\n child instanceof HTMLInputElement &&\n child.type === \"hidden\" &&\n child.value === values[index] &&\n child.name === name &&\n (child.getAttribute(\"form\") ?? \"\") === form,\n );\n if (submits) return false;\n\n container.replaceChildren(\n ...values.map((value) => {\n const input = document.createElement(\"input\");\n input.type = \"hidden\";\n input.name = name;\n input.value = value;\n if (form !== \"\") input.setAttribute(\"form\", form);\n return input;\n }),\n );\n return true;\n}\n\n/**\n * Reports a committed value from `target` the way a native form control does:\n * one bubbling, non-cancelable `change`.\n *\n * Call it only for a move the user made. Form-level behaviors listen for this —\n * validation re-checks, auto-submit — and a mirror refreshed by a morph or a\n * repair is not something the user submitted.\n *\n * `target` is the element that carries the value: the hidden field for a single\n * mirrored value, the container for a generated set (whose own inputs are\n * replaced on every write), or a widget's own native control where that is what\n * the form reads.\n */\nexport function commitField(target: HTMLElement): void {\n target.dispatchEvent(new Event(\"change\", { bubbles: true }));\n}\n\n/**\n * Reports an edit a widget made on the user's behalf, the way the browser\n * reports its own: one bubbling `input`, then one bubbling `change`.\n *\n * This is for a widget that owns the stepping or picking its *own* native\n * control would otherwise do — writing `control.value` from script fires\n * nothing, so a form listening for either event never hears the edit. Use\n * {@link commitField} instead where the element is a mirror of state held\n * elsewhere: a hidden field has no native edit to imitate, so it reports the\n * commit alone.\n *\n * Call it only for a move the user made, and only where the widget really did\n * the writing — an edit the browser already reported would be doubled.\n */\nexport function commitEdit(control: HTMLElement): void {\n control.dispatchEvent(new Event(\"input\", { bubbles: true }));\n control.dispatchEvent(new Event(\"change\", { bubbles: true }));\n}\n","/**\n * Whether a disabled `<fieldset>` ancestor actually reaches `control`.\n *\n * HTML exempts the contents of a fieldset's **first direct-child `<legend>`**, and\n * the exemption is per fieldset — a control legal in one legend can still be\n * disabled by a second, outer fieldset, so the walk continues upward.\n *\n * Exported on its own for callers that need the disabled-inheritance rule without\n * the rest of {@link canTakeFocus} — a control's availability check, or a `hidden`\n * walk bounded at a widget root, which is a different rule to compose with.\n */\nexport function inheritsFieldsetDisabled(control: HTMLElement): boolean {\n let fieldset: Element | null = control.closest(\"fieldset[disabled]\");\n while (fieldset) {\n const legend = Array.from(fieldset.children).find((child) => child.tagName === \"LEGEND\");\n if (!legend?.contains(control)) return true;\n fieldset = fieldset.parentElement?.closest(\"fieldset[disabled]\") ?? null;\n }\n return false;\n}\n\n/**\n * Whether an element can actually take focus, checked **before** `focus()` runs.\n *\n * A controller that must move focus off a control it is about to disable or hide\n * picks a destination and calls `focus()` on it. If that destination cannot take\n * focus, the call **fails silently**: `hidden` and natively `disabled` elements\n * swallow it, so the caret stays in the subtree that is disappearing and lands on\n * `<body>` a frame later — the exact outcome the rescue exists to prevent, minus\n * any signal that it happened.\n *\n * Testing after the fact is the obvious alternative and is deliberately not the\n * rule. Reading `document.activeElement` back only works in a real browser, and\n * looping over candidates that way performs a real focus move per failure —\n * observable to assistive technology. Checking first costs nothing and catches\n * the cases that actually occur.\n *\n * **`aria-disabled` is not disqualifying.** It is the attribute an author uses\n * for a control that must stay *discoverable*, and the roving contract keeps\n * such items reachable. Only the four conditions that make the platform refuse\n * focus are checked: `hidden` / `inert` (an ancestor's counts),\n * `input[type=\"hidden\"]`, the native `disabled` property, and `disabled`\n * inherited from an ancestor `fieldset`. CSS-only invisibility is handled by\n * {@link isRenderedForFocus} when a consumer needs sequential-focus semantics.\n *\n * Reading `:disabled` instead of walking the fieldset chain would be shorter, but\n * that pseudo-class is not evaluated consistently outside real browsers and this\n * has to be right headlessly too. happy-dom in particular focuses a `<button>`\n * inside a disabled fieldset where a real engine refuses, so the inheritance is\n * spelled out rather than delegated.\n *\n * What a consumer does when nothing survives is its own call: some fall back to\n * their landmark, while a widget whose caret already sits somewhere legitimate\n * refuses the move outright rather than relocating it.\n *\n * @example\n * ```ts\n * const target = candidates.find(canTakeFocus);\n * if (target) target.focus();\n * else {\n * this.#tabindex.lend(this.element); // nothing left: fall back to the landmark\n * this.element.focus();\n * }\n * ```\n *\n * @param element - the candidate destination\n */\nexport function canTakeFocus(element: HTMLElement): boolean {\n if (element.closest(\"[hidden], [inert]\")) return false;\n if (element instanceof HTMLInputElement && element.type === \"hidden\") return false;\n if (!(\"disabled\" in element)) return true;\n if ((element as HTMLElement & { disabled: boolean }).disabled) return false;\n return !inheritsFieldsetDisabled(element);\n}\n\n/** Elements whose semantics or authored attributes can place them in sequential focus order. */\nexport const TAB_STOP_CANDIDATE_SELECTOR = [\n \"a[href]\",\n \"area[href]\",\n \"button\",\n \"input\",\n \"select\",\n \"textarea\",\n \"summary\",\n \"iframe\",\n \"audio[controls]\",\n \"video[controls]\",\n \"[tabindex]\",\n \"[contenteditable]\",\n].join(\",\");\n\n/** Optional browser visibility API used to exclude CSS-hidden candidates. */\ninterface VisibilityCheckable {\n checkVisibility?: (options?: { visibilityProperty?: boolean }) => boolean;\n}\n\n/** Whether CSS visibility allows an otherwise eligible element to participate in focus order. */\nexport function isRenderedForFocus(element: HTMLElement): boolean {\n const check = (element as HTMLElement & VisibilityCheckable).checkVisibility;\n return typeof check === \"function\" ? check.call(element, { visibilityProperty: true }) : true;\n}\n\n/** Parses an authored `tabindex`; invalid syntax has no explicit focus-order meaning. */\nfunction authoredTabindex(element: HTMLElement): number | null {\n const value = element.getAttribute(\"tabindex\");\n if (value === null || !/^[+-]?\\d+$/.test(value.trim())) return null;\n return Number(value);\n}\n\n/** Whether the element's native semantics place it in sequential focus order. */\nfunction hasNativeTabStop(element: HTMLElement): boolean {\n if (element instanceof HTMLAnchorElement || element instanceof HTMLAreaElement) {\n return element.hasAttribute(\"href\");\n }\n if (\n element instanceof HTMLButtonElement ||\n element instanceof HTMLSelectElement ||\n element instanceof HTMLTextAreaElement\n ) {\n return true;\n }\n if (element instanceof HTMLInputElement) return element.type !== \"hidden\";\n if (element instanceof HTMLIFrameElement) return true;\n if (element.tagName === \"AUDIO\" || element.tagName === \"VIDEO\") {\n return element.hasAttribute(\"controls\");\n }\n if (element instanceof HTMLElement && element.tagName === \"SUMMARY\") {\n const details = element.parentElement;\n return (\n details instanceof HTMLDetailsElement &&\n Array.from(details.children).find((child) => child.tagName === \"SUMMARY\") === element\n );\n }\n return false;\n}\n\n/** Whether an explicit `contenteditable` value creates an editable tab stop. */\nfunction hasEditableTabStop(element: HTMLElement): boolean {\n const value = element.getAttribute(\"contenteditable\")?.toLowerCase();\n return value === \"\" || value === \"true\" || value === \"plaintext-only\";\n}\n\n/**\n * Whether an element is a usable sequential Tab stop right now.\n *\n * Native semantics, authored `tabindex`, editable hosts, inherited disabled state,\n * HTML `hidden`/`inert`, and CSS visibility are evaluated together. `aria-disabled`\n * remains focusable because it communicates unavailability without removing the\n * control from discovery order.\n */\nexport function isTabStop(element: HTMLElement): boolean {\n if (!canTakeFocus(element) || !isRenderedForFocus(element)) return false;\n\n const tabindex = authoredTabindex(element);\n if (tabindex !== null) return tabindex >= 0;\n return hasNativeTabStop(element) || hasEditableTabStop(element);\n}\n\n/** Returns every usable sequential Tab stop below `root` in document order. */\nexport function tabStopsWithin(root: ParentNode): HTMLElement[] {\n return Array.from(root.querySelectorAll<HTMLElement>(TAB_STOP_CANDIDATE_SELECTOR)).filter(\n isTabStop,\n );\n}\n\n/** Returns the first usable sequential Tab stop below `root`, if one exists. */\nexport function firstTabStop(root: ParentNode): HTMLElement | null {\n for (const candidate of root.querySelectorAll<HTMLElement>(TAB_STOP_CANDIDATE_SELECTOR)) {\n if (isTabStop(candidate)) return candidate;\n }\n return null;\n}\n\n/** Whether `root` contains at least one usable sequential Tab stop. */\nexport function hasTabStop(root: ParentNode): boolean {\n return firstTabStop(root) !== null;\n}\n","/**\n * Native interactive elements whose built-in activation must not be replaced\n * wholesale by a controller. Deliberately excludes `[tabindex]`: a generic\n * element made focusable is the supported host for several headless patterns.\n * Contenteditable is handled by {@link isInteractiveHost} because its state is\n * inherited and its keywords are ASCII-case-insensitive.\n */\nexport const INTERACTIVE_HOST_SELECTOR =\n \"button, input, select, textarea, label, a[href], area[href], summary, details, audio[controls], video[controls], iframe, object, embed\";\n\n/**\n * Whether an element owns native activation or an inherited editing surface.\n *\n * The `contenteditable` missing and invalid states inherit. Walking explicitly\n * also keeps `contenteditable=\"false\"` as a real boundary inside an editable\n * ancestor instead of treating any ancestor attribute as decisive.\n */\nexport function isInteractiveHost(element: HTMLElement): boolean {\n if (element.matches(INTERACTIVE_HOST_SELECTOR)) return true;\n\n let current: HTMLElement | null = element;\n while (current) {\n const raw = current.getAttribute(\"contenteditable\");\n if (raw !== null) {\n const value = raw.trim().toLowerCase();\n if (value === \"false\") return false;\n if (value === \"\" || value === \"true\" || value === \"plaintext-only\") return true;\n }\n current = current.parentElement;\n }\n return false;\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 * This file's own doc block is dropped from `dist`, but every member comment is\n * inlined into each consumer entry (`tsup` builds with `splitting: false`), so\n * rationale belongs here and only the contract belongs on the members.\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","/**\n * Low-level roving-tabindex primitive shared by composite-widget controllers.\n *\n * The APG roving-tabindex pattern keeps a composite widget a single Tab stop:\n * exactly one item is in the Tab sequence (`tabindex=\"0\"`) while the rest are\n * removed from it (`tabindex=\"-1\"`), and the arrow keys move both DOM focus and\n * that single tabbable position together. {@link RovingTabindex} owns *only* that\n * mechanical bookkeeping — \"which one item is tabbable, and move focus there\".\n *\n * It is intentionally **policy-free**. Orientation, wrapping vs. clamping,\n * selection-follows-focus, typeahead, and `Home`/`End` semantics differ per APG\n * pattern (Radio Group, Toolbar, Rating, …); folding them into one helper would\n * flatten those widgets to a lowest common denominator and lose each pattern's\n * correctness. Those decisions therefore stay in each controller, which calls\n * {@link RovingTabindex.setActive} with an index it computed itself (optionally\n * via the pure {@link rovingMove} helper).\n *\n * @remarks\n * Items are read lazily through a getter so a controller can add or remove\n * targets (Stimulus re-scans the DOM) without re-wiring this helper. A caller\n * that already captured one event-scoped snapshot may pass it to\n * {@link RovingTabindex.setActive}, avoiding a second live-target scan while\n * preserving one coherent collection for the whole operation.\n */\nexport class RovingTabindex {\n /** Returns the current ordered item elements; called on every operation. */\n readonly #getItems: () => HTMLElement[];\n\n /**\n * @param getItems - Returns the current ordered item elements. Called on every\n * operation so the live target list is always used.\n */\n constructor(getItems: () => HTMLElement[]) {\n this.#getItems = getItems;\n }\n\n /** Index of the currently tabbable item (`tabindex=\"0\"`), or `-1` if none. */\n get activeIndex(): number {\n return this.#getItems().findIndex((item) => item.tabIndex === 0);\n }\n\n /**\n * Makes exactly the item at `index` tabbable (`tabindex=\"0\"`) and removes every\n * other item from the Tab sequence (`tabindex=\"-1\"`). An out-of-range `index`\n * (e.g. `-1`) leaves all items at `-1`, which a controller can use to express\n * \"nothing is currently tabbable\".\n *\n * @param index - Position of the item to make tabbable.\n * @param options - Pass `{ focus: true }` to also move DOM focus to that item,\n * and `items` to reuse an event-scoped collection snapshot.\n */\n setActive(\n index: number,\n options: { focus?: boolean; items?: readonly HTMLElement[] } = {},\n ): void {\n const { focus = false } = options;\n const items = options.items ?? this.#getItems();\n items.forEach((item, i) => {\n item.tabIndex = i === index ? 0 : -1;\n });\n if (focus) items[index]?.focus();\n }\n}\n\n/** Edge behavior for {@link rovingMove}: cycle past the ends, or stop at them. */\nexport type RovingWrap = \"wrap\" | \"clamp\";\n\n/**\n * Pure helper that resolves the target index for a one-step directional move.\n *\n * Keyboard/orientation mapping stays in the caller: it decides that a key means\n * `delta` `+1` (next) or `-1` (previous) and whether the widget should `\"wrap\"`\n * (Radio Group, Toolbar) or `\"clamp\"` at the ends.\n *\n * @param current - The index focus is moving from.\n * @param length - Number of items in the set.\n * @param delta - `+1` to move to the next item, `-1` for the previous.\n * @param wrap - `\"wrap\"` cycles around the ends; `\"clamp\"` stops at them.\n * @returns The resolved index, or `-1` when there are no items.\n */\nexport function rovingMove(\n current: number,\n length: number,\n delta: number,\n wrap: RovingWrap,\n): number {\n if (length === 0) return -1;\n const next = current + delta;\n if (wrap === \"wrap\") return (next + length) % length;\n return Math.min(length - 1, Math.max(0, next));\n}\n","/**\n * Self-cleaning timer registries shared by Stimeo controllers.\n *\n * Stimulus controllers frequently schedule `setTimeout` / `setInterval` work\n * (auto-dismiss, debouncing, polling). When the element leaves the DOM — a\n * Turbo Drive navigation, a Turbo Stream replacement, or any `disconnect()` —\n * orphaned timers keep firing against a detached controller, leaking memory and\n * mutating stale state. {@link SafeTimeout} and {@link SafeInterval} track every\n * timer they create so a single {@link TimerRegistry.clearAll | clearAll()} call\n * in `disconnect()` tears them all down.\n *\n * These are intentionally low-level primitives: they own *registration and\n * cleanup only*. Higher-level policy (pause/resume, remaining-time accounting)\n * stays in the individual controllers so per-widget semantics are not flattened\n * into a lowest-common-denominator helper.\n */\n\n/**\n * Largest delay a timer can hold: the platform stores it in a 32-bit signed\n * integer, and anything larger overflows to `1`, so a delay meant to be far in\n * the future fires almost immediately. A declared delay above this bound names\n * no delay at all, and a controller reading one falls back to its default.\n */\nexport const MAX_TIMER_DELAY_MS = 2_147_483_647;\n\n/**\n * Shared registry bookkeeping for the timeout/interval variants.\n *\n * Subclasses provide the scheduling primitive ({@link schedule}) and its matching\n * canceller ({@link cancel}); this base owns the set of live ids plus the\n * per-id and bulk teardown shared by both.\n */\nabstract class TimerRegistry {\n /** Live timer ids that have not yet been cleared (or, for timeouts, fired). */\n protected readonly ids = new Set<number>();\n\n /** Schedules the underlying platform timer and returns its id. */\n protected abstract schedule(callback: () => void, delay: number): number;\n\n /** Cancels the underlying platform timer for `id`. */\n protected abstract cancel(id: number): void;\n\n /**\n * Cancels a single tracked timer.\n *\n * No-ops if the id is unknown (already cleared, fired, or never owned by this\n * registry), so callers can clear defensively without guarding.\n */\n clear(id: number): void {\n if (this.ids.delete(id)) {\n this.cancel(id);\n }\n }\n\n /**\n * Cancels every tracked timer. Call this from a controller's `disconnect()`\n * to guarantee no timer outlives the element.\n */\n clearAll(): void {\n for (const id of this.ids) {\n this.cancel(id);\n }\n this.ids.clear();\n }\n\n /** Number of timers currently tracked (pending). */\n get size(): number {\n return this.ids.size;\n }\n}\n\n/**\n * `setTimeout` wrapper that auto-forgets each timer once it fires and supports\n * bulk teardown on disconnect.\n *\n * @example\n * ```ts\n * #timers = new SafeTimeout();\n *\n * connect() {\n * this.#timers.set(() => this.dismiss(), 5000);\n * }\n *\n * disconnect() {\n * this.#timers.clearAll();\n * }\n * ```\n */\nexport class SafeTimeout extends TimerRegistry {\n /**\n * Schedules `callback` after `delay` ms and returns the timer id.\n *\n * The id is removed from the registry automatically when the timeout fires,\n * so {@link TimerRegistry.size | size} reflects only still-pending timers.\n */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(() => {\n this.ids.delete(id);\n callback();\n }, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setTimeout(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearTimeout(id);\n }\n}\n\n/**\n * `setInterval` wrapper that tracks every interval for bulk teardown on\n * disconnect. Unlike {@link SafeTimeout}, intervals are retained until they are\n * explicitly cleared because they fire repeatedly.\n *\n * @example\n * ```ts\n * #intervals = new SafeInterval();\n *\n * connect() {\n * this.#intervals.set(() => this.tick(), 1000);\n * }\n *\n * disconnect() {\n * this.#intervals.clearAll();\n * }\n * ```\n */\nexport class SafeInterval extends TimerRegistry {\n /** Schedules a repeating `callback` every `delay` ms and returns the timer id. */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(callback, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setInterval(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearInterval(id);\n }\n}\n","/**\n * Shared type-ahead (first-letter navigation) primitive for composite widgets.\n *\n * The APG asks list-like widgets to move focus by typed characters: one\n * character jumps to the next item starting with it, and several characters\n * typed in quick succession narrow the match to that prefix. Getting that right\n * needs three pieces of bookkeeping — accumulating the query, dropping it after\n * an idle window, and resolving the label each candidate is matched against —\n * and single-sourcing them here keeps every widget on the same rules instead of\n * letting them drift apart.\n *\n * The helpers here own *only* that mechanical part. They are deliberately\n * **policy-free** in the same sense as `RovingTabindex`: which elements are\n * candidates, where the search starts, and what happens on a match (real DOM\n * focus, a roving tab stop, or a virtual `aria-activedescendant`) differ per APG\n * pattern, so each controller keeps those decisions and calls\n * {@link findTypeaheadMatch} with a candidate list it assembled itself.\n *\n * Three decisions are worth stating up front:\n *\n * - **A repeated character collapses the stored query**, rather than deriving a\n * shorter one at search time. That is what lets a follow-up character resume\n * narrowing: `s`, `s`, `e` searches `\"se\"`, not the dead `\"sse\"` that no label\n * can match. Growing the query instead leaves the widget frozen on exactly\n * that sequence, which is why the choice is made here and not per widget.\n * - **`aria-label` wins over text**, so type-ahead matches what a screen reader\n * announces — but only when it contributes a name. A blank (empty or\n * whitespace-only) one is skipped, because accname skips it too: honoring it\n * would leave an element that AT announces by its text unreachable by that\n * text. This mirrors the accessible-name computation rather than the raw text.\n * - **`Space` and composition input are never type-ahead.** `Space` natively\n * activates a `<button>`-based item, and characters still being composed are\n * not a committed query.\n *\n * This file's own doc block is dropped from `dist`, but every member comment is\n * inlined into each consumer entry (`tsup` builds with `splitting: false`), so\n * rationale belongs here and contracts belong on the members.\n */\n\nimport { SafeTimeout } from \"./safe_timeout\";\n\n/** Idle window after which an accumulated query is dropped, in milliseconds. */\nexport const TYPEAHEAD_RESET_MS = 500;\n\n/** Construction options for {@link Typeahead}. */\nexport interface TypeaheadOptions {\n /** Idle window before the query resets. Defaults to {@link TYPEAHEAD_RESET_MS}. */\n resetMs?: number;\n}\n\n/**\n * Accumulates typed characters into a search query and drops it when idle.\n *\n * @remarks\n * Owns a {@link SafeTimeout} of its own, so a consumer's `#timers.clearAll()` does\n * **not** reach the pending reset — call {@link Typeahead.reset} from `disconnect()`.\n */\nexport class Typeahead {\n /** Timer registry for the pending idle reset; private so `reset()` is the only exit. */\n readonly #timers = new SafeTimeout();\n /** Idle window before the query resets, in milliseconds. */\n readonly #resetMs: number;\n /** The accumulated lowercase query, empty when idle. */\n #query = \"\";\n /** Id of the pending reset timer, `0` when none is scheduled. */\n #timerId = 0;\n\n /** @param options - Overrides for the idle window. */\n constructor({ resetMs = TYPEAHEAD_RESET_MS }: TypeaheadOptions = {}) {\n this.#resetMs = resetMs;\n }\n\n /** The query a search would currently run with; empty while idle. */\n get query(): string {\n return this.#query;\n }\n\n /**\n * Folds `key` into the query, restarts the idle window, and returns the query to\n * search with. A repeated character collapses the query to that one character.\n */\n push(key: string): string {\n const char = key.toLowerCase();\n const repeated = this.#query.length > 0 && [...this.#query].every((c) => c === char);\n this.#query = repeated ? char : this.#query + char;\n this.#timers.clear(this.#timerId);\n this.#timerId = this.#timers.set(() => this.reset(), this.#resetMs);\n return this.#query;\n }\n\n /** Clears the query and cancels the pending idle reset. */\n reset(): void {\n this.#query = \"\";\n this.#timers.clear(this.#timerId);\n this.#timerId = 0;\n }\n}\n\n/** Whether `event` is a bare printable character usable for type-ahead. */\nexport function isTypeaheadKey(event: KeyboardEvent): boolean {\n return (\n event.key.length === 1 &&\n event.key !== \" \" &&\n !event.ctrlKey &&\n !event.metaKey &&\n !event.altKey &&\n !event.isComposing\n );\n}\n\n/**\n * The name `element` is matched under, normalized for comparison.\n *\n * @param fallbackText - Produces the name when `aria-label` contributes none.\n * Defaults to the element's `textContent`; pass a narrower source when part of\n * the subtree is not part of the name (a tree item's nested child group, say).\n * Called lazily, so an `aria-label` costs no subtree walk.\n */\nexport function typeaheadLabel(element: HTMLElement, fallbackText?: () => string): string {\n // Blank means absent, matching accname: a whitespace-only `aria-label` is\n // skipped there too, so honoring it here would make an element that a screen\n // reader announces by its text unreachable by that text.\n const label = element.getAttribute(\"aria-label\")?.trim();\n if (label) return label.toLowerCase();\n const text = fallbackText ? fallbackText() : (element.textContent ?? \"\");\n return text.trim().toLowerCase();\n}\n\n/**\n * Index of the first item after `from` whose label starts with `query`, wrapping\n * and evaluating `from` itself last, or `-1` when nothing matches.\n *\n * @param items - Candidates in navigation order, already filtered by the caller.\n * @param from - Index the search moves on from; `-1` starts at the first item. Any\n * out-of-range value is folded back in, so `indexOf` results pass straight through.\n * @param query - Lowercase query, normally the return of {@link Typeahead.push}.\n * @param label - Resolves a candidate's name. Defaults to {@link typeaheadLabel}.\n */\nexport function findTypeaheadMatch(\n items: readonly HTMLElement[],\n from: number,\n query: string,\n label: (item: HTMLElement) => string = (item) => typeaheadLabel(item),\n): number {\n // An empty query would prefix-match everything; an empty list never enters the\n // loop, so it needs no guard of its own.\n if (query === \"\") return -1;\n const count = items.length;\n for (let step = 1; step <= count; step += 1) {\n // Folded twice so a negative `from` still lands on a real slot: `%` keeps the\n // sign of its left operand, and a negative index would read `undefined`.\n const index = (((from + step) % count) + count) % count;\n const candidate = items[index];\n if (candidate && label(candidate).startsWith(query)) return index;\n }\n return -1;\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { isReservedArrowChord } from \"../utils/arrow_step\";\nimport { commitField, writeField } from \"../utils/field_mirror\";\nimport { canTakeFocus } from \"../utils/focus_candidate\";\nimport { INTERACTIVE_HOST_SELECTOR, isInteractiveHost } from \"../utils/interactive_host\";\nimport { isRtl } from \"../utils/logical_scroll\";\nimport { MicrotaskCoalescer } from \"../utils/microtask_coalescer\";\nimport { RovingTabindex } from \"../utils/roving_tabindex\";\nimport { SafeTimeout } from \"../utils/safe_timeout\";\nimport { findTypeaheadMatch, isTypeaheadKey, Typeahead, typeaheadLabel } from \"../utils/typeahead\";\n\n/** Item attributes a page can rewrite in place that move the published selection. */\nconst OBSERVED_ATTRIBUTES = [\"aria-selected\", \"data-value\"];\n\n/**\n * Sets `name` on `element` only when its value differs. A same-value write still\n * queues a mutation record for every observer on the page, so an unchanged state\n * writes nothing.\n */\nfunction setAttributeIfChanged(element: Element, name: string, value: string): void {\n if (element.getAttribute(name) !== value) element.setAttribute(name, value);\n}\n\n/** The published selection: the selected item and the value its field submits. */\ninterface Selection {\n readonly item: HTMLElement | null;\n readonly value: string;\n}\n\n/**\n * Interactive descendants a `treeitem` may legitimately contain (inline rename\n * field, link, chevron). Keys and clicks raised inside one belong to it, not to\n * the tree, so the tree handlers step aside.\n *\n * The selector is matched with `closest()`, which also matches the item host\n * itself — deliberately. An item host that *is* one of these elements is out of\n * contract (see the class TSDoc), and the tree then stands down completely\n * rather than responding to some keys and not others.\n */\nconst NESTED_INTERACTIVE = INTERACTIVE_HOST_SELECTOR;\n\n/**\n * Headless, accessible single-select tree view.\n *\n * Markup contract (identifier: `stimeo--tree-view`):\n * <ul data-controller=\"stimeo--tree-view\" role=\"tree\" aria-label=\"Files\">\n * <input type=\"hidden\" name=\"path\" data-stimeo--tree-view-target=\"field\" />\n * <li role=\"treeitem\" aria-expanded=\"false\" aria-selected=\"false\" tabindex=\"0\"\n * data-value=\"src\"\n * data-stimeo--tree-view-target=\"item\"\n * data-action=\"keydown->stimeo--tree-view#onKeydown\n * click->stimeo--tree-view#onClick\">\n * <span>src</span>\n * <button type=\"button\" tabindex=\"-1\" aria-hidden=\"true\"\n * data-action=\"click->stimeo--tree-view#toggle\"></button>\n * <ul role=\"group\" data-stimeo--tree-view-target=\"group\" hidden>\n * <li role=\"treeitem\" aria-selected=\"false\" tabindex=\"-1\"\n * data-stimeo--tree-view-target=\"item\" data-action=\"…\">…</li>\n * </ul>\n * </li>\n * </ul>\n *\n * Implements the WAI-ARIA APG **Tree View** (single-select) pattern. Parent/child\n * structure is read from the DOM nesting (`treeitem` → child `group`). The whole\n * tree is one Tab stop (roving tabindex); arrows navigate visible items, expand /\n * collapse, and move between parent and child.\n *\n * Behavior provided:\n * - `ArrowDown`/`ArrowUp` move between visible items; `Home`/`End` jump to the\n * first / last visible item; printable characters typeahead by label prefix\n * (`aria-label` first, else the item's own text), with a repeated character\n * cycling through the items that start with it.\n * - `ArrowRight` expands a collapsed parent or steps into its first child;\n * `ArrowLeft` collapses an expanded parent or steps to the parent item.\n * - `Enter`/`Space`/click select the item (single selection via `aria-selected`);\n * an `aria-disabled=\"true\"` item stays focusable but cannot be selected.\n * - `aria-expanded` and each child `group`'s `hidden` stay in sync, dispatching\n * `stimeo--tree-view:toggle`; selection dispatches `stimeo--tree-view:select`.\n * - The optional `toggle` action expands / collapses the nearest item, so a\n * chevron gives pointer users the reach that `ArrowRight` gives the keyboard.\n * - The optional `field` target mirrors the selected item's `data-value` so the\n * selection can be submitted and read server-side; an item without one, and\n * an empty selection, both mirror `\"\"`. A selection the user made emits a\n * native bubbling `change` from that field, the way a form control does, so\n * `stimeo--auto-submit` and form-level validation hear it. The mirror is\n * refreshed silently on connect, on a replacement field, and whenever the\n * authored selection is normalized.\n * - A selection the page moves — the selected item removed, a morph or a script\n * writing an item's `aria-selected` or the selected item's `data-value`, a\n * selected item that arrives ahead of the selection — dispatches\n * `stimeo--tree-view:reconcile` instead of `select`: once per batch, and only\n * when the selected item or its value differs from the selection last settled\n * (on connect, by the user, or by the previous `reconcile`). Connecting\n * reports nothing. While connected a `MutationObserver` watches the items'\n * `aria-selected` and `data-value`; `disconnect()` releases it.\n *\n * Contract notes:\n * - A child container is resolved by `role=\"group\"`, not by the target: the\n * `group` target is what `stimeo check`'s accessibility rule anchors on, which\n * is why the contract still declares it.\n * - Nested interactive controls (natively interactive elements such as `button`,\n * `input`, `select`, `textarea`, `label`, `a[href]`, `summary`) and IME composition\n * keys are left alone; a click on a child `group`'s own box (its indentation\n * band) selects nothing rather than the parent row.\n * - A key a descendant already consumed (`event.defaultPrevented`) is yielded,\n * so a composed widget inside a row never causes a double move.\n *\n * Consumer contract — the item host:\n * - **Host `role=\"treeitem\"` on a non-interactive element** (`li`, `div`), never\n * on a natively interactive element. APG's *Navigation Treeview* does host it on a\n * link, but that variant activates on `Enter`, which this pattern has already\n * spent on selection; a link host is therefore out of contract and would need\n * `Enter` / click / `select` redefined first.\n * - An out-of-contract host makes `onKeydown` and `onClick` stand down\n * **entirely** — no movement, no selection, no `select` — rather than respond\n * partially. `stimeo check` reports the unsupported host before runtime.\n *\n * Consumer contract — controls nested inside a row:\n * - Keep them out of the Tab sequence (`tabindex=\"-1\"`); the tree is one Tab\n * stop and writes `tabindex` on nothing but its own item targets.\n * - Moving focus *into* a control (to start an inline rename) is the consumer's\n * call — the tree never focuses one.\n * - When the edit or action ends, focus the `treeitem` itself. The roving\n * position never moved, so that alone restores the keyboard path.\n * - The tree consumes none of the control's events, by two mechanisms:\n * the nested-interactive selector names controls by *element shape*, the\n * `defaultPrevented` yield covers widgets no selector can name.\n * `select` dispatches `{ item: HTMLElement }`.\n * `toggle` dispatches `{ item: HTMLElement, expanded: boolean }`.\n * `reconcile` dispatches `{ item: HTMLElement | null }` — the shape of `select`,\n * with `null` once nothing is selected.\n */\nexport class TreeViewController extends Controller<HTMLElement> {\n static override targets = [\"item\", \"group\", \"field\"];\n static actions = [\"onClick\", \"onKeydown\", \"toggle\"] as const;\n static events = [\"select\", \"toggle\", \"reconcile\"] as const;\n\n declare readonly itemTargets: HTMLElement[];\n declare readonly fieldTarget: HTMLInputElement;\n declare readonly hasFieldTarget: boolean;\n\n readonly #roving = new RovingTabindex(() => this.itemTargets);\n readonly #typeahead = new Typeahead();\n readonly #timers = new SafeTimeout();\n /**\n * Collapses the item and field callbacks and observed item writes of one DOM\n * mutation into a single selection pass; refused before `connect()` and after\n * `disconnect()`.\n */\n readonly #reconcile = new MicrotaskCoalescer(() => this.#reconcileSelection());\n /** Watches the item attributes a page can rewrite in place; set while connected. */\n #observer: MutationObserver | null = null;\n /** The selection last settled: on connect, by the user, or by a reported pass. */\n #settled: Selection = { item: null, value: \"\" };\n #connected = false;\n /**\n * Item targets in DOM order as of the last connect / target change. A removed\n * item is gone from `itemTargets` by the time Stimulus reports it, so this is\n * the only record of where in the order the gap opened.\n */\n #order: HTMLElement[] = [];\n /** The `treeitem` that last took DOM focus inside this tree, if any. */\n #focused: HTMLElement | null = null;\n\n /**\n * Records which row owns DOM focus. `focusin` bubbles, so one listener covers\n * rows and anything nested in them; the tree needs this because a removal is\n * only reported *after* the fact, when the browser has already reset\n * `document.activeElement`.\n */\n readonly #onFocusIn = (event: FocusEvent): void => {\n const target = event.target as HTMLElement | null;\n this.#focused = target?.closest<HTMLElement>('[role=\"treeitem\"]') ?? null;\n };\n\n /**\n * Forgets the tracked row when focus genuinely leaves the tree, so a later\n * removal of that row cannot be mistaken for \"focus died with it\" and pull\n * focus back in.\n *\n * A *removed* focused row must not clear the record — that is exactly the case\n * {@link TreeViewController.itemTargetDisconnected} needs it for. The two are\n * indistinguishable while the event is dispatching: Chromium fires `focusout`\n * for a removed row *before* the detach lands — `isConnected` still reads\n * `true` — with `document.activeElement` already on `<body>`, exactly like a\n * plain `blur()`. Deciding on a microtask separates them — a row that is gone\n * by then was removed. Focus moving between two rows needs no handling:\n * `focusin` overwrites the record right after.\n */\n readonly #onFocusOut = (event: FocusEvent): void => {\n const target = event.target as HTMLElement | null;\n const row = target?.closest<HTMLElement>('[role=\"treeitem\"]') ?? null;\n if (!row || row !== this.#focused) return;\n const next = event.relatedTarget;\n if (next instanceof Node && this.element.contains(next)) return;\n queueMicrotask(() => {\n if (this.#focused === row && row.isConnected) this.#focused = null;\n });\n };\n\n /**\n * Reconciles authored expansion state, establishes the single tab stop, and\n * settles the selection without reporting it. Idempotent, so a Turbo cache\n * restore / morph re-runs it safely (re-adding the same listener reference is a\n * no-op per the DOM spec).\n */\n override connect(): void {\n this.element.addEventListener(\"focusin\", this.#onFocusIn);\n this.element.addEventListener(\"focusout\", this.#onFocusOut);\n this.#seedFocused();\n this.#reconcileExpansion();\n this.#normalizeSelection();\n this.#normalizeTabStop();\n this.#mirrorField(false);\n this.#order = [...this.itemTargets];\n this.#settled = this.#selection();\n this.#connected = true;\n this.#reconcile.activate();\n this.#observeItems();\n }\n\n /** Fills a form field inserted or replaced at runtime with the current selection. */\n fieldTargetConnected(): void {\n this.#reconcile.schedule();\n }\n\n /**\n * Detaches the focus trackers and the item observer, drops a queued selection\n * pass, the typeahead buffer and its timer.\n */\n override disconnect(): void {\n this.element.removeEventListener(\"focusin\", this.#onFocusIn);\n this.element.removeEventListener(\"focusout\", this.#onFocusOut);\n this.#connected = false;\n this.#reconcile.cancel();\n this.#observer?.disconnect();\n this.#observer = null;\n this.#typeahead.reset();\n this.#timers.clearAll();\n this.#order = [];\n this.#focused = null;\n }\n\n /**\n * Seeds the focus record from the live document. A row can already hold focus\n * when the controller connects (a Turbo restore, or `data-controller` added to\n * a tree the user was already in), and the `focusin` listener alone would\n * never learn about it.\n */\n #seedFocused(): void {\n const active = this.element.ownerDocument.activeElement;\n this.#focused =\n active instanceof HTMLElement && this.element.contains(active)\n ? active.closest<HTMLElement>('[role=\"treeitem\"]')\n : null;\n }\n\n /**\n * Re-normalizes the tab stop when a `treeitem` is added at runtime, so an\n * appended item carrying `tabindex=\"0\"` cannot turn the tree into two Tab\n * stops, and settles the selection once the batch is in. Skipped before\n * `connect()`: Stimulus registers the initial targets first, and `connect()`\n * owns the initial tab-stop policy.\n */\n itemTargetConnected(): void {\n if (!this.#connected) return;\n this.#normalizeTabStop();\n this.#trackOrder();\n this.#reconcile.schedule();\n }\n\n /**\n * Brings authored `aria-selected` to the shape the APG requires, without\n * changing which item the author chose.\n *\n * Every item gets an explicit value — an absent `aria-selected` means \"not\n * selectable\" in ARIA, so a forgotten attribute hides a selectable row — and a\n * single-select tree keeps at most one `true`, first in DOM order: an item the\n * page inserts ahead of the selection, already selected, takes it over.\n * `connect()` reconciles authored expansion the same way. The scan is the\n * `item` target set: a `role=\"treeitem\"` without the target is outside the\n * contract and is neither counted nor written.\n */\n #normalizeSelection(): void {\n const items = this.itemTargets;\n const selected = items.find((item) => item.getAttribute(\"aria-selected\") === \"true\");\n for (const item of items) {\n setAttributeIfChanged(item, \"aria-selected\", item === selected ? \"true\" : \"false\");\n }\n }\n\n /**\n * Mirrors the selected item's `data-value` into the optional form field.\n * Nothing selected, and an item that carries no value, both submit `\"\"`.\n */\n #mirrorField(notify: boolean): void {\n if (!this.hasFieldTarget) return;\n const moved = writeField(this.fieldTarget, this.#selection().value);\n if (moved && notify) commitField(this.fieldTarget);\n }\n\n /** The selected item target, if any, and the value its field submits. */\n #selection(): Selection {\n const item =\n this.itemTargets.find((candidate) => candidate.getAttribute(\"aria-selected\") === \"true\") ??\n null;\n return { item, value: item?.dataset.value ?? \"\" };\n }\n\n /**\n * The pass the page's changes run: the selection is normalized and mirrored,\n * then reported if it moved. The report comes last, so items a subscriber\n * rewrites are the next pass's to settle.\n */\n #reconcileSelection(): void {\n this.#normalizeSelection();\n this.#dropOwnRecords();\n this.#mirrorField(false);\n this.#reportMove();\n }\n\n /**\n * Reports the selection as `reconcile` when its item or value differs from the\n * selection last settled. The settled selection is replaced before dispatching,\n * so a move a subscriber makes is measured against what it was told.\n */\n #reportMove(): void {\n const selection = this.#selection();\n if (selection.item === this.#settled.item && selection.value === this.#settled.value) return;\n this.#settled = selection;\n this.dispatch(\"reconcile\", { detail: { item: selection.item } });\n }\n\n /**\n * Watches the items' `aria-selected` and `data-value`. The tree's own writes to\n * them are dropped from the queue as they happen, so each record the callback\n * receives is the page's, and only a record on one of the tree's own items\n * schedules a pass.\n */\n #observeItems(): void {\n const observer = new MutationObserver((records) => {\n if (this.#concernsItems(records)) this.#reconcile.schedule();\n });\n observer.observe(this.element, {\n subtree: true,\n attributes: true,\n attributeFilter: OBSERVED_ATTRIBUTES,\n });\n this.#observer = observer;\n }\n\n /**\n * Whether any record is about one of this tree's own items. A widget nested in a\n * row writes the same attributes on its own elements, and those are no reason to\n * reconcile the tree.\n */\n #concernsItems(records: readonly MutationRecord[]): boolean {\n const items = new Set<Node>(this.itemTargets);\n return records.some((record) => items.has(record.target));\n }\n\n /**\n * Drops the records the tree's own writes just queued, so the observer never\n * takes them for the page's. A page write queued just before goes with them and\n * owes nothing: every such write leaves every item's `aria-selected` at the value\n * the selection gives it, and the selection is then read back from the DOM, so it\n * already holds what the page wrote.\n */\n #dropOwnRecords(): void {\n this.#observer?.takeRecords();\n }\n\n /**\n * Restores the roving invariants when a `treeitem` leaves at runtime — a Turbo\n * Stream `remove`, a morph, or plain DOM surgery. The mirror of\n * {@link TreeViewController.itemTargetConnected}: without it, removing the row\n * that held `tabindex=\"0\"` leaves every survivor at `-1`, i.e. a tree with\n * **no** Tab stop, and drops DOM focus to `<body>` when the removed subtree\n * held it.\n *\n * The replacement is the nearest surviving *visible* item in the pre-removal\n * order — the one after the gap, else the one before it — which is the row\n * `ArrowDown` would have reached. Removing a non-active item changes nothing.\n * When no visible item survives, a tree with zero Tab stops is the correct end\n * state.\n *\n * Focus is only *restored*, never *stolen*: DOM focus moves solely when it was\n * inside the removed subtree and the document has nowhere left to put it. The\n * roving recovery runs here, while the pre-removal order is still on record;\n * the selection is settled once the batch is in, and a selected item that left\n * is reported as `reconcile` with no item.\n */\n itemTargetDisconnected(item: HTMLElement): void {\n // Stimulus drains the target callbacks for a removed *tree* before calling\n // `disconnect()`; recovering a roving position for a widget that is already\n // gone would move focus for nothing.\n if (!this.#connected || !this.element.isConnected) return;\n const stranded = this.#focusDiedWith(item);\n if (stranded) this.#focused = null;\n if (stranded || this.#roving.activeIndex === -1) {\n const next = this.#neighborOf(item);\n if (next) this.#roving.setActive(this.itemTargets.indexOf(next), { focus: stranded });\n }\n this.#reconcile.schedule();\n // Drop only the item just reported. One DOM mutation batch arrives as\n // several callbacks, so rebuilding from `itemTargets` here would also erase\n // the recorded position of every sibling whose callback has yet to run.\n this.#order = this.#order.filter((tracked) => tracked !== item);\n }\n\n /**\n * Folds newly connected targets into the DOM-order snapshot, keeping the slot\n * of any item whose disconnect callback has not arrived yet — a morph that\n * both adds and removes rows reports one batch as several callbacks, and\n * {@link TreeViewController.#neighborOf} needs those slots.\n */\n #trackOrder(): void {\n const live = this.itemTargets;\n const pending = this.#order.filter((tracked) => !live.includes(tracked));\n if (pending.length === 0) {\n this.#order = live;\n return;\n }\n // `pending` is in ascending recorded order, so re-inserting each at the\n // index it held rebuilds the pre-mutation sequence.\n const merged = [...live];\n for (const tracked of pending) {\n merged.splice(Math.min(this.#order.indexOf(tracked), merged.length), 0, tracked);\n }\n this.#order = merged;\n }\n\n /**\n * Routes tree keyboard interaction. Because `treeitem`s nest, only the handler\n * on the nearest item to the event target acts; the same keydown bubbling to an\n * ancestor item's handler is ignored to avoid double moves / selections. Keys\n * from a nested interactive control (or an IME composition) are left untouched.\n */\n onKeydown(event: KeyboardEvent): void {\n // A descendant widget that already claimed the key (a grabbed drag handle, a\n // nested custom control) must not ALSO move the roving focus — composition\n // depends on this yield. `#ownerItem` covers the controls a selector can\n // name; this covers the ones it cannot.\n if (event.defaultPrevented) return;\n if (isReservedArrowChord(event)) return;\n if (event.isComposing) return;\n const item = this.#ownerItem(event);\n if (!item) return;\n\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault();\n this.#moveBy(item, 1);\n break;\n case \"ArrowUp\":\n event.preventDefault();\n this.#moveBy(item, -1);\n break;\n // Logical, not physical: APG describes these as \"to the child level\" and\n // \"to the parent level\" — a spatial move — so they follow the writing\n // direction, exactly as horizontal roving does. `isRtl()` is the shared\n // detector; never introduce a second one.\n //\n // Direction comes from the tree, not from `item`: the tree is what lays\n // the rows out, and a row may carry its own `dir` (an LTR path inside an\n // RTL browser is ordinary bidi authoring). Probing the focused row instead\n // would let one row's arrows mean the opposite of its sibling's.\n case \"ArrowRight\":\n event.preventDefault();\n if (isRtl(this.element)) this.#collapseOrLeave(item);\n else this.#expandOrEnter(item);\n break;\n case \"ArrowLeft\":\n event.preventDefault();\n if (isRtl(this.element)) this.#expandOrEnter(item);\n else this.#collapseOrLeave(item);\n break;\n case \"Home\":\n event.preventDefault();\n this.#focusEdge(0);\n break;\n case \"End\":\n event.preventDefault();\n this.#focusEdge(-1);\n break;\n case \"Enter\":\n case \" \":\n event.preventDefault();\n this.#select(item);\n break;\n default:\n if (isTypeaheadKey(event)) {\n event.preventDefault();\n this.#typeaheadTo(item, event.key);\n }\n break;\n }\n }\n\n /** Selects the clicked item (nearest to the target only). */\n onClick(event: Event): void {\n const item = this.#ownerItem(event);\n if (!item) return;\n // The indentation band of a child group is the group's own box, not the\n // parent row: a click that lands there must select nothing.\n const group = this.#childGroup(item);\n if (group?.contains(event.target as Node)) return;\n this.#focusItem(item);\n this.#select(item);\n }\n\n /**\n * Expands or collapses the `treeitem` nearest to the event target. Optional:\n * wire it on a chevron control so pointer users can reach child nodes, which\n * otherwise only `ArrowRight` / `ArrowLeft` can do. A leaf item is a no-op.\n *\n * The toggled row then takes focus and the tab stop, matching what the keyboard\n * path guarantees. Without it, a browser that focuses buttons on mousedown leaves\n * focus on the clicked chevron: arrows would stop working until focus returns to a\n * row, and a decorative `aria-hidden` chevron would hold focus. The synchronous\n * `stimeo--tree-view:toggle` event is observed before this final focus hand-off.\n */\n toggle(event: Event): void {\n const item = (event.target as HTMLElement).closest<HTMLElement>('[role=\"treeitem\"]');\n if (!item || !this.element.contains(item)) return;\n if (!this.#childGroup(item)) return;\n this.#setExpanded(item, !this.#isExpanded(item));\n this.#focusItem(item);\n }\n\n /**\n * The item a raw event belongs to, or `null` when the tree must not act: the\n * event came from a nested `treeitem` (it bubbled to an ancestor's handler) or\n * from an interactive control inside the item.\n */\n #ownerItem(event: Event): HTMLElement | null {\n const item = event.currentTarget as HTMLElement;\n const target = event.target as HTMLElement;\n if (target.closest('[role=\"treeitem\"]') !== item) return null;\n const control = target.closest<HTMLElement>(NESTED_INTERACTIVE);\n if (control && item.contains(control)) return null;\n if (isInteractiveHost(target)) return null;\n return item;\n }\n\n /**\n * The surviving visible item that inherits `removed`'s position: the next one\n * in the pre-removal order, else the previous one. Falls back to the first\n * visible item when `removed` is not in that order — a target that arrived and\n * left between two callbacks — and to `undefined` when nothing visible is left.\n */\n #neighborOf(removed: HTMLElement): HTMLElement | undefined {\n const visible = this.#visibleItems;\n const index = this.#order.indexOf(removed);\n for (let at = index + 1; at < this.#order.length; at += 1) {\n const candidate = this.#order[at];\n if (candidate && visible.includes(candidate)) return candidate;\n }\n for (let at = index - 1; at >= 0; at -= 1) {\n const candidate = this.#order[at];\n if (candidate && visible.includes(candidate)) return candidate;\n }\n return visible[0];\n }\n\n /**\n * Whether DOM focus went down with `removed`: it sat on that row (or inside\n * it) and the document now has nowhere to put focus. A browser falls back to\n * `<body>`; a still-referenced but detached `activeElement` is the same\n * condition seen from the other side. Focus that landed on a real element —\n * inside this tree or anywhere else on the page — is left alone, so a removal\n * can never steal it.\n */\n #focusDiedWith(removed: HTMLElement): boolean {\n const focused = this.#focused;\n if (!focused || (focused !== removed && !removed.contains(focused))) return false;\n const doc = this.element.ownerDocument;\n const active = doc.activeElement;\n if (active === null || active === doc.body || active === doc.documentElement) return true;\n return !active.isConnected;\n }\n\n /** Moves focus to the next (`delta=1`) or previous visible item, if any. */\n #moveBy(item: HTMLElement, delta: number): void {\n const visible = this.#visibleItems;\n const current = visible.indexOf(item);\n // An item outside the visible set has no neighbors; without this guard a\n // `-1 + 1` index would warp focus to the top of the tree.\n if (current === -1) return;\n const next = visible[current + delta];\n if (next) this.#focusItem(next);\n }\n\n /**\n * Expands a collapsed parent, else steps into the first child.\n *\n * Bound to `ArrowRight` under LTR and `ArrowLeft` under RTL — the caller picks;\n * this method only knows \"toward the child level\".\n */\n #expandOrEnter(item: HTMLElement): void {\n const group = this.#childGroup(item);\n if (!group) return;\n if (!this.#isExpanded(item)) {\n this.#setExpanded(item, true);\n return;\n }\n // Never move the only tab stop into a subtree a consumer hid directly.\n if (group.hidden) return;\n // The first child that is not itself hidden. Matched on the element rather\n // than through `#visibleItems`, so a `role=\"treeitem\"` without the item\n // target still receives focus (the fallback in `#focusItem`).\n const firstChild = Array.from(\n group.querySelectorAll<HTMLElement>(':scope > [role=\"treeitem\"]'),\n ).find((child) => !child.hidden);\n if (firstChild) this.#focusItem(firstChild);\n }\n\n /** `ArrowLeft`: collapse an expanded parent, else step out to the parent item. */\n #collapseOrLeave(item: HTMLElement): void {\n if (this.#childGroup(item) && this.#isExpanded(item)) {\n this.#setExpanded(item, false);\n return;\n }\n const parent = this.#parentItem(item);\n if (parent) this.#focusItem(parent);\n }\n\n /** Focuses the first (`0`) or last (`-1`) visible item. */\n #focusEdge(index: number): void {\n const visible = this.#visibleItems;\n const target = index < 0 ? visible[visible.length - 1] : visible[index];\n if (target) this.#focusItem(target);\n }\n\n /**\n * Whether the item is disabled — **its own attribute or an ancestor's**.\n *\n * ARIA is explicit that the state carries down: \"The state of being disabled\n * applies to the current element *and all focusable descendant elements* of the\n * element on which the `aria-disabled` attribute is applied.\" A tree is the only\n * pattern here whose items nest, so it is the only place the inheritance is\n * observable — but the rule is the shared one.\n *\n * The walk stops at the controller element: an `aria-disabled` on the tree root\n * would otherwise disable every row, and a disabled *tree* is the consumer's\n * call to make with `inert`, not something this controller infers.\n */\n #isDisabled(item: HTMLElement): boolean {\n let current: HTMLElement | null = item;\n while (current && current !== this.element) {\n if (current.getAttribute(\"aria-disabled\") === \"true\") return true;\n current = current.parentElement;\n }\n return false;\n }\n\n /**\n * Applies single selection and dispatches `select`. An `aria-disabled=\"true\"`\n * item stays focusable (APG keeps disabled nodes reachable) but is never\n * activated, so consumers see no `select` for it.\n */\n #select(item: HTMLElement): void {\n if (this.#isDisabled(item)) return;\n for (const candidate of this.itemTargets) {\n setAttributeIfChanged(candidate, \"aria-selected\", candidate === item ? \"true\" : \"false\");\n }\n this.#dropOwnRecords();\n // Settled before anything is reported, so a listener that moves the selection\n // again is measured against this one.\n this.#settled = this.#selection();\n this.#mirrorField(true);\n this.dispatch(\"select\", { detail: { item } });\n }\n\n /** Updates expansion, reconciles a collapsed subtree, then synchronously dispatches `toggle`. */\n #setExpanded(item: HTMLElement, expanded: boolean): void {\n const group = this.#childGroup(item);\n if (!group) return;\n item.setAttribute(\"aria-expanded\", String(expanded));\n group.hidden = !expanded;\n if (!expanded) this.#escapeCollapsedSubtree(item, group);\n this.dispatch(\"toggle\", { detail: { item, expanded } });\n }\n\n /**\n * Reconciles focus and the roving tab stop before a synchronous `toggle` event\n * observes a newly collapsed group. If DOM focus remains inside the group,\n * both move to the parent. Otherwise only a stranded tab stop moves, preserving\n * DOM focus. A native chevron click takes this path when mousedown has already\n * focused the button. {@link TreeViewController.toggle} completes its focus\n * hand-off after this reconciliation and event dispatch.\n */\n #escapeCollapsedSubtree(item: HTMLElement, group: HTMLElement): void {\n const active = document.activeElement;\n if (active instanceof HTMLElement && group.contains(active)) {\n this.#focusItem(item);\n return;\n }\n // Focus is elsewhere on the page: relocate the tab stop without stealing it.\n const activeIndex = this.#roving.activeIndex;\n const tabbable = activeIndex === -1 ? undefined : this.itemTargets[activeIndex];\n if (!tabbable || !group.contains(tabbable)) return;\n const index = this.itemTargets.indexOf(item);\n if (index !== -1) this.#roving.setActive(index);\n }\n\n /** Makes `item` the single tab stop and moves DOM focus to it. */\n #focusItem(item: HTMLElement): void {\n const index = this.itemTargets.indexOf(item);\n if (index !== -1) {\n this.#roving.setActive(index, { focus: true });\n return;\n }\n // A `role=\"treeitem\"` without the item target is outside roving bookkeeping,\n // yet DOM structure still reaches it as a parent / first child. Focus it so\n // the tree never silently stops responding; `tabindex=\"-1\"` keeps the single\n // Tab stop on a real target.\n //\n // Checked first: `hidden` and natively disabled elements swallow `focus()`,\n // and moving toward one would leave the caret where it was with no signal.\n // Refusing is the honest outcome here — there is no better destination to\n // fall back to, since roving already declined this node.\n if (!canTakeFocus(item)) return;\n item.tabIndex = -1;\n item.focus();\n }\n\n /** Advances the typeahead query and focuses the next matching visible item. */\n #typeaheadTo(item: HTMLElement, char: string): void {\n const query = this.#typeahead.push(char);\n // Only visible rows are candidates: an item inside a collapsed group is not\n // reachable by arrow keys either, so typeahead must not tunnel into one.\n const visible = this.#visibleItems;\n const index = findTypeaheadMatch(visible, visible.indexOf(item), query, (candidate) =>\n this.#label(candidate),\n );\n // A miss reads back as `undefined` (`visible[-1]`), so the guard below is the\n // only branch — spelling the `-1` out again would add an unreachable one.\n const match = visible[index];\n if (match) this.#focusItem(match);\n }\n\n /**\n * Aligns each parent's `aria-expanded` with its child `group`'s `hidden`.\n * `aria-expanded` wins when authored (it is what assistive tech reads); a\n * parent without it derives one from `hidden` (establish-only-when-absent, so\n * a restored snapshot is never clobbered). Disagreeing markup would otherwise\n * strand the only tab stop on an invisible item.\n */\n #reconcileExpansion(): void {\n for (const item of this.itemTargets) {\n const group = this.#childGroup(item);\n if (!group) continue;\n if (item.hasAttribute(\"aria-expanded\")) {\n group.hidden = !this.#isExpanded(item);\n } else {\n item.setAttribute(\"aria-expanded\", String(!group.hidden));\n }\n }\n }\n\n /**\n * Leaves exactly one item tabbable. An existing, still-visible tab stop wins\n * (the DOM is the source of truth after a Turbo restore, so the user's roving\n * position survives); otherwise the selected item takes it (APG: a single-select\n * tree puts initial focus on its selected node), else the first visible item.\n */\n #normalizeTabStop(): void {\n const items = this.itemTargets;\n const visible = this.#visibleItems;\n const activeIndex = this.#roving.activeIndex;\n const active = activeIndex === -1 ? undefined : items[activeIndex];\n if (active && visible.includes(active)) {\n this.#roving.setActive(activeIndex);\n return;\n }\n const selected = visible.find((item) => item.getAttribute(\"aria-selected\") === \"true\");\n const next = selected ?? visible[0];\n this.#roving.setActive(next ? items.indexOf(next) : -1);\n }\n\n /**\n * The visible items: those with no `hidden` ancestor up to the tree, and not\n * `hidden` themselves. A hidden row is out of the move set — it can hold\n * neither DOM focus nor the only Tab stop — and neither can anything nested\n * under one, which is why the walk tests every ancestor rather than only the\n * collapsed `role=\"group\"` containers.\n */\n get #visibleItems(): HTMLElement[] {\n return this.itemTargets.filter((item) => {\n let node: HTMLElement | null = item;\n while (node && node !== this.element) {\n if (node.hidden) return false;\n node = node.parentElement;\n }\n return true;\n });\n }\n\n /** The child `group` owned directly by `item`, or `null` for a leaf. */\n #childGroup(item: HTMLElement): HTMLElement | null {\n return item.querySelector<HTMLElement>(':scope > [role=\"group\"]');\n }\n\n /** The nearest ancestor `treeitem`, or `null` at the root level. */\n #parentItem(item: HTMLElement): HTMLElement | null {\n return item.parentElement?.closest<HTMLElement>('[role=\"treeitem\"]') ?? null;\n }\n\n /** Whether a parent item is currently expanded. */\n #isExpanded(item: HTMLElement): boolean {\n return item.getAttribute(\"aria-expanded\") === \"true\";\n }\n\n /**\n * `item`'s name for typeahead. The fallback is the item's *own* text: a parent\n * row's nested child group is rendered inside it, and folding a whole subtree\n * into one name would let \"readme\" match the folder containing it.\n */\n #label(item: HTMLElement): string {\n return typeaheadLabel(item, () => {\n let text = \"\";\n for (const node of Array.from(item.childNodes)) {\n if (node.nodeType === Node.ELEMENT_NODE && (node as Element).matches('[role=\"group\"]')) {\n continue;\n }\n text += node.textContent ?? \"\";\n }\n return text;\n });\n }\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -107,6 +107,33 @@ import { TooltipController } from './controllers/tooltip_controller.js';
107
107
  import { TransitionController } from './controllers/transition_controller.js';
108
108
  import { TreeViewController } from './controllers/tree_view_controller.js';
109
109
 
110
+ /**
111
+ * Why a component's public open/closed state moved, carried as `detail.reason`
112
+ * on the state event that reports the move.
113
+ *
114
+ * The attribute a component publishes (`aria-expanded`, `hidden`, `data-state`)
115
+ * only says *what* the state is now. A subscriber that mirrors the state
116
+ * elsewhere, or reports it, needs *why*: closing on `Escape` and closing because
117
+ * the consumer called the action are the same attribute write and different
118
+ * events to the page around it.
119
+ *
120
+ * `"api"` is the one every subscriber has to look at. A close the consumer asked
121
+ * for arrives back at their own listener, so a listener that closes something
122
+ * else on `close` loops unless it ignores its own calls.
123
+ *
124
+ * | Reason | The state moved because |
125
+ * | --- | --- |
126
+ * | `"user"` | a control of this component was operated |
127
+ * | `"select"` | an item inside it was activated |
128
+ * | `"escape"` | `Escape` was pressed and this layer owned it |
129
+ * | `"outside"` | a pointer landed outside it, or on its backdrop |
130
+ * | `"focus"` | focus entered or left it |
131
+ * | `"pointer"` | the pointer entered or left it |
132
+ * | `"scroll"` | a tracked scroll container scrolled |
133
+ * | `"api"` | a public action was called with no DOM event |
134
+ */
135
+ type StateReason = "user" | "select" | "escape" | "outside" | "focus" | "pointer" | "scroll" | "api";
136
+
110
137
  /**
111
138
  * Maps the core controller identifiers to their controller classes. The opt-in
112
139
  * `stimeo-ui/positioning` and `stimeo-ui/cable` entry points export their own maps.
@@ -239,4 +266,4 @@ declare const stimeoControllers: {
239
266
  */
240
267
  declare function registerStimeo(application: Application): void;
241
268
 
242
- export { AccordionController, AlertDialogController, AnnouncerController, AspectRatioController, AutoSubmitController, AvatarController, BreadcrumbController, BulkSelectController, CalendarController, CarouselController, CharacterCounterController, CheckboxController, ClipboardController, CollapsibleController, ColorPickerController, ComboboxController, CommandPaletteController, ConditionalFieldsController, ConfirmController, ContextMenuController, CountUpController, CountdownController, CurrencyInputController, DataGridController, DateRangePickerController, DialogController, DirectUploadController, DirtyFormController, DismissibleController, DrawerController, DropdownController, EditableController, EmptyStateController, FileDropzoneController, FilterController, FlashController, FocusController, FormFieldController, FormValidationController, FrameLoadingController, HighlightController, HoverCardController, IdleController, InputMaskController, IntersectionController, LazyFrameController, ListboxController, LocalTimeController, MasonryController, MenuController, MenubarController, MeterController, MultiSelectController, NavigationMenuController, NestedFormController, NetworkStatusController, NumberInputController, OptimisticController, OtpController, OverflowIndicatorController, OverflowMenuController, PaginationController, PasswordRevealController, PasswordStrengthController, PersistController, PointerDragController, PopoverController, PortalController, PreviewGuardController, ProgressController, RadioGroupController, RangeSliderController, RatingController, ReadMoreController, ReadingProgressController, RelativeTimeController, ResetBeforeCacheController, ResizableController, RovingController, ScrollAreaController, ScrollRestoreController, ScrollVisibilityController, ScrollspyController, SeparatorController, SidebarController, SkeletonController, SliderController, SmartStickyHeaderController, SortableController, SpinnerController, StepIndicatorController, StepperController, StickToBottomController, StickyObserverController, SubmitOnceController, SwitchController, TabsController, TagsInputController, TextareaAutosizeController, ThemeController, TimePickerController, ToastController, ToggleGroupController, ToolbarController, TooltipController, TransitionController, TreeViewController, registerStimeo, stimeoControllers };
269
+ export { AccordionController, AlertDialogController, AnnouncerController, AspectRatioController, AutoSubmitController, AvatarController, BreadcrumbController, BulkSelectController, CalendarController, CarouselController, CharacterCounterController, CheckboxController, ClipboardController, CollapsibleController, ColorPickerController, ComboboxController, CommandPaletteController, ConditionalFieldsController, ConfirmController, ContextMenuController, CountUpController, CountdownController, CurrencyInputController, DataGridController, DateRangePickerController, DialogController, DirectUploadController, DirtyFormController, DismissibleController, DrawerController, DropdownController, EditableController, EmptyStateController, FileDropzoneController, FilterController, FlashController, FocusController, FormFieldController, FormValidationController, FrameLoadingController, HighlightController, HoverCardController, IdleController, InputMaskController, IntersectionController, LazyFrameController, ListboxController, LocalTimeController, MasonryController, MenuController, MenubarController, MeterController, MultiSelectController, NavigationMenuController, NestedFormController, NetworkStatusController, NumberInputController, OptimisticController, OtpController, OverflowIndicatorController, OverflowMenuController, PaginationController, PasswordRevealController, PasswordStrengthController, PersistController, PointerDragController, PopoverController, PortalController, PreviewGuardController, ProgressController, RadioGroupController, RangeSliderController, RatingController, ReadMoreController, ReadingProgressController, RelativeTimeController, ResetBeforeCacheController, ResizableController, RovingController, ScrollAreaController, ScrollRestoreController, ScrollVisibilityController, ScrollspyController, SeparatorController, SidebarController, SkeletonController, SliderController, SmartStickyHeaderController, SortableController, SpinnerController, type StateReason, StepIndicatorController, StepperController, StickToBottomController, StickyObserverController, SubmitOnceController, SwitchController, TabsController, TagsInputController, TextareaAutosizeController, ThemeController, TimePickerController, ToastController, ToggleGroupController, ToolbarController, TooltipController, TransitionController, TreeViewController, registerStimeo, stimeoControllers };