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