stimeo-ui 0.17.0 → 0.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (299) hide show
  1. package/CHANGELOG.md +136 -2
  2. package/README.md +19 -10
  3. package/dist/cable/index.d.ts +11 -9
  4. package/dist/cable/index.js +22 -14
  5. package/dist/cable/index.js.map +1 -1
  6. package/dist/coerce-JtZVNG5h.d.ts +20 -0
  7. package/dist/controllers/accordion_controller.js +14 -6
  8. package/dist/controllers/accordion_controller.js.map +1 -1
  9. package/dist/controllers/alert_dialog_controller.js +145 -118
  10. package/dist/controllers/alert_dialog_controller.js.map +1 -1
  11. package/dist/controllers/announcer_controller.d.ts +5 -3
  12. package/dist/controllers/announcer_controller.js +113 -17
  13. package/dist/controllers/announcer_controller.js.map +1 -1
  14. package/dist/controllers/auto_submit_controller.js +185 -60
  15. package/dist/controllers/auto_submit_controller.js.map +1 -1
  16. package/dist/controllers/avatar_controller.js +55 -43
  17. package/dist/controllers/avatar_controller.js.map +1 -1
  18. package/dist/controllers/breadcrumb_controller.js.map +1 -1
  19. package/dist/controllers/bulk_select_controller.d.ts +3 -3
  20. package/dist/controllers/bulk_select_controller.js +64 -50
  21. package/dist/controllers/bulk_select_controller.js.map +1 -1
  22. package/dist/controllers/calendar_controller.d.ts +15 -12
  23. package/dist/controllers/calendar_controller.js +55 -53
  24. package/dist/controllers/calendar_controller.js.map +1 -1
  25. package/dist/controllers/carousel_controller.d.ts +9 -5
  26. package/dist/controllers/carousel_controller.js +124 -86
  27. package/dist/controllers/carousel_controller.js.map +1 -1
  28. package/dist/controllers/character_counter_controller.d.ts +27 -6
  29. package/dist/controllers/character_counter_controller.js +291 -95
  30. package/dist/controllers/character_counter_controller.js.map +1 -1
  31. package/dist/controllers/checkbox_controller.js +6 -0
  32. package/dist/controllers/checkbox_controller.js.map +1 -1
  33. package/dist/controllers/clipboard_controller.js +118 -27
  34. package/dist/controllers/clipboard_controller.js.map +1 -1
  35. package/dist/controllers/collapsible_controller.d.ts +1 -1
  36. package/dist/controllers/collapsible_controller.js +52 -49
  37. package/dist/controllers/collapsible_controller.js.map +1 -1
  38. package/dist/controllers/color_picker_controller.js +29 -7
  39. package/dist/controllers/color_picker_controller.js.map +1 -1
  40. package/dist/controllers/combobox_controller.d.ts +1 -1
  41. package/dist/controllers/combobox_controller.js +210 -71
  42. package/dist/controllers/combobox_controller.js.map +1 -1
  43. package/dist/controllers/command_palette_controller.d.ts +9 -9
  44. package/dist/controllers/command_palette_controller.js +290 -140
  45. package/dist/controllers/command_palette_controller.js.map +1 -1
  46. package/dist/controllers/conditional_fields_controller.js +145 -58
  47. package/dist/controllers/conditional_fields_controller.js.map +1 -1
  48. package/dist/controllers/confirm_controller.js +145 -118
  49. package/dist/controllers/confirm_controller.js.map +1 -1
  50. package/dist/controllers/context_menu_controller.d.ts +49 -14
  51. package/dist/controllers/context_menu_controller.js +138 -73
  52. package/dist/controllers/context_menu_controller.js.map +1 -1
  53. package/dist/controllers/count_up_controller.js.map +1 -1
  54. package/dist/controllers/countdown_controller.d.ts +2 -2
  55. package/dist/controllers/countdown_controller.js +109 -21
  56. package/dist/controllers/countdown_controller.js.map +1 -1
  57. package/dist/controllers/currency_input_controller.d.ts +75 -11
  58. package/dist/controllers/currency_input_controller.js +446 -72
  59. package/dist/controllers/currency_input_controller.js.map +1 -1
  60. package/dist/controllers/data_grid_controller.d.ts +10 -8
  61. package/dist/controllers/data_grid_controller.js +134 -28
  62. package/dist/controllers/data_grid_controller.js.map +1 -1
  63. package/dist/controllers/date_range_picker_controller.d.ts +12 -9
  64. package/dist/controllers/date_range_picker_controller.js +93 -72
  65. package/dist/controllers/date_range_picker_controller.js.map +1 -1
  66. package/dist/controllers/dialog_controller.d.ts +4 -5
  67. package/dist/controllers/dialog_controller.js +145 -118
  68. package/dist/controllers/dialog_controller.js.map +1 -1
  69. package/dist/controllers/direct_upload_controller.d.ts +1 -1
  70. package/dist/controllers/direct_upload_controller.js +24 -14
  71. package/dist/controllers/direct_upload_controller.js.map +1 -1
  72. package/dist/controllers/dirty_form_controller.d.ts +6 -1
  73. package/dist/controllers/dirty_form_controller.js +68 -31
  74. package/dist/controllers/dirty_form_controller.js.map +1 -1
  75. package/dist/controllers/dismissible_controller.js +66 -46
  76. package/dist/controllers/dismissible_controller.js.map +1 -1
  77. package/dist/controllers/drawer_controller.js +145 -118
  78. package/dist/controllers/drawer_controller.js.map +1 -1
  79. package/dist/controllers/dropdown_controller.js +76 -63
  80. package/dist/controllers/dropdown_controller.js.map +1 -1
  81. package/dist/controllers/editable_controller.js +200 -62
  82. package/dist/controllers/editable_controller.js.map +1 -1
  83. package/dist/controllers/empty_state_controller.js +13 -6
  84. package/dist/controllers/empty_state_controller.js.map +1 -1
  85. package/dist/controllers/file_dropzone_controller.d.ts +3 -0
  86. package/dist/controllers/file_dropzone_controller.js +55 -48
  87. package/dist/controllers/file_dropzone_controller.js.map +1 -1
  88. package/dist/controllers/filter_controller.js +9 -2
  89. package/dist/controllers/filter_controller.js.map +1 -1
  90. package/dist/controllers/flash_controller.d.ts +4 -4
  91. package/dist/controllers/flash_controller.js +66 -33
  92. package/dist/controllers/flash_controller.js.map +1 -1
  93. package/dist/controllers/focus_controller.d.ts +3 -3
  94. package/dist/controllers/focus_controller.js +39 -14
  95. package/dist/controllers/focus_controller.js.map +1 -1
  96. package/dist/controllers/form_field_controller.d.ts +2 -1
  97. package/dist/controllers/form_field_controller.js +52 -43
  98. package/dist/controllers/form_field_controller.js.map +1 -1
  99. package/dist/controllers/form_validation_controller.d.ts +3 -3
  100. package/dist/controllers/form_validation_controller.js +18 -11
  101. package/dist/controllers/form_validation_controller.js.map +1 -1
  102. package/dist/controllers/frame_loading_controller.d.ts +13 -12
  103. package/dist/controllers/frame_loading_controller.js +55 -48
  104. package/dist/controllers/frame_loading_controller.js.map +1 -1
  105. package/dist/controllers/highlight_controller.d.ts +5 -4
  106. package/dist/controllers/highlight_controller.js.map +1 -1
  107. package/dist/controllers/hover_card_controller.d.ts +4 -1
  108. package/dist/controllers/hover_card_controller.js +67 -61
  109. package/dist/controllers/hover_card_controller.js.map +1 -1
  110. package/dist/controllers/idle_controller.d.ts +6 -5
  111. package/dist/controllers/idle_controller.js +10 -6
  112. package/dist/controllers/idle_controller.js.map +1 -1
  113. package/dist/controllers/input_mask_controller.d.ts +31 -13
  114. package/dist/controllers/input_mask_controller.js +434 -24
  115. package/dist/controllers/input_mask_controller.js.map +1 -1
  116. package/dist/controllers/intersection_controller.js +3 -0
  117. package/dist/controllers/intersection_controller.js.map +1 -1
  118. package/dist/controllers/lazy_frame_controller.d.ts +3 -2
  119. package/dist/controllers/lazy_frame_controller.js +15 -3
  120. package/dist/controllers/lazy_frame_controller.js.map +1 -1
  121. package/dist/controllers/listbox_controller.d.ts +10 -7
  122. package/dist/controllers/listbox_controller.js +260 -79
  123. package/dist/controllers/listbox_controller.js.map +1 -1
  124. package/dist/controllers/local_time_controller.d.ts +8 -6
  125. package/dist/controllers/local_time_controller.js +48 -13
  126. package/dist/controllers/local_time_controller.js.map +1 -1
  127. package/dist/controllers/masonry_controller.js +11 -4
  128. package/dist/controllers/masonry_controller.js.map +1 -1
  129. package/dist/controllers/menu_controller.d.ts +24 -5
  130. package/dist/controllers/menu_controller.js +138 -82
  131. package/dist/controllers/menu_controller.js.map +1 -1
  132. package/dist/controllers/menubar_controller.d.ts +49 -7
  133. package/dist/controllers/menubar_controller.js +65 -19
  134. package/dist/controllers/menubar_controller.js.map +1 -1
  135. package/dist/controllers/meter_controller.d.ts +13 -20
  136. package/dist/controllers/meter_controller.js +270 -24
  137. package/dist/controllers/meter_controller.js.map +1 -1
  138. package/dist/controllers/multi_select_controller.d.ts +19 -16
  139. package/dist/controllers/multi_select_controller.js +272 -95
  140. package/dist/controllers/multi_select_controller.js.map +1 -1
  141. package/dist/controllers/navigation_menu_controller.d.ts +19 -5
  142. package/dist/controllers/navigation_menu_controller.js +67 -19
  143. package/dist/controllers/navigation_menu_controller.js.map +1 -1
  144. package/dist/controllers/nested_form_controller.js +98 -64
  145. package/dist/controllers/nested_form_controller.js.map +1 -1
  146. package/dist/controllers/network_status_controller.js +70 -52
  147. package/dist/controllers/network_status_controller.js.map +1 -1
  148. package/dist/controllers/number_input_controller.d.ts +24 -6
  149. package/dist/controllers/number_input_controller.js +344 -94
  150. package/dist/controllers/number_input_controller.js.map +1 -1
  151. package/dist/controllers/optimistic_controller.d.ts +8 -5
  152. package/dist/controllers/optimistic_controller.js +29 -11
  153. package/dist/controllers/optimistic_controller.js.map +1 -1
  154. package/dist/controllers/otp_controller.d.ts +35 -4
  155. package/dist/controllers/otp_controller.js +502 -104
  156. package/dist/controllers/otp_controller.js.map +1 -1
  157. package/dist/controllers/overflow_indicator_controller.js +6 -0
  158. package/dist/controllers/overflow_indicator_controller.js.map +1 -1
  159. package/dist/controllers/overflow_menu_controller.d.ts +10 -9
  160. package/dist/controllers/overflow_menu_controller.js +136 -21
  161. package/dist/controllers/overflow_menu_controller.js.map +1 -1
  162. package/dist/controllers/pagination_controller.d.ts +3 -2
  163. package/dist/controllers/pagination_controller.js +47 -14
  164. package/dist/controllers/pagination_controller.js.map +1 -1
  165. package/dist/controllers/password_reveal_controller.d.ts +5 -4
  166. package/dist/controllers/password_reveal_controller.js +52 -49
  167. package/dist/controllers/password_reveal_controller.js.map +1 -1
  168. package/dist/controllers/password_strength_controller.d.ts +1 -1
  169. package/dist/controllers/password_strength_controller.js +13 -6
  170. package/dist/controllers/password_strength_controller.js.map +1 -1
  171. package/dist/controllers/persist_controller.js +48 -128
  172. package/dist/controllers/persist_controller.js.map +1 -1
  173. package/dist/controllers/pointer_drag_controller.d.ts +20 -8
  174. package/dist/controllers/pointer_drag_controller.js +255 -24
  175. package/dist/controllers/pointer_drag_controller.js.map +1 -1
  176. package/dist/controllers/popover_controller.d.ts +9 -5
  177. package/dist/controllers/popover_controller.js +91 -65
  178. package/dist/controllers/popover_controller.js.map +1 -1
  179. package/dist/controllers/portal_controller.js +6 -0
  180. package/dist/controllers/portal_controller.js.map +1 -1
  181. package/dist/controllers/preview_guard_controller.js +14 -3
  182. package/dist/controllers/preview_guard_controller.js.map +1 -1
  183. package/dist/controllers/progress_controller.d.ts +9 -19
  184. package/dist/controllers/progress_controller.js +271 -27
  185. package/dist/controllers/progress_controller.js.map +1 -1
  186. package/dist/controllers/radio_group_controller.d.ts +11 -8
  187. package/dist/controllers/radio_group_controller.js +167 -80
  188. package/dist/controllers/radio_group_controller.js.map +1 -1
  189. package/dist/controllers/range_slider_controller.d.ts +4 -3
  190. package/dist/controllers/range_slider_controller.js +27 -7
  191. package/dist/controllers/range_slider_controller.js.map +1 -1
  192. package/dist/controllers/rating_controller.js +93 -57
  193. package/dist/controllers/rating_controller.js.map +1 -1
  194. package/dist/controllers/read_more_controller.js +10 -6
  195. package/dist/controllers/read_more_controller.js.map +1 -1
  196. package/dist/controllers/reading_progress_controller.js +22 -5
  197. package/dist/controllers/reading_progress_controller.js.map +1 -1
  198. package/dist/controllers/relative_time_controller.d.ts +4 -4
  199. package/dist/controllers/relative_time_controller.js +75 -11
  200. package/dist/controllers/relative_time_controller.js.map +1 -1
  201. package/dist/controllers/reset_on_restore_controller.d.ts +3 -3
  202. package/dist/controllers/reset_on_restore_controller.js +6 -0
  203. package/dist/controllers/reset_on_restore_controller.js.map +1 -1
  204. package/dist/controllers/resizable_controller.d.ts +3 -0
  205. package/dist/controllers/resizable_controller.js +55 -22
  206. package/dist/controllers/resizable_controller.js.map +1 -1
  207. package/dist/controllers/roving_controller.d.ts +3 -3
  208. package/dist/controllers/roving_controller.js +10 -4
  209. package/dist/controllers/roving_controller.js.map +1 -1
  210. package/dist/controllers/scroll_area_controller.js +129 -88
  211. package/dist/controllers/scroll_area_controller.js.map +1 -1
  212. package/dist/controllers/scroll_restore_controller.d.ts +3 -3
  213. package/dist/controllers/scroll_restore_controller.js +14 -2
  214. package/dist/controllers/scroll_restore_controller.js.map +1 -1
  215. package/dist/controllers/scroll_visibility_controller.js +93 -60
  216. package/dist/controllers/scroll_visibility_controller.js.map +1 -1
  217. package/dist/controllers/scrollspy_controller.js +11 -5
  218. package/dist/controllers/scrollspy_controller.js.map +1 -1
  219. package/dist/controllers/separator_controller.d.ts +1 -1
  220. package/dist/controllers/separator_controller.js +59 -43
  221. package/dist/controllers/separator_controller.js.map +1 -1
  222. package/dist/controllers/sidebar_controller.d.ts +1 -1
  223. package/dist/controllers/sidebar_controller.js +150 -122
  224. package/dist/controllers/sidebar_controller.js.map +1 -1
  225. package/dist/controllers/skeleton_controller.js +55 -48
  226. package/dist/controllers/skeleton_controller.js.map +1 -1
  227. package/dist/controllers/slider_controller.d.ts +2 -1
  228. package/dist/controllers/slider_controller.js +27 -6
  229. package/dist/controllers/slider_controller.js.map +1 -1
  230. package/dist/controllers/smart_sticky_header_controller.d.ts +1 -1
  231. package/dist/controllers/smart_sticky_header_controller.js +13 -5
  232. package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
  233. package/dist/controllers/sortable_controller.d.ts +4 -4
  234. package/dist/controllers/sortable_controller.js +27 -14
  235. package/dist/controllers/sortable_controller.js.map +1 -1
  236. package/dist/controllers/spinner_controller.js +55 -48
  237. package/dist/controllers/spinner_controller.js.map +1 -1
  238. package/dist/controllers/step_indicator_controller.d.ts +4 -4
  239. package/dist/controllers/step_indicator_controller.js +6 -0
  240. package/dist/controllers/step_indicator_controller.js.map +1 -1
  241. package/dist/controllers/stepper_controller.d.ts +2 -3
  242. package/dist/controllers/stepper_controller.js +6 -0
  243. package/dist/controllers/stepper_controller.js.map +1 -1
  244. package/dist/controllers/stick_to_bottom_controller.d.ts +3 -1
  245. package/dist/controllers/stick_to_bottom_controller.js +19 -8
  246. package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
  247. package/dist/controllers/sticky_observer_controller.js +49 -43
  248. package/dist/controllers/sticky_observer_controller.js.map +1 -1
  249. package/dist/controllers/submit_once_controller.d.ts +2 -1
  250. package/dist/controllers/submit_once_controller.js +145 -85
  251. package/dist/controllers/submit_once_controller.js.map +1 -1
  252. package/dist/controllers/switch_controller.d.ts +7 -4
  253. package/dist/controllers/switch_controller.js +172 -28
  254. package/dist/controllers/switch_controller.js.map +1 -1
  255. package/dist/controllers/tabs_controller.d.ts +7 -4
  256. package/dist/controllers/tabs_controller.js +6 -1
  257. package/dist/controllers/tabs_controller.js.map +1 -1
  258. package/dist/controllers/tags_input_controller.d.ts +4 -3
  259. package/dist/controllers/tags_input_controller.js +157 -28
  260. package/dist/controllers/tags_input_controller.js.map +1 -1
  261. package/dist/controllers/textarea_autosize_controller.d.ts +1 -1
  262. package/dist/controllers/textarea_autosize_controller.js +40 -19
  263. package/dist/controllers/textarea_autosize_controller.js.map +1 -1
  264. package/dist/controllers/theme_controller.d.ts +3 -2
  265. package/dist/controllers/theme_controller.js +10 -4
  266. package/dist/controllers/theme_controller.js.map +1 -1
  267. package/dist/controllers/time_picker_controller.d.ts +38 -5
  268. package/dist/controllers/time_picker_controller.js +192 -54
  269. package/dist/controllers/time_picker_controller.js.map +1 -1
  270. package/dist/controllers/toast_controller.d.ts +4 -5
  271. package/dist/controllers/toast_controller.js +58 -27
  272. package/dist/controllers/toast_controller.js.map +1 -1
  273. package/dist/controllers/toggle_group_controller.d.ts +2 -3
  274. package/dist/controllers/toggle_group_controller.js +158 -56
  275. package/dist/controllers/toggle_group_controller.js.map +1 -1
  276. package/dist/controllers/toolbar_controller.d.ts +8 -6
  277. package/dist/controllers/toolbar_controller.js +150 -21
  278. package/dist/controllers/toolbar_controller.js.map +1 -1
  279. package/dist/controllers/tooltip_controller.d.ts +4 -1
  280. package/dist/controllers/tooltip_controller.js.map +1 -1
  281. package/dist/controllers/transition_controller.js.map +1 -1
  282. package/dist/controllers/tree_view_controller.d.ts +1 -1
  283. package/dist/controllers/tree_view_controller.js +134 -25
  284. package/dist/controllers/tree_view_controller.js.map +1 -1
  285. package/dist/index.d.ts +4 -1
  286. package/dist/index.js +2515 -1315
  287. package/dist/index.js.map +1 -1
  288. package/dist/inspector/cli.d.ts +25 -12
  289. package/dist/inspector/cli.js +21 -3
  290. package/dist/inspector/cli.js.map +1 -1
  291. package/dist/inspector/cli_bin.d.ts +1 -1
  292. package/dist/inspector/cli_bin.js +62 -8
  293. package/dist/inspector/cli_bin.js.map +1 -1
  294. package/dist/inspector/examples.json +13 -13
  295. package/dist/inspector/manifest.json +19 -5
  296. package/dist/positioning/index.d.ts +9 -8
  297. package/dist/positioning/index.js +6 -0
  298. package/dist/positioning/index.js.map +1 -1
  299. package/package.json +11 -10
