跳至內容
+

建立主題組件

了解如何建立完全客製化的組件,以接受您應用程式的主題。

簡介

Joy UI 提供了強大的主題功能,讓您可以將自己的組件新增至主題,並將它們視為內建組件一樣處理。

如果您正在 Joy UI 之上建構組件庫,您可以按照下面的逐步指南建立一個自訂組件,該組件可以在多個專案中進行主題化。

或者,您可以使用提供的範本作為組件的起點。

逐步指南

本指南將引導您完成如何建構此統計組件,該組件會像內建的 Joy UI 組件一樣接受應用程式的主題

19,267
每月活躍使用者

1. 建立組件插槽

插槽讓您可以透過在主題的 styleOverrides中指定其各自的名稱來自訂組件的每個個別元素。

此統計組件由三個插槽組成

  • root:組件的容器
  • value:統計數字
  • unit:統計的單位或描述
19,267
value
每月活躍使用者
unit
root

使用帶有 nameslot 參數的 styled API 來建立插槽,如下所示

import * as React from 'react';
import { styled } from '@mui/joy/styles';

const StatRoot = styled('div', {
  name: 'JoyStat', // The component name
  slot: 'root', // The slot name
})(({ theme }) => ({
  display: 'flex',
  flexDirection: 'column',
  gap: theme.spacing(0.5),
  padding: theme.spacing(3, 4),
  backgroundColor: theme.vars.palette.background.surface,
  borderRadius: theme.vars.radius.sm,
  boxShadow: theme.vars.shadow.md,
}));

const StatValue = styled('div', {
  name: 'JoyStat',
  slot: 'value',
})(({ theme }) => ({
  ...theme.typography.h2,
}));

const StatUnit = styled('div', {
  name: 'JoyStat',
  slot: 'unit',
})(({ theme }) => ({
  ...theme.typography['body-sm'],
  color: theme.vars.palette.text.tertiary,
}));

2. 建立組件

使用上一步中建立的插槽組裝組件

// /path/to/Stat.js
import * as React from 'react';

const StatRoot = styled('div', {
  name: 'JoyStat',
  slot: 'root',
})();

const StatValue = styled('div', {
  name: 'JoyStat',
  slot: 'value',
})();

const StatUnit = styled('div', {
  name: 'JoyStat',
  slot: 'unit',
})();

const Stat = React.forwardRef(function Stat(props, ref) {
  const { value, unit, ...other } = props;

  return (
    <StatRoot ref={ref} {...other}>
      <StatValue>{value}</StatValue>
      <StatUnit>{unit}</StatUnit>
    </StatRoot>
  );
});

export default Stat;

此時,您將能夠像這樣將主題應用於 Stat 組件

import { extendTheme } from '@mui/joy/styles';

const theme = extendTheme({
  components: {
    // the component name defined in the `name` parameter
    // of the `styled` API
    JoyStat: {
      styleOverrides: {
        // the slot name defined in the `slot` and `overridesResolver` parameters
        // of the `styled` API
        root: {
          backgroundColor: '#121212',
        },
        value: {
          color: '#fff',
        },
        unit: {
          color: '#888',
        },
      },
    },
  },
});

3. 使用 ownerState 設定插槽樣式

當您需要設定基於插槽的 props 或內部狀態的樣式時,請將它們包裝在 ownerState 物件中,並將其作為 prop 傳遞給每個插槽。 ownerState 是一個特殊名稱,不會透過 styled API 擴展到 DOM。

variant prop 新增至 Stat 組件,並使用它來設定 root 插槽的樣式,如下所示

  const Stat = React.forwardRef(function Stat(props, ref) {
+   const { value, unit, variant, ...other } = props;
+
+   const ownerState = { ...props, variant };

    return (
-      <StatRoot ref={ref} {...other}>
-        <StatValue>{value}</StatValue>
-        <StatUnit>{unit}</StatUnit>
-      </StatRoot>
+      <StatRoot ref={ref} ownerState={ownerState} {...other}>
+        <StatValue ownerState={ownerState}>{value}</StatValue>
+        <StatUnit ownerState={ownerState}>{unit}</StatUnit>
+      </StatRoot>
    );
  });

