cdp-material-sdk 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +67 -0
  3. package/dist/DataScope-B7ffH5qN.js +106 -0
  4. package/dist/components/core/types.d.ts +16 -0
  5. package/dist/components/data-scope/DataScope.d.ts +45 -0
  6. package/dist/constants/event.d.ts +22 -0
  7. package/dist/context/DataContainerRuntimeContext.d.ts +16 -0
  8. package/dist/context/DataScopeContext.d.ts +51 -0
  9. package/dist/context/PageContext.d.ts +26 -0
  10. package/dist/hooks/useConcurrentLoading.d.ts +16 -0
  11. package/dist/hooks/useDataContainer.d.ts +61 -0
  12. package/dist/hooks/useDualLoading.d.ts +22 -0
  13. package/dist/host-react.d.ts +15 -0
  14. package/dist/host-react.js +11 -0
  15. package/dist/index.d.ts +2 -0
  16. package/dist/index.js +42 -0
  17. package/dist/plugin.d.ts +45 -0
  18. package/dist/portable.d.ts +20 -0
  19. package/dist/portable.js +33 -0
  20. package/dist/protocol/adapter.d.ts +50 -0
  21. package/dist/protocol/common-schemas.d.ts +64 -0
  22. package/dist/protocol/events.d.ts +48 -0
  23. package/dist/protocol/manifest-types.d.ts +10 -0
  24. package/dist/protocol/manifest.d.ts +144 -0
  25. package/dist/protocol/traits.d.ts +7 -0
  26. package/dist/types/category.d.ts +15 -0
  27. package/dist/utils/mergeInitialValue.d.ts +10 -0
  28. package/dist/validateManifest-CYqjvQe_.js +355 -0
  29. package/dist/validation/validateManifest.d.ts +59 -0
  30. package/docs/component-development/FAQ.md +203 -0
  31. package/docs/component-development/README.md +176 -0
  32. package/docs/component-development/getting-started/01-/345/210/233/345/273/272/346/210/226/346/216/245/345/205/245/347/273/204/344/273/266/345/272/223/345/267/245/347/250/213.md +172 -0
  33. package/docs/component-development/getting-started/02-/345/210/233/345/273/272/347/273/204/344/273/266/345/214/205/345/271/266/346/263/250/345/206/214.md +185 -0
  34. package/docs/component-development/getting-started/03-/345/274/200/345/217/221/346/234/200/345/260/217/345/217/257/350/277/220/350/241/214/347/273/204/344/273/266.md +196 -0
  35. package/docs/component-development/getting-started/04-/346/236/204/345/273/272/345/217/221/345/270/203/344/270/216/345/256/277/344/270/273/346/216/245/345/205/245.md +147 -0
  36. package/docs/component-development/getting-started/05-/350/207/252/346/243/200/344/270/216/346/216/222/351/224/231.md +186 -0
  37. package/docs/component-development/recipes//344/275/277/347/224/250Adapter/351/200/202/351/205/215/347/273/204/344/273/266API.md +131 -0
  38. package/docs/component-development/recipes//345/243/260/346/230/216/344/272/213/344/273/266.md +229 -0
  39. package/docs/component-development/recipes//345/243/260/346/230/216/345/212/250/344/275/234/344/270/216/347/212/266/346/200/201.md +188 -0
  40. package/docs/component-development/recipes//345/243/260/346/230/216/345/270/203/345/261/200/345/256/271/345/231/250/347/273/204/344/273/266.md +221 -0
  41. package/docs/component-development/recipes//345/243/260/346/230/216/346/217/222/346/247/275.md +141 -0
  42. package/docs/component-development/recipes//345/243/260/346/230/216/346/225/260/346/215/256/345/255/227/346/256/265/347/273/204/344/273/266.md +147 -0
  43. package/docs/component-development/recipes//345/243/260/346/230/216/346/225/260/346/215/256/345/256/271/345/231/250/347/273/204/344/273/266.md +258 -0
  44. package/docs/component-development/recipes//346/216/245/345/205/245/347/254/254/344/270/211/346/226/271React/347/273/204/344/273/266/345/272/223.md +209 -0
  45. package/docs/component-development/recipes//351/205/215/347/275/256DOM/346/240/271/350/212/202/347/202/271/346/263/250/345/205/245.md +222 -0
  46. package/docs/component-development/recipes//351/205/215/347/275/256Loading/347/255/226/347/225/245.md +198 -0
  47. package/docs/component-development/recipes//351/205/215/347/275/256/350/256/276/350/256/241/345/231/250/345/205/203/344/277/241/346/201/257.md +153 -0
  48. package/docs/component-development/reference/ActionsState/346/250/241/345/236/213.md +124 -0
  49. package/docs/component-development/reference/DOM/346/240/271/350/212/202/347/202/271/346/263/250/345/205/245/346/250/241/345/236/213.md +125 -0
  50. package/docs/component-development/reference/Events/346/250/241/345/236/213.md +170 -0
  51. package/docs/component-development/reference/Loading/347/255/226/347/225/245/346/250/241/345/236/213.md +140 -0
  52. package/docs/component-development/reference/Manifest/345/255/227/346/256/265/345/217/202/350/200/203.md +251 -0
  53. package/docs/component-development/reference/SDK/345/257/274/345/205/245/350/276/271/347/225/214.md +119 -0
  54. package/docs/component-development/reference/Slots/346/250/241/345/236/213.md +110 -0
  55. package/docs/component-development/reference/Traits/350/203/275/345/212/233/346/250/241/345/236/213.md +165 -0
  56. package/docs/component-development/reference/validateManifest/346/240/241/351/252/214/350/247/204/345/210/231.md +171 -0
  57. package/docs/component-development/reference//345/274/225/346/223/216/345/237/272/347/241/200/350/203/275/345/212/233/346/250/241/345/236/213.md +70 -0
  58. package/docs/component-development/reference//347/244/272/344/276/213/344/273/243/347/240/201/347/264/242/345/274/225.md +163 -0
  59. package/package.json +67 -0
