mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-17 13:13:49 +01:00
Allow drag on character overview
Makes the character overview (and tutorial) draggable, persisting the {x, y, opened} in the user's settings. - Remove margin & padding from html, body and ensure main content is full height - Add setting to disable progress bars - Refactor options to use new OptionSwitch - Add exclusions to prettierignore - Specify line ending in prettier & gitattributes
This commit is contained in:
parent
a53b36ed27
commit
7ee2612c17
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -1 +1 @@
|
||||
* text=auto
|
||||
* text=auto eol=lf
|
||||
|
@ -2,3 +2,14 @@ node_modules
|
||||
package.json
|
||||
dist
|
||||
doc/build/
|
||||
doc/source
|
||||
.build
|
||||
.package
|
||||
|
||||
editor.main.js
|
||||
main.bundle.js
|
||||
index.html
|
||||
markdown
|
||||
|
||||
package.json
|
||||
package.lock.json
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"trailingComma": "all",
|
||||
"endOfLine": "lf",
|
||||
"tabWidth": 2,
|
||||
"printWidth": 120
|
||||
}
|
||||
|
@ -2,7 +2,8 @@ import { ISelfInitializer, ISelfLoading } from "../types";
|
||||
import { OwnedAugmentationsOrderSetting, PurchaseAugmentationsOrderSetting } from "./SettingEnums";
|
||||
import { defaultTheme, ITheme } from "./Themes";
|
||||
import { defaultStyles, IStyleSettings } from "./Styles";
|
||||
import { WordWrapOptions } from '../ScriptEditor/ui/Options';
|
||||
import { WordWrapOptions } from "../ScriptEditor/ui/Options";
|
||||
import { OverviewSettings } from "../ui/React/Overview";
|
||||
|
||||
/**
|
||||
* Represents the default settings the player could customize.
|
||||
@ -41,6 +42,11 @@ interface IDefaultSettings {
|
||||
*/
|
||||
DisableTextEffects: boolean;
|
||||
|
||||
/**
|
||||
* Whether overview progress bars should be visible.
|
||||
*/
|
||||
DisableOverviewProgressBars: boolean;
|
||||
|
||||
/**
|
||||
* Enable bash hotkeys
|
||||
*/
|
||||
@ -125,6 +131,11 @@ interface IDefaultSettings {
|
||||
* Use GiB instead of GB
|
||||
*/
|
||||
UseIEC60027_2: boolean;
|
||||
|
||||
/*
|
||||
* Character overview settings
|
||||
*/
|
||||
overview: OverviewSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -160,6 +171,7 @@ export const defaultSettings: IDefaultSettings = {
|
||||
DisableASCIIArt: false,
|
||||
DisableHotkeys: false,
|
||||
DisableTextEffects: false,
|
||||
DisableOverviewProgressBars: false,
|
||||
EnableBashHotkeys: false,
|
||||
TimestampsFormat: "",
|
||||
Locale: "en",
|
||||
@ -178,6 +190,7 @@ export const defaultSettings: IDefaultSettings = {
|
||||
|
||||
theme: defaultTheme,
|
||||
styles: defaultStyles,
|
||||
overview: { x: 0, y: 0, opened: true },
|
||||
};
|
||||
|
||||
/**
|
||||
@ -192,6 +205,7 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
|
||||
DisableASCIIArt: defaultSettings.DisableASCIIArt,
|
||||
DisableHotkeys: defaultSettings.DisableHotkeys,
|
||||
DisableTextEffects: defaultSettings.DisableTextEffects,
|
||||
DisableOverviewProgressBars: defaultSettings.DisableOverviewProgressBars,
|
||||
EnableBashHotkeys: defaultSettings.EnableBashHotkeys,
|
||||
TimestampsFormat: defaultSettings.TimestampsFormat,
|
||||
Locale: "en",
|
||||
@ -213,10 +227,11 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
|
||||
MonacoInsertSpaces: false,
|
||||
MonacoFontSize: 20,
|
||||
MonacoVim: false,
|
||||
MonacoWordWrap: 'off',
|
||||
MonacoWordWrap: "off",
|
||||
|
||||
theme: { ...defaultTheme },
|
||||
styles: { ...defaultStyles },
|
||||
overview: defaultSettings.overview,
|
||||
init() {
|
||||
Object.assign(Settings, defaultSettings);
|
||||
},
|
||||
@ -226,6 +241,8 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
|
||||
delete save.theme;
|
||||
Object.assign(Settings.styles, save.styles);
|
||||
delete save.styles;
|
||||
Object.assign(Settings.overview, save.overview);
|
||||
delete save.overview;
|
||||
Object.assign(Settings, save);
|
||||
},
|
||||
};
|
||||
|
@ -5,8 +5,6 @@ import { BaseServer } from "../../Server/BaseServer";
|
||||
import { getServerOnNetwork } from "../../Server/ServerHelpers";
|
||||
import { GetServer } from "../../Server/AllServers";
|
||||
import { Server } from "../../Server/Server";
|
||||
import { Programs } from "src/Programs/Programs";
|
||||
import { programsMetadata } from "src/Programs/data/ProgramsMetadata";
|
||||
|
||||
export function connect(
|
||||
terminal: ITerminal,
|
||||
|
@ -52,6 +52,11 @@
|
||||
</script>
|
||||
<% } %>
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
}
|
||||
body {
|
||||
background-color: black;
|
||||
}
|
||||
|
@ -77,7 +77,6 @@ import { enterBitNode } from "../RedPill";
|
||||
import { Context } from "./Context";
|
||||
import { RecoveryMode, RecoveryRoot } from "./React/RecoveryRoot";
|
||||
import { AchievementsRoot } from "../Achievements/AchievementsRoot";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
|
||||
const htmlLocation = location;
|
||||
|
||||
@ -93,6 +92,10 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||
"-ms-overflow-style": "none" /* for Internet Explorer, Edge */,
|
||||
"scrollbar-width": "none" /* for Firefox */,
|
||||
margin: theme.spacing(0),
|
||||
flexGrow: 1,
|
||||
display: "block",
|
||||
padding: "8px",
|
||||
minHeight: "100vh",
|
||||
},
|
||||
}),
|
||||
);
|
||||
@ -187,7 +190,7 @@ export let Router: IRouter = {
|
||||
},
|
||||
toAchievements: () => {
|
||||
throw new Error("Router called before initialization");
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function determineStartPage(player: IPlayer): Page {
|
||||
@ -198,7 +201,7 @@ function determineStartPage(player: IPlayer): Page {
|
||||
|
||||
export function GameRoot({ player, engine, terminal }: IProps): React.ReactElement {
|
||||
const classes = useStyles();
|
||||
const [{files, vim}, setEditorOptions] = useState({files: {}, vim: false})
|
||||
const [{ files, vim }, setEditorOptions] = useState({ files: {}, vim: false });
|
||||
const [page, setPage] = useState(determineStartPage(player));
|
||||
const setRerender = useState(0)[1];
|
||||
const [faction, setFaction] = useState<Faction>(
|
||||
@ -315,7 +318,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
mainPage = <BitverseRoot flume={flume} enter={enterBitNode} quick={quick} />;
|
||||
withSidebar = false;
|
||||
withPopups = false;
|
||||
break
|
||||
break;
|
||||
}
|
||||
case Page.Infiltration: {
|
||||
mainPage = <InfiltrationRoot location={location} />;
|
||||
@ -351,13 +354,15 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
break;
|
||||
}
|
||||
case Page.ScriptEditor: {
|
||||
mainPage = <ScriptEditorRoot
|
||||
files={files}
|
||||
hostname={player.getCurrentServer().hostname}
|
||||
player={player}
|
||||
router={Router}
|
||||
vim={vim}
|
||||
/>;
|
||||
mainPage = (
|
||||
<ScriptEditorRoot
|
||||
files={files}
|
||||
hostname={player.getCurrentServer().hostname}
|
||||
player={player}
|
||||
router={Router}
|
||||
vim={vim}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case Page.ActiveScripts: {
|
||||
@ -385,13 +390,15 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
break;
|
||||
}
|
||||
case Page.Tutorial: {
|
||||
mainPage = <TutorialRoot
|
||||
reactivateTutorial={() => {
|
||||
prestigeAugmentation();
|
||||
Router.toTerminal();
|
||||
iTutorialStart();
|
||||
}}
|
||||
/>;
|
||||
mainPage = (
|
||||
<TutorialRoot
|
||||
reactivateTutorial={() => {
|
||||
prestigeAugmentation();
|
||||
Router.toTerminal();
|
||||
iTutorialStart();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case Page.DevMenu: {
|
||||
@ -419,59 +426,65 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
break;
|
||||
}
|
||||
case Page.StockMarket: {
|
||||
mainPage = <StockMarketRoot
|
||||
buyStockLong={buyStock}
|
||||
buyStockShort={shortStock}
|
||||
cancelOrder={cancelOrder}
|
||||
eventEmitterForReset={eventEmitterForUiReset}
|
||||
initStockMarket={initStockMarketFnForReact}
|
||||
p={player}
|
||||
placeOrder={placeOrder}
|
||||
sellStockLong={sellStock}
|
||||
sellStockShort={sellShort}
|
||||
stockMarket={StockMarket}
|
||||
/>;
|
||||
mainPage = (
|
||||
<StockMarketRoot
|
||||
buyStockLong={buyStock}
|
||||
buyStockShort={shortStock}
|
||||
cancelOrder={cancelOrder}
|
||||
eventEmitterForReset={eventEmitterForUiReset}
|
||||
initStockMarket={initStockMarketFnForReact}
|
||||
p={player}
|
||||
placeOrder={placeOrder}
|
||||
sellStockLong={sellStock}
|
||||
sellStockShort={sellShort}
|
||||
stockMarket={StockMarket}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case Page.City: {
|
||||
mainPage = <LocationCity />;
|
||||
break;
|
||||
}
|
||||
case Page.Job:
|
||||
case Page.Job:
|
||||
case Page.Location: {
|
||||
mainPage = <GenericLocation loc={location} />;
|
||||
break;
|
||||
}
|
||||
case Page.Options: {
|
||||
mainPage = <GameOptionsRoot
|
||||
player={player}
|
||||
save={() => saveObject.saveGame()}
|
||||
export={() => {
|
||||
// Apply the export bonus before saving the game
|
||||
onExport(player);
|
||||
saveObject.exportGame()
|
||||
}}
|
||||
forceKill={killAllScripts}
|
||||
softReset={() => {
|
||||
dialogBoxCreate("Soft Reset!");
|
||||
prestigeAugmentation();
|
||||
Router.toTerminal();
|
||||
}}
|
||||
/>;
|
||||
mainPage = (
|
||||
<GameOptionsRoot
|
||||
player={player}
|
||||
save={() => saveObject.saveGame()}
|
||||
export={() => {
|
||||
// Apply the export bonus before saving the game
|
||||
onExport(player);
|
||||
saveObject.exportGame();
|
||||
}}
|
||||
forceKill={killAllScripts}
|
||||
softReset={() => {
|
||||
dialogBoxCreate("Soft Reset!");
|
||||
prestigeAugmentation();
|
||||
Router.toTerminal();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case Page.Augmentations: {
|
||||
mainPage = <AugmentationsRoot
|
||||
exportGameFn={() => {
|
||||
// Apply the export bonus before saving the game
|
||||
onExport(player);
|
||||
saveObject.exportGame();
|
||||
}}
|
||||
installAugmentationsFn={() => {
|
||||
installAugmentations();
|
||||
Router.toTerminal();
|
||||
}}
|
||||
/>;
|
||||
mainPage = (
|
||||
<AugmentationsRoot
|
||||
exportGameFn={() => {
|
||||
// Apply the export bonus before saving the game
|
||||
onExport(player);
|
||||
saveObject.exportGame();
|
||||
}}
|
||||
installAugmentationsFn={() => {
|
||||
installAugmentations();
|
||||
Router.toTerminal();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case Page.Achievements: {
|
||||
@ -479,12 +492,12 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<Context.Player.Provider value={player}>
|
||||
<Context.Router.Provider value={Router}>
|
||||
<SnackbarProvider>
|
||||
<Overview>
|
||||
<Overview mode={ITutorial.isRunning ? "tutorial" : "overview"}>
|
||||
{!ITutorial.isRunning ? (
|
||||
<CharacterOverview save={() => saveObject.saveGame()} killScripts={killAllScripts} />
|
||||
) : (
|
||||
@ -494,19 +507,19 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
|
||||
{withSidebar ? (
|
||||
<Box display="flex" flexDirection="row" width="100%">
|
||||
<SidebarRoot player={player} router={Router} page={page} />
|
||||
<Box className={classes.root} flexGrow={1} display="block" px={1} min-height="100vh">
|
||||
{mainPage}
|
||||
</Box>
|
||||
<Box className={classes.root}>{mainPage}</Box>
|
||||
</Box>
|
||||
) : mainPage }
|
||||
) : (
|
||||
<Box className={classes.root}>{mainPage}</Box>
|
||||
)}
|
||||
<Unclickable />
|
||||
{withPopups && (
|
||||
<>
|
||||
<LogBoxManager />
|
||||
<AlertManager />
|
||||
<PromptManager />
|
||||
<InvitationModal />
|
||||
<Snackbar />
|
||||
<LogBoxManager />
|
||||
<AlertManager />
|
||||
<PromptManager />
|
||||
<InvitationModal />
|
||||
<Snackbar />
|
||||
</>
|
||||
)}
|
||||
</SnackbarProvider>
|
||||
|
@ -12,7 +12,6 @@ import { CopyableText } from "../React/CopyableText";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import EqualizerIcon from "@mui/icons-material/Equalizer";
|
||||
import LastPageIcon from "@mui/icons-material/LastPage";
|
||||
import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
|
||||
import HelpIcon from "@mui/icons-material/Help";
|
||||
import AccountTreeIcon from "@mui/icons-material/AccountTree";
|
||||
import StorageIcon from "@mui/icons-material/Storage";
|
||||
@ -61,7 +60,7 @@ export function InteractiveTutorialRoot(): React.ReactElement {
|
||||
This tutorial will show you the basics of the game. You may skip the tutorial at any time.
|
||||
<br />
|
||||
<br />
|
||||
You can also click the eye symbol <VisibilityOffIcon /> to temporarily hide this tutorial.
|
||||
You can also collapse this panel to temporarily hide this tutorial.
|
||||
</Typography>
|
||||
</>
|
||||
),
|
||||
|
@ -287,7 +287,9 @@ export function CharacterOverview({ save, killScripts }: IProps): React.ReactEle
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<StatsProgressOverviewCell progress={hackingProgress} color={theme.colors.hack} />
|
||||
{!Settings.DisableOverviewProgressBars && (
|
||||
<StatsProgressOverviewCell progress={hackingProgress} color={theme.colors.hack} />
|
||||
)}
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row" classes={{ root: classes.cell }}>
|
||||
@ -314,7 +316,9 @@ export function CharacterOverview({ save, killScripts }: IProps): React.ReactEle
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<StatsProgressOverviewCell progress={strengthProgress} color={theme.colors.combat} />
|
||||
{!Settings.DisableOverviewProgressBars && (
|
||||
<StatsProgressOverviewCell progress={strengthProgress} color={theme.colors.combat} />
|
||||
)}
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
@ -331,7 +335,9 @@ export function CharacterOverview({ save, killScripts }: IProps): React.ReactEle
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<StatsProgressOverviewCell progress={defenseProgress} color={theme.colors.combat} />
|
||||
{!Settings.DisableOverviewProgressBars && (
|
||||
<StatsProgressOverviewCell progress={defenseProgress} color={theme.colors.combat} />
|
||||
)}
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
@ -348,7 +354,9 @@ export function CharacterOverview({ save, killScripts }: IProps): React.ReactEle
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<StatsProgressOverviewCell progress={dexterityProgress} color={theme.colors.combat} />
|
||||
{!Settings.DisableOverviewProgressBars && (
|
||||
<StatsProgressOverviewCell progress={dexterityProgress} color={theme.colors.combat} />
|
||||
)}
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
@ -365,7 +373,9 @@ export function CharacterOverview({ save, killScripts }: IProps): React.ReactEle
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<StatsProgressOverviewCell progress={agilityProgress} color={theme.colors.combat} />
|
||||
{!Settings.DisableOverviewProgressBars && (
|
||||
<StatsProgressOverviewCell progress={agilityProgress} color={theme.colors.combat} />
|
||||
)}
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
@ -382,7 +392,9 @@ export function CharacterOverview({ save, killScripts }: IProps): React.ReactEle
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<StatsProgressOverviewCell progress={charismaProgress} color={theme.colors.cha} />
|
||||
{!Settings.DisableOverviewProgressBars && (
|
||||
<StatsProgressOverviewCell progress={charismaProgress} color={theme.colors.cha} />
|
||||
)}
|
||||
</TableRow>
|
||||
|
||||
<Intelligence />
|
||||
|
@ -8,8 +8,6 @@ import createStyles from "@mui/styles/createStyles";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Slider from "@mui/material/Slider";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import FormControlLabel from "@mui/material/FormControlLabel";
|
||||
import Switch from "@mui/material/Switch";
|
||||
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import Button from "@mui/material/Button";
|
||||
@ -35,6 +33,7 @@ import { SnackbarEvents } from "./Snackbar";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { save, deleteGame } from "../../db";
|
||||
import { formatTime } from "../../utils/helpers/formatTime";
|
||||
import { OptionSwitch } from "./OptionSwitch";
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
@ -68,27 +67,8 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
||||
const [logSize, setLogSize] = useState(Settings.MaxLogCapacity);
|
||||
const [portSize, setPortSize] = useState(Settings.MaxPortCapacity);
|
||||
const [terminalSize, setTerminalSize] = useState(Settings.MaxTerminalCapacity);
|
||||
|
||||
const [autosaveInterval, setAutosaveInterval] = useState(Settings.AutosaveInterval);
|
||||
|
||||
const [suppressMessages, setSuppressMessages] = useState(Settings.SuppressMessages);
|
||||
const [suppressFactionInvites, setSuppressFactionInvites] = useState(Settings.SuppressFactionInvites);
|
||||
const [suppressTravelConfirmations, setSuppressTravelConfirmations] = useState(Settings.SuppressTravelConfirmation);
|
||||
const [suppressBuyAugmentationConfirmation, setSuppressBuyAugmentationConfirmation] = useState(
|
||||
Settings.SuppressBuyAugmentationConfirmation,
|
||||
);
|
||||
const [suppressTIXPopup, setSuppressTIXPopup] = useState(Settings.SuppressTIXPopup);
|
||||
const [suppressBladeburnerPopup, setSuppressBladeburnerPopup] = useState(Settings.SuppressBladeburnerPopup);
|
||||
const [suppressSavedGameToast, setSuppresSavedGameToast] = useState(Settings.SuppressSavedGameToast);
|
||||
|
||||
const [disableHotkeys, setDisableHotkeys] = useState(Settings.DisableHotkeys);
|
||||
const [disableASCIIArt, setDisableASCIIArt] = useState(Settings.DisableASCIIArt);
|
||||
const [disableTextEffects, setDisableTextEffects] = useState(Settings.DisableTextEffects);
|
||||
const [enableBashHotkeys, setEnableBashHotkeys] = useState(Settings.EnableBashHotkeys);
|
||||
const [timestampFormat, setTimestampFormat] = useState(Settings.TimestampsFormat);
|
||||
const [saveGameOnFileSave, setSaveGameOnFileSave] = useState(Settings.SaveGameOnFileSave);
|
||||
const [useIEC60027_2, setUseIEC60027_2] = useState(Settings.UseIEC60027_2);
|
||||
|
||||
const [locale, setLocale] = useState(Settings.Locale);
|
||||
const [diagnosticOpen, setDiagnosticOpen] = useState(false);
|
||||
const [deleteGameOpen, setDeleteOpen] = useState(false);
|
||||
@ -123,76 +103,15 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
||||
Settings.AutosaveInterval = newValue as number;
|
||||
}
|
||||
|
||||
function handleSuppressMessagesChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setSuppressMessages(event.target.checked);
|
||||
Settings.SuppressMessages = event.target.checked;
|
||||
}
|
||||
|
||||
function handleSuppressFactionInvitesChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setSuppressFactionInvites(event.target.checked);
|
||||
Settings.SuppressFactionInvites = event.target.checked;
|
||||
}
|
||||
|
||||
function handleSuppressTravelConfirmationsChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setSuppressTravelConfirmations(event.target.checked);
|
||||
Settings.SuppressTravelConfirmation = event.target.checked;
|
||||
}
|
||||
|
||||
function handleSuppressBuyAugmentationConfirmationChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setSuppressBuyAugmentationConfirmation(event.target.checked);
|
||||
Settings.SuppressBuyAugmentationConfirmation = event.target.checked;
|
||||
}
|
||||
|
||||
function handleSuppressTIXPopupChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setSuppressTIXPopup(event.target.checked);
|
||||
Settings.SuppressTIXPopup = event.target.checked;
|
||||
}
|
||||
|
||||
function handleSuppressBladeburnerPopupChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setSuppressBladeburnerPopup(event.target.checked);
|
||||
Settings.SuppressBladeburnerPopup = event.target.checked;
|
||||
}
|
||||
|
||||
function handleSuppressSavedGameToastChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setSuppresSavedGameToast(event.target.checked);
|
||||
Settings.SuppressSavedGameToast = event.target.checked;
|
||||
}
|
||||
|
||||
function handleDisableHotkeysChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setDisableHotkeys(event.target.checked);
|
||||
Settings.DisableHotkeys = event.target.checked;
|
||||
}
|
||||
|
||||
function handleDisableASCIIArtChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setDisableASCIIArt(event.target.checked);
|
||||
Settings.DisableASCIIArt = event.target.checked;
|
||||
}
|
||||
function handleUseIEC60027_2Change(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setUseIEC60027_2(event.target.checked);
|
||||
Settings.UseIEC60027_2 = event.target.checked;
|
||||
}
|
||||
|
||||
function handleDisableTextEffectsChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setDisableTextEffects(event.target.checked);
|
||||
Settings.DisableTextEffects = event.target.checked;
|
||||
}
|
||||
function handleLocaleChange(event: SelectChangeEvent<string>): void {
|
||||
setLocale(event.target.value as string);
|
||||
Settings.Locale = event.target.value as string;
|
||||
}
|
||||
|
||||
function handleEnableBashHotkeysChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setEnableBashHotkeys(event.target.checked);
|
||||
Settings.EnableBashHotkeys = event.target.checked;
|
||||
}
|
||||
function handleTimestampFormatChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setTimestampFormat(event.target.value);
|
||||
Settings.TimestampsFormat = event.target.value;
|
||||
}
|
||||
function handleSaveGameOnFile(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setSaveGameOnFileSave(event.target.checked);
|
||||
Settings.SaveGameOnFileSave = event.target.checked;
|
||||
}
|
||||
|
||||
function startImport(): void {
|
||||
if (!window.File || !window.FileReader || !window.FileList || !window.Blob) return;
|
||||
@ -392,200 +311,120 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<FormControlLabel
|
||||
control={<Switch checked={suppressMessages} onChange={handleSuppressMessagesChange} />}
|
||||
label={
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
If this is set, then any messages you receive will not appear as popups on the screen. They will
|
||||
still get sent to your home computer as '.msg' files and can be viewed with the 'cat' Terminal
|
||||
command.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Suppress story messages</Typography>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
<OptionSwitch checked={Settings.SuppressMessages}
|
||||
onChange={(newValue) => Settings.SuppressMessages = newValue}
|
||||
text="Suppress story messages"
|
||||
tooltip={<>
|
||||
If this is set, then any messages you receive will not appear as popups on the screen. They will
|
||||
still get sent to your home computer as '.msg' files and can be viewed with the 'cat' Terminal
|
||||
command.
|
||||
</>} />
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<FormControlLabel
|
||||
control={<Switch checked={suppressFactionInvites} onChange={handleSuppressFactionInvitesChange} />}
|
||||
label={
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
If this is set, then any faction invites you receive will not appear as popups on the screen.
|
||||
Your outstanding faction invites can be viewed in the 'Factions' page.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Suppress faction invites</Typography>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
<OptionSwitch checked={Settings.SuppressFactionInvites}
|
||||
onChange={(newValue) => Settings.SuppressFactionInvites = newValue}
|
||||
text="Suppress faction invites"
|
||||
tooltip={<>
|
||||
If this is set, then any faction invites you receive will not appear as popups on the screen.
|
||||
Your outstanding faction invites can be viewed in the 'Factions' page.
|
||||
</>} />
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch checked={suppressTravelConfirmations} onChange={handleSuppressTravelConfirmationsChange} />
|
||||
}
|
||||
label={
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
If this is set, the confirmation message before traveling will not show up. You will
|
||||
automatically be deducted the travel cost as soon as you click.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Suppress travel confirmations</Typography>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
<OptionSwitch checked={Settings.SuppressTravelConfirmation}
|
||||
onChange={(newValue) => Settings.SuppressTravelConfirmation = newValue}
|
||||
text="Suppress travel confirmations"
|
||||
tooltip={<>
|
||||
If this is set, the confirmation message before traveling will not show up. You will
|
||||
automatically be deducted the travel cost as soon as you click.
|
||||
</>} />
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={suppressBuyAugmentationConfirmation}
|
||||
onChange={handleSuppressBuyAugmentationConfirmationChange}
|
||||
/>
|
||||
}
|
||||
label={
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
If this is set, the confirmation message before buying augmentation will not show up.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Suppress augmentations confirmation</Typography>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
<OptionSwitch checked={Settings.SuppressBuyAugmentationConfirmation}
|
||||
onChange={(newValue) => Settings.SuppressBuyAugmentationConfirmation = newValue}
|
||||
text="Suppress augmentations confirmation"
|
||||
tooltip={<>
|
||||
If this is set, the confirmation message before buying augmentation will not show up.
|
||||
</>} />
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<FormControlLabel
|
||||
control={<Switch checked={suppressTIXPopup} onChange={handleSuppressTIXPopupChange} />}
|
||||
label={
|
||||
<Tooltip
|
||||
title={<Typography>If this is set, the stock market will never create any popup.</Typography>}
|
||||
>
|
||||
<Typography>Suppress TIX messages</Typography>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
<OptionSwitch checked={Settings.SuppressTIXPopup}
|
||||
onChange={(newValue) => Settings.SuppressTIXPopup = newValue}
|
||||
text="Suppress TIX messages"
|
||||
tooltip={<>
|
||||
If this is set, the stock market will never create any popup.
|
||||
</>} />
|
||||
</ListItem>
|
||||
{!!props.player.bladeburner && (
|
||||
<ListItem>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch checked={suppressBladeburnerPopup} onChange={handleSuppressBladeburnerPopupChange} />
|
||||
}
|
||||
label={
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
If this is set, then having your Bladeburner actions interrupted by being busy with something
|
||||
else will not display a popup message.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Suppress bladeburner popup</Typography>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
<OptionSwitch checked={Settings.SuppressBladeburnerPopup}
|
||||
onChange={(newValue) => Settings.SuppressBladeburnerPopup = newValue}
|
||||
text="Suppress bladeburner popup"
|
||||
tooltip={<>
|
||||
If this is set, then having your Bladeburner actions interrupted by being busy with something
|
||||
else will not display a popup message.
|
||||
</>} />
|
||||
</ListItem>
|
||||
)}
|
||||
<ListItem>
|
||||
<FormControlLabel
|
||||
control={<Switch checked={suppressSavedGameToast} onChange={handleSuppressSavedGameToastChange} />}
|
||||
label={
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>If this is set, there will be no "Game Saved!" toast appearing after an auto-save.</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Suppress Auto-Save Game Toast</Typography>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
<OptionSwitch checked={Settings.SuppressSavedGameToast}
|
||||
onChange={(newValue) => Settings.SuppressSavedGameToast = newValue}
|
||||
text="Suppress Auto-Save Game Toast"
|
||||
tooltip={<>
|
||||
If this is set, there will be no "Game Saved!" toast appearing after an auto-save.
|
||||
</>} />
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<FormControlLabel
|
||||
control={<Switch checked={disableHotkeys} onChange={handleDisableHotkeysChange} />}
|
||||
label={
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
If this is set, then most hotkeys (keyboard shortcuts) in the game are disabled. This includes
|
||||
Terminal commands, hotkeys to navigate between different parts of the game, and the "Save and
|
||||
Close (Ctrl + b)" hotkey in the Text Editor.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Disable hotkeys</Typography>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
<OptionSwitch checked={Settings.DisableHotkeys}
|
||||
onChange={(newValue) => Settings.DisableHotkeys = newValue}
|
||||
text="Disable hotkeys"
|
||||
tooltip={<>
|
||||
If this is set, then most hotkeys (keyboard shortcuts) in the game are disabled. This includes
|
||||
Terminal commands, hotkeys to navigate between different parts of the game, and the "Save and
|
||||
Close (Ctrl + b)" hotkey in the Text Editor.
|
||||
</>} />
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<FormControlLabel
|
||||
control={<Switch checked={disableASCIIArt} onChange={handleDisableASCIIArtChange} />}
|
||||
label={
|
||||
<Tooltip title={<Typography>If this is set all ASCII art will be disabled.</Typography>}>
|
||||
<Typography>Disable ascii art</Typography>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
<OptionSwitch checked={Settings.DisableASCIIArt}
|
||||
onChange={(newValue) => Settings.DisableASCIIArt = newValue}
|
||||
text="Disable ascii art"
|
||||
tooltip={<>
|
||||
If this is set all ASCII art will be disabled.
|
||||
</>} />
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<FormControlLabel
|
||||
control={<Switch checked={disableTextEffects} onChange={handleDisableTextEffectsChange} />}
|
||||
label={
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
If this is set, text effects will not be displayed. This can help if text is difficult to read
|
||||
in certain areas.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Disable text effects</Typography>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
</ListItem>
|
||||
|
||||
<ListItem>
|
||||
<FormControlLabel
|
||||
control={<Switch checked={enableBashHotkeys} onChange={handleEnableBashHotkeysChange} />}
|
||||
label={
|
||||
<Tooltip
|
||||
title={
|
||||
<Typography>
|
||||
Improved Bash emulation mode. Setting this to 1 enables several new Terminal shortcuts and
|
||||
features that more closely resemble a real Bash-style shell. Note that when this mode is
|
||||
enabled, the default browser shortcuts are overriden by the new Bash shortcuts.
|
||||
</Typography>
|
||||
}
|
||||
>
|
||||
<Typography>Enable bash hotkeys</Typography>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
<OptionSwitch checked={Settings.DisableTextEffects}
|
||||
onChange={(newValue) => Settings.DisableTextEffects = newValue}
|
||||
text="Disable text effects"
|
||||
tooltip={<>
|
||||
If this is set, text effects will not be displayed. This can help if text is difficult to read
|
||||
in certain areas.
|
||||
</>} />
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<FormControlLabel
|
||||
control={<Switch checked={useIEC60027_2} onChange={handleUseIEC60027_2Change} />}
|
||||
label={
|
||||
<Tooltip title={<Typography>If this is set all references to memory will use GiB instead of GB, in accordance with IEC 60027-2.</Typography>}>
|
||||
<Typography>Use GiB instead of GB</Typography>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
<OptionSwitch checked={Settings.DisableOverviewProgressBars}
|
||||
onChange={(newValue) => Settings.DisableOverviewProgressBars = newValue}
|
||||
text="Disable Overview Progress Bars"
|
||||
tooltip={<>
|
||||
If this is set, the progress bars in the character overview will be hidden.
|
||||
</>} />
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<OptionSwitch checked={Settings.EnableBashHotkeys}
|
||||
onChange={(newValue) => Settings.EnableBashHotkeys = newValue}
|
||||
text="Enable bash hotkeys"
|
||||
tooltip={<>
|
||||
Improved Bash emulation mode. Setting this to 1 enables several new Terminal shortcuts and
|
||||
features that more closely resemble a real Bash-style shell. Note that when this mode is
|
||||
enabled, the default browser shortcuts are overriden by the new Bash shortcuts.
|
||||
</>} />
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<OptionSwitch checked={Settings.UseIEC60027_2}
|
||||
onChange={(newValue) => Settings.UseIEC60027_2 = newValue}
|
||||
text="Use GiB instead of GB"
|
||||
tooltip={<>
|
||||
If this is set all references to memory will use GiB instead of GB, in accordance with IEC 60027-2.
|
||||
</>} />
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
<Tooltip
|
||||
@ -620,16 +459,12 @@ export function GameOptionsRoot(props: IProps): React.ReactElement {
|
||||
</ListItem>
|
||||
|
||||
<ListItem>
|
||||
<FormControlLabel
|
||||
control={<Switch checked={saveGameOnFileSave} onChange={handleSaveGameOnFile} />}
|
||||
label={
|
||||
<Tooltip
|
||||
title={<Typography>Save your game any time a file is saved in the script editor.</Typography>}
|
||||
>
|
||||
<Typography>Save game on file save</Typography>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
<OptionSwitch checked={Settings.SaveGameOnFileSave}
|
||||
onChange={(newValue) => Settings.SaveGameOnFileSave = newValue}
|
||||
text="Save game on file save"
|
||||
tooltip={<>
|
||||
Save your game any time a file is saved in the script editor.
|
||||
</>} />
|
||||
</ListItem>
|
||||
|
||||
<ListItem>
|
||||
|
30
src/ui/React/OptionSwitch.tsx
Normal file
30
src/ui/React/OptionSwitch.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import { FormControlLabel, Switch, Tooltip, Typography } from "@mui/material";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
interface IProps {
|
||||
checked: boolean;
|
||||
onChange: (newValue: boolean, error?: string) => void;
|
||||
text: React.ReactNode;
|
||||
tooltip: React.ReactNode;
|
||||
}
|
||||
|
||||
export function OptionSwitch({ checked, onChange, text, tooltip }: IProps): React.ReactElement {
|
||||
const [value, setValue] = useState(checked);
|
||||
|
||||
function handleSwitchChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setValue(event.target.checked);
|
||||
}
|
||||
|
||||
useEffect(() => onChange(value), [value]);
|
||||
|
||||
return (
|
||||
<FormControlLabel
|
||||
control={<Switch checked={value} onChange={handleSwitchChange} />}
|
||||
label={
|
||||
<Tooltip title={<Typography>{tooltip}</Typography>}>
|
||||
<Typography>{text}</Typography>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
@ -1,21 +1,19 @@
|
||||
import React, { useState } from "react";
|
||||
|
||||
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 Fab from "@mui/material/Fab";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
|
||||
import VisibilityIcon from "@mui/icons-material/Visibility";
|
||||
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({
|
||||
visibilityToggle: {
|
||||
backgroundColor: "transparent",
|
||||
position: "absolute",
|
||||
top: "100%",
|
||||
right: 0,
|
||||
},
|
||||
overviewContainer: {
|
||||
position: "fixed",
|
||||
top: 0,
|
||||
@ -25,31 +23,116 @@ const useStyles = makeStyles({
|
||||
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 function Overview({ children }: IProps): React.ReactElement {
|
||||
const [open, setOpen] = useState(true);
|
||||
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 <></>;
|
||||
let icon;
|
||||
if (open) {
|
||||
icon = <VisibilityOffIcon color="primary" />;
|
||||
} else {
|
||||
icon = <VisibilityIcon color="primary" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper square classes={{ root: classes.overviewContainer }}>
|
||||
<Collapse in={open}>{children}</Collapse>
|
||||
<Fab size="small" classes={{ root: classes.visibilityToggle }} onClick={() => setOpen((old) => !old)}>
|
||||
{icon}
|
||||
</Fab>
|
||||
</Paper>
|
||||
<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 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>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user