mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-09 05:18:11 +08:00
dotplot proto full
This commit is contained in:
@@ -0,0 +1,216 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { connect, shallowEqual } from "react-redux";
|
||||||
|
|
||||||
|
import memoize from "memoize-one";
|
||||||
|
|
||||||
|
import Async from "react-async";
|
||||||
|
import ErrorLoading from "./err";
|
||||||
|
import StillLoading from "./load";
|
||||||
|
import Dot from "./dot";
|
||||||
|
|
||||||
|
import { createCategorySummaryFromDfCol } from "../../util/stateManager/controlsHelpers";
|
||||||
|
|
||||||
|
import {
|
||||||
|
createColorTable,
|
||||||
|
createColorQuery,
|
||||||
|
} from "../../util/stateManager/colorHelpers";
|
||||||
|
|
||||||
|
@connect((state) => ({
|
||||||
|
annoMatrix: state.annoMatrix,
|
||||||
|
colors: state.colors,
|
||||||
|
genesets: state.genesets.genesets,
|
||||||
|
pointDilation: state.pointDilation,
|
||||||
|
differential: state.differential,
|
||||||
|
}))
|
||||||
|
class Column extends React.Component {
|
||||||
|
static watchAsync(props, prevProps) {
|
||||||
|
return !shallowEqual(props.watchProps, prevProps.watchProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
createCategorySummaryFromDfCol = memoize(createCategorySummaryFromDfCol);
|
||||||
|
|
||||||
|
fetchAsyncProps = async (props) => {
|
||||||
|
const { annoMatrix, colors, _geneSymbol, _geneIndex } = props.watchProps;
|
||||||
|
|
||||||
|
const [categoryData, categorySummary, colorData] = await this.fetchData(
|
||||||
|
annoMatrix,
|
||||||
|
"tissue",
|
||||||
|
colors,
|
||||||
|
_geneSymbol,
|
||||||
|
_geneIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
categoryData,
|
||||||
|
categorySummary,
|
||||||
|
colorData,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
async fetchData(annoMatrix, metadataField, colors, _geneSymbol) {
|
||||||
|
/*
|
||||||
|
fetch our data and the color-by data if appropriate, and then build a summary
|
||||||
|
of our category and a color table for the color-by annotation.
|
||||||
|
*/
|
||||||
|
const { schema } = annoMatrix;
|
||||||
|
const { colorMode } = colors;
|
||||||
|
const { genesets, differential } = this.props;
|
||||||
|
let colorDataPromise = Promise.resolve(null);
|
||||||
|
if (_geneSymbol) {
|
||||||
|
const query = createColorQuery(
|
||||||
|
colorMode,
|
||||||
|
_geneSymbol,
|
||||||
|
schema,
|
||||||
|
genesets,
|
||||||
|
differential.diffExp
|
||||||
|
);
|
||||||
|
if (query) colorDataPromise = annoMatrix.fetch(...query);
|
||||||
|
}
|
||||||
|
const [categoryData, colorData] = await Promise.all([
|
||||||
|
annoMatrix.fetch("obs", metadataField),
|
||||||
|
colorDataPromise,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// our data
|
||||||
|
const column = categoryData.icol(0);
|
||||||
|
const colSchema = schema.annotations.obsByName[metadataField];
|
||||||
|
|
||||||
|
const categorySummary = this.createCategorySummaryFromDfCol(
|
||||||
|
column,
|
||||||
|
colSchema
|
||||||
|
);
|
||||||
|
|
||||||
|
return [categoryData, categorySummary, colorData];
|
||||||
|
}
|
||||||
|
|
||||||
|
updateColorTable(colors, colorDf) {
|
||||||
|
const { annoMatrix } = this.props;
|
||||||
|
const { schema } = annoMatrix;
|
||||||
|
|
||||||
|
/* update color table state */
|
||||||
|
if (!colors || !colorDf) {
|
||||||
|
return createColorTable(
|
||||||
|
null, // default mode
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
schema,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { colorAccessor, userColors, colorMode } = colors;
|
||||||
|
return createColorTable(
|
||||||
|
colorMode,
|
||||||
|
colorAccessor /* TODO(colinmegill) #632 dotplot wiring */,
|
||||||
|
colorDf,
|
||||||
|
schema,
|
||||||
|
userColors
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
createColorByQuery(colors) {
|
||||||
|
const { annoMatrix, genesets, differential } = this.props;
|
||||||
|
const { schema } = annoMatrix;
|
||||||
|
const { colorMode, colorAccessor } = colors;
|
||||||
|
|
||||||
|
return createColorQuery(
|
||||||
|
colorMode,
|
||||||
|
colorAccessor /* TODO(colinmegill) #632 dotplot wiring */,
|
||||||
|
schema,
|
||||||
|
genesets,
|
||||||
|
differential.diffExp
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
annoMatrix,
|
||||||
|
pointDilation,
|
||||||
|
colors,
|
||||||
|
viewport,
|
||||||
|
_geneSymbol,
|
||||||
|
_geneIndex,
|
||||||
|
rowColumnSize,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<g key={_geneSymbol}>
|
||||||
|
<Async
|
||||||
|
watchFn={Column.watchAsync}
|
||||||
|
promiseFn={this.fetchAsyncProps}
|
||||||
|
watchProps={{
|
||||||
|
annoMatrix,
|
||||||
|
pointDilation,
|
||||||
|
colors,
|
||||||
|
viewport,
|
||||||
|
_geneSymbol,
|
||||||
|
_geneIndex,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Async.Pending initial>
|
||||||
|
<StillLoading width={viewport.width} height={viewport.height} />
|
||||||
|
</Async.Pending>
|
||||||
|
<Async.Rejected>
|
||||||
|
{(error) => (
|
||||||
|
<ErrorLoading
|
||||||
|
width={viewport.width}
|
||||||
|
height={viewport.height}
|
||||||
|
error={error}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Async.Rejected>
|
||||||
|
<Async.Fulfilled persist>
|
||||||
|
{(asyncProps) => {
|
||||||
|
const {
|
||||||
|
categoryData,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
categorySummary,
|
||||||
|
colorData,
|
||||||
|
} = asyncProps;
|
||||||
|
|
||||||
|
if (!_geneSymbol || !colorData) return null;
|
||||||
|
|
||||||
|
/* TODO(colinmegill) #632 wire to dotplot */
|
||||||
|
|
||||||
|
const metadataField = "tissue";
|
||||||
|
|
||||||
|
const groupBy = categoryData.col(metadataField);
|
||||||
|
const col = colorData.icol(0);
|
||||||
|
const range = col.summarize();
|
||||||
|
|
||||||
|
const histogramMap = col.histogram(
|
||||||
|
100,
|
||||||
|
[range.min, range.max],
|
||||||
|
groupBy
|
||||||
|
);
|
||||||
|
|
||||||
|
return categorySummary.categoryValues.map(
|
||||||
|
(val, _categoryValueIndex) => {
|
||||||
|
return (
|
||||||
|
<Dot
|
||||||
|
key={val}
|
||||||
|
metadataField={metadataField}
|
||||||
|
categoryData={categoryData}
|
||||||
|
_geneSymbol={_geneSymbol}
|
||||||
|
colorData={colorData}
|
||||||
|
categoryValue={val}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
_categoryValueIndex={_categoryValueIndex}
|
||||||
|
histogramMap={histogramMap}
|
||||||
|
_geneIndex={_geneIndex}
|
||||||
|
rowColumnSize={rowColumnSize}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Async.Fulfilled>
|
||||||
|
</Async>
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Column;
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
const Dot = (props) => {
|
||||||
|
const {
|
||||||
|
categoryValue,
|
||||||
|
_categoryValueIndex,
|
||||||
|
histogramMap,
|
||||||
|
_geneSymbol,
|
||||||
|
_geneIndex,
|
||||||
|
rowColumnSize,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const bins = histogramMap.has(categoryValue)
|
||||||
|
? histogramMap.get(categoryValue)
|
||||||
|
: new Array(100).fill(0);
|
||||||
|
|
||||||
|
const zeros = bins.shift(); /* MUTATES, REMOVES FIRST ELEMENT */
|
||||||
|
const rest = bins.reduce(
|
||||||
|
(acc, current) => acc + current
|
||||||
|
); /* SUM REMAINING ELEMENTS */
|
||||||
|
|
||||||
|
/* TODO(colinmegill) #632 colorscale */
|
||||||
|
|
||||||
|
return (
|
||||||
|
<g
|
||||||
|
id={`row_${categoryValue}_${_geneSymbol}`}
|
||||||
|
key={`${_categoryValueIndex}_${categoryValue}`}
|
||||||
|
transform={`translate(${_geneIndex * rowColumnSize}, ${
|
||||||
|
_categoryValueIndex * rowColumnSize
|
||||||
|
})`}
|
||||||
|
>
|
||||||
|
{_geneIndex === 0 && (
|
||||||
|
<text
|
||||||
|
textAnchor="end"
|
||||||
|
style={{ fill: "black", font: "12px Roboto Condensed" }}
|
||||||
|
>
|
||||||
|
{categoryValue}
|
||||||
|
</text>
|
||||||
|
)}
|
||||||
|
<circle
|
||||||
|
r={(rest / zeros) * 10 > 20 ? 20 : (rest / zeros) * 10}
|
||||||
|
cx="11"
|
||||||
|
cy="-3.5"
|
||||||
|
style={{
|
||||||
|
fill: "steelblue",
|
||||||
|
fillOpacity: 0.3,
|
||||||
|
stroke: "none",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Dot;
|
||||||
@@ -1,40 +1,20 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect, shallowEqual } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
// import _regl from "regl";
|
|
||||||
import Async from "react-async";
|
|
||||||
import memoize from "memoize-one";
|
|
||||||
// import * as d3 from "d3";
|
|
||||||
|
|
||||||
import ErrorLoading from "./err";
|
import Column from "./column";
|
||||||
import StillLoading from "./load";
|
|
||||||
|
|
||||||
import { createCategorySummaryFromDfCol } from "../../util/stateManager/controlsHelpers";
|
|
||||||
|
|
||||||
import {
|
|
||||||
createColorTable,
|
|
||||||
createColorQuery,
|
|
||||||
} from "../../util/stateManager/colorHelpers";
|
|
||||||
|
|
||||||
@connect((state) => ({
|
@connect((state) => ({
|
||||||
annoMatrix: state.annoMatrix,
|
|
||||||
layoutChoice: state.layoutChoice,
|
layoutChoice: state.layoutChoice,
|
||||||
colors: state.colors,
|
|
||||||
genesets: state.genesets.genesets,
|
genesets: state.genesets.genesets,
|
||||||
differential: state.differential,
|
colors: state.colors,
|
||||||
pointDilation: state.pointDilation,
|
|
||||||
}))
|
}))
|
||||||
class Dotplot extends React.Component {
|
class Dotplot extends React.Component {
|
||||||
static watchAsync(props, prevProps) {
|
|
||||||
return !shallowEqual(props.watchProps, prevProps.watchProps);
|
|
||||||
}
|
|
||||||
|
|
||||||
createCategorySummaryFromDfCol = memoize(createCategorySummaryFromDfCol);
|
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
const viewport = this.getViewportDimensions();
|
const viewport = this.getViewportDimensions();
|
||||||
this.dotplotTopPadding = 150;
|
this.dotplotTopPadding = 120;
|
||||||
this.dotplotLeftPadding = 200;
|
this.dotplotLeftPadding = 170;
|
||||||
|
this.rowColumnSize = 15;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
viewport,
|
viewport,
|
||||||
@@ -49,8 +29,6 @@ class Dotplot extends React.Component {
|
|||||||
window.removeEventListener("resize", this.handleResize);
|
window.removeEventListener("resize", this.handleResize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleResize = () => {};
|
|
||||||
|
|
||||||
getViewportDimensions = () => {
|
getViewportDimensions = () => {
|
||||||
const { viewportRef } = this.props;
|
const { viewportRef } = this.props;
|
||||||
return {
|
return {
|
||||||
@@ -59,183 +37,16 @@ class Dotplot extends React.Component {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
createRow = (
|
|
||||||
metadataField,
|
|
||||||
categoryData,
|
|
||||||
colorAccessor,
|
|
||||||
colorData,
|
|
||||||
categoryValue,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
index,
|
|
||||||
histogramMap
|
|
||||||
) => {
|
|
||||||
const bins = histogramMap.has(categoryValue)
|
|
||||||
? histogramMap.get(categoryValue)
|
|
||||||
: new Array(100).fill(0);
|
|
||||||
|
|
||||||
const zeros = bins.shift(); /* MUTATES, REMOVES FIRST ELEMENT */
|
|
||||||
const rest = bins.reduce(
|
|
||||||
(acc, current) => acc + current
|
|
||||||
); /* SUM REMAINING ELEMENTS */
|
|
||||||
|
|
||||||
return (
|
|
||||||
<g
|
|
||||||
key={`${index}_${categoryValue}`}
|
|
||||||
transform={`translate(${this.dotplotLeftPadding}, ${
|
|
||||||
index * 15 + this.dotplotTopPadding
|
|
||||||
})`}
|
|
||||||
>
|
|
||||||
<text textAnchor="end">{categoryValue}</text>
|
|
||||||
<circle
|
|
||||||
r={(rest / zeros) * 10}
|
|
||||||
cx="11"
|
|
||||||
cy="-4"
|
|
||||||
style={{ fill: "rgba(70,130,180,.3)", stroke: "none" }}
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
dotplot = (
|
|
||||||
metadataField,
|
|
||||||
categoryData,
|
|
||||||
categorySummary,
|
|
||||||
colorAccessor = "tissue",
|
|
||||||
colorData,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
) => {
|
|
||||||
if (!colorAccessor || !colorData) return null;
|
|
||||||
|
|
||||||
const groupBy = categoryData.col(metadataField);
|
|
||||||
const col = colorData.icol(0);
|
|
||||||
const range = col.summarize();
|
|
||||||
|
|
||||||
const histogramMap = col.histogram(100, [range.min, range.max], groupBy);
|
|
||||||
|
|
||||||
return categorySummary.categoryValues.map((val, index) => {
|
|
||||||
return this.createRow(
|
|
||||||
metadataField,
|
|
||||||
categoryData,
|
|
||||||
colorAccessor,
|
|
||||||
colorData,
|
|
||||||
val,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
index,
|
|
||||||
histogramMap
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
dotplotColumns = () => {
|
|
||||||
const { genesets } = this.props;
|
|
||||||
/* TODO(colinmegill) #632 wire to dotplot reducer */
|
|
||||||
if (genesets && genesets.get("bladder urothelial")) {
|
|
||||||
const _geneset = genesets.get("bladder urothelial");
|
|
||||||
_geneset.genes.forEach((a, b) => {
|
|
||||||
console.log("bladder u", b);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchAsyncProps = async (props) => {
|
|
||||||
const { viewport, annoMatrix, colors } = props.watchProps;
|
|
||||||
|
|
||||||
const [categoryData, categorySummary, colorData] = await this.fetchData(
|
|
||||||
annoMatrix,
|
|
||||||
"tissue",
|
|
||||||
colors
|
|
||||||
);
|
|
||||||
|
|
||||||
const { width, height } = viewport;
|
|
||||||
return {
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
categoryData,
|
|
||||||
categorySummary,
|
|
||||||
colorData,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
async fetchData(annoMatrix, metadataField, colors) {
|
|
||||||
/*
|
|
||||||
fetch our data and the color-by data if appropriate, and then build a summary
|
|
||||||
of our category and a color table for the color-by annotation.
|
|
||||||
*/
|
|
||||||
const { schema } = annoMatrix;
|
|
||||||
const { colorAccessor, colorMode } = colors;
|
|
||||||
const { genesets, differential } = this.props;
|
|
||||||
let colorDataPromise = Promise.resolve(null);
|
|
||||||
if (colorAccessor) {
|
|
||||||
const query = createColorQuery(
|
|
||||||
colorMode,
|
|
||||||
colorAccessor,
|
|
||||||
schema,
|
|
||||||
genesets,
|
|
||||||
differential.diffExp
|
|
||||||
);
|
|
||||||
if (query) colorDataPromise = annoMatrix.fetch(...query);
|
|
||||||
}
|
|
||||||
const [categoryData, colorData] = await Promise.all([
|
|
||||||
annoMatrix.fetch("obs", metadataField),
|
|
||||||
colorDataPromise,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// our data
|
|
||||||
const column = categoryData.icol(0);
|
|
||||||
const colSchema = schema.annotations.obsByName[metadataField];
|
|
||||||
|
|
||||||
const categorySummary = this.createCategorySummaryFromDfCol(
|
|
||||||
column,
|
|
||||||
colSchema
|
|
||||||
);
|
|
||||||
return [categoryData, categorySummary, colorData];
|
|
||||||
}
|
|
||||||
|
|
||||||
updateColorTable(colors, colorDf) {
|
|
||||||
const { annoMatrix } = this.props;
|
|
||||||
const { schema } = annoMatrix;
|
|
||||||
|
|
||||||
/* update color table state */
|
|
||||||
if (!colors || !colorDf) {
|
|
||||||
return createColorTable(
|
|
||||||
null, // default mode
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
schema,
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { colorAccessor, userColors, colorMode } = colors;
|
|
||||||
return createColorTable(
|
|
||||||
colorMode,
|
|
||||||
colorAccessor,
|
|
||||||
colorDf,
|
|
||||||
schema,
|
|
||||||
userColors
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
createColorByQuery(colors) {
|
|
||||||
const { annoMatrix, genesets, differential } = this.props;
|
|
||||||
const { schema } = annoMatrix;
|
|
||||||
const { colorMode, colorAccessor } = colors;
|
|
||||||
|
|
||||||
return createColorQuery(
|
|
||||||
colorMode,
|
|
||||||
colorAccessor,
|
|
||||||
schema,
|
|
||||||
genesets,
|
|
||||||
differential.diffExp
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { viewport } = this.state;
|
const { viewport } = this.state;
|
||||||
const { annoMatrix, colors, pointDilation } = this.props;
|
const { genesets } = this.props;
|
||||||
|
|
||||||
|
const _TESTgeneset = genesets.get("bladder urothelial");
|
||||||
|
|
||||||
|
/* TODO(colinmegill) #632 componetize */
|
||||||
|
if (!_TESTgeneset) return null;
|
||||||
|
|
||||||
|
const _genes = Array.from(_TESTgeneset.genes.keys());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -247,6 +58,7 @@ class Dotplot extends React.Component {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
|
id="dotplot"
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
top: 0,
|
top: 0,
|
||||||
@@ -256,51 +68,43 @@ class Dotplot extends React.Component {
|
|||||||
width={viewport.width}
|
width={viewport.width}
|
||||||
height={viewport.height}
|
height={viewport.height}
|
||||||
>
|
>
|
||||||
{this.dotplotColumns()}
|
<g
|
||||||
<Async
|
id="dotplot_interface_margin"
|
||||||
watchFn={Dotplot.watchAsync}
|
transform={`translate(${this.dotplotLeftPadding},${this.dotplotTopPadding})`}
|
||||||
promiseFn={this.fetchAsyncProps}
|
|
||||||
watchProps={{
|
|
||||||
annoMatrix,
|
|
||||||
pointDilation,
|
|
||||||
colors,
|
|
||||||
viewport,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Async.Pending initial>
|
{/* Acaa1b, Mal, Foxq1 ... across the top of the dotplot */}
|
||||||
<StillLoading width={viewport.width} height={viewport.height} />
|
<g id="dotplot_column_labels" transform="translate(14,-13)">
|
||||||
</Async.Pending>
|
{_genes.map((_geneSymbol, _geneIndexInGeneset) => {
|
||||||
<Async.Rejected>
|
return (
|
||||||
{(error) => (
|
<text
|
||||||
<ErrorLoading
|
key={_geneSymbol}
|
||||||
width={viewport.width}
|
x={0}
|
||||||
height={viewport.height}
|
y={0}
|
||||||
error={error}
|
transform={`translate(${
|
||||||
/>
|
_geneIndexInGeneset * this.rowColumnSize
|
||||||
)}
|
}) rotate(270)`}
|
||||||
</Async.Rejected>
|
style={{ fill: "black", font: "12px Roboto Condensed" }}
|
||||||
<Async.Fulfilled persist>
|
>
|
||||||
{(asyncProps) => {
|
{_geneSymbol}
|
||||||
const {
|
</text>
|
||||||
colorAccessor,
|
|
||||||
categoryData,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
categorySummary,
|
|
||||||
colorData,
|
|
||||||
} = asyncProps;
|
|
||||||
return this.dotplot(
|
|
||||||
"tissue",
|
|
||||||
categoryData,
|
|
||||||
categorySummary,
|
|
||||||
colorAccessor,
|
|
||||||
colorData,
|
|
||||||
width,
|
|
||||||
height
|
|
||||||
);
|
);
|
||||||
}}
|
})}
|
||||||
</Async.Fulfilled>
|
</g>
|
||||||
</Async>
|
{/* loop over genes in the geneset, */}
|
||||||
|
<g id="dotplot_columns">
|
||||||
|
{_genes.map((_geneSymbol, _geneIndexInGeneset) => {
|
||||||
|
return (
|
||||||
|
<Column
|
||||||
|
key={_geneSymbol}
|
||||||
|
_geneSymbol={_geneSymbol}
|
||||||
|
_geneIndex={_geneIndexInGeneset}
|
||||||
|
viewport={viewport}
|
||||||
|
rowColumnSize={this.rowColumnSize}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user