我有一个应用程序,其中使用了Material UI及其主题提供程序(使用JSS)。
我现在要合并fullcalendar-react,它实际上并不是一个完善的React库-只是原始的全日历代码周围的薄薄的React组件包装。
这就是说,我无权使用诸如渲染道具之类的东西来控制其元素样式。
但是,确实可以通过渲染元素时调用的回调(例如eventRender method)直接访问DOM元素。
现在我要做的是使完整日历组件(例如按钮)与我的应用程序其余部分具有相同的外观。
执行此操作的一种方法是,我可以通过查看正在使用的类名并相应地实现样式来手动覆盖所有样式。
或者-I could implement a Bootstrap theme - as suggested in their documentation.
但这两种解决方案的问题在于:
我想做的是:
.fc-button = .MuiButtonBase-root.MuiButton-root.MuiButton-contained
.fc-button-primary= .MuiButton-containedPrimary
我不介意对选择器等进行按摩以使其工作(例如-MUI按钮具有两个内部范围,而“完整日历”只有一个)。这主要是关于更改主题的时间-不想在两个地方都进行更改。
我想到的是使用像Sass这样的@extend
语法。我可以很容易地用Sass创建全日历CSS-但是Sass如何访问MuiTheme?
也许我可以采取相反的方法-告诉MUI'嘿,这里的这些类名称应像这些MUI类一样设置样式”。
关于如何解决此问题的任何具体建议?
答案 0 :(得分:4)
这是我的建议(显然,这不是直截了当的)。从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} />
答案 1 :(得分:3)
您可以通过ref
来在MUI类名称和日历类名称之间创建映射。这可能不是某些人所说的“最佳实践”……但这是一种解决方案:)。 请注意,我已将您的组件从功能组件更新为类组件,但是您可以通过功能组件中的钩子来实现。
添加参考
向要设置为引用的MUI元素添加ref
,在您的情况下为Button
。
<Button
color="primary"
variant="contained"
ref={x => {
this.primaryBtn = x;
}}
>
将ref
和div
包裹在要映射的组件周围。您无法将其直接添加到组件中,因为那样一来我们就无法访问children
。
<div
ref={x => {
this.fullCal = x;
}}
>
<FullCalendar
...
/>
</div>
地图类
从componentDidMount()
中添加针对正确的DOM节点所需的任何逻辑(对于您的情况,我为type
和matchingClass
添加了逻辑)。然后在所有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中断,您必须弄清楚如何修复它们。