From 2a03afa847aae37c35def2c416b7d444c4ade37d Mon Sep 17 00:00:00 2001 From: Damon Ulmi <63123585+DamonU2@users.noreply.github.com> Date: Thu, 26 Sep 2024 11:40:17 -0700 Subject: [PATCH] feat(event): Event added to API for map ready (#2510) --- packages/geoview-core/src/api/api.ts | 71 ++++++++++++++++++- .../map-event-processor.ts | 5 +- packages/geoview-core/src/core/app-start.tsx | 11 +-- .../geoview-core/src/geo/map/map-viewer.ts | 2 +- 4 files changed, 76 insertions(+), 13 deletions(-) diff --git a/packages/geoview-core/src/api/api.ts b/packages/geoview-core/src/api/api.ts index c4eaee47331..27b66789e90 100644 --- a/packages/geoview-core/src/api/api.ts +++ b/packages/geoview-core/src/api/api.ts @@ -33,6 +33,9 @@ export class API { // utilities object utilities; + // Keep all callback delegates references + #onMapViewerReadyHandlers: MapViewerReadyDelegate[] = []; + // Keep all callback delegates references #onMapAddedToDivHandlers: MapAddedToDivDelegate[] = []; @@ -54,6 +57,32 @@ export class API { API.#manageKeyboardFocus(); } + // TODO Add getter function and make maps property private + /** + * Sets a map viewer in maps, or removes one if the mapViewer property is null. + * @param {string} mapId - ID of the map + * @param {MapViewer | null} mapViewer - The viewer to be added or null to remove + * @param {(mapViewer: MapViewer) => void} onMapViewerInit - Function to run on map init + */ + setMapViewer(mapId: string, mapViewer: MapViewer | null, onMapViewerInit?: (mapViewer: MapViewer) => void): void { + if (mapViewer) { + if (this.maps[mapId]) logger.logError(`Cannot add map. Map with ID ${mapId} already exists`); + else { + this.maps[mapId] = mapViewer; + + // Register a handler (which will only happen once) for when the map viewer will get initialized. + // At the time of writing, this happens later, asynchronously, via the components/map/map.tsx when 'MapViewer.initMap()' is called. + // That should be fixed eventually, but that refactoring is out of the scope at the time of writing. So, I'm doing like this for now. + this.maps[mapId].onMapInit((viewer) => { + // MapViewer has been created and initialized, callback about it + onMapViewerInit?.(viewer); + // Emit that viewer is ready + this.#emitMapViewerReady({ mapId }); + }); + } + } else delete this.maps[mapId]; + } + /** * Apply outline to elements when keyboard is use to navigate * Code from: https://github.com/MaxMaeder/keyboardFocus.js @@ -123,6 +152,33 @@ export class API { return Promise.reject(new Error(`Div with id ${divId} does not exist`)); } + /** + * Emits a map viewer ready event to all handlers. + * @private + */ + #emitMapViewerReady(event: MapViewerReadyEvent): void { + // Emit the event for all handlers + EventHelper.emitEvent(this, this.#onMapViewerReadyHandlers, event); + } + + /** + * Registers a map viewer ready event callback. + * @param {MapViewerReadyDelegate} callback - The callback to be executed whenever the event is emitted + */ + onMapViewerReady(callback: MapViewerReadyDelegate): void { + // Register the event handler + EventHelper.onEvent(this.#onMapViewerReadyHandlers, callback); + } + + /** + * Unregisters a map viewer ready event callback. + * @param {MapViewerReadyDelegate} callback - The callback to stop being called whenever the event is emitted + */ + offMapViewerReady(callback: MapViewerReadyDelegate): void { + // Unregister the event handler + EventHelper.offEvent(this.#onMapViewerReadyHandlers, callback); + } + /** * Emits an event to all handlers. * @param {MapAddedToDivEvent} event - The event to emit @@ -152,6 +208,19 @@ export class API { } } +/** + * Define a delegate for the event handler function signature + */ +type MapViewerReadyDelegate = EventDelegateBase; + +/** + * Define an event for the delegate + */ +export type MapViewerReadyEvent = { + // The added map + mapId: string; +}; + /** * Define a delegate for the event handler function signature */ @@ -161,6 +230,6 @@ type MapAddedToDivDelegate = EventDelegateBase; * Define an event for the delegate */ export type MapAddedToDivEvent = { - // The added layer + // The added map mapId: string; }; diff --git a/packages/geoview-core/src/api/event-processors/event-processor-children/map-event-processor.ts b/packages/geoview-core/src/api/event-processors/event-processor-children/map-event-processor.ts index ec7da9699db..728baafae89 100644 --- a/packages/geoview-core/src/api/event-processors/event-processor-children/map-event-processor.ts +++ b/packages/geoview-core/src/api/event-processors/event-processor-children/map-event-processor.ts @@ -17,6 +17,7 @@ import { TypeViewSettings, TypePointMarker, } from '@config/types/map-schema-types'; +import { cloneDeep } from 'lodash'; import { api } from '@/app'; import { LayerApi } from '@/geo/layer/layer'; import { MapViewer, TypeMapState, TypeMapMouseInfo } from '@/geo/map/map-viewer'; @@ -1050,7 +1051,9 @@ export class MapEventProcessor extends AbstractEventProcessor { : undefined, initialSettings, style: legendLayerInfo!.styleConfig ? legendLayerInfo!.styleConfig : undefined, - source: (layerEntryConfig! as VectorLayerEntryConfig).source ? (layerEntryConfig! as VectorLayerEntryConfig).source : undefined, + source: (layerEntryConfig! as VectorLayerEntryConfig).source + ? cloneDeep((layerEntryConfig! as VectorLayerEntryConfig).source) + : undefined, entryType: listOfLayerEntryConfig.length ? 'group' : undefined, listOfLayerEntryConfig: listOfLayerEntryConfig.length ? listOfLayerEntryConfig : [], }; diff --git a/packages/geoview-core/src/core/app-start.tsx b/packages/geoview-core/src/core/app-start.tsx index 418265f77ab..966775b9887 100644 --- a/packages/geoview-core/src/core/app-start.tsx +++ b/packages/geoview-core/src/core/app-start.tsx @@ -35,7 +35,6 @@ export type TypeMapContext = { */ interface AppStartProps { mapFeaturesConfig: TypeMapFeaturesConfig; - // eslint-disable-next-line react/require-default-props onMapViewerInit?: (mapViewer: MapViewer) => void; } @@ -75,17 +74,9 @@ function AppStart(props: AppStartProps): JSX.Element { // TODO: use store, remove the use of feature by viewer class and use state to gather values if (!(mapId in api.maps)) { const mapViewer = new MapViewer(mapFeaturesConfig, i18nInstance); - api.maps[mapId] = mapViewer; + api.setMapViewer(mapId, mapViewer, onMapViewerInit); } - // Register a handler (which will only happen once) for when the map viewer will get initialized. - // At the time of writing, this happens later, asynchronously, via the components/map/map.tsx when 'MapViewer.initMap()' is called. - // That should be fixed eventually, but that refactoring is out of the scope at the time of writing. So, I'm doing like this for now. - api.maps[mapId].onMapInit((mapViewer) => { - // MapViewer has been created and initialized, callback about it - onMapViewerInit?.(mapViewer); - }); - return ( diff --git a/packages/geoview-core/src/geo/map/map-viewer.ts b/packages/geoview-core/src/geo/map/map-viewer.ts index 61e66ef7679..87bf5f0d4c6 100644 --- a/packages/geoview-core/src/geo/map/map-viewer.ts +++ b/packages/geoview-core/src/geo/map/map-viewer.ts @@ -1201,7 +1201,7 @@ export class MapViewer { if (deleteContainer) mapContainer.remove(); // Delete the map instance from the maps array, will delete attached plugins - delete api.maps[this.mapId]; + api.setMapViewer(this.mapId, null); // Return the map container to be remove return mapContainer;