@unabridged/midwest 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/assets/javascript/midwest/index.ts +2 -0
- package/app/assets/javascript/midwest.js +275 -1
- package/app/assets/javascript/midwest.js.map +1 -1
- package/app/assets/stylesheets/midwest.css +1 -1
- package/app/assets/stylesheets/midwest.tailwind.css +1 -0
- package/dist/css/midwest.css +1 -1
- package/dist/javascript/collection/app/assets/javascript/midwest/index.js +2 -0
- package/dist/javascript/collection/app/assets/javascript/midwest/index.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/form/autocomplete_component/autocomplete_component_controller.js +262 -0
- package/dist/javascript/collection/app/components/midwest/form/autocomplete_component/autocomplete_component_controller.js.map +1 -0
- package/dist/javascript/collection/app/components/midwest/form/input_component/input_component_controller.js +16 -1
- package/dist/javascript/collection/app/components/midwest/form/input_component/input_component_controller.js.map +1 -1
- package/dist/javascript/midwest.js +275 -1
- package/dist/javascript/midwest.js.map +1 -1
- package/package.json +1 -1
|
@@ -21,6 +21,7 @@ import Chart from '../../../components/midwest/chart_component/chart_component_c
|
|
|
21
21
|
export { default as Chart } from '../../../components/midwest/chart_component/chart_component_controller.js';
|
|
22
22
|
import Range from '../../../components/midwest/form/range_component/range_component_controller.js';
|
|
23
23
|
import Rating from '../../../components/midwest/rating_component/rating_component_controller.js';
|
|
24
|
+
import Autocomplete from '../../../components/midwest/form/autocomplete_component/autocomplete_component_controller.js';
|
|
24
25
|
|
|
25
26
|
function registerMidwestControllers(application) {
|
|
26
27
|
application.register("midwest-card", Card);
|
|
@@ -42,6 +43,7 @@ function registerMidwestControllers(application) {
|
|
|
42
43
|
application.register("midwest-chart", Chart);
|
|
43
44
|
application.register("midwest-range", Range);
|
|
44
45
|
application.register("midwest-rating", Rating);
|
|
46
|
+
application.register("midwest-autocomplete", Autocomplete);
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
export { registerMidwestControllers };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../../../../../app/assets/javascript/midwest/index.ts"],"sourcesContent":["import Card from '../../../components/midwest/card_component/card_component_controller'\nimport Banner from '../../../components/midwest/banner_component/banner_component_controller'\nimport Input from '../../../components/midwest/form/input_component/input_component_controller'\nimport File from '../../../components/midwest/form/file_component/file_component_controller'\nimport Dropdown from '../../../components/midwest/dropdown_component/dropdown_component_controller'\nimport Prose from '../../../components/midwest/prose_component/prose_component_controller'\nimport Dialog from '../../../components/midwest/dialog_component/dialog_component_controller'\nimport Tabs from '../../../components/midwest/tabs_component/tabs_component_controller'\nimport Tooltip from '../../../components/midwest/tooltip_component/tooltip_component_controller'\nimport Carousel from '../../../components/midwest/carousel_component/carousel_component_controller'\nimport FormLiveSummary from '../../../components/midwest/form/live_summary_component/live_summary_component_controller'\nimport Form from '../../../components/midwest/form/form_controller'\nimport CountdownTimer from '../../../components/midwest/countdown_timer_component/countdown_timer_component_controller'\nimport Badge from '../../../components/midwest/badge_component/badge_component_controller'\nimport Confirmation from '../../../components/midwest/confirmation_component/confirmation_component_controller'\nimport AvatarGroup from '../../../components/midwest/avatar_group_component/avatar_group_component_controller'\nimport Chart from '../../../components/midwest/chart_component/chart_component_controller'\nimport Range from '../../../components/midwest/form/range_component/range_component_controller'\nimport Rating from '../../../components/midwest/rating_component/rating_component_controller'\n/* IMPORTS */\n\nexport { Card, Banner, CountdownTimer, Chart }\n\nexport function registerMidwestControllers (application: any) {\n application.register('midwest-card', Card)\n application.register('midwest-banner', Banner)\n application.register('midwest-countdown-timer', CountdownTimer)\n application.register('midwest-input', Input)\n application.register('midwest-file', File)\n application.register('midwest-dropdown', Dropdown)\n application.register('midwest-prose', Prose)\n application.register('midwest-dialog', Dialog)\n application.register('midwest-tabs', Tabs)\n application.register('midwest-tooltip', Tooltip)\n application.register('midwest-carousel', Carousel)\n application.register('midwest-form-live-summary', FormLiveSummary)\n application.register('midwest-form', Form)\n application.register('midwest-badge', Badge)\n application.register('midwest-confirmation', Confirmation)\n application.register('midwest-avatar-group', AvatarGroup)\n application.register('midwest-chart', Chart)\n application.register('midwest-range', Range)\n application.register('midwest-rating', Rating)\n /* EXPORTS */\n}\n"],"names":["Confirmation"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../../../../app/assets/javascript/midwest/index.ts"],"sourcesContent":["import Card from '../../../components/midwest/card_component/card_component_controller'\nimport Banner from '../../../components/midwest/banner_component/banner_component_controller'\nimport Input from '../../../components/midwest/form/input_component/input_component_controller'\nimport File from '../../../components/midwest/form/file_component/file_component_controller'\nimport Dropdown from '../../../components/midwest/dropdown_component/dropdown_component_controller'\nimport Prose from '../../../components/midwest/prose_component/prose_component_controller'\nimport Dialog from '../../../components/midwest/dialog_component/dialog_component_controller'\nimport Tabs from '../../../components/midwest/tabs_component/tabs_component_controller'\nimport Tooltip from '../../../components/midwest/tooltip_component/tooltip_component_controller'\nimport Carousel from '../../../components/midwest/carousel_component/carousel_component_controller'\nimport FormLiveSummary from '../../../components/midwest/form/live_summary_component/live_summary_component_controller'\nimport Form from '../../../components/midwest/form/form_controller'\nimport CountdownTimer from '../../../components/midwest/countdown_timer_component/countdown_timer_component_controller'\nimport Badge from '../../../components/midwest/badge_component/badge_component_controller'\nimport Confirmation from '../../../components/midwest/confirmation_component/confirmation_component_controller'\nimport AvatarGroup from '../../../components/midwest/avatar_group_component/avatar_group_component_controller'\nimport Chart from '../../../components/midwest/chart_component/chart_component_controller'\nimport Range from '../../../components/midwest/form/range_component/range_component_controller'\nimport Rating from '../../../components/midwest/rating_component/rating_component_controller'\nimport Autocomplete from '../../../components/midwest/form/autocomplete_component/autocomplete_component_controller'\n/* IMPORTS */\n\nexport { Card, Banner, CountdownTimer, Chart }\n\nexport function registerMidwestControllers (application: any) {\n application.register('midwest-card', Card)\n application.register('midwest-banner', Banner)\n application.register('midwest-countdown-timer', CountdownTimer)\n application.register('midwest-input', Input)\n application.register('midwest-file', File)\n application.register('midwest-dropdown', Dropdown)\n application.register('midwest-prose', Prose)\n application.register('midwest-dialog', Dialog)\n application.register('midwest-tabs', Tabs)\n application.register('midwest-tooltip', Tooltip)\n application.register('midwest-carousel', Carousel)\n application.register('midwest-form-live-summary', FormLiveSummary)\n application.register('midwest-form', Form)\n application.register('midwest-badge', Badge)\n application.register('midwest-confirmation', Confirmation)\n application.register('midwest-avatar-group', AvatarGroup)\n application.register('midwest-chart', Chart)\n application.register('midwest-range', Range)\n application.register('midwest-rating', Rating)\n application.register('midwest-autocomplete', Autocomplete)\n /* EXPORTS */\n}\n"],"names":["Confirmation"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAwBO,SAAS,2BAA4B,WAAkB,EAAA;AAC5D,EAAY,WAAA,CAAA,QAAA,CAAS,gBAAgB,IAAI,CAAA,CAAA;AACzC,EAAY,WAAA,CAAA,QAAA,CAAS,kBAAkB,MAAM,CAAA,CAAA;AAC7C,EAAY,WAAA,CAAA,QAAA,CAAS,2BAA2B,cAAc,CAAA,CAAA;AAC9D,EAAY,WAAA,CAAA,QAAA,CAAS,iBAAiB,KAAK,CAAA,CAAA;AAC3C,EAAY,WAAA,CAAA,QAAA,CAAS,gBAAgB,IAAI,CAAA,CAAA;AACzC,EAAY,WAAA,CAAA,QAAA,CAAS,oBAAoB,QAAQ,CAAA,CAAA;AACjD,EAAY,WAAA,CAAA,QAAA,CAAS,iBAAiB,KAAK,CAAA,CAAA;AAC3C,EAAY,WAAA,CAAA,QAAA,CAAS,kBAAkB,MAAM,CAAA,CAAA;AAC7C,EAAY,WAAA,CAAA,QAAA,CAAS,gBAAgB,IAAI,CAAA,CAAA;AACzC,EAAY,WAAA,CAAA,QAAA,CAAS,mBAAmB,OAAO,CAAA,CAAA;AAC/C,EAAY,WAAA,CAAA,QAAA,CAAS,oBAAoB,QAAQ,CAAA,CAAA;AACjD,EAAY,WAAA,CAAA,QAAA,CAAS,6BAA6B,eAAe,CAAA,CAAA;AACjE,EAAY,WAAA,CAAA,QAAA,CAAS,gBAAgB,IAAI,CAAA,CAAA;AACzC,EAAY,WAAA,CAAA,QAAA,CAAS,iBAAiB,KAAK,CAAA,CAAA;AAC3C,EAAY,WAAA,CAAA,QAAA,CAAS,wBAAwBA,sBAAY,CAAA,CAAA;AACzD,EAAY,WAAA,CAAA,QAAA,CAAS,wBAAwB,WAAW,CAAA,CAAA;AACxD,EAAY,WAAA,CAAA,QAAA,CAAS,iBAAiB,KAAK,CAAA,CAAA;AAC3C,EAAY,WAAA,CAAA,QAAA,CAAS,iBAAiB,KAAK,CAAA,CAAA;AAC3C,EAAY,WAAA,CAAA,QAAA,CAAS,kBAAkB,MAAM,CAAA,CAAA;AAC7C,EAAY,WAAA,CAAA,QAAA,CAAS,wBAAwB,YAAY,CAAA,CAAA;AAE3D;;;;"}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { Controller } from '@hotwired/stimulus';
|
|
2
|
+
|
|
3
|
+
class Autocomplete extends Controller {
|
|
4
|
+
static targets = ["input", "dropdown", "list", "tokens", "token", "multipleTokenTemplate", "singleTokenTemplate", "status"];
|
|
5
|
+
static values = {
|
|
6
|
+
options: { type: Array, default: [] },
|
|
7
|
+
url: { type: String, default: "" },
|
|
8
|
+
multiple: { type: Boolean, default: true }
|
|
9
|
+
};
|
|
10
|
+
selectedValues = /* @__PURE__ */ new Map();
|
|
11
|
+
highlightedIndex = -1;
|
|
12
|
+
fetchAbort = null;
|
|
13
|
+
connect() {
|
|
14
|
+
this.loadInitialTokens();
|
|
15
|
+
}
|
|
16
|
+
disconnect() {
|
|
17
|
+
this.fetchAbort?.abort();
|
|
18
|
+
}
|
|
19
|
+
// ── Public actions ─────────────────────────────────────────────────────────
|
|
20
|
+
focusInput() {
|
|
21
|
+
this.inputTarget.focus();
|
|
22
|
+
this.open();
|
|
23
|
+
}
|
|
24
|
+
open() {
|
|
25
|
+
if (this.dropdownTarget.matches(":popover-open"))
|
|
26
|
+
return;
|
|
27
|
+
const query = this.inputTarget.value.trim();
|
|
28
|
+
if (this.urlValue) {
|
|
29
|
+
this.fetch(query);
|
|
30
|
+
} else {
|
|
31
|
+
this.renderOptions(this.filteredOptions(query));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async search() {
|
|
35
|
+
const query = this.inputTarget.value.trim();
|
|
36
|
+
if (this.urlValue) {
|
|
37
|
+
await this.fetch(query);
|
|
38
|
+
} else {
|
|
39
|
+
this.renderOptions(this.filteredOptions(query));
|
|
40
|
+
}
|
|
41
|
+
this.showDropdown();
|
|
42
|
+
}
|
|
43
|
+
navigate(event) {
|
|
44
|
+
switch (event.key) {
|
|
45
|
+
case "ArrowDown": {
|
|
46
|
+
event.preventDefault();
|
|
47
|
+
if (!this.dropdownTarget.matches(":popover-open"))
|
|
48
|
+
this.open();
|
|
49
|
+
const items = this.visibleOptions();
|
|
50
|
+
this.highlightedIndex = Math.min(this.highlightedIndex + 1, items.length - 1);
|
|
51
|
+
this.syncHighlight(items);
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
case "ArrowUp": {
|
|
55
|
+
event.preventDefault();
|
|
56
|
+
const items = this.visibleOptions();
|
|
57
|
+
this.highlightedIndex = Math.max(this.highlightedIndex - 1, 0);
|
|
58
|
+
this.syncHighlight(items);
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
case "Enter": {
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
const items = this.visibleOptions();
|
|
64
|
+
if (this.highlightedIndex >= 0 && items[this.highlightedIndex]) {
|
|
65
|
+
this.pickItem(items[this.highlightedIndex]);
|
|
66
|
+
}
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
case "Escape":
|
|
70
|
+
this.hideDropdown();
|
|
71
|
+
break;
|
|
72
|
+
case "Backspace":
|
|
73
|
+
if (this.inputTarget.value === "" && this.multipleValue) {
|
|
74
|
+
const entries = Array.from(this.selectedValues.keys());
|
|
75
|
+
if (entries.length > 0)
|
|
76
|
+
this.deselect(entries[entries.length - 1]);
|
|
77
|
+
}
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
visibleOptions() {
|
|
82
|
+
return this.listTarget.querySelectorAll('.midwest-autocomplete-option:not([aria-selected="true"])');
|
|
83
|
+
}
|
|
84
|
+
handleBlur() {
|
|
85
|
+
setTimeout(() => this.hideDropdown(), 150);
|
|
86
|
+
}
|
|
87
|
+
removeToken(event) {
|
|
88
|
+
event.stopPropagation();
|
|
89
|
+
const btn = event.currentTarget;
|
|
90
|
+
const value = btn.dataset.value;
|
|
91
|
+
if (value)
|
|
92
|
+
this.deselect(value);
|
|
93
|
+
}
|
|
94
|
+
// ── Private ────────────────────────────────────────────────────────────────
|
|
95
|
+
get optionIdPrefix() {
|
|
96
|
+
return `ac-opt-${(this.element.dataset.name ?? "field").replace(/\W/g, "-")}`;
|
|
97
|
+
}
|
|
98
|
+
loadInitialTokens() {
|
|
99
|
+
this.tokenTargets.forEach((token) => {
|
|
100
|
+
const value = token.dataset.value ?? "";
|
|
101
|
+
const label = token.querySelector(".value")?.textContent?.trim() ?? value;
|
|
102
|
+
this.selectedValues.set(value, { value, label });
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
filteredOptions(query) {
|
|
106
|
+
const q = query.toLowerCase();
|
|
107
|
+
return this.optionsValue.filter(
|
|
108
|
+
(o) => q === "" || o.label.toLowerCase().includes(q) || (o.support?.toLowerCase().includes(q) ?? false)
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
async fetch(query) {
|
|
112
|
+
this.fetchAbort?.abort();
|
|
113
|
+
this.fetchAbort = new AbortController();
|
|
114
|
+
this.showLoading();
|
|
115
|
+
try {
|
|
116
|
+
const url = new URL(this.urlValue, window.location.href);
|
|
117
|
+
if (query)
|
|
118
|
+
url.searchParams.set("q", query);
|
|
119
|
+
const res = await globalThis.fetch(url.toString(), {
|
|
120
|
+
signal: this.fetchAbort.signal,
|
|
121
|
+
headers: { Accept: "application/json" }
|
|
122
|
+
});
|
|
123
|
+
const data = await res.json();
|
|
124
|
+
this.renderOptions(data);
|
|
125
|
+
} catch (err) {
|
|
126
|
+
if (err.name !== "AbortError") {
|
|
127
|
+
this.renderEmpty();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
select(option) {
|
|
132
|
+
if (!this.multipleValue) {
|
|
133
|
+
this.selectedValues.forEach((_, val) => this.removeTokenElement(val));
|
|
134
|
+
this.selectedValues.clear();
|
|
135
|
+
}
|
|
136
|
+
this.selectedValues.set(option.value, option);
|
|
137
|
+
this.appendToken(option);
|
|
138
|
+
this.hideDropdown();
|
|
139
|
+
this.inputTarget.value = "";
|
|
140
|
+
this.inputTarget.focus();
|
|
141
|
+
this.announce(`${option.label} selected`);
|
|
142
|
+
this.dispatch("change", { detail: { value: option.value, label: option.label } });
|
|
143
|
+
}
|
|
144
|
+
deselect(value) {
|
|
145
|
+
const option = this.selectedValues.get(value);
|
|
146
|
+
this.selectedValues.delete(value);
|
|
147
|
+
this.removeTokenElement(value);
|
|
148
|
+
if (option)
|
|
149
|
+
this.announce(`${option.label} removed`);
|
|
150
|
+
this.dispatch("change", { detail: { value: null } });
|
|
151
|
+
}
|
|
152
|
+
appendToken(option) {
|
|
153
|
+
const name = this.element.dataset.name ?? "";
|
|
154
|
+
if (this.multipleValue) {
|
|
155
|
+
const fragment = this.multipleTokenTemplateTarget.content.cloneNode(true);
|
|
156
|
+
const badge = fragment.querySelector(".midwest-badge");
|
|
157
|
+
badge.dataset.value = option.value;
|
|
158
|
+
badge.querySelector(".value").textContent = option.label;
|
|
159
|
+
const removeBtn = badge.querySelector(".remove");
|
|
160
|
+
removeBtn.dataset.value = option.value;
|
|
161
|
+
removeBtn.setAttribute("aria-label", `Remove ${option.label}`);
|
|
162
|
+
const input = badge.querySelector("input[type=hidden]");
|
|
163
|
+
input.name = name;
|
|
164
|
+
input.value = option.value;
|
|
165
|
+
this.tokensTarget.appendChild(fragment);
|
|
166
|
+
} else {
|
|
167
|
+
this.tokensTarget.querySelectorAll("[data-value]").forEach((el) => el.remove());
|
|
168
|
+
const fragment = this.singleTokenTemplateTarget.content.cloneNode(true);
|
|
169
|
+
const badge = fragment.querySelector(".midwest-badge");
|
|
170
|
+
badge.dataset.value = option.value;
|
|
171
|
+
badge.querySelector(".value").textContent = option.label;
|
|
172
|
+
const input = badge.querySelector("input[type=hidden]");
|
|
173
|
+
input.name = name;
|
|
174
|
+
input.value = option.value;
|
|
175
|
+
this.tokensTarget.appendChild(fragment);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
removeTokenElement(value) {
|
|
179
|
+
const token = this.tokensTarget.querySelector(`[data-value="${CSS.escape(value)}"]`);
|
|
180
|
+
token?.remove();
|
|
181
|
+
}
|
|
182
|
+
renderOptions(options) {
|
|
183
|
+
this.listTarget.innerHTML = "";
|
|
184
|
+
this.highlightedIndex = -1;
|
|
185
|
+
if (options.length === 0) {
|
|
186
|
+
this.renderEmpty();
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
options.forEach((option, index) => {
|
|
190
|
+
const li = document.createElement("li");
|
|
191
|
+
li.id = `${this.optionIdPrefix}-${index}`;
|
|
192
|
+
li.className = "midwest-autocomplete-option";
|
|
193
|
+
li.setAttribute("role", "option");
|
|
194
|
+
li.setAttribute("aria-selected", this.selectedValues.has(option.value) ? "true" : "false");
|
|
195
|
+
li.dataset.value = option.value;
|
|
196
|
+
li.innerHTML = `
|
|
197
|
+
<span class="midwest-autocomplete-option-label">${this.escapeHtml(option.label)}</span>
|
|
198
|
+
${option.support ? `<span class="midwest-autocomplete-option-support">${this.escapeHtml(option.support)}</span>` : ""}
|
|
199
|
+
`;
|
|
200
|
+
li.addEventListener("mousedown", (e) => {
|
|
201
|
+
e.preventDefault();
|
|
202
|
+
this.select(option);
|
|
203
|
+
});
|
|
204
|
+
this.listTarget.appendChild(li);
|
|
205
|
+
});
|
|
206
|
+
const availableCount = options.filter((o) => !this.selectedValues.has(o.value)).length;
|
|
207
|
+
if (availableCount === 0) {
|
|
208
|
+
this.renderEmpty("No more options");
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
this.announce(`${availableCount} option${availableCount === 1 ? "" : "s"} available`);
|
|
212
|
+
this.showDropdown();
|
|
213
|
+
}
|
|
214
|
+
renderEmpty(message = "No results") {
|
|
215
|
+
this.listTarget.innerHTML = `<li class="midwest-autocomplete-empty" aria-hidden="true">${this.escapeHtml(message)}</li>`;
|
|
216
|
+
this.announce(message);
|
|
217
|
+
this.showDropdown();
|
|
218
|
+
}
|
|
219
|
+
showLoading() {
|
|
220
|
+
this.listTarget.innerHTML = '<li class="midwest-autocomplete-loading" aria-hidden="true">Loading\u2026</li>';
|
|
221
|
+
this.announce("Loading");
|
|
222
|
+
this.showDropdown();
|
|
223
|
+
}
|
|
224
|
+
showDropdown() {
|
|
225
|
+
if (!this.dropdownTarget.matches(":popover-open"))
|
|
226
|
+
this.dropdownTarget.showPopover();
|
|
227
|
+
this.inputTarget.setAttribute("aria-expanded", "true");
|
|
228
|
+
}
|
|
229
|
+
hideDropdown() {
|
|
230
|
+
if (this.dropdownTarget.matches(":popover-open"))
|
|
231
|
+
this.dropdownTarget.hidePopover();
|
|
232
|
+
this.inputTarget.setAttribute("aria-expanded", "false");
|
|
233
|
+
this.inputTarget.removeAttribute("aria-activedescendant");
|
|
234
|
+
this.highlightedIndex = -1;
|
|
235
|
+
}
|
|
236
|
+
syncHighlight(items) {
|
|
237
|
+
items.forEach((item, i) => item.classList.toggle("is-highlighted", i === this.highlightedIndex));
|
|
238
|
+
const active = items[this.highlightedIndex];
|
|
239
|
+
active?.scrollIntoView({ block: "nearest" });
|
|
240
|
+
if (active) {
|
|
241
|
+
this.inputTarget.setAttribute("aria-activedescendant", active.id);
|
|
242
|
+
} else {
|
|
243
|
+
this.inputTarget.removeAttribute("aria-activedescendant");
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
pickItem(item) {
|
|
247
|
+
const value = item.dataset.value ?? "";
|
|
248
|
+
const label = item.querySelector(".midwest-autocomplete-option-label")?.textContent?.trim() ?? value;
|
|
249
|
+
const support = item.querySelector(".midwest-autocomplete-option-support")?.textContent?.trim();
|
|
250
|
+
this.select({ value, label, support });
|
|
251
|
+
}
|
|
252
|
+
announce(message) {
|
|
253
|
+
if (this.hasStatusTarget)
|
|
254
|
+
this.statusTarget.textContent = message;
|
|
255
|
+
}
|
|
256
|
+
escapeHtml(str) {
|
|
257
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export { Autocomplete as default };
|
|
262
|
+
//# sourceMappingURL=autocomplete_component_controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autocomplete_component_controller.js","sources":["../../../../../../../../app/components/midwest/form/autocomplete_component/autocomplete_component_controller.ts"],"sourcesContent":["import { Controller } from '@hotwired/stimulus'\n\ninterface Option {\n value: string\n label: string\n support?: string\n}\n\nexport default class Autocomplete extends Controller<HTMLElement> {\n static targets = ['input', 'dropdown', 'list', 'tokens', 'token', 'multipleTokenTemplate', 'singleTokenTemplate', 'status']\n static values = {\n options: { type: Array, default: [] },\n url: { type: String, default: '' },\n multiple: { type: Boolean, default: true }\n }\n\n declare readonly inputTarget: HTMLInputElement\n declare readonly dropdownTarget: HTMLElement\n declare readonly listTarget: HTMLElement\n declare readonly tokensTarget: HTMLElement\n declare readonly tokenTargets: HTMLElement[]\n declare readonly multipleTokenTemplateTarget: HTMLTemplateElement\n declare readonly singleTokenTemplateTarget: HTMLTemplateElement\n declare readonly statusTarget: HTMLElement\n declare readonly hasStatusTarget: boolean\n\n declare optionsValue: Option[]\n declare urlValue: string\n declare multipleValue: boolean\n\n private selectedValues = new Map<string, Option>()\n private highlightedIndex = -1\n private fetchAbort: AbortController | null = null\n\n connect () {\n this.loadInitialTokens()\n }\n\n disconnect () {\n this.fetchAbort?.abort()\n }\n\n // ── Public actions ─────────────────────────────────────────────────────────\n\n focusInput () {\n this.inputTarget.focus()\n this.open()\n }\n\n open () {\n if (this.dropdownTarget.matches(':popover-open')) return\n const query = this.inputTarget.value.trim()\n if (this.urlValue) {\n this.fetch(query)\n } else {\n this.renderOptions(this.filteredOptions(query))\n }\n }\n\n async search () {\n const query = this.inputTarget.value.trim()\n if (this.urlValue) {\n await this.fetch(query)\n } else {\n this.renderOptions(this.filteredOptions(query))\n }\n this.showDropdown()\n }\n\n navigate (event: KeyboardEvent) {\n switch (event.key) {\n case 'ArrowDown': {\n event.preventDefault()\n if (!this.dropdownTarget.matches(':popover-open')) this.open()\n const items = this.visibleOptions()\n this.highlightedIndex = Math.min(this.highlightedIndex + 1, items.length - 1)\n this.syncHighlight(items)\n break\n }\n\n case 'ArrowUp': {\n event.preventDefault()\n const items = this.visibleOptions()\n this.highlightedIndex = Math.max(this.highlightedIndex - 1, 0)\n this.syncHighlight(items)\n break\n }\n\n case 'Enter': {\n event.preventDefault()\n const items = this.visibleOptions()\n if (this.highlightedIndex >= 0 && items[this.highlightedIndex]) {\n this.pickItem(items[this.highlightedIndex])\n }\n break\n }\n\n case 'Escape':\n this.hideDropdown()\n break\n\n case 'Backspace':\n if (this.inputTarget.value === '' && this.multipleValue) {\n const entries = Array.from(this.selectedValues.keys())\n if (entries.length > 0) this.deselect(entries[entries.length - 1])\n }\n break\n }\n }\n\n private visibleOptions (): NodeListOf<HTMLElement> {\n return this.listTarget.querySelectorAll<HTMLElement>('.midwest-autocomplete-option:not([aria-selected=\"true\"])')\n }\n\n handleBlur () {\n // Delay to let mousedown on an option fire first.\n setTimeout(() => this.hideDropdown(), 150)\n }\n\n removeToken (event: MouseEvent) {\n event.stopPropagation()\n const btn = event.currentTarget as HTMLElement\n const value = btn.dataset.value\n if (value) this.deselect(value)\n }\n\n // ── Private ────────────────────────────────────────────────────────────────\n\n private get optionIdPrefix (): string {\n return `ac-opt-${(this.element.dataset.name ?? 'field').replace(/\\W/g, '-')}`\n }\n\n private loadInitialTokens () {\n this.tokenTargets.forEach(token => {\n const value = token.dataset.value ?? ''\n const label = token.querySelector('.value')?.textContent?.trim() ?? value\n this.selectedValues.set(value, { value, label })\n })\n }\n\n private filteredOptions (query: string): Option[] {\n const q = query.toLowerCase()\n return (this.optionsValue as Option[]).filter(\n o => q === '' || o.label.toLowerCase().includes(q) || (o.support?.toLowerCase().includes(q) ?? false)\n )\n }\n\n private async fetch (query: string) {\n this.fetchAbort?.abort()\n this.fetchAbort = new AbortController()\n\n this.showLoading()\n\n try {\n const url = new URL(this.urlValue, window.location.href)\n if (query) url.searchParams.set('q', query)\n\n const res = await globalThis.fetch(url.toString(), {\n signal: this.fetchAbort.signal,\n headers: { Accept: 'application/json' }\n })\n\n const data: Option[] = await res.json()\n this.renderOptions(data)\n } catch (err) {\n if ((err as Error).name !== 'AbortError') {\n this.renderEmpty()\n }\n }\n }\n\n private select (option: Option) {\n if (!this.multipleValue) {\n // Clear any existing hidden inputs and tokens for single-select\n this.selectedValues.forEach((_, val) => this.removeTokenElement(val))\n this.selectedValues.clear()\n }\n\n this.selectedValues.set(option.value, option)\n this.appendToken(option)\n this.hideDropdown()\n this.inputTarget.value = ''\n this.inputTarget.focus()\n this.announce(`${option.label} selected`)\n\n this.dispatch('change', { detail: { value: option.value, label: option.label } })\n }\n\n private deselect (value: string) {\n const option = this.selectedValues.get(value)\n this.selectedValues.delete(value)\n this.removeTokenElement(value)\n if (option) this.announce(`${option.label} removed`)\n this.dispatch('change', { detail: { value: null } })\n }\n\n private appendToken (option: Option) {\n const name = this.element.dataset.name ?? ''\n\n if (this.multipleValue) {\n const fragment = this.multipleTokenTemplateTarget.content.cloneNode(true) as DocumentFragment\n const badge = fragment.querySelector<HTMLElement>('.midwest-badge')!\n badge.dataset.value = option.value\n badge.querySelector<HTMLElement>('.value')!.textContent = option.label\n const removeBtn = badge.querySelector<HTMLElement>('.remove')!\n removeBtn.dataset.value = option.value\n removeBtn.setAttribute('aria-label', `Remove ${option.label}`)\n const input = badge.querySelector<HTMLInputElement>('input[type=hidden]')!\n input.name = name\n input.value = option.value\n this.tokensTarget.appendChild(fragment)\n } else {\n this.tokensTarget.querySelectorAll<HTMLElement>('[data-value]').forEach(el => el.remove())\n const fragment = this.singleTokenTemplateTarget.content.cloneNode(true) as DocumentFragment\n const badge = fragment.querySelector<HTMLElement>('.midwest-badge')!\n badge.dataset.value = option.value\n badge.querySelector<HTMLElement>('.value')!.textContent = option.label\n const input = badge.querySelector<HTMLInputElement>('input[type=hidden]')!\n input.name = name\n input.value = option.value\n this.tokensTarget.appendChild(fragment)\n }\n }\n\n private removeTokenElement (value: string) {\n const token = this.tokensTarget.querySelector(`[data-value=\"${CSS.escape(value)}\"]`)\n token?.remove()\n }\n\n private renderOptions (options: Option[]) {\n this.listTarget.innerHTML = ''\n this.highlightedIndex = -1\n\n if (options.length === 0) {\n this.renderEmpty()\n return\n }\n\n options.forEach((option, index) => {\n const li = document.createElement('li')\n li.id = `${this.optionIdPrefix}-${index}`\n li.className = 'midwest-autocomplete-option'\n li.setAttribute('role', 'option')\n li.setAttribute('aria-selected', this.selectedValues.has(option.value) ? 'true' : 'false')\n li.dataset.value = option.value\n li.innerHTML = `\n <span class=\"midwest-autocomplete-option-label\">${this.escapeHtml(option.label)}</span>\n ${option.support ? `<span class=\"midwest-autocomplete-option-support\">${this.escapeHtml(option.support)}</span>` : ''}\n `\n li.addEventListener('mousedown', e => {\n e.preventDefault()\n this.select(option)\n })\n this.listTarget.appendChild(li)\n })\n\n const availableCount = options.filter(o => !this.selectedValues.has(o.value)).length\n if (availableCount === 0) {\n this.renderEmpty('No more options')\n return\n }\n this.announce(`${availableCount} option${availableCount === 1 ? '' : 's'} available`)\n this.showDropdown()\n }\n\n private renderEmpty (message = 'No results') {\n this.listTarget.innerHTML = `<li class=\"midwest-autocomplete-empty\" aria-hidden=\"true\">${this.escapeHtml(message)}</li>`\n this.announce(message)\n this.showDropdown()\n }\n\n private showLoading () {\n this.listTarget.innerHTML = '<li class=\"midwest-autocomplete-loading\" aria-hidden=\"true\">Loading…</li>'\n this.announce('Loading')\n this.showDropdown()\n }\n\n private showDropdown () {\n if (!this.dropdownTarget.matches(':popover-open')) this.dropdownTarget.showPopover()\n this.inputTarget.setAttribute('aria-expanded', 'true')\n }\n\n private hideDropdown () {\n if (this.dropdownTarget.matches(':popover-open')) this.dropdownTarget.hidePopover()\n this.inputTarget.setAttribute('aria-expanded', 'false')\n this.inputTarget.removeAttribute('aria-activedescendant')\n this.highlightedIndex = -1\n }\n\n private syncHighlight (items: NodeListOf<HTMLElement>) {\n items.forEach((item, i) => item.classList.toggle('is-highlighted', i === this.highlightedIndex))\n const active = items[this.highlightedIndex]\n active?.scrollIntoView({ block: 'nearest' })\n if (active) {\n this.inputTarget.setAttribute('aria-activedescendant', active.id)\n } else {\n this.inputTarget.removeAttribute('aria-activedescendant')\n }\n }\n\n private pickItem (item: HTMLElement) {\n const value = item.dataset.value ?? ''\n const label = item.querySelector('.midwest-autocomplete-option-label')?.textContent?.trim() ?? value\n const support = item.querySelector('.midwest-autocomplete-option-support')?.textContent?.trim()\n this.select({ value, label, support })\n }\n\n private announce (message: string) {\n if (this.hasStatusTarget) this.statusTarget.textContent = message\n }\n\n private escapeHtml (str: string): string {\n return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"')\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
3
|
class Input extends Controller {
|
|
4
|
-
static targets = ["input"];
|
|
4
|
+
static targets = ["input", "counter"];
|
|
5
5
|
updateClearButton = null;
|
|
6
|
+
updateCounter = null;
|
|
6
7
|
connect() {
|
|
7
8
|
this.applyColor();
|
|
8
9
|
this.applySearchClear();
|
|
10
|
+
this.applyCounter();
|
|
9
11
|
}
|
|
10
12
|
disconnect() {
|
|
11
13
|
if (this.updateClearButton) {
|
|
12
14
|
this.inputTarget.removeEventListener("input", this.updateClearButton);
|
|
13
15
|
this.updateClearButton = null;
|
|
14
16
|
}
|
|
17
|
+
if (this.updateCounter) {
|
|
18
|
+
this.inputTarget.removeEventListener("input", this.updateCounter);
|
|
19
|
+
this.updateCounter = null;
|
|
20
|
+
}
|
|
15
21
|
}
|
|
16
22
|
applyColor() {
|
|
17
23
|
if (this.inputTarget.type !== "color") {
|
|
@@ -21,6 +27,15 @@ class Input extends Controller {
|
|
|
21
27
|
this.inputTarget.setAttribute("value", this.inputTarget.value);
|
|
22
28
|
});
|
|
23
29
|
}
|
|
30
|
+
applyCounter() {
|
|
31
|
+
if (!this.hasCounterTarget)
|
|
32
|
+
return;
|
|
33
|
+
this.counterTarget.textContent = String(this.inputTarget.value.length);
|
|
34
|
+
this.updateCounter = () => {
|
|
35
|
+
this.counterTarget.textContent = String(this.inputTarget.value.length);
|
|
36
|
+
};
|
|
37
|
+
this.inputTarget.addEventListener("input", this.updateCounter);
|
|
38
|
+
}
|
|
24
39
|
applySearchClear() {
|
|
25
40
|
if (this.inputTarget.type !== "search")
|
|
26
41
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input_component_controller.js","sources":["../../../../../../../../app/components/midwest/form/input_component/input_component_controller.ts"],"sourcesContent":["\nimport { Controller } from '@hotwired/stimulus'\n\nexport default class Input extends Controller<HTMLFormElement> {\n static targets = ['input']\n\n declare readonly hasInputTarget: boolean\n declare readonly inputTarget: HTMLInputElement\n declare readonly inputTargets: HTMLInputElement[]\n\n private updateClearButton: (() => void) | null = null\n\n connect () {\n this.applyColor()\n this.applySearchClear()\n }\n\n disconnect () {\n if (this.updateClearButton) {\n this.inputTarget.removeEventListener('input', this.updateClearButton)\n this.updateClearButton = null\n }\n }\n\n applyColor () {\n if (this.inputTarget.type !== 'color') {\n return\n }\n\n this.inputTarget.addEventListener('input', () => {\n this.inputTarget.setAttribute('value', this.inputTarget.value)\n })\n }\n\n private applySearchClear () {\n if (this.inputTarget.type !== 'search') return\n // Browsers that natively render a cancel button (Chrome, Safari, Edge)\n // already handle clearing. Only inject for browsers that don't (Firefox).\n if (CSS.supports('selector(::-webkit-search-cancel-button)')) return\n\n const button = document.createElement('button')\n button.type = 'button'\n button.setAttribute('aria-label', 'Clear search')\n button.classList.add('midwest-input-search-clear')\n button.hidden = this.inputTarget.value.length === 0\n\n const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')\n svg.setAttribute('viewBox', '0 0 24 24')\n svg.setAttribute('fill', 'none')\n svg.setAttribute('stroke', 'currentColor')\n svg.setAttribute('stroke-width', '2')\n svg.setAttribute('aria-hidden', 'true')\n svg.setAttribute('width', '16')\n svg.setAttribute('height', '16')\n\n const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')\n path.setAttribute('stroke-linecap', 'round')\n path.setAttribute('stroke-linejoin', 'round')\n path.setAttribute('d', 'M6 18L18 6M6 6l12 12')\n svg.appendChild(path)\n button.appendChild(svg)\n\n const wrapper = document.createElement('div')\n wrapper.classList.add('midwest-input-search-wrapper')\n this.inputTarget.parentElement?.insertBefore(wrapper, this.inputTarget)\n wrapper.appendChild(this.inputTarget)\n wrapper.appendChild(button)\n\n this.updateClearButton = () => {\n button.hidden = this.inputTarget.value.length === 0\n }\n\n this.inputTarget.addEventListener('input', this.updateClearButton)\n\n button.addEventListener('click', () => {\n this.inputTarget.value = ''\n button.hidden = true\n this.inputTarget.dispatchEvent(new Event('input', { bubbles: true }))\n this.inputTarget.focus()\n })\n }\n}\n"],"names":[],"mappings":";;AAGA,MAAqB,cAAc,UAA4B,CAAA;AAAA,EAC7D,OAAO,
|
|
1
|
+
{"version":3,"file":"input_component_controller.js","sources":["../../../../../../../../app/components/midwest/form/input_component/input_component_controller.ts"],"sourcesContent":["\nimport { Controller } from '@hotwired/stimulus'\n\nexport default class Input extends Controller<HTMLFormElement> {\n static targets = ['input', 'counter']\n\n declare readonly hasInputTarget: boolean\n declare readonly inputTarget: HTMLInputElement\n declare readonly inputTargets: HTMLInputElement[]\n declare readonly hasCounterTarget: boolean\n declare readonly counterTarget: HTMLElement\n\n private updateClearButton: (() => void) | null = null\n private updateCounter: (() => void) | null = null\n\n connect () {\n this.applyColor()\n this.applySearchClear()\n this.applyCounter()\n }\n\n disconnect () {\n if (this.updateClearButton) {\n this.inputTarget.removeEventListener('input', this.updateClearButton)\n this.updateClearButton = null\n }\n if (this.updateCounter) {\n this.inputTarget.removeEventListener('input', this.updateCounter)\n this.updateCounter = null\n }\n }\n\n applyColor () {\n if (this.inputTarget.type !== 'color') {\n return\n }\n\n this.inputTarget.addEventListener('input', () => {\n this.inputTarget.setAttribute('value', this.inputTarget.value)\n })\n }\n\n private applyCounter () {\n if (!this.hasCounterTarget) return\n\n this.counterTarget.textContent = String(this.inputTarget.value.length)\n\n this.updateCounter = () => {\n this.counterTarget.textContent = String(this.inputTarget.value.length)\n }\n\n this.inputTarget.addEventListener('input', this.updateCounter)\n }\n\n private applySearchClear () {\n if (this.inputTarget.type !== 'search') return\n // Browsers that natively render a cancel button (Chrome, Safari, Edge)\n // already handle clearing. Only inject for browsers that don't (Firefox).\n if (CSS.supports('selector(::-webkit-search-cancel-button)')) return\n\n const button = document.createElement('button')\n button.type = 'button'\n button.setAttribute('aria-label', 'Clear search')\n button.classList.add('midwest-input-search-clear')\n button.hidden = this.inputTarget.value.length === 0\n\n const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')\n svg.setAttribute('viewBox', '0 0 24 24')\n svg.setAttribute('fill', 'none')\n svg.setAttribute('stroke', 'currentColor')\n svg.setAttribute('stroke-width', '2')\n svg.setAttribute('aria-hidden', 'true')\n svg.setAttribute('width', '16')\n svg.setAttribute('height', '16')\n\n const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')\n path.setAttribute('stroke-linecap', 'round')\n path.setAttribute('stroke-linejoin', 'round')\n path.setAttribute('d', 'M6 18L18 6M6 6l12 12')\n svg.appendChild(path)\n button.appendChild(svg)\n\n const wrapper = document.createElement('div')\n wrapper.classList.add('midwest-input-search-wrapper')\n this.inputTarget.parentElement?.insertBefore(wrapper, this.inputTarget)\n wrapper.appendChild(this.inputTarget)\n wrapper.appendChild(button)\n\n this.updateClearButton = () => {\n button.hidden = this.inputTarget.value.length === 0\n }\n\n this.inputTarget.addEventListener('input', this.updateClearButton)\n\n button.addEventListener('click', () => {\n this.inputTarget.value = ''\n button.hidden = true\n this.inputTarget.dispatchEvent(new Event('input', { bubbles: true }))\n this.inputTarget.focus()\n })\n }\n}\n"],"names":[],"mappings":"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"}
|