mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-09-15 20:57:56 +08:00
tooltips, copy, scatterplot tray (#383)
* tooltips, copy, scatterplot tray * reset interface * copy
This commit is contained in:
@@ -115,12 +115,6 @@ const regraph = () => (dispatch, getState) => {
|
||||
});
|
||||
};
|
||||
|
||||
const resetGraph = () => (dispatch, getState) =>
|
||||
dispatch({
|
||||
type: "reset World to eq Universe",
|
||||
universe: getState().controls.universe
|
||||
});
|
||||
|
||||
/*
|
||||
Fetch expression vectors for each gene in genes. This is NOT an action
|
||||
function, but rather a helper to be called from an action helper that
|
||||
@@ -302,9 +296,30 @@ const requestDifferentialExpression = (set1, set2, num_genes = 10) => async (
|
||||
}
|
||||
};
|
||||
|
||||
const resetInterface = () => (dispatch, getState) => {
|
||||
const { universe } = getState().controls;
|
||||
|
||||
dispatch({
|
||||
type: "clear all user defined genes"
|
||||
});
|
||||
dispatch({
|
||||
type: "clear differential expression"
|
||||
});
|
||||
dispatch({
|
||||
type: "reset colorscale"
|
||||
});
|
||||
dispatch({
|
||||
type: "clear scatterplot"
|
||||
});
|
||||
dispatch({
|
||||
type: "reset World to eq Universe",
|
||||
universe
|
||||
});
|
||||
};
|
||||
|
||||
export default {
|
||||
regraph,
|
||||
resetGraph,
|
||||
resetInterface,
|
||||
requestSingleGeneExpressionCountsForColoringPOST,
|
||||
requestGeneExpressionCountsPOST,
|
||||
requestDifferentialExpression,
|
||||
|
||||
@@ -6,7 +6,7 @@ https://bl.ocks.org/SpaceActuary/2f004899ea1b2bd78d6f1dbb2febf771
|
||||
// jshint esversion: 6
|
||||
import React from "react";
|
||||
import _ from "lodash";
|
||||
import { Button, ButtonGroup } from "@blueprintjs/core";
|
||||
import { Button, ButtonGroup, Tooltip } from "@blueprintjs/core";
|
||||
import { connect } from "react-redux";
|
||||
import * as d3 from "d3";
|
||||
import memoize from "memoize-one";
|
||||
@@ -283,12 +283,14 @@ class HistogramBrush extends React.Component {
|
||||
remove
|
||||
</Button>
|
||||
) : null}
|
||||
<Button
|
||||
onClick={this.handleColorAction.bind(this)}
|
||||
active={colorAccessor === field}
|
||||
intent={colorAccessor === field ? "primary" : "none"}
|
||||
icon="tint"
|
||||
/>
|
||||
<Tooltip content="Use as color scale" position="bottom">
|
||||
<Button
|
||||
onClick={this.handleColorAction.bind(this)}
|
||||
active={colorAccessor === field}
|
||||
intent={colorAccessor === field ? "primary" : "none"}
|
||||
icon="tint"
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<svg
|
||||
width={this.width}
|
||||
|
||||
@@ -3,7 +3,7 @@ import _ from "lodash";
|
||||
import { connect } from "react-redux";
|
||||
import { FaChevronRight, FaChevronDown } from "react-icons/fa";
|
||||
import memoize from "memoize-one";
|
||||
import { Button } from "@blueprintjs/core";
|
||||
import { Button, Tooltip, Position } from "@blueprintjs/core";
|
||||
|
||||
import * as globals from "../../globals";
|
||||
import Value from "./value";
|
||||
@@ -162,12 +162,14 @@ class Category extends React.Component {
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<Button
|
||||
onClick={this.handleColorChange.bind(this)}
|
||||
active={colorAccessor === metadataField}
|
||||
intent={colorAccessor === metadataField ? "primary" : "none"}
|
||||
icon={"tint"}
|
||||
/>
|
||||
<Tooltip content="Use as color scale" position="bottom">
|
||||
<Button
|
||||
onClick={this.handleColorChange.bind(this)}
|
||||
active={colorAccessor === metadataField}
|
||||
intent={colorAccessor === metadataField ? "primary" : "none"}
|
||||
icon={"tint"}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div style={{ marginLeft: 26 }}>
|
||||
{isExpanded ? this.renderCategoryItems() : null}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// jshint esversion: 6
|
||||
import React from "react";
|
||||
import _ from "lodash";
|
||||
import { Button } from "@blueprintjs/core";
|
||||
import { Button, Tooltip } from "@blueprintjs/core";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
@connect()
|
||||
@@ -29,15 +29,22 @@ class CellSetButton extends React.Component {
|
||||
const { differential, eitherCellSetOneOrTwo } = this.props;
|
||||
const cellListName = `celllist${eitherCellSetOneOrTwo}`;
|
||||
return (
|
||||
<span style={{ marginRight: 10 }}>
|
||||
<Button type="button" onClick={this.set.bind(this)}>
|
||||
<Tooltip
|
||||
content="Save current selection for differential expression computation"
|
||||
position="top"
|
||||
>
|
||||
<Button
|
||||
style={{ marginRight: 10 }}
|
||||
type="button"
|
||||
onClick={this.set.bind(this)}
|
||||
>
|
||||
{eitherCellSetOneOrTwo}
|
||||
{": "}
|
||||
{differential[cellListName]
|
||||
? `${differential[cellListName].length} cells`
|
||||
: "0 cells"}
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// jshint esversion: 6
|
||||
import React from "react";
|
||||
import _ from "lodash";
|
||||
import { Button } from "@blueprintjs/core";
|
||||
import { Button, AnchorButton, Tooltip } from "@blueprintjs/core";
|
||||
import { connect } from "react-redux";
|
||||
import * as globals from "../../globals";
|
||||
import actions from "../../actions";
|
||||
@@ -60,27 +60,37 @@ class Expression extends React.Component {
|
||||
<CellSetButton {...this.props} eitherCellSetOneOrTwo={1} />
|
||||
<CellSetButton {...this.props} eitherCellSetOneOrTwo={2} />
|
||||
{!differential.diffExp ? (
|
||||
<Button
|
||||
style={{ marginTop: 10 }}
|
||||
disabled={!haveBothCellSets}
|
||||
intent="primary"
|
||||
fill
|
||||
type="button"
|
||||
onClick={this.computeDiffExp.bind(this)}
|
||||
<Tooltip
|
||||
content="Add two cells selections, see the top 15 differentially expressed genes between them"
|
||||
position="bottom"
|
||||
>
|
||||
Compute Differential Expression
|
||||
</Button>
|
||||
<AnchorButton
|
||||
style={{ marginTop: 10 }}
|
||||
disabled={!haveBothCellSets}
|
||||
intent="primary"
|
||||
fill
|
||||
type="button"
|
||||
onClick={this.computeDiffExp.bind(this)}
|
||||
>
|
||||
Compute Differential Expression
|
||||
</AnchorButton>
|
||||
</Tooltip>
|
||||
) : null}
|
||||
{differential.diffExp ? (
|
||||
<Button
|
||||
type="button"
|
||||
fill
|
||||
style={{ marginTop: 10 }}
|
||||
intent="warning"
|
||||
onClick={this.clearDifferentialExpression.bind(this)}
|
||||
<Tooltip
|
||||
content="Remove differentially expressed gene list and clear cell selections"
|
||||
position="bottom"
|
||||
>
|
||||
Clear Differential Expression
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
fill
|
||||
style={{ marginTop: 10 }}
|
||||
intent="warning"
|
||||
onClick={this.clearDifferentialExpression.bind(this)}
|
||||
>
|
||||
Clear Differential Expression
|
||||
</Button>
|
||||
</Tooltip>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -5,6 +5,7 @@ import React from "react";
|
||||
import _ from "lodash";
|
||||
import * as d3 from "d3";
|
||||
import { connect } from "react-redux";
|
||||
import { Button, Tooltip } from "@blueprintjs/core";
|
||||
import HistogramBrush from "../brushableHistogram";
|
||||
import * as globals from "../../globals";
|
||||
import actions from "../../actions";
|
||||
@@ -85,7 +86,7 @@ class GeneExpression extends React.Component {
|
||||
margin: 0
|
||||
})}
|
||||
>
|
||||
Custom genes
|
||||
Selected Genes
|
||||
</p>
|
||||
<div
|
||||
style={{ padding: globals.leftSidebarSectionPadding }}
|
||||
@@ -100,17 +101,18 @@ class GeneExpression extends React.Component {
|
||||
value={gene}
|
||||
type="text"
|
||||
className="bp3-input"
|
||||
placeholder="Add a custom gene"
|
||||
placeholder="Enter a gene name"
|
||||
style={{ paddingRight: 94 }}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="bp3-button bp3-intent-primary"
|
||||
onClick={this.handleClick.bind(this)}
|
||||
<Tooltip
|
||||
content="Add a gene to see its expression levels"
|
||||
position="bottom"
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
<Button intent="primary" onClick={this.handleClick.bind(this)}>
|
||||
Add
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{world && userDefinedGenes.length > 0
|
||||
? _.map(userDefinedGenes, (geneName, index) => {
|
||||
|
||||
@@ -5,7 +5,7 @@ import * as d3 from "d3";
|
||||
import { connect } from "react-redux";
|
||||
import mat4 from "gl-mat4";
|
||||
import _regl from "regl";
|
||||
import { Button } from "@blueprintjs/core";
|
||||
import { Button, AnchorButton, Tooltip } from "@blueprintjs/core";
|
||||
import { worldEqUniverse } from "../../util/stateManager/world";
|
||||
|
||||
import setupSVGandBrushElements from "./setupSVGandBrush";
|
||||
@@ -327,8 +327,13 @@ class Graph extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
resetInterface() {
|
||||
const { dispatch } = this.props;
|
||||
dispatch(actions.resetInterface());
|
||||
}
|
||||
|
||||
render() {
|
||||
const { dispatch, responsive, world, universe, crossfilter } = this.props;
|
||||
const { dispatch, responsive, crossfilter } = this.props;
|
||||
const { mode } = this.state;
|
||||
return (
|
||||
<div id="graphWrapper">
|
||||
@@ -347,56 +352,69 @@ class Graph extends React.Component {
|
||||
alignItems: "baseline"
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
type="button"
|
||||
disabled={
|
||||
crossfilter &&
|
||||
(crossfilter.countFiltered() === 0 ||
|
||||
crossfilter.countFiltered() === crossfilter.size())
|
||||
}
|
||||
style={{ marginRight: 10 }}
|
||||
onClick={() => {
|
||||
dispatch(actions.regraph());
|
||||
}}
|
||||
<Tooltip
|
||||
content="Show only metadata and cells which are currently selected"
|
||||
position="left"
|
||||
>
|
||||
subset to current selection
|
||||
</Button>
|
||||
<Button
|
||||
disabled={
|
||||
world && universe ? worldEqUniverse(world, universe) : false
|
||||
}
|
||||
type="button"
|
||||
intent="warning"
|
||||
style={{ marginRight: 10 }}
|
||||
onClick={() => {
|
||||
dispatch(actions.resetGraph());
|
||||
}}
|
||||
<AnchorButton
|
||||
type="button"
|
||||
disabled={
|
||||
crossfilter &&
|
||||
(crossfilter.countFiltered() === 0 ||
|
||||
crossfilter.countFiltered() === crossfilter.size())
|
||||
}
|
||||
style={{ marginRight: 10 }}
|
||||
onClick={() => {
|
||||
dispatch(actions.regraph());
|
||||
}}
|
||||
>
|
||||
subset to current selection
|
||||
</AnchorButton>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
content="Reset cellxgene, clearing all selections"
|
||||
position="left"
|
||||
>
|
||||
reset
|
||||
</Button>
|
||||
<AnchorButton
|
||||
disabled={
|
||||
false
|
||||
/* world && universe ? worldEqUniverse(world, universe) : false */
|
||||
}
|
||||
type="button"
|
||||
intent="warning"
|
||||
style={{ marginRight: 10 }}
|
||||
onClick={this.resetInterface.bind(this)}
|
||||
>
|
||||
reset
|
||||
</AnchorButton>
|
||||
</Tooltip>
|
||||
<div>
|
||||
<div className="bp3-button-group">
|
||||
<Button
|
||||
className="bp3-button bp3-icon-locate"
|
||||
type="button"
|
||||
active={mode === "brush"}
|
||||
onClick={() => {
|
||||
this.setState({ mode: "brush" });
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
className="bp3-button bp3-icon-zoom-in"
|
||||
active={mode === "zoom"}
|
||||
onClick={() => {
|
||||
this.handleBrushDeselectAction();
|
||||
this.restartReglLoop();
|
||||
this.setState({ mode: "zoom" });
|
||||
}}
|
||||
style={{
|
||||
cursor: "pointer"
|
||||
}}
|
||||
/>
|
||||
<Tooltip content="Lasso cells" position="left">
|
||||
<Button
|
||||
className="bp3-button bp3-icon-select"
|
||||
type="button"
|
||||
active={mode === "brush"}
|
||||
onClick={() => {
|
||||
this.setState({ mode: "brush" });
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip content="Pan and zoom" position="left">
|
||||
<Button
|
||||
type="button"
|
||||
className="bp3-button bp3-icon-zoom-in"
|
||||
active={mode === "zoom"}
|
||||
onClick={() => {
|
||||
this.handleBrushDeselectAction();
|
||||
this.restartReglLoop();
|
||||
this.setState({ mode: "zoom" });
|
||||
}}
|
||||
style={{
|
||||
cursor: "pointer"
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -408,9 +426,7 @@ class Graph extends React.Component {
|
||||
zIndex: -9999,
|
||||
position: "fixed",
|
||||
right: this.graphPaddingRight,
|
||||
bottom: this.graphPaddingBottom,
|
||||
borderLeft: "2px solid rgb(233,233,233)",
|
||||
borderBottom: "2px solid rgb(233,233,233)"
|
||||
bottom: this.graphPaddingBottom
|
||||
}}
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -280,9 +280,12 @@ class Scatterplot extends React.Component {
|
||||
style={{
|
||||
position: "fixed",
|
||||
bottom: minimized ? -height + -margin.top : 0,
|
||||
left: globals.leftSidebarWidth + globals.scatterplotPaddingLeft,
|
||||
paddingBottom: 20,
|
||||
backgroundColor: "white"
|
||||
borderRadius: "3px 3px 0px 0px",
|
||||
left: globals.leftSidebarWidth + globals.scatterplotMarginLeft,
|
||||
padding: "0px 20px 20px 0px",
|
||||
backgroundColor: "white",
|
||||
/* x y blur spread color */
|
||||
boxShadow: "0px 0px 6px 2px rgba(153,153,153,0.4)"
|
||||
}}
|
||||
id="scatterplot_wrapper"
|
||||
>
|
||||
|
||||
@@ -78,7 +78,7 @@ export const maxControlsWidth = 800;
|
||||
export const graphMargin = { top: 20, right: 10, bottom: 30, left: 40 };
|
||||
export const graphWidth = 700;
|
||||
export const graphHeight = 700;
|
||||
export const scatterplotPaddingLeft = 14;
|
||||
export const scatterplotMarginLeft = 25;
|
||||
|
||||
export const leftSidebarWidth = 365;
|
||||
export const leftSidebarSectionHeading = {
|
||||
|
||||
15
client/src/reducers/controls.js
vendored
15
client/src/reducers/controls.js
vendored
@@ -330,6 +330,21 @@ const Controls = (
|
||||
userDefinedGenes: newUserDefinedGenes
|
||||
};
|
||||
}
|
||||
case "clear all user defined genes": {
|
||||
const { userDefinedGenes, dimensionMap } = state;
|
||||
|
||||
_.forEach(userDefinedGenes, gene => {
|
||||
const dimension = dimensionMap[userDefinedDimensionName(gene)];
|
||||
dimension.dispose();
|
||||
delete dimensionMap[userDefinedDimensionName(gene)];
|
||||
});
|
||||
|
||||
return {
|
||||
...state,
|
||||
dimensionMap,
|
||||
userDefinedGenes: []
|
||||
};
|
||||
}
|
||||
case "reset colorscale": {
|
||||
const { world } = state;
|
||||
const colorName = new Array(world.nObs).fill(globals.defaultCellColor);
|
||||
|
||||
Reference in New Issue
Block a user