Moved maps and tokens to a data source model
This will allow for easier custom token support as well as changing default tokens
This commit is contained in:
parent
98798235c9
commit
ed8f3bd283
@ -9,6 +9,8 @@ import MapDrawing from "./MapDrawing";
|
||||
import MapControls from "./MapControls";
|
||||
|
||||
import { omit } from "../../helpers/shared";
|
||||
import useDataSource from "../../helpers/useDataSource";
|
||||
import { mapSources as defaultMapSources } from "../../maps";
|
||||
|
||||
const mapTokenProxyClassName = "map-token__proxy";
|
||||
const mapTokenMenuClassName = "map-token__menu";
|
||||
@ -27,6 +29,8 @@ function Map({
|
||||
onMapDrawUndo,
|
||||
onMapDrawRedo,
|
||||
}) {
|
||||
const mapSource = useDataSource(map, defaultMapSources);
|
||||
|
||||
function handleProxyDragEnd(isOnMap, token) {
|
||||
if (isOnMap && onMapTokenChange) {
|
||||
onMapTokenChange(token);
|
||||
@ -219,7 +223,7 @@ function Map({
|
||||
userSelect: "none",
|
||||
touchAction: "none",
|
||||
}}
|
||||
src={map && map.source}
|
||||
src={mapSource}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
@ -323,10 +327,12 @@ function Map({
|
||||
<ProxyToken
|
||||
tokenClassName={mapTokenProxyClassName}
|
||||
onProxyDragEnd={handleProxyDragEnd}
|
||||
tokens={mapState && mapState.tokens}
|
||||
/>
|
||||
<TokenMenu
|
||||
tokenClassName={mapTokenMenuClassName}
|
||||
onTokenChange={onMapTokenChange}
|
||||
tokens={mapState && mapState.tokens}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@ -7,6 +7,9 @@ import RemoveMapIcon from "../../icons/RemoveMapIcon";
|
||||
import ResetMapIcon from "../../icons/ResetMapIcon";
|
||||
import ExpandMoreDotIcon from "../../icons/ExpandMoreDotIcon";
|
||||
|
||||
import useDataSource from "../../helpers/useDataSource";
|
||||
import { mapSources as defaultMapSources } from "../../maps";
|
||||
|
||||
function MapTile({
|
||||
map,
|
||||
isSelected,
|
||||
@ -15,8 +18,10 @@ function MapTile({
|
||||
onMapReset,
|
||||
onSubmit,
|
||||
}) {
|
||||
const mapSource = useDataSource(map, defaultMapSources);
|
||||
const [isMapTileMenuOpen, setIsTileMenuOpen] = useState(false);
|
||||
const [hasMapState, setHasMapState] = useState(false);
|
||||
const isDefault = map.type === "default";
|
||||
|
||||
useEffect(() => {
|
||||
async function checkForMapState() {
|
||||
@ -28,7 +33,6 @@ function MapTile({
|
||||
setHasMapState(true);
|
||||
}
|
||||
}
|
||||
|
||||
checkForMapState();
|
||||
}, [map]);
|
||||
|
||||
@ -120,18 +124,18 @@ function MapTile({
|
||||
>
|
||||
<UIImage
|
||||
sx={{ width: "100%", height: "100%", objectFit: "contain" }}
|
||||
src={map.source}
|
||||
src={mapSource}
|
||||
/>
|
||||
{/* Show expand button only if both reset and remove is available */}
|
||||
{isSelected && (
|
||||
<Box sx={{ position: "absolute", top: 0, right: 0 }}>
|
||||
{map.default && hasMapState && resetButton(map)}
|
||||
{!map.default && hasMapState && !isMapTileMenuOpen && expandButton}
|
||||
{!map.default && !hasMapState && removeButton(map)}
|
||||
{isDefault && hasMapState && resetButton(map)}
|
||||
{!isDefault && hasMapState && !isMapTileMenuOpen && expandButton}
|
||||
{!isDefault && !hasMapState && removeButton(map)}
|
||||
</Box>
|
||||
)}
|
||||
{/* Tile menu for two actions */}
|
||||
{!map.default && isMapTileMenuOpen && isSelected && (
|
||||
{!isDefault && isMapTileMenuOpen && isSelected && (
|
||||
<Flex
|
||||
sx={{
|
||||
position: "absolute",
|
||||
@ -145,7 +149,7 @@ function MapTile({
|
||||
bg="muted"
|
||||
onClick={() => setIsTileMenuOpen(false)}
|
||||
>
|
||||
{!map.default && removeButton(map)}
|
||||
{!isDefault && removeButton(map)}
|
||||
{hasMapState && resetButton(map)}
|
||||
</Flex>
|
||||
)}
|
||||
|
@ -5,8 +5,13 @@ import TokenLabel from "../token/TokenLabel";
|
||||
import TokenStatus from "../token/TokenStatus";
|
||||
|
||||
import usePreventTouch from "../../helpers/usePreventTouch";
|
||||
import useDataSource from "../../helpers/useDataSource";
|
||||
|
||||
import { tokenSources } from "../../tokens";
|
||||
|
||||
function MapToken({ token, tokenSizePercent, className }) {
|
||||
const imageSource = useDataSource(token, tokenSources);
|
||||
|
||||
const imageRef = useRef();
|
||||
// Stop touch to prevent 3d touch gesutre on iOS
|
||||
usePreventTouch(imageRef);
|
||||
@ -47,15 +52,12 @@ function MapToken({ token, tokenSizePercent, className }) {
|
||||
touchAction: "none",
|
||||
width: "100%",
|
||||
}}
|
||||
src={token.image}
|
||||
// pass data into the dom element used to pass state to the ProxyToken
|
||||
src={imageSource}
|
||||
// pass id into the dom element which is then used by the ProxyToken
|
||||
data-id={token.id}
|
||||
data-size={token.size}
|
||||
data-label={token.label}
|
||||
data-status={token.status}
|
||||
ref={imageRef}
|
||||
/>
|
||||
{token.status && <TokenStatus statuses={token.status.split(" ")} />}
|
||||
{token.statuses && <TokenStatus statuses={token.statuses} />}
|
||||
{token.label && <TokenLabel label={token.label} />}
|
||||
</Box>
|
||||
</Box>
|
||||
|
@ -1,20 +1,29 @@
|
||||
import React, { useRef } from "react";
|
||||
import { Image } from "theme-ui";
|
||||
import { Box, Image } from "theme-ui";
|
||||
|
||||
import usePreventTouch from "../../helpers/usePreventTouch";
|
||||
import useDataSource from "../../helpers/useDataSource";
|
||||
|
||||
import { tokenSources } from "../../tokens";
|
||||
|
||||
function ListToken({ token, className }) {
|
||||
const imageSource = useDataSource(token, tokenSources);
|
||||
|
||||
function ListToken({ image, className }) {
|
||||
const imageRef = useRef();
|
||||
// Stop touch to prevent 3d touch gesutre on iOS
|
||||
usePreventTouch(imageRef);
|
||||
|
||||
return (
|
||||
<Image
|
||||
src={image}
|
||||
ref={imageRef}
|
||||
className={className}
|
||||
sx={{ userSelect: "none", touchAction: "none" }}
|
||||
/>
|
||||
<Box my={2} mx={3} sx={{ width: "48px", height: "48px" }}>
|
||||
<Image
|
||||
src={imageSource}
|
||||
ref={imageRef}
|
||||
className={className}
|
||||
sx={{ userSelect: "none", touchAction: "none" }}
|
||||
// pass id into the dom element which is then used by the ProxyToken
|
||||
data-id={token.id}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -8,14 +8,32 @@ import usePortal from "../../helpers/usePortal";
|
||||
import TokenLabel from "./TokenLabel";
|
||||
import TokenStatus from "./TokenStatus";
|
||||
|
||||
function ProxyToken({ tokenClassName, onProxyDragEnd }) {
|
||||
/**
|
||||
* @callback onProxyDragEnd
|
||||
* @param {boolean} isOnMap whether the token was dropped on the map
|
||||
* @param {Object} token the token that was dropped
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} tokenClassName The class name to attach the interactjs handler to
|
||||
* @param {onProxyDragEnd} onProxyDragEnd Called when the proxy token is dropped
|
||||
* @param {Object} tokens An optional mapping of tokens to use as a base when calling OnProxyDragEnd
|
||||
*/
|
||||
function ProxyToken({ tokenClassName, onProxyDragEnd, tokens }) {
|
||||
const proxyContainer = usePortal("root");
|
||||
|
||||
const [imageSource, setImageSource] = useState("");
|
||||
const [label, setLabel] = useState("");
|
||||
const [status, setStatus] = useState("");
|
||||
const [tokenId, setTokenId] = useState(null);
|
||||
const proxyRef = useRef();
|
||||
|
||||
// Store the tokens in a ref and access in the interactjs loop
|
||||
// This is needed to stop interactjs from creating multiple listeners
|
||||
const tokensRef = useRef(tokens);
|
||||
useEffect(() => {
|
||||
tokensRef.current = tokens;
|
||||
}, [tokens]);
|
||||
|
||||
const proxyOnMap = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
@ -26,8 +44,7 @@ function ProxyToken({ tokenClassName, onProxyDragEnd }) {
|
||||
// Hide the token and copy it's image to the proxy
|
||||
target.parentElement.style.opacity = "0.25";
|
||||
setImageSource(target.src);
|
||||
setLabel(target.dataset.label || "");
|
||||
setStatus(target.dataset.status || "");
|
||||
setTokenId(target.dataset.id);
|
||||
|
||||
let proxy = proxyRef.current;
|
||||
if (proxy) {
|
||||
@ -88,13 +105,14 @@ function ProxyToken({ tokenClassName, onProxyDragEnd }) {
|
||||
x = x / (mapImageRect.right - mapImageRect.left);
|
||||
y = y / (mapImageRect.bottom - mapImageRect.top);
|
||||
|
||||
target.setAttribute("data-x", x);
|
||||
target.setAttribute("data-y", y);
|
||||
// Get the token from the supplied tokens if it exists
|
||||
const id = target.getAttribute("data-id");
|
||||
const token = tokensRef.current[id] || {};
|
||||
|
||||
onProxyDragEnd(proxyOnMap.current, {
|
||||
image: target.src,
|
||||
// Pass in props stored as data- in the dom node
|
||||
...target.dataset,
|
||||
...token,
|
||||
x,
|
||||
y,
|
||||
});
|
||||
}
|
||||
|
||||
@ -140,12 +158,20 @@ function ProxyToken({ tokenClassName, onProxyDragEnd }) {
|
||||
width: "100%",
|
||||
}}
|
||||
/>
|
||||
{status && <TokenStatus statuses={status.split(" ")} />}
|
||||
{label && <TokenLabel label={label} />}
|
||||
{tokens[tokenId] && tokens[tokenId].statuses && (
|
||||
<TokenStatus statuses={tokens[tokenId].statuses} />
|
||||
)}
|
||||
{tokens[tokenId] && tokens[tokenId].label && (
|
||||
<TokenLabel label={tokens[tokenId].label} />
|
||||
)}
|
||||
</Box>
|
||||
</Box>,
|
||||
proxyContainer
|
||||
);
|
||||
}
|
||||
|
||||
ProxyToken.defaultProps = {
|
||||
tokens: {},
|
||||
};
|
||||
|
||||
export default ProxyToken;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import interact from "interactjs";
|
||||
import { Box, Input } from "theme-ui";
|
||||
|
||||
@ -6,13 +6,31 @@ import MapMenu from "../map/MapMenu";
|
||||
|
||||
import colors, { colorOptions } from "../../helpers/colors";
|
||||
|
||||
function TokenMenu({ tokenClassName, onTokenChange }) {
|
||||
/**
|
||||
* @callback onTokenChange
|
||||
* @param {Object} token the token that was changed
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} tokenClassName The class name to attach the interactjs handler to
|
||||
* @param {onProxyDragEnd} onTokenChange Called when the the token data is changed
|
||||
* @param {Object} tokens An mapping of tokens to use as a base when calling onTokenChange
|
||||
*/
|
||||
function TokenMenu({ tokenClassName, onTokenChange, tokens }) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
function handleRequestClose() {
|
||||
setIsOpen(false);
|
||||
}
|
||||
|
||||
// Store the tokens in a ref and access in the interactjs loop
|
||||
// This is needed to stop interactjs from creating multiple listeners
|
||||
const tokensRef = useRef(tokens);
|
||||
useEffect(() => {
|
||||
tokensRef.current = tokens;
|
||||
}, [tokens]);
|
||||
|
||||
const [currentToken, setCurrentToken] = useState({});
|
||||
const [menuLeft, setMenuLeft] = useState(0);
|
||||
const [menuTop, setMenuTop] = useState(0);
|
||||
@ -31,30 +49,26 @@ function TokenMenu({ tokenClassName, onTokenChange }) {
|
||||
}
|
||||
|
||||
function handleStatusChange(status) {
|
||||
const statuses =
|
||||
currentToken.status.split(" ").filter((s) => s !== "") || [];
|
||||
const statuses = currentToken.statuses;
|
||||
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,
|
||||
statuses: newStatuses,
|
||||
}));
|
||||
onTokenChange({ ...currentToken, status: newStatus });
|
||||
onTokenChange({ ...currentToken, statuses: newStatuses });
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
function handleTokenMenuOpen(event) {
|
||||
const target = event.target;
|
||||
const dataset = (target && target.dataset) || {};
|
||||
setCurrentToken({
|
||||
image: target.src,
|
||||
...dataset,
|
||||
});
|
||||
const id = target.getAttribute("data-id");
|
||||
const token = tokensRef.current[id] || {};
|
||||
setCurrentToken(token);
|
||||
|
||||
const targetRect = target.getBoundingClientRect();
|
||||
setMenuLeft(targetRect.left);
|
||||
@ -162,7 +176,7 @@ function TokenMenu({ tokenClassName, onTokenChange }) {
|
||||
onClick={() => handleStatusChange(color)}
|
||||
aria-label={`Token label Color ${color}`}
|
||||
>
|
||||
{currentToken.status && currentToken.status.includes(color) && (
|
||||
{currentToken.statuses && currentToken.statuses.includes(color) && (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
|
@ -1,17 +1,28 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Box } from "theme-ui";
|
||||
import shortid from "shortid";
|
||||
import SimpleBar from "simplebar-react";
|
||||
|
||||
import * as tokens from "../../tokens";
|
||||
import { tokens as defaultTokens } from "../../tokens";
|
||||
|
||||
import ListToken from "./ListToken";
|
||||
import ProxyToken from "./ProxyToken";
|
||||
import NumberInput from "../NumberInput";
|
||||
|
||||
import { fromEntries } from "../../helpers/shared";
|
||||
|
||||
const listTokenClassName = "list-token";
|
||||
|
||||
function Tokens({ onCreateMapToken }) {
|
||||
const [tokens, setTokens] = useState([]);
|
||||
useEffect(() => {
|
||||
const defaultTokensWithIds = [];
|
||||
for (let defaultToken of defaultTokens) {
|
||||
defaultTokensWithIds.push({ ...defaultToken, id: defaultToken.name });
|
||||
}
|
||||
setTokens(defaultTokensWithIds);
|
||||
}, []);
|
||||
|
||||
const [tokenSize, setTokenSize] = useState(1);
|
||||
|
||||
function handleProxyDragEnd(isOnMap, token) {
|
||||
@ -22,7 +33,7 @@ function Tokens({ onCreateMapToken }) {
|
||||
id: shortid.generate(),
|
||||
size: tokenSize,
|
||||
label: "",
|
||||
status: "",
|
||||
statuses: [],
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -38,10 +49,12 @@ function Tokens({ onCreateMapToken }) {
|
||||
}}
|
||||
>
|
||||
<SimpleBar style={{ height: "calc(100% - 58px)", overflowX: "hidden" }}>
|
||||
{Object.entries(tokens).map(([id, image]) => (
|
||||
<Box key={id} my={2} mx={3} sx={{ width: "48px", height: "48px" }}>
|
||||
<ListToken image={image} className={listTokenClassName} />
|
||||
</Box>
|
||||
{tokens.map((token) => (
|
||||
<ListToken
|
||||
key={token.id}
|
||||
token={token}
|
||||
className={listTokenClassName}
|
||||
/>
|
||||
))}
|
||||
</SimpleBar>
|
||||
<Box pt={1} bg="muted" sx={{ height: "58px" }}>
|
||||
@ -57,6 +70,7 @@ function Tokens({ onCreateMapToken }) {
|
||||
<ProxyToken
|
||||
tokenClassName={listTokenClassName}
|
||||
onProxyDragEnd={handleProxyDragEnd}
|
||||
tokens={fromEntries(tokens.map((token) => [token.id, token]))}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
29
src/helpers/useDataSource.js
Normal file
29
src/helpers/useDataSource.js
Normal file
@ -0,0 +1,29 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
// Helper function to load either file or default data
|
||||
// into a URL and ensure that it is revoked if needed
|
||||
function useDataSource(data, defaultSources) {
|
||||
const [dataSource, setDataSource] = useState(null);
|
||||
useEffect(() => {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
let url = null;
|
||||
if (data.type === "file") {
|
||||
url = URL.createObjectURL(data.file);
|
||||
} else if (data.type === "default") {
|
||||
url = defaultSources[data.name];
|
||||
}
|
||||
setDataSource(url);
|
||||
|
||||
return () => {
|
||||
if (data.type === "file" && url) {
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
};
|
||||
}, [data, defaultSources]);
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
export default useDataSource;
|
@ -5,46 +5,20 @@ import stoneImage from "./Stone Grid 22x22.jpg";
|
||||
import waterImage from "./Water Grid 22x22.jpg";
|
||||
import woodImage from "./Wood Grid 22x22.jpg";
|
||||
|
||||
const defaultProps = {
|
||||
export const mapSources = {
|
||||
blank: blankImage,
|
||||
grass: grassImage,
|
||||
sand: sandImage,
|
||||
stone: stoneImage,
|
||||
water: waterImage,
|
||||
wood: woodImage,
|
||||
};
|
||||
|
||||
export const maps = Object.keys(mapSources).map((name) => ({
|
||||
name,
|
||||
gridX: 22,
|
||||
gridY: 22,
|
||||
width: 1024,
|
||||
height: 1024,
|
||||
default: true,
|
||||
};
|
||||
|
||||
export const blank = {
|
||||
...defaultProps,
|
||||
source: blankImage,
|
||||
id: "__default_blank",
|
||||
};
|
||||
|
||||
export const grass = {
|
||||
...defaultProps,
|
||||
source: grassImage,
|
||||
id: "__default_grass",
|
||||
};
|
||||
|
||||
export const sand = {
|
||||
...defaultProps,
|
||||
source: sandImage,
|
||||
id: "__default_sand",
|
||||
};
|
||||
|
||||
export const stone = {
|
||||
...defaultProps,
|
||||
source: stoneImage,
|
||||
id: "__default_stone",
|
||||
};
|
||||
|
||||
export const water = {
|
||||
...defaultProps,
|
||||
source: waterImage,
|
||||
id: "__default_water",
|
||||
};
|
||||
|
||||
export const wood = {
|
||||
...defaultProps,
|
||||
source: woodImage,
|
||||
id: "__default_wood",
|
||||
};
|
||||
type: "default",
|
||||
}));
|
||||
|
@ -7,7 +7,7 @@ import db from "../database";
|
||||
import Modal from "../components/Modal";
|
||||
import MapTiles from "../components/map/MapTiles";
|
||||
|
||||
import * as defaultMaps from "../maps";
|
||||
import { maps as defaultMaps } from "../maps";
|
||||
|
||||
const defaultMapSize = 22;
|
||||
const defaultMapState = {
|
||||
@ -37,15 +37,15 @@ function SelectMapModal({
|
||||
async function loadDefaultMaps() {
|
||||
const defaultMapsWithIds = [];
|
||||
const defaultMapStates = [];
|
||||
// Store the default maps into the db in reverse so the whie map is first
|
||||
// in the UI
|
||||
const defaultMapArray = Object.values(defaultMaps).reverse();
|
||||
for (let i = 0; i < defaultMapArray.length; i++) {
|
||||
const defaultMap = defaultMapArray[i];
|
||||
const id = `${defaultMap.id}--${shortid.generate()}`;
|
||||
// Reverse maps to ensure the blank map is first in the list
|
||||
const sortedMaps = [...defaultMaps].reverse();
|
||||
for (let i = 0; i < sortedMaps.length; i++) {
|
||||
const defaultMap = sortedMaps[i];
|
||||
const id = `__default_${defaultMap.name}--${shortid.generate()}`;
|
||||
defaultMapsWithIds.push({
|
||||
...defaultMap,
|
||||
id,
|
||||
// Emulate the time increasing to avoid sort errors
|
||||
timestamp: Date.now() + i,
|
||||
});
|
||||
defaultMapStates.push({ ...defaultMapState, mapId: id });
|
||||
@ -64,12 +64,6 @@ function SelectMapModal({
|
||||
} else {
|
||||
// Sort maps by the time they were added
|
||||
storedMaps.sort((a, b) => b.timestamp - a.timestamp);
|
||||
for (let map of storedMaps) {
|
||||
// Recreate image urls for file based maps
|
||||
if (map.file) {
|
||||
map.source = URL.createObjectURL(map.file);
|
||||
}
|
||||
}
|
||||
setMaps(storedMaps);
|
||||
}
|
||||
}
|
||||
@ -101,21 +95,23 @@ function SelectMapModal({
|
||||
}
|
||||
}
|
||||
}
|
||||
const url = URL.createObjectURL(file);
|
||||
let image = new Image();
|
||||
setImageLoading(true);
|
||||
// Create and load the image temporarily to get its dimensions
|
||||
const url = URL.createObjectURL(file);
|
||||
image.onload = function () {
|
||||
handleMapAdd({
|
||||
file,
|
||||
type: "file",
|
||||
gridX: fileGridX,
|
||||
gridY: fileGridY,
|
||||
width: image.width,
|
||||
height: image.height,
|
||||
source: url,
|
||||
id: shortid.generate(),
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
setImageLoading(false);
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
image.src = url;
|
||||
}
|
||||
@ -135,7 +131,6 @@ function SelectMapModal({
|
||||
setGridY(map.gridY);
|
||||
}
|
||||
|
||||
// Keep track of removed maps
|
||||
async function handleMapRemove(id) {
|
||||
await db.table("maps").delete(id);
|
||||
await db.table("states").delete(id);
|
||||
@ -145,7 +140,7 @@ function SelectMapModal({
|
||||
return filtered;
|
||||
});
|
||||
// Removed the map from the map screen if needed
|
||||
if (currentMap.id === selectedMap.id) {
|
||||
if (currentMap && currentMap.id === selectedMap.id) {
|
||||
onMapChange(null);
|
||||
}
|
||||
}
|
||||
@ -160,7 +155,7 @@ function SelectMapModal({
|
||||
const state = { ...defaultMapState, mapId: id };
|
||||
await db.table("states").put(state);
|
||||
// Reset the state of the current map if needed
|
||||
if (currentMap.id === selectedMap.id) {
|
||||
if (currentMap && currentMap.id === selectedMap.id) {
|
||||
onMapStateChange(state);
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +63,9 @@ function Game() {
|
||||
}
|
||||
|
||||
async function handleMapTokenChange(token) {
|
||||
if (mapState === null) {
|
||||
return;
|
||||
}
|
||||
setMapState((prevMapState) => ({
|
||||
...prevMapState,
|
||||
tokens: {
|
||||
|
@ -19,7 +19,7 @@ import swords from "./Swords.png";
|
||||
import tree from "./Tree.png";
|
||||
import triangle from "./Triangle.png";
|
||||
|
||||
export {
|
||||
export const tokenSources = {
|
||||
axes,
|
||||
bird,
|
||||
book,
|
||||
@ -39,5 +39,10 @@ export {
|
||||
sun,
|
||||
swords,
|
||||
tree,
|
||||
triangle
|
||||
triangle,
|
||||
};
|
||||
|
||||
export const tokens = Object.keys(tokenSources).map((name) => ({
|
||||
name,
|
||||
type: "default",
|
||||
}));
|
||||
|
Loading…
Reference in New Issue
Block a user