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() {