创建新组件技能
从头开始构建新的Terrae组件的端到端工作流程。
涵盖所有8个输出:
- 组件源代码
- 注册表
- 导出
- 文档
- 示例
- 侧边栏
- 组件页面
- 更新日志
组件结构、模式、响应性和性能规则在.claude/rules/react/component.md中。
所有输出
| # | 输出 | 文件(s) |
|---|---|---|
| 1 | 组件源文件 | src/registry/map/{name}.tsx |
| 2 | 桶导出 | src/registry/map/index.tsx (更新) |
| 3 | 注册表条目 | registry.json (更新) |
| 4 | 示例文件(s) | src/app/docs/_components/examples/{name}-example.tsx |
| 5 | 文档页面 | src/app/docs/{slug}/page.tsx |
| 6 | 侧边栏导航 | src/app/docs/_components/docs-sidebar.tsx (更新) |
| 7 | 组件列表 | src/app/docs/components/page.tsx (更新) |
| 8 | 更新日志条目 | src/app/docs/changelog/page.tsx (更新) |
说明
当开发人员请求新组件时:
第1步:收集需求
询问:
- 组件名称(例如,
MapHeatmap,MapPolygon) - 核心功能
- 是否需要复合组件(如
MarkerContent,MarkerPopup) - 类别:
"core"或"features"(大多数组件是特性) - 侧边栏和组件页面的Lucide图标
- 是否公开控制钩子(例如,
useHeatmapControl)
如果有多种有效的实现方法(例如,Mapbox图层与DOM覆盖,canvas与CSS动画,GeoJSON源与自定义渲染),呈现选项及其权衡,让开发人员在选择写代码之前做出选择。
第2步:创建组件文件和导出
按照.claude/rules/react/component.md中的地图组件规则,进行组件结构、模板、模式和桶导出。
- 位置:
src/registry/map/{component-name}.tsx - 文件名使用小写连字符(例如,
heat-map.tsx) - 从
src/registry/map/index.tsx导出
第3步:添加注册表条目
通过向items数组添加条目来更新registry.json。
按照此结构:
{
"name": "heat-map",
"type": "registry:ui",
"title": "地图热力图",
"description": "组件的简短描述。",
"dependencies": ["mapbox-gl"],
"devDependencies": ["@types/mapbox-gl"],
"registryDependencies": ["https://www.terrae.dev/map.json"],
"files": [
{
"path": "src/registry/map/heat-map.tsx",
"type": "registry:ui",
"target": "components/ui/map/heat-map.tsx"
}
]
}
关键规则:
name使用小写连字符前缀(例如,heat-map)registryDependencies始终包括["https://www.terrae.dev/map.json"](所有其他组件依赖的核心Map组件)- 如果组件需要除
mapbox-gl之外的包,则添加额外的dependencies - 不需要
mapbox-gl的组件可以有空的dependencies(例如,水印)
第4步:创建示例文件(s)
位置:src/app/docs/_components/examples/{name}-example.tsx
文件名使用小写连字符。
基本示例应展示组件的最简单用法。
import { Map, MapHeatmap } from "@/registry/map"
export const HeatmapExample = () => {
const accessToken = process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN || ""
return (
<div className="h-full w-full">
<Map accessToken={accessToken} center={[-74.006, 40.7128]} zoom={10}>
<MapHeatmap id="heatmap-basic" {/* ...最小化属性 */} />
</Map>
</div>
)
}
关键规则:
- 仅当示例使用钩子、事件处理程序或浏览器API时添加
"use client"——纯粹组合示例仅渲染地图组件不需要它 - 从
@/registry/map导入 - 将地图包装在
<div className="h-full w-full">中 - 传递
process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN || ""作为访问令牌——Map组件验证它并在缺失时显示错误,因此示例不需要自己的检查 - 用描述性名称(PascalCase)导出组件
- 为每种变体创建额外的示例文件(例如,
heatmap-color-example.tsx,heatmap-custom-example.tsx)
第5步:创建文档页面
位置:src/app/docs/{slug}/page.tsx
使用lines-animated/page.tsx作为黄金标准参考。
slug应与侧边栏href匹配(例如,/docs/heatmap → src/app/docs/heatmap/page.tsx)。
import { DocsLayout, DocsSection, DocsCode, DocsLink } from "../_components/docs"
import { ComponentPreview } from "../_components/component-preview"
import { CodeBlock } from "../_components/code-block"
import { HeatmapExample } from "../_components/examples/heatmap-example"
import { getExampleSource } from "@/lib/get-example-source"
import { Metadata } from "next"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
export const metadata: Metadata = {
title: "热力图",
}
const HeatmapPage = () => {
const basicSource = getExampleSource("heatmap-example.tsx")
return (
<DocsLayout
title="热力图"
description="组件的作用的简短描述。"
prev={{ title: "前一个组件", href: "/docs/previous" }}
next={{ title: "下一个组件", href: "/docs/next" }}
>
<DocsSection title="安装">
<p>首先,确保你已经安装了基础地图组件:</p>
<CodeBlock code={`npx shadcn@latest add https://www.terrae.dev/map.json`} language="bash" />
<p className="mt-4">然后安装热力图组件:</p>
<CodeBlock
code={`npx shadcn@latest add https://www.terrae.dev/heat-map.json`}
language="bash"
/>
</DocsSection>
<ComponentPreview code={basicSource}>
<HeatmapExample />
</ComponentPreview>
{/* 附加部分,示例、属性表等。 */}
</DocsLayout>
)
}
export default HeatmapPage
关键规则:
- 总是导出带有
title的metadata - 使用带有
title、description、prev和next导航链接的DocsLayout - 第一节总是"安装",有两个
CodeBlocks(基础地图+组件) - 基本
ComponentPreview紧接在安装之后(没有节标题或描述,只有演示) - 使用
ComponentPreview包装每个示例及其源代码 - 使用
getExampleSource("filename.tsx")加载示例源代码 - 使用
DocsCode进行描述中的内联代码引用 - 使用带有
title的DocsSection为每个部分 - 当组件有许多可配置属性时,添加属性
Table - 设置
prev/next以匹配侧边栏导航中的相邻项
第6步:添加到侧边栏
更新src/app/docs/_components/docs-sidebar.tsx:
- 在顶部导入Lucide图标(如果尚未导入)
- 在
navigation数组的正确部分添加一个NavItem条目 - 添加
badge: "new"
{ title: "热力图", href: "/docs/heatmap", icon: Flame, badge: "new" },
部分:
"Explore"— 故事,更新日志"Get Started"— 介绍,安装,比较,组件,钩子,参考"Core"— 地图,控件,指南针,标记,弹出窗口"Features"— 其他所有内容
第7步:添加到组件页面
更新src/app/docs/components/page.tsx:
- 在顶部导入Lucide图标(如果尚未导入)
- 向
components数组添加一个ComponentItem条目 - 添加
isNew: true
{
title: "热力图",
href: "/docs/heatmap",
description: "与注册表描述相匹配的简短描述",
icon: Flame,
category: "features",
installCommand: "npx shadcn@latest add https://www.terrae.dev/heat-map.json",
isNew: true,
},
关键规则:
category是"core"或"features"(必须与侧边栏部分匹配)installCommandURL遵循模式https://www.terrae.dev/{registry-name}.json- 如果组件仅适用于Mapbox GL(而不是MapLibre),请添加
mapboxOnly: true - 在数组中将条目放置在类似组件附近
第8步:更新更新日志
更新src/app/docs/changelog/page.tsx:
在changelogs数组中最顶部(最新)的ChangelogEntry的components数组中添加一个新条目:
{
title: "热力图",
description: (
<>
新的<code className="rounded bg-muted px-1 py-0.5 text-xs">MapHeatmap</code>组件用于在地图上可视化数据密度。支持自定义颜色坡道、半径控制和强度调整。
</>
),
href: "/docs/heatmap",
},
关键规则:
- 在
components下添加新组件,在features下添加新功能,在fixes下添加错误修复,在properties下添加新属性 description使用JSX内联<code>标签用于组件名称- 总是包含链接到文档页面的
href
第9步:与用户审查
在最终确定之前,展示所有更改:
- 组件源代码
- 桶导出添加
- 注册表条目
- 示例文件(s)
- 文档页面
- 侧边栏条目
- 组件页面条目
- 更新日志条目