import { useEffect, useState } from "react"; import { Box, Flex, IconButton } from "theme-ui"; import MapMenu from "../map/MapMenu"; import usePrevious from "../../hooks/usePrevious"; import LockIcon from "../../icons/TokenLockIcon"; import UnlockIcon from "../../icons/TokenUnlockIcon"; import ShowIcon from "../../icons/TokenShowIcon"; import HideIcon from "../../icons/TokenHideIcon"; import { useUserId } from "../../contexts/UserIdContext"; import { SelectionItemsChangeEventHandler, RequestCloseEventHandler, } from "../../types/Events"; import { Map } from "../../types/Map"; import { Selection } from "../../types/Select"; import { TokenState } from "../../types/TokenState"; import { Note } from "../../types/Note"; import { getSelectionPoints } from "../../helpers/selection"; import Vector2 from "../../helpers/Vector2"; import { useMapStage } from "../../contexts/MapStageContext"; import { MapState } from "../../types/MapState"; type SelectionMenuProps = { isOpen: boolean; onRequestClose: RequestCloseEventHandler; selection: Selection | null; onSelectionItemsChange: SelectionItemsChangeEventHandler; map: Map | null; mapState: MapState | null; }; function SelectionMenu({ isOpen, onRequestClose, selection, onSelectionItemsChange, map, mapState, }: SelectionMenuProps) { const userId = useUserId(); const wasOpen = usePrevious(isOpen); const mapStageRef = useMapStage(); const [menuLeft, setMenuLeft] = useState(0); const [menuTop, setMenuTop] = useState(0); useEffect(() => { const mapStage = mapStageRef.current; if (isOpen && !wasOpen && selection && mapStage) { const points = getSelectionPoints(selection); const bounds = Vector2.getBoundingBox(points); let menuPosition = new Vector2( bounds.center.x + selection.x, bounds.max.y + selection.y ); const mapImage = mapStage.findOne("#mapImage"); if (!mapImage) { return; } menuPosition = Vector2.multiply(menuPosition, { x: mapImage.width(), y: mapImage.height(), }); const transform = mapImage.getAbsoluteTransform().copy(); const absolutePosition = transform.point(menuPosition); const mapElement = document.querySelector(".map"); if (mapElement) { const mapRect = mapElement.getBoundingClientRect(); setMenuLeft(mapRect.left + absolutePosition.x - 156 / 2); setMenuTop(mapRect.top + absolutePosition.y + 12); } } }, [isOpen, selection, wasOpen, mapStageRef]); function updateSelectedItems(change: Partial | Partial) { if (selection) { const tokenChanges: Record> = {}; const noteChanges: Record> = {}; for (let item of selection.items) { if (item.type === "token") { tokenChanges[item.id] = change; } else { noteChanges[item.id] = change; } } onSelectionItemsChange(tokenChanges, noteChanges); } } const [itemsVisible, setItemsVisible] = useState(false); function handleVisibleChange() { updateSelectedItems({ visible: !itemsVisible }); setItemsVisible(!itemsVisible); } const [itemsLocked, setItemsLocked] = useState(false); function handleLockChange() { updateSelectedItems({ locked: !itemsLocked }); setItemsLocked(!itemsLocked); } useEffect(() => { if (isOpen && selection && mapState) { let allVisible = true; let allLocked = true; for (let item of selection.items) { if (item.type === "token") { const tokenState = mapState.tokens[item.id]; if (!tokenState.visible) { allVisible = false; } if (!tokenState.locked) { allLocked = false; } } else { const note = mapState.notes[item.id]; if (!note.visible) { allVisible = false; } if (!note.locked) { allLocked = false; } } } setItemsVisible(allVisible); setItemsLocked(allLocked); } }, [mapState, selection, isOpen]); function handleModalContent(node: HTMLElement) { if (node) { // Focus input const tokenLabelInput = node.querySelector("#changeNoteText"); if (tokenLabelInput) { tokenLabelInput.focus(); tokenLabelInput.select(); } // Ensure menu is in bounds const nodeRect = node.getBoundingClientRect(); const mapElement = document.querySelector(".map"); if (mapElement) { 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 ( {/* Only show hide and lock token actions to map owners */} {map && map.owner === userId && ( {itemsVisible ? : } {itemsLocked ? : } )} ); } export default SelectionMenu;