From 8805ef01b5b1f8566861c6f95d5bc9072491939d Mon Sep 17 00:00:00 2001 From: jlamanskygitt Date: Mon, 8 Jun 2026 10:48:05 -0500 Subject: [PATCH 1/3] changed to event-based geocoding for DT location map item --- .../dt-location-map/dt-location-map-item.js | 212 ++++++++---------- 1 file changed, 92 insertions(+), 120 deletions(-) diff --git a/src/components/form/dt-location-map/dt-location-map-item.js b/src/components/form/dt-location-map/dt-location-map-item.js index 6cb44ba..dc4cd78 100644 --- a/src/components/form/dt-location-map/dt-location-map-item.js +++ b/src/components/form/dt-location-map/dt-location-map-item.js @@ -3,8 +3,6 @@ import { styleMap } from 'lit/directives/style-map.js'; import { classMap } from 'lit/directives/class-map.js'; import { msg } from '@lit/localize'; import DtBase from '../../dt-base.js'; -import MapboxService from '../../../services/mapboxService.js'; -import GoogleGeocodeService from '../../../services/googleGeocodeService.js'; import '../../icons/dt-icon.js'; import './dt-map-modal.js'; @@ -318,21 +316,6 @@ export default class DtLocationMapItem extends DtBase { input.focus(); } }); - - if (this.mapboxToken) { - this.mapboxService = new MapboxService(this.mapboxToken); - } - } - - firstUpdated() { - // Only load Google Maps API for new/empty location items - if (this.googleToken && !this.metadata?.lat) { - this.googleGeocodeService = new GoogleGeocodeService( - this.googleToken, - window, - document, - ); - } } disconnectedCallback() { @@ -428,28 +411,41 @@ export default class DtLocationMapItem extends DtBase { } async _select(metadata) { - if (metadata.place_id && this.googleGeocodeService) { - // Google Places autocomplete will give a place_id instead of geometry details, - // so we need to get those details by geocoding the full address from Place lookup - this.saved = false; - this.loading = true; - const place = await this.googleGeocodeService.getPlaceDetails( - metadata, - this.locale, - ); - this.loading = false; - if (place) { - if (place.error) { - console.error(place.error); - this.error = place.error.message; - return; - } - metadata.lat = place.lat; - metadata.lng = place.lng; - metadata.level = place.level; - } - } + this.saved = false; + this.loading = true; + this.dispatchEvent( + new CustomEvent('dt:geocode', { + bubbles: true, + composed: true, + detail: { + type: 'details', + suggestion: metadata, + locale: this.locale, + onSuccess: (place) => { + this.loading = false; + + const updatedMetadata = { ...metadata }; + + if (place) { + updatedMetadata.lat = place.lat ?? metadata.lat; + updatedMetadata.lng = place.lng ?? metadata.lng; + updatedMetadata.level = place.level ?? metadata.level; + } + + this._finalizeSelect(updatedMetadata); + }, + onError: (error) => { + this.loading = false; + console.error(error); + this.error = error?.message || 'Error fetching location details'; + this._finalizeSelect(metadata); + }, + }, + }) + ); + } + _finalizeSelect(metadata) { // Create custom event with new/old values to pass to onchange function const options = { detail: { @@ -541,58 +537,37 @@ export default class DtLocationMapItem extends DtBase { } /** - * Filter to options that: - * 1: are not selected - * 2: match the search query + * Filter to options that match the search query via injected geocoding * @private */ async _filterOptions() { if (this.query) { - if (this.googleToken && this.googleGeocodeService) { - this.saved = false; - this.loading = true; - - try { - const predictions = - await this.googleGeocodeService.getPlacePredictions( - this.query, - this.locale, - ); - - this.filteredOptions = (predictions || []).map(i => ({ - label: i.description, - place_id: i.place_id, - source: 'user', - raw: i, - })); - - this.loading = false; - } catch (ex) { - console.error(ex); - this.error = - ex.message || 'An error occurred while searching for locations.'; - this.loading = false; - return; - } - } else if (this.mapboxToken && this.mapboxService) { - this.saved = false; - this.loading = true; - - const results = await this.mapboxService.searchPlaces( - this.query, - this.locale, - ); - - this.filteredOptions = results.map(i => ({ - lng: i.center[0], - lat: i.center[1], - level: i.place_type[0], - label: i.place_name, - source: 'user', - })); - - this.loading = false; - } + this.saved = false; + this.loading = true; + + this.dispatchEvent( + new CustomEvent('dt:geocode', { + bubbles: true, + composed: true, + detail: { + type: 'search', + query: this.query, + locale: this.locale, + onSuccess: (results) => { + this.filteredOptions = results || []; + this.loading = false; + }, + onError: (error) => { + console.error(error); + this.error = + error?.message || 'An error occurred while searching for locations.'; + this.loading = false; + }, + }, + }), + ); + } else { + this.filteredOptions = []; } return this.filteredOptions; } @@ -646,40 +621,37 @@ export default class DtLocationMapItem extends DtBase { const { location } = e?.detail; const { lat, lng } = location; - // reverse geocode location and save - if (this.googleGeocodeService) { - const results = await this.googleGeocodeService.reverseGeocode( - lng, - lat, - this.locale, - ); - if (results && results.length) { - const place = results[0]; - this._select({ - lng: place.geometry.location.lng, - lat: place.geometry.location.lat, - level: place.types && place.types.length ? place.types[0] : null, - label: place.formatted_address, - source: 'user', - }); - } - } else if (this.mapboxService) { - const results = await this.mapboxService.reverseGeocode( - lng, - lat, - this.locale, - ); - if (results && results.length) { - const place = results[0]; - this._select({ - lng: place.center[0], - lat: place.center[1], - level: place.place_type[0], - label: place.place_name, - source: 'user', - }); - } - } + this.saved = false; + this.loading = true; + + this.dispatchEvent( + new CustomEvent('dt:geocode', { + bubbles: true, + composed: true, + detail: { + type: 'reverse', + lat, + lng, + locale: this.locale, + onSuccess: (place) => { + this.loading = false; + if (place) { + this._select({ + lng: place.lng, + lat: place.lat, + level: place.level, + label: place.label, + source: place.source || 'user', + }); + } + }, + onError: (error) => { + this.loading = false; + console.error(error); + }, + }, + }), + ); } } From 803a7d6566ca0c5eed73d23aef5cffaeec5f1e43 Mon Sep 17 00:00:00 2001 From: jlamanskygitt Date: Thu, 11 Jun 2026 11:02:34 -0500 Subject: [PATCH 2/3] Updated component service to receive geocode event --- src/services/componentService.js | 110 +++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/src/services/componentService.js b/src/services/componentService.js index 9e5b97b..e1a5a51 100644 --- a/src/services/componentService.js +++ b/src/services/componentService.js @@ -1,5 +1,7 @@ /* eslint-disable no-unused-vars */ import ApiService from './apiService.js'; +import MapboxService from './mapboxService.js'; +import GoogleGeocodeService from './googleGeocodeService.js'; export default class ComponentService { /** @@ -54,6 +56,7 @@ export default class ComponentService { 'dt-button', 'dt-location', 'dt-users-connection', + 'dt-location-map', ]; } @@ -100,6 +103,22 @@ export default class ComponentService { ); el.dataset.eventDtGetData = true; } + + if (!el.dataset.eventDtGeocode) { + el.addEventListener( + 'dt:geocode', + this.handleGeocodeEvent.bind(this) + ); + el.dataset.eventDtGeocode = true; + } + + if (el.googleToken && !this.googleGeocodeService) { + this.googleGeocodeService = new GoogleGeocodeService( + el.googleToken, + window, + document, + ); + } }); } } @@ -260,6 +279,97 @@ export default class ComponentService { } } + /** + * Event listener for geocode events. + * Handles Mapbox and Google native API fallbacks. + * @param {Event} event + */ + async handleGeocodeEvent(event) { + const details = event.detail; + if (!details) return; + + const { type, query, suggestion, lat, lng, locale, onSuccess, onError } = details; + const el = event.target; // This is the dt-location-map-item + + try { + if (el.googleToken) { + + if (type === 'search') { + const predictions = await this.googleGeocodeService.getPlacePredictions(query, locale); + const results = (predictions || []).map(i => ({ + label: i.description, + place_id: i.place_id, + source: 'user', + raw: i, + })); + onSuccess(results); + } + else if (type === 'details') { + const place = await this.googleGeocodeService.getPlaceDetails(suggestion, locale); + if (place && place.error) throw new Error(place.error.message); + + onSuccess({ + ...suggestion, + lat: place?.lat, + lng: place?.lng, + level: place?.level + }); + } + else if (type === 'reverse') { + const results = await this.googleGeocodeService.reverseGeocode(lng, lat, locale); + if (results && results.length) { + const place = results[0]; + onSuccess({ + lng: place.geometry.location.lng, + lat: place.geometry.location.lat, + level: place.types && place.types.length ? place.types[0] : null, + label: place.formatted_address, + source: 'user', + }); + } else { + onSuccess(null); + } + } + } + + else if (el.mapboxToken) { + const mapboxService = new MapboxService(el.mapboxToken); + + if (type === 'search') { + const results = await mapboxService.searchPlaces(query, locale); + const formatted = results.map(i => ({ + lng: i.center[0], + lat: i.center[1], + level: i.place_type[0], + label: i.place_name, + source: 'user', + })); + onSuccess(formatted); + } + else if (type === 'details') { + onSuccess(suggestion); + } + else if (type === 'reverse') { + const results = await mapboxService.reverseGeocode(lng, lat, locale); + if (results && results.length) { + const place = results[0]; + onSuccess({ + lng: place.center[0], + lat: place.center[1], + level: place.place_type[0], + label: place.place_name, + source: 'user', + }); + } else { + onSuccess(null); + } + } + } + } catch (ex) { + if (onError) onError(ex); + } + } + /** * Event listener for change events. * Will set loading property, attempt to save value via API, From 34af99de50ccd76103b164246764c152b0a50973 Mon Sep 17 00:00:00 2001 From: jlamanskygitt Date: Fri, 12 Jun 2026 10:14:36 -0500 Subject: [PATCH 3/3] Added attachGeocodeEvents function --- src/services/componentService.js | 47 +++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/src/services/componentService.js b/src/services/componentService.js index e1a5a51..3d13329 100644 --- a/src/services/componentService.js +++ b/src/services/componentService.js @@ -70,6 +70,7 @@ export default class ComponentService { this.attachLoadEvents(); this.attachFileUploadEvents(); + this.attachGeocodeEvents(); } /** @@ -103,22 +104,6 @@ export default class ComponentService { ); el.dataset.eventDtGetData = true; } - - if (!el.dataset.eventDtGeocode) { - el.addEventListener( - 'dt:geocode', - this.handleGeocodeEvent.bind(this) - ); - el.dataset.eventDtGeocode = true; - } - - if (el.googleToken && !this.googleGeocodeService) { - this.googleGeocodeService = new GoogleGeocodeService( - el.googleToken, - window, - document, - ); - } }); } } @@ -182,6 +167,36 @@ export default class ComponentService { } } + /** + * Attach geocode event listeners to components that handle geocoding operations + * @param {string} [selector] (Optional) Override default selector + */ + attachGeocodeEvents(selector) { + const elements = document.querySelectorAll( + selector || 'dt-location-map' + ); + if (elements) { + elements.forEach(el => { + // prevent multiple event attachments if this is called multiple times + if (!el.dataset.eventDtGeocode) { + el.addEventListener( + 'dt:geocode', + this.handleGeocodeEvent.bind(this) + ); + el.dataset.eventDtGeocode = true; + } + + if (el.googleToken && !this.googleGeocodeService) { + this.googleGeocodeService = new GoogleGeocodeService( + el.googleToken, + window, + document, + ); + } + }); + } + } + /** * Event listener for load events. * Will attempt to load data from API and call success/error callback