From 523467404b15eb29429d16945bbcf55c2135a90f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Or=C5=82=C3=B3w?= <adrian.orlow@fishbrain.com> Date: Thu, 29 Feb 2024 19:36:02 +0100 Subject: [PATCH] feat: add plugin data bio entities --- docs/data/bioentities.md | 116 +++ docs/types/BioEntity.md | 623 +++++++++++++++ docs/types/Chemical.md | 744 ++++++++++++++++++ docs/types/Drug.md | 725 +++++++++++++++++ docs/types/Marker.md | 12 + index.d.ts | 3 + package-lock.json | 79 +- package.json | 7 +- .../OverlayData/OverlayData.types.ts | 2 +- .../BioEntitiesAccordion.component.test.tsx | 4 +- .../ResultsList/PinsList/PinsList.types.tsx | 2 +- .../MapViewer/utils/config/getCanvasIcon.ts | 8 +- ...rseSurfaceMarkersToBioEntityRender.test.ts | 96 +++ .../parseSurfaceMarkersToBioEntityRender.ts | 24 + .../overlaysLayer/useOverlayFeatures.test.ts | 20 +- .../overlaysLayer/useOverlayFeatures.ts | 30 +- .../pinsLayer/getMarkerSingleFeature.test.ts | 58 ++ .../pinsLayer/getMarkerSingleFeature.ts | 24 + .../config/pinsLayer/getMarkersFeatures.ts | 19 + .../config/pinsLayer/getPinFeature.test.ts | 15 + .../utils/config/pinsLayer/getPinFeature.ts | 8 +- .../utils/config/pinsLayer/getPinStyle.ts | 2 +- .../config/pinsLayer/useOlMapPinsLayer.ts | 12 +- src/models/bioEntitySchema.ts | 2 +- src/redux/bioEntity/bioEntity.reducers.ts | 7 +- src/redux/bioEntity/bioEntity.selectors.ts | 6 + src/redux/bioEntity/bioEntity.slice.ts | 7 +- src/redux/chemicals/chemicals.selectors.ts | 6 + src/redux/contextMenu/contextMenu.types.ts | 2 +- src/redux/drawer/drawer.types.ts | 4 +- src/redux/drugs/drugs.selectors.ts | 6 + src/redux/markers/markers.constants.ts | 5 + src/redux/markers/markers.mock.ts | 5 + src/redux/markers/markers.reducers.ts | 31 + src/redux/markers/markers.selectors.ts | 33 + src/redux/markers/markers.slice.ts | 23 + src/redux/markers/markers.types.ts | 30 + src/redux/markers/markers.utils.ts | 4 + src/redux/models/marker.mock.ts | 25 + src/redux/root/root.fixtures.ts | 8 +- src/redux/store.ts | 6 +- .../bioEntities/addSingleMarker.ts | 12 + .../bioEntities/clearAllElements.ts | 27 + .../bioEntities/getAllChemicals.ts | 10 + .../bioEntities/getAllContent.ts | 10 + .../pluginsManager/bioEntities/getAllDrugs.ts | 10 + .../bioEntities/getAllMarkers.ts | 10 + .../bioEntities/getShownElements.ts | 22 + .../bioEntities/getShownElements.types.ts | 9 + .../pluginsManager/bioEntities/index.ts | 23 + .../bioEntities/removeAllMarkers.ts | 7 + .../bioEntities/removeSingleMarker.ts | 7 + src/services/pluginsManager/pluginsManager.ts | 4 + src/types/OLrendering.ts | 3 +- 54 files changed, 2955 insertions(+), 42 deletions(-) create mode 100644 docs/data/bioentities.md create mode 100644 docs/types/BioEntity.md create mode 100644 docs/types/Chemical.md create mode 100644 docs/types/Drug.md create mode 100644 docs/types/Marker.md create mode 100644 src/components/Map/MapViewer/utils/config/overlaysLayer/parseSurfaceMarkersToBioEntityRender.test.ts create mode 100644 src/components/Map/MapViewer/utils/config/overlaysLayer/parseSurfaceMarkersToBioEntityRender.ts create mode 100644 src/components/Map/MapViewer/utils/config/pinsLayer/getMarkerSingleFeature.test.ts create mode 100644 src/components/Map/MapViewer/utils/config/pinsLayer/getMarkerSingleFeature.ts create mode 100644 src/components/Map/MapViewer/utils/config/pinsLayer/getMarkersFeatures.ts create mode 100644 src/redux/markers/markers.constants.ts create mode 100644 src/redux/markers/markers.mock.ts create mode 100644 src/redux/markers/markers.reducers.ts create mode 100644 src/redux/markers/markers.selectors.ts create mode 100644 src/redux/markers/markers.slice.ts create mode 100644 src/redux/markers/markers.types.ts create mode 100644 src/redux/markers/markers.utils.ts create mode 100644 src/redux/models/marker.mock.ts create mode 100644 src/services/pluginsManager/bioEntities/addSingleMarker.ts create mode 100644 src/services/pluginsManager/bioEntities/clearAllElements.ts create mode 100644 src/services/pluginsManager/bioEntities/getAllChemicals.ts create mode 100644 src/services/pluginsManager/bioEntities/getAllContent.ts create mode 100644 src/services/pluginsManager/bioEntities/getAllDrugs.ts create mode 100644 src/services/pluginsManager/bioEntities/getAllMarkers.ts create mode 100644 src/services/pluginsManager/bioEntities/getShownElements.ts create mode 100644 src/services/pluginsManager/bioEntities/getShownElements.types.ts create mode 100644 src/services/pluginsManager/bioEntities/index.ts create mode 100644 src/services/pluginsManager/bioEntities/removeAllMarkers.ts create mode 100644 src/services/pluginsManager/bioEntities/removeSingleMarker.ts diff --git a/docs/data/bioentities.md b/docs/data/bioentities.md new file mode 100644 index 00000000..e17f46d1 --- /dev/null +++ b/docs/data/bioentities.md @@ -0,0 +1,116 @@ +### Data / BioEntities + +The methods contained within 'Data / BioEntities' are used to access/modify data on content/drugs/chemicals entities, as well as pin and surface markers. + +Below is a description of the methods, as well as the types they return. A description of the object types can be found in folder `/docs/types/`. + +**Available data access methods include:** + +- `getAllChemicals` + - gets list of searched chemicals + - returns array of `Chemical` +- `getAllContent` + - gets list of searched content + - returns array of `BioEntity` +- `getAllDrugs` + - gets list of searched drugs + - returns array of `Drug` +- `getAllMarkers` + - gets list of added markers + - returns array of `Marker` +- `getShownElements` + - gets list of all currently shown content/chemicals/drugs bioentities + markers + - returns object of + ``` + { + content: BioEntity[] + drugs: BioEntity[] + chemicals: BioEntity[] + markers: Marker[] + } + ``` + +**Available data modify methods include:** + +##### `clearAllElements` + +- accepts single argument + - array of `string` which includes one or more of: `'drugs'`, `'chemicals'`, `'content'`, `'marker'` +- clears **all** elements of provided type(s) +- returns nothing +- example: + ```ts + window.minerva.data.bioEntities.clearAllElements(['drugs', 'chemicals']); + ``` + +##### `addSingleMarker` + +- accepts single argument of object below + - **object:** + ``` + { + type: 'pin' OR 'surface' + id: string [optional] + color: string + opacity: number + x: number + y: number + width: number [optional] + height: number [optional] + number: number [optional] + modelId: number [optional] + } + ``` + - **id** - optional, if not provided uuidv4 is generated + - **color** - should be provided in hex format with hash (example: `#FF0000`) + - **opacity** - should be a float between `0` and `1` (example: `0.54`) + - **x** - x coord on the map + - **y** - y coord on the map + - **width** - width of surface [surface marker only] + - **height** - width of height [surface marker only] + - **number** - number presented on the pin [pin marker only] + - **modelId** - if marker should be visible only on single map, modelId should be provided +- adds one marker to markers list +- returns created `Marker` +- examples: + ```ts + window.minerva.data.bioEntities.addSingleMarker({ + type: 'surface', + color: '#106AD7', + opacity: 0.67, + x: 4438, + y: 1124, + width: 200, + height: 300, + modelId: 52, + }); + ``` + ```ts + window.minerva.data.bioEntities.addSingleMarker({ + type: 'pin', + color: '#106AD7', + opacity: 1, + x: 8723, + y: 4322, + number: 43, + }); + ``` + +##### `removeSingleMarker` + +- accepts single argument of `string` which represents marker `id` +- removes one marker from markers list +- returns nothing +- example: + ```ts + window.minerva.data.bioEntities.removeSingleMarker('f4c068d1-9695-4f1b-928c-d0cff92164b2'); + ``` + +##### `removeAllMarkers` + +- removes all markers from markers list +- returns nothing +- example: + ```ts + window.minerva.data.bioEntities.removeAllMarkers(); + ``` diff --git a/docs/types/BioEntity.md b/docs/types/BioEntity.md new file mode 100644 index 00000000..faba904a --- /dev/null +++ b/docs/types/BioEntity.md @@ -0,0 +1,623 @@ +```json +{ + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "stringType": { + "type": "string" + }, + "name": { + "type": "string" + }, + "elementId": { + "type": "string" + }, + "model": { + "type": "number" + }, + "references": { + "type": "array", + "items": { + "type": "object", + "properties": { + "link": { + "anyOf": [ + { + "type": "string", + "format": "uri" + }, + { + "type": "null" + } + ] + }, + "article": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "authors": { + "type": "array", + "items": { + "type": "string" + } + }, + "journal": { + "type": "string" + }, + "year": { + "type": "number" + }, + "link": { + "type": "string" + }, + "pubmedId": { + "type": "string" + }, + "citationCount": { + "type": "number" + } + }, + "required": [ + "title", + "authors", + "journal", + "year", + "link", + "pubmedId", + "citationCount" + ], + "additionalProperties": false + }, + "type": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "id": { + "type": "number" + }, + "annotatorClassName": { + "type": "string" + } + }, + "required": ["link", "type", "resource", "id", "annotatorClassName"], + "additionalProperties": false + } + }, + "z": { + "type": "number" + }, + "notes": { + "type": "string" + }, + "symbol": { + "type": ["string", "null"] + }, + "homodimer": { + "type": "number" + }, + "nameX": { + "type": "number" + }, + "nameY": { + "type": "number" + }, + "nameWidth": { + "type": "number" + }, + "nameHeight": { + "type": "number" + }, + "nameVerticalAlign": { + "type": "string" + }, + "nameHorizontalAlign": { + "type": "string" + }, + "width": { + "type": "number" + }, + "height": { + "type": "number" + }, + "visibilityLevel": { + "type": "string" + }, + "transparencyLevel": { + "type": "string" + }, + "synonyms": { + "type": "array", + "items": { + "type": "string" + } + }, + "formerSymbols": { + "type": "array", + "items": { + "type": "string" + } + }, + "fullName": { + "type": ["string", "null"] + }, + "compartmentName": { + "type": ["string", "null"] + }, + "abbreviation": { + "type": ["string", "null"] + }, + "formula": { + "type": ["string", "null"] + }, + "glyph": { + "anyOf": [ + { + "type": "object", + "properties": { + "file": { + "type": "number" + } + }, + "required": ["file"], + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "activity": { + "type": "boolean" + }, + "structuralState": { + "anyOf": [ + { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "position": { + "type": "object", + "properties": { + "x": { + "type": "number" + }, + "y": { + "type": "number" + } + }, + "required": ["x", "y"], + "additionalProperties": false + }, + "z": { + "type": "number" + }, + "width": { + "type": "number" + }, + "height": { + "type": "number" + }, + "fontSize": { + "type": "number" + }, + "size": { + "type": "number" + }, + "center": { + "$ref": "#/properties/structuralState/anyOf/0/properties/position" + }, + "borderColor": { + "type": "object", + "properties": { + "alpha": { + "type": "number" + }, + "rgb": { + "type": "number" + } + }, + "required": ["alpha", "rgb"], + "additionalProperties": false + }, + "elementId": { + "type": "string" + } + }, + "required": [ + "value", + "position", + "z", + "width", + "height", + "fontSize", + "size", + "center", + "borderColor", + "elementId" + ], + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "hypothetical": { + "type": ["boolean", "null"] + }, + "boundaryCondition": { + "type": "boolean" + }, + "constant": { + "type": "boolean" + }, + "initialAmount": { + "type": ["number", "null"] + }, + "initialConcentration": { + "type": ["number", "null"] + }, + "charge": { + "type": ["number", "null"] + }, + "substanceUnits": { + "type": ["string", "null"] + }, + "onlySubstanceUnits": { + "type": "boolean" + }, + "modificationResidues": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "idModificationResidue": { + "type": "string" + }, + "name": { + "type": "string" + }, + "position": { + "$ref": "#/properties/structuralState/anyOf/0/properties/position" + }, + "z": { + "type": "number" + }, + "borderColor": { + "$ref": "#/properties/structuralState/anyOf/0/properties/borderColor" + }, + "state": { + "anyOf": [ + { + "type": ["string", "number"] + }, + { + "type": "null" + } + ] + }, + "size": { + "type": "number" + }, + "elementId": { + "type": "string" + } + }, + "required": [ + "id", + "idModificationResidue", + "name", + "z", + "borderColor", + "size", + "elementId" + ], + "additionalProperties": false + } + }, + "complex": { + "type": ["number", "null"] + }, + "compartment": { + "type": ["number", "null"] + }, + "submodel": { + "anyOf": [ + { + "type": "object", + "properties": { + "mapId": { + "type": "number" + }, + "type": { + "type": "string" + } + }, + "required": ["mapId", "type"], + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "x": { + "type": "number" + }, + "y": { + "type": "number" + }, + "lineWidth": { + "type": "number" + }, + "fontColor": { + "$ref": "#/properties/structuralState/anyOf/0/properties/borderColor" + }, + "fontSize": { + "type": "number" + }, + "fillColor": { + "$ref": "#/properties/structuralState/anyOf/0/properties/borderColor" + }, + "borderColor": { + "$ref": "#/properties/structuralState/anyOf/0/properties/borderColor" + }, + "smiles": { + "anyOf": [ + { + "anyOf": [ + { + "not": {} + }, + { + "type": "string" + } + ] + }, + { + "type": "null" + } + ] + }, + "inChI": { + "type": ["string", "null"] + }, + "inChIKey": { + "type": ["string", "null"] + }, + "thickness": { + "type": "number" + }, + "outerWidth": { + "type": "number" + }, + "innerWidth": { + "type": "number" + }, + "idReaction": { + "type": "string" + }, + "reversible": { + "type": "boolean" + }, + "mechanicalConfidenceScore": { + "type": "boolean" + }, + "lowerBound": { + "type": "boolean" + }, + "upperBound": { + "type": "boolean" + }, + "subsystem": { + "type": "string" + }, + "geneProteinReaction": { + "type": "string" + }, + "kinetics": { + "type": "null" + }, + "products": { + "type": "array", + "items": { + "type": "object", + "properties": { + "aliasId": { + "type": "number" + }, + "stoichiometry": { + "type": ["number", "null"] + }, + "type": { + "type": "string" + } + }, + "required": ["aliasId", "stoichiometry"], + "additionalProperties": false + } + }, + "reactants": { + "type": "array", + "items": { + "$ref": "#/properties/products/items" + } + }, + "modifiers": { + "type": "array", + "items": { + "$ref": "#/properties/products/items" + } + }, + "processCoordinates": { + "type": "null" + }, + "line": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "width": { + "type": "number" + }, + "color": { + "$ref": "#/properties/structuralState/anyOf/0/properties/borderColor" + }, + "z": { + "type": "number" + }, + "segments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "x1": { + "type": "number" + }, + "y1": { + "type": "number" + }, + "x2": { + "type": "number" + }, + "y2": { + "type": "number" + } + }, + "required": ["x1", "y1", "x2", "y2"], + "additionalProperties": false + } + }, + "startArrow": { + "type": "object", + "properties": { + "arrowType": { + "type": "string" + }, + "angle": { + "type": "number" + }, + "lineType": { + "type": "string" + }, + "length": { + "type": "number" + } + }, + "required": ["arrowType", "angle", "lineType", "length"], + "additionalProperties": false + }, + "endArrow": { + "$ref": "#/properties/line/properties/startArrow" + }, + "lineType": { + "type": "string" + } + }, + "required": ["id", "width", "color", "z", "segments", "startArrow", "endArrow", "lineType"], + "additionalProperties": false + }, + "operators": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "line": { + "$ref": "#/properties/line" + }, + "inputs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + } + }, + "required": ["id"], + "additionalProperties": false + } + }, + "outputs": { + "not": {} + }, + "operatorText": { + "type": "string" + }, + "reactantOperator": { + "type": "boolean" + }, + "productOperator": { + "type": "boolean" + }, + "modifierOperator": { + "type": "boolean" + } + }, + "required": [ + "id", + "line", + "inputs", + "operatorText", + "reactantOperator", + "productOperator", + "modifierOperator" + ], + "additionalProperties": false + } + } + }, + "required": [ + "id", + "stringType", + "name", + "elementId", + "model", + "references", + "z", + "notes", + "symbol", + "nameX", + "nameY", + "nameWidth", + "nameHeight", + "nameVerticalAlign", + "nameHorizontalAlign", + "width", + "height", + "visibilityLevel", + "transparencyLevel", + "synonyms", + "formerSymbols", + "fullName", + "abbreviation", + "formula", + "glyph", + "compartment", + "submodel", + "x", + "y", + "fontColor", + "fontSize", + "fillColor", + "borderColor" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" +} +``` diff --git a/docs/types/Chemical.md b/docs/types/Chemical.md new file mode 100644 index 00000000..61add593 --- /dev/null +++ b/docs/types/Chemical.md @@ -0,0 +1,744 @@ +```json +{ + "type": "object", + "properties": { + "id": { + "type": "object", + "properties": { + "annotatorClassName": { + "type": "string" + }, + "id": { + "type": "number" + }, + "link": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": ["annotatorClassName", "id", "link", "resource", "type"], + "additionalProperties": false + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "directEvidence": { + "type": ["string", "null"] + }, + "directEvidenceReferences": { + "type": "array", + "items": { + "type": "object", + "properties": { + "link": { + "anyOf": [ + { + "type": "string", + "format": "uri" + }, + { + "type": "null" + } + ] + }, + "article": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "authors": { + "type": "array", + "items": { + "type": "string" + } + }, + "journal": { + "type": "string" + }, + "year": { + "type": "number" + }, + "link": { + "type": "string" + }, + "pubmedId": { + "type": "string" + }, + "citationCount": { + "type": "number" + } + }, + "required": [ + "title", + "authors", + "journal", + "year", + "link", + "pubmedId", + "citationCount" + ], + "additionalProperties": false + }, + "type": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "id": { + "type": "number" + }, + "annotatorClassName": { + "type": "string" + } + }, + "required": ["link", "type", "resource", "id", "annotatorClassName"], + "additionalProperties": false + } + }, + "synonyms": { + "type": "array", + "items": { + "type": "string" + } + }, + "references": { + "type": "array", + "items": { + "$ref": "#/properties/directEvidenceReferences/items" + } + }, + "targets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "references": { + "type": "array", + "items": { + "$ref": "#/properties/directEvidenceReferences/items" + } + }, + "targetElements": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "stringType": { + "type": "string" + }, + "name": { + "type": "string" + }, + "elementId": { + "type": "string" + }, + "model": { + "type": "number" + }, + "references": { + "type": "array", + "items": { + "$ref": "#/properties/directEvidenceReferences/items" + } + }, + "z": { + "type": "number" + }, + "notes": { + "type": "string" + }, + "symbol": { + "type": ["string", "null"] + }, + "homodimer": { + "type": "number" + }, + "nameX": { + "type": "number" + }, + "nameY": { + "type": "number" + }, + "nameWidth": { + "type": "number" + }, + "nameHeight": { + "type": "number" + }, + "nameVerticalAlign": { + "type": "string" + }, + "nameHorizontalAlign": { + "type": "string" + }, + "width": { + "type": "number" + }, + "height": { + "type": "number" + }, + "visibilityLevel": { + "type": "string" + }, + "transparencyLevel": { + "type": "string" + }, + "synonyms": { + "type": "array", + "items": { + "type": "string" + } + }, + "formerSymbols": { + "type": "array", + "items": { + "type": "string" + } + }, + "fullName": { + "type": ["string", "null"] + }, + "compartmentName": { + "type": ["string", "null"] + }, + "abbreviation": { + "type": ["string", "null"] + }, + "formula": { + "type": ["string", "null"] + }, + "glyph": { + "anyOf": [ + { + "type": "object", + "properties": { + "file": { + "type": "number" + } + }, + "required": ["file"], + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "activity": { + "type": "boolean" + }, + "structuralState": { + "anyOf": [ + { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "position": { + "type": "object", + "properties": { + "x": { + "type": "number" + }, + "y": { + "type": "number" + } + }, + "required": ["x", "y"], + "additionalProperties": false + }, + "z": { + "type": "number" + }, + "width": { + "type": "number" + }, + "height": { + "type": "number" + }, + "fontSize": { + "type": "number" + }, + "size": { + "type": "number" + }, + "center": { + "$ref": "#/properties/targets/items/properties/targetElements/items/properties/structuralState/anyOf/0/properties/position" + }, + "borderColor": { + "type": "object", + "properties": { + "alpha": { + "type": "number" + }, + "rgb": { + "type": "number" + } + }, + "required": ["alpha", "rgb"], + "additionalProperties": false + }, + "elementId": { + "type": "string" + } + }, + "required": [ + "value", + "position", + "z", + "width", + "height", + "fontSize", + "size", + "center", + "borderColor", + "elementId" + ], + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "hypothetical": { + "type": ["boolean", "null"] + }, + "boundaryCondition": { + "type": "boolean" + }, + "constant": { + "type": "boolean" + }, + "initialAmount": { + "type": ["number", "null"] + }, + "initialConcentration": { + "type": ["number", "null"] + }, + "charge": { + "type": ["number", "null"] + }, + "substanceUnits": { + "type": ["string", "null"] + }, + "onlySubstanceUnits": { + "type": "boolean" + }, + "modificationResidues": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "idModificationResidue": { + "type": "string" + }, + "name": { + "type": "string" + }, + "position": { + "$ref": "#/properties/targets/items/properties/targetElements/items/properties/structuralState/anyOf/0/properties/position" + }, + "z": { + "type": "number" + }, + "borderColor": { + "$ref": "#/properties/targets/items/properties/targetElements/items/properties/structuralState/anyOf/0/properties/borderColor" + }, + "state": { + "anyOf": [ + { + "type": ["string", "number"] + }, + { + "type": "null" + } + ] + }, + "size": { + "type": "number" + }, + "elementId": { + "type": "string" + } + }, + "required": [ + "id", + "idModificationResidue", + "name", + "z", + "borderColor", + "size", + "elementId" + ], + "additionalProperties": false + } + }, + "complex": { + "type": ["number", "null"] + }, + "compartment": { + "type": ["number", "null"] + }, + "submodel": { + "anyOf": [ + { + "type": "object", + "properties": { + "mapId": { + "type": "number" + }, + "type": { + "type": "string" + } + }, + "required": ["mapId", "type"], + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "x": { + "type": "number" + }, + "y": { + "type": "number" + }, + "lineWidth": { + "type": "number" + }, + "fontColor": { + "$ref": "#/properties/targets/items/properties/targetElements/items/properties/structuralState/anyOf/0/properties/borderColor" + }, + "fontSize": { + "type": "number" + }, + "fillColor": { + "$ref": "#/properties/targets/items/properties/targetElements/items/properties/structuralState/anyOf/0/properties/borderColor" + }, + "borderColor": { + "$ref": "#/properties/targets/items/properties/targetElements/items/properties/structuralState/anyOf/0/properties/borderColor" + }, + "smiles": { + "anyOf": [ + { + "anyOf": [ + { + "not": {} + }, + { + "type": "string" + } + ] + }, + { + "type": "null" + } + ] + }, + "inChI": { + "type": ["string", "null"] + }, + "inChIKey": { + "type": ["string", "null"] + }, + "thickness": { + "type": "number" + }, + "outerWidth": { + "type": "number" + }, + "innerWidth": { + "type": "number" + }, + "idReaction": { + "type": "string" + }, + "reversible": { + "type": "boolean" + }, + "mechanicalConfidenceScore": { + "type": "boolean" + }, + "lowerBound": { + "type": "boolean" + }, + "upperBound": { + "type": "boolean" + }, + "subsystem": { + "type": "string" + }, + "geneProteinReaction": { + "type": "string" + }, + "kinetics": { + "type": "null" + }, + "products": { + "type": "array", + "items": { + "type": "object", + "properties": { + "aliasId": { + "type": "number" + }, + "stoichiometry": { + "type": ["number", "null"] + }, + "type": { + "type": "string" + } + }, + "required": ["aliasId", "stoichiometry"], + "additionalProperties": false + } + }, + "reactants": { + "type": "array", + "items": { + "$ref": "#/properties/targets/items/properties/targetElements/items/properties/products/items" + } + }, + "modifiers": { + "type": "array", + "items": { + "$ref": "#/properties/targets/items/properties/targetElements/items/properties/products/items" + } + }, + "processCoordinates": { + "type": "null" + }, + "line": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "width": { + "type": "number" + }, + "color": { + "$ref": "#/properties/targets/items/properties/targetElements/items/properties/structuralState/anyOf/0/properties/borderColor" + }, + "z": { + "type": "number" + }, + "segments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "x1": { + "type": "number" + }, + "y1": { + "type": "number" + }, + "x2": { + "type": "number" + }, + "y2": { + "type": "number" + } + }, + "required": ["x1", "y1", "x2", "y2"], + "additionalProperties": false + } + }, + "startArrow": { + "type": "object", + "properties": { + "arrowType": { + "type": "string" + }, + "angle": { + "type": "number" + }, + "lineType": { + "type": "string" + }, + "length": { + "type": "number" + } + }, + "required": ["arrowType", "angle", "lineType", "length"], + "additionalProperties": false + }, + "endArrow": { + "$ref": "#/properties/targets/items/properties/targetElements/items/properties/line/properties/startArrow" + }, + "lineType": { + "type": "string" + } + }, + "required": [ + "id", + "width", + "color", + "z", + "segments", + "startArrow", + "endArrow", + "lineType" + ], + "additionalProperties": false + }, + "operators": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "line": { + "$ref": "#/properties/targets/items/properties/targetElements/items/properties/line" + }, + "inputs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + } + }, + "required": ["id"], + "additionalProperties": false + } + }, + "outputs": { + "not": {} + }, + "operatorText": { + "type": "string" + }, + "reactantOperator": { + "type": "boolean" + }, + "productOperator": { + "type": "boolean" + }, + "modifierOperator": { + "type": "boolean" + } + }, + "required": [ + "id", + "line", + "inputs", + "operatorText", + "reactantOperator", + "productOperator", + "modifierOperator" + ], + "additionalProperties": false + } + } + }, + "required": [ + "id", + "stringType", + "name", + "elementId", + "model", + "references", + "z", + "notes", + "symbol", + "nameX", + "nameY", + "nameWidth", + "nameHeight", + "nameVerticalAlign", + "nameHorizontalAlign", + "width", + "height", + "visibilityLevel", + "transparencyLevel", + "synonyms", + "formerSymbols", + "fullName", + "abbreviation", + "formula", + "glyph", + "compartment", + "submodel", + "x", + "y", + "fontColor", + "fontSize", + "fillColor", + "borderColor" + ], + "additionalProperties": false + } + }, + "targetParticipants": { + "type": "array", + "items": { + "type": "object", + "properties": { + "link": { + "type": "string" + }, + "type": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "id": { + "type": "number" + }, + "annotatorClassName": { + "type": "string" + } + }, + "required": ["link", "type", "resource", "id", "annotatorClassName"], + "additionalProperties": false + } + } + }, + "required": ["name", "references", "targetElements", "targetParticipants"], + "additionalProperties": false + } + } + }, + "required": [ + "id", + "name", + "description", + "directEvidence", + "directEvidenceReferences", + "synonyms", + "references", + "targets" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" +} +``` diff --git a/docs/types/Drug.md b/docs/types/Drug.md new file mode 100644 index 00000000..f7b21990 --- /dev/null +++ b/docs/types/Drug.md @@ -0,0 +1,725 @@ +```json +{ + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": ["string", "null"] + }, + "synonyms": { + "type": "array", + "items": { + "type": "string" + } + }, + "brandNames": { + "type": "array", + "items": { + "type": "string" + } + }, + "bloodBrainBarrier": { + "type": "string" + }, + "references": { + "type": "array", + "items": { + "type": "object", + "properties": { + "link": { + "anyOf": [ + { + "type": "string", + "format": "uri" + }, + { + "type": "null" + } + ] + }, + "article": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "authors": { + "type": "array", + "items": { + "type": "string" + } + }, + "journal": { + "type": "string" + }, + "year": { + "type": "number" + }, + "link": { + "type": "string" + }, + "pubmedId": { + "type": "string" + }, + "citationCount": { + "type": "number" + } + }, + "required": [ + "title", + "authors", + "journal", + "year", + "link", + "pubmedId", + "citationCount" + ], + "additionalProperties": false + }, + "type": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "id": { + "type": "number" + }, + "annotatorClassName": { + "type": "string" + } + }, + "required": ["link", "type", "resource", "id", "annotatorClassName"], + "additionalProperties": false + } + }, + "targets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "references": { + "type": "array", + "items": { + "$ref": "#/properties/references/items" + } + }, + "targetElements": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "stringType": { + "type": "string" + }, + "name": { + "type": "string" + }, + "elementId": { + "type": "string" + }, + "model": { + "type": "number" + }, + "references": { + "type": "array", + "items": { + "$ref": "#/properties/references/items" + } + }, + "z": { + "type": "number" + }, + "notes": { + "type": "string" + }, + "symbol": { + "type": ["string", "null"] + }, + "homodimer": { + "type": "number" + }, + "nameX": { + "type": "number" + }, + "nameY": { + "type": "number" + }, + "nameWidth": { + "type": "number" + }, + "nameHeight": { + "type": "number" + }, + "nameVerticalAlign": { + "type": "string" + }, + "nameHorizontalAlign": { + "type": "string" + }, + "width": { + "type": "number" + }, + "height": { + "type": "number" + }, + "visibilityLevel": { + "type": "string" + }, + "transparencyLevel": { + "type": "string" + }, + "synonyms": { + "type": "array", + "items": { + "type": "string" + } + }, + "formerSymbols": { + "type": "array", + "items": { + "type": "string" + } + }, + "fullName": { + "type": ["string", "null"] + }, + "compartmentName": { + "type": ["string", "null"] + }, + "abbreviation": { + "type": ["string", "null"] + }, + "formula": { + "type": ["string", "null"] + }, + "glyph": { + "anyOf": [ + { + "type": "object", + "properties": { + "file": { + "type": "number" + } + }, + "required": ["file"], + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "activity": { + "type": "boolean" + }, + "structuralState": { + "anyOf": [ + { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "position": { + "type": "object", + "properties": { + "x": { + "type": "number" + }, + "y": { + "type": "number" + } + }, + "required": ["x", "y"], + "additionalProperties": false + }, + "z": { + "type": "number" + }, + "width": { + "type": "number" + }, + "height": { + "type": "number" + }, + "fontSize": { + "type": "number" + }, + "size": { + "type": "number" + }, + "center": { + "$ref": "#/properties/targets/items/properties/targetElements/items/properties/structuralState/anyOf/0/properties/position" + }, + "borderColor": { + "type": "object", + "properties": { + "alpha": { + "type": "number" + }, + "rgb": { + "type": "number" + } + }, + "required": ["alpha", "rgb"], + "additionalProperties": false + }, + "elementId": { + "type": "string" + } + }, + "required": [ + "value", + "position", + "z", + "width", + "height", + "fontSize", + "size", + "center", + "borderColor", + "elementId" + ], + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "hypothetical": { + "type": ["boolean", "null"] + }, + "boundaryCondition": { + "type": "boolean" + }, + "constant": { + "type": "boolean" + }, + "initialAmount": { + "type": ["number", "null"] + }, + "initialConcentration": { + "type": ["number", "null"] + }, + "charge": { + "type": ["number", "null"] + }, + "substanceUnits": { + "type": ["string", "null"] + }, + "onlySubstanceUnits": { + "type": "boolean" + }, + "modificationResidues": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "idModificationResidue": { + "type": "string" + }, + "name": { + "type": "string" + }, + "position": { + "$ref": "#/properties/targets/items/properties/targetElements/items/properties/structuralState/anyOf/0/properties/position" + }, + "z": { + "type": "number" + }, + "borderColor": { + "$ref": "#/properties/targets/items/properties/targetElements/items/properties/structuralState/anyOf/0/properties/borderColor" + }, + "state": { + "anyOf": [ + { + "type": ["string", "number"] + }, + { + "type": "null" + } + ] + }, + "size": { + "type": "number" + }, + "elementId": { + "type": "string" + } + }, + "required": [ + "id", + "idModificationResidue", + "name", + "z", + "borderColor", + "size", + "elementId" + ], + "additionalProperties": false + } + }, + "complex": { + "type": ["number", "null"] + }, + "compartment": { + "type": ["number", "null"] + }, + "submodel": { + "anyOf": [ + { + "type": "object", + "properties": { + "mapId": { + "type": "number" + }, + "type": { + "type": "string" + } + }, + "required": ["mapId", "type"], + "additionalProperties": false + }, + { + "type": "null" + } + ] + }, + "x": { + "type": "number" + }, + "y": { + "type": "number" + }, + "lineWidth": { + "type": "number" + }, + "fontColor": { + "$ref": "#/properties/targets/items/properties/targetElements/items/properties/structuralState/anyOf/0/properties/borderColor" + }, + "fontSize": { + "type": "number" + }, + "fillColor": { + "$ref": "#/properties/targets/items/properties/targetElements/items/properties/structuralState/anyOf/0/properties/borderColor" + }, + "borderColor": { + "$ref": "#/properties/targets/items/properties/targetElements/items/properties/structuralState/anyOf/0/properties/borderColor" + }, + "smiles": { + "anyOf": [ + { + "anyOf": [ + { + "not": {} + }, + { + "type": "string" + } + ] + }, + { + "type": "null" + } + ] + }, + "inChI": { + "type": ["string", "null"] + }, + "inChIKey": { + "type": ["string", "null"] + }, + "thickness": { + "type": "number" + }, + "outerWidth": { + "type": "number" + }, + "innerWidth": { + "type": "number" + }, + "idReaction": { + "type": "string" + }, + "reversible": { + "type": "boolean" + }, + "mechanicalConfidenceScore": { + "type": "boolean" + }, + "lowerBound": { + "type": "boolean" + }, + "upperBound": { + "type": "boolean" + }, + "subsystem": { + "type": "string" + }, + "geneProteinReaction": { + "type": "string" + }, + "kinetics": { + "type": "null" + }, + "products": { + "type": "array", + "items": { + "type": "object", + "properties": { + "aliasId": { + "type": "number" + }, + "stoichiometry": { + "type": ["number", "null"] + }, + "type": { + "type": "string" + } + }, + "required": ["aliasId", "stoichiometry"], + "additionalProperties": false + } + }, + "reactants": { + "type": "array", + "items": { + "$ref": "#/properties/targets/items/properties/targetElements/items/properties/products/items" + } + }, + "modifiers": { + "type": "array", + "items": { + "$ref": "#/properties/targets/items/properties/targetElements/items/properties/products/items" + } + }, + "processCoordinates": { + "type": "null" + }, + "line": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "width": { + "type": "number" + }, + "color": { + "$ref": "#/properties/targets/items/properties/targetElements/items/properties/structuralState/anyOf/0/properties/borderColor" + }, + "z": { + "type": "number" + }, + "segments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "x1": { + "type": "number" + }, + "y1": { + "type": "number" + }, + "x2": { + "type": "number" + }, + "y2": { + "type": "number" + } + }, + "required": ["x1", "y1", "x2", "y2"], + "additionalProperties": false + } + }, + "startArrow": { + "type": "object", + "properties": { + "arrowType": { + "type": "string" + }, + "angle": { + "type": "number" + }, + "lineType": { + "type": "string" + }, + "length": { + "type": "number" + } + }, + "required": ["arrowType", "angle", "lineType", "length"], + "additionalProperties": false + }, + "endArrow": { + "$ref": "#/properties/targets/items/properties/targetElements/items/properties/line/properties/startArrow" + }, + "lineType": { + "type": "string" + } + }, + "required": [ + "id", + "width", + "color", + "z", + "segments", + "startArrow", + "endArrow", + "lineType" + ], + "additionalProperties": false + }, + "operators": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + }, + "line": { + "$ref": "#/properties/targets/items/properties/targetElements/items/properties/line" + }, + "inputs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "number" + } + }, + "required": ["id"], + "additionalProperties": false + } + }, + "outputs": { + "not": {} + }, + "operatorText": { + "type": "string" + }, + "reactantOperator": { + "type": "boolean" + }, + "productOperator": { + "type": "boolean" + }, + "modifierOperator": { + "type": "boolean" + } + }, + "required": [ + "id", + "line", + "inputs", + "operatorText", + "reactantOperator", + "productOperator", + "modifierOperator" + ], + "additionalProperties": false + } + } + }, + "required": [ + "id", + "stringType", + "name", + "elementId", + "model", + "references", + "z", + "notes", + "symbol", + "nameX", + "nameY", + "nameWidth", + "nameHeight", + "nameVerticalAlign", + "nameHorizontalAlign", + "width", + "height", + "visibilityLevel", + "transparencyLevel", + "synonyms", + "formerSymbols", + "fullName", + "abbreviation", + "formula", + "glyph", + "compartment", + "submodel", + "x", + "y", + "fontColor", + "fontSize", + "fillColor", + "borderColor" + ], + "additionalProperties": false + } + }, + "targetParticipants": { + "type": "array", + "items": { + "type": "object", + "properties": { + "link": { + "type": "string" + }, + "type": { + "type": "string" + }, + "resource": { + "type": "string" + }, + "id": { + "type": "number" + }, + "annotatorClassName": { + "type": "string" + } + }, + "required": ["link", "type", "resource", "id", "annotatorClassName"], + "additionalProperties": false + } + } + }, + "required": ["name", "references", "targetElements", "targetParticipants"], + "additionalProperties": false + } + } + }, + "required": [ + "id", + "name", + "description", + "synonyms", + "brandNames", + "bloodBrainBarrier", + "references", + "targets" + ], + "additionalProperties": false, + "$schema": "http://json-schema.org/draft-07/schema#" +} +``` diff --git a/docs/types/Marker.md b/docs/types/Marker.md new file mode 100644 index 00000000..c9aa40ed --- /dev/null +++ b/docs/types/Marker.md @@ -0,0 +1,12 @@ +```ts +interface MarkerBase { + type: 'pin' | 'surface'; + id: string; + color: string; + opacity: number; + x: number; + y: number; + number?: number; + modelId?: number; +} +``` diff --git a/index.d.ts b/index.d.ts index 248f6f8e..dcca7b12 100644 --- a/index.d.ts +++ b/index.d.ts @@ -22,6 +22,9 @@ declare global { plugins: { registerPlugin: RegisterPlugin; }; + data: { + bioEntities: BioEntitiesMethods; + }; }; } } diff --git a/package-lock.json b/package-lock.json index f03034da..016ad699 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,9 @@ "tailwindcss": "3.3.3", "ts-deepmerge": "^6.2.0", "use-debounce": "^9.0.4", - "zod": "^3.22.2" + "uuid": "^9.0.1", + "zod": "^3.22.2", + "zod-to-json-schema": "^3.22.4" }, "devDependencies": { "@commitlint/cli": "^17.7.1", @@ -50,6 +52,7 @@ "@types/jest": "^29.5.5", "@types/react-redux": "^7.1.26", "@types/redux-mock-store": "^1.0.6", + "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "^6.7.0", "@typescript-eslint/parser": "^6.7.0", "axios-mock-adapter": "^1.22.0", @@ -1180,6 +1183,15 @@ "node": ">= 0.12" } }, + "node_modules/@cypress/request/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@cypress/xvfb": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", @@ -2495,6 +2507,12 @@ "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true + }, "node_modules/@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", @@ -8532,6 +8550,15 @@ "node": ">=8" } }, + "node_modules/jest-junit/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/jest-leak-detector": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", @@ -13457,10 +13484,13 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { "uuid": "dist/bin/uuid" } @@ -14004,6 +14034,14 @@ "zod": ">=3.0.0" } }, + "node_modules/zod-to-json-schema": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.22.4.tgz", + "integrity": "sha512-2Ed5dJ+n/O3cU383xSY28cuVi0BCQhF8nYqWU5paEpl7fVdqdAmiLdqLyfblbNdfOFwFfi/mqU4O1pwc60iBhQ==", + "peerDependencies": { + "zod": "^3.22.4" + } + }, "node_modules/zstddec": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.1.0.tgz", @@ -14850,6 +14888,12 @@ "combined-stream": "^1.0.6", "mime-types": "^2.1.12" } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true } } }, @@ -15861,6 +15905,12 @@ "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" }, + "@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true + }, "@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", @@ -20183,6 +20233,12 @@ "requires": { "ansi-regex": "^5.0.1" } + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true } } }, @@ -23678,10 +23734,9 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" }, "v8-compile-cache-lib": { "version": "3.0.1", @@ -24095,6 +24150,12 @@ "randexp": "^0.5.3" } }, + "zod-to-json-schema": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.22.4.tgz", + "integrity": "sha512-2Ed5dJ+n/O3cU383xSY28cuVi0BCQhF8nYqWU5paEpl7fVdqdAmiLdqLyfblbNdfOFwFfi/mqU4O1pwc60iBhQ==", + "requires": {} + }, "zstddec": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.1.0.tgz", diff --git a/package.json b/package.json index 730be9f5..f3a8ec09 100644 --- a/package.json +++ b/package.json @@ -52,18 +52,21 @@ "tailwindcss": "3.3.3", "ts-deepmerge": "^6.2.0", "use-debounce": "^9.0.4", - "zod": "^3.22.2" + "uuid": "^9.0.1", + "zod": "^3.22.2", + "zod-to-json-schema": "^3.22.4" }, "devDependencies": { "@commitlint/cli": "^17.7.1", "@commitlint/config-conventional": "^17.7.0", "@testing-library/jest-dom": "^6.1.3", "@testing-library/react": "^14.0.0", - "@types/crypto-js": "^4.2.2", "@testing-library/user-event": "^14.5.2", + "@types/crypto-js": "^4.2.2", "@types/jest": "^29.5.5", "@types/react-redux": "^7.1.26", "@types/redux-mock-store": "^1.0.6", + "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "^6.7.0", "@typescript-eslint/parser": "^6.7.0", "axios-mock-adapter": "^1.22.0", diff --git a/src/components/Map/Drawer/BioEntityDrawer/OverlayData/OverlayData.types.ts b/src/components/Map/Drawer/BioEntityDrawer/OverlayData/OverlayData.types.ts index 4f78ef61..3d43db8f 100644 --- a/src/components/Map/Drawer/BioEntityDrawer/OverlayData/OverlayData.types.ts +++ b/src/components/Map/Drawer/BioEntityDrawer/OverlayData/OverlayData.types.ts @@ -1,7 +1,7 @@ import { GeneVariant } from '@/types/models'; export interface OverlayDataAxis { - id: number; + id: number | string; title: string; value?: number; color: string; diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/BioEntitiesAccordion.component.test.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/BioEntitiesAccordion.component.test.tsx index d65d2915..33d06350 100644 --- a/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/BioEntitiesAccordion.component.test.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/GroupedSearchResults/BioEntitiesAccordion/BioEntitiesAccordion.component.test.tsx @@ -74,7 +74,7 @@ describe('BioEntitiesAccordion - component', () => { expect(screen.getByText('Content (10)')).toBeInTheDocument(); expect(screen.getByText('Core PD map (5)')).toBeInTheDocument(); - expect(screen.getByText('Histamine signaling (2)')).toBeInTheDocument(); - expect(screen.getByText('PRKN substrates (3)')).toBeInTheDocument(); + expect(screen.getByText('Histamine signaling (3)')).toBeInTheDocument(); + expect(screen.getByText('PRKN substrates (2)')).toBeInTheDocument(); }); }); diff --git a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.types.tsx b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.types.tsx index 74d6fe34..e0bdafa3 100644 --- a/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.types.tsx +++ b/src/components/Map/Drawer/SearchDrawerWrapper/ResultsList/PinsList/PinsList.types.tsx @@ -10,7 +10,7 @@ export type PinItem = { export type PinTypeWithNone = PinType | 'none'; export type AvailableSubmaps = { - id: number; + id: number | string; modelId: number; name: string; }; diff --git a/src/components/Map/MapViewer/utils/config/getCanvasIcon.ts b/src/components/Map/MapViewer/utils/config/getCanvasIcon.ts index c377b0ef..7d3e6925 100644 --- a/src/components/Map/MapViewer/utils/config/getCanvasIcon.ts +++ b/src/components/Map/MapViewer/utils/config/getCanvasIcon.ts @@ -59,7 +59,9 @@ export const drawNumberOnCanvas = ( ctx.fillText(text, x, y); }; -export const getCanvasIcon = (args: Args): HTMLCanvasElement => { +export const getCanvasIcon = ( + args: Omit<Args, 'value'> & { value?: number }, +): HTMLCanvasElement => { const canvas = createCanvas(PIN_SIZE); const ctx = canvas.getContext('2d'); if (!ctx) { @@ -67,7 +69,9 @@ export const getCanvasIcon = (args: Args): HTMLCanvasElement => { } drawPinOnCanvas(args, ctx); - drawNumberOnCanvas(args, ctx); + if (args?.value !== undefined) { + drawNumberOnCanvas({ value: args.value }, ctx); + } return canvas; }; diff --git a/src/components/Map/MapViewer/utils/config/overlaysLayer/parseSurfaceMarkersToBioEntityRender.test.ts b/src/components/Map/MapViewer/utils/config/overlaysLayer/parseSurfaceMarkersToBioEntityRender.test.ts new file mode 100644 index 00000000..0510f572 --- /dev/null +++ b/src/components/Map/MapViewer/utils/config/overlaysLayer/parseSurfaceMarkersToBioEntityRender.test.ts @@ -0,0 +1,96 @@ +import { MarkerSurface } from '@/redux/markers/markers.types'; +import { OverlayBioEntityRender } from '@/types/OLrendering'; +import { parseSurfaceMarkersToBioEntityRender } from './parseSurfaceMarkersToBioEntityRender'; + +const MARKERS: MarkerSurface[] = [ + { + type: 'surface', + id: '1', + color: '#000000', + opacity: 0.1, + x: 1200, + y: 500, + width: 100, + height: 50, + number: 0, + modelId: 0, + }, + { + type: 'surface', + id: '2', + color: '#FF0000', + opacity: 0.67, + x: 432, + y: 2343, + width: 100, + height: 50, + number: 23, + modelId: 33, + }, + { + type: 'surface', + id: '3', + color: '#FFFFFF', + opacity: 0, + x: 1, + y: 1, + width: 2, + height: 2, + number: 1, + modelId: 1, + }, +]; + +const EXPECTED_RETURN: OverlayBioEntityRender[] = [ + { + color: null, + height: 50, + hexColor: '#0000001a', + id: '1', + modelId: 0, + overlayId: 0, + type: 'rectangle', + value: 0.1, + width: 100, + x1: 1200, + x2: 1300, + y1: 550, + y2: 500, + }, + { + color: null, + height: 50, + hexColor: '#FF0000ab', + id: '2', + modelId: 33, + overlayId: 0, + type: 'rectangle', + value: 0.67, + width: 100, + x1: 432, + x2: 532, + y1: 2393, + y2: 2343, + }, + { + color: null, + height: 2, + hexColor: '#FFFFFF00', + id: '3', + modelId: 1, + overlayId: 0, + type: 'rectangle', + value: 0, + width: 2, + x1: 1, + x2: 3, + y1: 3, + y2: 1, + }, +]; + +describe('parseSurfaceMarkersToBioEntityRender - util', () => { + it('returns correctly parsed markers for every element', () => { + expect(parseSurfaceMarkersToBioEntityRender(MARKERS)).toStrictEqual(EXPECTED_RETURN); + }); +}); diff --git a/src/components/Map/MapViewer/utils/config/overlaysLayer/parseSurfaceMarkersToBioEntityRender.ts b/src/components/Map/MapViewer/utils/config/overlaysLayer/parseSurfaceMarkersToBioEntityRender.ts new file mode 100644 index 00000000..bd6461e9 --- /dev/null +++ b/src/components/Map/MapViewer/utils/config/overlaysLayer/parseSurfaceMarkersToBioEntityRender.ts @@ -0,0 +1,24 @@ +import { ZERO } from '@/constants/common'; +import { MarkerSurface } from '@/redux/markers/markers.types'; +import { OverlayBioEntityRender } from '@/types/OLrendering'; +import { addAlphaToHexString } from '@/utils/convert/addAlphaToHexString'; + +export const parseSurfaceMarkersToBioEntityRender = ( + markers: MarkerSurface[], +): OverlayBioEntityRender[] => { + return markers.map(({ id, modelId, x, y, width, height, color, opacity }) => ({ + type: 'rectangle', + id, + x1: x, + y1: y + height, + x2: x + width, + y2: y, + width, + height, + value: opacity, + modelId: modelId || ZERO, // ignored in next steps + overlayId: ZERO, // ignored in next steps + color: null, // replaced by hexColor in next steps + hexColor: addAlphaToHexString(color, opacity), + })); +}; diff --git a/src/components/Map/MapViewer/utils/config/overlaysLayer/useOverlayFeatures.test.ts b/src/components/Map/MapViewer/utils/config/overlaysLayer/useOverlayFeatures.test.ts index 15c9cf3e..3c4dbcfd 100644 --- a/src/components/Map/MapViewer/utils/config/overlaysLayer/useOverlayFeatures.test.ts +++ b/src/components/Map/MapViewer/utils/config/overlaysLayer/useOverlayFeatures.test.ts @@ -1,6 +1,7 @@ /* eslint-disable no-magic-numbers */ import { CONFIGURATION_INITIAL_STORE_MOCKS } from '@/redux/configuration/configuration.mock'; import { mapStateWithCurrentlySelectedMainMapFixture } from '@/redux/map/map.fixtures'; +import { SURFACE_MARKER } from '@/redux/models/marker.mock'; import { MODELS_DATA_MOCK_WITH_MAIN_MAP } from '@/redux/models/models.mock'; import { MOCKED_OVERLAY_BIO_ENTITY_RENDER } from '@/redux/overlayBioEntity/overlayBioEntity.mock'; import { OVERLAYS_PUBLIC_FETCHED_STATE_MOCK } from '@/redux/overlays/overlays.mock'; @@ -43,6 +44,9 @@ describe('useOverlayFeatures', () => { models: { ...MODELS_DATA_MOCK_WITH_MAIN_MAP, }, + markers: { + data: [SURFACE_MARKER], + }, }); it('should return an array of features', () => { @@ -52,7 +56,7 @@ describe('useOverlayFeatures', () => { wrapper: Wrapper, }); - expect(features).toHaveLength(10); + expect(features).toHaveLength(11); // type: rectangle expect(features[0].getGeometry()?.getCoordinates()).toEqual([ @@ -81,5 +85,19 @@ describe('useOverlayFeatures', () => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore expect(features[7].getStyle().getFill().getColor()).toBe('#ff0000cc'); + + // type: rectangle (marker) + expect(features[10].getGeometry()?.getCoordinates()).toEqual([ + [ + [-19588560, 19831740], + [-19588560, 19850446], + [-19551147, 19850446], + [-19551147, 19831740], + [-19588560, 19831740], + ], + ]); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + expect(features[10].getStyle().getFill().getColor()).toBe('#0000001a'); }); }); diff --git a/src/components/Map/MapViewer/utils/config/overlaysLayer/useOverlayFeatures.ts b/src/components/Map/MapViewer/utils/config/overlaysLayer/useOverlayFeatures.ts index 98e528f3..93e24ec8 100644 --- a/src/components/Map/MapViewer/utils/config/overlaysLayer/useOverlayFeatures.ts +++ b/src/components/Map/MapViewer/utils/config/overlaysLayer/useOverlayFeatures.ts @@ -1,5 +1,6 @@ import { ZERO } from '@/constants/common'; import { useAppSelector } from '@/redux/hooks/useAppSelector'; +import { markersSufraceOfCurrentMapDataSelector } from '@/redux/markers/markers.selectors'; import { getOverlayOrderSelector } from '@/redux/overlayBioEntity/overlayBioEntity.selector'; import { LinePoint } from '@/types/reactions'; import { usePointToProjection } from '@/utils/map/usePointToProjection'; @@ -9,17 +10,36 @@ import type Polygon from 'ol/geom/Polygon'; import { useMemo } from 'react'; import { createOverlayGeometryFeature } from './createOverlayGeometryFeature'; import { createOverlayLineFeature } from './createOverlayLineFeature'; +import { createOverlaySubmapLinkRectangleFeature } from './createOverlaySubmapLinkRectangleFeature'; import { getPolygonLatitudeCoordinates } from './getPolygonLatitudeCoordinates'; -import { useGetOverlayColor } from './useGetOverlayColor'; +import { parseSurfaceMarkersToBioEntityRender } from './parseSurfaceMarkersToBioEntityRender'; import { useBioEntitiesWithSubmapsLinks } from './useBioEntitiesWithSubmapLinks'; -import { createOverlaySubmapLinkRectangleFeature } from './createOverlaySubmapLinkRectangleFeature'; +import { useGetOverlayColor } from './useGetOverlayColor'; export const useOverlayFeatures = (): Feature<Polygon>[] | Feature<SimpleGeometry>[] => { const pointToProjection = usePointToProjection(); const { getOverlayBioEntityColorByAvailableProperties } = useGetOverlayColor(); const overlaysOrder = useAppSelector(getOverlayOrderSelector); + const currentMarkers = useAppSelector(markersSufraceOfCurrentMapDataSelector); + const markersRender = parseSurfaceMarkersToBioEntityRender(currentMarkers); const bioEntities = useBioEntitiesWithSubmapsLinks(); + const markersFeatures = useMemo( + () => + markersRender.map(entity => { + const color = getOverlayBioEntityColorByAvailableProperties(entity); + + return createOverlayGeometryFeature( + [ + ...pointToProjection({ x: entity.x1, y: entity.y1 }), + ...pointToProjection({ x: entity.x2, y: entity.y2 }), + ], + entity?.hexColor || color, + ); + }), + [getOverlayBioEntityColorByAvailableProperties, markersRender, pointToProjection], + ); + const features = useMemo( () => bioEntities.map(entity => { @@ -36,7 +56,9 @@ export const useOverlayFeatures = (): Feature<Polygon>[] | Feature<SimpleGeometr overlaysOrder.find(({ id }) => id === entity.overlayId)?.index || ZERO, }); - const color = getOverlayBioEntityColorByAvailableProperties(entity); + const calculatedColor = getOverlayBioEntityColorByAvailableProperties(entity); + const hexColor = entity?.hexColor; + const color = hexColor || calculatedColor; if (entity.type === 'submap-link') { return createOverlaySubmapLinkRectangleFeature( @@ -72,5 +94,5 @@ export const useOverlayFeatures = (): Feature<Polygon>[] | Feature<SimpleGeometr [overlaysOrder, pointToProjection, getOverlayBioEntityColorByAvailableProperties, bioEntities], ); - return features; + return [...features, ...markersFeatures]; }; diff --git a/src/components/Map/MapViewer/utils/config/pinsLayer/getMarkerSingleFeature.test.ts b/src/components/Map/MapViewer/utils/config/pinsLayer/getMarkerSingleFeature.test.ts new file mode 100644 index 00000000..b83827e5 --- /dev/null +++ b/src/components/Map/MapViewer/utils/config/pinsLayer/getMarkerSingleFeature.test.ts @@ -0,0 +1,58 @@ +import { initialMapStateFixture } from '@/redux/map/map.fixtures'; +import { PIN_MARKER } from '@/redux/models/marker.mock'; +import { UsePointToProjectionResult, usePointToProjection } from '@/utils/map/usePointToProjection'; +import { + GetReduxWrapperUsingSliceReducer, + getReduxWrapperWithStore, +} from '@/utils/testing/getReduxWrapperWithStore'; +import { renderHook } from '@testing-library/react'; +import { Feature } from 'ol'; +import Style from 'ol/style/Style'; +import { getMarkerSingleFeature } from './getMarkerSingleFeature'; +import * as getPinStyle from './getPinStyle'; + +jest.mock('./getPinStyle', () => ({ + __esModule: true, + ...jest.requireActual('./getPinStyle'), +})); + +const getPinStyleSpy = jest.spyOn(getPinStyle, 'getPinStyle'); + +const getPointToProjection = ( + wrapper: ReturnType<GetReduxWrapperUsingSliceReducer>['Wrapper'], +): UsePointToProjectionResult => { + const { result: usePointToProjectionHook } = renderHook(() => usePointToProjection(), { + wrapper, + }); + + return usePointToProjectionHook.current; +}; + +describe('getMarkerSingleFeature - subUtil', () => { + const { Wrapper } = getReduxWrapperWithStore({ + map: initialMapStateFixture, + }); + const pointToProjection = getPointToProjection(Wrapper); + + it('should return instance of Feature with Style type=%s', () => { + const result = getMarkerSingleFeature(PIN_MARKER, { + pointToProjection, + }); + + const style = result.getStyle() as Style; + + expect(result).toBeInstanceOf(Feature); + expect(style).toBeInstanceOf(Style); + }); + + it('should run getPinStyle with valid args for type=%s', () => { + getMarkerSingleFeature(PIN_MARKER, { + pointToProjection, + }); + + expect(getPinStyleSpy).toHaveBeenCalledWith({ + color: '#0000001a', + value: PIN_MARKER.number, + }); + }); +}); diff --git a/src/components/Map/MapViewer/utils/config/pinsLayer/getMarkerSingleFeature.ts b/src/components/Map/MapViewer/utils/config/pinsLayer/getMarkerSingleFeature.ts new file mode 100644 index 00000000..705debb6 --- /dev/null +++ b/src/components/Map/MapViewer/utils/config/pinsLayer/getMarkerSingleFeature.ts @@ -0,0 +1,24 @@ +import { Marker } from '@/redux/markers/markers.types'; +import { addAlphaToHexString } from '@/utils/convert/addAlphaToHexString'; +import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection'; +import { Feature } from 'ol'; +import { getPinFeature } from './getPinFeature'; +import { getPinStyle } from './getPinStyle'; + +export const getMarkerSingleFeature = ( + marker: Marker, + { + pointToProjection, + }: { + pointToProjection: UsePointToProjectionResult; + }, +): Feature => { + const feature = getPinFeature(marker, pointToProjection); + const style = getPinStyle({ + color: addAlphaToHexString(marker.color, marker.opacity), + value: marker.number, + }); + + feature.setStyle(style); + return feature; +}; diff --git a/src/components/Map/MapViewer/utils/config/pinsLayer/getMarkersFeatures.ts b/src/components/Map/MapViewer/utils/config/pinsLayer/getMarkersFeatures.ts new file mode 100644 index 00000000..fa12fb5e --- /dev/null +++ b/src/components/Map/MapViewer/utils/config/pinsLayer/getMarkersFeatures.ts @@ -0,0 +1,19 @@ +import { Marker } from '@/redux/markers/markers.types'; +import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection'; +import { Feature } from 'ol'; +import { getMarkerSingleFeature } from './getMarkerSingleFeature'; + +export const getMarkersFeatures = ( + markers: Marker[], + { + pointToProjection, + }: { + pointToProjection: UsePointToProjectionResult; + }, +): Feature[] => { + return markers.map(marker => + getMarkerSingleFeature(marker, { + pointToProjection, + }), + ); +}; 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 64f3decc..28610fd8 100644 --- a/src/components/Map/MapViewer/utils/config/pinsLayer/getPinFeature.test.ts +++ b/src/components/Map/MapViewer/utils/config/pinsLayer/getPinFeature.test.ts @@ -1,6 +1,7 @@ import { HALF } from '@/constants/dividers'; import { bioEntityContentFixture } from '@/models/fixtures/bioEntityContentsFixture'; import { initialMapStateFixture } from '@/redux/map/map.fixtures'; +import { PIN_MARKER } from '@/redux/models/marker.mock'; import { usePointToProjection } from '@/utils/map/usePointToProjection'; import { getReduxWrapperWithStore } from '@/utils/testing/getReduxWrapperWithStore'; import { renderHook } from '@testing-library/react'; @@ -35,4 +36,18 @@ describe('getPinFeature - subUtil', () => { expect([x, y]).toStrictEqual(geometryPoint); }); + + describe('when is Marker Pin', () => { + const pinMarkerResult = getPinFeature(PIN_MARKER, pointToProjection); + + it('should return point parsed with point to projection', () => { + const [x, y] = pinMarkerResult.getGeometry()?.getExtent() || []; + const geometryPoint = pointToProjection({ + x: PIN_MARKER.x, + y: PIN_MARKER.y, + }); + + expect([x, y]).toStrictEqual(geometryPoint); + }); + }); }); diff --git a/src/components/Map/MapViewer/utils/config/pinsLayer/getPinFeature.ts b/src/components/Map/MapViewer/utils/config/pinsLayer/getPinFeature.ts index ec09a90d..ff7bfb00 100644 --- a/src/components/Map/MapViewer/utils/config/pinsLayer/getPinFeature.ts +++ b/src/components/Map/MapViewer/utils/config/pinsLayer/getPinFeature.ts @@ -1,16 +1,18 @@ +import { ZERO } from '@/constants/common'; import { HALF } from '@/constants/dividers'; +import { Marker } from '@/redux/markers/markers.types'; import { BioEntity } from '@/types/models'; import { UsePointToProjectionResult } from '@/utils/map/usePointToProjection'; import { Feature } from 'ol'; import { Point } from 'ol/geom'; export const getPinFeature = ( - { x, y, width, height, id }: BioEntity, + { x, y, width, height, id }: Pick<BioEntity, 'id' | 'width' | 'height' | 'x' | 'y'> | Marker, pointToProjection: UsePointToProjectionResult, ): Feature => { const point = { - x: x + width / HALF, - y: y + height / HALF, + x: x + (width || ZERO) / HALF, + y: y + (height || ZERO) / HALF, }; return new Feature({ diff --git a/src/components/Map/MapViewer/utils/config/pinsLayer/getPinStyle.ts b/src/components/Map/MapViewer/utils/config/pinsLayer/getPinStyle.ts index 3e91dfc0..77ee5c7f 100644 --- a/src/components/Map/MapViewer/utils/config/pinsLayer/getPinStyle.ts +++ b/src/components/Map/MapViewer/utils/config/pinsLayer/getPinStyle.ts @@ -4,7 +4,7 @@ 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 }: { value?: number; color: string }): Style => new Style({ image: new Icon({ displacement: [ZERO, PIN_SIZE.height], diff --git a/src/components/Map/MapViewer/utils/config/pinsLayer/useOlMapPinsLayer.ts b/src/components/Map/MapViewer/utils/config/pinsLayer/useOlMapPinsLayer.ts index ceecf01f..2e986604 100644 --- a/src/components/Map/MapViewer/utils/config/pinsLayer/useOlMapPinsLayer.ts +++ b/src/components/Map/MapViewer/utils/config/pinsLayer/useOlMapPinsLayer.ts @@ -2,6 +2,7 @@ import { searchedBioEntitesSelectorOfCurrentMap } from '@/redux/bioEntity/bioEntity.selectors'; import { searchedChemicalsBioEntitesOfCurrentMapSelector } from '@/redux/chemicals/chemicals.selectors'; import { searchedDrugsBioEntitesOfCurrentMapSelector } from '@/redux/drugs/drugs.selectors'; +import { markersPinsOfCurrentMapDataSelector } from '@/redux/markers/markers.selectors'; import { usePointToProjection } from '@/utils/map/usePointToProjection'; import Feature from 'ol/Feature'; import { Geometry } from 'ol/geom'; @@ -10,28 +11,31 @@ import VectorSource from 'ol/source/Vector'; import { useMemo } from 'react'; import { useSelector } from 'react-redux'; import { getBioEntitiesFeatures } from './getBioEntitiesFeatures'; +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 markersEntities = useSelector(markersPinsOfCurrentMapDataSelector); - const bioEntityFeatures = useMemo( + const elementsFeatures = useMemo( () => [ getBioEntitiesFeatures(contentBioEntites, { pointToProjection, type: 'bioEntity' }), getBioEntitiesFeatures(chemicalsBioEntities, { pointToProjection, type: 'chemicals' }), getBioEntitiesFeatures(drugsBioEntities, { pointToProjection, type: 'drugs' }), + getMarkersFeatures(markersEntities, { pointToProjection }), ].flat(), - [contentBioEntites, drugsBioEntities, chemicalsBioEntities, pointToProjection], + [contentBioEntites, drugsBioEntities, chemicalsBioEntities, pointToProjection, markersEntities], ); const vectorSource = useMemo(() => { return new VectorSource({ - features: [...bioEntityFeatures], + features: [...elementsFeatures], }); - }, [bioEntityFeatures]); + }, [elementsFeatures]); const pinsLayer = useMemo( () => diff --git a/src/models/bioEntitySchema.ts b/src/models/bioEntitySchema.ts index a747e0cd..6f786cc8 100644 --- a/src/models/bioEntitySchema.ts +++ b/src/models/bioEntitySchema.ts @@ -10,7 +10,7 @@ import { structuralStateSchema } from './structuralStateSchema'; import { submodelSchema } from './submodelSchema'; export const bioEntitySchema = z.object({ - id: z.number(), + id: z.union([z.number(), z.string()]), stringType: z.string(), name: z.string(), elementId: z.string(), diff --git a/src/redux/bioEntity/bioEntity.reducers.ts b/src/redux/bioEntity/bioEntity.reducers.ts index 53ad51de..7eb3b66c 100644 --- a/src/redux/bioEntity/bioEntity.reducers.ts +++ b/src/redux/bioEntity/bioEntity.reducers.ts @@ -1,5 +1,5 @@ -import { ActionReducerMapBuilder } from '@reduxjs/toolkit'; import { DEFAULT_ERROR } from '@/constants/errors'; +import { ActionReducerMapBuilder } from '@reduxjs/toolkit'; import { getBioEntity, getMultiBioEntity } from './bioEntity.thunks'; import { BioEntityContentsState } from './bioEntity.types'; @@ -49,3 +49,8 @@ export const getMultiBioEntityContentsReducer = ( // TODO: error management to be discussed in the team }); }; + +export const clearBioEntitiesDataReducer = (state: BioEntityContentsState): void => { + state.data = []; + state.loading = 'idle'; +}; diff --git a/src/redux/bioEntity/bioEntity.selectors.ts b/src/redux/bioEntity/bioEntity.selectors.ts index 8b39954f..b9566eb7 100644 --- a/src/redux/bioEntity/bioEntity.selectors.ts +++ b/src/redux/bioEntity/bioEntity.selectors.ts @@ -14,6 +14,12 @@ import { currentModelIdSelector, modelsDataSelector } from '../models/models.sel export const bioEntitySelector = createSelector(rootSelector, state => state.bioEntity); +export const bioEntityDataSelector = createSelector(bioEntitySelector, bioEntity => bioEntity.data); + +export const bioEntityDataListSelector = createSelector(bioEntityDataSelector, bioEntityData => + bioEntityData.map(b => b.data || []).flat(), +); + export const bioEntitiesForSelectedSearchElement = createSelector( bioEntitySelector, currentSelectedSearchElement, diff --git a/src/redux/bioEntity/bioEntity.slice.ts b/src/redux/bioEntity/bioEntity.slice.ts index 14821569..428e00ce 100644 --- a/src/redux/bioEntity/bioEntity.slice.ts +++ b/src/redux/bioEntity/bioEntity.slice.ts @@ -1,6 +1,7 @@ import { BioEntityContentsState } from '@/redux/bioEntity/bioEntity.types'; import { createSlice } from '@reduxjs/toolkit'; import { + clearBioEntitiesDataReducer, getBioEntityContentsReducer, getMultiBioEntityContentsReducer, } from './bioEntity.reducers'; @@ -14,11 +15,15 @@ const initialState: BioEntityContentsState = { export const bioEntityContentsSlice = createSlice({ name: 'bioEntityContents', initialState, - reducers: {}, + reducers: { + clearBioEntitiesData: clearBioEntitiesDataReducer, + }, extraReducers: builder => { getBioEntityContentsReducer(builder); getMultiBioEntityContentsReducer(builder); }, }); +export const { clearBioEntitiesData } = bioEntityContentsSlice.actions; + export default bioEntityContentsSlice.reducer; diff --git a/src/redux/chemicals/chemicals.selectors.ts b/src/redux/chemicals/chemicals.selectors.ts index f2d10808..03f829f8 100644 --- a/src/redux/chemicals/chemicals.selectors.ts +++ b/src/redux/chemicals/chemicals.selectors.ts @@ -8,6 +8,12 @@ import { currentModelIdSelector } from '../models/models.selectors'; export const chemicalsSelector = createSelector(rootSelector, state => state.chemicals); +export const chemicalsDataSelector = createSelector(chemicalsSelector, chemicals => chemicals.data); + +export const chemicalsDataListSelector = createSelector(chemicalsDataSelector, chemicalsData => + chemicalsData.map(c => c.data || []).flat(), +); + export const chemicalsForSelectedSearchElementSelector = createSelector( chemicalsSelector, currentSelectedSearchElement, diff --git a/src/redux/contextMenu/contextMenu.types.ts b/src/redux/contextMenu/contextMenu.types.ts index d95c38c9..7ab84923 100644 --- a/src/redux/contextMenu/contextMenu.types.ts +++ b/src/redux/contextMenu/contextMenu.types.ts @@ -4,5 +4,5 @@ export interface ContextMenuState { isOpen: boolean; coordinates: Pixel; uniprot: string; - currentSelectedBioEntityId: number; + currentSelectedBioEntityId: number | string; } diff --git a/src/redux/drawer/drawer.types.ts b/src/redux/drawer/drawer.types.ts index f4104f25..a348517b 100644 --- a/src/redux/drawer/drawer.types.ts +++ b/src/redux/drawer/drawer.types.ts @@ -20,7 +20,7 @@ export type ReactionDrawerState = { }; export type BioEntityDrawerState = { - bioentityId?: number; + bioentityId?: number | string; drugs: KeyedFetchDataState<Drug[], []>; chemicals: KeyedFetchDataState<Chemical[], []>; }; @@ -41,5 +41,5 @@ export type OpenSearchDrawerWithSelectedTabReducerAction = export type OpenReactionDrawerByIdPayload = number; export type OpenReactionDrawerByIdAction = PayloadAction<OpenReactionDrawerByIdPayload>; -export type OpenBioEntityDrawerByIdPayload = number; +export type OpenBioEntityDrawerByIdPayload = number | string; export type OpenBioEntityDrawerByIdAction = PayloadAction<OpenBioEntityDrawerByIdPayload>; diff --git a/src/redux/drugs/drugs.selectors.ts b/src/redux/drugs/drugs.selectors.ts index 6790c081..45e9c16c 100644 --- a/src/redux/drugs/drugs.selectors.ts +++ b/src/redux/drugs/drugs.selectors.ts @@ -8,6 +8,12 @@ import { currentModelIdSelector } from '../models/models.selectors'; export const drugsSelector = createSelector(rootSelector, state => state.drugs); +export const drugsDataSelector = createSelector(drugsSelector, drugs => drugs.data); + +export const drugsDataListSelector = createSelector(drugsDataSelector, drugsData => + drugsData.map(d => d.data || []).flat(), +); + export const drugsForSelectedSearchElementSelector = createSelector( drugsSelector, currentSelectedSearchElement, diff --git a/src/redux/markers/markers.constants.ts b/src/redux/markers/markers.constants.ts new file mode 100644 index 00000000..576e0557 --- /dev/null +++ b/src/redux/markers/markers.constants.ts @@ -0,0 +1,5 @@ +import { MarkersState } from './markers.types'; + +export const MARKERS_INITIAL_STATE: MarkersState = { + data: [], +}; diff --git a/src/redux/markers/markers.mock.ts b/src/redux/markers/markers.mock.ts new file mode 100644 index 00000000..3f7df875 --- /dev/null +++ b/src/redux/markers/markers.mock.ts @@ -0,0 +1,5 @@ +import { MarkersState } from './markers.types'; + +export const MARKERS_INITIAL_STATE_MOCK: MarkersState = { + data: [], +}; diff --git a/src/redux/markers/markers.reducers.ts b/src/redux/markers/markers.reducers.ts new file mode 100644 index 00000000..d0cad10d --- /dev/null +++ b/src/redux/markers/markers.reducers.ts @@ -0,0 +1,31 @@ +import { PayloadAction } from '@reduxjs/toolkit'; +import { Marker, MarkersState } from './markers.types'; + +export const setMarkersDataReducer = ( + state: MarkersState, + action: PayloadAction<Marker[]>, +): void => { + const { payload } = action; + + state.data = payload; +}; + +export const addMarkerToMarkersDataReducer = ( + state: MarkersState, + action: PayloadAction<Marker>, +): void => { + const { payload } = action; + + state.data.push(payload); +}; + +export const removeMarkerFromMarkersDataReducer = ( + state: MarkersState, + action: PayloadAction<string>, +): void => { + const currentMarkers = state.data; + const { payload: markerId } = action; + const newMarkers = currentMarkers.filter(m => m.id !== markerId); + + state.data = newMarkers; +}; diff --git a/src/redux/markers/markers.selectors.ts b/src/redux/markers/markers.selectors.ts new file mode 100644 index 00000000..da6987b3 --- /dev/null +++ b/src/redux/markers/markers.selectors.ts @@ -0,0 +1,33 @@ +import { createSelector } from '@reduxjs/toolkit'; +import { currentModelIdSelector } from '../models/models.selectors'; +import { rootSelector } from '../root/root.selectors'; +import { MarkerSurface } from './markers.types'; +import { isMarkerSurface } from './markers.utils'; + +export const markersSelector = createSelector(rootSelector, state => state.markers); + +export const markersDataSelector = createSelector(markersSelector, markers => markers.data); + +export const markersDataOfCurrentMapSelector = createSelector( + markersDataSelector, + currentModelIdSelector, + (markers, modelId) => markers.filter(p => (p.modelId ? p.modelId === modelId : true)), +); + +export const markersPinsDataSelector = createSelector(markersDataSelector, markersData => + markersData.filter(m => m.type === 'pin'), +); + +export const markersPinsOfCurrentMapDataSelector = createSelector( + markersDataOfCurrentMapSelector, + markersData => markersData.filter(m => m.type === 'pin'), +); + +export const markersSufraceSelector = createSelector(markersDataSelector, markersData => + markersData.filter(m => m.type === 'surface'), +); + +export const markersSufraceOfCurrentMapDataSelector = createSelector( + markersDataOfCurrentMapSelector, + (markers): MarkerSurface[] => markers.filter(isMarkerSurface), +); diff --git a/src/redux/markers/markers.slice.ts b/src/redux/markers/markers.slice.ts new file mode 100644 index 00000000..d37bd497 --- /dev/null +++ b/src/redux/markers/markers.slice.ts @@ -0,0 +1,23 @@ +import { createSlice } from '@reduxjs/toolkit'; +import { MARKERS_INITIAL_STATE } from './markers.constants'; +import { + addMarkerToMarkersDataReducer, + removeMarkerFromMarkersDataReducer, + setMarkersDataReducer, +} from './markers.reducers'; + +const markersSlice = createSlice({ + name: 'markers', + initialState: MARKERS_INITIAL_STATE, + reducers: { + setMarkersData: setMarkersDataReducer, + addMarkerToMarkersData: addMarkerToMarkersDataReducer, + removeMarkerFromMarkersData: removeMarkerFromMarkersDataReducer, + }, + extraReducers: () => {}, +}); + +export const { setMarkersData, addMarkerToMarkersData, removeMarkerFromMarkersData } = + markersSlice.actions; + +export default markersSlice.reducer; diff --git a/src/redux/markers/markers.types.ts b/src/redux/markers/markers.types.ts new file mode 100644 index 00000000..4ea83078 --- /dev/null +++ b/src/redux/markers/markers.types.ts @@ -0,0 +1,30 @@ +export type MarkerType = 'pin' | 'surface'; + +interface MarkerBase { + type: MarkerType; + id: string; + color: string; + opacity: number; + x: number; + y: number; + number?: number; + modelId?: number; +} + +export interface MarkerPin extends MarkerBase { + width?: number; + height?: number; +} + +export interface MarkerSurface extends MarkerBase { + width: number; + height: number; +} + +export type Marker = MarkerPin | MarkerSurface; + +export type MarkerWithoutId = Omit<Marker, 'id'>; + +export interface MarkersState { + data: Marker[]; +} diff --git a/src/redux/markers/markers.utils.ts b/src/redux/markers/markers.utils.ts new file mode 100644 index 00000000..08b862e3 --- /dev/null +++ b/src/redux/markers/markers.utils.ts @@ -0,0 +1,4 @@ +import { Marker, MarkerSurface } from './markers.types'; + +export const isMarkerSurface = (marker: Marker): marker is MarkerSurface => + Boolean(marker?.width && marker?.height && marker.type === 'surface'); diff --git a/src/redux/models/marker.mock.ts b/src/redux/models/marker.mock.ts new file mode 100644 index 00000000..951b5d62 --- /dev/null +++ b/src/redux/models/marker.mock.ts @@ -0,0 +1,25 @@ +import { MarkerPin, MarkerSurface } from '../markers/markers.types'; + +export const SURFACE_MARKER: MarkerSurface = { + type: 'surface', + id: '1', + color: '#000000', + opacity: 0.1, + x: 1200, + y: 500, + width: 100, + height: 50, + number: 0, + modelId: 0, +}; + +export const PIN_MARKER: MarkerPin = { + type: 'pin', + id: '1', + color: '#000000', + opacity: 0.1, + x: 1200, + y: 500, + number: 0, + modelId: 0, +}; diff --git a/src/redux/root/root.fixtures.ts b/src/redux/root/root.fixtures.ts index b27b88cc..66b6151c 100644 --- a/src/redux/root/root.fixtures.ts +++ b/src/redux/root/root.fixtures.ts @@ -1,6 +1,7 @@ import { BACKGROUND_INITIAL_STATE_MOCK } from '../backgrounds/background.mock'; import { BIOENTITY_INITIAL_STATE_MOCK } from '../bioEntity/bioEntity.mock'; import { CHEMICALS_INITIAL_STATE_MOCK } from '../chemicals/chemicals.mock'; +import { COMPARTMENT_PATHWAYS_INITIAL_STATE_MOCK } from '../compartmentPathways/compartmentPathways.mock'; import { CONFIGURATION_INITIAL_STATE } from '../configuration/configuration.adapter'; import { CONTEXT_MENU_INITIAL_STATE } from '../contextMenu/contextMenu.constants'; import { COOKIE_BANNER_INITIAL_STATE_MOCK } from '../cookieBanner/cookieBanner.mock'; @@ -9,19 +10,19 @@ import { DRUGS_INITIAL_STATE_MOCK } from '../drugs/drugs.mock'; import { EXPORT_INITIAL_STATE_MOCK } from '../export/export.mock'; import { LEGEND_INITIAL_STATE_MOCK } from '../legend/legend.mock'; import { initialMapStateFixture } from '../map/map.fixtures'; +import { MARKERS_INITIAL_STATE_MOCK } from '../markers/markers.mock'; import { MODAL_INITIAL_STATE_MOCK } from '../modal/modal.mock'; import { MODELS_INITIAL_STATE_MOCK } from '../models/models.mock'; import { OVERLAY_BIO_ENTITY_INITIAL_STATE_MOCK } from '../overlayBioEntity/overlayBioEntity.mock'; import { OVERLAYS_INITIAL_STATE_MOCK } from '../overlays/overlays.mock'; import { PLUGINS_INITIAL_STATE_MOCK } from '../plugins/plugins.mock'; import { PROJECT_STATE_INITIAL_MOCK } from '../project/project.mock'; +import { PUBLICATIONS_INITIAL_STATE_MOCK } from '../publications/publications.mock'; import { REACTIONS_STATE_INITIAL_MOCK } from '../reactions/reactions.mock'; import { SEARCH_STATE_INITIAL_MOCK } from '../search/search.mock'; +import { STATISTICS_STATE_INITIAL_MOCK } from '../statistics/statistics.mock'; import { RootState } from '../store'; import { USER_INITIAL_STATE_MOCK } from '../user/user.mock'; -import { STATISTICS_STATE_INITIAL_MOCK } from '../statistics/statistics.mock'; -import { COMPARTMENT_PATHWAYS_INITIAL_STATE_MOCK } from '../compartmentPathways/compartmentPathways.mock'; -import { PUBLICATIONS_INITIAL_STATE_MOCK } from '../publications/publications.mock'; export const INITIAL_STORE_STATE_MOCK: RootState = { search: SEARCH_STATE_INITIAL_MOCK, @@ -47,4 +48,5 @@ export const INITIAL_STORE_STATE_MOCK: RootState = { publications: PUBLICATIONS_INITIAL_STATE_MOCK, export: EXPORT_INITIAL_STATE_MOCK, plugins: PLUGINS_INITIAL_STATE_MOCK, + markers: MARKERS_INITIAL_STATE_MOCK, }; diff --git a/src/redux/store.ts b/src/redux/store.ts index 6aa4c445..2391ea22 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -22,13 +22,14 @@ import { TypedStartListening, configureStore, } from '@reduxjs/toolkit'; +import compartmentPathwaysReducer from './compartmentPathways/compartmentPathways.slice'; import exportReducer from './export/export.slice'; import legendReducer from './legend/legend.slice'; import { mapListenerMiddleware } from './map/middleware/map.middleware'; +import markersReducer from './markers/markers.slice'; import pluginsReducer from './plugins/plugins.slice'; -import statisticsReducer from './statistics/statistics.slice'; -import compartmentPathwaysReducer from './compartmentPathways/compartmentPathways.slice'; import publicationsReducer from './publications/publications.slice'; +import statisticsReducer from './statistics/statistics.slice'; export const reducers = { search: searchReducer, @@ -54,6 +55,7 @@ export const reducers = { publications: publicationsReducer, export: exportReducer, plugins: pluginsReducer, + markers: markersReducer, }; export const middlewares = [mapListenerMiddleware.middleware]; diff --git a/src/services/pluginsManager/bioEntities/addSingleMarker.ts b/src/services/pluginsManager/bioEntities/addSingleMarker.ts new file mode 100644 index 00000000..0b496795 --- /dev/null +++ b/src/services/pluginsManager/bioEntities/addSingleMarker.ts @@ -0,0 +1,12 @@ +import { addMarkerToMarkersData } from '@/redux/markers/markers.slice'; +import { Marker, MarkerWithoutId } from '@/redux/markers/markers.types'; +import { store } from '@/redux/store'; +import { v4 as uuidv4 } from 'uuid'; + +export const addSingleMarker = (markerWithoutId: MarkerWithoutId): Marker => { + const { dispatch } = store; + const marker = { id: uuidv4(), ...markerWithoutId }; + dispatch(addMarkerToMarkersData(marker)); + + return marker; +}; diff --git a/src/services/pluginsManager/bioEntities/clearAllElements.ts b/src/services/pluginsManager/bioEntities/clearAllElements.ts new file mode 100644 index 00000000..9177d501 --- /dev/null +++ b/src/services/pluginsManager/bioEntities/clearAllElements.ts @@ -0,0 +1,27 @@ +import { clearBioEntitiesData } from '@/redux/bioEntity/bioEntity.slice'; +import { clearChemicalsData } from '@/redux/chemicals/chemicals.slice'; +import { clearDrugsData } from '@/redux/drugs/drugs.slice'; +import { setMarkersData } from '@/redux/markers/markers.slice'; +import { store } from '@/redux/store'; + +type ElementName = 'drugs' | 'chemicals' | 'content' | 'marker'; + +export const clearAllElements = (elements: ElementName[]): void => { + const { dispatch } = store; + + if (elements.includes('content')) { + dispatch(clearBioEntitiesData()); + } + + if (elements.includes('chemicals')) { + dispatch(clearChemicalsData()); + } + + if (elements.includes('drugs')) { + dispatch(clearDrugsData()); + } + + if (elements.includes('marker')) { + dispatch(setMarkersData([])); + } +}; diff --git a/src/services/pluginsManager/bioEntities/getAllChemicals.ts b/src/services/pluginsManager/bioEntities/getAllChemicals.ts new file mode 100644 index 00000000..92cf1727 --- /dev/null +++ b/src/services/pluginsManager/bioEntities/getAllChemicals.ts @@ -0,0 +1,10 @@ +import { chemicalsDataListSelector } from '@/redux/chemicals/chemicals.selectors'; +import { store } from '@/redux/store'; +import { Chemical } from '@/types/models'; + +export const getAllChemicals = (): Chemical[] => { + const { getState } = store; + const chemicals = chemicalsDataListSelector(getState()); + + return chemicals || []; +}; diff --git a/src/services/pluginsManager/bioEntities/getAllContent.ts b/src/services/pluginsManager/bioEntities/getAllContent.ts new file mode 100644 index 00000000..af8f6d11 --- /dev/null +++ b/src/services/pluginsManager/bioEntities/getAllContent.ts @@ -0,0 +1,10 @@ +import { bioEntityDataListSelector } from '@/redux/bioEntity/bioEntity.selectors'; +import { store } from '@/redux/store'; +import { BioEntityContent } from '@/types/models'; + +export const getAllBioEntities = (): BioEntityContent[] => { + const { getState } = store; + const bioEntities = bioEntityDataListSelector(getState()); + + return bioEntities || []; +}; diff --git a/src/services/pluginsManager/bioEntities/getAllDrugs.ts b/src/services/pluginsManager/bioEntities/getAllDrugs.ts new file mode 100644 index 00000000..3cd7b8c8 --- /dev/null +++ b/src/services/pluginsManager/bioEntities/getAllDrugs.ts @@ -0,0 +1,10 @@ +import { drugsDataListSelector } from '@/redux/drugs/drugs.selectors'; +import { store } from '@/redux/store'; +import { Drug } from '@/types/models'; + +export const getAllDrugs = (): Drug[] => { + const { getState } = store; + const drugs = drugsDataListSelector(getState()); + + return drugs || []; +}; diff --git a/src/services/pluginsManager/bioEntities/getAllMarkers.ts b/src/services/pluginsManager/bioEntities/getAllMarkers.ts new file mode 100644 index 00000000..1ed70af6 --- /dev/null +++ b/src/services/pluginsManager/bioEntities/getAllMarkers.ts @@ -0,0 +1,10 @@ +import { markersDataSelector } from '@/redux/markers/markers.selectors'; +import { Marker } from '@/redux/markers/markers.types'; +import { store } from '@/redux/store'; + +export const getAllMarkers = (): Marker[] => { + const { getState } = store; + const markers = markersDataSelector(getState()); + + return markers; +}; diff --git a/src/services/pluginsManager/bioEntities/getShownElements.ts b/src/services/pluginsManager/bioEntities/getShownElements.ts new file mode 100644 index 00000000..89cf67bd --- /dev/null +++ b/src/services/pluginsManager/bioEntities/getShownElements.ts @@ -0,0 +1,22 @@ +import { searchedBioEntitesSelectorOfCurrentMap } from '@/redux/bioEntity/bioEntity.selectors'; +import { searchedChemicalsBioEntitesOfCurrentMapSelector } from '@/redux/chemicals/chemicals.selectors'; +import { searchedDrugsBioEntitesOfCurrentMapSelector } from '@/redux/drugs/drugs.selectors'; +import { markersPinsOfCurrentMapDataSelector } from '@/redux/markers/markers.selectors'; +import { store } from '@/redux/store'; +import { GetShownElementsPluginMethodResult } from './getShownElements.types'; + +export const getShownElements = (): GetShownElementsPluginMethodResult => { + const { getState } = store; + + const content = searchedBioEntitesSelectorOfCurrentMap(getState()); + const chemicals = searchedChemicalsBioEntitesOfCurrentMapSelector(getState()); + const drugs = searchedDrugsBioEntitesOfCurrentMapSelector(getState()); + const markers = markersPinsOfCurrentMapDataSelector(getState()); + + return { + content, + chemicals, + drugs, + markers, + }; +}; diff --git a/src/services/pluginsManager/bioEntities/getShownElements.types.ts b/src/services/pluginsManager/bioEntities/getShownElements.types.ts new file mode 100644 index 00000000..dcbacd29 --- /dev/null +++ b/src/services/pluginsManager/bioEntities/getShownElements.types.ts @@ -0,0 +1,9 @@ +import { Marker } from '@/redux/markers/markers.types'; +import { BioEntity } from '@/types/models'; + +export interface GetShownElementsPluginMethodResult { + content: BioEntity[]; + drugs: BioEntity[]; + chemicals: BioEntity[]; + markers: Marker[]; +} diff --git a/src/services/pluginsManager/bioEntities/index.ts b/src/services/pluginsManager/bioEntities/index.ts new file mode 100644 index 00000000..0e77668c --- /dev/null +++ b/src/services/pluginsManager/bioEntities/index.ts @@ -0,0 +1,23 @@ +import { addSingleMarker } from './addSingleMarker'; +import { clearAllElements } from './clearAllElements'; +import { getAllChemicals } from './getAllChemicals'; +import { getAllBioEntities } from './getAllContent'; +import { getAllDrugs } from './getAllDrugs'; +import { getAllMarkers } from './getAllMarkers'; +import { getShownElements } from './getShownElements'; +import { removeAllMarkers } from './removeAllMarkers'; +import { removeSingleMarker } from './removeSingleMarker'; + +export const bioEntitiesMethods = { + getAllBioEntities, + getAllChemicals, + getAllDrugs, + getAllMarkers, + getShownElements, + addSingleMarker, + removeSingleMarker, + removeAllMarkers, + clearAllElements, +}; + +export type BioEntitiesMethods = typeof bioEntitiesMethods; diff --git a/src/services/pluginsManager/bioEntities/removeAllMarkers.ts b/src/services/pluginsManager/bioEntities/removeAllMarkers.ts new file mode 100644 index 00000000..3224d83b --- /dev/null +++ b/src/services/pluginsManager/bioEntities/removeAllMarkers.ts @@ -0,0 +1,7 @@ +import { setMarkersData } from '@/redux/markers/markers.slice'; +import { store } from '@/redux/store'; + +export const removeAllMarkers = (): void => { + const { dispatch } = store; + dispatch(setMarkersData([])); +}; diff --git a/src/services/pluginsManager/bioEntities/removeSingleMarker.ts b/src/services/pluginsManager/bioEntities/removeSingleMarker.ts new file mode 100644 index 00000000..d8254453 --- /dev/null +++ b/src/services/pluginsManager/bioEntities/removeSingleMarker.ts @@ -0,0 +1,7 @@ +import { removeMarkerFromMarkersData } from '@/redux/markers/markers.slice'; +import { store } from '@/redux/store'; + +export const removeSingleMarker = (id: string): void => { + const { dispatch } = store; + dispatch(removeMarkerFromMarkersData(id)); +}; diff --git a/src/services/pluginsManager/pluginsManager.ts b/src/services/pluginsManager/pluginsManager.ts index 66ad3b93..fce57569 100644 --- a/src/services/pluginsManager/pluginsManager.ts +++ b/src/services/pluginsManager/pluginsManager.ts @@ -2,6 +2,7 @@ import { PLUGINS_CONTENT_ELEMENT_ATTR_NAME, PLUGINS_CONTENT_ELEMENT_ID } from '@ import { registerPlugin } from '@/redux/plugins/plugins.thunks'; import { store } from '@/redux/store'; import md5 from 'crypto-js/md5'; +import { bioEntitiesMethods } from './bioEntities'; import type { PluginsManagerType } from './pluginsManager.types'; import { configurationMapper } from './pluginsManager.utils'; @@ -19,6 +20,9 @@ export const PluginsManager: PluginsManagerType = { plugins: { registerPlugin: PluginsManager.registerPlugin, }, + data: { + bioEntities: bioEntitiesMethods, + }, }; const unsubscribe = store.subscribe(() => { diff --git a/src/types/OLrendering.ts b/src/types/OLrendering.ts index 9769064d..54f0c7ab 100644 --- a/src/types/OLrendering.ts +++ b/src/types/OLrendering.ts @@ -3,7 +3,7 @@ import { Color, GeneVariant } from './models'; export type OverlayBioEntityRenderType = 'line' | 'rectangle' | 'submap-link'; export type OverlayBioEntityRender = { - id: number; + id: number | string; modelId: number; /** bottom left corner of whole element, Xmin */ x1: number; @@ -18,6 +18,7 @@ export type OverlayBioEntityRender = { value: number | null; overlayId: number; color: Color | null; + hexColor?: string; type: OverlayBioEntityRenderType; geneVariants?: GeneVariant[] | null; }; -- GitLab