tooltips, copy, scatterplot tray (#383)

* tooltips, copy, scatterplot tray

* reset interface

* copy
This commit is contained in:
Colin Megill
2018-10-26 19:21:39 -04:00
committed by GitHub
parent 6c23a72e5f
commit ed66ae6e4a
10 changed files with 178 additions and 106 deletions

View File

@@ -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,

View File

@@ -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}

View File

@@ -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}

View File

@@ -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>
);
}
}

View File

@@ -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>
);

View File

@@ -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) => {

View File

@@ -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

View File

@@ -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"
>

View File

@@ -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 = {

View File

@@ -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);