build theme editor

This commit is contained in:
Olivier Gagnon 2021-09-22 02:20:29 -04:00
parent f9a4eadb71
commit 80560ce9f6
10 changed files with 306 additions and 127 deletions

34
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -97,6 +97,39 @@ interface IDefaultSettings {
* Whether the user should be displayed a popup message when his Bladeburner actions are cancelled.
*/
SuppressBladeburnerPopup: boolean;
/*
* Theme colors
*/
theme: {
[key: string]: string | undefined;
primarylight: string;
primary: string;
primarydark: string;
errorlight: string;
error: string;
errordark: string;
secondarylight: string;
secondary: string;
secondarydark: string;
warninglight: string;
warning: string;
warningdark: string;
infolight: string;
info: string;
infodark: string;
welllight: string;
well: string;
white: string;
black: string;
hp: string;
money: string;
hack: string;
combat: string;
cha: string;
int: string;
rep: string;
};
}
/**
@ -118,7 +151,7 @@ interface ISettings extends IDefaultSettings {
MonacoInsertSpaces: boolean;
}
const defaultSettings: IDefaultSettings = {
export const defaultSettings: IDefaultSettings = {
ActiveScriptsServerPageSize: 10,
ActiveScriptsScriptPageSize: 10,
AutosaveInterval: 60,
@ -138,6 +171,35 @@ const defaultSettings: IDefaultSettings = {
SuppressMessages: false,
SuppressTravelConfirmation: false,
SuppressBladeburnerPopup: false,
theme: {
primarylight: "#0f0",
primary: "#0c0",
primarydark: "#090",
errorlight: "#f00",
error: "#c00",
errordark: "#900",
secondarylight: "#AAA",
secondary: "#888",
secondarydark: "#666",
warninglight: "#ff0",
warning: "#cc0",
warningdark: "#990",
infolight: "#69f",
info: "#36c",
infodark: "#039",
welllight: "#444",
well: "#222",
white: "#fff",
black: "#000",
hp: "#dd3434",
money: "#ffd700",
hack: "#adff2f",
combat: "#faffdf",
cha: "#a671d1",
int: "#6495ed",
rep: "#faffdf",
},
};
/**
@ -168,6 +230,35 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
SuppressBladeburnerPopup: defaultSettings.SuppressBladeburnerPopup,
MonacoTheme: "vs-dark",
MonacoInsertSpaces: false,
theme: {
primarylight: defaultSettings.theme.primarylight,
primary: defaultSettings.theme.primary,
primarydark: defaultSettings.theme.primarydark,
errorlight: defaultSettings.theme.errorlight,
error: defaultSettings.theme.error,
errordark: defaultSettings.theme.errordark,
secondarylight: defaultSettings.theme.secondarylight,
secondary: defaultSettings.theme.secondary,
secondarydark: defaultSettings.theme.secondarydark,
warninglight: defaultSettings.theme.warninglight,
warning: defaultSettings.theme.warning,
warningdark: defaultSettings.theme.warningdark,
infolight: defaultSettings.theme.infolight,
info: defaultSettings.theme.info,
infodark: defaultSettings.theme.infodark,
welllight: defaultSettings.theme.welllight,
well: defaultSettings.theme.well,
white: defaultSettings.theme.white,
black: defaultSettings.theme.black,
hp: defaultSettings.theme.hp,
money: defaultSettings.theme.money,
hack: defaultSettings.theme.hack,
combat: defaultSettings.theme.combat,
cha: defaultSettings.theme.cha,
int: defaultSettings.theme.int,
rep: defaultSettings.theme.rep,
},
init() {
Object.assign(Settings, defaultSettings);
},

@ -1,7 +1,7 @@
import React from "react";
import ReactDOM from "react-dom";
import { TTheme as Theme, ThemeEvents } from "./ui/React/Theme";
import { TTheme as Theme, ThemeEvents, refreshTheme } from "./ui/React/Theme";
import { LoadingScreen } from "./ui/LoadingScreen";
import "./engineStyle";
@ -13,6 +13,7 @@ ReactDOM.render(
);
function rerender() {
refreshTheme();
ReactDOM.render(
<Theme>
<LoadingScreen />

@ -18,7 +18,6 @@ import Button from "@mui/material/Button";
import IconButton from "@mui/material/IconButton";
import SaveIcon from "@mui/icons-material/Save";
import { colors } from "./Theme";
import { Settings } from "../../Settings/Settings";
import { use } from "../Context";

@ -26,6 +26,7 @@ import UploadIcon from "@mui/icons-material/Upload";
import { FileDiagnosticModal } from "../../Diagnostic/FileDiagnosticModal";
import { dialogBoxCreate } from "../../../utils/DialogBox";
import { ConfirmationModal } from "./ConfirmationModal";
import { ThemeEditorModal } from "./ThemeEditorModal";
import { Settings } from "../../Settings/Settings";
import { save, deleteGame } from "../../db";
@ -80,6 +81,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
const [locale, setLocale] = useState(Settings.Locale);
const [diagnosticOpen, setDiagnosticOpen] = useState(false);
const [deleteGameOpen, setDeleteOpen] = useState(false);
const [themeEditorOpen, setThemeEditorOpen] = useState(false);
function handleExecTimeChange(event: any, newValue: number | number[]): void {
setExecTime(newValue as number);
@ -600,6 +602,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
>
<Button onClick={() => setDiagnosticOpen(true)}>Diagnose files</Button>
</Tooltip>
<Button onClick={() => setThemeEditorOpen(true)}>Theme editor</Button>
</Box>
<Box>
<Link href="https://github.com/danielyxie/bitburner/issues/new" target="_blank">
@ -632,6 +635,7 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
onClose={() => setDeleteOpen(false)}
confirmationText={"Really delete your game? (It's permanent!)"}
/>
<ThemeEditorModal open={themeEditorOpen} onClose={() => setThemeEditorOpen(false)} />
</div>
);
}

@ -5,23 +5,25 @@
import React from "react";
import { Select as MuiSelect, SelectProps } from "@mui/material";
import makeStyles from '@mui/styles/makeStyles';
import { colors } from "./Theme";
import { Theme } from "@mui/material/styles";
import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles";
const useStyles = makeStyles({
// Tries to emulate StdButton in buttons.scss
root: {
backgroundColor: colors.well,
color: colors.primarydark,
margin: "5px",
padding: "3px 5px",
"&:after": {
backgroundColor: colors.well,
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
backgroundColor: theme.palette.background.paper,
color: theme.palette.primary.dark,
margin: "5px",
padding: "3px 5px",
"&:after": {
backgroundColor: theme.palette.background.paper,
},
borderRadius: 0,
},
borderRadius: 0,
},
});
}),
);
export const Select: React.FC<SelectProps> = (props: SelectProps) => {
return (

@ -1,6 +1,7 @@
import React from "react";
import { createTheme, ThemeProvider, Theme, StyledEngineProvider } from "@mui/material/styles";
import { EventEmitter } from "../../utils/EventEmitter";
import { Settings } from "../../Settings/Settings";
export const ThemeEvents = new EventEmitter<[]>();
@ -28,83 +29,49 @@ declare module "@mui/material/styles" {
};
}
}
export let colors = {
primarylight: "#0f0",
primary: "#0c0",
primarydark: "#090",
errorlight: "#f00",
error: "#c00",
errordark: "#900",
secondarylight: "#AAA",
secondary: "#888",
secondarydark: "#666",
warninglight: "#ff0",
warning: "#cc0",
warningdark: "#990",
infolight: "#69f",
info: "#36c",
infodark: "#039",
welllight: "#444",
well: "#222",
white: "#fff",
black: "#000",
hp: "#dd3434",
money: "#ffd700",
hack: "#adff2f",
combat: "#faffdf",
cha: "#a671d1",
int: "#6495ed",
rep: "#faffdf",
};
let theme: Theme;
function refreshTheme() {
export function refreshTheme() {
theme = createTheme({
colors: {
hp: "#dd3434",
money: "#ffd700",
hack: "#adff2f",
combat: "#faffdf",
cha: "#a671d1",
int: "#6495ed",
rep: "#faffdf",
hp: Settings.theme.hp,
money: Settings.theme.money,
hack: Settings.theme.hack,
combat: Settings.theme.combat,
cha: Settings.theme.cha,
int: Settings.theme.int,
rep: Settings.theme.rep,
},
palette: {
primary: {
light: colors.primarylight,
main: colors.primary,
dark: colors.primarydark,
light: Settings.theme.primarylight,
main: Settings.theme.primary,
dark: Settings.theme.primarydark,
},
secondary: {
light: colors.secondarylight,
main: colors.secondary,
dark: colors.secondarydark,
light: Settings.theme.secondarylight,
main: Settings.theme.secondary,
dark: Settings.theme.secondarydark,
},
error: {
light: colors.errorlight,
main: colors.error,
dark: colors.errordark,
light: Settings.theme.errorlight,
main: Settings.theme.error,
dark: Settings.theme.errordark,
},
info: {
light: colors.infolight,
main: colors.info,
dark: colors.infodark,
light: Settings.theme.infolight,
main: Settings.theme.info,
dark: Settings.theme.infodark,
},
warning: {
light: colors.warninglight,
main: colors.warning,
dark: colors.warningdark,
light: Settings.theme.warninglight,
main: Settings.theme.warning,
dark: Settings.theme.warningdark,
},
background: {
default: colors.black,
paper: colors.well,
default: Settings.theme.black,
paper: Settings.theme.well,
},
},
typography: {
@ -117,13 +84,13 @@ function refreshTheme() {
MuiInputBase: {
styleOverrides: {
root: {
backgroundColor: colors.well,
color: colors.primary,
backgroundColor: Settings.theme.well,
color: Settings.theme.primary,
},
input: {
"&::placeholder": {
userSelect: "none",
color: colors.primarydark,
color: Settings.theme.primarydark,
},
},
},
@ -132,18 +99,18 @@ function refreshTheme() {
MuiInput: {
styleOverrides: {
root: {
backgroundColor: colors.well,
backgroundColor: Settings.theme.well,
borderBottomColor: "#fff",
},
underline: {
"&:hover": {
borderBottomColor: colors.primarydark,
borderBottomColor: Settings.theme.primarydark,
},
"&:before": {
borderBottomColor: colors.primary,
borderBottomColor: Settings.theme.primary,
},
"&:after": {
borderBottomColor: colors.primarylight,
borderBottomColor: Settings.theme.primarylight,
},
},
},
@ -152,10 +119,10 @@ function refreshTheme() {
MuiInputLabel: {
styleOverrides: {
root: {
color: colors.primarydark, // why is this switched?
color: Settings.theme.primarydark, // why is this switched?
userSelect: "none",
"&:before": {
color: colors.primarylight,
color: Settings.theme.primarylight,
},
},
},
@ -164,10 +131,10 @@ function refreshTheme() {
styleOverrides: {
root: {
backgroundColor: "#333",
border: "1px solid " + colors.well,
// color: colors.primary,
border: "1px solid " + Settings.theme.well,
// color: Settings.theme.primary,
"&:hover": {
backgroundColor: colors.black,
backgroundColor: Settings.theme.black,
},
borderRadius: 0,
@ -177,21 +144,21 @@ function refreshTheme() {
MuiSelect: {
styleOverrides: {
icon: {
color: colors.primary,
color: Settings.theme.primary,
},
},
},
MuiMenu: {
styleOverrides: {
list: {
backgroundColor: colors.well,
backgroundColor: Settings.theme.well,
},
},
},
MuiMenuItem: {
styleOverrides: {
root: {
color: colors.primary,
color: Settings.theme.primary,
},
},
},
@ -205,14 +172,14 @@ function refreshTheme() {
MuiAccordionDetails: {
styleOverrides: {
root: {
backgroundColor: colors.black,
backgroundColor: Settings.theme.black,
},
},
},
MuiIconButton: {
styleOverrides: {
root: {
color: colors.primary,
color: Settings.theme.primary,
},
},
},
@ -220,8 +187,8 @@ function refreshTheme() {
styleOverrides: {
tooltip: {
fontSize: "1em",
color: colors.primary,
backgroundColor: colors.well,
color: Settings.theme.primary,
backgroundColor: Settings.theme.well,
borderRadius: 0,
border: "2px solid white",
maxWidth: "100vh",
@ -231,8 +198,8 @@ function refreshTheme() {
MuiSlider: {
styleOverrides: {
valueLabel: {
color: colors.primary,
backgroundColor: colors.well,
color: Settings.theme.primary,
backgroundColor: Settings.theme.well,
},
},
},
@ -244,34 +211,34 @@ function refreshTheme() {
display: "none",
},
scrollbarWidth: "none", // firefox
backgroundColor: colors.black,
backgroundColor: Settings.theme.black,
},
paperAnchorDockedLeft: {
borderRight: "1px solid " + colors.welllight,
borderRight: "1px solid " + Settings.theme.welllight,
},
},
},
MuiDivider: {
styleOverrides: {
root: {
backgroundColor: colors.welllight,
backgroundColor: Settings.theme.welllight,
},
},
},
MuiFormControlLabel: {
styleOverrides: {
root: {
color: colors.primary,
color: Settings.theme.primary,
},
},
},
MuiSwitch: {
styleOverrides: {
switchBase: {
color: colors.primarydark,
color: Settings.theme.primarydark,
},
track: {
backgroundColor: colors.welllight,
backgroundColor: Settings.theme.welllight,
},
},
},
@ -279,15 +246,15 @@ function refreshTheme() {
styleOverrides: {
root: {
borderRadius: 0,
backgroundColor: colors.black,
border: "1px solid " + colors.welllight,
backgroundColor: Settings.theme.black,
border: "1px solid " + Settings.theme.welllight,
},
},
},
MuiTablePagination: {
styleOverrides: {
select: {
color: colors.primary,
color: Settings.theme.primary,
},
},
},

@ -0,0 +1,115 @@
import React, { useState } from "react";
import { Modal } from "./Modal";
import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography";
import TextField from "@mui/material/TextField";
import DoneIcon from "@mui/icons-material/Done";
import IconButton from "@mui/material/IconButton";
import ReplyIcon from "@mui/icons-material/Reply";
import { ThemeEvents } from "./Theme";
import { Settings, defaultSettings } from "../../Settings/Settings";
interface IProps {
open: boolean;
onClose: () => void;
}
function ColorEditor({ name }: { name: string }): React.ReactElement {
const [color, setColor] = useState(Settings.theme[name]);
if (color === undefined) return <></>;
const valid = color.match(/#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})/g);
function set(): void {
if (!valid) return;
Settings.theme[name] = color;
ThemeEvents.emit();
}
function revert(): void {
Settings.theme[name] = defaultSettings.theme[name];
setColor(defaultSettings.theme[name]);
ThemeEvents.emit();
}
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
setColor(event.target.value);
}
return (
<>
<TextField
sx={{ mx: 1 }}
label={name}
value={color}
onChange={onChange}
variant="standard"
InputProps={{
endAdornment: (
<>
<IconButton onClick={set} disabled={!valid}>
<DoneIcon color={valid ? "primary" : "error"} />
</IconButton>
<IconButton onClick={revert}>
<ReplyIcon color="primary" />
</IconButton>
</>
),
}}
/>
</>
);
}
export function ThemeEditorModal(props: IProps): React.ReactElement {
return (
<Modal open={props.open} onClose={props.onClose}>
<Button color="primary">primary</Button>
<Button color="secondary">secondary</Button>
<Button color="warning">warning</Button>
<Button color="info">info</Button>
<Button color="error">error</Button>
<Typography color="primary">primary</Typography>
<Typography color="secondary">secondary</Typography>
<Typography color="warning">warning</Typography>
<Typography color="info">info</Typography>
<Typography color="error">error</Typography>
<br />
<ColorEditor name="primarylight" />
<ColorEditor name="primary" />
<ColorEditor name="primarydark" />
<br />
<ColorEditor name="errorlight" />
<ColorEditor name="error" />
<ColorEditor name="errordark" />
<br />
<ColorEditor name="secondarylight" />
<ColorEditor name="secondary" />
<ColorEditor name="secondarydark" />
<br />
<ColorEditor name="warninglight" />
<ColorEditor name="warning" />
<ColorEditor name="warningdark" />
<br />
<ColorEditor name="infolight" />
<ColorEditor name="info" />
<ColorEditor name="infodark" />
<br />
<ColorEditor name="welllight" />
<ColorEditor name="well" />
<ColorEditor name="white" />
<ColorEditor name="black" />
<br />
<ColorEditor name="hp" />
<ColorEditor name="money" />
<ColorEditor name="hack" />
<ColorEditor name="combat" />
<ColorEditor name="cha" />
<ColorEditor name="int" />
<ColorEditor name="rep" />
</Modal>
);
}