From aa448cb5c3e2e372dad1c79ffc32ff32144b4140 Mon Sep 17 00:00:00 2001 From: Georgios Andreadis Date: Mon, 11 Sep 2017 11:53:06 +0200 Subject: Move zoom level and map position info to store --- src/actions/map.js | 26 +++++ src/components/map/MapStage.js | 140 ----------------------- src/components/map/MapStageComponent.js | 121 ++++++++++++++++++++ src/components/map/elements/TilePlusIcon.js | 21 ++-- src/components/map/layers/HoverLayerComponent.js | 17 ++- src/components/map/layers/MapLayerComponent.js | 17 +++ src/containers/map/MapStage.js | 26 +++++ src/containers/map/layers/MapLayer.js | 15 +++ src/containers/map/layers/ObjectHoverLayer.js | 2 + src/containers/map/layers/RoomHoverLayer.js | 2 + src/pages/App.js | 2 +- src/reducers/index.js | 2 + src/reducers/map.js | 35 ++++++ 13 files changed, 266 insertions(+), 160 deletions(-) create mode 100644 src/actions/map.js delete mode 100644 src/components/map/MapStage.js create mode 100644 src/components/map/MapStageComponent.js create mode 100644 src/components/map/layers/MapLayerComponent.js create mode 100644 src/containers/map/MapStage.js create mode 100644 src/containers/map/layers/MapLayer.js create mode 100644 src/reducers/map.js diff --git a/src/actions/map.js b/src/actions/map.js new file mode 100644 index 00000000..ef691873 --- /dev/null +++ b/src/actions/map.js @@ -0,0 +1,26 @@ +export const SET_MAP_POSITION = "SET_MAP_POSITION"; +export const SET_MAP_DIMENSIONS = "SET_MAP_DIMENSIONS"; +export const SET_MAP_SCALE = "SET_MAP_SCALE"; + +export function setMapPosition(x, y) { + return { + type: SET_MAP_POSITION, + x, + y + }; +} + +export function setMapDimensions(width, height) { + return { + type: SET_MAP_DIMENSIONS, + width, + height + }; +} + +export function setMapScale(scale) { + return { + type: SET_MAP_SCALE, + scale + }; +} diff --git a/src/components/map/MapStage.js b/src/components/map/MapStage.js deleted file mode 100644 index ab2f0839..00000000 --- a/src/components/map/MapStage.js +++ /dev/null @@ -1,140 +0,0 @@ -import React from "react"; -import {Group, Layer, Stage} from "react-konva"; -import {Shortcuts} from "react-shortcuts"; -import DatacenterContainer from "../../containers/map/DatacenterContainer"; -import ObjectHoverLayer from "../../containers/map/layers/ObjectHoverLayer"; -import RoomHoverLayer from "../../containers/map/layers/RoomHoverLayer"; -import jQuery from "../../util/jquery"; -import {NAVBAR_HEIGHT} from "../navigation/Navbar"; -import Backdrop from "./elements/Backdrop"; -import GridGroup from "./groups/GridGroup"; -import { - MAP_MAX_SCALE, - MAP_MIN_SCALE, - MAP_MOVE_PIXELS_PER_EVENT, - MAP_SCALE_PER_EVENT, - MAP_SIZE_IN_PIXELS -} from "./MapConstants"; - -class MapStage extends React.Component { - state = { - width: 600, - height: 400, - x: 0, - y: 0, - scale: 1, - mouseX: 0, - mouseY: 0 - }; - - componentWillMount() { - this.updateDimensions(); - } - - componentDidMount() { - window.addEventListener("resize", this.updateDimensions.bind(this)); - window.addEventListener("wheel", this.updateScale.bind(this)); - } - - componentWillUnmount() { - window.removeEventListener("resize", this.updateDimensions.bind(this)); - window.removeEventListener("wheel", this.updateScale.bind(this)); - } - - updateDimensions() { - this.setState({width: jQuery(window).width(), height: jQuery(window).height() - NAVBAR_HEIGHT}); - } - - updateScale(e) { - e.preventDefault(); - const mousePointsTo = { - x: this.state.mouseX / this.state.scale - this.state.x / this.state.scale, - y: this.state.mouseY / this.state.scale - this.state.y / this.state.scale, - }; - const newScale = e.deltaY < 0 ? this.state.scale * MAP_SCALE_PER_EVENT : this.state.scale / MAP_SCALE_PER_EVENT; - const boundedScale = Math.min(Math.max(MAP_MIN_SCALE, newScale), MAP_MAX_SCALE); - - const newX = -(mousePointsTo.x - this.state.mouseX / boundedScale) * boundedScale; - const newY = -(mousePointsTo.y - this.state.mouseY / boundedScale) * boundedScale; - - this.setPositionWithBoundsCheck(newX, newY); - this.setState({scale: boundedScale}); - } - - updateMousePosition() { - const mousePos = this.stage.getStage().getPointerPosition(); - this.setState({mouseX: mousePos.x, mouseY: mousePos.y}); - } - - handleShortcuts(action) { - switch (action) { - case "MOVE_LEFT": - this.moveWithDelta(MAP_MOVE_PIXELS_PER_EVENT, 0); - break; - case "MOVE_RIGHT": - this.moveWithDelta(-MAP_MOVE_PIXELS_PER_EVENT, 0); - break; - case "MOVE_UP": - this.moveWithDelta(0, MAP_MOVE_PIXELS_PER_EVENT); - break; - case "MOVE_DOWN": - this.moveWithDelta(0, -MAP_MOVE_PIXELS_PER_EVENT); - break; - default: - break; - } - } - - moveWithDelta(deltaX, deltaY) { - this.setPositionWithBoundsCheck(this.state.x + deltaX, this.state.y + deltaY); - } - - setPositionWithBoundsCheck(newX, newY) { - const scaledMapSize = MAP_SIZE_IN_PIXELS * this.state.scale; - const updatedPosition = { - x: newX > 0 ? 0 : - (newX < -scaledMapSize + this.state.width ? -scaledMapSize + this.state.width : newX), - y: newY > 0 ? 0 : - (newY < -scaledMapSize + this.state.height ? -scaledMapSize + this.state.height : newY) - }; - - this.setState(updatedPosition); - } - - render() { - return ( - - {this.stage = stage;}} - width={this.state.width} - height={this.state.height} - onMouseMove={this.updateMousePosition.bind(this)} - > - - - - - - - - - - - - ) - } -} - -export default MapStage; diff --git a/src/components/map/MapStageComponent.js b/src/components/map/MapStageComponent.js new file mode 100644 index 00000000..10d84948 --- /dev/null +++ b/src/components/map/MapStageComponent.js @@ -0,0 +1,121 @@ +import React from "react"; +import {Stage} from "react-konva"; +import {Shortcuts} from "react-shortcuts"; +import MapLayer from "../../containers/map/layers/MapLayer"; +import ObjectHoverLayer from "../../containers/map/layers/ObjectHoverLayer"; +import RoomHoverLayer from "../../containers/map/layers/RoomHoverLayer"; +import jQuery from "../../util/jquery"; +import {NAVBAR_HEIGHT} from "../navigation/Navbar"; +import { + MAP_MAX_SCALE, + MAP_MIN_SCALE, + MAP_MOVE_PIXELS_PER_EVENT, + MAP_SCALE_PER_EVENT, + MAP_SIZE_IN_PIXELS +} from "./MapConstants"; + +class MapStageComponent extends React.Component { + state = { + mouseX: 0, + mouseY: 0 + }; + + componentWillMount() { + this.updateDimensions(); + } + + componentDidMount() { + window.addEventListener("resize", this.updateDimensions.bind(this)); + window.addEventListener("wheel", this.updateScale.bind(this)); + } + + componentWillUnmount() { + window.removeEventListener("resize", this.updateDimensions.bind(this)); + window.removeEventListener("wheel", this.updateScale.bind(this)); + } + + updateDimensions() { + this.props.setMapDimensions(jQuery(window).width(), jQuery(window).height() - NAVBAR_HEIGHT); + } + + updateScale(e) { + e.preventDefault(); + const mousePointsTo = { + x: this.state.mouseX / this.props.mapScale - this.props.mapPosition.x / this.props.mapScale, + y: this.state.mouseY / this.props.mapScale - this.props.mapPosition.y / this.props.mapScale, + }; + const newScale = e.deltaY < 0 ? this.props.mapScale * MAP_SCALE_PER_EVENT : this.props.mapScale / MAP_SCALE_PER_EVENT; + const boundedScale = Math.min(Math.max(MAP_MIN_SCALE, newScale), MAP_MAX_SCALE); + + const newX = -(mousePointsTo.x - this.state.mouseX / boundedScale) * boundedScale; + const newY = -(mousePointsTo.y - this.state.mouseY / boundedScale) * boundedScale; + + this.setPositionWithBoundsCheck(newX, newY); + this.props.setMapScale(boundedScale); + } + + updateMousePosition() { + const mousePos = this.stage.getStage().getPointerPosition(); + this.setState({mouseX: mousePos.x, mouseY: mousePos.y}); + } + + handleShortcuts(action) { + switch (action) { + case "MOVE_LEFT": + this.moveWithDelta(MAP_MOVE_PIXELS_PER_EVENT, 0); + break; + case "MOVE_RIGHT": + this.moveWithDelta(-MAP_MOVE_PIXELS_PER_EVENT, 0); + break; + case "MOVE_UP": + this.moveWithDelta(0, MAP_MOVE_PIXELS_PER_EVENT); + break; + case "MOVE_DOWN": + this.moveWithDelta(0, -MAP_MOVE_PIXELS_PER_EVENT); + break; + default: + break; + } + } + + moveWithDelta(deltaX, deltaY) { + this.setPositionWithBoundsCheck(this.props.mapPosition.x + deltaX, this.props.mapPosition.y + deltaY); + } + + setPositionWithBoundsCheck(newX, newY) { + const scaledMapSize = MAP_SIZE_IN_PIXELS * this.props.mapScale; + const updatedX = newX > 0 ? 0 : + (newX < -scaledMapSize + this.props.mapDimensions.width + ? -scaledMapSize + this.props.mapDimensions.width : newX); + const updatedY = newY > 0 ? 0 : + (newY < -scaledMapSize + this.props.mapDimensions.height + ? -scaledMapSize + this.props.mapDimensions.height : newY); + + this.props.setMapPosition(updatedX, updatedY); + } + + render() { + return ( + + {this.stage = stage;}} + width={this.props.mapDimensions.width} + height={this.props.mapDimensions.height} + onMouseMove={this.updateMousePosition.bind(this)} + > + + + + + + ) + } +} + +export default MapStageComponent; diff --git a/src/components/map/elements/TilePlusIcon.js b/src/components/map/elements/TilePlusIcon.js index 3327525c..ed5ef5b5 100644 --- a/src/components/map/elements/TilePlusIcon.js +++ b/src/components/map/elements/TilePlusIcon.js @@ -4,19 +4,19 @@ import {Group, Line} from "react-konva"; import {TILE_PLUS_COLOR} from "../../../colors/index"; import {TILE_PLUS_MARGIN_IN_PIXELS, TILE_PLUS_WIDTH_IN_PIXELS, TILE_SIZE_IN_PIXELS} from "../MapConstants"; -const TilePlusIcon = ({pixelX, pixelY, scale}) => { +const TilePlusIcon = ({pixelX, pixelY, mapScale}) => { const linePoints = [ [ - pixelX + 0.5 * TILE_SIZE_IN_PIXELS * scale, - pixelY + TILE_PLUS_MARGIN_IN_PIXELS * scale, - pixelX + 0.5 * TILE_SIZE_IN_PIXELS * scale, - pixelY + TILE_SIZE_IN_PIXELS * scale - TILE_PLUS_MARGIN_IN_PIXELS * scale, + pixelX + 0.5 * TILE_SIZE_IN_PIXELS * mapScale, + pixelY + TILE_PLUS_MARGIN_IN_PIXELS * mapScale, + pixelX + 0.5 * TILE_SIZE_IN_PIXELS * mapScale, + pixelY + TILE_SIZE_IN_PIXELS * mapScale - TILE_PLUS_MARGIN_IN_PIXELS * mapScale, ], [ - pixelX + TILE_PLUS_MARGIN_IN_PIXELS * scale, - pixelY + 0.5 * TILE_SIZE_IN_PIXELS * scale, - pixelX + TILE_SIZE_IN_PIXELS * scale - TILE_PLUS_MARGIN_IN_PIXELS * scale, - pixelY + 0.5 * TILE_SIZE_IN_PIXELS * scale, + pixelX + TILE_PLUS_MARGIN_IN_PIXELS * mapScale, + pixelY + 0.5 * TILE_SIZE_IN_PIXELS * mapScale, + pixelX + TILE_SIZE_IN_PIXELS * mapScale - TILE_PLUS_MARGIN_IN_PIXELS * mapScale, + pixelY + 0.5 * TILE_SIZE_IN_PIXELS * mapScale, ], ]; return ( @@ -27,7 +27,7 @@ const TilePlusIcon = ({pixelX, pixelY, scale}) => { points={points} lineCap="round" stroke={TILE_PLUS_COLOR} - strokeWidth={TILE_PLUS_WIDTH_IN_PIXELS * scale} + strokeWidth={TILE_PLUS_WIDTH_IN_PIXELS * mapScale} listening={false} /> ))} @@ -38,6 +38,7 @@ const TilePlusIcon = ({pixelX, pixelY, scale}) => { TilePlusIcon.propTypes = { pixelX: PropTypes.number, pixelY: PropTypes.number, + mapScale: PropTypes.number, }; export default TilePlusIcon; diff --git a/src/components/map/layers/HoverLayerComponent.js b/src/components/map/layers/HoverLayerComponent.js index 54a63383..aa2e8313 100644 --- a/src/components/map/layers/HoverLayerComponent.js +++ b/src/components/map/layers/HoverLayerComponent.js @@ -8,9 +8,8 @@ class HoverLayerComponent extends React.Component { static propTypes = { mouseX: PropTypes.number.isRequired, mouseY: PropTypes.number.isRequired, - mainGroupX: PropTypes.number.isRequired, - mainGroupY: PropTypes.number.isRequired, - scale: PropTypes.number.isRequired, + mapPosition: PropTypes.object.isRequired, + mapScale: PropTypes.number.isRequired, isEnabled: PropTypes.func.isRequired, onClick: PropTypes.func.isRequired, }; @@ -26,8 +25,8 @@ class HoverLayerComponent extends React.Component { return; } - const positionX = Math.floor((this.props.mouseX - this.props.mainGroupX) / (this.props.scale * TILE_SIZE_IN_PIXELS)); - const positionY = Math.floor((this.props.mouseY - this.props.mainGroupY) / (this.props.scale * TILE_SIZE_IN_PIXELS)); + const positionX = Math.floor((this.props.mouseX - this.props.mapPosition.x) / (this.props.mapScale * TILE_SIZE_IN_PIXELS)); + const positionY = Math.floor((this.props.mouseY - this.props.mapPosition.y) / (this.props.mapScale * TILE_SIZE_IN_PIXELS)); if (positionX !== this.state.positionX || positionY !== this.state.positionY) { this.setState({positionX, positionY, validity: this.props.isValid(positionX, positionY)}); @@ -39,21 +38,21 @@ class HoverLayerComponent extends React.Component { return ; } - const pixelX = this.props.scale * this.state.positionX * TILE_SIZE_IN_PIXELS + this.props.mainGroupX; - const pixelY = this.props.scale * this.state.positionY * TILE_SIZE_IN_PIXELS + this.props.mainGroupY; + const pixelX = this.props.mapScale * this.state.positionX * TILE_SIZE_IN_PIXELS + this.props.mapPosition.x; + const pixelY = this.props.mapScale * this.state.positionY * TILE_SIZE_IN_PIXELS + this.props.mapPosition.y; return ( this.state.validity ? this.props.onClick(this.state.positionX, this.state.positionY) : undefined} /> {this.props.children ? - React.cloneElement(this.props.children, {pixelX, pixelY, scale: this.props.scale}) : + React.cloneElement(this.props.children, {pixelX, pixelY, scale: this.props.mapScale}) : undefined } diff --git a/src/components/map/layers/MapLayerComponent.js b/src/components/map/layers/MapLayerComponent.js new file mode 100644 index 00000000..3476bbc9 --- /dev/null +++ b/src/components/map/layers/MapLayerComponent.js @@ -0,0 +1,17 @@ +import React from 'react'; +import {Group, Layer} from "react-konva"; +import DatacenterContainer from "../../../containers/map/DatacenterContainer"; +import Backdrop from "../elements/Backdrop"; +import GridGroup from "../groups/GridGroup"; + +const MapLayerComponent = ({mapPosition, mapScale}) => ( + + + + + + + +); + +export default MapLayerComponent; diff --git a/src/containers/map/MapStage.js b/src/containers/map/MapStage.js new file mode 100644 index 00000000..62dd7463 --- /dev/null +++ b/src/containers/map/MapStage.js @@ -0,0 +1,26 @@ +import {connect} from "react-redux"; +import {setMapDimensions, setMapPosition, setMapScale} from "../../actions/map"; +import MapStageComponent from "../../components/map/MapStageComponent"; + +const mapStateToProps = state => { + return { + mapPosition: state.map.position, + mapDimensions: state.map.dimensions, + mapScale: state.map.scale, + }; +}; + +const mapDispatchToProps = dispatch => { + return { + setMapPosition: (x, y) => dispatch(setMapPosition(x, y)), + setMapDimensions: (width, height) => dispatch(setMapDimensions(width, height)), + setMapScale: (scale) => dispatch(setMapScale(scale)), + }; +}; + +const MapStage = connect( + mapStateToProps, + mapDispatchToProps +)(MapStageComponent); + +export default MapStage; diff --git a/src/containers/map/layers/MapLayer.js b/src/containers/map/layers/MapLayer.js new file mode 100644 index 00000000..9ef5c662 --- /dev/null +++ b/src/containers/map/layers/MapLayer.js @@ -0,0 +1,15 @@ +import {connect} from "react-redux"; +import MapLayerComponent from "../../../components/map/layers/MapLayerComponent"; + +const mapStateToProps = state => { + return { + mapPosition: state.map.position, + mapScale: state.map.scale, + }; +}; + +const MapLayer = connect( + mapStateToProps +)(MapLayerComponent); + +export default MapLayer; diff --git a/src/containers/map/layers/ObjectHoverLayer.js b/src/containers/map/layers/ObjectHoverLayer.js index d0cc35fd..b0201257 100644 --- a/src/containers/map/layers/ObjectHoverLayer.js +++ b/src/containers/map/layers/ObjectHoverLayer.js @@ -5,6 +5,8 @@ import {findTileWithPosition} from "../../../util/tile-calculations"; const mapStateToProps = state => { return { + mapPosition: state.map.position, + mapScale: state.map.scale, isEnabled: () => state.construction.inRackConstructionMode, isValid: (x, y) => { if (state.interactionLevel.mode !== "ROOM") { diff --git a/src/containers/map/layers/RoomHoverLayer.js b/src/containers/map/layers/RoomHoverLayer.js index 23f590d5..528e45d3 100644 --- a/src/containers/map/layers/RoomHoverLayer.js +++ b/src/containers/map/layers/RoomHoverLayer.js @@ -9,6 +9,8 @@ import { const mapStateToProps = state => { return { + mapPosition: state.map.position, + mapScale: state.map.scale, isEnabled: () => state.construction.currentRoomInConstruction !== -1, isValid: (x, y) => { if (state.interactionLevel.mode !== "BUILDING") { diff --git a/src/pages/App.js b/src/pages/App.js index 317442f6..21b3c445 100644 --- a/src/pages/App.js +++ b/src/pages/App.js @@ -5,8 +5,8 @@ import {ShortcutManager} from "react-shortcuts"; import {openSimulationSucceeded} from "../actions/simulations"; import {fetchLatestDatacenter, resetCurrentDatacenter} from "../actions/topology/building"; import LoadingScreen from "../components/map/LoadingScreen"; -import MapStage from "../components/map/MapStage"; import AppNavbar from "../components/navigation/AppNavbar"; +import MapStage from "../containers/map/MapStage"; import DeleteMachineModal from "../containers/modals/DeleteMachineModal"; import DeleteRackModal from "../containers/modals/DeleteRackModal"; import DeleteRoomModal from "../containers/modals/DeleteRoomModal"; diff --git a/src/reducers/index.js b/src/reducers/index.js index f1f51337..1f3aa8f2 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -2,6 +2,7 @@ import {combineReducers} from "redux"; import {auth} from "./auth"; import {construction} from "./construction"; import {interactionLevel} from "./interaction-level"; +import {map} from "./map"; import {modals} from "./modals"; import {objects} from "./objects"; import {simulationList} from "./simulation-list"; @@ -17,6 +18,7 @@ const rootReducer = combineReducers({ currentDatacenterId, interactionLevel, construction, + map, }); export default rootReducer; diff --git a/src/reducers/map.js b/src/reducers/map.js new file mode 100644 index 00000000..c36916b6 --- /dev/null +++ b/src/reducers/map.js @@ -0,0 +1,35 @@ +import {combineReducers} from "redux"; +import {SET_MAP_DIMENSIONS, SET_MAP_POSITION, SET_MAP_SCALE} from "../actions/map"; + +export function position(state = {x: 0, y: 0}, action) { + switch (action.type) { + case SET_MAP_POSITION: + return {x: action.x, y: action.y}; + default: + return state; + } +} + +export function dimensions(state = {width: 600, height: 400}, action) { + switch (action.type) { + case SET_MAP_DIMENSIONS: + return {width: action.width, height: action.height}; + default: + return state; + } +} + +export function scale(state = 1, action) { + switch (action.type) { + case SET_MAP_SCALE: + return action.scale; + default: + return state; + } +} + +export const map = combineReducers({ + position, + dimensions, + scale +}); -- cgit v1.2.3