From d14aa91dc5e23c9c557850ff506be35640455e98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Or=C5=82=C3=B3w?= <adrian.orlow@fishbrain.com> Date: Wed, 27 Mar 2024 19:58:54 +0100 Subject: [PATCH] feat: add base multisearch implementation --- .../MapViewer/utils/config/getCanvasIcon.ts | 9 +-- .../pinsLayer/getBioEntitiesFeatures.ts | 3 + .../pinsLayer/getBioEntitySingleFeature.ts | 18 +++++- .../utils/config/pinsLayer/getPinStyle.ts | 11 +++- .../config/pinsLayer/useOlMapPinsLayer.ts | 24 ++++++-- .../pinIconClick/useHandlePinIconClick.ts | 38 ++++++++++++ .../utils/listeners/useOlMapListeners.ts | 6 +- src/constants/canvas.ts | 4 +- src/redux/bioEntity/bioEntity.selectors.ts | 61 +++++++++++++++++++ src/redux/chemicals/chemicals.selectors.ts | 42 +++++++++++++ src/redux/drugs/drugs.selectors.ts | 42 +++++++++++++ .../pluginsEventBus.constants.ts | 2 + .../pluginsEventBus/pluginsEventBus.ts | 16 ++++- .../pluginsEventBus/pluginsEventBus.types.ts | 2 + src/types/elements.ts | 4 ++ 15 files changed, 264 insertions(+), 18 deletions(-) create mode 100644 src/components/Map/MapViewer/utils/listeners/pinIconClick/useHandlePinIconClick.ts create mode 100644 src/types/elements.ts diff --git a/src/components/Map/MapViewer/utils/config/getCanvasIcon.ts b/src/components/Map/MapViewer/utils/config/getCanvasIcon.ts index 7d3e6925..6b4b4b29 100644 --- a/src/components/Map/MapViewer/utils/config/getCanvasIcon.ts +++ b/src/components/Map/MapViewer/utils/config/getCanvasIcon.ts @@ -1,4 +1,4 @@ -import { PIN_PATH2D, PIN_SIZE } from '@/constants/canvas'; +import { PIN_PATH2D, PIN_SIZE, TEXT_COLOR } from '@/constants/canvas'; import { HALF, ONE_AND_HALF, QUARTER, THIRD, TWO_AND_HALF } from '@/constants/dividers'; import { DEFAULT_FONT_FAMILY } from '@/constants/font'; import { Point } from '@/types/map'; @@ -12,6 +12,7 @@ const BIG_TEXT_VALUE = 100; interface Args { color: string; value: number; + textColor?: string; } export const drawPinOnCanvas = ( @@ -42,7 +43,7 @@ export const getTextPosition = (textWidth: number, textHeight: number): Point => }); export const drawNumberOnCanvas = ( - { value }: Pick<Args, 'value'>, + { value, textColor }: Pick<Args, 'value' | 'textColor'>, ctx: CanvasRenderingContext2D, ): void => { const text = `${value}`; @@ -53,7 +54,7 @@ export const drawNumberOnCanvas = ( const textHeight = textMetrics.fontBoundingBoxAscent + textMetrics.fontBoundingBoxDescent; const { x, y } = getTextPosition(textWidth, textHeight); - ctx.fillStyle = 'white'; + ctx.fillStyle = textColor || TEXT_COLOR; ctx.textBaseline = 'top'; ctx.font = `${fontSize}px ${DEFAULT_FONT_FAMILY}`; ctx.fillText(text, x, y); @@ -70,7 +71,7 @@ export const getCanvasIcon = ( drawPinOnCanvas(args, ctx); if (args?.value !== undefined) { - drawNumberOnCanvas({ value: args.value }, ctx); + drawNumberOnCanvas({ value: args.value, textColor: args?.textColor }, ctx); } return canvas; diff --git a/src/components/Map/MapViewer/utils/config/pinsLayer/getBioEntitiesFeatures.ts b/src/components/Map/MapViewer/utils/config/pinsLayer/getBioEntitiesFeatures.ts index f45b6ea6..4e11dc14 100644 --- a/src/components/Map/MapViewer/utils/config/pinsLayer/getBioEntitiesFeatures.ts +++ b/src/components/Map/MapViewer/utils/config/pinsLayer/getBioEntitiesFeatures.ts @@ -11,10 +11,12 @@ export const getBioEntitiesFeatures = ( pointToProjection, type, entityNumber, + activeIds, }: { pointToProjection: UsePointToProjectionResult; type: PinType; entityNumber: EntityNumber; + activeIds: (string | number)[]; }, ): Feature[] => { return bioEntites.map(bioEntity => @@ -23,6 +25,7 @@ export const getBioEntitiesFeatures = ( type, // pin's index number value: entityNumber?.[bioEntity.elementId], + isActive: activeIds.includes(bioEntity.id), }), ); }; diff --git a/src/components/Map/MapViewer/utils/config/pinsLayer/getBioEntitySingleFeature.ts b/src/components/Map/MapViewer/utils/config/pinsLayer/getBioEntitySingleFeature.ts index 3290b1a0..cdb44412 100644 --- a/src/components/Map/MapViewer/utils/config/pinsLayer/getBioEntitySingleFeature.ts +++ b/src/components/Map/MapViewer/utils/config/pinsLayer/getBioEntitySingleFeature.ts @@ -1,27 +1,41 @@ -import { PINS_COLORS } from '@/constants/canvas'; +import { PINS_COLORS, TEXT_COLOR } from '@/constants/canvas'; import { BioEntity } from '@/types/models'; import { PinType } from '@/types/pin'; +import { addAlphaToHexString } from '@/utils/convert/addAlphaToHexString'; import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection'; import { Feature } from 'ol'; import { getPinFeature } from './getPinFeature'; import { getPinStyle } from './getPinStyle'; +const INACTIVE_ELEMENT_OPACITY = 0.5; + export const getBioEntitySingleFeature = ( bioEntity: BioEntity, { pointToProjection, type, value, + isActive, }: { pointToProjection: UsePointToProjectionResult; type: PinType; value: number; + isActive: boolean; }, ): Feature => { + const color = isActive + ? PINS_COLORS[type] + : addAlphaToHexString(PINS_COLORS.bioEntity, INACTIVE_ELEMENT_OPACITY); + + const textColor = isActive + ? TEXT_COLOR + : addAlphaToHexString(TEXT_COLOR, INACTIVE_ELEMENT_OPACITY); + const feature = getPinFeature(bioEntity, pointToProjection); const style = getPinStyle({ - color: PINS_COLORS[type], + color, value, + textColor, }); feature.setStyle(style); diff --git a/src/components/Map/MapViewer/utils/config/pinsLayer/getPinStyle.ts b/src/components/Map/MapViewer/utils/config/pinsLayer/getPinStyle.ts index 77ee5c7f..3ee13cb9 100644 --- a/src/components/Map/MapViewer/utils/config/pinsLayer/getPinStyle.ts +++ b/src/components/Map/MapViewer/utils/config/pinsLayer/getPinStyle.ts @@ -4,7 +4,15 @@ import Icon from 'ol/style/Icon'; import Style from 'ol/style/Style'; import { getCanvasIcon } from '../getCanvasIcon'; -export const getPinStyle = ({ value, color }: { value?: number; color: string }): Style => +export const getPinStyle = ({ + value, + color, + textColor, +}: { + value?: number; + color: string; + textColor?: string; +}): Style => new Style({ image: new Icon({ displacement: [ZERO, PIN_SIZE.height], @@ -13,6 +21,7 @@ export const getPinStyle = ({ value, color }: { value?: number; color: string }) img: getCanvasIcon({ color, value, + textColor, }), }), }); diff --git a/src/components/Map/MapViewer/utils/config/pinsLayer/useOlMapPinsLayer.ts b/src/components/Map/MapViewer/utils/config/pinsLayer/useOlMapPinsLayer.ts index b7b05f6c..29e095ba 100644 --- a/src/components/Map/MapViewer/utils/config/pinsLayer/useOlMapPinsLayer.ts +++ b/src/components/Map/MapViewer/utils/config/pinsLayer/useOlMapPinsLayer.ts @@ -1,7 +1,14 @@ /* eslint-disable no-magic-numbers */ -import { searchedBioEntitesSelectorOfCurrentMap } from '@/redux/bioEntity/bioEntity.selectors'; -import { searchedChemicalsBioEntitesOfCurrentMapSelector } from '@/redux/chemicals/chemicals.selectors'; -import { searchedDrugsBioEntitesOfCurrentMapSelector } from '@/redux/drugs/drugs.selectors'; +import { + allBioEntitesSelectorOfCurrentMap, + allVisibleBioEntitiesIdsSelector +} from '@/redux/bioEntity/bioEntity.selectors'; +import { + allChemicalsBioEntitesOfCurrentMapSelector +} from '@/redux/chemicals/chemicals.selectors'; +import { + allDrugsBioEntitesOfCurrentMapSelector +} from '@/redux/drugs/drugs.selectors'; import { entityNumberDataSelector } from '@/redux/entityNumber/entityNumber.selectors'; import { markersPinsOfCurrentMapDataSelector } from '@/redux/markers/markers.selectors'; import { usePointToProjection } from '@/utils/map/usePointToProjection'; @@ -16,9 +23,10 @@ import { getMarkersFeatures } from './getMarkersFeatures'; export const useOlMapPinsLayer = (): VectorLayer<VectorSource<Feature<Geometry>>> => { const pointToProjection = usePointToProjection(); - const contentBioEntites = useSelector(searchedBioEntitesSelectorOfCurrentMap); - const chemicalsBioEntities = useSelector(searchedChemicalsBioEntitesOfCurrentMapSelector); - const drugsBioEntities = useSelector(searchedDrugsBioEntitesOfCurrentMapSelector); + const activeIds = useSelector(allVisibleBioEntitiesIdsSelector); + const contentBioEntites = useSelector(allBioEntitesSelectorOfCurrentMap); + const chemicalsBioEntities = useSelector(allChemicalsBioEntitesOfCurrentMapSelector); + const drugsBioEntities = useSelector(allDrugsBioEntitesOfCurrentMapSelector); const markersEntities = useSelector(markersPinsOfCurrentMapDataSelector); const entityNumber = useSelector(entityNumberDataSelector); @@ -29,16 +37,19 @@ export const useOlMapPinsLayer = (): VectorLayer<VectorSource<Feature<Geometry>> pointToProjection, type: 'bioEntity', entityNumber, + activeIds, }), getBioEntitiesFeatures(chemicalsBioEntities, { pointToProjection, type: 'chemicals', entityNumber, + activeIds, }), getBioEntitiesFeatures(drugsBioEntities, { pointToProjection, type: 'drugs', entityNumber, + activeIds, }), getMarkersFeatures(markersEntities, { pointToProjection }), ].flat(), @@ -49,6 +60,7 @@ export const useOlMapPinsLayer = (): VectorLayer<VectorSource<Feature<Geometry>> pointToProjection, markersEntities, entityNumber, + activeIds, ], ); diff --git a/src/components/Map/MapViewer/utils/listeners/pinIconClick/useHandlePinIconClick.ts b/src/components/Map/MapViewer/utils/listeners/pinIconClick/useHandlePinIconClick.ts new file mode 100644 index 00000000..b7be9f91 --- /dev/null +++ b/src/components/Map/MapViewer/utils/listeners/pinIconClick/useHandlePinIconClick.ts @@ -0,0 +1,38 @@ +import { allBioEntitiesElementsIdsSelector } from '@/redux/bioEntity/bioEntity.selectors'; +import { currentSelectedSearchElement } from '@/redux/drawer/drawer.selectors'; +import { selectTab } from '@/redux/drawer/drawer.slice'; +import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; +import { useAppSelector } from '@/redux/hooks/useAppSelector'; +import { PluginsEventBus as EventBus } from '@/services/pluginsManager/pluginsEventBus'; +import { ClickedPinIcon } from '@/services/pluginsManager/pluginsEventBus/pluginsEventBus.types'; +import isUUID from 'is-uuid'; +import { useCallback, useEffect } from 'react'; + +export const useHandlePinIconClick = (): void => { + const dispatch = useAppDispatch(); + const currentTab = useAppSelector(currentSelectedSearchElement); + const idsTabs = useAppSelector(allBioEntitiesElementsIdsSelector); + + const onPinIconClick = useCallback( + ({ id }: ClickedPinIcon): void => { + const newTab = idsTabs[id]; + const isTabAlreadySelected = newTab === currentTab; + const isMarker = isUUID.anyNonNil(`${id}`); + + if (!newTab || isTabAlreadySelected || isMarker) { + return; + } + + dispatch(selectTab(idsTabs[id])); + }, + [idsTabs, dispatch, currentTab], + ); + + useEffect(() => { + EventBus.addLocalListener('onPinIconClick', onPinIconClick); + + return () => { + EventBus.removeLocalListener('onPinIconClick', onPinIconClick); + }; + }, [onPinIconClick]); +}; diff --git a/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.ts b/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.ts index 86e88297..187fc502 100644 --- a/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.ts +++ b/src/components/Map/MapViewer/utils/listeners/useOlMapListeners.ts @@ -1,4 +1,6 @@ import { DEFAULT_ZOOM, OPTIONS } from '@/constants/map'; +import { searchDistanceValSelector } from '@/redux/configuration/configuration.selectors'; +import { resultDrawerOpen } from '@/redux/drawer/drawer.selectors'; import { useAppDispatch } from '@/redux/hooks/useAppDispatch'; import { mapDataLastZoomValue, @@ -14,12 +16,11 @@ import { Pixel } from 'ol/pixel'; import { useEffect, useRef } from 'react'; import { useSelector } from 'react-redux'; import { useDebouncedCallback } from 'use-debounce'; -import { searchDistanceValSelector } from '@/redux/configuration/configuration.selectors'; -import { resultDrawerOpen } from '@/redux/drawer/drawer.selectors'; import { onMapRightClick } from './mapRightClick/onMapRightClick'; import { onMapSingleClick } from './mapSingleClick/onMapSingleClick'; import { onMapPositionChange } from './onMapPositionChange'; import { onPointerMove } from './onPointerMove'; +import { useHandlePinIconClick } from './pinIconClick/useHandlePinIconClick'; interface UseOlMapListenersInput { view: View; @@ -36,6 +37,7 @@ export const useOlMapListeners = ({ view, mapInstance }: UseOlMapListenersInput) const coordinate = useRef<Coordinate>([]); const pixel = useRef<Pixel>([]); const dispatch = useAppDispatch(); + useHandlePinIconClick(modelId); const handleRightClick = useDebouncedCallback( onMapRightClick(mapSize, modelId, dispatch), diff --git a/src/constants/canvas.ts b/src/constants/canvas.ts index 0bcb2708..00eeeff6 100644 --- a/src/constants/canvas.ts +++ b/src/constants/canvas.ts @@ -11,7 +11,7 @@ export const PIN_SIZE = { export const PINS_COLORS: Record<PinType, string> = { drugs: '#F48C41', - chemicals: '#640CE3', + chemicals: '#008325', bioEntity: '#106AD7', }; @@ -22,4 +22,6 @@ export const PINS_COLOR_WITH_NONE: Record<PinTypeWithNone, string> = { export const LINE_COLOR = '#00AAFF'; +export const TEXT_COLOR = '#FFFFFF'; + export const LINE_WIDTH = 6; diff --git a/src/redux/bioEntity/bioEntity.selectors.ts b/src/redux/bioEntity/bioEntity.selectors.ts index 8bf9877d..f3e8c2d2 100644 --- a/src/redux/bioEntity/bioEntity.selectors.ts +++ b/src/redux/bioEntity/bioEntity.selectors.ts @@ -1,10 +1,12 @@ import { SIZE_OF_EMPTY_ARRAY } from '@/constants/common'; import { rootSelector } from '@/redux/root/root.selectors'; +import { ElementIdTabObj } from '@/types/elements'; import { MultiSearchData } from '@/types/fetchDataState'; import { BioEntity, BioEntityContent, MapModel } from '@/types/models'; import { createSelector } from '@reduxjs/toolkit'; import { allChemicalsBioEntitesOfAllMapsSelector, + allChemicalsIdTabSelectorOfCurrentMap, searchedChemicalsBioEntitesOfCurrentMapSelector, } from '../chemicals/chemicals.selectors'; import { currentSelectedBioEntityIdSelector } from '../contextMenu/contextMenu.selector'; @@ -14,6 +16,7 @@ import { } from '../drawer/drawer.selectors'; import { allDrugsBioEntitesOfAllMapsSelector, + allDrugsIdTabSelectorOfCurrentMap, searchedDrugsBioEntitesOfCurrentMapSelector, } from '../drugs/drugs.selectors'; import { currentModelIdSelector, modelsDataSelector } from '../models/models.selectors'; @@ -92,6 +95,44 @@ export const searchedBioEntitesSelectorOfCurrentMap = createSelector( }, ); +export const allBioEntitesSelectorOfCurrentMap = createSelector( + bioEntitySelector, + currentModelIdSelector, + (bioEntities, currentModelId): BioEntity[] => { + if (!bioEntities) { + return []; + } + + return (bioEntities?.data || []) + .map(({ data }) => data || []) + .flat() + .filter(({ bioEntity }) => bioEntity.model === currentModelId) + .map(({ bioEntity }) => bioEntity); + }, +); + +export const allBioEntitesIdTabSelectorOfCurrentMap = createSelector( + bioEntitySelector, + currentModelIdSelector, + (bioEntities, currentModelId): ElementIdTabObj => { + if (!bioEntities) { + return {}; + } + + return Object.fromEntries( + (bioEntities?.data || []) + .map(({ data, searchQueryElement }): [typeof data, string] => [data, searchQueryElement]) + .map(([data, tab]) => + (data || []) + .flat() + .filter(({ bioEntity }) => bioEntity.model === currentModelId) + .map(d => [d.bioEntity.id, tab]), + ) + .flat(), + ); + }, +); + export const numberOfBioEntitiesSelector = createSelector( bioEntitiesForSelectedSearchElement, state => (state?.data ? state.data.length : SIZE_OF_EMPTY_ARRAY), @@ -129,6 +170,13 @@ export const allVisibleBioEntitiesSelector = createSelector( }, ); +export const allVisibleBioEntitiesIdsSelector = createSelector( + allVisibleBioEntitiesSelector, + (elements): (string | number)[] => { + return elements.map(e => e.id); + }, +); + export const allContentBioEntitesSelectorOfAllMaps = createSelector( bioEntitySelector, (bioEntities): BioEntity[] => { @@ -152,6 +200,19 @@ export const allBioEntitiesSelector = createSelector( }, ); +export const allBioEntitiesElementsIdsSelector = createSelector( + allBioEntitesIdTabSelectorOfCurrentMap, + allChemicalsIdTabSelectorOfCurrentMap, + allDrugsIdTabSelectorOfCurrentMap, + (content, chemicals, drugs): ElementIdTabObj => { + return { + ...content, + ...chemicals, + ...drugs, + }; + }, +); + export const currentDrawerBioEntitySelector = createSelector( allBioEntitiesSelector, currentSearchedBioEntityId, diff --git a/src/redux/chemicals/chemicals.selectors.ts b/src/redux/chemicals/chemicals.selectors.ts index bb8d4aee..d8394dc4 100644 --- a/src/redux/chemicals/chemicals.selectors.ts +++ b/src/redux/chemicals/chemicals.selectors.ts @@ -1,5 +1,6 @@ import { SIZE_OF_EMPTY_ARRAY } from '@/constants/common'; import { rootSelector } from '@/redux/root/root.selectors'; +import { ElementId, ElementIdTabObj, Tab } from '@/types/elements'; import { MultiSearchData } from '@/types/fetchDataState'; import { BioEntity, Chemical } from '@/types/models'; import { createSelector } from '@reduxjs/toolkit'; @@ -41,6 +42,47 @@ export const searchedChemicalsBioEntitesOfCurrentMapSelector = createSelector( }, ); +export const allChemicalsBioEntitesOfCurrentMapSelector = createSelector( + chemicalsSelector, + currentModelIdSelector, + (chemicalsState, currentModelId): BioEntity[] => { + return (chemicalsState?.data || []) + .map(({ data }) => data || []) + .flat() + .map(({ targets }) => targets.map(({ targetElements }) => targetElements)) + .flat() + .flat() + .filter(bioEntity => bioEntity.model === currentModelId); + }, +); + +export const allChemicalsIdTabSelectorOfCurrentMap = createSelector( + chemicalsSelector, + currentModelIdSelector, + (chemicalsState, currentModelId): ElementIdTabObj => { + if (!chemicalsState) { + return {}; + } + + return Object.fromEntries( + (chemicalsState?.data || []) + .map(({ data, searchQueryElement }): [typeof data, string] => [data, searchQueryElement]) + .map(([data, tab]) => + (data || []).map(({ targets }): [ElementId, Tab][] => + targets + .map(({ targetElements }) => targetElements) + .flat() + .flat() + .filter(bioEntity => bioEntity.model === currentModelId) + .map(bioEntity => [bioEntity.id, tab]), + ), + ) + .flat() + .flat(), + ); + }, +); + export const allChemicalsBioEntitesOfAllMapsSelector = createSelector( chemicalsSelector, (chemicalsState): BioEntity[] => { diff --git a/src/redux/drugs/drugs.selectors.ts b/src/redux/drugs/drugs.selectors.ts index f5c74de2..df2b10ae 100644 --- a/src/redux/drugs/drugs.selectors.ts +++ b/src/redux/drugs/drugs.selectors.ts @@ -1,5 +1,6 @@ import { SIZE_OF_EMPTY_ARRAY } from '@/constants/common'; import { rootSelector } from '@/redux/root/root.selectors'; +import { ElementId, ElementIdTabObj, Tab } from '@/types/elements'; import { MultiSearchData } from '@/types/fetchDataState'; import { BioEntity, Drug } from '@/types/models'; import { createSelector } from '@reduxjs/toolkit'; @@ -55,6 +56,47 @@ export const searchedDrugsBioEntitesOfCurrentMapSelector = createSelector( }, ); +export const allDrugsBioEntitesOfCurrentMapSelector = createSelector( + drugsSelector, + currentModelIdSelector, + (drugsState, currentModelId): BioEntity[] => { + return (drugsState?.data || []) + .map(({ data }) => data || []) + .flat() + .map(({ targets }) => targets.map(({ targetElements }) => targetElements)) + .flat() + .flat() + .filter(bioEntity => bioEntity.model === currentModelId); + }, +); + +export const allDrugsIdTabSelectorOfCurrentMap = createSelector( + drugsSelector, + currentModelIdSelector, + (drugsState, currentModelId): ElementIdTabObj => { + if (!drugsState) { + return {}; + } + + return Object.fromEntries( + (drugsState?.data || []) + .map(({ data, searchQueryElement }): [typeof data, string] => [data, searchQueryElement]) + .map(([data, tab]) => + (data || []).map(({ targets }): [ElementId, Tab][] => + targets + .map(({ targetElements }) => targetElements) + .flat() + .flat() + .filter(bioEntity => bioEntity.model === currentModelId) + .map(bioEntity => [bioEntity.id, tab]), + ), + ) + .flat() + .flat(), + ); + }, +); + export const allDrugsBioEntitesOfAllMapsSelector = createSelector( drugsSelector, (drugsState): BioEntity[] => { diff --git a/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.constants.ts b/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.constants.ts index e3491132..54310481 100644 --- a/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.constants.ts +++ b/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.constants.ts @@ -30,3 +30,5 @@ export const ALLOWED_PLUGINS_EVENTS = Object.values(PLUGINS_EVENTS).flatMap(obj ); export const LISTENER_NOT_FOUND = -1; + +export const LOCAL_LISTENER_ID = 'local'; diff --git a/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.ts b/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.ts index 9ff0bbce..5ce80301 100644 --- a/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.ts +++ b/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.ts @@ -1,6 +1,12 @@ /* eslint-disable no-magic-numbers */ import { CreatedOverlay, MapOverlay } from '@/types/models'; import { showToast } from '@/utils/showToast'; +import { ERROR_INVALID_EVENT_TYPE, ERROR_PLUGIN_CRASH } from '../errorMessages'; +import { + ALLOWED_PLUGINS_EVENTS, + LISTENER_NOT_FOUND, + LOCAL_LISTENER_ID, +} from './pluginsEventBus.constants'; import type { CenteredCoordinates, ClickedBioEntity, @@ -13,8 +19,6 @@ import type { SearchData, ZoomChanged, } from './pluginsEventBus.types'; -import { ALLOWED_PLUGINS_EVENTS, LISTENER_NOT_FOUND } from './pluginsEventBus.constants'; -import { ERROR_INVALID_EVENT_TYPE, ERROR_PLUGIN_CRASH } from '../errorMessages'; export function dispatchEvent(type: 'onPluginUnload', data: PluginUnloaded): void; export function dispatchEvent(type: 'onAddDataOverlay', createdOverlay: CreatedOverlay): void; @@ -67,6 +71,10 @@ export const PluginsEventBus: PluginsEventBusType = { }); }, + addLocalListener: (type: Events, callback: (data: unknown) => void) => { + PluginsEventBus.addListener(LOCAL_LISTENER_ID, LOCAL_LISTENER_ID, type, callback); + }, + removeListener: (hash: string, type: Events, callback: unknown) => { const eventIndex = PluginsEventBus.events.findIndex( event => event.hash === hash && event.type === type && event.callback === callback, @@ -79,6 +87,10 @@ export const PluginsEventBus: PluginsEventBusType = { } }, + removeLocalListener: (type: Events, callback: unknown) => { + PluginsEventBus.removeListener(LOCAL_LISTENER_ID, type, callback); + }, + removeAllListeners: (hash: string) => { PluginsEventBus.events = PluginsEventBus.events.filter(event => event.hash !== hash); }, diff --git a/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.types.ts b/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.types.ts index fa6d70e4..ff61d186 100644 --- a/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.types.ts +++ b/src/services/pluginsManager/pluginsEventBus/pluginsEventBus.types.ts @@ -102,7 +102,9 @@ export type PluginsEventBusType = { type: Events, callback: (data: unknown) => void, ) => void; + addLocalListener: <T>(type: Events, callback: (data: T) => void) => void; removeListener: (hash: string, type: Events, callback: unknown) => void; + removeLocalListener: <T>(type: Events, callback: T) => void; removeAllListeners: (hash: string) => void; dispatchEvent: typeof dispatchEvent; }; diff --git a/src/types/elements.ts b/src/types/elements.ts new file mode 100644 index 00000000..0bcc0967 --- /dev/null +++ b/src/types/elements.ts @@ -0,0 +1,4 @@ +export type ElementId = string | number; +export type Tab = string; +export type ElementIdTab = [ElementId, Tab]; +export type ElementIdTabObj = Record<ElementId, Tab>; -- GitLab