如何为 Material UI React 添加 RTL 支持

Zie*_* Hf 7 javascript css reactjs material-ui react-material

我正在构建一个 LTR 应用程序,我想添加 RTL 支持。该应用程序基于 Material UI React 之上。因为我使用的是 CSS Flex Box,所以我能够将应用程序旋转到 RTL,只需将 dir="rtl" 添加到主体即可。如上所述,我还在主题中添加了 direction="rtl"此处

然而,并非一切都改变了。

让我们以此为例: 选择国家 LTR 正如你在这里看到的,我在文本元素上留下了填充。在 RTL 版本中,由于所有内容都颠倒了,因此向左填充在 UI 中没有任何影响,我的意思是必须向右填充以显示两个元素之间的小空间: 选择国家 RTL

似乎我做错了什么,因为在 Material UI 文档中 此处的,此功能在添加此代码段并将组件包裹在其周围后必须是开箱即用的。

这是我的父组件应用程序:

import React, { PureComponent } from "react";
import { theme } from "./styling/theme";
import Routes from "./Routes";
// Redux
import { Provider } from "react-redux";
import store from "./app/store";

import LoadingBar from "react-redux-loading-bar";
// CSS
import { MuiThemeProvider } from "@material-ui/core/styles";
// import { ThemeProvider } from "@material-ui/styles";
import { create } from "jss";
import rtl from "jss-rtl";
import JssProvider from "react-jss/lib/JssProvider";
// import { StylesProvider, jssPreset } from "@material-ui/styles";
import { createGenerateClassName, jssPreset } from "@material-ui/core/styles";
import { themeObject, colors } from "./styling/theme";
// Helpers
import get from "lodash/get";

// Configure JSS
const jss = create({ plugins: [...jssPreset().plugins, rtl()] });
const generateClassName = createGenerateClassName();

function RTL(props) {
  return (
    <JssProvider jss={jss} generateClassName={generateClassName}>
      {
        props.children
      }
    </JssProvider>
  );
}

class App extends PureComponent {
  render() {
    const isRtl = get(store, "classified.language.rtl", false);
    return (
      <Provider store={store}>
        <RTL>
          <MuiThemeProvider
            theme={
              isRtl
                ? { ...theme, direction: "rtl" }
                : { ...theme, direction: "ltr" }
            }
          >
            <LoadingBar
              style={{
                backgroundColor: colors.primary[500],
                height: themeObject.spacing.unit,
                zIndex: 9999
              }}
            />
            <Routes />
          </MuiThemeProvider>
        </RTL>
      </Provider>
    );
  }
}

export default App;
Run Code Online (Sandbox Code Playgroud)

这是我的组件示例(上图中的组件:CLList):

import React, { Component } from "react";
import PropTypes from "prop-types";
import { withStyles } from "@material-ui/core/styles";
// Helpers
import isFunction from "lodash/isFunction";
import cloneDeep from "lodash/cloneDeep";

import styles from "./CLList.styles";

const defaultImg = "IMAGE_URL_HERE";
class CLList extends Component {
  static propTypes = {
    classes: PropTypes.object.isRequired,
    items: PropTypes.arrayOf(
      PropTypes.shape({
        img: PropTypes.string,
        name: PropTypes.string
      })
    ).isRequired,
    onClick: PropTypes.func
  };
  render() {
    const { classes, items, onClick } = this.props;
    return (
      <ul className={classes.list}>
        {items.map((item, key) => (
          <li
            className={classes.item}
            onClick={() => isFunction(onClick) && onClick(cloneDeep(item))}
            key={key}
          >
            <img
              className={classes.image}
              src={item.img || defaultImg}
              alt={item.name}
              title={item.name}
            />
            <span className={classes.label}>{item.name}</span>
          </li>
        ))}
      </ul>
    );
  }
}

export default withStyles(styles)(CLList);
Run Code Online (Sandbox Code Playgroud)

最后一个文件是 CLList 的 CSS:

import { colors } from "../..";
const styles = theme => ({
  list: {
    display: "flex",
    flexDirection: "column",
    listStyle: "none",
    padding: 5,
    margin: 0,
    "& > li:not(:last-child)": {
      marginBottom: 10
    }
  },
  item: {
    flex: 1,
    display: "flex",
    cursor: "pointer",
    "&:hover": {
      backgroundColor: colors.primary[50]
    }
  },
  image: {
    flex: "0 0 15%",
    maxWidth: "40px",
    maxHeight: "40px"
  },
  label: {
    flex: "1",
    alignSelf: "center",
    paddingLeft: 20
  }
});

