Skip to content

Commit cafd43e

Browse files
authored
UI: Theming & Layout change (#878)
* UI: Theming & Layout change * Enhance UI with color scheme management and layout adjustments - Introduced a global state management for color scheme and width mode using Zustand. - Updated App component to wrap the RouterProvider with AppTheme and CssBaseline for consistent theming. - Refactored ColorSchemeSelector to utilize the new state management and removed props for cleaner integration. - Simplified Header and SideMenu components to directly access the global state. - Removed redundant local state management from Root component, streamlining the overall structure.
1 parent 3d14959 commit cafd43e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1833
-715
lines changed

rcongui/src/App.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import siteConfig from './config/siteConfig';
1010
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
1111
import { queryClient } from './queryClient';
1212
import {StrictMode} from "react";
13+
import AppTheme from "@/themes/AppTheme";
14+
import { CssBaseline } from '@mui/material';
15+
import { useAppStore } from "@/hooks/useAppState";
1316

1417
const App = () => {
1518
// Dayjs plugins
@@ -26,11 +29,16 @@ const App = () => {
2629
driver: [localforage.INDEXEDDB, localforage.LOCALSTORAGE], // Preferred storage drivers in order
2730
});
2831

32+
const colorScheme = useAppStore((state) => state.colorScheme);
33+
2934
return (
3035
<StrictMode>
3136
<QueryClientProvider client={queryClient}>
32-
<RouterProvider router={adminRouter} />
33-
<ReactQueryDevtools initialIsOpen={false} />
37+
<AppTheme selectedScheme={colorScheme}>
38+
<CssBaseline enableColorScheme />
39+
<RouterProvider router={adminRouter} />
40+
<ReactQueryDevtools initialIsOpen={false} />
41+
</AppTheme>
3442
</QueryClientProvider>
3543
</StrictMode>
3644
);

rcongui/src/components/Header/server-status.jsx

+48-34
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,82 @@
11
import { useEffect } from "react";
2-
import { Typography } from "@mui/material";
2+
import { Skeleton, Stack, Typography } from "@mui/material";
33
import { styled } from "@mui/material/styles";
44
import { useQuery } from "@tanstack/react-query";
55
import { gameQueryOptions } from "@/queries/game-query";
66
import dayjs from "dayjs";
77

88
const Wrapper = styled("div")(({ theme }) => ({
9-
paddingLeft: theme.spacing(1),
109
color: theme.palette.text.primary,
11-
overflowX: "hidden",
1210
textOverflow: "ellipsis",
1311
textWrap: "nowrap",
12+
textAlign: "left",
13+
paddingLeft: theme.spacing(1),
14+
paddingRight: theme.spacing(1),
15+
[theme.breakpoints.up("lg")]: {
16+
textAlign: "center",
17+
padding: theme.spacing(1),
18+
textWrap: "wrap",
19+
},
1420
}));
1521

16-
const MenuBox = styled("div")(({ theme }) => ({
17-
display: "flex",
18-
gap: theme.spacing(1),
19-
}));
20-
21-
const ServerStatus = () => {
22+
const ServerStatus = ({ compact }) => {
2223
const { data: status, isLoading } = useQuery({
2324
...gameQueryOptions.publicState(),
24-
placeholderData: {
25-
name: {
26-
name: "Loading...",
27-
short_name: "Loading..."
28-
},
29-
current_map: {
30-
pretty_name: "Unknown Map"
31-
},
32-
raw_time_remaining: "0:00:00",
33-
num_allied_players: 0,
34-
num_axis_players: 0,
35-
allied_score: 0,
36-
axis_score: 0,
37-
}
38-
})
25+
});
3926

40-
const name = isLoading ? "Loading..." : status?.name?.name ?? "Something went wrong";
27+
const name = status?.name?.short_name ?? "<Server Name>";
4128
const numCurrentPlayers = status?.player_count ?? 0;
4229
const maxPlayers = status?.max_player_count ?? 100;
4330
const mapName = status?.current_map?.map?.pretty_name ?? "Unknown Map";
44-
const timeRemaining = dayjs.duration(status?.time_remaining ?? 0, "seconds").format("HH:mm:ss");
31+
const timeRemaining = dayjs
32+
.duration(status?.time_remaining ?? 0, "seconds")
33+
.format("HH:mm:ss");
4534
const balance = `${status?.player_count_by_team?.allied ?? 0}vs${
4635
status?.player_count_by_team?.axis ?? 0
4736
}`;
4837
const score = `${status?.score?.allied ?? 0}:${status?.score?.axis ?? 0}`;
4938

5039
useEffect(() => {
51-
document.title = `${timeRemaining} | ${mapName} (${numCurrentPlayers}) | ${status?.name?.name ?? "<Server Name>"}`;
40+
document.title = `${
41+
status?.name?.short_name ?? "<Server Name>"
42+
} | ${timeRemaining} | ${mapName} (${numCurrentPlayers})`;
5243
}, [status]);
5344

54-
return (
55-
<Wrapper>
56-
<MenuBox>
57-
<Typography variant="subtitle2">
58-
{name}
59-
</Typography>
60-
</MenuBox>
45+
if (isLoading) {
46+
return (
47+
<Wrapper>
48+
<Stack
49+
sx={{
50+
alignItems: { xs: "flex-start", lg: "center" },
51+
gap: { xs: 0, lg: 0.5 },
52+
}}
53+
>
54+
<Skeleton variant="text" width={100} height={16} />
55+
<Skeleton variant="text" width={compact ? 160 : 60} height={16} />
56+
{!compact && <Skeleton variant="text" width={60} height={16} />}
57+
</Stack>
58+
</Wrapper>
59+
);
60+
}
61+
62+
return compact ? (
63+
<Wrapper sx={{ textAlign: "left" }}>
64+
<Typography variant="subtitle2">{name}</Typography>
6165
<Typography variant="caption">
6266
{numCurrentPlayers}/{maxPlayers} ({balance}) - {mapName} -{" "}
6367
{timeRemaining} - {score}
6468
</Typography>
6569
</Wrapper>
70+
) : (
71+
<Wrapper>
72+
<Typography variant="subtitle2">{name}</Typography>
73+
<Typography variant="caption">
74+
{numCurrentPlayers}/{maxPlayers} ({balance}) - {timeRemaining}
75+
</Typography>
76+
<Typography component={"div"} variant="caption">
77+
{score} - {mapName}
78+
</Typography>
79+
</Wrapper>
6680
);
6781
};
6882

rcongui/src/components/NoRowsOverlay.jsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { Box } from '@mui/material';
2-
import { styled } from '@mui/system';
1+
import { Box } from "@mui/material";
2+
import { styled } from "@mui/material/styles";
33

44
const StyledGridOverlay = styled('div')(({ theme }) => ({
55
display: 'flex',

rcongui/src/components/PlayerProfileDrawer/index.jsx

+1-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {
33
IconButton,
44
Typography,
55
Drawer,
6-
Toolbar,
76
Divider,
87
Avatar,
98
Tabs,
@@ -25,7 +24,7 @@ import {
2524
} from "@mui/material";
2625
import { Close } from "@mui/icons-material";
2726
import { TabContext, TabPanel } from "@mui/lab";
28-
import { styled, useMediaQuery } from "@mui/system";
27+
import { styled } from "@mui/material/styles";
2928
import { ActionMenu } from "@/features/player-action/ActionMenu";
3029
import dayjs from "dayjs";
3130
import StarIcon from "@mui/icons-material/Star";
@@ -714,7 +713,6 @@ export const PlayerDetailDrawer = () => {
714713
anchor="right"
715714
onClose={close}
716715
>
717-
<Toolbar />
718716
{isLoading && !player ? (
719717
<LoadingSkeleton onClose={close} />
720718
) : profileError ? (

rcongui/src/components/layout/AppNavbar.js

+12-5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import SideMenuMobile from "./SideMenuMobile";
88
import MenuButton from "./MenuButton";
99
import NavbarBreadcrumbs from "./NavbarBreadcrumbs";
1010
import {useState} from "react";
11+
import { useMediaQuery, useTheme } from "@mui/system";
12+
import ServerStatus from "../Header/server-status";
13+
import { Box } from "@mui/material";
1114

1215
const Toolbar = styled(MuiToolbar)({
1316
width: "100%",
@@ -25,7 +28,10 @@ const Toolbar = styled(MuiToolbar)({
2528
},
2629
});
2730

28-
const AppNavbarBase = ({ toggleDrawer }) => {
31+
const AppNavbarBase = ({ toggleDrawer, open }) => {
32+
const theme = useTheme();
33+
const isSmallScreen = useMediaQuery(theme.breakpoints.down("lg"));
34+
2935
return (
3036
<AppBar
3137
position="fixed"
@@ -36,14 +42,12 @@ const AppNavbarBase = ({ toggleDrawer }) => {
3642
backgroundImage: "none",
3743
borderBottom: "1px solid",
3844
borderColor: "divider",
39-
top: "56px",
4045
}}
4146
>
4247
<Toolbar variant="regular">
4348
<Stack
4449
direction="row"
4550
sx={{
46-
justifyContent: "space-between",
4751
alignItems: "center",
4852
flexGrow: 1,
4953
width: "100%",
@@ -52,7 +56,10 @@ const AppNavbarBase = ({ toggleDrawer }) => {
5256
<MenuButton aria-label="menu" onClick={toggleDrawer(true)}>
5357
<MenuRoundedIcon />
5458
</MenuButton>
55-
<Stack direction="row" spacing={1} sx={{ justifyContent: "center" }}>
59+
<Box sx={{ overflow: "hidden" }}>
60+
{isSmallScreen && <ServerStatus compact={isSmallScreen || !open} />}
61+
</Box>
62+
<Stack direction="row" spacing={1} sx={{ justifyContent: "end", flexShrink: 0, flexGrow: 1, pl: 1 }}>
5663
<NavbarBreadcrumbs />
5764
</Stack>
5865
</Stack>
@@ -70,7 +77,7 @@ export default function AppNavbar() {
7077

7178
return (
7279
<>
73-
<AppNavbarBase toggleDrawer={toggleDrawer} />
80+
<AppNavbarBase toggleDrawer={toggleDrawer} open={open} />
7481
<SideMenuMobile open={open} toggleDrawer={toggleDrawer} />
7582
</>
7683
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import * as React from 'react';
2+
import DarkModeIcon from '@mui/icons-material/DarkModeRounded';
3+
import LightModeIcon from '@mui/icons-material/LightModeRounded';
4+
import Box from '@mui/material/Box';
5+
import IconButton from '@mui/material/IconButton';
6+
import Menu from '@mui/material/Menu';
7+
import MenuItem from '@mui/material/MenuItem';
8+
import { useColorScheme } from '@mui/material/styles';
9+
10+
export default function ColorModeIconDropdown(props) {
11+
const { mode, systemMode, setMode } = useColorScheme();
12+
const [anchorEl, setAnchorEl] = React.useState(null);
13+
const open = Boolean(anchorEl);
14+
const handleClick = (event) => {
15+
setAnchorEl(event.currentTarget);
16+
};
17+
const handleClose = () => {
18+
setAnchorEl(null);
19+
};
20+
const handleMode = (targetMode) => () => {
21+
setMode(targetMode);
22+
handleClose();
23+
};
24+
if (!mode) {
25+
return (
26+
<Box
27+
data-screenshot="toggle-mode"
28+
sx={(theme) => ({
29+
verticalAlign: 'bottom',
30+
display: 'inline-flex',
31+
width: '2.25rem',
32+
height: '2.25rem',
33+
borderRadius: (theme.vars || theme).shape.borderRadius,
34+
border: '1px solid',
35+
borderColor: (theme.vars || theme).palette.divider,
36+
})}
37+
/>
38+
);
39+
}
40+
const resolvedMode = systemMode || mode;
41+
const icon = {
42+
light: <LightModeIcon />,
43+
dark: <DarkModeIcon />,
44+
}[resolvedMode];
45+
return (
46+
<React.Fragment>
47+
<IconButton
48+
data-screenshot="toggle-mode"
49+
onClick={handleClick}
50+
disableRipple
51+
size="small"
52+
aria-controls={open ? 'color-scheme-menu' : undefined}
53+
aria-haspopup="true"
54+
aria-expanded={open ? 'true' : undefined}
55+
{...props}
56+
>
57+
{icon}
58+
</IconButton>
59+
<Menu
60+
anchorEl={anchorEl}
61+
id="account-menu"
62+
open={open}
63+
onClose={handleClose}
64+
onClick={handleClose}
65+
slotProps={{
66+
paper: {
67+
variant: 'outlined',
68+
elevation: 0,
69+
sx: {
70+
my: '4px',
71+
},
72+
},
73+
}}
74+
transformOrigin={{ horizontal: 'right', vertical: 'top' }}
75+
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
76+
>
77+
<MenuItem selected={mode === 'system'} onClick={handleMode('system')}>
78+
System
79+
</MenuItem>
80+
<MenuItem selected={mode === 'light'} onClick={handleMode('light')}>
81+
Light
82+
</MenuItem>
83+
<MenuItem selected={mode === 'dark'} onClick={handleMode('dark')}>
84+
Dark
85+
</MenuItem>
86+
</Menu>
87+
</React.Fragment>
88+
);
89+
}

0 commit comments

Comments
 (0)