diff --git a/docs/plugins/events.md b/docs/plugins/events.md index 7ad4cf647dc7cab321177ea49654401480f61fcd..63072dc23baf38719a6e45a4362f1c1d1db1b263 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 016ad6996bbef60abdeef8fe404277e991e7dca9..a1966db0917e65b4f510518c19e2655f98633fc9 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 f3a8ec09f2e124b622df2947627ea0a7576d99a3..f44f22efed3236434a8bad77d7b52e0245284733 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 2ee7eb5b5a6eb3e445c572fe5fa593062bd482a2..03c3f49587c5fb3ea0f8a6fb4bfd58549f77ee26 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 f79764b2ab2d992e9ad3ca2cdd8aa5feb9ec9d5c..a7daf8437d747dce8f28e45f30268d6b8a3e3505 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 b294d492153d7f74aea80a7836f30c0557032ba5..e11025d81b6401b8b26fc0879d8b51d255a1e034 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 cef983542a7777c57a8c551ed6e9d96aa1dcb35c..8a051f2f9fda0e7b21c601e40e1b9f6c96309831 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 93e24ec85be8ac6dbab4bcfcf307448311c16fc0..107add31699aceb508557e10d9b54420c4d5171a 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 28610fd818aa15ae7b6b9dc89fec3ab2e9042786..80af09f63b9e14b963a21b64de18e09c396451c8 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 31d33f86365ee5667a43a2a9d6af7489fc06f1f2..51d4f8d363697addadc40b00dae775744530ac0d 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 2ab3ea05ef07487fb13dfa07e88ccb4f728ee1fb..0000000000000000000000000000000000000000 --- 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 3d66ff05ac02bda8f981efb70e7cba7f813814d5..a957771e4eb8f3cd53824aab5de92347eb96af7f 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 b14875831efa1192261f2f9dc7f3099a87c41a03..f3ead6cc00da0d5e7cff55d9f8d095a662dad5a0 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 0000000000000000000000000000000000000000..3c1e5923d2290df828598a4b46d8c175979f020c --- /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 4661b41ae55f019c422500a163b33213bfb5ba58..3d1e425c2e5a8d894df37ecc3f04eceee865ead5 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 0000000000000000000000000000000000000000..47f997756b46779ed2cc1b54ae27d02a76c45692 --- /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 1c0b9fcf413876cbff92bcd408a9564b0756b3ea..7ca778b5965bafa6ac38cae39e887afbb5cc6c21 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 0000000000000000000000000000000000000000..4995bbcf07ff0173cb4c2760bf56874b1b9b9b1c --- /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 b9566eb75fbac7db3ed74d8ccdfdda56bd607adb..8bf9877d86bb70a7665c50a5f400895b585baaae 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 03f829f8de425665f9f0a4f6d15d94fd37fee2b8..bb8d4aee3e28ffe7c65490e541e04a96ffdee38d 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 3a72aa534ed977ae551c3a289ae05c4a29a3582a..29333ec2c70a27cb8088e55cee67a010d4d27b0f 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 a348517b0e6beea005555e28e870734205a4a090..a0d5198979607da4cd56dd22cfd129a5c3517418 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 45e9c16c828d894e31b3b5b3faf04a9fdd0981dc..f5c74de2ffeaf4b06ef5a34bbf8ae899e276e717 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 5a5eb6520a5ff713805d90e82fa4c997b5710629..2969f8ab7be3e86ef9f44f871c4eace00d0e97a4 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 aa7221ab01b36e9a3455c2e65f34631aaf423098..66d4ab43fd2d6dd73422f714ada6c8a897592d0e 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 4260f3994f3b0fdbb8c44fb81c8e749429db409a..a013af76713f4ee1b74d989544df7cca74e15ec7 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'