web-dev-qa-db-fra.com

Comment appliquer des styles gérés Material-UI à des éléments non-Material-Ui, non-react?

J'ai une application où j'utilise Material UI et son fournisseur de thème (en utilisant JSS).

J'incorpore maintenant fullcalendar-react , qui n'est pas vraiment une bibliothèque à part entière React - c'est juste une mince React enveloppe de composant autour du code complet d'origine.

C'est-à-dire que je n'ai pas accès à des choses comme les accessoires de rendu pour contrôler la façon dont il stylise ses éléments.

Cependant, il vous donne directement accès aux éléments DOM, via un rappel qui est appelé lors de leur rendu (par exemple, la méthode eventRender ).

Voici un sandbox de démonstration de base.

enter image description here

Maintenant, ce que je veux faire, c'est que les composants du calendrier complet (par exemple, les boutons) partagent le même aspect et la même sensation que le reste de mon application.

Une façon de le faire est que je pouvais remplacer manuellement tous les styles en regardant les noms de classe qu'il utilise et en implémentant le style en conséquence.

Ou - je pourrais implémenter un thème Bootstrap - comme suggéré dans leur documentation.

Mais le problème avec l'une ou l'autre de ces solutions est que:

  1. Ce serait beaucoup de travail
  2. J'aurais des problèmes de synchronisation, si j'apportais des modifications à mon thème MUI et que j'oubliais de mettre à jour le thème du calendrier, ils auraient l'air différents.

Ce que je voudrais faire, c'est:

  • Convertissez comme par magie le thème MUI en un thème Bootstrap.
  • Ou créez un mappage entre les noms de classe MUI et les noms de classe de calendrier, quelque chose comme:
.fc-button = .MuiButtonBase-root.MuiButton-root.MuiButton-contained
.fc-button-primary= .MuiButton-containedPrimary

Cela ne me dérangerait pas d'avoir à masser les sélecteurs, etc. pour le faire fonctionner (par exemple. Par exemple - les boutons MUI ont deux portées internes, tandis que le calendrier complet n'en a qu'une). C'est surtout quand je change de thème - je ne veux pas avoir à le changer à deux endroits.

Utiliser quelque chose comme Sass avec son @extend la syntaxe serait ce que j'ai en tête. Je pourrais créer le CSS complet du calendrier avec Sass assez facilement - mais comment Sass aurait-il accès au MuiTheme?

Je pourrais peut-être adopter l'approche inverse - dire à MUI 'Hé, ces noms de classe ici devraient être stylisés comme ces classes MUI'.

Avez-vous des suggestions concrètes sur la façon de résoudre ce problème?

9
dwjohnston

Voici ma suggestion (évidemment, ce n'est pas simple). Prenez les styles du thème MUI et générez la balise style en fonction de celle-ci en utilisant react-helmet . Pour le faire bien, j'ai créé un composant "wrapper" qui fait la carte. J'ai implémenté uniquement la règle principale mais elle peut être étendue à toutes les autres.

De cette façon, toute modification que vous apporterez au thème affectera également les sélecteurs mappés.

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>
  );
}

Et l'utilisation de l'adaptateur

<MuiAdapter theme={theme} />

Démo de travail: https://codesandbox.io/s/reverent-mccarthy-3o856

screen shot

4
Mosh Feu

Vous pouvez créer un mappage entre les noms de classe MUI et les noms de classe de calendrier en passant par ref. Il est possible que ce ne soit pas ce que certains appellent les "meilleures pratiques" ... mais c'est une solution :). Notez que j'ai mis à jour votre composant d'un composant fonctionnel vers un composant de classe, mais vous pouvez accomplir cela avec des hooks dans un composant fonctionnel.

Ajouter des références

Ajoutez un ref à l'élément MUI que vous souhaitez définir comme référence, dans votre cas le Button.

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

Et un ref vers un div enveloppant autour du composant que vous souhaitez mapper. Vous ne pouvez pas l'ajouter directement au composant car cela ne nous donnerait pas accès à children.

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

Classes de carte

Depuis componentDidMount() ajoutez la logique dont vous avez besoin pour cibler le bon noeud DOM (pour votre cas, j'ai ajouté la logique pour type et matchingClass). Exécutez ensuite cette logique sur tous les nœuds DOM FullCalendar et remplacez le classList sur tous ceux qui correspondent.

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);
    }
  });
};

C'est peut-être un peu lourd, mais cela répond à vos exigences de preuve future lorsque vous mettrez à jour l'interface utilisateur du matériel. Cela vous permettrait également de modifier le classList lorsque vous le transmettez à un élément, ce qui présente des avantages évidents.

Mises en garde

Si le composant "mappé" (FullCalendar) mettait à jour les classes sur les éléments que vous ciblez (comme s'il ajoutait .is-selected À un bouton actuel) ou ajoutait de nouveaux boutons après le montage, vous auriez pour trouver un moyen de suivre les changements pertinents et de réexécuter la logique.

Je dois également mentionner que (évidemment) la modification des classes peut avoir des conséquences inattendues comme une rupture de l'interface utilisateur et vous devrez trouver comment les corriger.

Voici le sandbox de travail: https://codesandbox.io/s/determ-frog-3loyf

3
sallf