diff --git a/src/App.js b/src/App.js
index d6bd778..e82e3d2 100644
--- a/src/App.js
+++ b/src/App.js
@@ -15,7 +15,8 @@ import { DatabaseProvider } from "./contexts/DatabaseContext";
import { MapDataProvider } from "./contexts/MapDataContext";
import { TokenDataProvider } from "./contexts/TokenDataContext";
import { MapLoadingProvider } from "./contexts/MapLoadingContext";
-import { SettingsProvider } from "./contexts/SettingsContext.js";
+import { SettingsProvider } from "./contexts/SettingsContext";
+import { KeyboardProvider } from "./contexts/KeyboardContext";
function App() {
return (
@@ -23,34 +24,36 @@ function App() {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/map/MapFog.js b/src/components/map/MapFog.js
index 96bc1df..be9e043 100644
--- a/src/components/map/MapFog.js
+++ b/src/components/map/MapFog.js
@@ -20,6 +20,7 @@ import {
getRelativePointerPositionNormalized,
Tick,
} from "../../helpers/konva";
+import useKeyboard from "../../helpers/useKeyboard";
function MapFog({
map,
@@ -248,44 +249,37 @@ function MapFog({
}, [toolSettings, drawingShape, onShapeSubtract, onShapeAdd]);
// Add keyboard shortcuts
- useEffect(() => {
- function handleKeyDown({ key }) {
- if (key === "Enter" && toolSettings.type === "polygon" && drawingShape) {
- finishDrawingPolygon();
- }
- if (key === "Escape" && drawingShape) {
- setDrawingShape(null);
- }
- if (key === "Alt" && drawingShape) {
- updateShapeColor();
- }
+ function handleKeyDown({ key }) {
+ if (key === "Enter" && toolSettings.type === "polygon" && drawingShape) {
+ finishDrawingPolygon();
}
+ if (key === "Escape" && drawingShape) {
+ setDrawingShape(null);
+ }
+ if (key === "Alt" && drawingShape) {
+ updateShapeColor();
+ }
+ }
- function handleKeyUp({ key }) {
- if (key === "Alt" && drawingShape) {
- updateShapeColor();
+ function handleKeyUp({ key }) {
+ if (key === "Alt" && drawingShape) {
+ updateShapeColor();
+ }
+ }
+
+ function updateShapeColor() {
+ setDrawingShape((prevShape) => {
+ if (!prevShape) {
+ return;
}
- }
+ return {
+ ...prevShape,
+ color: toolSettings.useFogSubtract ? "black" : "red",
+ };
+ });
+ }
- function updateShapeColor() {
- setDrawingShape((prevShape) => {
- if (!prevShape) {
- return;
- }
- return {
- ...prevShape,
- color: toolSettings.useFogSubtract ? "black" : "red",
- };
- });
- }
-
- interactionEmitter.on("keyDown", handleKeyDown);
- interactionEmitter.on("keyUp", handleKeyUp);
- return () => {
- interactionEmitter.off("keyDown", handleKeyDown);
- interactionEmitter.off("keyUp", handleKeyUp);
- };
- }, [finishDrawingPolygon, interactionEmitter, drawingShape, toolSettings]);
+ useKeyboard(handleKeyDown, handleKeyUp);
function handleShapeOver(shape, isDown) {
if (shouldHover && isDown) {
diff --git a/src/components/map/MapInteraction.js b/src/components/map/MapInteraction.js
index 0b1aba5..7194d93 100644
--- a/src/components/map/MapInteraction.js
+++ b/src/components/map/MapInteraction.js
@@ -9,6 +9,7 @@ import normalizeWheel from "normalize-wheel";
import usePreventOverscroll from "../../helpers/usePreventOverscroll";
import useDataSource from "../../helpers/useDataSource";
+import useKeyboard from "../../helpers/useKeyboard";
import { mapSources as defaultMapSources } from "../../maps";
@@ -18,6 +19,7 @@ import MapStageContext, {
} from "../../contexts/MapStageContext";
import AuthContext from "../../contexts/AuthContext";
import SettingsContext from "../../contexts/SettingsContext";
+import KeyboardContext from "../../contexts/KeyboardContext";
const wheelZoomSpeed = -0.001;
const touchZoomSpeed = 0.005;
@@ -206,88 +208,49 @@ function MapInteraction({
stageHeightRef.current = height;
}
- // Added key events to interaction emitter
- useEffect(() => {
- function handleKeyDown(event) {
- // Ignore text input
- if (event.target instanceof HTMLInputElement) {
- return;
- }
- interactionEmitter.emit("keyDown", event);
+ function handleKeyDown(event) {
+ // Change to pan tool when pressing space
+ if (event.key === " " && selectedToolId === "pan") {
+ // Stop active state on pan icon from being selected
+ event.preventDefault();
+ }
+ if (
+ event.key === " " &&
+ selectedToolId !== "pan" &&
+ !disabledControls.includes("pan")
+ ) {
+ event.preventDefault();
+ previousSelectedToolRef.current = selectedToolId;
+ onSelectedToolChange("pan");
}
- function handleKeyUp(event) {
- // Ignore text input
- if (event.target instanceof HTMLInputElement) {
- return;
- }
- interactionEmitter.emit("keyUp", event);
+ // Basic keyboard shortcuts
+ if (event.key === "w" && !disabledControls.includes("pan")) {
+ onSelectedToolChange("pan");
}
-
- document.body.addEventListener("keydown", handleKeyDown);
- document.body.addEventListener("keyup", handleKeyUp);
- document.body.tabIndex = 1;
- return () => {
- document.body.removeEventListener("keydown", handleKeyDown);
- document.body.removeEventListener("keyup", handleKeyUp);
- document.body.tabIndex = 0;
- };
- }, [interactionEmitter]);
-
- // Create default keyboard shortcuts
- useEffect(() => {
- function handleKeyDown(event) {
- // Change to pan tool when pressing space
- if (event.key === " " && selectedToolId === "pan") {
- // Stop active state on pan icon from being selected
- event.preventDefault();
- }
- if (
- event.key === " " &&
- selectedToolId !== "pan" &&
- !disabledControls.includes("pan")
- ) {
- event.preventDefault();
- previousSelectedToolRef.current = selectedToolId;
- onSelectedToolChange("pan");
- }
-
- // Basic keyboard shortcuts
- if (event.key === "w" && !disabledControls.includes("pan")) {
- onSelectedToolChange("pan");
- }
- if (event.key === "d" && !disabledControls.includes("drawing")) {
- onSelectedToolChange("drawing");
- }
- if (event.key === "f" && !disabledControls.includes("fog")) {
- onSelectedToolChange("fog");
- }
- if (event.key === "m" && !disabledControls.includes("measure")) {
- onSelectedToolChange("measure");
- }
- if (event.key === "q" && !disabledControls.includes("pointer")) {
- onSelectedToolChange("pointer");
- }
+ if (event.key === "d" && !disabledControls.includes("drawing")) {
+ onSelectedToolChange("drawing");
}
-
- function handleKeyUp(event) {
- if (event.key === " " && selectedToolId === "pan") {
- onSelectedToolChange(previousSelectedToolRef.current);
- }
+ if (event.key === "f" && !disabledControls.includes("fog")) {
+ onSelectedToolChange("fog");
}
+ if (event.key === "m" && !disabledControls.includes("measure")) {
+ onSelectedToolChange("measure");
+ }
+ if (event.key === "q" && !disabledControls.includes("pointer")) {
+ onSelectedToolChange("pointer");
+ }
+ }
- interactionEmitter.on("keyDown", handleKeyDown);
- interactionEmitter.on("keyUp", handleKeyUp);
- return () => {
- interactionEmitter.off("keyDown", handleKeyDown);
- interactionEmitter.off("keyUp", handleKeyUp);
- };
- }, [
- interactionEmitter,
- onSelectedToolChange,
- disabledControls,
- selectedToolId,
- ]);
+ function handleKeyUp(event) {
+ if (event.key === " " && selectedToolId === "pan") {
+ onSelectedToolChange(previousSelectedToolRef.current);
+ }
+ }
+
+ useKeyboard(handleKeyDown, handleKeyUp);
+ // Get keyboard context to pass to Konva
+ const keyboardValue = useContext(KeyboardContext);
function getCursorForTool(tool) {
switch (tool) {
@@ -360,11 +323,13 @@ function MapInteraction({
{/* Forward auth context to konva elements */}
-
-
- {mapLoaded && children}
-
-
+
+
+
+ {mapLoaded && children}
+
+
+
diff --git a/src/components/map/controls/DrawingToolSettings.js b/src/components/map/controls/DrawingToolSettings.js
index 9f0bb6f..1ff7274 100644
--- a/src/components/map/controls/DrawingToolSettings.js
+++ b/src/components/map/controls/DrawingToolSettings.js
@@ -1,4 +1,4 @@
-import React, { useEffect, useContext } from "react";
+import React, { useEffect } from "react";
import { Flex, IconButton } from "theme-ui";
import { useMedia } from "react-media";
@@ -21,7 +21,7 @@ import RedoButton from "./RedoButton";
import Divider from "../../Divider";
-import MapInteractionContext from "../../../contexts/MapInteractionContext";
+import useKeyboard from "../../../helpers/useKeyboard";
function DrawingToolSettings({
settings,
@@ -29,49 +29,41 @@ function DrawingToolSettings({
onToolAction,
disabledActions,
}) {
- const { interactionEmitter } = useContext(MapInteractionContext);
-
// Keyboard shotcuts
- useEffect(() => {
- function handleKeyDown({ key, ctrlKey, metaKey, shiftKey }) {
- if (key === "b") {
- onSettingChange({ type: "brush" });
- } else if (key === "p") {
- onSettingChange({ type: "paint" });
- } else if (key === "l") {
- onSettingChange({ type: "line" });
- } else if (key === "r") {
- onSettingChange({ type: "rectangle" });
- } else if (key === "c") {
- onSettingChange({ type: "circle" });
- } else if (key === "t") {
- onSettingChange({ type: "triangle" });
- } else if (key === "e") {
- onSettingChange({ type: "erase" });
- } else if (key === "o") {
- onSettingChange({ useBlending: !settings.useBlending });
- } else if (
- (key === "z" || key === "Z") &&
- (ctrlKey || metaKey) &&
- shiftKey &&
- !disabledActions.includes("redo")
- ) {
- onToolAction("mapRedo");
- } else if (
- key === "z" &&
- (ctrlKey || metaKey) &&
- !shiftKey &&
- !disabledActions.includes("undo")
- ) {
- onToolAction("mapUndo");
- }
+ function handleKeyDown({ key, ctrlKey, metaKey, shiftKey }) {
+ if (key === "b") {
+ onSettingChange({ type: "brush" });
+ } else if (key === "p") {
+ onSettingChange({ type: "paint" });
+ } else if (key === "l") {
+ onSettingChange({ type: "line" });
+ } else if (key === "r") {
+ onSettingChange({ type: "rectangle" });
+ } else if (key === "c") {
+ onSettingChange({ type: "circle" });
+ } else if (key === "t") {
+ onSettingChange({ type: "triangle" });
+ } else if (key === "e") {
+ onSettingChange({ type: "erase" });
+ } else if (key === "o") {
+ onSettingChange({ useBlending: !settings.useBlending });
+ } else if (
+ (key === "z" || key === "Z") &&
+ (ctrlKey || metaKey) &&
+ shiftKey &&
+ !disabledActions.includes("redo")
+ ) {
+ onToolAction("mapRedo");
+ } else if (
+ key === "z" &&
+ (ctrlKey || metaKey) &&
+ !shiftKey &&
+ !disabledActions.includes("undo")
+ ) {
+ onToolAction("mapUndo");
}
-
- interactionEmitter.on("keyDown", handleKeyDown);
- return () => {
- interactionEmitter.off("keyDown", handleKeyDown);
- };
- });
+ }
+ useKeyboard(handleKeyDown);
// Change to brush if on erase and it gets disabled
useEffect(() => {
diff --git a/src/components/map/controls/FogToolSettings.js b/src/components/map/controls/FogToolSettings.js
index 09ece28..9db94c3 100644
--- a/src/components/map/controls/FogToolSettings.js
+++ b/src/components/map/controls/FogToolSettings.js
@@ -1,4 +1,4 @@
-import React, { useContext, useEffect } from "react";
+import React from "react";
import { Flex } from "theme-ui";
import { useMedia } from "react-media";
@@ -15,11 +15,11 @@ import FogSubtractIcon from "../../../icons/FogSubtractIcon";
import UndoButton from "./UndoButton";
import RedoButton from "./RedoButton";
+import ToolSection from "./ToolSection";
import Divider from "../../Divider";
-import MapInteractionContext from "../../../contexts/MapInteractionContext";
-import ToolSection from "./ToolSection";
+import useKeyboard from "../../../helpers/useKeyboard";
function BrushToolSettings({
settings,
@@ -27,55 +27,46 @@ function BrushToolSettings({
onToolAction,
disabledActions,
}) {
- const { interactionEmitter } = useContext(MapInteractionContext);
-
// Keyboard shortcuts
- useEffect(() => {
- function handleKeyDown({ key, ctrlKey, metaKey, shiftKey }) {
- if (key === "Alt") {
- onSettingChange({ useFogSubtract: !settings.useFogSubtract });
- } else if (key === "p") {
- onSettingChange({ type: "polygon" });
- } else if (key === "b") {
- onSettingChange({ type: "brush" });
- } else if (key === "t") {
- onSettingChange({ type: "toggle" });
- } else if (key === "r") {
- onSettingChange({ type: "remove" });
- } else if (key === "s") {
- onSettingChange({ useEdgeSnapping: !settings.useEdgeSnapping });
- } else if (key === "f") {
- onSettingChange({ preview: !settings.preview });
- } else if (
- (key === "z" || key === "Z") &&
- (ctrlKey || metaKey) &&
- shiftKey &&
- !disabledActions.includes("redo")
- ) {
- onToolAction("fogRedo");
- } else if (
- key === "z" &&
- (ctrlKey || metaKey) &&
- !shiftKey &&
- !disabledActions.includes("undo")
- ) {
- onToolAction("fogUndo");
- }
+ function handleKeyDown({ key, ctrlKey, metaKey, shiftKey }) {
+ if (key === "Alt") {
+ onSettingChange({ useFogSubtract: !settings.useFogSubtract });
+ } else if (key === "p") {
+ onSettingChange({ type: "polygon" });
+ } else if (key === "b") {
+ onSettingChange({ type: "brush" });
+ } else if (key === "t") {
+ onSettingChange({ type: "toggle" });
+ } else if (key === "r") {
+ onSettingChange({ type: "remove" });
+ } else if (key === "s") {
+ onSettingChange({ useEdgeSnapping: !settings.useEdgeSnapping });
+ } else if (key === "f") {
+ onSettingChange({ preview: !settings.preview });
+ } else if (
+ (key === "z" || key === "Z") &&
+ (ctrlKey || metaKey) &&
+ shiftKey &&
+ !disabledActions.includes("redo")
+ ) {
+ onToolAction("fogRedo");
+ } else if (
+ key === "z" &&
+ (ctrlKey || metaKey) &&
+ !shiftKey &&
+ !disabledActions.includes("undo")
+ ) {
+ onToolAction("fogUndo");
}
+ }
- function handleKeyUp({ key }) {
- if (key === "Alt") {
- onSettingChange({ useFogSubtract: !settings.useFogSubtract });
- }
+ function handleKeyUp({ key }) {
+ if (key === "Alt") {
+ onSettingChange({ useFogSubtract: !settings.useFogSubtract });
}
+ }
- interactionEmitter.on("keyDown", handleKeyDown);
- interactionEmitter.on("keyUp", handleKeyUp);
- return () => {
- interactionEmitter.off("keyDown", handleKeyDown);
- interactionEmitter.off("keyUp", handleKeyUp);
- };
- });
+ useKeyboard(handleKeyDown, handleKeyUp);
const isSmallScreen = useMedia({ query: "(max-width: 799px)" });
const drawTools = [
diff --git a/src/components/map/controls/MeasureToolSettings.js b/src/components/map/controls/MeasureToolSettings.js
index 8886dbc..40be3ca 100644
--- a/src/components/map/controls/MeasureToolSettings.js
+++ b/src/components/map/controls/MeasureToolSettings.js
@@ -1,4 +1,4 @@
-import React, { useEffect, useContext } from "react";
+import React from "react";
import { Flex, Input, Text } from "theme-ui";
import ToolSection from "./ToolSection";
@@ -8,28 +8,21 @@ import MeasureManhattanIcon from "../../../icons/MeasureManhattanIcon";
import Divider from "../../Divider";
-import MapInteractionContext from "../../../contexts/MapInteractionContext";
+import useKeyboard from "../../../helpers/useKeyboard";
function MeasureToolSettings({ settings, onSettingChange }) {
- const { interactionEmitter } = useContext(MapInteractionContext);
-
// Keyboard shortcuts
- useEffect(() => {
- function handleKeyDown({ key }) {
- if (key === "g") {
- onSettingChange({ type: "chebyshev" });
- } else if (key === "l") {
- onSettingChange({ type: "euclidean" });
- } else if (key === "c") {
- onSettingChange({ type: "manhattan" });
- }
+ function handleKeyDown({ key }) {
+ if (key === "g") {
+ onSettingChange({ type: "chebyshev" });
+ } else if (key === "l") {
+ onSettingChange({ type: "euclidean" });
+ } else if (key === "c") {
+ onSettingChange({ type: "manhattan" });
}
- interactionEmitter.on("keyDown", handleKeyDown);
+ }
- return () => {
- interactionEmitter.off("keyDown", handleKeyDown);
- };
- });
+ useKeyboard(handleKeyDown);
const tools = [
{
diff --git a/src/contexts/KeyboardContext.js b/src/contexts/KeyboardContext.js
new file mode 100644
index 0000000..867c28d
--- /dev/null
+++ b/src/contexts/KeyboardContext.js
@@ -0,0 +1,42 @@
+import React, { useEffect, useState } from "react";
+import { EventEmitter } from "events";
+
+const KeyboardContext = React.createContext({ keyEmitter: new EventEmitter() });
+
+export function KeyboardProvider({ children }) {
+ const [keyEmitter] = useState(new EventEmitter());
+ useEffect(() => {
+ function handleKeyDown(event) {
+ // Ignore text input
+ if (event.target instanceof HTMLInputElement) {
+ return;
+ }
+ keyEmitter.emit("keyDown", event);
+ }
+
+ function handleKeyUp(event) {
+ // Ignore text input
+ if (event.target instanceof HTMLInputElement) {
+ return;
+ }
+ keyEmitter.emit("keyUp", event);
+ }
+
+ document.body.addEventListener("keydown", handleKeyDown);
+ document.body.addEventListener("keyup", handleKeyUp);
+ document.body.tabIndex = 1;
+ return () => {
+ document.body.removeEventListener("keydown", handleKeyDown);
+ document.body.removeEventListener("keyup", handleKeyUp);
+ document.body.tabIndex = 0;
+ };
+ }, [keyEmitter]);
+
+ return (
+
+ {children}
+
+ );
+}
+
+export default KeyboardContext;
diff --git a/src/helpers/useKeyboard.js b/src/helpers/useKeyboard.js
new file mode 100644
index 0000000..19958d4
--- /dev/null
+++ b/src/helpers/useKeyboard.js
@@ -0,0 +1,26 @@
+import { useEffect, useContext } from "react";
+
+import KeyboardContext from "../contexts/KeyboardContext";
+
+function useKeyboard(onKeyDown, onKeyUp) {
+ const { keyEmitter } = useContext(KeyboardContext);
+ useEffect(() => {
+ if (onKeyDown) {
+ keyEmitter.on("keyDown", onKeyDown);
+ }
+ if (onKeyUp) {
+ keyEmitter.on("keyUp", onKeyUp);
+ }
+
+ return () => {
+ if (onKeyDown) {
+ keyEmitter.off("keyDown", onKeyDown);
+ }
+ if (onKeyUp) {
+ keyEmitter.off("keyUp", onKeyUp);
+ }
+ };
+ });
+}
+
+export default useKeyboard;