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,186 @@
1
+ import { Controller } from '@hotwired/stimulus';
2
+
3
+ // src/controllers/combobox_controller.ts
4
+ var ComboboxController = class extends Controller {
5
+ static targets = ["input", "list", "option"];
6
+ static actions = ["close", "filter", "onKeydown", "open", "selectByClick"];
7
+ static events = ["selected"];
8
+ /** Index into the *visible* options of the active option, or -1 if none. */
9
+ #activeIndex = -1;
10
+ /**
11
+ * Suppresses {@link open} for the duration of the programmatic re-focus in
12
+ * `#select`, so committing a value (which returns focus to the input)
13
+ * does not immediately re-open the listbox via a `focus`-bound action.
14
+ */
15
+ #suppressOpen = false;
16
+ /** Starts closed with no active option and registers the outside-click listener. */
17
+ connect() {
18
+ this.close();
19
+ document.addEventListener("click", this.#onOutsideClick);
20
+ }
21
+ /** Removes the document-level listener registered in {@link connect}. */
22
+ disconnect() {
23
+ document.removeEventListener("click", this.#onOutsideClick);
24
+ }
25
+ /** Filters options by the current input value and opens the listbox. */
26
+ filter() {
27
+ this.open();
28
+ }
29
+ /**
30
+ * Opens the listbox, re-filtering the options against the current input value
31
+ * so the visible options and empty state always match what is typed (e.g.
32
+ * re-opening with a stale non-matching value still surfaces the empty state).
33
+ */
34
+ open() {
35
+ if (!this.hasListTarget || this.#suppressOpen) return;
36
+ this.#applyFilter();
37
+ this.listTarget.hidden = false;
38
+ this.inputTarget.setAttribute("aria-expanded", "true");
39
+ this.#setActive(-1);
40
+ this.#reflectEmptyState();
41
+ }
42
+ /**
43
+ * Hides options that don't match the current input value (case-insensitive
44
+ * substring). An empty query shows every option. Does not change open state.
45
+ */
46
+ #applyFilter() {
47
+ const query = this.inputTarget.value.trim().toLowerCase();
48
+ for (const option of this.optionTargets) {
49
+ const text = (option.textContent ?? "").trim().toLowerCase();
50
+ option.hidden = query.length > 0 && !text.includes(query);
51
+ }
52
+ }
53
+ /** Closes the listbox, clears the active option, and updates ARIA state. */
54
+ close() {
55
+ if (!this.hasListTarget) return;
56
+ this.listTarget.hidden = true;
57
+ this.inputTarget.setAttribute("aria-expanded", "false");
58
+ this.#setActive(-1);
59
+ this.element.removeAttribute("data-stimeo--combobox-empty");
60
+ }
61
+ /** Routes keyboard interaction per the APG combobox model. */
62
+ onKeydown(event) {
63
+ switch (event.key) {
64
+ case "ArrowDown": {
65
+ event.preventDefault();
66
+ if (this.#isClosed) this.open();
67
+ const visible = this.#visibleOptions();
68
+ if (visible.length > 0) {
69
+ const next = this.#activeIndex === -1 ? 0 : (this.#activeIndex + 1) % visible.length;
70
+ this.#setActive(next);
71
+ }
72
+ break;
73
+ }
74
+ case "ArrowUp": {
75
+ event.preventDefault();
76
+ if (this.#isClosed) this.open();
77
+ const visible = this.#visibleOptions();
78
+ if (visible.length > 0) {
79
+ const next = this.#activeIndex === -1 ? visible.length - 1 : (this.#activeIndex - 1 + visible.length) % visible.length;
80
+ this.#setActive(next);
81
+ }
82
+ break;
83
+ }
84
+ case "Home":
85
+ if (!this.#isClosed && this.#visibleOptions().length > 0) {
86
+ event.preventDefault();
87
+ this.#setActive(0);
88
+ }
89
+ break;
90
+ case "End": {
91
+ const visible = this.#visibleOptions();
92
+ if (!this.#isClosed && visible.length > 0) {
93
+ event.preventDefault();
94
+ this.#setActive(visible.length - 1);
95
+ }
96
+ break;
97
+ }
98
+ case "Enter": {
99
+ const visible = this.#visibleOptions();
100
+ const active = this.#activeIndex === -1 ? void 0 : visible[this.#activeIndex];
101
+ if (active) {
102
+ event.preventDefault();
103
+ this.#select(active);
104
+ }
105
+ break;
106
+ }
107
+ case "Escape":
108
+ event.preventDefault();
109
+ this.close();
110
+ break;
111
+ case "Tab":
112
+ this.close();
113
+ break;
114
+ }
115
+ }
116
+ /**
117
+ * Closes the listbox when a click lands outside the combobox. Mirrors the menu
118
+ * button's outside-click behavior; clicks on an option are inside the element,
119
+ * so `#select` (not this handler) closes the popup after committing.
120
+ */
121
+ #onOutsideClick = (event) => {
122
+ if (!this.#isClosed && !this.element.contains(event.target)) this.close();
123
+ };
124
+ /** Selects the clicked option. Bound via `data-action` (click). */
125
+ selectByClick(event) {
126
+ const option = event.currentTarget.closest('[role="option"]');
127
+ if (option) this.#select(option);
128
+ }
129
+ /** Commits an option: fills the input, closes the listbox, notifies listeners. */
130
+ #select(option) {
131
+ const value = option.dataset.value ?? (option.textContent ?? "").trim();
132
+ const changed = this.inputTarget.value !== value;
133
+ this.inputTarget.value = value;
134
+ this.close();
135
+ this.#suppressOpen = true;
136
+ this.inputTarget.focus();
137
+ this.#suppressOpen = false;
138
+ if (changed) {
139
+ this.inputTarget.dispatchEvent(new Event("change", { bubbles: true }));
140
+ }
141
+ this.dispatch("selected", { detail: { value } });
142
+ }
143
+ /**
144
+ * Reflects whether the open listbox currently has zero matching options by
145
+ * toggling `data-stimeo--combobox-empty` on the root element. Behavior only:
146
+ * consumers decide how to present the empty state (hide the list, show a
147
+ * "no results" node, etc.) via CSS keyed off this attribute.
148
+ */
149
+ #reflectEmptyState() {
150
+ const empty = !this.#isClosed && this.#visibleOptions().length === 0;
151
+ if (empty) {
152
+ this.element.setAttribute("data-stimeo--combobox-empty", "");
153
+ } else {
154
+ this.element.removeAttribute("data-stimeo--combobox-empty");
155
+ }
156
+ }
157
+ /**
158
+ * Marks the visible option at `index` active via `aria-selected` and the
159
+ * input's `aria-activedescendant`. Pass `-1` to clear the active option.
160
+ */
161
+ #setActive(index) {
162
+ this.#activeIndex = index;
163
+ const visible = this.#visibleOptions();
164
+ const active = index === -1 ? null : visible[index];
165
+ for (const option of this.optionTargets) {
166
+ option.setAttribute("aria-selected", option === active ? "true" : "false");
167
+ }
168
+ if (active?.id) {
169
+ this.inputTarget.setAttribute("aria-activedescendant", active.id);
170
+ } else {
171
+ this.inputTarget.removeAttribute("aria-activedescendant");
172
+ }
173
+ }
174
+ /** The options currently shown (not filtered out). */
175
+ #visibleOptions() {
176
+ return this.optionTargets.filter((option) => !option.hidden);
177
+ }
178
+ /** Whether the listbox is currently hidden. */
179
+ get #isClosed() {
180
+ return !this.hasListTarget || this.listTarget.hidden !== false;
181
+ }
182
+ };
183
+
184
+ export { ComboboxController };
185
+ //# sourceMappingURL=combobox_controller.js.map
186
+ //# sourceMappingURL=combobox_controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/controllers/combobox_controller.ts"],"names":[],"mappings":"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","file":"combobox_controller.js","sourcesContent":["import { Controller } from \"@hotwired/stimulus\";\n\n/**\n * Headless, accessible combobox behavior (list autocomplete).\n *\n * Markup contract (identifier: `stimeo--combobox`):\n * <div data-controller=\"stimeo--combobox\">\n * <input type=\"text\" role=\"combobox\" aria-expanded=\"false\"\n * aria-autocomplete=\"list\" aria-controls=\"listbox\"\n * data-stimeo--combobox-target=\"input\"\n * data-action=\"input->stimeo--combobox#filter\n * keydown->stimeo--combobox#onKeydown\n * focus->stimeo--combobox#open\n * click->stimeo--combobox#open\" />\n * <ul id=\"listbox\" role=\"listbox\" data-stimeo--combobox-target=\"list\" hidden>\n * <li role=\"option\" id=\"opt-apple\" data-value=\"apple\"\n * data-stimeo--combobox-target=\"option\"\n * data-action=\"click->stimeo--combobox#selectByClick\">Apple</li>\n * <!-- more options -->\n * </ul>\n * </div>\n *\n * Implements the WAI-ARIA APG **Combobox** pattern with a listbox popup and\n * list-autocomplete. Focus stays in the input; the active option is tracked with\n * `aria-activedescendant` rather than by moving DOM focus.\n *\n * @remarks\n * Behavior only. Options are authored in the DOM; the controller filters them by\n * toggling each option's `hidden` attribute (case-insensitive substring match on\n * its text). The consumer owns styling, typically keyed off `[aria-selected]`.\n * When an open listbox has no matching options, the root element gets\n * `data-stimeo--combobox-empty` so the consumer can style the empty state (hide\n * the list, show a \"no results\" node, …) — the library imposes no visuals.\n *\n * Behavior provided:\n * - Typing filters the options and opens the listbox.\n * - Focusing or clicking the input opens the listbox, re-filtered against the\n * current value (so re-opening with a non-matching value keeps the empty state).\n * - `ArrowDown`/`ArrowUp` move the active option (wrapping); `Enter` selects it;\n * `Escape` closes the listbox; `Home`/`End` jump to the first/last visible\n * option.\n * - Selecting an option fills the input (with the option's `data-value` if set,\n * otherwise its text) and closes the listbox.\n * - A click outside the combobox closes the listbox.\n */\nexport class ComboboxController extends Controller<HTMLElement> {\n static override targets = [\"input\", \"list\", \"option\"];\n static actions = [\"close\", \"filter\", \"onKeydown\", \"open\", \"selectByClick\"] as const;\n static events = [\"selected\"] as const;\n\n declare readonly inputTarget: HTMLInputElement;\n declare readonly listTarget: HTMLElement;\n declare readonly optionTargets: HTMLElement[];\n declare readonly hasInputTarget: boolean;\n declare readonly hasListTarget: boolean;\n\n /** Index into the *visible* options of the active option, or -1 if none. */\n #activeIndex = -1;\n /**\n * Suppresses {@link open} for the duration of the programmatic re-focus in\n * `#select`, so committing a value (which returns focus to the input)\n * does not immediately re-open the listbox via a `focus`-bound action.\n */\n #suppressOpen = false;\n\n /** Starts closed with no active option and registers the outside-click listener. */\n override connect(): void {\n this.close();\n document.addEventListener(\"click\", this.#onOutsideClick);\n }\n\n /** Removes the document-level listener registered in {@link connect}. */\n override disconnect(): void {\n document.removeEventListener(\"click\", this.#onOutsideClick);\n }\n\n /** Filters options by the current input value and opens the listbox. */\n filter(): void {\n this.open();\n }\n\n /**\n * Opens the listbox, re-filtering the options against the current input value\n * so the visible options and empty state always match what is typed (e.g.\n * re-opening with a stale non-matching value still surfaces the empty state).\n */\n open(): void {\n if (!this.hasListTarget || this.#suppressOpen) return;\n this.#applyFilter();\n this.listTarget.hidden = false;\n this.inputTarget.setAttribute(\"aria-expanded\", \"true\");\n this.#setActive(-1);\n this.#reflectEmptyState();\n }\n\n /**\n * Hides options that don't match the current input value (case-insensitive\n * substring). An empty query shows every option. Does not change open state.\n */\n #applyFilter(): void {\n const query = this.inputTarget.value.trim().toLowerCase();\n for (const option of this.optionTargets) {\n const text = (option.textContent ?? \"\").trim().toLowerCase();\n option.hidden = query.length > 0 && !text.includes(query);\n }\n }\n\n /** Closes the listbox, clears the active option, and updates ARIA state. */\n close(): void {\n if (!this.hasListTarget) return;\n this.listTarget.hidden = true;\n this.inputTarget.setAttribute(\"aria-expanded\", \"false\");\n this.#setActive(-1);\n this.element.removeAttribute(\"data-stimeo--combobox-empty\");\n }\n\n /** Routes keyboard interaction per the APG combobox model. */\n onKeydown(event: KeyboardEvent): void {\n switch (event.key) {\n case \"ArrowDown\": {\n event.preventDefault();\n // open() re-filters, so read the visible set afterwards.\n if (this.#isClosed) this.open();\n const visible = this.#visibleOptions();\n if (visible.length > 0) {\n const next = this.#activeIndex === -1 ? 0 : (this.#activeIndex + 1) % visible.length;\n this.#setActive(next);\n }\n break;\n }\n case \"ArrowUp\": {\n event.preventDefault();\n if (this.#isClosed) this.open();\n const visible = this.#visibleOptions();\n if (visible.length > 0) {\n // From the input (no active option) ArrowUp jumps to the last option,\n // per the APG; otherwise it wraps backwards.\n const next =\n this.#activeIndex === -1\n ? visible.length - 1\n : (this.#activeIndex - 1 + visible.length) % visible.length;\n this.#setActive(next);\n }\n break;\n }\n case \"Home\":\n if (!this.#isClosed && this.#visibleOptions().length > 0) {\n event.preventDefault();\n this.#setActive(0);\n }\n break;\n case \"End\": {\n const visible = this.#visibleOptions();\n if (!this.#isClosed && visible.length > 0) {\n event.preventDefault();\n this.#setActive(visible.length - 1);\n }\n break;\n }\n case \"Enter\": {\n const visible = this.#visibleOptions();\n const active = this.#activeIndex === -1 ? undefined : visible[this.#activeIndex];\n if (active) {\n event.preventDefault();\n this.#select(active);\n }\n break;\n }\n case \"Escape\":\n event.preventDefault();\n this.close();\n break;\n case \"Tab\":\n // Let focus leave naturally, but don't keep a stale popup open.\n this.close();\n break;\n default:\n break;\n }\n }\n\n /**\n * Closes the listbox when a click lands outside the combobox. Mirrors the menu\n * button's outside-click behavior; clicks on an option are inside the element,\n * so `#select` (not this handler) closes the popup after committing.\n */\n readonly #onOutsideClick = (event: MouseEvent): void => {\n if (!this.#isClosed && !this.element.contains(event.target as Node)) this.close();\n };\n\n /** Selects the clicked option. Bound via `data-action` (click). */\n selectByClick(event: Event): void {\n const option = (event.currentTarget as HTMLElement).closest<HTMLElement>('[role=\"option\"]');\n if (option) this.#select(option);\n }\n\n /** Commits an option: fills the input, closes the listbox, notifies listeners. */\n #select(option: HTMLElement): void {\n const value = option.dataset.value ?? (option.textContent ?? \"\").trim();\n const changed = this.inputTarget.value !== value;\n this.inputTarget.value = value;\n this.close();\n // Returning focus to the input would re-trigger a `focus`-bound open(); guard\n // it so the listbox stays closed after a selection.\n this.#suppressOpen = true;\n this.inputTarget.focus();\n this.#suppressOpen = false;\n if (changed) {\n // A native bubbling `change` (matching <select>/listbox semantics: only on\n // an actual value change) so form-level behaviors — validation re-checks,\n // auto-submit — hear the commit without knowing this widget. Deliberately\n // NOT `input`: that is this combobox's own filter trigger and would reopen\n // the popup on every selection.\n this.inputTarget.dispatchEvent(new Event(\"change\", { bubbles: true }));\n }\n this.dispatch(\"selected\", { detail: { value } });\n }\n\n /**\n * Reflects whether the open listbox currently has zero matching options by\n * toggling `data-stimeo--combobox-empty` on the root element. Behavior only:\n * consumers decide how to present the empty state (hide the list, show a\n * \"no results\" node, etc.) via CSS keyed off this attribute.\n */\n #reflectEmptyState(): void {\n const empty = !this.#isClosed && this.#visibleOptions().length === 0;\n if (empty) {\n this.element.setAttribute(\"data-stimeo--combobox-empty\", \"\");\n } else {\n this.element.removeAttribute(\"data-stimeo--combobox-empty\");\n }\n }\n\n /**\n * Marks the visible option at `index` active via `aria-selected` and the\n * input's `aria-activedescendant`. Pass `-1` to clear the active option.\n */\n #setActive(index: number): void {\n this.#activeIndex = index;\n const visible = this.#visibleOptions();\n const active = index === -1 ? null : visible[index];\n // Clear aria-selected on every option (not just the visible ones) so a\n // previously-active option that became hidden by filtering doesn't keep a\n // stale selected state.\n for (const option of this.optionTargets) {\n option.setAttribute(\"aria-selected\", option === active ? \"true\" : \"false\");\n }\n if (active?.id) {\n this.inputTarget.setAttribute(\"aria-activedescendant\", active.id);\n } else {\n this.inputTarget.removeAttribute(\"aria-activedescendant\");\n }\n }\n\n /** The options currently shown (not filtered out). */\n #visibleOptions(): HTMLElement[] {\n return this.optionTargets.filter((option) => !option.hidden);\n }\n\n /** Whether the listbox is currently hidden. */\n get #isClosed(): boolean {\n return !this.hasListTarget || this.listTarget.hidden !== false;\n }\n}\n"]}
@@ -0,0 +1,103 @@
1
+ import { Controller } from '@hotwired/stimulus';
2
+
3
+ /**
4
+ * Headless, highly accessible Command Palette behavior.
5
+ *
6
+ * Markup contract (identifier: `stimeo--command-palette`):
7
+ * <div data-controller="stimeo--command-palette"
8
+ * data-stimeo--command-palette-hotkey-value="mod+k">
9
+ * <div data-stimeo--command-palette-target="dialog" role="dialog"
10
+ * aria-modal="true" aria-label="Command palette"
11
+ * data-action="click->stimeo--command-palette#closeOnBackdrop" hidden>
12
+ * <input data-stimeo--command-palette-target="input" role="combobox"
13
+ * aria-expanded="false" aria-controls="cmdk-list"
14
+ * aria-autocomplete="list" aria-label="Search commands"
15
+ * data-action="input->stimeo--command-palette#filter
16
+ * keydown->stimeo--command-palette#onKeydown" />
17
+ * <ul id="cmdk-list" data-stimeo--command-palette-target="list" role="listbox">
18
+ * <li id="cmd-new" role="option"
19
+ * data-stimeo--command-palette-target="option"
20
+ * data-action="click->stimeo--command-palette#selectByClick">New…</li>
21
+ * <li role="option" data-stimeo--command-palette-target="option"
22
+ * data-disabled="true">Section heading (shown but not selectable)</li>
23
+ * </ul>
24
+ * <p data-stimeo--command-palette-target="empty" hidden>No commands</p>
25
+ * </div>
26
+ * </div>
27
+ *
28
+ * Implements a composite **Dialog (Modal)** + **Combobox** (Listbox options) pattern.
29
+ * A customizable global hotkey (Command/Ctrl + K) toggles it, focus is trapped
30
+ * within the dialog, virtual focus roves via `aria-activedescendant`, and filtering
31
+ * is performed in memory.
32
+ *
33
+ * @remarks
34
+ * Behavior only. The combobox concerns (filtering, virtual focus, selection) live
35
+ * here; the modal lifecycle — focus trap, scroll lock, background `inert`, focus
36
+ * restore, and teardown reversal — is delegated to the shared {@link FocusTrap}
37
+ * primitive (also used by dialog / alert-dialog / drawer). `Escape` closes and
38
+ * `Tab`/`Shift+Tab` cycle focus regardless of which element inside the dialog holds
39
+ * focus (input, close button, …), because the trap listens at the document level.
40
+ * Styling, transitions, and the actual command handlers remain the consumer's.
41
+ */
42
+ declare class CommandPaletteController extends Controller<HTMLElement> {
43
+ #private;
44
+ static targets: string[];
45
+ static values: {
46
+ hotkey: {
47
+ type: StringConstructor;
48
+ default: string;
49
+ };
50
+ open: {
51
+ type: BooleanConstructor;
52
+ default: boolean;
53
+ };
54
+ };
55
+ static actions: readonly ["close", "closeOnBackdrop", "filter", "onKeydown", "open", "selectByClick", "toggle"];
56
+ static events: readonly ["select"];
57
+ readonly dialogTarget: HTMLElement;
58
+ readonly inputTarget: HTMLInputElement;
59
+ readonly listTarget: HTMLElement;
60
+ readonly optionTargets: HTMLElement[];
61
+ readonly emptyTarget: HTMLElement;
62
+ readonly hasDialogTarget: boolean;
63
+ readonly hasInputTarget: boolean;
64
+ readonly hasListTarget: boolean;
65
+ readonly hasEmptyTarget: boolean;
66
+ hotkeyValue: string;
67
+ openValue: boolean;
68
+ /**
69
+ * Initializes the global hotkey handler and establishes the initial open state.
70
+ *
71
+ * The DOM is the source of truth on reconnect (Turbo cache restore / morph): if
72
+ * the restored snapshot already shows the dialog open, honor that rather than
73
+ * re-deriving from the declarative `open` Value (which would slam a user-opened
74
+ * palette shut). The `open` Value only seeds the initial state of a genuinely
75
+ * fresh render. We normalize to a clean closed baseline first so {@link open}
76
+ * runs its full setup — the {@link FocusTrap} is a fresh instance after a
77
+ * reconnect and must be re-activated.
78
+ */
79
+ connect(): void;
80
+ /** Tears down the global hotkey listener and reverts the modal side effects. */
81
+ disconnect(): void;
82
+ /** Toggles the open state of the command palette. */
83
+ toggle(): void;
84
+ /** Opens the palette, traps focus, and shifts focus to the input. */
85
+ open(): void;
86
+ /** Closes the palette and restores focus back to the opener. */
87
+ close(): void;
88
+ /** Filters option elements in-memory matching the input value. Bound to input target. */
89
+ filter(): void;
90
+ /** Selects the clicked option. Bound to option targets. */
91
+ selectByClick(event: MouseEvent): void;
92
+ /** Closes when the backdrop (the dialog target itself) is clicked, ignoring inner clicks. */
93
+ closeOnBackdrop(event: MouseEvent): void;
94
+ /**
95
+ * Combobox navigation keys (arrows / Home / End / Enter), bound to the input.
96
+ * `Tab` (focus trap) and `Escape` (close) are owned by the {@link FocusTrap} at
97
+ * the document level, so they work no matter which element inside the dialog has
98
+ * focus — not only the input.
99
+ */
100
+ onKeydown(event: KeyboardEvent): void;
101
+ }
102
+
103
+ export { CommandPaletteController };