From f024928ea76063d97c1f53ebdede073bad02f202 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Or=C5=82=C3=B3w?= <adrian.orlow@fishbrain.com> Date: Mon, 4 Mar 2024 16:08:48 +0100 Subject: [PATCH] feat: add partial bio entities events --- docs/plugins/events.md | 28 +++++++++++++++++++ .../overlaysLayer/createFeatureFromExtent.ts | 4 +-- .../utils/config/pinsLayer/getPinFeature.ts | 7 +++-- .../MapViewer/utils/config/useOlMapLayers.ts | 2 +- .../mapFeatureClick/useMapFeatureClick.ts | 15 ++++++++++ .../mapSingleClick/onMapSingleClick.ts | 3 +- .../utils/listeners/useOlMapListeners.ts | 19 ++++++++++++- .../pluginsEventBus.constants.ts | 2 ++ .../pluginsEventBus/pluginsEventBus.ts | 4 +++ .../pluginsEventBus/pluginsEventBus.types.ts | 16 +++++++++-- 10 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 src/components/Map/MapViewer/utils/listeners/mapFeatureClick/useMapFeatureClick.ts diff --git a/docs/plugins/events.md b/docs/plugins/events.md index 957b28a2..7ad4cf64 100644 --- a/docs/plugins/events.md +++ b/docs/plugins/events.md @@ -201,6 +201,34 @@ To listen for specific events, plugins can use the `addListener` method in `even } ``` +- onPinIconClick - triggered when someone clicks on a pin icon; the element to which the pin is attached is passed as an argument. Example argument: + +```javascript +{ + "id": 40072, +} +``` + +```javascript +{ + "id": "b0a478ad-7e7a-47f5-8130-e96cbeaa0cfe", // marker pin +} +``` + +- onSurfaceOverlayClick - triggered when someone clicks on a overlay surface; the element to which the pin is attached is passed as an argument. Example argument: + +```javascript +{ + "id": 18, +} +``` + +```javascript +{ + "id": "a3a5305f-acfa-47ff-bf77-a26d017c6eb3", // surface marker overlay +} +``` + - onSubmapOpen - triggered when submap opens; the submap identifier is passed as an argument. Example argument: ``` diff --git a/src/components/Map/MapViewer/utils/config/overlaysLayer/createFeatureFromExtent.ts b/src/components/Map/MapViewer/utils/config/overlaysLayer/createFeatureFromExtent.ts index 121bfe2b..f79764b2 100644 --- a/src/components/Map/MapViewer/utils/config/overlaysLayer/createFeatureFromExtent.ts +++ b/src/components/Map/MapViewer/utils/config/overlaysLayer/createFeatureFromExtent.ts @@ -1,5 +1,5 @@ -import Polygon, { fromExtent } from 'ol/geom/Polygon'; import Feature from 'ol/Feature'; +import Polygon, { fromExtent } from 'ol/geom/Polygon'; export const createFeatureFromExtent = ([xMin, yMin, xMax, yMax]: number[]): Feature<Polygon> => - new Feature({ geometry: fromExtent([xMin, yMin, xMax, yMax]) }); + new Feature({ geometry: fromExtent([xMin, yMin, xMax, yMax]), type: 'surface' }); diff --git a/src/components/Map/MapViewer/utils/config/pinsLayer/getPinFeature.ts b/src/components/Map/MapViewer/utils/config/pinsLayer/getPinFeature.ts index ff7bfb00..31d33f86 100644 --- a/src/components/Map/MapViewer/utils/config/pinsLayer/getPinFeature.ts +++ b/src/components/Map/MapViewer/utils/config/pinsLayer/getPinFeature.ts @@ -15,8 +15,11 @@ export const getPinFeature = ( y: y + (height || ZERO) / HALF, }; - return new Feature({ + const feature = new Feature({ geometry: new Point(pointToProjection(point)), - name: id, + id, + type: 'pin', }); + + return feature; }; diff --git a/src/components/Map/MapViewer/utils/config/useOlMapLayers.ts b/src/components/Map/MapViewer/utils/config/useOlMapLayers.ts index ff40ba91..a42e9677 100644 --- a/src/components/Map/MapViewer/utils/config/useOlMapLayers.ts +++ b/src/components/Map/MapViewer/utils/config/useOlMapLayers.ts @@ -22,7 +22,7 @@ export const useOlMapLayers = ({ mapInstance }: UseOlMapLayersInput): MapConfig[ return; } - mapInstance.setLayers([tileLayer, reactionsLayer, pinsLayer, overlaysLayer]); + mapInstance.setLayers([tileLayer, reactionsLayer, overlaysLayer, pinsLayer]); }, [reactionsLayer, tileLayer, pinsLayer, mapInstance, overlaysLayer]); return [tileLayer, pinsLayer, reactionsLayer, overlaysLayer]; diff --git a/src/components/Map/MapViewer/utils/listeners/mapFeatureClick/useMapFeatureClick.ts b/src/components/Map/MapViewer/utils/listeners/mapFeatureClick/useMapFeatureClick.ts new file mode 100644 index 00000000..2ab3ea05 --- /dev/null +++ b/src/components/Map/MapViewer/utils/listeners/mapFeatureClick/useMapFeatureClick.ts @@ -0,0 +1,15 @@ +import { FeatureLike } from 'ol/Feature'; + +interface UseMapFeatureClickResult { + handleFeatureClick(feature: FeatureLike): void; +} + +export const useMapFeatureClick = (): UseMapFeatureClickResult => { + const handleFeatureClick = (feature: FeatureLike): void => { + console.log(feature.get('type')); + }; + + return { + handleFeatureClick, + }; +}; diff --git a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/onMapSingleClick.ts b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/onMapSingleClick.ts index 6940b226..4661b41a 100644 --- a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/onMapSingleClick.ts +++ b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/onMapSingleClick.ts @@ -1,6 +1,7 @@ import { SIZE_OF_EMPTY_ARRAY } from '@/constants/common'; import { MapSize } from '@/redux/map/map.types'; import { AppDispatch } from '@/redux/store'; +import { MapInstance } from '@/types/map'; import { MapBrowserEvent } from 'ol'; import { getSearchResults } from './getSearchResults'; import { handleDataReset } from './handleDataReset'; @@ -9,7 +10,7 @@ import { handleSearchResultAction } from './handleSearchResultAction'; /* prettier-ignore */ export const onMapSingleClick = (mapSize: MapSize, modelId: number, dispatch: AppDispatch) => - async ({ coordinate }: MapBrowserEvent<UIEvent>): Promise<void> => { + async ({ coordinate }: Pick<MapBrowserEvent<UIEvent>, 'coordinate'>, mapInstance: MapInstance): Promise<void> => { // side-effect below is to prevent complications with data update - old data may conflict with new data // so we need to reset all the data before updating dispatch(handleDataReset); diff --git a/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.ts b/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.ts index 1742d6fa..1c0b9fcf 100644 --- a/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.ts +++ b/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.ts @@ -10,6 +10,7 @@ import { Pixel } from 'ol/pixel'; import { useEffect, useRef } from 'react'; import { useSelector } from 'react-redux'; import { useDebouncedCallback } from 'use-debounce'; +import { useMapFeatureClick } from './mapFeatureClick/useMapFeatureClick'; import { onMapRightClick } from './mapRightClick/onMapRightClick'; import { onMapSingleClick } from './mapSingleClick/onMapSingleClick'; import { onMapPositionChange } from './onMapPositionChange'; @@ -23,6 +24,7 @@ export const useOlMapListeners = ({ view, mapInstance }: UseOlMapListenersInput) const mapSize = useSelector(mapDataSizeSelector); const modelId = useSelector(currentModelIdSelector); const mapLastZoomValue = useSelector(mapDataLastZoomValue); + const { handleFeatureClick } = useMapFeatureClick(); const coordinate = useRef<Coordinate>([]); const pixel = useRef<Pixel>([]); const dispatch = useAppDispatch(); @@ -58,7 +60,22 @@ export const useOlMapListeners = ({ view, mapInstance }: UseOlMapListenersInput) return; } - const key = mapInstance.on('singleclick', handleMapSingleClick); + const key = mapInstance.on('click', e => { + mapInstance.forEachFeatureAtPixel(e.pixel, handleFeatureClick); + }); + + // eslint-disable-next-line consistent-return + return () => unByKey(key); + }, [mapInstance, handleFeatureClick]); + + useEffect(() => { + if (!mapInstance) { + return; + } + + const key = mapInstance.on('singleclick', ({ coordinate: ciird }) => + handleMapSingleClick({ coordinate }, mapInstance), + ); // eslint-disable-next-line consistent-return return () => unByKey(key); diff --git a/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.constants.ts b/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.constants.ts index 5fd9ea0a..5a5eb652 100644 --- a/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.constants.ts +++ b/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.constants.ts @@ -14,6 +14,8 @@ const PLUGINS_EVENTS = { onZoomChanged: 'onZoomChanged', onCenterChanged: 'onCenterChanged', onBioEntityClick: 'onBioEntityClick', + onPinIconClick: 'onPinIconClick', + onSurfaceOverlayClick: 'onSurfaceOverlayClick', }, search: { onSearch: 'onSearch', diff --git a/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.ts b/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.ts index 5227eabb..aa7221ab 100644 --- a/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.ts +++ b/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.ts @@ -4,6 +4,8 @@ import { ALLOWED_PLUGINS_EVENTS, LISTENER_NOT_FOUND } from './pluginsEventBus.co import type { CenteredCoordinates, ClickedBioEntity, + ClickedPinIcon, + ClickedSurfaceOverlay, Events, EventsData, PluginsEventBusType, @@ -21,6 +23,8 @@ export function dispatchEvent(type: 'onSubmapClose', submapId: number): void; export function dispatchEvent(type: 'onZoomChanged', data: ZoomChanged): void; export function dispatchEvent(type: 'onCenterChanged', data: CenteredCoordinates): void; export function dispatchEvent(type: 'onBioEntityClick', data: ClickedBioEntity): void; +export function dispatchEvent(type: 'onPinIconClick', data: ClickedPinIcon): void; +export function dispatchEvent(type: 'onSurfaceOverlayClick', data: ClickedSurfaceOverlay): void; export function dispatchEvent(type: 'onSearch', data: SearchData): void; export function dispatchEvent(type: Events, data: EventsData): void { if (!ALLOWED_PLUGINS_EVENTS.includes(type)) throw new Error(`Invalid event type: ${type}`); diff --git a/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.types.ts b/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.types.ts index 7679bb0a..4260f399 100644 --- a/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.types.ts +++ b/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.types.ts @@ -6,13 +6,15 @@ export type OverlayEvents = | 'onAddDataOverlay' | 'onRemoveDataOverlay' | 'onShowOverlay' - | 'onHideOverlay'; + | 'onHideOverlay' + | 'onSurfaceOverlayClick'; export type SubmapEvents = | 'onSubmapOpen' | 'onSubmapClose' | 'onZoomChanged' | 'onCenterChanged' - | 'onBioEntityClick'; + | 'onBioEntityClick' + | 'onPinIconClick'; export type SearchEvents = 'onSearch'; export type Events = OverlayEvents | BackgroundEvents | SubmapEvents | SearchEvents; @@ -34,6 +36,14 @@ export type ClickedBioEntity = { modelId: number; }; +export type ClickedPinIcon = { + id: number | string; +}; + +export type ClickedSurfaceOverlay = { + id: number | string; +}; + export type SearchDataBioEntity = { type: 'bioEntity'; searchValues: string[]; @@ -61,6 +71,8 @@ export type EventsData = | ZoomChanged | CenteredCoordinates | ClickedBioEntity + | ClickedPinIcon + | ClickedSurfaceOverlay | SearchData; export type PluginsEventBusType = { -- GitLab