#5 implement-dark-light-theme-toggle

Yhdistetty
stefan.stamenovic yhdistetty 6 committia lähteestä implement-dark-light-theme-toggle kohteeseen master 3 vuotta sitten

+ 57
- 0
src/components/MUI/DialogComponent.js Näytä tiedosto

@@ -0,0 +1,57 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
Dialog,
DialogContent,
DialogTitle,
DialogActions,
Button,
useMediaQuery,
useTheme,
} from '@mui/material';

const DialogComponent = ({
title,
content,
onClose,
open,
maxWidth,
fullWidth,
responsive,
}) => {
const theme = useTheme();
const fullScreen = useMediaQuery(theme.breakpoints.down('md'));

const handleClose = () => {
onClose();
};

return (
<Dialog
maxWidth={maxWidth}
fullWidth={fullWidth}
fullScreen={responsive && fullScreen}
onClose={handleClose}
open={open}
>
<DialogTitle>{title}</DialogTitle>
{content && <DialogContent>{content}</DialogContent>}
<DialogActions>
<Button onClick={handleClose}>OK</Button>
<Button onClick={handleClose}>Cancel</Button>
</DialogActions>
</Dialog>
);
};

DialogComponent.propTypes = {
title: PropTypes.string,
open: PropTypes.bool.isRequired,
content: PropTypes.any,
onClose: PropTypes.func.isRequired,
maxWidth: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl']),
fullWidth: PropTypes.bool,
responsive: PropTypes.bool,
};

export default DialogComponent;

+ 16
- 25
src/components/MUI/DrawerComponent.js Näytä tiedosto

@@ -1,37 +1,28 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
Drawer,
List,
ListItem,
ListItemButton,
ListItemIcon,
ListItemText,
} from '@mui/material';
import { Drawer } from '@mui/material';

const DrawerComponent = ({ open, toggleOpen }) => (
<Drawer anchor="right" open={open} onClose={toggleOpen}>
<List>
<ListItemButton divider onClick={toggleOpen}>
<ListItemIcon>
<ListItemText>Link 1</ListItemText>
</ListItemIcon>
</ListItemButton>
<ListItem divider onClick={toggleOpen}>
<ListItemIcon>
<ListItemText>Link 2</ListItemText>
</ListItemIcon>
</ListItem>
<ListItem divider onClick={toggleOpen}>
<ListItemText>Link 3</ListItemText>
</ListItem>
</List>
const DrawerComponent = ({ open, toggleOpen, content, anchor = 'right' }) => (
<Drawer
sx={{
minWidth: 250,
'& .MuiDrawer-paper': {
minWidth: 250,
},
}}
anchor={anchor}
open={open}
onClose={toggleOpen}
>
{content ? content : null}
</Drawer>
);

DrawerComponent.propTypes = {
open: PropTypes.bool,
toggleOpen: PropTypes.func,
content: PropTypes.any,
anchor: PropTypes.oneOf(['top', 'right', 'left', 'bottom']),
};

export default DrawerComponent;

+ 72
- 0
src/components/MUI/Examples/Modals.js Näytä tiedosto

@@ -0,0 +1,72 @@
import React, { useState } from 'react';
import { Box, Button, Divider, Paper, Typography } from '@mui/material';
import DialogComponent from '../DialogComponent';
import DrawerComponent from '../DrawerComponent';
import PopoverComponent from '../PopoverComponent';

const Modals = () => {
const [dialogOpen, setDialogOpen] = useState(false);
const [drawerOpen, setDrawerOpen] = useState(false);
const [popoverOpen, setPopoverOpen] = useState(false);
const [anchorEl, setAnchorEl] = useState(null);

return (
<Box
sx={{
mt: 4,
ml: 4,
display: 'flex',
flexGrow: 1,
}}
>
<Paper
sx={{
p: 4,
display: 'flex',
flexDirection: 'column',
}}
>
<Typography variant="h4" gutterBottom align="center">
Modals
</Typography>
<Divider />
<Button onClick={() => setDialogOpen(true)}>Open Dialog</Button>
<Button onClick={() => setDrawerOpen(true)}>Open Drawer</Button>
<Button
onClick={(e) => {
setPopoverOpen(true);
setAnchorEl(e.currentTarget);
}}
>
Open Popover
</Button>
</Paper>
<DialogComponent
title="Dialog Title"
content={<Typography>Dialog Content</Typography>}
open={dialogOpen}
onClose={() => setDialogOpen(false)}
maxWidth="md"
fullWidth
responsive
/>
<DrawerComponent
anchor="left"
content={<Typography sx={{ p: 2 }}>Drawer Content</Typography>}
open={drawerOpen}
toggleOpen={() => setDrawerOpen(!drawerOpen)}
/>
<PopoverComponent
anchorEl={anchorEl}
open={popoverOpen}
onClose={() => {
setPopoverOpen(false);
setAnchorEl(null);
}}
content={<Typography sx={{ p: 2 }}>Popover Content</Typography>}
/>
</Box>
);
};

export default Modals;

+ 117
- 63
src/components/MUI/Navbar.js Näytä tiedosto

@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useMemo, useContext } from 'react';
import {
AppBar,
Badge,
@@ -6,100 +6,154 @@ import {
IconButton,
Toolbar,
Typography,
List,
ListItem,
ListItemButton,
ListItemIcon,
ListItemText,
useMediaQuery,
} from '@mui/material';
import { useTheme } from '@mui/system';
import MenuOutlinedIcon from '@mui/icons-material/MenuOutlined';
import ShoppingBasketIcon from '@mui/icons-material/ShoppingBasket';
import Brightness4Icon from '@mui/icons-material/Brightness4';
import Brightness7Icon from '@mui/icons-material/Brightness7';
import MenuList from './MenuList';
import Drawer from './DrawerComponent';
import { ColorModeContext } from '../../context/ColorModeContext';

const Navbar = () => {
const [openDrawer, setOpenDrawer] = useState(false);
const theme = useTheme();
const matches = useMediaQuery(theme.breakpoints.down('sm'));
const toggleColorMode = useContext(ColorModeContext);

const handleToggleDrawer = () => {
setOpenDrawer(!openDrawer);
};

const drawerContent = useMemo(
() => (
<List>
<ListItemButton divider onClick={handleToggleDrawer}>
<ListItemIcon>
<ListItemText>Link 1</ListItemText>
</ListItemIcon>
</ListItemButton>
<ListItem divider onClick={handleToggleDrawer}>
<ListItemIcon>
<ListItemText>Link 2</ListItemText>
</ListItemIcon>
</ListItem>
<ListItem divider onClick={handleToggleDrawer}>
<ListItemText>Link 3</ListItemText>
</ListItem>
<ListItem divider>
<IconButton onClick={toggleColorMode}>
<ListItemText>Toggle {theme.palette.mode} mode</ListItemText>
{theme.palette.mode === 'dark' ? (
<Brightness7Icon />
) : (
<Brightness4Icon />
)}
</IconButton>
</ListItem>
</List>
),
[handleToggleDrawer]
);

return (
<>
<AppBar elevation={2} sx={{ backgroundColor: 'background.default' }}>
<Toolbar>
<AppBar
elevation={2}
sx={{ backgroundColor: 'background.default', position: 'relative' }}
>
<Toolbar>
<Box
component="div"
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
width: '100%',
}}
>
{matches ? (
<Drawer
open={openDrawer}
toggleOpen={handleToggleDrawer}
content={drawerContent}
/>
) : (
<Box sx={{ display: 'flex' }}>
<Typography
variant="h6"
sx={{
marginRight: 3,
cursor: 'pointer',
color: 'text.primary',
}}
>
Link 1
</Typography>
<Typography
variant="body1"
sx={{
marginRight: 3,
cursor: 'pointer',
color: 'text.primary',
}}
>
Link 2
</Typography>
<Typography
variant="subtitle1"
sx={{
marginRight: 3,
cursor: 'pointer',
color: 'text.primary',
}}
>
Link 3
</Typography>
</Box>
)}
<Box>
<MenuList />
</Box>
<Box
component="div"
sx={{
display: 'flex',
justifyContent: 'space-between',
justifyContent: 'center',
alignItems: 'center',
width: '100%',
}}
>
{matches ? (
<Drawer open={openDrawer} toggleOpen={handleToggleDrawer} />
) : (
<Box sx={{ display: 'flex' }}>
<Typography
variant="h6"
sx={{
marginRight: 3,
cursor: 'pointer',
color: 'text.primary',
}}
>
Link 1
</Typography>
<Typography
variant="body1"
sx={{
marginRight: 3,
cursor: 'pointer',
color: 'text.primary',
}}
>
Link 2
</Typography>
<Typography
variant="subtitle1"
sx={{
marginRight: 3,
cursor: 'pointer',
color: 'text.primary',
}}
>
Link 3
</Typography>
<Box>
<IconButton onClick={handleToggleDrawer}>
<MenuOutlinedIcon />
</IconButton>
</Box>
)}
<Box>
<MenuList />
</Box>
<Box
sx={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
>
{matches ? (
<Box>
<IconButton onClick={handleToggleDrawer}>
<MenuOutlinedIcon />
</IconButton>
</Box>
) : (
) : (
<Box>
<IconButton>
<Badge badgeContent={3} color="primary">
<ShoppingBasketIcon color="action" />
</Badge>
</IconButton>
)}
</Box>
<IconButton sx={{ ml: 1 }} onClick={toggleColorMode}>
{theme.palette.mode === 'dark' ? (
<Brightness7Icon />
) : (
<Brightness4Icon />
)}
</IconButton>
</Box>
)}
</Box>
</Toolbar>
</AppBar>
</>
</Box>
</Toolbar>
</AppBar>
);
};


