mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-01 18:58:11 +08:00
more lint - components (#336)
* more lint * fix typo introduced in delinting * more lint * remove dead code * lint fixes * lint
This commit is contained in:
@@ -7,10 +7,10 @@ https://bl.ocks.org/SpaceActuary/2f004899ea1b2bd78d6f1dbb2febf771
|
||||
import React from "react";
|
||||
import _ from "lodash";
|
||||
import { connect } from "react-redux";
|
||||
import { kvCache } from "../../util/stateManager/";
|
||||
import { FaPaintBrush } from "react-icons/fa";
|
||||
import * as d3 from "d3";
|
||||
import memoize from "memoize-one";
|
||||
import { kvCache } from "../../util/stateManager";
|
||||
import * as globals from "../../globals";
|
||||
import actions from "../../actions";
|
||||
|
||||
@@ -33,7 +33,6 @@ class HistogramBrush extends React.Component {
|
||||
histogramCache.y = d3
|
||||
.scaleLinear()
|
||||
.range([this.height - this.marginBottom, 0]);
|
||||
// .range([height - margin.bottom, margin.top]);
|
||||
|
||||
if (obsAnnotations[0][field]) {
|
||||
// recalculate expensive stuff
|
||||
@@ -116,6 +115,7 @@ class HistogramBrush extends React.Component {
|
||||
|
||||
drawHistogram(svgRef) {
|
||||
const { obsAnnotations, field, ranges } = this.props;
|
||||
const { brush, axis } = this.state;
|
||||
const histogramCache = this.calcHistogramCache(
|
||||
obsAnnotations,
|
||||
field,
|
||||
@@ -140,8 +140,8 @@ class HistogramBrush extends React.Component {
|
||||
.attr("width", d => Math.abs(x(d.x1) - x(d.x0) - 1))
|
||||
.attr("height", d => y(0) - y(d.length / numValues));
|
||||
|
||||
if (!this.state.brush && !this.state.axis) {
|
||||
const brush = d3
|
||||
if (!brush && !axis) {
|
||||
const newBrush = d3
|
||||
.select(svgRef)
|
||||
.append("g")
|
||||
.attr("class", "brush")
|
||||
@@ -153,12 +153,6 @@ class HistogramBrush extends React.Component {
|
||||
.attr("class", "axis axis--x")
|
||||
.attr("transform", `translate(0,${this.height - this.marginBottom})`)
|
||||
.call(d3.axisBottom(x).ticks(5));
|
||||
// .append("text")
|
||||
// .attr("x", this.width - 2)
|
||||
// .attr("y", -6)
|
||||
// .attr("fill", "#000")
|
||||
// // .attr("text-anchor", "end")
|
||||
// // .text(field);
|
||||
|
||||
d3.select(svgRef)
|
||||
.selectAll(".axis--x text")
|
||||
@@ -173,14 +167,13 @@ class HistogramBrush extends React.Component {
|
||||
.selectAll(".axis--x line")
|
||||
.style("stroke", "rgb(230,230,230)");
|
||||
|
||||
this.setState({ brush, xAxis });
|
||||
this.setState({ brush: newBrush, xAxis }); // eslint-disable-line react/no-unused-state
|
||||
}
|
||||
}
|
||||
|
||||
handleColorAction() {
|
||||
const {
|
||||
obsAnnotations,
|
||||
ranges,
|
||||
dispatch,
|
||||
field,
|
||||
world,
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import * as globals from "../../globals";
|
||||
import _ from "lodash";
|
||||
import * as constants from "./constants";
|
||||
|
||||
export const calcHistogram = (ranges, allValuesForContinuousFieldAsArray) => {
|
||||
const histogramData = {};
|
||||
|
||||
histogramData.x = d3
|
||||
.scaleLinear()
|
||||
.domain([ranges.min, ranges.max])
|
||||
.range([0, constants.width]);
|
||||
|
||||
histogramData.y = d3
|
||||
.scaleLinear()
|
||||
.range([constants.height - constants.marginBottom, 0]);
|
||||
// .range([height - margin.bottom, margin.top]);
|
||||
|
||||
histogramData.bins = d3
|
||||
.histogram()
|
||||
.domain(histogramData.x.domain())
|
||||
.thresholds(40)(allValuesForContinuousFieldAsArray);
|
||||
|
||||
histogramData.numValues = allValuesForContinuousFieldAsArray.length;
|
||||
|
||||
return histogramData;
|
||||
};
|
||||
@@ -3,169 +3,7 @@ import React from "react";
|
||||
import _ from "lodash";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { FaChevronRight, FaChevronDown, FaPaintBrush } from "react-icons/fa";
|
||||
|
||||
import * as globals from "../../globals";
|
||||
import Value from "./value";
|
||||
import { alphabeticallySortedValues } from "./util";
|
||||
|
||||
@connect(state => ({
|
||||
colorAccessor: state.controls.colorAccessor,
|
||||
categoricalAsBooleansMap: state.controls.categoricalAsBooleansMap
|
||||
}))
|
||||
class Category extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isChecked: true,
|
||||
isExpanded: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { categoricalAsBooleansMap, metadataField } = this.props;
|
||||
|
||||
const valuesAsBool = _.values(categoricalAsBooleansMap[metadataField]);
|
||||
/* count categories toggled on by counting true values */
|
||||
const categoriesToggledOn = _.values(valuesAsBool).filter(v => v).length;
|
||||
|
||||
if (categoriesToggledOn === valuesAsBool.length) {
|
||||
/* everything is on, so not indeterminate */
|
||||
this.checkbox.indeterminate = false;
|
||||
} else if (categoriesToggledOn === 0) {
|
||||
/* nothing is on, so no */
|
||||
this.checkbox.indeterminate = false;
|
||||
} else if (categoriesToggledOn < valuesAsBool.length) {
|
||||
/* to be explicit... */
|
||||
this.checkbox.indeterminate = true;
|
||||
}
|
||||
}
|
||||
|
||||
handleColorChange() {
|
||||
const { dispatch, metadataField } = this.props;
|
||||
dispatch({
|
||||
type: "color by categorical metadata",
|
||||
colorAccessor: metadataField
|
||||
});
|
||||
}
|
||||
|
||||
toggleAll() {
|
||||
const { dispatch, metadataField } = this.props;
|
||||
dispatch({
|
||||
type: "categorical metadata filter all of these",
|
||||
metadataField
|
||||
});
|
||||
this.setState({ isChecked: true });
|
||||
}
|
||||
|
||||
toggleNone() {
|
||||
const { dispatch, metadataField, value } = this.props;
|
||||
dispatch({
|
||||
type: "categorical metadata filter none of these",
|
||||
metadataField,
|
||||
value
|
||||
});
|
||||
this.setState({ isChecked: false });
|
||||
}
|
||||
|
||||
handleToggleAllClick() {
|
||||
const { isChecked } = this.state;
|
||||
// || this.checkbox.indeterminate === false
|
||||
if (isChecked) {
|
||||
this.toggleNone();
|
||||
} else if (!isChecked) {
|
||||
this.toggleAll();
|
||||
}
|
||||
}
|
||||
|
||||
renderCategoryItems() {
|
||||
const { values, metadataField } = this.props;
|
||||
return _.map(alphabeticallySortedValues(values), (v, i) => (
|
||||
<Value
|
||||
key={v}
|
||||
metadataField={metadataField}
|
||||
count={values[v]}
|
||||
value={v}
|
||||
i={i}
|
||||
/>
|
||||
));
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isExpanded, isChecked } = this.state;
|
||||
const { metadataField, colorAccessor } = this.props;
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
// display: "flex",
|
||||
// alignItems: "baseline",
|
||||
maxWidth: globals.maxControlsWidth
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
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}
|
||||
<input
|
||||
onChange={this.handleToggleAllClick.bind(this)}
|
||||
ref={el => (this.checkbox = el)}
|
||||
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>
|
||||
</div>
|
||||
<div>{isExpanded ? this.renderCategoryItems() : null}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
import Category from "./category";
|
||||
|
||||
@connect(state => {
|
||||
const ranges = _.get(state.controls.world, "summary.obs", null);
|
||||
@@ -202,6 +40,7 @@ class Categories extends React.Component {
|
||||
<Category key={key} metadataField={key} values={value.options} />
|
||||
);
|
||||
}
|
||||
return undefined;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
import React from "react";
|
||||
import _ from "lodash";
|
||||
import { connect } from "react-redux";
|
||||
import { FaChevronRight, FaChevronDown, FaPaintBrush } from "react-icons/fa";
|
||||
|
||||
import * as globals from "../../globals";
|
||||
import Value from "./value";
|
||||
import alphabeticallySortedValues from "./util";
|
||||
|
||||
@connect(state => ({
|
||||
colorAccessor: state.controls.colorAccessor,
|
||||
categoricalAsBooleansMap: state.controls.categoricalAsBooleansMap
|
||||
}))
|
||||
class Category extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isChecked: true,
|
||||
isExpanded: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { categoricalAsBooleansMap, metadataField } = this.props;
|
||||
|
||||
const valuesAsBool = _.values(categoricalAsBooleansMap[metadataField]);
|
||||
/* count categories toggled on by counting true values */
|
||||
const categoriesToggledOn = _.values(valuesAsBool).filter(v => v).length;
|
||||
|
||||
if (categoriesToggledOn === valuesAsBool.length) {
|
||||
/* everything is on, so not indeterminate */
|
||||
this.checkbox.indeterminate = false;
|
||||
} else if (categoriesToggledOn === 0) {
|
||||
/* nothing is on, so no */
|
||||
this.checkbox.indeterminate = false;
|
||||
} else if (categoriesToggledOn < valuesAsBool.length) {
|
||||
/* to be explicit... */
|
||||
this.checkbox.indeterminate = true;
|
||||
}
|
||||
}
|
||||
|
||||
handleColorChange() {
|
||||
const { dispatch, metadataField } = this.props;
|
||||
dispatch({
|
||||
type: "color by categorical metadata",
|
||||
colorAccessor: metadataField
|
||||
});
|
||||
}
|
||||
|
||||
toggleAll() {
|
||||
const { dispatch, metadataField } = this.props;
|
||||
dispatch({
|
||||
type: "categorical metadata filter all of these",
|
||||
metadataField
|
||||
});
|
||||
this.setState({ isChecked: true });
|
||||
}
|
||||
|
||||
toggleNone() {
|
||||
const { dispatch, metadataField, value } = this.props;
|
||||
dispatch({
|
||||
type: "categorical metadata filter none of these",
|
||||
metadataField,
|
||||
value
|
||||
});
|
||||
this.setState({ isChecked: false });
|
||||
}
|
||||
|
||||
handleToggleAllClick() {
|
||||
const { isChecked } = this.state;
|
||||
// || this.checkbox.indeterminate === false
|
||||
if (isChecked) {
|
||||
this.toggleNone();
|
||||
} else if (!isChecked) {
|
||||
this.toggleAll();
|
||||
}
|
||||
}
|
||||
|
||||
renderCategoryItems() {
|
||||
const { values, metadataField } = this.props;
|
||||
return _.map(alphabeticallySortedValues(values), (v, i) => (
|
||||
<Value
|
||||
key={v}
|
||||
metadataField={metadataField}
|
||||
count={values[v]}
|
||||
value={v}
|
||||
i={i}
|
||||
/>
|
||||
));
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isExpanded, isChecked } = this.state;
|
||||
const { metadataField, colorAccessor } = this.props;
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
// display: "flex",
|
||||
// alignItems: "baseline",
|
||||
maxWidth: globals.maxControlsWidth
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
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}
|
||||
<input
|
||||
onChange={this.handleToggleAllClick.bind(this)}
|
||||
ref={el => {
|
||||
this.checkbox = el;
|
||||
return el;
|
||||
}}
|
||||
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>
|
||||
</div>
|
||||
<div>{isExpanded ? this.renderCategoryItems() : null}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Category;
|
||||
@@ -1,5 +1,5 @@
|
||||
// jshint esversion: 6
|
||||
export const alphabeticallySortedValues = values =>
|
||||
export default values =>
|
||||
Object.keys(values).sort((a, b) => {
|
||||
const textA = a.toUpperCase();
|
||||
const textB = b.toUpperCase();
|
||||
|
||||
@@ -5,18 +5,16 @@ import React from "react";
|
||||
import _ from "lodash";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import HistogramBrush from "../brushableHistogram/";
|
||||
import HistogramBrush from "../brushableHistogram";
|
||||
|
||||
@connect(state => {
|
||||
return {
|
||||
ranges: _.get(state.controls.world, "summary.obs", null),
|
||||
metadata: _.get(state.controls.world, "obsAnnotations", null),
|
||||
colorAccessor: state.controls.colorAccessor,
|
||||
colorScale: state.controls.colorScale,
|
||||
selectionUpdate: _.get(state.controls, "crossfilter.updateTime", null),
|
||||
schema: _.get(state.controls.world, "schema", null)
|
||||
};
|
||||
})
|
||||
@connect(state => ({
|
||||
ranges: _.get(state.controls.world, "summary.obs", null),
|
||||
metadata: _.get(state.controls.world, "obsAnnotations", null),
|
||||
colorAccessor: state.controls.colorAccessor,
|
||||
colorScale: state.controls.colorScale,
|
||||
selectionUpdate: _.get(state.controls, "crossfilter.updateTime", null),
|
||||
schema: _.get(state.controls.world, "schema", null)
|
||||
}))
|
||||
class Continuous extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -26,6 +24,8 @@ class Continuous extends React.Component {
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
componentDidUpdate() {}
|
||||
|
||||
handleColorAction(key) {
|
||||
return () => {
|
||||
const { dispatch, ranges } = this.props;
|
||||
@@ -40,9 +40,10 @@ class Continuous extends React.Component {
|
||||
render() {
|
||||
const { ranges, obsAnnotations, schema } = this.props;
|
||||
if (schema && !this.continuousChecked) {
|
||||
this.hasContinuous = _.some(schema.annotations.obs, d => {
|
||||
return d.type === "int32" || d.type === "float32";
|
||||
});
|
||||
this.hasContinuous = _.some(
|
||||
schema.annotations.obs,
|
||||
d => d.type === "int32" || d.type === "float32"
|
||||
);
|
||||
this.continuousChecked = true; /* only do this once */
|
||||
}
|
||||
|
||||
|
||||
@@ -39,10 +39,8 @@ export const brushstart = () => {
|
||||
d3.event.sourceEvent.stopPropagation();
|
||||
};
|
||||
|
||||
export const d3_functor = v => {
|
||||
return typeof v === "function" ? v : () => v;
|
||||
};
|
||||
|
||||
// Unused.
|
||||
// export const d3_functor = v => (typeof v === "function" ? v : () => v);
|
||||
export const project = (d, dimensions, xscale) =>
|
||||
dimensions.map((p, i) => {
|
||||
// check if data element has property and contains a value
|
||||
|
||||
@@ -3,32 +3,35 @@ import React from "react";
|
||||
import _ from "lodash";
|
||||
import { connect } from "react-redux";
|
||||
import * as globals from "../../globals";
|
||||
import styles from "../framework/buttons.css";
|
||||
import actions from "../../actions";
|
||||
|
||||
@connect()
|
||||
class CellSetButton extends React.Component {
|
||||
set() {
|
||||
const { differential } = this.props;
|
||||
const {
|
||||
differential,
|
||||
crossfilter,
|
||||
dispatch,
|
||||
eitherCellSetOneOrTwo
|
||||
} = this.props;
|
||||
|
||||
const set = _.map(this.props.crossfilter.allFiltered(), "name");
|
||||
const set = _.map(crossfilter.allFiltered(), "name");
|
||||
|
||||
if (!differential.diffExp) {
|
||||
/* diffexp needs to be cleared before we store a new set */
|
||||
this.props.dispatch({
|
||||
type:
|
||||
"store current cell selection as differential set " +
|
||||
this.props.eitherCellSetOneOrTwo,
|
||||
dispatch({
|
||||
type: `store current cell selection as differential set ${eitherCellSetOneOrTwo}`,
|
||||
data: set
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { differential } = this.props;
|
||||
const { differential, eitherCellSetOneOrTwo } = this.props;
|
||||
const cellListName = `celllist${eitherCellSetOneOrTwo}`;
|
||||
return (
|
||||
<span style={{ marginRight: 10 }}>
|
||||
<button
|
||||
type="button"
|
||||
style={{
|
||||
color: "#FFF",
|
||||
borderRadius: 2,
|
||||
@@ -44,7 +47,7 @@ class CellSetButton extends React.Component {
|
||||
>
|
||||
<span style={{ fontSize: 14, fontWeight: 700 }}>
|
||||
{" "}
|
||||
{this.props.eitherCellSetOneOrTwo}
|
||||
{eitherCellSetOneOrTwo}
|
||||
{": "}
|
||||
</span>
|
||||
<span
|
||||
@@ -55,13 +58,9 @@ class CellSetButton extends React.Component {
|
||||
top: 0
|
||||
}}
|
||||
>
|
||||
{differential[
|
||||
"celllist" + this.props.eitherCellSetOneOrTwo
|
||||
]
|
||||
? this.props.differential[
|
||||
"celllist" + this.props.eitherCellSetOneOrTwo
|
||||
].length + " cells"
|
||||
: 0 + " cells"}
|
||||
{differential[cellListName]
|
||||
? `${differential[cellListName].length} cells`
|
||||
: "0 cells"}
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
@@ -70,17 +69,3 @@ class CellSetButton extends React.Component {
|
||||
}
|
||||
|
||||
export default CellSetButton;
|
||||
|
||||
//
|
||||
// <button style={{
|
||||
// marginLeft: 3,
|
||||
// color: "#FFF",
|
||||
// padding: "0px 10px",
|
||||
// height: 30,
|
||||
// backgroundColor: globals.brightBlue,
|
||||
// border: "none",
|
||||
// cursor: "pointer",
|
||||
// }}>
|
||||
// <span style={{fontSize: 24, fontWeight: 700}}> X </span>
|
||||
//
|
||||
// </button>
|
||||
|
||||
@@ -1,203 +1,10 @@
|
||||
// jshint esversion: 6
|
||||
import React from "react";
|
||||
import _ from "lodash";
|
||||
import memoize from "memoize-one";
|
||||
import { connect } from "react-redux";
|
||||
// import ReactAutocomplete from "react-autocomplete"; /* http://emilebres.github.io/react-virtualized-checkbox/ */
|
||||
import getContrast from "font-color-contrast"; // https://www.npmjs.com/package/font-color-contrast
|
||||
import { FaPaintBrush } from "react-icons/fa";
|
||||
import * as d3 from "d3";
|
||||
import { interpolateGreys } from "d3-scale-chromatic";
|
||||
import * as globals from "../../globals";
|
||||
import actions from "../../actions";
|
||||
|
||||
class HeatmapSquare extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
value: ""
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { backgroundColor, text } = this.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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************
|
||||
***********************************
|
||||
***********************************
|
||||
Row
|
||||
***********************************
|
||||
***********************************
|
||||
**********************************/
|
||||
@connect(state => ({
|
||||
scatterplotXXaccessor: state.controls.scatterplotXXaccessor,
|
||||
scatterplotYYaccessor: state.controls.scatterplotYYaccessor,
|
||||
colorAccessor: state.controls.colorAccessor
|
||||
}))
|
||||
class HeatmapRow extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
value: ""
|
||||
};
|
||||
}
|
||||
|
||||
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>
|
||||
);
|
||||
}
|
||||
}
|
||||
import HeatmapRow from "./heatmapRow";
|
||||
|
||||
/**********************************
|
||||
***********************************
|
||||
@@ -212,18 +19,6 @@ class HeatmapRow extends React.Component {
|
||||
world: state.controls.world
|
||||
}))
|
||||
class Heatmap extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
value: ""
|
||||
};
|
||||
}
|
||||
|
||||
// XXX TODO unused at the moment
|
||||
// getAllGeneNames = memoize(world =>
|
||||
// _.map(this.props.world.varAnnotations, "name")
|
||||
// );
|
||||
|
||||
render() {
|
||||
const { world, differential } = this.props;
|
||||
if (!differential.diffExp) {
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
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;
|
||||
@@ -0,0 +1,30 @@
|
||||
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;
|
||||
@@ -8,7 +8,6 @@ import { connect } from "react-redux";
|
||||
import { FaPlusCircle } from "react-icons/fa";
|
||||
import HistogramBrush from "../brushableHistogram";
|
||||
import * as globals from "../../globals";
|
||||
// import ReactAutocomplete from "react-autocomplete"; /* http://emilebres.github.io/react-virtualized-checkbox/ */
|
||||
import actions from "../../actions";
|
||||
|
||||
@connect(state => {
|
||||
@@ -64,7 +63,7 @@ class GeneExpression extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { world, userDefinedGenes } = this.props;
|
||||
const { world, userDefinedGenes, differential } = this.props;
|
||||
const { gene } = this.state;
|
||||
|
||||
return (
|
||||
@@ -106,43 +105,40 @@ class GeneExpression extends React.Component {
|
||||
) : null}
|
||||
{world && userDefinedGenes.length > 0
|
||||
? _.map(userDefinedGenes, geneName => {
|
||||
if (!world.varDataCache[geneName]) {
|
||||
const values = world.varDataCache[geneName];
|
||||
if (!values) {
|
||||
return null;
|
||||
} else {
|
||||
const values = world.varDataCache[geneName];
|
||||
return (
|
||||
<HistogramBrush
|
||||
key={geneName}
|
||||
field={geneName}
|
||||
ranges={d3.extent(values)}
|
||||
isUserDefined
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<HistogramBrush
|
||||
key={geneName}
|
||||
field={geneName}
|
||||
ranges={d3.extent(values)}
|
||||
isUserDefined
|
||||
/>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
{this.props.differential.diffExp ? (
|
||||
<p> Differentially Expressed Genes </p>
|
||||
) : null}
|
||||
{this.props.differential.diffExp
|
||||
? _.map(this.props.differential.diffExp, (value, key) => {
|
||||
const name = world.varAnnotations[value[0]].name;
|
||||
{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 (!world.varDataCache[world.varAnnotations[value[0]].name]) {
|
||||
if (!values) {
|
||||
return null;
|
||||
} else {
|
||||
return (
|
||||
<HistogramBrush
|
||||
key={name}
|
||||
field={name}
|
||||
ranges={d3.extent(values)}
|
||||
isDiffExp
|
||||
diffExp_avgDiff={value[1]}
|
||||
diffExp_set1AvgExp={value[4]}
|
||||
diffExp_set2AvgExp={value[5]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<HistogramBrush
|
||||
key={name}
|
||||
field={name}
|
||||
ranges={d3.extent(values)}
|
||||
isDiffExp
|
||||
diffExp_avgDiff={value[1]}
|
||||
diffExp_set1AvgExp={value[4]}
|
||||
diffExp_set2AvgExp={value[5]}
|
||||
/>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
</div>
|
||||
@@ -151,46 +147,3 @@ class GeneExpression extends React.Component {
|
||||
}
|
||||
|
||||
export default GeneExpression;
|
||||
|
||||
// <ReactAutocomplete
|
||||
// items={this.props.allGeneNames}
|
||||
// shouldItemRender={(item, value) =>
|
||||
// item.toLowerCase().indexOf(value.toLowerCase()) > -1
|
||||
// }
|
||||
// getItemValue={item => item}
|
||||
// renderItem={(item, highlighted) => (
|
||||
// <div
|
||||
// key={item}
|
||||
// style={{
|
||||
// backgroundColor: highlighted ? "#eee" : "transparent"
|
||||
// }}
|
||||
// >
|
||||
// {item}
|
||||
// </div>
|
||||
// )}
|
||||
// value={this.state.value}
|
||||
// onChange={e => this.setState({ value: e.target.value })}
|
||||
// onSelect={value => {
|
||||
// this.setState({ value });
|
||||
// this.props.dispatch(
|
||||
// actions.requestSingleGeneExpressionCountsPOST(
|
||||
// value
|
||||
// )
|
||||
// );
|
||||
// }}
|
||||
// />
|
||||
|
||||
// <div>
|
||||
// {_.map(this.props.ranges, (value, key) => {
|
||||
// const isColorField = key.includes("color") || key.includes("Color");
|
||||
// if (value.range && key !== "CellName" && !isColorField) {
|
||||
// return (
|
||||
// <HistogramBrush
|
||||
// key={key}
|
||||
// metadataField={key}
|
||||
// ranges={value.range}
|
||||
// />
|
||||
// );
|
||||
// }
|
||||
// })}
|
||||
// </div>
|
||||
|
||||
@@ -6,8 +6,7 @@ import { connect } from "react-redux";
|
||||
import mat4 from "gl-mat4";
|
||||
import _regl from "regl";
|
||||
|
||||
import { FaCrosshairs } from "react-icons/fa";
|
||||
import { FaSearchPlus } from "react-icons/fa";
|
||||
import { FaCrosshairs, FaSearchPlus } from "react-icons/fa";
|
||||
|
||||
import * as globals from "../../globals";
|
||||
import setupSVGandBrushElements from "./setupSVGandBrush";
|
||||
@@ -192,13 +191,13 @@ class Graph extends React.Component {
|
||||
d3.select("#graphAttachPoint")
|
||||
.selectAll("svg")
|
||||
.remove();
|
||||
const { svg, brush, brushContainer } = setupSVGandBrushElements(
|
||||
const { svg: newSvg, brush, brushContainer } = setupSVGandBrushElements(
|
||||
this.handleBrushSelectAction.bind(this),
|
||||
this.handleBrushDeselectAction.bind(this),
|
||||
responsive,
|
||||
this.graphPaddingTop
|
||||
);
|
||||
this.setState({ svg, brush, brushContainer });
|
||||
this.setState({ svg: newSvg, brush, brushContainer });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,15 +38,14 @@ export default function(regl) {
|
||||
uniforms: {
|
||||
distance: regl.prop("distance"),
|
||||
view: regl.prop("view"),
|
||||
projection: (context, props) => {
|
||||
return mat4.perspective(
|
||||
projection: (context, props) =>
|
||||
mat4.perspective(
|
||||
[],
|
||||
Math.PI / 2,
|
||||
context.viewportWidth * props.scale / context.viewportHeight,
|
||||
(context.viewportWidth * props.scale) / context.viewportHeight,
|
||||
0.01,
|
||||
1000
|
||||
);
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
count: regl.prop("count"),
|
||||
|
||||
@@ -238,7 +238,7 @@ class Scatterplot extends React.Component {
|
||||
// and it's alread defined to be a bottom axis.
|
||||
svg
|
||||
.append("g")
|
||||
.attr("transform", "translate(0," + height + ")")
|
||||
.attr("transform", `translate(0,${height})`)
|
||||
.attr("class", "x axis")
|
||||
.call(xAxis);
|
||||
|
||||
@@ -279,8 +279,8 @@ class Scatterplot extends React.Component {
|
||||
className={styles.scatterplot}
|
||||
id="scatterplot"
|
||||
style={{
|
||||
width: width + margin.left + margin.right + "px",
|
||||
height: height + margin.top + margin.bottom + "px"
|
||||
width: `${width + margin.left + margin.right}px`,
|
||||
height: `${height + margin.top + margin.bottom}px`
|
||||
}}
|
||||
>
|
||||
<canvas
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
import * as d3 from "d3";
|
||||
|
||||
const setupScatterplot = (width, height, margin) => {
|
||||
var container = d3.select("#scatterplot");
|
||||
const container = d3.select("#scatterplot");
|
||||
|
||||
var svg = container
|
||||
const svg = container
|
||||
.append("svg")
|
||||
.attr("width", width + margin.left + margin.right)
|
||||
.attr("height", height + margin.top + margin.bottom)
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||
.attr("transform", `translate(${margin.left},${margin.top})`);
|
||||
|
||||
return {
|
||||
svg
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
// jshint esversion: 6
|
||||
import _ from "lodash";
|
||||
|
||||
const paddingRight = 120;
|
||||
const continuousChartWidth = 340;
|
||||
|
||||
export const margin = { top: 66, right: 110, bottom: 20, left: 60 };
|
||||
export const width = 340;
|
||||
export const height = 340 - margin.top - margin.bottom;
|
||||
|
||||
Reference in New Issue
Block a user