diff --git a/src/components/MapToken.js b/src/components/MapToken.js index 27d49de..0923478 100644 --- a/src/components/MapToken.js +++ b/src/components/MapToken.js @@ -2,6 +2,7 @@ import React, { useRef } from "react"; import { Box, Image } from "theme-ui"; import TokenLabel from "./TokenLabel"; +import TokenStatus from "./TokenStatus"; import usePreventTouch from "../helpers/usePreventTouch"; @@ -51,8 +52,10 @@ function MapToken({ token, tokenSizePercent, className }) { data-id={token.id} data-size={token.size} data-label={token.label} + data-status={token.status} ref={imageRef} /> + {token.status && } {token.label && } diff --git a/src/components/ProxyToken.js b/src/components/ProxyToken.js index 1f0787a..e66d0a0 100644 --- a/src/components/ProxyToken.js +++ b/src/components/ProxyToken.js @@ -5,12 +5,14 @@ import interact from "interactjs"; import usePortal from "../helpers/usePortal"; import TokenLabel from "./TokenLabel"; +import TokenStatus from "./TokenStatus"; function ProxyToken({ tokenClassName, onProxyDragEnd }) { const proxyContainer = usePortal("root"); const [imageSource, setImageSource] = useState(""); const [label, setLabel] = useState(""); + const [status, setStatus] = useState(""); const proxyRef = useRef(); const proxyOnMap = useRef(false); @@ -24,6 +26,7 @@ function ProxyToken({ tokenClassName, onProxyDragEnd }) { target.parentElement.style.opacity = "0.25"; setImageSource(target.src); setLabel(target.dataset.label || ""); + setStatus(target.dataset.status || ""); let proxy = proxyRef.current; if (proxy) { @@ -141,6 +144,7 @@ function ProxyToken({ tokenClassName, onProxyDragEnd }) { width: "100%", }} /> + {status && } {label && } , diff --git a/src/components/TokenMenu.js b/src/components/TokenMenu.js index 49dffcb..817b18f 100644 --- a/src/components/TokenMenu.js +++ b/src/components/TokenMenu.js @@ -1,7 +1,9 @@ import React, { useEffect, useState } from "react"; import Modal from "react-modal"; import interact from "interactjs"; -import { useThemeUI, Box, Input } from "theme-ui"; +import { Box, Input } from "theme-ui"; + +import { statusOptions, statusToColor } from "../helpers/status"; function TokenMenu({ tokenClassName, onTokenChange }) { const [isOpen, setIsOpen] = useState(false); @@ -10,7 +12,7 @@ function TokenMenu({ tokenClassName, onTokenChange }) { setIsOpen(false); } - const [currentToken, setCurrentToken] = useState(0); + const [currentToken, setCurrentToken] = useState({}); const [menuLeft, setMenuLeft] = useState(0); const [menuTop, setMenuTop] = useState(0); @@ -27,6 +29,23 @@ function TokenMenu({ tokenClassName, onTokenChange }) { } } + function handleStatusChange(status) { + const statuses = + currentToken.status.split(" ").filter((s) => s !== "") || []; + let newStatuses = []; + if (statuses.includes(status)) { + newStatuses = statuses.filter((s) => s !== status); + } else { + newStatuses = [...statuses, status]; + } + const newStatus = newStatuses.join(" "); + setCurrentToken((prevToken) => ({ + ...prevToken, + status: newStatus, + })); + onTokenChange({ ...currentToken, status: newStatus }); + } + useEffect(() => { function handleTokenMenuOpen(event) { const target = event.target; @@ -64,17 +83,22 @@ function TokenMenu({ tokenClassName, onTokenChange }) { }; }, [tokenClassName]); - const { theme } = useThemeUI(); - function handleModalContent(node) { if (node) { const tokenLabelInput = node.querySelector("#changeTokenLabel"); tokenLabelInput.focus(); - // Highlight label section of input tokenLabelInput.setSelectionRange(7, 8); - tokenLabelInput.onblur = () => { - setIsOpen(false); - }; + + // Close modal if interacting with any other element + function handlePointerDown(event) { + const path = event.composedPath(); + if (!path.includes(node)) { + setIsOpen(false); + document.body.removeEventListener("pointerdown", handlePointerDown); + } + } + document.body.addEventListener("pointerdown", handlePointerDown); + // Check for wheel event to close modal as well document.body.addEventListener( "wheel", @@ -93,7 +117,7 @@ function TokenMenu({ tokenClassName, onTokenChange }) { style={{ overlay: { top: "0", bottom: "initial" }, content: { - backgroundColor: theme.colors.highlight, + backgroundColor: "hsla(230, 25%, 18%, 0.8)", top: `${menuTop}px`, left: `${menuLeft}px`, right: "initial", @@ -105,21 +129,63 @@ function TokenMenu({ tokenClassName, onTokenChange }) { }} contentRef={handleModalContent} > - { - e.preventDefault(); - handleRequestClose(); - }} - sx={{ width: "72px" }} - > - + + { + e.preventDefault(); + handleRequestClose(); + }} + > + + + + {statusOptions.map((status) => ( + handleStatusChange(status)} + > + {currentToken.status && currentToken.status.includes(status) && ( + + )} + + ))} + ); diff --git a/src/components/TokenStatus.js b/src/components/TokenStatus.js new file mode 100644 index 0000000..6da6302 --- /dev/null +++ b/src/components/TokenStatus.js @@ -0,0 +1,47 @@ +import React from "react"; +import { Box } from "theme-ui"; + +import { statusToColor } from "../helpers/status"; + +function TokenStatus({ statuses }) { + return ( + + {statuses.map((status, index) => ( + + + + + + ))} + + ); +} + +export default TokenStatus; diff --git a/src/components/Tokens.js b/src/components/Tokens.js index 9882c23..1d9a2ae 100644 --- a/src/components/Tokens.js +++ b/src/components/Tokens.js @@ -22,6 +22,7 @@ function Tokens({ onCreateMapToken }) { id: shortid.generate(), size: tokenSize, label: "", + status: "", }); } } diff --git a/src/helpers/status.js b/src/helpers/status.js new file mode 100644 index 0000000..b787bfc --- /dev/null +++ b/src/helpers/status.js @@ -0,0 +1,27 @@ +export function statusToColor(status) { + switch (status) { + case "blue": + return "rgb(26, 106, 255)"; + case "orange": + return "rgb(255, 116, 51)"; + case "red": + return "rgb(255, 77, 77)"; + case "purple": + return "rgb(136, 77, 255)"; + case "green": + return "rgb(133, 255, 102)"; + case "pink": + return "rgb(235, 138, 255)"; + default: + throw Error("Color not implemented"); + } +} + +export const statusOptions = [ + "blue", + "orange", + "red", + "purple", + "green", + "pink", +];