@@ -0,0 +1,229 @@
1
+ ---
2
+ type: recipe
3
+ capability: events
4
+ related:
5
+ - reference/Events模型.md
6
+ - reference/validateManifest校验规则.md
7
+ - reference/示例代码索引.md
8
+ ---
9
+ # 声明事件
10
+
11
+ 事件用于让组件向 CDP 宿主发出通知,例如值变化、点击、聚焦、行点击或业务自定义事件。
12
+
13
+ ---
14
+
15
+ ## 适用场景
16
+
17
+ - 组件需要触发页面编排、指令或 AI 流程。
18
+ - 组件内部发生某个业务动作,需要通知宿主。
19
+ - 第三方组件事件名与 CDP 默认事件名不同,需要映射。
20
+
21
+ ---
22
+
23
+ ## 可以跳过的情况
24
+
25
+ - 组件没有需要对外通知的行为。
26
+ - 组件只是静态展示。
27
+ - 组件只需要被外部调用方法,应使用 actions,而不是 events。
28
+
29
+ ---
30
+
31
+ ## 标准事件
32
+
33
+ 优先检查需求是否能落到标准事件。标准事件能覆盖时,不要额外声明自定义事件。
34
+
35
+ 常用标准事件:
36
+
37
+ | type | 适合场景 | 标准 payload | 推荐 props |
38
+ |------|----------|--------------|------------|
39
+ | `click` | 按钮、链接、卡片点击 | 无 | `onClick` |
40
+ | `focus` | 输入、选择器聚焦 | 无 | `onFocus` |
41
+ | `blur` | 输入、选择器失焦 | 无 | `onBlur` |
42
+ | `valueChange` | 输入值、选择值变化 | `{ newValue; oldValue }` | `onChange` |
43
+ | `itemClick` | 表格行、列表项点击 | `{ index; item }` | `onItemClick` |
44
+ | `itemDoubleClick` | 表格行、列表项双击 | `{ index; item }` | `onItemDoubleClick` |
45
+ | `itemRightClick` | 表格行、列表项右键点击 | `{ index; item }` | `onItemRightClick` |
46
+ | `itemLongPress` | 移动端列表项长按 | `{ index; item }` | `onItemLongPress` |
47
+ | `dataFetch` | 组件请求外部数据 | 数据请求参数对象 | `onDataFetch` |
48
+
49
+ 完整标准事件协议见 [Events 模型](../reference/Events模型.md)。
50
+
51
+ 标准事件写在 `manifest.events` 中:
52
+
53
+ ```ts
54
+ events: {
55
+ valueChange: { title: '值变化' },
56
+ click: { title: '点击' },
57
+ focus: { title: '聚焦' },
58
+ blur: { title: '失焦' },
59
+ }
60
+ ```
61
+
62
+ 组件实现通常通过对应 props 触发:
63
+
64
+ ```tsx
65
+ <button onClick={onClick}>提交</button>
66
+ ```
67
+
68
+ 数据字段组件通常通过 `onChange(nextValue)` 触发值变化。
69
+
70
+ ---
71
+
72
+ ## props 与 payload 转换
73
+
74
+ 如果组件已经使用推荐 props,并且回调参数符合标准 payload,可以直接声明标准事件。
75
+
76
+ ```tsx
77
+ export function AcmeButton({ onClick }: { onClick?: () => void }) {
78
+ return <button onClick={onClick}>提交</button>;
79
+ }
80
+ ```
81
+
82
+ 如果第三方组件 prop 名不同,但语义仍是标准事件,使用 `adapter.events` 映射 prop:
83
+
84
+ ```ts
85
+ events: {
86
+ click: { title: '点击' },
87
+ },
88
+ adapter: {
89
+ events: {
90
+ click: {
91
+ propName: 'onPress',
92
+ },
93
+ },
94
+ },
95
+ ```
96
+
97
+ 如果第三方组件回调参数不同,使用 `transform` 转换为标准 payload:
98
+
99
+ ```ts
100
+ events: {
101
+ valueChange: { title: '值变化' },
102
+ },
103
+ adapter: {
104
+ events: {
105
+ valueChange: {
106
+ propName: 'onSelectedValueChange',
107
+ transform: (nextValue, previousValue) => ({
108
+ newValue: nextValue,
109
+ oldValue: previousValue,
110
+ }),
111
+ },
112
+ },
113
+ },
114
+ ```
115
+
116
+ 如果回调来自列表、表格等作用域场景,可以同时用 `toScope` 提供当前行上下文:
117
+
118
+ ```ts
119
+ events: {
120
+ itemClick: { title: '行点击' },
121
+ },
122
+ adapter: {
123
+ events: {
124
+ itemClick: {
125
+ propName: 'onRowClick',
126
+ transform: (record, index) => ({
127
+ item: record,
128
+ index,
129
+ }),
130
+ toScope: (record, index) => ({
131
+ record,
132
+ index,
133
+ }),
134
+ },
135
+ },
136
+ },
137
+ ```
138
+
139
+ 更完整的 Adapter 用法见 [使用 Adapter 适配组件 API](./使用Adapter适配组件API.md)。
140
+
141
+ ---
142
+
143
+ ## 自定义事件
144
+
145
+ 只有标准事件无法表达业务语义或 payload 结构时,才声明自定义事件。
146
+
147
+ 自定义事件需要 namespaced 命名,并声明 `payloadSchema`:
148
+
149
+ ```ts
150
+ customEvents: {
151
+ 'acme:rowAction': {
152
+ title: '行点击',
153
+ payloadSchema: {
154
+ type: 'object',
155
+ properties: {
156
+ action: { type: 'string' },
157
+ rowKey: { type: 'string' },
158
+ record: { type: 'object' },
159
+ },
160
+ },
161
+ },
162
+ }
163
+ ```
164
+
165
+ 命名建议:
166
+
167
+ | 类型 | 示例 |
168
+ |------|------|
169
+ | 组织前缀 | `acme:rowAction` |
170
+ | 组件前缀 | `table:rowAction` |
171
+ | 业务前缀 | `order:approved` |
172
+
173
+ ---
174
+
175
+ ## 第三方组件事件名不同
176
+
177
+ 如果第三方组件使用 `onRowAction`,可以用 adapter 映射:
178
+
179
+ ```ts
180
+ adapter: {
181
+ customEvents: {
182
+ 'acme:rowAction': {
183
+ propName: 'onRowAction',
184
+ transform: (action, record) => ({
185
+ action,
186
+ rowKey: record.id,
187
+ record,
188
+ }),
189
+ },
190
+ },
191
+ },
192
+ ```
193
+
194
+ 注意:adapter 中引用的标准事件必须先在 `events` 声明;自定义事件必须先在 `customEvents` 声明。
195
+
196
+ ---
197
+
198
+ ## 自检
199
+
200
+ - [ ] 不需要事件的组件没有强行声明事件。
201
+ - [ ] 已优先检查是否能使用标准事件。
202
+ - [ ] 标准事件写在 `events`。
203
+ - [ ] 第三方组件 prop 名不同或 payload 不同时,已使用 `adapter.events` / `adapter.customEvents` 映射。
204
+ - [ ] 自定义事件写在 `customEvents`,并声明 `payloadSchema`。
205
+ - [ ] 自定义事件名是 namespaced 形态,例如 `acme:rowAction`。
206
+ - [ ] adapter 事件引用了已经声明的事件。
207
+ - [ ] `validateManifest()` 没有 error。
208
+
209
+ ---
210
+
211
+ ## 常见错误
212
+
213
+ ### adapter 中声明了事件,但 manifest 没有声明
214
+
215
+ `validateManifest()` 会报错。先在 `events` 或 `customEvents` 中声明,再在 adapter 中映射。
216
+
217
+ ### 自定义事件没有 payloadSchema
218
+
219
+ 自定义事件必须声明 payload 结构,方便设计器和 AI 工具理解事件数据。
220
+
221
+ ---
222
+
223
+ ## 关联文档
224
+
225
+ 本文档只保留本层级职责内容:Recipe 提供任务步骤,Reference 提供稳定模型和规则,validateManifest 文档提供校验级别事实源。
226
+
227
+ - 模型参考:[Events模型](../reference/Events模型.md)
228
+ - 校验规则:[validateManifest校验规则](../reference/validateManifest校验规则.md)
229
+ - 示例索引:[示例代码索引](../reference/示例代码索引.md)
@@ -0,0 +1,188 @@
1
+ ---
2
+ type: recipe
3
+ capability: actions-state
4
+ related:
5
+ - reference/ActionsState模型.md
6
+ - reference/validateManifest校验规则.md
7
+ - reference/示例代码索引.md
8
+ ---
9
+ # 声明动作与状态
10
+
11
+ 动作(Actions)用于让外部流程命令式调用组件;状态(State)用于让外部表达式只读访问组件内部运行时数据。
12
+
13
+ ---
14
+
15
+ ## 适用场景
16
+
17
+ - 外部流程需要调用组件方法,例如刷新、清空、展开、聚焦。
18
+ - 外部表达式需要读取组件内部状态,例如选中行、搜索条件、高亮状态。
19
+ - AI 或编排系统需要知道组件支持哪些命令。
20
+
21
+ ---
22
+
23
+ ## 可以跳过的情况
24
+
25
+ - 组件只是根据 props 渲染,不需要暴露命令式方法。
26
+ - 组件内部状态不需要被外部表达式读取。
27
+ - 组件只是向外通知行为,此时应使用 events。
28
+
29
+ ---
30
+
31
+ ## 声明 actions
32
+
33
+ Manifest:
34
+
35
+ ```ts
36
+ actions: {
37
+ refresh: {
38
+ title: '刷新',
39
+ description: '重新加载数据',
40
+ returns: { type: 'boolean' },
41
+ },
42
+ setKeyword: {
43
+ title: '设置关键词',
44
+ params: {
45
+ type: 'object',
46
+ required: ['keyword'],
47
+ properties: {
48
+ keyword: { type: 'string', title: '关键词' },
49
+ },
50
+ },
51
+ returns: { type: 'boolean' },
52
+ },
53
+ }
54
+ ```
55
+
56
+ 组件实现:
57
+
58
+ ```tsx
59
+ import { forwardRef, useImperativeHandle, useState } from 'react';
60
+
61
+ export const SearchPanel = forwardRef((props, ref) => {
62
+ const [keyword, setKeywordState] = useState('');
63
+
64
+ useImperativeHandle(ref, () => ({
65
+ refresh: async () => {
66
+ await reload();
67
+ return true;
68
+ },
69
+ setKeyword: ({ keyword }: { keyword: string }) => {
70
+ setKeywordState(keyword);
71
+ return true;
72
+ },
73
+ }), []);
74
+
75
+ return <div />;
76
+ });
77
+ ```
78
+
79
+ ---
80
+
81
+ ## 声明 state
82
+
83
+ Manifest:
84
+
85
+ ```ts
86
+ state: {
87
+ keyword: {
88
+ title: '关键词',
89
+ schema: { type: 'string' },
90
+ },
91
+ selectedRowKeys: {
92
+ title: '选中行',
93
+ schema: { type: 'array', items: { type: 'string' } },
94
+ },
95
+ }
96
+ ```
97
+
98
+ 组件实现:
99
+
100
+ ```tsx
101
+ import { forwardRef, useImperativeHandle, useState } from 'react';
102
+ import { COMPONENT_STATE_KEY } from 'cdp-material-sdk/portable';
103
+
104
+ export const TablePanel = forwardRef((props, ref) => {
105
+ const [keyword, setKeyword] = useState('');
106
+ const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]);
107
+
108
+ useImperativeHandle(ref, () => ({
109
+ clearSelection: () => setSelectedRowKeys([]),
110
+ [COMPONENT_STATE_KEY]: {
111
+ keyword,
112
+ selectedRowKeys,
113
+ },
114
+ }), [keyword, selectedRowKeys]);
115
+
116
+ return <div />;
117
+ });
118
+ ```
119
+
120
+ ---
121
+
122
+ ## 实现要点
123
+
124
+ | 项目 | 规则 |
125
+ |------|------|
126
+ | Action 名称 | manifest 中声明的 key 必须和 ref 暴露的方法名一致 |
127
+ | Action 参数 | `params.type` 必须是 `object` |
128
+ | Action 返回值 | 建议声明 `returns`;校验级别见 [validateManifest 校验规则](../reference/validateManifest校验规则.md) |
129
+ | State key | manifest 中声明的 state key 必须出现在 `COMPONENT_STATE_KEY` 对象中 |
130
+ | State 修改 | 外部表达式只读访问;修改应通过 action 完成 |
131
+ | deps | 暴露在 state 中的值必须进入 `useImperativeHandle` 依赖数组 |
132
+
133
+ ---
134
+
135
+ ## 自检
136
+
137
+ ```ts
138
+ import {
139
+ COMPONENT_STATE_KEY,
140
+ diagnoseMissingActionImpls,
141
+ diagnoseMissingStateKeys,
142
+ } from 'cdp-material-sdk/portable';
143
+
144
+ const missingActions = diagnoseMissingActionImpls(
145
+ Object.keys(manifest.actions ?? {}),
146
+ ref.current,
147
+ );
148
+
149
+ const missingStates = diagnoseMissingStateKeys(
150
+ manifest,
151
+ ref.current?.[COMPONENT_STATE_KEY],
152
+ );
153
+ ```
154
+
155
+ 完成标准:
156
+
157
+ - [ ] `actions.*.title` 已声明。
158
+ - [ ] 有参数的 action 使用 `params.type = 'object'`。
159
+ - [ ] ref 暴露了所有声明的 action 方法。
160
+ - [ ] `state.*.title` 和 `state.*.schema` 已声明。
161
+ - [ ] `COMPONENT_STATE_KEY` 中暴露了所有声明的 state key。
162
+ - [ ] `validateManifest()` 没有 error。
163
+
164
+ ---
165
+
166
+ ## 常见错误
167
+
168
+ ### 只写了 manifest.actions,没有实现 ref 方法
169
+
170
+ 宿主无法调用该 action。使用 `diagnoseMissingActionImpls()` 检查。
171
+
172
+ ### 直接写 `__state` 字符串
173
+
174
+ 推荐使用 `COMPONENT_STATE_KEY`,避免保留 key 拼写不一致。
175
+
176
+ ### State 用于写操作
177
+
178
+ State 是只读运行时快照。需要修改组件时,请暴露 action。
179
+
180
+ ---
181
+
182
+ ## 关联文档
183
+
184
+ 本文档只保留本层级职责内容:Recipe 提供任务步骤,Reference 提供稳定模型和规则,validateManifest 文档提供校验级别事实源。
185
+
186
+ - 模型参考:[ActionsState模型](../reference/ActionsState模型.md)
187
+ - 校验规则:[validateManifest校验规则](../reference/validateManifest校验规则.md)
188
+ - 示例索引:[示例代码索引](../reference/示例代码索引.md)
@@ -0,0 +1,221 @@
1
+ ---
2
+ type: recipe
3
+ capability: layout-container
4
+ related:
5
+ - reference/Traits能力模型.md
6
+ - reference/Manifest字段参考.md
7
+ - reference/Slots模型.md
8
+ - recipes/声明插槽.md
9
+ - recipes/声明数据容器组件.md
10
+ ---
11
+ # 声明布局容器组件
12
+
13
+ 布局容器组件用于承载子组件,例如 Card、Grid、Tabs、Collapse、Section 等。
14
+
15
+ `LAYOUT_CONTAINER` 与 `DATA_CONTAINER` **不互斥**:只要组件同时承载子组件、又管理子字段数据作用域,就应同时声明两个 trait。典型代表是 Form:既是布局容器(拖入字段子组件),又是数据容器(管理表单值)。
16
+
17
+ ---
18
+
19
+ ## 适用场景
20
+
21
+ - 组件提供一个可放置子组件的区域。
22
+ - 组件可能限制子组件的类型、最少/最多数量。
23
+ - 不论组件是否还参与数据作用域。只要可以拖入子组件,就应声明 `LAYOUT_CONTAINER`。
24
+
25
+ ---
26
+
27
+ ## 可以跳过的情况
28
+
29
+ - 组件只是单纯的展示组件,没有子内容。
30
+ - 子内容只以命名/作用域/动态区域出现,不需要默认 children 区域,应改用 [声明插槽](./声明插槽.md)。
31
+ - 组件是数据容器但不接受子组件(例如只接 props 的表格),只需要 [声明数据容器组件](./声明数据容器组件.md)。
32
+
33
+ ---
34
+
35
+ ## 选择 children、slots 还是 nesting
36
+
37
+ | 场景 | 推荐方案 |
38
+ |------|----------|
39
+ | 一个匿名的子组件区域 | `children` + `LAYOUT_CONTAINER` |
40
+ | 多个具名区域(header / footer / actions) | `slots` |
41
+ | 子区域带运行时上下文(行数据、列定义) | 作用域 `slots` |
42
+ | 同时限制可放入的组件类型或数量 | 加 `nesting` |
43
+
44
+ `children` 模式与 `slots` 模式可以共存:组件可以同时有默认 `children` 区域和若干命名插槽。
45
+
46
+ ---
47
+
48
+ ## 作者职责
49
+
50
+ 1. 声明 `COMPONENT_TRAIT.LAYOUT_CONTAINER`,如果同时是数据容器,同时声明 `COMPONENT_TRAIT.DATA_CONTAINER`。
51
+ 2. 在组件实现里渲染 `children`(或对应 slot 内容)。
52
+ 3. 如需限制嵌套关系,声明 `nesting`。
53
+ 4. 如果同时声明了 `DATA_CONTAINER`,还需按 [声明数据容器组件](./声明数据容器组件.md) 中的要求包 `DataScope`。
54
+ 5. 与设计器交互的元信息(图标、分组、隐藏)见 [配置设计器元信息](./配置设计器元信息.md)。
55
+
56
+ ---
57
+
58
+ ## Manifest 示例
59
+
60
+ ### 单一 children 区域(Card 类)
61
+
62
+ ```ts
63
+ import {
64
+ COMPONENT_CATEGORY,
65
+ COMPONENT_TRAIT,
66
+ INJECT_PATH_SLOT_PROPS,
67
+ type ComponentManifest,
68
+ } from 'cdp-material-sdk/portable';
69
+
70
+ export const acmeCardManifest = {
71
+ type: 'acme.Card',
72
+ traits: [COMPONENT_TRAIT.LAYOUT_CONTAINER],
73
+ meta: {
74
+ title: '卡片',
75
+ category: COMPONENT_CATEGORY.LAYOUT,
76
+ description: '带边框和阴影的内容容器',
77
+ },
78
+ engine: {
79
+ render: {
80
+ injection: {
81
+ rootPath: INJECT_PATH_SLOT_PROPS,
82
+ },
83
+ },
84
+ },
85
+ props: {
86
+ type: 'object',
87
+ properties: {
88
+ title: { type: 'string', title: '标题' },
89
+ },
90
+ },
91
+ } satisfies ComponentManifest;
92
+ ```
93
+
94
+ 组件实现:
95
+
96
+ ```tsx
97
+ export const AcmeCard = forwardRef<HTMLDivElement, AcmeCardProps>((props, ref) => {
98
+ const { title, children, slotProps } = props;
99
+ return (
100
+ <div {...slotProps?.root} ref={ref} className="acme-card">
101
+ {title && <header>{title}</header>}
102
+ <div className="acme-card-body">{children}</div>
103
+ </div>
104
+ );
105
+ });
106
+ ```
107
+
108
+ ### 限制嵌套关系(Tabs / FormItem 类)
109
+
110
+ ```ts
111
+ nesting: {
112
+ allowedChildren: ['acme.TabPane'],
113
+ minChildren: 1,
114
+ maxChildren: 12,
115
+ },
116
+ ```
117
+
118
+ ### 同时是数据容器(Form 类)
119
+
120
+ Form 这类组件同时承载字段子组件、又管理表单值,需同时声明两个 trait:
121
+
122
+ ```ts
123
+ export const acmeFormManifest = {
124
+ type: 'acme.Form',
125
+ traits: [COMPONENT_TRAIT.DATA_CONTAINER, COMPONENT_TRAIT.LAYOUT_CONTAINER],
126
+ meta: {
127
+ title: '表单',
128
+ category: COMPONENT_CATEGORY.DATA_ENTRY,
129
+ valueSchema: { type: 'object', default: {} },
130
+ },
131
+ // ... engine.render.injection
132
+ } satisfies ComponentManifest;
133
+ ```
134
+
135
+ 这种情况下:
136
+
137
+ - 本文档负责 `LAYOUT_CONTAINER` 部分:渲染 `children`、声明 `nesting`。
138
+ - `DATA_CONTAINER` 部分(`valueSchema`、`DataScope`、字段路径解析)走 [声明数据容器组件](./声明数据容器组件.md)。
139
+
140
+ ---
141
+
142
+ ## nesting 字段
143
+
144
+ | 字段 | 类型 | 说明 |
145
+ |------|------|------|
146
+ | `allowedChildren` | `string[]` | 允许放入的子组件 `type` 列表,留空表示不限制 |
147
+ | `allowedParents` | `string[]` | 限制本组件只能作为这些父组件的子节点 |
148
+ | `minChildren` | `number` | 最少子组件数量,常用于 Tabs、Steps |
149
+ | `maxChildren` | `number` | 最多子组件数量,常用于固定栅格 |
150
+
151
+ 经验法则:
152
+
153
+ - 组合关系强(Tabs/TabPane、Steps/Step、Form/FormItem)建议同时声明 `allowedChildren` 与 `allowedParents`。
154
+ - 通用容器(Card、Section)通常不需要 `nesting`,避免过度约束。
155
+ - `nesting` 与 `slots.allowedChildren` 不互斥:前者约束默认 children 区域,后者约束具名 slot 区域。
156
+
157
+ ---
158
+
159
+ ## children 与 slots 共存
160
+
161
+ ```ts
162
+ traits: [COMPONENT_TRAIT.LAYOUT_CONTAINER],
163
+ slots: {
164
+ header: { title: '头部' },
165
+ footer: { title: '底部' },
166
+ },
167
+ ```
168
+
169
+ 组件实现:
170
+
171
+ ```tsx
172
+ const { children, _slots, slotProps } = props;
173
+ return (
174
+ <section {...slotProps?.root}>
175
+ {_slots?.header && <header>{_slots.header}</header>}
176
+ <div className="body">{children}</div>
177
+ {_slots?.footer && <footer>{_slots.footer}</footer>}
178
+ </section>
179
+ );
180
+ ```
181
+
182
+ `children` 用于主内容区,`slots` 用于具名扩展区,两者由设计器分别提供拖拽入口。
183
+
184
+ ---
185
+
186
+ ## 自检
187
+
188
+ - [ ] `traits` 包含 `COMPONENT_TRAIT.LAYOUT_CONTAINER`。
189
+ - [ ] 组件实现已渲染 `children`(或对应 slot 内容)。
190
+ - [ ] 如声明了 `nesting`,`allowedChildren` / `allowedParents` 中的 type 真实存在。
191
+ - [ ] 如同时使用 `children` 与 `slots`,没有把 children 当成隐式 slot 处理。
192
+ - [ ] `validateManifest()` 没有 error。
193
+
194
+ ---
195
+
196
+ ## 常见错误
197
+
198
+ ### 把数据容器仅声明成布局容器
199
+
200
+ 容器同时承载字段子组件、又管理表单值(例如 Form)时,两个 trait 都要声明,并按数据容器 recipe 提供 `DataScope`。只声明 `LAYOUT_CONTAINER` 会丢失数据语义、表达式和 AI 工具无法识别表单值结构。
201
+
202
+ ### nesting.allowedChildren 写了不存在的 type
203
+
204
+ 设计器会过滤所有候选组件,导致用户在容器内拖不进任何组件。建议用常量或 type 集合维护。
205
+
206
+ ### 用 children 模拟具名插槽
207
+
208
+ 如果有 header / footer / extra 等多个区域,请直接声明 `slots`。把多个区域塞进 `children` 会让设计器和 AI 工具失去结构信息。
209
+
210
+ ---
211
+
212
+ ## 关联文档
213
+
214
+ 本文档只保留本层级职责内容:Recipe 提供任务步骤,Reference 提供稳定模型和规则,validateManifest 文档提供校验级别事实源。
215
+
216
+ - 模型参考:[Traits能力模型](../reference/Traits能力模型.md)
217
+ - 字段参考:[Manifest字段参考](../reference/Manifest字段参考.md)
218
+ - 模型参考:[Slots模型](../reference/Slots模型.md)
219
+ - 任务 Recipe:[声明插槽](./声明插槽.md)
220
+ - 任务 Recipe:[声明数据容器组件](./声明数据容器组件.md)
221
+ - 任务 Recipe:[配置设计器元信息](./配置设计器元信息.md)