@vitessce/heatmap 2.0.3 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/{deflate.1b70f605.mjs → deflate-16770a05.js} +2 -2
  2. package/dist/{index.35c24bfa.mjs → index-516800f9.js} +132612 -120210
  3. package/dist/{index.mjs → index.js} +2 -3
  4. package/dist/{jpeg.1b7865ea.mjs → jpeg-aaa6dcb6.js} +1 -1
  5. package/dist/{lerc.ff3140f6.mjs → lerc-4c94e66f.js} +76 -5
  6. package/dist/{lzw.1036ab46.mjs → lzw-072df1d0.js} +1 -1
  7. package/dist/{packbits.088a4e84.mjs → packbits-3dcafae7.js} +1 -1
  8. package/dist/{pako.esm.4b234125.mjs → pako.esm-68f84e2a.js} +97 -15
  9. package/dist/{raw.b3ce459e.mjs → raw-282aecb8.js} +1 -1
  10. package/dist/{webimage.4cecc301.mjs → webimage-0d9c3bfd.js} +1 -1
  11. package/dist-tsc/Heatmap.d.ts +43 -0
  12. package/dist-tsc/Heatmap.d.ts.map +1 -0
  13. package/dist-tsc/Heatmap.js +698 -0
  14. package/dist-tsc/Heatmap.test.d.ts +2 -0
  15. package/dist-tsc/Heatmap.test.d.ts.map +1 -0
  16. package/dist-tsc/Heatmap.test.fixtures.d.ts +7 -0
  17. package/dist-tsc/Heatmap.test.fixtures.d.ts.map +1 -0
  18. package/dist-tsc/Heatmap.test.fixtures.js +20 -0
  19. package/dist-tsc/Heatmap.test.js +17 -0
  20. package/dist-tsc/HeatmapOptions.d.ts +2 -0
  21. package/dist-tsc/HeatmapOptions.d.ts.map +1 -0
  22. package/dist-tsc/HeatmapOptions.js +29 -0
  23. package/dist-tsc/HeatmapSubscriber.d.ts +19 -0
  24. package/dist-tsc/HeatmapSubscriber.d.ts.map +1 -0
  25. package/dist-tsc/HeatmapSubscriber.js +120 -0
  26. package/dist-tsc/HeatmapTooltipSubscriber.d.ts +2 -0
  27. package/dist-tsc/HeatmapTooltipSubscriber.d.ts.map +1 -0
  28. package/dist-tsc/HeatmapTooltipSubscriber.js +24 -0
  29. package/dist-tsc/HeatmapWorkerPool.d.ts +28 -0
  30. package/dist-tsc/HeatmapWorkerPool.d.ts.map +1 -0
  31. package/dist-tsc/HeatmapWorkerPool.js +49 -0
  32. package/dist-tsc/index.d.ts +3 -0
  33. package/dist-tsc/index.d.ts.map +1 -0
  34. package/dist-tsc/index.js +2 -2
  35. package/dist-tsc/utils.d.ts +49 -0
  36. package/dist-tsc/utils.d.ts.map +1 -0
  37. package/dist-tsc/utils.js +181 -0
  38. package/dist-tsc/utils.test.d.ts +2 -0
  39. package/dist-tsc/utils.test.d.ts.map +1 -0
  40. package/dist-tsc/utils.test.js +71 -0
  41. package/package.json +26 -14
  42. package/src/Heatmap.js +45 -30
  43. package/src/Heatmap.test.jsx +7 -6
  44. package/src/HeatmapOptions.js +27 -4
  45. package/src/HeatmapSubscriber.js +62 -26
  46. package/src/index.js +2 -2
  47. package/src/utils.js +1 -1
  48. package/src/utils.test.js +1 -1
