Material UIとそのテーマプロバイダー(JSSを使用)を使用しているアプリケーションがあります。
私は fullcalendar-react を組み込んでいます。これは実際には完全な本格的なものではありませんReactライブラリ-それは薄いReact元のフルカレンダーコードのコンポーネントラッパー。
つまり、要素のスタイルを制御するためのレンダープロップなどにアクセスすることはできません。
ただし、レンダリング時に呼び出されるコールバックを介してDOM要素に直接アクセスできます(例: eventRender method )。
次に、フルカレンダーコンポーネント(ボタンなど)を他のアプリケーションと同じルックアンドフィールに共有します。
これを行う1つの方法は、使用しているクラス名を確認し、それに応じてスタイルを実装することにより、すべてのスタイルを手動でオーバーライドできることです。
または- Bootstrapテーマ-ドキュメントで提案されているように実装できます。
しかし、これらのソリューションのいずれかの問題は、次のとおりです。
私がしたいのは次のいずれかです:
.fc-button = .MuiButtonBase-root.MuiButton-root.MuiButton-contained
.fc-button-primary= .MuiButton-containedPrimary
セレクターなどをマッサージして機能させる必要はありません(たとえば、MUIボタンには2つの内部スパンがありますが、フルカレンダーには1つしかない)。ほとんどの場合、テーマを変更します。2か所で変更する必要はありません。
Sassのようなものを @extend
構文は私が考えているものです。 Sassを使用してフルカレンダーCSSを簡単に作成できましたが、SassはどのようにしてMuiThemeにアクセスできますか?
おそらく私は反対のアプローチを取ることができます-MUIに「ここのこれらのクラス名はこれらのMUIクラスのようにスタイル設定する必要があります」と伝えます。
これをどのように解決するかについて具体的な提案はありますか?
これが私の提案です(明らかに、簡単ではありません)。 MUIテーマからスタイルを取得し、それに基づいてstyle
タグを生成します react-helmet
。イベントをうまく行うために、マップを行う「ラッパー」コンポーネントを作成しました。基本ルールのみを実装しましたが、他のすべてのルールに拡張できます。
このように、テーマで行う変更は、マップされたセレクターにも影響します。
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} />
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が壊れるなどの予期しない結果が生じる可能性があることにも言及する必要があります。それらを修正する方法を理解する必要があります。
動作するサンドボックスは次のとおりです: https://codesandbox.io/s/determined-frog-3loyf