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
+22 -7
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 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>
); );
+10 -8
View File
@@ -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) => {
+67 -51
View File
@@ -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"
> >
+1 -1
View File
@@ -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 = {
+15
View File
@@ -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);