Skip to content

Commit

Permalink
Started
Browse files Browse the repository at this point in the history
Stores created
Style class created and separated from component
Fix for the path for the guide panel
Swiper, working state
Swiper improved
Moved the addEventListener and removeEventListener from the rendering phase to a useEffect mount hook.
Better logs and comments
Swiper working
Had to use async afterall to retrieve the OpenLayers layers
Adjusting the swiper template configs to use layer path
Finalizing
Cleanup
  • Loading branch information
Alex-NRCan committed Feb 26, 2024
1 parent f3675d4 commit cce9c13
Show file tree
Hide file tree
Showing 21 changed files with 662 additions and 278 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"orientation": "vertical",
"keyboardOffset": 10,
"layers": ["esriFeatureLYR4"],
"layers": ["esriFeatureLYR4/0"],
"suportedLanguages": ["en", "fr"]
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"orientation": "horizontal",
"keyboardOffset": 10,
"layers": ["esriFeatureLYR4-map2", "esriDynamicLYR3-map2"],
"layers": ["esriFeatureLYR4-map2/2", "esriDynamicLYR3-map2/0"],
"suportedLanguages": ["en", "fr"]
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"orientation": "vertical",
"keyboardOffset": 10,
"layers": ["swipe0", "swipe1", "swipe4", "swipe5", "swipe6", "rcs.ccc75c12-5acc-4a6a-959f-ef6f621147b9.en", "swipe7"],
"layers": ["swipe0/toner", "swipe1/msi-94-or-more", "swipe4/polygons.json", "swipe5/ec-msc:CURRENT_CONDITIONS", "swipe6/lakes", "rcs.ccc75c12-5acc-4a6a-959f-ef6f621147b9.en/0"],
"suportedLanguages": ["en", "fr"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export abstract class AbstractEventProcessor {

// eslint-disable-next-line @typescript-eslint/no-unused-vars
protected onInitialize(store: GeoviewStoreType): Array<() => void> | void {
// Here, `store` is unused, but used in inherited classes, so the eslint-disable should be kept
// This method should be overriden to initialize and return a list of subscribtions so that they can be destroyed later
return undefined;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export class FeatureInfoEventProcessor extends AbstractEventProcessor {
FeatureInfoEventProcessor.deleteFeatureAllInfo(store.getState().mapId, layerPath);

// Log
logger.logDebug('Removed Feature Info in stores for layer path:', layerPath);
logger.logInfo('Removed Feature Info in stores for layer path:', layerPath);
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,6 @@ export class GeochartEventProcessor extends AbstractEventProcessor {
if (prevOrderedLayerPaths.includes(layerPath) && !curOrderedLayerPaths.includes(layerPath)) {
// Remove it
GeochartEventProcessor.removeGeochartChart(store.getState().mapId, layerPath);

// Log
logger.logDebug('Removed GeoChart configs for layer path:', layerPath);
}
});
}
Expand Down Expand Up @@ -95,18 +92,23 @@ export class GeochartEventProcessor extends AbstractEventProcessor {
const chartData: GeoChartStoreByLayerPath = {};

// Loop on the charts
const layerPaths: string[] = [];
charts.forEach((chartInfo) => {
// For each layer path
chartInfo.layers.forEach((layer) => {
// Get the layer path
const layerPath = layer.layerId;
chartData[layerPath] = chartInfo;
layerPaths.push(layerPath);
});
});

// set store charts config
this.getGeochartState(mapId)?.actions.setGeochartCharts(chartData);

// Log
logger.logInfo('Added GeoChart configs for layer paths:', layerPaths);

// TODO: Also update the layer array in other store state to inform the later has a geochart attached to it (when code is done over there)?
}

Expand All @@ -128,6 +130,9 @@ export class GeochartEventProcessor extends AbstractEventProcessor {
// Update the layer data array in the store
this.getGeochartState(mapId)!.actions.setGeochartCharts({ ...this.getGeochartState(mapId)?.geochartChartsConfig, ...toAdd });

// Log
logger.logInfo('Added GeoChart configs for layer path:', layerPath);

// TODO: Also update the layer array in other store state to inform the later has a geochart attached to it (when code is done over there)?
}

Expand All @@ -153,6 +158,9 @@ export class GeochartEventProcessor extends AbstractEventProcessor {
// Update the layer data array in the store
this.getGeochartState(mapId)!.actions.setGeochartCharts({ ...chartConfigs });

// Log
logger.logInfo('Removed GeoChart configs for layer path:', layerPath);

// TODO: Also update the layer array in other store state to inform the later has a geochart attached to it (when code is done over there)?
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import { GeoviewStoreType } from '@/core/stores';
import { ISwiperState } from '@/core/stores/store-interface-and-intial-values/swiper-state';
import { logger } from '@/core/utils/logger';

import { AbstractEventProcessor } from '../abstract-event-processor';

/**
* Event processor focusing on interacting with the swiper state in the store.
*/
export class SwiperEventProcessor extends AbstractEventProcessor {
// **********************************************************
// Static functions for Typescript files to access store actions
// **********************************************************
//! Typescript MUST always use the defined store actions below to modify store - NEVER use setState!
//! Some action does state modifications AND map actions.
//! ALWAYS use map event processor when an action modify store and IS NOT trap by map state event handler

/**
* Overrides initialization of the Swiper Event Processor
* @param {GeoviewStoreType} store The store associated with the Swiper Event Processor
* @returns An array of the subscriptions callbacks which were created
*/
protected onInitialize(store: GeoviewStoreType): Array<() => void> | void {
// Checks for udpated layers in layer order
const unsubLayerRemoved = store.subscribe(
(state) => state.mapState.orderedLayerInfo,
(cur, prev) => {
// Log
logger.logTraceCoreStoreSubscription('SWIPER EVENT PROCESSOR - orderedLayerInfo', cur);

// Read the layer paths of each layer info
const curOrderedLayerPaths = cur.map((layerInfo) => layerInfo.layerPath);
const prevOrderedLayerPaths = prev.map((layerInfo) => layerInfo.layerPath);

// Get all the layer paths to check in a distinct array for looping purposes
const layerPathsToCheck = [...store.getState().swiperState.layerPaths];

// For each layer paths the swiper is using
layerPathsToCheck.forEach((layerPath) => {
// If it was in the layerdata array and is not anymore
if (prevOrderedLayerPaths.includes(layerPath) && !curOrderedLayerPaths.includes(layerPath)) {
// Remove it
SwiperEventProcessor.removeLayerPath(store.getState().mapId, layerPath);
}
});
}
);

return [unsubLayerRemoved];
}

/**
* Shortcut to get the Swiper state for a given map id
* @param {string} mapId The mapId
* @returns {ISwiperState | undefined} The Swiper state. Forcing the return to also be 'undefined', because
* there will be no swiperState if the Swiper plugin isn't active.
* This helps the developers making sure the existence is checked.
*/
protected static getSwiperState(mapId: string): ISwiperState | undefined {
// Return the swiper state when it exists
return super.getState(mapId).swiperState;
}

/**
* Sets the layer paths on which the swiper should be activated.
*
* @param {string} mapId the map id
* @param {string[]} layerPaths The array of layer paths
*/
static setLayerPaths(mapId: string, layerPaths: string[]): void {
// set store layer paths
this.getSwiperState(mapId)?.actions.setLayerPaths(layerPaths);

// Log
logger.logInfo('Added Swiper functionality for layer paths:', layerPaths);

// TODO: Also update the layer array in other store state to inform the later has a swiper attached to it?
}

/**
* Adds a swipe functionality to the specified map id and layer path
* @param {string} mapId The map ID
* @param {string} layerPath The layer path
*/
static addLayerPath(mapId: string, layerPath: string): void {
// The processor needs an initialized layer paths store which is only initialized if the Swiper Plugin exists.
// Therefore, we validate its existence first.
if (!this.getSwiperState(mapId)) return;
if (!this.getSwiperState(mapId)?.layerPaths) return;

// If not already added
if (!this.getSwiperState(mapId)!.layerPaths.includes(layerPath)) {
// Add in the array
const updatedArray = [...this.getSwiperState(mapId)!.layerPaths];
updatedArray.push(layerPath);

// Update the layer data array in the store
this.getSwiperState(mapId)!.actions.setLayerPaths(updatedArray);

// Log
logger.logInfo('Added Swiper functionality for layer path:', layerPath);

// TODO: Also update the layer array in other store state to inform the later has a swiper attached to it?
} else {
// Log
logger.logInfo('Swiper functionality already active for layer path:', layerPath);
}
}

/**
* Removes a swipe functionality for the specified map id and layer path
* @param {string} mapId The map ID
* @param {string} layerPath The layer path
*/
static removeLayerPath(mapId: string, layerPath: string): void {
// The processor needs an initialized layer paths store which is only initialized if the Swiper Plugin exists.
// Therefore, we validate its existence first.
if (!this.getSwiperState(mapId)) return;
if (!this.getSwiperState(mapId)?.layerPaths) return;

// Find the index with the layer path
const layerIndex = this.getSwiperState(mapId)!.layerPaths.findIndex((layer) => layer === layerPath);

// Config to remove
if (layerIndex !== undefined && layerIndex >= 0) {
// Remove from the array
const updatedArray = [...this.getSwiperState(mapId)!.layerPaths];
updatedArray.splice(layerIndex, 1);

// Update the layer data array in the store
this.getSwiperState(mapId)!.actions.setLayerPaths(updatedArray);

// Log
logger.logInfo('Removed Swiper functionality for layer path:', layerPath);

// TODO: Also update the layer array in other store state to inform the later has a swiper attached to it?
}
}

/**
* Removes the swipe functionality for all layer paths
* @param {string} mapId The map ID
*/
static removeAll(mapId: string) {
// The processor needs an initialized layer paths store which is only initialized if the Swiper Plugin exists.
// Therefore, we validate its existence first.
if (!this.getSwiperState(mapId)) return;
if (!this.getSwiperState(mapId)?.layerPaths) return;

// Get all layer paths
const { layerPaths } = this.getSwiperState(mapId)!;

// Update the layer data array in the store
this.getSwiperState(mapId)!.actions.setLayerPaths([]);

// Log
logger.logInfo('Removed Swiper functionality for all layer paths', layerPaths);

// TODO: Also update the layer array in other store state to inform the later has a swiper attached to it?
}

// #endregion

// **********************************************************
// Static functions for Store Map State to action on API
// **********************************************************
//! NEVER add a store action who does set state AND map action at a same time.
//! Review the action in store state to make sure
}
4 changes: 4 additions & 0 deletions packages/geoview-core/src/api/event-processors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { MapEventProcessor } from '@/api/event-processors/event-processor-childr
import { TimeSliderEventProcessor } from '@/api/event-processors/event-processor-children/time-slider-event-processor';
import { GeochartEventProcessor } from '@/api/event-processors/event-processor-children/geochart-event-processor';
import { DataTableProcessor } from '@/api/event-processors/event-processor-children/data-table-processor';
import { SwiperEventProcessor } from './event-processor-children/swiper-event-processor';

// core
const appEventProcessor = new AppEventProcessor();
Expand All @@ -17,6 +18,7 @@ const dataTableProcessor = new DataTableProcessor();
// packages
const timeSliderEventProcessor = new TimeSliderEventProcessor();
const geochartEventProcessor = new GeochartEventProcessor();
const swiperEventProcessor = new SwiperEventProcessor();

export function initializeEventProcessors(store: GeoviewStoreType) {
// core stores
Expand All @@ -30,6 +32,7 @@ export function initializeEventProcessors(store: GeoviewStoreType) {
// TODO: Change this check for something more generic that checks in appBar too
if (store.getState().mapConfig!.footerBar?.tabs.core.includes('time-slider')) timeSliderEventProcessor.initialize(store);
if (store.getState().mapConfig!.footerBar?.tabs.core.includes('geochart')) geochartEventProcessor.initialize(store);
if (store.getState().mapConfig!.corePackages?.includes('swiper')) swiperEventProcessor.initialize(store);
}

export function destroyEventProcessors(store: GeoviewStoreType) {
Expand All @@ -44,4 +47,5 @@ export function destroyEventProcessors(store: GeoviewStoreType) {
// TODO: Change this check for something more generic that checks in appBar too
if (store.getState().mapConfig!.footerBar?.tabs.core.includes('time-slider')) timeSliderEventProcessor.destroy();
if (store.getState().mapConfig!.footerBar?.tabs.core.includes('geochart')) geochartEventProcessor.destroy();
if (store.getState().mapConfig!.corePackages?.includes('swiper')) swiperEventProcessor.destroy();
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createRoot } from 'react-dom/client';
import { AbstractPlugin } from './abstract-plugin';
import { MapContext } from '@/app';

/** ******************************************************************************************************************************
* Map Plugin abstract class.
Expand All @@ -24,19 +25,16 @@ export abstract class MapPlugin extends AbstractPlugin {
* Called when a map plugin is being added
*/
onAdd(): void {
// If some layers set
if ((this.configObj?.layers as string[]).length > 0) {
// create the swiper container and insert it after top link
const el = document.createElement('div');
el.setAttribute('id', `${this.pluginProps.mapId}-${this.pluginId}`);
const mapElement = document.getElementById(`mapbox-${this.pluginProps.mapId}`);
mapElement?.insertBefore(el, mapElement.firstChild);
// create the swiper container and insert it after top link
const el = document.createElement('div');
el.setAttribute('id', `${this.pluginProps.mapId}-${this.pluginId}`);
const mapElement = document.getElementById(`mapbox-${this.pluginProps.mapId}`);
mapElement?.prepend(el);

// create the swiper component and render
const node = this.onCreateContent();
const root = createRoot(document.getElementById(`${this.pluginProps.mapId}-${this.pluginId}`)!);
root.render(node);
}
// create the swiper component and render
const node = this.onCreateContent();
const root = createRoot(el);
root.render(<MapContext.Provider value={{ mapId: this.pluginProps.mapId }}>{node}</MapContext.Provider>);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/geoview-core/src/core/app-start.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function AppStart(props: AppStartProps): JSX.Element {
// Log
logger.logTraceUseMemo('APP-START - mapContextValue', mapId);

return { mapId: mapId as string };
return { mapId };
}, [mapId]);

//! get store values by id because context is not set.... it is the only 2 atomic selector by id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export function GuidePanel({ fullWidth }: GuidePanelType): JSX.Element {
const allTabs: TypeValidFooterBarTabsCoreProps | undefined = footerBarConfig?.tabs.core;

// fetch the content of general guide items with custom hook
let mdFilePath = '/geoview/locales/markdown/general-content.md';
let mdFilePath = '/geoview/public/locales/markdown/general-content.md';
if (process.env.NODE_ENV === 'development') mdFilePath = '/locales/markdown/general-content.md';
useFetchAndParseMarkdown(mapId, mdFilePath, t('guide.errorMessage'), setLeftPanelHelpItems);

Expand Down
4 changes: 4 additions & 0 deletions packages/geoview-core/src/core/stores/geoview-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { IMapState, initializeMapState } from './store-interface-and-intial-valu
import { IMapDataTableState, initialDataTableState } from './store-interface-and-intial-values/data-table-state';
import { ITimeSliderState, initializeTimeSliderState } from './store-interface-and-intial-values/time-slider-state';
import { IGeochartState, initializeGeochartState } from './store-interface-and-intial-values/geochart-state';
import { ISwiperState, initializeSwiperState } from './store-interface-and-intial-values/swiper-state';
import { IUIState, initializeUIState } from './store-interface-and-intial-values/ui-state';

import { TypeMapFeaturesConfig } from '@/core/types/global-types';
Expand Down Expand Up @@ -38,6 +39,7 @@ export interface IGeoviewState {
// packages state interface
geochartState: IGeochartState;
timeSliderState: ITimeSliderState;
swiperState: ISwiperState;
}

export const geoviewStoreDefinition = (set: TypeSetStore, get: TypeGetStore) => {
Expand All @@ -61,6 +63,7 @@ export const geoviewStoreDefinition = (set: TypeSetStore, get: TypeGetStore) =>
// TODO: Change this check for something more generic that checks in appBar too
if (config.footerBar?.tabs.core.includes('time-slider')) set({ timeSliderState: initializeTimeSliderState(set, get) });
if (config.footerBar?.tabs.core.includes('geochart')) set({ geochartState: initializeGeochartState(set, get) });
if (config.corePackages?.includes('swiper')) set({ swiperState: initializeSwiperState(set, get) });
},

// core states
Expand All @@ -75,6 +78,7 @@ export const geoviewStoreDefinition = (set: TypeSetStore, get: TypeGetStore) =>

export const geoviewStoreDefinitionWithSubscribeSelector = subscribeWithSelector(geoviewStoreDefinition);

// TODO: Refactor - Indicate why we need to use a fake store?
const fakeStore = create<IGeoviewState>()(geoviewStoreDefinitionWithSubscribeSelector);
export type GeoviewStoreType = typeof fakeStore;

Expand Down
Loading

0 comments on commit cce9c13

Please sign in to comment.