Как бы я применил стили, управляемые Material-UI, к элементам non-material-Ui, не реагирующим?

9

У меня есть приложение, в котором я использую Material UI и его поставщика тем (используя JSS).

Сейчас я включаю fullcalendar-response , который на самом деле не является полноценной библиотекой 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».

Какие-нибудь конкретные предложения о том, как бы я решил это?

dwjohnston
источник

Ответы:

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. Например: 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). Затем запустите эту логику на всех 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

sallf
источник
1
Это работает. Обратите внимание, что вам не нужно конвертировать в компонент класса - вы можете использовать хуки для этого.
dwjohnston
Вы правы, это можно сделать с помощью хуков в функциональном компоненте. Я обновил ответ, чтобы отразить это.
sallf
Хороший, довольно прагматичный подход. Но не совсем выполнимо, так как вам всегда будет нужен реф.
Аникет Чоудхури