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