mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-05 05:18:12 +08:00
Embedding button to lower left, cell selection (#1658)
* embedding * menu bottom left * button * change gutters to support lower toolbar * fix scatterplot layout * fix tests to match new layout * fix smoke tests to match new layout * better sentence, dataset.nObs to top * scatterplot position Co-authored-by: bkmartinjr <bruce@chanzuckerberg.com>
This commit is contained in:
co-authored by
bkmartinjr
parent
a44da11f3f
commit
03bad04436
@@ -3,7 +3,7 @@
|
|||||||
exports[`annotations stacked bar graph renders 1`] = `
|
exports[`annotations stacked bar graph renders 1`] = `
|
||||||
Array [
|
Array [
|
||||||
"<div class=\\"categorical__value___2RKaC\\" data-testclass=\\"categorical-row\\" style=\\"padding: 4px 0px 4px 7px; display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 2px; border-radius: 2px;\\"><div style=\\"margin: 0px; padding: 0px; user-select: none; width: 220px; display: flex; justify-content: space-between;\\"><div style=\\"display: flex; align-items: baseline;\\"><label for=\\"value-toggle-checkbox-TEST-CATEGORY-TEST-LABEL\\" class=\\"bp3-control bp3-checkbox\\" style=\\"margin: 0px;\\"><input id=\\"value-toggle-checkbox-TEST-CATEGORY-TEST-LABEL\\" data-testclass=\\"categorical-value-select\\" data-testid=\\"categorical-value-select-TEST-CATEGORY-TEST-LABEL\\" type=\\"checkbox\\" checked=\\"\\"><span class=\\"bp3-control-indicator\\"></span></label><span class=\\"bp3-popover-wrapper\\"><span class=\\"bp3-popover-target\\"><span data-testid=\\"categorical-value-TEST-CATEGORY-TEST-LABEL\\" data-testclass=\\"categorical-value\\" aria-label=\\"TEST-LABEL\\" class=\\"\\" tabindex=\\"0\\" style=\\"width: 63px; color: black; font-style: normal; display: inline-block; overflow: hidden; line-height: 1.1em; height: 1.1em; vertical-align: middle; margin-right: 16px;\\"><span style=\\"width: 63px; color: black; font-style: normal; display: flex; overflow: hidden; line-height: 1.1em; height: 1.1em; vertical-align: middle; margin-right: 16px; justify-content: flex-start;\\"><span style=\\"overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex-shrink: 1; min-width: 5px;\\">TEST-</span><span style=\\"position: relative; overflow: hidden; white-space: nowrap;\\"><span style=\\"color: transparent;\\">LABEL</span><span style=\\"position: absolute; right: 0px; color: black;\\">LABEL</span></span></span></span></span></span></div><span style=\\"flex-shrink: 0;\\"></span></div><div><span><span data-testclass=\\"categorical-value-count\\" data-testid=\\"categorical-value-count-TEST-CATEGORY-TEST-LABEL\\" style=\\"color: black;\\">0</span><svg display=\\"none\\" style=\\"margin-left: 5px; width: 11px; height: 11px; background-color: inherit;\\"></svg><span><span class=\\"bp3-popover-wrapper\\"><span class=\\"bp3-popover-target\\"><button type=\\"button\\" data-testclass=\\"seeActions\\" data-testid=\\"TEST-CATEGORY:TEST-LABEL:see-actions\\" class=\\"bp3-button bp3-minimal bp3-small\\" tabindex=\\"0\\" style=\\"margin-left: 2px; position: relative; top: -1px; min-height: 16px;\\"><span icon=\\"more\\" class=\\"bp3-icon bp3-icon-more\\"><svg data-icon=\\"more\\" width=\\"10\\" height=\\"10\\" viewBox=\\"0 0 16 16\\"><desc>more</desc><path d=\\"M2 6.03a2 2 0 100 4 2 2 0 100-4zM14 6.03a2 2 0 100 4 2 2 0 100-4zM8 6.03a2 2 0 100 4 2 2 0 100-4z\\" fill-rule=\\"evenodd\\"></path></svg></span></button></span></span></span></span></div></div>",
|
"<div class=\\"categorical__value___2RKaC\\" data-testclass=\\"categorical-row\\" style=\\"padding: 4px 0px 4px 7px; display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 2px; border-radius: 2px;\\"><div style=\\"margin: 0px; padding: 0px; user-select: none; width: 220px; display: flex; justify-content: space-between;\\"><div style=\\"display: flex; align-items: baseline;\\"><label for=\\"value-toggle-checkbox-TEST-CATEGORY-TEST-LABEL\\" class=\\"bp3-control bp3-checkbox\\" style=\\"margin: 0px;\\"><input id=\\"value-toggle-checkbox-TEST-CATEGORY-TEST-LABEL\\" data-testclass=\\"categorical-value-select\\" data-testid=\\"categorical-value-select-TEST-CATEGORY-TEST-LABEL\\" type=\\"checkbox\\" checked=\\"\\"><span class=\\"bp3-control-indicator\\"></span></label><span class=\\"bp3-popover-wrapper\\"><span class=\\"bp3-popover-target\\"><span data-testid=\\"categorical-value-TEST-CATEGORY-TEST-LABEL\\" data-testclass=\\"categorical-value\\" aria-label=\\"TEST-LABEL\\" class=\\"\\" tabindex=\\"0\\" style=\\"width: 63px; color: black; font-style: normal; display: inline-block; overflow: hidden; line-height: 1.1em; height: 1.1em; vertical-align: middle; margin-right: 16px;\\"><span style=\\"width: 63px; color: black; font-style: normal; display: flex; overflow: hidden; line-height: 1.1em; height: 1.1em; vertical-align: middle; margin-right: 16px; justify-content: flex-start;\\"><span style=\\"overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex-shrink: 1; min-width: 5px;\\">TEST-</span><span style=\\"position: relative; overflow: hidden; white-space: nowrap;\\"><span style=\\"color: transparent;\\">LABEL</span><span style=\\"position: absolute; right: 0px; color: black;\\">LABEL</span></span></span></span></span></span></div><span style=\\"flex-shrink: 0;\\"></span></div><div><span><span data-testclass=\\"categorical-value-count\\" data-testid=\\"categorical-value-count-TEST-CATEGORY-TEST-LABEL\\" style=\\"color: black;\\">0</span><svg display=\\"none\\" style=\\"margin-left: 5px; width: 11px; height: 11px; background-color: inherit;\\"></svg><span><span class=\\"bp3-popover-wrapper\\"><span class=\\"bp3-popover-target\\"><button type=\\"button\\" data-testclass=\\"seeActions\\" data-testid=\\"TEST-CATEGORY:TEST-LABEL:see-actions\\" class=\\"bp3-button bp3-minimal bp3-small\\" tabindex=\\"0\\" style=\\"margin-left: 2px; position: relative; top: -1px; min-height: 16px;\\"><span icon=\\"more\\" class=\\"bp3-icon bp3-icon-more\\"><svg data-icon=\\"more\\" width=\\"10\\" height=\\"10\\" viewBox=\\"0 0 16 16\\"><desc>more</desc><path d=\\"M2 6.03a2 2 0 100 4 2 2 0 100-4zM14 6.03a2 2 0 100 4 2 2 0 100-4zM8 6.03a2 2 0 100 4 2 2 0 100-4z\\" fill-rule=\\"evenodd\\"></path></svg></span></button></span></span></span></span></div></div>",
|
||||||
"<div class=\\"categorical__value___2RKaC\\" data-testclass=\\"categorical-row\\" style=\\"padding: 4px 0px 4px 7px; display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 2px; border-radius: 2px;\\"><div style=\\"margin: 0px; padding: 0px; user-select: none; width: 220px; display: flex; justify-content: space-between;\\"><div style=\\"display: flex; align-items: baseline;\\"><label for=\\"value-toggle-checkbox-TEST-CATEGORY-unassigned\\" class=\\"bp3-control bp3-checkbox\\" style=\\"margin: 0px;\\"><input id=\\"value-toggle-checkbox-TEST-CATEGORY-unassigned\\" data-testclass=\\"categorical-value-select\\" data-testid=\\"categorical-value-select-TEST-CATEGORY-unassigned\\" type=\\"checkbox\\" checked=\\"\\"><span class=\\"bp3-control-indicator\\"></span></label><span class=\\"bp3-popover-wrapper\\"><span class=\\"bp3-popover-target\\"><span data-testid=\\"categorical-value-TEST-CATEGORY-unassigned\\" data-testclass=\\"categorical-value\\" aria-label=\\"unassigned\\" class=\\"\\" tabindex=\\"0\\" style=\\"width: 63px; color: rgb(171, 171, 171); font-style: italic; display: inline-block; overflow: hidden; line-height: 1.1em; height: 1.1em; vertical-align: middle; margin-right: 16px;\\"><span style=\\"width: 63px; color: rgb(171, 171, 171); font-style: italic; display: flex; overflow: hidden; line-height: 1.1em; height: 1.1em; vertical-align: middle; margin-right: 16px; justify-content: flex-start;\\"><span style=\\"overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex-shrink: 1; min-width: 5px;\\">unass</span><span style=\\"position: relative; overflow: hidden; white-space: nowrap;\\"><span style=\\"color: transparent;\\">igned</span><span style=\\"position: absolute; right: 0px; color: rgb(171, 171, 171);\\">igned</span></span></span></span></span></span></div><span style=\\"flex-shrink: 0;\\"><canvas class=\\"bp3-popover-targer\\" width=\\"100\\" height=\\"11\\" style=\\"margin-right: 5px; width: 100px; height: 11px;\\"></canvas></span></div><div><span><span data-testclass=\\"categorical-value-count\\" data-testid=\\"categorical-value-count-TEST-CATEGORY-unassigned\\" style=\\"color: rgb(171, 171, 171); font-style: italic;\\">2132</span><svg display=\\"none\\" style=\\"margin-left: 5px; width: 11px; height: 11px; background-color: inherit;\\"></svg><span><span class=\\"bp3-popover-wrapper\\"><span class=\\"bp3-popover-target\\"><button type=\\"button\\" data-testclass=\\"seeActions\\" data-testid=\\"TEST-CATEGORY:unassigned:see-actions\\" class=\\"bp3-button bp3-minimal bp3-small\\" tabindex=\\"0\\" style=\\"margin-left: 2px; position: relative; top: -1px; min-height: 16px;\\"><span icon=\\"more\\" class=\\"bp3-icon bp3-icon-more\\"><svg data-icon=\\"more\\" width=\\"10\\" height=\\"10\\" viewBox=\\"0 0 16 16\\"><desc>more</desc><path d=\\"M2 6.03a2 2 0 100 4 2 2 0 100-4zM14 6.03a2 2 0 100 4 2 2 0 100-4zM8 6.03a2 2 0 100 4 2 2 0 100-4z\\" fill-rule=\\"evenodd\\"></path></svg></span></button></span></span></span></span></div></div>",
|
"<div class=\\"categorical__value___2RKaC\\" data-testclass=\\"categorical-row\\" style=\\"padding: 4px 0px 4px 7px; display: flex; align-items: baseline; justify-content: space-between; margin-bottom: 2px; border-radius: 2px;\\"><div style=\\"margin: 0px; padding: 0px; user-select: none; width: 220px; display: flex; justify-content: space-between;\\"><div style=\\"display: flex; align-items: baseline;\\"><label for=\\"value-toggle-checkbox-TEST-CATEGORY-unassigned\\" class=\\"bp3-control bp3-checkbox\\" style=\\"margin: 0px;\\"><input id=\\"value-toggle-checkbox-TEST-CATEGORY-unassigned\\" data-testclass=\\"categorical-value-select\\" data-testid=\\"categorical-value-select-TEST-CATEGORY-unassigned\\" type=\\"checkbox\\" checked=\\"\\"><span class=\\"bp3-control-indicator\\"></span></label><span class=\\"bp3-popover-wrapper\\"><span class=\\"bp3-popover-target\\"><span data-testid=\\"categorical-value-TEST-CATEGORY-unassigned\\" data-testclass=\\"categorical-value\\" aria-label=\\"unassigned\\" class=\\"\\" tabindex=\\"0\\" style=\\"width: 63px; color: rgb(171, 171, 171); font-style: italic; display: inline-block; overflow: hidden; line-height: 1.1em; height: 1.1em; vertical-align: middle; margin-right: 16px;\\"><span style=\\"width: 63px; color: rgb(171, 171, 171); font-style: italic; display: flex; overflow: hidden; line-height: 1.1em; height: 1.1em; vertical-align: middle; margin-right: 16px; justify-content: flex-start;\\"><span style=\\"overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex-shrink: 1; min-width: 5px;\\">unass</span><span style=\\"position: relative; overflow: hidden; white-space: nowrap;\\"><span style=\\"color: transparent;\\">igned</span><span style=\\"position: absolute; right: 0px; color: rgb(171, 171, 171);\\">igned</span></span></span></span></span></span></div><span style=\\"flex-shrink: 0;\\"><canvas class=\\"bp3-popover-targer\\" width=\\"100\\" height=\\"11\\" style=\\"margin-right: 5px; width: 100px; height: 11px;\\"></canvas></span></div><div><span><span data-testclass=\\"categorical-value-count\\" data-testid=\\"categorical-value-count-TEST-CATEGORY-unassigned\\" style=\\"color: rgb(171, 171, 171); font-style: italic;\\">2133</span><svg display=\\"none\\" style=\\"margin-left: 5px; width: 11px; height: 11px; background-color: inherit;\\"></svg><span><span class=\\"bp3-popover-wrapper\\"><span class=\\"bp3-popover-target\\"><button type=\\"button\\" data-testclass=\\"seeActions\\" data-testid=\\"TEST-CATEGORY:unassigned:see-actions\\" class=\\"bp3-button bp3-minimal bp3-small\\" tabindex=\\"0\\" style=\\"margin-left: 2px; position: relative; top: -1px; min-height: 16px;\\"><span icon=\\"more\\" class=\\"bp3-icon bp3-icon-more\\"><svg data-icon=\\"more\\" width=\\"10\\" height=\\"10\\" viewBox=\\"0 0 16 16\\"><desc>more</desc><path d=\\"M2 6.03a2 2 0 100 4 2 2 0 100-4zM14 6.03a2 2 0 100 4 2 2 0 100-4zM8 6.03a2 2 0 100 4 2 2 0 100-4z\\" fill-rule=\\"evenodd\\"></path></svg></span></button></span></span></span></span></div></div>",
|
||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export const datasets = {
|
|||||||
lasso: [
|
lasso: [
|
||||||
{
|
{
|
||||||
"coordinates-as-percent": { x1: 0.1, y1: 0.25, x2: 0.7, y2: 0.75 },
|
"coordinates-as-percent": { x1: 0.1, y1: 0.25, x2: 0.7, y2: 0.75 },
|
||||||
count: "1173",
|
count: "1131",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
categorical: [
|
categorical: [
|
||||||
@@ -121,8 +121,8 @@ export const datasets = {
|
|||||||
},
|
},
|
||||||
newCount: {
|
newCount: {
|
||||||
bySubsetConfig: {
|
bySubsetConfig: {
|
||||||
false: "599",
|
false: "668",
|
||||||
true: "594",
|
true: "659",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import Legend from "./continuousLegend";
|
|||||||
import Graph from "./graph/graph";
|
import Graph from "./graph/graph";
|
||||||
import MenuBar from "./menubar";
|
import MenuBar from "./menubar";
|
||||||
import Autosave from "./autosave";
|
import Autosave from "./autosave";
|
||||||
|
import Embedding from "./embedding";
|
||||||
import TermsOfServicePrompt from "./termsPrompt";
|
import TermsOfServicePrompt from "./termsPrompt";
|
||||||
|
|
||||||
import actions from "../actions";
|
import actions from "../actions";
|
||||||
@@ -73,6 +74,7 @@ class App extends React.Component {
|
|||||||
{(viewportRef) => (
|
{(viewportRef) => (
|
||||||
<>
|
<>
|
||||||
<MenuBar />
|
<MenuBar />
|
||||||
|
<Embedding />
|
||||||
<Autosave />
|
<Autosave />
|
||||||
<TermsOfServicePrompt />
|
<TermsOfServicePrompt />
|
||||||
<Legend viewportRef={viewportRef} />
|
<Legend viewportRef={viewportRef} />
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import {
|
||||||
|
ButtonGroup,
|
||||||
|
Popover,
|
||||||
|
Button,
|
||||||
|
Radio,
|
||||||
|
RadioGroup,
|
||||||
|
Tooltip,
|
||||||
|
Position,
|
||||||
|
} from "@blueprintjs/core";
|
||||||
|
import * as globals from "../../globals";
|
||||||
|
import actions from "../../actions";
|
||||||
|
|
||||||
|
@connect((state) => {
|
||||||
|
return {
|
||||||
|
layoutChoice: state.layoutChoice,
|
||||||
|
schema: state.annoMatrix?.schema,
|
||||||
|
crossfilter: state.obsCrossfilter,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
class Embedding extends React.PureComponent {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
handleLayoutChoiceChange = (e) => {
|
||||||
|
const { dispatch } = this.props;
|
||||||
|
dispatch(actions.layoutChoiceAction(e.currentTarget.value));
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { layoutChoice, schema, crossfilter } = this.props;
|
||||||
|
return (
|
||||||
|
<ButtonGroup
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
display: "inherit",
|
||||||
|
left: 8,
|
||||||
|
bottom: 8,
|
||||||
|
zIndex: 9999,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Popover
|
||||||
|
target={
|
||||||
|
<Tooltip
|
||||||
|
content="Select embedding for visualization"
|
||||||
|
position="top"
|
||||||
|
hoverOpenDelay={globals.tooltipHoverOpenDelay}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
data-testid="layout-choice"
|
||||||
|
icon="heatmap"
|
||||||
|
// minimal
|
||||||
|
id="embedding"
|
||||||
|
style={{
|
||||||
|
cursor: "pointer",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{layoutChoice?.current}: {crossfilter.countSelected()} out of{" "}
|
||||||
|
{crossfilter.size()} cells
|
||||||
|
{/* BRUCE to extend 1559 */}
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
}
|
||||||
|
// minimal /* removes arrow */
|
||||||
|
position={Position.TOP_LEFT}
|
||||||
|
content={
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
flexDirection: "column",
|
||||||
|
padding: 10,
|
||||||
|
width: 400,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<h1>Embedding Choice</h1>
|
||||||
|
<p style={{ fontStyle: "italic" }}>
|
||||||
|
There are {schema?.dataframe?.nObs} cells in the entire dataset.
|
||||||
|
</p>
|
||||||
|
<RadioGroup
|
||||||
|
onChange={this.handleLayoutChoiceChange}
|
||||||
|
selectedValue={layoutChoice.current}
|
||||||
|
>
|
||||||
|
{layoutChoice.available.map((name) => (
|
||||||
|
<Radio
|
||||||
|
label={`${name} ${schema?.dataframe?.nObs} cells`}
|
||||||
|
value={name}
|
||||||
|
key={name}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</RadioGroup>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</ButtonGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Embedding;
|
||||||
@@ -34,8 +34,10 @@ function createProjectionTF(viewportWidth, viewportHeight) {
|
|||||||
the projection transform accounts for the screen size & other layout
|
the projection transform accounts for the screen size & other layout
|
||||||
*/
|
*/
|
||||||
const fractionToUse = 0.95; // fraction of min dimension to use
|
const fractionToUse = 0.95; // fraction of min dimension to use
|
||||||
const topGutterSizePx = 32; // toolbar box height
|
const topGutterSizePx = 32; // top gutter for tools
|
||||||
const heightMinusGutter = viewportHeight - topGutterSizePx;
|
const bottomGutterSizePx = 32; // bottom gutter for tools
|
||||||
|
const heightMinusGutter =
|
||||||
|
viewportHeight - topGutterSizePx - bottomGutterSizePx;
|
||||||
const minDim = Math.min(viewportWidth, heightMinusGutter);
|
const minDim = Math.min(viewportWidth, heightMinusGutter);
|
||||||
const aspectScale = [
|
const aspectScale = [
|
||||||
(fractionToUse * minDim) / viewportWidth,
|
(fractionToUse * minDim) / viewportWidth,
|
||||||
@@ -44,7 +46,7 @@ function createProjectionTF(viewportWidth, viewportHeight) {
|
|||||||
const m = mat3.create();
|
const m = mat3.create();
|
||||||
mat3.fromTranslation(m, [
|
mat3.fromTranslation(m, [
|
||||||
0,
|
0,
|
||||||
-topGutterSizePx / viewportHeight / aspectScale[1],
|
(bottomGutterSizePx - topGutterSizePx) / viewportHeight / aspectScale[1],
|
||||||
]);
|
]);
|
||||||
mat3.scale(m, m, aspectScale);
|
mat3.scale(m, m, aspectScale);
|
||||||
return m;
|
return m;
|
||||||
|
|||||||
@@ -1,118 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import {
|
|
||||||
ButtonGroup,
|
|
||||||
Popover,
|
|
||||||
Button,
|
|
||||||
Radio,
|
|
||||||
RadioGroup,
|
|
||||||
Tooltip,
|
|
||||||
Position,
|
|
||||||
} from "@blueprintjs/core";
|
|
||||||
import { connect } from "react-redux";
|
|
||||||
import * as globals from "../../globals";
|
|
||||||
import styles from "./menubar.css";
|
|
||||||
import actions from "../../actions";
|
|
||||||
|
|
||||||
@connect((state) => ({
|
|
||||||
layoutChoice: state.layoutChoice,
|
|
||||||
// disabled temporarily. TODO - issue #1606
|
|
||||||
// reembedController: state.reembedController,
|
|
||||||
// enableReembedding: state.config?.parameters?.["enable-reembedding"] ?? false,
|
|
||||||
enableReembedding: false,
|
|
||||||
}))
|
|
||||||
class Embedding extends React.PureComponent {
|
|
||||||
handleLayoutChoiceChange = (e) => {
|
|
||||||
const { dispatch } = this.props;
|
|
||||||
dispatch(actions.layoutChoiceAction(e.currentTarget.value));
|
|
||||||
};
|
|
||||||
|
|
||||||
// eslint-disable-next-line class-methods-use-this -- temporary disable
|
|
||||||
renderReembedding() {
|
|
||||||
return null;
|
|
||||||
/* disabled pending rewrite. TODO - issue #1606
|
|
||||||
const {
|
|
||||||
enableReembedding,
|
|
||||||
world,
|
|
||||||
universe,
|
|
||||||
dispatch,
|
|
||||||
reembedController,
|
|
||||||
} = this.props;
|
|
||||||
|
|
||||||
if (!enableReembedding) return null;
|
|
||||||
|
|
||||||
const loading = !!reembedController?.pendingFetch;
|
|
||||||
const disabled = World.worldEqUniverse(world, universe);
|
|
||||||
const tipContent = disabled
|
|
||||||
? "Subset cells first, then click to recompute UMAP embedding."
|
|
||||||
: "Click to recompute UMAP embedding on the current cell subset.";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
content={tipContent}
|
|
||||||
position="bottom"
|
|
||||||
hoverOpenDelay={globals.tooltipHoverOpenDelay}
|
|
||||||
>
|
|
||||||
<AnchorButton
|
|
||||||
icon="new-object"
|
|
||||||
style={{ marginRight: 10 }}
|
|
||||||
disabled={disabled}
|
|
||||||
onClick={() => dispatch(actions.requestReembed())}
|
|
||||||
loading={loading}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { layoutChoice } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ButtonGroup className={styles.menubarButton}>
|
|
||||||
<Popover
|
|
||||||
target={
|
|
||||||
<Tooltip
|
|
||||||
content="Select embedding for visualization"
|
|
||||||
position="bottom"
|
|
||||||
hoverOpenDelay={globals.tooltipHoverOpenDelay}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
data-testid="layout-choice"
|
|
||||||
icon="heatmap"
|
|
||||||
style={{
|
|
||||||
cursor: "pointer",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
position={Position.BOTTOM_RIGHT}
|
|
||||||
content={
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "flex-start",
|
|
||||||
alignItems: "flex-start",
|
|
||||||
flexDirection: "column",
|
|
||||||
padding: 10,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<RadioGroup
|
|
||||||
label="Embedding Choice"
|
|
||||||
onChange={this.handleLayoutChoiceChange}
|
|
||||||
selectedValue={layoutChoice.current}
|
|
||||||
>
|
|
||||||
{layoutChoice.available.map((name) => (
|
|
||||||
<Radio label={name} value={name} key={name} />
|
|
||||||
))}
|
|
||||||
</RadioGroup>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{this.renderReembedding()}
|
|
||||||
</ButtonGroup>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Embedding;
|
|
||||||
@@ -6,7 +6,6 @@ import * as globals from "../../globals";
|
|||||||
import styles from "./menubar.css";
|
import styles from "./menubar.css";
|
||||||
import actions from "../../actions";
|
import actions from "../../actions";
|
||||||
import Clip from "./clip";
|
import Clip from "./clip";
|
||||||
import Embedding from "./embedding";
|
|
||||||
import InformationMenu from "./infoMenu";
|
import InformationMenu from "./infoMenu";
|
||||||
import Subset from "./subset";
|
import Subset from "./subset";
|
||||||
import UndoRedoReset from "./undoRedo";
|
import UndoRedoReset from "./undoRedo";
|
||||||
@@ -264,7 +263,6 @@ class MenuBar extends React.PureComponent {
|
|||||||
this.handleClipPercentileMinValueChange
|
this.handleClipPercentileMinValueChange
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Embedding />
|
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content="When a category is colored by, show labels on the graph"
|
content="When a category is colored by, show labels on the graph"
|
||||||
position="bottom"
|
position="bottom"
|
||||||
|
|||||||
@@ -438,18 +438,19 @@ class Scatterplot extends React.PureComponent {
|
|||||||
pointDilation,
|
pointDilation,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { minimized, regl, viewport } = this.state;
|
const { minimized, regl, viewport } = this.state;
|
||||||
|
const bottomToolbarGutter = 48; // gutter for bottom tool bar
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: "fixed",
|
position: "fixed",
|
||||||
bottom: minimized ? -height + -margin.top - 2 : 0,
|
bottom: bottomToolbarGutter,
|
||||||
borderRadius: "3px 3px 0px 0px",
|
borderRadius: "3px 3px 0px 0px",
|
||||||
left: globals.leftSidebarWidth + globals.scatterplotMarginLeft,
|
left: globals.leftSidebarWidth + globals.scatterplotMarginLeft,
|
||||||
padding: "0px 20px 20px 0px",
|
padding: "0px 20px 20px 0px",
|
||||||
background: "white",
|
background: "white",
|
||||||
/* x y blur spread color */
|
/* x y blur spread color */
|
||||||
boxShadow: "0px 0px 6px 2px rgba(153,153,153,0.4)",
|
boxShadow: "0px 0px 3px 2px rgba(153,153,153,0.2)",
|
||||||
zIndex: 2,
|
zIndex: 2,
|
||||||
}}
|
}}
|
||||||
id="scatterplot_wrapper"
|
id="scatterplot_wrapper"
|
||||||
@@ -488,7 +489,9 @@ class Scatterplot extends React.PureComponent {
|
|||||||
id="scatterplot"
|
id="scatterplot"
|
||||||
style={{
|
style={{
|
||||||
width: `${width + margin.left + margin.right}px`,
|
width: `${width + margin.left + margin.right}px`,
|
||||||
height: `${height + margin.top + margin.bottom}px`,
|
height: `${
|
||||||
|
(minimized ? 0 : height + margin.top) + margin.bottom
|
||||||
|
}px`,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<canvas
|
<canvas
|
||||||
@@ -498,6 +501,7 @@ class Scatterplot extends React.PureComponent {
|
|||||||
style={{
|
style={{
|
||||||
marginLeft: margin.left,
|
marginLeft: margin.left,
|
||||||
marginTop: margin.top,
|
marginTop: margin.top,
|
||||||
|
display: minimized ? "none" : null,
|
||||||
}}
|
}}
|
||||||
ref={this.setReglCanvas}
|
ref={this.setReglCanvas}
|
||||||
/>
|
/>
|
||||||
@@ -523,9 +527,7 @@ class Scatterplot extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<ScatterplotAxis
|
<ScatterplotAxis
|
||||||
width={width}
|
minimized={minimized}
|
||||||
height={height}
|
|
||||||
margin={margin}
|
|
||||||
scatterplotYYaccessor={scatterplotXXaccessor}
|
scatterplotYYaccessor={scatterplotXXaccessor}
|
||||||
scatterplotXXaccessor={scatterplotYYaccessor}
|
scatterplotXXaccessor={scatterplotYYaccessor}
|
||||||
xScale={asyncProps.xScale}
|
xScale={asyncProps.xScale}
|
||||||
@@ -544,7 +546,13 @@ class Scatterplot extends React.PureComponent {
|
|||||||
export default Scatterplot;
|
export default Scatterplot;
|
||||||
|
|
||||||
const ScatterplotAxis = React.memo(
|
const ScatterplotAxis = React.memo(
|
||||||
({ scatterplotYYaccessor, scatterplotXXaccessor, xScale, yScale }) => {
|
({
|
||||||
|
minimized,
|
||||||
|
scatterplotYYaccessor,
|
||||||
|
scatterplotXXaccessor,
|
||||||
|
xScale,
|
||||||
|
yScale,
|
||||||
|
}) => {
|
||||||
/*
|
/*
|
||||||
Axis for the scatterplot, rendered with SVG/D3. Props:
|
Axis for the scatterplot, rendered with SVG/D3. Props:
|
||||||
* scatterplotXXaccessor - name of X axis
|
* scatterplotXXaccessor - name of X axis
|
||||||
@@ -559,7 +567,7 @@ const ScatterplotAxis = React.memo(
|
|||||||
const svgRef = useRef(null);
|
const svgRef = useRef(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!svgRef.current) return;
|
if (!svgRef.current || minimized) return;
|
||||||
const svg = d3.select(svgRef.current);
|
const svg = d3.select(svgRef.current);
|
||||||
|
|
||||||
svg.selectAll("*").remove();
|
svg.selectAll("*").remove();
|
||||||
@@ -608,6 +616,9 @@ const ScatterplotAxis = React.memo(
|
|||||||
width={width + margin.left + margin.right}
|
width={width + margin.left + margin.right}
|
||||||
height={height + margin.top + margin.bottom}
|
height={height + margin.top + margin.bottom}
|
||||||
data-testid="scatterplot-svg"
|
data-testid="scatterplot-svg"
|
||||||
|
style={{
|
||||||
|
display: minimized ? "none" : null,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<g ref={svgRef} transform={`translate(${margin.left},${margin.top})`} />
|
<g ref={svgRef} transform={`translate(${margin.left},${margin.top})`} />
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export const maxControlsWidth = 800;
|
|||||||
export const graphMargin = { top: 20, right: 10, bottom: 30, left: 40 };
|
export const graphMargin = { top: 20, right: 10, bottom: 30, left: 40 };
|
||||||
export const graphWidth = 700;
|
export const graphWidth = 700;
|
||||||
export const graphHeight = 700;
|
export const graphHeight = 700;
|
||||||
export const scatterplotMarginLeft = 25;
|
export const scatterplotMarginLeft = 11;
|
||||||
|
|
||||||
export const rightSidebarWidth = 365;
|
export const rightSidebarWidth = 365;
|
||||||
export const leftSidebarWidth = 365;
|
export const leftSidebarWidth = 365;
|
||||||
|
|||||||
Reference in New Issue
Block a user