@@ -0,0 +1,71 @@
1
+ import { mouseToHeatmapPosition, heatmapToMousePosition } from './utils.js';
2
+ describe('heatmap tooltip utils', () => {
3
+ it('transforms mouse coordinates to row and column indices when zoomed out', () => {
4
+ const mouseX = 35;
5
+ const mouseY = 78;
6
+ const [colI, rowI] = mouseToHeatmapPosition(mouseX, mouseY, {
7
+ offsetLeft: 10,
8
+ offsetTop: 10,
9
+ targetX: 0,
10
+ targetY: 0,
11
+ scaleFactor: 1,
12
+ matrixWidth: 100,
13
+ matrixHeight: 100,
14
+ numRows: 5,
15
+ numCols: 4,
16
+ });
17
+ expect(colI).toEqual(1);
18
+ expect(rowI).toEqual(3);
19
+ });
20
+ it('transforms mouse coordinates to row and column indices when zoomed in', () => {
21
+ const mouseX = 35;
22
+ const mouseY = 78;
23
+ const [colI, rowI] = mouseToHeatmapPosition(mouseX, mouseY, {
24
+ offsetLeft: 10,
25
+ offsetTop: 10,
26
+ targetX: 21,
27
+ targetY: -11,
28
+ scaleFactor: 4,
29
+ matrixWidth: 100,
30
+ matrixHeight: 100,
31
+ numRows: 5,
32
+ numCols: 4,
33
+ });
34
+ expect(colI).toEqual(2);
35
+ expect(rowI).toEqual(2);
36
+ });
37
+ it('transforms row and column indices when zoomed out', () => {
38
+ const colI = 1;
39
+ const rowI = 3;
40
+ const [mouseX, mouseY] = heatmapToMousePosition(colI, rowI, {
41
+ offsetLeft: 10,
42
+ offsetTop: 10,
43
+ targetX: 0,
44
+ targetY: 0,
45
+ scaleFactor: 1,
46
+ matrixWidth: 100,
47
+ matrixHeight: 100,
48
+ numRows: 5,
49
+ numCols: 4,
50
+ });
51
+ expect(mouseX).toEqual(47.5);
52
+ expect(mouseY).toEqual(80);
53
+ });
54
+ it('transforms row and column indices when zoomed in', () => {
55
+ const colI = 2;
56
+ const rowI = 2;
57
+ const [mouseX, mouseY] = heatmapToMousePosition(colI, rowI, {
58
+ offsetLeft: 10,
59
+ offsetTop: 10,
60
+ targetX: 21,
61
+ targetY: -11,
62
+ scaleFactor: 4,
63
+ matrixWidth: 100,
64
+ matrixHeight: 100,
65
+ numRows: 5,
66
+ numCols: 4,
67
+ });
68
+ expect(mouseX).toEqual(26);
69
+ expect(mouseY).toEqual(104);
70
+ });
71
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitessce/heatmap",
3
- "version": "2.0.3",
3
+ "version": "3.0.0",
4
4
  "author": "Gehlenborg Lab",
5
5
  "homepage": "http://vitessce.io",
6
6
  "repository": {
@@ -8,28 +8,33 @@
8
8
  "url": "git+https://github.com/vitessce/vitessce.git"
9
9
  },
10
10
  "license": "MIT",
11
- "main": "dist/index.mjs",
11
+ "type": "module",
12
+ "main": "dist/index.js",
12
13
  "files": [
14
+ "src",
13
15
  "dist",
14
- "src"
16
+ "dist-tsc"
15
17
  ],
16
18
  "dependencies": {
17
19
  "@material-ui/core": "~4.12.3",
18
- "lodash": "^4.17.21",
20
+ "lodash-es": "^4.17.21",
19
21
  "plur": "^5.1.0",
20
- "uuid": "^3.3.2",
21
- "@vitessce/constants-internal": "2.0.3",
22
- "@vitessce/gl": "2.0.3",
23
- "@vitessce/sets-utils": "2.0.3",
24
- "@vitessce/tooltip": "2.0.3",
25
- "@vitessce/utils": "2.0.3",
26
- "@vitessce/vit-s": "2.0.3",
27
- "@vitessce/workers": "2.0.3"
22
+ "uuid": "^9.0.0",
23
+ "@vitessce/constants-internal": "3.0.0",
24
+ "@vitessce/gl": "3.0.0",
25
+ "@vitessce/legend": "3.0.0",
26
+ "@vitessce/sets-utils": "3.0.0",
27
+ "@vitessce/tooltip": "3.0.0",
28
+ "@vitessce/utils": "3.0.0",
29
+ "@vitessce/vit-s": "3.0.0",
30
+ "@vitessce/workers": "3.0.0"
28
31
  },
29
32
  "devDependencies": {
30
33
  "react": "^18.0.0",
31
- "vite": "^3.0.0",
32
- "vitest": "^0.23.4"
34
+ "vite": "^4.3.0",
35
+ "vitest": "^0.23.4",
36
+ "@testing-library/jest-dom": "^5.16.4",
37
+ "@testing-library/react": "^13.3.0"
33
38
  },
34
39
  "peerDependencies": {
35
40
  "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
@@ -37,5 +42,12 @@
37
42
  "scripts": {
38
43
  "bundle": "pnpm exec vite build -c ../../../scripts/vite.config.js",
39
44
  "test": "pnpm exec vitest --run -r ../../../ --dir ."
45
+ },
46
+ "module": "dist/index.js",
47
+ "exports": {
48
+ ".": {
49
+ "types": "./dist-tsc/index.d.ts",
50
+ "import": "./dist/index.js"
51
+ }
40
52
  }
41
53
  }
package/src/Heatmap.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import React, {
3
3
  useRef, useState, useCallback, useMemo, useEffect, useReducer, forwardRef,
4
4
  } from 'react';
5
- import uuidv4 from 'uuid/v4';
5
+ import { v4 as uuidv4 } from 'uuid';
6
6
  import {
7
7
  deck, luma,
8
8
  HeatmapCompositeTextLayer,
@@ -17,9 +17,7 @@ import {
17
17
  DATA_TEXTURE_SIZE,
18
18
  PIXELATED_TEXTURE_PARAMETERS,
19
19
  } from '@vitessce/gl';
20
- import range from 'lodash/range';
21
- import clamp from 'lodash/clamp';
22
- import isEqual from 'lodash/isEqual';
20
+ import { range, clamp, isEqual } from 'lodash-es';
23
21
  import {
24
22
  getLongestString,
25
23
  DEFAULT_GL_OPTIONS,
@@ -38,8 +36,8 @@ import {
38
36
  mouseToHeatmapPosition,
39
37
  heatmapToMousePosition,
40
38
  mouseToCellColorPosition,
41
- } from './utils';
42
- import HeatmapWorkerPool from './HeatmapWorkerPool';
39
+ } from './utils.js';
40
+ import HeatmapWorkerPool from './HeatmapWorkerPool.js';
43
41
  // Only allocate the memory once for the container
44
42
  const paddedExpressionContainer = new Uint8Array(DATA_TEXTURE_SIZE * DATA_TEXTURE_SIZE);
45
43
 
@@ -122,6 +120,9 @@ const Heatmap = forwardRef((props, deckRef) => {
122
120
  useDevicePixels = 1,
123
121
  hideObservationLabels = false,
124
122
  hideVariableLabels = false,
123
+ onHeatmapClick,
124
+ setColorEncoding,
125
+ featureIndex,
125
126
  } = props;
126
127
 
127
128
  const viewState = {
@@ -146,6 +147,7 @@ const Heatmap = forwardRef((props, deckRef) => {
146
147
  const [axisLeftLabels, setAxisLeftLabels] = useState([]);
147
148
  const [axisTopLabels, setAxisTopLabels] = useState([]);
148
149
  const [numCellColorTracks, setNumCellColorTracks] = useState([]);
150
+ const [cursorType, setCursorType] = useState('default');
149
151
 
150
152
 
151
153
  // Since we are storing the tile data in a ref,
@@ -688,8 +690,9 @@ const Heatmap = forwardRef((props, deckRef) => {
688
690
  matrixWidth, tileWidth, tileHeight, transpose]);
689
691
 
690
692
 
693
+ const showText = width > 0 && height > 0;
691
694
  const layers = heatmapLayers
692
- .concat(textLayers)
695
+ .concat(showText ? textLayers : [])
693
696
  .concat(...cellColorsLayersList);
694
697
 
695
698
  // Set up the onHover function.
@@ -698,6 +701,9 @@ const Heatmap = forwardRef((props, deckRef) => {
698
701
  return;
699
702
  }
700
703
 
704
+ let highlightedCell = null;
705
+ let highlightedGene = null;
706
+
701
707
  const { x: mouseX, y: mouseY } = event.offsetCenter;
702
708
 
703
709
  const [trackColI, trackI] = mouseToCellColorPosition(mouseX, mouseY, {
@@ -717,13 +723,18 @@ const Heatmap = forwardRef((props, deckRef) => {
717
723
  numCols: width,
718
724
  });
719
725
 
726
+ // we are hovering over a gene colored track
720
727
  if (trackI === null || trackColI === null) {
721
728
  setTrackHighlight(null);
722
- } else {
723
- const obsI = expression.rows.indexOf(axisTopLabels[trackColI]);
729
+ setColorEncoding('geneSelection');
730
+ } else { // we are hovering over a cell colored track
731
+ const obsI = expression.rows.indexOf(transpose
732
+ ? axisTopLabels[trackColI]
733
+ : axisLeftLabels[trackColI]);
724
734
  const cellIndex = expression.rows[obsI];
725
-
726
735
  setTrackHighlight([cellIndex, trackI, mouseX, mouseY]);
736
+ highlightedCell = cellIndex;
737
+ setColorEncoding('cellSelection');
727
738
  }
728
739
 
729
740
  const [colI, rowI] = mouseToHeatmapPosition(mouseX, mouseY, {
@@ -738,22 +749,6 @@ const Heatmap = forwardRef((props, deckRef) => {
738
749
  numCols: width,
739
750
  });
740
751
 
741
- if (colI === null) {
742
- if (transpose) {
743
- setCellHighlight(null);
744
- } else {
745
- setGeneHighlight(null);
746
- }
747
- }
748
-
749
- if (rowI === null) {
750
- if (transpose) {
751
- setGeneHighlight(null);
752
- } else {
753
- setCellHighlight(null);
754
- }
755
- }
756
-
757
752
  const obsI = expression.rows.indexOf(transpose
758
753
  ? axisTopLabels[colI]
759
754
  : axisLeftLabels[rowI]);
@@ -762,13 +757,32 @@ const Heatmap = forwardRef((props, deckRef) => {
762
757
  : axisTopLabels[colI]);
763
758
 
764
759
  const obsId = expression.rows[obsI];
765
- const varId = expression.cols[varI];
760
+
761
+ // We need to use featureIndex here,
762
+ // because expression.cols may be mapped to
763
+ // use featureLabels (if those were available in the dataset).
764
+ // Highlights and selections are assumed to be in terms of
765
+ // obsIndex/featureIndex (as opposed to obsLabels/featureLabels).
766
+ const varId = featureIndex[varI];
766
767
 
767
768
  if (setComponentHover) {
768
769
  setComponentHover();
769
770
  }
770
- setCellHighlight(obsId || null);
771
- setGeneHighlight(varId || null);
771
+
772
+ if (obsId) {
773
+ highlightedCell = obsId;
774
+ }
775
+ if (varId) {
776
+ highlightedGene = varId;
777
+ }
778
+
779
+ setCellHighlight(highlightedCell);
780
+ setGeneHighlight(highlightedGene);
781
+ if (highlightedCell !== null || highlightedGene !== null) {
782
+ setCursorType('pointer');
783
+ } else {
784
+ setCursorType('default');
785
+ }
772
786
  }
773
787
 
774
788
  const cellColorsViews = useMemo(() => {
@@ -844,12 +858,13 @@ const Heatmap = forwardRef((props, deckRef) => {
844
858
  ]}
845
859
  layers={layers}
846
860
  layerFilter={layerFilter}
847
- getCursor={interactionState => (interactionState.isDragging ? 'grabbing' : 'default')}
861
+ getCursor={interactionState => (interactionState.isDragging ? 'grabbing' : cursorType)}
848
862
  glOptions={DEFAULT_GL_OPTIONS}
849
863
  onViewStateChange={onViewStateChange}
850
864
  viewState={viewState}
851
865
  onHover={onHover}
852
866
  useDevicePixels={useDevicePixels}
867
+ onClick={onHeatmapClick}
853
868
  />
854
869
  );
855
870
  });
@@ -1,17 +1,18 @@
1
1
  /* eslint-disable func-names */
2
2
  import '@testing-library/jest-dom';
3
- import { cleanup, render, screen } from '@testing-library/react'
4
- import { afterEach } from 'vitest'
3
+ import { cleanup, render } from '@testing-library/react';
4
+ import { afterEach } from 'vitest';
5
+ import React from 'react';
5
6
 
6
- import Heatmap from './Heatmap';
7
- import { expressionMatrix, cellColors } from './Heatmap.test.fixtures';
7
+ import Heatmap from './Heatmap.js';
8
+ import { expressionMatrix, cellColors } from './Heatmap.test.fixtures.js';
8
9
 
9
10
  afterEach(() => {
10
- cleanup()
11
+ cleanup();
11
12
  });
12
13
 
13
14
  describe('<Heatmap/>', () => {
14
- it('renders a DeckGL element', function () {
15
+ it('renders a DeckGL element', () => {
15
16
  const { container } = render(
16
17
  <Heatmap
17
18
  uuid="heatmap-0"
@@ -1,8 +1,6 @@
1
1
  import React, { useCallback } from 'react';
2
- import debounce from 'lodash/debounce';
3
- import Slider from '@material-ui/core/Slider';
4
- import TableCell from '@material-ui/core/TableCell';
5
- import TableRow from '@material-ui/core/TableRow';
2
+ import { debounce } from 'lodash-es';
3
+ import { Checkbox, Slider, TableCell, TableRow } from '@material-ui/core';
6
4
  import { usePlotOptionsStyles, OptionsContainer, OptionSelect } from '@vitessce/vit-s';
7
5
  import { GLSL_COLORMAPS } from '@vitessce/gl';
8
6
 
@@ -12,6 +10,8 @@ export default function HeatmapOptions(props) {
12
10
  setGeneExpressionColormap,
13
11
  geneExpressionColormapRange,
14
12
  setGeneExpressionColormapRange,
13
+ tooltipsVisible,
14
+ setTooltipsVisible,
15
15
  } = props;
16
16
 
17
17
  const classes = usePlotOptionsStyles();
@@ -20,6 +20,10 @@ export default function HeatmapOptions(props) {
20
20
  setGeneExpressionColormap(event.target.value);
21
21
  }
22
22
 
23
+ function handleTooltipsVisibilityChange(event) {
24
+ setTooltipsVisible(event.target.checked);
25
+ }
26
+
23
27
  function handleColormapRangeChange(event, value) {
24
28
  setGeneExpressionColormapRange(value);
25
29
  }
@@ -49,6 +53,25 @@ export default function HeatmapOptions(props) {
49
53
  </OptionSelect>
50
54
  </TableCell>
51
55
  </TableRow>
56
+ <TableRow>
57
+ <TableCell className={classes.labelCell}>
58
+ Tooltips Visible
59
+ </TableCell>
60
+ <TableCell className={classes.inputCell}>
61
+ <Checkbox
62
+ className={classes.checkbox}
63
+ /**
64
+ * We have to use "checked" here, not "value".
65
+ * The checkbox state is not persisting with value.
66
+ * For reference, https://v4.mui.com/api/checkbox/
67
+ */
68
+ checked={tooltipsVisible}
69
+ onChange={handleTooltipsVisibilityChange}
70
+ name="gene-expression-colormap-option-toltip-visibility"
71
+ color="default"
72
+ />
73
+ </TableCell>
74
+ </TableRow>
52
75
  <TableRow>
53
76
  <TableCell className={classes.labelCell}>
54
77
  Gene Expression Colormap Range
@@ -5,23 +5,25 @@ import plur from 'plur';
5
5
  import {
6
6
  TitleInfo,
7
7
  useDeckCanvasSize,
8
+ useGetObsMembership,
8
9
  useGetObsInfo,
9
10
  useReady,
10
11
  useUrls,
11
12
  useObsSetsData,
12
13
  useObsFeatureMatrixData,
14
+ useUint8ObsFeatureMatrix,
13
15
  useMultiObsLabels,
14
16
  useFeatureLabelsData,
15
17
  useCoordination, useLoaders,
16
18
  useSetComponentHover, useSetComponentViewInfo,
17
- registerPluginViewType,
18
19
  } from '@vitessce/vit-s';
19
20
  import { capitalize, commaNumber, getCellColors } from '@vitessce/utils';
20
- import { mergeObsSets } from '@vitessce/sets-utils';
21
+ import { mergeObsSets, findLongestCommonPath } from '@vitessce/sets-utils';
21
22
  import { COMPONENT_COORDINATION_TYPES, ViewType } from '@vitessce/constants-internal';
22
- import Heatmap from './Heatmap';
23
- import HeatmapTooltipSubscriber from './HeatmapTooltipSubscriber';
24
- import HeatmapOptions from './HeatmapOptions';
23
+ import { Legend } from '@vitessce/legend';
24
+ import Heatmap from './Heatmap.js';
25
+ import HeatmapTooltipSubscriber from './HeatmapTooltipSubscriber.js';
26
+ import HeatmapOptions from './HeatmapOptions.js';
25
27
 
26
28
 
27
29
  /**
@@ -34,8 +36,6 @@ import HeatmapOptions from './HeatmapOptions';
34
36
  * @param {string} props.title The component title.
35
37
  * @param {boolean} props.transpose Whether to
36
38
  * render as cell-by-gene or gene-by-cell.
37
- * @param {boolean} props.disableTooltip Whether to disable the
38
- * tooltip on mouse hover.
39
39
  */
40
40
  export function HeatmapSubscriber(props) {
41
41
  const {
@@ -46,7 +46,6 @@ export function HeatmapSubscriber(props) {
46
46
  transpose,
47
47
  observationsLabelOverride,
48
48
  variablesLabelOverride,
49
- disableTooltip = false,
50
49
  title = 'Heatmap',
51
50
  } = props;
52
51
 
@@ -71,17 +70,21 @@ export function HeatmapSubscriber(props) {
71
70
  additionalObsSets: additionalCellSets,
72
71
  featureValueColormap: geneExpressionColormap,
73
72
  featureValueColormapRange: geneExpressionColormapRange,
73
+ tooltipsVisible,
74
74
  }, {
75
75
  setHeatmapZoomX: setZoomX,
76
76
  setHeatmapZoomY: setZoomY,
77
77
  setHeatmapTargetX: setTargetX,
78
78
  setHeatmapTargetY: setTargetY,
79
79
  setObsHighlight: setCellHighlight,
80
+ setFeatureSelection: setGeneSelection,
81
+ setObsColorEncoding: setCellColorEncoding,
80
82
  setFeatureHighlight: setGeneHighlight,
81
83
  setObsSetSelection: setCellSetSelection,
82
84
  setObsSetColor: setCellSetColor,
83
85
  setFeatureValueColormapRange: setGeneExpressionColormapRange,
84
86
  setFeatureValueColormap: setGeneExpressionColormap,
87
+ setTooltipsVisible,
85
88
  }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.HEATMAP], coordinationScopes);
86
89
 
87
90
  const observationsLabel = observationsLabelOverride || obsType;
@@ -93,6 +96,10 @@ export function HeatmapSubscriber(props) {
93
96
  const variablesTitle = capitalize(variablesPluralLabel);
94
97
 
95
98
  const [isRendering, setIsRendering] = useState(false);
99
+ // We need to know whether the user is currently hovering over the expression part
100
+ // of the heatmap vs. the color bar part, which will affect whether we call
101
+ // setObsColorEncoding with 'geneSelection' or 'cellSetSelection' upon a click.
102
+ const [hoveredColorEncoding, setHoveredColorEncoding] = useState('geneSelection');
96
103
 
97
104
  const [urls, addUrl] = useUrls(loaders, dataset);
98
105
  const [width, height, deckRef] = useDeckCanvasSize();
@@ -122,46 +129,50 @@ export function HeatmapSubscriber(props) {
122
129
  obsSetsStatus,
123
130
  ]);
124
131
 
132
+ const [uint8ObsFeatureMatrix, obsFeatureMatrixExtent] = useUint8ObsFeatureMatrix(
133
+ { obsFeatureMatrix },
134
+ );
135
+
125
136
  const mergedCellSets = useMemo(() => mergeObsSets(
126
137
  cellSets, additionalCellSets,
127
138
  ), [cellSets, additionalCellSets]);
128
139
 
129
140
  const cellColors = useMemo(() => getCellColors({
130
- // Only show cell set selection on heatmap labels.
131
- cellColorEncoding: 'cellSetSelection',
132
- geneSelection,
133
141
  cellSets: mergedCellSets,
134
142
  cellSetSelection,
135
143
  cellSetColor,
136
144
  obsIndex,
137
145
  theme,
138
- }), [mergedCellSets, geneSelection, theme,
146
+ }), [mergedCellSets, theme,
139
147
  cellSetColor, cellSetSelection, obsIndex]);
140
148
 
141
149
  const getObsInfo = useGetObsInfo(
142
150
  observationsLabel, obsLabelsTypes, obsLabelsData, obsSetsMembership,
143
151
  );
144
152
 
153
+ const getObsMembership = useGetObsMembership(obsSetsMembership);
154
+
145
155
  const getFeatureInfo = useCallback((featureId) => {
146
156
  if (featureId) {
147
- return { [`${capitalize(variablesLabel)} ID`]: featureId };
157
+ const featureLabel = featureLabelsMap?.get(featureId) || featureId;
158
+ return { [`${capitalize(variablesLabel)} ID`]: featureLabel };
148
159
  }
149
160
  return null;
150
- }, [variablesLabel]);
161
+ }, [variablesLabel, featureLabelsMap]);
151
162
 
152
163
  const expressionMatrix = useMemo(() => {
153
- if (obsIndex && featureIndex && obsFeatureMatrix) {
164
+ if (obsIndex && featureIndex && uint8ObsFeatureMatrix) {
154
165
  return {
155
166
  rows: obsIndex,
156
167
  cols: (featureLabelsMap
157
168
  ? featureIndex.map(key => featureLabelsMap.get(key) || key)
158
169
  : featureIndex
159
170
  ),
160
- matrix: obsFeatureMatrix.data,
171
+ matrix: uint8ObsFeatureMatrix.data,
161
172
  };
162
173
  }
163
174
  return null;
164
- }, [obsIndex, featureIndex, obsFeatureMatrix, featureLabelsMap]);
175
+ }, [obsIndex, featureIndex, uint8ObsFeatureMatrix, featureLabelsMap]);
165
176
 
166
177
  const cellsCount = obsIndex ? obsIndex.length : 0;
167
178
  const genesCount = featureIndex ? featureIndex.length : 0;
@@ -171,6 +182,22 @@ export function HeatmapSubscriber(props) {
171
182
  // logs in the console on every hover event.
172
183
  }, []);
173
184
 
185
+ const onHeatmapClick = () => {
186
+ if (hoveredColorEncoding === 'geneSelection' && geneHighlight) {
187
+ setGeneSelection([geneHighlight]);
188
+ setCellColorEncoding('geneSelection');
189
+ } else if (hoveredColorEncoding === 'cellSelection' && cellSetSelection) {
190
+ const selectionFullPath = getObsMembership(cellHighlight);
191
+ if (selectionFullPath?.length > 0) {
192
+ const selectionToHighlight = findLongestCommonPath(selectionFullPath, cellSetSelection);
193
+ if (selectionToHighlight) {
194
+ setCellSetSelection([selectionToHighlight]);
195
+ setCellColorEncoding('cellSelection');
196
+ }
197
+ }
198
+ }
199
+ };
200
+
174
201
  const cellColorLabels = useMemo(() => ([
175
202
  `${capitalize(observationsLabel)} Set`,
176
203
  ]), [observationsLabel]);
@@ -191,6 +218,8 @@ export function HeatmapSubscriber(props) {
191
218
  setGeneExpressionColormap={setGeneExpressionColormap}
192
219
  geneExpressionColormapRange={geneExpressionColormapRange}
193
220
  setGeneExpressionColormapRange={setGeneExpressionColormapRange}
221
+ tooltipsVisible={tooltipsVisible}
222
+ setTooltipsVisible={setTooltipsVisible}
194
223
  />
195
224
  )}
196
225
  >
@@ -216,6 +245,7 @@ export function HeatmapSubscriber(props) {
216
245
  setIsRendering={setIsRendering}
217
246
  setCellHighlight={setCellHighlight}
218
247
  setGeneHighlight={setGeneHighlight}
248
+ featureIndex={featureIndex}
219
249
  setTrackHighlight={setTrackHighlight}
220
250
  setComponentHover={() => {
221
251
  setComponentHover(uuid);
@@ -227,8 +257,10 @@ export function HeatmapSubscriber(props) {
227
257
  observationsDashes={false}
228
258
  cellColorLabels={cellColorLabels}
229
259
  useDevicePixels
260
+ onHeatmapClick={onHeatmapClick}
261
+ setColorEncoding={setHoveredColorEncoding}
230
262
  />
231
- {!disableTooltip && (
263
+ {tooltipsVisible && (
232
264
  <HeatmapTooltipSubscriber
233
265
  parentUuid={uuid}
234
266
  width={width}
@@ -240,14 +272,18 @@ export function HeatmapSubscriber(props) {
240
272
  featureHighlight={geneHighlight}
241
273
  />
242
274
  )}
275
+ <Legend
276
+ visible
277
+ theme={theme}
278
+ featureType={featureType}
279
+ featureValueType={featureValueType}
280
+ obsColorEncoding="geneExpression"
281
+ considerSelections={false}
282
+ featureSelection={geneSelection}
283
+ featureValueColormap={geneExpressionColormap}
284
+ featureValueColormapRange={geneExpressionColormapRange}
285
+ extent={obsFeatureMatrixExtent}
286
+ />
243
287
  </TitleInfo>
244
288
  );
245
289
  }
246
-
247
- export function register() {
248
- registerPluginViewType(
249
- ViewType.HEATMAP,
250
- HeatmapSubscriber,
251
- COMPONENT_COORDINATION_TYPES[ViewType.HEATMAP],
252
- );
253
- }
package/src/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { HeatmapSubscriber, register } from './HeatmapSubscriber';
2
- export { default as Heatmap } from './Heatmap';
1
+ export { HeatmapSubscriber } from './HeatmapSubscriber.js';
2
+ export { default as Heatmap } from './Heatmap.js';
package/src/utils.js CHANGED
@@ -1,4 +1,4 @@
1
- import clamp from 'lodash/clamp';
1
+ import { clamp } from 'lodash-es';
2
2
  import {
3
3
  AXIS_LABEL_TEXT_SIZE,
4
4
  AXIS_FONT_FAMILY,
package/src/utils.test.js CHANGED
@@ -1,4 +1,4 @@
1
- import { mouseToHeatmapPosition, heatmapToMousePosition } from './utils';
1
+ import { mouseToHeatmapPosition, heatmapToMousePosition } from './utils.js';
2
2
 
3
3
  describe('heatmap tooltip utils', () => {
4
4
  it('transforms mouse coordinates to row and column indices when zoomed out', () => {