Come applicherei gli stili gestiti dall'IU dei materiali a elementi non materiali, non reattivi?


9

Ho un'applicazione in cui sto usando l'IU materiale e il suo provider di temi (usando JSS).

Ora sto incorporando fullcalendar-reazioni , che non è in realtà una libreria React a tutti gli effetti - è solo un sottile wrapper di componenti React attorno al codice fullcalendar originale.

Vale a dire, che non ho accesso a cose come i puntelli di rendering per controllare il modo in cui modella i suoi elementi.

Tuttavia, ti dà accesso agli elementi DOM direttamente, tramite un callback che viene chiamato quando li esegue il rendering (es. Il metodo eventRender ).

Ecco un sandbox demo di base.

inserisci qui la descrizione dell'immagine

Ora quello che voglio fare è fare in modo che i componenti del Calendario completo (ad es. I pulsanti) condividano lo stesso aspetto del resto della mia applicazione.

Un modo per farlo è che potrei sovrascrivere manualmente tutti gli stili guardando i nomi delle classi che sta usando e implementando lo stile di conseguenza.

Oppure - Potrei implementare un tema Bootstrap - come suggerito nella loro documentazione.

Ma il problema con una di queste soluzioni è che:

  1. Sarebbe molto lavoro
  2. Avrei problemi di sincronizzazione, se avessi apportato modifiche al mio tema MUI e avessi dimenticato di aggiornare il tema del calendario, avrebbero avuto un aspetto diverso.

Quello che vorrei fare è:

  • Converti magicamente il tema MUI in un tema Bootstrap.
  • Oppure crea una mappatura tra i nomi delle classi MUI e i nomi delle classi del calendario, in qualche modo:
.fc-button = .MuiButtonBase-root.MuiButton-root.MuiButton-contained
.fc-button-primary= .MuiButton-containedPrimary

Non mi dispiacerebbe dover massaggiare i selettori ecc. Per farlo funzionare (ad esempio, i pulsanti MUI hanno due span interni, mentre il calendario completo ne ha solo uno). Si tratta principalmente di quando cambio tema, non voglio cambiarlo in due punti.

Usare qualcosa come Sass con la sua @extendsintassi sarebbe ciò che ho in mente. Potrei creare il CSS completo del calendario con Sass abbastanza facilmente - ma come farebbe Sass ad accedere a MuiTheme?

Forse potrei adottare un approccio opposto: dire a MUI "Ehi, questi nomi di classe qui dovrebbero essere definiti come queste classi MUI".

Qualche suggerimento concreto su come lo risolverei?

Risposte:


4

Ecco il mio suggerimento (ovviamente, non è semplice). Prendi gli stili dal tema MUI e genera styletag basati su di esso usando react-helmet. Per farlo bene, ho creato un componente "wrapper" che esegue la mappa. Ho implementato solo la regola principale ma può essere estesa a tutte le altre.

In questo modo, qualsiasi modifica apportata al tema influirà anche sui selettori mappati.

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

E l'uso dell'adattatore

<MuiAdapter theme={theme} />

Demo funzionante: https://codesandbox.io/s/reverent-mccarthy-3o856

immagine dello schermo


Mi piace questo pensare. Il problema con questa soluzione è che dovresti comunque implementare da solo tutti gli stili (ad es. Il colore al passaggio del mouse, l'imbottitura, il raggio del bordo ecc.). Se, ad esempio, hai deciso di sottolineare il testo del pulsante, ricordati di aggiungere la text-decorationproprietà al casco.
dwjohnston,

Capisco quello che chiedi. Ho provato ad adottare un approccio diverso e manipolare i styletag MUI e aggiungerli .fc-selettori in base alla mappa, ma hanno conflitti nei livelli di base (come display, paddingecc.), Quindi non vedo come funzionerà anche se avessi il opzione per migrarlo in scss. Ad esempio: codesandbox.io/s/charming-sound-3xmj9
Mosh Feu

Haha - ora questo mi piace. Daremo un'occhiata più avanti.
dwjohnston,

3

È possibile creare una mappatura tra i nomi delle classi MUI e i nomi delle classi del calendario passando per ref'. È possibile che questo non sia ciò che alcuni chiamerebbero "best practice" ... ma è una soluzione :). Si noti che ho aggiornato il componente da un componente funzionale a un componente di classe, ma è possibile farlo con gli hook in un componente funzionale.

Aggiungi riferimenti

Aggiungi refa all'elemento MUI che vuoi impostare come riferimento, nel tuo caso il Button.

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

E refa un divgiro attorno al componente che si desidera mappare. Non puoi aggiungerlo direttamente al componente poiché non ci darebbe accesso children.

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

Classi di mappe

Da componentDidMount()aggiungere qualsiasi logica sia necessaria per indirizzare il nodo DOM corretto (nel tuo caso, ho aggiunto la logica per typee matchingClass). Quindi esegui quella logica su tutti i nodi DOM FullCalendar e sostituisci classListsu ogni corrispondente.

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

Questo è forse un po 'pesante, ma soddisfa i requisiti di prova futuri per l'aggiornamento dell'interfaccia utente del materiale. Ti consentirebbe anche di modificare il classListpassaggio quando lo passi a un elemento, il che ha evidenti benefici.

Avvertenze

Se il componente "mappato su" ( FullCalendar) aggiorna le classi sugli elementi target (come se fosse aggiunto .is-selecteda un pulsante corrente) o aggiunge nuovi pulsanti dopo il montaggio, dovresti trovare un modo per tenere traccia delle modifiche pertinenti ed eseguire nuovamente la logica.

Dovrei anche menzionare che (ovviamente) l'alterazione delle classi potrebbe avere conseguenze non intenzionali come un'interruzione dell'interfaccia utente e dovrai capire come risolverle.

Ecco la sandbox funzionante: https://codesandbox.io/s/determined-frog-3loyf


1
Questo funziona Nota che non devi convertirti in un componente di classe: puoi usare hook per farlo.
dwjohnston,

Ya hai ragione, questo può essere fatto con ganci in un componente funzionale. Ho aggiornato la risposta per riflettere ciò.
sallf

Bello, piuttosto l'approccio pragmatico. Ma non è fattibile in quanto avresti sempre bisogno di un riferimento.
Aniket Chowdhury,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.