stimeo-ui 0.1.0-alpha.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 (238) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +72 -0
  3. package/dist/controllers/accordion_controller.d.ts +59 -0
  4. package/dist/controllers/accordion_controller.js +76 -0
  5. package/dist/controllers/accordion_controller.js.map +1 -0
  6. package/dist/controllers/announcer_controller.d.ts +78 -0
  7. package/dist/controllers/announcer_controller.js +184 -0
  8. package/dist/controllers/announcer_controller.js.map +1 -0
  9. package/dist/controllers/aspect_ratio_controller.d.ts +39 -0
  10. package/dist/controllers/aspect_ratio_controller.js +36 -0
  11. package/dist/controllers/aspect_ratio_controller.js.map +1 -0
  12. package/dist/controllers/auto_submit_controller.d.ts +74 -0
  13. package/dist/controllers/auto_submit_controller.js +147 -0
  14. package/dist/controllers/auto_submit_controller.js.map +1 -0
  15. package/dist/controllers/avatar_controller.d.ts +50 -0
  16. package/dist/controllers/avatar_controller.js +66 -0
  17. package/dist/controllers/avatar_controller.js.map +1 -0
  18. package/dist/controllers/breadcrumb_controller.d.ts +60 -0
  19. package/dist/controllers/breadcrumb_controller.js +123 -0
  20. package/dist/controllers/breadcrumb_controller.js.map +1 -0
  21. package/dist/controllers/bulk_select_controller.d.ts +67 -0
  22. package/dist/controllers/bulk_select_controller.js +104 -0
  23. package/dist/controllers/bulk_select_controller.js.map +1 -0
  24. package/dist/controllers/calendar_controller.d.ts +100 -0
  25. package/dist/controllers/calendar_controller.js +394 -0
  26. package/dist/controllers/calendar_controller.js.map +1 -0
  27. package/dist/controllers/character_counter_controller.d.ts +63 -0
  28. package/dist/controllers/character_counter_controller.js +179 -0
  29. package/dist/controllers/character_counter_controller.js.map +1 -0
  30. package/dist/controllers/checkbox_controller.d.ts +52 -0
  31. package/dist/controllers/checkbox_controller.js +73 -0
  32. package/dist/controllers/checkbox_controller.js.map +1 -0
  33. package/dist/controllers/combobox_controller.d.ts +76 -0
  34. package/dist/controllers/combobox_controller.js +186 -0
  35. package/dist/controllers/combobox_controller.js.map +1 -0
  36. package/dist/controllers/command_palette_controller.d.ts +103 -0
  37. package/dist/controllers/command_palette_controller.js +381 -0
  38. package/dist/controllers/command_palette_controller.js.map +1 -0
  39. package/dist/controllers/conditional_fields_controller.d.ts +56 -0
  40. package/dist/controllers/conditional_fields_controller.js +112 -0
  41. package/dist/controllers/conditional_fields_controller.js.map +1 -0
  42. package/dist/controllers/confirm_controller.d.ts +83 -0
  43. package/dist/controllers/confirm_controller.js +276 -0
  44. package/dist/controllers/confirm_controller.js.map +1 -0
  45. package/dist/controllers/context_menu_controller.d.ts +81 -0
  46. package/dist/controllers/context_menu_controller.js +112 -0
  47. package/dist/controllers/context_menu_controller.js.map +1 -0
  48. package/dist/controllers/countdown_controller.d.ts +93 -0
  49. package/dist/controllers/countdown_controller.js +202 -0
  50. package/dist/controllers/countdown_controller.js.map +1 -0
  51. package/dist/controllers/dialog_controller.d.ts +57 -0
  52. package/dist/controllers/dialog_controller.js +207 -0
  53. package/dist/controllers/dialog_controller.js.map +1 -0
  54. package/dist/controllers/direct_upload_controller.d.ts +77 -0
  55. package/dist/controllers/direct_upload_controller.js +212 -0
  56. package/dist/controllers/direct_upload_controller.js.map +1 -0
  57. package/dist/controllers/dirty_form_controller.d.ts +52 -0
  58. package/dist/controllers/dirty_form_controller.js +128 -0
  59. package/dist/controllers/dirty_form_controller.js.map +1 -0
  60. package/dist/controllers/dropdown_controller.d.ts +49 -0
  61. package/dist/controllers/dropdown_controller.js +66 -0
  62. package/dist/controllers/dropdown_controller.js.map +1 -0
  63. package/dist/controllers/empty_state_controller.d.ts +60 -0
  64. package/dist/controllers/empty_state_controller.js +67 -0
  65. package/dist/controllers/empty_state_controller.js.map +1 -0
  66. package/dist/controllers/flash_controller.d.ts +63 -0
  67. package/dist/controllers/flash_controller.js +221 -0
  68. package/dist/controllers/flash_controller.js.map +1 -0
  69. package/dist/controllers/focus_controller.d.ts +69 -0
  70. package/dist/controllers/focus_controller.js +216 -0
  71. package/dist/controllers/focus_controller.js.map +1 -0
  72. package/dist/controllers/form_field_controller.d.ts +79 -0
  73. package/dist/controllers/form_field_controller.js +154 -0
  74. package/dist/controllers/form_field_controller.js.map +1 -0
  75. package/dist/controllers/form_validation_controller.d.ts +114 -0
  76. package/dist/controllers/form_validation_controller.js +202 -0
  77. package/dist/controllers/form_validation_controller.js.map +1 -0
  78. package/dist/controllers/frame_loading_controller.d.ts +58 -0
  79. package/dist/controllers/frame_loading_controller.js +177 -0
  80. package/dist/controllers/frame_loading_controller.js.map +1 -0
  81. package/dist/controllers/highlight_controller.d.ts +50 -0
  82. package/dist/controllers/highlight_controller.js +107 -0
  83. package/dist/controllers/highlight_controller.js.map +1 -0
  84. package/dist/controllers/hover_card_controller.d.ts +79 -0
  85. package/dist/controllers/hover_card_controller.js +165 -0
  86. package/dist/controllers/hover_card_controller.js.map +1 -0
  87. package/dist/controllers/idle_controller.d.ts +51 -0
  88. package/dist/controllers/idle_controller.js +141 -0
  89. package/dist/controllers/idle_controller.js.map +1 -0
  90. package/dist/controllers/input_mask_controller.d.ts +74 -0
  91. package/dist/controllers/input_mask_controller.js +166 -0
  92. package/dist/controllers/input_mask_controller.js.map +1 -0
  93. package/dist/controllers/lazy_frame_controller.d.ts +53 -0
  94. package/dist/controllers/lazy_frame_controller.js +68 -0
  95. package/dist/controllers/lazy_frame_controller.js.map +1 -0
  96. package/dist/controllers/listbox_controller.d.ts +88 -0
  97. package/dist/controllers/listbox_controller.js +256 -0
  98. package/dist/controllers/listbox_controller.js.map +1 -0
  99. package/dist/controllers/local_time_controller.d.ts +60 -0
  100. package/dist/controllers/local_time_controller.js +81 -0
  101. package/dist/controllers/local_time_controller.js.map +1 -0
  102. package/dist/controllers/menu_controller.d.ts +81 -0
  103. package/dist/controllers/menu_controller.js +134 -0
  104. package/dist/controllers/menu_controller.js.map +1 -0
  105. package/dist/controllers/meter_controller.d.ts +93 -0
  106. package/dist/controllers/meter_controller.js +96 -0
  107. package/dist/controllers/meter_controller.js.map +1 -0
  108. package/dist/controllers/nested_form_controller.d.ts +85 -0
  109. package/dist/controllers/nested_form_controller.js +131 -0
  110. package/dist/controllers/nested_form_controller.js.map +1 -0
  111. package/dist/controllers/network_status_controller.d.ts +49 -0
  112. package/dist/controllers/network_status_controller.js +126 -0
  113. package/dist/controllers/network_status_controller.js.map +1 -0
  114. package/dist/controllers/number_input_controller.d.ts +93 -0
  115. package/dist/controllers/number_input_controller.js +306 -0
  116. package/dist/controllers/number_input_controller.js.map +1 -0
  117. package/dist/controllers/otp_controller.d.ts +68 -0
  118. package/dist/controllers/otp_controller.js +201 -0
  119. package/dist/controllers/otp_controller.js.map +1 -0
  120. package/dist/controllers/overflow_indicator_controller.d.ts +62 -0
  121. package/dist/controllers/overflow_indicator_controller.js +169 -0
  122. package/dist/controllers/overflow_indicator_controller.js.map +1 -0
  123. package/dist/controllers/overflow_menu_controller.d.ts +64 -0
  124. package/dist/controllers/overflow_menu_controller.js +274 -0
  125. package/dist/controllers/overflow_menu_controller.js.map +1 -0
  126. package/dist/controllers/pagination_controller.d.ts +69 -0
  127. package/dist/controllers/pagination_controller.js +89 -0
  128. package/dist/controllers/pagination_controller.js.map +1 -0
  129. package/dist/controllers/password_strength_controller.d.ts +67 -0
  130. package/dist/controllers/password_strength_controller.js +175 -0
  131. package/dist/controllers/password_strength_controller.js.map +1 -0
  132. package/dist/controllers/persist_controller.d.ts +63 -0
  133. package/dist/controllers/persist_controller.js +259 -0
  134. package/dist/controllers/persist_controller.js.map +1 -0
  135. package/dist/controllers/popover_controller.d.ts +63 -0
  136. package/dist/controllers/popover_controller.js +94 -0
  137. package/dist/controllers/popover_controller.js.map +1 -0
  138. package/dist/controllers/portal_controller.d.ts +58 -0
  139. package/dist/controllers/portal_controller.js +63 -0
  140. package/dist/controllers/portal_controller.js.map +1 -0
  141. package/dist/controllers/preview_guard_controller.d.ts +47 -0
  142. package/dist/controllers/preview_guard_controller.js +69 -0
  143. package/dist/controllers/preview_guard_controller.js.map +1 -0
  144. package/dist/controllers/progress_controller.d.ts +85 -0
  145. package/dist/controllers/progress_controller.js +93 -0
  146. package/dist/controllers/progress_controller.js.map +1 -0
  147. package/dist/controllers/radio_group_controller.d.ts +49 -0
  148. package/dist/controllers/radio_group_controller.js +128 -0
  149. package/dist/controllers/radio_group_controller.js.map +1 -0
  150. package/dist/controllers/rating_controller.d.ts +82 -0
  151. package/dist/controllers/rating_controller.js +179 -0
  152. package/dist/controllers/rating_controller.js.map +1 -0
  153. package/dist/controllers/relative_time_controller.d.ts +48 -0
  154. package/dist/controllers/relative_time_controller.js +129 -0
  155. package/dist/controllers/relative_time_controller.js.map +1 -0
  156. package/dist/controllers/reset_before_cache_controller.d.ts +54 -0
  157. package/dist/controllers/reset_before_cache_controller.js +62 -0
  158. package/dist/controllers/reset_before_cache_controller.js.map +1 -0
  159. package/dist/controllers/resizable_controller.d.ts +81 -0
  160. package/dist/controllers/resizable_controller.js +163 -0
  161. package/dist/controllers/resizable_controller.js.map +1 -0
  162. package/dist/controllers/roving_controller.d.ts +63 -0
  163. package/dist/controllers/roving_controller.js +116 -0
  164. package/dist/controllers/roving_controller.js.map +1 -0
  165. package/dist/controllers/scroll_area_controller.d.ts +45 -0
  166. package/dist/controllers/scroll_area_controller.js +183 -0
  167. package/dist/controllers/scroll_area_controller.js.map +1 -0
  168. package/dist/controllers/scroll_visibility_controller.d.ts +72 -0
  169. package/dist/controllers/scroll_visibility_controller.js +103 -0
  170. package/dist/controllers/scroll_visibility_controller.js.map +1 -0
  171. package/dist/controllers/scrollspy_controller.d.ts +65 -0
  172. package/dist/controllers/scrollspy_controller.js +171 -0
  173. package/dist/controllers/scrollspy_controller.js.map +1 -0
  174. package/dist/controllers/skeleton_controller.d.ts +50 -0
  175. package/dist/controllers/skeleton_controller.js +125 -0
  176. package/dist/controllers/skeleton_controller.js.map +1 -0
  177. package/dist/controllers/slider_controller.d.ts +76 -0
  178. package/dist/controllers/slider_controller.js +109 -0
  179. package/dist/controllers/slider_controller.js.map +1 -0
  180. package/dist/controllers/spinner_controller.d.ts +62 -0
  181. package/dist/controllers/spinner_controller.js +164 -0
  182. package/dist/controllers/spinner_controller.js.map +1 -0
  183. package/dist/controllers/step_indicator_controller.d.ts +55 -0
  184. package/dist/controllers/step_indicator_controller.js +55 -0
  185. package/dist/controllers/step_indicator_controller.js.map +1 -0
  186. package/dist/controllers/stepper_controller.d.ts +66 -0
  187. package/dist/controllers/stepper_controller.js +78 -0
  188. package/dist/controllers/stepper_controller.js.map +1 -0
  189. package/dist/controllers/stick_to_bottom_controller.d.ts +61 -0
  190. package/dist/controllers/stick_to_bottom_controller.js +100 -0
  191. package/dist/controllers/stick_to_bottom_controller.js.map +1 -0
  192. package/dist/controllers/sticky_observer_controller.d.ts +54 -0
  193. package/dist/controllers/sticky_observer_controller.js +53 -0
  194. package/dist/controllers/sticky_observer_controller.js.map +1 -0
  195. package/dist/controllers/submit_once_controller.d.ts +66 -0
  196. package/dist/controllers/submit_once_controller.js +206 -0
  197. package/dist/controllers/submit_once_controller.js.map +1 -0
  198. package/dist/controllers/switch_controller.d.ts +43 -0
  199. package/dist/controllers/switch_controller.js +50 -0
  200. package/dist/controllers/switch_controller.js.map +1 -0
  201. package/dist/controllers/tabs_controller.d.ts +48 -0
  202. package/dist/controllers/tabs_controller.js +63 -0
  203. package/dist/controllers/tabs_controller.js.map +1 -0
  204. package/dist/controllers/textarea_autosize_controller.d.ts +47 -0
  205. package/dist/controllers/textarea_autosize_controller.js +72 -0
  206. package/dist/controllers/textarea_autosize_controller.js.map +1 -0
  207. package/dist/controllers/theme_controller.d.ts +70 -0
  208. package/dist/controllers/theme_controller.js +154 -0
  209. package/dist/controllers/theme_controller.js.map +1 -0
  210. package/dist/controllers/toast_controller.d.ts +123 -0
  211. package/dist/controllers/toast_controller.js +310 -0
  212. package/dist/controllers/toast_controller.js.map +1 -0
  213. package/dist/controllers/toggle_group_controller.d.ts +56 -0
  214. package/dist/controllers/toggle_group_controller.js +130 -0
  215. package/dist/controllers/toggle_group_controller.js.map +1 -0
  216. package/dist/controllers/toolbar_controller.d.ts +59 -0
  217. package/dist/controllers/toolbar_controller.js +113 -0
  218. package/dist/controllers/toolbar_controller.js.map +1 -0
  219. package/dist/controllers/tooltip_controller.d.ts +74 -0
  220. package/dist/controllers/tooltip_controller.js +165 -0
  221. package/dist/controllers/tooltip_controller.js.map +1 -0
  222. package/dist/controllers/transition_controller.d.ts +86 -0
  223. package/dist/controllers/transition_controller.js +203 -0
  224. package/dist/controllers/transition_controller.js.map +1 -0
  225. package/dist/index.d.ts +1931 -0
  226. package/dist/index.js +12241 -0
  227. package/dist/index.js.map +1 -0
  228. package/dist/inspector/cli.d.ts +149 -0
  229. package/dist/inspector/cli.js +663 -0
  230. package/dist/inspector/cli.js.map +1 -0
  231. package/dist/inspector/cli_bin.d.ts +2 -0
  232. package/dist/inspector/cli_bin.js +665 -0
  233. package/dist/inspector/cli_bin.js.map +1 -0
  234. package/dist/inspector/manifest.json +2235 -0
  235. package/dist/positioning/index.d.ts +226 -0
  236. package/dist/positioning/index.js +145 -0
  237. package/dist/positioning/index.js.map +1 -0
  238. package/package.json +93 -0
