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
@@ -27,25 +27,61 @@ import { Controller } from '@hotwired/stimulus';
27
27
  * non-focusable menu content), so a press aimed at another layer never closes
28
28
  * the menu or steals focus, and one keypress closes exactly one layer.
29
29
  * - A click outside the controller element closes the menu.
30
+ * - A trigger or a menu that takes over — in one task, or after an earlier one leaves
31
+ * in a later task — carries the open state, and an `aria-controls` this controller
32
+ * supplied names the menu that is first. With no menu left the dropdown reads
33
+ * closed and leaves the Escape stack. One that stops resolving as the target gets
34
+ * back what it carried before this controller wrote on it.
35
+ * - A Turbo morph that keeps the elements drops the attributes the server did not
36
+ * send — the menu's `hidden`, the trigger's `aria-expanded` and the
37
+ * `aria-controls` this controller supplied. They are written back from the open
38
+ * state the controller holds, so a closed menu does not show up and an open one
39
+ * stays described.
40
+ * - Each move of the open state is reported: `stimeo--dropdown:open` and
41
+ * `stimeo--dropdown:close` dispatch `{ reason: StateReason }`, after the
42
+ * attributes above are written. Both are informational, so neither is
43
+ * cancelable. A call that leaves the state where it already was, the
44
+ * normalization in {@link connect}, a trigger or a menu that takes over, the
45
+ * repair after a morph, and {@link disconnect} are all silent.
30
46
  */
31
47
  declare class DropdownController extends Controller<HTMLElement> {
32
48
  #private;
33
49
  static targets: string[];
34
50
  static actions: readonly ["close", "open", "toggle"];
51
+ static events: readonly ["close", "open"];
35
52
  readonly triggerTarget: HTMLButtonElement;
53
+ readonly triggerTargets: HTMLButtonElement[];
36
54
  readonly menuTarget: HTMLElement;
55
+ readonly menuTargets: HTMLElement[];
37
56
  readonly hasMenuTarget: boolean;
38
57
  readonly hasTriggerTarget: boolean;
39
58
  /** Starts in the closed state and registers outside-click handling. */
40
59
  connect(): void;
41
60
  /** Removes the listeners registered in {@link connect}. */
42
61
  disconnect(): void;
62
+ /** Brings a trigger that arrives after connect to the open state and names the menu from it. */
63
+ triggerTargetConnected(): void;
64
+ /**
65
+ * Gives a trigger that no longer resolves as one its own `aria-expanded` and `aria-controls`
66
+ * back — after `disconnect()` too, since dropping the identifier leaves the element on the
67
+ * page — and brings the trigger that stays to the open state.
68
+ */
69
+ triggerTargetDisconnected(trigger: HTMLButtonElement): void;
70
+ /** Applies the open state to a menu that arrives after connect in front of the others. */
71
+ menuTargetConnected(): void;
72
+ /**
73
+ * Applies the open state to the menu left, then gives a menu that no longer resolves as the
74
+ * target its own `hidden` back — after `disconnect()` too, since dropping the identifier
75
+ * leaves the element on the page. The open state is read off the departing menu first,
76
+ * while it still carries it.
77
+ */
78
+ menuTargetDisconnected(menu: HTMLElement): void;
43
79
  /** Toggles the menu between open and closed. Bound via `data-action`. */
44
- toggle(): void;
80
+ toggle(event?: Event): void;
45
81
  /** Reveals the menu and reflects the open state on the trigger. */
46
- open(): void;
82
+ open(event?: Event): void;
47
83
  /** Hides the menu and reflects the closed state on the trigger. */
48
- close(): void;
84
+ close(event?: Event): void;
49
85
  }
50
86
 
51
87
  export { DropdownController };
@@ -1,14 +1,21 @@
1
1
  import { Controller } from '@hotwired/stimulus';
2
2
 
3
- // src/controllers/dropdown_controller.ts
3
+ function sharedRegistry(key, create) {
4
+ const symbol = Symbol.for(key);
5
+ const scope = globalThis;
6
+ const existing = scope[symbol];
7
+ if (existing !== void 0) return existing;
8
+ const registry = create();
9
+ Object.defineProperty(globalThis, symbol, { value: registry });
10
+ return registry;
11
+ }
4
12
 
