From 81e517002bfdfbcd75109c562d890a27d190889b Mon Sep 17 00:00:00 2001 From: Georgios Andreadis Date: Sun, 3 Sep 2017 17:53:03 +0200 Subject: Convert map movement to keyboard-based navigation --- package.json | 1 + src/components/map/MapConstants.js | 2 + src/components/map/MapStage.js | 74 +++++++++++++++------- src/components/modals/Modal.js | 3 + src/components/sidebars/topology/NameComponent.js | 13 ++++ .../sidebars/topology/room/RoomNameComponent.js | 9 +-- .../sidebars/topology/room/RoomSidebarComponent.js | 14 +++- .../sidebars/topology/room/RoomTypeComponent.js | 10 +++ .../sidebars/topology/room/RoomTypeContainer.js | 14 ++++ src/pages/App.js | 13 ++++ src/shortcuts/keymap.js | 10 +++ src/util/room-types.js | 7 ++ 12 files changed, 138 insertions(+), 32 deletions(-) create mode 100644 src/components/sidebars/topology/NameComponent.js create mode 100644 src/components/sidebars/topology/room/RoomTypeComponent.js create mode 100644 src/containers/sidebars/topology/room/RoomTypeContainer.js create mode 100644 src/shortcuts/keymap.js create mode 100644 src/util/room-types.js diff --git a/package.json b/package.json index 25d27b02..5e71fcf9 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "react-redux": "^5.0.5", "react-router-dom": "^4.1.1", "react-scripts": "^1.0.11", + "react-shortcuts": "^1.6.1", "redux": "^3.7.2", "redux-localstorage": "^0.4.1", "redux-logger": "^3.0.6", diff --git a/src/components/map/MapConstants.js b/src/components/map/MapConstants.js index eca8e516..69fdb419 100644 --- a/src/components/map/MapConstants.js +++ b/src/components/map/MapConstants.js @@ -8,3 +8,5 @@ export const OBJECT_SIZE_IN_PIXELS = TILE_SIZE_IN_PIXELS - OBJECT_MARGIN_IN_PIXE export const GRID_LINE_WIDTH_IN_PIXELS = 2; export const WALL_WIDTH_IN_PIXELS = TILE_SIZE_IN_PIXELS / 8; export const OBJECT_BORDER_WIDTH_IN_PIXELS = TILE_SIZE_IN_PIXELS / 12; + +export const MAP_MOVE_PIXELS_PER_EVENT = 20; diff --git a/src/components/map/MapStage.js b/src/components/map/MapStage.js index 541df1d6..f3f38917 100644 --- a/src/components/map/MapStage.js +++ b/src/components/map/MapStage.js @@ -1,12 +1,13 @@ import React from "react"; import {Group, Layer, Stage} from "react-konva"; +import {Shortcuts} from "react-shortcuts"; import DatacenterContainer from "../../containers/map/DatacenterContainer"; import HoverTileLayer from "../../containers/map/layers/HoverTileLayer"; import jQuery from "../../util/jquery"; import {NAVBAR_HEIGHT} from "../navigation/Navbar"; import Backdrop from "./elements/Backdrop"; import GridGroup from "./groups/GridGroup"; -import {MAP_SIZE_IN_PIXELS} from "./MapConstants"; +import {MAP_MOVE_PIXELS_PER_EVENT, MAP_SIZE_IN_PIXELS} from "./MapConstants"; class MapStage extends React.Component { state = { @@ -39,12 +40,33 @@ class MapStage extends React.Component { this.setState({mouseX: mousePos.x, mouseY: mousePos.y}); } - dragBoundFunc(pos) { + 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) { const updatedPosition = { - x: pos.x > 0 ? 0 : - (pos.x < -MAP_SIZE_IN_PIXELS + this.state.width ? -MAP_SIZE_IN_PIXELS + this.state.width : pos.x), - y: pos.y > 0 ? 0 : - (pos.y < -MAP_SIZE_IN_PIXELS + this.state.height ? -MAP_SIZE_IN_PIXELS + this.state.height : pos.y) + x: this.state.x + deltaX > 0 ? 0 : + (this.state.x + deltaX < -MAP_SIZE_IN_PIXELS + this.state.width + ? -MAP_SIZE_IN_PIXELS + this.state.width : this.state.x + deltaX), + y: this.state.y + deltaY > 0 ? 0 : + (this.state.y + deltaY < -MAP_SIZE_IN_PIXELS + this.state.height + ? -MAP_SIZE_IN_PIXELS + this.state.height : this.state.y + deltaY) }; this.setState(updatedPosition); @@ -54,24 +76,28 @@ class MapStage extends React.Component { render() { return ( - {this.stage = stage;}} - width={this.state.width} - height={this.state.height} - onMouseMove={this.updateMousePosition.bind(this)}> - - - - - - - - - + + {this.stage = stage;}} + width={this.state.width} + height={this.state.height} + onMouseMove={this.updateMousePosition.bind(this)} + > + + + + + + + + + + ) } } diff --git a/src/components/modals/Modal.js b/src/components/modals/Modal.js index 193746b3..6eebfb6e 100644 --- a/src/components/modals/Modal.js +++ b/src/components/modals/Modal.js @@ -42,6 +42,9 @@ class Modal extends React.Component { if (this.visible) { this.props.onCancel(); } + }) + .on("keydown", e => { + e.stopPropagation(); }); } diff --git a/src/components/sidebars/topology/NameComponent.js b/src/components/sidebars/topology/NameComponent.js new file mode 100644 index 00000000..d663f4ae --- /dev/null +++ b/src/components/sidebars/topology/NameComponent.js @@ -0,0 +1,13 @@ +import React from "react"; +import FontAwesome from "react-fontawesome"; + +const NameComponent = ({name, onEdit}) => ( +

+ {name} + +

+); + +export default NameComponent; diff --git a/src/components/sidebars/topology/room/RoomNameComponent.js b/src/components/sidebars/topology/room/RoomNameComponent.js index 4a8de76d..4d3e41cc 100644 --- a/src/components/sidebars/topology/room/RoomNameComponent.js +++ b/src/components/sidebars/topology/room/RoomNameComponent.js @@ -1,13 +1,8 @@ import React from "react"; -import FontAwesome from "react-fontawesome"; +import NameComponent from "../NameComponent"; const RoomNameComponent = ({roomName, onEdit}) => ( -

- {roomName} - -

+ ); export default RoomNameComponent; diff --git a/src/components/sidebars/topology/room/RoomSidebarComponent.js b/src/components/sidebars/topology/room/RoomSidebarComponent.js index 5ee9821a..dc01a301 100644 --- a/src/components/sidebars/topology/room/RoomSidebarComponent.js +++ b/src/components/sidebars/topology/room/RoomSidebarComponent.js @@ -1,8 +1,20 @@ import React from "react"; import RoomNameContainer from "../../../../containers/sidebars/topology/room/RoomNameContainer"; +import RoomTypeContainer from "../../../../containers/sidebars/topology/room/RoomTypeContainer"; const RoomSidebarComponent = ({roomType}) => { - return + let allowedObjects; + if (roomType === "SERVER") { + allowedObjects = "test"; + } + + return ( +
+ + + {allowedObjects} +
+ ); }; export default RoomSidebarComponent; diff --git a/src/components/sidebars/topology/room/RoomTypeComponent.js b/src/components/sidebars/topology/room/RoomTypeComponent.js new file mode 100644 index 00000000..c48c185a --- /dev/null +++ b/src/components/sidebars/topology/room/RoomTypeComponent.js @@ -0,0 +1,10 @@ +import React from "react"; +import {ROOM_TYPE_TO_NAME_MAP} from "../../../../util/room-types"; + +const RoomTypeComponent = ({roomType}) => ( +

+ {ROOM_TYPE_TO_NAME_MAP[roomType]} +

+); + +export default RoomTypeComponent; diff --git a/src/containers/sidebars/topology/room/RoomTypeContainer.js b/src/containers/sidebars/topology/room/RoomTypeContainer.js new file mode 100644 index 00000000..392bc479 --- /dev/null +++ b/src/containers/sidebars/topology/room/RoomTypeContainer.js @@ -0,0 +1,14 @@ +import {connect} from "react-redux"; +import RoomTypeComponent from "../../../../components/sidebars/topology/room/RoomTypeComponent"; + +const mapStateToProps = state => { + return { + roomType: state.objects.room[state.interactionLevel.roomId].roomType, + }; +}; + +const RoomNameContainer = connect( + mapStateToProps +)(RoomTypeComponent); + +export default RoomNameContainer; diff --git a/src/pages/App.js b/src/pages/App.js index 7f690002..98e2eac3 100644 --- a/src/pages/App.js +++ b/src/pages/App.js @@ -1,23 +1,36 @@ import PropTypes from "prop-types"; import React from 'react'; import {connect} from "react-redux"; +import {ShortcutManager} from "react-shortcuts"; import {openSimulationSucceeded} from "../actions/simulations"; import {fetchLatestDatacenter} from "../actions/topology"; import MapStage from "../components/map/MapStage"; import AppNavbar from "../components/navigation/AppNavbar"; import EditRoomNameModal from "../containers/modals/EditRoomNameModal"; import TopologySidebar from "../containers/sidebars/topology/TopologySidebar"; +import KeymapConfiguration from "../shortcuts/keymap"; + +const shortcutManager = new ShortcutManager(KeymapConfiguration); class AppContainer extends React.Component { static propTypes = { simulationId: PropTypes.number.isRequired, }; + static childContextTypes = { + shortcuts: PropTypes.object.isRequired + }; componentDidMount() { this.props.storeSimulationId(this.props.simulationId); this.props.fetchLatestDatacenter(); } + getChildContext() { + return { + shortcuts: shortcutManager + } + } + render() { return (
diff --git a/src/shortcuts/keymap.js b/src/shortcuts/keymap.js new file mode 100644 index 00000000..75986557 --- /dev/null +++ b/src/shortcuts/keymap.js @@ -0,0 +1,10 @@ +const KeymapConfiguration = { + MAP: { + MOVE_LEFT: ["a", "left"], + MOVE_RIGHT: ["d", "right"], + MOVE_UP: ["w", "up"], + MOVE_DOWN: ["s", "down"], + }, +}; + +export default KeymapConfiguration; diff --git a/src/util/room-types.js b/src/util/room-types.js new file mode 100644 index 00000000..ec0c4473 --- /dev/null +++ b/src/util/room-types.js @@ -0,0 +1,7 @@ +export const ROOM_TYPE_TO_NAME_MAP = { + "SERVER": "Server room", + "HALLWAY": "Hallway", + "OFFICE": "Office", + "POWER": "Power room", + "COOLING": "Cooling room", +}; -- cgit v1.2.3