From 90fae26aa4bd0e0eb3272ff6e6524060e9004fbb Mon Sep 17 00:00:00 2001 From: Georgios Andreadis Date: Mon, 29 Jun 2020 15:47:09 +0200 Subject: Prepare frontend repository for monorepo This change prepares the frontend Git repository for the monorepo residing at https://github.com/atlarge-research.com/opendc. To accomodate for this, we move all files into a frontend subdirectory. --- frontend/src/components/app/map/LoadingScreen.js | 11 ++ frontend/src/components/app/map/MapConstants.js | 29 +++++ .../src/components/app/map/MapStageComponent.js | 126 +++++++++++++++++++++ .../app/map/controls/ExportCanvasComponent.js | 13 +++ .../app/map/controls/ScaleIndicatorComponent.js | 14 +++ .../app/map/controls/ScaleIndicatorComponent.sass | 9 ++ .../app/map/controls/ToolPanelComponent.js | 13 +++ .../app/map/controls/ToolPanelComponent.sass | 5 + .../app/map/controls/ZoomControlComponent.js | 24 ++++ .../src/components/app/map/elements/Backdrop.js | 16 +++ .../src/components/app/map/elements/GrayLayer.js | 17 +++ .../src/components/app/map/elements/HoverTile.js | 30 +++++ .../components/app/map/elements/ImageComponent.js | 48 ++++++++ .../src/components/app/map/elements/RackFillBar.js | 89 +++++++++++++++ .../src/components/app/map/elements/RoomTile.js | 20 ++++ .../src/components/app/map/elements/TileObject.js | 29 +++++ .../components/app/map/elements/TilePlusIcon.js | 52 +++++++++ .../src/components/app/map/elements/WallSegment.js | 39 +++++++ .../components/app/map/groups/DatacenterGroup.js | 40 +++++++ .../src/components/app/map/groups/GridGroup.js | 41 +++++++ .../src/components/app/map/groups/RackGroup.js | 43 +++++++ .../src/components/app/map/groups/RoomGroup.js | 56 +++++++++ .../src/components/app/map/groups/TileGroup.js | 43 +++++++ .../src/components/app/map/groups/WallGroup.js | 22 ++++ .../app/map/layers/HoverLayerComponent.js | 85 ++++++++++++++ .../components/app/map/layers/MapLayerComponent.js | 22 ++++ .../app/map/layers/ObjectHoverLayerComponent.js | 11 ++ .../app/map/layers/RoomHoverLayerComponent.js | 6 + 28 files changed, 953 insertions(+) create mode 100644 frontend/src/components/app/map/LoadingScreen.js create mode 100644 frontend/src/components/app/map/MapConstants.js create mode 100644 frontend/src/components/app/map/MapStageComponent.js create mode 100644 frontend/src/components/app/map/controls/ExportCanvasComponent.js create mode 100644 frontend/src/components/app/map/controls/ScaleIndicatorComponent.js create mode 100644 frontend/src/components/app/map/controls/ScaleIndicatorComponent.sass create mode 100644 frontend/src/components/app/map/controls/ToolPanelComponent.js create mode 100644 frontend/src/components/app/map/controls/ToolPanelComponent.sass create mode 100644 frontend/src/components/app/map/controls/ZoomControlComponent.js create mode 100644 frontend/src/components/app/map/elements/Backdrop.js create mode 100644 frontend/src/components/app/map/elements/GrayLayer.js create mode 100644 frontend/src/components/app/map/elements/HoverTile.js create mode 100644 frontend/src/components/app/map/elements/ImageComponent.js create mode 100644 frontend/src/components/app/map/elements/RackFillBar.js create mode 100644 frontend/src/components/app/map/elements/RoomTile.js create mode 100644 frontend/src/components/app/map/elements/TileObject.js create mode 100644 frontend/src/components/app/map/elements/TilePlusIcon.js create mode 100644 frontend/src/components/app/map/elements/WallSegment.js create mode 100644 frontend/src/components/app/map/groups/DatacenterGroup.js create mode 100644 frontend/src/components/app/map/groups/GridGroup.js create mode 100644 frontend/src/components/app/map/groups/RackGroup.js create mode 100644 frontend/src/components/app/map/groups/RoomGroup.js create mode 100644 frontend/src/components/app/map/groups/TileGroup.js create mode 100644 frontend/src/components/app/map/groups/WallGroup.js create mode 100644 frontend/src/components/app/map/layers/HoverLayerComponent.js create mode 100644 frontend/src/components/app/map/layers/MapLayerComponent.js create mode 100644 frontend/src/components/app/map/layers/ObjectHoverLayerComponent.js create mode 100644 frontend/src/components/app/map/layers/RoomHoverLayerComponent.js (limited to 'frontend/src/components/app/map') diff --git a/frontend/src/components/app/map/LoadingScreen.js b/frontend/src/components/app/map/LoadingScreen.js new file mode 100644 index 00000000..9f379e0b --- /dev/null +++ b/frontend/src/components/app/map/LoadingScreen.js @@ -0,0 +1,11 @@ +import React from "react"; +import FontAwesome from "react-fontawesome"; + +const LoadingScreen = () => ( +
+ + Loading your datacenter... +
+); + +export default LoadingScreen; diff --git a/frontend/src/components/app/map/MapConstants.js b/frontend/src/components/app/map/MapConstants.js new file mode 100644 index 00000000..32438b5e --- /dev/null +++ b/frontend/src/components/app/map/MapConstants.js @@ -0,0 +1,29 @@ +export const MAP_SIZE = 50; +export const TILE_SIZE_IN_PIXELS = 100; +export const TILE_SIZE_IN_METERS = 0.5; +export const MAP_SIZE_IN_PIXELS = MAP_SIZE * TILE_SIZE_IN_PIXELS; + +export const OBJECT_MARGIN_IN_PIXELS = TILE_SIZE_IN_PIXELS / 5; +export const TILE_PLUS_MARGIN_IN_PIXELS = TILE_SIZE_IN_PIXELS / 3; +export const OBJECT_SIZE_IN_PIXELS = + TILE_SIZE_IN_PIXELS - OBJECT_MARGIN_IN_PIXELS * 2; + +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 TILE_PLUS_WIDTH_IN_PIXELS = TILE_SIZE_IN_PIXELS / 10; + +export const SIDEBAR_WIDTH = 350; +export const VIEWPORT_PADDING = 50; + +export const RACK_FILL_ICON_WIDTH = OBJECT_SIZE_IN_PIXELS / 3; +export const RACK_FILL_ICON_OPACITY = 0.8; + +export const MAP_MOVE_PIXELS_PER_EVENT = 20; +export const MAP_SCALE_PER_EVENT = 1.1; +export const MAP_MIN_SCALE = 0.5; +export const MAP_MAX_SCALE = 1.5; + +export const MAX_NUM_UNITS_PER_MACHINE = 4; +export const DEFAULT_RACK_SLOT_CAPACITY = 42; +export const DEFAULT_RACK_POWER_CAPACITY = 10000; diff --git a/frontend/src/components/app/map/MapStageComponent.js b/frontend/src/components/app/map/MapStageComponent.js new file mode 100644 index 00000000..67b3349c --- /dev/null +++ b/frontend/src/components/app/map/MapStageComponent.js @@ -0,0 +1,126 @@ +import React from "react"; +import { Stage } from "react-konva"; +import { Shortcuts } from "react-shortcuts"; +import MapLayer from "../../../containers/app/map/layers/MapLayer"; +import ObjectHoverLayer from "../../../containers/app/map/layers/ObjectHoverLayer"; +import RoomHoverLayer from "../../../containers/app/map/layers/RoomHoverLayer"; +import jQuery from "../../../util/jquery"; +import { NAVBAR_HEIGHT } from "../../navigation/Navbar"; +import { MAP_MOVE_PIXELS_PER_EVENT } from "./MapConstants"; +import { Provider } from "react-redux"; +import { store } from "../../../store/configure-store"; + +class MapStageComponent extends React.Component { + state = { + mouseX: 0, + mouseY: 0 + }; + + constructor(props) { + super(props); + + this.updateDimensions = this.updateDimensions.bind(this); + this.updateScale = this.updateScale.bind(this); + } + + componentWillMount() { + this.updateDimensions(); + } + + componentDidMount() { + window.addEventListener("resize", this.updateDimensions); + window.addEventListener("wheel", this.updateScale); + + window["exportCanvasToImage"] = () => { + const download = document.createElement("a"); + download.href = this.stage.getStage().toDataURL(); + download.download = "opendc-canvas-export-" + Date.now() + ".png"; + download.click(); + }; + } + + componentWillUnmount() { + window.removeEventListener("resize", this.updateDimensions); + window.removeEventListener("wheel", this.updateScale); + } + + updateDimensions() { + this.props.setMapDimensions( + jQuery(window).width(), + jQuery(window).height() - NAVBAR_HEIGHT + ); + } + + updateScale(e) { + e.preventDefault(); + this.props.zoomInOnPosition( + e.deltaY < 0, + this.state.mouseX, + this.state.mouseY + ); + } + + 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.props.setMapPositionWithBoundsCheck( + this.props.mapPosition.x + deltaX, + this.props.mapPosition.y + deltaY + ); + } + + 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/frontend/src/components/app/map/controls/ExportCanvasComponent.js b/frontend/src/components/app/map/controls/ExportCanvasComponent.js new file mode 100644 index 00000000..ee934f21 --- /dev/null +++ b/frontend/src/components/app/map/controls/ExportCanvasComponent.js @@ -0,0 +1,13 @@ +import React from "react"; + +const ExportCanvasComponent = () => ( + +); + +export default ExportCanvasComponent; diff --git a/frontend/src/components/app/map/controls/ScaleIndicatorComponent.js b/frontend/src/components/app/map/controls/ScaleIndicatorComponent.js new file mode 100644 index 00000000..b7b5cc36 --- /dev/null +++ b/frontend/src/components/app/map/controls/ScaleIndicatorComponent.js @@ -0,0 +1,14 @@ +import React from "react"; +import { TILE_SIZE_IN_METERS, TILE_SIZE_IN_PIXELS } from "../MapConstants"; +import "./ScaleIndicatorComponent.css"; + +const ScaleIndicatorComponent = ({ scale }) => ( +
+ {TILE_SIZE_IN_METERS}m +
+); + +export default ScaleIndicatorComponent; diff --git a/frontend/src/components/app/map/controls/ScaleIndicatorComponent.sass b/frontend/src/components/app/map/controls/ScaleIndicatorComponent.sass new file mode 100644 index 00000000..f2d2b55b --- /dev/null +++ b/frontend/src/components/app/map/controls/ScaleIndicatorComponent.sass @@ -0,0 +1,9 @@ +.scale-indicator + position: absolute + right: 10px + bottom: 10px + z-index: 50 + + border: solid 2px #212529 + border-top: none + border-left: none diff --git a/frontend/src/components/app/map/controls/ToolPanelComponent.js b/frontend/src/components/app/map/controls/ToolPanelComponent.js new file mode 100644 index 00000000..605e9887 --- /dev/null +++ b/frontend/src/components/app/map/controls/ToolPanelComponent.js @@ -0,0 +1,13 @@ +import React from "react"; +import ZoomControlContainer from "../../../../containers/app/map/controls/ZoomControlContainer"; +import ExportCanvasComponent from "./ExportCanvasComponent"; +import "./ToolPanelComponent.css"; + +const ToolPanelComponent = () => ( +
+ + +
+); + +export default ToolPanelComponent; diff --git a/frontend/src/components/app/map/controls/ToolPanelComponent.sass b/frontend/src/components/app/map/controls/ToolPanelComponent.sass new file mode 100644 index 00000000..996712b3 --- /dev/null +++ b/frontend/src/components/app/map/controls/ToolPanelComponent.sass @@ -0,0 +1,5 @@ +.tool-panel + position: absolute + left: 10px + bottom: 10px + z-index: 50 diff --git a/frontend/src/components/app/map/controls/ZoomControlComponent.js b/frontend/src/components/app/map/controls/ZoomControlComponent.js new file mode 100644 index 00000000..e1b7491e --- /dev/null +++ b/frontend/src/components/app/map/controls/ZoomControlComponent.js @@ -0,0 +1,24 @@ +import React from "react"; + +const ZoomControlComponent = ({ zoomInOnCenter }) => { + return ( + + + + + ); +}; + +export default ZoomControlComponent; diff --git a/frontend/src/components/app/map/elements/Backdrop.js b/frontend/src/components/app/map/elements/Backdrop.js new file mode 100644 index 00000000..57414463 --- /dev/null +++ b/frontend/src/components/app/map/elements/Backdrop.js @@ -0,0 +1,16 @@ +import React from "react"; +import { Rect } from "react-konva"; +import { BACKDROP_COLOR } from "../../../../util/colors"; +import { MAP_SIZE_IN_PIXELS } from "../MapConstants"; + +const Backdrop = () => ( + +); + +export default Backdrop; diff --git a/frontend/src/components/app/map/elements/GrayLayer.js b/frontend/src/components/app/map/elements/GrayLayer.js new file mode 100644 index 00000000..28fadd8a --- /dev/null +++ b/frontend/src/components/app/map/elements/GrayLayer.js @@ -0,0 +1,17 @@ +import React from "react"; +import { Rect } from "react-konva"; +import { GRAYED_OUT_AREA_COLOR } from "../../../../util/colors"; +import { MAP_SIZE_IN_PIXELS } from "../MapConstants"; + +const GrayLayer = ({ onClick }) => ( + +); + +export default GrayLayer; diff --git a/frontend/src/components/app/map/elements/HoverTile.js b/frontend/src/components/app/map/elements/HoverTile.js new file mode 100644 index 00000000..42e6547c --- /dev/null +++ b/frontend/src/components/app/map/elements/HoverTile.js @@ -0,0 +1,30 @@ +import PropTypes from "prop-types"; +import React from "react"; +import { Rect } from "react-konva"; +import { + ROOM_HOVER_INVALID_COLOR, + ROOM_HOVER_VALID_COLOR +} from "../../../../util/colors"; +import { TILE_SIZE_IN_PIXELS } from "../MapConstants"; + +const HoverTile = ({ pixelX, pixelY, isValid, scale, onClick }) => ( + +); + +HoverTile.propTypes = { + pixelX: PropTypes.number.isRequired, + pixelY: PropTypes.number.isRequired, + isValid: PropTypes.bool.isRequired, + onClick: PropTypes.func.isRequired +}; + +export default HoverTile; diff --git a/frontend/src/components/app/map/elements/ImageComponent.js b/frontend/src/components/app/map/elements/ImageComponent.js new file mode 100644 index 00000000..cf41ddfe --- /dev/null +++ b/frontend/src/components/app/map/elements/ImageComponent.js @@ -0,0 +1,48 @@ +import PropTypes from "prop-types"; +import React from "react"; +import { Image } from "react-konva"; + +class ImageComponent extends React.Component { + static imageCaches = {}; + static propTypes = { + src: PropTypes.string.isRequired, + x: PropTypes.number.isRequired, + y: PropTypes.number.isRequired, + width: PropTypes.number.isRequired, + height: PropTypes.number.isRequired, + opacity: PropTypes.number.isRequired + }; + + state = { + image: null + }; + + componentDidMount() { + if (ImageComponent.imageCaches[this.props.src]) { + this.setState({ image: ImageComponent.imageCaches[this.props.src] }); + return; + } + + const image = new window.Image(); + image.src = this.props.src; + image.onload = () => { + this.setState({ image }); + ImageComponent.imageCaches[this.props.src] = image; + }; + } + + render() { + return ( + + ); + } +} + +export default ImageComponent; diff --git a/frontend/src/components/app/map/elements/RackFillBar.js b/frontend/src/components/app/map/elements/RackFillBar.js new file mode 100644 index 00000000..43701d97 --- /dev/null +++ b/frontend/src/components/app/map/elements/RackFillBar.js @@ -0,0 +1,89 @@ +import PropTypes from "prop-types"; +import React from "react"; +import { Group, Rect } from "react-konva"; +import { + RACK_ENERGY_BAR_BACKGROUND_COLOR, + RACK_ENERGY_BAR_FILL_COLOR, + RACK_SPACE_BAR_BACKGROUND_COLOR, + RACK_SPACE_BAR_FILL_COLOR +} from "../../../../util/colors"; +import { + OBJECT_BORDER_WIDTH_IN_PIXELS, + OBJECT_MARGIN_IN_PIXELS, + RACK_FILL_ICON_OPACITY, + RACK_FILL_ICON_WIDTH, + TILE_SIZE_IN_PIXELS +} from "../MapConstants"; +import ImageComponent from "./ImageComponent"; + +const RackFillBar = ({ positionX, positionY, type, fillFraction }) => { + const halfOfObjectBorderWidth = OBJECT_BORDER_WIDTH_IN_PIXELS / 2; + const x = + positionX * TILE_SIZE_IN_PIXELS + + OBJECT_MARGIN_IN_PIXELS + + (type === "space" + ? halfOfObjectBorderWidth + : 0.5 * (TILE_SIZE_IN_PIXELS - 2 * OBJECT_MARGIN_IN_PIXELS)); + const startY = + positionY * TILE_SIZE_IN_PIXELS + + OBJECT_MARGIN_IN_PIXELS + + halfOfObjectBorderWidth; + const width = + 0.5 * (TILE_SIZE_IN_PIXELS - OBJECT_MARGIN_IN_PIXELS * 2) - + halfOfObjectBorderWidth; + const fullHeight = + TILE_SIZE_IN_PIXELS - + OBJECT_MARGIN_IN_PIXELS * 2 - + OBJECT_BORDER_WIDTH_IN_PIXELS; + + const fractionHeight = fillFraction * fullHeight; + const fractionY = + (positionY + 1) * TILE_SIZE_IN_PIXELS - + OBJECT_MARGIN_IN_PIXELS - + halfOfObjectBorderWidth - + fractionHeight; + + return ( + + + + + + ); +}; + +RackFillBar.propTypes = { + positionX: PropTypes.number.isRequired, + positionY: PropTypes.number.isRequired, + type: PropTypes.string.isRequired, + fillFraction: PropTypes.number.isRequired +}; + +export default RackFillBar; diff --git a/frontend/src/components/app/map/elements/RoomTile.js b/frontend/src/components/app/map/elements/RoomTile.js new file mode 100644 index 00000000..71c3bf15 --- /dev/null +++ b/frontend/src/components/app/map/elements/RoomTile.js @@ -0,0 +1,20 @@ +import React from "react"; +import { Rect } from "react-konva"; +import Shapes from "../../../../shapes/index"; +import { TILE_SIZE_IN_PIXELS } from "../MapConstants"; + +const RoomTile = ({ tile, color }) => ( + +); + +RoomTile.propTypes = { + tile: Shapes.Tile +}; + +export default RoomTile; diff --git a/frontend/src/components/app/map/elements/TileObject.js b/frontend/src/components/app/map/elements/TileObject.js new file mode 100644 index 00000000..c1b631db --- /dev/null +++ b/frontend/src/components/app/map/elements/TileObject.js @@ -0,0 +1,29 @@ +import PropTypes from "prop-types"; +import React from "react"; +import { Rect } from "react-konva"; +import { OBJECT_BORDER_COLOR } from "../../../../util/colors"; +import { + OBJECT_BORDER_WIDTH_IN_PIXELS, + OBJECT_MARGIN_IN_PIXELS, + TILE_SIZE_IN_PIXELS +} from "../MapConstants"; + +const TileObject = ({ positionX, positionY, color }) => ( + +); + +TileObject.propTypes = { + positionX: PropTypes.number.isRequired, + positionY: PropTypes.number.isRequired, + color: PropTypes.string.isRequired +}; + +export default TileObject; diff --git a/frontend/src/components/app/map/elements/TilePlusIcon.js b/frontend/src/components/app/map/elements/TilePlusIcon.js new file mode 100644 index 00000000..06377152 --- /dev/null +++ b/frontend/src/components/app/map/elements/TilePlusIcon.js @@ -0,0 +1,52 @@ +import PropTypes from "prop-types"; +import React from "react"; +import { Group, Line } from "react-konva"; +import { TILE_PLUS_COLOR } from "../../../../util/colors"; +import { + TILE_PLUS_MARGIN_IN_PIXELS, + TILE_PLUS_WIDTH_IN_PIXELS, + TILE_SIZE_IN_PIXELS +} from "../MapConstants"; + +const TilePlusIcon = ({ pixelX, pixelY, mapScale }) => { + const linePoints = [ + [ + 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 * 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 ( + + {linePoints.map((points, index) => ( + + ))} + + ); +}; + +TilePlusIcon.propTypes = { + pixelX: PropTypes.number, + pixelY: PropTypes.number, + mapScale: PropTypes.number +}; + +export default TilePlusIcon; diff --git a/frontend/src/components/app/map/elements/WallSegment.js b/frontend/src/components/app/map/elements/WallSegment.js new file mode 100644 index 00000000..c5011656 --- /dev/null +++ b/frontend/src/components/app/map/elements/WallSegment.js @@ -0,0 +1,39 @@ +import React from "react"; +import { Line } from "react-konva"; +import Shapes from "../../../../shapes/index"; +import { WALL_COLOR } from "../../../../util/colors"; +import { TILE_SIZE_IN_PIXELS, WALL_WIDTH_IN_PIXELS } from "../MapConstants"; + +const WallSegment = ({ wallSegment }) => { + let points; + if (wallSegment.isHorizontal) { + points = [ + wallSegment.startPosX * TILE_SIZE_IN_PIXELS, + wallSegment.startPosY * TILE_SIZE_IN_PIXELS, + (wallSegment.startPosX + wallSegment.length) * TILE_SIZE_IN_PIXELS, + wallSegment.startPosY * TILE_SIZE_IN_PIXELS + ]; + } else { + points = [ + wallSegment.startPosX * TILE_SIZE_IN_PIXELS, + wallSegment.startPosY * TILE_SIZE_IN_PIXELS, + wallSegment.startPosX * TILE_SIZE_IN_PIXELS, + (wallSegment.startPosY + wallSegment.length) * TILE_SIZE_IN_PIXELS + ]; + } + + return ( + + ); +}; + +WallSegment.propTypes = { + wallSegment: Shapes.WallSegment +}; + +export default WallSegment; diff --git a/frontend/src/components/app/map/groups/DatacenterGroup.js b/frontend/src/components/app/map/groups/DatacenterGroup.js new file mode 100644 index 00000000..51e32db6 --- /dev/null +++ b/frontend/src/components/app/map/groups/DatacenterGroup.js @@ -0,0 +1,40 @@ +import React from "react"; +import { Group } from "react-konva"; +import GrayContainer from "../../../../containers/app/map/GrayContainer"; +import RoomContainer from "../../../../containers/app/map/RoomContainer"; +import Shapes from "../../../../shapes/index"; + +const DatacenterGroup = ({ datacenter, interactionLevel }) => { + if (!datacenter) { + return ; + } + + if (interactionLevel.mode === "BUILDING") { + return ( + + {datacenter.roomIds.map(roomId => ( + + ))} + + ); + } + + return ( + + {datacenter.roomIds + .filter(roomId => roomId !== interactionLevel.roomId) + .map(roomId => )} + {interactionLevel.mode === "ROOM" ? : null} + {datacenter.roomIds + .filter(roomId => roomId === interactionLevel.roomId) + .map(roomId => )} + + ); +}; + +DatacenterGroup.propTypes = { + datacenter: Shapes.Datacenter, + interactionLevel: Shapes.InteractionLevel +}; + +export default DatacenterGroup; diff --git a/frontend/src/components/app/map/groups/GridGroup.js b/frontend/src/components/app/map/groups/GridGroup.js new file mode 100644 index 00000000..bbb1eb68 --- /dev/null +++ b/frontend/src/components/app/map/groups/GridGroup.js @@ -0,0 +1,41 @@ +import React from "react"; +import { Group, Line } from "react-konva"; +import { GRID_COLOR } from "../../../../util/colors"; +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, index) => ( + + ))} + +); + +export default GridGroup; diff --git a/frontend/src/components/app/map/groups/RackGroup.js b/frontend/src/components/app/map/groups/RackGroup.js new file mode 100644 index 00000000..69d6ac10 --- /dev/null +++ b/frontend/src/components/app/map/groups/RackGroup.js @@ -0,0 +1,43 @@ +import React from "react"; +import { Group } from "react-konva"; +import RackEnergyFillContainer from "../../../../containers/app/map/RackEnergyFillContainer"; +import RackSpaceFillContainer from "../../../../containers/app/map/RackSpaceFillContainer"; +import Shapes from "../../../../shapes/index"; +import { RACK_BACKGROUND_COLOR } from "../../../../util/colors"; +import { convertLoadToSimulationColor } from "../../../../util/simulation-load"; +import TileObject from "../elements/TileObject"; + +const RackGroup = ({ tile, inSimulation, rackLoad }) => { + let color = RACK_BACKGROUND_COLOR; + if (inSimulation && rackLoad >= 0) { + color = convertLoadToSimulationColor(rackLoad); + } + + return ( + + + + + + + + ); +}; + +RackGroup.propTypes = { + tile: Shapes.Tile +}; + +export default RackGroup; diff --git a/frontend/src/components/app/map/groups/RoomGroup.js b/frontend/src/components/app/map/groups/RoomGroup.js new file mode 100644 index 00000000..c8f0d3db --- /dev/null +++ b/frontend/src/components/app/map/groups/RoomGroup.js @@ -0,0 +1,56 @@ +import React from "react"; +import { Group } from "react-konva"; +import GrayContainer from "../../../../containers/app/map/GrayContainer"; +import TileContainer from "../../../../containers/app/map/TileContainer"; +import WallContainer from "../../../../containers/app/map/WallContainer"; +import Shapes from "../../../../shapes/index"; + +const RoomGroup = ({ + room, + interactionLevel, + currentRoomInConstruction, + onClick +}) => { + if (currentRoomInConstruction === room.id) { + return ( + + {room.tileIds.map(tileId => ( + + ))} + + ); + } + + return ( + + {(() => { + if ( + (interactionLevel.mode === "RACK" || + interactionLevel.mode === "MACHINE") && + interactionLevel.roomId === room.id + ) { + return [ + room.tileIds + .filter(tileId => tileId !== interactionLevel.tileId) + .map(tileId => ), + , + room.tileIds + .filter(tileId => tileId === interactionLevel.tileId) + .map(tileId => ) + ]; + } else { + return room.tileIds.map(tileId => ( + + )); + } + })()} + + + ); +}; + +RoomGroup.propTypes = { + room: Shapes.Room +}; + +export default RoomGroup; diff --git a/frontend/src/components/app/map/groups/TileGroup.js b/frontend/src/components/app/map/groups/TileGroup.js new file mode 100644 index 00000000..8f3953d7 --- /dev/null +++ b/frontend/src/components/app/map/groups/TileGroup.js @@ -0,0 +1,43 @@ +import PropTypes from "prop-types"; +import React from "react"; +import { Group } from "react-konva"; +import RackContainer from "../../../../containers/app/map/RackContainer"; +import Shapes from "../../../../shapes/index"; +import { + ROOM_DEFAULT_COLOR, + ROOM_IN_CONSTRUCTION_COLOR +} from "../../../../util/colors"; +import { convertLoadToSimulationColor } from "../../../../util/simulation-load"; +import RoomTile from "../elements/RoomTile"; + +const TileGroup = ({ tile, newTile, inSimulation, roomLoad, onClick }) => { + let tileObject; + switch (tile.objectType) { + case "RACK": + tileObject = ; + break; + default: + tileObject = null; + } + + let color = ROOM_DEFAULT_COLOR; + if (newTile) { + color = ROOM_IN_CONSTRUCTION_COLOR; + } else if (inSimulation && roomLoad >= 0) { + color = convertLoadToSimulationColor(roomLoad); + } + + return ( + onClick(tile)}> + + {tileObject} + + ); +}; + +TileGroup.propTypes = { + tile: Shapes.Tile, + newTile: PropTypes.bool +}; + +export default TileGroup; diff --git a/frontend/src/components/app/map/groups/WallGroup.js b/frontend/src/components/app/map/groups/WallGroup.js new file mode 100644 index 00000000..43de66e8 --- /dev/null +++ b/frontend/src/components/app/map/groups/WallGroup.js @@ -0,0 +1,22 @@ +import PropTypes from "prop-types"; +import React from "react"; +import { Group } from "react-konva"; +import Shapes from "../../../../shapes/index"; +import { deriveWallLocations } from "../../../../util/tile-calculations"; +import WallSegment from "../elements/WallSegment"; + +const WallGroup = ({ tiles }) => { + return ( + + {deriveWallLocations(tiles).map((wallSegment, index) => ( + + ))} + + ); +}; + +WallGroup.propTypes = { + tiles: PropTypes.arrayOf(Shapes.Tile).isRequired +}; + +export default WallGroup; diff --git a/frontend/src/components/app/map/layers/HoverLayerComponent.js b/frontend/src/components/app/map/layers/HoverLayerComponent.js new file mode 100644 index 00000000..c39532f1 --- /dev/null +++ b/frontend/src/components/app/map/layers/HoverLayerComponent.js @@ -0,0 +1,85 @@ +import PropTypes from "prop-types"; +import React from "react"; +import { Layer } from "react-konva"; +import HoverTile from "../elements/HoverTile"; +import { TILE_SIZE_IN_PIXELS } from "../MapConstants"; + +class HoverLayerComponent extends React.Component { + static propTypes = { + mouseX: PropTypes.number.isRequired, + mouseY: PropTypes.number.isRequired, + mapPosition: PropTypes.object.isRequired, + mapScale: PropTypes.number.isRequired, + isEnabled: PropTypes.func.isRequired, + onClick: PropTypes.func.isRequired + }; + + state = { + positionX: -1, + positionY: -1, + validity: false + }; + + componentDidUpdate() { + if (!this.props.isEnabled()) { + return; + } + + 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) + }); + } + } + + render() { + if (!this.props.isEnabled()) { + return ; + } + + 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.mapScale + }) + : undefined} + + ); + } +} + +export default HoverLayerComponent; diff --git a/frontend/src/components/app/map/layers/MapLayerComponent.js b/frontend/src/components/app/map/layers/MapLayerComponent.js new file mode 100644 index 00000000..6ad3cb88 --- /dev/null +++ b/frontend/src/components/app/map/layers/MapLayerComponent.js @@ -0,0 +1,22 @@ +import React from "react"; +import { Group, Layer } from "react-konva"; +import DatacenterContainer from "../../../../containers/app/map/DatacenterContainer"; +import Backdrop from "../elements/Backdrop"; +import GridGroup from "../groups/GridGroup"; + +const MapLayerComponent = ({ mapPosition, mapScale }) => ( + + + + + + + +); + +export default MapLayerComponent; diff --git a/frontend/src/components/app/map/layers/ObjectHoverLayerComponent.js b/frontend/src/components/app/map/layers/ObjectHoverLayerComponent.js new file mode 100644 index 00000000..e7342d3c --- /dev/null +++ b/frontend/src/components/app/map/layers/ObjectHoverLayerComponent.js @@ -0,0 +1,11 @@ +import React from "react"; +import TilePlusIcon from "../elements/TilePlusIcon"; +import HoverLayerComponent from "./HoverLayerComponent"; + +const ObjectHoverLayerComponent = props => ( + + + +); + +export default ObjectHoverLayerComponent; diff --git a/frontend/src/components/app/map/layers/RoomHoverLayerComponent.js b/frontend/src/components/app/map/layers/RoomHoverLayerComponent.js new file mode 100644 index 00000000..feea5ae5 --- /dev/null +++ b/frontend/src/components/app/map/layers/RoomHoverLayerComponent.js @@ -0,0 +1,6 @@ +import React from "react"; +import HoverLayerComponent from "./HoverLayerComponent"; + +const RoomHoverLayerComponent = props => ; + +export default RoomHoverLayerComponent; -- cgit v1.2.3