From cb93922d59c70dd009fc20cc9f08305adce8bdab Mon Sep 17 00:00:00 2001 From: Mitchell McCaffrey Date: Mon, 13 Apr 2020 00:24:03 +1000 Subject: [PATCH] Added initial token label Added token pop up menu Added token label Added better token positioning Split tokens into list and map variants Moved size input to more generic number input Changed game handler names to be more consistent --- src/components/ListToken.js | 21 +++ src/components/Map.js | 141 +++++++++--------- src/components/MapToken.js | 57 +++++++ .../{SizeInput.js => NumberInput.js} | 25 ++-- src/components/ProxyToken.js | 5 +- src/components/Token.js | 28 ---- src/components/TokenLabel.js | 48 ++++++ src/components/TokenMenu.js | 129 ++++++++++++++++ src/components/Tokens.js | 21 ++- src/helpers/usePreventTouch.js | 22 +++ src/images/TokenLabel.png | Bin 0 -> 32135 bytes src/routes/Game.js | 14 +- 12 files changed, 388 insertions(+), 123 deletions(-) create mode 100644 src/components/ListToken.js create mode 100644 src/components/MapToken.js rename src/components/{SizeInput.js => NumberInput.js} (70%) delete mode 100644 src/components/Token.js create mode 100644 src/components/TokenLabel.js create mode 100644 src/components/TokenMenu.js create mode 100644 src/helpers/usePreventTouch.js create mode 100644 src/images/TokenLabel.png diff --git a/src/components/ListToken.js b/src/components/ListToken.js new file mode 100644 index 0000000..f90d3d0 --- /dev/null +++ b/src/components/ListToken.js @@ -0,0 +1,21 @@ +import React, { useRef } from "react"; +import { Image } from "theme-ui"; + +import usePreventTouch from "../helpers/usePreventTouch"; + +function ListToken({ image, className }) { + const imageRef = useRef(); + // Stop touch to prevent 3d touch gesutre on iOS + usePreventTouch(imageRef); + + return ( + + ); +} + +export default ListToken; diff --git a/src/components/Map.js b/src/components/Map.js index dc3ec02..6c9b811 100644 --- a/src/components/Map.js +++ b/src/components/Map.js @@ -2,9 +2,10 @@ import React, { useRef, useEffect, useState } from "react"; import { Box, Image } from "theme-ui"; import interact from "interactjs"; -import Token from "../components/Token"; import ProxyToken from "../components/ProxyToken"; import AddMapButton from "../components/AddMapButton"; +import TokenMenu from "../components/TokenMenu"; +import MapToken from "../components/MapToken"; const mapTokenClassName = "map-token"; const zoomSpeed = -0.005; @@ -15,13 +16,13 @@ function Map({ mapSource, mapData, tokens, - onMapTokenMove, + onMapTokenChange, onMapTokenRemove, - onMapChanged, + onMapChange, }) { function handleProxyDragEnd(isOnMap, token) { - if (isOnMap && onMapTokenMove) { - onMapTokenMove(token); + if (isOnMap && onMapTokenChange) { + onMapTokenChange(token); } if (!isOnMap && onMapTokenRemove) { @@ -120,6 +121,64 @@ function Map({ const tokenSizePercent = (1 / rows) * 100; const aspectRatio = (mapData && mapData.width / mapData.height) || 1; + const mapImage = ( + + + + ); + + const mapTokens = ( + + {Object.values(tokens).map((token) => ( + + ))} + + ); + + const mapActions = ( + + + + ); + return ( <> - - - - - {Object.values(tokens).map((token) => ( - - - - ))} - + {mapImage} + {mapTokens} - - - + {mapActions} + ); } diff --git a/src/components/MapToken.js b/src/components/MapToken.js new file mode 100644 index 0000000..fff70cb --- /dev/null +++ b/src/components/MapToken.js @@ -0,0 +1,57 @@ +import React, { useRef } from "react"; +import { Box, Image } from "theme-ui"; + +import TokenLabel from "./TokenLabel"; + +import usePreventTouch from "../helpers/usePreventTouch"; + +function MapToken({ token, tokenSizePercent, className }) { + const imageRef = useRef(); + // Stop touch to prevent 3d touch gesutre on iOS + usePreventTouch(imageRef); + + return ( + + + + + {token.label && } + + + + ); +} + +export default MapToken; diff --git a/src/components/SizeInput.js b/src/components/NumberInput.js similarity index 70% rename from src/components/SizeInput.js rename to src/components/NumberInput.js index 799eefa..e6985f8 100644 --- a/src/components/SizeInput.js +++ b/src/components/NumberInput.js @@ -1,17 +1,17 @@ import React from "react"; import { Box, Flex, IconButton, Text } from "theme-ui"; -function SizeInput({ value, onChange }) { +function NumberInput({ value, onChange, title, min, max }) { return ( - Size + {title} value > 1 && onChange(value - 1)} + aria-label={`Decrease ${title}`} + title={`Decrease ${title}`} + onClick={() => value > min && onChange(value - 1)} > - + {value} onChange(value + 1)} + aria-label={`Increase ${title}`} + title={`Increase ${title}`} + onClick={() => value < max && onChange(value + 1)} > {}, + title: "Number", + min: 0, + max: 10, }; -export default SizeInput; +export default NumberInput; diff --git a/src/components/ProxyToken.js b/src/components/ProxyToken.js index ae38900..27722ad 100644 --- a/src/components/ProxyToken.js +++ b/src/components/ProxyToken.js @@ -86,10 +86,11 @@ function ProxyToken({ tokenClassName, onProxyDragEnd }) { x = x / (mapRect.right - mapRect.left); y = y / (mapRect.bottom - mapRect.top); + target.setAttribute("data-x", x); + target.setAttribute("data-y", y); + onProxyDragEnd(proxyOnMap.current, { image: imageSource, - x, - y, // Pass in props stored as data- in the dom node ...target.dataset, }); diff --git a/src/components/Token.js b/src/components/Token.js deleted file mode 100644 index 4c2a191..0000000 --- a/src/components/Token.js +++ /dev/null @@ -1,28 +0,0 @@ -import React from "react"; -import { Image } from "theme-ui"; - -import { fromEntries } from "../helpers/shared"; - -// The data prop is used to pass data into the dom element -// this can be used to pass state to the ProxyToken -function Token({ image, className, data, sx }) { - // Map the keys in data to have the `data-` prefix - const dataProps = fromEntries( - Object.entries(data).map(([key, value]) => [`data-${key}`, value]) - ); - return ( - - ); -} - -Token.defaultProps = { - data: {}, - sx: {}, -}; - -export default Token; diff --git a/src/components/TokenLabel.js b/src/components/TokenLabel.js new file mode 100644 index 0000000..061b853 --- /dev/null +++ b/src/components/TokenLabel.js @@ -0,0 +1,48 @@ +import React from "react"; +import { Image, Box, Text } from "theme-ui"; + +import tokenLabel from "../images/TokenLabel.png"; + +function TokenLabel({ label }) { + return ( + + + + + + {label} + + + + + ); +} + +export default TokenLabel; diff --git a/src/components/TokenMenu.js b/src/components/TokenMenu.js new file mode 100644 index 0000000..085ea3a --- /dev/null +++ b/src/components/TokenMenu.js @@ -0,0 +1,129 @@ +import React, { useEffect, useState } from "react"; +import Modal from "react-modal"; +import interact from "interactjs"; +import { useThemeUI, Box, Input } from "theme-ui"; + +function TokenMenu({ tokenClassName, onTokenChange }) { + const [isOpen, setIsOpen] = useState(false); + + function handleRequestClose() { + setIsOpen(false); + } + + const [currentToken, setCurrentToken] = useState(0); + const [menuLeft, setMenuLeft] = useState(0); + const [menuTop, setMenuTop] = useState(0); + + function handleLabelChange(event) { + // Slice to remove Label: text + const label = event.target.value.slice(7); + if (label.length <= 1) { + setCurrentToken((prevToken) => ({ + ...prevToken, + label: label, + })); + + onTokenChange({ ...currentToken, label: label }); + } + } + + useEffect(() => { + function handleTokenMenuOpen(event) { + const target = event.target; + const dataset = (target && target.dataset) || {}; + setCurrentToken({ + image: target.src, + ...dataset, + }); + + const targetRect = target.getBoundingClientRect(); + setMenuLeft(targetRect.left); + setMenuTop(targetRect.bottom); + + setIsOpen(true); + } + + // Add listener for hold gesture + interact(`.${tokenClassName}`).on("hold", handleTokenMenuOpen); + + function handleMapContextMenu(event) { + event.preventDefault(); + if (event.target.classList.contains(tokenClassName)) { + handleTokenMenuOpen(event); + } + } + + // Handle context menu on the map level as handling + // on the token level lead to the default menu still + // being displayed + const map = document.querySelector(".map"); + map.addEventListener("contextmenu", handleMapContextMenu); + + return () => { + map.removeEventListener("contextmenu", handleMapContextMenu); + }; + }, [tokenClassName]); + + const { theme } = useThemeUI(); + + function handleModalContent(node) { + if (node) { + console.log(node); + const tokenLabelInput = node.querySelector("#changeTokenLabel"); + tokenLabelInput.focus(); + // Highlight label section of input + tokenLabelInput.setSelectionRange(7, 8); + tokenLabelInput.onblur = () => { + setIsOpen(false); + }; + // Check for wheel event to close modal as well + document.body.addEventListener( + "wheel", + () => { + setIsOpen(false); + }, + { once: true } + ); + } + } + + return ( + + { + e.preventDefault(); + handleRequestClose(); + }} + sx={{ width: "72px" }} + > + + + + ); +} + +export default TokenMenu; diff --git a/src/components/Tokens.js b/src/components/Tokens.js index 6980da5..9882c23 100644 --- a/src/components/Tokens.js +++ b/src/components/Tokens.js @@ -5,9 +5,9 @@ import SimpleBar from "simplebar-react"; import * as tokens from "../tokens"; -import Token from "./Token"; +import ListToken from "./ListToken"; import ProxyToken from "./ProxyToken"; -import SizeInput from "./SizeInput"; +import NumberInput from "./NumberInput"; const listTokenClassName = "list-token"; @@ -17,7 +17,12 @@ function Tokens({ onCreateMapToken }) { function handleProxyDragEnd(isOnMap, token) { if (isOnMap && onCreateMapToken) { // Give the token an id - onCreateMapToken({ ...token, id: shortid.generate(), size: tokenSize }); + onCreateMapToken({ + ...token, + id: shortid.generate(), + size: tokenSize, + label: "", + }); } } @@ -34,12 +39,18 @@ function Tokens({ onCreateMapToken }) { {Object.entries(tokens).map(([id, image]) => ( - + ))} - + { + // Stop 3d touch + function prevent3DTouch(event) { + event.preventDefault(); + } + const element = elementRef.current; + if (element) { + element.addEventListener("touchstart", prevent3DTouch, false); + } + + return () => { + if (element) { + element.removeEventListener("touchstart", prevent3DTouch); + } + }; + }, [elementRef.current]); +} + +export default usePreventTouch; diff --git a/src/images/TokenLabel.png b/src/images/TokenLabel.png new file mode 100644 index 0000000000000000000000000000000000000000..637ddab57a02ec38124aed7b1aa0aada212ebf44 GIT binary patch literal 32135 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 literal 0 HcmV?d00001 diff --git a/src/routes/Game.js b/src/routes/Game.js index 8d4522c..ddc1e01 100644 --- a/src/routes/Game.js +++ b/src/routes/Game.js @@ -28,7 +28,7 @@ function Game() { const [mapSource, setMapSource] = useState(null); const mapDataRef = useRef(null); - function handleMapChanged(mapData, mapSource) { + function handleMapChange(mapData, mapSource) { mapDataRef.current = mapData; setMapSource(mapSource); for (let peer of Object.values(peers)) { @@ -38,7 +38,7 @@ function Game() { const [mapTokens, setMapTokens] = useState({}); - function handleEditMapToken(token) { + function handleMapTokenChange(token) { if (!mapSource) { return; } @@ -52,7 +52,7 @@ function Game() { } } - function handleRemoveMapToken(token) { + function handleMapTokenRemove(token) { setMapTokens((prevMapTokens) => { const { [token.id]: old, ...rest } = prevMapTokens; return rest; @@ -213,11 +213,11 @@ function Game() { mapSource={mapSource} mapData={mapDataRef.current} tokens={mapTokens} - onMapTokenMove={handleEditMapToken} - onMapTokenRemove={handleRemoveMapToken} - onMapChanged={handleMapChanged} + onMapTokenChange={handleMapTokenChange} + onMapTokenRemove={handleMapTokenRemove} + onMapChange={handleMapChange} /> - + setPeerError(null)}>