mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-08 21:18:12 +08:00
lint fixes
This commit is contained in:
@@ -5,17 +5,8 @@ import React from "react";
|
|||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
|
|
||||||
import styles from "./parallelCoordinates.css";
|
|
||||||
import SectionHeader from "../framework/sectionHeader";
|
|
||||||
|
|
||||||
import setupParallelCoordinates from "./setupParallelCoordinates";
|
|
||||||
import drawAxes from "./drawAxes";
|
|
||||||
import drawLinesCanvas from "./drawLinesCanvas";
|
|
||||||
|
|
||||||
import HistogramBrush from "./histogramBrush";
|
import HistogramBrush from "./histogramBrush";
|
||||||
|
|
||||||
import { margin, width, height, createDimensions } from "./util";
|
|
||||||
|
|
||||||
@connect(state => {
|
@connect(state => {
|
||||||
const metadata = _.get(state.controls.world, "obsAnnotations", null);
|
const metadata = _.get(state.controls.world, "obsAnnotations", null);
|
||||||
const ranges = _.get(state.controls.world, "summary.obs", null);
|
const ranges = _.get(state.controls.world, "summary.obs", null);
|
||||||
@@ -29,35 +20,29 @@ import { margin, width, height, createDimensions } from "./util";
|
|||||||
};
|
};
|
||||||
})
|
})
|
||||||
class Continuous extends React.Component {
|
class Continuous extends React.Component {
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
svg: null,
|
|
||||||
ctx: null,
|
|
||||||
axes: null,
|
|
||||||
dimensions: null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
handleBrushAction(selection) {
|
handleBrushAction(selection) {
|
||||||
this.props.dispatch({
|
const { dispatch } = this.props;
|
||||||
|
dispatch({
|
||||||
type: "continuous selection using parallel coords brushing",
|
type: "continuous selection using parallel coords brushing",
|
||||||
data: selection
|
data: selection
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleColorAction(key) {
|
handleColorAction(key) {
|
||||||
this.props.dispatch({
|
const { dispatch, ranges } = this.props;
|
||||||
|
dispatch({
|
||||||
type: "color by continuous metadata",
|
type: "color by continuous metadata",
|
||||||
colorAccessor: key,
|
colorAccessor: key,
|
||||||
rangeMaxForColorAccessor: this.props.ranges[key].range.max
|
rangeMaxForColorAccessor: ranges[key].range.max
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { ranges } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{_.map(this.props.ranges, (value, key) => {
|
{_.map(ranges, (value, key) => {
|
||||||
const isColorField = key.includes("color") || key.includes("Color");
|
const isColorField = key.includes("color") || key.includes("Color");
|
||||||
if (value.range && key !== "name" && !isColorField) {
|
if (value.range && key !== "name" && !isColorField) {
|
||||||
return (
|
return (
|
||||||
@@ -68,6 +53,7 @@ class Continuous extends React.Component {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -75,5 +61,3 @@ class Continuous extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default Continuous;
|
export default Continuous;
|
||||||
|
|
||||||
// <SectionHeader text="Continuous Metadata"/>
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// jshint esversion: 6
|
// jshint esversion: 6
|
||||||
|
import * as d3 from "d3";
|
||||||
import styles from "./parallelCoordinates.css";
|
import styles from "./parallelCoordinates.css";
|
||||||
import { yAxis, brushstart } from "./util";
|
import { yAxis, brushstart } from "./util";
|
||||||
|
|
||||||
@@ -19,13 +20,11 @@ const drawAxes = (
|
|||||||
******************************************/
|
******************************************/
|
||||||
|
|
||||||
function brush() {
|
function brush() {
|
||||||
var actives = [];
|
const actives = [];
|
||||||
svg
|
svg
|
||||||
.selectAll(".parcoords_axis .parcoords_brush")
|
.selectAll(".parcoords_axis .parcoords_brush")
|
||||||
.filter(function(d) {
|
.filter(() => d3.brushSelection(this))
|
||||||
return d3.brushSelection(this);
|
.each(d => {
|
||||||
})
|
|
||||||
.each(function(d) {
|
|
||||||
actives.push({
|
actives.push({
|
||||||
dimension: d,
|
dimension: d,
|
||||||
extent: d3.brushSelection(this)
|
extent: d3.brushSelection(this)
|
||||||
@@ -35,20 +34,18 @@ const drawAxes = (
|
|||||||
handleBrushAction(actives);
|
handleBrushAction(actives);
|
||||||
}
|
}
|
||||||
|
|
||||||
var axes = svg
|
const axes = svg
|
||||||
.selectAll(".parcoords_axis")
|
.selectAll(".parcoords_axis")
|
||||||
.data(dimensions)
|
.data(dimensions)
|
||||||
.enter()
|
.enter()
|
||||||
.append("g")
|
.append("g")
|
||||||
.attr("class", `${styles.axis} parcoords_axis`)
|
.attr("class", `${styles.axis} parcoords_axis`)
|
||||||
.attr("transform", (d, i) => {
|
.attr("transform", (d, i) => `translate(${xscale(i)})`);
|
||||||
return "translate(" + xscale(i) + ")";
|
|
||||||
});
|
|
||||||
|
|
||||||
axes
|
axes
|
||||||
.append("g")
|
.append("g")
|
||||||
.each(function(d) {
|
.each(d => {
|
||||||
var renderAxis =
|
const renderAxis =
|
||||||
"axis" in d
|
"axis" in d
|
||||||
? d.axis.scale(d.scale) // custom axis
|
? d.axis.scale(d.scale) // custom axis
|
||||||
: yAxis.scale(d.scale); // default axis
|
: yAxis.scale(d.scale); // default axis
|
||||||
@@ -60,15 +57,13 @@ const drawAxes = (
|
|||||||
})
|
})
|
||||||
.attr("class", styles.title)
|
.attr("class", styles.title)
|
||||||
.attr("text-anchor", "start")
|
.attr("text-anchor", "start")
|
||||||
.text(function(d) {
|
.text(d => ("description" in d ? `${d.description} 🖌️` : `${d.key} 🖌️`));
|
||||||
return "description" in d ? d.description + " 🖌️" : d.key + " 🖌️";
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add and store a brush for each axis.
|
// Add and store a brush for each axis.
|
||||||
axes
|
axes
|
||||||
.append("g")
|
.append("g")
|
||||||
.attr("class", `${styles.brush} parcoords_brush`)
|
.attr("class", `${styles.brush} parcoords_brush`)
|
||||||
.each(function(d) {
|
.each(d => {
|
||||||
d3.select(this).call(
|
d3.select(this).call(
|
||||||
(d.brush = d3
|
(d.brush = d3
|
||||||
.brushY()
|
.brushY()
|
||||||
|
|||||||
@@ -10,54 +10,46 @@ import renderQueue from "../../util/renderQueue";
|
|||||||
******************************************
|
******************************************
|
||||||
******************************************/
|
******************************************/
|
||||||
|
|
||||||
const drawLinesCanvas = (
|
const drawLinesCanvas = (ctx, dimensions, xscale) => d => {
|
||||||
ctx,
|
ctx.globalAlpha = 0.1;
|
||||||
dimensions,
|
|
||||||
xscale,
|
|
||||||
colorAccessor,
|
|
||||||
colorScale
|
|
||||||
) => {
|
|
||||||
return d => {
|
|
||||||
ctx.globalAlpha = 0.1;
|
|
||||||
|
|
||||||
if (d["__selected__"]) {
|
if (d.__selected__) {
|
||||||
ctx.strokeStyle = d["__color__"];
|
ctx.strokeStyle = d.__color__;
|
||||||
} else {
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
const coords = project(d, dimensions, xscale);
|
||||||
|
coords.forEach((p, i) => {
|
||||||
|
// this tricky bit avoids rendering null values as 0
|
||||||
|
if (p === null) {
|
||||||
|
// this bit renders horizontal lines on the previous/next
|
||||||
|
// dimensions, so that sandwiched null values are visible
|
||||||
|
if (i > 0) {
|
||||||
|
const prev = coords[i - 1];
|
||||||
|
if (prev !== null) {
|
||||||
|
ctx.moveTo(prev[0], prev[1]);
|
||||||
|
ctx.lineTo(prev[0] + 6, prev[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i < coords.length - 1) {
|
||||||
|
const next = coords[i + 1];
|
||||||
|
if (next !== null) {
|
||||||
|
ctx.moveTo(next[0] - 6, next[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.beginPath();
|
if (i === 0) {
|
||||||
var coords = project(d, dimensions, xscale);
|
ctx.moveTo(p[0], p[1]);
|
||||||
coords.forEach((p, i) => {
|
return;
|
||||||
// this tricky bit avoids rendering null values as 0
|
}
|
||||||
if (p === null) {
|
|
||||||
// this bit renders horizontal lines on the previous/next
|
|
||||||
// dimensions, so that sandwiched null values are visible
|
|
||||||
if (i > 0) {
|
|
||||||
var prev = coords[i - 1];
|
|
||||||
if (prev !== null) {
|
|
||||||
ctx.moveTo(prev[0], prev[1]);
|
|
||||||
ctx.lineTo(prev[0] + 6, prev[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i < coords.length - 1) {
|
|
||||||
var next = coords[i + 1];
|
|
||||||
if (next !== null) {
|
|
||||||
ctx.moveTo(next[0] - 6, next[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == 0) {
|
ctx.lineTo(p[0], p[1]);
|
||||||
ctx.moveTo(p[0], p[1]);
|
});
|
||||||
return;
|
ctx.stroke();
|
||||||
}
|
|
||||||
|
|
||||||
ctx.lineTo(p[0], p[1]);
|
|
||||||
});
|
|
||||||
ctx.stroke();
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const drawCellLinesUsingRenderQueue = (
|
const drawCellLinesUsingRenderQueue = (
|
||||||
|
|||||||
@@ -12,14 +12,12 @@ import * as d3 from "d3";
|
|||||||
import memoize from "memoize-one";
|
import memoize from "memoize-one";
|
||||||
import * as globals from "../../globals";
|
import * as globals from "../../globals";
|
||||||
|
|
||||||
@connect(state => {
|
@connect(state => ({
|
||||||
return {
|
initializeRanges: _.get(state.controls.world, "summary.obs"),
|
||||||
initializeRanges: _.get(state.controls.world, "summary.obs"),
|
colorAccessor: state.controls.colorAccessor,
|
||||||
colorAccessor: state.controls.colorAccessor,
|
colorScale: state.controls.colorScale,
|
||||||
colorScale: state.controls.colorScale,
|
obsAnnotations: _.get(state.controls.world, "obsAnnotations", null)
|
||||||
obsAnnotations: _.get(state.controls.world, "obsAnnotations", null)
|
}))
|
||||||
};
|
|
||||||
})
|
|
||||||
class HistogramBrush extends React.Component {
|
class HistogramBrush extends React.Component {
|
||||||
calcHistogramCache = memoize((obsAnnotations, metadataField, ranges) => {
|
calcHistogramCache = memoize((obsAnnotations, metadataField, ranges) => {
|
||||||
// recalculate expensive stuff
|
// recalculate expensive stuff
|
||||||
@@ -101,18 +99,10 @@ class HistogramBrush extends React.Component {
|
|||||||
.enter()
|
.enter()
|
||||||
.append("rect")
|
.append("rect")
|
||||||
.attr("class", "bar")
|
.attr("class", "bar")
|
||||||
.attr("x", function(d) {
|
.attr("x", d => x(d.x0) + 1)
|
||||||
return x(d.x0) + 1;
|
.attr("y", d => y(d.length / numValues))
|
||||||
})
|
.attr("width", d => Math.abs(x(d.x1) - x(d.x0) - 1))
|
||||||
.attr("y", function(d) {
|
.attr("height", d => y(0) - y(d.length / numValues));
|
||||||
return y(d.length / numValues);
|
|
||||||
})
|
|
||||||
.attr("width", function(d) {
|
|
||||||
return Math.abs(x(d.x1) - x(d.x0) - 1);
|
|
||||||
})
|
|
||||||
.attr("height", function(d) {
|
|
||||||
return y(0) - y(d.length / numValues);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!this.state.brush && !this.state.axis) {
|
if (!this.state.brush && !this.state.axis) {
|
||||||
const brush = d3
|
const brush = d3
|
||||||
@@ -122,20 +112,14 @@ class HistogramBrush extends React.Component {
|
|||||||
.call(
|
.call(
|
||||||
d3
|
d3
|
||||||
.brushX()
|
.brushX()
|
||||||
.on(
|
.on("end", this.onBrush(metadataField, x.invert).bind(this))
|
||||||
"end",
|
|
||||||
this.onBrush(this.props.metadataField, x.invert).bind(this)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const xAxis = d3
|
const xAxis = d3
|
||||||
.select(svgRef)
|
.select(svgRef)
|
||||||
.append("g")
|
.append("g")
|
||||||
.attr("class", "axis axis--x")
|
.attr("class", "axis axis--x")
|
||||||
.attr(
|
.attr("transform", `translate(0,${this.height - this.marginBottom})`)
|
||||||
"transform",
|
|
||||||
"translate(0," + (this.height - this.marginBottom) + ")"
|
|
||||||
)
|
|
||||||
.call(d3.axisBottom(x).ticks(5))
|
.call(d3.axisBottom(x).ticks(5))
|
||||||
.append("text")
|
.append("text")
|
||||||
.attr("x", this.width - 2)
|
.attr("x", this.width - 2)
|
||||||
@@ -143,30 +127,30 @@ class HistogramBrush extends React.Component {
|
|||||||
.attr("fill", "#000")
|
.attr("fill", "#000")
|
||||||
.attr("text-anchor", "end")
|
.attr("text-anchor", "end")
|
||||||
.attr("font-weight", "bold")
|
.attr("font-weight", "bold")
|
||||||
.text(this.props.metadataField);
|
.text(metadataField);
|
||||||
|
|
||||||
this.setState({ brush, xAxis });
|
this.setState({ brush, xAxis });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleColorAction() {
|
handleColorAction() {
|
||||||
this.props.dispatch({
|
const { dispatch, metadataField, initializeRanges } = this.props;
|
||||||
|
dispatch({
|
||||||
type: "color by continuous metadata",
|
type: "color by continuous metadata",
|
||||||
colorAccessor: this.props.metadataField,
|
colorAccessor: metadataField,
|
||||||
rangeMaxForColorAccessor: this.props.initializeRanges[
|
rangeMaxForColorAccessor: initializeRanges[metadataField].range.max
|
||||||
this.props.metadataField
|
|
||||||
].range.max
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const { metadataField, ranges, colorAccessor } = this.props;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
marginTop: 10,
|
marginTop: 10,
|
||||||
position: "flex"
|
position: "flex"
|
||||||
}}
|
}}
|
||||||
id={"histogram_" + this.props.metadataField}
|
id={`histogram_${metadataField}`}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
width={this.width}
|
width={this.width}
|
||||||
@@ -175,9 +159,9 @@ class HistogramBrush extends React.Component {
|
|||||||
this.drawHistogram(svgRef);
|
this.drawHistogram(svgRef);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{this.props.ranges.min}
|
{ranges.min}
|
||||||
{" to "}
|
{" to "}
|
||||||
{this.props.ranges.max}
|
{ranges.max}
|
||||||
</svg>
|
</svg>
|
||||||
<span
|
<span
|
||||||
onClick={this.handleColorAction.bind(this)}
|
onClick={this.handleColorAction.bind(this)}
|
||||||
@@ -187,9 +171,7 @@ class HistogramBrush extends React.Component {
|
|||||||
// padding: this.props.colorAccessor === this.props.metadataField ? 3 : "auto",
|
// padding: this.props.colorAccessor === this.props.metadataField ? 3 : "auto",
|
||||||
borderRadius: 3,
|
borderRadius: 3,
|
||||||
color:
|
color:
|
||||||
this.props.colorAccessor === this.props.metadataField
|
colorAccessor === metadataField ? globals.brightBlue : "black",
|
||||||
? globals.brightBlue
|
|
||||||
: "black",
|
|
||||||
// backgroundColor: this.props.colorAccessor === this.props.metadataField ? globals.brightBlue : "inherit",
|
// backgroundColor: this.props.colorAccessor === this.props.metadataField ? globals.brightBlue : "inherit",
|
||||||
position: "relative",
|
position: "relative",
|
||||||
top: -29,
|
top: -29,
|
||||||
|
|||||||
@@ -4,26 +4,28 @@
|
|||||||
******************************************
|
******************************************
|
||||||
******************************************/
|
******************************************/
|
||||||
// jshint esversion: 6
|
// jshint esversion: 6
|
||||||
const setupParallelCoordinates = (width, height, margin) => {
|
import * as d3 from "d3";
|
||||||
var container = d3.select("#parcoords");
|
|
||||||
|
|
||||||
var svg = container
|
const setupParallelCoordinates = (width, height, margin) => {
|
||||||
|
const container = d3.select("#parcoords");
|
||||||
|
|
||||||
|
const svg = container
|
||||||
.append("svg")
|
.append("svg")
|
||||||
.attr("width", width + margin.left + margin.right)
|
.attr("width", width + margin.left + margin.right)
|
||||||
.attr("height", height + margin.top + margin.bottom)
|
.attr("height", height + margin.top + margin.bottom)
|
||||||
.append("g")
|
.append("g")
|
||||||
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
.attr("transform", `translate(${margin.left},${margin.top})`);
|
||||||
|
|
||||||
var canvas = container
|
const canvas = container
|
||||||
.append("canvas")
|
.append("canvas")
|
||||||
.attr("width", width * devicePixelRatio)
|
.attr("width", width * devicePixelRatio)
|
||||||
.attr("height", height * devicePixelRatio)
|
.attr("height", height * devicePixelRatio)
|
||||||
.style("width", width + "px")
|
.style("width", `${width}px`)
|
||||||
.style("height", height + "px")
|
.style("height", `${height}px`)
|
||||||
.style("margin-top", margin.top + "px")
|
.style("margin-top", `${margin.top}px`)
|
||||||
.style("margin-left", margin.left + "px");
|
.style("margin-left", `${margin.left}px`);
|
||||||
|
|
||||||
var ctx = canvas.node().getContext("2d");
|
const ctx = canvas.node().getContext("2d");
|
||||||
ctx.globalCompositeOperation = "darken";
|
ctx.globalCompositeOperation = "darken";
|
||||||
ctx.globalAlpha = 0.15;
|
ctx.globalAlpha = 0.15;
|
||||||
ctx.lineWidth = 1.5;
|
ctx.lineWidth = 1.5;
|
||||||
|
|||||||
@@ -18,11 +18,10 @@ export const createDimensions = data => {
|
|||||||
_.each(data, (value, key) => {
|
_.each(data, (value, key) => {
|
||||||
if (value.range) {
|
if (value.range) {
|
||||||
newArr.push({
|
newArr.push({
|
||||||
key: key /* room for confusion: lodash calls this key, it's also the name of the property parallel coords code is looking for */,
|
key /* room for confusion: lodash calls this key, it's also the name of the property parallel coords code is looking for */,
|
||||||
type: {
|
type: {
|
||||||
within: (d, extent, dim) => {
|
within: (d, extent, dim) =>
|
||||||
return extent[0] <= dim.scale(d) && dim.scale(d) <= extent[1];
|
extent[0] <= dim.scale(d) && dim.scale(d) <= extent[1]
|
||||||
}
|
|
||||||
},
|
},
|
||||||
scale: d3
|
scale: d3
|
||||||
.scaleSqrt()
|
.scaleSqrt()
|
||||||
@@ -41,18 +40,13 @@ export const brushstart = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const d3_functor = v => {
|
export const d3_functor = v => {
|
||||||
return typeof v === "function"
|
return typeof v === "function" ? v : () => v;
|
||||||
? v
|
|
||||||
: () => {
|
|
||||||
return v;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const project = (d, dimensions, xscale) => {
|
export const project = (d, dimensions, xscale) =>
|
||||||
return dimensions.map((p, i) => {
|
dimensions.map((p, i) => {
|
||||||
// check if data element has property and contains a value
|
// check if data element has property and contains a value
|
||||||
if (!(p.key in d) || d[p.key] === null) return null;
|
if (!(p.key in d) || d[p.key] === null) return null;
|
||||||
|
|
||||||
return [xscale(i), p.scale(d[p.key])];
|
return [xscale(i), p.scale(d[p.key])];
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|||||||
Reference in New Issue
Block a user