@@ -0,0 +1,1931 @@
1
+ import { Controller, Application } from '@hotwired/stimulus';
2
+ import { AccordionController } from './controllers/accordion_controller.js';
3
+ import { AnnouncerController } from './controllers/announcer_controller.js';
4
+ import { AspectRatioController } from './controllers/aspect_ratio_controller.js';
5
+ import { AutoSubmitController } from './controllers/auto_submit_controller.js';
6
+ import { AvatarController } from './controllers/avatar_controller.js';
7
+ import { BreadcrumbController } from './controllers/breadcrumb_controller.js';
8
+ import { BulkSelectController } from './controllers/bulk_select_controller.js';
9
+ import { CalendarController } from './controllers/calendar_controller.js';
10
+ import { CharacterCounterController } from './controllers/character_counter_controller.js';
11
+ import { CheckboxController } from './controllers/checkbox_controller.js';
12
+ import { ComboboxController } from './controllers/combobox_controller.js';
13
+ import { CommandPaletteController } from './controllers/command_palette_controller.js';
14
+ import { ConditionalFieldsController } from './controllers/conditional_fields_controller.js';
15
+ import { ConfirmController } from './controllers/confirm_controller.js';
16
+ import { ContextMenuController } from './controllers/context_menu_controller.js';
17
+ import { CountdownController } from './controllers/countdown_controller.js';
18
+ import { DialogController } from './controllers/dialog_controller.js';
19
+ import { DirectUploadController } from './controllers/direct_upload_controller.js';
20
+ import { DirtyFormController } from './controllers/dirty_form_controller.js';
21
+ import { DropdownController } from './controllers/dropdown_controller.js';
22
+ import { EmptyStateController } from './controllers/empty_state_controller.js';
23
+ import { FlashController } from './controllers/flash_controller.js';
24
+ import { FocusController } from './controllers/focus_controller.js';
25
+ import { FormFieldController } from './controllers/form_field_controller.js';
26
+ import { FormValidationController } from './controllers/form_validation_controller.js';
27
+ import { FrameLoadingController } from './controllers/frame_loading_controller.js';
28
+ import { HighlightController } from './controllers/highlight_controller.js';
29
+ import { HoverCardController } from './controllers/hover_card_controller.js';
30
+ import { IdleController } from './controllers/idle_controller.js';
31
+ import { InputMaskController } from './controllers/input_mask_controller.js';
32
+ import { LazyFrameController } from './controllers/lazy_frame_controller.js';
33
+ import { ListboxController } from './controllers/listbox_controller.js';
34
+ import { LocalTimeController } from './controllers/local_time_controller.js';
35
+ import { MenuController } from './controllers/menu_controller.js';
36
+ import { MeterController } from './controllers/meter_controller.js';
37
+ import { NestedFormController } from './controllers/nested_form_controller.js';
38
+ import { NetworkStatusController } from './controllers/network_status_controller.js';
39
+ import { NumberInputController } from './controllers/number_input_controller.js';
40
+ import { OtpController } from './controllers/otp_controller.js';
41
+ import { OverflowIndicatorController } from './controllers/overflow_indicator_controller.js';
42
+ import { OverflowMenuController } from './controllers/overflow_menu_controller.js';
43
+ import { PaginationController } from './controllers/pagination_controller.js';
44
+ import { PasswordStrengthController } from './controllers/password_strength_controller.js';
45
+ import { PersistController } from './controllers/persist_controller.js';
46
+ import { PopoverController } from './controllers/popover_controller.js';
47
+ import { PortalController } from './controllers/portal_controller.js';
48
+ import { PreviewGuardController } from './controllers/preview_guard_controller.js';
49
+ import { ProgressController } from './controllers/progress_controller.js';
50
+ import { RadioGroupController } from './controllers/radio_group_controller.js';
51
+ import { RatingController } from './controllers/rating_controller.js';
52
+ import { RelativeTimeController } from './controllers/relative_time_controller.js';
53
+ import { ResetBeforeCacheController } from './controllers/reset_before_cache_controller.js';
54
+ import { ResizableController } from './controllers/resizable_controller.js';
55
+ import { RovingController } from './controllers/roving_controller.js';
56
+ import { ScrollAreaController } from './controllers/scroll_area_controller.js';
57
+ import { ScrollVisibilityController } from './controllers/scroll_visibility_controller.js';
58
+ import { ScrollspyController } from './controllers/scrollspy_controller.js';
59
+ import { SkeletonController } from './controllers/skeleton_controller.js';
60
+ import { SliderController } from './controllers/slider_controller.js';
61
+ import { SpinnerController } from './controllers/spinner_controller.js';
62
+ import { StepIndicatorController } from './controllers/step_indicator_controller.js';
63
+ import { StepperController } from './controllers/stepper_controller.js';
64
+ import { StickToBottomController } from './controllers/stick_to_bottom_controller.js';
65
+ import { StickyObserverController } from './controllers/sticky_observer_controller.js';
66
+ import { SubmitOnceController } from './controllers/submit_once_controller.js';
67
+ import { SwitchController } from './controllers/switch_controller.js';
68
+ import { TabsController } from './controllers/tabs_controller.js';
69
+ import { TextareaAutosizeController } from './controllers/textarea_autosize_controller.js';
70
+ import { ThemeController } from './controllers/theme_controller.js';
71
+ import { ToastController } from './controllers/toast_controller.js';
72
+ import { ToggleGroupController } from './controllers/toggle_group_controller.js';
73
+ import { ToolbarController } from './controllers/toolbar_controller.js';
74
+ import { TooltipController } from './controllers/tooltip_controller.js';
75
+ import { TransitionController } from './controllers/transition_controller.js';
76
+
77
+ /**
78
+ * Headless, accessible **alert dialog** behavior.
79
+ *
80
+ * Markup contract (identifier: `stimeo--alert-dialog`):
81
+ * <div data-controller="stimeo--alert-dialog">
82
+ * <button data-stimeo--alert-dialog-target="trigger"
83
+ * data-action="click->stimeo--alert-dialog#open">Delete…</button>
84
+ * <div data-stimeo--alert-dialog-target="dialog" role="alertdialog"
85
+ * aria-modal="true" aria-labelledby="t" aria-describedby="d" hidden>
86
+ * <h2 id="t">…</h2><p id="d">…</p>
87
+ * <button data-stimeo--alert-dialog-target="initialFocus"
88
+ * data-action="click->stimeo--alert-dialog#cancel">Cancel</button>
89
+ * <button data-action="click->stimeo--alert-dialog#confirm">Delete</button>
90
+ * </div>
91
+ * </div>
92
+ *
93
+ * Implements the WAI-ARIA APG **Alert Dialog** pattern. It is the same modal as
94
+ * `stimeo--dialog` with two deliberate differences that suit an *interrupting
95
+ * confirmation*: it never closes on a backdrop click (an accidental dismissal
96
+ * would be dangerous), and it exposes explicit {@link confirm}/{@link cancel}
97
+ * actions that emit events so the consumer only writes the message and "what to
98
+ * do on confirm".
99
+ *
100
+ * @remarks
101
+ * Behavior only. The modal lifecycle (focus trap, scroll lock, background
102
+ * `inert`, focus restore, teardown reversal) is delegated to the shared
103
+ * {@link FocusTrap}. The only closing affordances are the confirm/cancel actions
104
+ * and `Escape` (which cancels) — there is intentionally no backdrop close.
105
+ *
106
+ * Behavior provided:
107
+ * - {@link open} shows the dialog and moves focus to the `initialFocus` target
108
+ * (the least destructive action, by convention), else the first focusable
109
+ * element.
110
+ * - `Tab`/`Shift+Tab` cycle focus within the dialog (focus trap).
111
+ * - {@link confirm} closes and dispatches `stimeo--alert-dialog:confirm`.
112
+ * - {@link cancel} and `Escape` close and dispatch `stimeo--alert-dialog:cancel`
113
+ * with a `reason` of `"user"` / `"escape"`. Focus returns to the opener.
114
+ */
115
+ declare class AlertDialogController extends Controller<HTMLElement> {
116
+ #private;
117
+ static targets: string[];
118
+ static actions: readonly ["cancel", "confirm", "open"];
119
+ static events: readonly ["cancel", "confirm"];
120
+ readonly triggerTarget: HTMLElement;
121
+ readonly dialogTarget: HTMLElement;
122
+ readonly initialFocusTarget: HTMLElement;
123
+ readonly hasTriggerTarget: boolean;
124
+ readonly hasDialogTarget: boolean;
125
+ readonly hasInitialFocusTarget: boolean;
126
+ /** Starts closed (idempotently reflects the closed state on the markup). */
127
+ connect(): void;
128
+ /** Reverts the modal side effects if torn down while open (Turbo navigation). */
129
+ disconnect(): void;
130
+ /** Opens the dialog, traps focus, and locks background scroll. */
131
+ open(): void;
132
+ /** Confirms: closes and dispatches `confirm`. Bound via `data-action`. */
133
+ confirm(): void;
134
+ /** Cancels (user action): closes and dispatches `cancel` with `reason: "user"`. */
135
+ cancel(): void;
136
+ }
137
+
138
+ /**
139
+ * Headless, accessible **Carousel** (slideshow) behavior.
140
+ *
141
+ * Markup contract (identifier: `stimeo--carousel`):
142
+ * <section data-controller="stimeo--carousel" aria-roledescription="carousel"
143
+ * aria-label="Featured"
144
+ * data-stimeo--carousel-autoplay-value="false"
145
+ * data-stimeo--carousel-interval-value="5000"
146
+ * data-stimeo--carousel-loop-value="true"
147
+ * data-action="mouseenter->stimeo--carousel#pause
148
+ * mouseleave->stimeo--carousel#resume
149
+ * focusin->stimeo--carousel#pause
150
+ * focusout->stimeo--carousel#resume">
151
+ * <button data-stimeo--carousel-target="playToggle"
152
+ * data-action="stimeo--carousel#togglePlay">…</button>
153
+ * <div data-stimeo--carousel-target="viewport">
154
+ * <div role="tabpanel" data-stimeo--carousel-target="slide">…</div>
155
+ * <div role="tabpanel" data-stimeo--carousel-target="slide" hidden>…</div>
156
+ * </div>
157
+ * <button data-stimeo--carousel-target="prev" data-action="stimeo--carousel#prev">‹</button>
158
+ * <button data-stimeo--carousel-target="next" data-action="stimeo--carousel#next">›</button>
159
+ * <div role="tablist">
160
+ * <button role="tab" data-stimeo--carousel-target="picker"
161
+ * data-action="stimeo--carousel#goto
162
+ * keydown->stimeo--carousel#onPickerKeydown"></button>
163
+ * </div>
164
+ * </section>
165
+ *
166
+ * Implements the WAI-ARIA APG **Carousel** (tabbed) pattern. The current slide is
167
+ * exposed through `data-state` (`active`/`inactive`) and the `hidden` attribute on
168
+ * inactive slides (removing them from focus order); the matching picker carries
169
+ * `aria-selected` and the single roving `tabindex`. The play/pause toggle's
170
+ * `aria-pressed` mirrors the autoplay state.
171
+ *
172
+ * @remarks
173
+ * Behavior only — transitions, layout, and visuals are the consumer's CSS.
174
+ * Autoplay honors WCAG 2.2.2: it suspends while the pointer is over the carousel
175
+ * and **hard-stops** when keyboard focus enters (it does not silently resume on
176
+ * focus out — the user must press play), so motion never surprises a keyboard
177
+ * user. The interval is cleared on `disconnect()` (Turbo navigation included).
178
+ * Picker arrow keys move focus only (manual activation); slide changes never steal
179
+ * focus from the control the user operated.
180
+ */
181
+ declare class CarouselController extends Controller<HTMLElement> {
182
+ #private;
183
+ static targets: string[];
184
+ static values: {
185
+ autoplay: {
186
+ type: BooleanConstructor;
187
+ default: boolean;
188
+ };
189
+ interval: {
190
+ type: NumberConstructor;
191
+ default: number;
192
+ };
193
+ loop: {
194
+ type: BooleanConstructor;
195
+ default: boolean;
196
+ };
197
+ };
198
+ static actions: readonly ["goto", "next", "onPickerKeydown", "pause", "prev", "resume", "togglePlay"];
199
+ static events: readonly ["change", "pause", "play"];
200
+ readonly slideTargets: HTMLElement[];
201
+ readonly pickerTargets: HTMLElement[];
202
+ readonly playToggleTarget: HTMLElement;
203
+ readonly hasPlayToggleTarget: boolean;
204
+ autoplayValue: boolean;
205
+ intervalValue: number;
206
+ loopValue: boolean;
207
+ /** Renders the initial slide and starts autoplay when requested. */
208
+ connect(): void;
209
+ /** Clears the autoplay interval so it never fires after teardown. */
210
+ disconnect(): void;
211
+ /** Advances to the next slide. Bound via `data-action`. */
212
+ next(): void;
213
+ /** Returns to the previous slide. Bound via `data-action`. */
214
+ prev(): void;
215
+ /** Jumps to the slide whose picker was activated (click / Enter / Space). */
216
+ goto(event: Event): void;
217
+ /** Toggles autoplay on the user's explicit request and syncs the timer. */
218
+ togglePlay(): void;
219
+ /**
220
+ * Suspends autoplay. Hover (`mouseenter`) is a temporary suspension that resumes
221
+ * on leave; keyboard focus (`focusin`) is a hard stop that turns autoplay off so
222
+ * it cannot resume without an explicit play (WCAG 2.2.2).
223
+ */
224
+ pause(event?: Event): void;
225
+ /**
226
+ * Lifts a hover suspension (`mouseleave`) and resumes autoplay if it is still
227
+ * on. A `focusout` does nothing here: the focus pause was a hard stop, so the
228
+ * user must press play to restart.
229
+ */
230
+ resume(event?: Event): void;
231
+ /** Picker roving: arrows move focus only; Home/End activate first/last slide. */
232
+ onPickerKeydown(event: KeyboardEvent): void;
233
+ }
234
+
235
+ /**
236
+ * Headless copy-to-clipboard behavior with a live-region completion notice.
237
+ *
238
+ * Markup contract (identifier: `stimeo--clipboard`):
239
+ * <div data-controller="stimeo--clipboard"
240
+ * data-stimeo--clipboard-feedback-duration-value="2000">
241
+ * <input type="text" value="https://example.com" readonly
242
+ * data-stimeo--clipboard-target="source">
243
+ * <button type="button" data-stimeo--clipboard-target="button"
244
+ * data-action="stimeo--clipboard#copy">Copy</button>
245
+ * <span role="status" aria-live="polite"
246
+ * data-stimeo--clipboard-target="feedback"></span>
247
+ * </div>
248
+ *
249
+ * No dedicated APG pattern; this follows the Button + live-region practice. The
250
+ * copy uses the standard `navigator.clipboard` API (no extra dependency); when
251
+ * it is unavailable or rejects, the failure is surfaced rather than silently
252
+ * swallowed, and never communicated by icon alone — the `role="status"` region
253
+ * carries text so screen readers announce the outcome.
254
+ *
255
+ * @remarks
256
+ * Behavior only — icon swaps and styling are the consumer's, keyed off
257
+ * `data-state` (`idle` / `copied` / `error`). The completion notice clears
258
+ * itself after `feedbackDuration`; that timer is torn down on disconnect (Turbo)
259
+ * via {@link SafeTimeout}.
260
+ */
261
+ declare class ClipboardController extends Controller<HTMLElement> {
262
+ #private;
263
+ static targets: string[];
264
+ static values: {
265
+ text: {
266
+ type: StringConstructor;
267
+ default: string;
268
+ };
269
+ feedbackDuration: {
270
+ type: NumberConstructor;
271
+ default: number;
272
+ };
273
+ copiedLabel: {
274
+ type: StringConstructor;
275
+ default: string;
276
+ };
277
+ errorLabel: {
278
+ type: StringConstructor;
279
+ default: string;
280
+ };
281
+ };
282
+ static actions: readonly ["copy"];
283
+ static events: readonly ["copy"];
284
+ readonly sourceTarget: HTMLElement;
285
+ readonly buttonTarget: HTMLElement;
286
+ readonly feedbackTarget: HTMLElement;
287
+ readonly hasSourceTarget: boolean;
288
+ readonly hasButtonTarget: boolean;
289
+ readonly hasFeedbackTarget: boolean;
290
+ textValue: string;
291
+ feedbackDurationValue: number;
292
+ copiedLabelValue: string;
293
+ errorLabelValue: string;
294
+ connect(): void;
295
+ disconnect(): void;
296
+ /**
297
+ * Copies the resolved text and reports the outcome. Bound via `data-action`
298
+ * (click). Always dispatches `stimeo--clipboard:copy` with `{ success, text }`
299
+ * — including on failure — so consumers can react either way.
300
+ */
301
+ copy(): Promise<void>;
302
+ }
303
+
304
+ /**
305
+ * Headless, accessible single-disclosure (collapsible) behavior.
306
+ *
307
+ * Markup contract (identifier: `stimeo--collapsible`):
308
+ * <div data-controller="stimeo--collapsible">
309
+ * <button data-stimeo--collapsible-target="trigger"
310
+ * data-action="stimeo--collapsible#toggle"
311
+ * aria-expanded="false" aria-controls="more">Show details</button>
312
+ * <div id="more" data-stimeo--collapsible-target="content"
313
+ * data-state="closed" hidden>…</div>
314
+ * </div>
315
+ *
316
+ * Implements the WAI-ARIA APG **Disclosure** pattern for a single inline region.
317
+ * Unlike {@link AccordionController} it manages exactly one trigger/content pair
318
+ * with no sibling coordination, and unlike a dropdown the content expands in
319
+ * flow and never closes on an outside click.
320
+ *
321
+ * @remarks
322
+ * Behavior only — the consumer owns the height transition and all decoration.
323
+ * The controller keeps the open lifecycle ordered so `hidden` (effectively
324
+ * `display:none`) never blocks measurement or the transition:
325
+ * - **Open**: drop `hidden` → measure the natural height into
326
+ * `--stimeo-collapsible-content-height` → set `data-state="open"`.
327
+ * - **Close**: set `data-state="closed"` (CSS shrinks the height) → re-apply
328
+ * `hidden` after `transitionend`. With no transition (or reduced motion, which
329
+ * the consumer's CSS expresses as a zero duration) it is applied immediately.
330
+ */
331
+ declare class CollapsibleController extends Controller<HTMLElement> {
332
+ #private;
333
+ static targets: string[];
334
+ static values: {
335
+ open: {
336
+ type: BooleanConstructor;
337
+ default: boolean;
338
+ };
339
+ };
340
+ static actions: readonly ["toggle"];
341
+ readonly triggerTarget: HTMLElement;
342
+ readonly contentTarget: HTMLElement;
343
+ readonly hasTriggerTarget: boolean;
344
+ readonly hasContentTarget: boolean;
345
+ openValue: boolean;
346
+ /**
347
+ * Establishes the initial open/closed state without animating.
348
+ *
349
+ * The DOM is the source of truth on reconnect (Turbo cache restore / morph): an
350
+ * **explicit** state attribute — `aria-expanded="true"`/`"false"` (or, with no
351
+ * trigger, `data-state="open"`/`"closed"`) — is honored verbatim so a region the
352
+ * user opened *or* closed survives a back-navigation, even when the declarative
353
+ * `open` Value disagrees. The Value only seeds a genuinely fresh render where no
354
+ * state attribute is present yet. Mirrors `sidebar`'s `#restoreCollapsed`.
355
+ */
356
+ connect(): void;
357
+ disconnect(): void;
358
+ /** Toggles the region open/closed. Bound via `data-action` (click). */
359
+ toggle(): void;
360
+ }
361
+
362
+ /**
363
+ * Headless, accessible **Color Picker** behavior.
364
+ *
365
+ * Markup contract (identifier: `stimeo--color-picker`):
366
+ * <div data-controller="stimeo--color-picker"
367
+ * data-stimeo--color-picker-value-value="#3366cc">
368
+ * <div role="slider" aria-label="Hue" data-channel="hue" tabindex="0"
369
+ * aria-valuemin="0" aria-valuemax="360" aria-valuenow="210"
370
+ * data-stimeo--color-picker-target="slider"
371
+ * data-action="keydown->stimeo--color-picker#onKeydown
372
+ * pointerdown->stimeo--color-picker#onPointerDown"></div>
373
+ * <!-- saturation / lightness / alpha sliders share the same contract -->
374
+ * <input type="text" aria-label="Hex color"
375
+ * data-stimeo--color-picker-target="hex"
376
+ * data-action="change->stimeo--color-picker#onHexInput" />
377
+ * <div data-stimeo--color-picker-target="preview" aria-hidden="true"></div>
378
+ * <input type="hidden" data-stimeo--color-picker-target="field" />
379
+ * </div>
380
+ *
381
+ * Decomposes color selection into independent APG **Slider** channels (hue,
382
+ * saturation, lightness, optional alpha) instead of a 2-D palette, so every
383
+ * adjustment is keyboard- and screen-reader-operable. Each slider exposes
384
+ * `aria-valuenow` and a human-readable `aria-valuetext` (e.g. "Hue 210 degrees");
385
+ * the hex input stays two-way synced; the current color is published on the
386
+ * preview and root as the `--stimeo-color` custom property and mirrored into a
387
+ * hidden form field.
388
+ *
389
+ * @remarks
390
+ * Behavior only — the swatch/gradient visuals are the consumer's CSS/canvas, fed
391
+ * by `--stimeo-color`. Pointer-drag listeners on `document` are bound to an
392
+ * {@link AbortController} and released on drag end and on `disconnect()` (Turbo
393
+ * navigation included). Color is fully reconstructable from the `value` (hex), so
394
+ * there is no transient state to restore after a Turbo cache/morph.
395
+ *
396
+ * The internal model is integer HSL(A), so a hex → HSL → hex round-trip is not
397
+ * exactly bijective: a typed hex can normalize to a near (not identical) value
398
+ * once the HSL sliders are touched. This keeps the model small and zero-dep; use a
399
+ * dedicated color library on the consumer side if exact hex preservation matters.
400
+ */
401
+ declare class ColorPickerController extends Controller<HTMLElement> {
402
+ #private;
403
+ static targets: string[];
404
+ static values: {
405
+ value: {
406
+ type: StringConstructor;
407
+ default: string;
408
+ };
409
+ alpha: {
410
+ type: BooleanConstructor;
411
+ default: boolean;
412
+ };
413
+ };
414
+ static actions: readonly ["onHexInput", "onKeydown", "onPointerDown"];
415
+ static events: readonly ["change"];
416
+ readonly sliderTargets: HTMLElement[];
417
+ readonly hexTarget: HTMLInputElement;
418
+ readonly hasHexTarget: boolean;
419
+ readonly previewTargets: HTMLElement[];
420
+ readonly fieldTargets: HTMLInputElement[];
421
+ valueValue: string;
422
+ alphaValue: boolean;
423
+ /** Seeds the model from the initial hex value and renders every surface. */
424
+ connect(): void;
425
+ /** Cancels any active pointer drag so document listeners never leak. */
426
+ disconnect(): void;
427
+ /** Keyboard stepping on the focused channel slider (APG Slider model). */
428
+ onKeydown(event: KeyboardEvent): void;
429
+ /** Begins a pointer drag on a channel slider and tracks movement. */
430
+ onPointerDown(event: PointerEvent): void;
431
+ /** Parses the hex input on confirm and syncs every channel + surface. */
432
+ onHexInput(): void;
433
+ }
434
+
435
+ /**
436
+ * Headless currency / amount input behavior: groups digits for display while
437
+ * keeping a machine-readable number in a hidden field, and mirrors the
438
+ * normalized value to a visually-hidden span so assistive tech reads the real
439
+ * amount rather than the grouped display string.
440
+ *
441
+ * Markup contract (identifier: `stimeo--currency-input`):
442
+ * <div data-controller="stimeo--currency-input"
443
+ * data-stimeo--currency-input-locale-value="en-US"
444
+ * data-stimeo--currency-input-currency-value="USD"
445
+ * data-stimeo--currency-input-precision-value="2">
446
+ * <input type="text" inputmode="decimal"
447
+ * aria-describedby="amt-sr"
448
+ * data-stimeo--currency-input-target="display"
449
+ * data-action="input->stimeo--currency-input#onInput
450
+ * blur->stimeo--currency-input#format" />
451
+ * <span id="amt-sr" class="visually-hidden"
452
+ * data-stimeo--currency-input-target="srValue"></span>
453
+ * <input type="hidden" data-stimeo--currency-input-target="field" />
454
+ * </div>
455
+ *
456
+ * @remarks
457
+ * Behavior only — no styling, no validation (range/required belong to the
458
+ * consumer or Form Field). The display field is the sole Tab stop and keeps its
459
+ * native text-editing behavior; this controller never steals focus. Grouping is
460
+ * applied on every input while preserving the caret (counted by digits to its
461
+ * left), and the fixed-precision rounding is applied on `blur`.
462
+ *
463
+ * Honest a11y note: a hidden `<input>` is not exposed to assistive tech, so the
464
+ * normalized value is *also* published as text in the `srValue` span referenced
465
+ * by the display's `aria-describedby` — that, not the hidden field, is what a
466
+ * screen reader announces.
467
+ */
468
+ declare class CurrencyInputController extends Controller<HTMLElement> {
469
+ #private;
470
+ static targets: string[];
471
+ static values: {
472
+ locale: {
473
+ type: StringConstructor;
474
+ default: string;
475
+ };
476
+ currency: {
477
+ type: StringConstructor;
478
+ default: string;
479
+ };
480
+ precision: {
481
+ type: NumberConstructor;
482
+ default: number;
483
+ };
484
+ };
485
+ static actions: readonly ["format", "onInput"];
486
+ static events: readonly ["change"];
487
+ readonly displayTarget: HTMLInputElement;
488
+ readonly fieldTarget: HTMLInputElement;
489
+ readonly srValueTarget: HTMLElement;
490
+ readonly hasDisplayTarget: boolean;
491
+ readonly hasFieldTarget: boolean;
492
+ readonly hasSrValueTarget: boolean;
493
+ localeValue: string;
494
+ currencyValue: string;
495
+ precisionValue: number;
496
+ /** Normalizes any pre-filled display value to its fixed-precision form. */
497
+ connect(): void;
498
+ /** Re-groups digits as the user types, preserving the caret position. */
499
+ onInput(): void;
500
+ /** Applies the fixed-precision rounding on blur. */
501
+ format(): void;
502
+ }
503
+
504
+ /**
505
+ * Headless, accessible **Data Grid** behavior: column sorting, row selection, and
506
+ * roving keyboard navigation over an interactive table.
507
+ *
508
+ * Markup contract (identifier: `stimeo--data-grid`):
509
+ * <table data-controller="stimeo--data-grid" role="grid" aria-label="Users"
510
+ * data-stimeo--data-grid-selection-value="single">
511
+ * <thead><tr role="row">
512
+ * <th role="columnheader" aria-sort="none" tabindex="-1"
513
+ * data-stimeo--data-grid-target="columnHeader"
514
+ * data-action="click->stimeo--data-grid#sort
515
+ * keydown->stimeo--data-grid#onKeydown">Name</th>
516
+ * </tr></thead>
517
+ * <tbody><tr role="row" aria-selected="false"
518
+ * data-stimeo--data-grid-target="row">
519
+ * <td role="gridcell" tabindex="0" data-stimeo--data-grid-target="cell"
520
+ * data-action="keydown->stimeo--data-grid#onKeydown">Jane</td>
521
+ * </tr></tbody>
522
+ * </table>
523
+ *
524
+ * Implements the WAI-ARIA APG **Grid** pattern plus `aria-sort`. The whole grid is
525
+ * a single Tab stop (roving `tabindex`: exactly one cell/header is `0`, the rest
526
+ * `-1`); arrow keys move both DOM focus and that tabbable position. Sort state is
527
+ * exposed via `aria-sort` on headers, selection via `aria-selected` on rows.
528
+ *
529
+ * @remarks
530
+ * Behavior only — the consumer performs the actual data sort/render in response to
531
+ * the `sort` event and owns all styling. No timers or observers are held, so there
532
+ * is nothing to leak across Turbo navigations; `connect()` rebuilds the single tab
533
+ * stop idempotently from the DOM.
534
+ *
535
+ * Behavior provided:
536
+ * - `Arrow*` move between cells (clamped at edges); `Home`/`End` to the row's
537
+ * first/last cell; `Ctrl+Home`/`Ctrl+End` to the grid's first/last cell.
538
+ * - `Enter`/`Space` cycles a header's sort (`none→ascending→descending`) or toggles
539
+ * the focused row's selection when selection is enabled.
540
+ */
541
+ declare class DataGridController extends Controller<HTMLElement> {
542
+ #private;
543
+ static targets: string[];
544
+ static values: {
545
+ selection: {
546
+ type: StringConstructor;
547
+ default: string;
548
+ };
549
+ };
550
+ static actions: readonly ["onKeydown", "sort", "toggleSelect"];
551
+ static events: readonly ["selectionchange", "sort"];
552
+ readonly columnHeaderTargets: HTMLElement[];
553
+ readonly rowTargets: HTMLElement[];
554
+ readonly cellTargets: HTMLElement[];
555
+ selectionValue: string;
556
+ /** Establishes a single tab stop across all navigable cells/headers. */
557
+ connect(): void;
558
+ /**
559
+ * Keeps `aria-multiselectable` in step with the `selection` Value. Fires on connect
560
+ * (so it self-heals after a Turbo morph) and on any runtime change, so the ARIA
561
+ * never drifts from the selection logic, which reads `selectionValue` live.
562
+ */
563
+ selectionValueChanged(): void;
564
+ /** Cycles the activated column header's sort and emits `sort`. */
565
+ sort(event: Event): void;
566
+ /** Toggles selection of the row owning the event target. Bound optionally. */
567
+ toggleSelect(event: Event): void;
568
+ /** Grid navigation + sort/select activation. Bound to cells and headers. */
569
+ onKeydown(event: KeyboardEvent): void;
570
+ }
571
+
572
+ /**
573
+ * Headless, accessible **date range picker** behavior. A derivative of
574
+ * {@link CalendarController}: it reuses the month-grid navigation model and adds
575
+ * two-point range selection, in-progress range preview, and presets.
576
+ *
577
+ * Markup contract (identifier: `stimeo--date-range-picker`):
578
+ * <div data-controller="stimeo--date-range-picker"
579
+ * data-stimeo--date-range-picker-min-value="2026-01-01"
580
+ * data-stimeo--date-range-picker-max-value="2026-12-31">
581
+ * <button data-action="stimeo--date-range-picker#prev">Prev</button>
582
+ * <span data-stimeo--date-range-picker-target="monthLabel" aria-live="polite"></span>
583
+ * <button data-action="stimeo--date-range-picker#next">Next</button>
584
+ * <div role="grid" data-stimeo--date-range-picker-target="grid">
585
+ * <!-- exactly 42 cell targets (7 days × 6 rows) -->
586
+ * <button role="gridcell" tabindex="-1"
587
+ * data-stimeo--date-range-picker-target="cell"
588
+ * data-action="click->stimeo--date-range-picker#selectDate
589
+ * mouseenter->stimeo--date-range-picker#previewTo
590
+ * focus->stimeo--date-range-picker#previewTo
591
+ * keydown->stimeo--date-range-picker#onKeydown"></button>
592
+ * </div>
593
+ * <button data-range="last7" data-action="stimeo--date-range-picker#applyPreset">…</button>
594
+ * <span role="status" aria-live="polite" data-stimeo--date-range-picker-target="status"></span>
595
+ * <input type="hidden" data-stimeo--date-range-picker-target="startField" />
596
+ * <input type="hidden" data-stimeo--date-range-picker-target="endField" />
597
+ * </div>
598
+ *
599
+ * @remarks
600
+ * Behavior only — the consumer styles the grid and renders the range using the
601
+ * `data-range-start` / `data-in-range` / `data-range-end` hooks. Assistive tech
602
+ * is told the two confirmed endpoints via `aria-selected`; inner cells use
603
+ * `data-in-range` (visual only) so the announcement stays to the two ends. The
604
+ * confirmed range is also mirrored to the live `status` region.
605
+ *
606
+ * Selection model: the first click/Enter sets a *pending* start and enters
607
+ * "selecting" mode (preview follows the pointer/focus); the second confirms the
608
+ * end (auto-swapped if earlier than the start) and dispatches `change`. Escape
609
+ * abandons an in-progress selection, restoring the last confirmed range.
610
+ */
611
+ declare class DateRangePickerController extends Controller<HTMLElement> {
612
+ #private;
613
+ static targets: string[];
614
+ static values: {
615
+ min: {
616
+ type: StringConstructor;
617
+ default: string;
618
+ };
619
+ max: {
620
+ type: StringConstructor;
621
+ default: string;
622
+ };
623
+ };
624
+ static actions: readonly ["applyPreset", "next", "onKeydown", "prev", "previewTo", "selectDate"];
625
+ static events: readonly ["change"];
626
+ readonly gridTarget: HTMLElement;
627
+ readonly monthLabelTarget: HTMLElement;
628
+ readonly cellTargets: HTMLElement[];
629
+ readonly statusTarget: HTMLElement;
630
+ readonly startFieldTarget: HTMLInputElement;
631
+ readonly endFieldTarget: HTMLInputElement;
632
+ readonly hasMonthLabelTarget: boolean;
633
+ readonly hasStatusTarget: boolean;
634
+ readonly hasStartFieldTarget: boolean;
635
+ readonly hasEndFieldTarget: boolean;
636
+ minValue: string;
637
+ maxValue: string;
638
+ /** Seeds the range from any pre-filled hidden fields and renders the grid. */
639
+ connect(): void;
640
+ /** Cancels any pending deferred focus so it never fires on a detached element. */
641
+ disconnect(): void;
642
+ /** Navigates to the previous month. */
643
+ prev(event?: Event): void;
644
+ /** Navigates to the next month. */
645
+ next(event?: Event): void;
646
+ /** Confirms a range endpoint from a clicked cell. */
647
+ selectDate(event: Event): void;
648
+ /** Previews the range up to a hovered/focused cell while selecting. */
649
+ previewTo(event: Event): void;
650
+ /** Applies a named preset (`today` / `last7` / `last30` / `thisMonth`). */
651
+ applyPreset(event: Event): void;
652
+ /** Grid keyboard navigation, selection (Enter/Space), and Escape-to-cancel. */
653
+ onKeydown(event: KeyboardEvent): void;
654
+ }
655
+
656
+ /**
657
+ * Headless "dismissible" behavior for banners, notices, and inline alerts.
658
+ *
659
+ * Markup contract (identifier: `stimeo--dismissible`):
660
+ * <div data-controller="stimeo--dismissible"
661
+ * data-stimeo--dismissible-mode-value="remove">
662
+ * <div data-stimeo--dismissible-target="root" role="status">
663
+ * <p>Saved.</p>
664
+ * <button type="button" aria-label="Close"
665
+ * data-action="stimeo--dismissible#dismiss">×</button>
666
+ * </div>
667
+ * </div>
668
+ *
669
+ * A general utility with no dedicated APG pattern. Its accessibility job is to
670
+ * keep focus from being orphaned: if focus is inside the element being removed,
671
+ * it is moved to a safe place first so the close button vanishing never strands
672
+ * the user (WCAG 2.4.3).
673
+ *
674
+ * @remarks
675
+ * Behavior only — the consumer owns any exit transition (use `hide` mode, which
676
+ * adds `hidden`, and animate off `data-state`) and any semantics like
677
+ * `role="alert"`. Focus retreats to, in order: the `fallback` target → the next
678
+ * focusable element after the root → the previous one → `document.body` as a
679
+ * last resort (weak focus; prefer providing a `fallback`).
680
+ */
681
+ declare class DismissibleController extends Controller<HTMLElement> {
682
+ #private;
683
+ static targets: string[];
684
+ static values: {
685
+ mode: {
686
+ type: StringConstructor;
687
+ default: string;
688
+ };
689
+ closeOnEscape: {
690
+ type: BooleanConstructor;
691
+ default: boolean;
692
+ };
693
+ };
694
+ static actions: readonly ["dismiss"];
695
+ static events: readonly ["dismiss"];
696
+ readonly rootTarget: HTMLElement;
697
+ readonly fallbackTarget: HTMLElement;
698
+ readonly hasRootTarget: boolean;
699
+ readonly hasFallbackTarget: boolean;
700
+ modeValue: string;
701
+ closeOnEscapeValue: boolean;
702
+ connect(): void;
703
+ disconnect(): void;
704
+ /** Dismisses the element. Bound via `data-action` (click on the close button). */
705
+ dismiss(): void;
706
+ }
707
+
708
+ /**
709
+ * Headless, accessible **drawer / slide-over** behavior.
710
+ *
711
+ * Markup contract (identifier: `stimeo--drawer`):
712
+ * <div data-controller="stimeo--drawer" data-stimeo--drawer-placement-value="right">
713
+ * <button data-stimeo--drawer-target="trigger"
714
+ * data-action="click->stimeo--drawer#open">Open panel</button>
715
+ * <div data-stimeo--drawer-target="overlay"
716
+ * data-action="click->stimeo--drawer#closeOnBackdrop">
717
+ * <aside data-stimeo--drawer-target="panel" role="dialog" aria-modal="true"
718
+ * aria-labelledby="t" data-state="closed" hidden>
719
+ * <h2 id="t">…</h2>
720
+ * <button data-action="click->stimeo--drawer#close">Close</button>
721
+ * </aside>
722
+ * </div>
723
+ * </div>
724
+ *
725
+ * Implements the WAI-ARIA APG **Dialog (Modal)** pattern. It is the same modal as
726
+ * `stimeo--dialog`; what it adds is the state plumbing an enter/exit *slide*
727
+ * needs: `data-state` (`open`/`closed`) is synced on the panel and overlay so CSS
728
+ * can animate, and `hidden` is applied only *after* the close transition finishes
729
+ * (so the exit animation can play). `placement` is reflected as `data-placement`
730
+ * for the CSS to read — the controller never computes coordinates.
731
+ *
732
+ * @remarks
733
+ * Behavior only. The modal lifecycle (focus trap, scroll lock, background
734
+ * `inert`, focus restore, teardown reversal) is delegated to the shared
735
+ * {@link FocusTrap}. Placement, slide direction, distance, and easing are all the
736
+ * consumer's CSS — `data-placement` is merely a flag.
737
+ *
738
+ * Behavior provided:
739
+ * - {@link open}/{@link close} toggle `data-state` and (deferred) `hidden`.
740
+ * - On open, focus moves to the first focusable element in the panel.
741
+ * - `Tab`/`Shift+Tab` cycle focus within the panel; `Escape` closes.
742
+ * - {@link closeOnBackdrop} closes only when the overlay *itself* is clicked.
743
+ */
744
+ declare class DrawerController extends Controller<HTMLElement> {
745
+ #private;
746
+ static targets: string[];
747
+ static values: {
748
+ placement: {
749
+ type: StringConstructor;
750
+ default: string;
751
+ };
752
+ open: {
753
+ type: BooleanConstructor;
754
+ default: boolean;
755
+ };
756
+ };
757
+ static actions: readonly ["close", "closeOnBackdrop", "open"];
758
+ readonly triggerTarget: HTMLElement;
759
+ readonly overlayTarget: HTMLElement;
760
+ readonly panelTarget: HTMLElement;
761
+ readonly hasTriggerTarget: boolean;
762
+ readonly hasOverlayTarget: boolean;
763
+ readonly hasPanelTarget: boolean;
764
+ placementValue: string;
765
+ openValue: boolean;
766
+ /**
767
+ * Reflects placement and establishes the initial open/closed state.
768
+ *
769
+ * The DOM is the source of truth on reconnect (Turbo cache restore / morph): a
770
+ * restored snapshot whose panel is already `data-state="open"` stays open
771
+ * rather than being re-derived from the declarative `open` Value (which would
772
+ * close a user-opened drawer). The `open` Value only seeds a genuinely fresh
773
+ * render. We normalize to a clean closed baseline first so {@link open} runs its
774
+ * full reveal + trap activation — the {@link FocusTrap} is a fresh instance
775
+ * after a reconnect and must be re-activated.
776
+ */
777
+ connect(): void;
778
+ /** Reverts the modal side effects and pending hide if torn down while open. */
779
+ disconnect(): void;
780
+ /** Keeps `data-placement` in sync if the value changes at runtime. */
781
+ placementValueChanged(): void;
782
+ /** Opens the drawer: reveals it, syncs `data-state`, traps focus. */
783
+ open(): void;
784
+ /**
785
+ * Closes the drawer: syncs `data-state` to start the exit transition, then
786
+ * defers both `hidden` *and* the modal teardown (scroll lock / background
787
+ * `inert` / focus restore) until the transition finishes — see
788
+ * `#applyHidden`. This keeps the background inert and focus trapped while
789
+ * the drawer is still visually on screen, preserving the modal contract during
790
+ * the exit animation.
791
+ */
792
+ close(): void;
793
+ /** Closes only when the overlay itself (not its contents) is clicked. */
794
+ closeOnBackdrop(event: MouseEvent): void;
795
+ }
796
+
797
+ /**
798
+ * Headless, accessible inline-editing behavior.
799
+ *
800
+ * Markup contract (identifier: `stimeo--editable`):
801
+ * <div data-controller="stimeo--editable"
802
+ * data-stimeo--editable-submit-on-blur-value="true">
803
+ * <button type="button" aria-label="Edit title"
804
+ * data-stimeo--editable-target="display"
805
+ * data-action="click->stimeo--editable#edit
806
+ * keydown->stimeo--editable#onDisplayKeydown">Current title</button>
807
+ * <input type="text" aria-label="Title" hidden
808
+ * data-stimeo--editable-target="input"
809
+ * data-action="keydown->stimeo--editable#onKeydown
810
+ * blur->stimeo--editable#onBlur" />
811
+ * </div>
812
+ *
813
+ * There is no dedicated APG pattern; this implements a display ⇄ edit toggle with
814
+ * focus management and keyboard commit/cancel, leaning on native form-control
815
+ * labeling (WCAG 1.3.1 / 4.1.2). The look is the consumer's CSS, keyed off
816
+ * `data-mode`.
817
+ *
818
+ * Behavior provided:
819
+ * - Activating the display element (`Enter`/`Space` via the `<button>`, or `F2`)
820
+ * enters edit mode, focuses the input, and selects its text.
821
+ * - `Enter` (single-line) or `Ctrl+Enter` / `Cmd+Enter` (multiline `<textarea>`)
822
+ * saves; `Escape` cancels. Both return focus to the display element.
823
+ * - Losing focus while editing saves when `submitOnBlur` is true (the default),
824
+ * honoring wherever focus moved; when false, editing is kept.
825
+ * - Saving dispatches `stimeo--editable:change` with `{ value, previous }` only
826
+ * when the value actually changed; cancelling dispatches
827
+ * `stimeo--editable:cancel`.
828
+ */
829
+ declare class EditableController extends Controller<HTMLElement> {
830
+ #private;
831
+ static targets: string[];
832
+ static values: {
833
+ submitOnBlur: {
834
+ type: BooleanConstructor;
835
+ default: boolean;
836
+ };
837
+ };
838
+ static actions: readonly ["edit", "onBlur", "onDisplayKeydown", "onKeydown"];
839
+ static events: readonly ["cancel", "change"];
840
+ readonly displayTarget: HTMLElement;
841
+ readonly inputTarget: HTMLInputElement | HTMLTextAreaElement;
842
+ readonly hasDisplayTarget: boolean;
843
+ readonly hasInputTarget: boolean;
844
+ submitOnBlurValue: boolean;
845
+ /** Establishes the initial display mode (display shown, input hidden). */
846
+ connect(): void;
847
+ /** Enters edit mode: seeds the input from the display text, focuses, selects. */
848
+ edit(): void;
849
+ /** Adds `F2` as an editing entry point alongside the button's native activation. */
850
+ onDisplayKeydown(event: KeyboardEvent): void;
851
+ /** Commits on `Enter` (or `Ctrl+Enter` when multiline) and cancels on `Escape`. */
852
+ onKeydown(event: KeyboardEvent): void;
853
+ /** Saves on blur when `submitOnBlur` is set; otherwise keeps editing. */
854
+ onBlur(): void;
855
+ }
856
+
857
+ /**
858
+ * Headless, accessible file drag-and-drop / upload field.
859
+ *
860
+ * Markup contract (identifier: `stimeo--file-dropzone`):
861
+ * <div data-controller="stimeo--file-dropzone"
862
+ * data-stimeo--file-dropzone-max-size-value="5242880">
863
+ * <div data-stimeo--file-dropzone-target="zone"
864
+ * data-action="dragover->stimeo--file-dropzone#onDragOver
865
+ * dragleave->stimeo--file-dropzone#onDragLeave
866
+ * drop->stimeo--file-dropzone#onDrop">
867
+ * <button type="button" data-stimeo--file-dropzone-target="trigger"
868
+ * data-action="click->stimeo--file-dropzone#openDialog">Choose…</button>
869
+ * <input type="file" accept="image/*" multiple class="visually-hidden"
870
+ * data-stimeo--file-dropzone-target="input"
871
+ * data-action="change->stimeo--file-dropzone#onChange" />
872
+ * </div>
873
+ * <ul data-stimeo--file-dropzone-target="list" aria-label="Selected files"></ul>
874
+ * <span role="status" aria-live="polite" class="visually-hidden"
875
+ * data-stimeo--file-dropzone-target="status"></span>
876
+ * <template data-stimeo--file-dropzone-target="itemTemplate">…</template>
877
+ * </div>
878
+ *
879
+ * There is no single APG pattern; the native `<input type="file">` stays the
880
+ * primary, keyboard-operable path and the drop zone is an enhancement, mapping to
881
+ * WCAG 2.1.1, 2.4.7, 4.1.2, 4.1.3, and 1.4.1 (drag state is conveyed in words,
882
+ * not color alone).
883
+ *
884
+ * Behavior provided:
885
+ * - Click / keyboard via the `trigger` opens the native file dialog; drag-and-drop
886
+ * over the `zone` adds files (with a `data-dragover` flag and a spoken hint).
887
+ * - Each file is validated against `accept`, `maxSize`, and the file count
888
+ * (`maxFiles`, or 1 when the input is not `multiple`); rejects fire
889
+ * `stimeo--file-dropzone:reject` and set `data-…-invalid`.
890
+ * - Accepted files render from `itemTemplate` with a `Remove {name}` button and an
891
+ * image thumbnail (`objectURL`); every change dispatches
892
+ * `stimeo--file-dropzone:change` with the current `File[]`.
893
+ * - Removing a file revokes its `objectURL` and moves focus to the next (else
894
+ * previous) remove button, falling back to the trigger; `disconnect()` revokes
895
+ * every outstanding `objectURL`.
896
+ */
897
+ declare class FileDropzoneController extends Controller<HTMLElement> {
898
+ #private;
899
+ static targets: string[];
900
+ static values: {
901
+ maxSize: {
902
+ type: NumberConstructor;
903
+ default: number;
904
+ };
905
+ maxFiles: {
906
+ type: NumberConstructor;
907
+ default: number;
908
+ };
909
+ dragLabel: {
910
+ type: StringConstructor;
911
+ default: string;
912
+ };
913
+ };
914
+ static actions: readonly ["onChange", "onDragLeave", "onDragOver", "onDrop", "openDialog"];
915
+ static events: readonly ["change", "reject"];
916
+ readonly inputTarget: HTMLInputElement;
917
+ readonly listTarget: HTMLElement;
918
+ readonly triggerTarget: HTMLElement;
919
+ readonly itemTemplateTarget: HTMLTemplateElement;
920
+ readonly statusTarget: HTMLElement;
921
+ readonly hasListTarget: boolean;
922
+ readonly hasTriggerTarget: boolean;
923
+ readonly hasItemTemplateTarget: boolean;
924
+ readonly hasStatusTarget: boolean;
925
+ readonly hasZoneTarget: boolean;
926
+ readonly zoneTarget: HTMLElement;
927
+ maxSizeValue: number;
928
+ maxFilesValue: number;
929
+ dragLabelValue: string;
930
+ /** Wires file removal as a delegated listener on the list container. */
931
+ connect(): void;
932
+ /** Revokes any outstanding preview URLs so none leaks across navigations. */
933
+ disconnect(): void;
934
+ /** Opens the native file dialog. Bound via `data-action` (trigger click). */
935
+ openDialog(): void;
936
+ /** Adds the files chosen through the native dialog. */
937
+ onChange(): void;
938
+ /** Marks the zone as a drop target and announces the affordance in words. */
939
+ onDragOver(event: DragEvent): void;
940
+ /** Clears the drag-over flag when the pointer leaves the zone. */
941
+ onDragLeave(): void;
942
+ /** Accepts dropped files, clearing the drag-over state. */
943
+ onDrop(event: DragEvent): void;
944
+ }
945
+
946
+ /** A control whose "on" state contributes its token to the active filter set. */
947
+ type FilterControl = HTMLElement;
948
+ /**
949
+ * Headless faceted-filter behavior: shows or hides a collection of items based on the
950
+ * set of currently-active facet tokens, decoupled from how those tokens are toggled
951
+ * (native checkboxes/radios, or button toggles such as `stimeo--toggle-group`).
952
+ *
953
+ * Markup contract (identifier: `stimeo--filter`):
954
+ * <div data-controller="stimeo--filter" data-stimeo--filter-match-value="all">
955
+ * <!-- controls: each contributes its token while "on" (aria-pressed / checked) -->
956
+ * <button type="button" aria-pressed="false" data-value="apg:dialog"
957
+ * data-stimeo--filter-target="control">Dialog</button>
958
+ * <!-- items: filtered in/out by their space-separated tokens -->
959
+ * <li data-stimeo--filter-target="item"
960
+ * data-stimeo--filter-tokens="apg:dialog overlay">…</li>
961
+ * <!-- optional: a container hidden once it holds no visible item -->
962
+ * <section data-stimeo--filter-target="group">…</section>
963
+ * <!-- optional: an element revealed when nothing matches -->
964
+ * <p data-stimeo--filter-target="empty" hidden>No matches</p>
965
+ * </div>
966
+ *
967
+ * `match` decides how multiple active tokens combine: `all` (default — an item must
968
+ * carry every active token) or `any` (at least one). With no active token every item
969
+ * is shown. Items are toggled via `hidden`; groups with zero visible items and the
970
+ * `empty` element are kept in sync, and `stimeo--filter:change` is dispatched.
971
+ *
972
+ * @remarks
973
+ * Behavior only — the consumer owns all styling and the controls' own accessible
974
+ * state (e.g. `aria-pressed`). Visibility is re-derived from the live DOM on
975
+ * `connect()` (Morph/Turbo-safe) and on every native `change` that bubbles to the
976
+ * root; button toggles that emit no native `change` wire their event to the `apply`
977
+ * action (e.g. `stimeo--toggle-group:change->stimeo--filter#apply`).
978
+ */
979
+ declare class FilterController extends Controller<HTMLElement> {
980
+ #private;
981
+ static targets: string[];
982
+ static values: {
983
+ match: {
984
+ type: StringConstructor;
985
+ default: string;
986
+ };
987
+ };
988
+ static actions: readonly ["apply", "clear"];
989
+ static events: readonly ["change"];
990
+ readonly itemTargets: HTMLElement[];
991
+ readonly controlTargets: FilterControl[];
992
+ readonly groupTargets: HTMLElement[];
993
+ readonly emptyTargets: HTMLElement[];
994
+ matchValue: string;
995
+ connect(): void;
996
+ disconnect(): void;
997
+ /** Re-derives every item's visibility from the active tokens and syncs groups/empty. */
998
+ apply(): void;
999
+ /** Turns every control off (uncheck / aria-pressed="false") and re-applies. */
1000
+ clear(): void;
1001
+ }
1002
+
1003
+ /**
1004
+ * Headless **Masonry** layout helper: assigns each item to the shortest column so
1005
+ * variable-height cards pack without vertical gaps. There is no APG widget — this
1006
+ * is a layout-only utility that emits state hooks, never visual structure.
1007
+ *
1008
+ * Markup contract (identifier: `stimeo--masonry`):
1009
+ * <div data-controller="stimeo--masonry"
1010
+ * data-stimeo--masonry-min-column-width-value="240"
1011
+ * data-stimeo--masonry-gap-value="16">
1012
+ * <div data-stimeo--masonry-target="item">…</div>
1013
+ * <div data-stimeo--masonry-target="item">…</div>
1014
+ * </div>
1015
+ *
1016
+ * The column count is derived responsively from the container width and
1017
+ * `minColumnWidth`; each item is then placed into whichever column is currently
1018
+ * shortest (measured from item heights). The count is published on the controller
1019
+ * element as the `--stimeo-masonry-columns` custom property and each item gets a
1020
+ * `data-column` index, so the consumer's CSS owns the actual placement.
1021
+ *
1022
+ * @remarks
1023
+ * Behavior only. **DOM order is never changed** — reading order and focus order
1024
+ * stay the source markup order (WCAG 1.3.2). The visual packing is purely the
1025
+ * column assignment a consumer reads from `data-column`; this controller writes no
1026
+ * positioning styles. Re-layout runs on connect, on resize ({@link LayoutObserver}),
1027
+ * and on item add/remove ({@link MutationObserver}); both observers are released on
1028
+ * `disconnect()` (Turbo navigation included). Use only for independent cards whose
1029
+ * visual order carries no meaning.
1030
+ */
1031
+ declare class MasonryController extends Controller<HTMLElement> {
1032
+ #private;
1033
+ static targets: string[];
1034
+ static values: {
1035
+ minColumnWidth: {
1036
+ type: NumberConstructor;
1037
+ default: number;
1038
+ };
1039
+ gap: {
1040
+ type: NumberConstructor;
1041
+ default: number;
1042
+ };
1043
+ };
1044
+ static events: readonly ["layout"];
1045
+ readonly itemTargets: HTMLElement[];
1046
+ minColumnWidthValue: number;
1047
+ gapValue: number;
1048
+ /** Observes size/content changes and performs the first layout pass. */
1049
+ connect(): void;
1050
+ /** Releases both observers and the load listener so nothing fires after detach. */
1051
+ disconnect(): void;
1052
+ }
1053
+
1054
+ /**
1055
+ * Headless, accessible **menubar** behavior.
1056
+ *
1057
+ * Markup contract (identifier: `stimeo--menubar`):
1058
+ * <div data-controller="stimeo--menubar" role="menubar" aria-label="Main">
1059
+ * <button role="menuitem" aria-haspopup="menu" aria-expanded="false"
1060
+ * aria-controls="m-file" data-stimeo--menubar-target="top"
1061
+ * data-action="click->stimeo--menubar#toggle
1062
+ * keydown->stimeo--menubar#onTopKeydown">File</button>
1063
+ * <ul id="m-file" role="menu" data-stimeo--menubar-target="menu" hidden>
1064
+ * <li role="none">
1065
+ * <button role="menuitem" tabindex="-1" data-stimeo--menubar-target="item"
1066
+ * data-action="click->stimeo--menubar#activate
1067
+ * keydown->stimeo--menubar#onItemKeydown">New</button>
1068
+ * </li>
1069
+ * </ul>
1070
+ * <!-- more top items + menus -->
1071
+ * </div>
1072
+ *
1073
+ * Implements the WAI-ARIA APG **Menubar** pattern (single level): the top items
1074
+ * form one Tab stop via roving tabindex and the arrow keys move between them;
1075
+ * `ArrowDown`/`Enter`/`Space` open a menu (`ArrowUp` opens it at the last item),
1076
+ * the arrow keys then move within the menu, and pressing `ArrowLeft`/`ArrowRight`
1077
+ * while a menu is open jumps to the adjacent top menu. `Escape` closes and
1078
+ * returns focus to the owning top item; `Tab` and an outside click close.
1079
+ *
1080
+ * @remarks
1081
+ * Behavior only. Each top item↔menu pair is linked by `aria-controls`/`id` (not by
1082
+ * position), so the markup order is free. Menu placement and viewport-edge
1083
+ * collision are out of scope — static placement is the consumer's CSS, and dynamic
1084
+ * placement is delegated to the opt-in `stimeo-ui/positioning` module (never
1085
+ * imported here, keeping the core zero-dependency). Roving mechanics across the top
1086
+ * items are delegated to {@link RovingTabindex}.
1087
+ */
1088
+ declare class MenubarController extends Controller<HTMLElement> {
1089
+ #private;
1090
+ static targets: string[];
1091
+ static actions: readonly ["activate", "onItemKeydown", "onTopKeydown", "toggle"];
1092
+ readonly topTargets: HTMLElement[];
1093
+ readonly menuTargets: HTMLElement[];
1094
+ readonly itemTargets: HTMLElement[];
1095
+ /** Establishes the single tab stop and the closed baseline. */
1096
+ connect(): void;
1097
+ /** Removes the document listener and any pending typeahead timer. */
1098
+ disconnect(): void;
1099
+ /** Toggles a top item's menu. Bound via `data-action` (click on the top item). */
1100
+ toggle(event: Event): void;
1101
+ /** Keyboard handling while focus is on a top item. */
1102
+ onTopKeydown(event: KeyboardEvent): void;
1103
+ /** Keyboard handling while focus is on a menu item. */
1104
+ onItemKeydown(event: KeyboardEvent): void;
1105
+ /** Closes the owning menu after an item is activated and refocuses its top. */
1106
+ activate(event: Event): void;
1107
+ }
1108
+
1109
+ /**
1110
+ * Headless, accessible multi-select combobox with chips.
1111
+ *
1112
+ * Markup contract (identifier: `stimeo--multi-select`):
1113
+ * <div data-controller="stimeo--multi-select">
1114
+ * <ul data-stimeo--multi-select-target="tags" aria-label="Selected"></ul>
1115
+ * <input type="text" role="combobox" aria-expanded="false"
1116
+ * aria-autocomplete="list" aria-controls="ms-list" aria-activedescendant=""
1117
+ * data-stimeo--multi-select-target="input"
1118
+ * data-action="input->stimeo--multi-select#filter
1119
+ * keydown->stimeo--multi-select#onKeydown
1120
+ * focus->stimeo--multi-select#open" />
1121
+ * <ul id="ms-list" role="listbox" aria-multiselectable="true" hidden
1122
+ * data-stimeo--multi-select-target="list">
1123
+ * <li id="ms-opt-1" role="option" aria-selected="false" data-value="apple"
1124
+ * data-stimeo--multi-select-target="option"
1125
+ * data-action="click->stimeo--multi-select#toggleOption">Apple</li>
1126
+ * </ul>
1127
+ * <span role="status" aria-live="polite" class="visually-hidden"
1128
+ * data-stimeo--multi-select-target="status"></span>
1129
+ * <template data-stimeo--multi-select-target="tagTemplate">…</template>
1130
+ * </div>
1131
+ *
1132
+ * Implements the WAI-ARIA APG **Combobox** pattern in its list-autocomplete,
1133
+ * multi-select form. Focus stays on the input; the active option is tracked with
1134
+ * `aria-activedescendant` and selection with `aria-selected`. Selected options are
1135
+ * mirrored as removable chips. For single selection use {@link ListboxController}
1136
+ * or Combobox; for free-text tags use {@link TagsInputController}.
1137
+ *
1138
+ * Behavior provided:
1139
+ * - Typing filters options by substring and opens the list; `ArrowDown`/`ArrowUp`
1140
+ * (wrapping), `Home`/`End` move the active option; `Enter` toggles it (the list
1141
+ * stays open); `Escape`/`Tab`/outside click close.
1142
+ * - Toggling syncs `aria-selected`, adds/removes a `Remove {label}` chip, mirrors
1143
+ * the live region, and dispatches `stimeo--multi-select:change` with `values`;
1144
+ * filtering dispatches `stimeo--multi-select:filter` for async candidates.
1145
+ * - The chips are one roving Tab stop: `ArrowLeft`/`ArrowRight` move between them,
1146
+ * `Delete`/`Backspace` remove the focused chip, and `Backspace` on an empty
1147
+ * input removes the last; removal re-homes focus to a neighbor or the input.
1148
+ * - `max` caps the selection (`0` = unlimited).
1149
+ */
1150
+ declare class MultiSelectController extends Controller<HTMLElement> {
1151
+ #private;
1152
+ static targets: string[];
1153
+ static values: {
1154
+ max: {
1155
+ type: NumberConstructor;
1156
+ default: number;
1157
+ };
1158
+ };
1159
+ static actions: readonly ["close", "filter", "onKeydown", "open", "toggleOption"];
1160
+ static events: readonly ["change", "filter"];
1161
+ readonly inputTarget: HTMLInputElement;
1162
+ readonly listTarget: HTMLElement;
1163
+ readonly optionTargets: HTMLElement[];
1164
+ readonly tagsTarget: HTMLElement;
1165
+ readonly tagTargets: HTMLElement[];
1166
+ readonly tagTemplateTarget: HTMLTemplateElement;
1167
+ readonly statusTarget: HTMLElement;
1168
+ readonly hasListTarget: boolean;
1169
+ readonly hasTagsTarget: boolean;
1170
+ readonly hasTagTemplateTarget: boolean;
1171
+ readonly hasStatusTarget: boolean;
1172
+ maxValue: number;
1173
+ /** Starts closed, syncs chips for any pre-selected options, and listens out. */
1174
+ connect(): void;
1175
+ /** Tears down document and chip listeners on disconnect (Turbo included). */
1176
+ disconnect(): void;
1177
+ /** Filters options by the input substring, opens, and re-seeds the active one. */
1178
+ filter(): void;
1179
+ /** Opens the list and activates the first visible option when none is active. */
1180
+ open(): void;
1181
+ /** Closes the list and clears the active option. */
1182
+ close(): void;
1183
+ /** Routes input keyboard interaction per the multi-select combobox model. */
1184
+ onKeydown(event: KeyboardEvent): void;
1185
+ /** Toggles the clicked option's selection. Bound via `data-action`. */
1186
+ toggleOption(event: Event): void;
1187
+ }
1188
+
1189
+ /**
1190
+ * Headless, accessible **navigation menu** behavior (disclosure navigation).
1191
+ *
1192
+ * Markup contract (identifier: `stimeo--navigation-menu`):
1193
+ * <nav data-controller="stimeo--navigation-menu" aria-label="Main">
1194
+ * <ul>
1195
+ * <li>
1196
+ * <button data-stimeo--navigation-menu-target="trigger"
1197
+ * aria-expanded="false" aria-controls="nav-products"
1198
+ * data-action="click->stimeo--navigation-menu#toggle
1199
+ * keydown->stimeo--navigation-menu#onTriggerKeydown">
1200
+ * Products
1201
+ * </button>
1202
+ * <div id="nav-products" data-stimeo--navigation-menu-target="panel" hidden>
1203
+ * <a href="/a">Product A</a><a href="/b">Product B</a>
1204
+ * </div>
1205
+ * </li>
1206
+ * </ul>
1207
+ * </nav>
1208
+ *
1209
+ * Implements the WAI-ARIA APG **Disclosure** navigation pattern: each top-level
1210
+ * button toggles its sub-panel (`aria-expanded` + `hidden` synced), only one panel
1211
+ * is open at a time, and the panel content is a plain set of links (not a
1212
+ * `role="menu"`). Focus is **not** trapped — `Tab` moves through the links
1213
+ * naturally. `Escape` closes the open panel and returns focus to its trigger; an
1214
+ * outside click or focus leaving the nav closes it. `ArrowLeft`/`ArrowRight` move
1215
+ * focus between triggers without rewriting `tabindex` (they keep their natural Tab
1216
+ * order). Hover open/close is opt-in via `openOnHover`.
1217
+ *
1218
+ * @remarks
1219
+ * Behavior only. Panel layout, mega-menu styling, and animation are the
1220
+ * consumer's CSS. Static placement is CSS; viewport-edge collision avoidance is
1221
+ * delegated to the opt-in `stimeo-ui/positioning` module (never imported here, so
1222
+ * the core stays zero-dependency). For an app command menu with arrow roving and
1223
+ * `role="menu"`, use `stimeo--menubar` instead.
1224
+ */
1225
+ declare class NavigationMenuController extends Controller<HTMLElement> {
1226
+ #private;
1227
+ static targets: string[];
1228
+ static values: {
1229
+ openOnHover: {
1230
+ type: BooleanConstructor;
1231
+ default: boolean;
1232
+ };
1233
+ hoverDelay: {
1234
+ type: NumberConstructor;
1235
+ default: number;
1236
+ };
1237
+ };
1238
+ static actions: readonly ["onTriggerKeydown", "toggle"];
1239
+ readonly triggerTargets: HTMLElement[];
1240
+ readonly panelTargets: HTMLElement[];
1241
+ openOnHoverValue: boolean;
1242
+ hoverDelayValue: number;
1243
+ /** Establishes the closed baseline and the dismissal listeners. */
1244
+ connect(): void;
1245
+ /** Removes every listener and pending hover timer registered in {@link connect}. */
1246
+ disconnect(): void;
1247
+ /** Toggles a trigger's panel (single-open). Bound via `data-action` (click). */
1248
+ toggle(event: Event): void;
1249
+ /** `ArrowLeft`/`ArrowRight` move focus between triggers (keeping Tab order). */
1250
+ onTriggerKeydown(event: KeyboardEvent): void;
1251
+ }
1252
+
1253
+ /**
1254
+ * Headless password show/hide (unmask) toggle behavior.
1255
+ *
1256
+ * Markup contract (identifier: `stimeo--password-reveal`):
1257
+ * <div data-controller="stimeo--password-reveal">
1258
+ * <input type="password" aria-label="Password"
1259
+ * data-stimeo--password-reveal-target="input">
1260
+ * <button type="button" aria-pressed="false" aria-label="Show password"
1261
+ * data-stimeo--password-reveal-target="toggle"
1262
+ * data-action="stimeo--password-reveal#toggle"></button>
1263
+ * </div>
1264
+ *
1265
+ * No dedicated APG pattern; this follows the toggle **Button** practice. The
1266
+ * accessible name stays state-independent ("Show password") while the pressed
1267
+ * state is conveyed by `aria-pressed`.
1268
+ *
1269
+ * @remarks
1270
+ * Behavior only — icon rendering is the consumer's, keyed off `aria-pressed` /
1271
+ * `data-state` (`hidden` / `visible`). Flipping `input.type` can drop focus and
1272
+ * the caret, so when (and only when) the input was the focused element its focus
1273
+ * and selection are restored afterward; when the toggle button holds focus
1274
+ * (keyboard use) focus is left on the button. An optional `autoHide` re-masks
1275
+ * after a delay, and that timer is torn down on disconnect (Turbo).
1276
+ */
1277
+ declare class PasswordRevealController extends Controller<HTMLElement> {
1278
+ #private;
1279
+ static targets: string[];
1280
+ static values: {
1281
+ autoHide: {
1282
+ type: NumberConstructor;
1283
+ default: number;
1284
+ };
1285
+ };
1286
+ static actions: readonly ["toggle"];
1287
+ static events: readonly ["toggle"];
1288
+ readonly inputTarget: HTMLInputElement;
1289
+ readonly toggleTarget: HTMLElement;
1290
+ readonly hasInputTarget: boolean;
1291
+ readonly hasToggleTarget: boolean;
1292
+ autoHideValue: number;
1293
+ connect(): void;
1294
+ disconnect(): void;
1295
+ /** Toggles the input between masked and revealed. Bound via `data-action`. */
1296
+ toggle(): void;
1297
+ }
1298
+
1299
+ /**
1300
+ * Headless, accessible **two-thumb range slider** behavior (APG Slider —
1301
+ * Multi-Thumb). A derivative of {@link SliderController}: it manages two thumbs
1302
+ * (`start` ≤ `end`) that constrain each other.
1303
+ *
1304
+ * Markup contract (identifier: `stimeo--range-slider`):
1305
+ * <div data-controller="stimeo--range-slider"
1306
+ * data-stimeo--range-slider-min-value="0"
1307
+ * data-stimeo--range-slider-max-value="100"
1308
+ * data-stimeo--range-slider-step-value="1"
1309
+ * data-stimeo--range-slider-start-value="20"
1310
+ * data-stimeo--range-slider-end-value="80">
1311
+ * <div data-stimeo--range-slider-target="track"
1312
+ * data-action="pointerdown->stimeo--range-slider#onPointerDown">
1313
+ * <div role="slider" tabindex="0" aria-label="Minimum"
1314
+ * data-stimeo--range-slider-target="startThumb"
1315
+ * data-action="keydown->stimeo--range-slider#onKeydown"></div>
1316
+ * <div role="slider" tabindex="0" aria-label="Maximum"
1317
+ * data-stimeo--range-slider-target="endThumb"
1318
+ * data-action="keydown->stimeo--range-slider#onKeydown"></div>
1319
+ * </div>
1320
+ * </div>
1321
+ *
1322
+ * @remarks
1323
+ * Behavior only — the consumer owns all layout (positioning the thumbs and the
1324
+ * selected range from the fractions). Only the horizontal orientation is handled
1325
+ * in this MVP. Each thumb's movable range is bounded by the *other* thumb's
1326
+ * current value, reflected on its `aria-valuemin`/`aria-valuemax` so assistive
1327
+ * tech announces the live constraint.
1328
+ *
1329
+ * Behavior provided (per focused thumb):
1330
+ * - `ArrowRight`/`ArrowUp` increase and `ArrowLeft`/`ArrowDown` decrease by one
1331
+ * step; `Home`/`End` jump to that thumb's movable min/max; `PageUp`/`PageDown`
1332
+ * move by ten steps. A thumb never crosses the other.
1333
+ * - Pointer press/drag on the track moves the nearest thumb.
1334
+ */
1335
+ declare class RangeSliderController extends Controller<HTMLElement> {
1336
+ #private;
1337
+ static targets: string[];
1338
+ static values: {
1339
+ min: {
1340
+ type: NumberConstructor;
1341
+ default: number;
1342
+ };
1343
+ max: {
1344
+ type: NumberConstructor;
1345
+ default: number;
1346
+ };
1347
+ step: {
1348
+ type: NumberConstructor;
1349
+ default: number;
1350
+ };
1351
+ start: {
1352
+ type: NumberConstructor;
1353
+ default: number;
1354
+ };
1355
+ end: {
1356
+ type: NumberConstructor;
1357
+ default: number;
1358
+ };
1359
+ };
1360
+ static actions: readonly ["onKeydown", "onPointerDown"];
1361
+ static events: readonly ["change"];
1362
+ readonly trackTarget: HTMLElement;
1363
+ readonly startThumbTarget: HTMLElement;
1364
+ readonly endThumbTarget: HTMLElement;
1365
+ readonly hasTrackTarget: boolean;
1366
+ readonly hasStartThumbTarget: boolean;
1367
+ readonly hasEndThumbTarget: boolean;
1368
+ minValue: number;
1369
+ maxValue: number;
1370
+ stepValue: number;
1371
+ startValue: number;
1372
+ endValue: number;
1373
+ /** Normalizes the initial pair (clamped, snapped, ordered) and renders. */
1374
+ connect(): void;
1375
+ /** Cancels any active pointer drag so document listeners never leak. */
1376
+ disconnect(): void;
1377
+ /** Keyboard stepping for whichever thumb is focused (the action's element). */
1378
+ onKeydown(event: KeyboardEvent): void;
1379
+ /** Begins a pointer drag on the track, moving the thumb nearest the press. */
1380
+ onPointerDown(event: PointerEvent): void;
1381
+ }
1382
+
1383
+ /**
1384
+ * Headless "read more / truncate" behavior for visually clamped text.
1385
+ *
1386
+ * Markup contract (identifier: `stimeo--read-more`):
1387
+ * <div data-controller="stimeo--read-more">
1388
+ * <p id="bio" data-stimeo--read-more-target="content" data-state="collapsed">…</p>
1389
+ * <button data-stimeo--read-more-target="trigger"
1390
+ * data-action="stimeo--read-more#toggle"
1391
+ * aria-expanded="false" aria-controls="bio" hidden>Read more</button>
1392
+ * </div>
1393
+ *
1394
+ * There is no dedicated APG widget; the toggle borrows the **Disclosure**
1395
+ * convention (`aria-expanded`). The visual clamp itself (`-webkit-line-clamp`
1396
+ * etc.) is the consumer's CSS, keyed off `data-state`.
1397
+ *
1398
+ * @remarks
1399
+ * Behavior only. The full text always stays in the DOM — the clamp is purely
1400
+ * visual, so assistive technology reads everything regardless of state; here
1401
+ * `aria-expanded` therefore signals the *visual* expansion, not content hidden
1402
+ * from AT. The controller's extra job is **overflow detection**: when the
1403
+ * content is not actually clamped (it fits), the toggle is `hidden` so no
1404
+ * pointless "read more" is offered. This is re-evaluated on resize.
1405
+ */
1406
+ declare class ReadMoreController extends Controller<HTMLElement> {
1407
+ #private;
1408
+ static targets: string[];
1409
+ static values: {
1410
+ collapsed: {
1411
+ type: BooleanConstructor;
1412
+ default: boolean;
1413
+ };
1414
+ };
1415
+ static actions: readonly ["toggle"];
1416
+ readonly contentTarget: HTMLElement;
1417
+ readonly triggerTarget: HTMLElement;
1418
+ readonly hasContentTarget: boolean;
1419
+ readonly hasTriggerTarget: boolean;
1420
+ collapsedValue: boolean;
1421
+ connect(): void;
1422
+ disconnect(): void;
1423
+ /** Toggles between the collapsed (clamped) and expanded states. */
1424
+ toggle(): void;
1425
+ }
1426
+
1427
+ /**
1428
+ * Headless **Scroll Restore** behavior: persists and restores the scroll
1429
+ * position of an inner scrolling region across Turbo Drive navigations (and full
1430
+ * reloads within the same tab session). No APG widget — a pure state-preservation
1431
+ * utility, like {@link import("./scroll_visibility_controller").ScrollVisibilityController}
1432
+ * and the Sticky State Observer.
1433
+ *
1434
+ * Markup contract (identifier: `stimeo--scroll-restore`):
1435
+ * <div data-controller="stimeo--scroll-restore"
1436
+ * data-stimeo--scroll-restore-key-value="sidebar"
1437
+ * style="overflow: auto">
1438
+ * …long content…
1439
+ * </div>
1440
+ *
1441
+ * Turbo swaps the whole `<body>` on navigation, so an inner scroll container is
1442
+ * rebuilt with `scrollTop` reset to 0. Rather than have every app hand-write a
1443
+ * controller for this (which contradicts a "ship the behavior" library), this
1444
+ * persists the offset under a stable key in `sessionStorage` and restores it on
1445
+ * `connect()`.
1446
+ *
1447
+ * @remarks
1448
+ * Behavior only — it sets no ARIA/`data-*`/CSS and never moves focus (restore is
1449
+ * a plain `scrollTop`/`scrollLeft` assignment). The `scroll` listener is internal
1450
+ * and `passive` (no consumer `data-action` needed): each event records the live
1451
+ * offset synchronously, and a `requestAnimationFrame` coalesces the writes to
1452
+ * `sessionStorage`. On `disconnect()` (Turbo navigation included) it flushes the
1453
+ * **last captured** offset rather than re-reading the element — by the time Turbo
1454
+ * fires `disconnect` it has already detached the node, whose `scrollTop` then
1455
+ * reads `0`, so a fresh read would clobber the saved position with `0`. Keying by
1456
+ * `key` (falling back to the element `id`) in `sessionStorage` makes it
1457
+ * multi-instance safe and survives full reloads — unlike a module-scope variable,
1458
+ * which only survives Turbo Drive and assumes a single instance.
1459
+ */
1460
+ declare class ScrollRestoreController extends Controller<HTMLElement> {
1461
+ #private;
1462
+ static values: {
1463
+ key: {
1464
+ type: StringConstructor;
1465
+ default: string;
1466
+ };
1467
+ axis: {
1468
+ type: StringConstructor;
1469
+ default: string;
1470
+ };
1471
+ };
1472
+ keyValue: string;
1473
+ axisValue: string;
1474
+ connect(): void;
1475
+ disconnect(): void;
1476
+ }
1477
+
1478
+ /**
1479
+ * Headless **Separator** behavior (APG Separator pattern).
1480
+ *
1481
+ * Markup contract (identifier: `stimeo--separator`):
1482
+ * <!-- decorative -->
1483
+ * <div data-controller="stimeo--separator" role="separator"
1484
+ * data-stimeo--separator-orientation-value="horizontal"></div>
1485
+ *
1486
+ * <!-- focusable, value-bearing -->
1487
+ * <div data-controller="stimeo--separator" role="separator" tabindex="0"
1488
+ * aria-label="Resize sidebar" aria-orientation="vertical"
1489
+ * aria-valuemin="0" aria-valuemax="100" aria-valuenow="50"
1490
+ * data-stimeo--separator-focusable-value="true"
1491
+ * data-action="keydown->stimeo--separator#onKeydown"></div>
1492
+ *
1493
+ * Most separators are static (`role="separator"` + `aria-orientation`); this
1494
+ * controller adds those semantics and, for the optional **focusable** variant,
1495
+ * keeps `aria-valuenow` in sync and emits arrow-key value changes. The actual
1496
+ * pane resize/drag is out of scope — that belongs to {@link ResizableController}.
1497
+ *
1498
+ * @remarks
1499
+ * Behavior only — line drawing is the consumer's CSS. Increment direction
1500
+ * follows the slider convention: ArrowUp/ArrowRight increase, ArrowDown/ArrowLeft
1501
+ * decrease, scoped to the relevant axis for the orientation.
1502
+ */
1503
+ declare class SeparatorController extends Controller<HTMLElement> {
1504
+ #private;
1505
+ static values: {
1506
+ orientation: {
1507
+ type: StringConstructor;
1508
+ default: string;
1509
+ };
1510
+ focusable: {
1511
+ type: BooleanConstructor;
1512
+ default: boolean;
1513
+ };
1514
+ step: {
1515
+ type: NumberConstructor;
1516
+ default: number;
1517
+ };
1518
+ };
1519
+ static actions: readonly ["onKeydown"];
1520
+ static events: readonly ["change"];
1521
+ orientationValue: string;
1522
+ focusableValue: boolean;
1523
+ stepValue: number;
1524
+ connect(): void;
1525
+ /** Adjusts the value on arrow / Home / End keys (focusable variant only). */
1526
+ onKeydown(event: KeyboardEvent): void;
1527
+ }
1528
+
1529
+ /**
1530
+ * Headless, accessible **responsive collapsible sidebar** behavior.
1531
+ *
1532
+ * Markup contract (identifier: `stimeo--sidebar`):
1533
+ * <div data-controller="stimeo--sidebar"
1534
+ * data-stimeo--sidebar-breakpoint-value="768"
1535
+ * data-stimeo--sidebar-key-value="main-nav">
1536
+ * <header>
1537
+ * <button data-stimeo--sidebar-target="trigger"
1538
+ * data-action="click->stimeo--sidebar#toggle"
1539
+ * aria-expanded="true" aria-controls="app-sidebar">Menu</button>
1540
+ * </header>
1541
+ * <div data-stimeo--sidebar-target="backdrop"
1542
+ * data-action="click->stimeo--sidebar#close" hidden></div>
1543
+ * <aside id="app-sidebar" data-stimeo--sidebar-target="panel"
1544
+ * aria-label="Main" data-mode="inline" data-state="expanded">
1545
+ * <nav aria-label="…">…</nav>
1546
+ * </aside>
1547
+ * </div>
1548
+ * <main>…</main> <!-- a body-level sibling so it can be made inert in overlay -->
1549
+ *
1550
+ * No dedicated APG pattern: the base is **Disclosure** (the trigger's
1551
+ * `aria-expanded` controls the panel's expanded state) and, *below* the
1552
+ * `breakpoint`, it borrows the **Dialog (Modal)** focus behavior via the shared
1553
+ * {@link FocusTrap} (the same trap used by dialog / alert-dialog / drawer).
1554
+ *
1555
+ * Above the breakpoint it is an **inline**, non-modal element that toggles
1556
+ * `expanded`↔`collapsed` (a rail), persisting that preference in `localStorage`.
1557
+ * Below it, it becomes an **overlay** off-canvas panel: opening activates the
1558
+ * trap (focus move, `Tab` cycle, `Escape`, body scroll lock, background `inert`,
1559
+ * focus restore); closing defers `hidden` and the trap teardown until the exit
1560
+ * transition ends (synchronously when there is none).
1561
+ *
1562
+ * @remarks
1563
+ * Behavior only — rail width, slide, and backdrop are the consumer's CSS, keyed
1564
+ * off `data-mode` (`inline`/`overlay`) and `data-state`. `aria-expanded` is an
1565
+ * abstract "is the panel expanded" flag, independent of the visual difference
1566
+ * between an inline collapsed rail (still in the DOM) and an overlay closed panel
1567
+ * (`hidden`/off-canvas). The `role="dialog"` semantics are intentionally **not**
1568
+ * applied — the sidebar stays an `<aside>`/`<nav>` landmark and only borrows the
1569
+ * modal *behavior* (cf. drawer's note on `<aside role="dialog">` conflicts).
1570
+ * The collapsed preference persists across Turbo navigations and full reloads;
1571
+ * the transient overlay-open state never persists, so "back/forward" never
1572
+ * restores a stuck-open menu.
1573
+ */
1574
+ declare class SidebarController extends Controller<HTMLElement> {
1575
+ #private;
1576
+ static targets: string[];
1577
+ static values: {
1578
+ breakpoint: {
1579
+ type: NumberConstructor;
1580
+ default: number;
1581
+ };
1582
+ key: {
1583
+ type: StringConstructor;
1584
+ default: string;
1585
+ };
1586
+ collapsed: {
1587
+ type: BooleanConstructor;
1588
+ default: boolean;
1589
+ };
1590
+ };
1591
+ static actions: readonly ["close", "open", "toggle"];
1592
+ readonly triggerTarget: HTMLElement;
1593
+ readonly panelTarget: HTMLElement;
1594
+ readonly backdropTarget: HTMLElement;
1595
+ readonly hasTriggerTarget: boolean;
1596
+ readonly hasPanelTarget: boolean;
1597
+ readonly hasBackdropTarget: boolean;
1598
+ breakpointValue: number;
1599
+ keyValue: string;
1600
+ collapsedValue: boolean;
1601
+ connect(): void;
1602
+ disconnect(): void;
1603
+ /** Toggles the panel: inline flips collapsed/expanded, overlay flips open/closed. */
1604
+ toggle(): void;
1605
+ /** Shows the panel (inline: expand; overlay: open). */
1606
+ open(): void;
1607
+ /** Hides the panel (inline: collapse; overlay: close). */
1608
+ close(): void;
1609
+ }
1610
+
1611
+ /**
1612
+ * Headless, accessible free-input tags / chips field.
1613
+ *
1614
+ * Markup contract (identifier: `stimeo--tags-input`):
1615
+ * <div data-controller="stimeo--tags-input"
1616
+ * data-stimeo--tags-input-delimiter-value=",">
1617
+ * <ul role="list" aria-label="Tags" data-stimeo--tags-input-target="tags"></ul>
1618
+ * <input type="text" aria-label="Add tag" aria-describedby="tags-help"
1619
+ * data-stimeo--tags-input-target="input"
1620
+ * data-action="keydown->stimeo--tags-input#onKeydown" />
1621
+ * <span role="status" aria-live="polite" class="visually-hidden"
1622
+ * data-stimeo--tags-input-target="status"></span>
1623
+ * <div data-stimeo--tags-input-target="fields"></div>
1624
+ * <template data-stimeo--tags-input-target="tagTemplate">
1625
+ * <li role="listitem" data-stimeo--tags-input-target="tag">
1626
+ * <span data-tags-input-slot="label"></span>
1627
+ * <!-- Removal is delegated on the tags container; no per-chip action needed. -->
1628
+ * <button type="button" tabindex="-1">×</button>
1629
+ * </li>
1630
+ * </template>
1631
+ * </div>
1632
+ *
1633
+ * There is no single established APG pattern; this composes a labeled text input
1634
+ * with a roving-tabindex list of removable chips, mapping to WCAG 2.1.1, 2.4.7,
1635
+ * 4.1.2, 4.1.3, and 1.3.1. Unlike {@link MultiSelectController} (pick from a
1636
+ * candidate list) the user types arbitrary strings.
1637
+ *
1638
+ * Behavior provided:
1639
+ * - `Enter` or the configured delimiter commits the trimmed input as a tag.
1640
+ * - Empty / duplicate / over-limit additions are rejected with
1641
+ * `stimeo--tags-input:reject`; duplicates are allowed when `allowDuplicates`.
1642
+ * - Tags render from the `tagTemplate`, each with a `Remove {label}` button; the
1643
+ * `fields` container mirrors the tag set as `name`d hidden inputs for form
1644
+ * submission, and every change dispatches `stimeo--tags-input:change`.
1645
+ * - The remove buttons form one roving Tab stop: `ArrowLeft`/`ArrowRight` move
1646
+ * between them (right past the end returns to the input), `Delete`/`Backspace`
1647
+ * delete the focused tag, and `Backspace` on an empty input deletes the last.
1648
+ * - Removing a tag moves focus to the neighboring tag, else back to the input.
1649
+ */
1650
+ declare class TagsInputController extends Controller<HTMLElement> {
1651
+ #private;
1652
+ static targets: string[];
1653
+ static values: {
1654
+ delimiter: {
1655
+ type: StringConstructor;
1656
+ default: string;
1657
+ };
1658
+ max: {
1659
+ type: NumberConstructor;
1660
+ default: number;
1661
+ };
1662
+ allowDuplicates: {
1663
+ type: BooleanConstructor;
1664
+ default: boolean;
1665
+ };
1666
+ name: {
1667
+ type: StringConstructor;
1668
+ default: string;
1669
+ };
1670
+ };
1671
+ static actions: readonly ["onKeydown"];
1672
+ static events: readonly ["change", "reject"];
1673
+ readonly inputTarget: HTMLInputElement;
1674
+ readonly tagsTarget: HTMLElement;
1675
+ readonly tagTargets: HTMLElement[];
1676
+ readonly tagTemplateTarget: HTMLTemplateElement;
1677
+ readonly statusTarget: HTMLElement;
1678
+ readonly fieldsTarget: HTMLElement;
1679
+ readonly hasStatusTarget: boolean;
1680
+ readonly hasFieldsTarget: boolean;
1681
+ readonly hasTagTemplateTarget: boolean;
1682
+ delimiterValue: string;
1683
+ maxValue: number;
1684
+ allowDuplicatesValue: boolean;
1685
+ nameValue: string;
1686
+ /** Wires tag-list keyboard navigation and removal, and seeds the single Tab stop. */
1687
+ connect(): void;
1688
+ /** Releases the delegated listeners so no handler outlives the element. */
1689
+ disconnect(): void;
1690
+ /** Commits on `Enter`/delimiter and deletes the last tag on empty `Backspace`. */
1691
+ onKeydown(event: KeyboardEvent): void;
1692
+ }
1693
+
1694
+ /**
1695
+ * Headless, accessible **time picker** behavior. Each segment (hour, minute,
1696
+ * optional second, optional AM/PM) is an APG **Spinbutton**; the controller
1697
+ * composes them into an `HH:MM[:SS]` value mirrored to a hidden field.
1698
+ *
1699
+ * Markup contract (identifier: `stimeo--time-picker`):
1700
+ * <div data-controller="stimeo--time-picker"
1701
+ * data-stimeo--time-picker-hour-cycle-value="24"
1702
+ * role="group" aria-label="Time">
1703
+ * <span role="spinbutton" aria-label="Hours" tabindex="0"
1704
+ * aria-valuenow="9" aria-valuemin="0" aria-valuemax="23" aria-valuetext="09"
1705
+ * data-segment="hour" data-stimeo--time-picker-target="segment"
1706
+ * data-action="keydown->stimeo--time-picker#onKeydown">09</span>
1707
+ * <span aria-hidden="true">:</span>
1708
+ * <span role="spinbutton" aria-label="Minutes" tabindex="0"
1709
+ * data-segment="minute" data-stimeo--time-picker-target="segment"
1710
+ * data-action="keydown->stimeo--time-picker#onKeydown">30</span>
1711
+ * <input type="hidden" data-stimeo--time-picker-target="field" />
1712
+ * </div>
1713
+ *
1714
+ * @remarks
1715
+ * Behavior only — no styling, no locale formatting. Every segment is its own Tab
1716
+ * stop (multi-tabstop, *not* roving); `ArrowLeft`/`ArrowRight` are an auxiliary
1717
+ * move between segments. `ArrowUp`/`ArrowDown` step the focused segment, wrapping
1718
+ * (and, when `wrap` is set, carrying minutes→hours and seconds→minutes). Typing
1719
+ * digits enters a value directly and advances after two digits. AM/PM is modeled
1720
+ * as a `meridiem` spinbutton that toggles; the hidden field is always 24-hour.
1721
+ */
1722
+ declare class TimePickerController extends Controller<HTMLElement> {
1723
+ #private;
1724
+ static targets: string[];
1725
+ static values: {
1726
+ hourCycle: {
1727
+ type: NumberConstructor;
1728
+ default: number;
1729
+ };
1730
+ step: {
1731
+ type: NumberConstructor;
1732
+ default: number;
1733
+ };
1734
+ seconds: {
1735
+ type: BooleanConstructor;
1736
+ default: boolean;
1737
+ };
1738
+ wrap: {
1739
+ type: BooleanConstructor;
1740
+ default: boolean;
1741
+ };
1742
+ };
1743
+ static actions: readonly ["onKeydown"];
1744
+ static events: readonly ["change"];
1745
+ readonly segmentTargets: HTMLElement[];
1746
+ readonly fieldTarget: HTMLInputElement;
1747
+ readonly hasFieldTarget: boolean;
1748
+ hourCycleValue: number;
1749
+ stepValue: number;
1750
+ secondsValue: boolean;
1751
+ wrapValue: boolean;
1752
+ /** Seeds each segment from its initial `aria-valuenow` and syncs the field. */
1753
+ connect(): void;
1754
+ /** Handles stepping, inter-segment focus moves, jumps, and direct entry. */
1755
+ onKeydown(event: KeyboardEvent): void;
1756
+ }
1757
+
1758
+ /**
1759
+ * Headless, accessible single-select tree view.
1760
+ *
1761
+ * Markup contract (identifier: `stimeo--tree-view`):
1762
+ * <ul data-controller="stimeo--tree-view" role="tree" aria-label="Files">
1763
+ * <li role="treeitem" aria-expanded="false" aria-selected="false" tabindex="0"
1764
+ * data-stimeo--tree-view-target="item"
1765
+ * data-action="keydown->stimeo--tree-view#onKeydown
1766
+ * click->stimeo--tree-view#onClick">
1767
+ * <span>src</span>
1768
+ * <ul role="group" data-stimeo--tree-view-target="group" hidden>
1769
+ * <li role="treeitem" aria-selected="false" tabindex="-1"
1770
+ * data-stimeo--tree-view-target="item" data-action="…">…</li>
1771
+ * </ul>
1772
+ * </li>
1773
+ * </ul>
1774
+ *
1775
+ * Implements the WAI-ARIA APG **Tree View** (single-select) pattern. Parent/child
1776
+ * structure is read from the DOM nesting (`treeitem` → child `group`). The whole
1777
+ * tree is one Tab stop (roving tabindex); arrows navigate visible items, expand /
1778
+ * collapse, and move between parent and child.
1779
+ *
1780
+ * Behavior provided:
1781
+ * - `ArrowDown`/`ArrowUp` move between visible items; `Home`/`End` jump to the
1782
+ * first / last visible item; printable characters typeahead by label prefix.
1783
+ * - `ArrowRight` expands a collapsed parent or steps into its first child;
1784
+ * `ArrowLeft` collapses an expanded parent or steps to the parent item.
1785
+ * - `Enter`/`Space`/click select the item (single selection via `aria-selected`).
1786
+ * - `aria-expanded` and each child `group`'s `hidden` stay in sync, dispatching
1787
+ * `stimeo--tree-view:toggle`; selection dispatches `stimeo--tree-view:select`.
1788
+ */
1789
+ declare class TreeViewController extends Controller<HTMLElement> {
1790
+ #private;
1791
+ static targets: string[];
1792
+ static actions: readonly ["onClick", "onKeydown"];
1793
+ static events: readonly ["select", "toggle"];
1794
+ readonly itemTargets: HTMLElement[];
1795
+ /** Establishes the single tab stop (keeps an existing one, else the first). */
1796
+ connect(): void;
1797
+ /** Clears the pending typeahead-reset timer. */
1798
+ disconnect(): void;
1799
+ /**
1800
+ * Routes tree keyboard interaction. Because `treeitem`s nest, only the handler
1801
+ * on the nearest item to the event target acts; the same keydown bubbling to an
1802
+ * ancestor item's handler is ignored to avoid double moves / selections.
1803
+ */
1804
+ onKeydown(event: KeyboardEvent): void;
1805
+ /** Selects the clicked item (nearest to the target only). */
1806
+ onClick(event: Event): void;
1807
+ }
1808
+
1809
+ /**
1810
+ * Maps Stimeo controller identifiers to their controller classes.
1811
+ * Identifiers use the official `stimeo--` namespace.
1812
+ */
1813
+ declare const stimeoControllers: {
1814
+ readonly "stimeo--accordion": typeof AccordionController;
1815
+ readonly "stimeo--alert-dialog": typeof AlertDialogController;
1816
+ readonly "stimeo--announcer": typeof AnnouncerController;
1817
+ readonly "stimeo--aspect-ratio": typeof AspectRatioController;
1818
+ readonly "stimeo--auto-submit": typeof AutoSubmitController;
1819
+ readonly "stimeo--avatar": typeof AvatarController;
1820
+ readonly "stimeo--breadcrumb": typeof BreadcrumbController;
1821
+ readonly "stimeo--bulk-select": typeof BulkSelectController;
1822
+ readonly "stimeo--calendar": typeof CalendarController;
1823
+ readonly "stimeo--carousel": typeof CarouselController;
1824
+ readonly "stimeo--character-counter": typeof CharacterCounterController;
1825
+ readonly "stimeo--checkbox": typeof CheckboxController;
1826
+ readonly "stimeo--clipboard": typeof ClipboardController;
1827
+ readonly "stimeo--collapsible": typeof CollapsibleController;
1828
+ readonly "stimeo--color-picker": typeof ColorPickerController;
1829
+ readonly "stimeo--combobox": typeof ComboboxController;
1830
+ readonly "stimeo--command-palette": typeof CommandPaletteController;
1831
+ readonly "stimeo--conditional-fields": typeof ConditionalFieldsController;
1832
+ readonly "stimeo--confirm": typeof ConfirmController;
1833
+ readonly "stimeo--context-menu": typeof ContextMenuController;
1834
+ readonly "stimeo--countdown": typeof CountdownController;
1835
+ readonly "stimeo--currency-input": typeof CurrencyInputController;
1836
+ readonly "stimeo--data-grid": typeof DataGridController;
1837
+ readonly "stimeo--date-range-picker": typeof DateRangePickerController;
1838
+ readonly "stimeo--dialog": typeof DialogController;
1839
+ readonly "stimeo--direct-upload": typeof DirectUploadController;
1840
+ readonly "stimeo--dirty-form": typeof DirtyFormController;
1841
+ readonly "stimeo--dismissible": typeof DismissibleController;
1842
+ readonly "stimeo--drawer": typeof DrawerController;
1843
+ readonly "stimeo--dropdown": typeof DropdownController;
1844
+ readonly "stimeo--editable": typeof EditableController;
1845
+ readonly "stimeo--empty-state": typeof EmptyStateController;
1846
+ readonly "stimeo--file-dropzone": typeof FileDropzoneController;
1847
+ readonly "stimeo--filter": typeof FilterController;
1848
+ readonly "stimeo--flash": typeof FlashController;
1849
+ readonly "stimeo--focus": typeof FocusController;
1850
+ readonly "stimeo--form-field": typeof FormFieldController;
1851
+ readonly "stimeo--form-validation": typeof FormValidationController;
1852
+ readonly "stimeo--frame-loading": typeof FrameLoadingController;
1853
+ readonly "stimeo--highlight": typeof HighlightController;
1854
+ readonly "stimeo--hover-card": typeof HoverCardController;
1855
+ readonly "stimeo--idle": typeof IdleController;
1856
+ readonly "stimeo--input-mask": typeof InputMaskController;
1857
+ readonly "stimeo--lazy-frame": typeof LazyFrameController;
1858
+ readonly "stimeo--listbox": typeof ListboxController;
1859
+ readonly "stimeo--local-time": typeof LocalTimeController;
1860
+ readonly "stimeo--masonry": typeof MasonryController;
1861
+ readonly "stimeo--menu": typeof MenuController;
1862
+ readonly "stimeo--menubar": typeof MenubarController;
1863
+ readonly "stimeo--meter": typeof MeterController;
1864
+ readonly "stimeo--multi-select": typeof MultiSelectController;
1865
+ readonly "stimeo--navigation-menu": typeof NavigationMenuController;
1866
+ readonly "stimeo--nested-form": typeof NestedFormController;
1867
+ readonly "stimeo--network-status": typeof NetworkStatusController;
1868
+ readonly "stimeo--number-input": typeof NumberInputController;
1869
+ readonly "stimeo--otp": typeof OtpController;
1870
+ readonly "stimeo--overflow-indicator": typeof OverflowIndicatorController;
1871
+ readonly "stimeo--overflow-menu": typeof OverflowMenuController;
1872
+ readonly "stimeo--pagination": typeof PaginationController;
1873
+ readonly "stimeo--password-reveal": typeof PasswordRevealController;
1874
+ readonly "stimeo--password-strength": typeof PasswordStrengthController;
1875
+ readonly "stimeo--persist": typeof PersistController;
1876
+ readonly "stimeo--popover": typeof PopoverController;
1877
+ readonly "stimeo--portal": typeof PortalController;
1878
+ readonly "stimeo--preview-guard": typeof PreviewGuardController;
1879
+ readonly "stimeo--progress": typeof ProgressController;
1880
+ readonly "stimeo--radio-group": typeof RadioGroupController;
1881
+ readonly "stimeo--range-slider": typeof RangeSliderController;
1882
+ readonly "stimeo--rating": typeof RatingController;
1883
+ readonly "stimeo--read-more": typeof ReadMoreController;
1884
+ readonly "stimeo--relative-time": typeof RelativeTimeController;
1885
+ readonly "stimeo--reset-before-cache": typeof ResetBeforeCacheController;
1886
+ readonly "stimeo--resizable": typeof ResizableController;
1887
+ readonly "stimeo--roving": typeof RovingController;
1888
+ readonly "stimeo--scroll-area": typeof ScrollAreaController;
1889
+ readonly "stimeo--scroll-restore": typeof ScrollRestoreController;
1890
+ readonly "stimeo--scroll-visibility": typeof ScrollVisibilityController;
1891
+ readonly "stimeo--scrollspy": typeof ScrollspyController;
1892
+ readonly "stimeo--separator": typeof SeparatorController;
1893
+ readonly "stimeo--sidebar": typeof SidebarController;
1894
+ readonly "stimeo--skeleton": typeof SkeletonController;
1895
+ readonly "stimeo--slider": typeof SliderController;
1896
+ readonly "stimeo--spinner": typeof SpinnerController;
1897
+ readonly "stimeo--step-indicator": typeof StepIndicatorController;
1898
+ readonly "stimeo--stepper": typeof StepperController;
1899
+ readonly "stimeo--stick-to-bottom": typeof StickToBottomController;
1900
+ readonly "stimeo--sticky-observer": typeof StickyObserverController;
1901
+ readonly "stimeo--submit-once": typeof SubmitOnceController;
1902
+ readonly "stimeo--switch": typeof SwitchController;
1903
+ readonly "stimeo--tabs": typeof TabsController;
1904
+ readonly "stimeo--tags-input": typeof TagsInputController;
1905
+ readonly "stimeo--textarea-autosize": typeof TextareaAutosizeController;
1906
+ readonly "stimeo--theme": typeof ThemeController;
1907
+ readonly "stimeo--time-picker": typeof TimePickerController;
1908
+ readonly "stimeo--toast": typeof ToastController;
1909
+ readonly "stimeo--toggle-group": typeof ToggleGroupController;
1910
+ readonly "stimeo--toolbar": typeof ToolbarController;
1911
+ readonly "stimeo--tooltip": typeof TooltipController;
1912
+ readonly "stimeo--transition": typeof TransitionController;
1913
+ readonly "stimeo--tree-view": typeof TreeViewController;
1914
+ };
1915
+ /**
1916
+ * Registers all official Stimeo controllers on a Stimulus Application.
1917
+ *
1918
+ * @param application - The Stimulus application to register controllers on.
1919
+ *
1920
+ * @example
1921
+ * ```ts
1922
+ * import { Application } from "@hotwired/stimulus";
1923
+ * import { registerStimeo } from "stimeo-ui";
1924
+ *
1925
+ * const application = Application.start();
1926
+ * registerStimeo(application);
1927
+ * ```
1928
+ */
1929
+ declare function registerStimeo(application: Application): void;
1930
+
1931
+ export { AccordionController, AlertDialogController, AnnouncerController, AspectRatioController, AutoSubmitController, AvatarController, BreadcrumbController, BulkSelectController, CalendarController, CarouselController, CharacterCounterController, CheckboxController, ClipboardController, CollapsibleController, ColorPickerController, ComboboxController, CommandPaletteController, ConditionalFieldsController, ConfirmController, ContextMenuController, CountdownController, CurrencyInputController, DataGridController, DateRangePickerController, DialogController, DirectUploadController, DirtyFormController, DismissibleController, DrawerController, DropdownController, EditableController, EmptyStateController, FileDropzoneController, FilterController, FlashController, FocusController, FormFieldController, FormValidationController, FrameLoadingController, HighlightController, HoverCardController, IdleController, InputMaskController, LazyFrameController, ListboxController, LocalTimeController, MasonryController, MenuController, MenubarController, MeterController, MultiSelectController, NavigationMenuController, NestedFormController, NetworkStatusController, NumberInputController, OtpController, OverflowIndicatorController, OverflowMenuController, PaginationController, PasswordRevealController, PasswordStrengthController, PersistController, PopoverController, PortalController, PreviewGuardController, ProgressController, RadioGroupController, RangeSliderController, RatingController, ReadMoreController, RelativeTimeController, ResetBeforeCacheController, ResizableController, RovingController, ScrollAreaController, ScrollRestoreController, ScrollVisibilityController, ScrollspyController, SeparatorController, SidebarController, SkeletonController, SliderController, SpinnerController, StepIndicatorController, StepperController, StickToBottomController, StickyObserverController, SubmitOnceController, SwitchController, TabsController, TagsInputController, TextareaAutosizeController, ThemeController, TimePickerController, ToastController, ToggleGroupController, ToolbarController, TooltipController, TransitionController, TreeViewController, registerStimeo, stimeoControllers };