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.
- package/LICENSE +21 -0
- package/README.md +67 -0
- package/dist/DataScope-B7ffH5qN.js +106 -0
- package/dist/components/core/types.d.ts +16 -0
- package/dist/components/data-scope/DataScope.d.ts +45 -0
- package/dist/constants/event.d.ts +22 -0
- package/dist/context/DataContainerRuntimeContext.d.ts +16 -0
- package/dist/context/DataScopeContext.d.ts +51 -0
- package/dist/context/PageContext.d.ts +26 -0
- package/dist/hooks/useConcurrentLoading.d.ts +16 -0
- package/dist/hooks/useDataContainer.d.ts +61 -0
- package/dist/hooks/useDualLoading.d.ts +22 -0
- package/dist/host-react.d.ts +15 -0
- package/dist/host-react.js +11 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +42 -0
- package/dist/plugin.d.ts +45 -0
- package/dist/portable.d.ts +20 -0
- package/dist/portable.js +33 -0
- package/dist/protocol/adapter.d.ts +50 -0
- package/dist/protocol/common-schemas.d.ts +64 -0
- package/dist/protocol/events.d.ts +48 -0
- package/dist/protocol/manifest-types.d.ts +10 -0
- package/dist/protocol/manifest.d.ts +144 -0
- package/dist/protocol/traits.d.ts +7 -0
- package/dist/types/category.d.ts +15 -0
- package/dist/utils/mergeInitialValue.d.ts +10 -0
- package/dist/validateManifest-CYqjvQe_.js +355 -0
- package/dist/validation/validateManifest.d.ts +59 -0
- package/docs/component-development/FAQ.md +203 -0
- package/docs/component-development/README.md +176 -0
- 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
- 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
- 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
- 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
- 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
- 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
- package/docs/component-development/recipes//345/243/260/346/230/216/344/272/213/344/273/266.md +229 -0
- 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
- 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
- package/docs/component-development/recipes//345/243/260/346/230/216/346/217/222/346/247/275.md +141 -0
- 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
- 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
- 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
- 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
- package/docs/component-development/recipes//351/205/215/347/275/256Loading/347/255/226/347/225/245.md +198 -0
- 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
- package/docs/component-development/reference/ActionsState/346/250/241/345/236/213.md +124 -0
- 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
- package/docs/component-development/reference/Events/346/250/241/345/236/213.md +170 -0
- package/docs/component-development/reference/Loading/347/255/226/347/225/245/346/250/241/345/236/213.md +140 -0
- package/docs/component-development/reference/Manifest/345/255/227/346/256/265/345/217/202/350/200/203.md +251 -0
- package/docs/component-development/reference/SDK/345/257/274/345/205/245/350/276/271/347/225/214.md +119 -0
- package/docs/component-development/reference/Slots/346/250/241/345/236/213.md +110 -0
- package/docs/component-development/reference/Traits/350/203/275/345/212/233/346/250/241/345/236/213.md +165 -0
- package/docs/component-development/reference/validateManifest/346/240/241/351/252/214/350/247/204/345/210/231.md +171 -0
- 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
- 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
- package/package.json +67 -0
package/docs/component-development/recipes//345/243/260/346/230/216/346/217/222/346/247/275.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: recipe
|
|
3
|
+
capability: slots
|
|
4
|
+
related:
|
|
5
|
+
- reference/Slots模型.md
|
|
6
|
+
- reference/validateManifest校验规则.md
|
|
7
|
+
- reference/示例代码索引.md
|
|
8
|
+
---
|
|
9
|
+
# 声明插槽
|
|
10
|
+
|
|
11
|
+
插槽用于让组件提供可被设计器填充的区域,例如卡片内容区、表格列模板、列表项模板等。
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 适用场景
|
|
16
|
+
|
|
17
|
+
- 组件需要承载子组件。
|
|
18
|
+
- 组件有固定区域允许业务方拖入内容。
|
|
19
|
+
- 组件需要按数据行渲染模板。
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 可以跳过的情况
|
|
24
|
+
|
|
25
|
+
- 组件不承载子组件。
|
|
26
|
+
- 组件只通过 props 配置内容。
|
|
27
|
+
- 组件只是一个单一输入或展示控件。
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 命名插槽
|
|
32
|
+
|
|
33
|
+
最常见,也最推荐第三方作者优先使用。
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
slots: {
|
|
37
|
+
header: {
|
|
38
|
+
title: '头部',
|
|
39
|
+
description: '卡片头部内容',
|
|
40
|
+
},
|
|
41
|
+
body: {
|
|
42
|
+
title: '内容',
|
|
43
|
+
description: '卡片主体内容',
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
组件通过宿主注入的 `_slots` 消费:
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
export function Card(props: any) {
|
|
52
|
+
const { _slots } = props;
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<section>
|
|
56
|
+
<header>{_slots?.header}</header>
|
|
57
|
+
<main>{_slots?.body}</main>
|
|
58
|
+
</section>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 作用域插槽
|
|
66
|
+
|
|
67
|
+
适合表格行、列表项这类需要向子内容提供上下文的场景。
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
slots: {
|
|
71
|
+
rowActions: {
|
|
72
|
+
title: '行操作',
|
|
73
|
+
scoped: true,
|
|
74
|
+
scopeDescription: '提供当前行 record 和 rowIndex',
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`scopeDescription` 不是强制字段,但建议声明,方便设计器和 AI 理解上下文。
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 动态插槽
|
|
84
|
+
|
|
85
|
+
适合根据 props 数组生成多个插槽,例如表格列模板。
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
slots: {
|
|
89
|
+
columnSlot: {
|
|
90
|
+
title: '列模板',
|
|
91
|
+
dynamic: true,
|
|
92
|
+
dynamicSource: 'columns',
|
|
93
|
+
dynamicKey: 'col:{dataIndex}',
|
|
94
|
+
dynamicTitle: '列:{title}',
|
|
95
|
+
scoped: true,
|
|
96
|
+
scopeDescription: '提供当前行 record',
|
|
97
|
+
},
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
动态插槽必须声明:
|
|
102
|
+
|
|
103
|
+
- `dynamicSource`
|
|
104
|
+
- `dynamicKey`
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## 自检
|
|
109
|
+
|
|
110
|
+
- [ ] 每个 slot 都有 `title`。
|
|
111
|
+
- [ ] 动态 slot 声明了 `dynamicSource` 和 `dynamicKey`。
|
|
112
|
+
- [ ] 作用域 slot 建议声明 `scopeDescription`。
|
|
113
|
+
- [ ] `allowedChildren` 如果声明,必须是字符串数组。
|
|
114
|
+
- [ ] 组件实际渲染了 `_slots` 或 `_scopedSlots`。
|
|
115
|
+
- [ ] `validateManifest()` 没有 error。
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 常见错误
|
|
120
|
+
|
|
121
|
+
### 声明了 slot,但组件没有渲染
|
|
122
|
+
|
|
123
|
+
设计器会显示插槽,但运行时不会看到内容。请确认组件消费了对应 `_slots[name]`。
|
|
124
|
+
|
|
125
|
+
### 动态 slot 缺少 dynamicKey
|
|
126
|
+
|
|
127
|
+
宿主无法生成稳定 slot 名称,`validateManifest()` 会报错。
|
|
128
|
+
|
|
129
|
+
### 普通容器只声明 LAYOUT_CONTAINER,不声明 slots
|
|
130
|
+
|
|
131
|
+
如果组件有明确可填充区域,建议声明 slots。`LAYOUT_CONTAINER` 只说明组件具备布局容器能力,slots 才说明具体区域。
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 关联文档
|
|
136
|
+
|
|
137
|
+
本文档只保留本层级职责内容:Recipe 提供任务步骤,Reference 提供稳定模型和规则,validateManifest 文档提供校验级别事实源。
|
|
138
|
+
|
|
139
|
+
- 模型参考:[Slots模型](../reference/Slots模型.md)
|
|
140
|
+
- 校验规则:[validateManifest校验规则](../reference/validateManifest校验规则.md)
|
|
141
|
+
- 示例索引:[示例代码索引](../reference/示例代码索引.md)
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: recipe
|
|
3
|
+
capability: data-field
|
|
4
|
+
related:
|
|
5
|
+
- reference/Traits能力模型.md
|
|
6
|
+
- reference/Manifest字段参考.md
|
|
7
|
+
- reference/validateManifest校验规则.md
|
|
8
|
+
- recipes/使用Adapter适配组件API.md
|
|
9
|
+
---
|
|
10
|
+
# 声明数据字段组件
|
|
11
|
+
|
|
12
|
+
数据字段组件用于接入 CDP 的值管理,例如输入框、选择器、开关、日期选择器等。
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 适用场景
|
|
17
|
+
|
|
18
|
+
- 组件有一个主要业务值。
|
|
19
|
+
- 组件需要接收 `value` 并通过 `onChange` 回传新值。
|
|
20
|
+
- 组件需要被表单、表达式或 AI 工具识别为数据字段。
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 可以跳过的情况
|
|
25
|
+
|
|
26
|
+
- 组件只是展示内容,不参与数据写入。
|
|
27
|
+
- 组件的值完全由内部管理,不需要暴露给 CDP。
|
|
28
|
+
- 组件是容器,管理的是子字段数据,此时应阅读 [声明数据容器组件](./声明数据容器组件.md)。
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 作者职责
|
|
33
|
+
|
|
34
|
+
1. 在 `traits` 中声明 `COMPONENT_TRAIT.DATA_FIELD`。
|
|
35
|
+
2. 在 `meta.valueSchema` 中描述 value 类型。
|
|
36
|
+
3. 组件接收 `value`。
|
|
37
|
+
4. 值变化时调用 `onChange(nextValue)`。
|
|
38
|
+
5. 如果组件在容器内使用,建议支持 `name` prop。
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 示例
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import { forwardRef } from 'react';
|
|
46
|
+
import type { BaseUIProps } from 'cdp-material-sdk/portable';
|
|
47
|
+
|
|
48
|
+
export interface AcmeInputProps extends BaseUIProps<HTMLInputElement> {
|
|
49
|
+
value?: string;
|
|
50
|
+
onChange?: (value: string) => void;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const AcmeInput = forwardRef<HTMLInputElement, AcmeInputProps>((props, ref) => {
|
|
54
|
+
const { value = '', onChange, slotProps } = props;
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<input
|
|
58
|
+
{...slotProps?.root}
|
|
59
|
+
ref={ref}
|
|
60
|
+
value={value}
|
|
61
|
+
onChange={(event) => onChange?.(event.target.value)}
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Manifest:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import {
|
|
71
|
+
COMPONENT_CATEGORY,
|
|
72
|
+
COMPONENT_TRAIT,
|
|
73
|
+
INJECT_PATH_SLOT_PROPS,
|
|
74
|
+
type ComponentManifest,
|
|
75
|
+
} from 'cdp-material-sdk/portable';
|
|
76
|
+
|
|
77
|
+
export const acmeInputManifest = {
|
|
78
|
+
type: 'acme.Input',
|
|
79
|
+
traits: [COMPONENT_TRAIT.DATA_FIELD],
|
|
80
|
+
meta: {
|
|
81
|
+
title: '输入框',
|
|
82
|
+
category: COMPONENT_CATEGORY.DATA_ENTRY,
|
|
83
|
+
valueSchema: { type: 'string', default: '' },
|
|
84
|
+
},
|
|
85
|
+
engine: {
|
|
86
|
+
render: {
|
|
87
|
+
injection: {
|
|
88
|
+
rootPath: INJECT_PATH_SLOT_PROPS,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
props: {
|
|
93
|
+
type: 'object',
|
|
94
|
+
properties: {
|
|
95
|
+
placeholder: { type: 'string', title: '占位提示' },
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
events: {
|
|
99
|
+
valueChange: { title: '值变化' },
|
|
100
|
+
},
|
|
101
|
+
} satisfies ComponentManifest;
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 声明后会得到什么
|
|
107
|
+
|
|
108
|
+
| 能力 | 说明 |
|
|
109
|
+
|------|------|
|
|
110
|
+
| 值注入 | 宿主向组件注入当前 `value` |
|
|
111
|
+
| 值回写 | 组件调用 `onChange` 后,宿主更新值 |
|
|
112
|
+
| 类型识别 | 设计器、表达式和 AI 工具可读取 `meta.valueSchema` |
|
|
113
|
+
| 容器协同 | 在数据容器内,字段可按 `name` 写入容器 value |
|
|
114
|
+
| 自动状态补充 | 宿主可基于数据能力补充标准 value 状态 |
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 自检
|
|
119
|
+
|
|
120
|
+
- [ ] `traits` 包含 `COMPONENT_TRAIT.DATA_FIELD`。
|
|
121
|
+
- [ ] `meta.valueSchema` 描述了 value 类型。
|
|
122
|
+
- [ ] 组件接收 `value`。
|
|
123
|
+
- [ ] 值变化时调用 `onChange(nextValue)`。
|
|
124
|
+
- [ ] `validateManifest()` 没有 error。
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 常见错误
|
|
129
|
+
|
|
130
|
+
### 只声明 trait,但没有 `valueSchema`
|
|
131
|
+
|
|
132
|
+
这会影响设计器和 AI 工具推断数据类型。`validateManifest()` 会给出 warning。
|
|
133
|
+
|
|
134
|
+
### 使用第三方组件但没有转换 value/onChange
|
|
135
|
+
|
|
136
|
+
如果第三方组件使用 `selectedValue`、`onSelectedValueChange` 等命名,需要在 wrapper 内转换,或阅读 [使用 Adapter 适配组件 API](./使用Adapter适配组件API.md)。
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 关联文档
|
|
141
|
+
|
|
142
|
+
本文档只保留本层级职责内容:Recipe 提供任务步骤,Reference 提供稳定模型和规则,validateManifest 文档提供校验级别事实源。
|
|
143
|
+
|
|
144
|
+
- 模型参考:[Traits能力模型](../reference/Traits能力模型.md)
|
|
145
|
+
- 字段参考:[Manifest字段参考](../reference/Manifest字段参考.md)
|
|
146
|
+
- 校验规则:[validateManifest校验规则](../reference/validateManifest校验规则.md)
|
|
147
|
+
- 任务 Recipe:[使用Adapter适配组件API](./使用Adapter适配组件API.md)
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: recipe
|
|
3
|
+
capability: data-container
|
|
4
|
+
related:
|
|
5
|
+
- reference/Traits能力模型.md
|
|
6
|
+
- reference/Manifest字段参考.md
|
|
7
|
+
- reference/validateManifest校验规则.md
|
|
8
|
+
- recipes/声明插槽.md
|
|
9
|
+
---
|
|
10
|
+
# 声明数据容器组件
|
|
11
|
+
|
|
12
|
+
数据容器组件用于管理一组子字段的数据作用域,例如 Form、Table、List、CardList 等。
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 适用场景
|
|
17
|
+
|
|
18
|
+
- 组件内部会渲染子字段。
|
|
19
|
+
- 子字段的值应该写入容器的 value 中。
|
|
20
|
+
- 容器需要读取、设置或重置自己的数据。
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 可以跳过的情况
|
|
25
|
+
|
|
26
|
+
- 组件只是布局容器,不管理数据,应该使用 `LAYOUT_CONTAINER`。
|
|
27
|
+
- 组件只是单个输入字段,应该使用 `DATA_FIELD`。
|
|
28
|
+
- 组件只展示数据,不承载子字段写入。
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 作者职责
|
|
33
|
+
|
|
34
|
+
1. 声明 `COMPONENT_TRAIT.DATA_CONTAINER`。
|
|
35
|
+
2. 在 `meta.valueSchema` 中描述容器 value 结构。
|
|
36
|
+
3. 在子组件渲染区域用 `DataScope` 建立数据作用域,由它负责字段路径解析与记录注入。
|
|
37
|
+
4. 如需读写容器数据,使用 `cdp-material-sdk/host-react` 中的 `useDataContainer` / `useDataContainerApi`。
|
|
38
|
+
|
|
39
|
+
`DataScope` 由 SDK 统一提供,作者不需要传 `componentId`,引擎会自动完成运行时上下文装配。
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Manifest 示例
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import {
|
|
47
|
+
COMPONENT_CATEGORY,
|
|
48
|
+
COMPONENT_TRAIT,
|
|
49
|
+
INJECT_PATH_SLOT_PROPS,
|
|
50
|
+
type ComponentManifest,
|
|
51
|
+
} from 'cdp-material-sdk/portable';
|
|
52
|
+
|
|
53
|
+
export const acmeFormManifest = {
|
|
54
|
+
type: 'acme.Form',
|
|
55
|
+
traits: [COMPONENT_TRAIT.DATA_CONTAINER, COMPONENT_TRAIT.LAYOUT_CONTAINER],
|
|
56
|
+
meta: {
|
|
57
|
+
title: '表单',
|
|
58
|
+
category: COMPONENT_CATEGORY.DATA_ENTRY,
|
|
59
|
+
valueSchema: {
|
|
60
|
+
type: 'object',
|
|
61
|
+
default: {},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
engine: {
|
|
65
|
+
render: {
|
|
66
|
+
injection: {
|
|
67
|
+
rootPath: INJECT_PATH_SLOT_PROPS,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
slots: {
|
|
72
|
+
default: {
|
|
73
|
+
title: '表单内容',
|
|
74
|
+
description: '放置表单字段',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
} satisfies ComponentManifest;
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 提供数据作用域
|
|
83
|
+
|
|
84
|
+
数据容器组件在子组件渲染区域包一层 `DataScope`,使内部字段组件能解析字段路径、读取当前记录、注册字段状态。
|
|
85
|
+
|
|
86
|
+
### DataScope 入参
|
|
87
|
+
|
|
88
|
+
| 入参 | 何时传 | 说明 |
|
|
89
|
+
|------|--------|------|
|
|
90
|
+
| `getRecord` | 对象型容器(Form) | 记录获取函数。各字段实时读取最新容器数据,避免 record 变化触发后代重渲染。 |
|
|
91
|
+
| `record` | 数组型容器(Table、CardList 每行) | 当前作用域的记录实例。与 `getRecord` 二选一。 |
|
|
92
|
+
| `relativePath` | 数组型容器 | 决定子字段在容器 value 内的访问位置,例如 `(childId) => `[${index}].${childId}``。默认为 `(childId) => childId`。 |
|
|
93
|
+
| `index` | 数组型容器 | 当前作用域索引,供子组件读取。 |
|
|
94
|
+
| `registerField` / `unregisterField` | 需要聚合字段状态的容器(Form) | 让子字段上报 `hidden` / `readOnly` / `required` / `label` 等状态。不需要可省略。 |
|
|
95
|
+
|
|
96
|
+
### 示例:Form 这类对象型容器
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
import { useCallback } from 'react';
|
|
100
|
+
import { DataScope, useDataContainerApi } from 'cdp-material-sdk/host-react';
|
|
101
|
+
import { useFieldRegistry } from './useFieldRegistry';
|
|
102
|
+
|
|
103
|
+
export function AcmeForm({ children }) {
|
|
104
|
+
const { getContainerData } = useDataContainerApi<Record<string, unknown>>();
|
|
105
|
+
|
|
106
|
+
// 实时获取最新表单数据,依赖项只有 getContainerData,引用稳定
|
|
107
|
+
const getCurrentFormData = useCallback(
|
|
108
|
+
() => getContainerData() ?? {},
|
|
109
|
+
[getContainerData],
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// useFieldRegistry 内部用 ref 维护字段表,register/unregister 引用稳定
|
|
113
|
+
const { registerField, unregisterField } = useFieldRegistry();
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<DataScope
|
|
117
|
+
getRecord={getCurrentFormData}
|
|
118
|
+
registerField={registerField}
|
|
119
|
+
unregisterField={unregisterField}
|
|
120
|
+
>
|
|
121
|
+
{children}
|
|
122
|
+
</DataScope>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
要点:
|
|
128
|
+
|
|
129
|
+
- `getRecord` 推荐用 `useCallback` 包,避免每次 render 给 `DataScope` 传新函数引用。
|
|
130
|
+
- `registerField` / `unregisterField` 需保证引用稳定,避免子字段重复注册。
|
|
131
|
+
- 不需要也不要传 `componentId`。
|
|
132
|
+
|
|
133
|
+
### 示例:Table 这类数组型容器
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
import { DataScope } from 'cdp-material-sdk/host-react';
|
|
137
|
+
|
|
138
|
+
export function TableRows({ rows, renderCells }) {
|
|
139
|
+
return rows.map((row, index) => (
|
|
140
|
+
<DataScope
|
|
141
|
+
key={getRowKey(row)}
|
|
142
|
+
relativePath={(childId) => `[${index}].${childId}`}
|
|
143
|
+
record={row}
|
|
144
|
+
index={index}
|
|
145
|
+
>
|
|
146
|
+
{renderCells(row, index)}
|
|
147
|
+
</DataScope>
|
|
148
|
+
));
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
要点:
|
|
153
|
+
|
|
154
|
+
- 每行一个 `DataScope`,`record` 直接传本行数据。
|
|
155
|
+
- `relativePath` 用于拼接表格下标 + 子字段,让子组件拿到容器 value 内的绝对路径。
|
|
156
|
+
- 数组型容器通常不需要 `registerField`;每行是独立作用域。
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## 读取和设置容器数据
|
|
161
|
+
|
|
162
|
+
只有在确认组件包与宿主共享 React runtime 和 Context 身份时,才使用 `host-react`。SDK 提供两个 hook:
|
|
163
|
+
|
|
164
|
+
| Hook | 返回值 | 是否订阅容器数据变化 | 典型场景 |
|
|
165
|
+
|------|--------|----------------------|----------|
|
|
166
|
+
| `useDataContainerApi` | `{ getContainerData, setContainerData, resetContainerData }` | 否 | 事件回调、`useImperativeHandle` 暴露的动作、`useCallback` 内部按需取值;容器自身不依赖数据触发重渲染 |
|
|
167
|
+
| `useDataContainer` | 在 `useDataContainerApi` 基础上额外返回 `containerData` | 是 | 需要随容器值变化重渲染的展示场景,例如调试预览、汇总统计 |
|
|
168
|
+
|
|
169
|
+
经验法则:
|
|
170
|
+
|
|
171
|
+
- 默认用 `useDataContainerApi`。命令式 API 引用稳定,可放进 `useCallback` 依赖列表,避免不必要的重渲染。
|
|
172
|
+
- 只有当组件 UI 必须随容器值变化而刷新时,才用 `useDataContainer`,因为它会让组件订阅整个容器值。
|
|
173
|
+
- 子字段读取自身值不要走这两个 hook,应通过 `DataScope` 解析的字段路径,由字段层 hook 处理。
|
|
174
|
+
|
|
175
|
+
### 示例:命令式读取(推荐默认)
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
import { useCallback } from 'react';
|
|
179
|
+
import { useDataContainerApi } from 'cdp-material-sdk/host-react';
|
|
180
|
+
|
|
181
|
+
export function AcmeFormSubmit() {
|
|
182
|
+
const { getContainerData, setContainerData } = useDataContainerApi<Record<string, unknown>>();
|
|
183
|
+
|
|
184
|
+
const handleSubmit = useCallback(() => {
|
|
185
|
+
const formData = getContainerData() ?? {};
|
|
186
|
+
submitToServer(formData);
|
|
187
|
+
}, [getContainerData]);
|
|
188
|
+
|
|
189
|
+
return <button onClick={handleSubmit}>提交</button>;
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### 示例:响应式订阅(仅在需要时用)
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
import { useDataContainer } from 'cdp-material-sdk/host-react';
|
|
197
|
+
|
|
198
|
+
export function FormPreview() {
|
|
199
|
+
const { containerData } = useDataContainer<Record<string, unknown>>();
|
|
200
|
+
return <pre>{JSON.stringify(containerData, null, 2)}</pre>;
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
如果无法确认共享 runtime,请不要依赖 `host-react`,改为通过 props、events 或 actions 与宿主交互。
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## valueSchema 选择
|
|
209
|
+
|
|
210
|
+
| 容器类型 | 推荐 valueSchema |
|
|
211
|
+
|----------|------------------|
|
|
212
|
+
| Form | `{ type: 'object', default: {} }` |
|
|
213
|
+
| Table | `{ type: 'array', default: [], items: { type: 'object' } }` |
|
|
214
|
+
| CardList | `{ type: 'array', default: [], items: { type: 'object' } }` |
|
|
215
|
+
| KeyValueMap | `{ type: 'object', additionalProperties: true }` |
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## 自检
|
|
220
|
+
|
|
221
|
+
- [ ] `traits` 包含 `COMPONENT_TRAIT.DATA_CONTAINER`。
|
|
222
|
+
- [ ] `meta.valueSchema` 描述容器数据结构。
|
|
223
|
+
- [ ] 在子组件渲染区域用 `DataScope` 建立了数据作用域,未传 `componentId`。
|
|
224
|
+
- [ ] `getRecord` / `registerField` / `unregisterField` 引用稳定(`useCallback` 或 ref 包装)。
|
|
225
|
+
- [ ] 如果允许拖入子组件,声明了 `LAYOUT_CONTAINER` 或 slots。
|
|
226
|
+
- [ ] 如果使用 `host-react`,已确认与宿主共享 React runtime。
|
|
227
|
+
- [ ] `validateManifest()` 没有 error。
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## 常见错误
|
|
232
|
+
|
|
233
|
+
### 把普通布局容器声明成 DATA_CONTAINER
|
|
234
|
+
|
|
235
|
+
如果组件只是 Card、Grid、Tabs 这类布局组件,不管理子字段数据,不应声明 `DATA_CONTAINER`。
|
|
236
|
+
|
|
237
|
+
### 声明了 DATA_CONTAINER 但没有 valueSchema
|
|
238
|
+
|
|
239
|
+
这会影响设计器、表达式和 AI 工具理解容器数据结构。`validateManifest()` 会给出 warning。
|
|
240
|
+
|
|
241
|
+
### 声明了 DATA_CONTAINER 但没有提供 DataScope
|
|
242
|
+
|
|
243
|
+
子字段无法解析容器内的字段路径,表达式、AI 工具和字段状态注册都会失效。请在子组件渲染区域包一层 `DataScope`。
|
|
244
|
+
|
|
245
|
+
### 给 DataScope 传 inline 函数
|
|
246
|
+
|
|
247
|
+
在 `DataScope` 上直接写 `getRecord={() => ...}` 或 `registerField={(field) => ...}` 会导致每次父组件 render 都生成新引用,子字段可能重复订阅/重复注册。请使用 `useCallback` 或稳定引用。
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## 关联文档
|
|
252
|
+
|
|
253
|
+
本文档只保留本层级职责内容:Recipe 提供任务步骤,Reference 提供稳定模型和规则,validateManifest 文档提供校验级别事实源。
|
|
254
|
+
|
|
255
|
+
- 模型参考:[Traits能力模型](../reference/Traits能力模型.md)
|
|
256
|
+
- 字段参考:[Manifest字段参考](../reference/Manifest字段参考.md)
|
|
257
|
+
- 校验规则:[validateManifest校验规则](../reference/validateManifest校验规则.md)
|
|
258
|
+
- 任务 Recipe:[声明插槽](./声明插槽.md)
|