From 9a8699f74486ee4afe2297de75a124e8952435db 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 18:25:29 +0100
Subject: [PATCH] feat: add bio entities events + hover w/o tests

---
 docs/plugins/events.md                        |  2 +-
 package-lock.json                             | 24 ++++++++++
 package.json                                  |  2 +
 .../BioEntityDrawer.component.tsx             |  8 ++--
 .../overlaysLayer/createFeatureFromExtent.ts  | 17 ++++++-
 .../createOverlayGeometryFeature.ts           |  6 ++-
 ...createOverlaySubmapLinkRectangleFeature.ts |  4 +-
 .../overlaysLayer/useOverlayFeatures.ts       |  3 ++
 .../config/pinsLayer/getPinFeature.test.ts    |  2 +-
 .../utils/config/pinsLayer/getPinFeature.ts   |  6 ++-
 .../mapFeatureClick/useMapFeatureClick.ts     | 15 ------
 .../mapRightClick/onMapRightClick.ts          |  8 ++--
 .../mapSingleClick/handleAliasResults.ts      |  3 +-
 .../mapSingleClick/handleFeaturesClick.ts     | 39 +++++++++++++++
 .../mapSingleClick/onMapSingleClick.ts        | 15 ++++--
 .../utils/listeners/onPointerMove.ts          | 26 ++++++++++
 .../utils/listeners/useOlMapListeners.ts      | 13 ++---
 src/constants/features.ts                     |  9 ++++
 src/redux/bioEntity/bioEntity.selectors.ts    | 47 ++++++++++++++++++-
 src/redux/chemicals/chemicals.selectors.ts    | 12 +++++
 src/redux/drawer/drawer.reducers.ts           |  1 -
 src/redux/drawer/drawer.types.ts              |  3 ++
 src/redux/drugs/drugs.selectors.ts            | 12 +++++
 .../pluginsEventBus.constants.ts              |  2 +-
 .../pluginsEventBus/pluginsEventBus.ts        |  2 +-
 .../pluginsEventBus/pluginsEventBus.types.ts  |  2 +-
 26 files changed, 234 insertions(+), 49 deletions(-)
 delete mode 100644 src/components/Map/MapViewer/utils/listeners/mapFeatureClick/useMapFeatureClick.ts
 create mode 100644 src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleFeaturesClick.ts
 create mode 100644 src/components/Map/MapViewer/utils/listeners/onPointerMove.ts
 create mode 100644 src/constants/features.ts

diff --git a/docs/plugins/events.md b/docs/plugins/events.md
index 7ad4cf64..63072dc2 100644
--- a/docs/plugins/events.md
+++ b/docs/plugins/events.md
@@ -215,7 +215,7 @@ To listen for specific events, plugins can use the `addListener` method in `even
 }
 ```
 
