1
1
import * as React from 'react' ;
2
2
import { Link as RouterLink , Route , Routes } from 'react-router-dom' ;
3
3
4
- import { styled } from '@mui/material/styles' ;
4
+ import { createTheme , styled , ThemeProvider } from '@mui/material/styles' ;
5
5
import Link from '@mui/material/Link' ;
6
6
import MuiDrawer from '@mui/material/Drawer' ;
7
7
import Box from '@mui/material/Box' ;
@@ -37,6 +37,19 @@ import ReadTag from './pages/tags/Read';
37
37
import ReadUser from './pages/users/Read' ;
38
38
import { useCurrentUser } from './authentication' ;
39
39
import ReadRequest from './pages/requests/Read' ;
40
+ import {
41
+ alpha ,
42
+ CssBaseline ,
43
+ PaletteMode ,
44
+ Stack ,
45
+ ToggleButton ,
46
+ ToggleButtonGroup ,
47
+ Tooltip ,
48
+ useMediaQuery ,
49
+ useTheme ,
50
+ } from '@mui/material' ;
51
+ import { DarkMode , LightMode , Monitor } from '@mui/icons-material' ;
52
+ import { lightGreen , red , yellow } from '@mui/material/colors' ;
40
53
41
54
const drawerWidth : number = 240 ;
42
55
@@ -88,7 +101,65 @@ const Drawer = styled(MuiDrawer, {
88
101
} ,
89
102
} ) ) ;
90
103
91
- function Dashboard ( ) {
104
+ function ThemeToggle ( { setThemeMode, condensed} : { setThemeMode : ( theme : PaletteMode ) => void ; condensed : boolean } ) {
105
+ const [ storedTheme , setStoredTheme ] = React . useState (
106
+ localStorage . getItem ( 'user-set-color-scheme' ) as 'light' | 'dark' | null ,
107
+ ) ;
108
+ const currentTheme = useTheme ( ) ;
109
+ const systemTheme = useMediaQuery ( '(prefers-color-scheme: dark)' ) ? 'dark' : 'light' ;
110
+
111
+ const handleThemeOverride = ( theme : PaletteMode ) => {
112
+ setThemeMode ( theme ) ;
113
+ localStorage . setItem ( 'user-set-color-scheme' , theme ) ;
114
+ setStoredTheme ( theme ) ;
115
+ } ;
116
+
117
+ const handleSystemDefault = ( ) => {
118
+ setThemeMode ( systemTheme ) ;
119
+ localStorage . removeItem ( 'user-set-color-scheme' ) ;
120
+ setStoredTheme ( null ) ;
121
+ } ;
122
+
123
+ return (
124
+ < ToggleButtonGroup size = "small" >
125
+ { ( currentTheme . palette . mode != 'light' || ! condensed ) && (
126
+ < Tooltip title = "Light Mode" >
127
+ < ToggleButton
128
+ value = "left"
129
+ selected = { storedTheme === 'light' }
130
+ onClick = { ( ) => handleThemeOverride ( 'light' ) }
131
+ aria-label = "Light mode" >
132
+ < LightMode />
133
+ </ ToggleButton >
134
+ </ Tooltip >
135
+ ) }
136
+ { ! condensed && (
137
+ < Tooltip title = "System Default" >
138
+ < ToggleButton
139
+ value = "center"
140
+ selected = { storedTheme == null }
141
+ onClick = { handleSystemDefault }
142
+ aria-label = "System Default" >
143
+ < Monitor />
144
+ </ ToggleButton >
145
+ </ Tooltip >
146
+ ) }
147
+ { ( currentTheme . palette . mode != 'dark' || ! condensed ) && (
148
+ < Tooltip title = "Dark Mode" >
149
+ < ToggleButton
150
+ value = "right"
151
+ selected = { storedTheme === 'dark' }
152
+ onClick = { ( ) => handleThemeOverride ( 'dark' ) }
153
+ aria-label = "Dark mode" >
154
+ < DarkMode />
155
+ </ ToggleButton >
156
+ </ Tooltip >
157
+ ) }
158
+ </ ToggleButtonGroup >
159
+ ) ;
160
+ }
161
+
162
+ function Dashboard ( { setThemeMode} : { setThemeMode : ( theme : PaletteMode ) => void } ) {
92
163
const [ open , setOpen ] = React . useState ( true ) ;
93
164
const toggleDrawer = ( ) => {
94
165
setOpen ( ! open ) ;
@@ -137,7 +208,7 @@ function Dashboard() {
137
208
textDecoration : 'none' ,
138
209
} } >
139
210
< Avatar src = "/logo-square.png" variant = "square" />
140
- < Typography component = "h1" variant = "h5" sx = { { px : 2 } } >
211
+ < Typography component = "h1" variant = "h5" sx = { { px : 2 } } color = "text.accent" >
141
212
ACCESS
142
213
</ Typography >
143
214
</ Link >
@@ -149,12 +220,15 @@ function Dashboard() {
149
220
< List component = "nav" >
150
221
< NavItems open = { open } />
151
222
</ List >
223
+ < Stack marginTop = "auto" p = { 2 } >
224
+ < ThemeToggle setThemeMode = { setThemeMode } condensed = { ! open } />
225
+ </ Stack >
152
226
</ Drawer >
153
227
< Box
154
228
component = "main"
155
229
sx = { {
156
230
backgroundColor : ( theme ) =>
157
- theme . palette . mode === 'light' ? theme . palette . grey [ 100 ] : theme . palette . grey [ 900 ] ,
231
+ theme . palette . mode === 'light' ? theme . palette . grey [ 200 ] : theme . palette . grey [ 800 ] ,
158
232
flexGrow : 1 ,
159
233
height : '100vh' ,
160
234
overflow : 'auto' ,
@@ -189,6 +263,81 @@ function Dashboard() {
189
263
}
190
264
191
265
export default function App ( ) {
266
+ const storedTheme = localStorage . getItem ( 'user-set-color-scheme' ) as 'light' | 'dark' | null ;
267
+ const systemTheme = useMediaQuery ( '(prefers-color-scheme: dark)' ) ? 'dark' : 'light' ;
268
+ const initialMode = storedTheme ?? systemTheme ;
269
+ const [ mode , setMode ] = React . useState < PaletteMode > ( initialMode ) ;
270
+
271
+ // See https://discord.com/branding
272
+ let theme = React . useMemo ( ( ) => {
273
+ const base = createTheme ( {
274
+ palette : {
275
+ mode,
276
+ primary : {
277
+ main : '#5865F2' ,
278
+ light : '#A5B2FF' ,
279
+ } ,
280
+ secondary : {
281
+ main : '#EB459E' ,
282
+ } ,
283
+ error : {
284
+ main : '#ED4245' ,
285
+ } ,
286
+ warning : {
287
+ main : '#FEE75C' ,
288
+ } ,
289
+ success : {
290
+ main : '#57F287' ,
291
+ } ,
292
+ text : {
293
+ accent : mode === 'light' ? '#5865F2' : '#A5B2FF' ,
294
+ } ,
295
+ } ,
296
+ components : {
297
+ MuiChip : {
298
+ styleOverrides : {
299
+ colorPrimary : ( { ownerState, theme} ) => ( {
300
+ ...( ownerState . variant === 'outlined' &&
301
+ ownerState . color === 'primary' && {
302
+ color : theme . palette . text . accent ,
303
+ borderColor : theme . palette . text . accent ,
304
+ } ) ,
305
+ } ) ,
306
+ deleteIcon : ( { ownerState, theme} ) => ( {
307
+ ...( ownerState . variant === 'outlined' &&
308
+ ownerState . color === 'primary' && {
309
+ color : theme . palette . text . accent ,
310
+ } ) ,
311
+ } ) ,
312
+ } ,
313
+ } ,
314
+ } ,
315
+ } ) ;
316
+ return createTheme ( base , {
317
+ palette : {
318
+ highlight : {
319
+ success : base . palette . augmentColor ( {
320
+ color : { main : mode === 'light' ? lightGreen [ 100 ] : alpha ( lightGreen [ 500 ] , 0.3 ) } ,
321
+ name : 'success' ,
322
+ } ) ,
323
+ warning : base . palette . augmentColor ( {
324
+ color : { main : mode === 'light' ? yellow [ 100 ] : alpha ( yellow [ 500 ] , 0.3 ) } ,
325
+ name : 'warning' ,
326
+ } ) ,
327
+ danger : base . palette . augmentColor ( {
328
+ color : { main : mode === 'light' ? red [ 100 ] : alpha ( red [ 500 ] , 0.3 ) } ,
329
+ name : 'danger' ,
330
+ } ) ,
331
+ } ,
332
+ } ,
333
+ } ) ;
334
+ } , [ mode ] ) ;
335
+
192
336
useCurrentUser ( ) ;
193
- return < Dashboard /> ;
337
+ return (
338
+ < ThemeProvider theme = { theme } >
339
+ < CssBaseline />
340
+ < Dashboard setThemeMode = { setMode } />
341
+ </ ThemeProvider >
342
+ ) ;
194
343
}
0 commit comments