SuperAPI AI 工具集

canvas-slots-for-repeatable-content

前端与建站

查看项目

更新于 10-15 10:52

技能介绍

用途

本技能用于在 Canvas 中建模可重复内容(列表、卡片、条目集合),采用基于插槽(slot)的父子组件模式。当需要构建列表/网格、把数组属性转换为插槽结构时适用。

插槽让组件能够在指定区域接收并渲染子组件。本技能聚焦可重复内容模式,尤其是因为 Canvas 不支持包含复杂对象的数组属性。

关于更广泛的插槽架构与拆分指导,可参考 canvas-component-composability。

Canvas 限制:不支持包含对象的数组属性

Canvas 会拒绝定义了嵌套对象条目数组属性的组件:

# This will fail to upload
props:
  properties:
    items:
      title: Items
      type: array
      items:
        type: object
        properties:
          title:
            type: string
          description:
            type: string
          linkUrl:
            type: string

上传会失败并报错:

Drupal Canvas does not know of a field type/widget to allow populating the
items prop, with the shape {...}. [props.items.items] 'items' is not a
supported key. [props.items.type] The value you selected is not a valid
choice.

简单的基本类型数组是受支持的:

# OK: Array of simple strings
props:
  properties:
    tags:
      title: Tags
      type: array
      items:
        type: string
      examples:
        - - tag1
          - tag2

模式:插槽 + 子组件

不要使用数组属性,而是为重复项创建一个独立的子组件,并在父组件中使用插槽。

第 1 步:创建子组件

把条目结构抽取到它自己的组件中:

# src/components/feature-card/component.yml
name: Feature Card
machineName: feature-card
status: true
required:
  - title
props:
  properties:
    title:
      title: Title
      type: string
      examples:
        - Feature One
    description:
      title: Description
      type: string
      examples:
        - A brief description of this feature and its benefits.
    linkText:
      title: Link Text
      type: string
      examples:
        - Learn more
    linkUrl:
      title: Link URL
      type: string
      format: uri-reference
      examples:
        - /features/feature-one
slots: []

第 2 步:更新父组件以使用插槽

用插槽替换数组属性:

遵循 canvas-component-metadata 的插槽 schema:已定义的插槽使用对象映射,仅当组件没有插槽时才使用 slots: []。

# src/components/features-section/component.yml
name: Features Section
machineName: features-section
status: true
required:
  - heading
props:
  properties:
    heading:
      title: Heading
      type: string
      examples:
        - Our Features
slots:
  features:
    title: Features

第 3 步:更新 JSX 以渲染插槽

在父组件的 JSX 中,把插槽作为 children 渲染:

// src/components/features-section/index.jsx
const FeaturesSection = ({ heading, features, className }) => {
  return (
    <div className={className}>
      <h2>{heading}</h2>
      {/* Render the slot content */}
      <div className="flex gap-10">{features}</div>
    </div>
  );
};

第 4 步:更新故事以使用子组件

在 Storybook 故事中,用子组件组合父组件:

// src/stories/features-section.stories.jsx
import FeatureCard from "@/components/feature-card";
import FeaturesSection from "@/components/features-section";

export const Default = {
  args: {
    heading: "Our Features",
    features: (
      <>
        <FeatureCard
          title="Feature One"
          description="A brief description of this feature..."
          linkText="Learn more"
          linkUrl="/features/feature-one"
        />
        <FeatureCard
          title="Feature Two"
          description="Another feature with its own benefits..."
          linkText="Learn more"
          linkUrl="/features/feature-two"
        />
      </>
    ),
  },
};

常见模式

以下组件模式总是需要插槽配合子组件:

父组件 子组件 用例
footer footer-link-group 页脚导航列
footer-link-group footer-link 页脚分组内的链接
card-grid card 卡片网格/轮播
testimonials testimonial-card 客户评价
cta-section cta-card 行动号召卡片
features-section feature-card 功能亮点卡片
stats-hero stat-item 统计数据显示
metadata-list metadata-item 键值元数据对
carousel carousel-item 轮播幻灯片

插槽容器最小尺寸

当插槽位于根据内容确定尺寸的容器内(flex 项、grid 项、行内元素)时,容器在空置时会塌缩为零尺寸。这会阻止 Canvas 编辑器显示拖放区域覆盖层。

问题示例:

// Container collapses when slot is empty
const Header = ({ branding, navigation }) => {
  return (
    <header className="flex items-center justify-between">
      <div>{branding}</div>
      <nav>{navigation}</nav>
    </header>
  );
};

解决方案:添加最小尺寸,使插槽在空置时仍可交互:

// Minimum width ensures the slot is always interactive
const Header = ({ branding, navigation }) => {
  return (
    <header className="flex items-center justify-between">
      <div className="min-w-32">{branding}</div>
      <nav>{navigation}</nav>
    </header>
  );
};

何时应用最小尺寸:

  • Flex 项(flex 父级)——水平布局使用 min-w-,垂直布局使用 min-h-
  • Grid 项(grid 父级)——按需使用 min-w-* 和/或 min-h-*
  • 行内元素(inline、inline-block、inline-flex)——使用 min-w-*

何时不需要最小尺寸:

  • 插槽容器已有固定宽度/高度
  • 容器使用块级布局(默认全宽)
  • 插槽是唯一内容且父级已有定义尺寸

子组件命名

为插槽创建子组件时:

  • 使用能表明条目类型的描述性名称:feature-card、testimonial-card、team-member
  • 对于通用条目,加后缀 -item:metadata-item、carousel-item、list-item
  • 对于链接相关条目,要具体:footer-link、nav-link、social-link
  • 始终为每个子组件创建对应的故事文件。