Added an indexedb database to store uploaded maps into
This commit is contained in:
parent
8681864ddc
commit
071cd3ea7f
|
@ -9,6 +9,7 @@
|
|||
"@testing-library/react": "^9.3.2",
|
||||
"@testing-library/user-event": "^7.1.2",
|
||||
"blob-to-buffer": "^1.2.8",
|
||||
"dexie": "^2.0.4",
|
||||
"gh-pages": "^2.2.0",
|
||||
"interactjs": "^1.9.7",
|
||||
"js-binarypack": "^0.0.9",
|
||||
|
|
|
@ -14,7 +14,9 @@ function AddMapButton({ onMapChange }) {
|
|||
}
|
||||
|
||||
function handleDone(map) {
|
||||
onMapChange(map);
|
||||
if (map) {
|
||||
onMapChange(map);
|
||||
}
|
||||
closeModal();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import React from "react";
|
||||
import { Flex, Image as UIImage } from "theme-ui";
|
||||
import { Flex, Image as UIImage, IconButton } from "theme-ui";
|
||||
|
||||
import AddIcon from "../../icons/AddIcon";
|
||||
import RemoveIcon from "../../icons/RemoveIcon";
|
||||
|
||||
function MapSelect({ maps, selectedMap, onMapSelected, onMapAdd }) {
|
||||
function MapSelect({ maps, selectedMap, onMapSelect, onMapAdd, onMapRemove }) {
|
||||
const tileProps = {
|
||||
m: 2,
|
||||
bg: "muted",
|
||||
|
@ -18,24 +19,38 @@ function MapSelect({ maps, selectedMap, onMapSelected, onMapAdd }) {
|
|||
cursor: "pointer",
|
||||
};
|
||||
|
||||
// TODO move from passing index in to using DB ID
|
||||
function tile(map, index) {
|
||||
function tile(map) {
|
||||
return (
|
||||
<Flex // TODO: use DB key
|
||||
key={map.source}
|
||||
<Flex
|
||||
key={map.id}
|
||||
sx={{
|
||||
borderColor: "primary",
|
||||
borderStyle: index === selectedMap ? "solid" : "none",
|
||||
borderStyle: map.id === selectedMap ? "solid" : "none",
|
||||
borderWidth: "4px",
|
||||
position: "relative",
|
||||
...tileStyle,
|
||||
}}
|
||||
{...tileProps}
|
||||
onClick={() => onMapSelected(index)}
|
||||
onClick={() => onMapSelect(map)}
|
||||
>
|
||||
<UIImage
|
||||
sx={{ width: "100%", objectFit: "contain" }}
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
@ -54,7 +69,6 @@ function MapSelect({ maps, selectedMap, onMapSelected, onMapAdd }) {
|
|||
flexGrow: 1,
|
||||
}}
|
||||
>
|
||||
{maps.map((map, index) => tile(map, index))}
|
||||
<Flex
|
||||
onClick={onMapAdd}
|
||||
sx={{
|
||||
|
@ -70,9 +84,12 @@ function MapSelect({ maps, selectedMap, onMapSelected, onMapAdd }) {
|
|||
...tileStyle,
|
||||
}}
|
||||
{...tileProps}
|
||||
aria-label="Add Map"
|
||||
title="Add Map"
|
||||
>
|
||||
<AddIcon />
|
||||
</Flex>
|
||||
{maps.map(tile)}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
|
6
src/database.js
Normal file
6
src/database.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
import Dexie from "dexie";
|
||||
|
||||
const db = new Dexie("OwlbearRodeoDB");
|
||||
db.version(1).stores({ maps: "id" });
|
||||
|
||||
export default db;
|
18
src/icons/RemoveIcon.js
Normal file
18
src/icons/RemoveIcon.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
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;
|
|
@ -10,40 +10,41 @@ const defaultProps = {
|
|||
gridY: 22,
|
||||
width: 1024,
|
||||
height: 1024,
|
||||
default: true,
|
||||
};
|
||||
|
||||
export const blank = {
|
||||
...defaultProps,
|
||||
source: blankImage,
|
||||
name: "Blank Grid 22x22",
|
||||
id: "Blank Grid 22x22",
|
||||
};
|
||||
|
||||
export const grass = {
|
||||
...defaultProps,
|
||||
source: grassImage,
|
||||
name: "Grass Grid 22x22",
|
||||
id: "Grass Grid 22x22",
|
||||
};
|
||||
|
||||
export const sand = {
|
||||
...defaultProps,
|
||||
source: sandImage,
|
||||
name: "Sand Grid 22x22",
|
||||
id: "Sand Grid 22x22",
|
||||
};
|
||||
|
||||
export const stone = {
|
||||
...defaultProps,
|
||||
source: stoneImage,
|
||||
name: "Stone Grid 22x22",
|
||||
id: "Stone Grid 22x22",
|
||||
};
|
||||
|
||||
export const water = {
|
||||
...defaultProps,
|
||||
source: waterImage,
|
||||
name: "Water Grid 22x22",
|
||||
id: "Water Grid 22x22",
|
||||
};
|
||||
|
||||
export const wood = {
|
||||
...defaultProps,
|
||||
source: woodImage,
|
||||
name: "Wood Grid 22x22",
|
||||
id: "Wood Grid 22x22",
|
||||
};
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import React, { useRef, useState, useEffect } from "react";
|
||||
import { Box, Button, Flex, Label, Input, Text } from "theme-ui";
|
||||
import shortid from "shortid";
|
||||
|
||||
import db from "../database";
|
||||
|
||||
import Modal from "../components/Modal";
|
||||
import MapSelect from "../components/map/MapSelect";
|
||||
|
@ -11,23 +14,25 @@ const defaultMapSize = 22;
|
|||
function AddMapModal({ isOpen, onRequestClose, onDone }) {
|
||||
const [imageLoading, setImageLoading] = useState(false);
|
||||
|
||||
const [currentMap, setCurrentMap] = useState(-1);
|
||||
const [currentMapId, setCurrentMapId] = useState(null);
|
||||
const [maps, setMaps] = useState(Object.values(defaultMaps));
|
||||
// Load maps from the database
|
||||
useEffect(() => {
|
||||
async function loadMaps() {
|
||||
let storedMaps = await db.table("maps").toArray();
|
||||
// reverse so maps are show in the order they were added
|
||||
storedMaps.reverse();
|
||||
for (let map of storedMaps) {
|
||||
// Recreate image urls for each map
|
||||
map.source = URL.createObjectURL(map.file);
|
||||
}
|
||||
setMaps((prevMaps) => [...storedMaps, ...prevMaps]);
|
||||
}
|
||||
loadMaps();
|
||||
}, []);
|
||||
|
||||
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]);
|
||||
|
||||
const fileInputRef = useRef();
|
||||
|
||||
function handleImageUpload(file) {
|
||||
|
@ -54,23 +59,15 @@ function AddMapModal({ isOpen, onRequestClose, onDone }) {
|
|||
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;
|
||||
handleMapAdd({
|
||||
file,
|
||||
gridX: fileGridX,
|
||||
gridY: fileGridY,
|
||||
width: image.width,
|
||||
height: image.height,
|
||||
source: url,
|
||||
id: shortid.generate(),
|
||||
});
|
||||
setGridX(fileGridX);
|
||||
setGridY(fileGridY);
|
||||
setImageLoading(false);
|
||||
};
|
||||
image.src = url;
|
||||
|
@ -82,10 +79,60 @@ function AddMapModal({ isOpen, onRequestClose, onDone }) {
|
|||
}
|
||||
}
|
||||
|
||||
function handleMapSelect(mapId) {
|
||||
setCurrentMap(mapId);
|
||||
setGridX(maps[mapId].gridX);
|
||||
setGridY(maps[mapId].gridY);
|
||||
async function handleMapAdd(map) {
|
||||
await db.table("maps").add(map);
|
||||
setMaps((prevMaps) => [map, ...prevMaps]);
|
||||
setCurrentMapId(map.id);
|
||||
setGridX(map.gridX);
|
||||
setGridY(map.gridY);
|
||||
}
|
||||
|
||||
async function handleMapRemove(id) {
|
||||
await db.table("maps").delete(id);
|
||||
setMaps((prevMaps) => {
|
||||
const filtered = prevMaps.filter((map) => map.id !== id);
|
||||
setCurrentMapId(filtered[0].id);
|
||||
return filtered;
|
||||
});
|
||||
}
|
||||
|
||||
function handleMapSelect(map) {
|
||||
setCurrentMapId(map.id);
|
||||
setGridX(map.gridX);
|
||||
setGridY(map.gridY);
|
||||
}
|
||||
|
||||
function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
onDone(maps.find((map) => map.id === currentMapId));
|
||||
}
|
||||
|
||||
async function handleGridXChange(e) {
|
||||
const newX = e.target.value;
|
||||
await db.table("maps").update(currentMapId, { gridX: newX });
|
||||
setGridX(newX);
|
||||
setMaps((prevMaps) => {
|
||||
const newMaps = [...prevMaps];
|
||||
const i = newMaps.findIndex((map) => map.id === currentMapId);
|
||||
if (i > -1) {
|
||||
newMaps[i].gridX = newX;
|
||||
}
|
||||
return newMaps;
|
||||
});
|
||||
}
|
||||
|
||||
async function handleGridYChange(e) {
|
||||
const newY = e.target.value;
|
||||
await db.table("maps").update(currentMapId, { gridY: newY });
|
||||
setGridY(newY);
|
||||
setMaps((prevMaps) => {
|
||||
const newMaps = [...prevMaps];
|
||||
const i = newMaps.findIndex((map) => map.id === currentMapId);
|
||||
if (i > -1) {
|
||||
newMaps[i].gridY = newY;
|
||||
}
|
||||
return newMaps;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -116,14 +163,7 @@ function AddMapModal({ isOpen, onRequestClose, onDone }) {
|
|||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onRequestClose={onRequestClose}>
|
||||
<Box
|
||||
as="form"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
onDone(maps[currentMap]);
|
||||
}}
|
||||
onDragEnter={handleImageDragEnter}
|
||||
>
|
||||
<Box as="form" onSubmit={handleSubmit} onDragEnter={handleImageDragEnter}>
|
||||
<input
|
||||
onChange={(event) => handleImageUpload(event.target.files[0])}
|
||||
type="file"
|
||||
|
@ -142,8 +182,9 @@ function AddMapModal({ isOpen, onRequestClose, onDone }) {
|
|||
<MapSelect
|
||||
maps={maps}
|
||||
onMapAdd={openImageDialog}
|
||||
selectedMap={currentMap}
|
||||
onMapSelected={handleMapSelect}
|
||||
onMapRemove={handleMapRemove}
|
||||
selectedMap={currentMapId}
|
||||
onMapSelect={handleMapSelect}
|
||||
/>
|
||||
<Flex>
|
||||
<Box mb={2} mr={1} sx={{ flexGrow: 1 }}>
|
||||
|
@ -152,7 +193,9 @@ function AddMapModal({ isOpen, onRequestClose, onDone }) {
|
|||
type="number"
|
||||
name="gridX"
|
||||
value={gridX}
|
||||
onChange={(e) => setGridX(e.target.value)}
|
||||
onChange={handleGridXChange}
|
||||
disabled={currentMapId === null}
|
||||
min={1}
|
||||
/>
|
||||
</Box>
|
||||
<Box mb={2} ml={1} sx={{ flexGrow: 1 }}>
|
||||
|
@ -161,7 +204,9 @@ function AddMapModal({ isOpen, onRequestClose, onDone }) {
|
|||
type="number"
|
||||
name="gridY"
|
||||
value={gridY}
|
||||
onChange={(e) => setGridY(e.target.value)}
|
||||
onChange={handleGridYChange}
|
||||
disabled={currentMapId === null}
|
||||
min={1}
|
||||
/>
|
||||
</Box>
|
||||
</Flex>
|
||||
|
|
|
@ -180,6 +180,8 @@ export default {
|
|||
},
|
||||
"&:disabled": {
|
||||
backgroundColor: "muted",
|
||||
color: "gray",
|
||||
borderColor: "text",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -4005,6 +4005,11 @@ detect-port-alt@1.1.6:
|
|||
address "^1.0.1"
|
||||
debug "^2.6.0"
|
||||
|
||||
dexie@^2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/dexie/-/dexie-2.0.4.tgz#6027a5e05879424e8f9979d8c14e7420f27e3a11"
|
||||
integrity sha512-aQ/s1U2wHxwBKRrt2Z/mwFNHMQWhESerFsMYzE+5P5OsIe5o1kgpFMWkzKTtkvkyyEni6mWr/T4HUJuY9xIHLA==
|
||||
|
||||
diff-sequences@^24.9.0:
|
||||
version "24.9.0"
|
||||
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5"
|
||||
|
|
Loading…
Reference in New Issue
Block a user