export default styles;
Run Code Online (Sandbox Code Playgroud)

我希望标签的 paddingLeft 为 => paddingRight。这可能吗 ?它是开箱即用的功能吗?或者我应该只使用RTL-CSS-JS并在主体包含 dir="RTL" 时包装我所有的样式对象以自动更改样式?

这两个库之间我也很困惑:

  • @material-ui/core/styles
  • @material-ui/styles

我应该使用第一个还是第二个?有什么不同 ?

谢谢你的时间。

编辑 1:

我在我的 CSS 对象上使用了rtlCSSJS,我得到了预期的结果。但我不确定这是否是最好的方法。CLList 的 CSS 现在看起来像这样:

import rtlCSSJS from "rtl-css-js";
import { colors } from "../..";
const defaultDir = document.body.getAttribute("dir");
const styles = theme =>
  defaultDir === 'rtl' ? rtlCSSJS({...CSS_HERE....}) : {...CSS_HERE....};
export default styles;
Run Code Online (Sandbox Code Playgroud)

Zie*_* Hf 5

我想我找到了我自己问题的解决方案,但是可以随意添加任何增强功能或更好的解决方案。

Material UI 默认使用 jss-rtl,最后一个是 rtl-css-js 的包装器。因此无需直接在其中使用 rtl-css-js,因为 Material UI 将完成这项工作。

我将我的父应用程序组件更改为:

import React, { PureComponent } from "react";
import Routes from "./Routes";
import RTL from "./RTL";
// Redux
import { Provider } from "react-redux";
import store from "./app/store";

import LoadingBar from "react-redux-loading-bar";

import { themeObject, colors } from "./styling/theme";

class App extends PureComponent {
  render() {
    return (
      <Provider store={store}>
        <RTL>
          <>
            <LoadingBar
              // className="loading"
              style={{
                backgroundColor: colors.primary[500],
                height: themeObject.spacing.unit,
                zIndex: 9999
              }}
            />
            <Routes />
          </>
        </RTL>
      </Provider>
    );
  }
}

export default App;
Run Code Online (Sandbox Code Playgroud)

我添加了 RTL 组件,它将连接到 Redux 以定义正确方向的正确主题。我将语言数据保存在 Redux 中,根据这些数据,我将定义为我的应用程序提供的主题。

这是 RTL 组件:

import React, { PureComponent } from "react";
import PropTypes from "prop-types";
// Redux
import { connect } from "react-redux";
// CSS
import { MuiThemeProvider, createMuiTheme } from "@material-ui/core/styles";
import { create } from "jss";
import rtl from "jss-rtl";
import JssProvider from "react-jss/lib/JssProvider";
import { createGenerateClassName, jssPreset } from "@material-ui/core/styles";

// Theme
import { themeObject } from "./styling/theme";

// Helpers
import get from "lodash/get";
// Configure JSS
const jss = create({ plugins: [...jssPreset().plugins, rtl()] });
const generateClassName = createGenerateClassName();

const G_isRtl = document.body.getAttribute("dir") === "rtl";

class RTL extends PureComponent {
  static propTypes = {
    children: PropTypes.oneOfType([
      PropTypes.array,
      PropTypes.object,
      PropTypes.node
    ]),
    language: PropTypes.object
  };

  render() {
    const { children, language } = this.props;
    const isRtl = get(language, "rtl", G_isRtl);

    const theme = createMuiTheme({
      ...themeObject,
      direction: isRtl ? "rtl" : "ltr"
    });

    return (
      <JssProvider jss={jss} generateClassName={generateClassName}>
        <MuiThemeProvider theme={theme}>{children}</MuiThemeProvider>
      </JssProvider>
    );
  }
}

const mapStateToProps = ({ classified }) => ({
  language: classified.language
});
export default connect(mapStateToProps)(RTL);
Run Code Online (Sandbox Code Playgroud)

现在所有的子组件将根据语言在 RTL 和 LTR 之间切换,我们可以只关注一种布局,所有的反向工作都通过这个插件完成。

另外我想说按照官方文档中的说明对我不起作用!我发现的大部分解决方案都基于此处的答案。