Skip to Content
文档

插槽配方

了解如何使用插槽配方设置多个部件的样式。

概述

¥Overview

当你需要将样式变化应用于组件的多个部分时,插槽配方会非常方便。

¥Slot Recipes come in handy when you need to apply style variations to multiple parts of a component.

插槽配方包含以下属性:

¥A slot recipe consists of these properties:

  • className:要附加到组件插槽的 className 前缀

  • slots:用于设置样式的组件数组

  • base:每个插槽的基本样式

  • variants:每个插槽的不同视觉样式

  • defaultVariants:组件的默认变体

  • compoundVariants:每个插槽的复合变体组合和样式覆盖。

定义配方

¥Defining the recipe

使用 defineSlotRecipe 标识函数创建插槽配方。

¥Use the defineSlotRecipe identity function to create a slot recipe.

checkbox.recipe.ts

import { defineSlotRecipe } from "@chakra-ui/react"

export const checkboxSlotRecipe = defineSlotRecipe({
  slots: ["root", "control", "label"],
  base: {
    root: { display: "flex", alignItems: "center", gap: "2" },
    control: { borderWidth: "1px", borderRadius: "sm" },
    label: { marginStart: "2" },
  },
  variants: {
    size: {
      sm: {
        control: { width: "8", height: "8" },
        label: { fontSize: "sm" },
      },
      md: {
        control: { width: "10", height: "10" },
        label: { fontSize: "md" },
      },
    },
  },
})

使用配方

¥Using the recipe

在组件中,有两种方法可以使用配方:

¥There are two ways to use the recipe in a component:

  • 直接在带有 useSlotRecipe 的组件中

  • 作为复合组件与 createSlotRecipeContext 一起使用(推荐)

info

需要添加 "use client" 指令才能使用 useSlotRecipe Hooks 或 createSlotRecipeContext 函数。这是因为它们在底层依赖于像 useContextuseInsertionEffect 这样的 React Hook。

¥Adding the "use client" directive is required to use the useSlotRecipe hook or createSlotRecipeContext function. This is because they rely on react hooks like useContext and useInsertionEffect under the hood.

直接在组件中

¥Directly in component

使用 useSlotRecipe 钩子获取组件的配方。然后,使用变体属性调用配方以获取样式。

¥Use the useSlotRecipe hook to get the recipe for a component. Then, call the recipe with its variant props to get the styles.

checkbox.tsx

"use client"

import { chakra, useSlotRecipe } from "@chakra-ui/react"
import { checkboxSlotRecipe } from "./checkbox.recipe"

export const Checkbox = (props) => {
  const { size, ...restProps } = props

  const recipe = useSlotRecipe({ recipe: checkboxSlotRecipe })
  const styles = recipe({ size })

  return (
    <chakra.label css={styles.root}>
      <chakra.input type="checkbox" css={styles.control} {...restProps} />
      <chakra.span css={styles.label}>Checkbox Label</chakra.span>
    </chakra.label>
  )
}

splitVariantProps

注意 size 属性是如何从要传递给配方的属性中解构出来的。更智能的方法是自动将配方属性与组件属性分离。

¥Notice how the size prop was destructured from the props to be passed to the recipe. A smarter approach would be to automatically split the recipe props from the component props.

为此,请使用 recipe.splitVariantProps 函数将配方属性与组件属性分离。

¥To do that, use the recipe.splitVariantProps function to split the recipe props from the component props.

checkbox.tsx

"use client"

import { chakra, useSlotRecipe } from "@chakra-ui/react"
import { checkboxSlotRecipe } from "./checkbox.recipe"

export const Checkbox = (props) => {
  const recipe = useSlotRecipe({ recipe: checkboxSlotRecipe })
  const [recipeProps, restProps] = recipe.splitVariantProps(props)
  const styles = recipe(recipeProps)

  //...
}

TypeScript

要推断配方变体属性的类型,请使用 RecipeVariantProps 类型助手。

¥To infer the recipe variant prop types, use the RecipeVariantProps type helper.

checkbox.tsx

import type { RecipeVariantProps } from "@chakra-ui/react"
import { checkboxSlotRecipe } from "./checkbox.recipe"

type CheckboxVariantProps = RecipeVariantProps<typeof checkboxSlotRecipe>

export interface CheckboxProps
  extends React.PropsWithChildren<CheckboxVariantProps> {}

创建复合组件

¥Create compound components

将配方传递给 createSlotRecipeContext 函数以创建插槽配方上下文。

¥Pass the recipe to the createSlotRecipeContext function to create a slot recipe context.

然后,使用 withProviderwithContext 函数创建共享相同上下文的复合组件。

¥Then, use the withProvider and withContext functions to create the compound components that share the same context.

info

你需要手动输入 withProviderwithContext 的泛型。此方法旨在优化 TypeScript 性能。自动推断虽然方便,但由于所涉及类型的复杂性,会降低 TypeScript 编译速度。

¥You will need to manually type the generics for withProvider and withContext. This approach is designed to optimize TypeScript performance. Auto-inference, while convenient, would slow down TypeScript compilation due to the complexity of the types involved.

checkbox.tsx

"use client"

import { createSlotRecipeContext } from "@chakra-ui/react"
import { checkboxSlotRecipe } from "./checkbox.recipe"

