Related
I am attempting to recreate the theme toggle feature that the material-ui website.
My Github Repo: https://github.com/jonnyg23/flask-rest-ecommerce/tree/next-app-migration
So far, I have noticed that material-ui's website uses a cookie called paletteType in order to store the client's theme choice. I understand that a Context Provider should be used to set the cookie, however, my nav-bar implementation has an issue changing themes after the second click of my theme toggle button.
Any help would be greatly appreciated, thank you.
CustomThemeProvider.js:
import React, { createContext, useState } from "react";
import { ThemeProvider } from "#material-ui/core/styles";
import getTheme from "../themes";
import Cookie from "js-cookie";
export const CustomThemeContext = createContext({
// Set the default theme and setter.
appTheme: "light",
setTheme: null,
});
const CustomThemeProvider = ({ children, initialAppTheme }) => {
// State to hold selected theme
const [themeName, _setThemeName] = useState(initialAppTheme);
// Retrieve theme object by theme name
const theme = getTheme(themeName);
// Wrap setThemeName to store new theme names as cookie.
const setThemeName = (name) => {
// console.log("CustomThemeProvider, SetThemeName", name);
Cookie.set("appTheme", name);
_setThemeName(name);
};
const contextValue = {
appTheme: themeName,
setTheme: setThemeName,
};
return (
<CustomThemeContext.Provider value={contextValue}>
<ThemeProvider theme={theme}>{children}</ThemeProvider>
</CustomThemeContext.Provider>
);
};
export default CustomThemeProvider;
_app.js:
import "../styles/globals.css";
import React, { useContext, useEffect } from "react";
import PropTypes from "prop-types";
import { useAuth0 } from "#auth0/auth0-react";
import Head from "next/head";
import { Provider as NextAuthProvider } from "next-auth/client";
import { makeStyles } from "#material-ui/core/styles";
import CssBaseline from "#material-ui/core/CssBaseline";
import Auth0ProviderWithHistory from "../auth/auth0-provider-with-history";
import CustomThemeProvider, {
CustomThemeContext,
} from "../context/CustomThemeProvider";
export default function App({ Component, pageProps }) {
// const { isLoading } = useAuth0();
const ThemeContext = useContext(CustomThemeContext);
useEffect(() => {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector("#jss-server-side");
if (jssStyles) {
jssStyles.parentElement.removeChild(jssStyles);
}
}, []);
return (
<React.Fragment>
<Head>
<meta
name="viewport"
content="minimum-scale=1, initial-scale=1, width=device-width"
/>
</Head>
<CustomThemeProvider initialAppTheme={ThemeContext.appTheme}>
<Auth0ProviderWithHistory>
<NextAuthProvider session={pageProps.session}>
<CssBaseline />
<Component {...pageProps} />
</NextAuthProvider>
</Auth0ProviderWithHistory>
</CustomThemeProvider>
</React.Fragment>
);
}
App.propTypes = {
Component: PropTypes.elementType.isRequired,
pageProps: PropTypes.object.isRequired,
};
ThemeModeToggle.js:
import React, { useContext } from "react";
import { IconButton } from "#material-ui/core";
import { makeStyles } from "#material-ui/core/styles";
import Brightness5TwoToneIcon from "#material-ui/icons/Brightness5TwoTone";
import Brightness2TwoToneIcon from "#material-ui/icons/Brightness2TwoTone";
import { CustomThemeContext } from "../context/CustomThemeProvider";
const useStyles = makeStyles((theme) => ({
light: {
color: theme.palette.secondary.main,
},
dark: {
color: theme.palette.secondary.main,
},
}));
const ThemeModeToggle = ({ fontSize }) => {
const classes = useStyles();
const { appTheme, setTheme } = useContext(CustomThemeContext);
// console.log("ThemeModeToggle", appTheme);
const handleThemeChange = (appTheme, setTheme) => {
if (appTheme === "light") {
setTheme("dark");
} else {
setTheme("light");
}
};
return (
<IconButton onClick={() => handleThemeChange(appTheme, setTheme)}>
{appTheme === "light" ? (
<Brightness5TwoToneIcon fontSize={fontSize} className={classes.light} />
) : (
<Brightness2TwoToneIcon fontSize={fontSize} className={classes.dark} />
)}
</IconButton>
);
};
export default ThemeModeToggle;
Solved, I found the issue!
It turns out that my page components such as StorefrontIcon and the AppBar colors had to be explicitly added with className={classes.NAME} or with style={{ backroundColor: theme.palette.primary.main }} for example. Both methods are shown below in my nav-bar.js file.
import React from "react";
import { AppBar, Grid, Container, Toolbar } from "#material-ui/core";
import { makeStyles, useTheme } from "#material-ui/core/styles";
import StorefrontIcon from "#material-ui/icons/Storefront";
import MainNav from "./main-nav";
import AuthNav from "./auth-nav";
import ThemeModeToggle from "./ThemeModeToggle";
import { Desktop, SmallScreen } from "./Responsive";
import HamburgerMenu from "./HamburgerMenu";
const useStyles = makeStyles((theme) => ({
root: {
[theme.breakpoints.down("sm")]: {
padding: 0,
},
},
icon: {
color: theme.palette.primary.contrastText,
},
}));
const NavBar = () => {
const classes = useStyles();
const theme = useTheme();
return (
<AppBar
position="static"
elevation={3}
style={{ backgroundColor: theme.palette.primary.main }}
>
<Toolbar>
<Container className={classes.root}>
<Grid container justify="center" alignItems="center" spacing={2}>
<Desktop>
<Grid item xs={1}>
<StorefrontIcon fontSize="large" className={classes.icon} />
</Grid>
<Grid item xs={7}>
<MainNav />
</Grid>
<Grid item xs={3}>
<AuthNav />
</Grid>
<Grid item xs={1}>
<ThemeModeToggle fontSize="large" />
</Grid>
</Desktop>
<SmallScreen>
<Grid item xs={2}>
<StorefrontIcon fontSize="medium" className={classes.icon} />
</Grid>
<Grid container item xs={10} justify="flex-end">
<HamburgerMenu />
</Grid>
</SmallScreen>
</Grid>
</Container>
</Toolbar>
</AppBar>
);
};
export default NavBar;
I've used Ryan Cogswell's answer to have my project compatible to RTL.
But for some reason, the Material-ui icon <Send/> didn't flip accordingly. Is it because it's not compatible to RTL? Or am I missing something?
Here's an example showing that the Send icon doesn't flip:
import React from "react";
import { create } from "jss";
import rtl from "jss-rtl";
import {
StylesProvider,
jssPreset,
ThemeProvider,
createMuiTheme
} from "#material-ui/core/styles";
import CssBaseline from "#material-ui/core/CssBaseline";
import TextField from "#material-ui/core/TextField";
import Button from "#material-ui/core/Button";
import Box from "#material-ui/core/Box";
import SendIcon from "#material-ui/icons/Send";
// Configure JSS
const jss = create({ plugins: [...jssPreset().plugins, rtl()] });
const ltrTheme = createMuiTheme({ direction: "ltr" });
const rtlTheme = createMuiTheme({ direction: "rtl" });
function AppContent() {
const [isRtl, setIsRtl] = React.useState(false);
React.useLayoutEffect(() => {
document.body.setAttribute("dir", isRtl ? "rtl" : "ltr");
}, [isRtl]);
return (
<ThemeProvider theme={isRtl ? rtlTheme : ltrTheme}>
<CssBaseline />
<Box m={2}>
<TextField label={isRtl ? "بريد الكتروني او هاتف" : "Email or Phone"} />
<br />
<SendIcon />
<br />
Current Direction: {isRtl ? "rtl" : "ltr"}
<br />
<Button onClick={() => setIsRtl(!isRtl)}>Toggle direction</Button>
</Box>
</ThemeProvider>
);
}
export default function App() {
return (
<StylesProvider jss={jss}>
<AppContent />
</StylesProvider>
);
}
Thanks
Material-UI icons are not automatically flipped for rtl. This is discussed some here: https://github.com/mui-org/material-ui/issues/22726.
Here is an example of one way to handle this for the Send icon (and this approach should be usable for other icons as well):
const DirectionAwareSendIcon = withStyles((theme) => ({
root: {
transform: theme.direction === "rtl" ? "scaleX(-1)" : undefined
}
}))(SendIcon);
It is also possible to handle this globally using overrides in the theme:
MuiSvgIcon: {
root: {
"body[dir=rtl] &": {
transform: "scaleX(-1)"
}
}
}
There is some risk that this could conflict with styling in some Material-UI components which use transform in the default styles, but the examples that I have looked at so far (e.g. AccordionSummary), seem to still work fine. This global approach would currently cause issues for TablePaginationActions and PaginationItem which both swap which icons they use based on theme.direction. This global approach would then flip the already-flipped icon, so if you use either of these components you would need to take this into consideration.
There are also some icons where flipping is not desirable, for instance icons with a recognizable symbol such as Help ("?") and AttachMoney ("$"), so my recommendation would be the first approach of just explicitly adding the flipping behavior to the icons that need it.
Here's a full working example with the theme approach:
import React from "react";
import { create } from "jss";
import rtl from "jss-rtl";
import {
StylesProvider,
jssPreset,
ThemeProvider,
createMuiTheme
} from "#material-ui/core/styles";
import CssBaseline from "#material-ui/core/CssBaseline";
import TextField from "#material-ui/core/TextField";
import Button from "#material-ui/core/Button";
import Box from "#material-ui/core/Box";
import SendIcon from "#material-ui/icons/Send";
const overrides = {
MuiSvgIcon: {
root: {
"body[dir=rtl] &": {
transform: "scaleX(-1)"
}
}
}
};
// Configure JSS
const jss = create({ plugins: [...jssPreset().plugins, rtl()] });
const ltrTheme = createMuiTheme({ direction: "ltr" });
const rtlTheme = createMuiTheme({ direction: "rtl", overrides });
function AppContent() {
const [isRtl, setIsRtl] = React.useState(false);
React.useLayoutEffect(() => {
document.body.setAttribute("dir", isRtl ? "rtl" : "ltr");
}, [isRtl]);
return (
<ThemeProvider theme={isRtl ? rtlTheme : ltrTheme}>
<CssBaseline />
<Box m={2}>
<TextField label={isRtl ? "بريد الكتروني او هاتف" : "Email or Phone"} />
<br />
<SendIcon />
<br />
Current Direction: {isRtl ? "rtl" : "ltr"}
<br />
<Button onClick={() => setIsRtl(!isRtl)}>Toggle direction</Button>
</Box>
</ThemeProvider>
);
}
export default function App() {
return (
<StylesProvider jss={jss}>
<AppContent />
</StylesProvider>
);
}
I'm using Material-Ui in my React Project !
i followed the steps in the documentation to allow RTL in my project and all work fine !
except the TextField Component
LTR DIRECTION :
RTL DIRECTION
Like you see ! the problem is with the label still in left ( the input text work fine )
App.js file
import React, {useState} from 'react';
//i18n
import {withTranslation} from "react-i18next";
import './i18n';
//jss
import { create } from 'jss';
import rtl from 'jss-rtl';
import { StylesProvider, jssPreset } from '#material-ui/core/styles';
// Configure JSS
const jss = create({ plugins: [...jssPreset().plugins, rtl()] });
function App(props) {
// initialize Language
const { i18n } = props;
const [ prefLang, setPrefLang] = useState(i18n.language);
let theme =createMuiTheme({
palette : {
primary : {
main : '#ed5ac0',
},
},
typography : {
fontFamily : "lalezar, cursive",
h3 : {
fontSize : 1.4,
},
h4 : {
fontSize : 1.5
},
fontAwseomeSize : {
xs : "14px",
sm : "14px",
md : "16px"
},
responsiveFont : {
xs : "20px",
sm : "12.5px",
md : "14px"
},
highLight : {
md : "25px"
},
subHighLight : {
md : "18px"
}
},
}
);
return (
<BrowserRouter>
<LangContext.Provider
value ={{
prefLang,
setPrefLang
}}
>
<CssBaseline/>
<ThemeProvider theme={theme}>
<StylesProvider jss={jss}>
<Grid dir={(prefLang === "ar") ? "rtl" : "ltr"}>
{/*<AppHeader/>*/}
<ContentRouter/>
</Grid>
</StylesProvider>
</ThemeProvider>
</LangContext.Provider>
</BrowserRouter>
);
}
export default withTranslation()(App);
My Form Component
const LoginForm = () => {
return (
<>
<Backdrop style={{ zIndex : 999 , color : theme.palette.primary.main}} open={backdrop} >
<CircularProgress color="inherit" />
</Backdrop>
<form onSubmit={formik.handleSubmit} style={{width: "100%", marginTop: "20px"}}>
{ userNotFound ? <Alert style={{marginBottom : "20px"}} variant="outlined" severity="error">
This is an error alert — check it out!
</Alert> : null}
<TextField
id="identifier"
name="identifier"
onChange={formik.handleChange}
value={formik.values.identifier}
label={t('formIdentifier')}
fullWidth
/>
{formik.touched.identifier && formik.errors.identifier ?
(
<Alert style={{ marginTop :"10px"}} variant="outlined" severity="error">{formik.errors.identifier}</Alert>
) : null}
<TextField
style={{marginTop: "50px"}}
id="password"
name="password"
type="password"
onChange={formik.handleChange}
value={formik.values.password}
label={t('formPassword')}
fullWidth
/>
{formik.touched.password && formik.errors.password ?
(
<Alert style={{ marginTop :"10px"}} variant="outlined" severity="error">{formik.errors.password}</Alert>
) : null}
<Button type="submit" color="primary">{t('login')}</Button>
</form>
</>
);
};
My Theme.js File
import createMuiTheme from "#material-ui/core/styles/createMuiTheme";
let theme =createMuiTheme({
direction : 'rtl',
palette : {
primary : {
main : '#ed5ac0',
},
},
typography : {
fontFamily : "Merienda One, sans-serif",
h3 : {
fontSize : 1.4,
},
h4 : {
fontSize : 1.5
},
fontAwseomeSize : {
xs : "14px",
sm : "14px",
md : "16px"
},
responsiveFont : {
xs : "20px",
sm : "12.5px",
md : "14px"
},
highLight : {
md : "40px"
}
},
}
);
export default theme;
Any suggestion to make the label RTL ?
The documentation contains four steps for rtl support:
Set the dir attribute on the body element.
In my examples below, this is handled by the following:
React.useLayoutEffect(() => {
document.body.setAttribute("dir", isRtl ? "rtl" : "ltr");
}, [isRtl]);
Set the direction in the theme.
In my examples below, I am toggling between two themes:
const ltrTheme = createTheme({ direction: "ltr" });
const rtlTheme = createTheme({ direction: "rtl" });
...
<ThemeProvider theme={isRtl ? rtlTheme : ltrTheme}>
...
</ThemeProvider>
Install the rtl plugin.
For v4 (using JSS), this means installing jss-rtl.
For v5 (using Emotion), this means installing stylis-plugin-rtl.
Load the rtl plugin.
Below is a v4 example showing how to load the rtl plugin for JSS (v5 example further down).
For performance reasons it is important to avoid re-rendering StylesProvider, so this should not be in a component with state that can change and therefore trigger a re-render. In my own app, I have the StylesProvider element in my index.js file as the first element inside the call to react-dom render.
import rtl from "jss-rtl";
import {
StylesProvider,
jssPreset
} from "#material-ui/core/styles";
// Configure JSS
const jss = create({ plugins: [...jssPreset().plugins, rtl()] });
export default function App() {
return (
<StylesProvider jss={jss}>
<AppContent />
</StylesProvider>
);
}
The example below includes a TextField and you can see that the label's position toggles correctly.
import React from "react";
import { create } from "jss";
import rtl from "jss-rtl";
import {
StylesProvider,
jssPreset,
ThemeProvider,
createTheme
} from "#material-ui/core/styles";
import CssBaseline from "#material-ui/core/CssBaseline";
import TextField from "#material-ui/core/TextField";
import Button from "#material-ui/core/Button";
import Box from "#material-ui/core/Box";
// Configure JSS
const jss = create({ plugins: [...jssPreset().plugins, rtl()] });
const ltrTheme = createTheme({ direction: "ltr" });
const rtlTheme = createTheme({ direction: "rtl" });
function AppContent() {
const [isRtl, setIsRtl] = React.useState(false);
const [value, setValue] = React.useState("initial value");
React.useLayoutEffect(() => {
document.body.setAttribute("dir", isRtl ? "rtl" : "ltr");
}, [isRtl]);
return (
<ThemeProvider theme={isRtl ? rtlTheme : ltrTheme}>
<CssBaseline />
<Box m={2}>
<TextField
variant="outlined"
value={value}
onChange={(event) => setValue(event.target.value)}
label={isRtl ? "بريد الكتروني او هاتف" : "Email or Phone"}
/>
<br />
<br />
Current Direction: {isRtl ? "rtl" : "ltr"}
<br />
<Button onClick={() => setIsRtl(!isRtl)}>Toggle direction</Button>
</Box>
</ThemeProvider>
);
}
export default function App() {
return (
<StylesProvider jss={jss}>
<AppContent />
</StylesProvider>
);
}
Below is an equivalent example for v5 using Emotion.
import React from "react";
import rtlPlugin from "stylis-plugin-rtl";
import { CacheProvider } from "#emotion/react";
import createCache from "#emotion/cache";
import { ThemeProvider, createTheme } from "#mui/material/styles";
import CssBaseline from "#mui/material/CssBaseline";
import TextField from "#mui/material/TextField";
import Button from "#mui/material/Button";
import Box from "#mui/material/Box";
import { prefixer } from "stylis";
const cacheLtr = createCache({
key: "muiltr"
});
const cacheRtl = createCache({
key: "muirtl",
// prefixer is the only stylis plugin by default, so when
// overriding the plugins you need to include it explicitly
// if you want to retain the auto-prefixing behavior.
stylisPlugins: [prefixer, rtlPlugin]
});
const ltrTheme = createTheme({ direction: "ltr" });
const rtlTheme = createTheme({ direction: "rtl" });
export default function App() {
const [isRtl, setIsRtl] = React.useState(false);
const [value, setValue] = React.useState("initial value");
React.useLayoutEffect(() => {
document.body.setAttribute("dir", isRtl ? "rtl" : "ltr");
}, [isRtl]);
return (
<CacheProvider value={isRtl ? cacheRtl : cacheLtr}>
<ThemeProvider theme={isRtl ? rtlTheme : ltrTheme}>
<CssBaseline />
<Box m={2}>
<TextField
value={value}
onChange={(event) => setValue(event.target.value)}
label={isRtl ? "بريد الكتروني او هاتف" : "Email or Phone"}
/>
<br />
<br />
Current Direction: {isRtl ? "rtl" : "ltr"}
<br />
<Button onClick={() => setIsRtl(!isRtl)}>Toggle direction</Button>
</Box>
</ThemeProvider>
</CacheProvider>
);
}
In addition, I have a later answer that discusses flipping icons: material-ui icons won't flip when I change to RTL.
Another solution I've found to set this for specific component is to add it via native JS after component is rendered.
First I created a ref to the input element:
const inputRef = createRef()
<TextField inputRef={inputRef} />
Then added a useEffect hook to perform once on each render:
useEffect(() => {
if(inputRef)
inputRef.current.dir = 'auto'
}, [])
Not the most beautiful code, but it sure works 😉
RTL for specific components, and not the whole app:
MUI rtl documentation and the accepted answer pertain to the entire application.
If you want just a few components here and there, all you need to do is create a simple wrapper component, and children will have rtl. No need for a theme provider.
Just be sure to wrap the children with a <div dir="rtl">.
Wrapper Component:
import React from 'react';
import rtlPlugin from "stylis-plugin-rtl";
import { CacheProvider } from "#emotion/react";
import createCache from "#emotion/cache";
import { prefixer } from 'stylis'
const cacheRtl = createCache({
key: "muirtl",
stylisPlugins: [prefixer, rtlPlugin]
});
const RtlProvider = ({ children }) => {
return (
<CacheProvider value={cacheRtl}>
<div dir="rtl">
{children}
</div>
</CacheProvider>
);
};
export default RtlProvider;
Simple Implementation
return (
<RtlProvider>
<TextField label="my label"/>
</RtlProvider>
)
Making CacheProvider tag as a youngest parent for your content, such like below:
<ThemeProvider>
<ScrollToTop />
<BaseOptionChartStyle />
<CacheProvider value={cacheRtl}>
<Router />
</CacheProvider>
</ThemeProvider>
*note:
(Router) includes the various textFields
apart from theme and jss-rtl and stuff that Ryan Cogswell said, there's one tiny problem that i found no where after days of searching (just 3-4 days actualy :)).
and that is the import statement.
make sure youre import statement is like the following:
import TextField from "#material-ui/core/TextField"; //true
if youre import statement is like following, none of those theming and jss stuff works:
import TextField from "#mui/material/TextField"; //false
How can I pass makeStyle classes from parent component to child component and combine them with the makeStyle classes in the child component? E.g. as below adding the breakpoint hiding to the child component style.
Example child component:
import React from "react"
import { Button } from "#material-ui/core"
import { makeStyles } from "#material-ui/core/styles"
const useStyles = makeStyles(theme => ({
button: {
background: "#000",
color: "white",
//lots of other css here so we dont want to repeat it in the parent component
},
}))
export default function PrimaryButton(props) {
const classes = useStyles()
const { children, fullWidth = false } = props
return (
<Button
fullWidth={fullWidth}
className={classes.button}
variant="contained"
>
{children}
</Button>
)
}
Example parent component:
import React from "react"
import { PrimaryButton } from "components/PrimaryButton"
import { makeStyles } from "#material-ui/core/styles"
const useStyles = makeStyles(theme => ({
primaryButton: {
display: "inline-block",
[theme.breakpoints.down("sm")]: {
display: "none",
},
},
}))
export default function PrimaryButton(props) {
const classes = useStyles()
return (
<PrimaryButton
className={classes.primaryButton}
>
Button text
</PrimaryButton>
)
}
clsx is used internally within Material-UI and is a convenient utility for combining multiple class names. In your child component, you can grab className from the props and then use className={clsx(className, classes.button)} in the Button it renders:
import React from "react";
import { Button } from "#material-ui/core";
import { makeStyles } from "#material-ui/core/styles";
import clsx from "clsx";
const useStyles = makeStyles(theme => ({
button: {
background: "#000",
color: "white"
}
}));
export default function PrimaryButton(props) {
const classes = useStyles();
const { children, className, fullWidth = false } = props;
return (
<Button
fullWidth={fullWidth}
className={clsx(className, classes.button)}
variant="contained"
>
{children}
</Button>
);
}
I need to implement a <BackButton /> in react-admin for example when I open show page for a resource I need able to back to the list page.
Can you guide me to implement this?
I'm not familiar with react-admin routing mechanism.
Now I'm using this component in my edit form actions props:
const MyActions = ({ basePath, data, resource }) => (
<CardActions>
<ShowButton basePath={basePath} record={data} />
<CloneButton basePath={basePath} record={data} />
{/* Need <BackButton /> here */}
</CardActions>
);
export const BookEdit = (props) => (
<Edit actions={<MyActions />} {...props}>
<SimpleForm>
...
</SimpleForm>
</Edit>
);
You can use react-router-redux's goBack() function to achieve this.
For example, your button component will look something like this:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Button from '#material-ui/core/Button';
import { goBack } from 'react-router-redux';
class BackButton extends Component {
handleClick = () => {
this.props.goBack();
};
render() {
return <Button variant="contained" color="primary" onClick={this.handleClick}>Go Back</Button>;
}
}
export default connect(null, {
goBack,
})(BackButton);
Now use that button component in your CardActions.
You can get help from an example which uses react-router-redux's push() function in a similar way from the official docs.
Create a back button. This one will pass props and children (text) and uses react-router directly, which I think makes more sense and keeps your code simple.
// BackButton.js
import React from 'react'
import Button from '#material-ui/core/Button'
import { withRouter } from 'react-router'
const BackButton = ({ history: { goBack }, children, ...props }) => (
<Button {...props} onClick={goBack}>
{children}
</Button>
)
export default withRouter(BackButton)
Example usage:
import { Toolbar, SaveButton } from 'react-admin'
import BackButton from './BackButton'
const SomeToolbar = props => (
<Toolbar {...props}>
<SaveButton />
<BackButton
variant='outlined'
color='secondary'
style={{ marginLeft: '1rem' }}
>
Cancel
</BackButton>
</Toolbar>
)
The complete code is below.
//BackButton.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import compose from 'recompose/compose';
import { withStyles, createStyles } from '#material-ui/core/styles';
import { translate } from 'ra-core';
import Button from '#material-ui/core/Button';
import ArrowBack from '#material-ui/icons/ArrowBack';
import classnames from 'classnames';
import { fade } from '#material-ui/core/styles/colorManipulator';
const styles = theme =>
createStyles({
deleteButton: {
color: theme.palette.error.main,
'&:hover': {
backgroundColor: fade(theme.palette.error.main, 0.12),
// Reset on mouse devices
'#media (hover: none)': {
backgroundColor: 'transparent',
},
},
},
});
const sanitizeRestProps = ({
basePath,
className,
classes,
label,
invalid,
variant,
translate,
handleSubmit,
handleSubmitWithRedirect,
submitOnEnter,
record,
redirect,
resource,
locale,
...rest
}) => rest;
class BackButton extends Component {
static contextTypes = {
router: () => true, // replace with PropTypes.object if you use them
}
static propTypes = {
label: PropTypes.string,
refreshView: PropTypes.func.isRequired,
icon: PropTypes.element,
};
static defaultProps = {
label: 'ra.action.back',
icon: <ArrowBack />,
};
render() {
const {
className,
classes = {},
invalid,
label = 'ra.action.back',
pristine,
redirect,
saving,
submitOnEnter,
translate,
icon,
onClick,
...rest
} = this.props;
return (
<Button
onClick={this.context.router.history.goBack}
label={label}
className={classnames(
'ra-back-button',
classes.backButton,
className
)}
key="button"
{...sanitizeRestProps(rest)}>
{icon} {label && translate(label, { _: label })}
</Button>
)
}
}
const enhance = compose(
withStyles(styles),
translate
);
export default enhance(BackButton);
//Toolbar.js
import React from 'react';
import {
Toolbar,
SaveButton,
DeleteButton,
} from 'react-admin';
import { withStyles } from '#material-ui/core';
import BackButton from './BackButton'
const toolbarStyles = {
toolbar: {
display: 'flex',
justifyContent: 'space-between',
},
};
export const CustomEditToolbar = withStyles(toolbarStyles)(props => (
<Toolbar {...props}>
<SaveButton/>
<DeleteButton/>
<BackButton/>
</Toolbar>
));
export const CustomCreateToolbar = withStyles(toolbarStyles)(props => (
<Toolbar {...props}>
<SaveButton/>
<BackButton/>
</Toolbar>
));