跳到主要內容
+

自訂 MUI Base 元件

有多種方法可以自訂 MUI Base 元件,從套用自訂 CSS 規則到使用 Hook 建構完全自訂的元件。

使用 MUI Base,您可以自由決定要自訂元件的結構和樣式的程度。

為元件設定樣式

本節回顧幾種可用的自訂方法:套用自訂 CSS 規則、覆寫預設子元件插槽、自訂插槽 props,以及使用 Hook 建構完全自訂的元件。

該選擇哪個選項?

眾多的選項可能會讓人不知所措,尤其是當您是 MUI Base 的新手時。那麼,該如何決定使用哪個選項呢?

首先要做的決定是是否使用無樣式元件或 Hook。Hook 更適合用於建立可以進一步自訂的元件庫。例如,我們自己的 Joy UI 就是使用 MUI Base 的 Hook 實作的。Hook 也作為許多 Material UI 元件的基礎,並且該程式庫的未來版本將更廣泛地使用它們。

如果您不需要使您的元件庫可自訂(例如,透過公開 slotProps),那麼無樣式元件可能是更好的選擇,因為它們更簡單。

在選擇無樣式元件之後,還有一個決定要做:如何為它們設定樣式。答案取決於專案中使用的樣式解決方案

純 CSS、Sass、Less

...或任何其他編譯為 CSS 的東西

您可以使用內建類別為元件設定樣式,或者指定您自己的類別並在樣式表中參考它們。

CSS Modules

當使用 CSS Modules 時,最簡單的方法是使用 slotProps 指定自訂類別,如下所示

import clsx from 'clsx';
import { Switch as BaseSwitch, SwitchOwnerState } from '@mui/base/Switch';
import classes from './styles.module.css';

export default function Switch(props) {
  const slotProps = {
    root: (ownerState: SwitchOwnerState) => ({
      className: clsx(classes.root, {
        [classes.checked]: ownerState.checked,
        [classes.disabled]: ownerState.disabled,
      }),
    }),
    thumb: { className: classes.thumb },
    track: { className: classes.track },
    input: { className: classes.input },
  };

  return <BaseSwitch {...props} slotProps={slotProps} />;
}

在此範例中,我們使用 clsx 實用工具來減少有條件地套用類別名稱所需的工作量。

Tailwind CSS

使用 slotProps 套用自訂樣式,使用 Tailwind CSS,如下所示

import { Switch as BaseSwitch, SwitchOwnerState } from '@mui/base/Switch';

export default function Switch(props) {
  const slotProps = {
    root: (ownerState: SwitchOwnerState) => ({
      className: `inline-block w-8 h-5 rounded-full cursor-pointer relative ${
        ownerState.checked ? 'bg-cyan-500' : 'bg-zinc-400'
      }`,
    }),
    thumb: (ownerState: SwitchOwnerState) => ({
      className: `bg-white block w-3.5 h-3.5 rounded-full relative top-[3px] ${
        ownerState.checked ? 'left-[3px]' : 'left-[14px]'
      }`,
    }),
    input: { className: 'absolute w-full h-full inset-0 opacity-0 z-10 m-0' },
  };

  return <BaseSwitch {...props} slotProps={slotProps} />;
}

請參閱我們的使用 Tailwind CSS 指南,以取得有關整合 MUI Base 和 Tailwind CSS 的更多資訊。

Styled components

如果您將 CSS-in-JS 解決方案與類似 styled-components 的 API(例如 MUI SystemEmotion)一起使用,最好的方法是使用 slots prop 提供樣式化的子元件,如下面的示範所示。

或者,您可以將整個無樣式元件包裝在 styled 實用工具中,並使用 CSS 類別來針對個別子元件

按下 Enter 開始編輯

套用自訂 CSS 規則

如果您對元件的呈現 HTML 的預設結構感到滿意,則可以將自訂樣式套用到元件的類別。

每個元件都有自己的一組類別。有些類別是靜態的,也就是說它們始終存在於元件上。其他類別是有條件地套用的——例如 base--disabled,它僅在元件被停用時存在。

每個元件的 API 文件都列出了元件使用的所有類別。此外,您可以匯入一個 [componentName]Classes 物件,該物件描述了給定元件使用的所有類別,如下面的示範所示

按下 Enter 開始編輯

如果您不使用這些類別,您可以透過停用它們來清理 DOM。請參閱停用預設 CSS 類別以取得說明。

