Skip to content

Commit

Permalink
Merge pull request #1262 from nextstrain/animation-controls
Browse files Browse the repository at this point in the history
Move animation controls to the sidebar
  • Loading branch information
jameshadfield authored Jan 11, 2021
2 parents 31815a0 + 9bb0671 commit cd30947
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 67 deletions.
69 changes: 69 additions & 0 deletions src/components/controls/animation-controls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React from "react";
import { connect } from "react-redux";
import { withTranslation } from "react-i18next";
import { FaUndo, FaPause, FaPlay } from "react-icons/fa";
import { changeDateFilter } from "../../actions/tree";
import { MAP_ANIMATION_PLAY_PAUSE_BUTTON } from "../../actions/types";
import Flex from "../framework/flex";
import { SidebarButton } from "./styles";

@connect((state) => {
return {
absoluteDateMin: state.controls.absoluteDateMin,
absoluteDateMax: state.controls.absoluteDateMax,
animationPlayPauseButton: state.controls.animationPlayPauseButton,
branchLengthsToDisplay: state.controls.branchLengthsToDisplay
};
})
class AnimationControls extends React.Component {

getPlayPauseButton() {
return (
<SidebarButton
onClick={() => {
this.props.animationPlayPauseButton === "Play" ?
this.props.dispatch({type: MAP_ANIMATION_PLAY_PAUSE_BUTTON, data: "Pause"}) :
this.props.dispatch({type: MAP_ANIMATION_PLAY_PAUSE_BUTTON, data: "Play"});
}}
>
<span>
{this.props.animationPlayPauseButton === "Play" ? <FaPlay color="#888"/> : <FaPause color="#888"/>}
{" " + this.props.t(this.props.animationPlayPauseButton === "Play" ? "Play" : "Pause")}
</span>
</SidebarButton>
);
}

getResetButton() {
return (
<SidebarButton
onClick={() => {
this.props.dispatch({type: MAP_ANIMATION_PLAY_PAUSE_BUTTON, data: "Play"});
this.props.dispatch(changeDateFilter({newMin: this.props.absoluteDateMin, newMax: this.props.absoluteDateMax, quickdraw: false}));
}}
>
<span>
{<FaUndo color="#888"/>}
{" " + this.props.t("Reset")}
</span>
</SidebarButton>
);
}

render() {
if (this.props.branchLengthsToDisplay === "divOnly") {
return null;
}
return (
<div style={{marginBottom: 0}}>
<Flex justifyContent="space-between">
{this.getPlayPauseButton()}
{this.getResetButton()}
</Flex>
</div>
);
}
}

