stimeo-ui 0.16.0 → 0.17.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 (328) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +8 -1
  3. package/dist/cable/index.d.ts +79 -23
  4. package/dist/cable/index.js +363 -436
  5. package/dist/cable/index.js.map +1 -1
  6. package/dist/controllers/accordion_controller.d.ts +4 -2
  7. package/dist/controllers/accordion_controller.js +166 -55
  8. package/dist/controllers/accordion_controller.js.map +1 -1
  9. package/dist/controllers/alert_dialog_controller.d.ts +12 -2
  10. package/dist/controllers/alert_dialog_controller.js +862 -221
  11. package/dist/controllers/alert_dialog_controller.js.map +1 -1
  12. package/dist/controllers/announcer_controller.d.ts +35 -5
  13. package/dist/controllers/announcer_controller.js +135 -198
  14. package/dist/controllers/announcer_controller.js.map +1 -1
  15. package/dist/controllers/aspect_ratio_controller.js +0 -10
  16. package/dist/controllers/aspect_ratio_controller.js.map +1 -1
  17. package/dist/controllers/auto_submit_controller.d.ts +30 -6
  18. package/dist/controllers/auto_submit_controller.js +271 -127
  19. package/dist/controllers/auto_submit_controller.js.map +1 -1
  20. package/dist/controllers/avatar_controller.js +171 -105
  21. package/dist/controllers/avatar_controller.js.map +1 -1
  22. package/dist/controllers/breadcrumb_controller.js +0 -130
  23. package/dist/controllers/breadcrumb_controller.js.map +1 -1
  24. package/dist/controllers/bulk_select_controller.d.ts +18 -0
  25. package/dist/controllers/bulk_select_controller.js +262 -52
  26. package/dist/controllers/bulk_select_controller.js.map +1 -1
  27. package/dist/controllers/calendar_controller.d.ts +24 -5
  28. package/dist/controllers/calendar_controller.js +175 -209
  29. package/dist/controllers/calendar_controller.js.map +1 -1
  30. package/dist/controllers/carousel_controller.d.ts +34 -7
  31. package/dist/controllers/carousel_controller.js +461 -399
  32. package/dist/controllers/carousel_controller.js.map +1 -1
  33. package/dist/controllers/character_counter_controller.d.ts +17 -1
  34. package/dist/controllers/character_counter_controller.js +246 -156
  35. package/dist/controllers/character_counter_controller.js.map +1 -1
  36. package/dist/controllers/checkbox_controller.d.ts +7 -2
  37. package/dist/controllers/checkbox_controller.js +32 -74
  38. package/dist/controllers/checkbox_controller.js.map +1 -1
  39. package/dist/controllers/clipboard_controller.d.ts +16 -5
  40. package/dist/controllers/clipboard_controller.js +168 -147
  41. package/dist/controllers/clipboard_controller.js.map +1 -1
  42. package/dist/controllers/collapsible_controller.d.ts +16 -4
  43. package/dist/controllers/collapsible_controller.js +301 -120
  44. package/dist/controllers/collapsible_controller.js.map +1 -1
  45. package/dist/controllers/color_picker_controller.d.ts +18 -6
  46. package/dist/controllers/color_picker_controller.js +149 -139
  47. package/dist/controllers/color_picker_controller.js.map +1 -1
  48. package/dist/controllers/combobox_controller.d.ts +23 -5
  49. package/dist/controllers/combobox_controller.js +360 -133
  50. package/dist/controllers/combobox_controller.js.map +1 -1
  51. package/dist/controllers/command_palette_controller.d.ts +45 -14
  52. package/dist/controllers/command_palette_controller.js +970 -358
  53. package/dist/controllers/command_palette_controller.js.map +1 -1
  54. package/dist/controllers/conditional_fields_controller.d.ts +15 -2
  55. package/dist/controllers/conditional_fields_controller.js +166 -124
  56. package/dist/controllers/conditional_fields_controller.js.map +1 -1
  57. package/dist/controllers/confirm_controller.d.ts +49 -12
  58. package/dist/controllers/confirm_controller.js +939 -248
  59. package/dist/controllers/confirm_controller.js.map +1 -1
  60. package/dist/controllers/context_menu_controller.d.ts +31 -4
  61. package/dist/controllers/context_menu_controller.js +256 -128
  62. package/dist/controllers/context_menu_controller.js.map +1 -1
  63. package/dist/controllers/count_up_controller.d.ts +13 -1
  64. package/dist/controllers/count_up_controller.js +73 -21
  65. package/dist/controllers/count_up_controller.js.map +1 -1
  66. package/dist/controllers/countdown_controller.d.ts +45 -1
  67. package/dist/controllers/countdown_controller.js +183 -148
  68. package/dist/controllers/countdown_controller.js.map +1 -1
  69. package/dist/controllers/currency_input_controller.d.ts +21 -2
  70. package/dist/controllers/currency_input_controller.js +134 -158
  71. package/dist/controllers/currency_input_controller.js.map +1 -1
  72. package/dist/controllers/data_grid_controller.d.ts +15 -4
  73. package/dist/controllers/data_grid_controller.js +114 -186
  74. package/dist/controllers/data_grid_controller.js.map +1 -1
  75. package/dist/controllers/date_range_picker_controller.d.ts +30 -5
  76. package/dist/controllers/date_range_picker_controller.js +288 -206
  77. package/dist/controllers/date_range_picker_controller.js.map +1 -1
  78. package/dist/controllers/dialog_controller.d.ts +13 -1
  79. package/dist/controllers/dialog_controller.js +858 -223
  80. package/dist/controllers/dialog_controller.js.map +1 -1
  81. package/dist/controllers/direct_upload_controller.d.ts +24 -7
  82. package/dist/controllers/direct_upload_controller.js +73 -153
  83. package/dist/controllers/direct_upload_controller.js.map +1 -1
  84. package/dist/controllers/dirty_form_controller.js +14 -48
  85. package/dist/controllers/dirty_form_controller.js.map +1 -1
  86. package/dist/controllers/dismissible_controller.d.ts +19 -0
  87. package/dist/controllers/dismissible_controller.js +175 -21
  88. package/dist/controllers/dismissible_controller.js.map +1 -1
  89. package/dist/controllers/drawer_controller.d.ts +39 -11
  90. package/dist/controllers/drawer_controller.js +933 -341
  91. package/dist/controllers/drawer_controller.js.map +1 -1
  92. package/dist/controllers/dropdown_controller.d.ts +31 -1
  93. package/dist/controllers/dropdown_controller.js +312 -82
  94. package/dist/controllers/dropdown_controller.js.map +1 -1
  95. package/dist/controllers/editable_controller.d.ts +12 -1
  96. package/dist/controllers/editable_controller.js +158 -68
  97. package/dist/controllers/editable_controller.js.map +1 -1
  98. package/dist/controllers/empty_state_controller.js +67 -53
  99. package/dist/controllers/empty_state_controller.js.map +1 -1
  100. package/dist/controllers/file_dropzone_controller.d.ts +60 -18
  101. package/dist/controllers/file_dropzone_controller.js +311 -207
  102. package/dist/controllers/file_dropzone_controller.js.map +1 -1
  103. package/dist/controllers/filter_controller.js +67 -39
  104. package/dist/controllers/filter_controller.js.map +1 -1
  105. package/dist/controllers/flash_controller.d.ts +31 -8
  106. package/dist/controllers/flash_controller.js +172 -314
  107. package/dist/controllers/flash_controller.js.map +1 -1
  108. package/dist/controllers/focus_controller.d.ts +12 -3
  109. package/dist/controllers/focus_controller.js +654 -240
  110. package/dist/controllers/focus_controller.js.map +1 -1
  111. package/dist/controllers/form_field_controller.js +147 -132
  112. package/dist/controllers/form_field_controller.js.map +1 -1
  113. package/dist/controllers/form_validation_controller.js +13 -86
  114. package/dist/controllers/form_validation_controller.js.map +1 -1
  115. package/dist/controllers/frame_loading_controller.d.ts +41 -9
  116. package/dist/controllers/frame_loading_controller.js +264 -213
  117. package/dist/controllers/frame_loading_controller.js.map +1 -1
  118. package/dist/controllers/highlight_controller.d.ts +22 -2
  119. package/dist/controllers/highlight_controller.js +107 -75
  120. package/dist/controllers/highlight_controller.js.map +1 -1
  121. package/dist/controllers/hover_card_controller.d.ts +56 -3
  122. package/dist/controllers/hover_card_controller.js +309 -118
  123. package/dist/controllers/hover_card_controller.js.map +1 -1
  124. package/dist/controllers/idle_controller.d.ts +42 -1
  125. package/dist/controllers/idle_controller.js +267 -101
  126. package/dist/controllers/idle_controller.js.map +1 -1
  127. package/dist/controllers/input_mask_controller.js +71 -72
  128. package/dist/controllers/input_mask_controller.js.map +1 -1
  129. package/dist/controllers/intersection_controller.d.ts +32 -13
  130. package/dist/controllers/intersection_controller.js +147 -106
  131. package/dist/controllers/intersection_controller.js.map +1 -1
  132. package/dist/controllers/lazy_frame_controller.d.ts +16 -0
  133. package/dist/controllers/lazy_frame_controller.js +9 -65
  134. package/dist/controllers/lazy_frame_controller.js.map +1 -1
  135. package/dist/controllers/listbox_controller.d.ts +34 -2
  136. package/dist/controllers/listbox_controller.js +306 -204
  137. package/dist/controllers/listbox_controller.js.map +1 -1
  138. package/dist/controllers/local_time_controller.d.ts +1 -1
  139. package/dist/controllers/local_time_controller.js +54 -64
  140. package/dist/controllers/local_time_controller.js.map +1 -1
  141. package/dist/controllers/masonry_controller.d.ts +11 -3
  142. package/dist/controllers/masonry_controller.js +70 -93
  143. package/dist/controllers/masonry_controller.js.map +1 -1
  144. package/dist/controllers/menu_controller.d.ts +32 -7
  145. package/dist/controllers/menu_controller.js +214 -156
  146. package/dist/controllers/menu_controller.js.map +1 -1
  147. package/dist/controllers/menubar_controller.d.ts +4 -4
  148. package/dist/controllers/menubar_controller.js +170 -329
  149. package/dist/controllers/menubar_controller.js.map +1 -1
  150. package/dist/controllers/meter_controller.d.ts +20 -0
  151. package/dist/controllers/meter_controller.js +151 -67
  152. package/dist/controllers/meter_controller.js.map +1 -1
  153. package/dist/controllers/multi_select_controller.d.ts +32 -4
  154. package/dist/controllers/multi_select_controller.js +593 -358
  155. package/dist/controllers/multi_select_controller.js.map +1 -1
  156. package/dist/controllers/navigation_menu_controller.d.ts +8 -1
  157. package/dist/controllers/navigation_menu_controller.js +124 -232
  158. package/dist/controllers/navigation_menu_controller.js.map +1 -1
  159. package/dist/controllers/nested_form_controller.d.ts +10 -0
  160. package/dist/controllers/nested_form_controller.js +352 -182
  161. package/dist/controllers/nested_form_controller.js.map +1 -1
  162. package/dist/controllers/network_status_controller.d.ts +23 -0
  163. package/dist/controllers/network_status_controller.js +242 -54
  164. package/dist/controllers/network_status_controller.js.map +1 -1
  165. package/dist/controllers/number_input_controller.d.ts +26 -2
  166. package/dist/controllers/number_input_controller.js +320 -263
  167. package/dist/controllers/number_input_controller.js.map +1 -1
  168. package/dist/controllers/optimistic_controller.js +16 -76
  169. package/dist/controllers/optimistic_controller.js.map +1 -1
  170. package/dist/controllers/otp_controller.d.ts +20 -4
  171. package/dist/controllers/otp_controller.js +198 -193
  172. package/dist/controllers/otp_controller.js.map +1 -1
  173. package/dist/controllers/overflow_indicator_controller.d.ts +5 -0
  174. package/dist/controllers/overflow_indicator_controller.js +116 -113
  175. package/dist/controllers/overflow_indicator_controller.js.map +1 -1
  176. package/dist/controllers/overflow_menu_controller.d.ts +48 -11
  177. package/dist/controllers/overflow_menu_controller.js +343 -283
  178. package/dist/controllers/overflow_menu_controller.js.map +1 -1
  179. package/dist/controllers/pagination_controller.d.ts +35 -6
  180. package/dist/controllers/pagination_controller.js +295 -119
  181. package/dist/controllers/pagination_controller.js.map +1 -1
  182. package/dist/controllers/password_reveal_controller.d.ts +45 -8
  183. package/dist/controllers/password_reveal_controller.js +389 -121
  184. package/dist/controllers/password_reveal_controller.js.map +1 -1
  185. package/dist/controllers/password_strength_controller.d.ts +14 -5
  186. package/dist/controllers/password_strength_controller.js +99 -153
  187. package/dist/controllers/password_strength_controller.js.map +1 -1
  188. package/dist/controllers/persist_controller.d.ts +11 -4
  189. package/dist/controllers/persist_controller.js +237 -103
  190. package/dist/controllers/persist_controller.js.map +1 -1
  191. package/dist/controllers/pointer_drag_controller.d.ts +19 -7
  192. package/dist/controllers/pointer_drag_controller.js +185 -165
  193. package/dist/controllers/pointer_drag_controller.js.map +1 -1
  194. package/dist/controllers/popover_controller.d.ts +38 -2
  195. package/dist/controllers/popover_controller.js +236 -97
  196. package/dist/controllers/popover_controller.js.map +1 -1
  197. package/dist/controllers/portal_controller.d.ts +44 -4
  198. package/dist/controllers/portal_controller.js +209 -76
  199. package/dist/controllers/portal_controller.js.map +1 -1
  200. package/dist/controllers/preview_guard_controller.d.ts +10 -4
  201. package/dist/controllers/preview_guard_controller.js +232 -116
  202. package/dist/controllers/preview_guard_controller.js.map +1 -1
  203. package/dist/controllers/progress_controller.d.ts +11 -0
  204. package/dist/controllers/progress_controller.js +125 -56
  205. package/dist/controllers/progress_controller.js.map +1 -1
  206. package/dist/controllers/radio_group_controller.d.ts +14 -5
  207. package/dist/controllers/radio_group_controller.js +117 -137
  208. package/dist/controllers/radio_group_controller.js.map +1 -1
  209. package/dist/controllers/range_slider_controller.d.ts +43 -3
  210. package/dist/controllers/range_slider_controller.js +192 -105
  211. package/dist/controllers/range_slider_controller.js.map +1 -1
  212. package/dist/controllers/rating_controller.d.ts +20 -4
  213. package/dist/controllers/rating_controller.js +418 -207
  214. package/dist/controllers/rating_controller.js.map +1 -1
  215. package/dist/controllers/read_more_controller.d.ts +2 -0
  216. package/dist/controllers/read_more_controller.js +134 -63
  217. package/dist/controllers/read_more_controller.js.map +1 -1
  218. package/dist/controllers/reading_progress_controller.d.ts +1 -1
  219. package/dist/controllers/reading_progress_controller.js +147 -135
  220. package/dist/controllers/reading_progress_controller.js.map +1 -1
  221. package/dist/controllers/relative_time_controller.d.ts +18 -4
  222. package/dist/controllers/relative_time_controller.js +145 -110
  223. package/dist/controllers/relative_time_controller.js.map +1 -1
  224. package/dist/controllers/reset_on_restore_controller.d.ts +85 -0
  225. package/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +97 -38
  226. package/dist/controllers/reset_on_restore_controller.js.map +1 -0
  227. package/dist/controllers/resizable_controller.d.ts +28 -2
  228. package/dist/controllers/resizable_controller.js +255 -139
  229. package/dist/controllers/resizable_controller.js.map +1 -1
  230. package/dist/controllers/roving_controller.js +39 -84
  231. package/dist/controllers/roving_controller.js.map +1 -1
  232. package/dist/controllers/scroll_area_controller.d.ts +7 -3
  233. package/dist/controllers/scroll_area_controller.js +428 -225
  234. package/dist/controllers/scroll_area_controller.js.map +1 -1
  235. package/dist/controllers/scroll_restore_controller.js +0 -53
  236. package/dist/controllers/scroll_restore_controller.js.map +1 -1
  237. package/dist/controllers/scroll_visibility_controller.d.ts +28 -12
  238. package/dist/controllers/scroll_visibility_controller.js +395 -145
  239. package/dist/controllers/scroll_visibility_controller.js.map +1 -1
  240. package/dist/controllers/scrollspy_controller.d.ts +6 -1
  241. package/dist/controllers/scrollspy_controller.js +181 -242
  242. package/dist/controllers/scrollspy_controller.js.map +1 -1
  243. package/dist/controllers/separator_controller.d.ts +19 -4
  244. package/dist/controllers/separator_controller.js +267 -140
  245. package/dist/controllers/separator_controller.js.map +1 -1
  246. package/dist/controllers/sidebar_controller.d.ts +64 -7
  247. package/dist/controllers/sidebar_controller.js +1023 -386
  248. package/dist/controllers/sidebar_controller.js.map +1 -1
  249. package/dist/controllers/skeleton_controller.d.ts +23 -0
  250. package/dist/controllers/skeleton_controller.js +248 -97
  251. package/dist/controllers/skeleton_controller.js.map +1 -1
  252. package/dist/controllers/slider_controller.d.ts +27 -1
  253. package/dist/controllers/slider_controller.js +159 -79
  254. package/dist/controllers/slider_controller.js.map +1 -1
  255. package/dist/controllers/smart_sticky_header_controller.d.ts +21 -3
  256. package/dist/controllers/smart_sticky_header_controller.js +162 -63
  257. package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
  258. package/dist/controllers/sortable_controller.js +0 -112
  259. package/dist/controllers/sortable_controller.js.map +1 -1
  260. package/dist/controllers/spinner_controller.d.ts +39 -4
  261. package/dist/controllers/spinner_controller.js +277 -169
  262. package/dist/controllers/spinner_controller.js.map +1 -1
  263. package/dist/controllers/step_indicator_controller.d.ts +5 -0
  264. package/dist/controllers/step_indicator_controller.js +99 -46
  265. package/dist/controllers/step_indicator_controller.js.map +1 -1
  266. package/dist/controllers/stepper_controller.d.ts +12 -3
  267. package/dist/controllers/stepper_controller.js +114 -73
  268. package/dist/controllers/stepper_controller.js.map +1 -1
  269. package/dist/controllers/stick_to_bottom_controller.d.ts +6 -0
  270. package/dist/controllers/stick_to_bottom_controller.js +262 -138
  271. package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
  272. package/dist/controllers/sticky_observer_controller.d.ts +11 -0
  273. package/dist/controllers/sticky_observer_controller.js +214 -49
  274. package/dist/controllers/sticky_observer_controller.js.map +1 -1
  275. package/dist/controllers/submit_once_controller.d.ts +23 -8
  276. package/dist/controllers/submit_once_controller.js +294 -205
  277. package/dist/controllers/submit_once_controller.js.map +1 -1
  278. package/dist/controllers/switch_controller.d.ts +12 -1
  279. package/dist/controllers/switch_controller.js +33 -60
  280. package/dist/controllers/switch_controller.js.map +1 -1
  281. package/dist/controllers/tabs_controller.d.ts +2 -2
  282. package/dist/controllers/tabs_controller.js +35 -29
  283. package/dist/controllers/tabs_controller.js.map +1 -1
  284. package/dist/controllers/tags_input_controller.d.ts +12 -0
  285. package/dist/controllers/tags_input_controller.js +136 -145
  286. package/dist/controllers/tags_input_controller.js.map +1 -1
  287. package/dist/controllers/textarea_autosize_controller.d.ts +10 -0
  288. package/dist/controllers/textarea_autosize_controller.js +71 -58
  289. package/dist/controllers/textarea_autosize_controller.js.map +1 -1
  290. package/dist/controllers/theme_controller.d.ts +21 -9
  291. package/dist/controllers/theme_controller.js +130 -112
  292. package/dist/controllers/theme_controller.js.map +1 -1
  293. package/dist/controllers/time_picker_controller.d.ts +18 -1
  294. package/dist/controllers/time_picker_controller.js +146 -80
  295. package/dist/controllers/time_picker_controller.js.map +1 -1
  296. package/dist/controllers/toast_controller.d.ts +36 -11
  297. package/dist/controllers/toast_controller.js +186 -297
  298. package/dist/controllers/toast_controller.js.map +1 -1
  299. package/dist/controllers/toggle_group_controller.d.ts +12 -3
  300. package/dist/controllers/toggle_group_controller.js +95 -136
  301. package/dist/controllers/toggle_group_controller.js.map +1 -1
  302. package/dist/controllers/toolbar_controller.js +68 -98
  303. package/dist/controllers/toolbar_controller.js.map +1 -1
  304. package/dist/controllers/tooltip_controller.d.ts +45 -3
  305. package/dist/controllers/tooltip_controller.js +158 -124
  306. package/dist/controllers/tooltip_controller.js.map +1 -1
  307. package/dist/controllers/transition_controller.d.ts +22 -4
  308. package/dist/controllers/transition_controller.js +123 -119
  309. package/dist/controllers/transition_controller.js.map +1 -1
  310. package/dist/controllers/tree_view_controller.d.ts +21 -11
  311. package/dist/controllers/tree_view_controller.js +150 -345
  312. package/dist/controllers/tree_view_controller.js.map +1 -1
  313. package/dist/index.d.ts +8 -5
  314. package/dist/index.js +6370 -9346
  315. package/dist/index.js.map +1 -1
  316. package/dist/inspector/cli.d.ts +23 -3
  317. package/dist/inspector/cli.js +92 -37
  318. package/dist/inspector/cli.js.map +1 -1
  319. package/dist/inspector/cli_bin.js +92 -56
  320. package/dist/inspector/cli_bin.js.map +1 -1
  321. package/dist/inspector/examples.json +8 -8
  322. package/dist/inspector/manifest.json +787 -100
  323. package/dist/positioning/index.d.ts +8 -0
  324. package/dist/positioning/index.js +148 -47
  325. package/dist/positioning/index.js.map +1 -1
  326. package/package.json +6 -6
  327. package/dist/controllers/reset_before_cache_controller.d.ts +0 -76
  328. package/dist/controllers/reset_before_cache_controller.js.map +0 -1
