diff --git a/src/components/map/Map.js b/src/components/map/Map.js index ba2fb2d..78fc6bf 100644 --- a/src/components/map/Map.js +++ b/src/components/map/Map.js @@ -10,6 +10,7 @@ import MapGrid from "./MapGrid"; import MapMeasure from "./MapMeasure"; import MapLoadingOverlay from "./MapLoadingOverlay"; import NetworkedMapPointer from "../../network/NetworkedMapPointer"; +import MapNotes from "./MapNotes"; import TokenDataContext from "../../contexts/TokenDataContext"; import SettingsContext from "../../contexts/SettingsContext"; @@ -133,6 +134,7 @@ function Map({ disabledControls.push("pan"); disabledControls.push("measure"); disabledControls.push("pointer"); + disabledControls.push("note"); } if (!allowFogDrawing) { disabledControls.push("fog"); @@ -350,6 +352,15 @@ function Map({ /> ); + const mapNotes = ( + + ); + return ( {mapGrid} + {mapNotes} {mapDrawing} {mapTokens} {mapFog} diff --git a/src/components/map/MapControls.js b/src/components/map/MapControls.js index a912201..80e4840 100644 --- a/src/components/map/MapControls.js +++ b/src/components/map/MapControls.js @@ -9,6 +9,7 @@ import SelectMapButton from "./SelectMapButton"; import FogToolSettings from "./controls/FogToolSettings"; import DrawingToolSettings from "./controls/DrawingToolSettings"; import MeasureToolSettings from "./controls/MeasureToolSettings"; +import NoteToolSettings from "./controls/NoteToolSettings"; import PanToolIcon from "../../icons/PanToolIcon"; import FogToolIcon from "../../icons/FogToolIcon"; @@ -18,6 +19,7 @@ import ExpandMoreIcon from "../../icons/ExpandMoreIcon"; import PointerToolIcon from "../../icons/PointerToolIcon"; import FullScreenIcon from "../../icons/FullScreenIcon"; import FullScreenExitIcon from "../../icons/FullScreenExitIcon"; +import NoteToolIcon from "../../icons/NoteToolIcon"; import useSetting from "../../helpers/useSetting"; @@ -66,8 +68,14 @@ function MapContols({ icon: , title: "Pointer Tool (Q)", }, + note: { + id: "note", + icon: , + title: "Note Tool (N)", + SettingsComponent: NoteToolSettings, + }, }; - const tools = ["pan", "fog", "drawing", "measure", "pointer"]; + const tools = ["pan", "fog", "drawing", "measure", "pointer", "note"]; const sections = [ { diff --git a/src/components/map/MapInteraction.js b/src/components/map/MapInteraction.js index b750f6d..6be1a86 100644 --- a/src/components/map/MapInteraction.js +++ b/src/components/map/MapInteraction.js @@ -135,6 +135,9 @@ function MapInteraction({ if (event.key === "q" && !disabledControls.includes("pointer")) { onSelectedToolChange("pointer"); } + if (event.key === "n" && !disabledControls.includes("note")) { + onSelectedToolChange("note"); + } } function handleKeyUp(event) { @@ -153,6 +156,10 @@ function MapInteraction({ return "move"; case "fog": case "drawing": + case "note": + return settings.settings[tool].type === "move" + ? "pointer" + : "crosshair"; case "measure": case "pointer": return "crosshair"; diff --git a/src/components/map/MapNotes.js b/src/components/map/MapNotes.js new file mode 100644 index 0000000..8d3ba34 --- /dev/null +++ b/src/components/map/MapNotes.js @@ -0,0 +1,87 @@ +import React, { useContext, useState, useEffect } from "react"; +import { Group, Rect } from "react-konva"; + +import MapInteractionContext from "../../contexts/MapInteractionContext"; +import MapStageContext from "../../contexts/MapStageContext"; + +import { getBrushPositionForTool } from "../../helpers/drawing"; +import { getRelativePointerPositionNormalized } from "../../helpers/konva"; + +const defaultNoteSize = 2; + +function MapNotes({ map, selectedToolSettings, active, gridSize }) { + const { mapWidth, mapHeight, interactionEmitter } = useContext( + MapInteractionContext + ); + const mapStageRef = useContext(MapStageContext); + const [isBrushDown, setIsBrushDown] = useState(false); + const [brushPosition, setBrushPosition] = useState({ x: 0, y: 0 }); + + useEffect(() => { + if (!active) { + return; + } + const mapStage = mapStageRef.current; + + function getBrushPosition() { + const mapImage = mapStage.findOne("#mapImage"); + return getBrushPositionForTool( + map, + getRelativePointerPositionNormalized(mapImage), + map.snapToGrid, + false, + gridSize, + [] + ); + } + + function handleBrushDown() { + setBrushPosition(getBrushPosition()); + setIsBrushDown(true); + } + + function handleBrushMove() { + setBrushPosition(getBrushPosition()); + setIsBrushDown(true); + } + + function handleBrushUp() { + setBrushPosition({ x: 0, y: 0 }); + setIsBrushDown(false); + } + + interactionEmitter.on("dragStart", handleBrushDown); + interactionEmitter.on("drag", handleBrushMove); + interactionEmitter.on("dragEnd", handleBrushUp); + + return () => { + interactionEmitter.off("dragStart", handleBrushDown); + interactionEmitter.off("drag", handleBrushMove); + interactionEmitter.off("dragEnd", handleBrushUp); + }; + }); + + const noteWidth = map && (mapWidth / map.grid.size.x) * defaultNoteSize; + const noteHeight = map && (mapHeight / map.grid.size.y) * defaultNoteSize; + + return ( + + {isBrushDown && ( + + )} + + ); +} + +export default MapNotes; diff --git a/src/components/map/controls/NoteToolSettings.js b/src/components/map/controls/NoteToolSettings.js new file mode 100644 index 0000000..869fd01 --- /dev/null +++ b/src/components/map/controls/NoteToolSettings.js @@ -0,0 +1,47 @@ +import React from "react"; +import { Flex } from "theme-ui"; + +import ToolSection from "./ToolSection"; +import NoteAddIcon from "../../../icons/NoteAddIcon"; +import MoveIcon from "../../../icons/MoveIcon"; + +import useKeyboard from "../../../helpers/useKeyboard"; + +function NoteToolSettings({ settings, onSettingChange }) { + // Keyboard shortcuts + function handleKeyDown({ key }) { + if (key === "a") { + onSettingChange({ type: "add" }); + } else if (key === "v") { + onSettingChange({ type: "move" }); + } + } + + useKeyboard(handleKeyDown); + + const tools = [ + { + id: "add", + title: "Add Note (A)", + isSelected: settings.type === "add", + icon: , + }, + { + id: "move", + title: "Move Note (V)", + isSelected: settings.type === "move", + icon: , + }, + ]; + + return ( + + onSettingChange({ type: tool.id })} + /> + + ); +} + +export default NoteToolSettings; diff --git a/src/icons/MoveIcon.js b/src/icons/MoveIcon.js new file mode 100644 index 0000000..233a691 --- /dev/null +++ b/src/icons/MoveIcon.js @@ -0,0 +1,19 @@ +import React from "react"; + +function MoveIcon() { + return ( + + + + + ); +} + +export default MoveIcon; diff --git a/src/icons/NoteAddIcon.js b/src/icons/NoteAddIcon.js new file mode 100644 index 0000000..6ad5e28 --- /dev/null +++ b/src/icons/NoteAddIcon.js @@ -0,0 +1,23 @@ +import React from "react"; + +function NoteAddIcon() { + return ( + + + + + + + + + ); +} + +export default NoteAddIcon; diff --git a/src/icons/NoteToolIcon.js b/src/icons/NoteToolIcon.js new file mode 100644 index 0000000..e9bb0e5 --- /dev/null +++ b/src/icons/NoteToolIcon.js @@ -0,0 +1,19 @@ +import React from "react"; + +function NoteToolIcon() { + return ( + + + + + ); +} + +export default NoteToolIcon; diff --git a/src/settings.js b/src/settings.js index 16bfa7c..e865967 100644 --- a/src/settings.js +++ b/src/settings.js @@ -32,6 +32,11 @@ function loadVersions(settings) { ...prev, map: { fullScreen: false, labelSize: 1 }, })); + // v1.7.0 - Added note tool + settings.version(3, (prev) => ({ + ...prev, + note: { type: "add" }, + })); } export function getSettings() {