@praxisui/dynamic-form 2.0.0-beta.0 → 3.0.0-beta.0

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.
@@ -6478,7 +6478,8 @@ class PraxisDynamicForm {
6478
6478
  const mode = header.mode ?? 'icon';
6479
6479
  const formData = this.form?.getRawValue?.() ?? {};
6480
6480
  if (mode === 'icon') {
6481
- return legacyIcon ? { kind: 'icon', icon: legacyIcon } : { kind: 'none' };
6481
+ const icon = legacyIcon || header.fallbackIcon;
6482
+ return icon ? { kind: 'icon', icon } : { kind: 'none' };
6482
6483
  }
6483
6484
  if (mode === 'avatar-image' || mode === 'auto') {
6484
6485
  const imageSrc = this.resolveSectionHeaderImageSrc(this.getSectionHeaderFieldValue(formData, header.sourceField));
@@ -6550,7 +6551,6 @@ class PraxisDynamicForm {
6550
6551
  if (emptyState === 'placeholder-avatar') {
6551
6552
  return {
6552
6553
  kind: 'placeholder',
6553
- icon: fallbackIcon,
6554
6554
  ariaLabel,
6555
6555
  };
6556
6556
  }
@@ -12692,30 +12692,41 @@ class SectionConfiguratorComponent {
12692
12692
  const previewLabel = this.resolveTextPreviewLabel(header);
12693
12693
  const imageLabel = this.resolveFieldLabel(header?.sourceField);
12694
12694
  if (mode === 'icon') {
12695
- return { kind: 'icon', icon: this.section.icon || 'label_important' };
12695
+ return { kind: 'icon', icon: this.section.icon || fallbackIcon || 'label_important' };
12696
12696
  }
12697
12697
  if (mode === 'avatar-image') {
12698
- return {
12699
- kind: 'placeholder',
12700
- icon: fallbackIcon || 'image',
12701
- tooltip: imageLabel,
12702
- };
12698
+ return this.resolveEmptyHeaderPreview(imageLabel, fallbackIcon, header?.emptyState, imageLabel ? 'image' : undefined);
12703
12699
  }
12704
12700
  if (mode === 'avatar-initials') {
12705
12701
  const initials = deriveSectionHeaderInitials(previewLabel);
12706
12702
  if (initials) {
12707
12703
  return { kind: 'initials', text: initials, tooltip: previewLabel };
12708
12704
  }
12709
- return { kind: 'placeholder', icon: fallbackIcon, tooltip: previewLabel };
12705
+ return this.resolveEmptyHeaderPreview(previewLabel, fallbackIcon, header?.emptyState);
12710
12706
  }
12711
12707
  if (mode === 'auto') {
12708
+ const initials = deriveSectionHeaderInitials(previewLabel);
12709
+ if (initials) {
12710
+ return { kind: 'initials', text: initials, tooltip: previewLabel };
12711
+ }
12712
+ return this.resolveEmptyHeaderPreview(imageLabel || previewLabel, fallbackIcon, header?.emptyState, imageLabel ? 'image' : undefined);
12713
+ }
12714
+ return this.resolveEmptyHeaderPreview(previewLabel, fallbackIcon, header?.emptyState);
12715
+ }
12716
+ resolveEmptyHeaderPreview(tooltip, fallbackIcon, emptyState, preferredPlaceholderIcon) {
12717
+ if (emptyState === 'none') {
12718
+ return { kind: 'none' };
12719
+ }
12720
+ if (emptyState === 'fallback-icon' && fallbackIcon) {
12712
12721
  return {
12713
12722
  kind: 'placeholder',
12714
- icon: fallbackIcon || (imageLabel ? 'image' : undefined),
12715
- tooltip: imageLabel || previewLabel,
12723
+ icon: fallbackIcon,
12724
+ tooltip,
12716
12725
  };
12717
12726
  }
12718
- return { kind: 'placeholder', icon: fallbackIcon, tooltip: previewLabel };
12727
+ return preferredPlaceholderIcon
12728
+ ? { kind: 'placeholder', icon: preferredPlaceholderIcon, tooltip }
12729
+ : { kind: 'placeholder', tooltip };
12719
12730
  }
12720
12731
  resolveFieldLabel(fieldName) {
12721
12732
  if (!fieldName)
@@ -12732,20 +12743,6 @@ class SectionConfiguratorComponent {
12732
12743
  }
12733
12744
  return null;
12734
12745
  }
12735
- derivePreviewInitials(value) {
12736
- if (!value)
12737
- return null;
12738
- const tokens = value.match(/[0-9A-Za-zÀ-ÿ]+/g) || [];
12739
- if (!tokens.length)
12740
- return null;
12741
- if (tokens.length === 1)
12742
- return tokens[0].slice(0, 2).toUpperCase();
12743
- const initials = [tokens[0], tokens[tokens.length - 1]]
12744
- .filter((token) => typeof token === 'string' && token.length > 0)
12745
- .map((token) => token.charAt(0))
12746
- .join('');
12747
- return initials ? initials.toUpperCase() : null;
12748
- }
12749
12746
  generateId(prefix) {
12750
12747
  return `${prefix}-${Math.random().toString(36).slice(2, 9)}`;
12751
12748
  }
@@ -18103,6 +18100,7 @@ class SectionEditorComponent {
18103
18100
  section;
18104
18101
  form;
18105
18102
  breakpoints = ['xs', 'sm', 'md', 'lg', 'xl'];
18103
+ fieldOptions = [];
18106
18104
  isDirty$ = new BehaviorSubject(false);
18107
18105
  isValid$ = new BehaviorSubject(true);
18108
18106
  isBusy$ = new BehaviorSubject(false);
@@ -18118,6 +18116,10 @@ class SectionEditorComponent {
18118
18116
  this.fieldMetadata = this.data.fieldMetadata || this.fieldMetadata;
18119
18117
  }
18120
18118
  ngOnInit() {
18119
+ this.fieldOptions = (this.fieldMetadata || []).map((field) => ({
18120
+ value: field.name,
18121
+ label: field.label ? `${field.label} (${field.name})` : field.name,
18122
+ }));
18121
18123
  const sectionHeader = this.section.sectionHeader || {};
18122
18124
  this.form = this.fb.group({
18123
18125
  title: [this.section.title, Validators.required],
@@ -18156,12 +18158,14 @@ class SectionEditorComponent {
18156
18158
  testId: [this.section.testId ?? ''],
18157
18159
  headerTooltip: [this.section.headerTooltip ?? ''],
18158
18160
  });
18161
+ this.syncCollapsedControlState(!!this.form.get('collapsible')?.value);
18159
18162
  this.form.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(() => {
18160
18163
  this.applyNormalizedFormValue();
18161
18164
  this.isDirty$.next(true);
18162
18165
  this.isValid$.next(this.form.valid);
18163
18166
  });
18164
18167
  this.form.get('collapsible')?.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((canCollapse) => {
18168
+ this.syncCollapsedControlState(!!canCollapse);
18165
18169
  if (!canCollapse) {
18166
18170
  this.form.get('collapsed')?.setValue(false, { emitEvent: false });
18167
18171
  }
@@ -18209,6 +18213,17 @@ class SectionEditorComponent {
18209
18213
  clearFallbackIcon() {
18210
18214
  this.form.get('sectionHeader.fallbackIcon')?.setValue('');
18211
18215
  }
18216
+ syncCollapsedControlState(canCollapse) {
18217
+ const collapsedControl = this.form.get('collapsed');
18218
+ if (!collapsedControl)
18219
+ return;
18220
+ if (canCollapse) {
18221
+ collapsedControl.enable({ emitEvent: false });
18222
+ return;
18223
+ }
18224
+ collapsedControl.setValue(false, { emitEvent: false });
18225
+ collapsedControl.disable({ emitEvent: false });
18226
+ }
18212
18227
  applyPreset(key) {
18213
18228
  const patch = (vals) => {
18214
18229
  this.form.patchValue(vals);
@@ -18287,11 +18302,8 @@ class SectionEditorComponent {
18287
18302
  this.form.get('descriptionStyle')?.setValue(style);
18288
18303
  this.isDirty$.next(true);
18289
18304
  }
18290
- get fieldOptions() {
18291
- return (this.fieldMetadata || []).map((field) => ({
18292
- value: field.name,
18293
- label: field.label ? `${field.label} (${field.name})` : field.name,
18294
- }));
18305
+ trackFieldOption(index, field) {
18306
+ return field.value || String(index);
18295
18307
  }
18296
18308
  tx(key, fallback) {
18297
18309
  return this.i18n.t(key, undefined, fallback, 'praxis-dynamic-form');
@@ -18376,11 +18388,10 @@ class SectionEditorComponent {
18376
18388
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: SectionEditorComponent, isStandalone: true, selector: "praxis-section-editor", inputs: { focusTarget: "focusTarget", fieldMetadata: "fieldMetadata" }, viewQueries: [{ propertyName: "titleInput", first: true, predicate: ["titleInput"], descendants: true }, { propertyName: "descriptionInput", first: true, predicate: ["descriptionInput"], descendants: true }], ngImport: i0, template: `
18377
18389
  <div class="editor-container" *ngIf="form">
18378
18390
  <mat-tab-group>
18379
- <mat-tab label="Cabeçalho">
18391
+ <mat-tab [label]="tx('dynamicForm.sectionEditor.tabs.header', 'Cabeçalho')">
18380
18392
  <div class="tips">
18381
18393
  <p>{{ tx('dynamicForm.sectionEditor.header.tip.identity', 'Configure titulo, descricao e a identidade visual exibida no topo da secao.') }}</p>
18382
- <p>{{ tx('dynamicForm.sectionEditor.header.tip.modes', 'O header pode usar icone estatico, foto vinda de um field do formulario ou iniciais derivadas de um field textual.') }}</p>
18383
- <p>{{ tx('dynamicForm.sectionEditor.header.tip.emptyState', 'Em fluxos de inclusao, defina o comportamento de vazio para evitar um cabecalho sem referencia visual antes do preenchimento.') }}</p>
18394
+ <p>{{ tx('dynamicForm.sectionEditor.header.tip.modes', 'A aba Avatar concentra os modos de foto, iniciais e fallback visual do titulo da secao.') }}</p>
18384
18395
  </div>
18385
18396
  <form [formGroup]="form" class="stack">
18386
18397
  <div class="preset-row">
@@ -18437,100 +18448,6 @@ class SectionEditorComponent {
18437
18448
  </mat-form-field>
18438
18449
  </div>
18439
18450
 
18440
- <div formGroupName="sectionHeader" class="stack">
18441
- <div class="row-3">
18442
- <mat-form-field appearance="fill">
18443
- <mat-label>{{ tx('dynamicForm.sectionEditor.header.mode.label', 'Visual do cabecalho') }}</mat-label>
18444
- <mat-select formControlName="mode">
18445
- <mat-option value="icon">{{ tx('dynamicForm.sectionEditor.header.mode.icon', 'Icone estatico') }}</mat-option>
18446
- <mat-option value="avatar-image">{{ tx('dynamicForm.sectionEditor.header.mode.image', 'Avatar por imagem') }}</mat-option>
18447
- <mat-option value="avatar-initials">{{ tx('dynamicForm.sectionEditor.header.mode.initials', 'Avatar por iniciais') }}</mat-option>
18448
- <mat-option value="auto">{{ tx('dynamicForm.sectionEditor.header.mode.auto', 'Automatico') }}</mat-option>
18449
- </mat-select>
18450
- </mat-form-field>
18451
-
18452
- <mat-form-field appearance="fill">
18453
- <mat-label>{{ tx('dynamicForm.sectionEditor.header.sourceField.label', 'Campo da imagem') }}</mat-label>
18454
- <mat-select formControlName="sourceField">
18455
- <mat-option value="">{{ tx('dynamicForm.sectionEditor.header.sourceField.none', 'Nao usar') }}</mat-option>
18456
- <mat-option *ngFor="let field of fieldOptions" [value]="field.value">{{ field.label }}</mat-option>
18457
- </mat-select>
18458
- </mat-form-field>
18459
-
18460
- <mat-form-field appearance="fill">
18461
- <mat-label>{{ tx('dynamicForm.sectionEditor.header.initialsSourceField.label', 'Campo para iniciais') }}</mat-label>
18462
- <mat-select formControlName="initialsSourceField">
18463
- <mat-option value="">{{ tx('dynamicForm.sectionEditor.header.auto', 'Auto') }}</mat-option>
18464
- <mat-option *ngFor="let field of fieldOptions" [value]="field.value">{{ field.label }}</mat-option>
18465
- </mat-select>
18466
- </mat-form-field>
18467
- </div>
18468
-
18469
- <div class="row-3">
18470
- <mat-form-field appearance="fill">
18471
- <mat-label>{{ tx('dynamicForm.sectionEditor.header.altField.label', 'Campo descritivo (alt)') }}</mat-label>
18472
- <mat-select formControlName="altField">
18473
- <mat-option value="">{{ tx('dynamicForm.sectionEditor.header.auto', 'Auto') }}</mat-option>
18474
- <mat-option *ngFor="let field of fieldOptions" [value]="field.value">{{ field.label }}</mat-option>
18475
- </mat-select>
18476
- </mat-form-field>
18477
-
18478
- <mat-form-field appearance="fill">
18479
- <mat-label>{{ tx('dynamicForm.sectionEditor.header.emptyState.label', 'Quando o valor estiver vazio') }}</mat-label>
18480
- <mat-select formControlName="emptyState">
18481
- <mat-option value="">{{ tx('dynamicForm.sectionEditor.header.emptyState.default', 'Placeholder neutro (padrao)') }}</mat-option>
18482
- <mat-option value="fallback-icon">{{ tx('dynamicForm.sectionEditor.header.emptyState.fallbackIcon', 'Mostrar icone') }}</mat-option>
18483
- <mat-option value="placeholder-avatar">{{ tx('dynamicForm.sectionEditor.header.emptyState.placeholder', 'Mostrar avatar placeholder') }}</mat-option>
18484
- <mat-option value="none">{{ tx('dynamicForm.sectionEditor.header.emptyState.none', 'Nao mostrar nada') }}</mat-option>
18485
- </mat-select>
18486
- </mat-form-field>
18487
-
18488
- <mat-form-field appearance="fill">
18489
- <mat-label>{{ tx('dynamicForm.sectionEditor.header.initialsMaxLength.label', 'Maximo de iniciais') }}</mat-label>
18490
- <input matInput type="number" formControlName="initialsMaxLength" min="1" max="4" />
18491
- </mat-form-field>
18492
- </div>
18493
-
18494
- <div class="row-2">
18495
- <mat-form-field appearance="fill">
18496
- <mat-label>{{ tx('dynamicForm.sectionEditor.header.fallbackIcon.label', 'Icone de fallback') }}</mat-label>
18497
- <span matPrefix *ngIf="form.get('sectionHeader.fallbackIcon')?.value">
18498
- <mat-icon aria-hidden="true" [praxisIcon]="form.get('sectionHeader.fallbackIcon')?.value"></mat-icon>
18499
- </span>
18500
- <input matInput formControlName="fallbackIcon" placeholder="ex.: person" />
18501
- <button
18502
- mat-icon-button
18503
- matSuffix
18504
- type="button"
18505
- (click)="pickFallbackIcon()"
18506
- [matTooltip]="tx('dynamicForm.sectionEditor.header.fallbackIcon.pick', 'Escolher icone de fallback')"
18507
- [attr.aria-label]="tx('dynamicForm.sectionEditor.header.fallbackIcon.pick', 'Escolher icone de fallback')"
18508
- data-testid="pick-fallback-icon"
18509
- >
18510
- <mat-icon [praxisIcon]="'search'"></mat-icon>
18511
- </button>
18512
- <button
18513
- *ngIf="form.get('sectionHeader.fallbackIcon')?.value"
18514
- mat-icon-button
18515
- matSuffix
18516
- type="button"
18517
- (click)="clearFallbackIcon()"
18518
- [matTooltip]="tx('dynamicForm.sectionEditor.header.fallbackIcon.clear', 'Limpar icone de fallback')"
18519
- [attr.aria-label]="tx('dynamicForm.sectionEditor.header.fallbackIcon.clear', 'Limpar icone de fallback')"
18520
- data-testid="clear-fallback-icon"
18521
- >
18522
- <mat-icon [praxisIcon]="'backspace'"></mat-icon>
18523
- </button>
18524
- </mat-form-field>
18525
-
18526
- <div class="avatar-guidance">
18527
- <p>{{ tx('dynamicForm.sectionEditor.header.guidance.formats', 'Imagem aceita URL, data URL, File, Blob e objetos com propriedades como url, base64 ou bytes.') }}</p>
18528
- <p>{{ tx('dynamicForm.sectionEditor.header.guidance.auto', 'No modo automatico, o runtime tenta imagem, depois iniciais do campo configurado e por fim o estado vazio escolhido.') }}</p>
18529
- <p>{{ tx('dynamicForm.sectionEditor.header.guidance.neutral', 'Se nenhum campo tiver valor ainda, o padrao e um placeholder neutro, adequado a qualquer dominio corporativo.') }}</p>
18530
- </div>
18531
- </div>
18532
- </div>
18533
-
18534
18451
  <mat-form-field appearance="fill">
18535
18452
  <mat-label>Descrição</mat-label>
18536
18453
  <textarea matInput #descriptionInput formControlName="description" placeholder="Resumo curto do conteúdo da seção"></textarea>
@@ -18625,7 +18542,7 @@ class SectionEditorComponent {
18625
18542
  Colapsável
18626
18543
  </mat-slide-toggle>
18627
18544
 
18628
- <mat-slide-toggle formControlName="collapsed" [disabled]="!form.value.collapsible">
18545
+ <mat-slide-toggle formControlName="collapsed">
18629
18546
  Iniciar recolhida
18630
18547
  </mat-slide-toggle>
18631
18548
  </div>
@@ -18649,7 +18566,111 @@ class SectionEditorComponent {
18649
18566
  </form>
18650
18567
  </mat-tab>
18651
18568
 
18652
- <mat-tab label="Organização">
18569
+ <mat-tab [label]="tx('dynamicForm.sectionEditor.tabs.avatar', 'Avatar')">
18570
+ <ng-template matTabContent>
18571
+ <div class="tips">
18572
+ <p>{{ tx('dynamicForm.sectionEditor.header.tip.modes', 'O header pode usar icone estatico, foto vinda de um field do formulario ou iniciais derivadas de um field textual.') }}</p>
18573
+ <p>{{ tx('dynamicForm.sectionEditor.header.tip.emptyState', 'Em fluxos de inclusao, defina o comportamento de vazio para evitar um cabecalho sem referencia visual antes do preenchimento.') }}</p>
18574
+ </div>
18575
+ <form [formGroup]="form" class="stack">
18576
+ <div formGroupName="sectionHeader" class="stack">
18577
+ <div class="row-3">
18578
+ <mat-form-field appearance="fill">
18579
+ <mat-label>{{ tx('dynamicForm.sectionEditor.header.mode.label', 'Visual do cabecalho') }}</mat-label>
18580
+ <mat-select formControlName="mode">
18581
+ <mat-option value="icon">{{ tx('dynamicForm.sectionEditor.header.mode.icon', 'Icone estatico') }}</mat-option>
18582
+ <mat-option value="avatar-image">{{ tx('dynamicForm.sectionEditor.header.mode.image', 'Avatar por imagem') }}</mat-option>
18583
+ <mat-option value="avatar-initials">{{ tx('dynamicForm.sectionEditor.header.mode.initials', 'Avatar por iniciais') }}</mat-option>
18584
+ <mat-option value="auto">{{ tx('dynamicForm.sectionEditor.header.mode.auto', 'Automatico') }}</mat-option>
18585
+ </mat-select>
18586
+ </mat-form-field>
18587
+
18588
+ <mat-form-field appearance="fill">
18589
+ <mat-label>{{ tx('dynamicForm.sectionEditor.header.sourceField.label', 'Campo da imagem') }}</mat-label>
18590
+ <mat-select formControlName="sourceField">
18591
+ <mat-option value="">{{ tx('dynamicForm.sectionEditor.header.sourceField.none', 'Nao usar') }}</mat-option>
18592
+ <mat-option *ngFor="let field of fieldOptions; trackBy: trackFieldOption" [value]="field.value">{{ field.label }}</mat-option>
18593
+ </mat-select>
18594
+ </mat-form-field>
18595
+
18596
+ <mat-form-field appearance="fill">
18597
+ <mat-label>{{ tx('dynamicForm.sectionEditor.header.initialsSourceField.label', 'Campo para iniciais') }}</mat-label>
18598
+ <mat-select formControlName="initialsSourceField">
18599
+ <mat-option value="">{{ tx('dynamicForm.sectionEditor.header.auto', 'Auto') }}</mat-option>
18600
+ <mat-option *ngFor="let field of fieldOptions; trackBy: trackFieldOption" [value]="field.value">{{ field.label }}</mat-option>
18601
+ </mat-select>
18602
+ </mat-form-field>
18603
+ </div>
18604
+
18605
+ <div class="row-3">
18606
+ <mat-form-field appearance="fill">
18607
+ <mat-label>{{ tx('dynamicForm.sectionEditor.header.altField.label', 'Campo descritivo (alt)') }}</mat-label>
18608
+ <mat-select formControlName="altField">
18609
+ <mat-option value="">{{ tx('dynamicForm.sectionEditor.header.auto', 'Auto') }}</mat-option>
18610
+ <mat-option *ngFor="let field of fieldOptions; trackBy: trackFieldOption" [value]="field.value">{{ field.label }}</mat-option>
18611
+ </mat-select>
18612
+ </mat-form-field>
18613
+
18614
+ <mat-form-field appearance="fill">
18615
+ <mat-label>{{ tx('dynamicForm.sectionEditor.header.emptyState.label', 'Quando o valor estiver vazio') }}</mat-label>
18616
+ <mat-select formControlName="emptyState">
18617
+ <mat-option value="">{{ tx('dynamicForm.sectionEditor.header.emptyState.default', 'Placeholder neutro (padrao)') }}</mat-option>
18618
+ <mat-option value="fallback-icon">{{ tx('dynamicForm.sectionEditor.header.emptyState.fallbackIcon', 'Mostrar icone') }}</mat-option>
18619
+ <mat-option value="placeholder-avatar">{{ tx('dynamicForm.sectionEditor.header.emptyState.placeholder', 'Mostrar avatar placeholder') }}</mat-option>
18620
+ <mat-option value="none">{{ tx('dynamicForm.sectionEditor.header.emptyState.none', 'Nao mostrar nada') }}</mat-option>
18621
+ </mat-select>
18622
+ </mat-form-field>
18623
+
18624
+ <mat-form-field appearance="fill">
18625
+ <mat-label>{{ tx('dynamicForm.sectionEditor.header.initialsMaxLength.label', 'Maximo de iniciais') }}</mat-label>
18626
+ <input matInput type="number" formControlName="initialsMaxLength" min="1" max="4" />
18627
+ </mat-form-field>
18628
+ </div>
18629
+
18630
+ <div class="row-2">
18631
+ <mat-form-field appearance="fill">
18632
+ <mat-label>{{ tx('dynamicForm.sectionEditor.header.fallbackIcon.label', 'Icone de fallback') }}</mat-label>
18633
+ <span matPrefix *ngIf="form.get('sectionHeader.fallbackIcon')?.value">
18634
+ <mat-icon aria-hidden="true" [praxisIcon]="form.get('sectionHeader.fallbackIcon')?.value"></mat-icon>
18635
+ </span>
18636
+ <input matInput formControlName="fallbackIcon" placeholder="ex.: person" />
18637
+ <button
18638
+ mat-icon-button
18639
+ matSuffix
18640
+ type="button"
18641
+ (click)="pickFallbackIcon()"
18642
+ [matTooltip]="tx('dynamicForm.sectionEditor.header.fallbackIcon.pick', 'Escolher icone de fallback')"
18643
+ [attr.aria-label]="tx('dynamicForm.sectionEditor.header.fallbackIcon.pick', 'Escolher icone de fallback')"
18644
+ data-testid="pick-fallback-icon"
18645
+ >
18646
+ <mat-icon [praxisIcon]="'search'"></mat-icon>
18647
+ </button>
18648
+ <button
18649
+ *ngIf="form.get('sectionHeader.fallbackIcon')?.value"
18650
+ mat-icon-button
18651
+ matSuffix
18652
+ type="button"
18653
+ (click)="clearFallbackIcon()"
18654
+ [matTooltip]="tx('dynamicForm.sectionEditor.header.fallbackIcon.clear', 'Limpar icone de fallback')"
18655
+ [attr.aria-label]="tx('dynamicForm.sectionEditor.header.fallbackIcon.clear', 'Limpar icone de fallback')"
18656
+ data-testid="clear-fallback-icon"
18657
+ >
18658
+ <mat-icon [praxisIcon]="'backspace'"></mat-icon>
18659
+ </button>
18660
+ </mat-form-field>
18661
+
18662
+ <div class="avatar-guidance">
18663
+ <p>{{ tx('dynamicForm.sectionEditor.header.guidance.formats', 'Imagem aceita URL, data URL, File, Blob e objetos com propriedades como url, base64 ou bytes.') }}</p>
18664
+ <p>{{ tx('dynamicForm.sectionEditor.header.guidance.auto', 'No modo automatico, o runtime tenta imagem, depois iniciais do campo configurado e por fim o estado vazio escolhido.') }}</p>
18665
+ <p>{{ tx('dynamicForm.sectionEditor.header.guidance.neutral', 'Se nenhum campo tiver valor ainda, o padrao e um placeholder neutro, adequado a qualquer dominio corporativo.') }}</p>
18666
+ </div>
18667
+ </div>
18668
+ </div>
18669
+ </form>
18670
+ </ng-template>
18671
+ </mat-tab>
18672
+
18673
+ <mat-tab [label]="tx('dynamicForm.sectionEditor.tabs.organization', 'Organização')">
18653
18674
  <div class="tips">
18654
18675
  <p>Controle ordem e visibilidade básica da seção.</p>
18655
18676
  </div>
@@ -18670,7 +18691,7 @@ class SectionEditorComponent {
18670
18691
  </mat-tab>
18671
18692
  </mat-tab-group>
18672
18693
  </div>
18673
- `, isInline: true, styles: [".editor-container{padding:16px}.preset-row{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-bottom:12px}.preset-title{font-weight:600;color:var(--md-sys-color-on-surface-variant)}.bp-group{display:flex;flex-wrap:wrap;gap:8px;margin:12px 0}.stack{display:flex;flex-direction:column;gap:12px}.row-2,.row-3{display:grid;gap:10px;align-items:start}.row-2{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.row-3{grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}mat-form-field{width:100%;margin-bottom:12px}.help-icon-button{--mdc-icon-button-state-layer-size: 28px;--mdc-icon-button-icon-size: 18px;width:28px;height:28px;padding:0;display:inline-flex;align-items:center;justify-content:center;vertical-align:middle}.help-icon-button mat-icon{font-size:18px;line-height:18px;width:18px;height:18px}.avatar-guidance{border-radius:12px;padding:12px 14px;background:color-mix(in srgb,var(--md-sys-color-primary) 6%,white);border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 14%,transparent)}.avatar-guidance p{margin:0 0 6px;font-size:12px;line-height:1.5;color:var(--md-sys-color-on-surface-variant)}.avatar-guidance p:last-child{margin-bottom:0}.tips p{margin:0 0 6px;font-size:13px;color:var(--md-sys-color-on-surface-variant)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$3.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1$3.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i7$2.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i7$2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i6$3.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i9.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i11$1.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6$4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6$4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }] });
18694
+ `, isInline: true, styles: [".editor-container{padding:16px}.preset-row{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-bottom:12px}.preset-title{font-weight:600;color:var(--md-sys-color-on-surface-variant)}.bp-group{display:flex;flex-wrap:wrap;gap:8px;margin:12px 0}.stack{display:flex;flex-direction:column;gap:12px}.row-2,.row-3{display:grid;gap:10px;align-items:start}.row-2{grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.row-3{grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}mat-form-field{width:100%;margin-bottom:12px}.help-icon-button{--mdc-icon-button-state-layer-size: 28px;--mdc-icon-button-icon-size: 18px;width:28px;height:28px;padding:0;display:inline-flex;align-items:center;justify-content:center;vertical-align:middle}.help-icon-button mat-icon{font-size:18px;line-height:18px;width:18px;height:18px}.avatar-guidance{border-radius:12px;padding:12px 14px;background:color-mix(in srgb,var(--md-sys-color-primary) 6%,white);border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 14%,transparent)}.avatar-guidance p{margin:0 0 6px;font-size:12px;line-height:1.5;color:var(--md-sys-color-on-surface-variant)}.avatar-guidance p:last-child{margin-bottom:0}.tips p{margin:0 0 6px;font-size:13px;color:var(--md-sys-color-on-surface-variant)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$3.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1$3.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i7$2.MatTabContent, selector: "[matTabContent]" }, { kind: "component", type: i7$2.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i7$2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i6$3.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i9.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i11$1.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6$4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6$4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }] });
18674
18695
  }
18675
18696
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SectionEditorComponent, decorators: [{
18676
18697
  type: Component,
@@ -18691,11 +18712,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
18691
18712
  ], template: `
18692
18713
  <div class="editor-container" *ngIf="form">
18693
18714
  <mat-tab-group>
18694
- <mat-tab label="Cabeçalho">
18715
+ <mat-tab [label]="tx('dynamicForm.sectionEditor.tabs.header', 'Cabeçalho')">
18695
18716
  <div class="tips">
18696
18717
  <p>{{ tx('dynamicForm.sectionEditor.header.tip.identity', 'Configure titulo, descricao e a identidade visual exibida no topo da secao.') }}</p>
18697
- <p>{{ tx('dynamicForm.sectionEditor.header.tip.modes', 'O header pode usar icone estatico, foto vinda de um field do formulario ou iniciais derivadas de um field textual.') }}</p>
18698
- <p>{{ tx('dynamicForm.sectionEditor.header.tip.emptyState', 'Em fluxos de inclusao, defina o comportamento de vazio para evitar um cabecalho sem referencia visual antes do preenchimento.') }}</p>
18718
+ <p>{{ tx('dynamicForm.sectionEditor.header.tip.modes', 'A aba Avatar concentra os modos de foto, iniciais e fallback visual do titulo da secao.') }}</p>
18699
18719
  </div>
18700
18720
  <form [formGroup]="form" class="stack">
18701
18721
  <div class="preset-row">
@@ -18752,100 +18772,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
18752
18772
  </mat-form-field>
18753
18773
  </div>
18754
18774
 
18755
- <div formGroupName="sectionHeader" class="stack">
18756
- <div class="row-3">
18757
- <mat-form-field appearance="fill">
18758
- <mat-label>{{ tx('dynamicForm.sectionEditor.header.mode.label', 'Visual do cabecalho') }}</mat-label>
18759
- <mat-select formControlName="mode">
18760
- <mat-option value="icon">{{ tx('dynamicForm.sectionEditor.header.mode.icon', 'Icone estatico') }}</mat-option>
18761
- <mat-option value="avatar-image">{{ tx('dynamicForm.sectionEditor.header.mode.image', 'Avatar por imagem') }}</mat-option>
18762
- <mat-option value="avatar-initials">{{ tx('dynamicForm.sectionEditor.header.mode.initials', 'Avatar por iniciais') }}</mat-option>
18763
- <mat-option value="auto">{{ tx('dynamicForm.sectionEditor.header.mode.auto', 'Automatico') }}</mat-option>
18764
- </mat-select>
18765
- </mat-form-field>
18766
-
18767
- <mat-form-field appearance="fill">
18768
- <mat-label>{{ tx('dynamicForm.sectionEditor.header.sourceField.label', 'Campo da imagem') }}</mat-label>
18769
- <mat-select formControlName="sourceField">
18770
- <mat-option value="">{{ tx('dynamicForm.sectionEditor.header.sourceField.none', 'Nao usar') }}</mat-option>
18771
- <mat-option *ngFor="let field of fieldOptions" [value]="field.value">{{ field.label }}</mat-option>
18772
- </mat-select>
18773
- </mat-form-field>
18774
-
18775
- <mat-form-field appearance="fill">
18776
- <mat-label>{{ tx('dynamicForm.sectionEditor.header.initialsSourceField.label', 'Campo para iniciais') }}</mat-label>
18777
- <mat-select formControlName="initialsSourceField">
18778
- <mat-option value="">{{ tx('dynamicForm.sectionEditor.header.auto', 'Auto') }}</mat-option>
18779
- <mat-option *ngFor="let field of fieldOptions" [value]="field.value">{{ field.label }}</mat-option>
18780
- </mat-select>
18781
- </mat-form-field>
18782
- </div>
18783
-
18784
- <div class="row-3">
18785
- <mat-form-field appearance="fill">
18786
- <mat-label>{{ tx('dynamicForm.sectionEditor.header.altField.label', 'Campo descritivo (alt)') }}</mat-label>
18787
- <mat-select formControlName="altField">
18788
- <mat-option value="">{{ tx('dynamicForm.sectionEditor.header.auto', 'Auto') }}</mat-option>
18789
- <mat-option *ngFor="let field of fieldOptions" [value]="field.value">{{ field.label }}</mat-option>
18790
- </mat-select>
18791
- </mat-form-field>
18792
-
18793
- <mat-form-field appearance="fill">
18794
- <mat-label>{{ tx('dynamicForm.sectionEditor.header.emptyState.label', 'Quando o valor estiver vazio') }}</mat-label>
18795
- <mat-select formControlName="emptyState">
18796
- <mat-option value="">{{ tx('dynamicForm.sectionEditor.header.emptyState.default', 'Placeholder neutro (padrao)') }}</mat-option>
18797
- <mat-option value="fallback-icon">{{ tx('dynamicForm.sectionEditor.header.emptyState.fallbackIcon', 'Mostrar icone') }}</mat-option>
18798
- <mat-option value="placeholder-avatar">{{ tx('dynamicForm.sectionEditor.header.emptyState.placeholder', 'Mostrar avatar placeholder') }}</mat-option>
18799
- <mat-option value="none">{{ tx('dynamicForm.sectionEditor.header.emptyState.none', 'Nao mostrar nada') }}</mat-option>
18800
- </mat-select>
18801
- </mat-form-field>
18802
-
18803
- <mat-form-field appearance="fill">
18804
- <mat-label>{{ tx('dynamicForm.sectionEditor.header.initialsMaxLength.label', 'Maximo de iniciais') }}</mat-label>
18805
- <input matInput type="number" formControlName="initialsMaxLength" min="1" max="4" />
18806
- </mat-form-field>
18807
- </div>
18808
-
18809
- <div class="row-2">
18810
- <mat-form-field appearance="fill">
18811
- <mat-label>{{ tx('dynamicForm.sectionEditor.header.fallbackIcon.label', 'Icone de fallback') }}</mat-label>
18812
- <span matPrefix *ngIf="form.get('sectionHeader.fallbackIcon')?.value">
18813
- <mat-icon aria-hidden="true" [praxisIcon]="form.get('sectionHeader.fallbackIcon')?.value"></mat-icon>
18814
- </span>
18815
- <input matInput formControlName="fallbackIcon" placeholder="ex.: person" />
18816
- <button
18817
- mat-icon-button
18818
- matSuffix
18819
- type="button"
18820
- (click)="pickFallbackIcon()"
18821
- [matTooltip]="tx('dynamicForm.sectionEditor.header.fallbackIcon.pick', 'Escolher icone de fallback')"
18822
- [attr.aria-label]="tx('dynamicForm.sectionEditor.header.fallbackIcon.pick', 'Escolher icone de fallback')"
18823
- data-testid="pick-fallback-icon"
18824
- >
18825
- <mat-icon [praxisIcon]="'search'"></mat-icon>
18826
- </button>
18827
- <button
18828
- *ngIf="form.get('sectionHeader.fallbackIcon')?.value"
18829
- mat-icon-button
18830
- matSuffix
18831
- type="button"
18832
- (click)="clearFallbackIcon()"
18833
- [matTooltip]="tx('dynamicForm.sectionEditor.header.fallbackIcon.clear', 'Limpar icone de fallback')"
18834
- [attr.aria-label]="tx('dynamicForm.sectionEditor.header.fallbackIcon.clear', 'Limpar icone de fallback')"
18835
- data-testid="clear-fallback-icon"
18836
- >
18837
- <mat-icon [praxisIcon]="'backspace'"></mat-icon>
18838
- </button>
18839
- </mat-form-field>
18840
-
18841
- <div class="avatar-guidance">
18842
- <p>{{ tx('dynamicForm.sectionEditor.header.guidance.formats', 'Imagem aceita URL, data URL, File, Blob e objetos com propriedades como url, base64 ou bytes.') }}</p>
18843
- <p>{{ tx('dynamicForm.sectionEditor.header.guidance.auto', 'No modo automatico, o runtime tenta imagem, depois iniciais do campo configurado e por fim o estado vazio escolhido.') }}</p>
18844
- <p>{{ tx('dynamicForm.sectionEditor.header.guidance.neutral', 'Se nenhum campo tiver valor ainda, o padrao e um placeholder neutro, adequado a qualquer dominio corporativo.') }}</p>
18845
- </div>
18846
- </div>
18847
- </div>
18848
-
18849
18775
  <mat-form-field appearance="fill">
18850
18776
  <mat-label>Descrição</mat-label>
18851
18777
  <textarea matInput #descriptionInput formControlName="description" placeholder="Resumo curto do conteúdo da seção"></textarea>
@@ -18940,7 +18866,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
18940
18866
  Colapsável
18941
18867
  </mat-slide-toggle>
18942
18868
 
18943
- <mat-slide-toggle formControlName="collapsed" [disabled]="!form.value.collapsible">
18869
+ <mat-slide-toggle formControlName="collapsed">
18944
18870
  Iniciar recolhida
18945
18871
  </mat-slide-toggle>
18946
18872
  </div>
@@ -18964,7 +18890,111 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
18964
18890
  </form>
18965
18891
  </mat-tab>
18966
18892
 
18967
- <mat-tab label="Organização">
18893
+ <mat-tab [label]="tx('dynamicForm.sectionEditor.tabs.avatar', 'Avatar')">
18894
+ <ng-template matTabContent>
18895
+ <div class="tips">
18896
+ <p>{{ tx('dynamicForm.sectionEditor.header.tip.modes', 'O header pode usar icone estatico, foto vinda de um field do formulario ou iniciais derivadas de um field textual.') }}</p>
18897
+ <p>{{ tx('dynamicForm.sectionEditor.header.tip.emptyState', 'Em fluxos de inclusao, defina o comportamento de vazio para evitar um cabecalho sem referencia visual antes do preenchimento.') }}</p>
18898
+ </div>
18899
+ <form [formGroup]="form" class="stack">
18900
+ <div formGroupName="sectionHeader" class="stack">
18901
+ <div class="row-3">
18902
+ <mat-form-field appearance="fill">
18903
+ <mat-label>{{ tx('dynamicForm.sectionEditor.header.mode.label', 'Visual do cabecalho') }}</mat-label>
18904
+ <mat-select formControlName="mode">
18905
+ <mat-option value="icon">{{ tx('dynamicForm.sectionEditor.header.mode.icon', 'Icone estatico') }}</mat-option>
18906
+ <mat-option value="avatar-image">{{ tx('dynamicForm.sectionEditor.header.mode.image', 'Avatar por imagem') }}</mat-option>
18907
+ <mat-option value="avatar-initials">{{ tx('dynamicForm.sectionEditor.header.mode.initials', 'Avatar por iniciais') }}</mat-option>
18908
+ <mat-option value="auto">{{ tx('dynamicForm.sectionEditor.header.mode.auto', 'Automatico') }}</mat-option>
18909
+ </mat-select>
18910
+ </mat-form-field>
18911
+
18912
+ <mat-form-field appearance="fill">
18913
+ <mat-label>{{ tx('dynamicForm.sectionEditor.header.sourceField.label', 'Campo da imagem') }}</mat-label>
18914
+ <mat-select formControlName="sourceField">
18915
+ <mat-option value="">{{ tx('dynamicForm.sectionEditor.header.sourceField.none', 'Nao usar') }}</mat-option>
18916
+ <mat-option *ngFor="let field of fieldOptions; trackBy: trackFieldOption" [value]="field.value">{{ field.label }}</mat-option>
18917
+ </mat-select>
18918
+ </mat-form-field>
18919
+
18920
+ <mat-form-field appearance="fill">
18921
+ <mat-label>{{ tx('dynamicForm.sectionEditor.header.initialsSourceField.label', 'Campo para iniciais') }}</mat-label>
18922
+ <mat-select formControlName="initialsSourceField">
18923
+ <mat-option value="">{{ tx('dynamicForm.sectionEditor.header.auto', 'Auto') }}</mat-option>
18924
+ <mat-option *ngFor="let field of fieldOptions; trackBy: trackFieldOption" [value]="field.value">{{ field.label }}</mat-option>
18925
+ </mat-select>
18926
+ </mat-form-field>
18927
+ </div>
18928
+
18929
+ <div class="row-3">
18930
+ <mat-form-field appearance="fill">
18931
+ <mat-label>{{ tx('dynamicForm.sectionEditor.header.altField.label', 'Campo descritivo (alt)') }}</mat-label>
18932
+ <mat-select formControlName="altField">
18933
+ <mat-option value="">{{ tx('dynamicForm.sectionEditor.header.auto', 'Auto') }}</mat-option>
18934
+ <mat-option *ngFor="let field of fieldOptions; trackBy: trackFieldOption" [value]="field.value">{{ field.label }}</mat-option>
18935
+ </mat-select>
18936
+ </mat-form-field>
18937
+
18938
+ <mat-form-field appearance="fill">
18939
+ <mat-label>{{ tx('dynamicForm.sectionEditor.header.emptyState.label', 'Quando o valor estiver vazio') }}</mat-label>
18940
+ <mat-select formControlName="emptyState">
18941
+ <mat-option value="">{{ tx('dynamicForm.sectionEditor.header.emptyState.default', 'Placeholder neutro (padrao)') }}</mat-option>
18942
+ <mat-option value="fallback-icon">{{ tx('dynamicForm.sectionEditor.header.emptyState.fallbackIcon', 'Mostrar icone') }}</mat-option>
18943
+ <mat-option value="placeholder-avatar">{{ tx('dynamicForm.sectionEditor.header.emptyState.placeholder', 'Mostrar avatar placeholder') }}</mat-option>
18944
+ <mat-option value="none">{{ tx('dynamicForm.sectionEditor.header.emptyState.none', 'Nao mostrar nada') }}</mat-option>
18945
+ </mat-select>
18946
+ </mat-form-field>
18947
+
18948
+ <mat-form-field appearance="fill">
18949
+ <mat-label>{{ tx('dynamicForm.sectionEditor.header.initialsMaxLength.label', 'Maximo de iniciais') }}</mat-label>
18950
+ <input matInput type="number" formControlName="initialsMaxLength" min="1" max="4" />
18951
+ </mat-form-field>
18952
+ </div>
18953
+
18954
+ <div class="row-2">
18955
+ <mat-form-field appearance="fill">
18956
+ <mat-label>{{ tx('dynamicForm.sectionEditor.header.fallbackIcon.label', 'Icone de fallback') }}</mat-label>
18957
+ <span matPrefix *ngIf="form.get('sectionHeader.fallbackIcon')?.value">
18958
+ <mat-icon aria-hidden="true" [praxisIcon]="form.get('sectionHeader.fallbackIcon')?.value"></mat-icon>
18959
+ </span>
18960
+ <input matInput formControlName="fallbackIcon" placeholder="ex.: person" />
18961
+ <button
18962
+ mat-icon-button
18963
+ matSuffix
18964
+ type="button"
18965
+ (click)="pickFallbackIcon()"
18966
+ [matTooltip]="tx('dynamicForm.sectionEditor.header.fallbackIcon.pick', 'Escolher icone de fallback')"
18967
+ [attr.aria-label]="tx('dynamicForm.sectionEditor.header.fallbackIcon.pick', 'Escolher icone de fallback')"
18968
+ data-testid="pick-fallback-icon"
18969
+ >
18970
+ <mat-icon [praxisIcon]="'search'"></mat-icon>
18971
+ </button>
18972
+ <button
18973
+ *ngIf="form.get('sectionHeader.fallbackIcon')?.value"
18974
+ mat-icon-button
18975
+ matSuffix
18976
+ type="button"
18977
+ (click)="clearFallbackIcon()"
18978
+ [matTooltip]="tx('dynamicForm.sectionEditor.header.fallbackIcon.clear', 'Limpar icone de fallback')"
18979
+ [attr.aria-label]="tx('dynamicForm.sectionEditor.header.fallbackIcon.clear', 'Limpar icone de fallback')"
18980
+ data-testid="clear-fallback-icon"
18981
+ >
18982
+ <mat-icon [praxisIcon]="'backspace'"></mat-icon>
18983
+ </button>
18984
+ </mat-form-field>
18985
+
18986
+ <div class="avatar-guidance">
18987
+ <p>{{ tx('dynamicForm.sectionEditor.header.guidance.formats', 'Imagem aceita URL, data URL, File, Blob e objetos com propriedades como url, base64 ou bytes.') }}</p>
18988
+ <p>{{ tx('dynamicForm.sectionEditor.header.guidance.auto', 'No modo automatico, o runtime tenta imagem, depois iniciais do campo configurado e por fim o estado vazio escolhido.') }}</p>
18989
+ <p>{{ tx('dynamicForm.sectionEditor.header.guidance.neutral', 'Se nenhum campo tiver valor ainda, o padrao e um placeholder neutro, adequado a qualquer dominio corporativo.') }}</p>
18990
+ </div>
18991
+ </div>
18992
+ </div>
18993
+ </form>
18994
+ </ng-template>
18995
+ </mat-tab>
18996
+
18997
+ <mat-tab [label]="tx('dynamicForm.sectionEditor.tabs.organization', 'Organização')">
18968
18998
  <div class="tips">
18969
18999
  <p>Controle ordem e visibilidade básica da seção.</p>
18970
19000
  </div>