diff --git a/components/BoardCanvas/Tokens/index.tsx b/components/BoardCanvas/Tokens/index.tsx
new file mode 100644
index 0000000..0ada566
--- /dev/null
+++ b/components/BoardCanvas/Tokens/index.tsx
@@ -0,0 +1,28 @@
+const Image = ({ imageUrl }: { imageUrl: string }) =>
+ ``;
+
+const Circle = ({
+ color,
+ size,
+}: {
+ color?: string;
+ size?: number;
+}) => ``;
+
+export const TokenIcon = {
+ Image,
+ Circle,
+};
+
+/**
+ * WIP
+ *
+ * - cannot have more than 2 props with defaults. If one is not empty, then the default on the other will break
+ * - need a cleaner version for adding svgs with this function wrapper. Too much repetition
+ * - how can I create an enum for this
+ *
+ * */
diff --git a/components/BoardCanvas/defaultTokenImages.ts b/components/BoardCanvas/defaultTokenImages.ts
new file mode 100644
index 0000000..d84961e
--- /dev/null
+++ b/components/BoardCanvas/defaultTokenImages.ts
@@ -0,0 +1,21 @@
+/**
+ * Default Tokens for the board!
+ *
+ * To add to this list, add the file to /public/tokens/...
+ *
+ * Then add the filename to IMAGES
+ * */
+
+const IMAGES = [
+ "Alien.svg",
+ "BrickWall.svg",
+ "CloudFog.svg",
+ "Fire.svg",
+ "Flag.svg",
+ "HandShield.svg",
+ "ShieldHalf.svg",
+ "Totem.svg",
+ "Trap.svg",
+];
+
+export const DEFAULT_TOKEN_IMAGES = IMAGES.map((img) => `/tokens/${img}`);
diff --git a/components/BoardCanvas/index.tsx b/components/BoardCanvas/index.tsx
index a123068..5c5c380 100644
--- a/components/BoardCanvas/index.tsx
+++ b/components/BoardCanvas/index.tsx
@@ -53,7 +53,14 @@ export const BoardCanvas: React.FC = ({
backgroundColor: "ghostwhite",
}}
>
-
+
);
diff --git a/components/BoardCanvas/useCanvas.ts b/components/BoardCanvas/useCanvas.tsx
similarity index 78%
rename from components/BoardCanvas/useCanvas.ts
rename to components/BoardCanvas/useCanvas.tsx
index 4edc065..40ae561 100644
--- a/components/BoardCanvas/useCanvas.ts
+++ b/components/BoardCanvas/useCanvas.tsx
@@ -1,6 +1,7 @@
import * as d3 from "d3";
import { MutableRefObject, RefObject, useEffect } from "react";
import { PositionType } from "../Positions/position.type";
+import { TokenIcon } from "./Tokens";
type CanvasProps = {
canvasRef: RefObject;
@@ -44,14 +45,16 @@ export const useCanvas = ({
}
const g = d3.select(gRef.current);
- g.selectAll("circle")
+ g.selectAll("g")
.data(data)
- .join("circle")
- .attr("cx", ({ x }) => x)
- .attr("cy", ({ y }) => y)
- .attr("r", ({ r }) => (r ? r : 15))
- .attr("fill", ({ color }) => color ?? "black")
- // TODO: replace this to limit which token the user can control
+ .join((enter) => enter.append("g"))
+ .attr("transform", (d) => `translate(${d.x}, ${d.y})`)
+ .html((props) =>
+ props?.imageUrl
+ ? TokenIcon.Image({ imageUrl: props.imageUrl })
+ : TokenIcon.Circle({ color: props.color, size: props.r }),
+ )
+ // NOTE: Bellow attr & filter shows and limits user to moving their own tokens
.attr("opacity", ({ id }) => (id.includes(self as string) ? 1 : 0.75))
.filter(({ id }) => {
const isSidekick = id.includes("_");
@@ -60,7 +63,7 @@ export const useCanvas = ({
})
.call(
d3
- .drag()
+ .drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
@@ -82,7 +85,13 @@ export const useCanvas = ({
}),
);
- function dragstarted(e: { target: any }) {
+ function dragstarted(e: any, d: PositionType) {
+ const isSelf = d?.id === self;
+ const isSidekick = d?.id.includes("_");
+ const isSidekickSelf = d?.id.split("_")[0] === self;
+ if (isSidekick && !isSidekickSelf) return;
+ if (!isSelf) return;
+
d3.select(e.target).raise();
g.attr("cursor", "grabbing");
//@ts-expect-error: implicit any
@@ -98,11 +107,6 @@ export const useCanvas = ({
event: DragEvent & { subject: PositionType },
d: PositionType,
) {
- //@ts-expect-error: implicit any
- d3.select(this)
- .attr("cx", (d.x = event.x))
- .attr("cy", (d.y = event.y));
-
if (!move) return;
move({
diff --git a/components/Game/game.modal-template.tsx b/components/Game/game.modal-template.tsx
index 9761816..b68c10b 100644
--- a/components/Game/game.modal-template.tsx
+++ b/components/Game/game.modal-template.tsx
@@ -121,7 +121,6 @@ export const ModalContainer: React.FC = ({
}),
);
- console.log({ modalType, isOpen });
return (
<>
!isCommit && onClose()}>
diff --git a/components/Positions/position.modal.tsx b/components/Positions/position.modal.tsx
index 7320b6a..974097d 100644
--- a/components/Positions/position.modal.tsx
+++ b/components/Positions/position.modal.tsx
@@ -10,15 +10,30 @@ import {
Flex,
ModalFooter,
Button,
+ VStack,
+ Divider,
+ Grid,
+ Input,
+ Menu,
+ MenuButton,
+ MenuList,
+ MenuItem,
+ Image,
+ FormLabel,
+ HStack,
} from "@chakra-ui/react";
-import { FC, useCallback, useState } from "react";
+import { Dispatch, FC, SetStateAction, useCallback, useState } from "react";
import { MoonIcon, PlusSquareIcon, SunIcon } from "@chakra-ui/icons";
import { useWebGame } from "@/lib/contexts/WebGameProvider";
+import { MdUpload as IconUpload } from "react-icons/md";
+
//@ts-ignore
import { CirclePicker } from "react-color";
-import { PositionType, Size } from "./position.type";
+import { PositionType } from "./position.type";
import { useRouter } from "next/router";
+import { toast } from "react-hot-toast";
+import { DEFAULT_TOKEN_IMAGES } from "../BoardCanvas/defaultTokenImages";
export const PositionModal: FC<{
isOpen: boolean;
@@ -37,15 +52,15 @@ export const PositionModal: FC<{
name as keyof typeof gamePositions.content
] as PositionType;
+ const [images, setImages] = useState<{ id: string; url: string }[]>([]);
+
const [selectedColor, setSelectedColor] = useState(
selectedPosition?.color ?? "#000",
);
- const [selectedSize, setSelectedSize] = useState("lg");
const [sidekicks, setSidekicks] = useState<
PositionType["sidekicks"] | undefined
>(selectedPosition?.sidekicks);
- const setSize = (size: Size) =>
- size === "lg" ? 2 : size === "md" ? 1.65 : 1.35;
+
const handleColorChange = ({ hex }: { hex: string }) => setSelectedColor(hex);
const _setGamePosition = (props: PositionType) => {
@@ -60,10 +75,18 @@ export const PositionModal: FC<{
setGamePosition({
...selected,
color: selectedColor,
- r: selectedSize === "lg" ? 20 : selectedSize === "md" ? 15 : 10,
+ imageUrl:
+ images?.find((img) => img.id === selected?.id)?.url ??
+ selected?.imageUrl ??
+ undefined,
sidekicks: sidekicks?.map((kick) => ({
...kick,
color: selectedColor,
+ r: kick?.r ?? 50,
+ imageUrl:
+ images?.find((img) => img.id === kick?.id)?.url ??
+ kick?.imageUrl ??
+ undefined,
})),
});
}
@@ -82,10 +105,21 @@ export const PositionModal: FC<{
},
];
});
+
+ toast.success("Preparing new token. Click apply to confirm changes");
}
+ const gameKickIds = selectedPosition?.sidekicks?.map((kick) => kick.id);
+
return (
-
+ {
+ setSidekicks(selectedPosition?.sidekicks);
+ setImages([]);
+ onClose();
+ }}
+ >
@@ -98,46 +132,117 @@ export const PositionModal: FC<{
-
-
- setSelectedSize((prev) =>
- prev === "lg" ? "md" : prev === "md" ? "sm" : "lg",
- )
- }
- />
- {sidekicks?.map((kick) => (
-
+
+
+
- ))}
+ {selectedPosition?.sidekicks?.map((kick) => (
+
+ ))}
+
+ {sidekicks
+ ?.filter((kick) => !gameKickIds?.includes(kick.id))
+ .map((kick) => (
+
+ ))}
+
+
+
-
-
-
-
-
);
};
+
+const TokenPreview = ({
+ token,
+ selectedColor,
+ setImages,
+}: {
+ token: PositionType;
+ selectedColor: string;
+ setImages: Dispatch>;
+}) => {
+ const setImage = (url: string) => {
+ setImages((prev) => {
+ return [...prev?.filter((p) => p.id !== token.id), { id: token.id, url }];
+ });
+ toast.success("New Image Prepared. Click Apply to confirm changes");
+ };
+
+ const [imageUrl, setImageUrl] = useState("");
+
+ return (
+
+ {token?.imageUrl ? (
+
+ ) : (
+
+ )}
+
+
+
+ );
+};
diff --git a/components/Positions/position.type.ts b/components/Positions/position.type.ts
index 3abe296..46e02df 100644
--- a/components/Positions/position.type.ts
+++ b/components/Positions/position.type.ts
@@ -7,6 +7,7 @@ export type PositionType = {
tokenSize?: Size;
color?: string;
sidekicks?: Sidekick[];
+ imageUrl?: string;
};
type Sidekick = Omit;
diff --git a/public/tokens/Alien.svg b/public/tokens/Alien.svg
new file mode 100644
index 0000000..96bfd17
--- /dev/null
+++ b/public/tokens/Alien.svg
@@ -0,0 +1,5 @@
+
diff --git a/public/tokens/BrickWall.svg b/public/tokens/BrickWall.svg
new file mode 100644
index 0000000..2955fe7
--- /dev/null
+++ b/public/tokens/BrickWall.svg
@@ -0,0 +1,5 @@
+
diff --git a/public/tokens/CloudFog.svg b/public/tokens/CloudFog.svg
new file mode 100644
index 0000000..f285ca3
--- /dev/null
+++ b/public/tokens/CloudFog.svg
@@ -0,0 +1,5 @@
+
diff --git a/public/tokens/Fire.svg b/public/tokens/Fire.svg
new file mode 100644
index 0000000..e20cf65
--- /dev/null
+++ b/public/tokens/Fire.svg
@@ -0,0 +1,5 @@
+
diff --git a/public/tokens/Flag.svg b/public/tokens/Flag.svg
new file mode 100644
index 0000000..12b1710
--- /dev/null
+++ b/public/tokens/Flag.svg
@@ -0,0 +1,5 @@
+
diff --git a/public/tokens/HandShield.svg b/public/tokens/HandShield.svg
new file mode 100644
index 0000000..4775a39
--- /dev/null
+++ b/public/tokens/HandShield.svg
@@ -0,0 +1,5 @@
+
diff --git a/public/tokens/ShieldHalf.svg b/public/tokens/ShieldHalf.svg
new file mode 100644
index 0000000..3061fd3
--- /dev/null
+++ b/public/tokens/ShieldHalf.svg
@@ -0,0 +1,5 @@
+
diff --git a/public/tokens/Totem.svg b/public/tokens/Totem.svg
new file mode 100644
index 0000000..0e0c879
--- /dev/null
+++ b/public/tokens/Totem.svg
@@ -0,0 +1,5 @@
+
diff --git a/public/tokens/Trap.svg b/public/tokens/Trap.svg
new file mode 100644
index 0000000..5d8c5f5
--- /dev/null
+++ b/public/tokens/Trap.svg
@@ -0,0 +1,5 @@
+