bitburner-src/src/Themes/ui/ThemeBrowser.tsx
Martin Fournier a26b9c8dcf Add theme browser page accessible from game options
Removed the themes buttons that were in the ThemeEditorModal and only
left a "Revert to Default" button along with a link to the ThemeBrowser
page.

Split off the buttons into reusable components since they are now used
in two pages.

Display the themes in big cards with a zoomable screenshot. Applying the
theme now shows a toast with an option to undo the action.

The snackbar now allows ReactNode instead of only strings.

- Add link with details on how to create a new theme in the game.
- Add link to the theme-sharing discord channel.
- Add icons to the theme & style buttons in GameOptions
- Add "Theme Editor" button to ThemeBrowser
- Add "Style Editor" button to ThemeBrowser
- Move Styles related files into Themes folder
- Includes a modal that shows a bigger version of the screenshot.
- Change Snackbar to allow for ReactNode as the message
2022-01-20 18:41:49 -05:00

94 lines
2.9 KiB
TypeScript

import React, { useEffect, useState } from "react";
import Typography from "@mui/material/Typography";
import Paper from "@mui/material/Paper";
import { ThemeEvents } from "./Theme";
import { Settings } from "../../Settings/Settings";
import { getPredefinedThemes, IPredefinedTheme } from "../Themes";
import { Box, ButtonGroup, Button } from "@mui/material";
import { IRouter } from "../../ui/Router";
import { ThemeEditorButton } from "./ThemeEditorButton";
import { StyleEditorButton } from "./StyleEditorButton";
import { ThemeEntry } from "./ThemeEntry";
import { ThemeCollaborate } from "./ThemeCollaborate";
import { Modal } from "../../ui/React/Modal";
import { SnackbarEvents } from "../../ui/React/Snackbar";
interface IProps {
router: IRouter;
}
// Everything dies when the theme gets reloaded, so we'll keep the current scroll to not jump around.
let previousScrollY = 0;
export function ThemeBrowser({ router }: IProps): React.ReactElement {
const [modalOpen, setModalOpen] = useState(false);
const [modalImageSrc, setModalImageSrc] = useState<string | undefined>();
const predefinedThemes = getPredefinedThemes();
const themes = (predefinedThemes &&
Object.entries(predefinedThemes).map(([key, templateTheme]) => (
<ThemeEntry
key={key}
theme={templateTheme}
onActivated={() => setTheme(templateTheme)}
onImageClick={handleZoom}
/>
))) || <></>;
function setTheme(theme: IPredefinedTheme): void {
previousScrollY = window.scrollY;
const previousColors = { ...Settings.theme };
Object.assign(Settings.theme, theme.colors);
ThemeEvents.emit();
SnackbarEvents.emit(
<>
Updated theme to "<strong>{theme.name}</strong>"
<Button
sx={{ ml: 1 }}
color="secondary"
size="small"
onClick={() => {
Object.assign(Settings.theme, previousColors);
ThemeEvents.emit();
}}
>
UNDO
</Button>
</>,
"info",
30000,
);
}
function handleZoom(src: string): void {
previousScrollY = window.scrollY;
setModalImageSrc(src);
setModalOpen(true);
}
function handleCloseZoom(): void {
previousScrollY = window.scrollY;
setModalOpen(false);
}
useEffect(() => {
requestAnimationFrame(() => window.scrollTo(0, previousScrollY));
});
return (
<Box sx={{ mx: 2 }}>
<Typography variant="h4">Theme Browser</Typography>
<Paper sx={{ px: 2, py: 1, my: 1 }}>
<ThemeCollaborate />
<ButtonGroup sx={{ mb: 2, display: "block" }}>
<ThemeEditorButton router={router} />
<StyleEditorButton />
</ButtonGroup>
<Box sx={{ display: "flex", flexWrap: "wrap" }}>{themes}</Box>
<Modal open={modalOpen} onClose={handleCloseZoom}>
<img src={modalImageSrc} style={{ width: "100%" }} />
</Modal>
</Paper>
</Box>
);
}