簡體   English   中英

我如何將 Material-UI 托管樣式應用於非 Material-ui、非 React 元素?

[英]How would I apply Material-UI managed styles to non-material-ui, non-react elements?

我有一個應用程序,我在其中使用 Material UI 及其主題提供程序(使用 JSS)。

我現在正在合並fullcalendar-react ,它並不是一個真正成熟的 React 庫——它只是一個圍繞原始 fullcalendar 代碼的薄 React 組件包裝器。

也就是說,我無法訪問諸如渲染道具之類的東西來控制它如何設置其元素的樣式。

但是,它確實讓您可以通過在呈現它們時調用的回調(例如eventRender 方法)直接訪問 DOM 元素。

這是一個基本的演示沙箱。

在此處輸入圖片說明

現在我想要做的是使完整日歷組件(例如,按鈕)與我的應用程序的其余部分共享相同的外觀和感覺。

一種方法是,我可以通過查看它使用的類名並相應地實現樣式來手動覆蓋所有樣式。

或者 -我可以實現一個 Bootstrap 主題 - 正如他們的文檔中所建議的那樣。

但是這些解決方案中的任何一個的問題在於:

  1. 這將是很多工作
  2. 我會遇到同步問題,如果我更改了我的 MUI 主題並忘記更新日歷主題,它們看起來會有所不同。

我想做的是:

  • 神奇地將 MUI 主題轉換為 Bootstrap 主題。
  • 或者在 MUI 類名和日歷類名之間創建一個映射,例如:
.fc-button = .MuiButtonBase-root.MuiButton-root.MuiButton-contained
.fc-button-primary= .MuiButton-containedPrimary

我不介意必須按摩選擇器等以使其工作(例如 - MUI 按鈕有兩個內部跨度,而完整日歷只有一個)。 這主要是關於我何時更改主題 - 不想在兩個地方更改它。

使用 Sass 之類的東西和它的@extend語法就是我的想法。 我可以很容易地使用 Sass 創建完整的日歷 CSS - 但是 Sass 如何訪問 MuiTheme?

也許我可以采取相反的方法 - 告訴 MUI '嘿,這里的這些類名的樣式應該像這些 MUI 類一樣'。

關於我如何解決這個問題的任何具體建議?

這是我的建議(顯然,這不是直接的)。 從 MUI 主題中獲取樣式並使用react-helmet基於它生成style標簽。 為了很好地完成它,我創建了一個“包裝器”組件來完成地圖。 我只實施了主要規則,但它可以擴展到所有其他規則。

這樣,您在主題中所做的任何更改也會影響映射的選擇器。

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

截屏

您可以通過遍歷ref來創建 MUI 類名和日歷類名之間的映射。 這可能不是某些人所說的“最佳實踐”……但這是一個解決方案:)。 請注意,我將您的組件從功能組件更新為類組件,但您可以使用功能組件中的鈎子來完成此操作。

添加參考

ref添加到要設置為引用的 MUI 元素,在您的情況下為Button

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

以及對要映射到的組件周圍的環繞divref 您不能將它直接添加到組件中,因為這不會讓我們訪問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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM