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()}
/>
{
+type TokenImageProps = {
+ token: Token;
+} & ImageProps;
+
+function TokenImage({ token, ...props }: TokenImageProps) {
const tokenURL = useDataURL(
token,
defaultTokenSources,
@@ -35,12 +40,11 @@ const TokenImage = React.forwardRef(({ token, ...props }, ref) => {
setShowOutline(false)}
src={tokenURL}
- ref={ref}
style={showOutline ? { display: "none" } : props.style}
{...props}
/>
>
);
-});
+}
export default TokenImage;
diff --git a/src/components/token/TokenLabel.js b/src/components/token/TokenLabel.tsx
similarity index 84%
rename from src/components/token/TokenLabel.js
rename to src/components/token/TokenLabel.tsx
index b178149..f0401d3 100644
--- a/src/components/token/TokenLabel.js
+++ b/src/components/token/TokenLabel.tsx
@@ -1,13 +1,21 @@
-import React, { useRef, useEffect, useState } from "react";
+import Konva from "konva";
+import { useRef, useEffect, useState } from "react";
import { Rect, Text, Group } from "react-konva";
import useSetting from "../../hooks/useSetting";
+import { TokenState } from "../../types/TokenState";
const maxTokenSize = 3;
const defaultFontSize = 16;
-function TokenLabel({ tokenState, width, height }) {
- const [labelSize] = useSetting("map.labelSize");
+type TokenLabelProps = {
+ tokenState: TokenState;
+ width: number;
+ height: number;
+};
+
+function TokenLabel({ tokenState, width, height }: TokenLabelProps) {
+ const [labelSize] = useSetting("map.labelSize");
const paddingY =
(height / 12 / tokenState.size) * Math.min(tokenState.size, maxTokenSize);
@@ -22,7 +30,7 @@ function TokenLabel({ tokenState, width, height }) {
return;
}
- let fontSizes = [];
+ let fontSizes: number[] = [];
for (let size = 20 * labelSize; size >= 6; size--) {
const verticalSize = height / size / tokenState.size;
const tokenSize = Math.min(tokenState.size, maxTokenSize);
@@ -30,7 +38,7 @@ function TokenLabel({ tokenState, width, height }) {
fontSizes.push(fontSize);
}
- function findFontScale() {
+ const findFontScale = () => {
const size = fontSizes.reduce((prev, curr) => {
text.fontSize(curr);
const textWidth = text.getTextWidth() + paddingX * 2;
@@ -42,7 +50,7 @@ function TokenLabel({ tokenState, width, height }) {
}, 1);
setFontScale(size / defaultFontSize);
- }
+ };
findFontScale();
}, [
@@ -68,8 +76,8 @@ function TokenLabel({ tokenState, width, height }) {
}
}, [tokenState.label, paddingX, width, fontScale]);
- const textRef = useRef();
- const textSizerRef = useRef();
+ const textRef = useRef(null);
+ const textSizerRef = useRef(null);
return (
diff --git a/src/components/token/TokenMenu.js b/src/components/token/TokenMenu.tsx
similarity index 80%
rename from src/components/token/TokenMenu.js
rename to src/components/token/TokenMenu.tsx
index 03ce419..df6a24a 100644
--- a/src/components/token/TokenMenu.js
+++ b/src/components/token/TokenMenu.tsx
@@ -1,5 +1,6 @@
import React, { useEffect, useState } from "react";
import { Box, Input, Flex, Text, IconButton } from "theme-ui";
+import Konva from "konva";
import Slider from "../Slider";
@@ -16,6 +17,22 @@ import HideIcon from "../../icons/TokenHideIcon";
import { useUserId } from "../../contexts/UserIdContext";
+import {
+ RequestCloseEventHandler,
+ TokenStateChangeEventHandler,
+} from "../../types/Events";
+import { TokenState } from "../../types/TokenState";
+import { Map } from "../../types/Map";
+
+type TokenMenuProps = {
+ isOpen: boolean;
+ onRequestClose: RequestCloseEventHandler;
+ tokenState: TokenState;
+ tokenImage: Konva.Node;
+ onTokenStateChange: TokenStateChangeEventHandler;
+ map: Map;
+};
+
const defaultTokenMaxSize = 6;
function TokenMenu({
isOpen,
@@ -24,7 +41,7 @@ function TokenMenu({
tokenImage,
onTokenStateChange,
map,
-}) {
+}: TokenMenuProps) {
const userId = useUserId();
const wasOpen = usePrevious(isOpen);
@@ -39,22 +56,25 @@ function TokenMenu({
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);
+ if (mapElement) {
+ 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) {
+ function handleLabelChange(event: React.ChangeEvent) {
const label = event.target.value.substring(0, 48);
tokenState && onTokenStateChange({ [tokenState.id]: { label: label } });
}
- function handleStatusChange(status) {
+ function handleStatusChange(status: string) {
if (!tokenState) {
return;
}
@@ -69,12 +89,12 @@ function TokenMenu({
});
}
- function handleSizeChange(event) {
+ function handleSizeChange(event: React.ChangeEvent) {
const newSize = parseFloat(event.target.value);
tokenState && onTokenStateChange({ [tokenState.id]: { size: newSize } });
}
- function handleRotationChange(event) {
+ function handleRotationChange(event: React.ChangeEvent) {
const newRotation = parseInt(event.target.value);
tokenState &&
onTokenStateChange({
@@ -96,26 +116,31 @@ function TokenMenu({
});
}
- function handleModalContent(node) {
+ function handleModalContent(node: HTMLElement) {
if (node) {
// Focus input
- const tokenLabelInput = node.querySelector("#changeTokenLabel");
- tokenLabelInput.focus();
- tokenLabelInput.select();
+ const tokenLabelInput =
+ node.querySelector("#changeTokenLabel");
+ if (tokenLabelInput) {
+ 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)
- );
+ 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)
+ );
+ }
}
}
diff --git a/src/components/token/TokenOutline.js b/src/components/token/TokenOutline.tsx
similarity index 82%
rename from src/components/token/TokenOutline.js
rename to src/components/token/TokenOutline.tsx
index 20d4396..12f5638 100644
--- a/src/components/token/TokenOutline.js
+++ b/src/components/token/TokenOutline.tsx
@@ -1,9 +1,19 @@
-import React from "react";
import { Rect, Circle, Line } from "react-konva";
import colors from "../../helpers/colors";
+import { Outline } from "../../types/Outline";
-export function TokenOutlineSVG({ outline, width, height }) {
+type TokenOutlineSVGProps = {
+ outline: Outline;
+ width: number;
+ height: number;
+};
+
+export function TokenOutlineSVG({
+ outline,
+ width,
+ height,
+}: TokenOutlineSVGProps) {
if (outline.type === "rect") {
return (
);
diff --git a/src/components/token/TokenPreview.js b/src/components/token/TokenPreview.tsx
similarity index 85%
rename from src/components/token/TokenPreview.js
rename to src/components/token/TokenPreview.tsx
index 4ceab47..09d1930 100644
--- a/src/components/token/TokenPreview.js
+++ b/src/components/token/TokenPreview.tsx
@@ -1,8 +1,9 @@
-import React, { useState, useRef, useEffect } from "react";
+import { useState, useRef } from "react";
import { Box, IconButton } from "theme-ui";
import { Stage, Layer, Image, Rect, Group } from "react-konva";
import ReactResizeDetector from "react-resize-detector";
import useImage from "use-image";
+import Konva from "konva";
import usePreventOverscroll from "../../hooks/usePreventOverscroll";
import useStageInteraction from "../../hooks/useStageInteraction";
@@ -18,32 +19,32 @@ import GridOffIcon from "../../icons/GridOffIcon";
import { tokenSources } from "../../tokens";
import Grid from "../Grid";
+import { Token } from "../../types/Token";
-function TokenPreview({ token }) {
- const [tokenSourceData, setTokenSourceData] = useState({});
- useEffect(() => {
- if (token.id !== tokenSourceData.id) {
- setTokenSourceData(token);
- }
- }, [token, tokenSourceData]);
+type TokenPreviewProps = {
+ token: Token;
+};
- const tokenURL = useDataURL(tokenSourceData, tokenSources);
- const [tokenSourceImage] = useImage(tokenURL);
+function TokenPreview({ token }: TokenPreviewProps) {
+ const tokenURL = useDataURL(token, tokenSources);
+ const [tokenSourceImage] = useImage(tokenURL || "");
const [stageWidth, setStageWidth] = useState(1);
const [stageHeight, setStageHeight] = useState(1);
const [stageScale, setStageScale] = useState(1);
const stageTranslateRef = useRef({ x: 0, y: 0 });
- const tokenStageRef = useRef();
- const tokenLayerRef = useRef();
+ const tokenStageRef = useRef(null);
+ const tokenLayerRef = useRef(null);
- function handleResize(width, height) {
- setStageWidth(width);
- setStageHeight(height);
+ function handleResize(width?: number, height?: number) {
+ if (width && height) {
+ setStageWidth(width);
+ setStageHeight(height);
+ }
}
- const containerRef = useRef();
+ const containerRef = useRef(null);
usePreventOverscroll(containerRef);
const [tokenWidth, tokenHeight] = useImageCenter(
@@ -59,11 +60,11 @@ function TokenPreview({ token }) {
);
useStageInteraction(
- tokenStageRef.current,
+ tokenStageRef,
stageScale,
setStageScale,
stageTranslateRef,
- tokenLayerRef.current
+ tokenLayerRef
);
const [showGridPreview, setShowGridPreview] = useState(true);
diff --git a/src/components/token/TokenSettings.js b/src/components/token/TokenSettings.tsx
similarity index 62%
rename from src/components/token/TokenSettings.js
rename to src/components/token/TokenSettings.tsx
index 47e20df..92bffd2 100644
--- a/src/components/token/TokenSettings.js
+++ b/src/components/token/TokenSettings.tsx
@@ -1,17 +1,25 @@
-import React from "react";
import { Flex, Box, Input, Label } from "theme-ui";
import { isEmpty } from "../../helpers/shared";
import Select from "../Select";
-const categorySettings = [
+import { Token, TokenCategory } from "../../types/Token";
+import { TokenSettingsChangeEventHandler } from "../../types/Events";
+
+type CategorySetting = { value: TokenCategory; label: string };
+const categorySettings: CategorySetting[] = [
{ value: "character", label: "Character" },
{ value: "prop", label: "Prop" },
{ value: "vehicle", label: "Vehicle / Mount" },
];
-function TokenSettings({ token, onSettingsChange }) {
+type TokenSettingsProps = {
+ token: Token;
+ onSettingsChange: TokenSettingsChangeEventHandler;
+};
+
+function TokenSettings({ token, onSettingsChange }: TokenSettingsProps) {
const tokenEmpty = !token || isEmpty(token);
return (
@@ -20,7 +28,7 @@ function TokenSettings({ token, onSettingsChange }) {
onSettingsChange("name", e.target.value)}
+ onChange={(e) => onSettingsChange({ name: e.target.value })}
disabled={tokenEmpty}
my={1}
/>
@@ -30,12 +38,14 @@ function TokenSettings({ token, onSettingsChange }) {