@@ -53,11 +53,18 @@ import { Controller } from '@hotwired/stimulus';
53
53
  * exactly one layer. `Tab` lets the browser move focus first, then closes on
54
54
  * the next task. An outside click or context-menu invocation closes without
55
55
  * stealing focus from its destination.
56
+ * - A region or a menu that takes over — in one task, or after an earlier one
57
+ * leaves in a later task — carries the open state, and the menu that takes over
58
+ * an open one is placed where it was opened; focus stays where the swap left it.
59
+ * With no menu left the region reads closed and the menu leaves the Escape stack.
60
+ * One that stops resolving as the target gets back the `data-state` or `hidden` it
61
+ * carried before this controller wrote on it.
56
62
  * - Each move of the open state is reported: `stimeo--context-menu:open` and
57
63
  * `stimeo--context-menu:close` dispatch `{ reason: StateReason }`, after the
58
64
  * state attributes are written. Both are informational, so neither is
59
65
  * cancelable. A call that leaves the state where it already was, the
60
- * normalization in {@link connect}, and {@link disconnect} are all silent.
66
+ * normalization in {@link connect}, a region or a menu that takes over, and
67
+ * {@link disconnect} are all silent.
61
68
  *
62
69
  * Roving focus skips `hidden` and natively `disabled` items. An
