diff --git a/package.json b/package.json
index 27b9b63..dd0a35d 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/src/components/map/AddMapButton.js b/src/components/map/AddMapButton.js
index 99dd26b..ac95b20 100644
--- a/src/components/map/AddMapButton.js
+++ b/src/components/map/AddMapButton.js
@@ -14,7 +14,9 @@ function AddMapButton({ onMapChange }) {
}
function handleDone(map) {
- onMapChange(map);
+ if (map) {
+ onMapChange(map);
+ }
closeModal();
}
diff --git a/src/components/map/MapSelect.js b/src/components/map/MapSelect.js
index 40b3e42..817fea8 100644
--- a/src/components/map/MapSelect.js
+++ b/src/components/map/MapSelect.js
@@ -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 (
- onMapSelected(index)}
+ onClick={() => onMapSelect(map)}
>
+ {!map.default && map.id === selectedMap && (
+ {
+ e.preventDefault();
+ e.stopPropagation();
+ onMapRemove(map.id);
+ }}
+ sx={{ position: "absolute", top: 0, right: 0 }}
+ >
+
+
+ )}
);
}
@@ -54,7 +69,6 @@ function MapSelect({ maps, selectedMap, onMapSelected, onMapAdd }) {
flexGrow: 1,
}}
>
- {maps.map((map, index) => tile(map, index))}
+ {maps.map(tile)}
);
}
diff --git a/src/database.js b/src/database.js
new file mode 100644
index 0000000..cf9c91f
--- /dev/null
+++ b/src/database.js
@@ -0,0 +1,6 @@
+import Dexie from "dexie";
+
+const db = new Dexie("OwlbearRodeoDB");
+db.version(1).stores({ maps: "id" });
+
+export default db;
diff --git a/src/icons/RemoveIcon.js b/src/icons/RemoveIcon.js
new file mode 100644
index 0000000..d09fbc8
--- /dev/null
+++ b/src/icons/RemoveIcon.js
@@ -0,0 +1,18 @@
+import React from "react";
+
+function RemoveIcon() {
+ return (
+
+ );
+}
+
+export default RemoveIcon;
diff --git a/src/maps/index.js b/src/maps/index.js
index 5deb7b4..4a9524d 100644
--- a/src/maps/index.js
+++ b/src/maps/index.js
@@ -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",
};
diff --git a/src/modals/AddMapModal.js b/src/modals/AddMapModal.js
index 07f742f..1e68ecb 100644
--- a/src/modals/AddMapModal.js
+++ b/src/modals/AddMapModal.js
@@ -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 (
- {
- e.preventDefault();
- onDone(maps[currentMap]);
- }}
- onDragEnter={handleImageDragEnter}
- >
+
handleImageUpload(event.target.files[0])}
type="file"
@@ -142,8 +182,9 @@ function AddMapModal({ isOpen, onRequestClose, onDone }) {
@@ -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}
/>
@@ -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}
/>
diff --git a/src/theme.js b/src/theme.js
index 5823fb6..6e28eea 100644
--- a/src/theme.js
+++ b/src/theme.js
@@ -180,6 +180,8 @@ export default {
},
"&:disabled": {
backgroundColor: "muted",
+ color: "gray",
+ borderColor: "text",
},
},
},
diff --git a/yarn.lock b/yarn.lock
index 650a864..2d7160e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -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"