1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | 5x 5x 5x 5x 17x 17x 17x 17x 17x 17x 5x 17x 17x 12x 12x 12x 12x 12x 5x 5x 5x 52x 52x 52x 18x 18x 17x 17x 17x 35x 35x | /** * Copyright 2018, Plotly, Inc. * All rights reserved. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ /** * A special Redux store that DOES NOT trigger automatically React Tree calculations. * This is only used to dispatch very frequent events like `POSITION_{MOVE,UPDATE}`. */ import { createStore } from 'redux'; import { createAction } from './actions'; import { clamp, floor, pick, } from 'lodash-es'; import assert from '../assert'; // send when the main store changes export const updateMainStore = createAction("MAINSTORE_UPDATE"); // move the position relatively by {xMovement, yMovement} export const movePosition = createAction("POSITION_MOVE"); // set an absolute position with {yPos, xPos} export const updatePosition = createAction("POSITION_UPDATE"); export const actions = { updateMainStore, updatePosition, movePosition, } /** * Makes sure that the position isn't set isn't out of its boundaries. */ function commonPositionReducer(prevState, pos) { const maximum = prevState.sequences.maxLength; const maxWidth = maximum * prevState.props.tileWidth - prevState.props.width; pos.xPos = clamp(pos.xPos, 0, maxWidth); const maxHeight = prevState.sequences.raw.length * prevState.props.tileHeight - prevState.props.height; pos.yPos = clamp(pos.yPos, 0, maxHeight); return { ...prevState, position: pos, }; } /** * Reducer for the {move,update}Position events */ const relativePositionReducer = (prevState = {position: {xPos: 0, yPos: 0}}, action) => { const pos = prevState.position; switch (action.type) { case movePosition.key: assert(action.payload.xMovement !== undefined || action.payload.yMovement !== undefined, "must contain at least xMovement or yMovement"); // be sure to copy the previous state const movePayload = {...pos} movePayload.xPos += action.payload.xMovement || 0; movePayload.yPos += action.payload.yMovement || 0; return commonPositionReducer(prevState, movePayload); case updatePosition.key: assert(action.payload.xPos !== undefined || action.payload.yPos !== undefined, "must contain at least xPos or yPos"); const updatePayload = { xPos: action.payload.xPos || pos.xPos, yPos: action.payload.yPos || pos.yPos, }; return commonPositionReducer(prevState, updatePayload); default: return prevState; } } /** * The main position store reducer which adds "position" to * the reduced main store. */ export function positionReducer(oldState = {position: {xPos: 0, yPos: 0}}, action){ let state = oldState; let position = oldState.position; switch(action.type) { case updateMainStore.key: // merge updates of the main store with this store for now state = { ...pick(state, ["props", "sequenceStats", "sequences"]), ...action.payload, } break; case updatePosition.key: case movePosition.key: position = relativePositionReducer(state, action).position; break; default: return state; } const addedState = { xPosOffset: -(position.xPos % state.props.tileWidth), yPosOffset: -(position.yPos % state.props.tileWidth), currentViewSequence: clamp( floor(position.yPos / state.props.tileHeight), 0, state.sequences.length - 1 ), currentViewSequencePosition: clamp( floor(position.xPos / state.props.tileWidth), 0, state.sequences.maxLength, ), position, }; return { ...state, ...addedState, }; } // for future flexibility export { createStore as createPositionStore, }; |