const WithTranslation = withTranslation()(AnimationControls);
export default WithTranslation;
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { SidebarSubtitle, SidebarButton } from "./styles";
mapAnimationShouldLoop: state.controls.mapAnimationShouldLoop
};
})
class MapAnimationControls extends React.Component {
class AnimationOptions extends React.Component {
handleChangeAnimationTimeClicked(userSelectedDuration) {
return () => {
const loopRunning = window.NEXTSTRAIN && window.NEXTSTRAIN.animationTickReference;
Expand Down Expand Up @@ -98,5 +98,5 @@ class MapAnimationControls extends React.Component {
}
}

const WithTranslations = withTranslation()(MapAnimationControls);
const WithTranslations = withTranslation()(AnimationOptions);
export default WithTranslations;
17 changes: 11 additions & 6 deletions src/components/controls/controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next';

import ColorBy, {ColorByInfo} from "./color-by";
import DateRangeInputs, {DateRangeInfo} from "./date-range-inputs";
import AnimationControls from "./animation-controls";
import ChooseBranchLabelling from "./choose-branch-labelling";
import ChooseLayout from "./choose-layout";
import ChooseDataset from "./choose-dataset";
Expand All @@ -13,13 +14,13 @@ import PanelLayout from "./panel-layout";
import GeoResolution from "./geo-resolution";
import TransmissionLines from './transmission-lines';
import NormalizeFrequencies from "./frequency-normalization";
import MapAnimationControls from "./map-animation";
import AnimationOptions from "./animation-options";
import PanelToggles from "./panel-toggles";
import ToggleTangle from "./toggle-tangle";
import Language from "./language";
import { ControlsContainer } from "./styles";
import FilterData, {FilterInfo} from "./filter";
import {TreeOptionsInfo, MapOptionsInfo, PanelOptionsInfo, FrequencyInfo} from "./miscInfoText";
import {TreeOptionsInfo, MapOptionsInfo, AnimationOptionsInfo, PanelOptionsInfo, FrequencyInfo} from "./miscInfoText";
import { AnnotatedHeader } from "./annotatedHeader";

function Controls({mapOn, frequenciesOn, mobileDisplay}) {
Expand All @@ -31,14 +32,14 @@ function Controls({mapOn, frequenciesOn, mobileDisplay}) {

<AnnotatedHeader title={t("sidebar:Date Range")} tooltip={DateRangeInfo} mobile={mobileDisplay}/>
<DateRangeInputs />
<AnimationControls />

<AnnotatedHeader title={t("sidebar:Color By")} tooltip={ColorByInfo} mobile={mobileDisplay}/>
<ColorBy />

<AnnotatedHeader title={t("sidebar:Filter Data")} tooltip={FilterInfo} mobile={mobileDisplay}/>
<FilterData />


<AnnotatedHeader title={t("sidebar:Tree Options")} tooltip={TreeOptionsInfo} mobile={mobileDisplay}/>
<ChooseLayout />
<ChooseMetric />
Expand All @@ -48,21 +49,25 @@ function Controls({mapOn, frequenciesOn, mobileDisplay}) {
<ToggleTangle />

{mapOn ? (
<span style={{ marginTop: "15px" }}>
<span style={{ marginTop: "10px" }}>
<AnnotatedHeader title={t("sidebar:Map Options")} tooltip={MapOptionsInfo} mobile={mobileDisplay}/>
<GeoResolution />
<TransmissionLines />
<MapAnimationControls />
</span>
) : null}

{frequenciesOn ? (
<span style={{ marginTop: "15px" }}>
<span style={{ marginTop: "10px" }}>
<AnnotatedHeader title={t("sidebar:Frequency Options")} tooltip={FrequencyInfo} mobile={mobileDisplay}/>
<NormalizeFrequencies />
</span>
) : null}

<span style={{ marginTop: "10px" }}>
<AnnotatedHeader title={t("sidebar:Animation Options")} tooltip={AnimationOptionsInfo} mobile={mobileDisplay}/>
<AnimationOptions />
</span>

<span style={{ paddingTop: "10px" }} />
<AnnotatedHeader title={t("sidebar:Panel Options")} tooltip={PanelOptionsInfo} mobile={mobileDisplay}/>
<PanelLayout />
Expand Down
6 changes: 6 additions & 0 deletions src/components/controls/miscInfoText.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ export const MapOptionsInfo = (
</>
);

export const AnimationOptionsInfo = (
<>
Change various options relating to how the animation proceeds.
</>
);

export const PanelOptionsInfo = (
<>
Control which panels are being displayed and whether to show the tree and the map side-by-side (<em>grid</em>) or expanded (<em>full</em>).
Expand Down
2 changes: 1 addition & 1 deletion src/components/controls/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const HeaderIconContainer = styled.span`
export const SidebarButton = styled.button`
border: 0px;
background-color: inherit;
margin: 5px 0px 10px 5px;
margin: 5px 5px 10px 5px;
border-radius: 2px;
cursor: pointer;
padding: 2px;
Expand Down
59 changes: 1 addition & 58 deletions src/components/map/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,9 @@ import {
updateTransmissionDataLatLong,
updateDemeDataLatLong
} from "./mapHelpersLatLong";
import { changeDateFilter } from "../../actions/tree";
import { MAP_ANIMATION_PLAY_PAUSE_BUTTON } from "../../actions/types";
// import { incommingMapPNG } from "../download/helperFunctions";
import { timerStart, timerEnd } from "../../util/perf";
import { tabSingle, darkGrey, lightGrey, goColor, pauseColor } from "../../globalStyles";
import { tabSingle, darkGrey, lightGrey } from "../../globalStyles";
import ErrorBoundary from "../../util/errorBoundry";
import { getMapTilesSettings } from "../../util/globals";
import Legend from "../tree/legend/legend";
Expand All @@ -47,7 +45,6 @@ import "../../css/mapbox.css";
colorScaleVersion: state.controls.colorScale.version,
map: state.map,
geoResolution: state.controls.geoResolution,
animationPlayPauseButton: state.controls.animationPlayPauseButton,
mapTriplicate: state.controls.mapTriplicate,
dateMinNumeric: state.controls.dateMinNumeric,
dateMaxNumeric: state.controls.dateMaxNumeric,
Expand Down Expand Up @@ -84,8 +81,6 @@ class Map extends React.Component {
tilesSettings: getMapTilesSettings()
};
// https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md#es6-classes
this.playPauseButtonClicked = this.playPauseButtonClicked.bind(this);
this.resetButtonClicked = this.resetButtonClicked.bind(this);
this.fitMapBoundsToData = this.fitMapBoundsToData.bind(this);
}

Expand Down Expand Up @@ -525,47 +520,11 @@ class Map extends React.Component {
this.setState({map});
}

animationButtons() {
if (this.props.narrativeMode) return null;
const buttonBaseStyle = {
color: "#FFFFFF",
fontWeight: 400,
fontSize: 12,
borderRadius: 3,
padding: 12,
border: "none",
zIndex: 900,
position: "relative",
textTransform: "uppercase"
};
if (this.props.branchLengthsToDisplay !== "divOnly") {
return (
<div style={{position: "absolute"}}>
<button
style={{...buttonBaseStyle, top: 20, left: 20, backgroundColor: this.props.animationPlayPauseButton === "Pause" ? pauseColor : goColor}}
onClick={this.playPauseButtonClicked}
>
{this.props.t(this.props.animationPlayPauseButton)}
</button>
<button
style={{...buttonBaseStyle, top: 20, left: 30, backgroundColor: lightGrey}}
onClick={this.resetButtonClicked}
>
{this.props.t("Reset")}
</button>
</div>
);
}
/* else - divOnly */
return (<div/>);
}

maybeCreateMapDiv() {
let container = null;
if (this.state.responsive) {
container = (
<div style={{position: "relative"}}>
{this.animationButtons()}
<div
onClick={() => {this.setState({userHasInteractedWithMap: true});}}
id="map"
Expand All @@ -579,17 +538,6 @@ class Map extends React.Component {
}
return container;
}
playPauseButtonClicked() {
if (this.props.animationPlayPauseButton === "Play") {
this.props.dispatch({type: MAP_ANIMATION_PLAY_PAUSE_BUTTON, data: "Pause"});
} else {
this.props.dispatch({type: MAP_ANIMATION_PLAY_PAUSE_BUTTON, data: "Play"});
}
}
resetButtonClicked() {
this.props.dispatch({type: MAP_ANIMATION_PLAY_PAUSE_BUTTON, data: "Play"});
this.props.dispatch(changeDateFilter({newMin: this.props.absoluteDateMin, newMax: this.props.absoluteDateMax, quickdraw: false}));
}
moveMapAccordingToData({geoResolutionChanged, visibilityChanged, demeData, demeIndices}) {
/* Given d3 data (may not be drawn) we can compute map bounds & move as appropriate */
if (!this.state.boundsSet) {
Expand All @@ -599,11 +547,6 @@ class Map extends React.Component {
return;
}

/* if we're animating, then we don't want to move the map all the time */
if (this.props.animationPlayPauseButton === "Pause") {
return;
}

if (geoResolutionChanged || visibilityChanged) {
/* changed visiblity (e.g. filters applied) in narrative mode => reset view */
if (this.props.narrativeMode) {
Expand Down

0 comments on commit cd30947

Please sign in to comment.