插槽配方
了解如何使用插槽配方设置多个部件的样式。
概述
¥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
一起使用(推荐)
需要添加 "use client"
指令才能使用 useSlotRecipe
Hooks 或 createSlotRecipeContext
函数。这是因为它们在底层依赖于像 useContext
和 useInsertionEffect
这样的 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.
然后,使用 withProvider
和 withContext
函数创建共享相同上下文的复合组件。
¥Then, use the withProvider
and withContext
functions to create the compound
components that share the same context.
你需要手动输入 withProvider
和 withContext
的泛型。此方法旨在优化 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",
})