覆寫子元件插槽

如果您想變更元件的呈現 HTML 結構,可以使用 slots 和/或 component prop 覆寫預設子元件(「插槽」)——請參閱 Base 用法頁面上的「共用 props」以取得更多詳細資訊。

以下示範使用 Switch 來展示如何透過將樣式套用到其三個子元件插槽來建立樣式化元件:rootthumbinput

請注意,雖然此示範使用 MUI System 作為樣式解決方案,但您可以自由選擇任何替代方案。

按下 Enter 開始編輯

您在 slots prop 中傳遞的元件會從頂層元件(「擁有者」)接收 ownerState prop。依照慣例,它包含傳遞給擁有者的所有 props,並與其呈現狀態合併。

例如

<Switch slots={{ thumb: MyCustomThumb }} data-foo="42" />

在這種情況下,MyCustomThumb 元件接收具有以下資料的 ownerState 物件

{
  checked: boolean;
  disabled: boolean;
  focusVisible: boolean;
  readOnly: boolean;
  'data-foo': string;
}

您可以使用此物件來為您的元件設定樣式。

如果您需要使用 ownerState 將某些 props 傳播到第三方元件,則必須為此目的建立自訂包裝器。但是,如果您不需要 ownerState 而只想解決錯誤,則可以使用 prepareForSlot 實用工具

按下 Enter 開始編輯

自訂插槽 props

使用 slotProps prop 自訂內部元件 props。最常見的用例是設定類別名稱,但您可以設定任何 prop,包括事件處理常式。

以下範例示範如何將自訂類別新增到 Switch 的兩個插槽

function Switch(props: SwitchProps) {
  const slotProps: SwitchProps['slotProps'] = {
    thumb: {
      className: 'my-thumb',
    },
    track: {
      className: 'my-track',
    },
  };

  return <Switch {...props} slotProps={slotProps} />;
}

switch:thumbswitch:class 是無條件新增的——它們將始終存在於 Switch 元件上。

您可能需要僅在元件處於特定狀態時才套用類別。一個很好的例子是根據 Switch 的 checked 狀態將 onoff 類別新增到 Switch,如下面的示範所示

按下 Enter 開始編輯

在這裡,根插槽接收的是回呼函式,而不是具有 props 的物件。它唯一的參數是 ownerState,它是一個描述「擁有者元件」(在本例中為 Switch)狀態的物件。ownerState 保留擁有者元件的所有 props(在適用情況下套用預設值),並透過元件的內部狀態進行擴充。就 Select 而言,額外資訊包括 checkeddisabledfocusVisiblereadOnly 布林值欄位。

使用 Hook 建立自訂元件

如果您需要完全控制元件的呈現 HTML 結構,可以使用 Hook 建構它。

Hook 讓您可以存取元件使用的邏輯,但沒有任何預設結構。請參閱 Base 用法頁面上的「元件與 Hook」以取得更多詳細資訊。

Hook 傳回元件的目前狀態(例如 checkeddisabledopen 等),並提供傳回您可以套用到完全自訂元件的 props 的函式。

Switch 而言,該元件附帶 useSwitch Hook,它在沒有任何結構的情況下為您提供所有功能。

它傳回以下物件

{
  checked: Readonly<boolean>;
  disabled: Readonly<boolean>;
  readOnly: Readonly<boolean>;
  focusVisible: Readonly<boolean>;
  getInputProps: (otherProps?: object) => SwitchInputProps;
}

checkeddisabledreadOnlyfocusVisible 欄位代表 Switch 的狀態。使用它們將樣式套用到您的 HTML 元素。

getInputProps 函式可用於取得要放置在 HTML <input> 上的 props,以使 Switch 具有無障礙功能。

按下 Enter 開始編輯

停用預設 CSS 類別

如果您不需要元件上的內建類別,您可以停用它們。這將清理 DOM,並且如果您套用自己的類別或使用 CSS-in-JS 解決方案為元件設定樣式,則這可能特別有用。若要執行此操作,請將您的元件包裝在 ClassNameConfigurator 元件中(從 @mui/base/utils 匯入)

<ClassNameConfigurator disableDefaultClasses>
  <Button>I'm classless!</Button>
</ClassNameConfigurator>

檢查以下示範中的元素以查看差異

按下 Enter 開始編輯