+ 35
- 0
src/components/MUI/PopoverComponent.js Näytä tiedosto

@@ -0,0 +1,35 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Box, Popover } from '@mui/material';

const PopoverComponent = ({ open, anchorEl, onClose, content }) => {
const handleClose = () => {
onClose();
};

return (
<Box component="div">
<Popover
sx={{ p: 5 }}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
>
{content}
</Popover>
</Box>
);
};

PopoverComponent.propTypes = {
anchorEl: PropTypes.object,
open: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
content: PropTypes.any,
};

export default PopoverComponent;

+ 21
- 0
src/context/ColorModeContext.js Näytä tiedosto

@@ -0,0 +1,21 @@
import React, { createContext } from 'react';
import PropTypes from 'prop-types';
import { ThemeProvider } from '@mui/material/styles';
import useToggleColorMode from '../hooks/useToggleColorMode';

export const ColorModeContext = createContext();

const ColorModeProvider = ({ children }) => {
const [toggleColorMode, theme] = useToggleColorMode();
return (
<ColorModeContext.Provider value={toggleColorMode}>
<ThemeProvider theme={theme}>{children}</ThemeProvider>
</ColorModeContext.Provider>
);
};

ColorModeProvider.propTypes = {
children: PropTypes.node,
};

export default ColorModeProvider;

+ 31
- 0
src/hooks/useToggleColorMode.js Näytä tiedosto

@@ -0,0 +1,31 @@
import { useState, useMemo } from 'react';
import { createTheme } from '@mui/material/styles';
import {
authScopeSetHelper,
authScopeStringGetHelper,
} from '../util/helpers/authScopeHelpers';

const useToggleColorMode = () => {
const currentColorMode = authScopeStringGetHelper('colorMode') || 'light';
const [mode, setMode] = useState(currentColorMode);

const toggleColorMode = () => {
const nextMode = mode === 'light' ? 'dark' : 'light';
setMode(nextMode);
authScopeSetHelper('colorMode', nextMode);
};

const theme = useMemo(
() =>
createTheme({
palette: {
mode,
},
}),
[mode]
);

return [toggleColorMode, theme];
};

export default useToggleColorMode;

+ 1
- 1
src/i18n/index.js Näytä tiedosto

@@ -7,7 +7,7 @@ import enTranslations from './resources/en';
i18n.use(initReactI18next).init({
lng: 'en',
fallbackLng: 'en',
debug: true,
debug: false,
supportedLngs: ['en'],
resources: {
en: {

+ 5
- 2
src/index.js Näytä tiedosto

@@ -8,14 +8,17 @@ import App from './App';
import store from './store';

import './i18n';
import ColorModeProvider from './context/ColorModeContext';

ReactDOM.render(
<HelmetProvider>
<React.StrictMode>
<Provider store={store}>
<App />
<ColorModeProvider>
<App />
</ColorModeProvider>
</Provider>
</React.StrictMode>
</HelmetProvider>,
document.getElementById('root'),
);
);

+ 8
- 2
src/pages/HomePage/HomePageMUI.js Näytä tiedosto

@@ -1,10 +1,16 @@
import React from 'react';
import Navbar from '../../components/MUI/Navbar';
import Modals from '../../components/MUI/Examples/Modals';

const HomePage = () => {


return (
<Navbar />
)
<>
<Navbar />
<Modals />
</>
);
};

export default HomePage;

+ 10
- 12
src/pages/LoginPage/LoginPageMUI.js Näytä tiedosto

@@ -30,11 +30,6 @@ import ErrorMessage from '../../components/MUI/CustomErrorMessage';
import { selectIsLoadingByActionType } from '../../store/selectors/loadingSelectors';
import { LOGIN_USER_LOADING } from '../../store/actions/login/loginActionConstants';

const LoginValidationSchema = Yup.object().shape({
username: Yup.string().required(i18next.t('login.usernameRequired')),
password: Yup.string().required(i18next.t('login.passwordRequired')),
});

const LoginPage = ({ history }) => {
const dispatch = useDispatch();
const { t } = useTranslation();
@@ -60,12 +55,12 @@ const LoginPage = ({ history }) => {
);

const handleApiResponseSuccess = () => {
// history.push({
// pathname: HOME_PAGE,
// state: {
// from: history.location.pathname,
// },
// });
history.push({
pathname: HOME_PAGE,
state: {
from: history.location.pathname,
},
});
};

const handleSubmit = (values) => {
@@ -85,7 +80,10 @@ const LoginPage = ({ history }) => {
username: '',
password: '',
},
validationSchema: LoginValidationSchema,
validationSchema: Yup.object().shape({
username: Yup.string().required(t('login.usernameRequired')),
password: Yup.string().required(t('login.passwordRequired')),
}),
onSubmit: handleSubmit,
validateOnBlur: true,
enableReinitialize: true,

Loading…
Peruuta
Tallenna