cdp-material-sdk 0.0.9 → 0.0.10

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.
@@ -10,38 +10,62 @@ related:
10
10
  ---
11
11
  # 声明布局容器组件
12
12
 
13
- 布局容器组件用于承载子组件,例如 Card、Grid、Tabs、Collapse、Section 等。
13
+ `LAYOUT_CONTAINER` trait 用于声明组件具备一个**默认 children 区域**——设计器中拖入的子节点会被宿主递归渲染并作为 React `children` 传入组件。
14
14
 
15
- `LAYOUT_CONTAINER` 与 `DATA_CONTAINER` **不互斥**:只要组件同时承载子组件、又管理子字段数据作用域,就应同时声明两个 trait。典型代表是 Form:既是布局容器(拖入字段子组件),又是数据容器(管理表单值)。
15
+ `LAYOUT_CONTAINER` 与 `DATA_CONTAINER` **不互斥**:Form 这类组件既需要默认 children 区域承载字段子组件、又需要管理表单值,应同时声明两个 trait
16
+
17
+ ---
18
+
19
+ ## LAYOUT_CONTAINER 与 slots 是两条独立路径
20
+
21
+ 宿主在运行时**独立判断**这两个开关,互不依赖:
22
+
23
+ | 接子节点的方式 | 是否需要 LAYOUT_CONTAINER | 是否需要 `manifest.slots` 声明 |
24
+ |---|:---:|:---:|
25
+ | 默认 children 区域(用户在画布拖入子节点) | ✅ | ❌ |
26
+ | 命名 / 作用域 / 动态插槽(用户填充具名区域) | ❌ | ✅ |
27
+ | 两者并存(既有匿名主区又有 header / footer 等具名区) | ✅ | ✅ |
28
+ | 两者都没有(按钮、输入框) | ❌ | ❌ |
29
+
30
+ 只通过具名插槽接子内容的组件**不需要** `LAYOUT_CONTAINER` trait,直接走 [声明插槽](./声明插槽.md)。
16
31
 
17
32
  ---
18
33
 
19
34
  ## 适用场景
20
35
 
21
- - 组件提供一个可放置子组件的区域。
22
- - 组件可能限制子组件的类型、最少/最多数量。
23
- - 不论组件是否还参与数据作用域。只要可以拖入子组件,就应声明 `LAYOUT_CONTAINER`。
36
+ - 组件需要一个默认的、匿名的子组件区域,由用户在设计器拖入子节点。
37
+ - 组件需要按 `nesting` 约束限制子组件类型、最少/最多数量。
24
38
 
25
39
  ---
26
40
 
27
41
  ## 可以跳过的情况
28
42
 
29
43
  - 组件只是单纯的展示组件,没有子内容。
30
- - 子内容只以命名/作用域/动态区域出现,不需要默认 children 区域,应改用 [声明插槽](./声明插槽.md)。
44
+ - 子内容只以命名 / 作用域 / 动态插槽出现,没有默认 children 区域 → 走 [声明插槽](./声明插槽.md)。
31
45
  - 组件是数据容器但不接受子组件(例如只接 props 的表格),只需要 [声明数据容器组件](./声明数据容器组件.md)。
32
46
 
33
47
  ---
34
48
 
35
- ## 选择 children、slots 还是 nesting
49
+ ## 选择 children + nesting 还是 slots
50
+
51
+ | 子节点形态 | 推荐方案 | 例子 |
52
+ |---|---|---|
53
+ | 通用匿名子区域 | `LAYOUT_CONTAINER`(无 `nesting`) | Card、Section、Grid |
54
+ | 单一/受限类型的重复子节点(强组合关系) | `LAYOUT_CONTAINER` + `nesting.allowedChildren` / `allowedParents` | Tabs/TabPane、Steps/Step、Form/FormItem、Collapse/Panel |
55
+ | 多个不同语义的具名区域 | `slots`(**不需要** LAYOUT_CONTAINER) | Modal 的 footer、Card 的 header/extra |
56
+ | 数据驱动的多个子区域 | 动态 / 作用域 `slots` | Table 列模板、List 行模板 |
57
+ | 既有匿名主区又有具名扩展区 | `LAYOUT_CONTAINER` + `slots` | Section + header/footer |
58
+
59
+ ### DSL / 低代码场景的归类原则
60
+
61
+ **单一类型重复的子节点关系优先用 children + nesting,不用 slots**:
62
+
63
+ - **children + nesting**:子节点是**真正的 DSL 节点**,在节点树中可独立选中、配置、命名、移动、复制粘贴。
64
+ - **slots**:slot 内容是 slot 项的填充物,节点树中会被嵌套在 slot 名下,结构表达更"封闭"。
36
65
 