然後您可以在插槽中讀取 ownerState,以根據 variant prop 設定其樣式。

  const StatRoot = styled('div', {
    name: 'JoyStat',
    slot: 'root',
-  })(({ theme }) => ({
+  })(({ theme, ownerState }) => ({
    display: 'flex',
    flexDirection: 'column',
    gap: theme.spacing(0.5),
    padding: theme.spacing(3, 4),
    backgroundColor: theme.palette.background.paper,
    borderRadius: theme.shape.borderRadius,
    boxShadow: theme.shadows[2],
    letterSpacing: '-0.025em',
    fontWeight: 600,
+   ...ownerState.variant === 'outlined' && {
+    border: `2px solid ${theme.palette.divider}`,
+   },
  }));

4. 支援主題預設 props

若要為不同的專案自訂組件的預設 props,您需要使用 useThemeProps API。

+ import { useThemeProps } from '@mui/joy/styles';

- const Stat = React.forwardRef(function Stat(props, ref) {
+ const Stat = React.forwardRef(function Stat(inProps, ref) {
+   const props = useThemeProps({ props: inProps, name: 'JoyStat' });
    const { value, unit, ...other } = props;

    return (
      <StatRoot ref={ref} {...other}>
        <StatValue>{value}</StatValue>
        <StatUnit>{unit}</StatUnit>
      </StatRoot>
    );
  });

然後您可以像這樣自訂組件的預設 props

import { extendTheme } from '@mui/joy/styles';

const theme = extendTheme({
  components: {
    JoyStat: {
      defaultProps: {
        variant: 'outlined',
      },
    },
  },
});

TypeScript

如果您使用 TypeScript,則必須為組件 props 和 ownerState 建立介面

interface StatProps {
  value: number | string;
  unit: string;
  variant?: 'outlined';
}

interface StatOwnerState extends StatProps {
  // …key value pairs for the internal state that you want to style the slot
  // but don't want to expose to the users
}

然後您可以在組件和插槽中使用它們。

const StatRoot = styled('div', {
  name: 'JoyStat',
  slot: 'root',
})<{ ownerState: StatOwnerState }>(({ theme, ownerState }) => ({
  display: 'flex',
  flexDirection: 'column',
  gap: theme.spacing(0.5),
  padding: theme.spacing(3, 4),
  backgroundColor: theme.palette.background.paper,
  borderRadius: theme.shape.borderRadius,
  boxShadow: theme.shadows[2],
  letterSpacing: '-0.025em',
  fontWeight: 600,
  // typed-safe access to the `variant` prop
  ...(ownerState.variant === 'outlined' && {
    border: `2px solid ${theme.palette.divider}`,
    boxShadow: 'none',
  }),
}));

// …do the same for other slots

const Stat = React.forwardRef<HTMLDivElement, StatProps>(function Stat(inProps, ref) {
  const props = useThemeProps({ props: inProps, name: 'JoyStat' });
  const { value, unit, variant, ...other } = props;

  const ownerState = { ...props, variant };

  return (
    <StatRoot ref={ref} ownerState={ownerState} {...other}>
      <StatValue ownerState={ownerState}>{value}</StatValue>
      <StatUnit ownerState={ownerState}>{unit}</StatUnit>
    </StatRoot>
  );
});

最後,將 Stat 組件新增至主題類型。

import { Theme, StyleOverrides } from '@mui/joy/styles';
import { StatProps, StatOwnerState } from '/path/to/Stat';

declare module '@mui/joy/styles' {
  interface Components {
    JoyStat?: {
      defaultProps?: Partial<StatProps>;
      styleOverrides?: StyleOverrides<StatProps, StatOwnerState, Theme>;
    };
  }
}

範本

此範本是上述逐步指南的最終產品,示範如何建構可以使用主題設定樣式的自訂組件,就像它是內建組件一樣。

1.9 百萬
收藏
5.1 百萬
瀏覽次數
按下 Enter 開始編輯