@@ -6,7 +6,8 @@ import { Controller } from '@hotwired/stimulus';
6
6
  * Markup contract (identifier: `stimeo--context-menu`):
7
7
  * <div data-controller="stimeo--context-menu">
8
8
  * <div data-stimeo--context-menu-target="region" tabindex="0"
9
- * aria-haspopup="menu" aria-controls="ctx"
9
+ * role="group" aria-label="Files"
10
+ * aria-keyshortcuts="Shift+F10" aria-controls="ctx"
10
11
  * data-action="contextmenu->stimeo--context-menu#open
11
12
  * keydown->stimeo--context-menu#onRegionKeydown">…</div>
12
13
  * <ul id="ctx" role="menu" data-stimeo--context-menu-target="menu" hidden>
@@ -32,12 +33,25 @@ import { Controller } from '@hotwired/stimulus';
32
33
  * required). Viewport-edge flip/shift is delegated to the opt-in
33
34
  * `stimeo-ui/positioning` module, which this controller never imports.
34
35
  *
36
+ * The region's role and name are the author's: a `role="group"` with
37
+ * `aria-label`, or an element that has a role of its own (a table row, a list
38
+ * item, a button), chosen to match what the region holds. The controller writes
39
+ * no role. The author also points `aria-controls` at the menu and advertises the
40
+ * opening key with `aria-keyshortcuts="Shift+F10"`. The region carries no
41
+ * `aria-haspopup`, whatever its role. On a role that supports it, such as a
42
+ * button, the attribute conveys what ARIA 1.2's note says its visual
43
+ * counterparts, such as a downward-pointing triangle, signal: that activating
44
+ * the control (a click, `Enter`) displays a popup. This menu opens on a
45
+ * `contextmenu` event or the `Shift+F10` / `ContextMenu` key instead, so the
46
+ * attribute would point to the wrong gesture. On a role that does not, such as
47
+ * `generic`, `group`, `row` or `listitem`, it would be a global use of the
48
+ * attribute, which ARIA 1.2 deprecates.
49
+ *
35
50
  * Open state is exposed on the region as `data-state` (`open`/`closed`) — a CSS
