@vipsolucoes/dynamic-form 1.0.7 → 1.0.9

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 CHANGED
@@ -137,6 +137,8 @@ A biblioteca suporta os seguintes tipos de campos:
137
137
 
138
138
  ### Campo Select
139
139
 
140
+ #### Formato Padrão (Dados Fixos)
141
+
140
142
  ```typescript
141
143
  {
142
144
  key: 'pais',
@@ -152,6 +154,135 @@ A biblioteca suporta os seguintes tipos de campos:
152
154
  }
153
155
  ```
154
156
 
157
+ #### Com Dados de API (Sem Mapeamento Manual)
158
+
159
+ ```typescript
160
+ // Service retorna dados brutos: [{ id: 1, nome: 'São Paulo', uf: 'SP' }, ...]
161
+
162
+ {
163
+ key: 'cidade',
164
+ controlType: 'select',
165
+ label: 'Cidade',
166
+ options: [],
167
+ optionLabel: 'nome', // Campo do objeto para label
168
+ optionValue: 'id', // Campo do objeto para value
169
+ optionFilter: true, // Habilita filtro de busca
170
+ optionShowClear: true, // Mostra botão limpar
171
+ validators: [Validators.required]
172
+ }
173
+
174
+ // No componente, passe os dados diretamente:
175
+ this.service.loadCidades().subscribe(cidades => {
176
+ this.atualizarCampo('cidade', { options: cidades });
177
+ });
178
+ ```
179
+
180
+ #### Com optionMapper (Labels Compostos)
181
+
182
+ ```typescript
183
+ {
184
+ key: 'cidade',
185
+ controlType: 'select',
186
+ label: 'Cidade',
187
+ options: [],
188
+ optionMapper: (cidade) => ({
189
+ label: `${cidade.nome} - ${cidade.uf}`, // Label composto
190
+ value: cidade.id
191
+ }),
192
+ optionFilter: true,
193
+ validators: [Validators.required]
194
+ }
195
+ ```
196
+
197
+ **Propriedades Disponíveis:**
198
+
199
+ - `optionLabel?`: Nome do campo para label (default: `'label'`)
200
+ - `optionValue?`: Nome do campo para value (default: `'value'`)
201
+ - `optionMapper?`: Função para mapear itens (tem prioridade sobre optionLabel/optionValue)
202
+ - `optionFilter?`: Habilita filtro de busca
203
+ - `optionShowClear?`: Mostra botão para limpar seleção
204
+
205
+ ### Campo Number
206
+
207
+ O campo `number` suporta números inteiros, decimais e monetários com muitas opções de formatação:
208
+
209
+ #### Números Inteiros (Padrão)
210
+
211
+ ```typescript
212
+ {
213
+ key: 'quantidade',
214
+ controlType: 'number',
215
+ label: 'Quantidade',
216
+ validators: [Validators.required, Validators.min(1)]
217
+ }
218
+ ```
219
+
220
+ #### Campo Monetário (Currency)
221
+
222
+ ```typescript
223
+ {
224
+ key: 'preco',
225
+ controlType: 'number',
226
+ label: 'Preço',
227
+ numberConfig: {
228
+ mode: 'currency',
229
+ currency: 'BRL',
230
+ locale: 'pt-BR',
231
+ minFractionDigits: 2,
232
+ maxFractionDigits: 2
233
+ },
234
+ validators: [Validators.required, Validators.min(0)]
235
+ }
236
+ ```
237
+
238
+ #### Campo com Prefixo/Sufixo
239
+
240
+ ```typescript
241
+ {
242
+ key: 'desconto',
243
+ controlType: 'number',
244
+ label: 'Desconto',
245
+ numberConfig: {
246
+ prefix: '%',
247
+ min: 0,
248
+ max: 100,
249
+ showButtons: true
250
+ },
251
+ validators: [Validators.required, Validators.min(0), Validators.max(100)]
252
+ }
253
+ ```
254
+
255
+ #### Campo Decimal com Unidade
256
+
257
+ ```typescript
258
+ {
259
+ key: 'peso',
260
+ controlType: 'number',
261
+ label: 'Peso',
262
+ numberConfig: {
263
+ mode: 'decimal',
264
+ suffix: ' kg',
265
+ minFractionDigits: 2,
266
+ maxFractionDigits: 3,
267
+ showClear: true
268
+ },
269
+ validators: [Validators.required, Validators.min(0)]
270
+ }
271
+ ```
272
+
273
+ **Propriedades Disponíveis em `numberConfig`:**
274
+
275
+ - `mode`: `'decimal'` | `'currency'`
276
+ - `currency`: Código da moeda (ISO 4217) - obrigatório para currency
277
+ - `prefix`: Texto antes do valor (ex: `'R$'`, `'%'`)
278
+ - `suffix`: Texto após o valor (ex: `' kg'`, `' m²'`)
279
+ - `min` / `max`: Valores mínimo e máximo
280
+ - `minFractionDigits` / `maxFractionDigits`: Casas decimais
281
+ - `showButtons`: Exibir botões de incremento/decremento
282
+ - `showClear`: Exibir botão para limpar valor
283
+ - `locale`: Localização para formatação (ex: `'pt-BR'`, `'en-US'`)
284
+ - E muitas outras opções...
285
+
155
286
  ### Campo DatePicker
