web-dev-qa-db-ja.com

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

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

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

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

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

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

enter image description here

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

これを行う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か所で変更する必要はありません。

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

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

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

9
dwjohnston

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

screen shot

4
Mosh Feu

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ノードをターゲットにするために必要なロジックを追加します(あなたのケースでは、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

3
sallf