36
- * hook, not an ARIA one. `aria-expanded` is deliberately *not* set on the region
37
- * because it is a generic container, not a role that supports that state (doing so
38
- * is an ARIA violation); the region's static `aria-haspopup="menu"` advertises the
39
- * popup, and assistive tech perceives the open state when focus moves into the
40
- * `role="menu"`.
51
+ * hook, not an ARIA one. `aria-expanded` is deliberately *not* set on the region:
52
+ * the role is the author's, and ARIA 1.2 lists neither `generic` nor `group`
53
+ * among the roles that support that state. Assistive tech perceives the open
54
+ * state when focus moves into the `role="menu"`.
41
55
  *
42
56
  * Behavior provided:
43
57
  * - `contextmenu` on the region suppresses the browser menu and opens this one at
@@ -45,8 +59,17 @@ import { Controller } from '@hotwired/stimulus';
45
59
  * - On open, focus moves to the first item; the region's `data-state` syncs.
46
60
  * - Roving focus inside the menu: `ArrowUp`/`ArrowDown` (wrapping), `Home`/`End`.
47
61
  * - Activating an enabled item (click / native `Enter`/`Space` on the button)
48
- * closes the menu and restores focus to the region. `aria-disabled` activation
49
- * is blocked before consumer click handlers run.
62
+ * closes the menu and restores focus to the region. An item that is `hidden`,
63
+ * natively `disabled`, or `aria-disabled="true"` itself or through an ancestor
64
+ * is not activated on any path, and a click on an `aria-disabled` one is also
65
+ * stopped, in a capture listener on the controller element, before the click
66
+ * handlers inside that element run. Items can nest, and an event is
67
+ * refused when any item holding its target refuses. A click is judged once, in
68
+ * that capture pass, so a consumer handler that disables the item while the
69
+ * click runs does not take back the activation. The verdict holds only for an
70
+ * item that held the click's target when it was taken: an item it was not taken
71
+ * for — a handler moved the target into another one — is judged when
72
+ * {@link activate} runs, as is any other event.
50
73
  * - `Escape` closes and restores focus to the region. While open the menu is a
