From 1b6545fa653df44b019f6676faed39880999b2bf Mon Sep 17 00:00:00 2001 From: Georgios Andreadis Date: Mon, 21 Aug 2017 12:08:41 +0200 Subject: Add basic react-konva rendering prototype --- package.json | 2 + src/actions/simulations.js | 6 +-- src/colors/index.js | 30 ++++++++++++ src/components/map/MapConstants.js | 6 +++ src/components/map/MapStage.js | 55 ++++++++++++++++++++++ src/components/map/elements/Backdrop.js | 16 +++++++ src/components/map/elements/MapTile.js | 24 ++++++++++ src/components/map/groups/GridGroup.js | 28 +++++++++++ src/components/map/groups/RoomGroup.js | 16 +++++++ src/components/modals/Modal.js | 23 ++++----- .../simulations/SimulationActionButtons.js | 8 ++-- src/components/simulations/SimulationAuthList.sass | 7 ++- src/containers/simulations/SimulationActions.js | 3 +- src/pages/App.js | 45 ++++++++++++++++++ src/reducers/simulations.js | 10 ++++ src/routes/index.js | 4 ++ src/util/jquery.js | 2 + 17 files changed, 263 insertions(+), 22 deletions(-) create mode 100644 src/colors/index.js create mode 100644 src/components/map/MapConstants.js create mode 100644 src/components/map/MapStage.js create mode 100644 src/components/map/elements/Backdrop.js create mode 100644 src/components/map/elements/MapTile.js create mode 100644 src/components/map/groups/GridGroup.js create mode 100644 src/components/map/groups/RoomGroup.js create mode 100644 src/pages/App.js create mode 100644 src/util/jquery.js diff --git a/package.json b/package.json index 31af798c..7f0ecfd8 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "classnames": "^2.2.5", "history": "^4.6.3", "isomorphic-fetch": "^2.2.1", + "konva": "^1.6.7", "node-sass-chokidar": "^0.0.3", "normalizr": "^3.2.3", "npm-run-all": "^4.0.2", @@ -16,6 +17,7 @@ "react-dom": "^15.6.1", "react-fontawesome": "^1.6.1", "react-google-login": "^2.9.3", + "react-konva": "^1.1.4", "react-mailto": "^0.4.0", "react-redux": "^5.0.5", "react-router-dom": "^4.1.1", diff --git a/src/actions/simulations.js b/src/actions/simulations.js index ddfacaf0..926c67bc 100644 --- a/src/actions/simulations.js +++ b/src/actions/simulations.js @@ -5,7 +5,7 @@ export const ADD_SIMULATION = "ADD_SIMULATION"; export const ADD_SIMULATION_SUCCEEDED = "ADD_SIMULATION_SUCCEEDED"; export const DELETE_SIMULATION = "DELETE_SIMULATION"; export const DELETE_SIMULATION_SUCCEEDED = "DELETE_SIMULATION_SUCCEEDED"; -export const OPEN_SIMULATION = "OPEN_SIMULATION"; +export const OPEN_SIMULATION_SUCCEEDED = "OPEN_SIMULATION_SUCCEEDED"; export function setAuthVisibilityFilter(filter) { return { @@ -58,9 +58,9 @@ export function deleteSimulationSucceeded(id) { }; } -export function openSimulation(id) { +export function openSimulationSucceeded(id) { return { - type: OPEN_SIMULATION, + type: OPEN_SIMULATION_SUCCEEDED, id }; } diff --git a/src/colors/index.js b/src/colors/index.js new file mode 100644 index 00000000..aa7c0a5d --- /dev/null +++ b/src/colors/index.js @@ -0,0 +1,30 @@ +export const GRID_COLOR = "rgba(0, 0, 0, 0.5)"; +export const BACKDROP_COLOR = "rgba(255, 255, 255, 1)"; +export const WALL_COLOR = "rgba(0, 0, 0, 1)"; + +export const ROOM_DEFAULT_COLOR = "rgba(150, 150, 150, 1)"; +export const ROOM_SELECTED_COLOR = "rgba(51, 153, 255, 1)"; +export const ROOM_HOVER_VALID_COLOR = "rgba(51, 153, 255, 0.5)"; +export const ROOM_HOVER_INVALID_COLOR = "rgba(255, 102, 0, 0.5)"; +export const ROOM_NAME_COLOR = "rgba(245, 245, 245, 1)"; +export const ROOM_TYPE_COLOR = "rgba(245, 245, 245, 1)"; + +export const RACK_BACKGROUND_COLOR = "rgba(170, 170, 170, 1)"; +export const RACK_BORDER_COLOR = "rgba(0, 0, 0, 1)"; +export const RACK_SPACE_BAR_BACKGROUND_COLOR = "rgba(222, 235, 247, 1)"; +export const RACK_SPACE_BAR_FILL_COLOR = "rgba(91, 155, 213, 1)"; +export const RACK_ENERGY_BAR_BACKGROUND_COLOR = "rgba(255, 242, 204, 1)"; +export const RACK_ENERGY_BAR_FILL_COLOR = "rgba(255, 192, 0, 1)"; + +export const COOLING_ITEM_BACKGROUND_COLOR = "rgba(40, 50, 230, 1)"; +export const COOLING_ITEM_BORDER_COLOR = "rgba(0, 0, 0, 1)"; + +export const PSU_BACKGROUND_COLOR = "rgba(230, 50, 60, 1)"; +export const PSU_BORDER_COLOR = "rgba(0, 0, 0, 1)"; + +export const GRAYED_OUT_AREA_COLOR = "rgba(0, 0, 0, 0.6)"; + +export const SIM_LOW_COLOR = "rgba(197, 224, 180, 1)"; +export const SIM_MID_LOW_COLOR = "rgba(255, 230, 153, 1)"; +export const SIM_MID_HIGH_COLOR = "rgba(248, 203, 173, 1)"; +export const SIM_HIGH_COLOR = "rgba(249, 165, 165, 1)"; diff --git a/src/components/map/MapConstants.js b/src/components/map/MapConstants.js new file mode 100644 index 00000000..74779c94 --- /dev/null +++ b/src/components/map/MapConstants.js @@ -0,0 +1,6 @@ +export const MAP_SIZE = 50; +export const TILE_SIZE_IN_PIXELS = 50; +export const MAP_SIZE_IN_PIXELS = MAP_SIZE * TILE_SIZE_IN_PIXELS; + +export const GRID_LINE_WIDTH_IN_PIXELS = 2; +export const ROOM_BORDER_WIDTH_IN_PIXELS = 5; diff --git a/src/components/map/MapStage.js b/src/components/map/MapStage.js new file mode 100644 index 00000000..38047064 --- /dev/null +++ b/src/components/map/MapStage.js @@ -0,0 +1,55 @@ +import React from "react"; +import {Group, Layer, Stage} from "react-konva"; +import jQuery from "../../util/jquery"; +import Backdrop from "./elements/Backdrop"; +import GridGroup from "./groups/GridGroup"; +import RoomGroup from "./groups/RoomGroup"; +import {MAP_SIZE_IN_PIXELS} from "./MapConstants"; + +class MapStage extends React.Component { + state = { + width: 600, + height: 400 + }; + + componentWillMount() { + this.updateDimensions(); + } + + componentDidMount() { + window.addEventListener("resize", this.updateDimensions.bind(this)); + } + + componentWillUnmount() { + window.removeEventListener("resize", this.updateDimensions.bind(this)); + } + + updateDimensions() { + this.setState({width: jQuery(window).width(), height: jQuery(window).height()}); + } + + dragBoundHandler(pos) { + return { + 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) + } + } + + render() { + return ( + + + + + + + + + + ) + } +} + +export default MapStage; diff --git a/src/components/map/elements/Backdrop.js b/src/components/map/elements/Backdrop.js new file mode 100644 index 00000000..32e95989 --- /dev/null +++ b/src/components/map/elements/Backdrop.js @@ -0,0 +1,16 @@ +import React from "react"; +import {Rect} from "react-konva"; +import {BACKDROP_COLOR} from "../../../colors/index"; +import {MAP_SIZE_IN_PIXELS} from "../MapConstants"; + +const Backdrop = () => ( + +); + +export default Backdrop; diff --git a/src/components/map/elements/MapTile.js b/src/components/map/elements/MapTile.js new file mode 100644 index 00000000..b0f4959d --- /dev/null +++ b/src/components/map/elements/MapTile.js @@ -0,0 +1,24 @@ +import PropTypes from "prop-types"; +import React from "react"; +import {Group, Rect} from "react-konva"; +import {TILE_SIZE_IN_PIXELS} from "../MapConstants"; + +const MapTile = () => ( + + + +); + +MapTile.propTypes = { + tileX: PropTypes.number.isRequired, + tileY: PropTypes.number.isRequired, + fillColor: PropTypes.string.isRequired, +}; + +export default MapTile; diff --git a/src/components/map/groups/GridGroup.js b/src/components/map/groups/GridGroup.js new file mode 100644 index 00000000..2651bf19 --- /dev/null +++ b/src/components/map/groups/GridGroup.js @@ -0,0 +1,28 @@ +import React from "react"; +import {Group, Line} from "react-konva"; +import {GRID_COLOR} from "../../../colors/index"; +import {GRID_LINE_WIDTH_IN_PIXELS, MAP_SIZE, MAP_SIZE_IN_PIXELS, TILE_SIZE_IN_PIXELS} from "../MapConstants"; + +const MAP_COORDINATE_ENTRIES = Array.from(new Array(MAP_SIZE), (x, i) => i); +const HORIZONTAL_POINT_PAIRS = MAP_COORDINATE_ENTRIES.map(index => [ + 0, index * TILE_SIZE_IN_PIXELS, + MAP_SIZE_IN_PIXELS, index * TILE_SIZE_IN_PIXELS +]); +const VERTICAL_POINT_PAIRS = MAP_COORDINATE_ENTRIES.map(index => [ + index * TILE_SIZE_IN_PIXELS, 0, + index * TILE_SIZE_IN_PIXELS, MAP_SIZE_IN_PIXELS +]); + +const GridGroup = () => ( + + {HORIZONTAL_POINT_PAIRS.concat(VERTICAL_POINT_PAIRS).map(points => ( + + ))} + +); + +export default GridGroup; diff --git a/src/components/map/groups/RoomGroup.js b/src/components/map/groups/RoomGroup.js new file mode 100644 index 00000000..1a8b18d5 --- /dev/null +++ b/src/components/map/groups/RoomGroup.js @@ -0,0 +1,16 @@ +import React from "react"; +import {Group, Rect} from "react-konva"; + +const RoomGroup = () => ( + + + +); + +export default RoomGroup; diff --git a/src/components/modals/Modal.js b/src/components/modals/Modal.js index 06273a46..193746b3 100644 --- a/src/components/modals/Modal.js +++ b/src/components/modals/Modal.js @@ -1,6 +1,7 @@ import classNames from "classnames"; import PropTypes from "prop-types"; import React from "react"; +import jQuery from "../../util/jquery"; class Modal extends React.Component { static propTypes = { @@ -33,15 +34,15 @@ class Modal extends React.Component { this.openOrCloseModal(); // Trigger auto-focus - window["$"]("#" + this.id).on("shown.bs.modal", function () { - window["$"](this).find("input").first().focus(); - }); - - window["$"]("#" + this.id).on("hide.bs.modal", () => { - if (this.visible) { - this.props.onCancel(); - } - }); + jQuery("#" + this.id) + .on("shown.bs.modal", function () { + jQuery(this).find("input").first().focus(); + }) + .on("hide.bs.modal", () => { + if (this.visible) { + this.props.onCancel(); + } + }); } componentDidUpdate() { @@ -66,11 +67,11 @@ class Modal extends React.Component { } openModal() { - window["$"]("#" + this.id).modal("show"); + jQuery("#" + this.id).modal("show"); } closeModal() { - window["$"]("#" + this.id).modal("hide"); + jQuery("#" + this.id).modal("hide"); } openOrCloseModal() { diff --git a/src/components/simulations/SimulationActionButtons.js b/src/components/simulations/SimulationActionButtons.js index d48b4bcf..1731b9be 100644 --- a/src/components/simulations/SimulationActionButtons.js +++ b/src/components/simulations/SimulationActionButtons.js @@ -1,11 +1,12 @@ import PropTypes from "prop-types"; import React from 'react'; +import {Link} from "react-router-dom"; -const SimulationActionButtons = ({simulationId, onOpen, onViewUsers, onDelete}) => ( +const SimulationActionButtons = ({simulationId, onViewUsers, onDelete}) => (
-
onOpen(simulationId)}> + -
+
onViewUsers(simulationId)}> @@ -18,7 +19,6 @@ const SimulationActionButtons = ({simulationId, onOpen, onViewUsers, onDelete}) SimulationActionButtons.propTypes = { simulationId: PropTypes.number.isRequired, - onOpen: PropTypes.func, onViewUsers: PropTypes.func, onDelete: PropTypes.func, }; diff --git a/src/components/simulations/SimulationAuthList.sass b/src/components/simulations/SimulationAuthList.sass index 10334c9f..58683446 100644 --- a/src/components/simulations/SimulationAuthList.sass +++ b/src/components/simulations/SimulationAuthList.sass @@ -57,8 +57,8 @@ .simulation-row .simulation-icons text-align: right -.simulation-row .simulation-icons div - display: inline +.simulation-row .simulation-icons div, .simulation-row .simulation-icons a + display: inline-block position: relative top: 4px width: 30px @@ -79,6 +79,9 @@ $icon-color: #0c60bf background: $icon-color + span + left: 1px + &:hover background: lighten($icon-color, 10%) diff --git a/src/containers/simulations/SimulationActions.js b/src/containers/simulations/SimulationActions.js index e2ca2795..01ccaa91 100644 --- a/src/containers/simulations/SimulationActions.js +++ b/src/containers/simulations/SimulationActions.js @@ -1,5 +1,5 @@ import {connect} from "react-redux"; -import {deleteSimulation, openSimulation} from "../../actions/simulations"; +import {deleteSimulation} from "../../actions/simulations"; import SimulationActionButtons from "../../components/simulations/SimulationActionButtons"; const mapStateToProps = (state, ownProps) => { @@ -10,7 +10,6 @@ const mapStateToProps = (state, ownProps) => { const mapDispatchToProps = dispatch => { return { - onOpen: (id) => dispatch(openSimulation(id)), onViewUsers: (id) => {}, onDelete: (id) => dispatch(deleteSimulation(id)), }; diff --git a/src/pages/App.js b/src/pages/App.js new file mode 100644 index 00000000..a2a1050b --- /dev/null +++ b/src/pages/App.js @@ -0,0 +1,45 @@ +import PropTypes from "prop-types"; +import React from 'react'; +import {connect} from "react-redux"; +import {openSimulationSucceeded} from "../actions/simulations"; +import {fetchAuthorizationsOfCurrentUser} from "../actions/users"; +import MapStage from "../components/map/MapStage"; +import Navbar from "../components/navigation/Navbar"; +import Login from "../containers/auth/Login"; + +class AppContainer extends React.Component { + static propTypes = { + simulationId: PropTypes.number.isRequired, + }; + + componentDidMount() { + this.props.storeSimulationId(this.props.simulationId); + this.props.fetchAuthorizationsOfCurrentUser(); + } + + render() { + return ( +
+ +
+ +
+ +
+ ); + } +} + +const mapDispatchToProps = dispatch => { + return { + storeSimulationId: id => dispatch(openSimulationSucceeded(id)), + fetchAuthorizationsOfCurrentUser: () => dispatch(fetchAuthorizationsOfCurrentUser()), + }; +}; + +const App = connect( + undefined, + mapDispatchToProps +)(AppContainer); + +export default App; diff --git a/src/reducers/simulations.js b/src/reducers/simulations.js index 7d0b9d66..9bca7740 100644 --- a/src/reducers/simulations.js +++ b/src/reducers/simulations.js @@ -1,6 +1,7 @@ import { ADD_SIMULATION_SUCCEEDED, DELETE_SIMULATION_SUCCEEDED, + OPEN_SIMULATION_SUCCEEDED, SET_AUTH_VISIBILITY_FILTER } from "../actions/simulations"; import {FETCH_AUTHORIZATIONS_OF_CURRENT_USER_SUCCEEDED} from "../actions/users"; @@ -29,3 +30,12 @@ export function authVisibilityFilter(state = "SHOW_ALL", action) { return state; } } + +export function currentSimulationId(state = -1, action) { + switch (action.type) { + case OPEN_SIMULATION_SUCCEEDED: + return action.id; + default: + return state; + } +} diff --git a/src/routes/index.js b/src/routes/index.js index af1b70b5..deaf8ded 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -1,18 +1,22 @@ import React from 'react'; import {BrowserRouter, Redirect, Route, Switch} from "react-router-dom"; import {userIsLoggedIn} from "../auth/index"; +import App from "../pages/App"; import Home from "../pages/Home"; import NotFound from "../pages/NotFound"; import Profile from "../pages/Profile"; import Simulations from "../pages/Simulations"; const ProtectedComponent = (component) => () => userIsLoggedIn() ? component : ; +const AppComponent = ({match}) => userIsLoggedIn() ? + : ; const Routes = () => ( )}/> + )}/> diff --git a/src/util/jquery.js b/src/util/jquery.js new file mode 100644 index 00000000..dd0a7222 --- /dev/null +++ b/src/util/jquery.js @@ -0,0 +1,2 @@ +const jQuery = window["$"]; +export default jQuery; -- cgit v1.2.3