Added token status rings

This commit is contained in:
Mitchell McCaffrey 2020-04-13 23:42:18 +10:00
parent 8ba3881529
commit 441e589ec3
6 changed files with 172 additions and 24 deletions

View File

@ -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 && <TokenStatus statuses={token.status.split(" ")} />}
{token.label && <TokenLabel label={token.label} />}
</Box>
</Box>

View File

@ -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 && <TokenStatus statuses={status.split(" ")} />}
{label && <TokenLabel label={label} />}
</Box>
</Box>,

View File

@ -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}
>
<Box
as="form"
bg="background"
onSubmit={(e) => {
e.preventDefault();
handleRequestClose();
}}
sx={{ width: "72px" }}
>
<Input
id="changeTokenLabel"
onChange={handleLabelChange}
value={`Label: ${currentToken.label}`}
sx={{ padding: "4px" }}
/>
<Box sx={{ width: "80px" }} p={1}>
<Box
as="form"
onSubmit={(e) => {
e.preventDefault();
handleRequestClose();
}}
>
<Input
id="changeTokenLabel"
onChange={handleLabelChange}
value={`Label: ${currentToken.label}`}
sx={{
padding: "4px",
border: "none",
":focus": {
outline: "none",
},
}}
autoComplete="off"
/>
</Box>
<Box
sx={{
display: "flex",
flexWrap: "wrap",
justifyContent: "space-between",
}}
>
{statusOptions.map((status) => (
<Box
key={status}
sx={{
width: "33%",
paddingTop: "33%",
borderRadius: "50%",
transform: "scale(0.75)",
backgroundColor: statusToColor(status),
cursor: "pointer",
}}
onClick={() => handleStatusChange(status)}
>
{currentToken.status && currentToken.status.includes(status) && (
<Box
sx={{
width: "100%",
height: "100%",
border: "2px solid white",
position: "absolute",
top: 0,
borderRadius: "50%",
}}
/>
)}
</Box>
))}
</Box>
</Box>
</Modal>
);

View File

@ -0,0 +1,47 @@
import React from "react";
import { Box } from "theme-ui";
import { statusToColor } from "../helpers/status";
function TokenStatus({ statuses }) {
return (
<Box
sx={{
position: "absolute",
width: "100%",
height: "100%",
pointerEvents: "none",
}}
>
{statuses.map((status, index) => (
<Box
key={status}
sx={{
width: "100%",
height: "100%",
position: "absolute",
opacity: 0.8,
transform: `scale(1.${index + 1})`,
}}
>
<svg
style={{ position: "absolute" }}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 100"
>
<circle
r={47}
cx={50}
cy={50}
fill="none"
stroke={statusToColor(status)}
strokeWidth={4}
/>
</svg>
</Box>
))}
</Box>
);
}
export default TokenStatus;

View File

@ -22,6 +22,7 @@ function Tokens({ onCreateMapToken }) {
id: shortid.generate(),
size: tokenSize,
label: "",
status: "",
});
}
}

27
src/helpers/status.js Normal file
View File

@ -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",
];