diff --git a/src/components/map/AddMapButton.js b/src/components/map/AddMapButton.js index d0ff197..99dd26b 100644 --- a/src/components/map/AddMapButton.js +++ b/src/components/map/AddMapButton.js @@ -1,11 +1,9 @@ -import React, { useRef, useState, useEffect } from "react"; +import React, { useState } from "react"; import { IconButton } from "theme-ui"; import AddMapModal from "../../modals/AddMapModal"; import AddMapIcon from "../../icons/AddMapIcon"; -const defaultMapSize = 22; - function AddMapButton({ onMapChange }) { const [isAddModalOpen, setIsAddModalOpen] = useState(false); function openModal() { @@ -15,49 +13,11 @@ function AddMapButton({ onMapChange }) { setIsAddModalOpen(false); } - const [imageLoaded, setImageLoaded] = useState(false); - - const mapDataRef = useRef(null); - const [mapSource, setMapSource] = useState(null); - function handleImageUpload(file, fileGridX, fileGridY) { - const url = URL.createObjectURL(file); - let image = new Image(); - image.onload = function () { - mapDataRef.current = { - file, - gridX: fileGridX || gridX, - gridY: fileGridY || gridY, - width: image.width, - height: image.height, - }; - setImageLoaded(true); - }; - image.src = url; - setMapSource(url); - if (fileGridX) { - setGridX(fileGridX); - } - if (fileGridY) { - setGridY(fileGridY); - } - } - - function handleDone() { - if (mapDataRef.current && mapSource) { - onMapChange(mapDataRef.current, mapSource); - } + function handleDone(map) { + onMapChange(map); closeModal(); } - const [gridX, setGridX] = useState(defaultMapSize); - const [gridY, setGridY] = useState(defaultMapSize); - useEffect(() => { - if (mapDataRef.current) { - mapDataRef.current.gridX = gridX; - mapDataRef.current.gridY = gridY; - } - }, [gridX, gridY]); - return ( <> @@ -67,13 +27,6 @@ function AddMapButton({ onMapChange }) { isOpen={isAddModalOpen} onRequestClose={closeModal} onDone={handleDone} - onImageUpload={handleImageUpload} - gridX={gridX} - onGridXChange={setGridX} - gridY={gridY} - onGridYChange={setGridY} - imageLoaded={imageLoaded} - mapSource={mapSource} /> ); diff --git a/src/components/map/Map.js b/src/components/map/Map.js index 0108a9a..620ee94 100644 --- a/src/components/map/Map.js +++ b/src/components/map/Map.js @@ -17,8 +17,7 @@ const minZoom = 0.1; const maxZoom = 5; function Map({ - mapSource, - mapData, + map, tokens, onMapTokenChange, onMapTokenRemove, @@ -80,7 +79,7 @@ function Map({ }, [drawActions, drawActionIndex]); const disabledTools = []; - if (!mapData) { + if (!map) { disabledTools.push("pan"); disabledTools.push("brush"); } @@ -134,7 +133,7 @@ function Map({ move: (e) => handleMove(e, false), }, cursorChecker: () => { - return selectedTool === "pan" && mapData ? "move" : "default"; + return selectedTool === "pan" && map ? "move" : "default"; }, }) .on("doubletap", (event) => { @@ -147,12 +146,12 @@ function Map({ return () => { mapInteract.unset(); }; - }, [selectedTool, mapData]); + }, [selectedTool, map]); // Reset map transform when map changes useEffect(() => { setTranslateAndScale({ x: 0, y: 0 }, 1); - }, [mapSource]); + }, [map]); // Bind the wheel event of the map via a ref // in order to support non-passive event listening @@ -194,11 +193,11 @@ function Map({ const mapRef = useRef(null); const mapContainerRef = useRef(); - const gridX = mapData && mapData.gridX; - const gridY = mapData && mapData.gridY; + const gridX = map && map.gridX; + const gridY = map && map.gridY; const gridSizeNormalized = { x: 1 / gridX || 0, y: 1 / gridY || 0 }; const tokenSizePercent = gridSizeNormalized.x * 100; - const aspectRatio = (mapData && mapData.width / mapData.height) || 1; + const aspectRatio = (map && map.width / map.height) || 1; const mapImage = ( ); @@ -278,8 +277,8 @@ function Map({ /> {mapImage} + + + ); + } + + return ( + + {maps.map(tile)} + + + + + ); +} + +export default MapSelect; diff --git a/src/icons/AddIcon.js b/src/icons/AddIcon.js new file mode 100644 index 0000000..69c5efd --- /dev/null +++ b/src/icons/AddIcon.js @@ -0,0 +1,18 @@ +import React from "react"; + +function AddIcon() { + return ( + + + + + ); +} + +export default AddIcon; diff --git a/src/maps/Blank Grid 22x22.jpg b/src/maps/Blank Grid 22x22.jpg new file mode 100755 index 0000000..317b171 Binary files /dev/null and b/src/maps/Blank Grid 22x22.jpg differ diff --git a/src/maps/Grass Grid 22x22.jpg b/src/maps/Grass Grid 22x22.jpg new file mode 100755 index 0000000..93a20fc Binary files /dev/null and b/src/maps/Grass Grid 22x22.jpg differ diff --git a/src/maps/Sand Grid 22x22.jpg b/src/maps/Sand Grid 22x22.jpg new file mode 100755 index 0000000..41995c3 Binary files /dev/null and b/src/maps/Sand Grid 22x22.jpg differ diff --git a/src/maps/Stone Grid 22x22.jpg b/src/maps/Stone Grid 22x22.jpg new file mode 100755 index 0000000..1748205 Binary files /dev/null and b/src/maps/Stone Grid 22x22.jpg differ diff --git a/src/maps/Water Grid 22x22.jpg b/src/maps/Water Grid 22x22.jpg new file mode 100755 index 0000000..83fbc3e Binary files /dev/null and b/src/maps/Water Grid 22x22.jpg differ diff --git a/src/maps/Wood Grid 22x22.jpg b/src/maps/Wood Grid 22x22.jpg new file mode 100755 index 0000000..5aae7a6 Binary files /dev/null and b/src/maps/Wood Grid 22x22.jpg differ diff --git a/src/maps/index.js b/src/maps/index.js new file mode 100644 index 0000000..5deb7b4 --- /dev/null +++ b/src/maps/index.js @@ -0,0 +1,49 @@ +import blankImage from "./Blank Grid 22x22.jpg"; +import grassImage from "./Grass Grid 22x22.jpg"; +import sandImage from "./Sand Grid 22x22.jpg"; +import stoneImage from "./Stone Grid 22x22.jpg"; +import waterImage from "./Water Grid 22x22.jpg"; +import woodImage from "./Wood Grid 22x22.jpg"; + +const defaultProps = { + gridX: 22, + gridY: 22, + width: 1024, + height: 1024, +}; + +export const blank = { + ...defaultProps, + source: blankImage, + name: "Blank Grid 22x22", +}; + +export const grass = { + ...defaultProps, + source: grassImage, + name: "Grass Grid 22x22", +}; + +export const sand = { + ...defaultProps, + source: sandImage, + name: "Sand Grid 22x22", +}; + +export const stone = { + ...defaultProps, + source: stoneImage, + name: "Stone Grid 22x22", +}; + +export const water = { + ...defaultProps, + source: waterImage, + name: "Water Grid 22x22", +}; + +export const wood = { + ...defaultProps, + source: woodImage, + name: "Wood Grid 22x22", +}; diff --git a/src/modals/AddMapModal.js b/src/modals/AddMapModal.js index dedd74c..6dbcab9 100644 --- a/src/modals/AddMapModal.js +++ b/src/modals/AddMapModal.js @@ -1,31 +1,41 @@ -import React, { useRef, useState } from "react"; -import { - Box, - Button, - Image as UIImage, - Flex, - Label, - Input, - Text, -} from "theme-ui"; +import React, { useRef, useState, useEffect } from "react"; +import { Box, Button, Flex, Label, Input, Text } from "theme-ui"; import Modal from "../components/Modal"; +import MapSelect from "../components/map/MapSelect"; + +import * as defaultMaps from "../maps"; + +const defaultMapSize = 22; + +function AddMapModal({ isOpen, onRequestClose, onDone }) { + const [imageLoading, setImageLoading] = useState(false); + + const [currentMap, setCurrentMap] = useState(-1); + const [maps, setMaps] = useState(Object.values(defaultMaps)); + + const [gridX, setGridX] = useState(defaultMapSize); + const [gridY, setGridY] = useState(defaultMapSize); + useEffect(() => { + setMaps((prevMaps) => { + const newMaps = [...prevMaps]; + const changedMap = newMaps[currentMap]; + if (changedMap) { + changedMap.gridX = gridX; + changedMap.gridY = gridY; + } + return newMaps; + }); + }, [gridX, gridY, currentMap]); -function AddMapModal({ - isOpen, - onRequestClose, - onDone, - onImageUpload, - gridX, - onGridXChange, - gridY, - onGridYChange, - imageLoaded, - mapSource, -}) { const fileInputRef = useRef(); function handleImageUpload(file) { + if (!file) { + return; + } + let fileGridX = defaultMapSize; + let fileGridY = defaultMapSize; if (file.name) { // Match against a regex to find the grid size in the file name // e.g. Cave 22x23 will return [["22x22", "22", "x", "23"]] @@ -35,12 +45,35 @@ function AddMapModal({ const matchX = parseInt(lastMatch[1]); const matchY = parseInt(lastMatch[3]); if (!isNaN(matchX) && !isNaN(matchY)) { - onImageUpload(file, matchX, matchY); - return; + fileGridX = matchX; + fileGridY = matchY; } } } - onImageUpload(file); + const url = URL.createObjectURL(file); + let image = new Image(); + setImageLoading(true); + image.onload = function () { + setMaps((prevMaps) => { + const newMaps = [ + ...prevMaps, + { + file, + gridX: fileGridX, + gridY: fileGridY, + width: image.width, + height: image.height, + source: url, + }, + ]; + setCurrentMap(newMaps.length - 1); + return newMaps; + }); + setGridX(fileGridX); + setGridY(fileGridY); + setImageLoading(false); + }; + image.src = url; } function openImageDialog() { @@ -78,7 +111,7 @@ function AddMapModal({ as="form" onSubmit={(e) => { e.preventDefault(); - onDone(); + onDone(maps[currentMap]); }} onDragEnter={handleImageDragEnter} > @@ -89,7 +122,6 @@ function AddMapModal({ style={{ display: "none" }} ref={fileInputRef} /> - Add map - + @@ -118,7 +138,7 @@ function AddMapModal({ type="number" name="gridX" value={gridX} - onChange={(e) => onGridXChange(e.target.value)} + onChange={(e) => setGridX(e.target.value)} /> @@ -127,25 +147,13 @@ function AddMapModal({ type="number" name="gridY" value={gridY} - onChange={(e) => onGridYChange(e.target.value)} + onChange={(e) => setGridY(e.target.value)} /> - {mapSource ? ( - - ) : ( - - )} + {dragging && ( ({ @@ -150,8 +142,8 @@ function Game() { function handlePeerData({ data, peer }) { if (data.id === "sync") { - if (mapSource) { - peer.connection.send({ id: "map", data: mapDataRef.current }); + if (map) { + peer.connection.send({ id: "map", data: map }); } if (mapTokens) { peer.connection.send({ id: "tokenEdit", data: mapTokens }); @@ -164,9 +156,9 @@ function Game() { } } if (data.id === "map") { - const blob = new Blob([data.data.file]); - mapDataRef.current = { ...data.data, file: blob }; - setMapSource(URL.createObjectURL(mapDataRef.current.file)); + const file = new Blob([data.data.file]); + const source = URL.createObjectURL(file); + setMap({ ...data.data, file, source }); } if (data.id === "tokenEdit") { setMapTokens((prevMapTokens) => ({ @@ -302,8 +294,7 @@ function Game() { onStreamEnd={handleStreamEnd} />