@vitessce/statistical-plots 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 (38) hide show
  1. package/dist/{index.mjs → index.js} +20501 -10821
  2. package/dist-tsc/CellSetExpressionPlot.d.ts +33 -0
  3. package/dist-tsc/CellSetExpressionPlot.d.ts.map +1 -0
  4. package/dist-tsc/CellSetExpressionPlot.js +248 -0
  5. package/dist-tsc/CellSetExpressionPlotOptions.d.ts +2 -0
  6. package/dist-tsc/CellSetExpressionPlotOptions.d.ts.map +1 -0
  7. package/dist-tsc/CellSetExpressionPlotOptions.js +30 -0
  8. package/dist-tsc/CellSetExpressionPlotSubscriber.d.ts +16 -0
  9. package/dist-tsc/CellSetExpressionPlotSubscriber.d.ts.map +1 -0
  10. package/dist-tsc/CellSetExpressionPlotSubscriber.js +107 -0
  11. package/dist-tsc/CellSetSizesPlot.d.ts +29 -0
  12. package/dist-tsc/CellSetSizesPlot.d.ts.map +1 -0
  13. package/dist-tsc/CellSetSizesPlot.js +149 -0
  14. package/dist-tsc/CellSetSizesPlotSubscriber.d.ts +18 -0
  15. package/dist-tsc/CellSetSizesPlotSubscriber.d.ts.map +1 -0
  16. package/dist-tsc/CellSetSizesPlotSubscriber.js +77 -0
  17. package/dist-tsc/ExpressionHistogram.d.ts +34 -0
  18. package/dist-tsc/ExpressionHistogram.d.ts.map +1 -0
  19. package/dist-tsc/ExpressionHistogram.js +93 -0
  20. package/dist-tsc/ExpressionHistogramSubscriber.d.ts +16 -0
  21. package/dist-tsc/ExpressionHistogramSubscriber.d.ts.map +1 -0
  22. package/dist-tsc/ExpressionHistogramSubscriber.js +70 -0
  23. package/dist-tsc/index.d.ts +7 -0
  24. package/dist-tsc/index.d.ts.map +1 -0
  25. package/dist-tsc/index.js +6 -6
  26. package/dist-tsc/styles.d.ts +2 -0
  27. package/dist-tsc/styles.d.ts.map +1 -0
  28. package/dist-tsc/styles.js +8 -0
  29. package/package.json +19 -10
  30. package/src/CellSetExpressionPlot.js +3 -3
  31. package/src/CellSetExpressionPlotOptions.js +1 -3
  32. package/src/CellSetExpressionPlotSubscriber.js +8 -16
  33. package/src/CellSetSizesPlot.js +90 -13
  34. package/src/CellSetSizesPlotSubscriber.js +65 -19
  35. package/src/ExpressionHistogram.js +58 -3
  36. package/src/ExpressionHistogramSubscriber.js +36 -18
  37. package/src/index.js +6 -6
  38. package/src/styles.js +1 -1
