From f5d1cdf60f6047ce0e046bcf900b0b6cc65ae50b Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Mon, 20 Apr 2020 11:56:56 +1000 Subject: [PATCH] Added map control submenus for brush color and erase all --- src/components/Map.js | 19 ++- src/components/MapControls.js | 228 ++++++++++++++++++++++++---------- src/components/MapDrawing.js | 14 ++- src/components/MapMenu.js | 3 + 4 files changed, 189 insertions(+), 75 deletions(-) diff --git a/src/components/Map.js b/src/components/Map.js index 51a523c..60b67c8 100644 --- a/src/components/Map.js +++ b/src/components/Map.js @@ -43,14 +43,19 @@ function Map({ */ const [selectedTool, setSelectedTool] = useState("pan"); + const [brushColor, setBrushColor] = useState("black"); const [drawnShapes, setDrawnShapes] = useState([]); function handleShapeAdd(shape) { - onMapDraw({ type: "add", shape }); + onMapDraw({ type: "add", shapes: [shape] }); } function handleShapeRemove(shapeId) { - onMapDraw({ type: "remove", shapeId }); + onMapDraw({ type: "remove", shapeIds: [shapeId] }); + } + + function handleShapeRemoveAll() { + onMapDraw({ type: "remove", shapeIds: drawnShapes.map((s) => s.id) }); } // Replay the draw actions and convert them to shapes for the map drawing @@ -59,10 +64,12 @@ function Map({ for (let i = 0; i <= drawActionIndex; i++) { const action = drawActions[i]; if (action.type === "add") { - shapesById[action.shape.id] = action.shape; + for (let shape of action.shapes) { + shapesById[shape.id] = shape; + } } if (action.type === "remove") { - shapesById = omit(shapesById, [action.shapeId]); + shapesById = omit(shapesById, action.shapeIds); } } setDrawnShapes(Object.values(shapesById)); @@ -267,6 +274,7 @@ function Map({ shapes={drawnShapes} onShapeAdd={handleShapeAdd} onShapeRemove={handleShapeRemove} + brushColor={brushColor} /> {mapTokens} @@ -280,6 +288,9 @@ function Map({ onRedo={onMapDrawRedo} undoDisabled={drawActionIndex < 0} redoDisabled={drawActionIndex === drawActions.length - 1} + brushColor={brushColor} + onBrushColorChange={setBrushColor} + onEraseAll={handleShapeRemoveAll} /> + + {colorOptions.map((color) => ( + onBrushColorChange(color)} + > + {brushColor === color && ( + + )} + + ))} + + + ), + erase: ( + + + + ), + }; + + const [currentSubmenu, setCurrentSubmenu] = useState(null); + const [currentSubmenuOptions, setCurrentSubmenuOptions] = useState({}); + + function handleToolClick(event, tool) { + if (tool !== selectedTool) { + onToolChange(tool); + } else if (subMenus[tool]) { + const toolRect = event.target.getBoundingClientRect(); + setCurrentSubmenu(tool); + setCurrentSubmenuOptions({ + // Align the right of the submenu to the left of the tool and center vertically + left: `${toolRect.left - 4}px`, + top: `${toolRect.bottom - toolRect.height / 2}px`, + style: { transform: "translate(-100%, -50%)" }, + }); + } + } + const divider = ( ); return ( - - setIsExpanded(!isExpanded)} - sx={{ - transform: `rotate(${isExpanded ? "0" : "180deg"})`, - display: "block", - }} - > - - - + - - {divider} onToolChange("pan")} - sx={{ color: selectedTool === "pan" ? "primary" : "text" }} - disabled={disabledTools.includes("pan")} + aria-label={isExpanded ? "Hide Map Controls" : "Show Map Controls"} + title={isExpanded ? "Hide Map Controls" : "Show Map Controls"} + onClick={() => setIsExpanded(!isExpanded)} + sx={{ + transform: `rotate(${isExpanded ? "0" : "180deg"})`, + display: "block", + }} > - + - onToolChange("brush")} - sx={{ color: selectedTool === "brush" ? "primary" : "text" }} - disabled={disabledTools.includes("brush")} + - - - onToolChange("erase")} - sx={{ color: selectedTool === "erase" ? "primary" : "text" }} - disabled={disabledTools.includes("erase")} - > - - - {divider} - onUndo()} - disabled={undoDisabled} - > - - - onRedo()} - disabled={redoDisabled} - > - - - - + + {divider} + handleToolClick(e, "pan")} + sx={{ color: selectedTool === "pan" ? "primary" : "text" }} + disabled={disabledTools.includes("pan")} + > + + + handleToolClick(e, "brush")} + sx={{ color: selectedTool === "brush" ? "primary" : "text" }} + disabled={disabledTools.includes("brush")} + > + + + handleToolClick(e, "erase")} + sx={{ color: selectedTool === "erase" ? "primary" : "text" }} + disabled={disabledTools.includes("erase")} + > + + + {divider} + onUndo()} + disabled={undoDisabled} + > + + + onRedo()} + disabled={redoDisabled} + > + + + + + { + setCurrentSubmenu(null); + setCurrentSubmenuOptions({}); + }} + {...currentSubmenuOptions} + > + {currentSubmenu && subMenus[currentSubmenu]} + + ); } diff --git a/src/components/MapDrawing.js b/src/components/MapDrawing.js index 5a1abd2..b806bc2 100644 --- a/src/components/MapDrawing.js +++ b/src/components/MapDrawing.js @@ -2,6 +2,8 @@ import React, { useRef, useEffect, useState } from "react"; import simplify from "simplify-js"; import shortid from "shortid"; +import colors from "../helpers/colors"; + function MapDrawing({ width, height, @@ -9,6 +11,7 @@ function MapDrawing({ shapes, onShapeAdd, onShapeRemove, + brushColor, }) { const canvasRef = useRef(); const containerRef = useRef(); @@ -70,7 +73,11 @@ function MapDrawing({ if (selectedTool === "brush") { if (brushPoints.length > 0) { const simplifiedPoints = simplify(brushPoints, 0.001); - onShapeAdd({ id: shortid.generate(), points: simplifiedPoints }); + onShapeAdd({ + id: shortid.generate(), + points: simplifiedPoints, + color: brushColor, + }); setBrushPoints([]); } } @@ -118,11 +125,11 @@ function MapDrawing({ hoveredShape = shape; } } - drawPath(path, "#000000", context); + drawPath(path, colors[shape.color], context); } if (selectedTool === "brush" && brushPoints.length > 0) { const path = pointsToPath(brushPoints); - drawPath(path, "#000000", context); + drawPath(path, colors[brushColor], context); } if (hoveredShape) { const path = pointsToPath(hoveredShape.points); @@ -138,6 +145,7 @@ function MapDrawing({ isDrawing, selectedTool, brushPoints, + brushColor, ]); return ( diff --git a/src/components/MapMenu.js b/src/components/MapMenu.js index 7e528dc..5f63b49 100644 --- a/src/components/MapMenu.js +++ b/src/components/MapMenu.js @@ -10,6 +10,7 @@ function MapMenu({ bottom, right, children, + style, }) { function handleModalContent(node) { if (node) { @@ -49,6 +50,7 @@ function MapMenu({ padding: 0, borderRadius: "4px", border: "none", + ...style, }, }} contentRef={handleModalContent} @@ -64,6 +66,7 @@ MapMenu.defaultProps = { left: "initial", right: "initial", bottom: "initial", + style: {}, }; export default MapMenu;