diff --git a/src/components/drag/Draggable.tsx b/src/components/drag/Draggable.tsx index a0b9268..cb0858b 100644 --- a/src/components/drag/Draggable.tsx +++ b/src/components/drag/Draggable.tsx @@ -5,7 +5,7 @@ import { Data } from "@dnd-kit/core/dist/store/types"; type DraggableProps = { id: string; children: React.ReactNode; - data: Data; + data?: Data; }; function Draggable({ id, children, data }: DraggableProps) { diff --git a/src/components/map/Map.tsx b/src/components/map/Map.tsx index f4419ea..e970f24 100644 --- a/src/components/map/Map.tsx +++ b/src/components/map/Map.tsx @@ -259,11 +259,9 @@ function Map({ onMapTokenStateRemove(state); setTokenDraggingOptions(undefined); }} - onTokenStateChange={onMapTokenStateChange} tokenState={tokenDraggingOptions && tokenDraggingOptions.tokenState} - tokenGroup={tokenDraggingOptions && tokenDraggingOptions.tokenGroup} + tokenNode={tokenDraggingOptions && tokenDraggingOptions.tokenNode} dragging={!!(tokenDraggingOptions && tokenDraggingOptions.dragging)} - token={tokensById[tokenDraggingOptions.tokenState.tokenId]} /> ); diff --git a/src/components/map/MapTokens.tsx b/src/components/map/MapTokens.tsx index 6de6b7c..2e18b69 100644 --- a/src/components/map/MapTokens.tsx +++ b/src/components/map/MapTokens.tsx @@ -89,7 +89,7 @@ function MapTokens({ setTokenDraggingOptions({ dragging: true, tokenState, - tokenGroup: e.target, + tokenNode: e.target, }) } onTokenDragEnd={() => diff --git a/src/components/token/SelectTokensButton.js b/src/components/token/SelectTokensButton.tsx similarity index 72% rename from src/components/token/SelectTokensButton.js rename to src/components/token/SelectTokensButton.tsx index 398aef7..e7a41b3 100644 --- a/src/components/token/SelectTokensButton.js +++ b/src/components/token/SelectTokensButton.tsx @@ -1,11 +1,18 @@ -import React, { useState } from "react"; +import { useState } from "react"; import { IconButton } from "theme-ui"; import SelectTokensIcon from "../../icons/SelectTokensIcon"; import SelectTokensModal from "../../modals/SelectTokensModal"; +import { MapTokensStateCreateHandler } from "../../types/Events"; -function SelectTokensButton({ onMapTokensStateCreate }) { +type SelectTokensButtonProps = { + onMapTokensStateCreate: MapTokensStateCreateHandler; +}; + +function SelectTokensButton({ + onMapTokensStateCreate, +}: SelectTokensButtonProps) { const [isModalOpen, setIsModalOpen] = useState(false); function openModal() { setIsModalOpen(true); @@ -14,9 +21,6 @@ function SelectTokensButton({ onMapTokensStateCreate }) { setIsModalOpen(false); } - function handleDone() { - closeModal(); - } return ( <> diff --git a/src/components/token/TokenBar.js b/src/components/token/TokenBar.tsx similarity index 85% rename from src/components/token/TokenBar.js rename to src/components/token/TokenBar.tsx index 83947a7..dbeac67 100644 --- a/src/components/token/TokenBar.js +++ b/src/components/token/TokenBar.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import { useState } from "react"; import { createPortal } from "react-dom"; import { Box, Flex, Grid } from "theme-ui"; import SimpleBar from "simplebar-react"; @@ -9,6 +9,7 @@ import { KeyboardSensor, useSensor, useSensors, + DragStartEvent, } from "@dnd-kit/core"; import TokenBarToken from "./TokenBarToken"; @@ -23,7 +24,7 @@ import usePreventSelect from "../../hooks/usePreventSelect"; import { useTokenData } from "../../contexts/TokenDataContext"; import { useUserId } from "../../contexts/UserIdContext"; import { useMapStage } from "../../contexts/MapStageContext"; -import DragContext from "../../contexts/DragContext"; +import DragContext, { CustomDragEndEvent } from "../../contexts/DragContext"; import { createTokenState, @@ -31,13 +32,19 @@ import { } from "../../helpers/token"; import { findGroup } from "../../helpers/group"; import Vector2 from "../../helpers/Vector2"; +import { MapTokensStateCreateHandler } from "../../types/Events"; +import { Group } from "../../types/Group"; -function TokenBar({ onMapTokensStateCreate }) { +type TokenBarProps = { + onMapTokensStateCreate: MapTokensStateCreateHandler; +}; + +function TokenBar({ onMapTokensStateCreate }: TokenBarProps) { const userId = useUserId(); const { tokensById, tokenGroups } = useTokenData(); - const [fullScreen] = useSetting("map.fullScreen"); + const [fullScreen] = useSetting("map.fullScreen"); - const [dragId, setDragId] = useState(); + const [dragId, setDragId] = useState(null); const mapStageRef = useMapStage(); @@ -52,14 +59,20 @@ function TokenBar({ onMapTokensStateCreate }) { const [preventSelect, resumeSelect] = usePreventSelect(); - function handleDragStart({ active }) { + function handleDragStart({ active }: DragStartEvent) { setDragId(active.id); preventSelect(); } - function handleDragEnd({ active, overlayNodeClientRect }) { + function handleDragEnd({ + active, + overlayNodeClientRect, + }: CustomDragEndEvent) { setDragId(null); - + resumeSelect(); + if (!userId) { + return; + } const mapStage = mapStageRef.current; if (mapStage && overlayNodeClientRect) { const dragRect = overlayNodeClientRect; @@ -96,8 +109,6 @@ function TokenBar({ onMapTokensStateCreate }) { } } } - - resumeSelect(); } function handleDragCancel() { @@ -105,7 +116,7 @@ function TokenBar({ onMapTokensStateCreate }) { resumeSelect(); } - function renderToken(group, draggable = true) { + function renderToken(group: Group, draggable = true) { if (group.type === "item") { const token = tokensById[group.id]; if (token && !token.hideInSidebar) { @@ -140,6 +151,8 @@ function TokenBar({ onMapTokensStateCreate }) { } } + const dragGroup = dragId && findGroup(tokenGroups, dragId); + return ( {createPortal( - {dragId && renderToken(findGroup(tokenGroups, dragId), false)} + {dragGroup && renderToken(dragGroup, false)} , document.body )} diff --git a/src/components/token/TokenBarToken.js b/src/components/token/TokenBarToken.tsx similarity index 82% rename from src/components/token/TokenBarToken.js rename to src/components/token/TokenBarToken.tsx index cd89691..5c96fd8 100644 --- a/src/components/token/TokenBarToken.js +++ b/src/components/token/TokenBarToken.tsx @@ -1,10 +1,15 @@ -import React from "react"; import { Box } from "theme-ui"; import { useInView } from "react-intersection-observer"; import TokenImage from "./TokenImage"; -function TokenBarToken({ token }) { +import { Token } from "../../types/Token"; + +type TokenBarTokenProps = { + token: Token; +}; + +function TokenBarToken({ token }: TokenBarTokenProps) { const [ref, inView] = useInView({ triggerOnce: true }); return ( diff --git a/src/components/token/TokenBarTokenGroup.js b/src/components/token/TokenBarTokenGroup.tsx similarity index 85% rename from src/components/token/TokenBarTokenGroup.js rename to src/components/token/TokenBarTokenGroup.tsx index 7b36d60..c6837d7 100644 --- a/src/components/token/TokenBarTokenGroup.js +++ b/src/components/token/TokenBarTokenGroup.tsx @@ -1,4 +1,4 @@ -import React, { useState, useRef } from "react"; +import { useState, useRef } from "react"; import { Grid, Flex, Box } from "theme-ui"; import { useSpring, animated } from "react-spring"; import { useDraggable } from "@dnd-kit/core"; @@ -11,10 +11,22 @@ import Draggable from "../drag/Draggable"; import Vector2 from "../../helpers/Vector2"; import GroupIcon from "../../icons/GroupIcon"; +import { GroupContainer } from "../../types/Group"; +import { Token } from "../../types/Token"; -function TokenBarTokenGroup({ group, tokens, draggable }) { +type TokenBarTokenGroupProps = { + group: GroupContainer; + tokens: Token[]; + draggable: boolean; +}; + +function TokenBarTokenGroup({ + group, + tokens, + draggable, +}: TokenBarTokenGroupProps) { const { attributes, listeners, setNodeRef, isDragging } = useDraggable({ - id: draggable && group.id, + id: group.id, disabled: !draggable, }); const [isOpen, setIsOpen] = useState(false); @@ -23,7 +35,7 @@ function TokenBarTokenGroup({ group, tokens, draggable }) { height: isOpen ? (tokens.length + 1) * 56 : 56, }); - function renderToken(token) { + function renderToken(token: Token) { if (draggable) { return ( @@ -77,7 +89,6 @@ function TokenBarTokenGroup({ group, tokens, draggable }) { gridTemplateRows: "1fr 1fr", }} p="2px" - alt={group.name} title={group.name} {...listeners} {...attributes} @@ -100,10 +111,13 @@ function TokenBarTokenGroup({ group, tokens, draggable }) { // Reject the opening of a group if the pointer has moved const clickDownPositionRef = useRef(new Vector2(0, 0)); - function handleOpenDown(event) { + function handleOpenDown(event: React.PointerEvent) { clickDownPositionRef.current = new Vector2(event.clientX, event.clientY); } - function handleOpenClick(event, newOpen) { + function handleOpenClick( + event: React.MouseEvent, + newOpen: boolean + ) { const clickPosition = new Vector2(event.clientX, event.clientY); const distance = Vector2.distance( clickPosition, diff --git a/src/components/token/TokenDragOverlay.js b/src/components/token/TokenDragOverlay.js deleted file mode 100644 index f484a0c..0000000 --- a/src/components/token/TokenDragOverlay.js +++ /dev/null @@ -1,56 +0,0 @@ -import React from "react"; - -import { useUserId } from "../../contexts/UserIdContext"; -import { - useMapWidth, - useMapHeight, -} from "../../contexts/MapInteractionContext"; - -import DragOverlay from "../map/DragOverlay"; - -function TokenDragOverlay({ - onTokenStateRemove, - onTokenStateChange, - token, - tokenState, - tokenGroup, - dragging, -}) { - const userId = useUserId(); - - const mapWidth = useMapWidth(); - const mapHeight = useMapHeight(); - - function handleTokenRemove() { - // Handle other tokens when a vehicle gets deleted - if (token && token.category === "vehicle") { - const layer = tokenGroup.getLayer(); - const mountedTokens = tokenGroup.find(".token"); - for (let mountedToken of mountedTokens) { - // Save and restore token position after moving layer - const position = mountedToken.absolutePosition(); - mountedToken.moveTo(layer); - mountedToken.absolutePosition(position); - onTokenStateChange({ - [mountedToken.id()]: { - x: mountedToken.x() / mapWidth, - y: mountedToken.y() / mapHeight, - lastModifiedBy: userId, - lastModified: Date.now(), - }, - }); - } - } - onTokenStateRemove(tokenState); - } - - return ( - - ); -} - -export default TokenDragOverlay; diff --git a/src/components/token/TokenDragOverlay.tsx b/src/components/token/TokenDragOverlay.tsx new file mode 100644 index 0000000..f6a4844 --- /dev/null +++ b/src/components/token/TokenDragOverlay.tsx @@ -0,0 +1,33 @@ +import Konva from "konva"; + +import DragOverlay from "../map/DragOverlay"; +import { MapTokenStateRemoveHandler } from "../../types/Events"; +import { TokenState } from "../../types/TokenState"; + +type TokenDragOverlayProps = { + onTokenStateRemove: MapTokenStateRemoveHandler; + tokenState: TokenState; + tokenNode: Konva.Node; + dragging: boolean; +}; + +function TokenDragOverlay({ + onTokenStateRemove, + tokenState, + tokenNode, + dragging, +}: TokenDragOverlayProps) { + function handleTokenRemove() { + onTokenStateRemove(tokenState); + } + + return ( + + ); +} + +export default TokenDragOverlay; diff --git a/src/components/token/TokenEditBar.js b/src/components/token/TokenEditBar.tsx similarity index 89% rename from src/components/token/TokenEditBar.js rename to src/components/token/TokenEditBar.tsx index 273d733..be4e656 100644 --- a/src/components/token/TokenEditBar.js +++ b/src/components/token/TokenEditBar.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import { useState, useEffect } from "react"; import { Flex, Close, IconButton } from "theme-ui"; import { groupsFromIds, itemsFromGroups } from "../../helpers/group"; @@ -15,10 +15,15 @@ import { useKeyboard } from "../../contexts/KeyboardContext"; import shortcuts from "../../shortcuts"; -function TokenEditBar({ disabled, onLoad }) { +type TokenEditBarProps = { + disabled: boolean; + onLoad: (load: boolean) => void; +}; + +function TokenEditBar({ disabled, onLoad }: TokenEditBarProps) { const { tokens, removeTokens, updateTokensHidden } = useTokenData(); - const { activeGroups, selectedGroupIds, onGroupSelect } = useGroup(); + const { activeGroups, selectedGroupIds, onClearSelection } = useGroup(); const [allTokensVisible, setAllTokensVisisble] = useState(false); @@ -40,12 +45,12 @@ function TokenEditBar({ disabled, onLoad }) { setIsTokensRemoveModalOpen(false); const selectedTokens = getSelectedTokens(); const selectedTokenIds = selectedTokens.map((token) => token.id); - onGroupSelect(); + onClearSelection(); await removeTokens(selectedTokenIds); onLoad(false); } - async function handleTokensHide(hideInSidebar) { + async function handleTokensHide(hideInSidebar: boolean) { const selectedTokens = getSelectedTokens(); const selectedTokenIds = selectedTokens.map((token) => token.id); // Show loading indicator if hiding more than 10 tokens @@ -61,7 +66,7 @@ function TokenEditBar({ disabled, onLoad }) { /** * Shortcuts */ - function handleKeyDown(event) { + function handleKeyDown(event: KeyboardEvent) { if (disabled) { return; } @@ -101,7 +106,7 @@ function TokenEditBar({ disabled, onLoad }) { onGroupSelect()} + onClick={() => onClearSelection()} />