stimeo-ui 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (328) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +8 -1
  3. package/dist/cable/index.d.ts +79 -23
  4. package/dist/cable/index.js +363 -436
  5. package/dist/cable/index.js.map +1 -1
  6. package/dist/controllers/accordion_controller.d.ts +4 -2
  7. package/dist/controllers/accordion_controller.js +166 -55
  8. package/dist/controllers/accordion_controller.js.map +1 -1
  9. package/dist/controllers/alert_dialog_controller.d.ts +12 -2
  10. package/dist/controllers/alert_dialog_controller.js +862 -221
  11. package/dist/controllers/alert_dialog_controller.js.map +1 -1
  12. package/dist/controllers/announcer_controller.d.ts +35 -5
  13. package/dist/controllers/announcer_controller.js +135 -198
  14. package/dist/controllers/announcer_controller.js.map +1 -1
  15. package/dist/controllers/aspect_ratio_controller.js +0 -10
  16. package/dist/controllers/aspect_ratio_controller.js.map +1 -1
  17. package/dist/controllers/auto_submit_controller.d.ts +30 -6
  18. package/dist/controllers/auto_submit_controller.js +271 -127
  19. package/dist/controllers/auto_submit_controller.js.map +1 -1
  20. package/dist/controllers/avatar_controller.js +171 -105
  21. package/dist/controllers/avatar_controller.js.map +1 -1
  22. package/dist/controllers/breadcrumb_controller.js +0 -130
  23. package/dist/controllers/breadcrumb_controller.js.map +1 -1
  24. package/dist/controllers/bulk_select_controller.d.ts +18 -0
  25. package/dist/controllers/bulk_select_controller.js +262 -52
  26. package/dist/controllers/bulk_select_controller.js.map +1 -1
  27. package/dist/controllers/calendar_controller.d.ts +24 -5
  28. package/dist/controllers/calendar_controller.js +175 -209
  29. package/dist/controllers/calendar_controller.js.map +1 -1
  30. package/dist/controllers/carousel_controller.d.ts +34 -7
  31. package/dist/controllers/carousel_controller.js +461 -399
  32. package/dist/controllers/carousel_controller.js.map +1 -1
  33. package/dist/controllers/character_counter_controller.d.ts +17 -1
  34. package/dist/controllers/character_counter_controller.js +246 -156
  35. package/dist/controllers/character_counter_controller.js.map +1 -1
  36. package/dist/controllers/checkbox_controller.d.ts +7 -2
  37. package/dist/controllers/checkbox_controller.js +32 -74
  38. package/dist/controllers/checkbox_controller.js.map +1 -1
  39. package/dist/controllers/clipboard_controller.d.ts +16 -5
  40. package/dist/controllers/clipboard_controller.js +168 -147
  41. package/dist/controllers/clipboard_controller.js.map +1 -1
  42. package/dist/controllers/collapsible_controller.d.ts +16 -4
  43. package/dist/controllers/collapsible_controller.js +301 -120
  44. package/dist/controllers/collapsible_controller.js.map +1 -1
  45. package/dist/controllers/color_picker_controller.d.ts +18 -6
  46. package/dist/controllers/color_picker_controller.js +149 -139
  47. package/dist/controllers/color_picker_controller.js.map +1 -1
  48. package/dist/controllers/combobox_controller.d.ts +23 -5
  49. package/dist/controllers/combobox_controller.js +360 -133
  50. package/dist/controllers/combobox_controller.js.map +1 -1
  51. package/dist/controllers/command_palette_controller.d.ts +45 -14
  52. package/dist/controllers/command_palette_controller.js +970 -358
  53. package/dist/controllers/command_palette_controller.js.map +1 -1
  54. package/dist/controllers/conditional_fields_controller.d.ts +15 -2
  55. package/dist/controllers/conditional_fields_controller.js +166 -124
  56. package/dist/controllers/conditional_fields_controller.js.map +1 -1
  57. package/dist/controllers/confirm_controller.d.ts +49 -12
  58. package/dist/controllers/confirm_controller.js +939 -248
  59. package/dist/controllers/confirm_controller.js.map +1 -1
  60. package/dist/controllers/context_menu_controller.d.ts +31 -4
  61. package/dist/controllers/context_menu_controller.js +256 -128
  62. package/dist/controllers/context_menu_controller.js.map +1 -1
  63. package/dist/controllers/count_up_controller.d.ts +13 -1
  64. package/dist/controllers/count_up_controller.js +73 -21
  65. package/dist/controllers/count_up_controller.js.map +1 -1
  66. package/dist/controllers/countdown_controller.d.ts +45 -1
  67. package/dist/controllers/countdown_controller.js +183 -148
  68. package/dist/controllers/countdown_controller.js.map +1 -1
  69. package/dist/controllers/currency_input_controller.d.ts +21 -2
  70. package/dist/controllers/currency_input_controller.js +134 -158
  71. package/dist/controllers/currency_input_controller.js.map +1 -1
  72. package/dist/controllers/data_grid_controller.d.ts +15 -4
  73. package/dist/controllers/data_grid_controller.js +114 -186
  74. package/dist/controllers/data_grid_controller.js.map +1 -1
  75. package/dist/controllers/date_range_picker_controller.d.ts +30 -5
  76. package/dist/controllers/date_range_picker_controller.js +288 -206
  77. package/dist/controllers/date_range_picker_controller.js.map +1 -1
  78. package/dist/controllers/dialog_controller.d.ts +13 -1
  79. package/dist/controllers/dialog_controller.js +858 -223
  80. package/dist/controllers/dialog_controller.js.map +1 -1
  81. package/dist/controllers/direct_upload_controller.d.ts +24 -7
  82. package/dist/controllers/direct_upload_controller.js +73 -153
  83. package/dist/controllers/direct_upload_controller.js.map +1 -1
  84. package/dist/controllers/dirty_form_controller.js +14 -48
  85. package/dist/controllers/dirty_form_controller.js.map +1 -1
  86. package/dist/controllers/dismissible_controller.d.ts +19 -0
  87. package/dist/controllers/dismissible_controller.js +175 -21
  88. package/dist/controllers/dismissible_controller.js.map +1 -1
  89. package/dist/controllers/drawer_controller.d.ts +39 -11
  90. package/dist/controllers/drawer_controller.js +933 -341
  91. package/dist/controllers/drawer_controller.js.map +1 -1
  92. package/dist/controllers/dropdown_controller.d.ts +31 -1
  93. package/dist/controllers/dropdown_controller.js +312 -82
  94. package/dist/controllers/dropdown_controller.js.map +1 -1
  95. package/dist/controllers/editable_controller.d.ts +12 -1
  96. package/dist/controllers/editable_controller.js +158 -68
  97. package/dist/controllers/editable_controller.js.map +1 -1
  98. package/dist/controllers/empty_state_controller.js +67 -53
  99. package/dist/controllers/empty_state_controller.js.map +1 -1
  100. package/dist/controllers/file_dropzone_controller.d.ts +60 -18
  101. package/dist/controllers/file_dropzone_controller.js +311 -207
  102. package/dist/controllers/file_dropzone_controller.js.map +1 -1
  103. package/dist/controllers/filter_controller.js +67 -39
  104. package/dist/controllers/filter_controller.js.map +1 -1
  105. package/dist/controllers/flash_controller.d.ts +31 -8
  106. package/dist/controllers/flash_controller.js +172 -314
  107. package/dist/controllers/flash_controller.js.map +1 -1
  108. package/dist/controllers/focus_controller.d.ts +12 -3
  109. package/dist/controllers/focus_controller.js +654 -240
  110. package/dist/controllers/focus_controller.js.map +1 -1
  111. package/dist/controllers/form_field_controller.js +147 -132
  112. package/dist/controllers/form_field_controller.js.map +1 -1
  113. package/dist/controllers/form_validation_controller.js +13 -86
  114. package/dist/controllers/form_validation_controller.js.map +1 -1
  115. package/dist/controllers/frame_loading_controller.d.ts +41 -9
  116. package/dist/controllers/frame_loading_controller.js +264 -213
  117. package/dist/controllers/frame_loading_controller.js.map +1 -1
  118. package/dist/controllers/highlight_controller.d.ts +22 -2
  119. package/dist/controllers/highlight_controller.js +107 -75
  120. package/dist/controllers/highlight_controller.js.map +1 -1
  121. package/dist/controllers/hover_card_controller.d.ts +56 -3
  122. package/dist/controllers/hover_card_controller.js +309 -118
  123. package/dist/controllers/hover_card_controller.js.map +1 -1
  124. package/dist/controllers/idle_controller.d.ts +42 -1
  125. package/dist/controllers/idle_controller.js +267 -101
  126. package/dist/controllers/idle_controller.js.map +1 -1
  127. package/dist/controllers/input_mask_controller.js +71 -72
  128. package/dist/controllers/input_mask_controller.js.map +1 -1
  129. package/dist/controllers/intersection_controller.d.ts +32 -13
  130. package/dist/controllers/intersection_controller.js +147 -106
  131. package/dist/controllers/intersection_controller.js.map +1 -1
  132. package/dist/controllers/lazy_frame_controller.d.ts +16 -0
  133. package/dist/controllers/lazy_frame_controller.js +9 -65
  134. package/dist/controllers/lazy_frame_controller.js.map +1 -1
  135. package/dist/controllers/listbox_controller.d.ts +34 -2
  136. package/dist/controllers/listbox_controller.js +306 -204
  137. package/dist/controllers/listbox_controller.js.map +1 -1
  138. package/dist/controllers/local_time_controller.d.ts +1 -1
  139. package/dist/controllers/local_time_controller.js +54 -64
  140. package/dist/controllers/local_time_controller.js.map +1 -1
  141. package/dist/controllers/masonry_controller.d.ts +11 -3
  142. package/dist/controllers/masonry_controller.js +70 -93
  143. package/dist/controllers/masonry_controller.js.map +1 -1
  144. package/dist/controllers/menu_controller.d.ts +32 -7
  145. package/dist/controllers/menu_controller.js +214 -156
  146. package/dist/controllers/menu_controller.js.map +1 -1
  147. package/dist/controllers/menubar_controller.d.ts +4 -4
  148. package/dist/controllers/menubar_controller.js +170 -329
  149. package/dist/controllers/menubar_controller.js.map +1 -1
  150. package/dist/controllers/meter_controller.d.ts +20 -0
  151. package/dist/controllers/meter_controller.js +151 -67
  152. package/dist/controllers/meter_controller.js.map +1 -1
  153. package/dist/controllers/multi_select_controller.d.ts +32 -4
  154. package/dist/controllers/multi_select_controller.js +593 -358
  155. package/dist/controllers/multi_select_controller.js.map +1 -1
  156. package/dist/controllers/navigation_menu_controller.d.ts +8 -1
  157. package/dist/controllers/navigation_menu_controller.js +124 -232
  158. package/dist/controllers/navigation_menu_controller.js.map +1 -1
  159. package/dist/controllers/nested_form_controller.d.ts +10 -0
  160. package/dist/controllers/nested_form_controller.js +352 -182
  161. package/dist/controllers/nested_form_controller.js.map +1 -1
  162. package/dist/controllers/network_status_controller.d.ts +23 -0
  163. package/dist/controllers/network_status_controller.js +242 -54
  164. package/dist/controllers/network_status_controller.js.map +1 -1
  165. package/dist/controllers/number_input_controller.d.ts +26 -2
  166. package/dist/controllers/number_input_controller.js +320 -263
  167. package/dist/controllers/number_input_controller.js.map +1 -1
  168. package/dist/controllers/optimistic_controller.js +16 -76
  169. package/dist/controllers/optimistic_controller.js.map +1 -1
  170. package/dist/controllers/otp_controller.d.ts +20 -4
  171. package/dist/controllers/otp_controller.js +198 -193
  172. package/dist/controllers/otp_controller.js.map +1 -1
  173. package/dist/controllers/overflow_indicator_controller.d.ts +5 -0
  174. package/dist/controllers/overflow_indicator_controller.js +116 -113
  175. package/dist/controllers/overflow_indicator_controller.js.map +1 -1
  176. package/dist/controllers/overflow_menu_controller.d.ts +48 -11
  177. package/dist/controllers/overflow_menu_controller.js +343 -283
  178. package/dist/controllers/overflow_menu_controller.js.map +1 -1
  179. package/dist/controllers/pagination_controller.d.ts +35 -6
  180. package/dist/controllers/pagination_controller.js +295 -119
  181. package/dist/controllers/pagination_controller.js.map +1 -1
  182. package/dist/controllers/password_reveal_controller.d.ts +45 -8
  183. package/dist/controllers/password_reveal_controller.js +389 -121
  184. package/dist/controllers/password_reveal_controller.js.map +1 -1
  185. package/dist/controllers/password_strength_controller.d.ts +14 -5
  186. package/dist/controllers/password_strength_controller.js +99 -153
  187. package/dist/controllers/password_strength_controller.js.map +1 -1
  188. package/dist/controllers/persist_controller.d.ts +11 -4
  189. package/dist/controllers/persist_controller.js +237 -103
  190. package/dist/controllers/persist_controller.js.map +1 -1
  191. package/dist/controllers/pointer_drag_controller.d.ts +19 -7
  192. package/dist/controllers/pointer_drag_controller.js +185 -165
  193. package/dist/controllers/pointer_drag_controller.js.map +1 -1
  194. package/dist/controllers/popover_controller.d.ts +38 -2
  195. package/dist/controllers/popover_controller.js +236 -97
  196. package/dist/controllers/popover_controller.js.map +1 -1
  197. package/dist/controllers/portal_controller.d.ts +44 -4
  198. package/dist/controllers/portal_controller.js +209 -76
  199. package/dist/controllers/portal_controller.js.map +1 -1
  200. package/dist/controllers/preview_guard_controller.d.ts +10 -4
  201. package/dist/controllers/preview_guard_controller.js +232 -116
  202. package/dist/controllers/preview_guard_controller.js.map +1 -1
  203. package/dist/controllers/progress_controller.d.ts +11 -0
  204. package/dist/controllers/progress_controller.js +125 -56
  205. package/dist/controllers/progress_controller.js.map +1 -1
  206. package/dist/controllers/radio_group_controller.d.ts +14 -5
  207. package/dist/controllers/radio_group_controller.js +117 -137
  208. package/dist/controllers/radio_group_controller.js.map +1 -1
  209. package/dist/controllers/range_slider_controller.d.ts +43 -3
  210. package/dist/controllers/range_slider_controller.js +192 -105
  211. package/dist/controllers/range_slider_controller.js.map +1 -1
  212. package/dist/controllers/rating_controller.d.ts +20 -4
  213. package/dist/controllers/rating_controller.js +418 -207
  214. package/dist/controllers/rating_controller.js.map +1 -1
  215. package/dist/controllers/read_more_controller.d.ts +2 -0
  216. package/dist/controllers/read_more_controller.js +134 -63
  217. package/dist/controllers/read_more_controller.js.map +1 -1
  218. package/dist/controllers/reading_progress_controller.d.ts +1 -1
  219. package/dist/controllers/reading_progress_controller.js +147 -135
  220. package/dist/controllers/reading_progress_controller.js.map +1 -1
  221. package/dist/controllers/relative_time_controller.d.ts +18 -4
  222. package/dist/controllers/relative_time_controller.js +145 -110
  223. package/dist/controllers/relative_time_controller.js.map +1 -1
  224. package/dist/controllers/reset_on_restore_controller.d.ts +85 -0
  225. package/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +97 -38
  226. package/dist/controllers/reset_on_restore_controller.js.map +1 -0
  227. package/dist/controllers/resizable_controller.d.ts +28 -2
  228. package/dist/controllers/resizable_controller.js +255 -139
  229. package/dist/controllers/resizable_controller.js.map +1 -1
  230. package/dist/controllers/roving_controller.js +39 -84
  231. package/dist/controllers/roving_controller.js.map +1 -1
  232. package/dist/controllers/scroll_area_controller.d.ts +7 -3
  233. package/dist/controllers/scroll_area_controller.js +428 -225
  234. package/dist/controllers/scroll_area_controller.js.map +1 -1
  235. package/dist/controllers/scroll_restore_controller.js +0 -53
  236. package/dist/controllers/scroll_restore_controller.js.map +1 -1
  237. package/dist/controllers/scroll_visibility_controller.d.ts +28 -12
  238. package/dist/controllers/scroll_visibility_controller.js +395 -145
  239. package/dist/controllers/scroll_visibility_controller.js.map +1 -1
  240. package/dist/controllers/scrollspy_controller.d.ts +6 -1
  241. package/dist/controllers/scrollspy_controller.js +181 -242
  242. package/dist/controllers/scrollspy_controller.js.map +1 -1
  243. package/dist/controllers/separator_controller.d.ts +19 -4
  244. package/dist/controllers/separator_controller.js +267 -140
  245. package/dist/controllers/separator_controller.js.map +1 -1
  246. package/dist/controllers/sidebar_controller.d.ts +64 -7
  247. package/dist/controllers/sidebar_controller.js +1023 -386
  248. package/dist/controllers/sidebar_controller.js.map +1 -1
  249. package/dist/controllers/skeleton_controller.d.ts +23 -0
  250. package/dist/controllers/skeleton_controller.js +248 -97
  251. package/dist/controllers/skeleton_controller.js.map +1 -1
  252. package/dist/controllers/slider_controller.d.ts +27 -1
  253. package/dist/controllers/slider_controller.js +159 -79
  254. package/dist/controllers/slider_controller.js.map +1 -1
  255. package/dist/controllers/smart_sticky_header_controller.d.ts +21 -3
  256. package/dist/controllers/smart_sticky_header_controller.js +162 -63
  257. package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
  258. package/dist/controllers/sortable_controller.js +0 -112
  259. package/dist/controllers/sortable_controller.js.map +1 -1
  260. package/dist/controllers/spinner_controller.d.ts +39 -4
  261. package/dist/controllers/spinner_controller.js +277 -169
  262. package/dist/controllers/spinner_controller.js.map +1 -1
  263. package/dist/controllers/step_indicator_controller.d.ts +5 -0
  264. package/dist/controllers/step_indicator_controller.js +99 -46
  265. package/dist/controllers/step_indicator_controller.js.map +1 -1
  266. package/dist/controllers/stepper_controller.d.ts +12 -3
  267. package/dist/controllers/stepper_controller.js +114 -73
  268. package/dist/controllers/stepper_controller.js.map +1 -1
  269. package/dist/controllers/stick_to_bottom_controller.d.ts +6 -0
  270. package/dist/controllers/stick_to_bottom_controller.js +262 -138
  271. package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
  272. package/dist/controllers/sticky_observer_controller.d.ts +11 -0
  273. package/dist/controllers/sticky_observer_controller.js +214 -49
  274. package/dist/controllers/sticky_observer_controller.js.map +1 -1
  275. package/dist/controllers/submit_once_controller.d.ts +23 -8
  276. package/dist/controllers/submit_once_controller.js +294 -205
  277. package/dist/controllers/submit_once_controller.js.map +1 -1
  278. package/dist/controllers/switch_controller.d.ts +12 -1
  279. package/dist/controllers/switch_controller.js +33 -60
  280. package/dist/controllers/switch_controller.js.map +1 -1
  281. package/dist/controllers/tabs_controller.d.ts +2 -2
  282. package/dist/controllers/tabs_controller.js +35 -29
  283. package/dist/controllers/tabs_controller.js.map +1 -1
  284. package/dist/controllers/tags_input_controller.d.ts +12 -0
  285. package/dist/controllers/tags_input_controller.js +136 -145
  286. package/dist/controllers/tags_input_controller.js.map +1 -1
  287. package/dist/controllers/textarea_autosize_controller.d.ts +10 -0
  288. package/dist/controllers/textarea_autosize_controller.js +71 -58
  289. package/dist/controllers/textarea_autosize_controller.js.map +1 -1
  290. package/dist/controllers/theme_controller.d.ts +21 -9
  291. package/dist/controllers/theme_controller.js +130 -112
  292. package/dist/controllers/theme_controller.js.map +1 -1
  293. package/dist/controllers/time_picker_controller.d.ts +18 -1
  294. package/dist/controllers/time_picker_controller.js +146 -80
  295. package/dist/controllers/time_picker_controller.js.map +1 -1
  296. package/dist/controllers/toast_controller.d.ts +36 -11
  297. package/dist/controllers/toast_controller.js +186 -297
  298. package/dist/controllers/toast_controller.js.map +1 -1
  299. package/dist/controllers/toggle_group_controller.d.ts +12 -3
  300. package/dist/controllers/toggle_group_controller.js +95 -136
  301. package/dist/controllers/toggle_group_controller.js.map +1 -1
  302. package/dist/controllers/toolbar_controller.js +68 -98
  303. package/dist/controllers/toolbar_controller.js.map +1 -1
  304. package/dist/controllers/tooltip_controller.d.ts +45 -3
  305. package/dist/controllers/tooltip_controller.js +158 -124
  306. package/dist/controllers/tooltip_controller.js.map +1 -1
  307. package/dist/controllers/transition_controller.d.ts +22 -4
  308. package/dist/controllers/transition_controller.js +123 -119
  309. package/dist/controllers/transition_controller.js.map +1 -1
  310. package/dist/controllers/tree_view_controller.d.ts +21 -11
  311. package/dist/controllers/tree_view_controller.js +150 -345
  312. package/dist/controllers/tree_view_controller.js.map +1 -1
  313. package/dist/index.d.ts +8 -5
  314. package/dist/index.js +6370 -9346
  315. package/dist/index.js.map +1 -1
  316. package/dist/inspector/cli.d.ts +23 -3
  317. package/dist/inspector/cli.js +92 -37
  318. package/dist/inspector/cli.js.map +1 -1
  319. package/dist/inspector/cli_bin.js +92 -56
  320. package/dist/inspector/cli_bin.js.map +1 -1
  321. package/dist/inspector/examples.json +8 -8
  322. package/dist/inspector/manifest.json +787 -100
  323. package/dist/positioning/index.d.ts +8 -0
  324. package/dist/positioning/index.js +148 -47
  325. package/dist/positioning/index.js.map +1 -1
  326. package/package.json +6 -6
  327. package/dist/controllers/reset_before_cache_controller.d.ts +0 -76
  328. package/dist/controllers/reset_before_cache_controller.js.map +0 -1
