stimeo-ui 0.15.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 (332) hide show
  1. package/CHANGELOG.md +209 -0
  2. package/README.md +128 -1
  3. package/dist/cable/index.d.ts +79 -23
  4. package/dist/cable/index.js +392 -417
  5. package/dist/cable/index.js.map +1 -1
  6. package/dist/controllers/accordion_controller.d.ts +34 -5
  7. package/dist/controllers/accordion_controller.js +228 -28
  8. package/dist/controllers/accordion_controller.js.map +1 -1
  9. package/dist/controllers/alert_dialog_controller.d.ts +20 -9
  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 +38 -7
  13. package/dist/controllers/announcer_controller.js +135 -191
  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 +295 -110
  19. package/dist/controllers/auto_submit_controller.js.map +1 -1
  20. package/dist/controllers/avatar_controller.js +172 -106
  21. package/dist/controllers/avatar_controller.js.map +1 -1
  22. package/dist/controllers/breadcrumb_controller.d.ts +10 -7
  23. package/dist/controllers/breadcrumb_controller.js +0 -129
  24. package/dist/controllers/breadcrumb_controller.js.map +1 -1
  25. package/dist/controllers/bulk_select_controller.d.ts +22 -4
  26. package/dist/controllers/bulk_select_controller.js +264 -53
  27. package/dist/controllers/bulk_select_controller.js.map +1 -1
  28. package/dist/controllers/calendar_controller.d.ts +109 -28
  29. package/dist/controllers/calendar_controller.js +346 -180
  30. package/dist/controllers/calendar_controller.js.map +1 -1
  31. package/dist/controllers/carousel_controller.d.ts +99 -27
  32. package/dist/controllers/carousel_controller.js +587 -300
  33. package/dist/controllers/carousel_controller.js.map +1 -1
  34. package/dist/controllers/character_counter_controller.d.ts +18 -2
  35. package/dist/controllers/character_counter_controller.js +271 -143
  36. package/dist/controllers/character_counter_controller.js.map +1 -1
  37. package/dist/controllers/checkbox_controller.d.ts +7 -2
  38. package/dist/controllers/checkbox_controller.js +77 -75
  39. package/dist/controllers/checkbox_controller.js.map +1 -1
  40. package/dist/controllers/clipboard_controller.d.ts +44 -7
  41. package/dist/controllers/clipboard_controller.js +187 -108
  42. package/dist/controllers/clipboard_controller.js.map +1 -1
  43. package/dist/controllers/collapsible_controller.d.ts +41 -5
  44. package/dist/controllers/collapsible_controller.js +374 -108
  45. package/dist/controllers/collapsible_controller.js.map +1 -1
  46. package/dist/controllers/color_picker_controller.d.ts +47 -14
  47. package/dist/controllers/color_picker_controller.js +183 -127
  48. package/dist/controllers/color_picker_controller.js.map +1 -1
  49. package/dist/controllers/combobox_controller.d.ts +33 -6
  50. package/dist/controllers/combobox_controller.js +426 -109
  51. package/dist/controllers/combobox_controller.js.map +1 -1
  52. package/dist/controllers/command_palette_controller.d.ts +45 -14
  53. package/dist/controllers/command_palette_controller.js +994 -350
  54. package/dist/controllers/command_palette_controller.js.map +1 -1
  55. package/dist/controllers/conditional_fields_controller.d.ts +15 -2
  56. package/dist/controllers/conditional_fields_controller.js +212 -125
  57. package/dist/controllers/conditional_fields_controller.js.map +1 -1
  58. package/dist/controllers/confirm_controller.d.ts +49 -12
  59. package/dist/controllers/confirm_controller.js +939 -245
  60. package/dist/controllers/confirm_controller.js.map +1 -1
  61. package/dist/controllers/context_menu_controller.d.ts +36 -3
  62. package/dist/controllers/context_menu_controller.js +275 -127
  63. package/dist/controllers/context_menu_controller.js.map +1 -1
  64. package/dist/controllers/count_up_controller.d.ts +13 -1
  65. package/dist/controllers/count_up_controller.js +73 -21
  66. package/dist/controllers/count_up_controller.js.map +1 -1
  67. package/dist/controllers/countdown_controller.d.ts +75 -2
  68. package/dist/controllers/countdown_controller.js +235 -97
  69. package/dist/controllers/countdown_controller.js.map +1 -1
  70. package/dist/controllers/currency_input_controller.d.ts +93 -15
  71. package/dist/controllers/currency_input_controller.js +241 -118
  72. package/dist/controllers/currency_input_controller.js.map +1 -1
  73. package/dist/controllers/data_grid_controller.d.ts +78 -22
  74. package/dist/controllers/data_grid_controller.js +229 -135
  75. package/dist/controllers/data_grid_controller.js.map +1 -1
  76. package/dist/controllers/date_range_picker_controller.d.ts +64 -11
  77. package/dist/controllers/date_range_picker_controller.js +359 -165
  78. package/dist/controllers/date_range_picker_controller.js.map +1 -1
  79. package/dist/controllers/dialog_controller.d.ts +22 -3
  80. package/dist/controllers/dialog_controller.js +887 -225
  81. package/dist/controllers/dialog_controller.js.map +1 -1
  82. package/dist/controllers/direct_upload_controller.d.ts +24 -7
  83. package/dist/controllers/direct_upload_controller.js +80 -142
  84. package/dist/controllers/direct_upload_controller.js.map +1 -1
  85. package/dist/controllers/dirty_form_controller.d.ts +2 -2
  86. package/dist/controllers/dirty_form_controller.js +26 -47
  87. package/dist/controllers/dirty_form_controller.js.map +1 -1
  88. package/dist/controllers/dismissible_controller.d.ts +19 -0
  89. package/dist/controllers/dismissible_controller.js +175 -20
  90. package/dist/controllers/dismissible_controller.js.map +1 -1
  91. package/dist/controllers/drawer_controller.d.ts +52 -16
  92. package/dist/controllers/drawer_controller.js +963 -336
  93. package/dist/controllers/drawer_controller.js.map +1 -1
  94. package/dist/controllers/dropdown_controller.d.ts +39 -3
  95. package/dist/controllers/dropdown_controller.js +342 -85
  96. package/dist/controllers/dropdown_controller.js.map +1 -1
  97. package/dist/controllers/editable_controller.d.ts +12 -1
  98. package/dist/controllers/editable_controller.js +182 -58
  99. package/dist/controllers/editable_controller.js.map +1 -1
  100. package/dist/controllers/empty_state_controller.js +67 -53
  101. package/dist/controllers/empty_state_controller.js.map +1 -1
  102. package/dist/controllers/file_dropzone_controller.d.ts +60 -18
  103. package/dist/controllers/file_dropzone_controller.js +426 -229
  104. package/dist/controllers/file_dropzone_controller.js.map +1 -1
  105. package/dist/controllers/filter_controller.d.ts +10 -4
  106. package/dist/controllers/filter_controller.js +78 -36
  107. package/dist/controllers/filter_controller.js.map +1 -1
  108. package/dist/controllers/flash_controller.d.ts +57 -14
  109. package/dist/controllers/flash_controller.js +328 -211
  110. package/dist/controllers/flash_controller.js.map +1 -1
  111. package/dist/controllers/focus_controller.d.ts +12 -3
  112. package/dist/controllers/focus_controller.js +654 -239
  113. package/dist/controllers/focus_controller.js.map +1 -1
  114. package/dist/controllers/form_field_controller.js +153 -136
  115. package/dist/controllers/form_field_controller.js.map +1 -1
  116. package/dist/controllers/form_validation_controller.js +29 -96
  117. package/dist/controllers/form_validation_controller.js.map +1 -1
  118. package/dist/controllers/frame_loading_controller.d.ts +41 -9
  119. package/dist/controllers/frame_loading_controller.js +263 -198
  120. package/dist/controllers/frame_loading_controller.js.map +1 -1
  121. package/dist/controllers/highlight_controller.d.ts +22 -2
  122. package/dist/controllers/highlight_controller.js +119 -70
  123. package/dist/controllers/highlight_controller.js.map +1 -1
  124. package/dist/controllers/hover_card_controller.d.ts +65 -4
  125. package/dist/controllers/hover_card_controller.js +335 -118
  126. package/dist/controllers/hover_card_controller.js.map +1 -1
  127. package/dist/controllers/idle_controller.d.ts +58 -4
  128. package/dist/controllers/idle_controller.js +316 -65
  129. package/dist/controllers/idle_controller.js.map +1 -1
  130. package/dist/controllers/input_mask_controller.d.ts +5 -2
  131. package/dist/controllers/input_mask_controller.js +116 -61
  132. package/dist/controllers/input_mask_controller.js.map +1 -1
  133. package/dist/controllers/intersection_controller.d.ts +32 -13
  134. package/dist/controllers/intersection_controller.js +147 -103
  135. package/dist/controllers/intersection_controller.js.map +1 -1
  136. package/dist/controllers/lazy_frame_controller.d.ts +16 -0
  137. package/dist/controllers/lazy_frame_controller.js +9 -56
  138. package/dist/controllers/lazy_frame_controller.js.map +1 -1
  139. package/dist/controllers/listbox_controller.d.ts +84 -10
  140. package/dist/controllers/listbox_controller.js +413 -153
  141. package/dist/controllers/listbox_controller.js.map +1 -1
  142. package/dist/controllers/local_time_controller.d.ts +1 -1
  143. package/dist/controllers/local_time_controller.js +54 -64
  144. package/dist/controllers/local_time_controller.js.map +1 -1
  145. package/dist/controllers/masonry_controller.d.ts +11 -3
  146. package/dist/controllers/masonry_controller.js +70 -95
  147. package/dist/controllers/masonry_controller.js.map +1 -1
  148. package/dist/controllers/menu_controller.d.ts +40 -9
  149. package/dist/controllers/menu_controller.js +254 -167
  150. package/dist/controllers/menu_controller.js.map +1 -1
  151. package/dist/controllers/menubar_controller.d.ts +15 -4
  152. package/dist/controllers/menubar_controller.js +206 -331
  153. package/dist/controllers/menubar_controller.js.map +1 -1
  154. package/dist/controllers/meter_controller.d.ts +20 -0
  155. package/dist/controllers/meter_controller.js +159 -71
  156. package/dist/controllers/meter_controller.js.map +1 -1
  157. package/dist/controllers/multi_select_controller.d.ts +50 -7
  158. package/dist/controllers/multi_select_controller.js +797 -388
  159. package/dist/controllers/multi_select_controller.js.map +1 -1
  160. package/dist/controllers/navigation_menu_controller.d.ts +19 -1
  161. package/dist/controllers/navigation_menu_controller.js +156 -231
  162. package/dist/controllers/navigation_menu_controller.js.map +1 -1
  163. package/dist/controllers/nested_form_controller.d.ts +10 -0
  164. package/dist/controllers/nested_form_controller.js +373 -174
  165. package/dist/controllers/nested_form_controller.js.map +1 -1
  166. package/dist/controllers/network_status_controller.d.ts +23 -0
  167. package/dist/controllers/network_status_controller.js +241 -45
  168. package/dist/controllers/network_status_controller.js.map +1 -1
  169. package/dist/controllers/number_input_controller.d.ts +62 -10
  170. package/dist/controllers/number_input_controller.js +387 -227
  171. package/dist/controllers/number_input_controller.js.map +1 -1
  172. package/dist/controllers/optimistic_controller.js +28 -75
  173. package/dist/controllers/optimistic_controller.js.map +1 -1
  174. package/dist/controllers/otp_controller.d.ts +38 -7
  175. package/dist/controllers/otp_controller.js +344 -196
  176. package/dist/controllers/otp_controller.js.map +1 -1
  177. package/dist/controllers/overflow_indicator_controller.d.ts +13 -12
  178. package/dist/controllers/overflow_indicator_controller.js +167 -93
  179. package/dist/controllers/overflow_indicator_controller.js.map +1 -1
  180. package/dist/controllers/overflow_menu_controller.d.ts +66 -9
  181. package/dist/controllers/overflow_menu_controller.js +408 -277
  182. package/dist/controllers/overflow_menu_controller.js.map +1 -1
  183. package/dist/controllers/pagination_controller.d.ts +52 -15
  184. package/dist/controllers/pagination_controller.js +342 -120
  185. package/dist/controllers/pagination_controller.js.map +1 -1
  186. package/dist/controllers/password_reveal_controller.d.ts +60 -9
  187. package/dist/controllers/password_reveal_controller.js +424 -99
  188. package/dist/controllers/password_reveal_controller.js.map +1 -1
  189. package/dist/controllers/password_strength_controller.d.ts +14 -5
  190. package/dist/controllers/password_strength_controller.js +99 -153
  191. package/dist/controllers/password_strength_controller.js.map +1 -1
  192. package/dist/controllers/persist_controller.d.ts +11 -4
  193. package/dist/controllers/persist_controller.js +252 -96
  194. package/dist/controllers/persist_controller.js.map +1 -1
  195. package/dist/controllers/pointer_drag_controller.d.ts +19 -7
  196. package/dist/controllers/pointer_drag_controller.js +263 -164
  197. package/dist/controllers/pointer_drag_controller.js.map +1 -1
  198. package/dist/controllers/popover_controller.d.ts +46 -4
  199. package/dist/controllers/popover_controller.js +267 -94
  200. package/dist/controllers/popover_controller.js.map +1 -1
  201. package/dist/controllers/portal_controller.d.ts +44 -4
  202. package/dist/controllers/portal_controller.js +209 -72
  203. package/dist/controllers/portal_controller.js.map +1 -1
  204. package/dist/controllers/preview_guard_controller.d.ts +10 -4
  205. package/dist/controllers/preview_guard_controller.js +243 -112
  206. package/dist/controllers/preview_guard_controller.js.map +1 -1
  207. package/dist/controllers/progress_controller.d.ts +11 -0
  208. package/dist/controllers/progress_controller.js +132 -59
  209. package/dist/controllers/progress_controller.js.map +1 -1
  210. package/dist/controllers/radio_group_controller.d.ts +19 -8
  211. package/dist/controllers/radio_group_controller.js +138 -133
  212. package/dist/controllers/radio_group_controller.js.map +1 -1
  213. package/dist/controllers/range_slider_controller.d.ts +92 -4
  214. package/dist/controllers/range_slider_controller.js +245 -112
  215. package/dist/controllers/range_slider_controller.js.map +1 -1
  216. package/dist/controllers/rating_controller.d.ts +33 -6
  217. package/dist/controllers/rating_controller.js +429 -194
  218. package/dist/controllers/rating_controller.js.map +1 -1
  219. package/dist/controllers/read_more_controller.d.ts +26 -2
  220. package/dist/controllers/read_more_controller.js +185 -47
  221. package/dist/controllers/read_more_controller.js.map +1 -1
  222. package/dist/controllers/reading_progress_controller.d.ts +1 -1
  223. package/dist/controllers/reading_progress_controller.js +147 -135
  224. package/dist/controllers/reading_progress_controller.js.map +1 -1
  225. package/dist/controllers/relative_time_controller.d.ts +18 -4
  226. package/dist/controllers/relative_time_controller.js +145 -110
  227. package/dist/controllers/relative_time_controller.js.map +1 -1
  228. package/dist/controllers/reset_on_restore_controller.d.ts +85 -0
  229. package/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +97 -38
  230. package/dist/controllers/reset_on_restore_controller.js.map +1 -0
  231. package/dist/controllers/resizable_controller.d.ts +44 -4
  232. package/dist/controllers/resizable_controller.js +294 -118
  233. package/dist/controllers/resizable_controller.js.map +1 -1
  234. package/dist/controllers/roving_controller.js +39 -84
  235. package/dist/controllers/roving_controller.js.map +1 -1
  236. package/dist/controllers/scroll_area_controller.d.ts +7 -3
  237. package/dist/controllers/scroll_area_controller.js +428 -221
  238. package/dist/controllers/scroll_area_controller.js.map +1 -1
  239. package/dist/controllers/scroll_restore_controller.js +0 -53
  240. package/dist/controllers/scroll_restore_controller.js.map +1 -1
  241. package/dist/controllers/scroll_visibility_controller.d.ts +28 -12
  242. package/dist/controllers/scroll_visibility_controller.js +395 -146
  243. package/dist/controllers/scroll_visibility_controller.js.map +1 -1
  244. package/dist/controllers/scrollspy_controller.d.ts +9 -3
  245. package/dist/controllers/scrollspy_controller.js +181 -236
  246. package/dist/controllers/scrollspy_controller.js.map +1 -1
  247. package/dist/controllers/separator_controller.d.ts +59 -17
  248. package/dist/controllers/separator_controller.js +288 -132
  249. package/dist/controllers/separator_controller.js.map +1 -1
  250. package/dist/controllers/sidebar_controller.d.ts +81 -7
  251. package/dist/controllers/sidebar_controller.js +1071 -375
  252. package/dist/controllers/sidebar_controller.js.map +1 -1
  253. package/dist/controllers/skeleton_controller.d.ts +23 -0
  254. package/dist/controllers/skeleton_controller.js +248 -92
  255. package/dist/controllers/skeleton_controller.js.map +1 -1
  256. package/dist/controllers/slider_controller.d.ts +72 -8
  257. package/dist/controllers/slider_controller.js +211 -96
  258. package/dist/controllers/slider_controller.js.map +1 -1
  259. package/dist/controllers/smart_sticky_header_controller.d.ts +21 -3
  260. package/dist/controllers/smart_sticky_header_controller.js +163 -54
  261. package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
  262. package/dist/controllers/sortable_controller.js +13 -110
  263. package/dist/controllers/sortable_controller.js.map +1 -1
  264. package/dist/controllers/spinner_controller.d.ts +39 -4
  265. package/dist/controllers/spinner_controller.js +277 -161
  266. package/dist/controllers/spinner_controller.js.map +1 -1
  267. package/dist/controllers/step_indicator_controller.d.ts +24 -17
  268. package/dist/controllers/step_indicator_controller.js +107 -53
  269. package/dist/controllers/step_indicator_controller.js.map +1 -1
  270. package/dist/controllers/stepper_controller.d.ts +43 -9
  271. package/dist/controllers/stepper_controller.js +171 -48
  272. package/dist/controllers/stepper_controller.js.map +1 -1
  273. package/dist/controllers/stick_to_bottom_controller.d.ts +25 -0
  274. package/dist/controllers/stick_to_bottom_controller.js +310 -116
  275. package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
  276. package/dist/controllers/sticky_observer_controller.d.ts +11 -0
  277. package/dist/controllers/sticky_observer_controller.js +214 -49
  278. package/dist/controllers/sticky_observer_controller.js.map +1 -1
  279. package/dist/controllers/submit_once_controller.d.ts +22 -6
  280. package/dist/controllers/submit_once_controller.js +294 -192
  281. package/dist/controllers/submit_once_controller.js.map +1 -1
  282. package/dist/controllers/switch_controller.d.ts +40 -7
  283. package/dist/controllers/switch_controller.js +107 -43
  284. package/dist/controllers/switch_controller.js.map +1 -1
  285. package/dist/controllers/tabs_controller.d.ts +13 -1
  286. package/dist/controllers/tabs_controller.js +50 -25
  287. package/dist/controllers/tabs_controller.js.map +1 -1
  288. package/dist/controllers/tags_input_controller.d.ts +27 -3
  289. package/dist/controllers/tags_input_controller.js +290 -149
  290. package/dist/controllers/tags_input_controller.js.map +1 -1
  291. package/dist/controllers/textarea_autosize_controller.d.ts +10 -0
  292. package/dist/controllers/textarea_autosize_controller.js +71 -58
  293. package/dist/controllers/textarea_autosize_controller.js.map +1 -1
  294. package/dist/controllers/theme_controller.d.ts +37 -8
  295. package/dist/controllers/theme_controller.js +148 -80
  296. package/dist/controllers/theme_controller.js.map +1 -1
  297. package/dist/controllers/time_picker_controller.d.ts +18 -1
  298. package/dist/controllers/time_picker_controller.js +162 -81
  299. package/dist/controllers/time_picker_controller.js.map +1 -1
  300. package/dist/controllers/toast_controller.d.ts +85 -23
  301. package/dist/controllers/toast_controller.js +365 -193
  302. package/dist/controllers/toast_controller.js.map +1 -1
  303. package/dist/controllers/toggle_group_controller.d.ts +60 -9
  304. package/dist/controllers/toggle_group_controller.js +194 -99
  305. package/dist/controllers/toggle_group_controller.js.map +1 -1
  306. package/dist/controllers/toolbar_controller.js +92 -90
  307. package/dist/controllers/toolbar_controller.js.map +1 -1
  308. package/dist/controllers/tooltip_controller.d.ts +52 -2
  309. package/dist/controllers/tooltip_controller.js +185 -125
  310. package/dist/controllers/tooltip_controller.js.map +1 -1
  311. package/dist/controllers/transition_controller.d.ts +22 -4
  312. package/dist/controllers/transition_controller.js +123 -115
  313. package/dist/controllers/transition_controller.js.map +1 -1
  314. package/dist/controllers/tree_view_controller.d.ts +59 -18
  315. package/dist/controllers/tree_view_controller.js +248 -290
  316. package/dist/controllers/tree_view_controller.js.map +1 -1
  317. package/dist/index.d.ts +33 -3
  318. package/dist/index.js +8649 -8572
  319. package/dist/index.js.map +1 -1
  320. package/dist/inspector/cli.d.ts +95 -9
  321. package/dist/inspector/cli.js +343 -77
  322. package/dist/inspector/cli.js.map +1 -1
  323. package/dist/inspector/cli_bin.js +390 -96
  324. package/dist/inspector/cli_bin.js.map +1 -1
  325. package/dist/inspector/examples.json +45 -45
  326. package/dist/inspector/manifest.json +1308 -155
  327. package/dist/positioning/index.d.ts +8 -0
  328. package/dist/positioning/index.js +148 -45
  329. package/dist/positioning/index.js.map +1 -1
  330. package/package.json +7 -7
  331. package/dist/controllers/reset_before_cache_controller.d.ts +0 -76
  332. package/dist/controllers/reset_before_cache_controller.js.map +0 -1
