Skip to content

Commit

Permalink
Merge pull request #1863: Tree component refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
victorlin authored Oct 21, 2024
2 parents 556d4fa + 4658803 commit 887624a
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 63 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { connect } from "react-redux";
import UnconnectedTree from "./tree";
import { RootState } from "../../store";

const Tree = connect((state) => ({
const Tree = connect((state: RootState) => ({
tree: state.tree,
treeToo: state.treeToo,
selectedNode: state.controls.selectedNode,
Expand Down
44 changes: 23 additions & 21 deletions src/components/tree/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,24 @@ class Tree extends React.Component {
tree: null,
treeToo: null
};

/* bind callbacks */
this.clearSelectedNode = callbacks.clearSelectedNode.bind(this);
// this.handleIconClickHOF = callbacks.handleIconClickHOF.bind(this);
this.redrawTree = () => {
this.props.dispatch(updateVisibleTipsAndBranchThicknesses({
root: [0, 0]
}));
};
/* pressing the escape key should dismiss an info modal (if one exists) */
this.handlekeydownEvent = (event) => {
if (event.key==="Escape" && this.props.selectedNode) {
this.clearSelectedNode(this.props.selectedNode);
}
};
}

redrawTree = () => {
this.props.dispatch(updateVisibleTipsAndBranchThicknesses({
root: [0, 0]
}));
}

/* pressing the escape key should dismiss an info modal (if one exists) */
handlekeydownEvent = (event) => {
if (event.key==="Escape" && this.props.selectedNode) {
this.clearSelectedNode(this.props.selectedNode);
}
}

setUpAndRenderTreeToo(props, newState) {
/* this.setState(newState) will be run sometime after this returns */
/* modifies newState in place */
Expand All @@ -59,6 +62,7 @@ class Tree extends React.Component {
}
renderTree(this, false, newState.treeToo, props);
}

componentDidMount() {
document.addEventListener('keyup', this.handlekeydownEvent);
if (this.props.tree.loaded) {
Expand All @@ -72,6 +76,7 @@ class Tree extends React.Component {
this.setState(newState); /* this will trigger an unnecessary CDU :( */
}
}

componentDidUpdate(prevProps) {
let newState = {};
let rightTreeUpdated = false;
Expand Down Expand Up @@ -110,16 +115,13 @@ class Tree extends React.Component {
}

getStyles = () => {
const activeResetTreeButton = this.props.tree.idxOfInViewRootNode !== 0 ||
this.props.treeToo.idxOfInViewRootNode !== 0;

const filteredTree = !!this.props.tree.idxOfFilteredRoot &&
this.props.tree.idxOfInViewRootNode !== this.props.tree.idxOfFilteredRoot;
const filteredTreeToo = !!this.props.treeToo.idxOfFilteredRoot &&
this.props.treeToo.idxOfInViewRootNode !== this.props.treeToo.idxOfFilteredRoot;
const activeZoomButton = filteredTree || filteredTreeToo;

const treeIsZoomed = this.props.tree.idxOfInViewRootNode !== 0 ||
const anyTreeZoomed = this.props.tree.idxOfInViewRootNode !== 0 ||
this.props.treeToo.idxOfInViewRootNode !== 0;

return {
Expand All @@ -133,8 +135,8 @@ class Tree extends React.Component {
zIndex: 100,
display: "inline-block",
marginLeft: 4,
cursor: activeResetTreeButton ? "pointer" : "auto",
color: activeResetTreeButton ? darkGrey : lightGrey
cursor: anyTreeZoomed ? "pointer" : "auto",
color: anyTreeZoomed ? darkGrey : lightGrey
},
zoomToSelectedButton: {
zIndex: 100,
Expand All @@ -146,9 +148,9 @@ class Tree extends React.Component {
zoomOutButton: {
zIndex: 100,
display: "inline-block",
cursor: treeIsZoomed ? "pointer" : "auto",
color: treeIsZoomed ? darkGrey : lightGrey,
pointerEvents: treeIsZoomed ? "auto" : "none",
cursor: anyTreeZoomed ? "pointer" : "auto",
color: anyTreeZoomed ? darkGrey : lightGrey,
pointerEvents: anyTreeZoomed ? "auto" : "none",
marginRight: "4px"
}
};
Expand Down
19 changes: 18 additions & 1 deletion src/reducers/controls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,24 @@ import { calcBrowserDimensionsInitialState } from "./browserDimensions";
import { doesColorByHaveConfidence } from "../actions/recomputeReduxState";
import { hasMultipleGridPanels } from "../actions/panelDisplay";

type Layout = "rect" | "radial" | "unrooted" | "clock" | "scatter"

interface Defaults {
distanceMeasure: string
layout: Layout
geoResolution: string
filters: Record<string, any>
filtersInFooter: string[]
colorBy: string
selectedBranchLabel: string
tipLabelKey: typeof strainSymbol
showTransmissionLines: boolean
sidebarOpen?: boolean
}

export interface BasicControlsState {
defaults: Defaults
layout: Layout
panelsAvailable: string[]
panelsToDisplay: string[]
showTreeToo: boolean
Expand Down Expand Up @@ -40,7 +57,7 @@ export interface ControlsState extends BasicControlsState, MeasurementsControlSt
at any time, e.g. if we want to revert things (e.g. on dataset change)
*/
export const getDefaultControlsState = () => {
const defaults: Partial<ControlsState> = {
const defaults: Defaults = {
distanceMeasure: defaultDistanceMeasure,
layout: defaultLayout,
geoResolution: defaultGeoResolution,
Expand Down
30 changes: 0 additions & 30 deletions src/reducers/index.js

This file was deleted.

45 changes: 45 additions & 0 deletions src/reducers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { combineReducers } from "redux";
import metadata from "./metadata";
import tree from "./tree";
import frequencies from "./frequencies";
import entropy from "./entropy";
import controls, { ControlsState } from "./controls";
import browserDimensions from "./browserDimensions";
import notifications from "./notifications";
import narrative, { NarrativeState } from "./narrative";
import treeToo from "./treeToo";
import general from "./general";
import jsonCache from "./jsonCache";
import measurements from "./measurements";

interface RootState {
metadata: ReturnType<typeof metadata>
tree: ReturnType<typeof tree>
frequencies: ReturnType<typeof frequencies>
controls: ControlsState
entropy: ReturnType<typeof entropy>
browserDimensions: ReturnType<typeof browserDimensions>
notifications: ReturnType<typeof notifications>
narrative: NarrativeState
treeToo: ReturnType<typeof treeToo>
general: ReturnType<typeof general>
jsonCache: ReturnType<typeof jsonCache>
measurements: ReturnType<typeof measurements>
}

const rootReducer = combineReducers<RootState>({
metadata,
tree,
frequencies,
controls,
entropy,
browserDimensions,
notifications,
narrative,
treeToo,
general,
jsonCache,
measurements
});

export default rootReducer;
54 changes: 44 additions & 10 deletions src/reducers/narrative.js → src/reducers/narrative.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,47 @@
import * as types from "../actions/types";
import { AnyAction } from 'redux';

const narrative = (state = {
export interface NarrativeState {
loaded: boolean
/**
* array of paragraphs (aka blocks)
*/
blocks: { __html: string }[] | null

/**
* which block is currently "in view"
*/
blockIdx?: number

/**
* the pathname of the _narrative_
*/
pathname?: string

display: boolean
title?: string
}

const defaultState: NarrativeState = {
loaded: false,
blocks: null, /* array of paragraphs (aka blocks) */
blockIdx: undefined, /* which block is currently "in view" */
pathname: undefined, /* the pathname of the _narrative_ */
blocks: null,
blockIdx: undefined,
pathname: undefined,
display: false,
title: undefined
}, action) => {
};

const narrative = (
state: NarrativeState = defaultState,
action: AnyAction,
): NarrativeState => {
switch (action.type) {
case types.DATA_INVALID:
return Object.assign({}, state, {
return {
...state,
loaded: false,
display: false
});
display: false,
};
case types.CLEAN_START:
if (action.narrative) {
const blocks = action.narrative;
Expand All @@ -29,12 +57,18 @@ const narrative = (state = {
return state;
case types.URL_QUERY_CHANGE_WITH_COMPUTED_STATE:
if (Object.prototype.hasOwnProperty.call(action.query, "n")) {
return Object.assign({}, state, {blockIdx: action.query.n});
return {
...state,
blockIdx: action.query.n,
};
}
return state;
case types.TOGGLE_NARRATIVE:
if (state.loaded) {
return Object.assign({}, state, {display: action.narrativeOn});
return {
...state,
display: action.narrativeOn,
};
}
console.warn("Attempted to toggle narrative that was not loaded");
return state;
Expand Down
1 change: 1 addition & 0 deletions src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ if (process.env.NODE_ENV !== 'production' && module.hot) {
}

// Infer types from the store.
// This is more clearly defined in src/reducers/index.ts but exported here.
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch

Expand Down

0 comments on commit 887624a

Please sign in to comment.