Files
tartarus-app/src/app/dev-tools/page.tsx

362 lines
9.3 KiB
TypeScript
Raw Normal View History

'use client'
import React, { SyntheticEvent } from "react";
import styles from "@/styles/dev-tools.module.css"
import { Container, Select, Stack, Chip, MenuItem, InputLabel, Snackbar, IconButton, SnackbarCloseReason, Typography } from "@mui/material"
import { RichTreeView } from '@mui/x-tree-view/RichTreeView';
import CloseIcon from '@mui/icons-material/Close';
import Draggable from 'react-draggable';
type EditorMode = "insert" | "update";
interface DBObject {
record_num: number
data_id: string;
change_timestamp: string;
}
interface Unit extends DBObject {
}
interface Card extends DBObject {
}
interface EditorContextType {
editorMode: "insert" | "update";
currentRecordNum: number | null;
currentDataId: string | null;
cards: Card[] | null;
units: Unit[] | null;
error: string | null,
changeEditorMode?: (m: string) => void;
changeCurrentRecordNum?: (n: number) => void;
changeCurrentDataId?: (n: string) => void;
changeError?: (e: string) => void;
}
const EditorContext = React.createContext<EditorContextType>({
currentRecordNum: null,
currentDataId: null,
editorMode: "insert",
cards: null,
units: null,
error: null,
})
function useEditor() {
const ctx = React.useContext(EditorContext);
if(!ctx){
throw new Error("Error: use Editor must be used within an EditorContext")
}
return ctx;
}
const unitTypes: string[] = ["minion", "enemy", "hero", "ally"];
const buildingTypes: string[] = ["enemy", "minion", "terrain", "support"];
export default function Page(){
const [currentRecordNum, setCurrentRecordNum] = React.useState<number>(0);
const [currentDataId, setCurrentDataId] = React.useState<string>("");
const [editorMode, setEditorMode] = React.useState<EditorMode>("insert");
const [cards, setCards] = React.useState<Card[] | null>(null);
const [units, setUnits] = React.useState<Unit[] | null>(null);
const [error, setError] = React.useState<string | null>(null);
const [open, setOpen] = React.useState<boolean>(false);
const changeCurrentRecordNum = (n: number) => setCurrentRecordNum(n)
const changeCurrentDataId = (id: string) => setCurrentDataId(id)
const changeEditorMode = (m: string) =>{
if(m !== "insert" && m !== "update") return
setEditorMode(m)
}
const changeError = (e: string) => setError(e);
// get all of the cards, or units
React.useEffect(() => {
const getCards = async () => {
try{
// fetch our db
}catch (error) {
}
}
const getUnits = async () => {
try{
}catch (error) {
}
}
getCards();
getUnits();
}, [])
const handleClose = (
event: React.SyntheticEvent | Event,
reason?: SnackbarCloseReason,
) => {
if (reason === 'clickaway') {
return;
}
setOpen(false);
};
const action = (
<React.Fragment>
<Typography>
{error}
</Typography>
<IconButton
size="small"
aria-label="close"
color="inherit"
onClick={handleClose}
>
<CloseIcon fontSize="small" />
</IconButton>
</React.Fragment>
);
return (
<EditorContext.Provider value={{
currentRecordNum,
currentDataId,
editorMode,
error,
cards,
units,
changeCurrentRecordNum,
changeCurrentDataId,
changeEditorMode,
changeError
}}>
<div className={styles.maincontainer}>
<div className={styles.leftmenucontainer}>
<LeftMenu />
</div>
<div className={styles.editorcontainer}>
<Editor />
</div>
</div>
{error && <Snackbar
open={open}
autoHideDuration={6000}
onClose={handleClose}
action={action}
/>}
</EditorContext.Provider>
)
}
function DynamicCard() {
// to add a new kind of data type "map", "string", "bool", etc. they add a prop for it.
return (
<>
</>
)
}
interface ThemeSelection {
value: string;
label: string;
}
const debugCard = [
{
id: "terrain_forest",
label: "Forest",
children: [
{
id: "terrain_haunted_forest",
label: "Haunted Forest",
children: [
{
id: 'terrain_undead_candy_forest',
label: "Undead Candy Forest"
}
]
}
]
},
{
id: "support_blacksmith",
label: "Forest",
children: [
{
id: "support_terrain_haunted_forest",
label: "Haunted Forest",
children: [
{
id: 'SUppror_terrain_undead_candy_forest',
label: "Undead Candy Forest"
}
]
}
]
},
{
id: "enemy_graveyard",
label: "Forest",
children: [
{
id: "enemy_terrain_haunted_forest",
label: "Haunted Forest",
children: [
{
id: 'enemy_terrain_undead_candy_forest',
label: "Undead Candy Forest"
}
]
}
]
}
]
function LeftMenu(){
const [themes, setThemes] = React.useState<ThemeSelection[]>([]); // get from the database
const [menuMode, setMenuMode] = React.useState<"unit" | "card">("card");
const { currentRecordNum, } = useEditor();
const handleSelectAsset = (e: React.MouseEvent<Element, MouseEvent>) => {
console.log("Got e:", e)
}
return (
<Container className={styles.leftmenu}>
<Stack >
<Typography id="assetTypeFilter">Asset Type</Typography>
<Select className="assetTypeFilter">
<MenuItem value="">All</MenuItem>
{menuMode === "card" ?
buildingTypes.map((opt, i) => (<MenuItem value={opt} key={i}>{opt[0].toUpperCase() + opt.slice(1)}</MenuItem>)) :
unitTypes.map((opt, i) => (<MenuItem value={opt} key={i}>{opt[0].toUpperCase() + opt.slice(1)}</MenuItem>))
}
</Select>
<InputLabel id="themeFilter">Theme</InputLabel>
<Select className="themeFilter" label="Theme" >
<MenuItem value="">All</MenuItem>
</Select>
</Stack>
<Typography >
Current {menuMode}s created
</Typography>
<RichTreeView items={debugCard} onItemClick={handleSelectAsset}>
</RichTreeView>
</Container>
)
}
function Editor(){
const { editorMode } = useEditor();
const canvasWrapperRef = React.useRef<HTMLDivElement | null>(null);
const canvasRef = React.useRef<HTMLCanvasElement | null>(null);
const onMouseUp = (e: React.MouseEvent) => {
console.log("event for mouse up", e)
}
const onMouseDown = (e: React.MouseEvent) => {
console.log("event for mouse down", e)
}
const onDragOver = (e: React.DragEvent) => {
e.preventDefault()
//console.log("event for drag over:", e)
}
const onDrop = (e: React.DragEvent) => {
e.preventDefault();
const raw = e.dataTransfer.getData("text/plain");
console.log("DROP canvas raw:", raw);
// create a box?
}
return (
<Container className={styles.editor} maxWidth={false} disableGutters>
{editorMode === "insert" ? <CreateNewAsset type="building"/> : null}
<div
ref={canvasWrapperRef}
onDragOver={(e: React.DragEvent) => {
e.preventDefault();
e.dataTransfer.dropEffect = "copy";
}}
onDrop={(e: React.DragEvent) => {
e.preventDefault();
const raw = e.dataTransfer.getData("text/plain");
console.log("DROP wrapper raw:", raw);
}}
style={{ height: "100%", width: "100%", position: "relative" }}
>
<canvas
ref={canvasRef}
onDragOver={onDragOver}
onDrop={onDrop}
style={{ height: "100%", width: "100%" }}
/>
</div>
</Container>
)
}
function CreateNewAsset({ type }: { type: string }) {
return (
<div style={{ backgroundColor: "#F1E9DB", borderRadius: 20, height: 40, display: "flex", alignItems: "center", justifyContent: "center", columnGap: 20 }}>
{type === "building"
? buildingTypes.map((buildingType, i) => {
const label = buildingType[0].toUpperCase() + buildingType.slice(1);
return (
<div
key={i}
draggable
onDragStart={(e) => {
console.log("DRAG START", buildingType);
const payload = { kind: "building", type: buildingType, label, w: 160, h: 90 };
e.dataTransfer.setData("text/plain", JSON.stringify(payload));
e.dataTransfer.effectAllowed = "copy";
}}
style={{ display: "inline-flex" }}
>
<Chip sx={{ backgroundColor: "#5DB7DE" }} label={label} />
</div>
);
})
: unitTypes.map((unitType, i) => {
const label = unitType[0].toUpperCase() + unitType.slice(1);
return (
<div
key={i}
draggable
onDragStart={(e) => {
const payload = { kind: "unit", type: unitType, label, w: 140, h: 80 };
e.dataTransfer.setData("text/plain", JSON.stringify(payload));
e.dataTransfer.effectAllowed = "copy";
}}
style={{ display: "inline-flex" }}
>
<Chip sx={{ backgroundColor: "#5DB7DE" }} label={label} />
</div>
);
})}
</div>
);
}