mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-09-26 09:38:11 +08:00
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:
@@ -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 =
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,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;
|
||||
+14
-39
@@ -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>
|
||||
);
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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:
|
||||
{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,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;
|
||||
|
||||
Reference in New Issue
Block a user