156
287
 
157
288
  ```typescript
@@ -337,7 +468,12 @@ interface iFormConfig {
337
468
  disabled?: boolean; // Campo desabilitado
338
469
  enabledWhen?: string; // Chave do toggle que controla este campo
339
470
  styleClass?: string; // Classes CSS customizadas
340
- options?: iFieldOption[]; // Opções para select (obrigatório se controlType for 'select')
471
+ options?: iFieldOption[] | unknown[]; // Opções para select (obrigatório se controlType for 'select')
472
+ optionLabel?: string; // Campo do objeto para label no select (default: 'label')
473
+ optionValue?: string; // Campo do objeto para value no select (default: 'value')
474
+ optionMapper?: (item: any) => iFieldOption; // Função para mapear itens (tem prioridade sobre optionLabel/optionValue)
475
+ optionFilter?: boolean; // Habilita filtro de busca no select
476
+ optionShowClear?: boolean; // Mostra botão para limpar seleção no select
341
477
  validators?: ValidatorFn[]; // Validadores Angular
342
478
  dateFormat?: string; // Formato da data (default: 'dd/mm/yy')
343
479
  dateViewType?: 'date' | 'month' | 'year'; // Tipo de visualização da data (default: 'date')
@@ -346,14 +482,44 @@ interface iFormConfig {
346
482
  textareaCols?: number; // Número de colunas do textarea
347
483
  toggleTrueValue?: any; // Valor quando toggle está ativo (default: true)
348
484
  toggleFalseValue?: any; // Valor quando toggle está inativo (default: false)
349
- buttonConfig?: { // Configuração do botão para campos 'input-button'
485
+ buttonConfig?: {
486
+ // Configuração do botão para campos 'input-button'
350
487
  icon?: string; // Ícone do PrimeIcons
351
488
  label?: string; // Texto do botão
352
489
  tooltip?: string; // Tooltip do botão
353
490
  position?: 'left' | 'right'; // Posição do botão (default: 'right')
354
- severity?: 'primary' | 'secondary' | 'success' | 'info' | 'warning' | 'danger' | 'help' | 'contrast'; // Estilo do botão
491
+ severity?:
492
+ | 'primary'
493
+ | 'secondary'
494
+ | 'success'
495
+ | 'info'
496
+ | 'warning'
497
+ | 'danger'
498
+ | 'help'
499
+ | 'contrast'; // Estilo do botão
355
500
  };
356
501
  buttonCallback?: (fieldKey: string, fieldValue: any) => void | Promise<void>; // Callback executado ao clicar no botão
502
+ numberConfig?: {
503
+ // Configuração específica para campos 'number'
504
+ mode?: 'decimal' | 'currency';
505
+ currency?: string; // Código da moeda (ISO 4217)
506
+ currencyDisplay?: 'symbol' | 'code' | 'name';
507
+ minFractionDigits?: number;
508
+ maxFractionDigits?: number;
509
+ useGrouping?: boolean;
510
+ prefix?: string;
511
+ suffix?: string;
512
+ min?: number;
513
+ max?: number;
514
+ step?: number;
515
+ showButtons?: boolean;
516
+ buttonLayout?: 'stacked' | 'horizontal' | 'vertical';
517
+ showClear?: boolean;
518
+ locale?: string;
519
+ readonly?: boolean;
520
+ size?: 'small' | 'large';
521
+ variant?: 'outlined' | 'filled';
522
+ };
357
523
  }
358
524
  ```
359
525
 
