@vitessce/scatterplot 2.0.3 → 3.0.1

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 (67) hide show
  1. package/dist/{deflate.65a17097.mjs → deflate-162fa772.js} +2 -2
  2. package/dist/{index.8206952d.mjs → index-578beee3.js} +61450 -51757
  3. package/dist/{index.mjs → index.js} +5 -5
  4. package/dist/{jpeg.4221d32f.mjs → jpeg-00fef901.js} +1 -1
  5. package/dist/{lerc.8d649494.mjs → lerc-55d38607.js} +76 -5
  6. package/dist/{lzw.89350f4e.mjs → lzw-c3bdd9f6.js} +1 -1
  7. package/dist/{packbits.986f9d9f.mjs → packbits-c1c71d64.js} +1 -1
  8. package/dist/{pako.esm.4b234125.mjs → pako.esm-68f84e2a.js} +97 -15
  9. package/dist/{raw.1cc73933.mjs → raw-e1c8b3d7.js} +1 -1
  10. package/dist/{webimage.be69a2d5.mjs → webimage-c7fc297e.js} +1 -1
  11. package/dist-tsc/EmptyMessage.d.ts +2 -0
  12. package/dist-tsc/EmptyMessage.d.ts.map +1 -0
  13. package/dist-tsc/EmptyMessage.js +6 -0
  14. package/dist-tsc/Scatterplot.d.ts +10 -0
  15. package/dist-tsc/Scatterplot.d.ts.map +1 -0
  16. package/dist-tsc/Scatterplot.js +314 -0
  17. package/dist-tsc/ScatterplotOptions.d.ts +2 -0
  18. package/dist-tsc/ScatterplotOptions.d.ts.map +1 -0
  19. package/dist-tsc/ScatterplotOptions.js +56 -0
  20. package/dist-tsc/ScatterplotTooltipSubscriber.d.ts +2 -0
  21. package/dist-tsc/ScatterplotTooltipSubscriber.d.ts.map +1 -0
  22. package/dist-tsc/ScatterplotTooltipSubscriber.js +14 -0
  23. package/dist-tsc/index.d.ts +6 -0
  24. package/dist-tsc/index.d.ts.map +1 -0
  25. package/dist-tsc/index.js +5 -5
  26. package/dist-tsc/shared-spatial-scatterplot/AbstractSpatialOrScatterplot.d.ts +82 -0
  27. package/dist-tsc/shared-spatial-scatterplot/AbstractSpatialOrScatterplot.d.ts.map +1 -0
  28. package/dist-tsc/shared-spatial-scatterplot/AbstractSpatialOrScatterplot.js +218 -0
  29. package/dist-tsc/shared-spatial-scatterplot/ToolMenu.d.ts +4 -0
  30. package/dist-tsc/shared-spatial-scatterplot/ToolMenu.d.ts.map +1 -0
  31. package/dist-tsc/shared-spatial-scatterplot/ToolMenu.js +83 -0
  32. package/dist-tsc/shared-spatial-scatterplot/ToolMenu.test.d.ts +2 -0
  33. package/dist-tsc/shared-spatial-scatterplot/ToolMenu.test.d.ts.map +1 -0
  34. package/dist-tsc/shared-spatial-scatterplot/ToolMenu.test.js +23 -0
  35. package/dist-tsc/shared-spatial-scatterplot/cursor.d.ts +4 -0
  36. package/dist-tsc/shared-spatial-scatterplot/cursor.d.ts.map +1 -0
  37. package/dist-tsc/shared-spatial-scatterplot/cursor.js +22 -0
  38. package/dist-tsc/shared-spatial-scatterplot/dynamic-opacity.d.ts +3 -0
  39. package/dist-tsc/shared-spatial-scatterplot/dynamic-opacity.d.ts.map +1 -0
  40. package/dist-tsc/shared-spatial-scatterplot/dynamic-opacity.js +47 -0
  41. package/dist-tsc/shared-spatial-scatterplot/dynamic-opacity.test.d.ts +2 -0
  42. package/dist-tsc/shared-spatial-scatterplot/dynamic-opacity.test.d.ts.map +1 -0
  43. package/dist-tsc/shared-spatial-scatterplot/dynamic-opacity.test.js +29 -0
  44. package/dist-tsc/shared-spatial-scatterplot/force-collide-rects.d.ts +13 -0
  45. package/dist-tsc/shared-spatial-scatterplot/force-collide-rects.d.ts.map +1 -0
  46. package/dist-tsc/shared-spatial-scatterplot/force-collide-rects.js +169 -0
  47. package/dist-tsc/shared-spatial-scatterplot/force-collide-rects.test.d.ts +2 -0
  48. package/dist-tsc/shared-spatial-scatterplot/force-collide-rects.test.d.ts.map +1 -0
  49. package/dist-tsc/shared-spatial-scatterplot/force-collide-rects.test.js +59 -0
  50. package/dist-tsc/shared-spatial-scatterplot/index.d.ts +6 -0
  51. package/dist-tsc/shared-spatial-scatterplot/index.d.ts.map +1 -0
  52. package/dist-tsc/shared-spatial-scatterplot/index.js +5 -0
  53. package/dist-tsc/shared-spatial-scatterplot/quadtree.d.ts +10 -0
  54. package/dist-tsc/shared-spatial-scatterplot/quadtree.d.ts.map +1 -0
  55. package/dist-tsc/shared-spatial-scatterplot/quadtree.js +26 -0
  56. package/package.json +25 -13
  57. package/src/Scatterplot.js +13 -2
  58. package/src/ScatterplotOptions.js +27 -5
  59. package/src/index.js +5 -5
  60. package/src/shared-spatial-scatterplot/AbstractSpatialOrScatterplot.js +10 -4
  61. package/src/shared-spatial-scatterplot/ToolMenu.js +56 -18
  62. package/src/shared-spatial-scatterplot/ToolMenu.test.jsx +14 -6
  63. package/src/shared-spatial-scatterplot/dynamic-opacity.js +1 -1
  64. package/src/shared-spatial-scatterplot/dynamic-opacity.test.js +2 -1
  65. package/src/shared-spatial-scatterplot/force-collide-rects.test.js +2 -1
  66. package/src/shared-spatial-scatterplot/index.js +5 -5
  67. package/src/shared-spatial-scatterplot/quadtree.js +1 -1