51
74
  * layer on the shared `EscapeLayer` stack; it claims a press only while
52
75
  * focus is inside the controller or fell to the body, so one keypress closes
@@ -66,10 +89,11 @@ import { Controller } from '@hotwired/stimulus';
66
89
  * normalization in {@link connect}, a region or a menu that takes over, and
67
90
  * {@link disconnect} are all silent.
68
91
  *
69
- * Roving focus skips `hidden` and natively `disabled` items. An
70
- * `aria-disabled="true"` item stays reachable by arrow keys — APG marks that
71
- * attribute precisely for controls that must remain discoverable — while its
72
- * activation is suppressed, so it announces itself and does nothing.
92
+ * Roving focus skips `hidden` and natively `disabled` items. An item that
93
+ * `aria-disabled="true"` reaches, on the item or an ancestor, stays reachable by
94
+ * arrow keys — APG marks that attribute precisely for controls that must remain
95
+ * discoverable — while its activation is suppressed, so it announces itself and
96
+ * does nothing.
73
97
  */
74
98
  declare class ContextMenuController extends Controller<HTMLElement> {
75
99
  #private;
@@ -114,8 +138,19 @@ declare class ContextMenuController extends Controller<HTMLElement> {
114
138
  onRegionKeydown(event: KeyboardEvent): void;
115
139
  /** Roving focus and closing keys inside the menu. */
116
140
  onItemKeydown(event: KeyboardEvent): void;
117
- /** Closes after an item is activated and restores focus to the region. */
118
- activate(): void;
141
+ /**
142
+ * Closes after an item is activated and restores focus to the region. An event
143
+ * whose target lies in an item that is `hidden`, natively `disabled`, or
144
+ * `aria-disabled="true"` itself or through an ancestor does nothing: the menu
145
+ * stays open, nothing is dispatched and focus stays where it is. Every item
146
+ * holding the target counts, so an item nested in another one refuses for both,
147
+ * wherever the action is bound. A click is judged as its capture pass found the
148
+ * items, before the handlers inside the controller element ran, when the
149
+ * item this resolves was among them; an item that was not, and any other event, is
150
+ * judged when this runs.
151
+ * An event that resolves to no item, and a call with no event, always close.
152
+ */
153
+ activate(event?: Event): void;
119
154
  }
120
155
 
121
156
  export { ContextMenuController };
@@ -22,21 +22,42 @@ function actionSource(source) {
22
22
  };
23
23
  }