5
- // src/utils/aria_ids.ts
6
- var counter = 0;
13
+ var ids = sharedRegistry("stimeo-ui.aria-ids.registry.v1", () => ({ counter: 0 }));
7
14
  function uniqueId(prefix = "stimeo") {
8
15
  let candidate;
9
16
  do {
10
- counter += 1;
11
- candidate = `${prefix}-${counter}`;
17
+ ids.counter += 1;
18
+ candidate = `${prefix}-${ids.counter}`;
12
19
  } while (typeof document !== "undefined" && document.getElementById(candidate) !== null);
13
20
  return candidate;
14
21
  }
@@ -19,7 +26,143 @@ function ensureId(element, prefix = "stimeo") {
19
26
  return id;
20
27
  }
21
28
 
22
- // src/utils/escape_layer.ts
29
+ function isAttributeValue(value) {
30
+ return value === null || typeof value === "string";
31
+ }
32
+ function readLeaseRecord(element, record) {
33
+ try {
34
+ const parsed = JSON.parse(element.getAttribute(record) ?? "null");
35
+ if (!Array.isArray(parsed)) return void 0;
36
+ const [original, written, ...rest] = parsed;
37
+ const linked = rest[0] !== false;
38
+ const beneath = linked ? rest : rest.slice(1);
39
+ if (!beneath.every((under) => typeof under === "string")) return void 0;
40
+ return isAttributeValue(original) && isAttributeValue(written) ? { original, written, linked, beneath } : void 0;
41
+ } catch {
42
+ return void 0;
43
+ }
44
+ }
45
+ var AttributeLease = class _AttributeLease {
46
+ static #registry = sharedRegistry(
47
+ "stimeo-ui.attribute-lease.registry.v1",
48
+ () => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
49
+ );
50
+ #attribute;
51
+ #record;
52
+ #base;
53
+ #held = /* @__PURE__ */ new Set();
54
+ constructor(attribute, owner, options = {}) {
55
+ this.#attribute = attribute;
56
+ this.#record = `data-${owner}-${attribute}-lease`;
57
+ this.#base = options.base === true;
58
+ const names = _AttributeLease.#registry.names;
59
+ names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
60
+ }
61
+ write(element, value) {
62
+ const stack = this.#stack(element);
63
+ const hold = this.#take(stack) ?? this.#acquire(element, stack);
64
+ this.#held.add(element);
65
+ const shows = !this.#covered(element, stack, hold);
66
+ hold.written = value;
67
+ this.#mark(element, stack);
68
+ if (shows) this.#reflect(element, value);
69
+ }
70
+ return(element) {
71
+ const held = this.#held.delete(element);
72
+ const known = _AttributeLease.#registry.stacks.get(element)?.has(this.#attribute);
73
+ const stack = held || known || element.hasAttribute(this.#record) ? this.#stack(element) : [];
74
+ const hold = this.#take(stack);
75
+ if (!hold) return false;
76
+ const carries = element.getAttribute(this.#attribute) === hold.written;
77
+ const owns = carries && !this.#covered(element, stack, hold);
78
+ const index = stack.indexOf(hold);
79
+ stack.splice(index, 1);
80
+ const above = stack[index];
81
+ if (above) above.linked &&= hold.linked;
82
+ this.#mark(element, stack);
83
+ if (!owns) return true;
84
+ const below = hold.linked ? stack[index - 1] : void 0;
85
+ this.#reflect(element, below ? below.written : hold.original);
86
+ return true;
87
+ }
88
+ returnAll() {
89
+ for (const element of Array.from(this.#held)) this.return(element);
90
+ }
91
+ #stack(element) {
92
+ const stacks = _AttributeLease.#registry.stacks;
93
+ const byAttribute = stacks.get(element) ?? /* @__PURE__ */ new Map();
94
+ stacks.set(element, byAttribute);
95
+ const stack = byAttribute.get(this.#attribute) ?? this.#seed(element);
96
+ byAttribute.set(this.#attribute, stack);
97
+ return stack;
98
+ }
99
+ #seed(element) {
100
+ const found = [];
101
+ for (const record of _AttributeLease.#registry.names.get(this.#attribute) ?? []) {
102
+ const read = readLeaseRecord(element, record);
103
+ if (read) found.push({ ...read, record });
104
+ }
105
+ const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
106
+ return found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
107
+ lease: null,
108
+ record,
109
+ written,
110
+ original,
111
+ linked: linked && index > 0
112
+ }));
113
+ }
114
+ #take(stack) {
115
+ const hold = stack.find((held) => held.lease === this) ?? stack.find((held) => held.lease === null && held.record === this.#record);
116
+ if (hold) hold.lease = this;
117
+ return hold;
118
+ }
119
+ #acquire(element, stack) {
120
+ const current = element.getAttribute(this.#attribute);
121
+ const top = stack.at(-1);
122
+ const bottom = stack[0];
123
+ const over = top?.written === current ? top : void 0;
124
+ const from = this.#base ? bottom : over;
125
+ const hold = {
126
+ lease: this,
127
+ record: this.#record,
128
+ written: current,
129
+ original: from ? from.original : current,
130
+ linked: !this.#base && over !== void 0
131
+ };
132
+ if (this.#base && bottom) {
133
+ bottom.linked = true;
134
+ stack.unshift(hold);
135
+ } else stack.push(hold);
136
+ return hold;
137
+ }
138
+ #covered(element, stack, hold) {
139
+ const current = element.getAttribute(this.#attribute);
140
+ return stack.slice(stack.indexOf(hold) + 1).some((above) => above.written === current);
141
+ }
142
+ #mark(element, stack) {
143
+ const records = /* @__PURE__ */ new Map([[this.#record, null]]);
144
+ stack.forEach((hold, index) => {
145
+ const names = stack.slice(0, index).map((under) => under.record);
146
+ const beneath = [...new Set(names.reverse())].filter((name) => name !== hold.record);
147
+ const marker = hold.linked || beneath.length === 0 ? [] : [false];
148
+ records.set(
149
+ hold.record,
150
+ hold.written === hold.original && beneath.length === 0 && index === stack.length - 1 ? null : JSON.stringify([hold.original, hold.written, ...marker, ...beneath])
151
+ );
152
+ });
153
+ for (const [name, recorded] of records) {
154
+ if (element.getAttribute(name) === recorded) continue;
155
+ if (recorded === null) element.removeAttribute(name);
156
+ else element.setAttribute(name, recorded);
157
+ }
158
+ }
159
+ #reflect(element, value) {
160
+ if (element.getAttribute(this.#attribute) === value) return;
161
+ if (value === null) element.removeAttribute(this.#attribute);
162
+ else element.setAttribute(this.#attribute, value);
163
+ }
164
+ };
165
+
23
166
  function claimsWhileFocusWithin(element) {
24
167
  return () => {
25
168
  const active = element.ownerDocument.activeElement;
@@ -27,17 +170,11 @@ function claimsWhileFocusWithin(element) {
27
170
  };
28
171
  }
29
172
  var EscapeLayer = class _EscapeLayer {
30
- static #registries = /* @__PURE__ */ new WeakMap();
31
- #ownerDocument = null;
32
- /** Dismissal callback while active; `null` when inactive. */
33
- #onDismiss = null;
34
- /** Live predicate deciding whether the layer claims a press; `null` = always. */
35
- #claims = null;
36
- /**
37
- * Activates this layer at the top of its document's Escape stack, installing
38
- * the document's shared resolver listener if this is its first layer.
39
- * Re-activating an already-active layer moves it to the top.
40
- */
173
+ static #registries = sharedRegistry(
174
+ "stimeo-ui.escape-layer.registry.v1",
175
+ () => /* @__PURE__ */ new WeakMap()
176
+ );
177
+ #active = null;
41
178
  activate(ownerDocument = document, options) {
42
179
  this.deactivate();
43
180
  let registry = _EscapeLayer.#registries.get(ownerDocument);
@@ -46,44 +183,31 @@ var EscapeLayer = class _EscapeLayer {
46
183
  _EscapeLayer.#registries.set(ownerDocument, registry);
47
184
  ownerDocument.addEventListener("keydown", registry.onKeydown);
48
185
  }
49
- registry.stack.push(this);
50
- this.#ownerDocument = ownerDocument;
51
- this.#onDismiss = options.onDismiss;
52
- this.#claims = options.claims ?? null;
53
- }
54
- /**
55
- * Removes this layer from its document's Escape stack, uninstalling the
56
- * shared listener when the stack empties. Safe to call when inactive.
57
- */
186
+ const entry = { onDismiss: options.onDismiss, claims: options.claims ?? null };
187
+ registry.stack.push(entry);
188
+ this.#active = { document: ownerDocument, entry };
189
+ }
58
190
  deactivate() {
59
- const ownerDocument = this.#ownerDocument;
60
- if (!ownerDocument) return;
61
- const registry = _EscapeLayer.#registries.get(ownerDocument);
191
+ const active = this.#active;
192
+ if (!active) return;
193
+ const registry = _EscapeLayer.#registries.get(active.document);
62
194
  if (registry) {
63
- const index = registry.stack.lastIndexOf(this);
195
+ const index = registry.stack.lastIndexOf(active.entry);
64
196
  if (index >= 0) registry.stack.splice(index, 1);
65
197
  if (registry.stack.length === 0) {
66
- ownerDocument.removeEventListener("keydown", registry.onKeydown);
67
- _EscapeLayer.#registries.delete(ownerDocument);
198
+ active.document.removeEventListener("keydown", registry.onKeydown);
199
+ _EscapeLayer.#registries.delete(active.document);
68
200
  }
69
201
  }
70
- this.#ownerDocument = null;
71
- this.#onDismiss = null;
72
- this.#claims = null;
73
- }
74
- /**
75
- * Whether this active layer would own a press right now: it is the topmost
76
- * layer whose {@link EscapeLayerOptions.claims} passes. Exposed for tests
77
- * and diagnostics — production dismissal goes through the shared listener.
78
- */
202
+ this.#active = null;
203
+ }
79
204
  get ownsEscape() {
80
- const ownerDocument = this.#ownerDocument;
81
- if (!ownerDocument) return false;
82
- const registry = _EscapeLayer.#registries.get(ownerDocument);
205
+ const active = this.#active;
206
+ if (!active) return false;
207
+ const registry = _EscapeLayer.#registries.get(active.document);
83
208
  if (!registry) return false;
84
- return _EscapeLayer.#resolveOwner(registry.stack) === this;
209
+ return _EscapeLayer.#resolveOwner(registry.stack) === active.entry;
85
210
  }
86
- /** Builds a document's registry with its shared resolver listener. */
87
211
  static #createRegistry() {
88
212
  const registry = {
89
213
  stack: [],
@@ -92,98 +216,231 @@ var EscapeLayer = class _EscapeLayer {
92
216
  const owner = _EscapeLayer.#resolveOwner(registry.stack);
93
217
  if (!owner) return;
94
218
  event.preventDefault();
95
- owner.#onDismiss?.();
219
+ owner.onDismiss();
96
220
  }
97
221
  };
98
222
  return registry;
99
223
  }
100
- /** The topmost stack layer whose claims predicate passes, or `null`. */
101
224
  static #resolveOwner(stack) {
102
225
  for (let index = stack.length - 1; index >= 0; index--) {
103
226
  const layer = stack[index];
104
227
  if (!layer) continue;
105
- if (layer.#claims && !layer.#claims()) continue;
228
+ if (layer.claims && !layer.claims()) continue;
106
229
  return layer;
107
230
  }
108
231
  return null;
109
232
  }
110
233
  };
111
234
 
112
- // src/controllers/dropdown_controller.ts
235
+ var ListenerSet = class {
236
+ #abort = new AbortController();
237
+ add(target, type, handler, options) {
238
+ target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
239
+ }
240
+ dispose() {
241
+ this.#abort.abort();
242
+ this.#abort = new AbortController();
243
+ }
244
+ };
245
+
246
+ var MicrotaskCoalescer = class {
247
+ #run;
248
+ #queued = false;
249
+ #active = false;
250
+ #generation = 0;
251
+ constructor(run) {
252
+ this.#run = run;
253
+ }
254
+ activate() {
255
+ this.#active = true;
256
+ }
257
+ cancel() {
258
+ this.#active = false;
259
+ this.#queued = false;
260
+ this.#generation += 1;
261
+ }
262
+ schedule() {
263
+ if (!this.#active || this.#queued) return;
264
+ this.#queued = true;
265
+ const generation = this.#generation;
266
+ queueMicrotask(() => {
267
+ if (generation !== this.#generation || !this.#queued || !this.#active) return;
268
+ this.#queued = false;
269
+ this.#run();
270
+ });
271
+ }
272
+ };
273
+
274
+ var MorphRenderWatcher = class {
275
+ #listeners = new ListenerSet();
276
+ #pass;
277
+ #element = null;
278
+ #onMorph = () => this.#pass.schedule();
279
+ constructor(run) {
280
+ this.#pass = new MicrotaskCoalescer(run);
281
+ }
282
+ observe(element) {
283
+ if (this.#element === element) return;
284
+ this.disconnect();
285
+ this.#element = element;
286
+ this.#pass.activate();
287
+ this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
288
+ }
289
+ schedule() {
290
+ this.#pass.schedule();
291
+ }
292
+ disconnect() {
293
+ this.#listeners.dispose();
294
+ this.#pass.cancel();
295
+ this.#element = null;
296
+ }
297
+ };
298
+
299
+ var FOCUS_EVENTS = /* @__PURE__ */ new Set(["blur", "focus", "focusin", "focusout"]);
300
+ var POINTER_EVENTS = /* @__PURE__ */ new Set(["mouseenter", "mouseleave", "pointerenter", "pointerleave"]);
301
+ function stateReasonFor(event) {
302
+ if (!event) return "api";
303
+ if (FOCUS_EVENTS.has(event.type)) return "focus";
304
+ if (POINTER_EVENTS.has(event.type)) return "pointer";
305
+ return "user";
306
+ }
307
+
113
308
  var DropdownController = class extends Controller {
114
309
  static targets = ["trigger", "menu"];
115
310
  static actions = ["close", "open", "toggle"];
116
- /** Escape-stack membership while open; the shared resolver dismisses via it. */
311
+ static events = ["close", "open"];
117
312
  #escapeLayer = new EscapeLayer();
118
- /** Closes the menu when a click lands outside the controller's element. */
313
+ #menuHidden = new AttributeLease("hidden", this.identifier);
314
+ #expanded = new AttributeLease("aria-expanded", this.identifier);
315
+ #suppliedControls = /* @__PURE__ */ new WeakMap();
316
+ #menu = null;
317
+ #connected = false;
318
+ #reporting = false;
319
+ #openState = false;
320
+ #morphRender = new MorphRenderWatcher(() => this.#repair());
119
321
  #onOutsideClick = (event) => {
120
322
  if (!this.element.contains(event.target)) {
121
- this.close();
323
+ this.#close("outside");
122
324
  }
123
325
  };
124
- /** Starts in the closed state and registers outside-click handling. */
125
326
  connect() {
126
327
  this.#associateTriggerWithMenu();
127
- this.close();
328
+ this.#close("api");
128
329
  document.addEventListener("click", this.#onOutsideClick, true);
330
+ this.#morphRender.observe(this.element);
331
+ this.#connected = true;
332
+ this.#reporting = true;
129
333
  }
130
- /** Removes the listeners registered in {@link connect}. */
131
334
  disconnect() {
335
+ this.#connected = false;
336
+ this.#reporting = false;
132
337
  this.#escapeLayer.deactivate();
133
338
  document.removeEventListener("click", this.#onOutsideClick, true);
339
+ this.#morphRender.disconnect();
340
+ }
341
+ triggerTargetConnected() {
342
+ if (this.#connected) this.#reflectTrigger();
134
343
  }
135
- /** Toggles the menu between open and closed. Bound via `data-action`. */
136
- toggle() {
344
+ triggerTargetDisconnected(trigger) {
345
+ if (!this.triggerTargets.includes(trigger)) {
346
+ this.#expanded.return(trigger);
347
+ this.#withdrawControls(trigger);
348
+ }
349
+ if (this.#connected) this.#reflectTrigger();
350
+ }
351
+ menuTargetConnected() {
352
+ this.#adoptMenu();
353
+ }
354
+ menuTargetDisconnected(menu) {
355
+ this.#adoptMenu();
356
+ if (!this.menuTargets.includes(menu)) this.#menuHidden.return(menu);
357
+ }
358
+ toggle(event) {
137
359
  if (this.#isOpen) {
138
- this.close();
360
+ this.close(event);
139
361
  } else {
140
- this.open();
362
+ this.open(event);
141
363
  }
142
364
  }
143
- /** Reveals the menu and reflects the open state on the trigger. */
144
- open() {
365
+ open(event) {
366
+ this.#open(stateReasonFor(event));
367
+ }
368
+ close(event) {
369
+ this.#close(stateReasonFor(event));
370
+ }
371
+ #open(reason) {
145
372
  if (!this.hasMenuTarget) return;
373
+ const menu = this.menuTarget;
374
+ const was = this.#isOpen;
146
375
  this.#escapeLayer.activate(document, {
147
376
  onDismiss: () => this.#closeAndRestore(),
148
377
  claims: claimsWhileFocusWithin(this.element)
149
378
  });
150
- this.menuTarget.hidden = false;
151
- if (this.hasTriggerTarget) {
152
- this.triggerTarget.setAttribute("aria-expanded", "true");
153
- }
379
+ this.#menuHidden.write(menu, null);
380
+ this.#menu = menu;
381
+ this.#openState = true;
382
+ if (this.hasTriggerTarget) this.#expanded.write(this.triggerTarget, "true");
383
+ if (!was && this.#reporting) this.dispatch("open", { detail: { reason }, cancelable: false });
154
384
  }
155
- /** Hides the menu and reflects the closed state on the trigger. */
156
- close() {
385
+ #close(reason) {
386
+ const was = this.#isOpen;
157
387
  this.#escapeLayer.deactivate();
158
- if (!this.hasMenuTarget) return;
159
- this.menuTarget.hidden = true;
160
- if (this.hasTriggerTarget) {
161
- this.triggerTarget.setAttribute("aria-expanded", "false");
388
+ this.#openState = false;
389
+ const menu = this.hasMenuTarget ? this.menuTarget : null;
390
+ this.#menu = menu;
391
+ if (!menu) return;
392
+ this.#menuHidden.write(menu, "");
393
+ if (this.hasTriggerTarget) this.#expanded.write(this.triggerTarget, "false");
394
+ if (was && this.#reporting) this.dispatch("close", { detail: { reason }, cancelable: false });
395
+ }
396
+ #adoptMenu() {
397
+ if (!this.#connected) return;
398
+ const menu = this.hasMenuTarget ? this.menuTarget : null;
399
+ const previous = this.#menu;
400
+ this.#menu = menu;
401
+ if (!menu) {
402
+ this.#escapeLayer.deactivate();
403
+ this.#openState = false;
404
+ } else if (menu !== previous) {
405
+ this.#menuHidden.write(menu, previous?.hidden ?? true ? "" : null);
162
406
  }
407
+ this.#reflectTrigger();
408
+ }
409
+ #reflectTrigger() {
410
+ if (!this.hasTriggerTarget) return;
411
+ this.#expanded.write(this.triggerTarget, String(this.#isOpen));
412
+ this.#associateTriggerWithMenu();
413
+ }
414
+ #repair() {
415
+ if (!this.hasMenuTarget) return;
416
+ this.#menuHidden.write(this.menuTarget, this.#openState ? null : "");
417
+ this.#reflectTrigger();
163
418
  }
164
- /** Closes and restores focus to the trigger (the keyboard-dismissal path). */
165
419
  #closeAndRestore() {
166
- this.close();
420
+ this.#close("escape");
167
421
  if (this.hasTriggerTarget) this.triggerTarget.focus();
168
422
  }
169
- /** Whether the menu is currently visible. */
170
423
  get #isOpen() {
171
424
  return this.hasMenuTarget && !this.menuTarget.hidden;
172
425
  }
173
- /**
174
- * Associates the disclosure trigger and controlled region without clobbering
175
- * authored markup.
176
- */
177
426
  #associateTriggerWithMenu() {
178
427
  if (!this.hasTriggerTarget || !this.hasMenuTarget) return;
179
- if (this.triggerTarget.hasAttribute("aria-controls")) return;
180
- this.triggerTarget.setAttribute(
181
- "aria-controls",
182
- ensureId(this.menuTarget, "stimeo--dropdown-menu")
183
- );
428
+ const trigger = this.triggerTarget;
429
+ const controls = trigger.getAttribute("aria-controls");
430
+ if (controls !== null && controls !== this.#suppliedControls.get(trigger)) return;
431
+ const id = ensureId(this.menuTarget, "stimeo--dropdown-menu");
432
+ if (controls === id) return;
433
+ trigger.setAttribute("aria-controls", id);
434
+ this.#suppliedControls.set(trigger, id);
435
+ }
436
+ #withdrawControls(trigger) {
437
+ const supplied = this.#suppliedControls.get(trigger);
438
+ this.#suppliedControls.delete(trigger);
439
+ if (supplied !== void 0 && trigger.getAttribute("aria-controls") === supplied) {
440
+ trigger.removeAttribute("aria-controls");
441
+ }
184
442
  }
185
443
  };
186
444
 
187
445
  export { DropdownController };
188
- //# sourceMappingURL=dropdown_controller.js.map
189
446
  //# sourceMappingURL=dropdown_controller.js.map