bitburner-src/src/ui/React/Overview.tsx
2022-01-20 16:11:48 -05:00

144 lines
4.5 KiB
TypeScript

import React, { useState, useEffect, useRef } from "react";
import Draggable, { DraggableEventHandler } from "react-draggable";
import makeStyles from "@mui/styles/makeStyles";
import Collapse from "@mui/material/Collapse";
import Paper from "@mui/material/Paper";
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
import EqualizerIcon from "@mui/icons-material/Equalizer";
import SchoolIcon from "@mui/icons-material/School";
import { use } from "../Context";
import { Page } from "../Router";
import { Settings } from "../../Settings/Settings";
import { Box, Button, Typography } from "@mui/material";
import { debounce } from "lodash";
const useStyles = makeStyles({
overviewContainer: {
position: "fixed",
top: 0,
right: 0,
zIndex: 1500,
display: "flex",
justifyContent: "flex-end",
flexDirection: "column",
},
header: {
cursor: "grab",
textAlign: "center",
display: "flex",
flexDirection: "row",
alignItems: "center",
},
visibilityToggle: {
padding: "2px",
minWidth: "inherit",
backgroundColor: "transparent",
border: "none",
"&:hover": {
backgroundColor: "rgba(255, 255, 255, 0.1)",
},
},
collapse: {
borderTop: `1px solid ${Settings.theme.welllight}`,
margin: "0 auto",
},
icon: {
fontSize: "24px",
},
});
interface IProps {
children: JSX.Element[] | JSX.Element | React.ReactElement[] | React.ReactElement;
mode: "tutorial" | "overview";
}
export interface OverviewSettings {
opened: boolean;
x: number;
y: number;
}
export function Overview({ children, mode }: IProps): React.ReactElement {
const draggableRef = useRef<HTMLDivElement>(null);
const [open, setOpen] = useState(Settings.overview.opened);
const [x, setX] = useState(Settings.overview.x);
const [y, setY] = useState(Settings.overview.y);
const classes = useStyles();
const router = use.Router();
const CurrentIcon = open ? KeyboardArrowUpIcon : KeyboardArrowDownIcon;
const LeftIcon = mode === "tutorial" ? SchoolIcon : EqualizerIcon;
const header = mode === "tutorial" ? "Tutorial" : "Overview";
const handleStop: DraggableEventHandler = (e, data) => {
setX(data.x);
setY(data.y);
};
useEffect(() => {
Settings.overview = { x, y, opened: open };
}, [open, x, y]);
// Trigger fakeDrag once to make sure loaded data is not outside bounds
useEffect(() => fakeDrag(), []);
// And trigger fakeDrag when the window is resized
useEffect(() => {
window.addEventListener("resize", fakeDrag);
return () => {
window.removeEventListener("resize", fakeDrag);
};
}, []);
const fakeDrag = debounce((): void => {
const node = draggableRef?.current;
if (!node) return;
// No official way to trigger an onChange to recompute the bounds
// See: https://github.com/react-grid-layout/react-draggable/issues/363#issuecomment-947751127
triggerMouseEvent(node, "mouseover");
triggerMouseEvent(node, "mousedown");
triggerMouseEvent(document, "mousemove");
triggerMouseEvent(node, "mouseup");
triggerMouseEvent(node, "click");
}, 100);
const triggerMouseEvent = (node: HTMLDivElement | Document, eventType: string): void => {
const clickEvent = document.createEvent("MouseEvents");
clickEvent.initEvent(eventType, true, true);
node.dispatchEvent(clickEvent);
};
if (router.page() === Page.BitVerse || router.page() === Page.Loading || router.page() === Page.Recovery)
return <></>;
return (
<Draggable handle=".drag" bounds="body" onStop={handleStop} defaultPosition={{ x, y }}>
<Paper className={classes.overviewContainer} square>
<Box className="drag" onDoubleClick={() => setOpen((old) => !old)} ref={draggableRef}>
<Box className={classes.header}>
<LeftIcon color="secondary" className={classes.icon} sx={{ padding: "2px" }} />
<Typography flexGrow={1} color="secondary">
{header}
</Typography>
<Button
aria-label="expand or collapse character overview"
variant="text"
size="small"
className={classes.visibilityToggle}
>
{<CurrentIcon className={classes.icon} color="secondary" onClick={() => setOpen((old) => !old)} />}
</Button>
</Box>
</Box>
<Collapse in={open} className={classes.collapse}>
{children}
</Collapse>
</Paper>
</Draggable>
);
}