Begin integrating blueprint (#359)

* toast test

* modify webpack build to allow for global CSS; use CSS modules to scope component styling

* remove heatmap

* add label to index resets

* swapping out buttons for blueprint

* adding constants to globals

* swapping out blueprint buttons

* title position, sidebar improvements

* sidebar styles

* adding toasts for validation errors

* zebra section trial

* integrating blueprint, switching buttons over, breaking out scatter

* clean up console
This commit is contained in:
Colin Megill
2018-10-22 15:46:37 -04:00
committed by GitHub
parent 0c271bc815
commit df671192be
34 changed files with 717 additions and 737 deletions
@@ -230,9 +230,9 @@ class HistogramBrush extends React.Component {
colorAccessor,
isUserDefined,
isDiffExp,
diffExp_avgDiff,
diffExp_set1AvgExp,
diffExp_set2AvgExp,
avgDiff,
set1AvgExp,
set2AvgExp,
scatterplotXXaccessor,
scatterplotYYaccessor
} = this.props;
@@ -269,7 +269,7 @@ class HistogramBrush extends React.Component {
}}
>
<strong>1:</strong>
{` ${diffExp_set1AvgExp.toPrecision(2)}`}
{` ${set1AvgExp.toPrecision(2)}`}
</span>
<span
style={{
@@ -280,7 +280,7 @@ class HistogramBrush extends React.Component {
}}
>
<strong>2:</strong>
{` ${diffExp_set2AvgExp.toPrecision(2)}`}
{` ${set2AvgExp.toPrecision(2)}`}
</span>
<span
style={{
@@ -288,7 +288,7 @@ class HistogramBrush extends React.Component {
marginLeft: 7
}}
>
{`Av. Diff: ${diffExp_avgDiff.toFixed(2)}`}
{`Av. Diff: ${avgDiff.toFixed(2)}`}
</span>
<span
onClick={this.handleSetGeneAsScatterplotX(field).bind(this)}
@@ -2,6 +2,7 @@
import React from "react";
import _ from "lodash";
import { connect } from "react-redux";
import * as globals from "../../globals";
import * as globals from "../../globals";
import Category from "./category";
@@ -38,13 +39,16 @@ class Categories extends React.Component {
return (
<div
style={{
width: 310,
marginRight: 40,
paddingRight: 20,
flexShrink: 0
padding: globals.leftSidebarSectionPadding
}}
>
<p> Categorical Metadata </p>
<p
style={Object.assign({}, globals.leftSidebarSectionHeading, {
marginTop: 4
})}
>
Categorical Metadata
</p>
{_.map(ranges, (value, key) => {
const isColorField = key.includes("color") || key.includes("Color");
const isSelectableCategory =
+36 -47
View File
@@ -119,34 +119,11 @@ class Category extends React.Component {
<div
style={{
display: "flex",
justifyContent: "space-between",
justifyContent: "flex-start",
alignItems: "baseline"
}}
>
<p
style={{
// flexShrink: 0,
fontWeight: 500,
// textAlign: "right",
// fontFamily: globals.accentFont,
// fontStyle: "italic",
margin: "3px 10px 3px 0px"
}}
>
<span
style={{
cursor: "pointer",
display: "inline-block",
position: "relative",
top: 2
}}
onClick={() => {
this.setState({ isExpanded: !isExpanded });
}}
>
{isExpanded ? <FaChevronDown /> : <FaChevronRight />}
</span>
{metadataField}
<label className="bp3-control bp3-checkbox">
<input
onChange={this.handleToggleAllClick.bind(this)}
ref={el => {
@@ -156,29 +133,41 @@ class Category extends React.Component {
checked={isChecked}
type="checkbox"
/>
<span
onClick={this.handleColorChange.bind(this)}
style={{
fontSize: 14,
marginLeft: 4,
// padding: this.props.colorAccessor === this.props.metadataField ? 3 : "auto",
borderRadius: 3,
color:
colorAccessor === metadataField
? globals.brightBlue
: "black",
// backgroundColor: this.props.colorAccessor === this.props.metadataField ? globals.brightBlue : "inherit",
display: "inline-block",
position: "relative",
top: 2,
cursor: "pointer"
}}
>
<FaPaintBrush />
</span>
</p>
<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.handleColorChange.bind(this)}
style={{
marginLeft: 4,
color:
colorAccessor === metadataField ? globals.brightBlue : "black",
display: "inline-block",
cursor: "pointer"
}}
>
<FaPaintBrush style={{ fontSize: 12, marginLeft: 10 }} />
</span>
</div>
<div>{isExpanded ? this.renderCategoryItems() : null}</div>
<div style={{ marginLeft: 26 }}>{isExpanded ? this.renderCategoryItems() : null}</div>
<div>
{isExpanded && isTruncated ? (
<p style={{ paddingLeft: 15 }}>... truncated list ...</p>
+27 -30
View File
@@ -49,42 +49,39 @@ class CategoryValue extends React.Component {
style={{
display: "flex",
alignItems: "baseline",
justifyContent: "space-between",
fontWeight: selected ? 700 : 400
justifyContent: "space-between"
}}
>
<p
<div
style={{
paddingLeft: 15,
width: 200,
flexShrink: 0,
margin: 0
// lineHeight: "1em"
margin: 0,
padding: 0,
userSelect: "none"
}}
>
<input
style={{ position: "relative", top: 1 }}
onChange={
selected ? this.toggleOff.bind(this) : this.toggleOn.bind(this)
}
checked={selected}
type="checkbox"
<label className="bp3-control bp3-checkbox">
<input
onChange={
selected ? this.toggleOff.bind(this) : this.toggleOn.bind(this)
}
checked={selected}
type="checkbox"
/>
<span className="bp3-control-indicator" />
{value}
</label>
</div>
<span>
<span>{count}</span>
<svg
style={{
marginLeft: 5,
width: 11,
height: 11,
backgroundColor: c ? colorScale(value) : "inherit"
}}
/>
{value}
</p>
<p
style={{
padding: "1px 10px",
width: 80,
textAlign: "center",
backgroundColor: c ? colorScale(value) : "inherit",
color: c ? "white" : "black",
margin: 0
// lineHeight: "1em"
}}
>
{count}
</p>
</span>
</div>
);
}
+15 -2
View File
@@ -4,6 +4,7 @@
import React from "react";
import _ from "lodash";
import { connect } from "react-redux";
import * as globals from "../../globals";
import HistogramBrush from "../brushableHistogram";
@@ -48,8 +49,20 @@ class Continuous extends React.Component {
}
return (
<div>
{this.hasContinuous ? <p> Continuous metadata </p> : null}
<div
style={{
padding: globals.leftSidebarSectionPadding
}}
>
{this.hasContinuous ? (
<p
style={Object.assign({}, globals.leftSidebarSectionHeading, {
marginTop: 40
})}
>
Continuous metadata
</p>
) : null}
{_.map(ranges, (value, key) => {
const isColorField = key.includes("color") || key.includes("Color");
if (value.range && key !== "name" && !isColorField) {
@@ -7,24 +7,24 @@ body {
}
*/
.parcoords {
:local(.parcoords) {
display: block;
}
.parcoords svg,
.parcoords canvas {
:local(.parcoords) svg,
:local(.parcoords) canvas {
font: 10px sans-serif;
position: absolute;
}
.parcoords canvas {
:local(.parcoords) canvas {
opacity: 0.9;
pointer-events: none;
}
.axis .title {
:local(.axis .title) {
font-size: 10px;
transform: rotate(-21deg) translate(-5px,-6px);
transform: rotate(-21deg) translate(-5px, -6px);
fill: #222;
cursor: pointer;
}
@@ -32,14 +32,14 @@ body {
/* -webkit-filter: grayscale(100%);
filter: grayscale(100%); */
.axis line,
.axis path {
:local(.axis) line,
:local(.axis) path {
fill: none;
stroke: #ccc;
stroke-width: 1px;
}
.axis .tick text {
:local(.axis .tick) text {
fill: #222;
pointer-events: none;
}
@@ -52,39 +52,39 @@ old, from reference bl.ocks
}
*/
.axis:hover line,
.axis:hover path,
.axis.active line,
.axis.active path {
:local(.axis:hover) line,
:local(.axis:hover) path,
:local(.axis.active) line,
:local(.axis.active) path {
fill: none;
stroke: #222;
stroke-width: 1px;
}
.axis:hover .title {
:local(.axis:hover .title) {
font-weight: bold;
}
.axis:hover .tick text {
:local(.axis:hover .tick) text {
opacity: 1;
}
.axis.active .title {
:local(.axis.active .title) {
font-weight: bold;
}
.axis.active .tick text {
:local(.axis.active .tick) text {
opacity: 1;
font-weight: bold;
}
.brush .extent {
fill-opacity: .3;
:local(.brush .extent) {
fill-opacity: 0.3;
stroke: #fff;
stroke-width: 1px;
}
.pre {
:local(.pre) {
width: 100%;
height: 300px;
margin: 6px 12px;
@@ -6,7 +6,7 @@ import { interpolateViridis } from "d3-scale-chromatic";
// create continuous color legend
// http://bl.ocks.org/syntagmatic/e8ccca52559796be775553b467593a9f
const continuous = (selectorId, colorscale) => {
const continuous = (selectorId, colorscale, colorAccessor) => {
const legendheight = 200;
const legendwidth = 80;
const margin = { top: 10, right: 60, bottom: 10, left: 2 };
@@ -84,6 +84,17 @@ const continuous = (selectorId, colorscale) => {
`translate(${legendwidth - margin.left - margin.right + 3},${margin.top})`
)
.call(legendaxis);
// text label for the y axis
// svg
// .append("text")
// .attr("transform", "rotate(-90)")
// .attr("y", 3)
// .attr("x", 0 - legendheight / 2)
// .attr("dy", "1em")
// .style("text-anchor", "middle")
// .style("fill", "white")
// .text(colorAccessor);
};
@connect(state => ({
@@ -115,7 +126,8 @@ class ContinuousLegend extends React.Component {
if (colorScale.range()[0][0] !== "#") {
continuous(
"#continuous_legend",
d3.scaleSequential(interpolateViridis).domain(colorScale.domain())
d3.scaleSequential(interpolateViridis).domain(colorScale.domain()),
colorAccessor
);
}
}
@@ -1,71 +0,0 @@
// jshint esversion: 6
import React from "react";
import _ from "lodash";
import { connect } from "react-redux";
import * as globals from "../../globals";
@connect()
class CellSetButton extends React.Component {
set() {
const {
differential,
crossfilter,
dispatch,
eitherCellSetOneOrTwo
} = this.props;
const set = _.map(crossfilter.allFiltered(), "name");
if (!differential.diffExp) {
/* diffexp needs to be cleared before we store a new set */
dispatch({
type: `store current cell selection as differential set ${eitherCellSetOneOrTwo}`,
data: set
});
}
}
render() {
const { differential, eitherCellSetOneOrTwo } = this.props;
const cellListName = `celllist${eitherCellSetOneOrTwo}`;
return (
<span style={{ marginRight: 10 }}>
<button
type="button"
style={{
color: "#FFF",
borderRadius: 2,
padding: "0px 10px",
height: 30,
backgroundColor: !differential.diffExp
? globals.brightBlue
: globals.lightGrey,
border: "none",
cursor: "pointer"
}}
onClick={this.set.bind(this)}
>
<span style={{ fontSize: 14, fontWeight: 700 }}>
{" "}
{eitherCellSetOneOrTwo}
{": "}
</span>
<span
style={{
fontSize: 14,
marginLeft: 8,
position: "relative",
top: 0
}}
>
{differential[cellListName]
? `${differential[cellListName].length} cells`
: "0 cells"}
</span>
</button>
</span>
);
}
}
export default CellSetButton;
@@ -1,83 +0,0 @@
// jshint esversion: 6
import React from "react";
import _ from "lodash";
import { connect } from "react-redux";
import * as d3 from "d3";
import { interpolateGreys } from "d3-scale-chromatic";
import HeatmapRow from "./heatmapRow";
/**********************************
***********************************
***********************************
DiffExp Heatmap
***********************************
***********************************
**********************************/
@connect(state => ({
differential: state.differential,
world: state.controls.world
}))
class Heatmap extends React.Component {
render() {
const { world, differential } = this.props;
if (!differential.diffExp) {
return <p>Select cells & compute differential to see heatmap</p>;
}
// summarize the information for display.
const topGenes = _.map(differential.diffExp, val => ({
varIndex: val[0],
geneName: world.varAnnotations[val[0]].name,
avgDiff: val[1],
set1AvgExp: val[4],
set2AvgExp: val[5]
}));
// average expression extent
const extent = d3.extent(
_.concat(
_.map(differential.diffExp, val => val[4]),
_.map(differential.diffExp, val => val[5])
)
);
const greyColorScale = d3
.scaleSequential()
.domain(extent)
.interpolator(interpolateGreys);
return (
<div>
<div
style={{
display: "flex",
justifyContent: "flex-start",
width: 400,
fontWeight: 700
}}
>
<p style={{ marginRight: 110 }}>Gene</p>
<p style={{ marginRight: 25 }}>1</p>
<p style={{ marginRight: 20 }}>2</p>
<p>ave diff</p>
</div>
{topGenes.map(g => {
const { geneName, avgDiff, set1AvgExp, set2AvgExp } = g;
return (
<HeatmapRow
key={geneName}
gene={geneName}
greyColorScale={greyColorScale}
aveDiff={avgDiff}
set1exp={set1AvgExp}
set2exp={set2AvgExp}
/>
);
})}
</div>
);
}
}
export default Heatmap;
@@ -1,154 +0,0 @@
import React from "react";
import { connect } from "react-redux";
import { FaPaintBrush } from "react-icons/fa";
import HeatmapSquare from "./heatmapSquare";
import * as globals from "../../globals";
import actions from "../../actions";
/**********************************
***********************************
***********************************
Row
***********************************
***********************************
**********************************/
@connect(state => ({
scatterplotXXaccessor: state.controls.scatterplotXXaccessor,
scatterplotYYaccessor: state.controls.scatterplotYYaccessor,
colorAccessor: state.controls.colorAccessor
}))
class HeatmapRow extends React.Component {
handleGeneColorScaleClick() {
return () => {
const { dispatch, gene } = this.props;
dispatch(actions.requestSingleGeneExpressionCountsForColoringPOST(gene));
};
}
handleSetGeneAsScatterplotX() {
return () => {
const { dispatch, gene } = this.props;
dispatch({
type: "set scatterplot x",
data: gene
});
};
}
handleSetGeneAsScatterplotY() {
return () => {
const { dispatch, gene } = this.props;
dispatch({
type: "set scatterplot y",
data: gene
});
};
}
render() {
const {
gene,
aveDiff,
set1exp,
set2exp,
greyColorScale,
scatterplotXXaccessor,
scatterplotYYaccessor,
colorAccessor
} = this.props;
return (
<div
style={{
width: 220,
display: "flex",
justifyContent: "flex-start",
alignItems: "baseline"
}}
>
<div style={{ width: 150, flexShrink: 0 }}>
<span
style={{
fontSize: 14
}}
>
{gene}
</span>
</div>
<HeatmapSquare
backgroundColor={greyColorScale(set1exp)}
text={set1exp.toPrecision(2)}
/>
<HeatmapSquare
backgroundColor={greyColorScale(set2exp)}
text={set2exp.toPrecision(2)}
/>
<span
title={aveDiff}
style={{
fontSize: 12,
marginLeft: 10,
marginRight: 10
}}
>
{aveDiff.toFixed(2)}
</span>
<span
onClick={this.handleSetGeneAsScatterplotX(gene).bind(this)}
style={{
fontSize: 16,
color:
scatterplotXXaccessor === gene ? "white" : globals.brightBlue,
cursor: "pointer",
position: "relative",
top: 1,
fontWeight: 700,
marginRight: 4,
borderRadius: 3,
padding: "2px 3px",
backgroundColor:
scatterplotXXaccessor === gene ? globals.brightBlue : "inherit"
}}
>
X
</span>
<span
onClick={this.handleSetGeneAsScatterplotY(gene).bind(this)}
style={{
fontSize: 16,
color:
scatterplotYYaccessor === gene ? "white" : globals.brightBlue,
cursor: "pointer",
position: "relative",
top: 1,
fontWeight: 700,
marginRight: 4,
borderRadius: 3,
padding: "2px 3px",
backgroundColor:
scatterplotYYaccessor === gene ? globals.brightBlue : "inherit"
}}
>
Y
</span>
<span
onClick={this.handleGeneColorScaleClick(gene).bind(this)}
style={{
fontSize: 16,
cursor: "pointer",
position: "relative",
marginRight: 6,
borderRadius: 3,
padding: "0px 2px 2px 2px",
color: colorAccessor === gene ? "white" : "inherit",
backgroundColor:
colorAccessor === gene ? globals.brightBlue : "inherit"
}}
>
<FaPaintBrush style={{ display: "inline-block" }} />
</span>
</div>
);
}
}
export default HeatmapRow;
@@ -1,30 +0,0 @@
import React from "react";
import getContrast from "font-color-contrast"; // https://www.npmjs.com/package/font-color-contrast
const HeatmapSquare = props => {
const { backgroundColor, text } = props;
const contrastColor = getContrast(
backgroundColor
.substring(4, backgroundColor.length - 1)
.replace(/ /g, "")
.split(",")
);
return (
<p
style={{
padding: "12px 6px",
textAlign: "center",
color: contrastColor,
width: 40,
flexShrink: 0,
fontSize: 12,
margin: 0,
backgroundColor
}}
>
{text}
</p>
);
};
export default HeatmapSquare;
+1 -1
View File
@@ -1,4 +1,4 @@
.btn {
:local(.btn) {
background: none;
cursor: pointer;
color: "#FFF";
@@ -1,3 +1,3 @@
.container {
:local(.container) {
min-height: 100%;
}
@@ -0,0 +1,12 @@
import { Position, Toaster, Intent } from "@blueprintjs/core";
/** Singleton toaster instance. Create separate instances for different options. */
export const ErrorToastTopCenter = Toaster.create({
className: "recipe-toaster",
position: Position.TOP,
intent: Intent.WARNING
});
export default {
ErrorToastTopCenter
};
@@ -0,0 +1,46 @@
// jshint esversion: 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 {
set() {
const {
differential,
crossfilter,
dispatch,
eitherCellSetOneOrTwo
} = this.props;
const set = _.map(crossfilter.allFiltered(), "name");
if (!differential.diffExp) {
/* diffexp needs to be cleared before we store a new set */
dispatch({
type: `store current cell selection as differential set ${eitherCellSetOneOrTwo}`,
data: set
});
}
}
render() {
const { differential, eitherCellSetOneOrTwo } = this.props;
const cellListName = `celllist${eitherCellSetOneOrTwo}`;
return (
<span style={{ marginRight: 10 }}>
<Button type="button" onClick={this.set.bind(this)}>
{eitherCellSetOneOrTwo}
{": "}
{differential[cellListName]
? `${differential[cellListName].length} cells`
: "0 cells"}
</Button>
</span>
);
}
}
export default CellSetButton;
@@ -1,6 +1,7 @@
// jshint esversion: 6
import React from "react";
import _ from "lodash";
import { Button } from "@blueprintjs/core";
import { connect } from "react-redux";
import * as globals from "../../globals";
import actions from "../../actions";
@@ -43,58 +44,32 @@ class Expression extends React.Component {
if (!differential) {
return null;
}
const haveBothCellSets =
!!differential.celllist1 && !!differential.celllist2;
return (
<div style={{ marginRight: 10, marginBottom: 10 }}>
<CellSetButton {...this.props} eitherCellSetOneOrTwo={1} />
<CellSetButton {...this.props} eitherCellSetOneOrTwo={2} />
{!differential.diffExp ? (
<button
<Button
style={{ marginTop: 10 }}
disabled={!haveBothCellSets}
intent="primary"
type="button"
style={{
fontSize: 14,
fontWeight: 400,
color: "#FFF",
padding: "0px 10px",
height: 30,
borderRadius: 2,
backgroundColor:
differential.celllist1 && differential.celllist2
? globals.brightBlue
: globals.lightGrey,
border: "none",
cursor:
differential.celllist1 && differential.celllist2
? "pointer"
: "auto"
}}
onClick={this.computeDiffExp.bind(this)}
>
{" "}
Compute Differential Expression{" "}
</button>
Compute Differential Expression
</Button>
) : null}
{differential.diffExp ? (
<button
<Button
type="button"
style={{
fontSize: 14,
fontWeight: 400,
color: "#FFF",
padding: "0px 10px",
height: 30,
borderRadius: 2,
backgroundColor: globals.brightBlue,
border: "none",
cursor:
differential.celllist1 && differential.celllist2
? "pointer"
: "auto"
}}
style={{ marginTop: 10 }}
intent="warning"
onClick={this.clearDifferentialExpression.bind(this)}
>
{" "}
Clear Differential Expression{" "}
</button>
Clear Differential Expression
</Button>
) : null}
</div>
);
+91 -76
View File
@@ -5,10 +5,11 @@ import React from "react";
import _ from "lodash";
import * as d3 from "d3";
import { connect } from "react-redux";
import { FaPlusCircle } from "react-icons/fa";
import HistogramBrush from "../brushableHistogram";
import * as globals from "../../globals";
import actions from "../../actions";
import { ErrorToastTopCenter } from "../framework/toasters";
import ExpressionButtons from "./expressionButtons";
@connect(state => {
const metadata = _.get(state.controls.world, "obsAnnotations", null);
@@ -45,13 +46,18 @@ class GeneExpression extends React.Component {
const { gene } = this.state;
if (userDefinedGenes.indexOf(gene) !== -1) {
console.log("That gene already exists");
ErrorToastTopCenter.show({
message: "That gene already exists"
});
} else if (userDefinedGenes.length > 15) {
console.log(
"That's too many genes, you can have at most 15 user defined genes"
);
ErrorToastTopCenter.show({
message:
"That's too many genes, you can have at most 15 user defined genes"
});
} else if (!_.find(world.varAnnotations, { name: gene })) {
console.log("That doesn't appear to be a valid gene name.");
ErrorToastTopCenter.show({
message: "That doesn't appear to be a valid gene name."
});
} else {
dispatch(actions.requestUserDefinedGene(gene));
dispatch({
@@ -68,79 +74,88 @@ class GeneExpression extends React.Component {
return (
<div>
<input
onKeyDown={this.keyPress.bind(this)}
onChange={e => {
this.setState({ gene: e.target.value });
}}
type="text"
value={gene}
/>
<button
type="button"
<div
style={{
border: "none",
background: "none",
cursor: "pointer",
outline: "none",
marginTop: 20,
padding: 0
padding: globals.leftSidebarSectionPadding,
marginTop: 30
}}
onClick={this.handleClick.bind(this)}
>
<FaPlusCircle
style={{
display: "inline-block",
marginLeft: 7,
position: "relative",
top: 5,
color: globals.brightBlue,
fontSize: 22
}}
/>{" "}
add gene{" "}
</button>
{world && userDefinedGenes.length > 0 ? (
<p>User defined genes</p>
) : null}
{world && userDefinedGenes.length > 0
? _.map(userDefinedGenes, geneName => {
const values = world.varDataCache[geneName];
if (!values) {
return null;
}
return (
<HistogramBrush
key={geneName}
field={geneName}
ranges={d3.extent(values)}
isUserDefined
/>
);
})
: null}
{differential.diffExp ? <p> Differentially Expressed Genes </p> : null}
{differential.diffExp
? _.map(differential.diffExp, value => {
const annotations = world.varAnnotations[value[0]];
const { name } = annotations;
const values = world.varDataCache[name];
if (!values) {
return null;
}
return (
<HistogramBrush
key={name}
field={name}
ranges={d3.extent(values)}
isDiffExp
diffExp_avgDiff={value[1]}
diffExp_set1AvgExp={value[4]}
diffExp_set2AvgExp={value[5]}
/>
);
})
: null}
<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" />
<input
onKeyDown={this.keyPress.bind(this)}
onChange={e => {
this.setState({ gene: e.target.value });
}}
value={gene}
type="text"
className="bp3-input"
placeholder="Add a custom gene"
style={{ paddingRight: 94 }}
/>
</div>
<button
type="button"
className="bp3-button bp3-intent-primary"
onClick={this.handleClick.bind(this)}
>
Add
</button>
</div>
{world && userDefinedGenes.length > 0
? _.map(userDefinedGenes, geneName => {
const values = world.varDataCache[geneName];
if (!values) {
return null;
}
return (
<HistogramBrush
key={geneName}
field={geneName}
ranges={d3.extent(values)}
isUserDefined
/>
);
})
: null}
</div>
<div
style={{
padding: globals.leftSidebarSectionPadding
}}
>
<p
style={Object.assign({}, globals.leftSidebarSectionHeading, {
marginTop: 40
})}
>
Differentially Expressed Genes
</p>
<ExpressionButtons />
{differential.diffExp
? _.map(differential.diffExp, value => {
const annotations = world.varAnnotations[value[0]];
const { name } = annotations;
const values = world.varDataCache[name];
if (!values) {
return null;
}
return (
<HistogramBrush
key={name}
field={name}
ranges={d3.extent(values)}
isDiffExp
avgDiff={value[1]}
set1AvgExp={value[4]}
set2AvgExp={value[5]}
/>
);
})
: null}
</div>
</div>
);
}
+4 -4
View File
@@ -1,4 +1,4 @@
.graphCanvas path {
:local(.graphCanvas) path {
shape-rendering: crispEdges;
}
@@ -16,8 +16,8 @@
pointer-events: none;
}
.axis path,
.axis line {
:local(.axis) path,
:local(.axis) line {
fill: none;
stroke: #000;
stroke-width: 1px;
@@ -29,6 +29,6 @@ circle {
fill: none;
}
.hidden {
:local(.hidden) {
display: none;
}
+18 -63
View File
@@ -5,10 +5,8 @@ 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 { FaCrosshairs, FaSearchPlus } from "react-icons/fa";
import * as globals from "../../globals";
import setupSVGandBrushElements from "./setupSVGandBrush";
import actions from "../../actions";
import _camera from "../../util/camera";
@@ -340,91 +338,48 @@ class Graph extends React.Component {
alignItems: "baseline"
}}
>
<button
<Button
type="button"
style={{ marginRight: 10 }}
onClick={() => {
dispatch(actions.resetGraph());
}}
style={{
fontSize: 14,
fontWeight: 400,
color: "white",
padding: "0px 10px",
height: 30,
marginRight: 10,
borderRadius: 2,
backgroundColor: globals.brightBlue,
border: "none",
cursor: "pointer"
}}
>
reset graph
</button>
<button
</Button>
<Button
type="button"
style={{ marginRight: 10 }}
onClick={() => {
dispatch(actions.regraph());
}}
style={{
fontSize: 14,
fontWeight: 400,
color: "white",
padding: "0px 10px",
height: 30,
marginRight: 10,
borderRadius: 2,
backgroundColor: globals.brightBlue,
border: "none",
cursor: "pointer"
}}
>
regraph selection
</button>
</Button>
<div>
<span>
<button
<div className="bp3-button-group">
<Button
className="bp3-button bp3-icon-locate"
type="button"
active={mode === "brush"}
onClick={() => {
this.setState({ mode: "brush" });
}}
style={{
cursor: "pointer",
border:
mode === "brush" ? "1px solid black" : "1px solid white",
backgroundColor: "white",
padding: "5px 5px 2px 5px",
position: "relative",
top: 2,
marginRight: 10,
borderRadius: 3
}}
>
{" "}
<FaCrosshairs />{" "}
</button>
<button
/>
<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",
border:
mode === "zoom" ? "1px solid black" : "1px solid white",
backgroundColor: "white",
padding: "5px 5px 2px 5px",
position: "relative",
top: 2,
marginRight: 10,
borderRadius: 3
cursor: "pointer"
}}
>
{" "}
<FaSearchPlus />{" "}
</button>
</span>
/>
</div>
</div>
</div>
</div>
+20 -27
View File
@@ -5,8 +5,6 @@ import { connect } from "react-redux";
import Categorical from "./categorical/categorical";
import Continuous from "./continuous/continuous";
import GeneExpression from "./geneExpression";
import ExpressionButtons from "./expression/expressionButtons";
import Heatmap from "./expression/diffExpHeatmap";
import * as globals from "../globals";
import DynamicScatterplot from "./scatterplot/scatterplot";
@@ -37,30 +35,36 @@ class LeftSideBar extends React.Component {
this magic number should be made less fragile,
if cellxgene logo or tabs change, this must as well
*/
const metadataSectionPadding =
scatterplotXXaccessor && scatterplotYYaccessor ? 450 : 0;
const metadataSectionPadding = 0;
// scatterplotXXaccessor && scatterplotYYaccessor ? 450 : 0;
return (
<div style={{ position: "fixed" }}>
<div
style={{
position: "fixed",
backgroundColor: "white",
/* x y blur spread color */
boxShadow: "1px 0px 6px 2px rgba(153,153,153,0.4)"
}}
>
<p
style={{
position: "fixed",
left: responsive.width / 2,
top: 14,
top: globals.cellxgeneTitleTopPadding,
left: globals.leftSidebarWidth + globals.cellxgeneTitleLeftPadding,
margin: 0,
fontSize: 24,
fontSize: globals.largestFontSize,
color: globals.darkerGrey,
fontWeight: 700,
width: "100%"
}}
>
cellxgene: &nbsp;
{datasetTitle}
cellxgene: {datasetTitle}
</p>
<div
style={{
height: responsive.height - metadataSectionPadding,
width: 400,
padding: "0px 10px 10px 10px",
width: globals.leftSidebarWidth,
overflowY: "auto",
overflowX: "hidden"
}}
@@ -69,20 +73,9 @@ class LeftSideBar extends React.Component {
<GeneExpression />
<Continuous />
</div>
<div
style={{
position: "fixed",
bottom: 0,
left: 0
}}
>
{scatterplotXXaccessor && scatterplotYYaccessor ? (
<DynamicScatterplot />
) : null}
</div>
<div style={{ position: "fixed", bottom: 0, right: 0 }}>
{currentTab === "metadata" ? <ExpressionButtons /> : null}
</div>
{scatterplotXXaccessor && scatterplotYYaccessor ? (
<DynamicScatterplot />
) : null}
</div>
);
}
@@ -1,15 +1,14 @@
.scatterplot {
:local(.scatterplot) {
display: block;
}
.scatterplot svg,
.scatterplot canvas {
:local(.scatterplot) svg,
:local(.scatterplot) canvas {
font: 10px sans-serif;
position: absolute;
}
.scatterplot canvas {
:local(.scatterplot) canvas {
opacity: 0.9;
pointer-events: none;
}
@@ -5,8 +5,10 @@
import React from "react";
import _ from "lodash";
import { connect } from "react-redux";
import { Button, ButtonGroup } from "@blueprintjs/core";
import _regl from "regl";
import * as d3 from "d3";
import * as globals from "../../globals";
import _camera from "../../util/camera";
@@ -64,6 +66,7 @@ class Scatterplot extends React.Component {
this.axes = false;
this.state = {
svg: null,
minimized: null,
xScale: null,
yScale: null
};
@@ -267,14 +270,48 @@ class Scatterplot extends React.Component {
}
render() {
const { dispatch } = this.props;
const { minimized } = this.state;
return (
<div
style={{
backgroundColor: "white",
paddingBottom: 20
position: "fixed",
bottom: minimized ? -height + -margin.top : 0,
left: globals.leftSidebarWidth + globals.scatterplotPaddingLeft,
paddingBottom: 20,
backgroundColor: "white"
}}
id="scatterplot_wrapper"
>
<ButtonGroup
minimal
style={{
position: "absolute",
right: 5,
top: 5
}}
>
<Button
type="button"
icon={minimized ? "plus" : "minus"}
onClick={() => {
this.setState({ minimized: !minimized });
}}
/>
<Button
type="button"
icon="cross"
onClick={() => {
dispatch({
type: "clear scatterplot"
});
dispatch({
type: "reset colorscale"
});
}}
/>
</ButtonGroup>
<div
className={styles.scatterplot}
id="scatterplot"
+1 -1
View File
@@ -1,5 +1,5 @@
// jshint esversion: 6
export const margin = { top: 66, right: 110, bottom: 20, left: 60 };
export const margin = { top: 40, right: 5, bottom: 20, left: 60 };
export const width = 340;
export const height = 340 - margin.top - margin.bottom;
export const innerHeight = height - 2;