37
- | 场景 | 推荐方案 |
38
- |------|----------|
39
- | 一个匿名的子组件区域 | `children` + `LAYOUT_CONTAINER` |
40
- | 多个具名区域(header / footer / actions) | `slots` |
41
- | 子区域带运行时上下文(行数据、列定义) | 作用域 `slots` |
42
- | 同时限制可放入的组件类型或数量 | 加 `nesting` |
66
+ Tabs/TabPane、Steps/Step、Form/FormItem、Collapse/Panel 这类**强组合关系**应走 children + nesting,用 `nesting.allowedChildren` / `allowedParents` 锁定可拖入的子类型与父类型。
43
67
 
44
- `children` 模式与 `slots` 模式可以共存:组件可以同时有默认 `children` 区域和若干命名插槽。
68
+ slots 更适合"多个不同语义的装饰区/扩展区"和"数据驱动的模板区域"。
45
69
 
46
70
  ---
47
71
 
@@ -126,9 +126,11 @@ slots: {
126
126
 
127
127
  宿主无法生成稳定 slot 名称,`validateManifest()` 会报错。
128
128
 
129
- ### 普通容器只声明 LAYOUT_CONTAINER,不声明 slots
129
+ ### 把多个固定区域都塞进默认 children
130
130
 
131
- 如果组件有明确可填充区域,建议声明 slots。`LAYOUT_CONTAINER` 只说明组件具备布局容器能力,slots 才说明具体区域。
131
+ `LAYOUT_CONTAINER` 只控制**默认 children 区域**的开关,与 `slots` 是两条独立机制。如果组件有 header / footer / extra 等多个固定区域,应声明 `slots` 让宿主分别注入到 `_slots[name]`,不要把它们都挤进 `children`,否则设计器和 AI 工具会失去结构信息。
132
+
133
+ 只用 slots 接子内容的组件**不需要**声明 `LAYOUT_CONTAINER` trait。
132
134
 
133
135
  ---
134
136
 
@@ -37,7 +37,8 @@ related:
37
37
  2. 在 `meta.valueSchema` 中描述 value 类型。
38
38
  3. 组件接收 `value`,值变化时调用 `onChange(nextValue)`。
39
39
  4. 如组件支持只读/必填/标签语义,分别接收并应用 `readOnly` / `required` / `label`;置于容器内时支持 `name`。
40
- 5. 一般不需要自己声明 `value` / `readOnly` / `required` / `name` / `label` / `labelStrategy` props,也不需要重复声明 `getValue` / `setValue` / `getReadOnly` / `setReadOnly` / `toggleReadOnly` / `getRequired` / `setRequired` / `toggleRequired` / `triggerValueChange` actions、`value` / `readOnly` / `required` state 或 `valueChange` event——它们由引擎在 `DATA_FIELD` trait 上自动注入并按 `valueSchema` 特化。重复声明会**覆盖**引擎版本,会丢失自动特化;**仅在确有特化需求**(如定制 setValue 取值规则)时才建议显式覆盖。完整字段规格见 [Traits能力模型 · DATA_FIELD](../reference/Traits能力模型.md#data_field)。
40
+ 5. 如组件支持 `placeholder`,**仅在可编辑态显示**;`readOnly` / `disabled` 等非编辑态默认隐藏占位提示,避免与已锁定的真实值混淆。
41
+ 6. 一般不需要自己声明 `value` / `readOnly` / `required` / `name` / `label` / `labelStrategy` props,也不需要重复声明 `getValue` / `setValue` / `getReadOnly` / `setReadOnly` / `toggleReadOnly` / `getRequired` / `setRequired` / `toggleRequired` / `triggerValueChange` actions、`value` / `readOnly` / `required` state 或 `valueChange` event——它们由引擎在 `DATA_FIELD` trait 上自动注入并按 `valueSchema` 特化。重复声明会**覆盖**引擎版本,会丢失自动特化;**仅在确有特化需求**(如定制 setValue 取值规则)时才建议显式覆盖。完整字段规格见 [Traits能力模型 · DATA_FIELD](../reference/Traits能力模型.md#data_field)。
41
42
 
42
43
  ---
43
44
 
@@ -68,7 +69,8 @@ export const AcmeInput = forwardRef<HTMLInputElement, AcmeInputProps>((props, re
68
69
  readOnly={readOnly}
69
70
  required={required}
70
71
  name={name}
71
- placeholder={placeholder}
72
+ // placeholder 仅在可编辑态显示;只读态隐藏
73
+ placeholder={readOnly ? undefined : placeholder}
72
74
  onChange={(event) => onChange?.(event.target.value)}
73
75
  />
74
76
  );
@@ -215,6 +217,7 @@ export const AcmeSelect = forwardRef<HTMLDivElement, AcmeSelectProps>((props, re
215
217
  - [ ] `meta.valueSchema` 描述了 value 类型。
216
218
  - [ ] 组件接收 `value`,值变化时调用 `onChange(nextValue)`。
217
219
  - [ ] 组件按需接收并应用 `readOnly` / `required` / `name` / `label` / `labelStrategy`(不必全部支持,但接收的 prop 必须真正生效)。
220
+ - [ ] 若支持 `placeholder`,已确保仅在可编辑态显示,`readOnly` / `disabled` 时隐藏。
218
221
  - [ ] manifest 中没有不必要地重复声明 DATA_FIELD 自动注入的 props / actions / state / events;确有覆盖的,能明确说出特化原因。
219
222
  - [ ] `validateManifest()` 没有 error。
220
223
 
@@ -234,6 +237,10 @@ export const AcmeSelect = forwardRef<HTMLDivElement, AcmeSelectProps>((props, re
234
237
 
235
238
  在自己的 manifest 里再写一遍 `value` prop、`getValue` / `setValue` action、`valueChange` event 等,会**覆盖**引擎注入的版本,并**丢失 `valueSchema` 自动特化**。默认只写**业务专属**的 props(如 `placeholder`)和事件;仅在**确有特化需求**时才显式覆盖自动注入的字段,并在代码/PR 中说明原因。
236
239
 
240
+ ### `placeholder` 在只读/禁用态仍然显示
241
+
242
+ 占位提示与已锁定的真实值同时出现,会让使用者误解控件状态。约定是**只在可编辑态显示** `placeholder`,进入 `readOnly` / `disabled` 时清空或不传该 prop(如 `placeholder={readOnly ? undefined : placeholder}`)。
243
+
237
244
  ### 接收了 `readOnly` / `required` 却没有真正生效
238
245
 
239
246
  引擎会注入 `readOnly` / `required` 并在 state 中持续更新;如果组件渲染时忽略这两个 prop,事件编排里 `setReadOnly` / `toggleRequired` 等 action 看起来生效(state 变了)但 UI 不变,会让使用者困惑。要么接收并应用,要么不接收(说明组件不支持该语义)。
@@ -22,7 +22,7 @@ Traits 之间不互斥,一个组件可以同时声明多个 trait。例如 For
22
22
  |-------|------|----------|------------|
23
23
  | `Data.Field` | `COMPONENT_TRAIT.DATA_FIELD` | Input、Select、Switch、DatePicker | 组件是一个数据字段 |
24
24
  | `Data.Container` | `COMPONENT_TRAIT.DATA_CONTAINER` | Form、Table、List、CardList | 组件管理一组数据作用域 |
25
- | `Layout.Container` | `COMPONENT_TRAIT.LAYOUT_CONTAINER` | Card、Grid、Tabs、Collapse | 组件可以承载子组件或插槽内容 |
25
+ | `Layout.Container` | `COMPONENT_TRAIT.LAYOUT_CONTAINER` | 通用容器:Card、Grid、Section;强组合关系:Tabs、Steps、Form(带 `nesting` 类型约束) | 组件具备**默认 children 区域**(与 `manifest.slots` 是两条独立机制) |
26
26
  | `Interaction.Clickable` | `COMPONENT_TRAIT.INTERACTION_CLICKABLE` | Button、Link、ClickableCard | 组件具备点击交互语义 |
27
27
 
28
28
  ---
@@ -112,15 +112,21 @@ Traits 之间不互斥,一个组件可以同时声明多个 trait。例如 For
112
112
 
113
113
  ### 什么时候声明
114
114
 
115
- 组件可以承载子组件、插槽或模板内容。
115
+ 组件需要一个**默认 children 区域**:设计器中拖入的子节点会被宿主递归渲染并作为 React `children` 传入。
116
+
117
+ 只通过命名 / 作用域 / 动态插槽承载子内容的组件**不需要**声明此 trait,直接通过 `manifest.slots` 即可。
116
118
 
117
119
  ### 作者要做什么
118
120
 
119
121
  - 声明 `COMPONENT_TRAIT.LAYOUT_CONTAINER`。
120
- - 在组件实现中渲染 `children`(单一区域)或对应 slot 内容(多具名区域)。
121
- - 需要限制子组件类型、最少/最多数量、或能作为子节点的父组件时,声明 `nesting`。
122
+ - 在组件实现中渲染 `children`。
123
+ - 需要限制默认 children 区域的子组件类型、最少/最多数量、或能作为子节点的父组件时,声明 `nesting`。
122
124
  - 详细任务步骤见 [声明布局容器组件](../recipes/声明布局容器组件.md)。
123
125
 
126
+ ### 与 slots 的关系
127
+
128
+ 宿主在运行时独立判断 `LAYOUT_CONTAINER` trait 与 `manifest.slots`:前者控制默认 children 区域,后者控制具名插槽。两者可以并存,也可以独立使用。
129
+
124
130
  ### 宿主可据此做什么
125
131
 
126
132
  - 在设计器中允许拖入子组件。
@@ -170,9 +176,11 @@ meta: {
170
176
  | 组件场景 | 推荐 traits |
171
177
  |----------|-------------|
172
178
  | 输入框、选择器、开关 | `DATA_FIELD` |
173
- | 表单 | `DATA_CONTAINER` + `LAYOUT_CONTAINER` |
174
- | 表格、列表 | `DATA_CONTAINER`,按需加 `LAYOUT_CONTAINER` |
175
- | 卡片、栅格、Tabs | `LAYOUT_CONTAINER` |
179
+ | 表单 | `DATA_CONTAINER` + `LAYOUT_CONTAINER`(搭配 `nesting.allowedChildren` 锁定 FormItem 等) |
180
+ | 表格、列表 | `DATA_CONTAINER`,列/行模板用动态 / 作用域 `slots` |
181
+ | 卡片、栅格、Section(通用 children 区域) | `LAYOUT_CONTAINER` |
182
+ | Tabs / Steps / Collapse(强组合关系) | `LAYOUT_CONTAINER` + `nesting`(子/父类型约束) |
183
+ | Modal / Card 的多具名扩展区(header / footer) | `slots` |
176
184
  | 按钮、链接 | `INTERACTION_CLICKABLE` |
177
185
  | 普通文本展示 | 可不声明 |
178
186
 
@@ -184,7 +192,7 @@ meta: {
184
192
  |------|------|
185
193
  | `DATA_FIELD` | 仅用于有主要业务值且能回写的组件 |
186
194
  | `DATA_CONTAINER` | 用于管理子字段数据作用域;必须在子组件渲染区域提供 `DataScope`。与 `LAYOUT_CONTAINER` 不互斥,Form 这类容器应同时声明 |
187
- | `LAYOUT_CONTAINER` | 表示可承载子内容;有明确区域时建议同时声明 slots |
195
+ | `LAYOUT_CONTAINER` | 控制**默认 children 区域**开关,仅影响 `schema.children` 是否被宿主递归渲染。具名子区域走 `manifest.slots`,与本 trait 互不依赖 |
188
196
  | `INTERACTION_CLICKABLE` | 表示具备点击语义;需要编排响应时再声明 click event |
189
197
 
190
198
  数据 trait 与 `meta.valueSchema` 的校验级别见 [validateManifest 校验规则](./validateManifest校验规则.md)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdp-material-sdk",
3
- "version": "0.0.9",
3
+ "version": "0.0.10",
4
4
  "license": "MIT",
5
5
  "publishConfig": {
6
6
  "access": "public",