mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-09-25 03:18:11 +08:00
Merge branch 'master' into colinmegill/geneset-prototype
This commit is contained in:
@@ -18,7 +18,7 @@ import {
|
||||
saveInProgress: state.autosave?.saveInProgress ?? false,
|
||||
lastSavedObsAnnotations: state.autosave?.lastSavedObsAnnotations,
|
||||
error: state.autosave?.error,
|
||||
writableCategoriesEnabled: state.config?.parameters?.["annotations"] ?? false,
|
||||
writableCategoriesEnabled: state.config?.parameters?.annotations ?? false,
|
||||
}))
|
||||
class FilenameDialog extends React.Component {
|
||||
constructor(props) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import * as globals from "../../globals";
|
||||
import actions from "../../actions";
|
||||
import FilenameDialog from "./filenameDialog";
|
||||
|
||||
@@ -11,7 +10,7 @@ import FilenameDialog from "./filenameDialog";
|
||||
saveInProgress: state.autosave?.saveInProgress ?? false,
|
||||
lastSavedObsAnnotations: state.autosave?.lastSavedObsAnnotations,
|
||||
error: state.autosave?.error,
|
||||
writableCategoriesEnabled: state.config?.parameters?.["annotations"] ?? false,
|
||||
writableCategoriesEnabled: state.config?.parameters?.annotations ?? false,
|
||||
initialDataLoadComplete: state.autosave?.initialDataLoadComplete,
|
||||
}))
|
||||
class Autosave extends React.Component {
|
||||
|
||||
@@ -64,7 +64,7 @@ class Category extends React.Component {
|
||||
} else if (categoryCount.selectedCatCount < categoryCount.totalCatCount) {
|
||||
/* to be explicit... */
|
||||
this.checkbox.indeterminate = true;
|
||||
this.setState({ isChecked: false });
|
||||
this.setState({ isChecked: false }); // eslint-disable-line react/no-did-update-set-state
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,14 +77,15 @@ class Category extends React.Component {
|
||||
});
|
||||
};
|
||||
|
||||
toggleAll() {
|
||||
const { dispatch, metadataField } = this.props;
|
||||
dispatch({
|
||||
type: "categorical metadata filter all of these",
|
||||
metadataField,
|
||||
});
|
||||
this.setState({ isChecked: true });
|
||||
}
|
||||
handleCategoryClick = () => {
|
||||
const { annotations, metadataField, onExpansionChange } = this.props;
|
||||
const editingCategory =
|
||||
annotations.isEditingCategoryName &&
|
||||
annotations.categoryBeingEdited === metadataField;
|
||||
if (!editingCategory) {
|
||||
onExpansionChange(metadataField);
|
||||
}
|
||||
};
|
||||
|
||||
toggleNone() {
|
||||
const { dispatch, metadataField } = this.props;
|
||||
@@ -95,6 +96,15 @@ class Category extends React.Component {
|
||||
this.setState({ isChecked: false });
|
||||
}
|
||||
|
||||
toggleAll() {
|
||||
const { dispatch, metadataField } = this.props;
|
||||
dispatch({
|
||||
type: "categorical metadata filter all of these",
|
||||
metadataField,
|
||||
});
|
||||
this.setState({ isChecked: true });
|
||||
}
|
||||
|
||||
handleToggleAllClick() {
|
||||
const { isChecked } = this.state;
|
||||
// || this.checkbox.indeterminate === false
|
||||
@@ -115,6 +125,8 @@ class Category extends React.Component {
|
||||
globals.categoryDisplayStringMaxLength
|
||||
);
|
||||
|
||||
const checkboxID = `category-select-${metadataField}`;
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
@@ -135,8 +147,8 @@ class Category extends React.Component {
|
||||
alignItems: "flex-start",
|
||||
}}
|
||||
>
|
||||
<label className="bp3-control bp3-checkbox">
|
||||
<input disabled checked type="checkbox" />
|
||||
<label htmlFor={checkboxID} className="bp3-control bp3-checkbox">
|
||||
<input disabled id={checkboxID} checked type="checkbox" />
|
||||
<span className="bp3-control-indicator" />
|
||||
</label>
|
||||
<Tooltip
|
||||
@@ -174,9 +186,7 @@ class Category extends React.Component {
|
||||
metadataField,
|
||||
categoricalSelection,
|
||||
isColorAccessor,
|
||||
annotations,
|
||||
isExpanded,
|
||||
onExpansionChange,
|
||||
schema,
|
||||
} = this.props;
|
||||
|
||||
@@ -185,6 +195,8 @@ class Category extends React.Component {
|
||||
return this.renderIsStillLoading();
|
||||
}
|
||||
|
||||
const checkboxID = `category-select-${metadataField}`;
|
||||
|
||||
const isUserAnno =
|
||||
schema?.annotations?.obsByName[metadataField]?.writable ?? false;
|
||||
const isTruncated = _.get(
|
||||
@@ -225,8 +237,9 @@ class Category extends React.Component {
|
||||
alignItems: "flex-start",
|
||||
}}
|
||||
>
|
||||
<label className="bp3-control bp3-checkbox">
|
||||
<label className="bp3-control bp3-checkbox" htmlFor={checkboxID}>
|
||||
<input
|
||||
id={checkboxID}
|
||||
data-testclass="category-select"
|
||||
data-testid={`${metadataField}:category-select`}
|
||||
onChange={this.handleToggleAllClick.bind(this)}
|
||||
@@ -251,19 +264,19 @@ class Category extends React.Component {
|
||||
}}
|
||||
>
|
||||
<span
|
||||
role="menuitem"
|
||||
tabIndex="0"
|
||||
data-testid={`${metadataField}:category-expand`}
|
||||
onKeyPress={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
this.handleCategoryClick();
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
display: "inline-block",
|
||||
}}
|
||||
onClick={() => {
|
||||
const editingCategory =
|
||||
annotations.isEditingCategoryName &&
|
||||
annotations.categoryBeingEdited === metadataField;
|
||||
if (!editingCategory) {
|
||||
onExpansionChange(metadataField);
|
||||
}
|
||||
}}
|
||||
onClick={this.handleCategoryClick}
|
||||
>
|
||||
{isUserAnno ? (
|
||||
<Icon style={{ marginRight: 5 }} icon="tag" iconSize={16} />
|
||||
|
||||
@@ -11,7 +11,7 @@ import LabelInput from "./labelInput";
|
||||
import { labelPrompt } from "./labelUtil";
|
||||
|
||||
@connect((state) => ({
|
||||
writableCategoriesEnabled: state.config?.parameters?.["annotations"] ?? false,
|
||||
writableCategoriesEnabled: state.config?.parameters?.annotations ?? false,
|
||||
schema: state.world?.schema,
|
||||
config: state.config,
|
||||
ontology: state.ontology,
|
||||
|
||||
@@ -23,6 +23,9 @@ export default class LabelInput extends React.PureComponent {
|
||||
* popoverProps -- will be passed to <Suggest>
|
||||
*/
|
||||
|
||||
/* maxinum number of suggestions */
|
||||
static QueryResultLimit = 100;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -103,9 +106,6 @@ export default class LabelInput extends React.PureComponent {
|
||||
);
|
||||
};
|
||||
|
||||
/* maxinum number of suggestions */
|
||||
static QueryResultLimit = 100;
|
||||
|
||||
filterLabels(query) {
|
||||
const { labelSuggestions } = this.props;
|
||||
if (!labelSuggestions) return [];
|
||||
|
||||
@@ -62,6 +62,8 @@ class CategoryValue extends React.Component {
|
||||
prevProps.metadataField !== metadataField ||
|
||||
prevProps.categoryIndex !== categoryIndex
|
||||
) {
|
||||
// adequately checked to prevent looping
|
||||
// eslint-disable-next-line react/no-did-update-set-state
|
||||
this.setState({
|
||||
editedLabelText: this.currentLabel(),
|
||||
});
|
||||
@@ -327,6 +329,8 @@ class CategoryValue extends React.Component {
|
||||
annotations.isEditingLabelName &&
|
||||
annotations.labelEditable.label === categoryIndex;
|
||||
|
||||
const valueToggleLabel = `value-toggle-checkbox-${displayString}`;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={i}
|
||||
@@ -360,8 +364,13 @@ class CategoryValue extends React.Component {
|
||||
}}
|
||||
>
|
||||
<div style={{ display: "flex", alignItems: "baseline" }}>
|
||||
<label className="bp3-control bp3-checkbox" style={{ margin: 0 }}>
|
||||
<label
|
||||
htmlFor={valueToggleLabel}
|
||||
className="bp3-control bp3-checkbox"
|
||||
style={{ margin: 0 }}
|
||||
>
|
||||
<input
|
||||
id={valueToggleLabel}
|
||||
onChange={selected ? this.toggleOff : this.toggleOn}
|
||||
data-testclass="categorical-value-select"
|
||||
data-testid={`categorical-value-select-${metadataField}-${displayString}`}
|
||||
|
||||
@@ -7,11 +7,6 @@ const ToastTopCenter = Toaster.create({
|
||||
position: Position.TOP,
|
||||
});
|
||||
|
||||
const ToastBottomCenter = Toaster.create({
|
||||
className: "recipe-toaster",
|
||||
position: Position.BOTTOM,
|
||||
});
|
||||
|
||||
/*
|
||||
A "user" error - eg, bad input
|
||||
*/
|
||||
|
||||
@@ -262,7 +262,7 @@ class AddGenes extends React.Component {
|
||||
}}
|
||||
itemListPredicate={filterGenes}
|
||||
onActiveItemChange={(item) => this.setState({ activeItem: item })}
|
||||
itemRenderer={renderGene.bind(this)}
|
||||
itemRenderer={renderGene}
|
||||
items={varIndex || ["No genes"]}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
|
||||
@@ -10,7 +10,6 @@ import setupSVGandBrushElements from "./setupSVGandBrush";
|
||||
import _camera from "../../util/camera";
|
||||
import _drawPoints from "./drawPointsRegl";
|
||||
import { isTypedArray } from "../../util/typeHelpers";
|
||||
import styles from "./graph.css";
|
||||
|
||||
import GraphOverlayLayer from "./overlays/graphOverlayLayer";
|
||||
import CentroidLabels from "./overlays/centroidLabels";
|
||||
@@ -190,7 +189,7 @@ class Graph extends React.Component {
|
||||
tool: null,
|
||||
container: null,
|
||||
cameraRender: 0,
|
||||
viewport
|
||||
viewport,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -209,7 +208,10 @@ class Graph extends React.Component {
|
||||
|
||||
// create all default rendering transformations
|
||||
const modelTF = createModelTF();
|
||||
const projectionTF = createProjectionTF(this.reglCanvas.width, this.reglCanvas.height);
|
||||
const projectionTF = createProjectionTF(
|
||||
this.reglCanvas.width,
|
||||
this.reglCanvas.height
|
||||
);
|
||||
|
||||
// initial draw to canvas
|
||||
this.renderPoints(
|
||||
@@ -250,7 +252,8 @@ class Graph extends React.Component {
|
||||
} = this.props;
|
||||
const { regl, toolSVG, camera, modelTF, viewport } = this.state;
|
||||
let { projectionTF } = this.state;
|
||||
const hasResized = prevState.viewport.height !== this.reglCanvas.height ||
|
||||
const hasResized =
|
||||
prevState.viewport.height !== this.reglCanvas.height ||
|
||||
prevState.viewport.width !== this.reglCanvas.width;
|
||||
let stateChanges = {};
|
||||
let needsRepaint = hasResized;
|
||||
@@ -261,7 +264,10 @@ class Graph extends React.Component {
|
||||
const { drawPoints, pointBuffer, colorBuffer, flagBuffer } = this.state;
|
||||
|
||||
if (hasResized) {
|
||||
projectionTF = createProjectionTF(this.reglCanvas.width, this.reglCanvas.height);
|
||||
projectionTF = createProjectionTF(
|
||||
this.reglCanvas.width,
|
||||
this.reglCanvas.height
|
||||
);
|
||||
stateChanges = {
|
||||
...stateChanges,
|
||||
projectionTF,
|
||||
@@ -322,7 +328,10 @@ class Graph extends React.Component {
|
||||
...stateChanges,
|
||||
...this.createToolSVG(),
|
||||
};
|
||||
} else if ((viewport.height && viewport.width && !toolSVG) || selectionTool !== prevProps.selectionTool ) {
|
||||
} else if (
|
||||
(viewport.height && viewport.width && !toolSVG) ||
|
||||
selectionTool !== prevProps.selectionTool
|
||||
) {
|
||||
// first time or change of selection tool
|
||||
stateChanges = { ...stateChanges, ...this.createToolSVG() };
|
||||
} else if (prevProps.graphInteractionMode !== graphInteractionMode) {
|
||||
@@ -349,6 +358,7 @@ class Graph extends React.Component {
|
||||
);
|
||||
}
|
||||
if (Object.keys(stateChanges).length > 0) {
|
||||
// Preventing update loop via stateChanges and diff checks
|
||||
// eslint-disable-next-line react/no-did-update-set-state
|
||||
this.setState(stateChanges);
|
||||
}
|
||||
@@ -371,12 +381,11 @@ class Graph extends React.Component {
|
||||
const { viewportRef } = this.props;
|
||||
return {
|
||||
height: viewportRef.clientHeight,
|
||||
width: viewportRef.clientWidth
|
||||
width: viewportRef.clientWidth,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
handleCanvasEvent = e => {
|
||||
handleCanvasEvent = (e) => {
|
||||
const { camera, projectionTF } = this.state;
|
||||
if (e.type !== "wheel") e.preventDefault();
|
||||
if (camera.handleEvent(e, projectionTF)) {
|
||||
@@ -547,12 +556,8 @@ class Graph extends React.Component {
|
||||
vec2.transformMat3(xy, xy, projectionTF);
|
||||
|
||||
return [
|
||||
Math.round(
|
||||
(xy[0] + 1) * viewport.width / 2
|
||||
),
|
||||
Math.round(
|
||||
-((xy[1] + 1) / 2 - 1) * viewport.height
|
||||
)
|
||||
Math.round(((xy[0] + 1) * viewport.width) / 2),
|
||||
Math.round(-((xy[1] + 1) / 2 - 1) * viewport.height),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -698,7 +703,7 @@ class Graph extends React.Component {
|
||||
count: this.count,
|
||||
projView,
|
||||
nPoints: universe.nObs,
|
||||
minViewportDimension: Math.min(width, height)
|
||||
minViewportDimension: Math.min(width, height),
|
||||
});
|
||||
regl._gl.flush();
|
||||
}
|
||||
@@ -744,9 +749,11 @@ class Graph extends React.Component {
|
||||
cameraTF={cameraTF}
|
||||
modelTF={modelTF}
|
||||
projectionTF={projectionTF}
|
||||
handleCanvasEvent={graphInteractionMode === "zoom" ? this.handleCanvasEvent : undefined}
|
||||
handleCanvasEvent={
|
||||
graphInteractionMode === "zoom" ? this.handleCanvasEvent : undefined
|
||||
}
|
||||
>
|
||||
<CentroidLabels/>
|
||||
<CentroidLabels />
|
||||
</GraphOverlayLayer>
|
||||
<svg
|
||||
id="lasso-layer"
|
||||
@@ -756,13 +763,11 @@ class Graph extends React.Component {
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
zIndex: 1
|
||||
zIndex: 1,
|
||||
}}
|
||||
width={viewport.width}
|
||||
height={viewport.height}
|
||||
pointerEvents={
|
||||
graphInteractionMode === "select" ? "auto" : "none"
|
||||
}
|
||||
pointerEvents={graphInteractionMode === "select" ? "auto" : "none"}
|
||||
/>
|
||||
<canvas
|
||||
width={viewport.width}
|
||||
@@ -777,7 +782,9 @@ class Graph extends React.Component {
|
||||
}}
|
||||
className="graph-canvas"
|
||||
data-testid="layout-graph"
|
||||
ref={canvas => { this.reglCanvas = canvas; }}
|
||||
ref={(canvas) => {
|
||||
this.reglCanvas = canvas;
|
||||
}}
|
||||
onMouseDown={this.handleCanvasEvent}
|
||||
onMouseUp={this.handleCanvasEvent}
|
||||
onMouseMove={this.handleCanvasEvent}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable max-classes-per-file */
|
||||
/* eslint-disable jsx-a11y/mouse-events-have-key-events */
|
||||
import React, { PureComponent } from "react";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
@@ -26,7 +24,7 @@ class CentroidLabels extends PureComponent {
|
||||
// Notify overlay layer of display change
|
||||
overlayToggled("centroidLabels", displayChangeOn);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
@@ -59,6 +57,7 @@ class CentroidLabels extends PureComponent {
|
||||
|
||||
labelSVGS.push(
|
||||
<g
|
||||
// label is unique so disabling eslint rule
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
key={label}
|
||||
className="centroid-label"
|
||||
@@ -66,6 +65,8 @@ class CentroidLabels extends PureComponent {
|
||||
data-testclass="centroid-label"
|
||||
data-testid={`${label}-centroid-label`}
|
||||
>
|
||||
{/* The mouse actions for centroid labels do not have a screen reader alternative */}
|
||||
{/* eslint-disable-next-line jsx-a11y/mouse-events-have-key-events */}
|
||||
<text
|
||||
transform={inverseTransform}
|
||||
textAnchor="middle"
|
||||
|
||||
@@ -2,8 +2,7 @@ import React, { PureComponent, cloneElement } from "react";
|
||||
|
||||
import styles from "../graph.css";
|
||||
|
||||
export default
|
||||
class GraphOverlayLayer extends PureComponent {
|
||||
export default class GraphOverlayLayer extends PureComponent {
|
||||
/*
|
||||
This component takes its children (assumed in the data coordinate space ([0, 1] range, origin in bottom left corner))
|
||||
and transforms itself multiple times resulting in screen space ([0, screenWidth/Height] range, origin in top left corner)
|
||||
@@ -49,7 +48,6 @@ class GraphOverlayLayer extends PureComponent {
|
||||
handleCanvasEvent,
|
||||
width,
|
||||
height,
|
||||
style
|
||||
} = this.props;
|
||||
const { display } = this.state;
|
||||
|
||||
@@ -63,7 +61,7 @@ class GraphOverlayLayer extends PureComponent {
|
||||
cameraTF
|
||||
)} ${this.reverseMatrixScaleTransformString(
|
||||
projectionTF
|
||||
)} scale(1 2) scale(1 ${1 / (-height)}) scale(2 1) scale(${1 / width} 1)`;
|
||||
)} scale(1 2) scale(1 ${1 / -height}) scale(2 1) scale(${1 / width} 1)`;
|
||||
|
||||
// Copy the children passed with the overlay and add the inverse transform and onDisplayChange props
|
||||
const newChildren = React.Children.map(children, (child) =>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { connect } from "react-redux";
|
||||
import * as globals from "../../globals";
|
||||
import Logo from "../framework/logo";
|
||||
|
||||
@connect(state => ({
|
||||
@connect((state) => ({
|
||||
datasetTitle: state.config?.displayNames?.dataset ?? "",
|
||||
aboutURL: state.config?.links?.["about-dataset"],
|
||||
scatterplotXXaccessor: state.controls.scatterplotXXaccessor,
|
||||
@@ -75,7 +75,7 @@ class LeftSideBar extends React.Component {
|
||||
title={datasetTitle}
|
||||
>
|
||||
{aboutURL ? (
|
||||
<a href={aboutURL} target="_blank">
|
||||
<a href={aboutURL} target="_blank" rel="noopener noreferrer">
|
||||
{displayTitle}
|
||||
</a>
|
||||
) : (
|
||||
|
||||
@@ -45,7 +45,9 @@ class CellSetButton extends React.PureComponent {
|
||||
<AnchorButton
|
||||
type="button"
|
||||
disabled={differential.diffExp}
|
||||
onClick={this.set.bind(this)}
|
||||
onClick={() => {
|
||||
this.set();
|
||||
}}
|
||||
data-testid={`cellset-button-${eitherCellSetOneOrTwo}`}
|
||||
>
|
||||
{eitherCellSetOneOrTwo}
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
// jshint esversion: 6
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import {
|
||||
Popover,
|
||||
Button,
|
||||
ButtonGroup,
|
||||
AnchorButton,
|
||||
Tooltip,
|
||||
Position,
|
||||
} from "@blueprintjs/core";
|
||||
import { Button, ButtonGroup, AnchorButton, Tooltip } from "@blueprintjs/core";
|
||||
import * as globals from "../../globals";
|
||||
import styles from "./menubar.css";
|
||||
import actions from "../../actions";
|
||||
@@ -21,16 +14,9 @@ import CellSetButton from "./cellSetButtons";
|
||||
celllist1: state.differential?.celllist1,
|
||||
celllist2: state.differential?.celllist2,
|
||||
diffexpMayBeSlow: state.config?.parameters?.["diffexp-may-be-slow"] ?? false,
|
||||
diffexpCellcountMax: state.config?.limits?.diffexp_cellcount_max,
|
||||
diffexpCellcountMax: state.config?.limits?.["diffexp_cellcount_max"],
|
||||
}))
|
||||
class DiffexpButtons extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
userDismissedPopover: false,
|
||||
};
|
||||
}
|
||||
|
||||
computeDiffExp = () => {
|
||||
const { dispatch, differential } = this.props;
|
||||
if (differential.celllist1 && differential.celllist2) {
|
||||
@@ -54,16 +40,9 @@ class DiffexpButtons extends React.Component {
|
||||
});
|
||||
};
|
||||
|
||||
handlePopoverDismiss = () => {
|
||||
this.setState({
|
||||
userDismissedPopover: true,
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
/* diffexp-related buttons may be disabled */
|
||||
const { differential, diffexpMayBeSlow, diffexpCellcountMax } = this.props;
|
||||
const { userDismissedPopover } = this.state;
|
||||
|
||||
const haveBothCellSets =
|
||||
!!differential.celllist1 && !!differential.celllist2;
|
||||
@@ -86,7 +65,7 @@ class DiffexpButtons extends React.Component {
|
||||
diffexpCellcountMax;
|
||||
|
||||
return (
|
||||
<ButtonGroup className={styles.menubarButton} >
|
||||
<ButtonGroup className={styles.menubarButton}>
|
||||
<CellSetButton
|
||||
{...this.props} // eslint-disable-line react/jsx-props-no-spreading
|
||||
eitherCellSetOneOrTwo={1}
|
||||
|
||||
@@ -69,9 +69,7 @@ class Embedding extends React.PureComponent {
|
||||
const { layoutChoice } = this.props;
|
||||
|
||||
return (
|
||||
<ButtonGroup
|
||||
className={styles.menubarButton}
|
||||
>
|
||||
<ButtonGroup className={styles.menubarButton}>
|
||||
<Popover
|
||||
target={
|
||||
<Tooltip
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// jshint esversion: 6
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Button, ButtonGroup, AnchorButton, Tooltip } from "@blueprintjs/core";
|
||||
import { ButtonGroup, AnchorButton, Tooltip } from "@blueprintjs/core";
|
||||
|
||||
import * as globals from "../../globals";
|
||||
import styles from "./menubar.css";
|
||||
@@ -28,7 +28,7 @@ import DiffexpButtons from "./diffexpButtons";
|
||||
scatterplotYYaccessor: state.controls.scatterplotYYaccessor,
|
||||
celllist1: state.differential.celllist1,
|
||||
celllist2: state.differential.celllist2,
|
||||
libraryVersions: state.config?.library_versions, // eslint-disable-line camelcase
|
||||
libraryVersions: state.config?.["library_versions"],
|
||||
undoDisabled: state["@@undoable/past"].length === 0,
|
||||
redoDisabled: state["@@undoable/future"].length === 0,
|
||||
aboutLink: state.config?.links?.["about-dataset"],
|
||||
@@ -207,9 +207,10 @@ class MenuBar extends React.Component {
|
||||
const { pendingClipPercentiles } = this.state;
|
||||
|
||||
// constants used to create selection tool button
|
||||
const [selectionTooltip, selectionButtonIcon] = selectionTool === "brush"
|
||||
? ["Brush selection", "Lasso selection"]
|
||||
: ["select", "polygon-filter"];
|
||||
const [selectionTooltip, selectionButtonIcon] =
|
||||
selectionTool === "brush"
|
||||
? ["Brush selection", "Lasso selection"]
|
||||
: ["select", "polygon-filter"];
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -245,80 +246,82 @@ class MenuBar extends React.Component {
|
||||
handleClipCommit={this.handleClipCommit}
|
||||
isClipDisabled={this.isClipDisabled}
|
||||
handleClipOnKeyPress={this.handleClipOnKeyPress}
|
||||
handleClipPercentileMaxValueChange={this.handleClipPercentileMaxValueChange}
|
||||
handleClipPercentileMinValueChange={this.handleClipPercentileMinValueChange}
|
||||
handleClipPercentileMaxValueChange={
|
||||
this.handleClipPercentileMaxValueChange
|
||||
}
|
||||
handleClipPercentileMinValueChange={
|
||||
this.handleClipPercentileMinValueChange
|
||||
}
|
||||
/>
|
||||
<Embedding />
|
||||
<Tooltip
|
||||
content="When a category is colored by, show labels on the graph"
|
||||
position="bottom"
|
||||
disabled={graphInteractionMode === "zoom"}
|
||||
>
|
||||
<AnchorButton
|
||||
className={styles.menubarButton}
|
||||
type="button"
|
||||
data-testid="centroid-label-toggle"
|
||||
icon="property"
|
||||
onClick={this.handleCentroidChange}
|
||||
active={showCentroidLabels}
|
||||
intent={showCentroidLabels ? "primary" : "none"}
|
||||
/>
|
||||
</Tooltip>
|
||||
<ButtonGroup
|
||||
className={styles.menubarButton}
|
||||
>
|
||||
<Tooltip
|
||||
content={selectionTooltip}
|
||||
position="bottom"
|
||||
hoverOpenDelay={globals.tooltipHoverOpenDelay}
|
||||
>
|
||||
<AnchorButton
|
||||
type="button"
|
||||
data-testid="mode-lasso"
|
||||
icon={selectionButtonIcon}
|
||||
active={graphInteractionMode === "select"}
|
||||
onClick={() => {
|
||||
dispatch({
|
||||
type: "change graph interaction mode",
|
||||
data: "select"
|
||||
});
|
||||
<Embedding />
|
||||
<Tooltip
|
||||
content="When a category is colored by, show labels on the graph"
|
||||
position="bottom"
|
||||
disabled={graphInteractionMode === "zoom"}
|
||||
>
|
||||
<AnchorButton
|
||||
className={styles.menubarButton}
|
||||
type="button"
|
||||
data-testid="centroid-label-toggle"
|
||||
icon="property"
|
||||
onClick={this.handleCentroidChange}
|
||||
active={showCentroidLabels}
|
||||
intent={showCentroidLabels ? "primary" : "none"}
|
||||
/>
|
||||
</Tooltip>
|
||||
<ButtonGroup className={styles.menubarButton}>
|
||||
<Tooltip
|
||||
content={selectionTooltip}
|
||||
position="bottom"
|
||||
hoverOpenDelay={globals.tooltipHoverOpenDelay}
|
||||
>
|
||||
<AnchorButton
|
||||
type="button"
|
||||
data-testid="mode-lasso"
|
||||
icon={selectionButtonIcon}
|
||||
active={graphInteractionMode === "select"}
|
||||
onClick={() => {
|
||||
dispatch({
|
||||
type: "change graph interaction mode",
|
||||
data: "select",
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
content="Drag to pan, scroll to zoom"
|
||||
position="bottom"
|
||||
hoverOpenDelay={globals.tooltipHoverOpenDelay}
|
||||
>
|
||||
<AnchorButton
|
||||
type="button"
|
||||
data-testid="mode-pan-zoom"
|
||||
icon="zoom-in"
|
||||
active={graphInteractionMode === "zoom"}
|
||||
onClick={() => {
|
||||
dispatch({
|
||||
type: "change graph interaction mode",
|
||||
data: "zoom",
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</ButtonGroup>
|
||||
<Subset
|
||||
subsetPossible={this.subsetPossible()}
|
||||
subsetResetPossible={this.subsetResetPossible()}
|
||||
handleSubset={() => {
|
||||
dispatch(actions.setWorldToSelection());
|
||||
dispatch({ type: "increment graph render counter" });
|
||||
}}
|
||||
handleSubsetReset={() => {
|
||||
dispatch(actions.resetWorldToUniverse());
|
||||
dispatch({ type: "increment graph render counter" });
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
content="Drag to pan, scroll to zoom"
|
||||
position="bottom"
|
||||
hoverOpenDelay={globals.tooltipHoverOpenDelay}
|
||||
>
|
||||
<AnchorButton
|
||||
type="button"
|
||||
data-testid="mode-pan-zoom"
|
||||
icon="zoom-in"
|
||||
active={graphInteractionMode === "zoom"}
|
||||
onClick={() => {
|
||||
dispatch({
|
||||
type: "change graph interaction mode",
|
||||
data: "zoom"
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</ButtonGroup>
|
||||
<Subset
|
||||
subsetPossible={this.subsetPossible()}
|
||||
subsetResetPossible={this.subsetResetPossible()}
|
||||
handleSubset={() => {
|
||||
dispatch(actions.setWorldToSelection());
|
||||
dispatch({ type: "increment graph render counter" });
|
||||
}}
|
||||
handleSubsetReset={() => {
|
||||
dispatch(actions.resetWorldToUniverse());
|
||||
dispatch({ type: "increment graph render counter" });
|
||||
}}
|
||||
/>
|
||||
{disableDiffexp ? null : <DiffexpButtons/>}
|
||||
</div>
|
||||
);
|
||||
{disableDiffexp ? null : <DiffexpButtons />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ class Scatterplot extends React.PureComponent {
|
||||
viewport: {
|
||||
height: null,
|
||||
width: null,
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ class Scatterplot extends React.PureComponent {
|
||||
svg,
|
||||
drawPoints,
|
||||
projectionTF,
|
||||
viewport
|
||||
viewport,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -286,20 +286,11 @@ class Scatterplot extends React.PureComponent {
|
||||
return {
|
||||
viewport: {
|
||||
height: window.height,
|
||||
width: window.width
|
||||
}
|
||||
width: window.width,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
handleResize = () => {
|
||||
const { state } = this.state;
|
||||
const viewport = this.getViewportDimensions();
|
||||
this.setState({
|
||||
...state,
|
||||
viewport,
|
||||
});
|
||||
};
|
||||
|
||||
static setupScales(expressionX, expressionY) {
|
||||
const xScale = d3
|
||||
.scaleLinear()
|
||||
@@ -316,6 +307,15 @@ class Scatterplot extends React.PureComponent {
|
||||
};
|
||||
}
|
||||
|
||||
handleResize = () => {
|
||||
const { state } = this.state;
|
||||
const viewport = this.getViewportDimensions();
|
||||
this.setState({
|
||||
...state,
|
||||
viewport,
|
||||
});
|
||||
};
|
||||
|
||||
updateViewportDimensions = () => {
|
||||
this.setState(this.getViewportDimensions());
|
||||
};
|
||||
@@ -390,7 +390,7 @@ class Scatterplot extends React.PureComponent {
|
||||
minViewportDimension: Math.min(
|
||||
viewport.width - globals.leftSidebarWidth || width,
|
||||
viewport.height || height
|
||||
)
|
||||
),
|
||||
});
|
||||
regl._gl.flush();
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ import {
|
||||
Colors,
|
||||
Icon,
|
||||
} from "@blueprintjs/core";
|
||||
import * as globals from "../../globals";
|
||||
import { termsOfServiceToast } from "../framework/toasters";
|
||||
|
||||
const CookieDecision = "cxg.cookieDecision";
|
||||
|
||||
@@ -26,12 +24,14 @@ function storageGet(key, defaultValue = null) {
|
||||
function storageSet(key, value) {
|
||||
try {
|
||||
window.localStorage.setItem(key, value);
|
||||
} catch {}
|
||||
} catch {
|
||||
// continue
|
||||
}
|
||||
}
|
||||
|
||||
@connect((state) => ({
|
||||
tosURL: state.config?.parameters?.about_legal_tos,
|
||||
privacyURL: state.config?.parameters?.about_legal_privacy,
|
||||
tosURL: state.config?.parameters?.["about_legal_tos"],
|
||||
privacyURL: state.config?.parameters?.["about_legal_privacy"],
|
||||
}))
|
||||
class TermsPrompt extends React.PureComponent {
|
||||
constructor(props) {
|
||||
@@ -59,7 +59,9 @@ class TermsPrompt extends React.PureComponent {
|
||||
if (window.cookieDecisionCallback instanceof Function) {
|
||||
try {
|
||||
window.cookieDecisionCallback();
|
||||
} catch (e) {}
|
||||
} catch (e) {
|
||||
// continue
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -82,6 +84,7 @@ class TermsPrompt extends React.PureComponent {
|
||||
}}
|
||||
href={tosURL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
terms of service
|
||||
</a>
|
||||
@@ -103,6 +106,7 @@ class TermsPrompt extends React.PureComponent {
|
||||
}}
|
||||
href={privacyURL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
privacy policy
|
||||
</a>
|
||||
@@ -118,12 +122,10 @@ class TermsPrompt extends React.PureComponent {
|
||||
<Drawer
|
||||
onclose={this.drawerClose}
|
||||
isOpen={isOpen}
|
||||
size={"120px"}
|
||||
size="120px"
|
||||
position={Position.BOTTOM}
|
||||
canOutsideClickClose={false}
|
||||
hasBackdrop={
|
||||
true /* if the user can't use the app or click outside to dismiss, that should be visually represented with a backdrop */
|
||||
}
|
||||
hasBackdrop /* if the user can't use the app or click outside to dismiss, that should be visually represented with a backdrop */
|
||||
enforceFocus={false}
|
||||
autoFocus={false}
|
||||
portal={false}
|
||||
@@ -149,12 +151,9 @@ class TermsPrompt extends React.PureComponent {
|
||||
onClick={this.handleOK}
|
||||
data-testid="tos-cookies-accept"
|
||||
>
|
||||
I'm OK with cookies!
|
||||
I'm OK with cookies!
|
||||
</Button>{" "}
|
||||
<Button
|
||||
onClick={this.handleNo}
|
||||
data-testid="tos-cookies-reject"
|
||||
>
|
||||
<Button onClick={this.handleNo} data-testid="tos-cookies-reject">
|
||||
No thanks
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user