material-ui icons won't flip when I change to RTL

Viewed 2895

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>
  );
}

Edit rtl example

Thanks

1 Answers

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);

Edit rtl example (forked)

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>
  );
}

Edit rtl example (forked)

Related