import React, { useEffect, useState } from "react"; import interact from "interactjs"; import { Box, Input } from "theme-ui"; import MapMenu from "./MapMenu"; import colors, { colorOptions } from "../helpers/colors"; function TokenMenu({ tokenClassName, onTokenChange }) { const [isOpen, setIsOpen] = useState(false); function handleRequestClose() { setIsOpen(false); } const [currentToken, setCurrentToken] = useState({}); 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 }); } } 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; 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 tap gesture const tokenInteract = interact(`.${tokenClassName}`).on( "tap", 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); tokenInteract.unset(); }; }, [tokenClassName]); function handleModalContent(node) { if (node) { // Focus input const tokenLabelInput = node.querySelector("#changeTokenLabel"); tokenLabelInput.focus(); tokenLabelInput.setSelectionRange(7, 8); // Ensure menu is in bounds const nodeRect = node.getBoundingClientRect(); const map = document.querySelector(".map"); const mapRect = map.getBoundingClientRect(); setMenuLeft((prevLeft) => Math.min( mapRect.right - nodeRect.width, Math.max(mapRect.left, prevLeft) ) ); setMenuTop((prevTop) => Math.min(mapRect.bottom - nodeRect.height, prevTop) ); } } return ( { e.preventDefault(); handleRequestClose(); }} > {colorOptions.map((color) => ( handleStatusChange(color)} aria-label={`Token label Color ${color}`} > {currentToken.status && currentToken.status.includes(color) && ( )} ))} ); } export default TokenMenu;