Material-UIで管理されたスタイルを非Material-UI、非React要素に適用するにはどうすればよいですか?


9

Material UIとそのテーマプロバイダー(JSSを使用)を使用しているアプリケーションがあります。

私は今、完全に本格的なReactライブラリーではないfullcalendar-reactを組み込んでいます。これは、元のフルカレンダーコードの薄いReactコンポーネントラッパーにすぎません。

つまり、要素のスタイルを制御するレンダープロップなどにアクセスすることはできません。

ただし、レンダリング時に呼び出されるコールバック(例:eventRenderメソッド)を介して、DOM要素に直接アクセスできます。

これが基本的なデモサンドボックスです。

ここに画像の説明を入力してください

次に、フルカレンダーコンポーネント(ボタンなど)を他のアプリケーションと同じルックアンドフィールにしたいと思います。

これを行う1つの方法は、使用しているクラス名を確認し、それに応じてスタイルを実装することにより、すべてのスタイルを手動でオーバーライドできることです。

または、ドキュメントで提案されているように、Bootstrapテーマを実装することもできます。

しかし、これらのソリューションのいずれかの問題は、次のとおりです。

  1. 大変な作業になるでしょう
  2. MUIテーマに変更を加えて、カレンダーのテーマを更新するのを忘れた場合、それらが異なるように見えるため、同期の問題が発生します。

私がしたいのは次のいずれかです:

  • MUIテーマを魔法のようにBootstrapテーマに変換します。
  • または、次のようなMUIクラス名とカレンダークラス名の間のマッピングを作成します。
.fc-button = .MuiButtonBase-root.MuiButton-root.MuiButton-contained
.fc-button-primary= .MuiButton-containedPrimary

セレクターなどをマッサージして機能させる必要はありません(たとえば、MUIボタンには2つの内部スパンがありますが、フルカレンダーには1つしかない)。ほとんどの場合、テーマを変更します。2か所で変更する必要はありません。

@extend構文でSassのようなものを使用することは、私が考えていることです。Sassを使用してフルカレンダーCSSを簡単に作成できましたが、SassはどのようにしてMuiThemeにアクセスできますか?

おそらく私は反対のアプローチを取ることができます-MUIに「ここのこれらのクラス名はこれらのMUIクラスのようにスタイル設定する必要があります」と伝えます。

これをどのように解決するかについて具体的な提案はありますか?

回答:


4

これが私の提案です(明らかに、簡単ではありません)。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} />

作業デモ:https : //codesandbox.io/s/reverent-mccarthy-3o856

スクリーンショット


私はこの考え方が好きです。このソリューションの問題は、基本的にすべてのスタイル(ホバー色、パディング、境界線の半径など)を自分で実装することです。たとえば、ボタンのテキストに下線を引く必要があると判断した場合は、text-decorationプロパティをヘルメットに追加することを忘れないでください。
dwjohnston

私はあなたが何を求めているのか理解しています。私は別のアプローチを取ると、MUIの操作を試みたstyleタグを、それら追加.fc-マップに従ってセレクタを、彼らは、ベースレベルの競合を(のような持ってdisplaypadding私はあなたが持っているならば、それも動作する方法を見ていないので、など)それをscssで移行するオプション。例:codesandbox.io/s/charming-sound-3xmj9
Mosh Feu

はは-今これが好きです。後で詳しく見ていきます。
dwjohnston

3

を介してMUIクラス名とカレンダークラス名の間のマッピングを作成できますref。これが「ベストプラクティス」と呼ばれるものではない可能性もありますが、それは解決策です:)。コンポーネントを機能コンポーネントからクラスコンポーネントに更新しましたが、機能コンポーネントのフックを使用してこれを実行できます。

参照を追加

ref参照として設定するMUI要素(この場合は)にを追加します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


1
これは機能します。クラスコンポーネントに変換する必要がないことに注意してください。これを行うにはフックを使用できます。
dwjohnston

そうです、これは機能コンポーネントのフックで行うことができます。私はそれを反映するように答えを更新しました。
sallf

ニース、かなり実用的なアプローチ。ただし、常に参照が必要になるため、実際には実現できません。
Aniket Chowdhury
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.