@@ -27,11 +27,22 @@ 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.
30
40
  * - Each move of the open state is reported: `stimeo--dropdown:open` and
31
41
  * `stimeo--dropdown:close` dispatch `{ reason: StateReason }`, after the
32
42
  * attributes above are written. Both are informational, so neither is
33
43
  * cancelable. A call that leaves the state where it already was, the
34
- * normalization in {@link connect}, and {@link disconnect} are all silent.
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.
35
46
  */
36
47
  declare class DropdownController extends Controller<HTMLElement> {
37
48
  #private;
@@ -39,13 +50,32 @@ declare class DropdownController extends Controller<HTMLElement> {
39
50
  static actions: readonly ["close", "open", "toggle"];
40
51
  static events: readonly ["close", "open"];
41
52
  readonly triggerTarget: HTMLButtonElement;
53
+ readonly triggerTargets: HTMLButtonElement[];
42
54
  readonly menuTarget: HTMLElement;
55
+ readonly menuTargets: HTMLElement[];
43
56
  readonly hasMenuTarget: boolean;
44
57
  readonly hasTriggerTarget: boolean;
45
58
  /** Starts in the closed state and registers outside-click handling. */
46
59
  connect(): void;
47
60
  /** Removes the listeners registered in {@link connect}. */
48
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;
49
79
  /** Toggles the menu between open and closed. Bound via `data-action`. */
50
80
  toggle(event?: Event): void;
51
81
  /** Reveals the menu and reflects the open state on the trigger. */
@@ -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,24 +216,86 @@ 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/utils/state_reason.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
+
113
299
  var FOCUS_EVENTS = /* @__PURE__ */ new Set(["blur", "focus", "focusin", "focusout"]);
114
300
  var POINTER_EVENTS = /* @__PURE__ */ new Set(["mouseenter", "mouseleave", "pointerenter", "pointerleave"]);
115
301
  function stateReasonFor(event) {
@@ -119,35 +305,56 @@ function stateReasonFor(event) {
119
305
  return "user";
120
306
  }
121
307
 
122
- // src/controllers/dropdown_controller.ts
123
308
  var DropdownController = class extends Controller {
124
309
  static targets = ["trigger", "menu"];
125
310
  static actions = ["close", "open", "toggle"];
126
311
  static events = ["close", "open"];
127
- /** Escape-stack membership while open; the shared resolver dismisses via it. */
128
312
  #escapeLayer = new EscapeLayer();
129
- /** Whether state moves are reported: set once `connect()` settled the baseline. */
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;
130
318
  #reporting = false;
131
- /** Closes the menu when a click lands outside the controller's element. */
319
+ #openState = false;
320
+ #morphRender = new MorphRenderWatcher(() => this.#repair());
132
321
  #onOutsideClick = (event) => {
133
322
  if (!this.element.contains(event.target)) {
134
323
  this.#close("outside");
135
324
  }
136
325
  };
137
- /** Starts in the closed state and registers outside-click handling. */
138
326
  connect() {
139
327
  this.#associateTriggerWithMenu();
140
328
  this.#close("api");
141
329
  document.addEventListener("click", this.#onOutsideClick, true);
330
+ this.#morphRender.observe(this.element);
331
+ this.#connected = true;
142
332
  this.#reporting = true;
143
333
  }
144
- /** Removes the listeners registered in {@link connect}. */
145
334
  disconnect() {
335
+ this.#connected = false;
146
336
  this.#reporting = false;
147
337
  this.#escapeLayer.deactivate();
148
338
  document.removeEventListener("click", this.#onOutsideClick, true);
339
+ this.#morphRender.disconnect();
340
+ }
341
+ triggerTargetConnected() {
342
+ if (this.#connected) this.#reflectTrigger();
343
+ }
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);
149
357
  }
150
- /** Toggles the menu between open and closed. Bound via `data-action`. */
151
358
  toggle(event) {
152
359
  if (this.#isOpen) {
153
360
  this.close(event);
@@ -155,62 +362,85 @@ var DropdownController = class extends Controller {
155
362
  this.open(event);
156
363
  }
157
364
  }
158
- /** Reveals the menu and reflects the open state on the trigger. */
159
365
  open(event) {
160
366
  this.#open(stateReasonFor(event));
161
367
  }
162
- /** Hides the menu and reflects the closed state on the trigger. */
163
368
  close(event) {
164
369
  this.#close(stateReasonFor(event));
165
370
  }
166
- /** Reveals the menu, reflects the open state, and reports a move. */
167
371
  #open(reason) {
168
372
  if (!this.hasMenuTarget) return;
373
+ const menu = this.menuTarget;
169
374
  const was = this.#isOpen;
170
375
  this.#escapeLayer.activate(document, {
171
376
  onDismiss: () => this.#closeAndRestore(),
172
377
  claims: claimsWhileFocusWithin(this.element)
173
378
  });
174
- this.menuTarget.hidden = false;
175
- if (this.hasTriggerTarget) {
176
- this.triggerTarget.setAttribute("aria-expanded", "true");
177
- }
379
+ this.#menuHidden.write(menu, null);
380
+ this.#menu = menu;
381
+ this.#openState = true;
382
+ if (this.hasTriggerTarget) this.#expanded.write(this.triggerTarget, "true");
178
383
  if (!was && this.#reporting) this.dispatch("open", { detail: { reason }, cancelable: false });
179
384
  }
180
- /** Hides the menu, reflects the closed state, and reports a move. */
181
385
  #close(reason) {
182
386
  const was = this.#isOpen;
183
387
  this.#escapeLayer.deactivate();
184
- if (!this.hasMenuTarget) return;
185
- this.menuTarget.hidden = true;
186
- if (this.hasTriggerTarget) {
187
- this.triggerTarget.setAttribute("aria-expanded", "false");
188
- }
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");
189
394
  if (was && this.#reporting) this.dispatch("close", { detail: { reason }, cancelable: false });
190
395
  }
191
- /** Closes and restores focus to the trigger (the keyboard-dismissal path). */
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);
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();
418
+ }
192
419
  #closeAndRestore() {
193
420
  this.#close("escape");
194
421
  if (this.hasTriggerTarget) this.triggerTarget.focus();
195
422
  }
196
- /** Whether the menu is currently visible. */
197
423
  get #isOpen() {
198
424
  return this.hasMenuTarget && !this.menuTarget.hidden;
199
425
  }
200
- /**
201
- * Associates the disclosure trigger and controlled region without clobbering
202
- * authored markup.
203
- */
204
426
  #associateTriggerWithMenu() {
205
427
  if (!this.hasTriggerTarget || !this.hasMenuTarget) return;
206
- if (this.triggerTarget.hasAttribute("aria-controls")) return;
207
- this.triggerTarget.setAttribute(
208
- "aria-controls",
209
- ensureId(this.menuTarget, "stimeo--dropdown-menu")
210
- );
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
+ }
211
442
  }
212
443
  };
213
444
 
214
445
  export { DropdownController };
215
- //# sourceMappingURL=dropdown_controller.js.map
216
446
  //# sourceMappingURL=dropdown_controller.js.map