24
24
 
25
+ function isAriaDisabled(element) {
26
+ for (let current = element; current; current = current.parentElement) {
27
+ if (current.getAttribute("aria-disabled") === "true") return true;
28
+ }
29
+ return false;
30
+ }
31
+
25
32
  function isReservedArrowChord(event, allow = []) {
26
33
  if (!event.key.startsWith("Arrow")) return false;
27
34
  return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
28
35
  }
36
+ function hasModifierChord(event) {
37
+ return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
38
+ }
39
+
40
+ function setAttributeIfChanged(element, name, value, beforeWrite) {
41
+ if (element.getAttribute(name) === value) return false;
42
+ if (value === null) element.removeAttribute(name);
43
+ else element.setAttribute(name, value);
44
+ return true;
45
+ }
29
46
 
30
47
  function sharedRegistry(key, create) {
31
48
  const symbol = Symbol.for(key);
32
49
  const scope = globalThis;
33
50
  const existing = scope[symbol];
34
51
  if (existing !== void 0) return existing;
35
- const registry = create();
36
- Object.defineProperty(globalThis, symbol, { value: registry });
37
- return registry;
52
+ const registry2 = create();
53
+ Object.defineProperty(globalThis, symbol, { value: registry2 });
54
+ return registry2;
38
55
  }
