繁体   English   中英

我如何将 Material-UI 托管样式应用于非 Material-ui、非 React 元素?

[英]How would I apply Material-UI managed styles to non-material-ui, non-react elements?

我有一个应用程序,我在其中使用 Material UI 及其主题提供程序(使用 JSS)。

我现在正在合并fullcalendar-react ,它并不是一个真正成熟的 React 库——它只是一个围绕原始 fullcalendar 代码的薄 React 组件包装器。

也就是说,我无法访问诸如渲染道具之类的东西来控制它如何设置其元素的样式。

但是,它确实让您可以通过在呈现它们时调用的回调(例如eventRender 方法)直接访问 DOM 元素。

这是一个基本的演示沙箱。

在此处输入图片说明

现在我想要做的是使完整日历组件(例如,按钮)与我的应用程序的其余部分共享相同的外观和感觉。

一种方法是,我可以通过查看它使用的类名并相应地实现样式来手动覆盖所有样式。

或者 -我可以实现一个 Bootstrap 主题 - 正如他们的文档中所建议的那样。

但是这些解决方案中的任何一个的问题在于:

  1. 这将是很多工作
  2. 我会遇到同步问题,如果我更改了我的 MUI 主题并忘记更新日历主题,它们看起来会有所不同。

我想做的是:

  • 神奇地将 MUI 主题转换为 Bootstrap 主题。
  • 或者在 MUI 类名和日历类名之间创建一个映射,例如:
.fc-button = .MuiButtonBase-root.MuiButton-root.MuiButton-contained
.fc-button-primary= .MuiButton-containedPrimary

我不介意必须按摩选择器等以使其工作(例如 - MUI 按钮有两个内部跨度,而完整日历只有一个)。 这主要是关于我何时更改主题 - 不想在两个地方更改它。

使用 Sass 之类的东西和它的@extend语法就是我的想法。 我可以很容易地使用 Sass 创建完整的日历 CSS - 但是 Sass 如何访问 MuiTheme?

也许我可以采取相反的方法 - 告诉 MUI '嘿,这里的这些类名的样式应该像这些 MUI 类一样'。

关于我如何解决这个问题的任何具体建议?

这是我的建议(显然,这不是直接的)。 从 MUI 主题中获取样式并使用react-helmet基于它生成style标签。 为了很好地完成它,我创建了一个“包装器”组件来完成地图。 我只实施了主要规则,但它可以扩展到所有其他规则。

这样,您在主题中所做的任何更改也会影响映射的选择器。

import React from "react";
import { Helmet } from "react-helmet";

export function MuiAdapter({ theme }) {
  if (!theme.palette) {
    return <></>;
  }
  return (
    <Helmet>
      <style type="text/css">{`
          .fc-button-primary {
            background: ${theme.palette.primary.main}
          }
          /* more styles go here */
      `}</style>
    </Helmet>
  );
}

以及适配器的使用

<MuiAdapter theme={theme} />

工作演示: https : //codesandbox.io/s/reverent-mccarthy-3o856

截屏

您可以通过遍历ref来创建 MUI 类名和日历类名之间的映射。 这可能不是某些人所说的“最佳实践”……但这是一个解决方案:)。 请注意,我将您的组件从功能组件更新为类组件,但您可以使用功能组件中的钩子来完成此操作。

添加参考

ref添加到要设置为引用的 MUI 元素,在您的情况下为Button

<Button
  color="primary"
  variant="contained"
  ref={x => {
    this.primaryBtn = x;
  }}
>

以及对要映射到的组件周围的环绕divref 您不能将它直接添加到组件中,因为这不会让我们访问children

<div
  ref={x => {
    this.fullCal = x;
  }}
>
  <FullCalendar
    ...
  />
</div>

地图类

componentDidMount()添加您需要的任何逻辑来定位正确的 DOM 节点(对于您的情况,我添加了typematchingClass逻辑)。 然后在所有 FullCalendar DOM 节点上运行该逻辑并替换任何匹配的classList

componentDidMount() {
  this.updatePrimaryBtns();
}

updatePrimaryBtns = () => {
  const children = Array.from(this.fullCal.children);
  // Options
  const type = "BUTTON";
  const matchingClass = "fc-button-primary";

  this.mapClassToElem(children, type, matchingClass);
};

mapClassToElem = (arr, type, matchingClass) => {
  arr.forEach(elem => {
    const { tagName, classList } = elem;
    // Check for match
    if (tagName === type && Array.from(classList).includes(matchingClass)) {
      elem.classList = this.primaryBtn.classList.value;
    }

    // Run on any children
    const next = elem.children;
    if (next.length > 0) {
      this.mapClassToElem(Array.from(next), type, matchingClass);
    }
  });
};

这可能有点笨手笨脚,但它满足您更新 Material UI 时的未来证明要求。 它还允许您在将classList传递给元素时更改它,这具有明显的好处。

注意事项

如果“映射到”组件 ( FullCalendar ) 更新了您定位的元素上的类(例如,如果将.is-selected添加到当前按钮)或在安装后添加新按钮,则您必须想办法跟踪相关更改并重新运行逻辑。

我还应该提到(显然)更改类可能会产生意想不到的后果,例如破坏 UI,您必须弄清楚如何修复它们。

这是工作沙箱: https : //codesandbox.io/s/determined-frog-3loyf

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM