left sidebar improvements (#368)

* left sidebar improvements

* lint
This commit is contained in:
Colin Megill
2018-10-24 10:21:18 -04:00
committed by GitHub
parent 3556bd11f5
commit 7aa390b5a1
14 changed files with 190 additions and 194 deletions
+1 -4
View File
@@ -33,7 +33,4 @@ npm-debug.log
*.idea*
__pycache__
*.DS_Store*
data
+1 -1
View File
@@ -6,7 +6,7 @@
<title>cellxgene</title>
<link href="https://fonts.googleapis.com/css?family=Roboto+Condensed:400,400i,700" rel="stylesheet">
<style>
html, body, p, h1, h2, h3, h4, h5, h6, span, button, input, label, text {
html, body, p, h1, h2, h3, h4, h5, h6, span, button, input, label, text, div {
font-family: 'Roboto Condensed','Helvetica Neue','Helvetica','Arial',sans-serif;
font-size: 14px;
}
+1 -1
View File
@@ -6,7 +6,7 @@
<title>cellxgene</title>
<link href="https://fonts.googleapis.com/css?family=Roboto+Condensed:400,400i,700" rel="stylesheet">
<style>
html, body, p, h1, h2, h3, h4, h5, h6, span, button, input, label, text {
html, body, p, h1, h2, h3, h4, h5, h6, span, button, input, label, text, div {
font-family: 'Roboto Condensed','Helvetica Neue','Helvetica','Arial',sans-serif;
font-size: 14px;
}
+80 -105
View File
@@ -6,8 +6,8 @@ https://bl.ocks.org/SpaceActuary/2f004899ea1b2bd78d6f1dbb2febf771
// jshint esversion: 6
import React from "react";
import _ from "lodash";
import { Button, ButtonGroup } from "@blueprintjs/core";
import { connect } from "react-redux";
import { FaPaintBrush } from "react-icons/fa";
import * as d3 from "d3";
import memoize from "memoize-one";
import { kvCache } from "../../util/stateManager";
@@ -74,7 +74,7 @@ class HistogramBrush extends React.Component {
constructor(props) {
super(props);
this.width = 300;
this.width = 340;
this.height = 100;
this.marginBottom = 20;
@@ -156,8 +156,7 @@ class HistogramBrush extends React.Component {
d3.select(svgRef)
.selectAll(".axis--x text")
.style("fill", "rgb(80,80,80)")
.style("font-size", 12);
.style("fill", "rgb(80,80,80)");
d3.select(svgRef)
.selectAll(".axis--x path")
@@ -234,110 +233,62 @@ class HistogramBrush extends React.Component {
set1AvgExp,
set2AvgExp,
scatterplotXXaccessor,
scatterplotYYaccessor
scatterplotYYaccessor,
zebra
} = this.props;
return (
<div
style={{
marginTop: 10,
position: "flex"
}}
id={`histogram_${field}`}
style={{
padding: globals.leftSidebarSectionPadding,
backgroundColor: zebra ? globals.lightestGrey : "white"
}}
>
<div>
<span style={{ fontSize: globals.tiniestFontSize }}> {field} </span>
<div style={{ display: "flex", justifyContent: "flex-end" }}>
{isDiffExp ? (
<span>
<span
style={{ marginRight: 7 }}
className="bp3-icon-standard bp3-icon-scatter-plot"
/>
<ButtonGroup style={{ marginRight: 7 }}>
<Button
onClick={this.handleSetGeneAsScatterplotX(field).bind(this)}
active={scatterplotXXaccessor === field}
intent={scatterplotXXaccessor === field ? "primary" : "none"}
>
plot x
</Button>
<Button
onClick={this.handleSetGeneAsScatterplotY(field).bind(this)}
active={scatterplotYYaccessor === field}
intent={scatterplotYYaccessor === field ? "primary" : "none"}
>
plot y
</Button>
</ButtonGroup>
</span>
) : null}
{isUserDefined ? (
<span
<Button
minimal
onClick={this.removeHistogram.bind(this)}
style={{
fontSize: globals.tiniestFontSize,
color: globals.blue,
cursor: "pointer",
marginLeft: 7
}}
>
remove
</span>
) : null}
{isDiffExp ? (
<span>
<span
style={{
fontSize: globals.tiniestFontSize,
marginLeft: 7
}}
>
<strong>1:</strong>
{` ${set1AvgExp.toPrecision(2)}`}
</span>
<span
style={{
fontSize: globals.tiniestFontSize,
marginLeft: 7,
backgroundColor: globals.lighterGrey,
padding: 2
}}
>
<strong>2:</strong>
{` ${set2AvgExp.toPrecision(2)}`}
</span>
<span
style={{
fontSize: globals.tiniestFontSize,
marginLeft: 7
}}
>
{`Av. Diff: ${avgDiff.toFixed(2)}`}
</span>
<span
onClick={this.handleSetGeneAsScatterplotX(field).bind(this)}
style={{
fontSize: 16,
color:
scatterplotXXaccessor === field
? "white"
: globals.brightBlue,
cursor: "pointer",
position: "relative",
top: 1,
fontWeight: 700,
marginRight: 4,
borderRadius: 3,
padding: "2px 3px",
backgroundColor:
scatterplotXXaccessor === field
? globals.brightBlue
: "inherit"
}}
>
X
</span>
<span
onClick={this.handleSetGeneAsScatterplotY(field).bind(this)}
style={{
fontSize: 16,
color:
scatterplotYYaccessor === field
? "white"
: globals.brightBlue,
cursor: "pointer",
position: "relative",
top: 1,
fontWeight: 700,
marginRight: 4,
borderRadius: 3,
padding: "2px 3px",
backgroundColor:
scatterplotYYaccessor === field
? globals.brightBlue
: "inherit"
}}
>
Y
</span>
</span>
</Button>
) : null}
<Button
onClick={this.handleColorAction.bind(this)}
active={colorAccessor === field}
intent={colorAccessor === field ? "primary" : "none"}
icon="tint"
/>
</div>
<svg
width={this.width}
@@ -346,22 +297,46 @@ class HistogramBrush extends React.Component {
this.drawHistogram(svgRef);
}}
/>
<span
onClick={this.handleColorAction.bind(this)}
<div
style={{
fontSize: 14,
marginLeft: 4,
// padding: this.props.colorAccessor === this.props.field ? 3 : "auto",
borderRadius: 3,
color: colorAccessor === field ? globals.brightBlue : "black",
// backgroundColor: this.props.colorAccessor === this.props.field ? globals.brightBlue : "inherit",
position: "relative",
top: -29,
cursor: "pointer"
display: "flex",
justifyContent: "center"
}}
>
<FaPaintBrush />
</span>
<span style={{ fontStyle: "italic" }}>{field}</span>
</div>
{isDiffExp ? (
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "baseline"
}}
>
<span>
<strong>1:</strong>
{` ${set1AvgExp.toPrecision(2)}`}
</span>
<span
style={{
marginLeft: 7,
backgroundColor: globals.lighterGrey,
padding: 2
}}
>
<strong>2:</strong>
{` ${set2AvgExp.toPrecision(2)}`}
</span>
<span
style={{
marginLeft: 7
}}
>
{`Av. Diff: ${avgDiff.toFixed(2)}`}
</span>
</div>
) : null}
</div>
);
}
+48 -44
View File
@@ -1,8 +1,9 @@
import React from "react";
import _ from "lodash";
import { connect } from "react-redux";
import { FaChevronRight, FaChevronDown, FaPaintBrush } from "react-icons/fa";
import { FaChevronRight, FaChevronDown } from "react-icons/fa";
import memoize from "memoize-one";
import { Button } from "@blueprintjs/core";
import * as globals from "../../globals";
import Value from "./value";
@@ -119,55 +120,58 @@ class Category extends React.Component {
<div
style={{
display: "flex",
justifyContent: "flex-start",
justifyContent: "space-between",
alignItems: "baseline"
}}
>
<label className="bp3-control bp3-checkbox">
<input
onChange={this.handleToggleAllClick.bind(this)}
ref={el => {
this.checkbox = el;
return el;
<div
style={{
display: "flex",
justifyContent: "flex-start",
alignItems: "baseline"
}}
>
<label className="bp3-control bp3-checkbox">
<input
onChange={this.handleToggleAllClick.bind(this)}
ref={el => {
this.checkbox = el;
return el;
}}
checked={isChecked}
type="checkbox"
/>
<span className="bp3-control-indicator" />
{""}
</label>
<span
style={{
cursor: "pointer",
display: "inline-block"
}}
checked={isChecked}
type="checkbox"
/>
<span className="bp3-control-indicator" />
{""}
</label>
<span
style={{
cursor: "pointer",
display: "inline-block"
}}
onClick={() => {
this.setState({ isExpanded: !isExpanded });
}}
>
{metadataField}
{isExpanded ? (
<FaChevronDown style={{ fontSize: 10, marginLeft: 5 }} />
) : (
<FaChevronRight style={{ fontSize: 10, marginLeft: 5 }} />
)}
</span>
<span
onClick={() => {
this.setState({ isExpanded: !isExpanded });
}}
>
{metadataField}
{isExpanded ? (
<FaChevronDown style={{ fontSize: 10, marginLeft: 5 }} />
) : (
<FaChevronRight style={{ fontSize: 10, marginLeft: 5 }} />
)}
</span>
</div>
<Button
onClick={this.handleColorChange.bind(this)}
style={{
marginLeft: 4,
color:
colorAccessor === metadataField ? globals.brightBlue : "black",
display: "inline-block",
cursor: "pointer"
}}
>
<FaPaintBrush style={{ fontSize: 12, marginLeft: 10 }} />
</span>
active={colorAccessor === metadataField}
intent={colorAccessor === metadataField ? "primary" : "none"}
icon={"tint"}
/>
</div>
<div style={{ marginLeft: 26 }}>
{isExpanded ? this.renderCategoryItems() : null}
</div>
<div style={{ marginLeft: 26 }}>{isExpanded ? this.renderCategoryItems() : null}</div>
<div>
{isExpanded && isTruncated ? (
<p style={{ paddingLeft: 15 }}>... truncated list ...</p>
@@ -48,16 +48,16 @@ class Continuous extends React.Component {
this.continuousChecked = true; /* only do this once */
}
/* initial value for iterator to simulate index, ranges is an object */
let zebra = -1;
return (
<div
style={{
padding: globals.leftSidebarSectionPadding
}}
>
<div>
{this.hasContinuous ? (
<p
style={Object.assign({}, globals.leftSidebarSectionHeading, {
marginTop: 40
marginTop: 40,
paddingLeft: globals.leftSidebarSectionPadding
})}
>
Continuous metadata
@@ -65,12 +65,14 @@ class Continuous extends React.Component {
) : null}
{_.map(ranges, (value, key) => {
const isColorField = key.includes("color") || key.includes("Color");
zebra += 1;
if (value.range && key !== "name" && !isColorField) {
return (
<HistogramBrush
key={key}
field={key}
isObs
zebra={zebra % 2 === 0}
fieldValues={obsAnnotations}
ranges={value.range}
handleColorAction={this.handleColorAction(key).bind(this)}
@@ -6,7 +6,7 @@ import { interpolateViridis } from "d3-scale-chromatic";
// create continuous color legend
// http://bl.ocks.org/syntagmatic/e8ccca52559796be775553b467593a9f
const continuous = (selectorId, colorscale, colorAccessor) => {
const continuous = (selectorId, colorscale) => {
const legendheight = 200;
const legendwidth = 80;
const margin = { top: 10, right: 60, bottom: 10, left: 2 };
@@ -3,7 +3,6 @@ import React from "react";
import _ from "lodash";
import { Button } from "@blueprintjs/core";
import { connect } from "react-redux";
import * as globals from "../../globals";
@connect()
class CellSetButton extends React.Component {
@@ -37,6 +37,9 @@ class Expression extends React.Component {
type: "clear differential expression",
diffExp: differential.diffExp
});
dispatch({
type: "clear scatterplot"
});
}
render() {
@@ -47,7 +50,13 @@ class Expression extends React.Component {
const haveBothCellSets =
!!differential.celllist1 && !!differential.celllist2;
return (
<div style={{ marginRight: 10, marginBottom: 10 }}>
<div
style={{
marginRight: 10,
marginBottom: 10,
paddingLeft: globals.leftSidebarSectionPadding
}}
>
<CellSetButton {...this.props} eitherCellSetOneOrTwo={1} />
<CellSetButton {...this.props} eitherCellSetOneOrTwo={2} />
{!differential.diffExp ? (
@@ -55,6 +64,7 @@ class Expression extends React.Component {
style={{ marginTop: 10 }}
disabled={!haveBothCellSets}
intent="primary"
fill
type="button"
onClick={this.computeDiffExp.bind(this)}
>
@@ -64,6 +74,7 @@ class Expression extends React.Component {
{differential.diffExp ? (
<Button
type="button"
fill
style={{ marginTop: 10 }}
intent="warning"
onClick={this.clearDifferentialExpression.bind(this)}
+20 -13
View File
@@ -76,14 +76,22 @@ class GeneExpression extends React.Component {
<div>
<div
style={{
padding: globals.leftSidebarSectionPadding,
marginTop: 30
}}
>
<p style={globals.leftSidebarSectionHeading}>Custom genes</p>
<div style={{ marginTop: 11 }} className="bp3-control-group">
<div className="bp3-input-group">
<span className="bp3-icon bp3-icon-symbol-cross" />
<p
style={Object.assign({}, globals.leftSidebarSectionHeading, {
paddingLeft: globals.leftSidebarSectionPadding,
margin: 0
})}
>
Custom genes
</p>
<div
style={{ padding: globals.leftSidebarSectionPadding }}
className="bp3-control-group"
>
<div className="bp3-input-group bp3-fill">
<input
onKeyDown={this.keyPress.bind(this)}
onChange={e => {
@@ -105,7 +113,7 @@ class GeneExpression extends React.Component {
</button>
</div>
{world && userDefinedGenes.length > 0
? _.map(userDefinedGenes, geneName => {
? _.map(userDefinedGenes, (geneName, index) => {
const values = world.varDataCache[geneName];
if (!values) {
return null;
@@ -114,6 +122,7 @@ class GeneExpression extends React.Component {
<HistogramBrush
key={geneName}
field={geneName}
zebra={index % 2 === 0}
ranges={d3.extent(values)}
isUserDefined
/>
@@ -121,21 +130,18 @@ class GeneExpression extends React.Component {
})
: null}
</div>
<div
style={{
padding: globals.leftSidebarSectionPadding
}}
>
<div>
<p
style={Object.assign({}, globals.leftSidebarSectionHeading, {
marginTop: 40
marginTop: 40,
paddingLeft: globals.leftSidebarSectionPadding
})}
>
Differentially Expressed Genes
</p>
<ExpressionButtons />
{differential.diffExp
? _.map(differential.diffExp, value => {
? _.map(differential.diffExp, (value, index) => {
const annotations = world.varAnnotations[value[0]];
const { name } = annotations;
const values = world.varDataCache[name];
@@ -146,6 +152,7 @@ class GeneExpression extends React.Component {
<HistogramBrush
key={name}
field={name}
zebra={index % 2 === 0}
ranges={d3.extent(values)}
isDiffExp
avgDiff={value[1]}
+5 -4
View File
@@ -96,7 +96,8 @@ class Graph extends React.Component {
camera,
pointBuffer,
colorBuffer,
sizeBuffer
sizeBuffer,
svg
} = this.state;
if (reglRender && this.reglRenderState === "rendering" && mode !== "zoom") {
@@ -183,19 +184,19 @@ class Graph extends React.Component {
prevProps.responsive.height !== responsive.height ||
prevProps.responsive.width !== responsive.width ||
/* first time */
(responsive.height && responsive.width && !this.state.svg)
(responsive.height && responsive.width && !svg)
) {
/* clear out whatever was on the div, even if nothing, but usually the brushes etc */
d3.select("#graphAttachPoint")
.selectAll("svg")
.remove();
const { svg: newSvg, brush, brushContainer } = setupSVGandBrushElements(
const { svg: newSvg, brush } = setupSVGandBrushElements(
this.handleBrushSelectAction.bind(this),
this.handleBrushDeselectAction.bind(this),
responsive,
this.graphPaddingTop
);
this.setState({ svg: newSvg, brush, brushContainer });
this.setState({ svg: newSvg, brush });
}
}
-8
View File
@@ -15,15 +15,7 @@ import DynamicScatterplot from "./scatterplot/scatterplot";
scatterplotYYaccessor: state.controls.scatterplotYYaccessor
}))
class LeftSideBar extends React.Component {
constructor(props) {
super(props);
this.state = {
currentTab: "metadata"
};
}
render() {
const { currentTab } = this.state;
const {
responsive,
datasetTitle,
@@ -258,6 +258,7 @@ class Scatterplot extends React.Component {
.attr("x", 10)
.attr("y", 10)
.attr("class", "label")
.style("font-style", "italic")
.text(scatterplotYYaccessor);
svg
@@ -266,6 +267,7 @@ class Scatterplot extends React.Component {
.attr("y", height - 10)
.attr("text-anchor", "end")
.attr("class", "label")
.style("font-style", "italic")
.text(scatterplotXXaccessor);
}
@@ -285,7 +287,6 @@ class Scatterplot extends React.Component {
id="scatterplot_wrapper"
>
<ButtonGroup
minimal
style={{
position: "absolute",
right: 5,
@@ -294,14 +295,16 @@ class Scatterplot extends React.Component {
>
<Button
type="button"
icon={minimized ? "plus" : "minus"}
minimal
onClick={() => {
this.setState({ minimized: !minimized });
}}
/>
>
{minimized ? "show scatterplot" : "hide"}
</Button>
<Button
type="button"
icon="cross"
minimal
onClick={() => {
dispatch({
type: "clear scatterplot"
@@ -310,7 +313,9 @@ class Scatterplot extends React.Component {
type: "reset colorscale"
});
}}
/>
>
remove
</Button>
</ButtonGroup>
<div
className={styles.scatterplot}
+3
View File
@@ -3,6 +3,7 @@
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { FocusStyleManager } from "@blueprintjs/core";
import "./index.css";
@@ -10,6 +11,8 @@ import "./index.css";
import App from "./components/app";
import store from "./reducers";
FocusStyleManager.onlyShowFocusOnTabs();
ReactDOM.render(
<Provider store={store}>
<App />