@@ -53,6 +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.
62
+ * - Each move of the open state is reported: `stimeo--context-menu:open` and
63
+ * `stimeo--context-menu:close` dispatch `{ reason: StateReason }`, after the
64
+ * state attributes are written. Both are informational, so neither is
65
+ * cancelable. A call that leaves the state where it already was, the
66
+ * normalization in {@link connect}, a region or a menu that takes over, and
67
+ * {@link disconnect} are all silent.
56
68
  *
57
69
  * Roving focus skips `hidden` and natively `disabled` items. An
58
70
  * `aria-disabled="true"` item stays reachable by arrow keys — APG marks that
@@ -63,8 +75,11 @@ declare class ContextMenuController extends Controller<HTMLElement> {
63
75
  #private;
64
76
  static targets: string[];
65
77
  static actions: readonly ["activate", "onItemKeydown", "onRegionKeydown", "open"];
78
+ static events: readonly ["close", "open"];
66
79
  readonly regionTarget: HTMLElement;
80
+ readonly regionTargets: HTMLElement[];
67
81
  readonly menuTarget: HTMLElement;
82
+ readonly menuTargets: HTMLElement[];
68
83
  readonly itemTargets: HTMLButtonElement[];
69
84
  readonly hasRegionTarget: boolean;
70
85
  readonly hasMenuTarget: boolean;
@@ -72,11 +87,29 @@ declare class ContextMenuController extends Controller<HTMLElement> {
72
87
  connect(): void;
73
88
  /** Releases the listeners, stack membership, and pending Tab-close task. */
74
89
  disconnect(): void;
90
+ /** Brings a region that arrives after connect to the open state. */
91
+ regionTargetConnected(): void;
75
92
  /**
76
- * Opens the menu from a `contextmenu` event: suppresses the native menu and
77
- * 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.
78
96
  */
79
- 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;
80
113
  /** Keyboard entry on the region: `Shift+F10` / `ContextMenu` open at center. */
81
114
  onRegionKeydown(event: KeyboardEvent): void;
82
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,46 +277,68 @@ var SafeTimeout = class extends TimerRegistry {
151
277
  }
152
278
  };
153
279
 
154
- // src/controllers/context_menu_controller.ts
155
- var ContextMenuController = class extends Controller {
280
+ var ContextMenuController = class _ContextMenuController extends Controller {
281
+ static #COORDINATE = ["--stimeo--context-menu-x", "--stimeo--context-menu-y"];
156
282
  static targets = ["region", "menu", "item"];
157
283
  static actions = ["activate", "onItemKeydown", "onRegionKeydown", "open"];
284
+ static events = ["close", "open"];
158
285
  #timers = new SafeTimeout();
159
- /** Escape-stack membership while open; the shared resolver dismisses via it. */
160
286
  #escapeLayer = new EscapeLayer();
161
- /** Starts closed and registers delegated activation and outside-pointer listeners. */
287
+ #menuHidden = new AttributeLease("hidden", this.identifier);
288
+ #regionState = new AttributeLease("data-state", this.identifier);
289
+ #menu = null;
290
+ #connected = false;
291
+ #reporting = false;
162
292
  connect() {
163
- this.#closeMenu();
293
+ this.#closeMenu("api");
164
294
  this.element.addEventListener("click", this.#onItemClickCapture, true);
165
295
  document.addEventListener("click", this.#onOutsidePointer, true);
166
296
  document.addEventListener("contextmenu", this.#onOutsidePointer, true);
297
+ this.#connected = true;
298
+ this.#reporting = true;
167
299
  }
168
- /** Releases the listeners, stack membership, and pending Tab-close task. */
169
300
  disconnect() {
301
+ this.#connected = false;
302
+ this.#reporting = false;
170
303
  this.#timers.clearAll();
171
304
  this.#escapeLayer.deactivate();
172
305
  this.element.removeEventListener("click", this.#onItemClickCapture, true);
173
306
  document.removeEventListener("click", this.#onOutsidePointer, true);
174
307
  document.removeEventListener("contextmenu", this.#onOutsidePointer, true);
175
308
  }
176
- /**
177
- * Opens the menu from a `contextmenu` event: suppresses the native menu and
178
- * places this one at the pointer coordinate.
179
- */
180
- open(event) {
181
- event.preventDefault();
182
- this.#openAt(event.clientX, event.clientY);
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);
183
333
  }
184
- /** Keyboard entry on the region: `Shift+F10` / `ContextMenu` open at center. */
185
334
  onRegionKeydown(event) {
186
335
  if (event.defaultPrevented) return;
187
336
  const isContextKey = event.key === "ContextMenu" || event.key === "F10" && event.shiftKey;
188
337
  if (!isContextKey) return;
189
338
  event.preventDefault();
190
339
  const rect = this.hasRegionTarget ? this.regionTarget.getBoundingClientRect() : { left: 0, top: 0, width: 0, height: 0 };
191
- this.#openAt(rect.left + rect.width / 2, rect.top + rect.height / 2);
340
+ this.#openAt(rect.left + rect.width / 2, rect.top + rect.height / 2, stateReasonFor(event));
192
341
  }
193
- /** Roving focus and closing keys inside the menu. */
194
342
  onItemKeydown(event) {
195
343
  if (event.defaultPrevented) return;
196
344
  if (isReservedArrowChord(event)) return;
@@ -221,55 +369,71 @@ var ContextMenuController = class extends Controller {
221
369
  break;
222
370
  case "Tab":
223
371
  this.#timers.clearAll();
224
- this.#timers.set(() => this.#closeMenu(), 0);
372
+ this.#timers.set(() => this.#closeMenu("focus"), 0);
225
373
  break;
226
374
  }
227
375
  }
228
- /** Closes after an item is activated and restores focus to the region. */
229
376
  activate() {
230
- this.#closeAndRestore();
377
+ this.#closeAndRestore("select");
231
378
  }
232
- /** Opens the menu at viewport coordinates `(x, y)` and focuses the first item. */
233
- #openAt(x, y) {
379
+ #openAt(x, y, reason) {
234
380
  if (!this.hasMenuTarget) return;
235
381
  this.#timers.clearAll();
382
+ const menu = this.menuTarget;
383
+ const was = this.#isOpen;
236
384
  this.#escapeLayer.activate(document, {
237
- onDismiss: () => this.#closeAndRestore(),
385
+ onDismiss: () => this.#closeAndRestore("escape"),
238
386
  claims: claimsWhileFocusWithin(this.element)
239
387
  });
240
- this.menuTarget.style.setProperty("--stimeo--context-menu-x", `${x}px`);
241
- this.menuTarget.style.setProperty("--stimeo--context-menu-y", `${y}px`);
242
- this.menuTarget.hidden = false;
243
- 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");
393
+ if (!was && this.#reporting) this.dispatch("open", { detail: { reason }, cancelable: false });
394
+ if (!this.#isOpen) return;
244
395
  this.#navigableItems[0]?.focus();
245
396
  }
246
- /** Hides the menu and reflects the collapsed state on the region. */
247
- #closeMenu() {
397
+ #closeMenu(reason) {
248
398
  this.#timers.clearAll();
249
399
  this.#escapeLayer.deactivate();
250
- if (!this.hasMenuTarget) return;
251
- this.menuTarget.hidden = true;
252
- if (this.hasRegionTarget) this.regionTarget.setAttribute("data-state", "closed");
400
+ const menu = this.hasMenuTarget ? this.menuTarget : null;
401
+ this.#menu = menu;
402
+ if (!menu) return;
403
+ const was = this.#isOpen;
404
+ this.#menuHidden.write(menu, "");
405
+ if (this.hasRegionTarget) this.#regionState.write(this.regionTarget, "closed");
406
+ if (was && this.#reporting) this.dispatch("close", { detail: { reason }, cancelable: false });
407
+ }
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");
253
429
  }
254
- /** Closes the menu and returns focus to the region (Escape / activation). */
255
- #closeAndRestore() {
256
- this.#closeMenu();
430
+ #closeAndRestore(reason) {
431
+ this.#closeMenu(reason);
257
432
  if (this.hasRegionTarget) this.regionTarget.focus();
258
433
  }
259
- /**
260
- * Closes when a click or context-menu invocation lands outside this instance.
261
- * Both subscriptions observe in the capture phase, so the target is judged
262
- * against the tree the user actually pressed even when a consumer handler
263
- * detaches it, and application code that stops bubbling cannot leave the menu
264
- * open.
265
- */
266
434
  #onOutsidePointer = (event) => {
267
- if (this.#isOpen && !this.element.contains(event.target)) this.#closeMenu();
435
+ if (this.#isOpen && !this.element.contains(event.target)) this.#closeMenu("outside");
268
436
  };
269
- /**
270
- * Captures clicks so `aria-disabled` commands cannot reach consumer handlers.
271
- * Native Enter/Space activation also synthesizes a click and is blocked here.
272
- */
273
437
  #onItemClickCapture = (event) => {
274
438
  const target = event.target;
275
439
  if (!(target instanceof Node)) return;
@@ -280,33 +444,17 @@ var ContextMenuController = class extends Controller {
280
444
  event.preventDefault();
281
445
  event.stopImmediatePropagation();
282
446
  };
283
- /** Menu items eligible for roving focus (excludes hidden / natively disabled). */
284
447
  get #navigableItems() {
285
448
  return this.itemTargets.filter((item) => this.#isNavigable(item));
286
449
  }
287
- /**
288
- * An item can take roving focus unless it is `hidden` or a natively `disabled`
289
- * form control. CSS-only visibility is not detectable here and is the
290
- * consumer's responsibility.
291
- *
292
- * **`aria-disabled="true"` stays reachable.** APG separates the two attributes
293
- * by intent: `disabled` is for controls whose existence can be inferred from a
294
- * neighbour (a greyed Next next to a Prev), while `aria-disabled` marks a
295
- * control that must stay *discoverable* — and it names menu items as the
296
- * example. Skipping it would hide the command's existence from a keyboard user
297
- * entirely. Activation is suppressed separately, so the item announces itself
298
- * and does nothing.
299
- */
300
450
  #isNavigable(item) {
301
451
  if (item.hasAttribute("hidden")) return false;
302
452
  return !item.disabled;
303
453
  }
304
- /** Whether the menu is currently visible. */
305
454
  get #isOpen() {
306
455
  return this.hasMenuTarget && !this.menuTarget.hidden;
307
456
  }
308
457
  };
309
458
 
310
459
  export { ContextMenuController };
311
- //# sourceMappingURL=context_menu_controller.js.map
312
460
  //# sourceMappingURL=context_menu_controller.js.map