39
56
 
57
+ var registry = sharedRegistry(
58
+ "stimeo-ui.attribute-lease.registry.v1",
59
+ () => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
60
+ );
40
61
  function isAttributeValue(value) {
41
62
  return value === null || typeof value === "string";
42
63
  }
@@ -53,24 +74,46 @@ function readLeaseRecord(element, record) {
53
74
  return void 0;
54
75
  }
55
76
  }
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
- );
77
+ function attributeStack(element, attribute) {
78
+ const byAttribute = registry.stacks.get(element) ?? /* @__PURE__ */ new Map();
79
+ registry.stacks.set(element, byAttribute);
80
+ const known = byAttribute.get(attribute);
81
+ if (known) return known;
82
+ const found = [];
83
+ for (const record of registry.names.get(attribute) ?? []) {
84
+ const read = readLeaseRecord(element, record);
85
+ if (read) found.push({ ...read, record });
86
+ }
87
+ const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
88
+ const stack = found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
89
+ lease: null,
90
+ record,
91
+ written,
92
+ original,
93
+ linked: linked && index > 0
94
+ }));
95
+ byAttribute.set(attribute, stack);
96
+ return stack;
97
+ }
98
+ function findHold(stack, lease, record) {
99
+ return stack.find((held) => held.lease === lease) ?? stack.find((held) => held.lease === null && held.record === record);
100
+ }
101
+ var AttributeLease = class {
61
102
  #attribute;
62
103
  #record;
63
104
  #base;
105
+ #preserveAuthor;
64
106
  #held = /* @__PURE__ */ new Set();
65
107
  constructor(attribute, owner, options = {}) {
66
108
  this.#attribute = attribute;
67
109
  this.#record = `data-${owner}-${attribute}-lease`;
68
110
  this.#base = options.base === true;
69
- const names = _AttributeLease.#registry.names;
111
+ this.#preserveAuthor = options.preserveAuthor === true;
112
+ const names = registry.names;
70
113
  names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
71
114
  }
72
115
  write(element, value) {
73
- const stack = this.#stack(element);
116
+ const stack = attributeStack(element, this.#attribute);
74
117
  const hold = this.#take(stack) ?? this.#acquire(element, stack);
75
118
  this.#held.add(element);
76
119
  const shows = !this.#covered(element, stack, hold);
@@ -80,8 +123,8 @@ var AttributeLease = class _AttributeLease {
80
123
  }
81
124
  return(element) {
82
125
  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) : [];
126
+ const known = registry.stacks.get(element)?.has(this.#attribute);
127
+ const stack = held || known || element.hasAttribute(this.#record) ? attributeStack(element, this.#attribute) : [];
85
128
  const hold = this.#take(stack);
86
129
  if (!hold) return false;
87
130
  const carries = element.getAttribute(this.#attribute) === hold.written;
@@ -99,31 +142,8 @@ var AttributeLease = class _AttributeLease {
99
142
  returnAll() {
100
143
  for (const element of Array.from(this.#held)) this.return(element);
101
144
  }
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
145
  #take(stack) {
126
- const hold = stack.find((held) => held.lease === this) ?? stack.find((held) => held.lease === null && held.record === this.#record);
146
+ const hold = findHold(stack, this, this.#record);
127
147
  if (hold) hold.lease = this;
128
148
  return hold;
129
149
  }
@@ -132,7 +152,7 @@ var AttributeLease = class _AttributeLease {
132
152
  const top = stack.at(-1);
133
153
  const bottom = stack[0];
134
154
  const over = top?.written === current ? top : void 0;
135
- const from = this.#base ? bottom : over;
155
+ const from = this.#base ? this.#preserveAuthor && !stack.some((held) => held.written === current) ? void 0 : bottom : over;
136
156
  const hold = {
137
157
  lease: this,
138
158
  record: this.#record,
@@ -162,15 +182,23 @@ var AttributeLease = class _AttributeLease {
162
182
  );
163
183
  });
164
184
  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);
185
+ setAttributeIfChanged(element, name, recorded);
168
186
  }
169
187
  }
170
188
  #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);
189
+ setAttributeIfChanged(element, this.#attribute, value);
190
+ }
191
+ };
192
+
193
+ var ClickVerdicts = class {
194
+ #verdicts = /* @__PURE__ */ new WeakMap();
195
+ record(event, entries, refused) {
196
+ this.#verdicts.set(event, { entries, refused });
197
+ }
198
+ for(event, entry) {
199
+ const verdict = event && this.#verdicts.get(event);
200
+ if (!verdict?.entries.includes(entry)) return void 0;
201
+ return verdict.refused;
174
202
  }
175
203
  };
176
204
 
@@ -188,25 +216,25 @@ var EscapeLayer = class _EscapeLayer {
188
216
  #active = null;
189
217
  activate(ownerDocument = document, options) {
190
218
  this.deactivate();
191
- let registry = _EscapeLayer.#registries.get(ownerDocument);
192
- if (!registry) {
193
- registry = _EscapeLayer.#createRegistry();
194
- _EscapeLayer.#registries.set(ownerDocument, registry);
195
- ownerDocument.addEventListener("keydown", registry.onKeydown);
219
+ let registry2 = _EscapeLayer.#registries.get(ownerDocument);
220
+ if (!registry2) {
221
+ registry2 = _EscapeLayer.#createRegistry();
222
+ _EscapeLayer.#registries.set(ownerDocument, registry2);
223
+ ownerDocument.addEventListener("keydown", registry2.onKeydown);
196
224
  }
197
225
  const entry = { onDismiss: options.onDismiss, claims: options.claims ?? null };
198
- registry.stack.push(entry);
226
+ registry2.stack.push(entry);
199
227
  this.#active = { document: ownerDocument, entry };
200
228
  }
201
229
  deactivate() {
202
230
  const active = this.#active;
203
231
  if (!active) return;
204
- const registry = _EscapeLayer.#registries.get(active.document);
205
- if (registry) {
206
- const index = registry.stack.lastIndexOf(active.entry);
207
- if (index >= 0) registry.stack.splice(index, 1);
208
- if (registry.stack.length === 0) {
209
- active.document.removeEventListener("keydown", registry.onKeydown);
232
+ const registry2 = _EscapeLayer.#registries.get(active.document);
233
+ if (registry2) {
234
+ const index = registry2.stack.lastIndexOf(active.entry);
235
+ if (index >= 0) registry2.stack.splice(index, 1);
236
+ if (registry2.stack.length === 0) {
237
+ active.document.removeEventListener("keydown", registry2.onKeydown);
210
238
  _EscapeLayer.#registries.delete(active.document);
211
239
  }
212
240
  }
@@ -215,22 +243,22 @@ var EscapeLayer = class _EscapeLayer {
215
243
  get ownsEscape() {
216
244
  const active = this.#active;
217
245
  if (!active) return false;
218
- const registry = _EscapeLayer.#registries.get(active.document);
219
- if (!registry) return false;
220
- return _EscapeLayer.#resolveOwner(registry.stack) === active.entry;
246
+ const registry2 = _EscapeLayer.#registries.get(active.document);
247
+ if (!registry2) return false;
248
+ return _EscapeLayer.#resolveOwner(registry2.stack) === active.entry;
221
249
  }
222
250
  static #createRegistry() {
223
- const registry = {
251
+ const registry2 = {
224
252
  stack: [],
225
253
  onKeydown: (event) => {
226
254
  if (event.key !== "Escape" || event.defaultPrevented || event.isComposing) return;
227
- const owner = _EscapeLayer.#resolveOwner(registry.stack);
255
+ const owner = _EscapeLayer.#resolveOwner(registry2.stack);
228
256
  if (!owner) return;
229
257
  event.preventDefault();
230
258
  owner.onDismiss();
231
259
  }
232
260
  };
233
- return registry;
261
+ return registry2;
234
262
  }
235
263
  static #resolveOwner(stack) {
236
264
  for (let index = stack.length - 1; index >= 0; index--) {
@@ -243,6 +271,29 @@ var EscapeLayer = class _EscapeLayer {
243
271
  }
244
272
  };
245
273
 
274
+ function ownerIndex(candidates, node) {
275
+ if (!(node instanceof Node)) return -1;
276
+ return candidates.findIndex((candidate) => candidate.contains(node));
277
+ }
278
+ function ownerOf(candidates, node) {
279
+ return candidates[ownerIndex(candidates, node)] ?? null;
280
+ }
281
+ function ownersOf(candidates, node) {
282
+ if (!(node instanceof Node)) return [];
283
+ return candidates.filter((candidate) => candidate.contains(node));
284
+ }
285
+
286
+ var ListenerSet = class {
287
+ #abort = new AbortController();
288
+ add(target, type, handler, options) {
289
+ target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
290
+ }
291
+ dispose() {
292
+ this.#abort.abort();
293
+ this.#abort = new AbortController();
294
+ }
295
+ };
296
+
246
297
  var TimerRegistry = class {
247
298
  ids = /* @__PURE__ */ new Set();
248
299
  clear(id) {
@@ -289,11 +340,13 @@ var ContextMenuController = class _ContextMenuController extends Controller {
289
340
  #menu = null;
290
341
  #connected = false;
291
342
  #reporting = false;
343
+ #clickVerdicts = new ClickVerdicts();
344
+ #listeners = new ListenerSet();
292
345
  connect() {
293
346
  this.#closeMenu("api");
294
- this.element.addEventListener("click", this.#onItemClickCapture, true);
295
- document.addEventListener("click", this.#onOutsidePointer, true);
296
- document.addEventListener("contextmenu", this.#onOutsidePointer, true);
347
+ this.#listeners.add(this.element, "click", this.#onItemClickCapture, { capture: true });
348
+ this.#listeners.add(document, "click", this.#onOutsidePointer, { capture: true });
349
+ this.#listeners.add(document, "contextmenu", this.#onOutsidePointer, { capture: true });
297
350
  this.#connected = true;
298
351
  this.#reporting = true;
299
352
  }
@@ -302,9 +355,7 @@ var ContextMenuController = class _ContextMenuController extends Controller {
302
355
  this.#reporting = false;
303
356
  this.#timers.clearAll();
304
357
  this.#escapeLayer.deactivate();
305
- this.element.removeEventListener("click", this.#onItemClickCapture, true);
306
- document.removeEventListener("click", this.#onOutsidePointer, true);
307
- document.removeEventListener("contextmenu", this.#onOutsidePointer, true);
358
+ this.#listeners.dispose();
308
359
  }
309
360
  regionTargetConnected() {
310
361
  if (this.#connected) this.#reflectRegion();
@@ -342,6 +393,7 @@ var ContextMenuController = class _ContextMenuController extends Controller {
342
393
  onItemKeydown(event) {
343
394
  if (event.defaultPrevented) return;
344
395
  if (isReservedArrowChord(event)) return;
396
+ if ((event.key === "Home" || event.key === "End") && hasModifierChord(event)) return;
345
397
  const items = this.#navigableItems;
346
398
  const currentIndex = items.indexOf(event.currentTarget);
347
399
  switch (event.key) {
@@ -373,9 +425,18 @@ var ContextMenuController = class _ContextMenuController extends Controller {
373
425
  break;
374
426
  }
375
427
  }
376
- activate() {
428
+ activate(event) {
429
+ const item = ownerOf(this.itemTargets, event?.target);
430
+ if (item !== null && this.#refuses(item, event)) return;
377
431
  this.#closeAndRestore("select");
378
432
  }
433
+ #refuses(item, event) {
434
+ const recorded = this.#clickVerdicts.for(event, item);
435
+ if (recorded !== void 0) return recorded;
436
+ return ownersOf(this.itemTargets, event?.target).some(
437
+ (owner) => this.#isActivationDisabled(owner)
438
+ );
439
+ }
379
440
  #openAt(x, y, reason) {
380
441
  if (!this.hasMenuTarget) return;
381
442
  this.#timers.clearAll();
@@ -435,12 +496,13 @@ var ContextMenuController = class _ContextMenuController extends Controller {
435
496
  if (this.#isOpen && !this.element.contains(event.target)) this.#closeMenu("outside");
436
497
  };
437
498
  #onItemClickCapture = (event) => {
438
- const target = event.target;
439
- if (!(target instanceof Node)) return;
440
- const disabled = this.itemTargets.some(
441
- (item) => item.getAttribute("aria-disabled") === "true" && item.contains(target)
499
+ const items = ownersOf(this.itemTargets, event.target);
500
+ this.#clickVerdicts.record(
501
+ event,
502
+ items,
503
+ items.some((item) => this.#isActivationDisabled(item))
442
504
  );
443
- if (!disabled) return;
505
+ if (!items.some((item) => isAriaDisabled(item))) return;
444
506
  event.preventDefault();
445
507
  event.stopImmediatePropagation();
446
508
  };
@@ -451,6 +513,9 @@ var ContextMenuController = class _ContextMenuController extends Controller {
451
513
  if (item.hasAttribute("hidden")) return false;
452
514
  return !item.disabled;
453
515
  }
516
+ #isActivationDisabled(item) {
517
+ return !this.#isNavigable(item) || isAriaDisabled(item);
518
+ }
454
519
  get #isOpen() {
455
520
  return this.hasMenuTarget && !this.menuTarget.hidden;
456
521
  }