const { withProvider, withContext } = createSlotRecipeContext({
  recipe: checkboxSlotRecipe,
})

interface CheckboxRootProps
  extends HTMLChakraProps<
    "label",
    RecipeVariantProps<typeof checkboxSlotRecipe>
  > {}
export const CheckboxRoot = withProvider<HTMLLabelElement, CheckboxRootProps>(
  "label",
  "root",
)

interface CheckboxControlProps extends HTMLChakraProps<"input"> {}
export const CheckboxControl = withContext<
  HTMLInputElement,
  CheckboxControlProps
>("input", "control")

interface CheckboxLabelProps extends HTMLChakraProps<"span"> {}
export const CheckboxLabel = withContext<HTMLSpanElement, CheckboxLabelProps>(
  "span",
  "label",
)

将 variant 属性传递给 "根" 组件,以便应用样式。

¥Pass the variant props to the "root" component that to apply the styles.

注意:根组件是使用 withProvider 函数的组件。

app.tsx

const App = () => {
  return (
    <CheckboxRoot size="md">
      <CheckboxControl />
      <CheckboxLabel />
    </CheckboxRoot>
  )
}

无样式属性

¥unstyled prop

此方法支持使用 unstyled 属性移除配方应用的样式。

¥This approach supports the use of the unstyled prop to remove the styles applied by the recipe.

checkbox.tsx

<CheckboxRoot unstyled>
  <CheckboxControl />
  <CheckboxLabel />
</CheckboxRoot>

TypeScript

要推断配方变体属性的类型,请使用 RecipeVariantProps 类型助手。

¥To infer the recipe variant prop types, use the RecipeVariantProps type helper.

import type { RecipeVariantProps, UnstyledProp } from "@chakra-ui/react"
import { checkboxSlotRecipe } from "./checkbox.recipe"

type CheckboxVariantProps = RecipeVariantProps<typeof checkboxSlotRecipe>

export interface CheckboxProps
  extends React.PropsWithChildren<CheckboxVariantProps>,
    UnstyledProp {}

复合变体

¥Compound Variants

使用 compoundVariants 属性定义一组基于其他变体组合应用的变体。

¥Use the compoundVariants property to define a set of variants that are applied based on a combination of other variants.

checkbox.recipe.ts

import { defineSlotRecipe } from "@chakra-ui/react"

export const checkboxRecipe = defineSlotRecipe({
  slots: ["root", "control", "label"],
  base: {},
  variants: {
    size: {
      sm: {},
      md: {},
    },
    visual: {
      contained: {},
      outline: {},
    },
  },
  compoundVariants: [
    {
      size: "sm",
      visual: "outline",
      css: {
        control: { borderWidth: "1px" },
        label: { color: "green.500" },
      },
    },
  ],
})

定位到某个插槽

¥Targeting a slot

在某些情况下,可能需要通过 className 定位到插槽。

¥In some cases, targeting a slot by className might be needed.

  • 在配置中设置 className 属性

  • 命名约定为 ${className}__${slot}

checkbox.recipe.ts

import { defineSlotRecipe } from "@chakra-ui/react"

export const checkboxRecipe = defineSlotRecipe({
  className: "checkbox",
  slots: ["root", "control", "label"],
  base: {
    root: {
      bg: "blue.500",
      _hover: {
        "& .checkbox__label": { color: "white" },
      },
    },
  },
})

主题使用

¥Theme Usage

要以可重复使用配方的方式,请将其移至系统主题并将其添加到 theme.slotRecipes 属性。

¥To use the recipe in a reusable manner, move it to the system theme and add it to theme.slotRecipes property.

在主题中使用配方时无需添加 "use client" 指令。

theme.ts

import { createSystem, defaultConfig, defineConfig } from "@chakra-ui/react"
import { checkboxSlotRecipe } from "./checkbox.recipe"

const config = defineConfig({
  theme: {
    slotRecipes: {
      checkbox: checkboxSlotRecipe,
    },
  },
})

export default createSystem(defaultConfig, config)

TypeScript

使用 CLI 生成秘诀的类型。

¥Use the CLI to generate the types for the recipe.

npx @chakra-ui/cli typegen ./theme.ts

然后,将生成的类型导入到你的组件中。

¥Then, import the generated types in your component.

checkbox.tsx

import type { SlotRecipeProps, UnstyledProp } from "@chakra-ui/react"

export interface CheckboxProps
  extends SlotRecipeProps<"checkbox">,
    UnstyledProp {}

更新代码

¥Update code

如果你直接在组件中使用配方,请更新 useRecipe 以使用 key 属性从主题中获取配方。

¥If you use the recipe directly in your component, update the useRecipe to use the key property to get the recipe from the theme.

checkbox.tsx

const Checkbox = () => {
-  const recipe = useRecipe({ recipe: checkboxRecipe })
+  const recipe = useRecipe({ key: "checkbox" })
  // ...
}

如果你创建了复合组件,请更新 createSlotRecipeContext 以使用 key 属性。

¥If you create a compound component, update the createSlotRecipeContext to use the key property.

checkbox.tsx

const { withProvider, withContext } = createSlotRecipeContext({
-  recipe: checkboxRecipe,
+  key: "checkbox",
})

Previous

秘诀

Next

动画