-- onSurfaceOverlayClick - triggered when someone clicks on a overlay surface; the element to which the pin is attached is passed as an argument. Example argument:
+- onSurfaceClick - triggered when someone clicks on a overlay surface; the element to which the pin is attached is passed as an argument. Example argument:
 
 ```javascript
 {
diff --git a/package-lock.json b/package-lock.json
index 016ad699..a1966db0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -22,6 +22,7 @@
         "crypto-js": "^4.2.0",
         "downshift": "^8.2.3",
         "eslint-config-next": "13.4.19",
+        "is-uuid": "^1.0.2",
         "molart": "github:davidhoksza/MolArt",
         "next": "13.4.19",
         "ol": "^8.1.0",
@@ -49,6 +50,7 @@
         "@testing-library/react": "^14.0.0",
         "@testing-library/user-event": "^14.5.2",
         "@types/crypto-js": "^4.2.2",
+        "@types/is-uuid": "^1.0.2",
         "@types/jest": "^29.5.5",
         "@types/react-redux": "^7.1.26",
         "@types/redux-mock-store": "^1.0.6",
@@ -2319,6 +2321,12 @@
         "hoist-non-react-statics": "^3.3.0"
       }
     },
+    "node_modules/@types/is-uuid": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@types/is-uuid/-/is-uuid-1.0.2.tgz",
+      "integrity": "sha512-S+gWwUEApOjGCCO5LQrft4kciGWatvB0LyiyWTXSlDkclZBr6glSgstET573GsC5QPFdw2NeOw2PHOOMuTDFSQ==",
+      "dev": true
+    },
     "node_modules/@types/istanbul-lib-coverage": {
       "version": "2.0.6",
       "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
@@ -7968,6 +7976,11 @@
       "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==",
       "dev": true
     },
+    "node_modules/is-uuid": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-uuid/-/is-uuid-1.0.2.tgz",
+      "integrity": "sha512-tCByphFcJgf2qmiMo5hMCgNAquNSagOetVetDvBXswGkNfoyEMvGH1yDlF8cbZbKnbVBr4Y5/rlpMz9umxyBkQ=="
+    },
     "node_modules/is-weakmap": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz",
@@ -15724,6 +15737,12 @@
         "hoist-non-react-statics": "^3.3.0"
       }
     },
+    "@types/is-uuid": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@types/is-uuid/-/is-uuid-1.0.2.tgz",
+      "integrity": "sha512-S+gWwUEApOjGCCO5LQrft4kciGWatvB0LyiyWTXSlDkclZBr6glSgstET573GsC5QPFdw2NeOw2PHOOMuTDFSQ==",
+      "dev": true
+    },
     "@types/istanbul-lib-coverage": {
       "version": "2.0.6",
       "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
@@ -19786,6 +19805,11 @@
       "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==",
       "dev": true
     },
+    "is-uuid": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-uuid/-/is-uuid-1.0.2.tgz",
+      "integrity": "sha512-tCByphFcJgf2qmiMo5hMCgNAquNSagOetVetDvBXswGkNfoyEMvGH1yDlF8cbZbKnbVBr4Y5/rlpMz9umxyBkQ=="
+    },
     "is-weakmap": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz",
diff --git a/package.json b/package.json
index f3a8ec09..f44f22ef 100644
--- a/package.json
+++ b/package.json
@@ -36,6 +36,7 @@
     "crypto-js": "^4.2.0",
     "downshift": "^8.2.3",
     "eslint-config-next": "13.4.19",
+    "is-uuid": "^1.0.2",
     "molart": "github:davidhoksza/MolArt",
     "next": "13.4.19",
     "ol": "^8.1.0",
@@ -63,6 +64,7 @@
     "@testing-library/react": "^14.0.0",
     "@testing-library/user-event": "^14.5.2",
     "@types/crypto-js": "^4.2.2",
+    "@types/is-uuid": "^1.0.2",
     "@types/jest": "^29.5.5",
     "@types/react-redux": "^7.1.26",
     "@types/redux-mock-store": "^1.0.6",
diff --git a/src/components/Map/Drawer/BioEntityDrawer/BioEntityDrawer.component.tsx b/src/components/Map/Drawer/BioEntityDrawer/BioEntityDrawer.component.tsx
index 2ee7eb5b..03c3f495 100644
--- a/src/components/Map/Drawer/BioEntityDrawer/BioEntityDrawer.component.tsx
+++ b/src/components/Map/Drawer/BioEntityDrawer/BioEntityDrawer.component.tsx
@@ -1,7 +1,7 @@
 import { ZERO } from '@/constants/common';
 import {
-  searchedFromMapBioEntityElement,
-  searchedFromMapBioEntityElementRelatedSubmapSelector,
+  currentDrawerBioEntityRelatedSubmapSelector,
+  currentDrawerBioEntitySelector,
 } from '@/redux/bioEntity/bioEntity.selectors';
 import {
   getChemicalsForBioEntityDrawerTarget,
@@ -22,8 +22,8 @@ const TARGET_PREFIX: ElementSearchResultType = `ALIAS`;
 
 export const BioEntityDrawer = (): React.ReactNode => {
   const dispatch = useAppDispatch();
-  const bioEntityData = useAppSelector(searchedFromMapBioEntityElement);
-  const relatedSubmap = useAppSelector(searchedFromMapBioEntityElementRelatedSubmapSelector);
+  const bioEntityData = useAppSelector(currentDrawerBioEntitySelector);
+  const relatedSubmap = useAppSelector(currentDrawerBioEntityRelatedSubmapSelector);
   const currentTargetId = bioEntityData?.id ? `${TARGET_PREFIX}:${bioEntityData.id}` : '';
 
   const fetchChemicalsForTarget = (): void => {
diff --git a/src/components/Map/MapViewer/utils/config/overlaysLayer/createFeatureFromExtent.ts b/src/components/Map/MapViewer/utils/config/overlaysLayer/createFeatureFromExtent.ts
index f79764b2..a7daf843 100644
--- a/src/components/Map/MapViewer/utils/config/overlaysLayer/createFeatureFromExtent.ts
+++ b/src/components/Map/MapViewer/utils/config/overlaysLayer/createFeatureFromExtent.ts
@@ -1,5 +1,18 @@
+import { FEATURE_TYPE } from '@/constants/features';
+import { OverlayBioEntityRender } from '@/types/OLrendering';
+import isUUID from 'is-uuid';
 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]), type: 'surface' });
+export const createFeatureFromExtent = (
+  [xMin, yMin, xMax, yMax]: number[],
+  entityId: OverlayBioEntityRender['id'],
+): Feature<Polygon> => {
+  const isMarker = isUUID.anyNonNil(`${entityId}`);
+
+  return new Feature({
+    geometry: fromExtent([xMin, yMin, xMax, yMax]),
+    id: entityId,
+    type: isMarker ? FEATURE_TYPE.SURFACE_MARKER : FEATURE_TYPE.SURFACE_OVERLAY,
+  });
+};
diff --git a/src/components/Map/MapViewer/utils/config/overlaysLayer/createOverlayGeometryFeature.ts b/src/components/Map/MapViewer/utils/config/overlaysLayer/createOverlayGeometryFeature.ts
index b294d492..e11025d8 100644
--- a/src/components/Map/MapViewer/utils/config/overlaysLayer/createOverlayGeometryFeature.ts
+++ b/src/components/Map/MapViewer/utils/config/overlaysLayer/createOverlayGeometryFeature.ts
@@ -1,6 +1,7 @@
-import { Fill, Stroke, Style } from 'ol/style';
+import { OverlayBioEntityRender } from '@/types/OLrendering';
 import Feature from 'ol/Feature';
 import type Polygon from 'ol/geom/Polygon';
+import { Fill, Stroke, Style } from 'ol/style';
 import { createFeatureFromExtent } from './createFeatureFromExtent';
 
 const getBioEntityOverlayFeatureStyle = (color: string): Style =>
@@ -9,8 +10,9 @@ const getBioEntityOverlayFeatureStyle = (color: string): Style =>
 export const createOverlayGeometryFeature = (
   [xMin, yMin, xMax, yMax]: number[],
   color: string,
+  entityId: OverlayBioEntityRender['id'],
 ): Feature<Polygon> => {
-  const feature = createFeatureFromExtent([xMin, yMin, xMax, yMax]);
+  const feature = createFeatureFromExtent([xMin, yMin, xMax, yMax], entityId);
   feature.setStyle(getBioEntityOverlayFeatureStyle(color));
   return feature;
 };
diff --git a/src/components/Map/MapViewer/utils/config/overlaysLayer/createOverlaySubmapLinkRectangleFeature.ts b/src/components/Map/MapViewer/utils/config/overlaysLayer/createOverlaySubmapLinkRectangleFeature.ts
index cef98354..8a051f2f 100644
--- a/src/components/Map/MapViewer/utils/config/overlaysLayer/createOverlaySubmapLinkRectangleFeature.ts
+++ b/src/components/Map/MapViewer/utils/config/overlaysLayer/createOverlaySubmapLinkRectangleFeature.ts
@@ -1,4 +1,5 @@
 /* eslint-disable no-magic-numbers */
+import { OverlayBioEntityRender } from '@/types/OLrendering';
 import Feature from 'ol/Feature';
 import type Polygon from 'ol/geom/Polygon';
 import { createFeatureFromExtent } from './createFeatureFromExtent';
@@ -7,8 +8,9 @@ import { getOverlaySubmapLinkRectangleFeatureStyle } from './getOverlaySubmapLin
 export const createOverlaySubmapLinkRectangleFeature = (
   [xMin, yMin, xMax, yMax]: number[],
   color: string | null,
+  entityId: OverlayBioEntityRender['id'],
 ): Feature<Polygon> => {
-  const feature = createFeatureFromExtent([xMin, yMin, xMax, yMax]);
+  const feature = createFeatureFromExtent([xMin, yMin, xMax, yMax], entityId);
   feature.setStyle(getOverlaySubmapLinkRectangleFeatureStyle(color));
   return feature;
 };
diff --git a/src/components/Map/MapViewer/utils/config/overlaysLayer/useOverlayFeatures.ts b/src/components/Map/MapViewer/utils/config/overlaysLayer/useOverlayFeatures.ts
index 93e24ec8..107add31 100644
--- a/src/components/Map/MapViewer/utils/config/overlaysLayer/useOverlayFeatures.ts
+++ b/src/components/Map/MapViewer/utils/config/overlaysLayer/useOverlayFeatures.ts
@@ -35,6 +35,7 @@ export const useOverlayFeatures = (): Feature<Polygon>[] | Feature<SimpleGeometr
             ...pointToProjection({ x: entity.x2, y: entity.y2 }),
           ],
           entity?.hexColor || color,
+          entity.id,
         );
       }),
     [getOverlayBioEntityColorByAvailableProperties, markersRender, pointToProjection],
@@ -67,6 +68,7 @@ export const useOverlayFeatures = (): Feature<Polygon>[] | Feature<SimpleGeometr
               ...pointToProjection({ x: xMax, y: entity.y2 }),
             ],
             entity.value === Infinity ? null : color,
+            entity.id,
           );
         }
 
@@ -77,6 +79,7 @@ export const useOverlayFeatures = (): Feature<Polygon>[] | Feature<SimpleGeometr
               ...pointToProjection({ x: xMax, y: entity.y2 }),
             ],
             color,
+            entity.id,
           );
         }
 
diff --git a/src/components/Map/MapViewer/utils/config/pinsLayer/getPinFeature.test.ts b/src/components/Map/MapViewer/utils/config/pinsLayer/getPinFeature.test.ts
index 28610fd8..80af09f6 100644
--- a/src/components/Map/MapViewer/utils/config/pinsLayer/getPinFeature.test.ts
+++ b/src/components/Map/MapViewer/utils/config/pinsLayer/getPinFeature.test.ts
@@ -24,7 +24,7 @@ describe('getPinFeature - subUtil', () => {
   });
 
   it('should return id as name', () => {
-    expect(result.get('name')).toBe(bioEntity.id);
+    expect(result.get('id')).toBe(bioEntity.id);
   });
 
   it('should return point parsed with point to projection', () => {
diff --git a/src/components/Map/MapViewer/utils/config/pinsLayer/getPinFeature.ts b/src/components/Map/MapViewer/utils/config/pinsLayer/getPinFeature.ts
index 31d33f86..51d4f8d3 100644
--- a/src/components/Map/MapViewer/utils/config/pinsLayer/getPinFeature.ts
+++ b/src/components/Map/MapViewer/utils/config/pinsLayer/getPinFeature.ts
@@ -1,8 +1,10 @@
 import { ZERO } from '@/constants/common';
 import { HALF } from '@/constants/dividers';
+import { FEATURE_TYPE } from '@/constants/features';
 import { Marker } from '@/redux/markers/markers.types';
 import { BioEntity } from '@/types/models';
 import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection';
+import isUUID from 'is-uuid';
 import { Feature } from 'ol';
 import { Point } from 'ol/geom';
 
@@ -10,6 +12,8 @@ export const getPinFeature = (
   { x, y, width, height, id }: Pick<BioEntity, 'id' | 'width' | 'height' | 'x' | 'y'> | Marker,
   pointToProjection: UsePointToProjectionResult,
 ): Feature => {
+  const isMarker = isUUID.anyNonNil(`${id}`);
+
   const point = {
     x: x + (width || ZERO) / HALF,
     y: y + (height || ZERO) / HALF,
@@ -18,7 +22,7 @@ export const getPinFeature = (
   const feature = new Feature({
     geometry: new Point(pointToProjection(point)),
     id,
-    type: 'pin',
+    type: isMarker ? FEATURE_TYPE.PIN_ICON_MARKER : FEATURE_TYPE.PIN_ICON_BIOENTITY,
   });
 
   return feature;
diff --git a/src/components/Map/MapViewer/utils/listeners/mapFeatureClick/useMapFeatureClick.ts b/src/components/Map/MapViewer/utils/listeners/mapFeatureClick/useMapFeatureClick.ts
deleted file mode 100644
index 2ab3ea05..00000000
--- a/src/components/Map/MapViewer/utils/listeners/mapFeatureClick/useMapFeatureClick.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-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/mapRightClick/onMapRightClick.ts b/src/components/Map/MapViewer/utils/listeners/mapRightClick/onMapRightClick.ts
index 3d66ff05..a957771e 100644
--- a/src/components/Map/MapViewer/utils/listeners/mapRightClick/onMapRightClick.ts
+++ b/src/components/Map/MapViewer/utils/listeners/mapRightClick/onMapRightClick.ts
@@ -1,12 +1,12 @@
+import { SIZE_OF_EMPTY_ARRAY } from '@/constants/common';
 import { openContextMenu } from '@/redux/contextMenu/contextMenu.slice';
+import { MapSize } from '@/redux/map/map.types';
 import { AppDispatch } from '@/redux/store';
-import { Pixel } from 'ol/pixel';
 import { Coordinate } from 'ol/coordinate';
-import { MapSize } from '@/redux/map/map.types';
-import { SIZE_OF_EMPTY_ARRAY } from '@/constants/common';
+import { Pixel } from 'ol/pixel';
+import { getSearchResults } from '../mapSingleClick/getSearchResults';
 import { handleDataReset } from '../mapSingleClick/handleDataReset';
 import { handleSearchResultForRightClickAction } from './handleSearchResultForRightClickAction';
-import { getSearchResults } from '../mapSingleClick/getSearchResults';
 
 /* prettier-ignore */
 export const onMapRightClick =
diff --git a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleAliasResults.ts b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleAliasResults.ts
index b1487583..f3ead6cc 100644
--- a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleAliasResults.ts
+++ b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleAliasResults.ts
@@ -1,5 +1,5 @@
 import { getMultiBioEntity } from '@/redux/bioEntity/bioEntity.thunks';
-import { openBioEntityDrawerById } from '@/redux/drawer/drawer.slice';
+import { openBioEntityDrawerById, selectTab } from '@/redux/drawer/drawer.slice';
 import { AppDispatch } from '@/redux/store';
 import { ElementSearchResult } from '@/types/models';
 
@@ -8,6 +8,7 @@ export const handleAliasResults =
   (dispatch: AppDispatch) =>
     async ({ id }: ElementSearchResult): Promise<void> => {
 
+      dispatch(selectTab(`${id}`));
       dispatch(openBioEntityDrawerById(id));
       dispatch(
         getMultiBioEntity({
diff --git a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleFeaturesClick.ts b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleFeaturesClick.ts
new file mode 100644
index 00000000..3c1e5923
--- /dev/null
+++ b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/handleFeaturesClick.ts
@@ -0,0 +1,39 @@
+import { FEATURE_TYPE, PIN_ICON_ANY, SURFACE_ANY } from '@/constants/features';
+import { openBioEntityDrawerById } from '@/redux/drawer/drawer.slice';
+import { clearSearchData } from '@/redux/search/search.slice';
+import { AppDispatch } from '@/redux/store';
+import { PluginsEventBus } from '@/services/pluginsManager/pluginsEventBus';
+import { FeatureLike } from 'ol/Feature';
+
+interface HandleFeaturesClickResult {
+  shouldBlockCoordSearch: boolean;
+}
+
+export const handleFeaturesClick = (
+  features: FeatureLike[],
+  dispatch: AppDispatch,
+): HandleFeaturesClickResult => {
+  let shouldBlockCoordSearch = false;
+  const pinFeatures = features.filter(feature => PIN_ICON_ANY.includes(feature.get('type')));
+  const surfaceFeatures = features.filter(feature => SURFACE_ANY.includes(feature.get('type')));
+
+  pinFeatures.forEach(pin => {
+    const pinId = pin.get('id') as string | number;
+    PluginsEventBus.dispatchEvent('onPinIconClick', { id: pinId });
+
+    if (pin.get('type') === FEATURE_TYPE.PIN_ICON_BIOENTITY) {
+      dispatch(clearSearchData());
+      dispatch(openBioEntityDrawerById(pinId));
+      shouldBlockCoordSearch = true;
+    }
+  });
+
+  surfaceFeatures.forEach(surface => {
+    const surfaceId = surface.get('id') as string | number;
+    PluginsEventBus.dispatchEvent('onSurfaceClick', { id: surfaceId });
+  });
+
+  return {
+    shouldBlockCoordSearch,
+  };
+};
diff --git a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/onMapSingleClick.ts b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/onMapSingleClick.ts
index 4661b41a..3d1e425c 100644
--- a/src/components/Map/MapViewer/utils/listeners/mapSingleClick/onMapSingleClick.ts
+++ b/src/components/Map/MapViewer/utils/listeners/mapSingleClick/onMapSingleClick.ts
@@ -1,16 +1,25 @@
 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 { Map, MapBrowserEvent } from 'ol';
+import { FeatureLike } from 'ol/Feature';
 import { getSearchResults } from './getSearchResults';
 import { handleDataReset } from './handleDataReset';
+import { handleFeaturesClick } from './handleFeaturesClick';
 import { handleSearchResultAction } from './handleSearchResultAction';
 
 /* prettier-ignore */
 export const onMapSingleClick =
   (mapSize: MapSize, modelId: number, dispatch: AppDispatch) =>
-    async ({ coordinate }: Pick<MapBrowserEvent<UIEvent>, 'coordinate'>, mapInstance: MapInstance): Promise<void> => {
+    async ({ coordinate, pixel }: Pick<MapBrowserEvent<UIEvent>, 'coordinate' | 'pixel'>, mapInstance: Map): Promise<void> => {
+      const featuresAtPixel: FeatureLike[] = [];
+      mapInstance.forEachFeatureAtPixel(pixel, (feature) => featuresAtPixel.push(feature));
+      const { shouldBlockCoordSearch } = handleFeaturesClick(featuresAtPixel, dispatch);
+
+      if (shouldBlockCoordSearch) {
+        return;
+      }
+
       // 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/onPointerMove.ts b/src/components/Map/MapViewer/utils/listeners/onPointerMove.ts
new file mode 100644
index 00000000..47f99775
--- /dev/null
+++ b/src/components/Map/MapViewer/utils/listeners/onPointerMove.ts
@@ -0,0 +1,26 @@
+import { PIN_ICON_ANY } from '@/constants/features';
+import { Map } from 'ol';
+import MapBrowserEvent from 'ol/MapBrowserEvent';
+
+/* prettier-ignore */
+export const onPointerMove =
+    (mapInstance: Map, event: MapBrowserEvent<PointerEvent>): void => {
+      if (event.dragging) {
+        return;
+      }
+
+      const pixel = mapInstance.getEventPixel(event.originalEvent);
+      const feature = mapInstance.forEachFeatureAtPixel(pixel, firstFeature => {
+        const isPinIcon = PIN_ICON_ANY.includes(firstFeature.get('type'));
+        if (!isPinIcon) {
+          return undefined;
+        }
+
+        return firstFeature;
+      });
+
+      const target = mapInstance.getTarget();
+      if (target && typeof target !== 'string' && 'style' in target) {
+        target.style.cursor = feature ? 'pointer' : '';
+      }
+    };
diff --git a/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.ts b/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.ts
index 1c0b9fcf..7ca778b5 100644
--- a/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.ts
+++ b/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.ts
@@ -10,10 +10,10 @@ 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';
+import { onPointerMove } from './onPointerMove';
 
 interface UseOlMapListenersInput {
   view: View;
@@ -24,7 +24,6 @@ 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();
@@ -60,21 +59,19 @@ export const useOlMapListeners = ({ view, mapInstance }: UseOlMapListenersInput)
       return;
     }
 
-    const key = mapInstance.on('click', e => {
-      mapInstance.forEachFeatureAtPixel(e.pixel, handleFeatureClick);
-    });
+    const key = mapInstance.on('pointermove', event => onPointerMove(mapInstance, event));
 
     // eslint-disable-next-line consistent-return
     return () => unByKey(key);
-  }, [mapInstance, handleFeatureClick]);
+  }, [mapInstance]);
 
   useEffect(() => {
     if (!mapInstance) {
       return;
     }
 
-    const key = mapInstance.on('singleclick', ({ coordinate: ciird }) =>
-      handleMapSingleClick({ coordinate }, mapInstance),
+    const key = mapInstance.on('singleclick', event =>
+      handleMapSingleClick({ coordinate: event.coordinate, pixel: event.pixel }, mapInstance),
     );
 
     // eslint-disable-next-line consistent-return
diff --git a/src/constants/features.ts b/src/constants/features.ts
new file mode 100644
index 00000000..4995bbcf
--- /dev/null
+++ b/src/constants/features.ts
@@ -0,0 +1,9 @@
+export const FEATURE_TYPE = {
+  PIN_ICON_BIOENTITY: 'PIN_ICON_BIOENTITY',
+  PIN_ICON_MARKER: 'PIN_ICON_MARKER',
+  SURFACE_OVERLAY: 'SURFACE_OVERLAY',
+  SURFACE_MARKER: 'SURFACE_MARKER',
+} as const;
+
+export const PIN_ICON_ANY = [FEATURE_TYPE.PIN_ICON_BIOENTITY, FEATURE_TYPE.PIN_ICON_MARKER];
+export const SURFACE_ANY = [FEATURE_TYPE.SURFACE_OVERLAY, FEATURE_TYPE.SURFACE_MARKER];
diff --git a/src/redux/bioEntity/bioEntity.selectors.ts b/src/redux/bioEntity/bioEntity.selectors.ts
index b9566eb7..8bf9877d 100644
--- a/src/redux/bioEntity/bioEntity.selectors.ts
+++ b/src/redux/bioEntity/bioEntity.selectors.ts
@@ -3,13 +3,19 @@ import { rootSelector } from '@/redux/root/root.selectors';
 import { MultiSearchData } from '@/types/fetchDataState';
 import { BioEntity, BioEntityContent, MapModel } from '@/types/models';
 import { createSelector } from '@reduxjs/toolkit';
-import { searchedChemicalsBioEntitesOfCurrentMapSelector } from '../chemicals/chemicals.selectors';
+import {
+  allChemicalsBioEntitesOfAllMapsSelector,
+  searchedChemicalsBioEntitesOfCurrentMapSelector,
+} from '../chemicals/chemicals.selectors';
 import { currentSelectedBioEntityIdSelector } from '../contextMenu/contextMenu.selector';
 import {
   currentSearchedBioEntityId,
   currentSelectedSearchElement,
 } from '../drawer/drawer.selectors';
-import { searchedDrugsBioEntitesOfCurrentMapSelector } from '../drugs/drugs.selectors';
+import {
+  allDrugsBioEntitesOfAllMapsSelector,
+  searchedDrugsBioEntitesOfCurrentMapSelector,
+} from '../drugs/drugs.selectors';
 import { currentModelIdSelector, modelsDataSelector } from '../models/models.selectors';
 
 export const bioEntitySelector = createSelector(rootSelector, state => state.bioEntity);
@@ -122,3 +128,40 @@ export const allVisibleBioEntitiesSelector = createSelector(
     return [content, chemicals, drugs].flat();
   },
 );
+
+export const allContentBioEntitesSelectorOfAllMaps = createSelector(
+  bioEntitySelector,
+  (bioEntities): BioEntity[] => {
+    if (!bioEntities) {
+      return [];
+    }
+
+    return (bioEntities?.data || [])
+      .map(({ data }) => data || [])
+      .flat()
+      .map(({ bioEntity }) => bioEntity);
+  },
+);
+
+export const allBioEntitiesSelector = createSelector(
+  allContentBioEntitesSelectorOfAllMaps,
+  allChemicalsBioEntitesOfAllMapsSelector,
+  allDrugsBioEntitesOfAllMapsSelector,
+  (content, chemicals, drugs): BioEntity[] => {
+    return [content, chemicals, drugs].flat();
+  },
+);
+
+export const currentDrawerBioEntitySelector = createSelector(
+  allBioEntitiesSelector,
+  currentSearchedBioEntityId,
+  (bioEntities, currentBioEntityId): BioEntity | undefined =>
+    bioEntities.find(({ id }) => id === currentBioEntityId),
+);
+
+export const currentDrawerBioEntityRelatedSubmapSelector = createSelector(
+  currentDrawerBioEntitySelector,
+  modelsDataSelector,
+  (bioEntity, models): MapModel | undefined =>
+    models.find(({ idObject }) => idObject === bioEntity?.submodel?.mapId),
+);
diff --git a/src/redux/chemicals/chemicals.selectors.ts b/src/redux/chemicals/chemicals.selectors.ts
index 03f829f8..bb8d4aee 100644
--- a/src/redux/chemicals/chemicals.selectors.ts
+++ b/src/redux/chemicals/chemicals.selectors.ts
@@ -41,6 +41,18 @@ export const searchedChemicalsBioEntitesOfCurrentMapSelector = createSelector(
   },
 );
 
+export const allChemicalsBioEntitesOfAllMapsSelector = createSelector(
+  chemicalsSelector,
+  (chemicalsState): BioEntity[] => {
+    return (chemicalsState?.data || [])
+      .map(({ data }) => data || [])
+      .flat()
+      .map(({ targets }) => targets.map(({ targetElements }) => targetElements))
+      .flat()
+      .flat();
+  },
+);
+
 export const loadingChemicalsStatusSelector = createSelector(
   chemicalsForSelectedSearchElementSelector,
   state => state?.loading,
diff --git a/src/redux/drawer/drawer.reducers.ts b/src/redux/drawer/drawer.reducers.ts
index 3a72aa53..29333ec2 100644
--- a/src/redux/drawer/drawer.reducers.ts
+++ b/src/redux/drawer/drawer.reducers.ts
@@ -111,7 +111,6 @@ export const openBioEntityDrawerByIdReducer = (
   state.isOpen = true;
   state.drawerName = 'bio-entity';
   state.bioEntityDrawerState.bioentityId = action.payload;
-  state.searchDrawerState.selectedSearchElement = action.payload.toString();
 };
 
 export const getBioEntityDrugsForTargetReducers = (
diff --git a/src/redux/drawer/drawer.types.ts b/src/redux/drawer/drawer.types.ts
index a348517b..a0d51989 100644
--- a/src/redux/drawer/drawer.types.ts
+++ b/src/redux/drawer/drawer.types.ts
@@ -43,3 +43,6 @@ export type OpenReactionDrawerByIdAction = PayloadAction<OpenReactionDrawerByIdP
 
 export type OpenBioEntityDrawerByIdPayload = number | string;
 export type OpenBioEntityDrawerByIdAction = PayloadAction<OpenBioEntityDrawerByIdPayload>;
+
+export type SetSelectedSearchElementPayload = string;
+export type SetSelectedSearchElementAction = PayloadAction<SetSelectedSearchElementPayload>;
diff --git a/src/redux/drugs/drugs.selectors.ts b/src/redux/drugs/drugs.selectors.ts
index 45e9c16c..f5c74de2 100644
--- a/src/redux/drugs/drugs.selectors.ts
+++ b/src/redux/drugs/drugs.selectors.ts
@@ -54,3 +54,15 @@ export const searchedDrugsBioEntitesOfCurrentMapSelector = createSelector(
       .filter(bioEntity => bioEntity.model === currentModelId);
   },
 );
+
+export const allDrugsBioEntitesOfAllMapsSelector = createSelector(
+  drugsSelector,
+  (drugsState): BioEntity[] => {
+    return (drugsState?.data || [])
+      .map(({ data }) => data || [])
+      .flat()
+      .map(({ targets }) => targets.map(({ targetElements }) => targetElements))
+      .flat()
+      .flat();
+  },
+);
diff --git a/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.constants.ts b/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.constants.ts
index 5a5eb652..2969f8ab 100644
--- a/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.constants.ts
+++ b/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.constants.ts
@@ -15,7 +15,7 @@ const PLUGINS_EVENTS = {
     onCenterChanged: 'onCenterChanged',
     onBioEntityClick: 'onBioEntityClick',
     onPinIconClick: 'onPinIconClick',
-    onSurfaceOverlayClick: 'onSurfaceOverlayClick',
+    onSurfaceClick: 'onSurfaceClick',
   },
   search: {
     onSearch: 'onSearch',
diff --git a/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.ts b/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.ts
index aa7221ab..66d4ab43 100644
--- a/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.ts
+++ b/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.ts
@@ -24,7 +24,7 @@ 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: 'onSurfaceClick', 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 4260f399..a013af76 100644
--- a/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.types.ts
+++ b/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.types.ts
@@ -7,7 +7,7 @@ export type OverlayEvents =
   | 'onRemoveDataOverlay'
   | 'onShowOverlay'
   | 'onHideOverlay'
-  | 'onSurfaceOverlayClick';
+  | 'onSurfaceClick';
 export type SubmapEvents =
   | 'onSubmapOpen'
   | 'onSubmapClose'
-- 
GitLab