Change interfaces to types, adjust theme context

This commit is contained in:
Corey 2024-05-07 00:05:37 +00:00
parent 2d1a162100
commit 5d041d5e77
14 changed files with 98 additions and 87 deletions

View File

@ -4,8 +4,8 @@ import { BrowserRouter, Route, Routes } from 'react-router-dom';
import AlertComponent from './components/Alert';
import Footer from './components/Footer';
import Header from './components/Header';
import SearchState, { SearchStateDefaults } from './data/SearchState';
import ServerData from './data/ServerData';
import { SearchState, SearchStateDefaults } from './data/SearchState';
import { ServerData } from './data/ServerData';
import About from './pages/About';
import Home from './pages/Home';
import NotFound from './pages/NotFound';

View File

@ -12,7 +12,7 @@ import {
import { useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { postData } from '../apiUtil';
import ServerData from '../data/ServerData';
import { ServerData } from '../data/ServerData';
import { AlertResponse } from './Alert';
const AddServerInput = styled('div')(({ theme }) => ({

View File

@ -1,5 +1,5 @@
import { ToggleButton, ToggleButtonGroup } from '@mui/material';
import ServerData from '../data/ServerData';
import { ServerData } from '../data/ServerData';
const expansions = [
<ToggleButton

View File

@ -1,17 +1,15 @@
import { Brightness4, Brightness7, QuestionMark } from '@mui/icons-material';
import { AppBar, Box, Toolbar, Tooltip, useTheme } from '@mui/material';
import { AppBar, Box, Toolbar, Tooltip } from '@mui/material';
import IconButton from '@mui/material/IconButton';
import { useContext } from 'react';
import { Link } from 'react-router-dom';
import { ThemeContext } from '../context/ThemeContext';
import { ThemeMode, useThemeModeContext } from '../context/ThemeContext';
function scrollToTop() {
window.scrollTo({ top: 0, behavior: 'smooth' }); // Scrolls to the top of the page smoothly
}
export default function Footer() {
const theme = useTheme();
const { switchColorMode } = useContext(ThemeContext);
const { themeMode, setThemeMode } = useThemeModeContext();
return (
<Box className="bottom-0 w-full flex-none">
@ -42,12 +40,14 @@ export default function Footer() {
</IconButton>
</Tooltip>
<Tooltip title="Toggle Theme" arrow disableInteractive>
<IconButton onClick={switchColorMode}>
{theme.palette.mode === 'dark' ? (
<Brightness7 />
) : (
<Brightness4 />
)}
<IconButton
onClick={() => {
setThemeMode((prev: ThemeMode) =>
prev === 'light' ? 'dark' : 'light'
);
}}
>
{themeMode === 'dark' ? <Brightness7 /> : <Brightness4 />}
</IconButton>
</Tooltip>
</Box>

View File

@ -11,8 +11,8 @@ import IconButton from '@mui/material/IconButton';
import { useCallback, useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import { fetchDataFromBackend } from '../apiUtil';
import SearchState from '../data/SearchState';
import ServerData from '../data/ServerData';
import { SearchState } from '../data/SearchState';
import { ServerData } from '../data/ServerData';
import AddServer from './AddServer';
import { AlertResponse } from './Alert';
import SearchServers from './SearchServers';

View File

@ -10,7 +10,7 @@ import {
Typography,
} from '@mui/material';
import { useEffect, useRef, useState } from 'react';
import SearchState from '../data/SearchState';
import { SearchState } from '../data/SearchState';
export default function SearchServers({
showSearchServer,

View File

@ -22,7 +22,8 @@ import {
} from '@mui/material';
import { useState } from 'react';
import { Link } from 'react-router-dom';
import ServerData, {
import {
ServerData,
ServerSetting,
ServerSettingsInfo,
} from '../data/ServerData';

View File

@ -3,7 +3,7 @@ import { Card, IconButton, Tooltip, Typography } from '@mui/material';
import Modal from '@mui/material/Modal';
import { useState } from 'react';
import { fetchDataFromBackend } from '../apiUtil';
import ServerData from '../data/ServerData';
import { ServerData } from '../data/ServerData';
import ServerSettingsDataGrid from './ServerSettingsDataGrid';
export default function ServerDetailsModal({ id }: { id: number }) {

View File

@ -6,14 +6,14 @@ import {
ServerSettingsInfo,
} from '../data/ServerData';
interface KeyValueRow {
type KeyValueRow = {
id: number;
key: string;
rawValue: string | number | boolean;
name: string;
value: string | number | boolean;
description: string;
}
};
const columns: GridColDef[] = [
{ field: 'key', headerName: 'Key', align: 'right', flex: 4 },

View File

@ -1,44 +1,47 @@
import { useMediaQuery } from '@mui/material';
import { createTheme, StyledEngineProvider } from '@mui/material/styles';
import ThemeProvider from '@mui/material/styles/ThemeProvider';
import { createContext, ReactNode, useEffect, useMemo, useState } from 'react';
import React, {
createContext,
ReactNode,
useContext,
useEffect,
useState,
} from 'react';
type ThemeContextType = {
switchColorMode: () => void;
export type ThemeMode = 'dark' | 'light';
type ThemeModeState = {
themeMode: ThemeMode;
setThemeMode: React.Dispatch<React.SetStateAction<ThemeMode>>;
};
type ThemeProviderProps = {
export const ThemeModeContext = createContext<ThemeModeState | null>(null);
type ThemeModeContextProviderProps = {
children: ReactNode;
};
export const ThemeContext = createContext<ThemeContextType>({
switchColorMode: () => {},
});
export function ThemeContextProvider({ children }: ThemeProviderProps) {
export function ThemeContextProvider({
children,
}: ThemeModeContextProviderProps) {
const rootElement = document.getElementById('root');
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
const [mode, setMode] = useState<'light' | 'dark'>(() => {
const manualTheme = localStorage.getItem('theme');
if (manualTheme && (manualTheme === 'light' || manualTheme === 'dark')) {
return manualTheme;
const [themeMode, setThemeMode] = useState<ThemeMode>(() => {
const manualThemeMode = localStorage.getItem('themeMode') as ThemeMode;
if (manualThemeMode) {
return manualThemeMode;
}
return prefersDarkMode ? 'dark' : 'light';
});
useEffect(() => {
localStorage.setItem('theme', mode);
}, [mode]);
localStorage.setItem('themeMode', themeMode as ThemeMode);
}, [themeMode]);
const switchColorMode = () => {
setMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light'));
};
const theme = useMemo(
() =>
createTheme({
const muiTheme = createTheme({
palette: {
mode,
mode: themeMode,
},
// All `Portal`-related components need to have the the main app wrapper element as a container
// so that they are in the subtree under the element used in the `important` option of the Tailwind's config.
@ -64,20 +67,27 @@ export function ThemeContextProvider({ children }: ThemeProviderProps) {
},
},
},
}),
[rootElement, mode]
);
});
return (
<StyledEngineProvider injectFirst>
<ThemeContext.Provider
<ThemeModeContext.Provider
value={
// eslint-disable-next-line react/jsx-no-constructed-context-values
{ switchColorMode }
{ themeMode, setThemeMode }
}
>
<ThemeProvider theme={theme}>{children}</ThemeProvider>
</ThemeContext.Provider>
<ThemeProvider theme={muiTheme}>{children}</ThemeProvider>
</ThemeModeContext.Provider>
</StyledEngineProvider>
);
}
// eslint-disable-next-line react-refresh/only-export-components
export function useThemeModeContext() {
const context = useContext(ThemeModeContext);
if (!context) {
throw new Error('Theme context error.');
}
return context;
}

View File

@ -1,11 +1,11 @@
export default interface SearchState {
export type SearchState = {
name: string;
multibox: string[] | null;
trusts: string[] | null;
levelSync: string[] | null;
maxLevel: number[];
expansions: string[] | null;
}
};
export const SearchStateDefaults: SearchState = {
name: '',

View File

@ -1,17 +1,17 @@
import LsbDefaults from '../../../api/defaultLsbSettings.json';
export interface ServerSetting {
export type ServerSetting = {
name: string;
description: string;
unit?: string;
transform?: (arg: boolean | string | number) => boolean | string | number;
}
};
export interface ServerSettings {
export type ServerSettings = {
[key: string]: boolean | string | number;
}
};
export default interface ServerData {
export type ServerData = {
name: string;
url: string;
location: string;
@ -22,7 +22,7 @@ export default interface ServerData {
active_sessions: number;
updated: string;
up: boolean;
}
};
export const DemoServerData: ServerData = {
name: 'LandSandBoat Demo',

View File

@ -1,8 +1,8 @@
import { Box } from '@mui/material';
import ErrorCard from '../components/ErrorCard';
import ServerCard from '../components/ServerCard';
import SearchState from '../data/SearchState';
import ServerData from '../data/ServerData';
import { SearchState } from '../data/SearchState';
import { ServerData } from '../data/ServerData';
export default function Home({
servers,

View File

@ -22,7 +22,7 @@ import { AlertResponse } from '../components/Alert';
import ErrorCard from '../components/ErrorCard';
import ExpansionBar from '../components/ExpansionsBar';
import ServerSettingsDataGrid from '../components/ServerSettingsDataGrid';
import ServerData from '../data/ServerData';
import { ServerData } from '../data/ServerData';
import CopyImageIcon from '../images/copy-image.png';
export default function ServerDetails({