63
70
  * `aria-disabled="true"` item stays reachable by arrow keys — APG marks that
@@ -70,7 +77,9 @@ declare class ContextMenuController extends Controller<HTMLElement> {
70
77
  static actions: readonly ["activate", "onItemKeydown", "onRegionKeydown", "open"];
71
78
  static events: readonly ["close", "open"];
72
79
  readonly regionTarget: HTMLElement;
80
+ readonly regionTargets: HTMLElement[];
73
81
  readonly menuTarget: HTMLElement;
82
+ readonly menuTargets: HTMLElement[];
74
83
  readonly itemTargets: HTMLButtonElement[];
75
84
  readonly hasRegionTarget: boolean;
76
85
  readonly hasMenuTarget: boolean;
@@ -78,11 +87,29 @@ declare class ContextMenuController extends Controller<HTMLElement> {
78
87
  connect(): void;
79
88
  /** Releases the listeners, stack membership, and pending Tab-close task. */
80
89
  disconnect(): void;
90
+ /** Brings a region that arrives after connect to the open state. */
91
+ regionTargetConnected(): void;
81
92
  /**
82
- * Opens the menu from a `contextmenu` event: suppresses the native menu and
83
- * places this one at the pointer coordinate.
93
+ * Gives a region that no longer resolves as one its own `data-state` back — after
94
+ * `disconnect()` too, since dropping the identifier leaves the element on the page — and
95
+ * brings the region that stays to the open state.
84
96
  */
85
- open(event: MouseEvent): void;
97
+ regionTargetDisconnected(region: HTMLElement): void;
98
+ /** Applies the open state to a menu that arrives after connect in front of the others. */
99
+ menuTargetConnected(): void;
100
+ /**
101
+ * Applies the open state to the menu left, then gives a menu that no longer resolves as the
102
+ * target its own `hidden` back — after `disconnect()` too, since dropping the identifier
103
+ * leaves the element on the page. The open state is read off the departing menu first,
104
+ * while it still carries it.
105
+ */
106
+ menuTargetDisconnected(menu: HTMLElement): void;
107
+ /**
108
+ * Opens at a mouse event's coordinate, an explicit element's center, or the
109
+ * region's center with no argument. Opening focuses the new menu in all paths.
110
+ * An explicit element may be an anchor outside the controller.
111
+ */
112
+ open(input?: Event | HTMLElement): void;
86
113
  /** Keyboard entry on the region: `Shift+F10` / `ContextMenu` open at center. */
87
114
  onRegionKeydown(event: KeyboardEvent): void;
88
115
  /** Roving focus and closing keys inside the menu. */
@@ -1,14 +1,179 @@
1
1
  import { Controller } from '@hotwired/stimulus';
2
2
 
3
- // src/controllers/context_menu_controller.ts
3
+ var FOCUS_EVENTS = /* @__PURE__ */ new Set(["blur", "focus", "focusin", "focusout"]);
4
+ var POINTER_EVENTS = /* @__PURE__ */ new Set(["mouseenter", "mouseleave", "pointerenter", "pointerleave"]);
5
+ function stateReasonFor(event) {
6
+ if (!event) return "api";
7
+ if (FOCUS_EVENTS.has(event.type)) return "focus";
8
+ if (POINTER_EVENTS.has(event.type)) return "pointer";
9
+ return "user";
10
+ }
11
+
12
+ function actionSource(source) {
13
+ if (source instanceof HTMLElement) {
14
+ return { event: null, host: source, origin: source, reason: "api" };
15
+ }
16
+ const event = source ?? null;
17
+ return {
18
+ event,
19
+ host: event?.currentTarget instanceof Element ? event.currentTarget : null,
20
+ origin: event?.target instanceof Element ? event.target : null,
21
+ reason: stateReasonFor(event)
22
+ };
23
+ }
4
24
 
5
- // src/utils/arrow_step.ts
6
25
  function isReservedArrowChord(event, allow = []) {
7
26
  if (!event.key.startsWith("Arrow")) return false;
8
27
  return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
9
28
  }
10
29
 
11
- // src/utils/escape_layer.ts
30
+ function sharedRegistry(key, create) {
31
+ const symbol = Symbol.for(key);
32
+ const scope = globalThis;
33
+ const existing = scope[symbol];
34
+ if (existing !== void 0) return existing;
35
+ const registry = create();
36
+ Object.defineProperty(globalThis, symbol, { value: registry });
37
+ return registry;
38
+ }
39
+
40
+ function isAttributeValue(value) {
41
+ return value === null || typeof value === "string";
42
+ }
43
+ function readLeaseRecord(element, record) {
44
+ try {
45
+ const parsed = JSON.parse(element.getAttribute(record) ?? "null");
46
+ if (!Array.isArray(parsed)) return void 0;
47
+ const [original, written, ...rest] = parsed;
48
+ const linked = rest[0] !== false;
49
+ const beneath = linked ? rest : rest.slice(1);
50
+ if (!beneath.every((under) => typeof under === "string")) return void 0;
51
+ return isAttributeValue(original) && isAttributeValue(written) ? { original, written, linked, beneath } : void 0;
52
+ } catch {
53
+ return void 0;
54
+ }
55
+ }
56
+ var AttributeLease = class _AttributeLease {
57
+ static #registry = sharedRegistry(
58
+ "stimeo-ui.attribute-lease.registry.v1",
59
+ () => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
60
+ );
61
+ #attribute;
62
+ #record;
63
+ #base;
64
+ #held = /* @__PURE__ */ new Set();
65
+ constructor(attribute, owner, options = {}) {
66
+ this.#attribute = attribute;
67
+ this.#record = `data-${owner}-${attribute}-lease`;
68
+ this.#base = options.base === true;
69
+ const names = _AttributeLease.#registry.names;
70
+ names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
71
+ }
72
+ write(element, value) {
73
+ const stack = this.#stack(element);
74
+ const hold = this.#take(stack) ?? this.#acquire(element, stack);
75
+ this.#held.add(element);
76
+ const shows = !this.#covered(element, stack, hold);
77
+ hold.written = value;
78
+ this.#mark(element, stack);
79
+ if (shows) this.#reflect(element, value);
80
+ }
81
+ return(element) {
82
+ const held = this.#held.delete(element);
83
+ const known = _AttributeLease.#registry.stacks.get(element)?.has(this.#attribute);
84
+ const stack = held || known || element.hasAttribute(this.#record) ? this.#stack(element) : [];
85
+ const hold = this.#take(stack);
86
+ if (!hold) return false;
87
+ const carries = element.getAttribute(this.#attribute) === hold.written;
88
+ const owns = carries && !this.#covered(element, stack, hold);
89
+ const index = stack.indexOf(hold);
90
+ stack.splice(index, 1);
91
+ const above = stack[index];
92
+ if (above) above.linked &&= hold.linked;
93
+ this.#mark(element, stack);
94
+ if (!owns) return true;
95
+ const below = hold.linked ? stack[index - 1] : void 0;
96
+ this.#reflect(element, below ? below.written : hold.original);
97
+ return true;
98
+ }
99
+ returnAll() {
100
+ for (const element of Array.from(this.#held)) this.return(element);
101
+ }
102
+ #stack(element) {
103
+ const stacks = _AttributeLease.#registry.stacks;
104
+ const byAttribute = stacks.get(element) ?? /* @__PURE__ */ new Map();
105
+ stacks.set(element, byAttribute);
106
+ const stack = byAttribute.get(this.#attribute) ?? this.#seed(element);
107
+ byAttribute.set(this.#attribute, stack);
108
+ return stack;
109
+ }
110
+ #seed(element) {
111
+ const found = [];
112
+ for (const record of _AttributeLease.#registry.names.get(this.#attribute) ?? []) {
113
+ const read = readLeaseRecord(element, record);
114
+ if (read) found.push({ ...read, record });
115
+ }
116
+ const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
117
+ return found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
118
+ lease: null,
119
+ record,
120
+ written,
121
+ original,
122
+ linked: linked && index > 0
123
+ }));
124
+ }
125
+ #take(stack) {
126
+ const hold = stack.find((held) => held.lease === this) ?? stack.find((held) => held.lease === null && held.record === this.#record);
127
+ if (hold) hold.lease = this;
128
+ return hold;
129
+ }
130
+ #acquire(element, stack) {
131
+ const current = element.getAttribute(this.#attribute);
132
+ const top = stack.at(-1);
133
+ const bottom = stack[0];
134
+ const over = top?.written === current ? top : void 0;
135
+ const from = this.#base ? bottom : over;
136
+ const hold = {
137
+ lease: this,
138
+ record: this.#record,
139
+ written: current,
140
+ original: from ? from.original : current,
141
+ linked: !this.#base && over !== void 0
142
+ };
143
+ if (this.#base && bottom) {
144
+ bottom.linked = true;
145
+ stack.unshift(hold);
146
+ } else stack.push(hold);
147
+ return hold;
148
+ }
149
+ #covered(element, stack, hold) {
150
+ const current = element.getAttribute(this.#attribute);
151
+ return stack.slice(stack.indexOf(hold) + 1).some((above) => above.written === current);
152
+ }
153
+ #mark(element, stack) {
154
+ const records = /* @__PURE__ */ new Map([[this.#record, null]]);
155
+ stack.forEach((hold, index) => {
156
+ const names = stack.slice(0, index).map((under) => under.record);
157
+ const beneath = [...new Set(names.reverse())].filter((name) => name !== hold.record);
158
+ const marker = hold.linked || beneath.length === 0 ? [] : [false];
159
+ records.set(
160
+ hold.record,
161
+ hold.written === hold.original && beneath.length === 0 && index === stack.length - 1 ? null : JSON.stringify([hold.original, hold.written, ...marker, ...beneath])
162
+ );
163
+ });
164
+ for (const [name, recorded] of records) {
165
+ if (element.getAttribute(name) === recorded) continue;
166
+ if (recorded === null) element.removeAttribute(name);
167
+ else element.setAttribute(name, recorded);
168
+ }
169
+ }
170
+ #reflect(element, value) {
171
+ if (element.getAttribute(this.#attribute) === value) return;
172
+ if (value === null) element.removeAttribute(this.#attribute);
173
+ else element.setAttribute(this.#attribute, value);
174
+ }
175
+ };
176
+
12
177
  function claimsWhileFocusWithin(element) {
13
178
  return () => {
14
179
  const active = element.ownerDocument.activeElement;
@@ -16,17 +181,11 @@ function claimsWhileFocusWithin(element) {
16
181
  };
17
182
  }
18
183
  var EscapeLayer = class _EscapeLayer {
19
- static #registries = /* @__PURE__ */ new WeakMap();
20
- #ownerDocument = null;
21
- /** Dismissal callback while active; `null` when inactive. */
22
- #onDismiss = null;
23
- /** Live predicate deciding whether the layer claims a press; `null` = always. */
24
- #claims = null;
25
- /**
26
- * Activates this layer at the top of its document's Escape stack, installing
27
- * the document's shared resolver listener if this is its first layer.
28
- * Re-activating an already-active layer moves it to the top.
29
- */
184
+ static #registries = sharedRegistry(
185
+ "stimeo-ui.escape-layer.registry.v1",
186
+ () => /* @__PURE__ */ new WeakMap()
187
+ );
188
+ #active = null;
30
189
  activate(ownerDocument = document, options) {
31
190
  this.deactivate();
32
191
  let registry = _EscapeLayer.#registries.get(ownerDocument);
@@ -35,44 +194,31 @@ var EscapeLayer = class _EscapeLayer {
35
194
  _EscapeLayer.#registries.set(ownerDocument, registry);
36
195
  ownerDocument.addEventListener("keydown", registry.onKeydown);
37
196
  }
38
- registry.stack.push(this);
39
- this.#ownerDocument = ownerDocument;
40
- this.#onDismiss = options.onDismiss;
41
- this.#claims = options.claims ?? null;
42
- }
43
- /**
44
- * Removes this layer from its document's Escape stack, uninstalling the
45
- * shared listener when the stack empties. Safe to call when inactive.
46
- */
197
+ const entry = { onDismiss: options.onDismiss, claims: options.claims ?? null };
198
+ registry.stack.push(entry);
199
+ this.#active = { document: ownerDocument, entry };
200
+ }
47
201
  deactivate() {
48
- const ownerDocument = this.#ownerDocument;
49
- if (!ownerDocument) return;
50
- const registry = _EscapeLayer.#registries.get(ownerDocument);
202
+ const active = this.#active;
203
+ if (!active) return;
204
+ const registry = _EscapeLayer.#registries.get(active.document);
51
205
  if (registry) {
52
- const index = registry.stack.lastIndexOf(this);
206
+ const index = registry.stack.lastIndexOf(active.entry);
53
207
  if (index >= 0) registry.stack.splice(index, 1);
54
208
  if (registry.stack.length === 0) {
55
- ownerDocument.removeEventListener("keydown", registry.onKeydown);
56
- _EscapeLayer.#registries.delete(ownerDocument);
209
+ active.document.removeEventListener("keydown", registry.onKeydown);
210
+ _EscapeLayer.#registries.delete(active.document);
57
211
  }
58
212
  }
59
- this.#ownerDocument = null;
60
- this.#onDismiss = null;
61
- this.#claims = null;
62
- }
63
- /**
64
- * Whether this active layer would own a press right now: it is the topmost
65
- * layer whose {@link EscapeLayerOptions.claims} passes. Exposed for tests
66
- * and diagnostics — production dismissal goes through the shared listener.
67
- */
213
+ this.#active = null;
214
+ }
68
215
  get ownsEscape() {
69
- const ownerDocument = this.#ownerDocument;
70
- if (!ownerDocument) return false;
71
- const registry = _EscapeLayer.#registries.get(ownerDocument);
216
+ const active = this.#active;
217
+ if (!active) return false;
218
+ const registry = _EscapeLayer.#registries.get(active.document);
72
219
  if (!registry) return false;
73
- return _EscapeLayer.#resolveOwner(registry.stack) === this;
220
+ return _EscapeLayer.#resolveOwner(registry.stack) === active.entry;
74
221
  }
75
- /** Builds a document's registry with its shared resolver listener. */
76
222
  static #createRegistry() {
77
223
  const registry = {
78
224
  stack: [],
@@ -81,60 +227,40 @@ var EscapeLayer = class _EscapeLayer {
81
227
  const owner = _EscapeLayer.#resolveOwner(registry.stack);
82
228
  if (!owner) return;
83
229
  event.preventDefault();
84
- owner.#onDismiss?.();
230
+ owner.onDismiss();
85
231
  }
86
232
  };
87
233
  return registry;
88
234
  }
89
- /** The topmost stack layer whose claims predicate passes, or `null`. */
90
235
  static #resolveOwner(stack) {
91
236
  for (let index = stack.length - 1; index >= 0; index--) {
92
237
  const layer = stack[index];
93
238
  if (!layer) continue;
94
- if (layer.#claims && !layer.#claims()) continue;
239
+ if (layer.claims && !layer.claims()) continue;
95
240
  return layer;
96
241
  }
97
242
  return null;
98
243
  }
99
244
  };
100
245
 
101
- // src/utils/safe_timeout.ts
102
246
  var TimerRegistry = class {
103
- /** Live timer ids that have not yet been cleared (or, for timeouts, fired). */
104
247
  ids = /* @__PURE__ */ new Set();
105
- /**
106
- * Cancels a single tracked timer.
107
- *
108
- * No-ops if the id is unknown (already cleared, fired, or never owned by this
109
- * registry), so callers can clear defensively without guarding.
110
- */
111
248
  clear(id) {
112
249
  if (this.ids.delete(id)) {
113
250
  this.cancel(id);
114
251
  }
115
252
  }
116
- /**
117
- * Cancels every tracked timer. Call this from a controller's `disconnect()`
118
- * to guarantee no timer outlives the element.
119
- */
120
253
  clearAll() {
121
254
  for (const id of this.ids) {
122
255
  this.cancel(id);
123
256
  }
124
257
  this.ids.clear();
125
258
  }
126
- /** Number of timers currently tracked (pending). */
127
259
  get size() {
128
260
  return this.ids.size;
129
261
  }
130
262
  };
131
263
  var SafeTimeout = class extends TimerRegistry {
132
- /**
133
- * Schedules `callback` after `delay` ms and returns the timer id.
134
- *
135
- * The id is removed from the registry automatically when the timeout fires,
136
- * so {@link TimerRegistry.size | size} reflects only still-pending timers.
137
- */
138
264
  set(callback, delay) {
139
265
  const id = this.schedule(() => {
140
266
  this.ids.delete(id);
@@ -151,36 +277,28 @@ var SafeTimeout = class extends TimerRegistry {
151
277
  }
152
278
  };
153
279
 
154
- // src/utils/state_reason.ts
155
- var FOCUS_EVENTS = /* @__PURE__ */ new Set(["blur", "focus", "focusin", "focusout"]);
156
- var POINTER_EVENTS = /* @__PURE__ */ new Set(["mouseenter", "mouseleave", "pointerenter", "pointerleave"]);
157
- function stateReasonFor(event) {
158
- if (!event) return "api";
159
- if (FOCUS_EVENTS.has(event.type)) return "focus";
160
- if (POINTER_EVENTS.has(event.type)) return "pointer";
161
- return "user";
162
- }
163
-
164
- // src/controllers/context_menu_controller.ts
165
- var ContextMenuController = class extends Controller {
280
+ var ContextMenuController = class _ContextMenuController extends Controller {
281
+ static #COORDINATE = ["--stimeo--context-menu-x", "--stimeo--context-menu-y"];
166
282
  static targets = ["region", "menu", "item"];
167
283
  static actions = ["activate", "onItemKeydown", "onRegionKeydown", "open"];
168
284
  static events = ["close", "open"];
169
285
  #timers = new SafeTimeout();
170
- /** Escape-stack membership while open; the shared resolver dismisses via it. */
171
286
  #escapeLayer = new EscapeLayer();
172
- /** Whether state moves are reported: set once `connect()` settled the baseline. */
287
+ #menuHidden = new AttributeLease("hidden", this.identifier);
288
+ #regionState = new AttributeLease("data-state", this.identifier);
289
+ #menu = null;
290
+ #connected = false;
173
291
  #reporting = false;
174
- /** Starts closed and registers delegated activation and outside-pointer listeners. */
175
292
  connect() {
176
293
  this.#closeMenu("api");
177
294
  this.element.addEventListener("click", this.#onItemClickCapture, true);
178
295
  document.addEventListener("click", this.#onOutsidePointer, true);
179
296
  document.addEventListener("contextmenu", this.#onOutsidePointer, true);
297
+ this.#connected = true;
180
298
  this.#reporting = true;
181
299
  }
182
- /** Releases the listeners, stack membership, and pending Tab-close task. */
183
300
  disconnect() {
301
+ this.#connected = false;
184
302
  this.#reporting = false;
185
303
  this.#timers.clearAll();
186
304
  this.#escapeLayer.deactivate();
@@ -188,15 +306,31 @@ var ContextMenuController = class extends Controller {
188
306
  document.removeEventListener("click", this.#onOutsidePointer, true);
189
307
  document.removeEventListener("contextmenu", this.#onOutsidePointer, true);
190
308
  }
191
- /**
192
- * Opens the menu from a `contextmenu` event: suppresses the native menu and
193
- * places this one at the pointer coordinate.
194
- */
195
- open(event) {
196
- event.preventDefault();
197
- this.#openAt(event.clientX, event.clientY, stateReasonFor(event));
309
+ regionTargetConnected() {
310
+ if (this.#connected) this.#reflectRegion();
311
+ }
312
+ regionTargetDisconnected(region) {
313
+ if (!this.regionTargets.includes(region)) this.#regionState.return(region);
314
+ if (this.#connected) this.#reflectRegion();
315
+ }
316
+ menuTargetConnected() {
317
+ this.#adoptMenu();
318
+ }
319
+ menuTargetDisconnected(menu) {
320
+ this.#adoptMenu();
321
+ if (!this.menuTargets.includes(menu)) this.#menuHidden.return(menu);
322
+ }
323
+ open(input) {
324
+ const source = actionSource(input);
325
+ source.event?.preventDefault();
326
+ if (source.event instanceof MouseEvent) {
327
+ this.#openAt(source.event.clientX, source.event.clientY, source.reason);
328
+ return;
329
+ }
330
+ const anchor = source.host ?? (this.hasRegionTarget ? this.regionTarget : null);
331
+ const rect = anchor?.getBoundingClientRect() ?? { left: 0, top: 0, width: 0, height: 0 };
332
+ this.#openAt(rect.left + rect.width / 2, rect.top + rect.height / 2, source.reason);
198
333
  }
199
- /** Keyboard entry on the region: `Shift+F10` / `ContextMenu` open at center. */
200
334
  onRegionKeydown(event) {
201
335
  if (event.defaultPrevented) return;
202
336
  const isContextKey = event.key === "ContextMenu" || event.key === "F10" && event.shiftKey;
@@ -205,7 +339,6 @@ var ContextMenuController = class extends Controller {
205
339
  const rect = this.hasRegionTarget ? this.regionTarget.getBoundingClientRect() : { left: 0, top: 0, width: 0, height: 0 };
206
340
  this.#openAt(rect.left + rect.width / 2, rect.top + rect.height / 2, stateReasonFor(event));
207
341
  }
208
- /** Roving focus and closing keys inside the menu. */
209
342
  onItemKeydown(event) {
210
343
  if (event.defaultPrevented) return;
211
344
  if (isReservedArrowChord(event)) return;
@@ -240,56 +373,67 @@ var ContextMenuController = class extends Controller {
240
373
  break;
241
374
  }
242
375
  }
243
- /** Closes after an item is activated and restores focus to the region. */
244
376
  activate() {
245
377
  this.#closeAndRestore("select");
246
378
  }
247
- /** Opens the menu at viewport coordinates `(x, y)` and focuses the first item. */
248
379
  #openAt(x, y, reason) {
249
380
  if (!this.hasMenuTarget) return;
250
381
  this.#timers.clearAll();
382
+ const menu = this.menuTarget;
251
383
  const was = this.#isOpen;
252
384
  this.#escapeLayer.activate(document, {
253
385
  onDismiss: () => this.#closeAndRestore("escape"),
254
386
  claims: claimsWhileFocusWithin(this.element)
255
387
  });
256
- this.menuTarget.style.setProperty("--stimeo--context-menu-x", `${x}px`);
257
- this.menuTarget.style.setProperty("--stimeo--context-menu-y", `${y}px`);
258
- this.menuTarget.hidden = false;
259
- if (this.hasRegionTarget) this.regionTarget.setAttribute("data-state", "open");
388
+ menu.style.setProperty(_ContextMenuController.#COORDINATE[0], `${x}px`);
389
+ menu.style.setProperty(_ContextMenuController.#COORDINATE[1], `${y}px`);
390
+ this.#menuHidden.write(menu, null);
391
+ this.#menu = menu;
392
+ if (this.hasRegionTarget) this.#regionState.write(this.regionTarget, "open");
260
393
  if (!was && this.#reporting) this.dispatch("open", { detail: { reason }, cancelable: false });
261
394
  if (!this.#isOpen) return;
262
395
  this.#navigableItems[0]?.focus();
263
396
  }
264
- /** Hides the menu, reflects the collapsed state on the region, and reports a move. */
265
397
  #closeMenu(reason) {
266
398
  this.#timers.clearAll();
267
399
  this.#escapeLayer.deactivate();
268
- if (!this.hasMenuTarget) return;
400
+ const menu = this.hasMenuTarget ? this.menuTarget : null;
401
+ this.#menu = menu;
402
+ if (!menu) return;
269
403
  const was = this.#isOpen;
270
- this.menuTarget.hidden = true;
271
- if (this.hasRegionTarget) this.regionTarget.setAttribute("data-state", "closed");
404
+ this.#menuHidden.write(menu, "");
405
+ if (this.hasRegionTarget) this.#regionState.write(this.regionTarget, "closed");
272
406
  if (was && this.#reporting) this.dispatch("close", { detail: { reason }, cancelable: false });
273
407
  }
274
- /** Closes the menu and returns focus to the region (Escape / activation). */
408
+ #adoptMenu() {
409
+ if (!this.#connected) return;
410
+ const menu = this.hasMenuTarget ? this.menuTarget : null;
411
+ const previous = this.#menu;
412
+ this.#menu = menu;
413
+ if (!menu) {
414
+ this.#escapeLayer.deactivate();
415
+ } else if (menu !== previous) {
416
+ const open = previous !== null && !previous.hidden;
417
+ if (open) {
418
+ for (const property of _ContextMenuController.#COORDINATE) {
419
+ menu.style.setProperty(property, previous.style.getPropertyValue(property));
420
+ }
421
+ }
422
+ this.#menuHidden.write(menu, open ? null : "");
423
+ }
424
+ this.#reflectRegion();
425
+ }
426
+ #reflectRegion() {
427
+ if (!this.hasRegionTarget) return;
428
+ this.#regionState.write(this.regionTarget, this.#isOpen ? "open" : "closed");
429
+ }
275
430
  #closeAndRestore(reason) {
276
431
  this.#closeMenu(reason);
277
432
  if (this.hasRegionTarget) this.regionTarget.focus();
278
433
  }
279
- /**
280
- * Closes when a click or context-menu invocation lands outside this instance.
281
- * Both subscriptions observe in the capture phase, so the target is judged
282
- * against the tree the user actually pressed even when a consumer handler
283
- * detaches it, and application code that stops bubbling cannot leave the menu
284
- * open.
285
- */
286
434
  #onOutsidePointer = (event) => {
287
435
  if (this.#isOpen && !this.element.contains(event.target)) this.#closeMenu("outside");
288
436
  };
289
- /**
290
- * Captures clicks so `aria-disabled` commands cannot reach consumer handlers.
291
- * Native Enter/Space activation also synthesizes a click and is blocked here.
292
- */
293
437
  #onItemClickCapture = (event) => {
294
438
  const target = event.target;
295
439
  if (!(target instanceof Node)) return;
@@ -300,33 +444,17 @@ var ContextMenuController = class extends Controller {
300
444
  event.preventDefault();
301
445
  event.stopImmediatePropagation();
302
446
  };
303
- /** Menu items eligible for roving focus (excludes hidden / natively disabled). */
304
447
  get #navigableItems() {
305
448
  return this.itemTargets.filter((item) => this.#isNavigable(item));
306
449
  }
307
- /**
308
- * An item can take roving focus unless it is `hidden` or a natively `disabled`
309
- * form control. CSS-only visibility is not detectable here and is the
310
- * consumer's responsibility.
311
- *
312
- * **`aria-disabled="true"` stays reachable.** APG separates the two attributes
313
- * by intent: `disabled` is for controls whose existence can be inferred from a
314
- * neighbour (a greyed Next next to a Prev), while `aria-disabled` marks a
315
- * control that must stay *discoverable* — and it names menu items as the
316
- * example. Skipping it would hide the command's existence from a keyboard user
317
- * entirely. Activation is suppressed separately, so the item announces itself
318
- * and does nothing.
319
- */
320
450
  #isNavigable(item) {
321
451
  if (item.hasAttribute("hidden")) return false;
322
452
  return !item.disabled;
323
453
  }
324
- /** Whether the menu is currently visible. */
325
454
  get #isOpen() {
326
455
  return this.hasMenuTarget && !this.menuTarget.hidden;
327
456
  }
328
457
  };
329
458
 
330
459
  export { ContextMenuController };
331
- //# sourceMappingURL=context_menu_controller.js.map
332
460
  //# sourceMappingURL=context_menu_controller.js.map