@@ -0,0 +1,77 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React, { useMemo, useState } from 'react';
3
+ import { TitleInfo, useCoordination, useLoaders, useUrls, useReady, useGridItemSize, useObsSetsData, } from '@vitessce/vit-s';
4
+ import { isEqual } from 'lodash-es';
5
+ import { ViewType, COMPONENT_COORDINATION_TYPES } from '@vitessce/constants-internal';
6
+ import { mergeObsSets, treeToSetSizesBySetNames, filterPathsByExpansionAndSelection, findChangedHierarchy, } from '@vitessce/sets-utils';
7
+ import { capitalize } from '@vitessce/utils';
8
+ import CellSetSizesPlot from './CellSetSizesPlot.js';
9
+ import { useStyles } from './styles.js';
10
+ /**
11
+ * A subscriber component for `CellSetSizePlot`,
12
+ * which listens for cell sets data updates and
13
+ * `GRID_RESIZE` events.
14
+ * @param {object} props
15
+ * @param {function} props.removeGridComponent The grid component removal function.
16
+ * @param {function} props.onReady The function to call when the subscriptions
17
+ * have been made.
18
+ * @param {string} props.theme The name of the current Vitessce theme.
19
+ * @param {string} props.title The component title.
20
+ */
21
+ export function CellSetSizesPlotSubscriber(props) {
22
+ const { coordinationScopes, removeGridComponent, theme, title: titleOverride, } = props;
23
+ const classes = useStyles();
24
+ const loaders = useLoaders();
25
+ // Get "props" from the coordination space.
26
+ const [{ dataset, obsType, obsSetSelection: cellSetSelection, obsSetColor: cellSetColor, additionalObsSets: additionalCellSets, obsSetExpansion: cellSetExpansion, }, { setObsSetSelection: setCellSetSelection, setObsSetColor: setCellSetColor, }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.OBS_SET_SIZES], coordinationScopes);
27
+ const title = titleOverride || `${capitalize(obsType)} Set Sizes`;
28
+ const [width, height, containerRef] = useGridItemSize();
29
+ const [urls, addUrl] = useUrls(loaders, dataset);
30
+ // the name of the hierarchy that was clicked on last
31
+ const [currentHierarchy, setCurrentHierarchy] = useState([]);
32
+ // the previous cell set that was selected
33
+ const [prevCellSetSelection, setPrevCellSetSelection] = useState([]);
34
+ // Get data from loaders using the data hooks.
35
+ const [{ obsSets: cellSets }, obsSetsStatus] = useObsSetsData(loaders, dataset, addUrl, true, { setObsSetSelection: setCellSetSelection, setObsSetColor: setCellSetColor }, { obsSetSelection: cellSetSelection, obsSetColor: cellSetColor }, { obsType });
36
+ const isReady = useReady([
37
+ obsSetsStatus,
38
+ ]);
39
+ const mergedCellSets = useMemo(() => mergeObsSets(cellSets, additionalCellSets), [cellSets, additionalCellSets]);
40
+ const data = useMemo(() => {
41
+ if (cellSetSelection && cellSetColor && mergedCellSets && cellSets) {
42
+ let newHierarchy = currentHierarchy;
43
+ if (cellSetSelection) {
44
+ const changedHierarchy = findChangedHierarchy(prevCellSetSelection, cellSetSelection);
45
+ setPrevCellSetSelection(cellSetSelection);
46
+ if (changedHierarchy) {
47
+ setCurrentHierarchy(changedHierarchy);
48
+ newHierarchy = changedHierarchy;
49
+ }
50
+ }
51
+ const cellSetPaths = filterPathsByExpansionAndSelection(mergedCellSets, newHierarchy, cellSetExpansion, cellSetSelection);
52
+ if (mergedCellSets && cellSets && cellSetSelection && cellSetColor) {
53
+ return treeToSetSizesBySetNames(mergedCellSets, cellSetPaths, cellSetSelection, cellSetColor, theme);
54
+ }
55
+ }
56
+ return [];
57
+ }, [
58
+ mergedCellSets,
59
+ cellSetSelection,
60
+ cellSetExpansion,
61
+ cellSetColor,
62
+ theme,
63
+ ]);
64
+ const onBarSelect = (setNamePath, wasGrayedOut, selectOnlyEnabled = false) => {
65
+ if (selectOnlyEnabled) {
66
+ setCellSetSelection([setNamePath]);
67
+ return;
68
+ }
69
+ if (!wasGrayedOut) {
70
+ setCellSetSelection(cellSetSelection.filter(d => !isEqual(d, setNamePath)));
71
+ }
72
+ else if (wasGrayedOut) {
73
+ setCellSetSelection([...cellSetSelection, setNamePath]);
74
+ }
75
+ };
76
+ return (_jsx(TitleInfo, { title: title, removeGridComponent: removeGridComponent, urls: urls, theme: theme, isReady: isReady, children: _jsx("div", { ref: containerRef, className: classes.vegaContainer, children: _jsx(CellSetSizesPlot, { data: data, onBarSelect: onBarSelect, theme: theme, width: width, height: height, obsType: obsType }) }) }));
77
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * We use debounce, so that onSelect is called only after the user has finished the selection.
3
+ * Due to vega-lite limitations, we cannot use the vega-lite signals to implement this.
4
+ * See this issue: https://github.com/vega/vega-lite/issues/5728
5
+ * See this for reference on what is supported: https://vega.github.io/vega-lite/docs/selection.html
6
+ */
7
+ /**
8
+ * Gene expression histogram displayed as a bar chart,
9
+ * implemented with the VegaPlot component.
10
+ * @param {object} props
11
+ * @param {string[]} props.geneSelection The list of genes
12
+ * currently selected.
13
+ * @param {object[]} props.data The expression data, an array
14
+ * of objects with properties `value` and `gene`.
15
+ * @param {string} props.theme The name of the current Vitessce theme.
16
+ * @param {number} props.width The container width.
17
+ * @param {number} props.height The container height.
18
+ * @param {number} props.marginRight The size of the margin
19
+ * on the right side of the plot, to account for the vega menu button.
20
+ * By default, 90.
21
+ * @param {number} props.marginBottom The size of the margin
22
+ * on the bottom of the plot, to account for long x-axis labels.
23
+ * By default, 50.
24
+ */
25
+ export default function ExpressionHistogram(props: {
26
+ geneSelection: string[];
27
+ data: object[];
28
+ theme: string;
29
+ width: number;
30
+ height: number;
31
+ marginRight: number;
32
+ marginBottom: number;
33
+ }): JSX.Element;
34
+ //# sourceMappingURL=ExpressionHistogram.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpressionHistogram.d.ts","sourceRoot":"","sources":["../src/ExpressionHistogram.js"],"names":[],"mappings":"AAIA;;;;;GAKG;AAEH;;;;;;;;;;;;;;;;;GAiBG;AACH;IAd2B,aAAa,EAA7B,MAAM,EAAE;IAEQ,IAAI,EAApB,MAAM,EAAE;IAEM,KAAK,EAAnB,MAAM;IACQ,KAAK,EAAnB,MAAM;IACQ,MAAM,EAApB,MAAM;IACQ,WAAW,EAAzB,MAAM;IAGQ,YAAY,EAA1B,MAAM;gBA8FhB"}
@@ -0,0 +1,93 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React, { useState, useEffect, useCallback } from 'react';
3
+ import { clamp, debounce } from 'lodash-es';
4
+ import { VegaPlot, VEGA_THEMES } from '@vitessce/vega';
5
+ /**
6
+ * We use debounce, so that onSelect is called only after the user has finished the selection.
7
+ * Due to vega-lite limitations, we cannot use the vega-lite signals to implement this.
8
+ * See this issue: https://github.com/vega/vega-lite/issues/5728
9
+ * See this for reference on what is supported: https://vega.github.io/vega-lite/docs/selection.html
10
+ */
11
+ /**
12
+ * Gene expression histogram displayed as a bar chart,
13
+ * implemented with the VegaPlot component.
14
+ * @param {object} props
15
+ * @param {string[]} props.geneSelection The list of genes
16
+ * currently selected.
17
+ * @param {object[]} props.data The expression data, an array
18
+ * of objects with properties `value` and `gene`.
19
+ * @param {string} props.theme The name of the current Vitessce theme.
20
+ * @param {number} props.width The container width.
21
+ * @param {number} props.height The container height.
22
+ * @param {number} props.marginRight The size of the margin
23
+ * on the right side of the plot, to account for the vega menu button.
24
+ * By default, 90.
25
+ * @param {number} props.marginBottom The size of the margin
26
+ * on the bottom of the plot, to account for long x-axis labels.
27
+ * By default, 50.
28
+ */
29
+ export default function ExpressionHistogram(props) {
30
+ const { geneSelection, data, theme, width, height, marginRight = 90, marginBottom = 50, onSelect, } = props;
31
+ const [selectedRanges, setSelectedRanges] = useState([]);
32
+ const xTitle = geneSelection && geneSelection.length >= 1
33
+ ? 'Normalized Expression Value'
34
+ : 'Total Normalized Transcript Count';
35
+ const spec = {
36
+ data: { values: data },
37
+ mark: 'bar',
38
+ encoding: {
39
+ x: {
40
+ field: 'value',
41
+ type: 'quantitative',
42
+ bin: { maxbins: 50 },
43
+ title: xTitle,
44
+ },
45
+ y: {
46
+ type: 'quantitative',
47
+ aggregate: 'count',
48
+ title: 'Number of Cells',
49
+ },
50
+ color: { value: 'gray' },
51
+ opacity: {
52
+ condition: { selection: 'brush', value: 1 },
53
+ value: 0.7,
54
+ },
55
+ },
56
+ params: [
57
+ {
58
+ name: 'brush',
59
+ select: { type: 'interval', encodings: ['x'] },
60
+ },
61
+ ],
62
+ width: clamp(width - marginRight, 10, Infinity),
63
+ height: clamp(height - marginBottom, 10, Infinity),
64
+ config: VEGA_THEMES[theme],
65
+ };
66
+ const handleSignal = (name, value) => {
67
+ if (name === 'brush') {
68
+ setSelectedRanges(value.value);
69
+ }
70
+ };
71
+ // eslint-disable-next-line react-hooks/exhaustive-deps
72
+ const debouncedOnSelect = useCallback(debounce((ranges, latestOnSelect) => {
73
+ latestOnSelect(ranges);
74
+ // We set a debounce timer of 1000ms: the assumption here is that the user has
75
+ // finished the selection when there's been no mouse movement on the histogram for a second.
76
+ // We do not pass any dependencies for the useCallback
77
+ // since we only want to define the debounced function once (on the initial render).
78
+ }, 1000), []);
79
+ useEffect(() => {
80
+ if (!selectedRanges || selectedRanges.length === 0)
81
+ return () => { };
82
+ // Call the debounced function instead of directly calling onSelect
83
+ debouncedOnSelect(selectedRanges, onSelect);
84
+ // Clean up the debounce timer when the component unmounts or the dependency changes
85
+ return () => {
86
+ debouncedOnSelect.cancel();
87
+ };
88
+ // We only want to call the debounced function when the selectedRanges changes.
89
+ // eslint-disable-next-line react-hooks/exhaustive-deps
90
+ }, [selectedRanges]);
91
+ const signalListeners = { brush: handleSignal };
92
+ return (_jsx(VegaPlot, { data: data, signalListeners: signalListeners, spec: spec }));
93
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * A subscriber component for `ExpressionHistogram`,
3
+ * which listens for gene selection updates and
4
+ * `GRID_RESIZE` events.
5
+ * @param {object} props
6
+ * @param {function} props.removeGridComponent The grid component removal function.
7
+ * @param {object} props.coordinationScopes An object mapping coordination
8
+ * types to coordination scopes.
9
+ * @param {string} props.theme The name of the current Vitessce theme.
10
+ */
11
+ export function ExpressionHistogramSubscriber(props: {
12
+ removeGridComponent: Function;
13
+ coordinationScopes: object;
14
+ theme: string;
15
+ }): JSX.Element;
16
+ //# sourceMappingURL=ExpressionHistogramSubscriber.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpressionHistogramSubscriber.d.ts","sourceRoot":"","sources":["../src/ExpressionHistogramSubscriber.js"],"names":[],"mappings":"AAcA;;;;;;;;;GASG;AACH;IAL2B,mBAAmB;IACrB,kBAAkB,EAAhC,MAAM;IAEQ,KAAK,EAAnB,MAAM;gBAiHhB"}
@@ -0,0 +1,70 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import React, { useMemo, useCallback, } from 'react';
3
+ import { sum } from 'd3-array';
4
+ import { TitleInfo, useCoordination, useLoaders, useUrls, useReady, useGridItemSize, useObsFeatureMatrixData, useFeatureSelection, } from '@vitessce/vit-s';
5
+ import { ViewType, COMPONENT_COORDINATION_TYPES } from '@vitessce/constants-internal';
6
+ import { setObsSelection, getObsInfoFromDataWithinRange } from '@vitessce/sets-utils';
7
+ import ExpressionHistogram from './ExpressionHistogram.js';
8
+ import { useStyles } from './styles.js';
9
+ /**
10
+ * A subscriber component for `ExpressionHistogram`,
11
+ * which listens for gene selection updates and
12
+ * `GRID_RESIZE` events.
13
+ * @param {object} props
14
+ * @param {function} props.removeGridComponent The grid component removal function.
15
+ * @param {object} props.coordinationScopes An object mapping coordination
16
+ * types to coordination scopes.
17
+ * @param {string} props.theme The name of the current Vitessce theme.
18
+ */
19
+ export function ExpressionHistogramSubscriber(props) {
20
+ const { coordinationScopes, removeGridComponent, theme, } = props;
21
+ const classes = useStyles();
22
+ const loaders = useLoaders();
23
+ // Get "props" from the coordination space.
24
+ const [{ dataset, obsType, featureType, featureValueType, featureSelection: geneSelection, additionalObsSets: additionalCellSets, obsSetColor: cellSetColor, }, { setAdditionalObsSets: setAdditionalCellSets, setObsSetColor: setCellSetColor, setObsColorEncoding: setCellColorEncoding, setObsSetSelection: setCellSetSelection, }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.FEATURE_VALUE_HISTOGRAM], coordinationScopes);
25
+ const [width, height, containerRef] = useGridItemSize();
26
+ const [urls, addUrl] = useUrls(loaders, dataset);
27
+ // Get data from loaders using the data hooks.
28
+ const [{ obsIndex, featureIndex, obsFeatureMatrix }, matrixStatus] = useObsFeatureMatrixData(loaders, dataset, addUrl, true, {}, {}, { obsType, featureType, featureValueType });
29
+ // eslint-disable-next-line no-unused-vars
30
+ const [expressionData, loadedFeatureSelection, featureSelectionStatus] = useFeatureSelection(loaders, dataset, false, geneSelection, { obsType, featureType, featureValueType });
31
+ const isReady = useReady([
32
+ matrixStatus,
33
+ featureSelectionStatus,
34
+ ]);
35
+ const firstGeneSelected = geneSelection && geneSelection.length >= 1
36
+ ? geneSelection[0]
37
+ : null;
38
+ // From the expression matrix and the list of selected genes,
39
+ // generate the array of data points for the histogram.
40
+ const data = useMemo(() => {
41
+ if (firstGeneSelected && obsFeatureMatrix && expressionData) {
42
+ return obsIndex.map((cellId, cellIndex) => {
43
+ const value = expressionData[0][cellIndex];
44
+ // Create new cellColors map based on the selected gene.
45
+ const normValue = value * 100 / 255;
46
+ const newItem = { value: normValue, gene: firstGeneSelected, cellId };
47
+ return newItem;
48
+ });
49
+ }
50
+ if (obsFeatureMatrix) {
51
+ const numGenes = featureIndex.length;
52
+ return obsIndex.map((cellId, cellIndex) => {
53
+ const values = obsFeatureMatrix.data
54
+ .subarray(cellIndex * numGenes, (cellIndex + 1) * numGenes);
55
+ const sumValue = sum(values) * 100 / 255;
56
+ const newItem = { value: sumValue, gene: null, cellId };
57
+ return newItem;
58
+ });
59
+ }
60
+ return null;
61
+ }, [obsIndex, featureIndex, obsFeatureMatrix, firstGeneSelected, expressionData]);
62
+ const onSelect = useCallback((value) => {
63
+ const geneName = firstGeneSelected ? [firstGeneSelected, 'values'].join(' ') : 'transcript count';
64
+ const selectedCellIds = getObsInfoFromDataWithinRange(value, data);
65
+ setObsSelection(selectedCellIds, additionalCellSets, cellSetColor, setCellSetSelection, setAdditionalCellSets, setCellSetColor, setCellColorEncoding, 'Selection ', `: based on ${geneName} in range [${value[0].toFixed(1)}, ${value[1].toFixed(1)}] `);
66
+ }, [additionalCellSets, cellSetColor, data, setAdditionalCellSets,
67
+ setCellColorEncoding, setCellSetColor, setCellSetSelection, firstGeneSelected,
68
+ ]);
69
+ return (_jsx(TitleInfo, { title: `Expression Histogram${(firstGeneSelected ? ` (${firstGeneSelected})` : '')}`, removeGridComponent: removeGridComponent, urls: urls, theme: theme, isReady: isReady, children: _jsx("div", { ref: containerRef, className: classes.vegaContainer, children: _jsx(ExpressionHistogram, { geneSelection: geneSelection, onSelect: onSelect, data: data, theme: theme, width: width, height: height }) }) }));
70
+ }
@@ -0,0 +1,7 @@
1
+ export { CellSetExpressionPlotSubscriber } from "./CellSetExpressionPlotSubscriber.js";
2
+ export { CellSetSizesPlotSubscriber } from "./CellSetSizesPlotSubscriber.js";
3
+ export { ExpressionHistogramSubscriber } from "./ExpressionHistogramSubscriber.js";
4
+ export { default as CellSetSizesPlot } from "./CellSetSizesPlot.js";
5
+ export { default as CellSetExpressionPlot } from "./CellSetExpressionPlot.js";
6
+ export { default as ExpressionHistogram } from "./ExpressionHistogram.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":""}
package/dist-tsc/index.js CHANGED
@@ -1,6 +1,6 @@
1
- export { CellSetExpressionPlotSubscriber, register as registerCellSetExpression } from './CellSetExpressionPlotSubscriber';
2
- export { CellSetSizesPlotSubscriber, register as registerCellSetSizes } from './CellSetSizesPlotSubscriber';
3
- export { ExpressionHistogramSubscriber, register as registerExpressionHistogram } from './ExpressionHistogramSubscriber';
4
- export { default as CellSetSizesPlot } from './CellSetSizesPlot';
5
- export { default as CellSetExpressionPlot } from './CellSetExpressionPlot';
6
- export { default as ExpressionHistogram } from './ExpressionHistogram';
1
+ export { CellSetExpressionPlotSubscriber } from './CellSetExpressionPlotSubscriber.js';
2
+ export { CellSetSizesPlotSubscriber } from './CellSetSizesPlotSubscriber.js';
3
+ export { ExpressionHistogramSubscriber } from './ExpressionHistogramSubscriber.js';
4
+ export { default as CellSetSizesPlot } from './CellSetSizesPlot.js';
5
+ export { default as CellSetExpressionPlot } from './CellSetExpressionPlot.js';
6
+ export { default as ExpressionHistogram } from './ExpressionHistogram.js';
@@ -0,0 +1,2 @@
1
+ export const useStyles: (props?: any) => import("@material-ui/core/styles/withStyles").ClassNameMap<"vegaContainer">;
2
+ //# sourceMappingURL=styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../src/styles.js"],"names":[],"mappings":"AAEA,qHAMI"}
@@ -0,0 +1,8 @@
1
+ import { makeStyles } from '@material-ui/core';
2
+ export const useStyles = makeStyles(() => ({
3
+ vegaContainer: {
4
+ display: 'flex',
5
+ flex: '1 1 auto',
6
+ overflow: 'hidden',
7
+ },
8
+ }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitessce/statistical-plots",
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,24 +8,26 @@
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
20
  "d3-array": "^2.4.0",
19
- "lodash": "^4.17.21",
20
- "@vitessce/constants-internal": "2.0.3",
21
- "@vitessce/sets-utils": "2.0.3",
22
- "@vitessce/utils": "2.0.3",
23
- "@vitessce/vega": "2.0.3",
24
- "@vitessce/vit-s": "2.0.3"
21
+ "lodash-es": "^4.17.21",
22
+ "@vitessce/constants-internal": "3.0.0",
23
+ "@vitessce/sets-utils": "3.0.0",
24
+ "@vitessce/utils": "3.0.0",
25
+ "@vitessce/vega": "3.0.0",
26
+ "@vitessce/vit-s": "3.0.0"
25
27
  },
26
28
  "devDependencies": {
27
29
  "react": "^18.0.0",
28
- "vite": "^3.0.0",
30
+ "vite": "^4.3.0",
29
31
  "vitest": "^0.23.4"
30
32
  },
31
33
  "peerDependencies": {
@@ -34,5 +36,12 @@
34
36
  "scripts": {
35
37
  "bundle": "pnpm exec vite build -c ../../../scripts/vite.config.js",
36
38
  "test": "pnpm exec vitest --run -r ../../../ --dir ."
39
+ },
40
+ "module": "dist/index.js",
41
+ "exports": {
42
+ ".": {
43
+ "types": "./dist-tsc/index.d.ts",
44
+ "import": "./dist/index.js"
45
+ }
37
46
  }
38
47
  }
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import clamp from 'lodash/clamp';
2
+ import { clamp } from 'lodash-es';
3
3
  import { VegaPlot, VEGA_THEMES, DATASET_NAME } from '@vitessce/vega';
4
4
  import { colorArrayToString } from '@vitessce/sets-utils';
5
5
  import { capitalize } from '@vitessce/utils';
@@ -155,8 +155,8 @@ export default function CellSetExpressionPlot(props) {
155
155
  scale: 'yscale',
156
156
  zindex: 1,
157
157
  title: (featureValueTransformName && featureValueTransformName !== 'None')
158
- ? [`${featureValueTransformName}-Transformed`, `Normalized ${capitalize(featureValueType)} Values`]
159
- : `Normalized ${capitalize(featureValueType)} Values`,
158
+ ? [`${featureValueTransformName}-Transformed`, `${capitalize(featureValueType)} Values`]
159
+ : `${capitalize(featureValueType)} Values`,
160
160
  },
161
161
  {
162
162
  orient: 'bottom',
@@ -1,7 +1,5 @@
1
1
  import React from 'react';
2
- import TableCell from '@material-ui/core/TableCell';
3
- import TableRow from '@material-ui/core/TableRow';
4
- import TextField from '@material-ui/core/TextField';
2
+ import { TableCell, TableRow, TextField } from '@material-ui/core';
5
3
  import { usePlotOptionsStyles, OptionsContainer, OptionSelect } from '@vitessce/vit-s';
6
4
 
7
5
  export default function CellSetExpressionPlotOptions(props) {
@@ -6,14 +6,13 @@ import {
6
6
  useFeatureSelection, useObsSetsData,
7
7
  useObsFeatureMatrixIndices,
8
8
  useFeatureLabelsData,
9
- registerPluginViewType,
10
9
  } from '@vitessce/vit-s';
11
10
  import { ViewType, COMPONENT_COORDINATION_TYPES } from '@vitessce/constants-internal';
12
11
  import { VALUE_TRANSFORM_OPTIONS, capitalize, getValueTransformFunction } from '@vitessce/utils';
13
12
  import { treeToObjectsBySetNames, treeToSetSizesBySetNames, mergeObsSets } from '@vitessce/sets-utils';
14
- import CellSetExpressionPlotOptions from './CellSetExpressionPlotOptions';
15
- import CellSetExpressionPlot from './CellSetExpressionPlot';
16
- import { useStyles } from './styles';
13
+ import CellSetExpressionPlotOptions from './CellSetExpressionPlotOptions.js';
14
+ import CellSetExpressionPlot from './CellSetExpressionPlot.js';
15
+ import { useStyles } from './styles.js';
17
16
 
18
17
  /**
19
18
  * Get expression data for the cells
@@ -36,7 +35,7 @@ import { useStyles } from './styles';
36
35
  * @param {string} theme "light" or "dark" for the vitessce theme
37
36
  * `path` and `color`.
38
37
  */
39
- export function useExpressionByCellSet(
38
+ function useExpressionByCellSet(
40
39
  expressionData, obsIndex, cellSets, additionalCellSets,
41
40
  geneSelection, cellSetSelection, cellSetColor,
42
41
  featureValueTransform, featureValueTransformCoefficient,
@@ -68,11 +67,10 @@ export function useExpressionByCellSet(
68
67
  const exprValues = cellObjects.map((cell) => {
69
68
  const cellIndex = cellIndices[cell.obsId];
70
69
  const value = expressionData[0][cellIndex];
71
- const normValue = value * 100 / 255;
72
70
  const transformFunction = getValueTransformFunction(
73
71
  featureValueTransform, featureValueTransformCoefficient,
74
72
  );
75
- const transformedValue = transformFunction(normValue);
73
+ const transformedValue = transformFunction(value);
76
74
  exprMax = Math.max(transformedValue, exprMax);
77
75
  return { value: transformedValue, gene: firstGeneSelected, set: cell.name };
78
76
  });
@@ -87,7 +85,9 @@ export function useExpressionByCellSet(
87
85
  // From the cell sets hierarchy and the list of selected cell sets,
88
86
  // generate the array of set sizes data points for the bar plot.
89
87
  const setArr = useMemo(() => (mergedCellSets && cellSetSelection && cellSetColor
90
- ? treeToSetSizesBySetNames(mergedCellSets, cellSetSelection, cellSetColor, theme)
88
+ ? treeToSetSizesBySetNames(
89
+ mergedCellSets, cellSetSelection, cellSetSelection, cellSetColor, theme,
90
+ )
91
91
  : []
92
92
  ), [mergedCellSets, cellSetSelection, cellSetColor, theme]);
93
93
 
@@ -218,11 +218,3 @@ export function CellSetExpressionPlotSubscriber(props) {
218
218
  </TitleInfo>
219
219
  );
220
220
  }
221
-
222
- export function register() {
223
- registerPluginViewType(
224
- ViewType.OBS_SET_FEATURE_VALUE_DISTRIBUTION,
225
- CellSetExpressionPlotSubscriber,
226
- COMPONENT_COORDINATION_TYPES[ViewType.OBS_SET_FEATURE_VALUE_DISTRIBUTION],
227
- );
228
- }
@@ -1,8 +1,8 @@
1
- import React from 'react';
2
- import clamp from 'lodash/clamp';
1
+ import React, { useCallback } from 'react';
2
+ import { clamp } from 'lodash-es';
3
3
  import { VegaPlot, VEGA_THEMES } from '@vitessce/vega';
4
4
  import { colorArrayToString } from '@vitessce/sets-utils';
5
- import { capitalize } from '@vitessce/utils';
5
+ import { capitalize, getDefaultColor } from '@vitessce/utils';
6
6
 
7
7
  /**
8
8
  * Cell set sizes displayed as a bar chart,
@@ -33,6 +33,7 @@ export default function CellSetSizesPlot(props) {
33
33
  marginBottom = 120,
34
34
  keyLength = 36,
35
35
  obsType,
36
+ onBarSelect,
36
37
  } = props;
37
38
 
38
39
  // Add a property `keyName` which concatenates the key and the name,
@@ -46,18 +47,53 @@ export default function CellSetSizesPlot(props) {
46
47
  colorString: colorArrayToString(d.color),
47
48
  }));
48
49
 
49
- // Manually set the color scale so that Vega-Lite does
50
- // not choose the colors automatically.
51
- const colors = {
52
- domain: data.map(d => d.key),
53
- range: data.map(d => d.colorString),
54
- };
55
-
56
50
  // Get an array of keys for sorting purposes.
57
51
  const keys = data.map(d => d.keyName);
58
52
 
53
+ const colorScale = {
54
+ // Manually set the color scale so that Vega-Lite does
55
+ // not choose the colors automatically.
56
+ domain: data.map(d => d.key),
57
+ range: data.map((d) => {
58
+ const [r, g, b] = !d.isGrayedOut ? d.color : getDefaultColor(theme);
59
+ return `rgba(${r}, ${g}, ${b}, 1)`;
60
+ }),
61
+ };
62
+ const captializedObsType = capitalize(obsType);
63
+
59
64
  const spec = {
60
- mark: { type: 'bar' },
65
+ mark: { type: 'bar', stroke: 'black', cursor: 'pointer' },
66
+ params: [
67
+ {
68
+ name: 'highlight',
69
+ select: {
70
+ type: 'point',
71
+ on: 'mouseover',
72
+ },
73
+ },
74
+ {
75
+ name: 'select',
76
+ select: 'point',
77
+ },
78
+ {
79
+ name: 'bar_select',
80
+ select: {
81
+ type: 'point',
82
+ on: 'click[event.shiftKey === false]',
83
+ fields: ['setNamePath', 'isGrayedOut'],
84
+ empty: 'none',
85
+ },
86
+ },
87
+ {
88
+ name: 'shift_bar_select',
89
+ select: {
90
+ type: 'point',
91
+ on: 'click[event.shiftKey]',
92
+ fields: ['setNamePath', 'isGrayedOut'],
93
+ empty: 'none',
94
+ },
95
+ },
96
+ ],
61
97
  encoding: {
62
98
  x: {
63
99
  field: 'keyName',
@@ -69,28 +105,69 @@ export default function CellSetSizesPlot(props) {
69
105
  y: {
70
106
  field: 'size',
71
107
  type: 'quantitative',
72
- title: `${capitalize(obsType)} Set Size`,
108
+ title: `${captializedObsType} Set Size`,
73
109
  },
74
110
  color: {
75
111
  field: 'key',
76
112
  type: 'nominal',
77
- scale: colors,
113
+ scale: colorScale,
78
114
  legend: null,
79
115
  },
80
116
  tooltip: {
81
117
  field: 'size',
82
118
  type: 'quantitative',
83
119
  },
120
+ fillOpacity: {
121
+ condition: {
122
+ param: 'select',
123
+ value: 1,
124
+ },
125
+ value: 0.3,
126
+ },
127
+ strokeWidth: {
128
+ condition: [
129
+ {
130
+ param: 'select',
131
+ empty: false,
132
+ value: 1,
133
+ },
134
+ {
135
+ param: 'highlight',
136
+ empty: false,
137
+ value: 2,
138
+ },
139
+ ],
140
+ value: 0,
141
+ },
84
142
  },
85
143
  width: clamp(width - marginRight, 10, Infinity),
86
144
  height: clamp(height - marginBottom, 10, Infinity),
87
145
  config: VEGA_THEMES[theme],
88
146
  };
89
147
 
148
+ const handleSignal = (name, value) => {
149
+ if (name === 'bar_select') {
150
+ onBarSelect(value.setNamePath, value.isGrayedOut[0]);
151
+ } else if (name === 'shift_bar_select') {
152
+ const isGrayedOut = false;
153
+ const selectOnlyEnabled = true;
154
+ onBarSelect(value.setNamePath, isGrayedOut, selectOnlyEnabled);
155
+ }
156
+ };
157
+
158
+ const signalListeners = { bar_select: handleSignal, shift_bar_select: handleSignal };
159
+ const getTooltipText = useCallback(item => ({
160
+ [`${captializedObsType} Set`]: item.datum.name,
161
+ [`${captializedObsType} Set Size`]: item.datum.size,
162
+ }
163
+ ), [captializedObsType]);
164
+
90
165
  return (
91
166
  <VegaPlot
92
167
  data={data}
93
168
  spec={spec}
169
+ signalListeners={signalListeners}
170
+ getTooltipText={getTooltipText}
94
171
  />
95
172
  );
96
173
  }