Material-Ui TextField 不受 RTL 方向影响

问题描述 投票:0回答:6

我在我的 React 项目中使用 Material-Ui!

我按照文档中的步骤在我的项目中允许RTL并且一切正常!

除了 TextField 组件

LTR方向:

RTL方向

就像你看到的!问题是标签仍在左侧(输入文本工作正常)

App.js 文件

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

我的表单组件

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

我的 Theme.js 文件

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"
        }
    },

} 

);

导出默认主题;

对于制作标签 RTL 有什么建议吗?

reactjs material-ui
6个回答
33
投票

文档包含 RTL 支持的四个步骤:

  1. dir
    元素上设置
    body
    属性。

在下面的示例中,这是通过以下方式处理的:

  React.useLayoutEffect(() => {
    document.body.setAttribute("dir", isRtl ? "rtl" : "ltr");
  }, [isRtl]);
  1. 在主题中设置
    direction

在下面的示例中,我在两个主题之间切换:

const ltrTheme = createTheme({ direction: "ltr" });
const rtlTheme = createTheme({ direction: "rtl" });
...
<ThemeProvider theme={isRtl ? rtlTheme : ltrTheme}>
...
</ThemeProvider>
  1. 安装rtl插件。

    • 对于 v4(使用 JSS),这意味着安装
      jss-rtl
    • 对于 v5(使用 Emotion),这意味着安装
      stylis-plugin-rtl
  2. 加载rtl插件。

下面是一个 v4 示例,展示了如何加载 JSS 的 rtl 插件(下面是 v5 示例)。

出于性能原因,避免重新渲染非常重要

StylesProvider
,因此这不应该位于状态可以更改并因此触发重新渲染的组件中。在我自己的应用程序中,我的
StylesProvider
文件中的
index.js
元素作为对 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>
  );
}

下面的示例包含一个

TextField
,您可以看到标签的位置正确切换。

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

Edit rtl example

下面是使用 Emotion 的 v5 的等效示例。

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

Edit rtl example

此外,我稍后有一个讨论翻转图标的答案:material-ui icon won't Flip when I Change to RTL.


3
投票

特定组件的 RTL,而不是整个应用程序:

MUI rtl 文档和接受的答案适用于整个应用程序。

如果你只需要一些组件,你所需要做的就是创建一个简单的包装组件,并且子组件将拥有 rtl。不需要主题提供商。

请务必用

children
包裹
<div dir="rtl">

包装组件

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;

简单实施

return (
  <RtlProvider>
      <TextField label="my label"/>
  </RtlProvider>
)

2
投票

我发现为特定组件设置此选项的另一个解决方案是在渲染组件后通过本机 JS 添加它。

首先我创建了对输入元素的引用:

const inputRef = createRef() 
<TextField inputRef={inputRef} />

然后添加了一个 useEffect 钩子以在每个渲染上执行一次:

useEffect(() => { 
    if(inputRef) 
        inputRef.current.dir = 'auto' 
}, [])

不是最漂亮的代码,但它确实有效 😉


0
投票

将 CacheProvider 标记作为您内容的最年轻的父级标记,如下所示:

<ThemeProvider>
      <ScrollToTop />
      <BaseOptionChartStyle />
      <CacheProvider value={cacheRtl}>    
      <Router />
    </CacheProvider>
</ThemeProvider>

*注意: (路由器)包括各种文本字段


0
投票

以上均不适用于 mui 5 所以这是一个简单的CSS解决方案

[dir="rtl"] .MuiFormLabel-root {
  left: auto;
  right: auto;
  transform-origin: top right;
  text-align: right;
  margin-right: 25px;
}

[dir="rtl"] .MuiOutlinedInput-notchedOutline {
  text-align: right;
}

-2
投票

除了主题和 jss-rtl 以及 Ryan Cogswell 所说的东西之外,还有一个小问题,我经过几天的搜索(实际上只有 3-4 天:))没有找到任何地方。 这就是导入声明。

确保您的导入语句如下所示:

import TextField from "@material-ui/core/TextField";   //true

如果你的导入语句如下所示,那么这些主题和jss东西都不起作用:

import TextField from "@mui/material/TextField";   //false
© www.soinside.com 2019 - 2024. All rights reserved.