@praxisui/dynamic-fields 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,6 @@
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
package/README.md ADDED
@@ -0,0 +1,397 @@
1
+ # @praxis/dynamic-fields — Dynamic Form Fields
2
+
3
+ ### Concept Usage
4
+
5
+ - [Dynamic Component Rendering](../../../../docs/concepts/dynamic-component-rendering.md)
6
+ - [Headless UI & Design Systems](../../../../docs/concepts/headless-ui-and-design-systems.md)
7
+ - [Data Driven Forms](../../../../docs/concepts/data-driven-forms.md)
8
+
9
+ Biblioteca de campos dinâmicos para aplicações Angular (v20+) com Material Design. Renderiza campos a partir de metadados, com carregamento lazy e integração com Reactive Forms.
10
+
11
+ ## Instalação
12
+
13
+ ```bash
14
+ npm install @praxis/dynamic-fields
15
+ ```
16
+
17
+ Peers (instale no app host):
18
+ - `@angular/core` `^20.1.0`, `@angular/common` `^20.1.0`, `@angular/forms` `^20.1.0`
19
+ - `@angular/material` `^20.1.0`, `@angular/cdk` `^20.1.0`, `@angular/router` `^20.1.0`
20
+ - `rxjs` `^7.8.0`
21
+ - `@praxis/core` `^0.0.1`, `@praxis/specification-core` `^0.0.1`
22
+ - Opcional conforme uso: `@praxis/dialog`, `@praxis/cron-builder`
23
+
24
+ ## ✨ Características
25
+
26
+ - **Registro Simplificado**: Sistema de registro de componentes focado no essencial
27
+ - **Lazy Loading**: Carregamento sob demanda com cache inteligente
28
+ - **Material Design**: Componentes baseados no Angular Material
29
+ - **Color Picker**: Novo componente de seleção de cores com suporte a paleta e canvas
30
+ - **Novos Componentes**: Toggle, Slider, Time Picker e Rating
31
+ - **Material Select Modular**: Fragmentado em subcomponentes (SearchInput, OptionsList e Chips)
32
+ - **TypeScript**: Totalmente tipado com integração do `@praxis/core`
33
+ - **Corporativo**: Adequado para cenários empresariais
34
+
35
+ ## 🏗️ Arquitetura
36
+
37
+ ### Sistema de Registro
38
+
39
+ ```typescript
40
+ import { ComponentRegistryService } from "@praxisui/dynamic-fields";
41
+ import { FieldControlType } from "@praxisui/core";
42
+
43
+ // Obter componente usando constantes
44
+ const component = await registry.getComponent(FieldControlType.INPUT);
45
+
46
+ // Verificar registro
47
+ const isRegistered = registry.isRegistered(FieldControlType.INPUT);
48
+ ```
49
+
50
+ ### Componentes Suportados
51
+
52
+ O sistema usa as constantes do `@praxis/core` para garantir consistência:
53
+
54
+ - `FieldControlType.INPUT` - Campo de texto Material Design
55
+ - `FieldControlType.TEXTAREA` - Área de texto Material Design
56
+ - `FieldControlType.SELECT` - Campo de seleção Material Design
57
+ - `FieldControlType.CHECKBOX` - Caixa de seleção Material Design
58
+ - `FieldControlType.RADIO` - Botão de rádio Material Design
59
+ - `FieldControlType.DATE_PICKER` - Seletor de data Material Design
60
+ - `FieldControlType.EMAIL_INPUT` - Campo de email
61
+ - `FieldControlType.PASSWORD` - Campo de senha
62
+ - `FieldControlType.CURRENCY_INPUT` - Campo monetário
63
+ - `FieldControlType.NUMERIC_TEXT_BOX` - Campo numérico
64
+ - `FieldControlType.MULTI_SELECT` - Seleção múltipla
65
+ - `FieldControlType.AUTO_COMPLETE` - Auto completar (busca habilitada por padrão)
66
+ - `FieldControlType.DATE_TIME_PICKER` - Data e hora
67
+ - `FieldControlType.DATE_RANGE` - Intervalo de datas
68
+ - `FieldControlType.FILE_UPLOAD` - Upload de arquivos
69
+ - `FieldControlType.TOGGLE` - Interruptor Material Design
70
+ - `FieldControlType.SLIDER` - Slider Material Design
71
+ - `FieldControlType.TIME_PICKER` - Seletor de horário
72
+ - `FieldControlType.RATING` - Classificação por estrelas
73
+ - `FieldControlType.COLOR_PICKER` - Seletor de cores
74
+
75
+ Campos com `FieldControlType.AUTO_COMPLETE` utilizam internamente o `MaterialSearchableSelectComponent`, habilitando busca automaticamente.
76
+
77
+ ## 🧩 MaterialSelectComponent
78
+
79
+ O `MaterialSelectComponent` agora está dividido em subcomponentes menores para facilitar manutenção e testes:
80
+
81
+ - **SelectSearchInputComponent** - Campo de busca opcional exibido dentro do painel.
82
+ - **SelectOptionsListComponent** - Lista de opções com suporte a grupos e virtualização.
83
+ - **SelectChipsComponent** - Exibe as opções selecionadas como chips quando `multipleDisplay` é `"chips"`.
84
+
85
+ Esses subcomponentes são utilizados internamente pelo select e não exigem alterações na utilização normal do componente.
86
+
87
+ ## 🧩 MaterialCheckboxGroupComponent
88
+
89
+ Renderiza uma lista de checkboxes conectada ao `@angular/forms`, permitindo a seleção de múltiplos valores.
90
+
91
+ - **Select All**: opção para marcar todas as entradas habilitadas.
92
+ - **maxSelections**: limita o número de escolhas possíveis.
93
+ - **Carregamento Remoto**: aceita `resourcePath`, `optionLabelKey` e `optionValueKey` para popular opções via API.
94
+ - **Layout**: suporta `labelPosition` e `color` conforme [documentação do Angular Material](https://material.angular.dev/components/checkbox/overview).
95
+
96
+ ## 🧩 MaterialRadioGroupComponent
97
+
98
+ Grupo de botões de rádio que consome metadados ou dados remotos para criar seleções exclusivas.
99
+
100
+ - **Seleção Única**: utiliza `MatRadioGroup` para garantir apenas um valor ativo.
101
+ - **Carregamento Dinâmico**: mesmas chaves de configuração de `MaterialSelectComponent` para buscar opções.
102
+ - **Layout Flexível**: configuração de orientação, `labelPosition` e `color` segundo a [documentação oficial](https://material.angular.dev/components/radio/overview).
103
+
104
+ ## 📦 Instalação
105
+
106
+ ```bash
107
+ npm install @praxis/dynamic-fields
108
+ ```
109
+
110
+ ## 🚀 Uso Básico (DynamicFieldLoaderDirective)
111
+
112
+ Exemplo mínimo usando a diretiva `dynamicFieldLoader` para criar campos Material a partir de metadados.
113
+
114
+ ```ts
115
+ import { Component, signal, inject } from '@angular/core';
116
+ import { ReactiveFormsModule, NonNullableFormBuilder, Validators } from '@angular/forms';
117
+ import { DynamicFieldLoaderDirective } from '@praxis/dynamic-fields';
118
+ import { FieldMetadata, FieldControlType } from '@praxis/core';
119
+
120
+ @Component({
121
+ selector: 'app-dynamic-form',
122
+ standalone: true,
123
+ imports: [ReactiveFormsModule, DynamicFieldLoaderDirective],
124
+ template: `
125
+ <form [formGroup]="form">
126
+ <ng-container
127
+ dynamicFieldLoader
128
+ [fields]="fields"
129
+ [formGroup]="form"
130
+ (componentsCreated)="onReady($event)">
131
+ </ng-container>
132
+ </form>
133
+ `,
134
+ })
135
+ export class DynamicFormComponent {
136
+ private readonly fb = inject(NonNullableFormBuilder);
137
+
138
+ form = this.fb.group({
139
+ fullName: this.fb.control('', { validators: [Validators.required] }),
140
+ category: this.fb.control(null),
141
+ });
142
+
143
+ fields: FieldMetadata[] = [
144
+ {
145
+ name: 'fullName',
146
+ label: 'Nome',
147
+ controlType: FieldControlType.INPUT,
148
+ placeholder: 'Seu nome completo',
149
+ required: true,
150
+ },
151
+ {
152
+ name: 'category',
153
+ label: 'Categoria',
154
+ controlType: FieldControlType.SELECT,
155
+ options: [
156
+ { label: 'A', value: 'A' },
157
+ { label: 'B', value: 'B' },
158
+ ],
159
+ multiple: false,
160
+ searchable: true,
161
+ },
162
+ ];
163
+
164
+ onReady(map: Map<string, any>) {
165
+ // Recebe referências dos componentes criados (opcional)
166
+ console.log('components', Array.from(map.keys()));
167
+ }
168
+ }
169
+ ```
170
+
171
+ Template-only:
172
+
173
+ ```html
174
+ <ng-container dynamicFieldLoader [fields]="fields" [formGroup]="form"></ng-container>
175
+ ```
176
+
177
+ ```typescript
178
+ import { ComponentRegistryService } from "@praxis/dynamic-fields";
179
+ import { FieldControlType } from "@praxisui/core";
180
+
181
+ @Component({
182
+ selector: "app-dynamic-form",
183
+ template: `<ng-container #dynamicContainer></ng-container>`,
184
+ })
185
+ export class DynamicFormComponent {
186
+ @ViewChild("dynamicContainer", { read: ViewContainerRef })
187
+ container!: ViewContainerRef;
188
+
189
+ constructor(private registry: ComponentRegistryService) {}
190
+
191
+ async loadField(type: FieldControlType) {
192
+ const component = await this.registry.getComponent(type);
193
+ if (component) {
194
+ this.container.createComponent(component);
195
+ }
196
+ }
197
+
198
+ // Exemplo prático
199
+ async loadInputField() {
200
+ await this.loadField(FieldControlType.INPUT);
201
+ }
202
+
203
+ async loadDatePicker() {
204
+ await this.loadField(FieldControlType.DATE_PICKER);
205
+ }
206
+
207
+ async loadColorPicker() {
208
+ await this.loadField(FieldControlType.COLOR_PICKER);
209
+ }
210
+ }
211
+ ```
212
+
213
+ ## 🔧 Registrar Componente Customizado
214
+
215
+ ```typescript
216
+ import { FieldControlType } from "@praxis/core";
217
+
218
+ // Registrar componente customizado - SUPER SIMPLES!
219
+ registry.register("customField" as FieldControlType, () => import("./custom-field.component").then((m) => m.CustomFieldComponent));
220
+
221
+ // Uso posterior
222
+ const customComponent = await registry.getComponent("customField" as FieldControlType);
223
+ ```
224
+
225
+ ## 📊 Estatísticas
226
+
227
+ ```typescript
228
+ const stats = registry.getStats();
229
+ console.log(stats);
230
+ // {
231
+ // registeredComponents: 7,
232
+ // cachedComponents: 3,
233
+ // registeredTypes: ['input', 'select', ...]
234
+ // }
235
+ ```
236
+
237
+ ## 🛠️ API (Pontos de Entrada)
238
+
239
+ Exportados principais:
240
+ - Diretivas: `DynamicFieldLoaderDirective`
241
+ - Serviços: `ComponentRegistryService`
242
+ - Base: `SimpleBaseInputComponent`, `SimpleBaseSelectComponent`, `SimpleBaseButtonComponent`
243
+ - Componentes Material (exemplos): `MaterialSelectComponent`, `MaterialRadioGroupComponent`, `MaterialCheckboxGroupComponent`, `MaterialDatepickerComponent`, `MaterialSliderComponent`, `MaterialTimepickerComponent`, `MaterialTextareaComponent`, `MaterialAutocompleteComponent`, `MaterialChipsComponent`, `MaterialButtonComponent`, `MaterialFileUploadComponent`
244
+
245
+ Referencie `src/public-api.ts` do pacote para a lista completa de símbolos exportados.
246
+
247
+ ### ComponentRegistryService
248
+
249
+ #### Métodos Principais
250
+
251
+ - `register<T>(type, factory)` - Registra componente (ultra-simples!)
252
+ - `getComponent<T>(type)` - Obtém componente (async)
253
+ - `isRegistered(type)` - Verifica se está registrado
254
+ - `getRegisteredTypes()` - Lista tipos registrados
255
+
256
+ #### Métodos Utilitários
257
+
258
+ - `getStats()` - Estatísticas do registro
259
+ - `clearCache(type?)` - Limpa cache
260
+ - `unregister(type)` - Remove componente
261
+ - `preload(types[])` - Pré-carrega componentes
262
+
263
+ ### Interfaces
264
+
265
+ ```typescript
266
+ interface RegistryStats {
267
+ registeredComponents: number;
268
+ cachedComponents: number;
269
+ registeredTypes: FieldControlType[];
270
+ }
271
+
272
+ interface ComponentRegistration {
273
+ factory: () => Promise<Type<any>>;
274
+ cached?: Type<any>;
275
+ }
276
+ ```
277
+
278
+ ## 🎯 Integração com @praxisui/core
279
+
280
+ Esta biblioteca usa os tipos e metadados do `@praxisui/core`:
281
+
282
+ - `FieldControlType` - Tipos de controle unificados
283
+ - `UnifiedFieldMetadata` - Sistema de metadados corporativo
284
+ - `MaterialInputMetadata`, `MaterialSelectMetadata`, etc. - Metadados específicos
285
+
286
+ ## 📋 Desenvolvimento
287
+
288
+ ```bash
289
+ # Build da biblioteca
290
+ ng build praxis-dynamic-fields
291
+
292
+ # Testes
293
+ ng test praxis-dynamic-fields
294
+
295
+ # Lint
296
+ ng lint praxis-dynamic-fields
297
+ ```
298
+
299
+ ## 🏷️ Versão
300
+
301
+ **Versão atual**: Sistema simplificado pós-refatoração
302
+ **Dependências**: `@praxisui/core`, `@angular/material`
303
+ **Angular**: 20+
304
+ **TypeScript**: 5.8+
305
+
306
+ ---
307
+
308
+ _Sistema desenvolvido seguindo diretrizes de simplicidade e foco no essencial._
309
+
310
+ ## 🔌 Extensão pelo App Host (registrando seu próprio componente)
311
+
312
+ Aplicações host podem registrar componentes próprios para uso no DynamicFieldLoader e expor metadados para título/ícone amigáveis no editor.
313
+
314
+ ### 1) Registrar o componente no runtime (DynamicFieldLoader)
315
+
316
+ Use `ENVIRONMENT_INITIALIZER` para registrar o componente no `ComponentRegistryService` durante o bootstrap do app:
317
+
318
+ ```ts
319
+ import { ApplicationConfig, ENVIRONMENT_INITIALIZER } from '@angular/core';
320
+ import { ComponentRegistryService } from '@praxisui/dynamic-fields';
321
+ import { FieldControlType } from '@praxisui/core';
322
+
323
+ export const appConfig: ApplicationConfig = {
324
+ providers: [
325
+ {
326
+ provide: ENVIRONMENT_INITIALIZER,
327
+ multi: true,
328
+ useFactory: (registry: ComponentRegistryService) => () => {
329
+ // "my-custom" será usado como controlType no metadata do formulário
330
+ registry.register('my-custom' as FieldControlType, () =>
331
+ import('./components/my-custom.component').then((m) => m.MyCustomComponent),
332
+ );
333
+ },
334
+ deps: [ComponentRegistryService],
335
+ },
336
+ ],
337
+ };
338
+ ```
339
+
340
+ Observações de integração com o DynamicFieldLoader:
341
+ - Metadados: implemente `setInputMetadata(metadata)` ou exponha um signal `metadata` (WritableSignal) e use `.set(metadata)`.
342
+ - FormControl: exponha um signal `formControl` com `.set(control)` ou implemente `setExternalControl(control)`; alternativamente implemente corretamente `ControlValueAccessor` e faça `[formControl]` no template para evitar NG01203.
343
+ - Flags globais (opcional): suporte `[readonlyMode]`, `[disabledMode]`, `[visible]`, `[presentationMode]` para integração com o shell/canvas.
344
+
345
+ ### 2) Registrar metadados para título/ícone amigáveis (ComponentMetadataRegistry)
346
+
347
+ Registre `ComponentDocMeta` para que o editor de campos use nome/ícone do seu componente:
348
+
349
+ ```ts
350
+ import { ApplicationConfig, ENVIRONMENT_INITIALIZER } from '@angular/core';
351
+ import { ComponentDocMeta, ComponentMetadataRegistry } from '@praxisui/core';
352
+ import { MyCustomComponent } from './components/my-custom.component';
353
+
354
+ const MY_CUSTOM_METADATA: ComponentDocMeta = {
355
+ id: 'my-custom',
356
+ selector: 'my-custom',
357
+ component: MyCustomComponent,
358
+ friendlyName: 'Meu Componente',
359
+ description: 'Componente custom do app host',
360
+ icon: 'bolt',
361
+ lib: 'app-host',
362
+ };
363
+
364
+ export const appConfig: ApplicationConfig = {
365
+ providers: [
366
+ {
367
+ provide: ENVIRONMENT_INITIALIZER,
368
+ multi: true,
369
+ useFactory: (registry: ComponentMetadataRegistry) => () => {
370
+ registry.register(MY_CUSTOM_METADATA);
371
+ },
372
+ deps: [ComponentMetadataRegistry],
373
+ },
374
+ ],
375
+ };
376
+ ```
377
+
378
+ Por que isso importa?
379
+ - O editor “Configurar campo” compõe o título como `"<Friendly Type> — <Label>"` e resolve o ícone via `ComponentMetadataRegistry`. Se `controlType` do seu campo for igual ao `id` do metadata (ex.: `'my-custom'`), o Dynamic Form usará diretamente os valores do registry.
380
+
381
+ ### 3) Usar no formulário dinâmico
382
+
383
+ Defina o `controlType` do campo como o id registrado:
384
+
385
+ ```ts
386
+ const field: FieldMetadata = {
387
+ name: 'customField',
388
+ label: 'Meu Campo',
389
+ controlType: 'my-custom', // corresponde ao id do metadata e ao registro do runtime
390
+ };
391
+ ```
392
+
393
+ ### ✅ Checklist de Compatibilidade
394
+ - Implementa `ControlValueAccessor` ou liga `[formControl]` a um controle nativo (evita NG01203).
395
+ - Suporta `setInputMetadata` ou signal `metadata` com `.set(...)` para hot updates.
396
+ - Opcional: aceita `[readonlyMode]`, `[disabledMode]`, `[visible]`, `[presentationMode]`.
397
+ - O `controlType` do campo coincide com o `id` do `ComponentDocMeta` (para resolver título/ícone no editor).
@@ -0,0 +1,2 @@
1
+ export { MaterialRatingComponent, PDX_MATERIAL_RATING_COMPONENT_METADATA } from './praxisui-dynamic-fields.mjs';
2
+ //# sourceMappingURL=praxisui-dynamic-fields-index-C9IUU4lo.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"praxisui-dynamic-fields-index-C9IUU4lo.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export { PDX_COLOR_PICKER_COMPONENT_METADATA, PdxColorPickerComponent } from './praxisui-dynamic-fields.mjs';
2
+ //# sourceMappingURL=praxisui-dynamic-fields-index-GJtthzkD.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"praxisui-dynamic-fields-index-GJtthzkD.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}