@praxisui/dynamic-form 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ This package is licensed under the Apache License, Version 2.0.
2
+
3
+ For the full license text, see the repository root LICENSE file:
4
+ ../../LICENSE
5
+
6
+ Apache License 2.0: https://www.apache.org/licenses/LICENSE-2.0
7
+
package/README.md ADDED
@@ -0,0 +1,166 @@
1
+ # @praxisui/dynamic-form
2
+
3
+ > Standalone dynamic form component with schema-driven UI, native field cascades, settings integration, and a built-in configuration editor.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm i @praxisui/dynamic-form
9
+ ```
10
+
11
+ Peer dependencies (Angular v20):
12
+ - `@angular/core` `^20.0.0`
13
+ - `@angular/common` `^20.0.0`
14
+ - `@angular/cdk` `^20.0.0`
15
+ - `@praxisui/core` `^0.0.1`
16
+ - `@praxisui/specification-core` `^0.0.1`
17
+ - `@praxisui/specification` `^0.0.1`
18
+ - `@praxisui/visual-builder` `^0.0.1`
19
+ - `@praxisui/settings-panel` `^0.0.1`
20
+ - `@praxisui/cron-builder` `^0.0.1`
21
+
22
+ ## Quick Start
23
+
24
+ ### 1) PraxisDynamicForm (standalone)
25
+
26
+ ```ts
27
+ import { Component } from '@angular/core';
28
+ import { PraxisDynamicForm } from '@praxisui/dynamic-form';
29
+ import type { FormConfig } from '@praxisui/core';
30
+
31
+ @Component({
32
+ selector: 'app-form-demo',
33
+ standalone: true,
34
+ imports: [PraxisDynamicForm],
35
+ template: `
36
+ <praxis-dynamic-form
37
+ [config]="config"
38
+ [mode]="'create'"
39
+ [editModeEnabled]="true"
40
+ (formSubmit)="onSubmit($event)"
41
+ ></praxis-dynamic-form>
42
+ `,
43
+ })
44
+ export class FormDemoComponent {
45
+ config: FormConfig = {
46
+ sections: [
47
+ {
48
+ id: 'main',
49
+ label: 'Employee',
50
+ rows: [
51
+ {
52
+ columns: [
53
+ { fields: [{ name: 'fullName', label: 'Full Name', controlType: 'text' }] },
54
+ { fields: [{ name: 'email', label: 'E-mail', controlType: 'email' }] },
55
+ ],
56
+ },
57
+ ],
58
+ },
59
+ ],
60
+ } as any;
61
+
62
+ onSubmit(evt: any) {
63
+ console.log('Submitted:', evt);
64
+ }
65
+ }
66
+ ```
67
+
68
+ Tip: connect to a backend resource by setting `resourcePath`/`resourceId`. The component can fetch schemas and reconcile local layout with server metadata when `editModeEnabled` is true.
69
+
70
+ ### 2) Config Editor component
71
+
72
+ Use the standalone editor directly to edit and reconcile the form configuration.
73
+
74
+ ```ts
75
+ import { Component } from '@angular/core';
76
+ import { MatDialog } from '@angular/material/dialog';
77
+ import { PraxisDynamicFormConfigEditor } from '@praxisui/dynamic-form';
78
+ import type { FormConfig } from '@praxisui/core';
79
+
80
+ @Component({
81
+ selector: 'app-form-editor-launcher',
82
+ standalone: true,
83
+ template: `<button (click)="openEditor()">Open Config Editor</button>`,
84
+ })
85
+ export class FormEditorLauncherComponent {
86
+ constructor(private dialog: MatDialog) {}
87
+
88
+ openEditor() {
89
+ const initial: FormConfig = { sections: [] } as any;
90
+ this.dialog.open(PraxisDynamicFormConfigEditor, {
91
+ width: '1024px',
92
+ data: { formConfig: initial, formId: 'employees-form', mode: 'edit' },
93
+ });
94
+ }
95
+ }
96
+ ```
97
+
98
+ Alternatively, when `editModeEnabled` is true, `praxis-dynamic-form` renders a gear button that opens the editor internally.
99
+
100
+ ## API Surface
101
+
102
+ - Components: `PraxisDynamicForm`, `PraxisDynamicFormConfigEditor`, `JsonConfigEditorComponent`, `LayoutEditorComponent`
103
+ - Services: `FormConfigService`, `FormLayoutService`, `DynamicFormLayoutService`, `FormContextService`
104
+ - Utilities: form rule converters, normalize date arrays
105
+ - Metadata helpers: `providePraxisDynamicFormMetadata`
106
+
107
+ See public exports: `projects/praxis-dynamic-form/src/public-api.ts`.
108
+
109
+ ## Compatibility
110
+
111
+ - `@praxisui/dynamic-form` `0.0.x` → Angular `20.x`
112
+ - Module format: `ESM2022`
113
+
114
+ ## License
115
+
116
+ Apache-2.0 – see the `LICENSE` packaged with this library or the repository root.
117
+
118
+ # Praxis Dynamic Form — Cascata vs Connections
119
+
120
+ ### Concept Usage
121
+
122
+ - [Data Driven Forms](../../../../docs/concepts/data-driven-forms.md)
123
+ - [Declarative UI](../../../../docs/concepts/declarative-ui.md)
124
+ - [Schema‑driven UI](../../../../docs/concepts/schema-driven-ui.md)
125
+
126
+ ## Quando usar cada mecanismo
127
+
128
+ - Connections (dot‑path)
129
+ - Para orquestração “externa” (entre widgets, tabs, tabelas ou fora do formulário).
130
+ - Escrevem em `inputs.config.*` do widget do formulário, inclusive metadados de campos (ex.: `inputs.config.fieldMetadata[i].filterCriteria`).
131
+ - Escala bem para cenários de página e integração cross‑widget.
132
+
133
+ - Cascata nativa (campo→campo, intra‑form)
134
+ - Declarativa via metadata (`dependencyFields`, `resetOnDependentChange`, etc.).
135
+ - Observa `FormControl.valueChanges` dos campos dependentes, aplica `filterCriteria` e recarrega conforme estratégia (`loadOn`, `dependencyLoadOnChange`).
136
+ - Elimina código ad hoc na página.
137
+
138
+ ## Convivência e fonte de verdade
139
+
140
+ - Se Connections atualizam `filterCriteria` de um campo, considere desativar a cascata nativa nesse campo (`enableDependencyCascade: false`) ou usar `dependencyLoadOnChange: 'manual'` (cascata só atualiza filtros e Connections disparam o load).
141
+ - Estratégia de merge recomendada: `dependencyMergeStrategy: 'merge'` para preservar chaves vindas de Connections.
142
+
143
+ ## Links úteis
144
+
145
+ - Fluxo de Schema (ETag/304, schemaId, reconciliação): `docs/schemas/fluxo-schema.md`
146
+ - Guia de implementação e metadados da cascata: `docs/CASCADE-NATIVA.md`
147
+ - Padrões de endpoints (Options vs Filter) para selects: `docs/DEVS-GENERIC-CRUD-SERVICE.md`
148
+
149
+ ## Verificação de Schema (ETag/If-None-Match)
150
+
151
+ - Inputs (opcionais; ativos apenas com `editModeEnabled=true`):
152
+ - `notifyIfOutdated: 'inline' | 'snackbar' | 'both' | 'none' = 'both'`
153
+ - `snoozeMs: number = 86400000`
154
+ - `autoOpenSettingsOnOutdated: boolean = false`
155
+ - Output:
156
+ - `schemaStatusChange: { outdated: boolean; serverHash?: string; lastVerifiedAt?: string; formId?: string }`
157
+ - Persistência (ConfigStorage):
158
+ - `form-schema-meta:{formId}` → `{ serverHash?: string; lastVerifiedAt?: string }`
159
+ - `form-schema-prefs:{formId}` → `{ notifyIfOutdated?, snoozeMs?, autoOpenSettingsOnOutdated? }`
160
+ - Por hash (suppress): `schemaIgnore:{formId}:{hash}`, `schemaSnooze:{formId}:{hash}`, `schemaNotified:{formId}:{hash}`
161
+ - Comportamento:
162
+ - Quando já existe base local (ex.: `config.sections.length > 0`), o componente faz uma verificação leve via `/schemas/filtered` com `If-None-Match`.
163
+ - 304 → apenas atualiza `lastVerifiedAt` e emite `schemaStatusChange(outdated=false)`.
164
+ - 200 → atualiza `serverHash/lastVerifiedAt`, define `schemaOutdated = editModeEnabled && hadBase`, emite `schemaStatusChange`. Não aplica schema automaticamente.
165
+ - Primeira vez (sem base): baixa o corpo do schema para gerar o layout; persiste `form-schema-meta:{formId}`.
166
+ - Notificações respeitam preferências e são one‑shot por hash; o banner/snackbar oferecem ações para Reconciliar, Lembrar depois (snooze) e Ignorar.