@praxisui/dynamic-form 8.0.0-beta.2 → 8.0.0-beta.21
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/README.md +166 -18
- package/docs/dynamic-form-authoring-document-semantics.md +80 -0
- package/docs/dynamic-form-llm-rule-authoring-guide.md +318 -0
- package/docs/dynamic-form-rules-authoring-plan.md +379 -0
- package/docs/hot-metadata-updates.md +141 -0
- package/docs/layout-items-visual-blocks.md +406 -0
- package/fesm2022/praxisui-dynamic-form-dynamic-form-agentic-authoring-turn-flow-DuH1ad7h.mjs +417 -0
- package/fesm2022/praxisui-dynamic-form.mjs +9147 -2078
- package/index.d.ts +640 -22
- package/package.json +12 -6
- package/src/lib/components/praxis-form-actions/praxis-form-actions.json-api.md +441 -0
- package/src/lib/config-editor/praxis-dynamic-form-config-editor.json-api.md +481 -0
- package/src/lib/filter-form/praxis-filter-form.json-api.md +507 -0
- package/src/lib/json-config-editor/form-json-config-editor.json-api.md +491 -0
- package/src/lib/layout-editor/praxis-layout-editor.json-api.md +432 -0
- package/src/lib/praxis-dynamic-form.json-api.md +1011 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Dynamic Form Layout Items and Visual Blocks"
|
|
3
|
+
slug: "dynamic-form-layout-items-visual-blocks"
|
|
4
|
+
description: "Guia canônico para usar FormColumn.items, campos da API e blocos visuais richContent no Dynamic Form."
|
|
5
|
+
doc_type: "guide"
|
|
6
|
+
document_kind: "contract-guide"
|
|
7
|
+
category: "forms"
|
|
8
|
+
audience:
|
|
9
|
+
- "frontend"
|
|
10
|
+
- "host"
|
|
11
|
+
- "platform-team"
|
|
12
|
+
- "architect"
|
|
13
|
+
level: "advanced"
|
|
14
|
+
status: "active"
|
|
15
|
+
owner: "praxis-ui"
|
|
16
|
+
tags:
|
|
17
|
+
- "dynamic-form"
|
|
18
|
+
- "layout-items"
|
|
19
|
+
- "visual-blocks"
|
|
20
|
+
- "rich-content"
|
|
21
|
+
- "form-column-items"
|
|
22
|
+
toc: true
|
|
23
|
+
sidebar: true
|
|
24
|
+
reading_time: "12 min"
|
|
25
|
+
estimated_setup_time: "20 min"
|
|
26
|
+
last_updated: "2026-04-22"
|
|
27
|
+
source_of_truth:
|
|
28
|
+
- "projects/praxis-dynamic-form/README.md"
|
|
29
|
+
- "projects/praxis-dynamic-form/docs/dynamic-form-authoring-document-semantics.md"
|
|
30
|
+
- "projects/praxis-dynamic-form/docs/dynamic-form-rules-authoring-plan.md"
|
|
31
|
+
- "projects/praxis-dynamic-form/src/lib/layout-editor/praxis-layout-editor.json-api.md"
|
|
32
|
+
- "projects/praxis-core/src/lib/models/form/form-layout-items.model.ts"
|
|
33
|
+
- "projects/praxis-core/src/lib/models/form/rule-property.schema.ts"
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
# Dynamic Form Layout Items and Visual Blocks
|
|
37
|
+
|
|
38
|
+
`FormColumn.items` é o contrato canônico para ordenar campos da API e conteúdo visual dentro de uma coluna do Dynamic Form.
|
|
39
|
+
|
|
40
|
+
A regra principal é simples: campos de negócio continuam em `fieldMetadata[]`; conteúdo visual explicativo entra em `columns[].items[]` como `kind: 'richContent'`.
|
|
41
|
+
|
|
42
|
+
## Modelo mental
|
|
43
|
+
|
|
44
|
+
```mermaid
|
|
45
|
+
flowchart TD
|
|
46
|
+
metadata["fieldMetadata[]<br/>campos vindos do DTO/API"] --> fieldItem["items[]<br/>kind: field"]
|
|
47
|
+
richContent["RichContentDocument<br/>texto, aviso, separador ou card"] --> visualItem["items[]<br/>kind: richContent"]
|
|
48
|
+
fieldItem --> column["FormColumn.items[]<br/>ordem canônica"]
|
|
49
|
+
visualItem --> column
|
|
50
|
+
column --> runtime["Dynamic Form runtime"]
|
|
51
|
+
column --> editor["Layout editor"]
|
|
52
|
+
fieldItem --> payload["formData / payload HTTP"]
|
|
53
|
+
visualItem --> presentation["apenas apresentação"]
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Use `items[]` quando a coluna precisa misturar campos e blocos visuais na mesma ordem. Use `fields[]` apenas como projeção derivada para compatibilidade e migração.
|
|
57
|
+
|
|
58
|
+
## Contrato mínimo
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"sections": [
|
|
63
|
+
{
|
|
64
|
+
"id": "identity",
|
|
65
|
+
"rows": [
|
|
66
|
+
{
|
|
67
|
+
"columns": [
|
|
68
|
+
{
|
|
69
|
+
"id": "identity-main",
|
|
70
|
+
"items": [
|
|
71
|
+
{
|
|
72
|
+
"kind": "richContent",
|
|
73
|
+
"id": "identity-introduction-block",
|
|
74
|
+
"layout": "block",
|
|
75
|
+
"document": {
|
|
76
|
+
"schemaVersion": 1,
|
|
77
|
+
"nodes": [
|
|
78
|
+
{
|
|
79
|
+
"type": "paragraph",
|
|
80
|
+
"text": "Revise os dados principais antes de salvar."
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"kind": "field",
|
|
87
|
+
"id": "field-nome",
|
|
88
|
+
"fieldName": "nome"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"kind": "field",
|
|
92
|
+
"id": "field-email",
|
|
93
|
+
"fieldName": "email"
|
|
94
|
+
}
|
|
95
|
+
],
|
|
96
|
+
"fields": ["nome", "email"]
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
],
|
|
103
|
+
"fieldMetadata": [
|
|
104
|
+
{ "name": "nome", "label": "Nome", "editorType": "text" },
|
|
105
|
+
{ "name": "email", "label": "E-mail", "editorType": "email" }
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Neste exemplo, `identity-introduction-block` é conteúdo visual. Ele não tem entrada em `fieldMetadata[]`, não cria `FormControl` e não participa do payload de submit.
|
|
111
|
+
|
|
112
|
+
## Campos da API
|
|
113
|
+
|
|
114
|
+
“Adicionar campo da API” significa reinserir no layout um campo que já existe em `fieldMetadata[]`, mas ainda não aparece em `columns[].items[]` como `kind: 'field'`.
|
|
115
|
+
|
|
116
|
+
Essa ação não cria metadado novo. Ela apenas escreve um item de campo no layout:
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"kind": "field",
|
|
121
|
+
"id": "field-cargo",
|
|
122
|
+
"fieldName": "cargo"
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
O editor deve manter `fields[]` sincronizado como projeção:
|
|
127
|
+
|
|
128
|
+
```json
|
|
129
|
+
{
|
|
130
|
+
"items": [
|
|
131
|
+
{ "kind": "field", "id": "field-nome", "fieldName": "nome" },
|
|
132
|
+
{ "kind": "field", "id": "field-cargo", "fieldName": "cargo" }
|
|
133
|
+
],
|
|
134
|
+
"fields": ["nome", "cargo"]
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Campos já alocados no layout não devem aparecer novamente na lista de disponíveis. Quando um campo é removido do layout, ele volta para “Campos da API”.
|
|
139
|
+
|
|
140
|
+
## Blocos visuais e presets
|
|
141
|
+
|
|
142
|
+
“Adicionar bloco visual” cria um item `kind: 'richContent'` com um `RichContentDocument` válido. Presets como texto, aviso, separador e card informativo são atalhos de autoria: o preset não é persistido como semântica de runtime.
|
|
143
|
+
|
|
144
|
+
Forma persistida correta:
|
|
145
|
+
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"kind": "richContent",
|
|
149
|
+
"id": "contact-guidance-block",
|
|
150
|
+
"layout": "block",
|
|
151
|
+
"document": {
|
|
152
|
+
"schemaVersion": 1,
|
|
153
|
+
"nodes": [
|
|
154
|
+
{
|
|
155
|
+
"type": "callout",
|
|
156
|
+
"tone": "info",
|
|
157
|
+
"title": "Dados de contato",
|
|
158
|
+
"body": "Use um e-mail corporativo e telefone com DDD."
|
|
159
|
+
}
|
|
160
|
+
]
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Evite persistir IDs que pareçam contrato de preset, como `preset-notice-contact`. Prefira nomes de domínio, como `contact-guidance-block`. Isso deixa claro que o contrato persistido é `richContent`, não `presetId`.
|
|
166
|
+
|
|
167
|
+
## Regras para visual blocks
|
|
168
|
+
|
|
169
|
+
Blocos visuais podem participar de `formRules` como alvo `targetType: "visualBlock"`. Isso permite alterar visibilidade, classe, estilo e, de forma controlada, partes textuais do bloco conforme o estado do formulario.
|
|
170
|
+
|
|
171
|
+
A regra continua visual-only: ela nunca transforma o bloco em campo, nunca cria `FormControl`, nunca escreve em `fieldMetadata[]` e nunca entra em `formData` ou no payload HTTP.
|
|
172
|
+
|
|
173
|
+
### Propriedades permitidas
|
|
174
|
+
|
|
175
|
+
| Propriedade | Uso seguro |
|
|
176
|
+
| --- | --- |
|
|
177
|
+
| `visible` / `hidden` | Exibir ou ocultar o bloco conforme a condicao. |
|
|
178
|
+
| `layout` | Alternar entre `block` e `inline`, quando o host aceita essa variacao. |
|
|
179
|
+
| `className` / `rootClassName` | Aplicar classes CSS controladas pelo host/design system. |
|
|
180
|
+
| `style` | Aplicar estilo inline saneado pelo runtime. |
|
|
181
|
+
| `text` | Sobrescrever texto apenas em documento simples ou node de texto identificado. |
|
|
182
|
+
| `title` | Sobrescrever titulo apenas em documento simples, card/media/timeline suportado ou node identificado. |
|
|
183
|
+
| `message` | Sobrescrever mensagem apenas em documento simples, card/media suportado ou node identificado. |
|
|
184
|
+
| `textNodeId` / `titleNodeId` / `messageNodeId` | Selecionar explicitamente o node que recebera `text`, `title` ou `message`. |
|
|
185
|
+
|
|
186
|
+
O editor visual deve mostrar para usuarios finais as propriedades de negocio (`text`, `title`, `message`, `visible`, `style` etc.) e tratar `*NodeId` como detalhe tecnico guiado por seletor de node. Quando o documento tem mais de um texto possivel, prefira sempre node explicito.
|
|
187
|
+
|
|
188
|
+
Exemplo com nodes explicitos:
|
|
189
|
+
|
|
190
|
+
```json
|
|
191
|
+
{
|
|
192
|
+
"formRules": [
|
|
193
|
+
{
|
|
194
|
+
"id": "employee-guidance-by-status",
|
|
195
|
+
"targetType": "visualBlock",
|
|
196
|
+
"targets": ["employee-guidance-block"],
|
|
197
|
+
"effect": {
|
|
198
|
+
"condition": { "===": [{ "var": "ativo" }, false] },
|
|
199
|
+
"properties": {
|
|
200
|
+
"title": "Revise a inativacao",
|
|
201
|
+
"titleNodeId": "custom-title",
|
|
202
|
+
"message": "Informe justificativa antes de salvar.",
|
|
203
|
+
"messageNodeId": "custom-message",
|
|
204
|
+
"className": "employee-guidance-warning"
|
|
205
|
+
},
|
|
206
|
+
"propertiesWhenFalse": {
|
|
207
|
+
"title": "Cadastro ativo",
|
|
208
|
+
"titleNodeId": "custom-title",
|
|
209
|
+
"message": "Mantenha os dados funcionais atualizados.",
|
|
210
|
+
"messageNodeId": "custom-message",
|
|
211
|
+
"className": "employee-guidance-success"
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
]
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Exemplos por dominio
|
|
220
|
+
|
|
221
|
+
Use exemplos de dominio quando a regra muda texto/estilo para orientar o usuario sem alterar payload. Esses exemplos devem continuar dentro do contrato `visualBlock`: nada de `document`, HTML livre, handlers ou campos falsos em `fieldMetadata`.
|
|
222
|
+
|
|
223
|
+
#### RH: orientacao de funcionario inativo
|
|
224
|
+
|
|
225
|
+
```json
|
|
226
|
+
{
|
|
227
|
+
"formRules": [
|
|
228
|
+
{
|
|
229
|
+
"id": "employee-inactive-guidance",
|
|
230
|
+
"targetType": "visualBlock",
|
|
231
|
+
"targets": ["profile-status-card"],
|
|
232
|
+
"effect": {
|
|
233
|
+
"condition": { "===": [{ "var": "ativo" }, false] },
|
|
234
|
+
"properties": {
|
|
235
|
+
"title": "Funcionario inativo",
|
|
236
|
+
"titleNodeId": "profile-status-title",
|
|
237
|
+
"message": "Revise desligamento, acessos e justificativa antes de salvar.",
|
|
238
|
+
"messageNodeId": "profile-status-message",
|
|
239
|
+
"className": "employee-status-card employee-status-card--warning"
|
|
240
|
+
},
|
|
241
|
+
"propertiesWhenFalse": {
|
|
242
|
+
"title": "Funcionario ativo",
|
|
243
|
+
"titleNodeId": "profile-status-title",
|
|
244
|
+
"message": "Mantenha cargo, departamento e contatos atualizados.",
|
|
245
|
+
"messageNodeId": "profile-status-message",
|
|
246
|
+
"className": "employee-status-card employee-status-card--ok"
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
]
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
#### Compras: aviso de fornecedor critico
|
|
255
|
+
|
|
256
|
+
```json
|
|
257
|
+
{
|
|
258
|
+
"formRules": [
|
|
259
|
+
{
|
|
260
|
+
"id": "supplier-risk-guidance",
|
|
261
|
+
"targetType": "visualBlock",
|
|
262
|
+
"targets": ["supplier-risk-notice"],
|
|
263
|
+
"effect": {
|
|
264
|
+
"condition": { "in": [{ "var": "riscoFornecedor" }, ["alto", "critico"]] },
|
|
265
|
+
"properties": {
|
|
266
|
+
"message": "Fornecedor exige aprovacao de compliance antes da emissao do pedido.",
|
|
267
|
+
"messageNodeId": "message",
|
|
268
|
+
"className": "supplier-risk-notice supplier-risk-notice--critical"
|
|
269
|
+
},
|
|
270
|
+
"propertiesWhenFalse": {
|
|
271
|
+
"message": "Fornecedor sem bloqueios criticos para este pedido.",
|
|
272
|
+
"messageNodeId": "message",
|
|
273
|
+
"className": "supplier-risk-notice supplier-risk-notice--neutral"
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
]
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
#### Atendimento: copy operacional por prioridade
|
|
282
|
+
|
|
283
|
+
```json
|
|
284
|
+
{
|
|
285
|
+
"formRules": [
|
|
286
|
+
{
|
|
287
|
+
"id": "ticket-priority-copy",
|
|
288
|
+
"targetType": "visualBlock",
|
|
289
|
+
"targets": ["ticket-sla-guidance"],
|
|
290
|
+
"effect": {
|
|
291
|
+
"condition": { "===": [{ "var": "prioridade" }, "alta"] },
|
|
292
|
+
"properties": {
|
|
293
|
+
"text": "Prioridade alta: confirme impacto, prazo de SLA e responsavel de escalacao.",
|
|
294
|
+
"textNodeId": "text",
|
|
295
|
+
"rootClassName": "ticket-sla-guidance ticket-sla-guidance--high"
|
|
296
|
+
},
|
|
297
|
+
"propertiesWhenFalse": {
|
|
298
|
+
"text": "Registre contexto suficiente para triagem e acompanhamento.",
|
|
299
|
+
"textNodeId": "text",
|
|
300
|
+
"rootClassName": "ticket-sla-guidance ticket-sla-guidance--standard"
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
]
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Limites do override textual
|
|
309
|
+
|
|
310
|
+
O suporte a `text`, `title` e `message` nao e um editor livre de documento rico. Ele e propositalmente limitado para evitar corrupcao estrutural do `RichContentDocument`.
|
|
311
|
+
|
|
312
|
+
O runtime aplica override somente quando encontra um alvo seguro:
|
|
313
|
+
|
|
314
|
+
- documento simples com um unico node textual ou um unico container simples;
|
|
315
|
+
- node com id convencional `text`, `title` ou `message`;
|
|
316
|
+
- node indicado explicitamente por `textNodeId`, `titleNodeId` ou `messageNodeId`;
|
|
317
|
+
- nodes aninhados em `compose`, `card` ou `mediaBlock` quando o tipo suporta aquela propriedade.
|
|
318
|
+
|
|
319
|
+
O runtime nao deve aceitar HTML arbitrario, handlers, URLs inseguras nem substituicao de documento inteiro via regra. Se o documento for ambiguo e nao houver node identificado, a regra textual deve ser ignorada de forma segura; a configuracao deve aparecer em diagnostico/editor como limite de autoria, nao como falha silenciosa.
|
|
320
|
+
|
|
321
|
+
### DnD, alinhamento e UX
|
|
322
|
+
|
|
323
|
+
Ao arrastar visual blocks entre campos, a experiencia esperada e a mesma dos campos:
|
|
324
|
+
|
|
325
|
+
- preview e placeholder devem preservar largura, altura aproximada e espaçamento da coluna;
|
|
326
|
+
- drop feedback deve indicar o ponto de insercao, nao apenas a coluna;
|
|
327
|
+
- o bloco inserido entre campos deve manter alinhamento horizontal e ritmo vertical;
|
|
328
|
+
- handles de arraste devem ser claros em hover/focus e nao disparar eventos dos controles do formulario;
|
|
329
|
+
- selects, menus e campos interativos proximos nao devem abrir popup quando o usuario inicia drag pelo handle.
|
|
330
|
+
|
|
331
|
+
No runtime, `kind: "richContent"` participa do mesmo ritmo vertical dos campos: o bloco ocupa a largura da coluna e reserva a margem de `--pfx-field-gap`, assim como os campos Material. O conteudo interno continua livre para ser texto, aviso, divisor ou card, mas exemplos publicos nao devem usar cards altos como substitutos de linhas sincronizadas entre colunas; quando o bloco for um painel explicativo grande, prefira uma linha dedicada, coluna lateral consistente ou bloco full-width.
|
|
332
|
+
|
|
333
|
+
O teste de aceitacao deve validar tanto ordem do JSON quanto resultado visual no runtime. Em formularios reais, mova campo com regras entre secoes e confirme que a regra continua referenciando o campo por nome/id canonico, nao por indice visual.
|
|
334
|
+
|
|
335
|
+
### LLM e regras
|
|
336
|
+
|
|
337
|
+
LLMs podem sugerir regras para `visualBlock`, mas devem operar dentro do mesmo contrato:
|
|
338
|
+
|
|
339
|
+
- escrever apenas `formRules`, nunca `formRulesState`;
|
|
340
|
+
- usar somente propriedades publicadas em `RULE_PROPERTY_SCHEMA.visualBlock`;
|
|
341
|
+
- referenciar apenas targets existentes em `columns[].items[]`;
|
|
342
|
+
- usar `textNodeId`, `titleNodeId` ou `messageNodeId` quando o bloco tiver documento composto;
|
|
343
|
+
- marcar a sugestao como pendente de revisao humana antes de persistir;
|
|
344
|
+
- deixar o editor gerar ou reconciliar `formRulesState` apos aceite.
|
|
345
|
+
|
|
346
|
+
Quando a regra sugerida nao faz round-trip para o builder visual, ela continua valida para runtime apenas se passar pelo validador de regras; nesse caso a UI deve trata-la como `runtime-only` e explicar a limitacao.
|
|
347
|
+
|
|
348
|
+
## Submit e payload
|
|
349
|
+
|
|
350
|
+
O runtime deve separar dados de negócio de conteúdo visual:
|
|
351
|
+
|
|
352
|
+
| Superfície | Inclui campo da API | Inclui bloco visual |
|
|
353
|
+
| --- | --- | --- |
|
|
354
|
+
| `fieldMetadata[]` | Sim | Não |
|
|
355
|
+
| `FormControl` | Sim | Não |
|
|
356
|
+
| `formData` | Sim | Não |
|
|
357
|
+
| Payload HTTP | Sim, conforme política do campo | Não |
|
|
358
|
+
| `columns[].items[]` | Sim | Sim |
|
|
359
|
+
| `columns[].fields[]` | Sim, como projeção | Não |
|
|
360
|
+
|
|
361
|
+
Blocos visuais também não usam `source`, `transient` ou `submitPolicy`. Essas propriedades pertencem a campos. Se a experiencia precisa de valor, validacao, regras de campo ou leitura em `rawFormData`, use um campo local em `fieldMetadata[]`; se precisa apenas de orientacao visual ou texto condicionado, use `visualBlock`.
|
|
362
|
+
|
|
363
|
+
## Responsividade e WCAG
|
|
364
|
+
|
|
365
|
+
Blocos visuais entram no fluxo da coluna, então precisam respeitar o mesmo espaço dos campos.
|
|
366
|
+
|
|
367
|
+
Use estas regras em exemplos e authoring corporativo:
|
|
368
|
+
|
|
369
|
+
- Se um bloco usa composição horizontal com texto longo, habilite quebra de linha com `wrap: true` quando o node suportar.
|
|
370
|
+
- Não coloque ação visual sem operação real conectada pelo host.
|
|
371
|
+
- Não use bloco visual para capturar dado de negócio.
|
|
372
|
+
- Não esconda validação, política ou decisão crítica apenas em texto decorativo.
|
|
373
|
+
- Preserve contraste, foco navegável e nomes acessíveis quando o bloco incluir ações, links ou ícones.
|
|
374
|
+
- Prefira texto curto, títulos objetivos e conteúdo que continue legível em zoom alto.
|
|
375
|
+
|
|
376
|
+
## Quando usar
|
|
377
|
+
|
|
378
|
+
Use bloco visual quando a coluna precisa de orientação, separação contextual, aviso operacional ou resumo informativo que pertence ao layout do formulário, mas não ao DTO.
|
|
379
|
+
|
|
380
|
+
Use campo da API quando o dado já existe no contrato backend e apenas foi removido ou ocultado do layout atual.
|
|
381
|
+
|
|
382
|
+
Use campo local, em ciclo separado, quando o dado precisa existir no formulário, mas não pertence ao DTO de submit por padrão. Esse caso exige `fieldMetadata[]` com semântica explícita de campo local/transiente.
|
|
383
|
+
|
|
384
|
+
## Checklist enterprise
|
|
385
|
+
|
|
386
|
+
Antes de publicar um exemplo ou configuração:
|
|
387
|
+
|
|
388
|
+
- `items[]` é a fonte canônica da ordem da coluna.
|
|
389
|
+
- `fields[]` contém apenas nomes de campos e está sincronizado com os itens `kind: 'field'`.
|
|
390
|
+
- Blocos visuais não aparecem em `fieldMetadata[]`.
|
|
391
|
+
- Blocos visuais não aparecem no payload de submit.
|
|
392
|
+
- Regras de visual block alteram apenas apresentacao, classe, estilo ou texto seguro; elas nao criam valor persistivel.
|
|
393
|
+
- Overrides textuais em documentos compostos usam node id explicito.
|
|
394
|
+
- Regras sugeridas por LLM sao revisadas antes de persistir e nao escrevem `formRulesState`.
|
|
395
|
+
- IDs de blocos descrevem o domínio, não o preset usado para criá-los.
|
|
396
|
+
- Exemplos públicos declaram dependência operacional em `@praxisui/dynamic-form` com suporte a `FormColumn.items` e `richContent`.
|
|
397
|
+
- Composições horizontais com texto longo foram revisadas em viewport móvel e zoom alto.
|
|
398
|
+
- A documentação do host deixa claro qual ação é demonstrável e qual conteúdo é apenas apresentação.
|
|
399
|
+
|
|
400
|
+
## Exemplo completo
|
|
401
|
+
|
|
402
|
+
Use o recipe oficial como referência operacional completa:
|
|
403
|
+
|
|
404
|
+
`examples/ai-recipes/praxis-dynamic-page.visual-block-presets-form.json`
|
|
405
|
+
|
|
406
|
+
Ele demonstra campos remotos, blocos `richContent`, projeção `fields[]`, ausência de metadado para blocos visuais e uma página Dynamic Page preparada para catálogo público.
|