@@ -47,6 +47,8 @@ class DynamicFormErrorComponent {
47
47
  email: () => this.customMessages?.email ?? 'Por favor, insira um e-mail válido.',
48
48
  minlength: (err) => this.customMessages?.minlength?.(err.requiredLength) ?? `Mínimo de ${err.requiredLength} caracteres.`,
49
49
  maxlength: (err) => this.customMessages?.maxlength?.(err.requiredLength) ?? `Máximo de ${err.requiredLength} caracteres.`,
50
+ min: (err) => this.customMessages?.min?.(err.min) ?? `O valor mínimo é ${err.min}.`,
51
+ max: (err) => this.customMessages?.max?.(err.max) ?? `O valor máximo é ${err.max}.`,
50
52
  custom: (err) => (this.customMessages?.custom ? this.customMessages.custom(err) : err),
51
53
  };
52
54
  // Getter simples - Angular detecta mudanças automaticamente
@@ -459,22 +461,163 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.7", ngImpor
459
461
  class NumberInputFieldComponent {
460
462
  form = input.required(...(ngDevMode ? [{ debugName: "form" }] : []));
461
463
  field = input.required(...(ngDevMode ? [{ debugName: "field" }] : []));
464
+ /**
465
+ * Retorna a configuração numberConfig do campo, se existir.
466
+ */
467
+ numberConfig = computed(() => this.field().numberConfig, ...(ngDevMode ? [{ debugName: "numberConfig" }] : []));
468
+ /**
469
+ * Determina o modo efetivo do InputNumber.
470
+ * Se não houver numberConfig, retorna undefined (comportamento padrão para inteiros).
471
+ * Se houver numberConfig mas mode não estiver definido, retorna 'decimal'.
472
+ */
473
+ effectiveMode = computed(() => {
474
+ const config = this.numberConfig();
475
+ if (!config) {
476
+ return undefined; // Comportamento padrão (inteiros)
477
+ }
478
+ return config.mode ?? 'decimal';
479
+ }, ...(ngDevMode ? [{ debugName: "effectiveMode" }] : []));
480
+ /**
481
+ * Determina a localização efetiva.
482
+ * Se não especificada no numberConfig, retorna undefined (usa localização do navegador).
483
+ */
484
+ effectiveLocale = computed(() => {
485
+ return this.numberConfig()?.locale;
486
+ }, ...(ngDevMode ? [{ debugName: "effectiveLocale" }] : []));
487
+ /**
488
+ * Determina o número mínimo de casas decimais.
489
+ * Para currency mode, default é 2. Para decimal mode, default é 0.
490
+ * Se não houver numberConfig, retorna undefined (comportamento padrão para inteiros).
491
+ */
492
+ effectiveMinFractionDigits = computed(() => {
493
+ const config = this.numberConfig();
494
+ if (!config) {
495
+ return undefined; // Comportamento padrão (inteiros)
496
+ }
497
+ if (config.minFractionDigits !== undefined) {
498
+ return config.minFractionDigits;
499
+ }
500
+ // Default baseado no mode
501
+ return config.mode === 'currency' ? 2 : 0;
502
+ }, ...(ngDevMode ? [{ debugName: "effectiveMinFractionDigits" }] : []));
503
+ /**
504
+ * Determina o número máximo de casas decimais.
505
+ * Para currency mode, default é 2. Para decimal mode, default é 3.
506
+ * Se não houver numberConfig, retorna undefined (comportamento padrão para inteiros).
507
+ */
508
+ effectiveMaxFractionDigits = computed(() => {
509
+ const config = this.numberConfig();
510
+ if (!config) {
511
+ return undefined; // Comportamento padrão (inteiros)
512
+ }
513
+ if (config.maxFractionDigits !== undefined) {
514
+ return config.maxFractionDigits;
515
+ }
516
+ // Default baseado no mode
517
+ return config.mode === 'currency' ? 2 : 3;
518
+ }, ...(ngDevMode ? [{ debugName: "effectiveMaxFractionDigits" }] : []));
519
+ /**
520
+ * Verifica se deve exibir botões de incremento/decremento.
521
+ */
522
+ hasButtons = computed(() => {
523
+ return this.numberConfig()?.showButtons ?? false;
524
+ }, ...(ngDevMode ? [{ debugName: "hasButtons" }] : []));
525
+ /**
526
+ * Verifica se deve exibir botão para limpar valor.
527
+ */
528
+ hasClearButton = computed(() => {
529
+ return this.numberConfig()?.showClear ?? false;
530
+ }, ...(ngDevMode ? [{ debugName: "hasClearButton" }] : []));
531
+ /**
532
+ * Verifica se o campo está em estado inválido.
533
+ */
534
+ isInvalid = computed(() => {
535
+ const control = this.form().get(this.field().key);
536
+ if (!control) {
537
+ return false;
538
+ }
539
+ return control.invalid && (control.touched || control.dirty);
540
+ }, ...(ngDevMode ? [{ debugName: "isInvalid" }] : []));
541
+ /**
542
+ * Verifica se o campo tem validação required.
543
+ */
544
+ hasRequiredValidator = computed(() => {
545
+ const control = this.form().get(this.field().key);
546
+ if (!control) {
547
+ return false;
548
+ }
549
+ return control.hasError('required');
550
+ }, ...(ngDevMode ? [{ debugName: "hasRequiredValidator" }] : []));
462
551
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.7", ngImport: i0, type: NumberInputFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
463
552
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.7", type: NumberInputFieldComponent, isStandalone: true, selector: "vp-number-input-field", inputs: { form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: true, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
464
553
  <div [formGroup]="form()">
465
554
  <p-iftalabel>
466
- <p-inputNumber [id]="field().key" [formControlName]="field().key" [placeholder]="field().placeholder || ''" styleClass="w-full" class="w-full" />
555
+ <p-inputNumber
556
+ [inputId]="field().key"
557
+ [formControlName]="field().key"
558
+ [placeholder]="field().placeholder || ''"
559
+ [mode]="effectiveMode() ?? undefined"
560
+ [currency]="numberConfig()?.currency ?? undefined"
561
+ [currencyDisplay]="numberConfig()?.currencyDisplay ?? undefined"
562
+ [minFractionDigits]="effectiveMinFractionDigits() ?? undefined"
563
+ [maxFractionDigits]="effectiveMaxFractionDigits() ?? undefined"
564
+ [useGrouping]="numberConfig()?.useGrouping ?? true"
565
+ [prefix]="numberConfig()?.prefix ?? undefined"
566
+ [suffix]="numberConfig()?.suffix ?? undefined"
567
+ [min]="numberConfig()?.min ?? undefined"
568
+ [max]="numberConfig()?.max ?? undefined"
569
+ [step]="numberConfig()?.step ?? undefined"
570
+ [showButtons]="hasButtons()"
571
+ [buttonLayout]="numberConfig()?.buttonLayout ?? 'stacked'"
572
+ [showClear]="hasClearButton()"
573
+ [locale]="effectiveLocale() ?? undefined"
574
+ [readonly]="numberConfig()?.readonly ?? false"
575
+ [size]="numberConfig()?.size ?? undefined"
576
+ [variant]="numberConfig()?.variant ?? 'outlined'"
577
+ [invalid]="isInvalid()"
578
+ [disabled]="field().disabled ?? false"
579
+ [required]="hasRequiredValidator()"
580
+ styleClass="w-full"
581
+ class="w-full"
582
+ />
467
583
  <label [for]="field().key">{{ field().label }}</label>
468
584
  </p-iftalabel>
469
585
  </div>
470
- `, isInline: true, styles: [":host{display:block;width:100%}p-iftalabel{display:block;width:100%}:host ::ng-deep p-inputNumber,:host ::ng-deep .p-inputnumber,:host ::ng-deep .p-inputnumber input{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: IftaLabelModule }, { kind: "component", type: i2.IftaLabel, selector: "p-iftalabel, p-iftaLabel, p-ifta-label" }, { kind: "ngmodule", type: InputNumberModule }, { kind: "component", type: i3$2.InputNumber, selector: "p-inputNumber, p-inputnumber, p-input-number", inputs: ["showButtons", "format", "buttonLayout", "inputId", "styleClass", "placeholder", "tabindex", "title", "ariaLabelledBy", "ariaDescribedBy", "ariaLabel", "ariaRequired", "autocomplete", "incrementButtonClass", "decrementButtonClass", "incrementButtonIcon", "decrementButtonIcon", "readonly", "allowEmpty", "locale", "localeMatcher", "mode", "currency", "currencyDisplay", "useGrouping", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "inputStyle", "inputStyleClass", "showClear", "autofocus"], outputs: ["onInput", "onFocus", "onBlur", "onKeyDown", "onClear"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
586
+ `, isInline: true, styles: [":host{display:block;width:100%}p-iftalabel{display:block;width:100%}:host ::ng-deep p-inputNumber,:host ::ng-deep .p-inputnumber,:host ::ng-deep .p-inputnumber input{width:100%}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: IftaLabelModule }, { kind: "component", type: i2.IftaLabel, selector: "p-iftalabel, p-iftaLabel, p-ifta-label" }, { kind: "ngmodule", type: InputNumberModule }, { kind: "component", type: i3$2.InputNumber, selector: "p-inputNumber, p-inputnumber, p-input-number", inputs: ["showButtons", "format", "buttonLayout", "inputId", "styleClass", "placeholder", "tabindex", "title", "ariaLabelledBy", "ariaDescribedBy", "ariaLabel", "ariaRequired", "autocomplete", "incrementButtonClass", "decrementButtonClass", "incrementButtonIcon", "decrementButtonIcon", "readonly", "allowEmpty", "locale", "localeMatcher", "mode", "currency", "currencyDisplay", "useGrouping", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "inputStyle", "inputStyleClass", "showClear", "autofocus"], outputs: ["onInput", "onFocus", "onBlur", "onKeyDown", "onClear"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
471
587
  }
472
588
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.7", ngImport: i0, type: NumberInputFieldComponent, decorators: [{
473
589
  type: Component,
474
590
  args: [{ selector: 'vp-number-input-field', standalone: true, imports: [ReactiveFormsModule, IftaLabelModule, InputNumberModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
475
591
  <div [formGroup]="form()">
476
592
  <p-iftalabel>
477
- <p-inputNumber [id]="field().key" [formControlName]="field().key" [placeholder]="field().placeholder || ''" styleClass="w-full" class="w-full" />
593
+ <p-inputNumber
594
+ [inputId]="field().key"
595
+ [formControlName]="field().key"
596
+ [placeholder]="field().placeholder || ''"
597
+ [mode]="effectiveMode() ?? undefined"
598
+ [currency]="numberConfig()?.currency ?? undefined"
599
+ [currencyDisplay]="numberConfig()?.currencyDisplay ?? undefined"
600
+ [minFractionDigits]="effectiveMinFractionDigits() ?? undefined"
601
+ [maxFractionDigits]="effectiveMaxFractionDigits() ?? undefined"
602
+ [useGrouping]="numberConfig()?.useGrouping ?? true"
603
+ [prefix]="numberConfig()?.prefix ?? undefined"
604
+ [suffix]="numberConfig()?.suffix ?? undefined"
605
+ [min]="numberConfig()?.min ?? undefined"
606
+ [max]="numberConfig()?.max ?? undefined"
607
+ [step]="numberConfig()?.step ?? undefined"
608
+ [showButtons]="hasButtons()"
609
+ [buttonLayout]="numberConfig()?.buttonLayout ?? 'stacked'"
610
+ [showClear]="hasClearButton()"
611
+ [locale]="effectiveLocale() ?? undefined"
612
+ [readonly]="numberConfig()?.readonly ?? false"
613
+ [size]="numberConfig()?.size ?? undefined"
614
+ [variant]="numberConfig()?.variant ?? 'outlined'"
615
+ [invalid]="isInvalid()"
616
+ [disabled]="field().disabled ?? false"
617
+ [required]="hasRequiredValidator()"
618
+ styleClass="w-full"
619
+ class="w-full"
620
+ />
478
621
  <label [for]="field().key">{{ field().label }}</label>
479
622
  </p-iftalabel>
480
623
  </div>
@@ -523,6 +666,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.7", ngImpor
523
666
  class RadioButtonFieldComponent {
524
667
  form = input.required(...(ngDevMode ? [{ debugName: "form" }] : []));
525
668
  field = input.required(...(ngDevMode ? [{ debugName: "field" }] : []));
669
+ options = computed(() => {
670
+ return this.field().options;
671
+ }, ...(ngDevMode ? [{ debugName: "options" }] : []));
526
672
  layout = computed(() => {
527
673
  return this.field().radioLayout ?? 'vertical';
528
674
  }, ...(ngDevMode ? [{ debugName: "layout" }] : []));
@@ -544,7 +690,7 @@ class RadioButtonFieldComponent {
544
690
  [class.radiobutton-options-horizontal]="layout() === 'horizontal'"
545
691
  [class.radiobutton-options-vertical]="layout() === 'vertical'"
546
692
  >
547
- @for (option of field().options; track option.value) {
693
+ @for (option of options(); track option.value) {
548
694
  <div class="radiobutton-option">
549
695
  <p-radiobutton
550
696
  [formControlName]="field().key"
@@ -577,7 +723,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.7", ngImpor
577
723
  [class.radiobutton-options-horizontal]="layout() === 'horizontal'"
578
724
  [class.radiobutton-options-vertical]="layout() === 'vertical'"
579
725
  >
580
- @for (option of field().options; track option.value) {
726
+ @for (option of options(); track option.value) {
581
727
  <div class="radiobutton-option">
582
728
  <p-radiobutton
583
729
  [formControlName]="field().key"
@@ -602,6 +748,40 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.7", ngImpor
602
748
  class SelectFieldComponent {
603
749
  form = input.required(...(ngDevMode ? [{ debugName: "form" }] : []));
604
750
  field = input.required(...(ngDevMode ? [{ debugName: "field" }] : []));
751
+ /**
752
+ * Aplica optionMapper se existir, caso contrário retorna as opções originais.
753
+ * Prioridade: optionMapper > optionLabel/optionValue > label/value padrão
754
+ */
755
+ mappedOptions = computed(() => {
756
+ const options = this.field().options ?? [];
757
+ const mapper = this.field().optionMapper;
758
+ if (mapper) {
759
+ return options.map(mapper);
760
+ }
761
+ return options;
762
+ }, ...(ngDevMode ? [{ debugName: "mappedOptions" }] : []));
763
+ /**
764
+ * Determina o optionLabel efetivo a ser usado.
765
+ * Se optionMapper estiver definido, usa 'label' (pois mapper já transformou).
766
+ * Caso contrário, usa optionLabel configurado ou 'label' como padrão.
767
+ */
768
+ effectiveOptionLabel = computed(() => {
769
+ if (this.field().optionMapper) {
770
+ return 'label';
771
+ }
772
+ return this.field().optionLabel ?? 'label';
773
+ }, ...(ngDevMode ? [{ debugName: "effectiveOptionLabel" }] : []));
774
+ /**
775
+ * Determina o optionValue efetivo a ser usado.
776
+ * Se optionMapper estiver definido, usa 'value' (pois mapper já transformou).
777
+ * Caso contrário, usa optionValue configurado ou 'value' como padrão.
778
+ */
779
+ effectiveOptionValue = computed(() => {
780
+ if (this.field().optionMapper) {
781
+ return 'value';
782
+ }
783
+ return this.field().optionValue ?? 'value';
784
+ }, ...(ngDevMode ? [{ debugName: "effectiveOptionValue" }] : []));
605
785
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.7", ngImport: i0, type: SelectFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
606
786
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.7", type: SelectFieldComponent, isStandalone: true, selector: "vp-select-field", inputs: { form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: true, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
607
787
  <div [formGroup]="form()">
@@ -609,11 +789,14 @@ class SelectFieldComponent {
609
789
  <p-select
610
790
  [inputId]="field().key"
611
791
  [formControlName]="field().key"
612
- [options]="field().options!"
792
+ [options]="mappedOptions()"
613
793
  [placeholder]="field().placeholder || 'Selecione'"
614
- optionLabel="label"
615
- optionValue="value"
616
- class="w-full" />
794
+ [optionLabel]="effectiveOptionLabel()"
795
+ [optionValue]="effectiveOptionValue()"
796
+ [filter]="field().optionFilter ?? false"
797
+ [showClear]="field().optionShowClear ?? false"
798
+ class="w-full"
799
+ />
617
800
  <label [for]="field().key">{{ field().label }}</label>
618
801
  </p-iftalabel>
619
802
  </div>
@@ -627,11 +810,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.7", ngImpor
627
810
  <p-select
628
811
  [inputId]="field().key"
629
812
  [formControlName]="field().key"
630
- [options]="field().options!"
813
+ [options]="mappedOptions()"
631
814
  [placeholder]="field().placeholder || 'Selecione'"
632
- optionLabel="label"
633
- optionValue="value"
634
- class="w-full" />
815
+ [optionLabel]="effectiveOptionLabel()"
816
+ [optionValue]="effectiveOptionValue()"
817
+ [filter]="field().optionFilter ?? false"
818
+ [showClear]="field().optionShowClear ?? false"
819
+ class="w-full"
820
+ />
635
821
  <label [for]="field().key">{{ field().label }}</label>
636
822
  </p-iftalabel>
637
823
  </div>