UNPKG

auspice

Version:

Web app for visualizing pathogen evolution

251 lines (247 loc) 8.85 kB
import { numericToCalendar, calendarToNumeric, currentNumDate, currentCalDate } from "../util/dateHelpers"; import { defaultGeoResolution, defaultColorBy, defaultDateRange, defaultDistanceMeasure, defaultLayout, defaultMutType, twoColumnBreakpoint } from "../util/globals"; import * as types from "../actions/types"; import { calcBrowserDimensionsInitialState } from "./browserDimensions"; import { doesColorByHaveConfidence } from "../actions/recomputeReduxState"; /* defaultState is a fn so that we can re-create it at any time, e.g. if we want to revert things (e.g. on dataset change) */ export const getDefaultControlsState = () => { const defaults = { distanceMeasure: defaultDistanceMeasure, layout: defaultLayout, geoResolution: defaultGeoResolution, filters: {}, colorBy: defaultColorBy }; const dateMin = numericToCalendar(currentNumDate() - defaultDateRange); const dateMax = currentCalDate(); const dateMinNumeric = calendarToNumeric(dateMin); const dateMaxNumeric = calendarToNumeric(dateMax); return { defaults, available: undefined, canTogglePanelLayout: true, selectedBranch: null, selectedNode: null, region: null, search: null, strain: null, geneLength: {}, mutType: defaultMutType, temporalConfidence: {exists: false, display: false, on: false}, layout: defaults.layout, distanceMeasure: defaults.distanceMeasure, dateMin, dateMinNumeric, dateMax, dateMaxNumeric, absoluteDateMin: dateMin, absoluteDateMinNumeric: dateMinNumeric, absoluteDateMax: dateMax, absoluteDateMaxNumeric: dateMaxNumeric, colorBy: defaults.colorBy, colorByConfidence: {display: false, on: false}, colorScale: undefined, selectedBranchLabel: false, analysisSlider: false, geoResolution: defaults.geoResolution, filters: {}, showDownload: false, quickdraw: false, // if true, components may skip expensive computes. mapAnimationDurationInMilliseconds: 30000, // in milliseconds mapAnimationStartDate: null, // Null so it can pull the absoluteDateMin as the default mapAnimationCumulative: false, mapAnimationShouldLoop: false, animationPlayPauseButton: "Play", panelsAvailable: [], panelsToDisplay: [], panelLayout: calcBrowserDimensionsInitialState().width > twoColumnBreakpoint ? "grid" : "full", showTreeToo: undefined, showTangle: false, zoomMin: undefined, zoomMax: undefined, branchLengthsToDisplay: "divAndDate" }; }; const Controls = (state = getDefaultControlsState(), action) => { switch (action.type) { case types.URL_QUERY_CHANGE_WITH_COMPUTED_STATE: /* fallthrough */ case types.CLEAN_START: return action.controls; case types.SET_AVAILABLE: return Object.assign({}, state, {available: action.data}); case types.BRANCH_MOUSEENTER: return Object.assign({}, state, { selectedBranch: action.data }); case types.BRANCH_MOUSELEAVE: return Object.assign({}, state, { selectedBranch: null }); case types.NODE_MOUSEENTER: return Object.assign({}, state, { selectedNode: action.data }); case types.NODE_MOUSELEAVE: return Object.assign({}, state, { selectedNode: null }); case types.CHANGE_BRANCH_LABEL: return Object.assign({}, state, {selectedBranchLabel: action.value}); case types.CHANGE_LAYOUT: { const layout = action.data; /* temporal confidence can only be displayed for rectangular trees */ const temporalConfidence = { exists: state.temporalConfidence.exists, display: state.temporalConfidence.exists && layout === "rect", on: false }; return Object.assign({}, state, { layout, temporalConfidence }); } case types.CHANGE_DISTANCE_MEASURE: /* while this may change, div currently doesn't have CIs, so they shouldn't be displayed. */ if (state.temporalConfidence.exists) { if (state.temporalConfidence.display && action.data === "div") { return Object.assign({}, state, { distanceMeasure: action.data, branchLengthsToDisplay: state.branchLengthsToDisplay, temporalConfidence: Object.assign({}, state.temporalConfidence, {display: false, on: false}) }); } else if (state.layout === "rect" && action.data === "num_date") { return Object.assign({}, state, { distanceMeasure: action.data, branchLengthsToDisplay: state.branchLengthsToDisplay, temporalConfidence: Object.assign({}, state.temporalConfidence, {display: true}) }); } } return Object.assign({}, state, { distanceMeasure: action.data, branchLengthsToDisplay: state.branchLengthsToDisplay }); case types.CHANGE_DATES_VISIBILITY_THICKNESS: { const newDates = {quickdraw: action.quickdraw}; if (action.dateMin) { newDates.dateMin = action.dateMin; newDates.dateMinNumeric = action.dateMinNumeric; } if (action.dateMax) { newDates.dateMax = action.dateMax; newDates.dateMaxNumeric = action.dateMaxNumeric; } return Object.assign({}, state, newDates); } case types.CHANGE_ABSOLUTE_DATE_MIN: return Object.assign({}, state, { absoluteDateMin: action.data, absoluteDateMinNumeric: calendarToNumeric(action.data) }); case types.CHANGE_ABSOLUTE_DATE_MAX: return Object.assign({}, state, { absoluteDateMax: action.data, absoluteDateMaxNumeric: calendarToNumeric(action.data) }); case types.CHANGE_ANIMATION_TIME: return Object.assign({}, state, { mapAnimationDurationInMilliseconds: action.data }); case types.CHANGE_ANIMATION_CUMULATIVE: return Object.assign({}, state, { mapAnimationCumulative: action.data }); case types.CHANGE_ANIMATION_LOOP: return Object.assign({}, state, { mapAnimationShouldLoop: action.data }); case types.MAP_ANIMATION_PLAY_PAUSE_BUTTON: return Object.assign({}, state, { quickdraw: action.data !== "Play", animationPlayPauseButton: action.data }); case types.CHANGE_ANIMATION_START: return Object.assign({}, state, { mapAnimationStartDate: action.data }); case types.CHANGE_PANEL_LAYOUT: return Object.assign({}, state, { panelLayout: action.data }); case types.TREE_TOO_DATA: return action.controls; case types.TOGGLE_PANEL_DISPLAY: return Object.assign({}, state, { panelsToDisplay: action.panelsToDisplay, panelLayout: action.panelLayout, canTogglePanelLayout: action.panelsToDisplay.indexOf("tree") !== -1 && action.panelsToDisplay.indexOf("map") !== -1 }); case types.NEW_COLORS: { const newState = Object.assign({}, state, { colorBy: action.colorBy, colorScale: action.colorScale, colorByConfidence: doesColorByHaveConfidence(state, action.colorBy) }); return newState; } case types.CHANGE_GEO_RESOLUTION: return Object.assign({}, state, { geoResolution: action.data }); case types.APPLY_FILTER: { // values arrive as array const filters = Object.assign({}, state.filters, {}); filters[action.trait] = action.values; return Object.assign({}, state, { filters }); } case types.TOGGLE_MUT_TYPE: return Object.assign({}, state, { mutType: action.data }); case types.TOGGLE_TEMPORAL_CONF: return Object.assign({}, state, { temporalConfidence: Object.assign({}, state.temporalConfidence, { on: !state.temporalConfidence.on }) }); case types.TRIGGER_DOWNLOAD_MODAL: return Object.assign({}, state, { showDownload: true }); case types.DISMISS_DOWNLOAD_MODAL: return Object.assign({}, state, { showDownload: false }); case types.REMOVE_TREE_TOO: return Object.assign({}, state, { showTreeToo: undefined, showTangle: false, canTogglePanelLayout: state.panelsAvailable.indexOf("map") !== -1, panelsToDisplay: state.panelsAvailable.slice() }); case types.TOGGLE_TANGLE: if (state.showTreeToo) { return Object.assign({}, state, {showTangle: !state.showTangle}); } return state; case types.ADD_COLOR_BYS: for (const colorBy of Object.keys(action.newColorings)) { state.coloringsPresentOnTree.add(colorBy); } return Object.assign({}, state, {coloringsPresentOnTree: state.coloringsPresentOnTree}); default: return state; } }; export default Controls;