@@ -0,0 +1,59 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { forceSimulation } from 'd3-force';
3
+ import { forceCollideRects } from './force-collide-rects.js';
4
+ describe('force-collide-rects.js', () => {
5
+ describe('forceCollideRects', () => {
6
+ it('can be initialized with a size function', () => {
7
+ const collisionForce = forceCollideRects()
8
+ .size(d => ([d.width, d.height]));
9
+ const size = collisionForce.size();
10
+ const [w, h] = size({ width: 2, height: 3 });
11
+ expect(w).toEqual(2);
12
+ expect(h).toEqual(3);
13
+ });
14
+ it('cannot prevent a collision of rects after few iterations', () => {
15
+ const collisionForce = forceCollideRects()
16
+ .size(d => ([d.width, d.height]));
17
+ const nodes = [
18
+ {
19
+ label: 'A', width: 100, height: 100, x: 2, y: 2,
20
+ },
21
+ {
22
+ label: 'B', width: 100, height: 100, x: 3, y: 3,
23
+ },
24
+ {
25
+ label: 'C', width: 100, height: 100, x: 3, y: 2,
26
+ },
27
+ ];
28
+ forceSimulation()
29
+ .nodes(nodes)
30
+ .force('collision', collisionForce)
31
+ .tick(2);
32
+ const collisionAB = (Math.abs(nodes[0].x - nodes[1].x) < 100
33
+ && Math.abs(nodes[0].y - nodes[1].y) < 100);
34
+ expect(collisionAB).toEqual(true);
35
+ });
36
+ it('can prevent a collision of rects after many iterations', () => {
37
+ const collisionForce = forceCollideRects()
38
+ .size(d => ([d.width, d.height]));
39
+ const nodes = [
40
+ {
41
+ label: 'A', width: 100, height: 100, x: 2, y: 2,
42
+ },
43
+ {
44
+ label: 'B', width: 100, height: 100, x: 3, y: 3,
45
+ },
46
+ {
47
+ label: 'C', width: 100, height: 100, x: 3, y: 2,
48
+ },
49
+ ];
50
+ forceSimulation()
51
+ .nodes(nodes)
52
+ .force('collision', collisionForce)
53
+ .tick(50);
54
+ const collisionAB = (Math.abs(nodes[0].x - nodes[1].x) < 100
55
+ && Math.abs(nodes[0].y - nodes[1].y) < 100);
56
+ expect(collisionAB).toEqual(false);
57
+ });
58
+ });
59
+ });
@@ -0,0 +1,6 @@
1
+ export { default as AbstractSpatialOrScatterplot } from "./AbstractSpatialOrScatterplot.js";
2
+ export { createQuadTree } from "./quadtree.js";
3
+ export { forceCollideRects } from "./force-collide-rects.js";
4
+ export { getOnHoverCallback } from "./cursor.js";
5
+ export { getPointSizeDevicePixels, getPointOpacity } from "./dynamic-opacity.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shared-spatial-scatterplot/index.js"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ export { default as AbstractSpatialOrScatterplot } from './AbstractSpatialOrScatterplot.js';
2
+ export { createQuadTree } from './quadtree.js';
3
+ export { forceCollideRects } from './force-collide-rects.js';
4
+ export { getOnHoverCallback } from './cursor.js';
5
+ export { getPointSizeDevicePixels, getPointOpacity, } from './dynamic-opacity.js';
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Create a d3-quadtree object for cells data points.
3
+ * @param {array} cellsEntries Array of [cellId, cell] tuples,
4
+ * resulting from running Object.entries on the cells object.
5
+ * @param {function} getCellCoords Given a cell object, return the
6
+ * spatial/scatterplot coordinates [x, y].
7
+ * @returns {object} Quadtree instance.
8
+ */
9
+ export function createQuadTree(obsEmbedding: any, getCellCoords: Function): object;
10
+ //# sourceMappingURL=quadtree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quadtree.d.ts","sourceRoot":"","sources":["../../src/shared-spatial-scatterplot/quadtree.js"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,4EAFa,MAAM,CAiBlB"}
@@ -0,0 +1,26 @@
1
+ import { quadtree } from 'd3-quadtree';
2
+ import { range } from 'lodash-es';
3
+ /**
4
+ * Create a d3-quadtree object for cells data points.
5
+ * @param {array} cellsEntries Array of [cellId, cell] tuples,
6
+ * resulting from running Object.entries on the cells object.
7
+ * @param {function} getCellCoords Given a cell object, return the
8
+ * spatial/scatterplot coordinates [x, y].
9
+ * @returns {object} Quadtree instance.
10
+ */
11
+ export function createQuadTree(obsEmbedding, getCellCoords) {
12
+ // Use the cellsEntries variable since it is already
13
+ // an array, converted by Object.entries().
14
+ // Only use cellsEntries in quadtree calculation if there is
15
+ // centroid data in the cells (i.e not just ids).
16
+ // eslint-disable-next-line no-unused-vars
17
+ if (!obsEmbedding) {
18
+ // Abort if the cells data is not yet available.
19
+ return null;
20
+ }
21
+ const tree = quadtree()
22
+ .x(i => getCellCoords(i)[0])
23
+ .y(i => getCellCoords(i)[1])
24
+ .addAll(range(obsEmbedding.shape[1]));
25
+ return tree;
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitessce/scatterplot",
3
- "version": "2.0.3",
3
+ "version": "3.0.1",
4
4
  "author": "Gehlenborg Lab",
5
5
  "homepage": "http://vitessce.io",
6
6
  "repository": {
@@ -8,34 +8,46 @@
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",
20
+ "@material-ui/icons": "~4.11.2",
18
21
  "clsx": "^1.1.1",
19
22
  "d3-force": "^2.1.1",
20
23
  "d3-quadtree": "^1.0.7",
21
- "lodash": "^4.17.21",
22
- "@vitessce/constants-internal": "2.0.3",
23
- "@vitessce/gl": "2.0.3",
24
- "@vitessce/icons": "2.0.3",
25
- "@vitessce/tooltip": "2.0.3",
26
- "@vitessce/utils": "2.0.3",
27
- "@vitessce/vit-s": "2.0.3"
24
+ "lodash-es": "^4.17.21",
25
+ "@vitessce/constants-internal": "3.0.1",
26
+ "@vitessce/icons": "3.0.1",
27
+ "@vitessce/tooltip": "3.0.1",
28
+ "@vitessce/utils": "3.0.1",
29
+ "@vitessce/vit-s": "3.0.1",
30
+ "@vitessce/gl": "3.0.1"
28
31
  },
29
32
  "devDependencies": {
33
+ "@testing-library/jest-dom": "^5.16.4",
34
+ "@testing-library/react": "^13.3.0",
30
35
  "react": "^18.0.0",
31
- "vite": "^3.0.0",
32
- "vitest": "^0.23.4"
36
+ "vite": "^4.3.0",
37
+ "vitest": "^0.32.2"
33
38
  },
34
39
  "peerDependencies": {
35
40
  "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
36
41
  },
37
42
  "scripts": {
38
43
  "bundle": "pnpm exec vite build -c ../../../scripts/vite.config.js",
39
- "test": "pnpm exec vitest --run -r ../../../ --dir ."
44
+ "test": "pnpm exec vitest --run"
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
  }
@@ -7,7 +7,7 @@ import {
7
7
  import { getDefaultColor } from '@vitessce/utils';
8
8
  import {
9
9
  AbstractSpatialOrScatterplot, createQuadTree, forceCollideRects, getOnHoverCallback,
10
- } from './shared-spatial-scatterplot';
10
+ } from './shared-spatial-scatterplot/index.js';
11
11
 
12
12
  const CELLS_LAYER_ID = 'scatterplot';
13
13
  const LABEL_FONT_FAMILY = "-apple-system, 'Helvetica Neue', Arial, sans-serif";
@@ -64,8 +64,10 @@ const getPosition = (object, { index, data, target }) => {
64
64
  * @param {function} props.setCellHighlight
65
65
  * @param {function} props.updateViewInfo
66
66
  * @param {function} props.onToolChange Callback for tool changes
67
- * (lasso/pan/rectangle selection tools).
67
+ * (lasso/pan selection tools).
68
68
  * @param {function} props.onCellClick Getter function for cell layer onClick.
69
+ * @param {object} props.originalViewState A viewState object to pass to
70
+ * setViewState upon clicking the recenter button.
69
71
  */
70
72
  class Scatterplot extends AbstractSpatialOrScatterplot {
71
73
  constructor(props) {
@@ -343,6 +345,7 @@ class Scatterplot extends AbstractSpatialOrScatterplot {
343
345
  'obsEmbeddingIndex', 'obsEmbedding', 'cellFilter', 'cellSelection', 'cellColors',
344
346
  'cellRadius', 'cellOpacity', 'cellRadiusMode', 'geneExpressionColormap',
345
347
  'geneExpressionColormapRange', 'geneSelection', 'cellColorEncoding',
348
+ 'getExpressionValue',
346
349
  ].some(shallowDiff)) {
347
350
  // Cells layer props changed.
348
351
  this.onUpdateCellsLayer();
@@ -366,6 +369,13 @@ class Scatterplot extends AbstractSpatialOrScatterplot {
366
369
  }
367
370
  }
368
371
 
372
+ recenter() {
373
+ const { originalViewState, setViewState } = this.props;
374
+ if (Array.isArray(originalViewState?.target) && typeof originalViewState?.zoom === 'number') {
375
+ setViewState(originalViewState);
376
+ }
377
+ }
378
+
369
379
  // render() is implemented in the abstract parent class.
370
380
  }
371
381
 
@@ -382,4 +392,5 @@ const ScatterplotWrapper = forwardRef((props, deckRef) => (
382
392
  deckRef={deckRef}
383
393
  />
384
394
  ));
395
+ ScatterplotWrapper.displayName = 'ScatterplotWrapper';
385
396
  export default ScatterplotWrapper;
@@ -1,9 +1,6 @@
1
1
  import React, { useCallback } from 'react';
2
- import debounce from 'lodash/debounce';
3
- import Checkbox from '@material-ui/core/Checkbox';
4
- import Slider from '@material-ui/core/Slider';
5
- import TableCell from '@material-ui/core/TableCell';
6
- import TableRow from '@material-ui/core/TableRow';
2
+ import { debounce } from 'lodash-es';
3
+ import { Checkbox, Slider, TableCell, TableRow } from '@material-ui/core';
7
4
  import { capitalize } from '@vitessce/utils';
8
5
  import {
9
6
  usePlotOptionsStyles, CellColorEncodingOption, OptionsContainer, OptionSelect,
@@ -24,6 +21,8 @@ export default function ScatterplotOptions(props) {
24
21
  setCellOpacityMode,
25
22
  cellSetLabelsVisible,
26
23
  setCellSetLabelsVisible,
24
+ tooltipsVisible,
25
+ setTooltipsVisible,
27
26
  cellSetLabelSize,
28
27
  setCellSetLabelSize,
29
28
  cellSetPolygonsVisible,
@@ -60,6 +59,10 @@ export default function ScatterplotOptions(props) {
60
59
  setCellSetLabelsVisible(event.target.checked);
61
60
  }
62
61
 
62
+ function handleTooltipsVisibilityChange(event) {
63
+ setTooltipsVisible(event.target.checked);
64
+ }
65
+
63
66
  function handleLabelSizeChange(event, value) {
64
67
  setCellSetLabelSize(value);
65
68
  }
@@ -102,6 +105,25 @@ export default function ScatterplotOptions(props) {
102
105
  />
103
106
  </TableCell>
104
107
  </TableRow>
108
+ <TableRow>
109
+ <TableCell className={classes.labelCell}>
110
+ Tooltips Visible
111
+ </TableCell>
112
+ <TableCell className={classes.inputCell}>
113
+ <Checkbox
114
+ className={classes.checkbox}
115
+ /**
116
+ * We have to use "checked" here, not "value".
117
+ * The checkbox state is not persisting with value.
118
+ * For reference, https://v4.mui.com/api/checkbox/
119
+ */
120
+ checked={tooltipsVisible}
121
+ onChange={handleTooltipsVisibilityChange}
122
+ name="scatterplot-option-toltip-visibility"
123
+ color="default"
124
+ />
125
+ </TableCell>
126
+ </TableRow>
105
127
  <TableRow>
106
128
  <TableCell className={classes.labelCell}>
107
129
  {observationsLabelNice} Set Label Size
package/src/index.js CHANGED
@@ -1,11 +1,11 @@
1
- export { default as Scatterplot } from './Scatterplot';
2
- export { default as ScatterplotOptions } from './ScatterplotOptions';
3
- export { default as ScatterplotTooltipSubscriber } from './ScatterplotTooltipSubscriber';
4
- export { default as EmptyMessage } from './EmptyMessage';
1
+ export { default as Scatterplot } from './Scatterplot.js';
2
+ export { default as ScatterplotOptions } from './ScatterplotOptions.js';
3
+ export { default as ScatterplotTooltipSubscriber } from './ScatterplotTooltipSubscriber.js';
4
+ export { default as EmptyMessage } from './EmptyMessage.js';
5
5
  export {
6
6
  getPointSizeDevicePixels,
7
7
  getPointOpacity,
8
8
  getOnHoverCallback,
9
9
  createQuadTree,
10
10
  AbstractSpatialOrScatterplot,
11
- } from './shared-spatial-scatterplot/index';
11
+ } from './shared-spatial-scatterplot/index.js';
@@ -1,7 +1,7 @@
1
1
  import React, { PureComponent } from 'react';
2
2
  import { deck, DEFAULT_GL_OPTIONS } from '@vitessce/gl';
3
- import ToolMenu from './ToolMenu';
4
- import { getCursor, getCursorWithTool } from './cursor';
3
+ import ToolMenu from './ToolMenu.js';
4
+ import { getCursor, getCursorWithTool } from './cursor.js';
5
5
 
6
6
  /**
7
7
  * Abstract class component intended to be inherited by
@@ -19,12 +19,12 @@ export default class AbstractSpatialOrScatterplot extends PureComponent {
19
19
  };
20
20
 
21
21
  this.viewport = null;
22
-
23
22
  this.onViewStateChange = this.onViewStateChange.bind(this);
24
23
  this.onInitializeViewInfo = this.onInitializeViewInfo.bind(this);
25
24
  this.onWebGLInitialized = this.onWebGLInitialized.bind(this);
26
25
  this.onToolChange = this.onToolChange.bind(this);
27
26
  this.onHover = this.onHover.bind(this);
27
+ this.recenter = this.recenter.bind(this);
28
28
  }
29
29
 
30
30
  /**
@@ -198,6 +198,12 @@ export default class AbstractSpatialOrScatterplot extends PureComponent {
198
198
 
199
199
  }
200
200
 
201
+ /** Intended to be overridden by descendants.
202
+ * Resets the view type to its original position.
203
+ */
204
+ // eslint-disable-next-line class-methods-use-this
205
+ recenter() {}
206
+
201
207
  /**
202
208
  * Intended to be overridden by descendants.
203
209
  * @returns {boolean} Whether or not any layers are 3D.
@@ -238,9 +244,9 @@ export default class AbstractSpatialOrScatterplot extends PureComponent {
238
244
  setActiveTool={this.onToolChange}
239
245
  visibleTools={{
240
246
  pan: showPanTool && !hideTools,
241
- selectRectangle: showCellSelectionTools && !hideTools,
242
247
  selectLasso: showCellSelectionTools && !hideTools,
243
248
  }}
249
+ recenterOnClick={this.recenter}
244
250
  />
245
251
  <deck.DeckGL
246
252
  id={`deckgl-overlay-${uuid}`}
@@ -1,10 +1,18 @@
1
1
  import React from 'react';
2
2
  import clsx from 'clsx';
3
3
  import { SELECTION_TYPE } from '@vitessce/gl';
4
- import { PointerIconSVG, SelectRectangleIconSVG, SelectLassoIconSVG } from '@vitessce/icons';
4
+ import { PointerIconSVG, SelectLassoIconSVG } from '@vitessce/icons';
5
5
  import { makeStyles } from '@material-ui/core';
6
+ import { CenterFocusStrong } from '@material-ui/icons';
6
7
 
7
8
  const useStyles = makeStyles(() => ({
9
+ toolButton: {
10
+ display: 'inline-flex',
11
+ '&:active': {
12
+ opacity: '.65',
13
+ extend: 'iconClicked',
14
+ },
15
+ },
8
16
  tool: {
9
17
  position: 'absolute',
10
18
  display: 'inline',
@@ -15,7 +23,12 @@ const useStyles = makeStyles(() => ({
15
23
  opacity: '.90',
16
24
  },
17
25
  },
18
- iconButton: {
26
+ iconClicked: {
27
+ // Styles for the clicked state
28
+ boxShadow: 'none',
29
+ transform: 'scale(0.98)', // make the button slightly smaller
30
+ },
31
+ toolIcon: {
19
32
  // btn btn-outline-secondary mr-2 icon
20
33
  padding: '0',
21
34
  height: '2em',
@@ -39,9 +52,14 @@ const useStyles = makeStyles(() => ({
39
52
 
40
53
  '& > svg': {
41
54
  verticalAlign: 'middle',
55
+ color: 'black',
56
+ },
57
+ '&:active': {
58
+ extend: 'iconClicked',
42
59
  },
60
+
43
61
  },
44
- iconButtonActive: {
62
+ toolActive: {
45
63
  // active
46
64
  color: '#fff',
47
65
  backgroundColor: '#6c757d',
@@ -50,14 +68,31 @@ const useStyles = makeStyles(() => ({
50
68
  },
51
69
  }));
52
70
 
53
- export function IconButton(props) {
71
+ export function IconTool(props) {
54
72
  const {
55
73
  alt, onClick, isActive, children,
56
74
  } = props;
57
75
  const classes = useStyles();
58
76
  return (
59
77
  <button
60
- className={clsx(classes.iconButton, { [classes.iconButtonActive]: isActive })}
78
+ className={clsx(classes.toolIcon, { [classes.toolActive]: isActive })}
79
+ onClick={onClick}
80
+ type="button"
81
+ title={alt}
82
+ >
83
+ {children}
84
+ </button>
85
+ );
86
+ }
87
+
88
+ export function IconButton(props) {
89
+ const {
90
+ alt, onClick, children,
91
+ } = props;
92
+ const classes = useStyles();
93
+ return (
94
+ <button
95
+ className={clsx(classes.toolIcon, classes.toolButton)}
61
96
  onClick={onClick}
62
97
  type="button"
63
98
  title={alt}
@@ -71,35 +106,38 @@ export default function ToolMenu(props) {
71
106
  const {
72
107
  setActiveTool,
73
108
  activeTool,
74
- visibleTools = { pan: true, selectRectangle: true, selectLasso: true },
109
+ visibleTools = { pan: true, selectLasso: true },
110
+ recenterOnClick = () => {},
75
111
  } = props;
76
112
  const classes = useStyles();
113
+
114
+ const onRecenterButtonCLick = () => {
115
+ recenterOnClick();
116
+ };
117
+
77
118
  return (
78
119
  <div className={classes.tool}>
79
120
  {visibleTools.pan && (
80
- <IconButton
121
+ <IconTool
81
122
  alt="pointer tool"
82
123
  onClick={() => setActiveTool(null)}
83
124
  isActive={activeTool === null}
84
125
  ><PointerIconSVG />
85
- </IconButton>
126
+ </IconTool>
86
127
  )}
87
- {visibleTools.selectRectangle ? (
88
- <IconButton
89
- alt="select rectangle"
90
- onClick={() => setActiveTool(SELECTION_TYPE.RECTANGLE)}
91
- isActive={activeTool === SELECTION_TYPE.RECTANGLE}
92
- ><SelectRectangleIconSVG />
93
- </IconButton>
94
- ) : null}
95
128
  {visibleTools.selectLasso ? (
96
- <IconButton
129
+ <IconTool
97
130
  alt="select lasso"
98
131
  onClick={() => setActiveTool(SELECTION_TYPE.POLYGON)}
99
132
  isActive={activeTool === SELECTION_TYPE.POLYGON}
100
133
  ><SelectLassoIconSVG />
101
- </IconButton>
134
+ </IconTool>
102
135
  ) : null}
136
+ <IconButton
137
+ alt="click to recenter"
138
+ onClick={() => onRecenterButtonCLick()}
139
+ ><CenterFocusStrong />
140
+ </IconButton>
103
141
  </div>
104
142
  );
105
143
  }
@@ -1,18 +1,26 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
1
2
  import '@testing-library/jest-dom';
2
- import { cleanup, render, screen } from '@testing-library/react'
3
- import { afterEach, expect } from 'vitest'
3
+ import { cleanup, render } from '@testing-library/react';
4
+ import React from 'react';
4
5
 
5
- import { IconButton } from './ToolMenu';
6
+ import { IconTool, IconButton } from './ToolMenu.js';
6
7
 
7
8
  afterEach(() => {
8
- cleanup()
9
+ cleanup();
9
10
  });
10
11
 
11
12
  describe('ToolMenu.js', () => {
12
- describe('<IconButton />', () => {
13
+ describe('<IconTool />', () => {
13
14
  it('renders with title attribute', () => {
14
- const { container } = render(<IconButton isActive alt="Lasso" />);
15
+ const { container } = render(<IconTool isActive alt="Lasso" />);
15
16
  expect(container.querySelectorAll('[title="Lasso"]').length).toEqual(1);
16
17
  });
17
18
  });
19
+
20
+ describe('<IconButton />', () => {
21
+ it('renders with title attribute', () => {
22
+ const { container } = render(<IconButton alt="click to recenter" />);
23
+ expect(container.querySelectorAll('[title="click to recenter"]').length).toEqual(1);
24
+ });
25
+ });
18
26
  });
@@ -1,5 +1,5 @@
1
1
  import { deck } from '@vitessce/gl';
2
- import clamp from 'lodash/clamp';
2
+ import { clamp } from 'lodash-es';
3
3
 
4
4
  // Reference: https://observablehq.com/@rreusser/selecting-the-right-opacity-for-2d-point-clouds
5
5
  // Reference: https://observablehq.com/@bmschmidt/dot-density-election-maps-with-webgl
@@ -1,4 +1,5 @@
1
- import { getPointSizeDevicePixels, getPointOpacity } from './dynamic-opacity';
1
+ import { describe, it, expect } from 'vitest';
2
+ import { getPointSizeDevicePixels, getPointOpacity } from './dynamic-opacity.js';
2
3
 
3
4
  describe('dynamic-opacity.js', () => {
4
5
  describe('getPointSizeDevicePixels', () => {
@@ -1,5 +1,6 @@
1
+ import { describe, it, expect } from 'vitest';
1
2
  import { forceSimulation } from 'd3-force';
2
- import { forceCollideRects } from './force-collide-rects';
3
+ import { forceCollideRects } from './force-collide-rects.js';
3
4
 
4
5
  describe('force-collide-rects.js', () => {
5
6
  describe('forceCollideRects', () => {
@@ -1,8 +1,8 @@
1
- export { default as AbstractSpatialOrScatterplot } from './AbstractSpatialOrScatterplot';
2
- export { createQuadTree } from './quadtree';
3
- export { forceCollideRects } from './force-collide-rects';
4
- export { getOnHoverCallback } from './cursor';
1
+ export { default as AbstractSpatialOrScatterplot } from './AbstractSpatialOrScatterplot.js';
2
+ export { createQuadTree } from './quadtree.js';
3
+ export { forceCollideRects } from './force-collide-rects.js';
4
+ export { getOnHoverCallback } from './cursor.js';
5
5
  export {
6
6
  getPointSizeDevicePixels,
7
7
  getPointOpacity,
8
- } from './dynamic-opacity';
8
+ } from './dynamic-opacity.js';
@@ -1,5 +1,5 @@
1
1
  import { quadtree } from 'd3-quadtree';
2
- import range from 'lodash/range';
2
+ import { range } from 'lodash-es';
3
3
 
4
4
  /**
5
5
  * Create a d3-quadtree object for cells data points.