Як я можу застосувати керовані стилем Material-UI до елементів, що не відповідають матеріалу, що не реагують?


9

У мене є додаток, де я використовую інтерфейс матеріалу та його постачальник тем (використовую JSS).

Зараз я включаю fullcalendar-react , який насправді не є повноцінною бібліотекою React - це лише тонка обгортка компонентів React навколо оригінального коду fullcalendar.

Тобто, у мене немає доступу до таких речей, як рендеринг, щоб контролювати, як він стилізує свої елементи.

Однак він надає вам доступ до елементів DOM безпосередньо через зворотний виклик, який викликається, коли він надає їх (наприклад, метод eventRender ).

Ось основна демонстраційна скринька.

введіть тут опис зображення

Тепер я хочу зробити так, щоб компоненти Full Calendar (наприклад, кнопки) мали такий же зовнішній вигляд, як і решта моєї програми.

Один із способів зробити це - це те, що я міг би вручну змінити всі стилі, переглянувши назви класів, які він використовує, і відповідно реалізувати стиль.

Або - я міг би реалізувати тему Bootstrap - як це запропоновано в їхній документації.

Але проблема будь-якого з цих рішень полягає в тому, що:

  1. Було б багато роботи
  2. У мене були б проблеми з синхронізацією, якби я змінив свою тему MUI і забув оновити тему календаря, вони виглядали б інакше.

Що я хотів би зробити:

  • Магічне перетворення теми MUI в тему Bootstrap.
  • Або створіть відображення між іменами класів MUI та назвами класів календаря, наприклад:
.fc-button = .MuiButtonBase-root.MuiButton-root.MuiButton-contained
.fc-button-primary= .MuiButton-containedPrimary

Я б не заперечував про необхідність масажувати селектори тощо, щоб змусити його працювати (наприклад, кнопки MUI мають два внутрішні проміжки, тоді як у повного календаря лише один). В основному це стосується коли я зміню тему - не хочу, щоб я її міняв у двох місцях.

Використовувати щось на кшталт Sass з його @extendсинтаксисом - це те, що я маю на увазі. Я міг би створити CSS з повним календарем за допомогою Sass досить легко - але як би 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

Я розумію, про що ви просите. Я спробував застосувати інший підхід і маніпулювати styleтегами MUI і додати їх .fc-селектори відповідно до карти, але вони мають конфлікти в базових рівнях (наприклад display, paddingтощо), тому я не бачу, як це буде працювати, навіть якщо у вас буде можливість перемістити його в scss. Наприклад: codeandbox.io/s/charming-sound-3xmj9
Mosh Feu

Ха-ха - тепер це мені подобається. Я згодом краще ознайомлюсь.
dwjohnston

3

Ви можете створити відображення між іменами класів MUI та назвами класів календаря, перейшовши через refs. Цілком можливо, що це не те, що деякі називали б "найкращою практикою" ... але це рішення :). Зауважте, що я оновив ваш компонент з функціонального компонента до компонент класу, але ви могли це досягти за допомогою гачків у функціональному компоненті.

Додати реф

Додайте 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). Потім запустіть цю логіку на всіх вузлах DOM FullCalendar і замініть 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);
    }
  });
};

Це, можливо, трохи важко, але це відповідає вашій майбутній вимозі доказування, коли ви оновили оновлення інтерфейсу матеріалу. Це також дозволить вам змінити, classListколи ви передаєте його елементу, який має очевидні переваги.

Коваджі

Якщо компонент 'mapped to' ( FullCalendarоновлений клас) оновив класи на націлені елементи (наприклад, якщо він був доданий .is-selectedдо поточної кнопки) або додає нові кнопки після монтажу, то вам доведеться винайти спосіб відстеження відповідних змін та повторного повторення логіка.

Я також повинен зазначити, що (очевидно) зміна класів може мати непередбачувані наслідки, такі як порушення інтерфейсу користувача, і вам доведеться з'ясувати, як їх виправити.

Ось робоча пісочниця: https://codesandbox.io/s/determined-frog-3loyf


1
Це працює. Зауважте, що вам не потрібно перетворювати на компонент класу - для цього можна використовувати гачки.
dwjohnston

Так, ви праві, це можна зробити за допомогою гачків у функціональній складовій. Я оновив відповідь, щоб це відобразити.
sallf

Гарний, цілком прагматичний підхід. Але не дуже реально, тому що вам завжди потрібна посилання.
Aniket Chowdhury
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.