插槽配方
了解如何使用插槽配方设置多个部件的样式。
概述
¥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",
})