@unabridged/midwest 0.24.3 → 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 +8 -0
- package/app/assets/javascript/midwest/map_providers.ts +61 -0
- package/app/assets/javascript/midwest.js +972 -6
- package/app/assets/javascript/midwest.js.map +1 -1
- package/app/assets/stylesheets/midwest.css +1 -1
- package/app/assets/stylesheets/midwest.tailwind.css +6 -1
- package/dist/css/midwest.css +1 -1
- package/dist/javascript/collection/app/assets/javascript/midwest/index.js +8 -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/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/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/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/midwest.js +972 -6
- package/dist/javascript/midwest.js.map +1 -1
- package/package.json +1 -1
|
@@ -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) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"live_summary_component_controller.js","sources":["../../../../../../../../app/components/midwest/form/live_summary_component/live_summary_component_controller.ts"],"sourcesContent":["import { Controller } from '@hotwired/stimulus'\n\n// Input types that should never appear in a form live summary.\nconst SKIPPED_INPUT_TYPES = new Set([\n 'hidden', 'submit', 'reset', 'button', 'image'\n])\n\ninterface FieldSummary {\n label: string\n value: string\n /** When set, a colour swatch is prepended to the value cell. */\n swatchColor?: string\n}\n\nexport default class FormLiveSummary extends Controller<HTMLElement> {\n static targets = ['list']\n\n declare readonly listTarget: HTMLElement\n\n connect () {\n this.render()\n this.form?.addEventListener('input', this.render)\n this.form?.addEventListener('change', this.render)\n this.form?.addEventListener('midwest-autocomplete:change', this.render)\n this.form?.addEventListener('midwest-color-picker:update', this.render)\n this.form?.addEventListener('midwest-repeatable:change', this.render)\n }\n\n disconnect () {\n this.form?.removeEventListener('input', this.render)\n this.form?.removeEventListener('change', this.render)\n this.form?.removeEventListener('midwest-autocomplete:change', this.render)\n this.form?.removeEventListener('midwest-color-picker:update', this.render)\n this.form?.removeEventListener('midwest-repeatable:change', this.render)\n }\n\n private get form (): HTMLFormElement | null {\n return this.element.closest('form')\n }\n\n private render = () => {\n const fields = this.collectFields()\n\n if (fields.length === 0) {\n this.listTarget.replaceChildren(\n Object.assign(document.createElement('p'), {\n className: 'midwest-form-live-summary-empty',\n textContent: 'No values to display.'\n })\n )\n return\n }\n\n const dl = document.createElement('dl')\n dl.className = 'midwest-form-live-summary-list'\n\n fields.forEach(({ label, value, swatchColor }) => {\n const dt = Object.assign(document.createElement('dt'), {\n className: 'midwest-form-live-summary-term',\n textContent: label\n })\n const dd = document.createElement('dd')\n dd.className = 'midwest-form-live-summary-definition'\n\n if (swatchColor !== undefined) {\n dd.append(this.buildSwatch(swatchColor), value || '—')\n } else {\n dd.textContent = value || '—'\n }\n\n dl.append(dt, dd)\n })\n\n this.listTarget.replaceChildren(dl)\n }\n\n private collectFields (): FieldSummary[] {\n const form = this.form\n if (!form) return []\n\n const fields: FieldSummary[] = []\n const seenRadioNames = new Set<string>()\n\n Array.from(form.elements).forEach(el => {\n const element = el as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLButtonElement\n\n if (!element.name) return\n\n // Skip elements that live inside the summary itself.\n if (this.element.contains(element)) return\n\n // Skip elements inside a hidden ShowIf fieldset — they are disabled\n // and excluded from form submission, so omit them from the summary.\n if (element.closest('fieldset.midwest-show-if[hidden]')) return\n\n // Skip all button elements regardless of how they are declared.\n if (element instanceof HTMLButtonElement) return\n\n // Skip tree-view checkboxes — they are collected separately below.\n if (element.closest('.midwest-tree-view')) return\n\n // Skip repeatable fields — templates, destroyed items, and active items\n // are all collected separately below as grouped summaries.\n if (element.closest('.midwest-repeatable')) return\n\n if (element instanceof HTMLInputElement) {\n if (SKIPPED_INPUT_TYPES.has(element.type)) return\n\n if (element.type === 'radio') {\n if (seenRadioNames.has(element.name)) return\n seenRadioNames.add(element.name)\n\n const checked = form.querySelector<HTMLInputElement>(\n `input[type=\"radio\"][name=\"${CSS.escape(element.name)}\"]:checked`\n )\n if (!checked) return\n\n fields.push({\n label: this.groupLabelFor(element),\n value: this.labelFor(checked) || checked.value\n })\n return\n }\n\n if (element.type === 'checkbox') {\n fields.push({\n label: this.labelFor(element),\n value: element.checked ? 'Yes' : 'No'\n })\n return\n }\n }\n\n if (element instanceof HTMLSelectElement) {\n fields.push({\n label: this.labelFor(element),\n value: Array.from(element.selectedOptions).map(o => o.text).join(', ') || '—'\n })\n return\n }\n\n // Text-like inputs and textareas only reach this point.\n fields.push({\n label: this.labelFor(element),\n value: element.value\n })\n })\n\n // Color picker components store their value in a hidden input tagged with\n // data-midwest-color-picker-target=\"input\". The standard loop skips hidden\n // inputs, so query them separately and render with an inline colour swatch.\n form.querySelectorAll<HTMLInputElement>('[data-midwest-color-picker-target=\"input\"]').forEach(input => {\n if (this.element.contains(input)) return\n if (!input.name) return\n\n // Prefer the label inside the picker's own form-group header; fall back\n // to a label[for] match or a prettified version of the input name.\n const picker = input.closest<HTMLElement>('.midwest-color-picker')\n const labelEl = picker?.querySelector<HTMLElement>('.midwest-form-group__top label') ??\n this.form?.querySelector<HTMLElement>(`label[for=\"${CSS.escape(input.id)}\"]`)\n const label = labelEl?.textContent?.trim() ||\n this.prettifyName(input.name.replace(/\\[.*\\]$/, ''))\n\n const value = input.value\n const display = value.startsWith('#') ? value : (value ? value.charAt(0).toUpperCase() + value.slice(1) : '—')\n\n fields.push({ label, value: display, swatchColor: value || undefined })\n })\n\n // Autocomplete components use a nameless combobox input + hidden inputs —\n // neither surfaces through the standard form.elements loop. Query them\n // separately so their selected badge labels appear in the summary.\n // Note: labelFor(input) doesn't work here because InputComponent assigns\n // its own id to the <input>, which won't match the <label>'s for attribute.\n form.querySelectorAll<HTMLElement>('.midwest-autocomplete').forEach(ac => {\n if (this.element.contains(ac)) return\n if (!ac.querySelector('[role=\"combobox\"]')) return\n\n const labelEl = ac.querySelector<HTMLElement>('label')\n const label = labelEl?.textContent?.trim() ||\n this.prettifyName((ac.dataset.name ?? '').replace(/\\[.*\\]$/, ''))\n\n const selected = Array.from(ac.querySelectorAll<HTMLElement>('.midwest-autocomplete-tokens .value'))\n .map(el => el.textContent?.trim())\n .filter(Boolean)\n .join(', ')\n fields.push({ label, value: selected || '—' })\n })\n\n // Date picker components store their value in a hidden input tagged with\n // data-midwest-date-picker-target=\"input\". The standard loop skips hidden\n // inputs, so query the display badge for the human-readable value.\n form.querySelectorAll<HTMLElement>('.midwest-date-picker').forEach(picker => {\n if (this.element.contains(picker)) return\n\n const input = picker.querySelector<HTMLInputElement>('[data-midwest-date-picker-target=\"input\"]')\n if (!input?.name) return\n\n const labelEl = picker.querySelector<HTMLElement>('.midwest-form-group__top label')\n const label = labelEl?.textContent?.trim() ||\n this.prettifyName(input.name.replace(/\\[.*\\]$/, ''))\n\n const display = picker.querySelector<HTMLElement>('[data-midwest-date-picker-target=\"display\"] .value')\n const value = display?.textContent?.trim() || ''\n\n fields.push({ label, value: value || '—' })\n })\n\n // Tree view components render multiple checkboxes sharing the same name\n // (e.g. \"category_ids[]\"). Aggregate the checked nodes into a single\n // comma-separated summary row instead of listing each checkbox separately.\n const seenTreeNames = new Set<string>()\n form.querySelectorAll<HTMLElement>('.midwest-tree-view').forEach(tree => {\n if (this.element.contains(tree)) return\n\n const checked = Array.from(\n tree.querySelectorAll<HTMLInputElement>('.tree-node-checkbox:checked')\n )\n\n // Derive label from the first checkbox's name attribute.\n const firstName = checked[0]?.name || tree.querySelectorAll<HTMLInputElement>('.tree-node-checkbox')[0]?.name\n if (!firstName) return\n if (seenTreeNames.has(firstName)) return\n seenTreeNames.add(firstName)\n\n const label = this.prettifyName(firstName.replace(/\\[\\]$/, ''))\n const values = checked.map(cb => {\n const nodeLabel = cb.closest('.midwest-tree-node')\n ?.querySelector<HTMLElement>(':scope > .tree-node-trigger > .tree-node-label')\n return nodeLabel?.textContent?.trim() || cb.value\n })\n\n fields.push({ label, value: values.join(', ') || '—' })\n })\n\n // Repeatable components contain nested attribute fields grouped inside\n // item wrappers. Summarise each active item as its own row, skipping\n // the hidden clone template and any items marked for destruction.\n form.querySelectorAll<HTMLElement>('.midwest-repeatable').forEach(repeatable => {\n if (this.element.contains(repeatable)) return\n\n const groupLabel = repeatable.querySelector<HTMLElement>('.midwest-form-group__top .midwest-label')\n ?.textContent?.trim() || 'Items'\n\n const items = Array.from(\n repeatable.querySelectorAll<HTMLElement>('[data-midwest-repeatable-target=\"item\"]')\n ).filter(item => {\n const destroy = item.querySelector<HTMLInputElement>('[data-midwest-repeatable-target=\"destroy\"]')\n return destroy?.value !== '1'\n })\n\n items.forEach((item, i) => {\n const values = Array.from(\n item.querySelectorAll<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>(\n 'input:not([type=\"hidden\"]), textarea, select'\n )\n )\n .map(input => {\n if (input instanceof HTMLSelectElement) {\n return Array.from(input.selectedOptions).map(o => o.text).join(', ')\n }\n return input.value\n })\n .filter(Boolean)\n\n fields.push({\n label: items.length > 1 ? `${groupLabel} ${i + 1}` : groupLabel,\n value: values.join(' · ') || '—'\n })\n })\n })\n\n return fields\n }\n\n // Builds a small inline colour swatch to display alongside a colour value.\n // Hex values are applied as an inline background-color; named theme colours\n // use a scoped theme-{color} class so the CSS variable cascade does the work.\n // An empty/transparent value renders a diagonal-stripe placeholder.\n private buildSwatch (color: string): HTMLElement {\n const swatch = document.createElement('span')\n swatch.className = 'midwest-form-live-summary-swatch'\n\n if (color.startsWith('#')) {\n swatch.style.backgroundColor = color\n } else if (color && color !== 'transparent') {\n swatch.classList.add(`theme-${color}`, 'bg-theme-6')\n } else {\n swatch.classList.add('is-transparent')\n }\n\n return swatch\n }\n\n // Finds the label associated with a specific element via `for` attribute,\n // wrapping label, or falls back to a prettified version of the element name.\n private labelFor (el: Element): string {\n const id = (el as HTMLElement).id\n if (id) {\n const label = this.form?.querySelector(`label[for=\"${CSS.escape(id)}\"]`)\n if (label) return label.textContent?.trim() ?? ''\n }\n const wrapper = el.closest('label')\n if (wrapper) return wrapper.textContent?.trim() ?? ''\n return this.prettifyName((el as HTMLInputElement).name ?? '')\n }\n\n // For radio groups, prefer the containing fieldset legend over the\n // individual radio's label, then fall back to prettifying the name.\n private groupLabelFor (el: Element): string {\n const legend = el.closest('fieldset')?.querySelector('legend')\n if (legend) return legend.textContent?.trim() ?? ''\n return this.prettifyName((el as HTMLInputElement).name ?? '')\n }\n\n // Converts \"user[first_name]\" or \"first_name\" into \"First name\".\n private prettifyName (name: string): string {\n return name\n .replace(/\\[.*?\\]/g, '')\n .replace(/_/g, ' ')\n .replace(/^\\w/, c => c.toUpperCase())\n .trim()\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"live_summary_component_controller.js","sources":["../../../../../../../../app/components/midwest/form/live_summary_component/live_summary_component_controller.ts"],"sourcesContent":["import { Controller } from '@hotwired/stimulus'\n\n// Input types that should never appear in a form live summary.\nconst SKIPPED_INPUT_TYPES = new Set([\n 'hidden', 'submit', 'reset', 'button', 'image'\n])\n\ninterface FieldSummary {\n label: string\n value: string\n /** When set, a colour swatch is prepended to the value cell. */\n swatchColor?: string\n /** When set, hovering the value reveals a geocode detail popup. */\n details?: Record<string, string>\n}\n\nexport default class FormLiveSummary extends Controller<HTMLElement> {\n static targets = ['list']\n\n declare readonly listTarget: HTMLElement\n\n connect () {\n this.render()\n this.form?.addEventListener('input', this.render)\n this.form?.addEventListener('change', this.render)\n this.form?.addEventListener('midwest-autocomplete:change', this.render)\n this.form?.addEventListener('midwest-color-picker:update', this.render)\n this.form?.addEventListener('midwest-repeatable:change', this.render)\n this.form?.addEventListener('midwest-address:geocode', this.render)\n }\n\n disconnect () {\n this.form?.removeEventListener('input', this.render)\n this.form?.removeEventListener('change', this.render)\n this.form?.removeEventListener('midwest-autocomplete:change', this.render)\n this.form?.removeEventListener('midwest-color-picker:update', this.render)\n this.form?.removeEventListener('midwest-repeatable:change', this.render)\n this.form?.removeEventListener('midwest-address:geocode', this.render)\n }\n\n private get form (): HTMLFormElement | null {\n return this.element.closest('form')\n }\n\n private render = () => {\n const fields = this.collectFields()\n\n if (fields.length === 0) {\n this.listTarget.replaceChildren(\n Object.assign(document.createElement('p'), {\n className: 'midwest-form-live-summary-empty',\n textContent: 'No values to display.'\n })\n )\n return\n }\n\n const dl = document.createElement('dl')\n dl.className = 'midwest-form-live-summary-list'\n\n fields.forEach(({ label, value, swatchColor, details }) => {\n const dt = Object.assign(document.createElement('dt'), {\n className: 'midwest-form-live-summary-term',\n textContent: label\n })\n const dd = document.createElement('dd')\n dd.className = 'midwest-form-live-summary-definition'\n\n if (swatchColor !== undefined) {\n dd.append(this.buildSwatch(swatchColor), value || '—')\n } else if (details && Object.keys(details).length > 0) {\n dd.append(this.buildGeocode(value || '—', details))\n } else {\n dd.textContent = value || '—'\n }\n\n dl.append(dt, dd)\n })\n\n this.listTarget.replaceChildren(dl)\n }\n\n private collectFields (): FieldSummary[] {\n const form = this.form\n if (!form) return []\n\n const fields: FieldSummary[] = []\n const seenRadioNames = new Set<string>()\n\n Array.from(form.elements).forEach(el => {\n const element = el as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLButtonElement\n\n if (!element.name) return\n\n // Skip elements that live inside the summary itself.\n if (this.element.contains(element)) return\n\n // Skip elements inside a hidden ShowIf fieldset — they are disabled\n // and excluded from form submission, so omit them from the summary.\n if (element.closest('fieldset.midwest-show-if[hidden]')) return\n\n // Skip all button elements regardless of how they are declared.\n if (element instanceof HTMLButtonElement) return\n\n // Skip tree-view checkboxes — they are collected separately below.\n if (element.closest('.midwest-tree-view')) return\n\n // Skip repeatable fields — templates, destroyed items, and active items\n // are all collected separately below as grouped summaries.\n if (element.closest('.midwest-repeatable')) return\n\n // Skip address fields — the combobox input and geocode hidden inputs are\n // all collected separately below as a single row with a geocode detail popup.\n if (element.closest('.midwest-address')) return\n\n if (element instanceof HTMLInputElement) {\n if (SKIPPED_INPUT_TYPES.has(element.type)) return\n\n if (element.type === 'radio') {\n if (seenRadioNames.has(element.name)) return\n seenRadioNames.add(element.name)\n\n const checked = form.querySelector<HTMLInputElement>(\n `input[type=\"radio\"][name=\"${CSS.escape(element.name)}\"]:checked`\n )\n if (!checked) return\n\n fields.push({\n label: this.groupLabelFor(element),\n value: this.labelFor(checked) || checked.value\n })\n return\n }\n\n if (element.type === 'checkbox') {\n fields.push({\n label: this.labelFor(element),\n value: element.checked ? 'Yes' : 'No'\n })\n return\n }\n }\n\n if (element instanceof HTMLSelectElement) {\n fields.push({\n label: this.labelFor(element),\n value: Array.from(element.selectedOptions).map(o => o.text).join(', ') || '—'\n })\n return\n }\n\n // Text-like inputs and textareas only reach this point.\n fields.push({\n label: this.labelFor(element),\n value: element.value\n })\n })\n\n // Color picker components store their value in a hidden input tagged with\n // data-midwest-color-picker-target=\"input\". The standard loop skips hidden\n // inputs, so query them separately and render with an inline colour swatch.\n form.querySelectorAll<HTMLInputElement>('[data-midwest-color-picker-target=\"input\"]').forEach(input => {\n if (this.element.contains(input)) return\n if (!input.name) return\n\n // Prefer the label inside the picker's own form-group header; fall back\n // to a label[for] match or a prettified version of the input name.\n const picker = input.closest<HTMLElement>('.midwest-color-picker')\n const labelEl = picker?.querySelector<HTMLElement>('.midwest-form-group__top label') ??\n this.form?.querySelector<HTMLElement>(`label[for=\"${CSS.escape(input.id)}\"]`)\n const label = labelEl?.textContent?.trim() ||\n this.prettifyName(input.name.replace(/\\[.*\\]$/, ''))\n\n const value = input.value\n const display = value.startsWith('#') ? value : (value ? value.charAt(0).toUpperCase() + value.slice(1) : '—')\n\n fields.push({ label, value: display, swatchColor: value || undefined })\n })\n\n // Autocomplete components use a nameless combobox input + hidden inputs —\n // neither surfaces through the standard form.elements loop. Query them\n // separately so their selected badge labels appear in the summary.\n // Note: labelFor(input) doesn't work here because InputComponent assigns\n // its own id to the <input>, which won't match the <label>'s for attribute.\n form.querySelectorAll<HTMLElement>('.midwest-autocomplete').forEach(ac => {\n if (this.element.contains(ac)) return\n if (!ac.querySelector('[role=\"combobox\"]')) return\n\n const labelEl = ac.querySelector<HTMLElement>('label')\n const label = labelEl?.textContent?.trim() ||\n this.prettifyName((ac.dataset.name ?? '').replace(/\\[.*\\]$/, ''))\n\n const selected = Array.from(ac.querySelectorAll<HTMLElement>('.midwest-autocomplete-tokens .value'))\n .map(el => el.textContent?.trim())\n .filter(Boolean)\n .join(', ')\n fields.push({ label, value: selected || '—' })\n })\n\n // Date picker components store their value in a hidden input tagged with\n // data-midwest-date-picker-target=\"input\". The standard loop skips hidden\n // inputs, so query the display badge for the human-readable value.\n form.querySelectorAll<HTMLElement>('.midwest-date-picker').forEach(picker => {\n if (this.element.contains(picker)) return\n\n const input = picker.querySelector<HTMLInputElement>('[data-midwest-date-picker-target=\"input\"]')\n if (!input?.name) return\n\n const labelEl = picker.querySelector<HTMLElement>('.midwest-form-group__top label')\n const label = labelEl?.textContent?.trim() ||\n this.prettifyName(input.name.replace(/\\[.*\\]$/, ''))\n\n const display = picker.querySelector<HTMLElement>('[data-midwest-date-picker-target=\"display\"] .value')\n const value = display?.textContent?.trim() || ''\n\n fields.push({ label, value: value || '—' })\n })\n\n // Tree view components render multiple checkboxes sharing the same name\n // (e.g. \"category_ids[]\"). Aggregate the checked nodes into a single\n // comma-separated summary row instead of listing each checkbox separately.\n const seenTreeNames = new Set<string>()\n form.querySelectorAll<HTMLElement>('.midwest-tree-view').forEach(tree => {\n if (this.element.contains(tree)) return\n\n const checked = Array.from(\n tree.querySelectorAll<HTMLInputElement>('.tree-node-checkbox:checked')\n )\n\n // Derive label from the first checkbox's name attribute.\n const firstName = checked[0]?.name || tree.querySelectorAll<HTMLInputElement>('.tree-node-checkbox')[0]?.name\n if (!firstName) return\n if (seenTreeNames.has(firstName)) return\n seenTreeNames.add(firstName)\n\n const label = this.prettifyName(firstName.replace(/\\[\\]$/, ''))\n const values = checked.map(cb => {\n const nodeLabel = cb.closest('.midwest-tree-node')\n ?.querySelector<HTMLElement>(':scope > .tree-node-trigger > .tree-node-label')\n return nodeLabel?.textContent?.trim() || cb.value\n })\n\n fields.push({ label, value: values.join(', ') || '—' })\n })\n\n // Repeatable components contain nested attribute fields grouped inside\n // item wrappers. Summarise each active item as its own row, skipping\n // the hidden clone template and any items marked for destruction.\n form.querySelectorAll<HTMLElement>('.midwest-repeatable').forEach(repeatable => {\n if (this.element.contains(repeatable)) return\n\n const groupLabel = repeatable.querySelector<HTMLElement>('.midwest-form-group__top .midwest-label')\n ?.textContent?.trim() || 'Items'\n\n const items = Array.from(\n repeatable.querySelectorAll<HTMLElement>('[data-midwest-repeatable-target=\"item\"]')\n ).filter(item => {\n const destroy = item.querySelector<HTMLInputElement>('[data-midwest-repeatable-target=\"destroy\"]')\n return destroy?.value !== '1'\n })\n\n items.forEach((item, i) => {\n const values = Array.from(\n item.querySelectorAll<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>(\n 'input:not([type=\"hidden\"]), textarea, select'\n )\n )\n .map(input => {\n if (input instanceof HTMLSelectElement) {\n return Array.from(input.selectedOptions).map(o => o.text).join(', ')\n }\n return input.value\n })\n .filter(Boolean)\n\n fields.push({\n label: items.length > 1 ? `${groupLabel} ${i + 1}` : groupLabel,\n value: values.join(' · ') || '—'\n })\n })\n })\n\n // Address components display the typed/selected address as plain text. When\n // a place has been geocoded, hovering the value reveals lat, lng, and place\n // ID in a compact popup.\n form.querySelectorAll<HTMLElement>('.midwest-address').forEach(addr => {\n if (this.element.contains(addr)) return\n\n const input = addr.querySelector<HTMLInputElement>('[data-midwest-address-target=\"input\"]')\n const latInput = addr.querySelector<HTMLInputElement>('[data-midwest-address-target=\"latInput\"]')\n const lngInput = addr.querySelector<HTMLInputElement>('[data-midwest-address-target=\"lngInput\"]')\n const placeIdInput = addr.querySelector<HTMLInputElement>('[data-midwest-address-target=\"placeIdInput\"]')\n\n const labelEl = addr.querySelector<HTMLElement>('.midwest-form-group__top label')\n const label = labelEl?.textContent?.trim() ||\n this.prettifyName((input?.name ?? latInput?.name?.replace(/_latitude$/, '') ?? '').replace(/\\[.*\\]$/, ''))\n\n const value = input?.value?.trim() || ''\n\n const details: Record<string, string> = {}\n if (latInput?.value) details['Latitude'] = latInput.value\n if (lngInput?.value) details['Longitude'] = lngInput.value\n if (placeIdInput?.value) details['Place ID'] = placeIdInput.value\n\n fields.push({\n label,\n value: value || '—',\n details: Object.keys(details).length > 0 ? details : undefined\n })\n })\n\n return fields\n }\n\n // Builds a small inline colour swatch to display alongside a colour value.\n // Hex values are applied as an inline background-color; named theme colours\n // use a scoped theme-{color} class so the CSS variable cascade does the work.\n // An empty/transparent value renders a diagonal-stripe placeholder.\n private buildSwatch (color: string): HTMLElement {\n const swatch = document.createElement('span')\n swatch.className = 'midwest-form-live-summary-swatch'\n\n if (color.startsWith('#')) {\n swatch.style.backgroundColor = color\n } else if (color && color !== 'transparent') {\n swatch.classList.add(`theme-${color}`, 'bg-theme-6')\n } else {\n swatch.classList.add('is-transparent')\n }\n\n return swatch\n }\n\n // Wraps an address value in a span that reveals a geocode detail popup on hover.\n private buildGeocode (value: string, details: Record<string, string>): HTMLElement {\n const wrapper = document.createElement('span')\n wrapper.className = 'midwest-form-live-summary-geocode'\n\n const text = document.createElement('span')\n text.className = 'midwest-form-live-summary-geocode-text'\n text.textContent = value\n\n const popup = document.createElement('span')\n popup.className = 'midwest-form-live-summary-geocode-popup'\n popup.setAttribute('aria-hidden', 'true')\n\n Object.entries(details).forEach(([key, val]) => {\n const row = document.createElement('span')\n row.className = 'midwest-form-live-summary-geocode-row'\n\n const keyEl = document.createElement('span')\n keyEl.className = 'midwest-form-live-summary-geocode-key'\n keyEl.textContent = key\n\n const valEl = document.createElement('span')\n valEl.className = 'midwest-form-live-summary-geocode-val'\n valEl.textContent = val\n\n row.append(keyEl, valEl)\n popup.append(row)\n })\n\n wrapper.append(text, popup)\n return wrapper\n }\n\n // Finds the label associated with a specific element via `for` attribute,\n // wrapping label, or falls back to a prettified version of the element name.\n private labelFor (el: Element): string {\n const id = (el as HTMLElement).id\n if (id) {\n const label = this.form?.querySelector(`label[for=\"${CSS.escape(id)}\"]`)\n if (label) return label.textContent?.trim() ?? ''\n }\n const wrapper = el.closest('label')\n if (wrapper) return wrapper.textContent?.trim() ?? ''\n return this.prettifyName((el as HTMLInputElement).name ?? '')\n }\n\n // For radio groups, prefer the containing fieldset legend over the\n // individual radio's label, then fall back to prettifying the name.\n private groupLabelFor (el: Element): string {\n const legend = el.closest('fieldset')?.querySelector('legend')\n if (legend) return legend.textContent?.trim() ?? ''\n return this.prettifyName((el as HTMLInputElement).name ?? '')\n }\n\n // Converts \"user[first_name]\" or \"first_name\" into \"First name\".\n private prettifyName (name: string): string {\n return name\n .replace(/\\[.*?\\]/g, '')\n .replace(/_/g, ' ')\n .replace(/^\\w/, c => c.toUpperCase())\n .trim()\n }\n}\n"],"names":[],"mappings":"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"}
|
package/dist/javascript/collection/app/components/midwest/map_component/map_component_controller.js
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { Controller } from '@hotwired/stimulus';
|
|
2
|
+
import { loadMapKitScript, loadGoogleMapsScript, loadLeafletScript } from '../../../assets/javascript/midwest/map_providers.js';
|
|
3
|
+
|
|
4
|
+
const LEAFLET_TILES = {
|
|
5
|
+
MutedStandard: {
|
|
6
|
+
url: "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png",
|
|
7
|
+
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>'
|
|
8
|
+
},
|
|
9
|
+
Standard: {
|
|
10
|
+
url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
|
11
|
+
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
|
12
|
+
maxZoom: 19
|
|
13
|
+
},
|
|
14
|
+
Hybrid: {
|
|
15
|
+
url: "https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png",
|
|
16
|
+
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>'
|
|
17
|
+
},
|
|
18
|
+
Satellite: {
|
|
19
|
+
url: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
|
|
20
|
+
attribution: "Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community",
|
|
21
|
+
maxZoom: 18
|
|
22
|
+
},
|
|
23
|
+
Dark: {
|
|
24
|
+
url: "https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png",
|
|
25
|
+
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>'
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const GOOGLE_MUTED_STYLES = [
|
|
29
|
+
{ elementType: "geometry", stylers: [{ color: "#f5f5f5" }] },
|
|
30
|
+
{ elementType: "labels.icon", stylers: [{ visibility: "off" }] },
|
|
31
|
+
{ elementType: "labels.text.fill", stylers: [{ color: "#616161" }] },
|
|
32
|
+
{ elementType: "labels.text.stroke", stylers: [{ color: "#f5f5f5" }] },
|
|
33
|
+
{ featureType: "administrative.land_parcel", elementType: "labels", stylers: [{ visibility: "off" }] },
|
|
34
|
+
{ featureType: "poi", elementType: "geometry", stylers: [{ color: "#eeeeee" }] },
|
|
35
|
+
{ featureType: "poi.park", elementType: "geometry", stylers: [{ color: "#e5e5e5" }] },
|
|
36
|
+
{ featureType: "road", elementType: "geometry", stylers: [{ color: "#ffffff" }] },
|
|
37
|
+
{ featureType: "road.highway", elementType: "geometry", stylers: [{ color: "#dadada" }] },
|
|
38
|
+
{ featureType: "water", elementType: "geometry", stylers: [{ color: "#c9c9c9" }] }
|
|
39
|
+
];
|
|
40
|
+
const GOOGLE_DARK_STYLES = [
|
|
41
|
+
{ elementType: "geometry", stylers: [{ color: "#242f3e" }] },
|
|
42
|
+
{ elementType: "labels.text.stroke", stylers: [{ color: "#242f3e" }] },
|
|
43
|
+
{ elementType: "labels.text.fill", stylers: [{ color: "#746855" }] },
|
|
44
|
+
{ featureType: "administrative.locality", elementType: "labels.text.fill", stylers: [{ color: "#d59563" }] },
|
|
45
|
+
{ featureType: "poi", elementType: "labels.text.fill", stylers: [{ color: "#d59563" }] },
|
|
46
|
+
{ featureType: "poi.park", elementType: "geometry", stylers: [{ color: "#263c3f" }] },
|
|
47
|
+
{ featureType: "poi.park", elementType: "labels.text.fill", stylers: [{ color: "#6b9a76" }] },
|
|
48
|
+
{ featureType: "road", elementType: "geometry", stylers: [{ color: "#38414e" }] },
|
|
49
|
+
{ featureType: "road", elementType: "geometry.stroke", stylers: [{ color: "#212a37" }] },
|
|
50
|
+
{ featureType: "road", elementType: "labels.text.fill", stylers: [{ color: "#9ca5b3" }] },
|
|
51
|
+
{ featureType: "road.highway", elementType: "geometry", stylers: [{ color: "#746855" }] },
|
|
52
|
+
{ featureType: "road.highway", elementType: "geometry.stroke", stylers: [{ color: "#1f2835" }] },
|
|
53
|
+
{ featureType: "road.highway", elementType: "labels.text.fill", stylers: [{ color: "#f3d19c" }] },
|
|
54
|
+
{ featureType: "transit", elementType: "geometry", stylers: [{ color: "#2f3948" }] },
|
|
55
|
+
{ featureType: "transit.station", elementType: "labels.text.fill", stylers: [{ color: "#d59563" }] },
|
|
56
|
+
{ featureType: "water", elementType: "geometry", stylers: [{ color: "#17263c" }] },
|
|
57
|
+
{ featureType: "water", elementType: "labels.text.fill", stylers: [{ color: "#515c6d" }] },
|
|
58
|
+
{ featureType: "water", elementType: "labels.text.stroke", stylers: [{ color: "#17263c" }] }
|
|
59
|
+
];
|
|
60
|
+
class Map extends Controller {
|
|
61
|
+
static values = {
|
|
62
|
+
latitude: { type: Number, default: 37.334886 },
|
|
63
|
+
longitude: { type: Number, default: -122.008988 },
|
|
64
|
+
span: { type: Number, default: 0.1 },
|
|
65
|
+
mapType: { type: String, default: "MutedStandard" },
|
|
66
|
+
colorScheme: { type: String, default: "Auto" },
|
|
67
|
+
provider: { type: String, default: "" },
|
|
68
|
+
token: { type: String, default: "" },
|
|
69
|
+
tokenUrl: { type: String, default: "" },
|
|
70
|
+
apiKey: { type: String, default: "" },
|
|
71
|
+
zoom: { type: Number, default: -1 },
|
|
72
|
+
markers: { type: Array, default: [] },
|
|
73
|
+
showsUserLocation: { type: Boolean, default: false },
|
|
74
|
+
showsMapTypeControl: { type: Boolean, default: false },
|
|
75
|
+
showsZoomControl: { type: Boolean, default: true },
|
|
76
|
+
showsCompass: { type: String, default: "Adaptive" },
|
|
77
|
+
rotation: { type: Number, default: 0 }
|
|
78
|
+
};
|
|
79
|
+
mapInstance = null;
|
|
80
|
+
async connect() {
|
|
81
|
+
const provider = this.providerValue;
|
|
82
|
+
if (!provider)
|
|
83
|
+
return;
|
|
84
|
+
try {
|
|
85
|
+
if (provider === "mapkit") {
|
|
86
|
+
await loadMapKitScript();
|
|
87
|
+
this.initMapKit();
|
|
88
|
+
this.initMapKitMap();
|
|
89
|
+
} else if (provider === "google") {
|
|
90
|
+
await loadGoogleMapsScript(this.apiKeyValue);
|
|
91
|
+
this.initGoogleMap();
|
|
92
|
+
} else if (provider === "leaflet") {
|
|
93
|
+
await loadLeafletScript();
|
|
94
|
+
this.initLeafletMap();
|
|
95
|
+
}
|
|
96
|
+
} catch (err) {
|
|
97
|
+
console.error(`[midwest-map] Failed to initialise ${provider} map:`, err);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
disconnect() {
|
|
101
|
+
if (this.mapInstance) {
|
|
102
|
+
if (this.providerValue === "mapkit")
|
|
103
|
+
this.mapInstance.destroy();
|
|
104
|
+
if (this.providerValue === "leaflet")
|
|
105
|
+
this.mapInstance.remove();
|
|
106
|
+
this.mapInstance = null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
tokenValueChanged() {
|
|
110
|
+
if (this.mapInstance && this.providerValue === "mapkit")
|
|
111
|
+
this.initMapKit();
|
|
112
|
+
}
|
|
113
|
+
// --- MapKit ---
|
|
114
|
+
initMapKit() {
|
|
115
|
+
const mk = window.mapkit;
|
|
116
|
+
const token = this.tokenValue;
|
|
117
|
+
const tokenUrl = this.tokenUrlValue;
|
|
118
|
+
mk.init({
|
|
119
|
+
authorizationCallback: (done) => {
|
|
120
|
+
if (token) {
|
|
121
|
+
done(token);
|
|
122
|
+
} else if (tokenUrl) {
|
|
123
|
+
fetch(tokenUrl).then((r) => r.text()).then(done).catch((err) => console.error("[midwest-map] Token fetch failed:", err));
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
language: document.documentElement.lang || "en"
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
initMapKitMap() {
|
|
130
|
+
const mk = window.mapkit;
|
|
131
|
+
const region = new mk.CoordinateRegion(
|
|
132
|
+
new mk.Coordinate(this.latitudeValue, this.longitudeValue),
|
|
133
|
+
new mk.CoordinateSpan(this.spanValue, this.spanValue)
|
|
134
|
+
);
|
|
135
|
+
const colorScheme = this.colorSchemeValue === "Auto" ? window.matchMedia("(prefers-color-scheme: dark)").matches ? mk.Map.ColorSchemes.Dark : mk.Map.ColorSchemes.Light : mk.Map.ColorSchemes[this.colorSchemeValue];
|
|
136
|
+
this.mapInstance = new mk.Map(this.element, {
|
|
137
|
+
region,
|
|
138
|
+
mapType: mk.Map.MapTypes[this.mapTypeValue],
|
|
139
|
+
colorScheme,
|
|
140
|
+
showsUserLocation: this.showsUserLocationValue,
|
|
141
|
+
showsMapTypeControl: this.showsMapTypeControlValue,
|
|
142
|
+
showsZoomControl: this.showsZoomControlValue,
|
|
143
|
+
showsCompass: mk.FeatureVisibility[this.showsCompassValue],
|
|
144
|
+
rotation: this.rotationValue
|
|
145
|
+
});
|
|
146
|
+
this.addMapKitMarkers();
|
|
147
|
+
this.element.dataset.mapLoaded = "true";
|
|
148
|
+
}
|
|
149
|
+
addMapKitMarkers() {
|
|
150
|
+
const mk = window.mapkit;
|
|
151
|
+
if (!this.markersValue.length)
|
|
152
|
+
return;
|
|
153
|
+
const annotations = this.markersValue.map((m) => {
|
|
154
|
+
const opts = {};
|
|
155
|
+
if (m.title)
|
|
156
|
+
opts.title = m.title;
|
|
157
|
+
if (m.subtitle)
|
|
158
|
+
opts.subtitle = m.subtitle;
|
|
159
|
+
if (m.color)
|
|
160
|
+
opts.color = m.color;
|
|
161
|
+
if (m.glyphText)
|
|
162
|
+
opts.glyphText = m.glyphText;
|
|
163
|
+
return new mk.MarkerAnnotation(
|
|
164
|
+
new mk.Coordinate(m.latitude, m.longitude),
|
|
165
|
+
opts
|
|
166
|
+
);
|
|
167
|
+
});
|
|
168
|
+
this.mapInstance.addAnnotations(annotations);
|
|
169
|
+
const openIndex = this.markersValue.findIndex((m) => m.open);
|
|
170
|
+
if (openIndex >= 0) {
|
|
171
|
+
this.mapInstance.selectedAnnotation = annotations[openIndex];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// --- Google Maps ---
|
|
175
|
+
initGoogleMap() {
|
|
176
|
+
const g = window.google;
|
|
177
|
+
const zoom = this.zoomValue > 0 ? this.zoomValue : this.spanToZoom(this.spanValue);
|
|
178
|
+
const isDark = this.colorSchemeValue === "Dark" || this.colorSchemeValue === "Auto" && window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
179
|
+
const mapTypeId = this.googleMapTypeId();
|
|
180
|
+
const opts = {
|
|
181
|
+
center: { lat: this.latitudeValue, lng: this.longitudeValue },
|
|
182
|
+
zoom,
|
|
183
|
+
mapTypeId,
|
|
184
|
+
disableDefaultUI: true,
|
|
185
|
+
zoomControl: this.showsZoomControlValue,
|
|
186
|
+
mapTypeControl: this.showsMapTypeControlValue,
|
|
187
|
+
fullscreenControl: false,
|
|
188
|
+
streetViewControl: false
|
|
189
|
+
};
|
|
190
|
+
const styles = this.googleMapStyles(isDark, mapTypeId);
|
|
191
|
+
if (styles)
|
|
192
|
+
opts.styles = styles;
|
|
193
|
+
this.mapInstance = new g.maps.Map(this.element, opts);
|
|
194
|
+
this.addGoogleMarkers();
|
|
195
|
+
this.element.dataset.mapLoaded = "true";
|
|
196
|
+
}
|
|
197
|
+
googleMapTypeId() {
|
|
198
|
+
const types = {
|
|
199
|
+
Standard: "roadmap",
|
|
200
|
+
Hybrid: "hybrid",
|
|
201
|
+
Satellite: "satellite",
|
|
202
|
+
MutedStandard: "roadmap"
|
|
203
|
+
};
|
|
204
|
+
return types[this.mapTypeValue] ?? "roadmap";
|
|
205
|
+
}
|
|
206
|
+
googleMapStyles(isDark, mapTypeId) {
|
|
207
|
+
if (mapTypeId !== "roadmap")
|
|
208
|
+
return null;
|
|
209
|
+
if (isDark)
|
|
210
|
+
return GOOGLE_DARK_STYLES;
|
|
211
|
+
if (this.mapTypeValue === "MutedStandard")
|
|
212
|
+
return GOOGLE_MUTED_STYLES;
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
addGoogleMarkers() {
|
|
216
|
+
const g = window.google;
|
|
217
|
+
if (!this.markersValue.length)
|
|
218
|
+
return;
|
|
219
|
+
this.markersValue.forEach((m) => {
|
|
220
|
+
const marker = new g.maps.Marker({
|
|
221
|
+
map: this.mapInstance,
|
|
222
|
+
position: { lat: m.latitude, lng: m.longitude },
|
|
223
|
+
title: m.title ?? ""
|
|
224
|
+
});
|
|
225
|
+
if (m.open && m.title) {
|
|
226
|
+
const content = m.subtitle ? `<strong>${m.title}</strong><br><span style="color:#777">${m.subtitle}</span>` : `<strong>${m.title}</strong>`;
|
|
227
|
+
const infoWindow = new g.maps.InfoWindow({ content });
|
|
228
|
+
infoWindow.open(this.mapInstance, marker);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
// --- Leaflet ---
|
|
233
|
+
initLeafletMap() {
|
|
234
|
+
const L = window.L;
|
|
235
|
+
const zoom = this.zoomValue > 0 ? this.zoomValue : this.spanToZoom(this.spanValue);
|
|
236
|
+
const isDark = this.colorSchemeValue === "Dark" || this.colorSchemeValue === "Auto" && window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
237
|
+
const tileKey = isDark ? "Dark" : this.mapTypeValue;
|
|
238
|
+
const tile = LEAFLET_TILES[tileKey] ?? LEAFLET_TILES.MutedStandard;
|
|
239
|
+
this.mapInstance = L.map(this.element, {
|
|
240
|
+
center: [this.latitudeValue, this.longitudeValue],
|
|
241
|
+
zoom,
|
|
242
|
+
zoomControl: this.showsZoomControlValue,
|
|
243
|
+
attributionControl: true
|
|
244
|
+
});
|
|
245
|
+
L.tileLayer(tile.url, {
|
|
246
|
+
attribution: tile.attribution,
|
|
247
|
+
maxZoom: tile.maxZoom ?? 19
|
|
248
|
+
}).addTo(this.mapInstance);
|
|
249
|
+
this.addLeafletMarkers();
|
|
250
|
+
this.element.dataset.mapLoaded = "true";
|
|
251
|
+
}
|
|
252
|
+
addLeafletMarkers() {
|
|
253
|
+
const L = window.L;
|
|
254
|
+
if (!this.markersValue.length)
|
|
255
|
+
return;
|
|
256
|
+
this.markersValue.forEach((m) => {
|
|
257
|
+
const icon = this.leafletMarkerIcon(m.color, m.glyphText);
|
|
258
|
+
const marker = L.marker([m.latitude, m.longitude], { icon }).addTo(this.mapInstance);
|
|
259
|
+
if (m.title) {
|
|
260
|
+
const content = m.subtitle ? `<strong>${m.title}</strong><br><span style="color:#777">${m.subtitle}</span>` : `<strong>${m.title}</strong>`;
|
|
261
|
+
marker.bindPopup(content);
|
|
262
|
+
if (m.open)
|
|
263
|
+
marker.openPopup();
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
// Returns a Leaflet icon — default blue when no color/glyph, otherwise a
|
|
268
|
+
// custom SVG pin rendered via divIcon.
|
|
269
|
+
leafletMarkerIcon(color, glyphText) {
|
|
270
|
+
const L = window.L;
|
|
271
|
+
if (!color && !glyphText)
|
|
272
|
+
return new L.Icon.Default();
|
|
273
|
+
const fill = color ?? "#2563eb";
|
|
274
|
+
const inner = glyphText ? `<text x="12" y="16" text-anchor="middle" font-size="9" font-family="system-ui,sans-serif" fill="white">${glyphText}</text>` : `<circle cx="12" cy="12" r="4" fill="rgba(255,255,255,0.9)"/>`;
|
|
275
|
+
const svg = [
|
|
276
|
+
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 36" width="24" height="36">',
|
|
277
|
+
` <path d="M12 0C5.373 0 0 5.373 0 12c0 9 12 24 12 24s12-15 12-24C24 5.373 18.627 0 12 0z" fill="${fill}"/>`,
|
|
278
|
+
` ${inner}`,
|
|
279
|
+
"</svg>"
|
|
280
|
+
].join("");
|
|
281
|
+
return L.divIcon({
|
|
282
|
+
html: svg,
|
|
283
|
+
className: "",
|
|
284
|
+
iconSize: [24, 36],
|
|
285
|
+
iconAnchor: [12, 36],
|
|
286
|
+
popupAnchor: [0, -36]
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
// Approximate MapKit coordinate span to a Web Mercator zoom level.
|
|
290
|
+
// CoordinateSpan 360° = zoom 0; halving span adds 1 zoom level.
|
|
291
|
+
spanToZoom(span) {
|
|
292
|
+
return Math.round(Math.log2(360 / span));
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export { Map as default };
|
|
297
|
+
//# sourceMappingURL=map_component_controller.js.map
|