import React, { useEffect, useState, useContext } from "react"; import { Box, Input, Slider, Flex, Text, IconButton } from "theme-ui"; import MapMenu from "../map/MapMenu"; import colors, { colorOptions } from "../../helpers/colors"; import usePrevious from "../../helpers/usePrevious"; import LockIcon from "../../icons/TokenLockIcon"; import UnlockIcon from "../../icons/TokenUnlockIcon"; import ShowIcon from "../../icons/TokenShowIcon"; import HideIcon from "../../icons/TokenHideIcon"; import AuthContext from "../../contexts/AuthContext"; const defaultTokenMaxSize = 6; function TokenMenu({ isOpen, onRequestClose, tokenState, tokenImage, onTokenStateChange, map, }) { const { userId } = useContext(AuthContext); const wasOpen = usePrevious(isOpen); const [tokenMaxSize, setTokenMaxSize] = useState(defaultTokenMaxSize); const [menuLeft, setMenuLeft] = useState(0); const [menuTop, setMenuTop] = useState(0); useEffect(() => { if (isOpen && !wasOpen && tokenState) { setTokenMaxSize(Math.max(tokenState.size, defaultTokenMaxSize)); // Update menu position if (tokenImage) { const imageRect = tokenImage.getClientRect(); const mapElement = document.querySelector(".map"); const mapRect = mapElement.getBoundingClientRect(); // Center X for the menu which is 156px wide setMenuLeft(mapRect.left + imageRect.x + imageRect.width / 2 - 156 / 2); // Y 12px from the bottom setMenuTop(mapRect.top + imageRect.y + imageRect.height + 12); } } }, [isOpen, tokenState, wasOpen, tokenImage]); function handleLabelChange(event) { const label = event.target.value; onTokenStateChange({ [tokenState.id]: { ...tokenState, label: label } }); } function handleStatusChange(status) { const statuses = tokenState.statuses; let newStatuses = []; if (statuses.includes(status)) { newStatuses = statuses.filter((s) => s !== status); } else { newStatuses = [...statuses, status]; } onTokenStateChange({ [tokenState.id]: { ...tokenState, statuses: newStatuses }, }); } function handleSizeChange(event) { const newSize = parseInt(event.target.value); onTokenStateChange({ [tokenState.id]: { ...tokenState, size: newSize } }); } function handleRotationChange(event) { const newRotation = parseInt(event.target.value); onTokenStateChange({ [tokenState.id]: { ...tokenState, rotation: newRotation }, }); } function handleVisibleChange() { onTokenStateChange({ [tokenState.id]: { ...tokenState, visible: !tokenState.visible }, }); } function handleLockChange() { onTokenStateChange({ [tokenState.id]: { ...tokenState, locked: !tokenState.locked }, }); } function handleModalContent(node) { if (node) { // Focus input const tokenLabelInput = node.querySelector("#changeTokenLabel"); tokenLabelInput.focus(); tokenLabelInput.select(); // Ensure menu is in bounds const nodeRect = node.getBoundingClientRect(); const mapElement = document.querySelector(".map"); const mapRect = mapElement.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(); onRequestClose(); }} sx={{ alignItems: "center" }} > Label: {colorOptions.map((color) => ( handleStatusChange(color)} aria-label={`Token label Color ${color}`} > {tokenState && tokenState.statuses && tokenState.statuses.includes(color) && ( )} ))} Size: Rotation: {/* Only show hide and lock token actions to map owners */} {map && map.owner === userId && ( {tokenState && tokenState.visible ? : } {tokenState && tokenState.locked ? : } )} ); } export default TokenMenu;