Added state to default maps and added clear state option to map tile
This commit is contained in:
parent
6f0df1c674
commit
10c259a6b3
143
src/components/map/MapTile.js
Normal file
143
src/components/map/MapTile.js
Normal file
@ -0,0 +1,143 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Flex, Image as UIImage, IconButton, Box } from "theme-ui";
|
||||
|
||||
import db from "../../database";
|
||||
|
||||
import RemoveMapIcon from "../../icons/RemoveMapIcon";
|
||||
import ResetMapIcon from "../../icons/ResetMapIcon";
|
||||
import ExpandMoreDotIcon from "../../icons/ExpandMoreDotIcon";
|
||||
|
||||
function MapTile({ map, isSelected, onMapSelect, onMapRemove, onMapReset }) {
|
||||
const [isMapTileMenuOpen, setIsTileMenuOpen] = useState(false);
|
||||
const [hasMapState, setHasMapState] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
async function checkForMapState() {
|
||||
const state = await db.table("states").get(map.id);
|
||||
if (
|
||||
state &&
|
||||
(Object.values(state.tokens).length > 0 || state.drawActions.length > 0)
|
||||
) {
|
||||
setHasMapState(true);
|
||||
}
|
||||
}
|
||||
|
||||
checkForMapState();
|
||||
}, [map]);
|
||||
|
||||
const expandButton = (
|
||||
<IconButton
|
||||
aria-label="Show Map Actions"
|
||||
title="Show Map Actions"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsTileMenuOpen(true);
|
||||
}}
|
||||
bg="overlay"
|
||||
sx={{ borderRadius: "50%" }}
|
||||
m={1}
|
||||
>
|
||||
<ExpandMoreDotIcon />
|
||||
</IconButton>
|
||||
);
|
||||
|
||||
function removeButton(map) {
|
||||
return (
|
||||
<IconButton
|
||||
aria-label="Remove Map"
|
||||
title="Remove Map"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsTileMenuOpen(false);
|
||||
onMapRemove(map.id);
|
||||
}}
|
||||
bg="overlay"
|
||||
sx={{ borderRadius: "50%" }}
|
||||
m={1}
|
||||
>
|
||||
<RemoveMapIcon />
|
||||
</IconButton>
|
||||
);
|
||||
}
|
||||
|
||||
function resetButton(map) {
|
||||
return (
|
||||
<IconButton
|
||||
aria-label="Reset Map"
|
||||
title="Reset Map"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setHasMapState(false);
|
||||
setIsTileMenuOpen(false);
|
||||
onMapReset(map.id);
|
||||
}}
|
||||
bg="overlay"
|
||||
sx={{ borderRadius: "50%" }}
|
||||
m={1}
|
||||
>
|
||||
<ResetMapIcon />
|
||||
</IconButton>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex
|
||||
key={map.id}
|
||||
sx={{
|
||||
borderColor: "primary",
|
||||
borderStyle: isSelected ? "solid" : "none",
|
||||
borderWidth: "4px",
|
||||
position: "relative",
|
||||
width: "150px",
|
||||
height: "150px",
|
||||
borderRadius: "4px",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
m={2}
|
||||
bg="muted"
|
||||
onClick={() => {
|
||||
setIsTileMenuOpen(false);
|
||||
onMapSelect(map);
|
||||
}}
|
||||
>
|
||||
<UIImage
|
||||
sx={{ width: "100%", height: "100%", objectFit: "contain" }}
|
||||
src={map.source}
|
||||
/>
|
||||
{/* 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)}
|
||||
</Box>
|
||||
)}
|
||||
{/* Tile menu for two actions */}
|
||||
{!map.default && isMapTileMenuOpen && isSelected && (
|
||||
<Flex
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
bg="muted"
|
||||
onClick={() => setIsTileMenuOpen(false)}
|
||||
>
|
||||
{!map.default && removeButton(map)}
|
||||
{hasMapState && resetButton(map)}
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
export default MapTile;
|
@ -1,60 +1,18 @@
|
||||
import React from "react";
|
||||
import { Flex, Image as UIImage, IconButton } from "theme-ui";
|
||||
import { Flex } from "theme-ui";
|
||||
|
||||
import AddIcon from "../../icons/AddIcon";
|
||||
import RemoveIcon from "../../icons/RemoveIcon";
|
||||
|
||||
function MapTiles({ maps, selectedMap, onMapSelect, onMapAdd, onMapRemove }) {
|
||||
const tileProps = {
|
||||
m: 2,
|
||||
bg: "muted",
|
||||
};
|
||||
|
||||
const tileStyle = {
|
||||
width: "150px",
|
||||
height: "150px",
|
||||
borderRadius: "4px",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
cursor: "pointer",
|
||||
};
|
||||
|
||||
function tile(map) {
|
||||
return (
|
||||
<Flex
|
||||
key={map.id}
|
||||
sx={{
|
||||
borderColor: "primary",
|
||||
borderStyle: map.id === selectedMap ? "solid" : "none",
|
||||
borderWidth: "4px",
|
||||
position: "relative",
|
||||
...tileStyle,
|
||||
}}
|
||||
{...tileProps}
|
||||
onClick={() => onMapSelect(map)}
|
||||
>
|
||||
<UIImage
|
||||
sx={{ width: "100%", height: "100%", objectFit: "contain" }}
|
||||
src={map.source}
|
||||
/>
|
||||
{!map.default && map.id === selectedMap && (
|
||||
<IconButton
|
||||
aria-label="Remove Map"
|
||||
title="Remove map"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
onMapRemove(map.id);
|
||||
}}
|
||||
sx={{ position: "absolute", top: 0, right: 0 }}
|
||||
>
|
||||
<RemoveIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
import MapTile from "./MapTile";
|
||||
|
||||
function MapTiles({
|
||||
maps,
|
||||
selectedMap,
|
||||
onMapSelect,
|
||||
onMapAdd,
|
||||
onMapRemove,
|
||||
onMapReset,
|
||||
}) {
|
||||
return (
|
||||
<Flex
|
||||
my={2}
|
||||
@ -81,15 +39,30 @@ function MapTiles({ maps, selectedMap, onMapSelect, onMapAdd, onMapRemove }) {
|
||||
":active": {
|
||||
color: "secondary",
|
||||
},
|
||||
...tileStyle,
|
||||
width: "150px",
|
||||
height: "150px",
|
||||
borderRadius: "4px",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
{...tileProps}
|
||||
m={2}
|
||||
bg="muted"
|
||||
aria-label="Add Map"
|
||||
title="Add Map"
|
||||
>
|
||||
<AddIcon large />
|
||||
</Flex>
|
||||
{maps.map(tile)}
|
||||
{maps.map((map) => (
|
||||
<MapTile
|
||||
key={map.id}
|
||||
map={map}
|
||||
isSelected={map.id === selectedMap}
|
||||
onMapSelect={onMapSelect}
|
||||
onMapRemove={onMapRemove}
|
||||
onMapReset={onMapReset}
|
||||
/>
|
||||
))}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
18
src/icons/ExpandMoreDotIcon.js
Normal file
18
src/icons/ExpandMoreDotIcon.js
Normal file
@ -0,0 +1,18 @@
|
||||
import React from "react";
|
||||
|
||||
function ExpandMoreDotIcon() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
fill="currentcolor"
|
||||
>
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default ExpandMoreDotIcon;
|
@ -1,18 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
function RemoveIcon() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
fill="currentcolor"
|
||||
>
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path d="M18.3 5.71c-.39-.39-1.02-.39-1.41 0L12 10.59 7.11 5.7c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41L10.59 12 5.7 16.89c-.39.39-.39 1.02 0 1.41.39.39 1.02.39 1.41 0L12 13.41l4.89 4.89c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L13.41 12l4.89-4.89c.38-.38.38-1.02 0-1.4z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default RemoveIcon;
|
18
src/icons/RemoveMapIcon.js
Normal file
18
src/icons/RemoveMapIcon.js
Normal file
@ -0,0 +1,18 @@
|
||||
import React from "react";
|
||||
|
||||
function RemoveMapIcon() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
fill="currentcolor"
|
||||
>
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V9c0-1.1-.9-2-2-2H8c-1.1 0-2 .9-2 2v10zM18 4h-2.5l-.71-.71c-.18-.18-.44-.29-.7-.29H9.91c-.26 0-.52.11-.7.29L8.5 4H6c-.55 0-1 .45-1 1s.45 1 1 1h12c.55 0 1-.45 1-1s-.45-1-1-1z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default RemoveMapIcon;
|
18
src/icons/ResetMapIcon.js
Normal file
18
src/icons/ResetMapIcon.js
Normal file
@ -0,0 +1,18 @@
|
||||
import React from "react";
|
||||
|
||||
function ResetMapIcon() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
fill="currentcolor"
|
||||
>
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path d="M17.65 6.35c-1.63-1.63-3.94-2.57-6.48-2.31-3.67.37-6.69 3.35-7.1 7.02C3.52 15.91 7.27 20 12 20c3.19 0 5.93-1.87 7.21-4.56.32-.67-.16-1.44-.9-1.44-.37 0-.72.2-.88.53-1.13 2.43-3.84 3.97-6.8 3.31-2.22-.49-4.01-2.3-4.48-4.52C5.31 9.44 8.26 6 12 6c1.66 0 3.14.69 4.22 1.78l-1.51 1.51c-.63.63-.19 1.71.7 1.71H19c.55 0 1-.45 1-1V6.41c0-.89-1.08-1.34-1.71-.71l-.64.65z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default ResetMapIcon;
|
@ -16,35 +16,35 @@ const defaultProps = {
|
||||
export const blank = {
|
||||
...defaultProps,
|
||||
source: blankImage,
|
||||
id: "Blank Grid 22x22",
|
||||
id: "__default_blank",
|
||||
};
|
||||
|
||||
export const grass = {
|
||||
...defaultProps,
|
||||
source: grassImage,
|
||||
id: "Grass Grid 22x22",
|
||||
id: "__default_grass",
|
||||
};
|
||||
|
||||
export const sand = {
|
||||
...defaultProps,
|
||||
source: sandImage,
|
||||
id: "Sand Grid 22x22",
|
||||
id: "__default_sand",
|
||||
};
|
||||
|
||||
export const stone = {
|
||||
...defaultProps,
|
||||
source: stoneImage,
|
||||
id: "Stone Grid 22x22",
|
||||
id: "__default_stone",
|
||||
};
|
||||
|
||||
export const water = {
|
||||
...defaultProps,
|
||||
source: waterImage,
|
||||
id: "Water Grid 22x22",
|
||||
id: "__default_water",
|
||||
};
|
||||
|
||||
export const wood = {
|
||||
...defaultProps,
|
||||
source: woodImage,
|
||||
id: "Wood Grid 22x22",
|
||||
id: "__default_wood",
|
||||
};
|
||||
|
@ -23,7 +23,7 @@ function SelectMapModal({ isOpen, onRequestClose, onDone }) {
|
||||
|
||||
const [currentMap, setCurrentMap] = useState(null);
|
||||
const [maps, setMaps] = useState(Object.values(defaultMaps));
|
||||
// Load maps from the database
|
||||
// Load maps from the database and ensure state is properly setup
|
||||
useEffect(() => {
|
||||
async function loadMaps() {
|
||||
let storedMaps = await db.table("maps").toArray();
|
||||
@ -35,7 +35,20 @@ function SelectMapModal({ isOpen, onRequestClose, onDone }) {
|
||||
}
|
||||
setMaps((prevMaps) => [...storedMaps, ...prevMaps]);
|
||||
}
|
||||
|
||||
async function setupDefaultMapStatesIfNeeded() {
|
||||
for (let defaultMap of Object.values(defaultMaps)) {
|
||||
let state = await db.table("states").get(defaultMap.id);
|
||||
if (!state) {
|
||||
await db
|
||||
.table("states")
|
||||
.add({ ...defaultMapState, mapId: defaultMap.id });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadMaps();
|
||||
setupDefaultMapStatesIfNeeded();
|
||||
}, []);
|
||||
|
||||
const [gridX, setGridX] = useState(defaultMapSize);
|
||||
@ -111,6 +124,10 @@ function SelectMapModal({ isOpen, onRequestClose, onDone }) {
|
||||
setGridY(map.gridY);
|
||||
}
|
||||
|
||||
async function handleMapReset(id) {
|
||||
await db.table("states").put({ ...defaultMapState, mapId: id });
|
||||
}
|
||||
|
||||
async function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
if (currentMap) {
|
||||
@ -199,6 +216,7 @@ function SelectMapModal({ isOpen, onRequestClose, onDone }) {
|
||||
onMapRemove={handleMapRemove}
|
||||
selectedMap={currentMap && currentMap.id}
|
||||
onMapSelect={handleMapSelect}
|
||||
onMapReset={handleMapReset}
|
||||
/>
|
||||
<Flex>
|
||||
<Box mb={2} mr={1} sx={{ flexGrow: 1 }}>
|
||||
|
Loading…
Reference in New Issue
Block a user