@unabridged/midwest 0.24.0 → 0.24.4
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 +10 -0
- package/app/assets/javascript/midwest/map_providers.ts +61 -0
- package/app/assets/javascript/midwest/viewport_observer.ts +49 -0
- package/app/assets/javascript/midwest.js +1056 -70
- package/app/assets/javascript/midwest.js.map +1 -1
- package/app/assets/stylesheets/midwest.css +1 -1
- package/app/assets/stylesheets/midwest.tailwind.css +7 -1
- package/dist/css/midwest.css +1 -1
- package/dist/javascript/collection/app/assets/javascript/midwest/index.js +10 -0
- package/dist/javascript/collection/app/assets/javascript/midwest/index.js.map +1 -1
- package/dist/javascript/collection/app/assets/javascript/midwest/map_providers.js +56 -0
- package/dist/javascript/collection/app/assets/javascript/midwest/map_providers.js.map +1 -0
- package/dist/javascript/collection/app/assets/javascript/midwest/viewport_observer.js +22 -0
- package/dist/javascript/collection/app/assets/javascript/midwest/viewport_observer.js.map +1 -0
- package/dist/javascript/collection/app/components/midwest/carousel_component/carousel_component_controller.js +4 -7
- package/dist/javascript/collection/app/components/midwest/carousel_component/carousel_component_controller.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/dropdown_component/dropdown_component_controller.js +0 -6
- package/dist/javascript/collection/app/components/midwest/dropdown_component/dropdown_component_controller.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/form/address_component/address_component_controller.js +441 -0
- package/dist/javascript/collection/app/components/midwest/form/address_component/address_component_controller.js.map +1 -0
- package/dist/javascript/collection/app/components/midwest/form/live_summary_component/live_summary_component_controller.js +55 -1
- package/dist/javascript/collection/app/components/midwest/form/live_summary_component/live_summary_component_controller.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/intersection_observer_component/intersection_observer_component_controller.js +20 -25
- package/dist/javascript/collection/app/components/midwest/intersection_observer_component/intersection_observer_component_controller.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/map_component/map_component_controller.js +297 -0
- package/dist/javascript/collection/app/components/midwest/map_component/map_component_controller.js.map +1 -0
- package/dist/javascript/collection/app/components/midwest/motion_component/motion_component_controller.js +32 -0
- package/dist/javascript/collection/app/components/midwest/motion_component/motion_component_controller.js.map +1 -0
- package/dist/javascript/collection/app/components/midwest/notification_component/notification_component_controller.js +4 -12
- package/dist/javascript/collection/app/components/midwest/notification_component/notification_component_controller.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/onboarding_component/onboarding_component_controller.js +34 -5
- package/dist/javascript/collection/app/components/midwest/onboarding_component/onboarding_component_controller.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/page_transition_component/page_transition_component_controller.js +38 -0
- package/dist/javascript/collection/app/components/midwest/page_transition_component/page_transition_component_controller.js.map +1 -0
- package/dist/javascript/collection/app/components/midwest/popover_component/popover_component_controller.js +67 -0
- package/dist/javascript/collection/app/components/midwest/popover_component/popover_component_controller.js.map +1 -0
- package/dist/javascript/collection/app/components/midwest/table_component/load_more_controller.js +10 -14
- package/dist/javascript/collection/app/components/midwest/table_component/load_more_controller.js.map +1 -1
- package/dist/javascript/midwest.js +1056 -70
- package/dist/javascript/midwest.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
import { Controller } from '@hotwired/stimulus';
|
|
2
|
+
import { loadMapKitScript, loadGoogleMapsScript, loadLeafletScript } from '../../../../assets/javascript/midwest/map_providers.js';
|
|
3
|
+
|
|
4
|
+
const DEBOUNCE_MS = 300;
|
|
5
|
+
const GOOGLE_PREVIEW_STYLES = [
|
|
6
|
+
{ elementType: "geometry", stylers: [{ color: "#f5f5f5" }] },
|
|
7
|
+
{ elementType: "labels.icon", stylers: [{ visibility: "off" }] },
|
|
8
|
+
{ elementType: "labels.text.fill", stylers: [{ color: "#616161" }] },
|
|
9
|
+
{ elementType: "labels.text.stroke", stylers: [{ color: "#f5f5f5" }] },
|
|
10
|
+
{ featureType: "road", elementType: "geometry", stylers: [{ color: "#ffffff" }] },
|
|
11
|
+
{ featureType: "road.highway", elementType: "geometry", stylers: [{ color: "#dadada" }] },
|
|
12
|
+
{ featureType: "water", elementType: "geometry", stylers: [{ color: "#c9c9c9" }] },
|
|
13
|
+
{ featureType: "poi", elementType: "geometry", stylers: [{ color: "#eeeeee" }] }
|
|
14
|
+
];
|
|
15
|
+
class Address extends Controller {
|
|
16
|
+
static targets = [
|
|
17
|
+
"input",
|
|
18
|
+
"dropdown",
|
|
19
|
+
"suggestions",
|
|
20
|
+
"latInput",
|
|
21
|
+
"lngInput",
|
|
22
|
+
"placeIdInput",
|
|
23
|
+
"mapContainer"
|
|
24
|
+
];
|
|
25
|
+
static values = {
|
|
26
|
+
provider: { type: String, default: "" },
|
|
27
|
+
mapProvider: { type: String, default: "" },
|
|
28
|
+
apiKey: { type: String, default: "" },
|
|
29
|
+
token: { type: String, default: "" },
|
|
30
|
+
tokenUrl: { type: String, default: "" },
|
|
31
|
+
countryCodes: { type: Array, default: [] },
|
|
32
|
+
showMap: { type: Boolean, default: false },
|
|
33
|
+
nominatimUrl: { type: String, default: "" }
|
|
34
|
+
};
|
|
35
|
+
searchTimer;
|
|
36
|
+
highlightedIndex = -1;
|
|
37
|
+
mapInstance = null;
|
|
38
|
+
mapMarker = null;
|
|
39
|
+
mkSearch = null;
|
|
40
|
+
autocompleteService = null;
|
|
41
|
+
placesService = null;
|
|
42
|
+
// ── Named handlers ────────────────────────────────────────────────────────
|
|
43
|
+
onToggle = (e) => {
|
|
44
|
+
const opened = e.newState === "open";
|
|
45
|
+
if (this.hasInputTarget) {
|
|
46
|
+
this.inputTarget.setAttribute("aria-expanded", String(opened));
|
|
47
|
+
}
|
|
48
|
+
if (!opened) {
|
|
49
|
+
this.highlightedIndex = -1;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
onFocusOut = (e) => {
|
|
53
|
+
if (!this.element.contains(e.relatedTarget)) {
|
|
54
|
+
this.closeDropdown();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
// ── Lifecycle ─────────────────────────────────────────────────────────────
|
|
58
|
+
async connect() {
|
|
59
|
+
this.element.addEventListener("focusout", this.onFocusOut);
|
|
60
|
+
const provider = this.providerValue;
|
|
61
|
+
const mapProv = this.effectiveMapProvider();
|
|
62
|
+
if (!provider && !mapProv)
|
|
63
|
+
return;
|
|
64
|
+
try {
|
|
65
|
+
if (provider === "mapkit") {
|
|
66
|
+
await loadMapKitScript();
|
|
67
|
+
this.initMapKit();
|
|
68
|
+
} else if (provider === "google") {
|
|
69
|
+
await loadGoogleMapsScript(this.apiKeyValue);
|
|
70
|
+
this.initGoogle();
|
|
71
|
+
}
|
|
72
|
+
if (this.showMapValue && mapProv === "leaflet") {
|
|
73
|
+
await loadLeafletScript();
|
|
74
|
+
}
|
|
75
|
+
} catch (err) {
|
|
76
|
+
console.error("[midwest-address] Failed to initialise:", err);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
disconnect() {
|
|
80
|
+
clearTimeout(this.searchTimer);
|
|
81
|
+
this.element.removeEventListener("focusout", this.onFocusOut);
|
|
82
|
+
if (this.mapInstance) {
|
|
83
|
+
const mapProv = this.effectiveMapProvider();
|
|
84
|
+
if (mapProv === "mapkit")
|
|
85
|
+
this.mapInstance.destroy();
|
|
86
|
+
if (mapProv === "leaflet")
|
|
87
|
+
this.mapInstance.remove();
|
|
88
|
+
}
|
|
89
|
+
this.mapInstance = null;
|
|
90
|
+
}
|
|
91
|
+
dropdownTargetConnected(el) {
|
|
92
|
+
el.addEventListener("toggle", this.onToggle);
|
|
93
|
+
}
|
|
94
|
+
dropdownTargetDisconnected(el) {
|
|
95
|
+
el.removeEventListener("toggle", this.onToggle);
|
|
96
|
+
}
|
|
97
|
+
// ── Public Stimulus actions ───────────────────────────────────────────────
|
|
98
|
+
search() {
|
|
99
|
+
this.clearGeocode();
|
|
100
|
+
const query = this.inputTarget.value.trim();
|
|
101
|
+
if (query.length < 2) {
|
|
102
|
+
this.closeDropdown();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
clearTimeout(this.searchTimer);
|
|
106
|
+
this.searchTimer = setTimeout(() => {
|
|
107
|
+
if (this.providerValue === "mapkit") {
|
|
108
|
+
this.searchMapKit(query);
|
|
109
|
+
} else if (this.providerValue === "google") {
|
|
110
|
+
this.searchGoogle(query);
|
|
111
|
+
} else if (this.providerValue === "nominatim") {
|
|
112
|
+
this.searchNominatim(query);
|
|
113
|
+
}
|
|
114
|
+
}, DEBOUNCE_MS);
|
|
115
|
+
}
|
|
116
|
+
navigate(event) {
|
|
117
|
+
const items = this.suggestionElements();
|
|
118
|
+
if (!items.length)
|
|
119
|
+
return;
|
|
120
|
+
switch (event.key) {
|
|
121
|
+
case "ArrowDown":
|
|
122
|
+
event.preventDefault();
|
|
123
|
+
this.highlightedIndex = Math.min(this.highlightedIndex + 1, items.length - 1);
|
|
124
|
+
this.applyHighlight(items);
|
|
125
|
+
break;
|
|
126
|
+
case "ArrowUp":
|
|
127
|
+
event.preventDefault();
|
|
128
|
+
this.highlightedIndex = Math.max(this.highlightedIndex - 1, -1);
|
|
129
|
+
this.applyHighlight(items);
|
|
130
|
+
break;
|
|
131
|
+
case "Enter":
|
|
132
|
+
event.preventDefault();
|
|
133
|
+
if (this.highlightedIndex >= 0)
|
|
134
|
+
items[this.highlightedIndex].dispatchEvent(new MouseEvent("mousedown"));
|
|
135
|
+
break;
|
|
136
|
+
case "Escape":
|
|
137
|
+
this.closeDropdown();
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// ── Provider init ─────────────────────────────────────────────────────────
|
|
142
|
+
initMapKit() {
|
|
143
|
+
const mk = window.mapkit;
|
|
144
|
+
const { tokenValue: token, tokenUrlValue: tokenUrl } = this;
|
|
145
|
+
mk.init({
|
|
146
|
+
authorizationCallback: (done) => {
|
|
147
|
+
if (token) {
|
|
148
|
+
done(token);
|
|
149
|
+
} else if (tokenUrl) {
|
|
150
|
+
fetch(tokenUrl).then((r) => r.text()).then(done).catch((err) => console.error("[midwest-address] Token fetch failed:", err));
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
language: document.documentElement.lang || "en"
|
|
154
|
+
});
|
|
155
|
+
this.mkSearch = new mk.Search({ getsUserLocation: false });
|
|
156
|
+
}
|
|
157
|
+
initGoogle() {
|
|
158
|
+
const g = window.google;
|
|
159
|
+
this.autocompleteService = new g.maps.places.AutocompleteService();
|
|
160
|
+
this.placesService = new g.maps.places.PlacesService(document.createElement("div"));
|
|
161
|
+
}
|
|
162
|
+
// ── Search ────────────────────────────────────────────────────────────────
|
|
163
|
+
searchMapKit(query) {
|
|
164
|
+
if (!this.mkSearch)
|
|
165
|
+
return;
|
|
166
|
+
this.mkSearch.autocomplete(query, (err, data) => {
|
|
167
|
+
if (err || !data?.results?.length) {
|
|
168
|
+
this.closeDropdown();
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const results = data.results.map((r) => ({
|
|
172
|
+
id: r.completionURL ?? r.displayLines?.join(", ") ?? "",
|
|
173
|
+
label: r.displayLines?.[0] ?? "",
|
|
174
|
+
secondary: r.displayLines?.[1] ?? "",
|
|
175
|
+
payload: r
|
|
176
|
+
}));
|
|
177
|
+
this.renderSuggestions(results);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
searchGoogle(query) {
|
|
181
|
+
if (!this.autocompleteService)
|
|
182
|
+
return;
|
|
183
|
+
const request = { input: query };
|
|
184
|
+
if (this.countryCodesValue.length) {
|
|
185
|
+
request.componentRestrictions = { country: this.countryCodesValue };
|
|
186
|
+
}
|
|
187
|
+
this.autocompleteService.getPlacePredictions(
|
|
188
|
+
request,
|
|
189
|
+
(predictions, status) => {
|
|
190
|
+
const g = window.google;
|
|
191
|
+
if (status !== g.maps.places.PlacesServiceStatus.OK || !predictions?.length) {
|
|
192
|
+
this.closeDropdown();
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const results = predictions.map((p) => ({
|
|
196
|
+
id: p.place_id,
|
|
197
|
+
label: p.structured_formatting?.main_text ?? p.description,
|
|
198
|
+
secondary: p.structured_formatting?.secondary_text ?? "",
|
|
199
|
+
payload: p
|
|
200
|
+
}));
|
|
201
|
+
this.renderSuggestions(results);
|
|
202
|
+
}
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
// ── Suggestions UI ────────────────────────────────────────────────────────
|
|
206
|
+
renderSuggestions(results) {
|
|
207
|
+
if (!this.hasSuggestionsTarget)
|
|
208
|
+
return;
|
|
209
|
+
const list = this.suggestionsTarget;
|
|
210
|
+
list.innerHTML = "";
|
|
211
|
+
results.forEach((r, i) => {
|
|
212
|
+
const li = document.createElement("li");
|
|
213
|
+
li.className = "midwest-address__suggestion";
|
|
214
|
+
li.setAttribute("role", "option");
|
|
215
|
+
li.setAttribute("id", `${this.element.id || "addr"}-s${i}`);
|
|
216
|
+
li.dataset.index = String(i);
|
|
217
|
+
const main = document.createElement("span");
|
|
218
|
+
main.className = "midwest-address__suggestion-main";
|
|
219
|
+
main.textContent = r.label;
|
|
220
|
+
li.appendChild(main);
|
|
221
|
+
if (r.secondary) {
|
|
222
|
+
const sec = document.createElement("span");
|
|
223
|
+
sec.className = "midwest-address__suggestion-secondary";
|
|
224
|
+
sec.textContent = r.secondary;
|
|
225
|
+
li.appendChild(sec);
|
|
226
|
+
}
|
|
227
|
+
li.addEventListener("mousedown", (e) => {
|
|
228
|
+
e.preventDefault();
|
|
229
|
+
this.pickSuggestion(r);
|
|
230
|
+
});
|
|
231
|
+
list.appendChild(li);
|
|
232
|
+
});
|
|
233
|
+
this.openDropdown();
|
|
234
|
+
this.highlightedIndex = -1;
|
|
235
|
+
}
|
|
236
|
+
openDropdown() {
|
|
237
|
+
if (!this.hasDropdownTarget)
|
|
238
|
+
return;
|
|
239
|
+
if (!this.dropdownTarget.matches(":popover-open"))
|
|
240
|
+
this.dropdownTarget.showPopover();
|
|
241
|
+
}
|
|
242
|
+
closeDropdown() {
|
|
243
|
+
if (this.hasDropdownTarget && this.dropdownTarget.matches(":popover-open")) {
|
|
244
|
+
this.dropdownTarget.hidePopover();
|
|
245
|
+
}
|
|
246
|
+
if (this.hasSuggestionsTarget)
|
|
247
|
+
this.suggestionsTarget.innerHTML = "";
|
|
248
|
+
this.highlightedIndex = -1;
|
|
249
|
+
}
|
|
250
|
+
suggestionElements() {
|
|
251
|
+
if (!this.hasSuggestionsTarget)
|
|
252
|
+
return [];
|
|
253
|
+
return Array.from(this.suggestionsTarget.querySelectorAll(".midwest-address__suggestion"));
|
|
254
|
+
}
|
|
255
|
+
applyHighlight(items) {
|
|
256
|
+
items.forEach((item, i) => {
|
|
257
|
+
const active = i === this.highlightedIndex;
|
|
258
|
+
item.classList.toggle("is-highlighted", active);
|
|
259
|
+
item.setAttribute("aria-selected", String(active));
|
|
260
|
+
if (active) {
|
|
261
|
+
this.inputTarget.setAttribute("aria-activedescendant", item.id);
|
|
262
|
+
item.scrollIntoView({ block: "nearest" });
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
if (this.highlightedIndex < 0) {
|
|
266
|
+
this.inputTarget.removeAttribute("aria-activedescendant");
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// ── Pick a suggestion ─────────────────────────────────────────────────────
|
|
270
|
+
pickSuggestion(r) {
|
|
271
|
+
this.closeDropdown();
|
|
272
|
+
if (this.providerValue === "mapkit") {
|
|
273
|
+
this.pickMapKit(r);
|
|
274
|
+
} else if (this.providerValue === "google") {
|
|
275
|
+
this.pickGoogle(r);
|
|
276
|
+
} else if (this.providerValue === "nominatim") {
|
|
277
|
+
this.pickNominatim(r);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
pickMapKit(r) {
|
|
281
|
+
if (!this.mkSearch)
|
|
282
|
+
return;
|
|
283
|
+
this.mkSearch.search(r.payload, (err, data) => {
|
|
284
|
+
if (err || !data?.places?.length)
|
|
285
|
+
return;
|
|
286
|
+
const place = data.places[0];
|
|
287
|
+
const coord = place.coordinate;
|
|
288
|
+
this.inputTarget.value = [r.label, r.secondary].filter(Boolean).join(", ");
|
|
289
|
+
this.fillGeocode(coord.latitude, coord.longitude, place.id ?? "");
|
|
290
|
+
this.updateMapPreview(coord.latitude, coord.longitude);
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
pickGoogle(r) {
|
|
294
|
+
if (!this.placesService || !r.id)
|
|
295
|
+
return;
|
|
296
|
+
const g = window.google;
|
|
297
|
+
this.placesService.getDetails(
|
|
298
|
+
{ placeId: r.id, fields: ["geometry", "formatted_address", "place_id"] },
|
|
299
|
+
(place, status) => {
|
|
300
|
+
if (status !== g.maps.places.PlacesServiceStatus.OK || !place)
|
|
301
|
+
return;
|
|
302
|
+
const lat = place.geometry.location.lat();
|
|
303
|
+
const lng = place.geometry.location.lng();
|
|
304
|
+
this.inputTarget.value = place.formatted_address;
|
|
305
|
+
this.fillGeocode(lat, lng, place.place_id);
|
|
306
|
+
this.updateMapPreview(lat, lng);
|
|
307
|
+
}
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
// ── Geocode field management ──────────────────────────────────────────────
|
|
311
|
+
fillGeocode(lat, lng, placeId) {
|
|
312
|
+
if (this.hasLatInputTarget)
|
|
313
|
+
this.latInputTarget.value = String(lat);
|
|
314
|
+
if (this.hasLngInputTarget)
|
|
315
|
+
this.lngInputTarget.value = String(lng);
|
|
316
|
+
if (this.hasPlaceIdInputTarget)
|
|
317
|
+
this.placeIdInputTarget.value = placeId;
|
|
318
|
+
this.dispatch("geocode", { detail: { lat, lng, placeId } });
|
|
319
|
+
}
|
|
320
|
+
clearGeocode() {
|
|
321
|
+
if (this.hasLatInputTarget)
|
|
322
|
+
this.latInputTarget.value = "";
|
|
323
|
+
if (this.hasLngInputTarget)
|
|
324
|
+
this.lngInputTarget.value = "";
|
|
325
|
+
if (this.hasPlaceIdInputTarget)
|
|
326
|
+
this.placeIdInputTarget.value = "";
|
|
327
|
+
}
|
|
328
|
+
// ── Nominatim search (OpenStreetMap, no API key) ──────────────────────────
|
|
329
|
+
searchNominatim(query) {
|
|
330
|
+
const base = this.nominatimUrlValue || "https://nominatim.openstreetmap.org";
|
|
331
|
+
const url = `${base}/search?q=${encodeURIComponent(query)}&format=json&limit=5&addressdetails=1`;
|
|
332
|
+
fetch(url, { headers: { "Accept-Language": document.documentElement.lang || "en" } }).then((r) => r.json()).then((data) => {
|
|
333
|
+
if (!data?.length) {
|
|
334
|
+
this.closeDropdown();
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const results = data.map((r) => {
|
|
338
|
+
const parts = r.display_name.split(", ");
|
|
339
|
+
return {
|
|
340
|
+
id: String(r.place_id),
|
|
341
|
+
label: r.name || parts[0] || r.display_name,
|
|
342
|
+
secondary: parts.length > 1 ? parts.slice(1).join(", ") : "",
|
|
343
|
+
payload: r
|
|
344
|
+
};
|
|
345
|
+
});
|
|
346
|
+
this.renderSuggestions(results);
|
|
347
|
+
}).catch(() => this.closeDropdown());
|
|
348
|
+
}
|
|
349
|
+
pickNominatim(r) {
|
|
350
|
+
const place = r.payload;
|
|
351
|
+
const lat = parseFloat(place.lat);
|
|
352
|
+
const lng = parseFloat(place.lon);
|
|
353
|
+
this.inputTarget.value = place.display_name;
|
|
354
|
+
this.fillGeocode(lat, lng, `${place.osm_type}:${place.osm_id}`);
|
|
355
|
+
this.updateMapPreview(lat, lng);
|
|
356
|
+
}
|
|
357
|
+
// ── Map preview ───────────────────────────────────────────────────────────
|
|
358
|
+
// Returns the provider used for the map preview. `mapProviderValue` is set
|
|
359
|
+
// when the geocoder and map providers differ; falls back to `providerValue`
|
|
360
|
+
// so existing single-provider setups (mapkit/google) work without change.
|
|
361
|
+
effectiveMapProvider() {
|
|
362
|
+
return this.mapProviderValue || this.providerValue;
|
|
363
|
+
}
|
|
364
|
+
updateMapPreview(lat, lng) {
|
|
365
|
+
if (!this.hasMapContainerTarget || !this.showMapValue)
|
|
366
|
+
return;
|
|
367
|
+
this.mapContainerTarget.hidden = false;
|
|
368
|
+
const mapProv = this.effectiveMapProvider();
|
|
369
|
+
if (mapProv === "mapkit") {
|
|
370
|
+
this.updateMapKitPreview(lat, lng);
|
|
371
|
+
} else if (mapProv === "google") {
|
|
372
|
+
this.updateGooglePreview(lat, lng);
|
|
373
|
+
} else if (mapProv === "leaflet") {
|
|
374
|
+
this.updateLeafletPreview(lat, lng);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
updateMapKitPreview(lat, lng) {
|
|
378
|
+
const mk = window.mapkit;
|
|
379
|
+
const coord = new mk.Coordinate(lat, lng);
|
|
380
|
+
if (!this.mapInstance) {
|
|
381
|
+
this.mapInstance = new mk.Map(this.mapContainerTarget, {
|
|
382
|
+
mapType: mk.Map.MapTypes.MutedStandard,
|
|
383
|
+
showsCompass: mk.FeatureVisibility.Hidden,
|
|
384
|
+
showsZoomControl: false,
|
|
385
|
+
showsMapTypeControl: false,
|
|
386
|
+
isScrollEnabled: false,
|
|
387
|
+
isZoomEnabled: false
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
this.mapInstance.setRegionAnimated(
|
|
391
|
+
new mk.CoordinateRegion(coord, new mk.CoordinateSpan(0.01, 0.01))
|
|
392
|
+
);
|
|
393
|
+
if (this.mapMarker)
|
|
394
|
+
this.mapInstance.removeAnnotation(this.mapMarker);
|
|
395
|
+
this.mapMarker = new mk.MarkerAnnotation(coord);
|
|
396
|
+
this.mapInstance.addAnnotation(this.mapMarker);
|
|
397
|
+
}
|
|
398
|
+
updateGooglePreview(lat, lng) {
|
|
399
|
+
const g = window.google;
|
|
400
|
+
const center = { lat, lng };
|
|
401
|
+
if (!this.mapInstance) {
|
|
402
|
+
this.mapInstance = new g.maps.Map(this.mapContainerTarget, {
|
|
403
|
+
center,
|
|
404
|
+
zoom: 15,
|
|
405
|
+
disableDefaultUI: true,
|
|
406
|
+
gestureHandling: "none",
|
|
407
|
+
styles: GOOGLE_PREVIEW_STYLES
|
|
408
|
+
});
|
|
409
|
+
} else {
|
|
410
|
+
this.mapInstance.setCenter(center);
|
|
411
|
+
this.mapInstance.setZoom(15);
|
|
412
|
+
}
|
|
413
|
+
if (this.mapMarker)
|
|
414
|
+
this.mapMarker.setMap(null);
|
|
415
|
+
this.mapMarker = new g.maps.Marker({ map: this.mapInstance, position: center });
|
|
416
|
+
}
|
|
417
|
+
updateLeafletPreview(lat, lng) {
|
|
418
|
+
const L = window.L;
|
|
419
|
+
if (!L)
|
|
420
|
+
return;
|
|
421
|
+
if (!this.mapInstance) {
|
|
422
|
+
this.mapInstance = L.map(this.mapContainerTarget, {
|
|
423
|
+
center: [lat, lng],
|
|
424
|
+
zoom: 15,
|
|
425
|
+
zoomControl: false,
|
|
426
|
+
attributionControl: false
|
|
427
|
+
});
|
|
428
|
+
L.tileLayer("https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png", {
|
|
429
|
+
maxZoom: 19
|
|
430
|
+
}).addTo(this.mapInstance);
|
|
431
|
+
} else {
|
|
432
|
+
this.mapInstance.setView([lat, lng], 15);
|
|
433
|
+
}
|
|
434
|
+
if (this.mapMarker)
|
|
435
|
+
this.mapMarker.remove();
|
|
436
|
+
this.mapMarker = L.marker([lat, lng]).addTo(this.mapInstance);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
export { Address as default };
|
|
441
|
+
//# sourceMappingURL=address_component_controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"address_component_controller.js","sources":["../../../../../../../../app/components/midwest/form/address_component/address_component_controller.ts"],"sourcesContent":["import { Controller } from '@hotwired/stimulus'\nimport { loadMapKitScript, loadGoogleMapsScript, loadLeafletScript } from '../../../../assets/javascript/midwest/map_providers'\n\n// Milliseconds to wait after the last keystroke before firing the search.\nconst DEBOUNCE_MS = 300\n\ninterface NominatimResult {\n place_id: number\n osm_type: string\n osm_id: number\n lat: string\n lon: string\n display_name: string\n name?: string\n address?: Record<string, string>\n}\n\n// Compact muted style for the Google Maps preview.\nconst GOOGLE_PREVIEW_STYLES = [\n { elementType: 'geometry', stylers: [{ color: '#f5f5f5' }] },\n { elementType: 'labels.icon', stylers: [{ visibility: 'off' }] },\n { elementType: 'labels.text.fill', stylers: [{ color: '#616161' }] },\n { elementType: 'labels.text.stroke', stylers: [{ color: '#f5f5f5' }] },\n { featureType: 'road', elementType: 'geometry', stylers: [{ color: '#ffffff' }] },\n { featureType: 'road.highway', elementType: 'geometry', stylers: [{ color: '#dadada' }] },\n { featureType: 'water', elementType: 'geometry', stylers: [{ color: '#c9c9c9' }] },\n { featureType: 'poi', elementType: 'geometry', stylers: [{ color: '#eeeeee' }] }\n]\n\ninterface Suggestion {\n id: string\n label: string\n secondary: string\n // Opaque payload carried through to the detail fetch\n payload: any\n}\n\nexport default class Address extends Controller<HTMLElement> {\n static targets = [\n 'input',\n 'dropdown',\n 'suggestions',\n 'latInput',\n 'lngInput',\n 'placeIdInput',\n 'mapContainer'\n ]\n\n static values = {\n provider: { type: String, default: '' },\n mapProvider: { type: String, default: '' },\n apiKey: { type: String, default: '' },\n token: { type: String, default: '' },\n tokenUrl: { type: String, default: '' },\n countryCodes: { type: Array, default: [] },\n showMap: { type: Boolean, default: false },\n nominatimUrl: { type: String, default: '' }\n }\n\n declare readonly inputTarget: HTMLInputElement\n declare readonly hasInputTarget: boolean\n declare readonly dropdownTarget: HTMLElement\n declare readonly hasDropdownTarget: boolean\n declare readonly suggestionsTarget: HTMLUListElement\n declare readonly hasSuggestionsTarget: boolean\n declare readonly latInputTarget: HTMLInputElement\n declare readonly hasLatInputTarget: boolean\n declare readonly lngInputTarget: HTMLInputElement\n declare readonly hasLngInputTarget: boolean\n declare readonly placeIdInputTarget: HTMLInputElement\n declare readonly hasPlaceIdInputTarget: boolean\n declare readonly mapContainerTarget: HTMLElement\n declare readonly hasMapContainerTarget: boolean\n\n declare providerValue: string\n declare mapProviderValue: string\n declare apiKeyValue: string\n declare tokenValue: string\n declare tokenUrlValue: string\n declare countryCodesValue: string[]\n declare showMapValue: boolean\n declare nominatimUrlValue: string\n\n private searchTimer: ReturnType<typeof setTimeout> | undefined\n private highlightedIndex = -1\n private mapInstance: any = null\n private mapMarker: any = null\n private mkSearch: any = null\n private autocompleteService: any = null\n private placesService: any = null\n\n // ── Named handlers ────────────────────────────────────────────────────────\n\n private onToggle = (e: Event) => {\n const opened = (e as ToggleEvent).newState === 'open'\n if (this.hasInputTarget) {\n this.inputTarget.setAttribute('aria-expanded', String(opened))\n }\n if (!opened) {\n this.highlightedIndex = -1\n }\n }\n\n private onFocusOut = (e: FocusEvent) => {\n if (!this.element.contains(e.relatedTarget as Node)) {\n this.closeDropdown()\n }\n }\n\n // ── Lifecycle ─────────────────────────────────────────────────────────────\n\n async connect (): Promise<void> {\n this.element.addEventListener('focusout', this.onFocusOut)\n\n const provider = this.providerValue\n const mapProv = this.effectiveMapProvider()\n\n if (!provider && !mapProv) return\n\n try {\n if (provider === 'mapkit') {\n await loadMapKitScript()\n this.initMapKit()\n } else if (provider === 'google') {\n await loadGoogleMapsScript(this.apiKeyValue)\n this.initGoogle()\n }\n // nominatim: no SDK to load\n\n // Pre-load Leaflet when it will be needed for the map preview\n if (this.showMapValue && mapProv === 'leaflet') {\n await loadLeafletScript()\n }\n } catch (err) {\n console.error('[midwest-address] Failed to initialise:', err)\n }\n }\n\n disconnect (): void {\n clearTimeout(this.searchTimer)\n this.element.removeEventListener('focusout', this.onFocusOut)\n if (this.mapInstance) {\n const mapProv = this.effectiveMapProvider()\n if (mapProv === 'mapkit') this.mapInstance.destroy()\n if (mapProv === 'leaflet') this.mapInstance.remove()\n }\n this.mapInstance = null\n }\n\n dropdownTargetConnected (el: HTMLElement) {\n el.addEventListener('toggle', this.onToggle)\n }\n\n dropdownTargetDisconnected (el: HTMLElement) {\n el.removeEventListener('toggle', this.onToggle)\n }\n\n // ── Public Stimulus actions ───────────────────────────────────────────────\n\n search (): void {\n this.clearGeocode()\n\n const query = this.inputTarget.value.trim()\n if (query.length < 2) {\n this.closeDropdown()\n return\n }\n\n clearTimeout(this.searchTimer)\n this.searchTimer = setTimeout(() => {\n if (this.providerValue === 'mapkit') {\n this.searchMapKit(query)\n } else if (this.providerValue === 'google') {\n this.searchGoogle(query)\n } else if (this.providerValue === 'nominatim') {\n this.searchNominatim(query)\n }\n }, DEBOUNCE_MS)\n }\n\n navigate (event: KeyboardEvent): void {\n const items = this.suggestionElements()\n if (!items.length) return\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n this.highlightedIndex = Math.min(this.highlightedIndex + 1, items.length - 1)\n this.applyHighlight(items)\n break\n\n case 'ArrowUp':\n event.preventDefault()\n this.highlightedIndex = Math.max(this.highlightedIndex - 1, -1)\n this.applyHighlight(items)\n break\n\n case 'Enter':\n event.preventDefault()\n if (this.highlightedIndex >= 0) items[this.highlightedIndex].dispatchEvent(new MouseEvent('mousedown'))\n break\n\n case 'Escape':\n this.closeDropdown()\n break\n }\n }\n\n // ── Provider init ─────────────────────────────────────────────────────────\n\n private initMapKit (): void {\n const mk = (window as any).mapkit\n const { tokenValue: token, tokenUrlValue: tokenUrl } = this\n\n mk.init({\n authorizationCallback: (done: (t: string) => void) => {\n if (token) {\n done(token)\n } else if (tokenUrl) {\n fetch(tokenUrl)\n .then(r => r.text())\n .then(done)\n .catch(err => console.error('[midwest-address] Token fetch failed:', err))\n }\n },\n language: document.documentElement.lang || 'en'\n })\n\n this.mkSearch = new mk.Search({ getsUserLocation: false })\n }\n\n private initGoogle (): void {\n const g = (window as any).google\n this.autocompleteService = new g.maps.places.AutocompleteService()\n // PlacesService requires a DOM element or map instance; pass a detached div\n // so the component doesn't depend on a rendered map for detail fetches.\n this.placesService = new g.maps.places.PlacesService(document.createElement('div'))\n }\n\n // ── Search ────────────────────────────────────────────────────────────────\n\n private searchMapKit (query: string): void {\n if (!this.mkSearch) return\n\n this.mkSearch.autocomplete(query, (err: any, data: any) => {\n if (err || !data?.results?.length) {\n this.closeDropdown()\n return\n }\n\n const results: Suggestion[] = (data.results as any[]).map(r => ({\n id: r.completionURL ?? r.displayLines?.join(', ') ?? '',\n label: r.displayLines?.[0] ?? '',\n secondary: r.displayLines?.[1] ?? '',\n payload: r\n }))\n\n this.renderSuggestions(results)\n })\n }\n\n private searchGoogle (query: string): void {\n if (!this.autocompleteService) return\n\n const request: any = { input: query }\n if (this.countryCodesValue.length) {\n request.componentRestrictions = { country: this.countryCodesValue }\n }\n\n this.autocompleteService.getPlacePredictions(\n request,\n (predictions: any[] | null, status: string) => {\n const g = (window as any).google\n if (status !== g.maps.places.PlacesServiceStatus.OK || !predictions?.length) {\n this.closeDropdown()\n return\n }\n\n const results: Suggestion[] = predictions.map(p => ({\n id: p.place_id,\n label: p.structured_formatting?.main_text ?? p.description,\n secondary: p.structured_formatting?.secondary_text ?? '',\n payload: p\n }))\n\n this.renderSuggestions(results)\n }\n )\n }\n\n // ── Suggestions UI ────────────────────────────────────────────────────────\n\n private renderSuggestions (results: Suggestion[]): void {\n if (!this.hasSuggestionsTarget) return\n\n const list = this.suggestionsTarget\n list.innerHTML = ''\n\n results.forEach((r, i) => {\n const li = document.createElement('li')\n li.className = 'midwest-address__suggestion'\n li.setAttribute('role', 'option')\n li.setAttribute('id', `${this.element.id || 'addr'}-s${i}`)\n li.dataset.index = String(i)\n\n const main = document.createElement('span')\n main.className = 'midwest-address__suggestion-main'\n main.textContent = r.label\n li.appendChild(main)\n\n if (r.secondary) {\n const sec = document.createElement('span')\n sec.className = 'midwest-address__suggestion-secondary'\n sec.textContent = r.secondary\n li.appendChild(sec)\n }\n\n // mousedown fires before blur so the input doesn't lose focus before we\n // can read its value and fill the hidden fields.\n li.addEventListener('mousedown', (e) => {\n e.preventDefault()\n this.pickSuggestion(r)\n })\n\n list.appendChild(li)\n })\n\n this.openDropdown()\n this.highlightedIndex = -1\n }\n\n private openDropdown (): void {\n if (!this.hasDropdownTarget) return\n if (!this.dropdownTarget.matches(':popover-open')) this.dropdownTarget.showPopover()\n }\n\n private closeDropdown (): void {\n if (this.hasDropdownTarget && this.dropdownTarget.matches(':popover-open')) {\n this.dropdownTarget.hidePopover()\n }\n if (this.hasSuggestionsTarget) this.suggestionsTarget.innerHTML = ''\n this.highlightedIndex = -1\n }\n\n private suggestionElements (): HTMLLIElement[] {\n if (!this.hasSuggestionsTarget) return []\n return Array.from(this.suggestionsTarget.querySelectorAll<HTMLLIElement>('.midwest-address__suggestion'))\n }\n\n private applyHighlight (items: HTMLLIElement[]): void {\n items.forEach((item, i) => {\n const active = i === this.highlightedIndex\n item.classList.toggle('is-highlighted', active)\n item.setAttribute('aria-selected', String(active))\n if (active) {\n this.inputTarget.setAttribute('aria-activedescendant', item.id)\n item.scrollIntoView({ block: 'nearest' })\n }\n })\n if (this.highlightedIndex < 0) {\n this.inputTarget.removeAttribute('aria-activedescendant')\n }\n }\n\n // ── Pick a suggestion ─────────────────────────────────────────────────────\n\n private pickSuggestion (r: Suggestion): void {\n this.closeDropdown()\n\n if (this.providerValue === 'mapkit') {\n this.pickMapKit(r)\n } else if (this.providerValue === 'google') {\n this.pickGoogle(r)\n } else if (this.providerValue === 'nominatim') {\n this.pickNominatim(r)\n }\n }\n\n private pickMapKit (r: Suggestion): void {\n if (!this.mkSearch) return\n\n // Pass the raw completion object so MapKit resolves the exact place.\n this.mkSearch.search(r.payload, (err: any, data: any) => {\n if (err || !data?.places?.length) return\n\n const place = data.places[0]\n const coord = place.coordinate\n\n this.inputTarget.value = [r.label, r.secondary].filter(Boolean).join(', ')\n this.fillGeocode(coord.latitude, coord.longitude, place.id ?? '')\n this.updateMapPreview(coord.latitude, coord.longitude)\n })\n }\n\n private pickGoogle (r: Suggestion): void {\n if (!this.placesService || !r.id) return\n\n const g = (window as any).google\n this.placesService.getDetails(\n { placeId: r.id, fields: ['geometry', 'formatted_address', 'place_id'] },\n (place: any, status: string) => {\n if (status !== g.maps.places.PlacesServiceStatus.OK || !place) return\n\n const lat = place.geometry.location.lat()\n const lng = place.geometry.location.lng()\n\n this.inputTarget.value = place.formatted_address\n this.fillGeocode(lat, lng, place.place_id)\n this.updateMapPreview(lat, lng)\n }\n )\n }\n\n // ── Geocode field management ──────────────────────────────────────────────\n\n private fillGeocode (lat: number, lng: number, placeId: string): void {\n if (this.hasLatInputTarget) this.latInputTarget.value = String(lat)\n if (this.hasLngInputTarget) this.lngInputTarget.value = String(lng)\n if (this.hasPlaceIdInputTarget) this.placeIdInputTarget.value = placeId\n this.dispatch('geocode', { detail: { lat, lng, placeId } })\n }\n\n private clearGeocode (): void {\n if (this.hasLatInputTarget) this.latInputTarget.value = ''\n if (this.hasLngInputTarget) this.lngInputTarget.value = ''\n if (this.hasPlaceIdInputTarget) this.placeIdInputTarget.value = ''\n }\n\n // ── Nominatim search (OpenStreetMap, no API key) ──────────────────────────\n\n private searchNominatim (query: string): void {\n const base = this.nominatimUrlValue || 'https://nominatim.openstreetmap.org'\n const url = `${base}/search?q=${encodeURIComponent(query)}&format=json&limit=5&addressdetails=1`\n\n fetch(url, { headers: { 'Accept-Language': document.documentElement.lang || 'en' } })\n .then(r => r.json())\n .then((data: NominatimResult[]) => {\n if (!data?.length) { this.closeDropdown(); return }\n\n const results: Suggestion[] = data.map(r => {\n const parts = r.display_name.split(', ')\n return {\n id: String(r.place_id),\n label: r.name || parts[0] || r.display_name,\n secondary: parts.length > 1 ? parts.slice(1).join(', ') : '',\n payload: r\n }\n })\n\n this.renderSuggestions(results)\n })\n .catch(() => this.closeDropdown())\n }\n\n private pickNominatim (r: Suggestion): void {\n const place = r.payload as NominatimResult\n const lat = parseFloat(place.lat)\n const lng = parseFloat(place.lon)\n\n this.inputTarget.value = place.display_name\n this.fillGeocode(lat, lng, `${place.osm_type}:${place.osm_id}`)\n this.updateMapPreview(lat, lng)\n }\n\n // ── Map preview ───────────────────────────────────────────────────────────\n\n // Returns the provider used for the map preview. `mapProviderValue` is set\n // when the geocoder and map providers differ; falls back to `providerValue`\n // so existing single-provider setups (mapkit/google) work without change.\n private effectiveMapProvider (): string {\n return this.mapProviderValue || this.providerValue\n }\n\n private updateMapPreview (lat: number, lng: number): void {\n if (!this.hasMapContainerTarget || !this.showMapValue) return\n\n this.mapContainerTarget.hidden = false\n\n const mapProv = this.effectiveMapProvider()\n if (mapProv === 'mapkit') {\n this.updateMapKitPreview(lat, lng)\n } else if (mapProv === 'google') {\n this.updateGooglePreview(lat, lng)\n } else if (mapProv === 'leaflet') {\n this.updateLeafletPreview(lat, lng)\n }\n }\n\n private updateMapKitPreview (lat: number, lng: number): void {\n const mk = (window as any).mapkit\n const coord = new mk.Coordinate(lat, lng)\n\n if (!this.mapInstance) {\n this.mapInstance = new mk.Map(this.mapContainerTarget, {\n mapType: mk.Map.MapTypes.MutedStandard,\n showsCompass: mk.FeatureVisibility.Hidden,\n showsZoomControl: false,\n showsMapTypeControl: false,\n isScrollEnabled: false,\n isZoomEnabled: false\n })\n }\n\n this.mapInstance.setRegionAnimated(\n new mk.CoordinateRegion(coord, new mk.CoordinateSpan(0.01, 0.01))\n )\n\n if (this.mapMarker) this.mapInstance.removeAnnotation(this.mapMarker)\n this.mapMarker = new mk.MarkerAnnotation(coord)\n this.mapInstance.addAnnotation(this.mapMarker)\n }\n\n private updateGooglePreview (lat: number, lng: number): void {\n const g = (window as any).google\n const center = { lat, lng }\n\n if (!this.mapInstance) {\n this.mapInstance = new g.maps.Map(this.mapContainerTarget, {\n center,\n zoom: 15,\n disableDefaultUI: true,\n gestureHandling: 'none',\n styles: GOOGLE_PREVIEW_STYLES\n })\n } else {\n this.mapInstance.setCenter(center)\n this.mapInstance.setZoom(15)\n }\n\n if (this.mapMarker) this.mapMarker.setMap(null)\n this.mapMarker = new g.maps.Marker({ map: this.mapInstance, position: center })\n }\n\n private updateLeafletPreview (lat: number, lng: number): void {\n const L = (window as any).L\n if (!L) return\n\n if (!this.mapInstance) {\n this.mapInstance = L.map(this.mapContainerTarget, {\n center: [lat, lng],\n zoom: 15,\n zoomControl: false,\n attributionControl: false\n })\n L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {\n maxZoom: 19\n }).addTo(this.mapInstance)\n } else {\n this.mapInstance.setView([lat, lng], 15)\n }\n\n if (this.mapMarker) this.mapMarker.remove()\n this.mapMarker = L.marker([lat, lng]).addTo(this.mapInstance)\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -16,6 +16,7 @@ class FormLiveSummary extends Controller {
|
|
|
16
16
|
this.form?.addEventListener("midwest-autocomplete:change", this.render);
|
|
17
17
|
this.form?.addEventListener("midwest-color-picker:update", this.render);
|
|
18
18
|
this.form?.addEventListener("midwest-repeatable:change", this.render);
|
|
19
|
+
this.form?.addEventListener("midwest-address:geocode", this.render);
|
|
19
20
|
}
|
|
20
21
|
disconnect() {
|
|
21
22
|
this.form?.removeEventListener("input", this.render);
|
|
@@ -23,6 +24,7 @@ class FormLiveSummary extends Controller {
|
|
|
23
24
|
this.form?.removeEventListener("midwest-autocomplete:change", this.render);
|
|
24
25
|
this.form?.removeEventListener("midwest-color-picker:update", this.render);
|
|
25
26
|
this.form?.removeEventListener("midwest-repeatable:change", this.render);
|
|
27
|
+
this.form?.removeEventListener("midwest-address:geocode", this.render);
|
|
26
28
|
}
|
|
27
29
|
get form() {
|
|
28
30
|
return this.element.closest("form");
|
|
@@ -40,7 +42,7 @@ class FormLiveSummary extends Controller {
|
|
|
40
42
|
}
|
|
41
43
|
const dl = document.createElement("dl");
|
|
42
44
|
dl.className = "midwest-form-live-summary-list";
|
|
43
|
-
fields.forEach(({ label, value, swatchColor }) => {
|
|
45
|
+
fields.forEach(({ label, value, swatchColor, details }) => {
|
|
44
46
|
const dt = Object.assign(document.createElement("dt"), {
|
|
45
47
|
className: "midwest-form-live-summary-term",
|
|
46
48
|
textContent: label
|
|
@@ -49,6 +51,8 @@ class FormLiveSummary extends Controller {
|
|
|
49
51
|
dd.className = "midwest-form-live-summary-definition";
|
|
50
52
|
if (swatchColor !== void 0) {
|
|
51
53
|
dd.append(this.buildSwatch(swatchColor), value || "\u2014");
|
|
54
|
+
} else if (details && Object.keys(details).length > 0) {
|
|
55
|
+
dd.append(this.buildGeocode(value || "\u2014", details));
|
|
52
56
|
} else {
|
|
53
57
|
dd.textContent = value || "\u2014";
|
|
54
58
|
}
|
|
@@ -76,6 +80,8 @@ class FormLiveSummary extends Controller {
|
|
|
76
80
|
return;
|
|
77
81
|
if (element.closest(".midwest-repeatable"))
|
|
78
82
|
return;
|
|
83
|
+
if (element.closest(".midwest-address"))
|
|
84
|
+
return;
|
|
79
85
|
if (element instanceof HTMLInputElement) {
|
|
80
86
|
if (SKIPPED_INPUT_TYPES.has(element.type))
|
|
81
87
|
return;
|
|
@@ -195,6 +201,29 @@ class FormLiveSummary extends Controller {
|
|
|
195
201
|
});
|
|
196
202
|
});
|
|
197
203
|
});
|
|
204
|
+
form.querySelectorAll(".midwest-address").forEach((addr) => {
|
|
205
|
+
if (this.element.contains(addr))
|
|
206
|
+
return;
|
|
207
|
+
const input = addr.querySelector('[data-midwest-address-target="input"]');
|
|
208
|
+
const latInput = addr.querySelector('[data-midwest-address-target="latInput"]');
|
|
209
|
+
const lngInput = addr.querySelector('[data-midwest-address-target="lngInput"]');
|
|
210
|
+
const placeIdInput = addr.querySelector('[data-midwest-address-target="placeIdInput"]');
|
|
211
|
+
const labelEl = addr.querySelector(".midwest-form-group__top label");
|
|
212
|
+
const label = labelEl?.textContent?.trim() || this.prettifyName((input?.name ?? latInput?.name?.replace(/_latitude$/, "") ?? "").replace(/\[.*\]$/, ""));
|
|
213
|
+
const value = input?.value?.trim() || "";
|
|
214
|
+
const details = {};
|
|
215
|
+
if (latInput?.value)
|
|
216
|
+
details["Latitude"] = latInput.value;
|
|
217
|
+
if (lngInput?.value)
|
|
218
|
+
details["Longitude"] = lngInput.value;
|
|
219
|
+
if (placeIdInput?.value)
|
|
220
|
+
details["Place ID"] = placeIdInput.value;
|
|
221
|
+
fields.push({
|
|
222
|
+
label,
|
|
223
|
+
value: value || "\u2014",
|
|
224
|
+
details: Object.keys(details).length > 0 ? details : void 0
|
|
225
|
+
});
|
|
226
|
+
});
|
|
198
227
|
return fields;
|
|
199
228
|
}
|
|
200
229
|
// Builds a small inline colour swatch to display alongside a colour value.
|
|
@@ -213,6 +242,31 @@ class FormLiveSummary extends Controller {
|
|
|
213
242
|
}
|
|
214
243
|
return swatch;
|
|
215
244
|
}
|
|
245
|
+
// Wraps an address value in a span that reveals a geocode detail popup on hover.
|
|
246
|
+
buildGeocode(value, details) {
|
|
247
|
+
const wrapper = document.createElement("span");
|
|
248
|
+
wrapper.className = "midwest-form-live-summary-geocode";
|
|
249
|
+
const text = document.createElement("span");
|
|
250
|
+
text.className = "midwest-form-live-summary-geocode-text";
|
|
251
|
+
text.textContent = value;
|
|
252
|
+
const popup = document.createElement("span");
|
|
253
|
+
popup.className = "midwest-form-live-summary-geocode-popup";
|
|
254
|
+
popup.setAttribute("aria-hidden", "true");
|
|
255
|
+
Object.entries(details).forEach(([key, val]) => {
|
|
256
|
+
const row = document.createElement("span");
|
|
257
|
+
row.className = "midwest-form-live-summary-geocode-row";
|
|
258
|
+
const keyEl = document.createElement("span");
|
|
259
|
+
keyEl.className = "midwest-form-live-summary-geocode-key";
|
|
260
|
+
keyEl.textContent = key;
|
|
261
|
+
const valEl = document.createElement("span");
|
|
262
|
+
valEl.className = "midwest-form-live-summary-geocode-val";
|
|
263
|
+
valEl.textContent = val;
|
|
264
|
+
row.append(keyEl, valEl);
|
|
265
|
+
popup.append(row);
|
|
266
|
+
});
|
|
267
|
+
wrapper.append(text, popup);
|
|
268
|
+
return wrapper;
|
|
269
|
+
}
|
|
216
270
|
// Finds the label associated with a specific element via `for` attribute,
|
|
217
271
|
// wrapping label, or falls back to a prettified version of the element name.
|
|
218
272
|
labelFor(el) {
|