@praxisui/dynamic-form 8.0.0-beta.0 → 8.0.0-beta.11

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.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { EventEmitter, HostListener, Output, Input, Component, Optional, Inject, Injectable, inject, ViewChildren, ViewChild, ChangeDetectionStrategy, ENVIRONMENT_INITIALIZER } from '@angular/core';
2
+ import { EventEmitter, HostListener, Output, Input, Component, inject, Optional, Inject, Injectable, ViewChildren, ViewChild, ChangeDetectionStrategy, ENVIRONMENT_INITIALIZER } from '@angular/core';
3
3
  import * as i1$1 from '@angular/common';
4
4
  import { CommonModule } from '@angular/common';
5
5
  import * as i2 from '@angular/common/http';
@@ -21,9 +21,9 @@ import { MatSnackBarModule } from '@angular/material/snack-bar';
21
21
  import * as i18 from '@angular/material/badge';
22
22
  import { MatBadgeModule } from '@angular/material/badge';
23
23
  import { firstValueFrom, BehaviorSubject, Subject, throwError, debounceTime as debounceTime$1, takeUntil as takeUntil$1 } from 'rxjs';
24
- import { map, take, takeUntil, tap, debounceTime, finalize } from 'rxjs/operators';
24
+ import { map, take, takeUntil, timeout, tap, debounceTime, finalize } from 'rxjs/operators';
25
25
  import * as i1$2 from '@praxisui/core';
26
- import { PraxisIconDirective, RULE_PROPERTY_SCHEMA, FIELD_METADATA_CAPABILITIES, deepMerge, createDefaultFormConfig, normalizeFormConfig as normalizeFormConfig$1, ensureIds, createEmptyRichContentDocument, ASYNC_CONFIG_STORAGE, migrateFormLayoutRule, LoggerService, createCorporateLoggerConfig, ConsoleLoggerSink, PraxisI18nService, ResourceQuickConnectComponent, composeHeadersWithVersion, buildApiUrl, mapFieldDefinitionsToMetadata, syncWithServerMetadata, PRAXIS_LOADING_CTX, normalizeControlTypeKey, resolveSpan, resolveOffset, resolveOrder, resolveHidden, buildSchemaId, fetchWithETag, resolveControlTypeAlias, getTextTransformer, MemoryCacheAdapter, LocalStorageCacheAdapter, SchemaMetadataClient, CONNECTION_STORAGE, API_URL, PRAXIS_LOADING_RENDERER, FORM_HOOKS_PRESETS, EmptyStateCardComponent, providePraxisI18nConfig, isValidFormConfig, ComponentMetadataRegistry, FieldControlType, GLOBAL_ACTION_CATALOG, GLOBAL_ACTION_SPEC_CATALOG, PRAXIS_GLOBAL_ACTION_CATALOG, getGlobalActionCatalog, SURFACE_OPEN_I18N_NAMESPACE, getGlobalActionUiSchema, SurfaceOpenActionEditorComponent, SURFACE_OPEN_I18N_CONFIG, PraxisJsonLogicService } from '@praxisui/core';
26
+ import { PraxisI18nService, PraxisIconDirective, providePraxisI18nConfig, RULE_PROPERTY_SCHEMA, FIELD_METADATA_CAPABILITIES, deepMerge, createDefaultFormConfig, normalizeFormConfig as normalizeFormConfig$1, ensureIds, createEmptyRichContentDocument, ASYNC_CONFIG_STORAGE, migrateFormLayoutRule, LoggerService, createCorporateLoggerConfig, ConsoleLoggerSink, ResourceQuickConnectComponent, composeHeadersWithVersion, buildApiUrl, mapFieldDefinitionsToMetadata, reconcileFormConfig, syncWithServerMetadata, PRAXIS_LOADING_CTX, normalizeControlTypeKey, resolveSpan, resolveOffset, resolveOrder, resolveHidden, buildSchemaId, fetchWithETag, resolveControlTypeAlias, getTextTransformer, MemoryCacheAdapter, LocalStorageCacheAdapter, SchemaMetadataClient, CONNECTION_STORAGE, API_URL, PRAXIS_LOADING_RENDERER, FORM_HOOKS_PRESETS, EmptyStateCardComponent, isValidFormConfig, ComponentMetadataRegistry, FieldControlType, isRequiredGlobalActionPayloadMissing, GLOBAL_ACTION_CATALOG, PRAXIS_GLOBAL_ACTION_CATALOG, getGlobalActionCatalog, SURFACE_OPEN_I18N_NAMESPACE, getGlobalActionUiSchema, getRequiredGlobalActionPayloadKeys, hasMeaningfulGlobalActionPayloadValue, SurfaceOpenActionEditorComponent, SURFACE_OPEN_I18N_CONFIG, validateGlobalActionRefs, PraxisJsonLogicService } from '@praxisui/core';
27
27
  import * as i1 from '@praxisui/dynamic-fields';
28
28
  import { getControlTypeCatalog, ConfirmDialogComponent, DynamicFieldLoaderDirective } from '@praxisui/dynamic-fields';
29
29
  import { BaseAiAdapter, PraxisAiAssistantComponent } from '@praxisui/ai';
@@ -174,6 +174,90 @@ function pad(value) {
174
174
  return String(value).padStart(2, '0');
175
175
  }
176
176
 
177
+ function prepareSubmitPayload(rawValue, fieldMetadata, options = {}) {
178
+ const normalized = normalizeSubmitPayload(rawValue);
179
+ if (!normalized || typeof normalized !== 'object' || Array.isArray(normalized)) {
180
+ return normalized;
181
+ }
182
+ const dirtyFields = new Set(options.dirtyFields || []);
183
+ return filterRecordForSubmit(normalized, fieldMetadata || [], dirtyFields);
184
+ }
185
+ function shouldOmitEmptyOptionalField(field, value, dirtyFields, scopePath) {
186
+ if (isFieldDirty(field.name, dirtyFields, scopePath) || isRequiredField(field)) {
187
+ return false;
188
+ }
189
+ return value === null || value === undefined || value === '';
190
+ }
191
+ function isRequiredField(field) {
192
+ const validators = field.validators;
193
+ return field.required === true || validators?.required === true;
194
+ }
195
+ function shouldOmitFieldFromSubmit(field, dirtyFields = new Set(), scopePath) {
196
+ const policy = field.submitPolicy;
197
+ if (policy) {
198
+ return shouldOmitByPolicy(policy, field.name, dirtyFields, scopePath);
199
+ }
200
+ return field.source === 'local' || field.transient === true;
201
+ }
202
+ function shouldOmitByPolicy(policy, fieldName, dirtyFields, scopePath) {
203
+ switch (policy) {
204
+ case 'include':
205
+ return false;
206
+ case 'omit':
207
+ return true;
208
+ case 'includeWhenDirty':
209
+ return !isFieldDirty(fieldName, dirtyFields, scopePath);
210
+ default:
211
+ return false;
212
+ }
213
+ }
214
+ function filterRecordForSubmit(value, fields, dirtyFields, scopePath) {
215
+ const payload = { ...value };
216
+ for (const field of fields) {
217
+ if (shouldOmitFieldFromSubmit(field, dirtyFields, scopePath)) {
218
+ delete payload[field.name];
219
+ continue;
220
+ }
221
+ const fieldValue = payload[field.name];
222
+ const fieldPath = buildScopedFieldPath(field.name, scopePath);
223
+ payload[field.name] = filterNestedArrayForSubmit(field, fieldValue, dirtyFields, fieldPath);
224
+ if (shouldOmitEmptyOptionalField(field, payload[field.name], dirtyFields, scopePath)) {
225
+ delete payload[field.name];
226
+ }
227
+ }
228
+ return payload;
229
+ }
230
+ function filterNestedArrayForSubmit(field, value, dirtyFields, fieldPath) {
231
+ const itemFields = field.array?.itemSchema?.fields;
232
+ if (!Array.isArray(value) || !itemFields?.length) {
233
+ return value;
234
+ }
235
+ return value.map((item, index) => {
236
+ if (!item || typeof item !== 'object' || Array.isArray(item)) {
237
+ return item;
238
+ }
239
+ return filterRecordForSubmit(item, itemFields, dirtyFields, `${fieldPath}.${index}`);
240
+ });
241
+ }
242
+ function isFieldDirty(fieldName, dirtyFields, scopePath) {
243
+ if (dirtyFields.has(fieldName)) {
244
+ return true;
245
+ }
246
+ if (!scopePath) {
247
+ return false;
248
+ }
249
+ return (dirtyFields.has(scopePath) ||
250
+ dirtyFields.has(buildScopedFieldPath(fieldName, scopePath)) ||
251
+ dirtyFields.has(buildBracketScopedFieldPath(fieldName, scopePath)));
252
+ }
253
+ function buildScopedFieldPath(fieldName, scopePath) {
254
+ return scopePath ? `${scopePath}.${fieldName}` : fieldName;
255
+ }
256
+ function buildBracketScopedFieldPath(fieldName, scopePath) {
257
+ const bracketPath = scopePath.replace(/\.(\d+)(?=\.|$)/g, '[$1]');
258
+ return `${bracketPath}.${fieldName}`;
259
+ }
260
+
177
261
  const SECTION_HEADER_INITIALS_MIN = 1;
178
262
  const SECTION_HEADER_INITIALS_MAX = 4;
179
263
  const SECTION_HEADER_INITIALS_DEFAULT = 2;
@@ -417,8 +501,48 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
417
501
  args: ['document:keydown', ['$event']]
418
502
  }] } });
419
503
 
504
+ const PRAXIS_DYNAMIC_FORM_I18N_NAMESPACE = 'praxisDynamicForm';
505
+ const PRAXIS_DYNAMIC_FORM_I18N_CONFIG = {
506
+ namespaces: {
507
+ [PRAXIS_DYNAMIC_FORM_I18N_NAMESPACE]: {
508
+ 'pt-BR': {
509
+ 'settings.title': 'Configuração do Formulário',
510
+ 'actions.invalidRequired': 'Preencha os campos obrigatórios para continuar.',
511
+ 'actions.invalidRequiredWithFields': 'Preencha: {{fields}}.',
512
+ },
513
+ 'en-US': {
514
+ 'settings.title': 'Form Configuration',
515
+ 'actions.invalidRequired': 'Complete the required fields to continue.',
516
+ 'actions.invalidRequiredWithFields': 'Complete: {{fields}}.',
517
+ },
518
+ },
519
+ },
520
+ };
521
+
420
522
  class PraxisFormActionsComponent {
421
523
  shortcuts;
524
+ i18n = inject(PraxisI18nService);
525
+ actionButtonsCache = null;
526
+ actionButtonsCacheSource;
527
+ actionButtonsCacheOverrides;
528
+ formActionsClassesCacheSource;
529
+ formActionsClassesCache = null;
530
+ viewClassMapCacheSource;
531
+ viewClassMapCacheEditorialContext;
532
+ viewClassMapCache = null;
533
+ visibleButtonsCacheSource = null;
534
+ visibleButtonsCache = [];
535
+ collapsedButtonsCacheSource = null;
536
+ collapsedButtonsCache = [];
537
+ primaryButtonsCacheSource = null;
538
+ primaryButtonsCache = [];
539
+ secondaryButtonsCacheSource = null;
540
+ secondaryButtonsCache = [];
541
+ buttonClassCache = new WeakMap();
542
+ buttonStylesCache = new WeakMap();
543
+ invalidSubmitHintCacheSignature = null;
544
+ invalidSubmitHintCacheValue = '';
545
+ invalidSubmitHint = this.i18n.t('actions.invalidRequired', undefined, 'Preencha os campos obrigatórios para continuar.', PRAXIS_DYNAMIC_FORM_I18N_NAMESPACE);
422
546
  /** Configuration object describing form action buttons */
423
547
  actions;
424
548
  /** Whether the actions are rendered inside an editorial visual context */
@@ -429,6 +553,8 @@ class PraxisFormActionsComponent {
429
553
  formIsValid = true;
430
554
  /** Optional error message displayed above the actions */
431
555
  submitError;
556
+ /** Visible required fields that currently block submit */
557
+ invalidRequiredFieldLabels = [];
432
558
  /** Identifier used when registering keyboard shortcuts */
433
559
  formId;
434
560
  /** Overrides coming from runtime rules */
@@ -440,7 +566,14 @@ class PraxisFormActionsComponent {
440
566
  this.shortcuts = shortcuts;
441
567
  }
442
568
  ngOnChanges(changes) {
443
- if (changes['actions'] || changes['formId']) {
569
+ if (changes['actions'] || changes['actionOverrides']) {
570
+ this.refreshActionButtons();
571
+ }
572
+ if (changes['actions'] || changes['editorialVisualContext']) {
573
+ this.formActionsClassesCache = null;
574
+ this.viewClassMapCache = null;
575
+ }
576
+ if (changes['actions'] || changes['formId'] || changes['actionOverrides']) {
444
577
  this.registerActionShortcuts();
445
578
  }
446
579
  }
@@ -449,7 +582,12 @@ class PraxisFormActionsComponent {
449
582
  this.shortcutDisposers = [];
450
583
  }
451
584
  get formActionsClasses() {
452
- return {
585
+ if (this.formActionsClassesCache &&
586
+ this.formActionsClassesCacheSource === this.actions) {
587
+ return this.formActionsClassesCache;
588
+ }
589
+ this.formActionsClassesCacheSource = this.actions;
590
+ this.formActionsClassesCache = {
453
591
  ['position-' + (this.actions?.position || 'right')]: true,
454
592
  ['orientation-' + (this.actions?.orientation || 'horizontal')]: true,
455
593
  ['spacing-' + (this.actions?.spacing || 'normal')]: true,
@@ -457,16 +595,25 @@ class PraxisFormActionsComponent {
457
595
  sticky: !!this.actions?.sticky,
458
596
  'with-divider': this.actions?.divider !== false,
459
597
  };
598
+ return this.formActionsClassesCache;
460
599
  }
461
600
  // Map for ngClass including optional container class
462
601
  get viewClassMap() {
602
+ if (this.viewClassMapCache &&
603
+ this.viewClassMapCacheSource === this.actions &&
604
+ this.viewClassMapCacheEditorialContext === this.editorialVisualContext) {
605
+ return this.viewClassMapCache;
606
+ }
463
607
  const base = this.formActionsClasses;
464
608
  const extra = this.actions?.containerClassName;
465
- return {
609
+ this.viewClassMapCacheSource = this.actions;
610
+ this.viewClassMapCacheEditorialContext = this.editorialVisualContext;
611
+ this.viewClassMapCache = {
466
612
  ...base,
467
613
  'editorial-visual-context': this.editorialVisualContext,
468
614
  ...(extra ? { [extra]: true } : {}),
469
615
  };
616
+ return this.viewClassMapCache;
470
617
  }
471
618
  // Map configured color to Angular Material supported values
472
619
  getMatColor(btn) {
@@ -491,6 +638,14 @@ class PraxisFormActionsComponent {
491
638
  }
492
639
  }
493
640
  getActionButtons() {
641
+ if (this.actionButtonsCache === null ||
642
+ this.actionButtonsCacheSource !== this.actions ||
643
+ this.actionButtonsCacheOverrides !== this.actionOverrides) {
644
+ this.refreshActionButtons();
645
+ }
646
+ return this.actionButtonsCache ?? [];
647
+ }
648
+ computeActionButtons() {
494
649
  const actions = this.actions;
495
650
  if (!actions) {
496
651
  return [
@@ -536,26 +691,61 @@ class PraxisFormActionsComponent {
536
691
  }
537
692
  return buttons.map((btn) => this.applyOverrides(btn));
538
693
  }
694
+ refreshActionButtons() {
695
+ this.actionButtonsCache = this.computeActionButtons();
696
+ this.actionButtonsCacheSource = this.actions;
697
+ this.actionButtonsCacheOverrides = this.actionOverrides;
698
+ this.visibleButtonsCacheSource = null;
699
+ this.collapsedButtonsCacheSource = null;
700
+ this.primaryButtonsCacheSource = null;
701
+ this.secondaryButtonsCacheSource = null;
702
+ this.buttonClassCache = new WeakMap();
703
+ this.buttonStylesCache = new WeakMap();
704
+ }
539
705
  getVisibleButtons() {
540
- return this.getActionButtons()
706
+ const buttons = this.getActionButtons();
707
+ if (this.visibleButtonsCacheSource === buttons) {
708
+ return this.visibleButtonsCache;
709
+ }
710
+ this.visibleButtonsCacheSource = buttons;
711
+ this.visibleButtonsCache = buttons
541
712
  .filter((btn) => btn.visible !== false)
542
713
  .slice(0, 1);
714
+ return this.visibleButtonsCache;
543
715
  }
544
716
  getCollapsedButtons() {
545
- return this.getActionButtons()
717
+ const buttons = this.getActionButtons();
718
+ if (this.collapsedButtonsCacheSource === buttons) {
719
+ return this.collapsedButtonsCache;
720
+ }
721
+ this.collapsedButtonsCacheSource = buttons;
722
+ this.collapsedButtonsCache = buttons
546
723
  .filter((btn) => btn.visible !== false)
547
724
  .slice(1);
725
+ return this.collapsedButtonsCache;
548
726
  }
549
727
  isSplitLayout() {
550
728
  return (this.actions?.position || 'right') === 'split';
551
729
  }
552
730
  getPrimaryButtons() {
553
- return this.getActionButtons().filter((button) => button.visible !== false
731
+ const buttons = this.getActionButtons();
732
+ if (this.primaryButtonsCacheSource === buttons) {
733
+ return this.primaryButtonsCache;
734
+ }
735
+ this.primaryButtonsCacheSource = buttons;
736
+ this.primaryButtonsCache = buttons.filter((button) => button.visible !== false
554
737
  && !this.isSecondaryButton(button));
738
+ return this.primaryButtonsCache;
555
739
  }
556
740
  getSecondaryButtons() {
557
- return this.getActionButtons().filter((button) => button.visible !== false
741
+ const buttons = this.getActionButtons();
742
+ if (this.secondaryButtonsCacheSource === buttons) {
743
+ return this.secondaryButtonsCache;
744
+ }
745
+ this.secondaryButtonsCacheSource = buttons;
746
+ this.secondaryButtonsCache = buttons.filter((button) => button.visible !== false
558
747
  && this.isSecondaryButton(button));
748
+ return this.secondaryButtonsCache;
559
749
  }
560
750
  onActionButtonClick(button, event) {
561
751
  if (this.isSubmitting) {
@@ -563,7 +753,7 @@ class PraxisFormActionsComponent {
563
753
  event.stopPropagation();
564
754
  return;
565
755
  }
566
- const actionId = button.id || button.action;
756
+ const actionId = this.resolveButtonActionId(button);
567
757
  if (!actionId)
568
758
  return;
569
759
  if (button.type === 'submit') {
@@ -576,23 +766,61 @@ class PraxisFormActionsComponent {
576
766
  const className = button.className;
577
767
  const variant = button.variant || 'raised';
578
768
  const tone = String(button.color || 'basic').toLowerCase();
579
- return {
769
+ const signature = JSON.stringify([className ?? null, variant, tone, button.size ?? null]);
770
+ const cached = this.buttonClassCache.get(button);
771
+ if (cached?.signature === signature) {
772
+ return cached.value;
773
+ }
774
+ const value = {
580
775
  ...(className ? { [className]: true } : {}),
581
776
  ...(button.size ? { [`size-${button.size}`]: true } : {}),
582
777
  [`variant-${variant}`]: true,
583
778
  [`tone-${tone}`]: true,
584
779
  };
780
+ this.buttonClassCache.set(button, { signature, value });
781
+ return value;
585
782
  }
586
783
  getButtonStyles(button) {
587
784
  const styles = button.style;
588
- return styles && Object.keys(styles).length ? styles : null;
785
+ const signature = styles ? JSON.stringify(styles) : '';
786
+ const cached = this.buttonStylesCache.get(button);
787
+ if (cached?.signature === signature) {
788
+ return cached.value;
789
+ }
790
+ const value = styles && Object.keys(styles).length ? styles : null;
791
+ this.buttonStylesCache.set(button, { signature, value });
792
+ return value;
589
793
  }
590
794
  isActionButtonDisabled(button) {
591
795
  const submitGuard = button.type === 'submit' && !this.formIsValid;
592
796
  return this.isSubmitting || !!button.disabled || !!button.loading || submitGuard;
593
797
  }
798
+ shouldShowInvalidSubmitHint() {
799
+ if (this.formIsValid || this.isSubmitting || this.submitError) {
800
+ return false;
801
+ }
802
+ return this.getActionButtons().some((button) => button.visible !== false && button.type === 'submit');
803
+ }
804
+ getInvalidSubmitHint() {
805
+ const labels = this.invalidRequiredFieldLabels
806
+ .map((label) => String(label || '').trim())
807
+ .filter(Boolean)
808
+ .slice(0, 3);
809
+ const signature = labels.join('|');
810
+ if (signature === this.invalidSubmitHintCacheSignature) {
811
+ return this.invalidSubmitHintCacheValue;
812
+ }
813
+ if (labels.length > 0) {
814
+ this.invalidSubmitHintCacheSignature = signature;
815
+ this.invalidSubmitHintCacheValue = this.i18n.t('actions.invalidRequiredWithFields', { fields: labels.join(', ') }, `Preencha: ${labels.join(', ')}.`, PRAXIS_DYNAMIC_FORM_I18N_NAMESPACE);
816
+ return this.invalidSubmitHintCacheValue;
817
+ }
818
+ this.invalidSubmitHintCacheSignature = signature;
819
+ this.invalidSubmitHintCacheValue = this.invalidSubmitHint;
820
+ return this.invalidSubmitHintCacheValue;
821
+ }
594
822
  applyOverrides(button) {
595
- const actionId = button.id || button.action;
823
+ const actionId = this.resolveButtonActionId(button);
596
824
  const override = actionId ? this.actionOverrides?.[actionId] : undefined;
597
825
  if (!override)
598
826
  return button;
@@ -629,7 +857,7 @@ class PraxisFormActionsComponent {
629
857
  return;
630
858
  const dispose = this.shortcuts.registerShortcut(shortcut, {
631
859
  callback: () => {
632
- const actionId = btn.id || btn.action;
860
+ const actionId = this.resolveButtonActionId(btn);
633
861
  if (!actionId)
634
862
  return;
635
863
  this.action.emit({
@@ -644,12 +872,21 @@ class PraxisFormActionsComponent {
644
872
  this.shortcutDisposers.push(dispose);
645
873
  });
646
874
  }
875
+ resolveButtonActionId(button) {
876
+ const id = button.id?.trim();
877
+ if (id === 'submit' || id === 'cancel' || id === 'reset')
878
+ return id;
879
+ return (button.action?.trim() ||
880
+ id ||
881
+ button.globalAction?.actionId?.trim() ||
882
+ undefined);
883
+ }
647
884
  isSecondaryButton(button) {
648
885
  const actionId = (button.id || button.action || '').toLowerCase();
649
886
  return actionId === 'cancel' || actionId === 'reset';
650
887
  }
651
888
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisFormActionsComponent, deps: [{ token: i1.KeyboardShortcutService }], target: i0.ɵɵFactoryTarget.Component });
652
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PraxisFormActionsComponent, isStandalone: true, selector: "praxis-form-actions", inputs: { actions: "actions", editorialVisualContext: "editorialVisualContext", isSubmitting: "isSubmitting", formIsValid: "formIsValid", submitError: "submitError", formId: "formId", actionOverrides: "actionOverrides" }, outputs: { action: "action" }, usesOnChanges: true, ngImport: i0, template: "<div\n class=\"form-actions\"\n [ngClass]=\"viewClassMap\"\n [ngStyle]=\"actions?.containerStyles\"\n>\n @if (submitError) {\n <div class=\"alert alert--error\" role=\"alert\" aria-live=\"polite\">\n <mat-icon class=\"alert__icon\" aria-hidden=\"true\">error_outline</mat-icon>\n <div>\n <h4 class=\"alert__title\">Erro ao salvar</h4>\n <p class=\"alert__text\">{{ submitError }}</p>\n </div>\n </div>\n }\n <div class=\"desktop-actions\">\n @if (isSplitLayout()) {\n <div class=\"desktop-actions-group secondary-group\">\n @for (button of getSecondaryButtons(); track button.id) {\n @if (button.visible) {\n @switch (button.variant || 'raised') {\n @case ('stroked') {\n <button mat-stroked-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('flat') {\n <button mat-flat-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('fab') {\n <button mat-fab\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @default {\n <button mat-raised-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n }\n }\n }\n </div>\n <div class=\"desktop-actions-group primary-group\">\n @for (button of getPrimaryButtons(); track button.id) {\n @if (button.visible) {\n @switch (button.variant || 'raised') {\n @case ('stroked') {\n <button mat-stroked-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('flat') {\n <button mat-flat-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('fab') {\n <button mat-fab\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @default {\n <button mat-raised-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n }\n }\n }\n </div>\n } @else {\n @for (button of getActionButtons(); track button.id) {\n @if (button.visible) {\n @switch (button.variant || 'raised') {\n @case ('stroked') {\n <button mat-stroked-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('flat') {\n <button mat-flat-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('fab') {\n <button mat-fab\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @default {\n <button mat-raised-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n }\n }\n }\n }\n </div>\n @if (actions?.mobile?.collapseToMenu) {\n <div class=\"mobile-actions\">\n @for (button of getVisibleButtons(); track button.id) {\n @switch (button.variant || 'raised') {\n @case ('stroked') {\n <button mat-stroked-button [type]=\"button.type || 'button'\" [color]=\"getMatColor(button)\" [disabled]=\"isActionButtonDisabled(button)\" [attr.aria-busy]=\"button.loading ? 'true' : null\" [matTooltip]=\"button.tooltip\" (click)=\"onActionButtonClick(button, $event)\" [attr.aria-label]=\"button.label\" [ngClass]=\"getButtonNgClass(button)\" [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('flat') {\n <button mat-flat-button [type]=\"button.type || 'button'\" [color]=\"getMatColor(button)\" [disabled]=\"isActionButtonDisabled(button)\" [attr.aria-busy]=\"button.loading ? 'true' : null\" [matTooltip]=\"button.tooltip\" (click)=\"onActionButtonClick(button, $event)\" [attr.aria-label]=\"button.label\" [ngClass]=\"getButtonNgClass(button)\" [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('fab') {\n <button mat-fab [type]=\"button.type || 'button'\" [color]=\"getMatColor(button)\" [disabled]=\"isActionButtonDisabled(button)\" [attr.aria-busy]=\"button.loading ? 'true' : null\" [matTooltip]=\"button.tooltip\" (click)=\"onActionButtonClick(button, $event)\" [attr.aria-label]=\"button.label\" [ngClass]=\"getButtonNgClass(button)\" [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon aria-hidden=\"true\">{{ button.icon }}</mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @default {\n <button mat-raised-button [type]=\"button.type || 'button'\" [color]=\"getMatColor(button)\" [disabled]=\"isActionButtonDisabled(button)\" [attr.aria-busy]=\"button.loading ? 'true' : null\" [matTooltip]=\"button.tooltip\" (click)=\"onActionButtonClick(button, $event)\" [attr.aria-label]=\"button.label\" [ngClass]=\"getButtonNgClass(button)\" [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon aria-hidden=\"true\">{{ button.icon }}</mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n }\n }\n @if (getCollapsedButtons().length > 0) {\n <button\n mat-icon-button\n [matMenuTriggerFor]=\"actionsMenu\"\n aria-label=\"More actions\"\n >\n <mat-icon>more_vert</mat-icon>\n </button>\n <mat-menu #actionsMenu=\"matMenu\">\n @for (button of getCollapsedButtons(); track button.id) {\n <button\n mat-menu-item\n (click)=\"onActionButtonClick(button, $event)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\"\n >\n @if (button.icon) { <mat-icon [praxisIcon]=\"button.icon\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n </mat-menu>\n }\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";.form-actions{display:flex;justify-content:flex-end;align-items:center;gap:.75rem;padding:1rem 1.1rem;z-index:1;font-family:inherit;border-radius:18px;background:var(--pfx-form-footer-surface, color-mix(in srgb, var(--md-sys-color-surface-container) 78%, var(--md-sys-color-surface-container-low) 22%));border:1px solid var(--pfx-form-footer-border, color-mix(in srgb, var(--md-sys-color-outline-variant) 76%, transparent))}.form-actions.with-divider{border-top:1px solid var(--pfx-form-footer-border, color-mix(in srgb, var(--md-sys-color-outline-variant) 76%, transparent))}.form-actions.editorial-visual-context{font-family:var(--editorial-body-font-family, inherit)}.form-actions.editorial-visual-context.with-divider{border-top:var(--editorial-card-border-width, 1px) solid color-mix(in srgb,var(--editorial-border-color, var(--md-sys-color-outline-variant)) 72%,transparent)}.form-actions.sticky{position:sticky;bottom:0}.form-actions.position-left{justify-content:flex-start}.form-actions.position-center{justify-content:center}.form-actions.position-right{justify-content:flex-end}.form-actions.position-justified,.form-actions.position-split{justify-content:space-between}.form-actions.orientation-vertical .desktop-actions{display:flex;flex-direction:column}.form-actions.orientation-horizontal .desktop-actions{display:flex;flex-direction:row}.form-actions.spacing-compact{gap:.25rem}.form-actions.spacing-normal{gap:.5rem}.form-actions.spacing-spacious{gap:1rem}.form-actions.spacing-compact .desktop-actions{gap:.25rem}.form-actions.spacing-normal .desktop-actions{gap:.5rem}.form-actions.spacing-spacious .desktop-actions{gap:1rem}.form-actions.loading{pointer-events:none;opacity:.7}.mobile-actions{display:none}.desktop-actions{display:flex;flex-wrap:wrap;gap:.5rem}.form-actions.position-split .desktop-actions{width:100%;justify-content:space-between;align-items:center}.desktop-actions-group{display:flex;flex-wrap:wrap;gap:.5rem;align-items:center}.desktop-actions-group.primary-group{justify-content:flex-end}.desktop-actions-group.secondary-group{opacity:.94}.form-actions .mdc-button{letter-spacing:.01em;border-radius:999px}.form-actions.editorial-visual-context .mdc-button{border-radius:var(--editorial-button-radius, 999px);font-family:var(--editorial-body-font-family, inherit)}.form-actions .mdc-button.size-small{min-height:36px;padding-inline:12px;font-size:.82rem}.form-actions .mdc-button.size-medium{min-height:44px;padding-inline:18px}.form-actions .mdc-button.size-large{min-height:50px;padding-inline:24px;font-size:.98rem}.form-actions .mat-mdc-raised-button,.form-actions .mat-mdc-unelevated-button{box-shadow:none}.form-actions .mat-mdc-raised-button:not([disabled]),.form-actions .mat-mdc-unelevated-button:not([disabled]){--mdc-protected-button-container-color: var(--md-sys-color-primary);--mdc-protected-button-label-text-color: var(--md-sys-color-on-primary);--mdc-filled-button-container-color: var(--md-sys-color-primary);--mdc-filled-button-label-text-color: var(--md-sys-color-on-primary)}.form-actions .mat-mdc-raised-button[disabled],.form-actions .mat-mdc-unelevated-button[disabled]{opacity:.55}.form-actions .mat-mdc-outlined-button,.form-actions .mat-mdc-button-base.mat-mdc-outlined-button{background:transparent}.form-actions.editorial-visual-context .mat-mdc-raised-button,.form-actions.editorial-visual-context .mat-mdc-unelevated-button{--mdc-protected-button-container-color: var(--editorial-cta-primary, var(--md-sys-color-primary));--mdc-protected-button-label-text-color: var(--editorial-cta-primary-text, var(--md-sys-color-on-primary));--mdc-filled-button-container-color: var(--editorial-cta-primary, var(--md-sys-color-primary));--mdc-filled-button-label-text-color: var(--editorial-cta-primary-text, var(--md-sys-color-on-primary));box-shadow:var(--editorial-floating-shadow, var(--editorial-card-shadow, none))}.form-actions.editorial-visual-context .mat-mdc-outlined-button{--mdc-outlined-button-outline-color: var(--editorial-border-color, var(--md-sys-color-outline-variant));--mdc-outlined-button-label-text-color: var( --editorial-cta-secondary-text, var(--editorial-text-primary, var(--md-sys-color-on-surface)) );background:color-mix(in srgb,var(--editorial-cta-secondary, var(--editorial-surface-secondary, var(--md-sys-color-surface-container-low))) 72%,transparent)}.form-actions.editorial-visual-context .mat-mdc-outlined-button.tone-primary,.form-actions.editorial-visual-context .mat-mdc-outlined-button.tone-accent{--mdc-outlined-button-label-text-color: var(--editorial-cta-primary, var(--md-sys-color-primary));background:transparent}.form-actions button .mat-icon{font-size:20px;width:20px;height:20px;line-height:1;display:inline-flex;align-items:center;justify-content:center}.form-actions button .mat-icon+span{margin-left:8px}@media(max-width:768px){.form-actions{padding:.9rem;border-radius:16px}.form-actions.mobile-menu-active .desktop-actions{display:none}.form-actions.mobile-menu-active .mobile-actions{display:flex;align-items:center;gap:.5rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { 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: "component", type: i4.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { 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: MatMenuModule }, { kind: "component", type: i6.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i6.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i6.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }] });
889
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PraxisFormActionsComponent, isStandalone: true, selector: "praxis-form-actions", inputs: { actions: "actions", editorialVisualContext: "editorialVisualContext", isSubmitting: "isSubmitting", formIsValid: "formIsValid", submitError: "submitError", invalidRequiredFieldLabels: "invalidRequiredFieldLabels", formId: "formId", actionOverrides: "actionOverrides" }, outputs: { action: "action" }, providers: [providePraxisI18nConfig(PRAXIS_DYNAMIC_FORM_I18N_CONFIG)], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"form-actions\"\n [ngClass]=\"viewClassMap\"\n [ngStyle]=\"actions?.containerStyles\"\n>\n @if (submitError) {\n <div class=\"alert alert--error\" role=\"alert\" aria-live=\"polite\">\n <mat-icon class=\"alert__icon\" aria-hidden=\"true\">error_outline</mat-icon>\n <div>\n <h4 class=\"alert__title\">Erro ao salvar</h4>\n <p class=\"alert__text\">{{ submitError }}</p>\n </div>\n </div>\n }\n @if (shouldShowInvalidSubmitHint()) {\n <div class=\"form-actions__hint\" role=\"status\" aria-live=\"polite\">\n <mat-icon class=\"form-actions__hint-icon\" aria-hidden=\"true\">info</mat-icon>\n <span>{{ getInvalidSubmitHint() }}</span>\n </div>\n }\n <div class=\"desktop-actions\">\n @if (isSplitLayout()) {\n <div class=\"desktop-actions-group secondary-group\">\n @for (button of getSecondaryButtons(); track button.id) {\n @if (button.visible) {\n @switch (button.variant || 'raised') {\n @case ('stroked') {\n <button mat-stroked-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('flat') {\n <button mat-flat-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('fab') {\n <button mat-fab\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @default {\n <button mat-raised-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n }\n }\n }\n </div>\n <div class=\"desktop-actions-group primary-group\">\n @for (button of getPrimaryButtons(); track button.id) {\n @if (button.visible) {\n @switch (button.variant || 'raised') {\n @case ('stroked') {\n <button mat-stroked-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('flat') {\n <button mat-flat-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('fab') {\n <button mat-fab\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @default {\n <button mat-raised-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n }\n }\n }\n </div>\n } @else {\n @for (button of getActionButtons(); track button.id) {\n @if (button.visible) {\n @switch (button.variant || 'raised') {\n @case ('stroked') {\n <button mat-stroked-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('flat') {\n <button mat-flat-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('fab') {\n <button mat-fab\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @default {\n <button mat-raised-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n }\n }\n }\n }\n </div>\n @if (actions?.mobile?.collapseToMenu) {\n <div class=\"mobile-actions\">\n @for (button of getVisibleButtons(); track button.id) {\n @switch (button.variant || 'raised') {\n @case ('stroked') {\n <button mat-stroked-button [type]=\"button.type || 'button'\" [color]=\"getMatColor(button)\" [disabled]=\"isActionButtonDisabled(button)\" [attr.aria-busy]=\"button.loading ? 'true' : null\" [matTooltip]=\"button.tooltip\" (click)=\"onActionButtonClick(button, $event)\" [attr.aria-label]=\"button.label\" [ngClass]=\"getButtonNgClass(button)\" [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('flat') {\n <button mat-flat-button [type]=\"button.type || 'button'\" [color]=\"getMatColor(button)\" [disabled]=\"isActionButtonDisabled(button)\" [attr.aria-busy]=\"button.loading ? 'true' : null\" [matTooltip]=\"button.tooltip\" (click)=\"onActionButtonClick(button, $event)\" [attr.aria-label]=\"button.label\" [ngClass]=\"getButtonNgClass(button)\" [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('fab') {\n <button mat-fab [type]=\"button.type || 'button'\" [color]=\"getMatColor(button)\" [disabled]=\"isActionButtonDisabled(button)\" [attr.aria-busy]=\"button.loading ? 'true' : null\" [matTooltip]=\"button.tooltip\" (click)=\"onActionButtonClick(button, $event)\" [attr.aria-label]=\"button.label\" [ngClass]=\"getButtonNgClass(button)\" [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon aria-hidden=\"true\">{{ button.icon }}</mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @default {\n <button mat-raised-button [type]=\"button.type || 'button'\" [color]=\"getMatColor(button)\" [disabled]=\"isActionButtonDisabled(button)\" [attr.aria-busy]=\"button.loading ? 'true' : null\" [matTooltip]=\"button.tooltip\" (click)=\"onActionButtonClick(button, $event)\" [attr.aria-label]=\"button.label\" [ngClass]=\"getButtonNgClass(button)\" [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon aria-hidden=\"true\">{{ button.icon }}</mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n }\n }\n @if (getCollapsedButtons().length > 0) {\n <button\n mat-icon-button\n [matMenuTriggerFor]=\"actionsMenu\"\n aria-label=\"More actions\"\n >\n <mat-icon>more_vert</mat-icon>\n </button>\n <mat-menu #actionsMenu=\"matMenu\">\n @for (button of getCollapsedButtons(); track button.id) {\n <button\n mat-menu-item\n (click)=\"onActionButtonClick(button, $event)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\"\n >\n @if (button.icon) { <mat-icon [praxisIcon]=\"button.icon\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n </mat-menu>\n }\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";.form-actions{display:flex;justify-content:flex-end;align-items:center;gap:.75rem;padding:1rem 1.1rem;z-index:1;font-family:inherit;border-radius:18px;background:var(--pfx-form-footer-surface, color-mix(in srgb, var(--md-sys-color-surface-container) 78%, var(--md-sys-color-surface-container-low) 22%));border:1px solid var(--pfx-form-footer-border, color-mix(in srgb, var(--md-sys-color-outline-variant) 76%, transparent))}.form-actions.with-divider{border-top:1px solid var(--pfx-form-footer-border, color-mix(in srgb, var(--md-sys-color-outline-variant) 76%, transparent))}.form-actions.editorial-visual-context{font-family:var(--editorial-body-font-family, inherit)}.form-actions.editorial-visual-context.with-divider{border-top:var(--editorial-card-border-width, 1px) solid color-mix(in srgb,var(--editorial-border-color, var(--md-sys-color-outline-variant)) 72%,transparent)}.form-actions.sticky{position:sticky;bottom:0}.form-actions.position-left{justify-content:flex-start}.form-actions.position-center{justify-content:center}.form-actions.position-right{justify-content:flex-end}.form-actions.position-justified,.form-actions.position-split{justify-content:space-between}.form-actions.orientation-vertical .desktop-actions{display:flex;flex-direction:column}.form-actions.orientation-horizontal .desktop-actions{display:flex;flex-direction:row}.form-actions.spacing-compact{gap:.25rem}.form-actions.spacing-normal{gap:.5rem}.form-actions.spacing-spacious{gap:1rem}.form-actions.spacing-compact .desktop-actions{gap:.25rem}.form-actions.spacing-normal .desktop-actions{gap:.5rem}.form-actions.spacing-spacious .desktop-actions{gap:1rem}.form-actions.loading{pointer-events:none;opacity:.7}.mobile-actions{display:none}.desktop-actions{display:flex;flex-wrap:wrap;gap:.5rem}.form-actions__hint{display:inline-flex;align-items:center;gap:.45rem;max-width:min(100%,32rem);padding:.5rem .65rem;border-radius:8px;font-size:.84rem;line-height:1.25;color:var(--md-sys-color-on-secondary-container);background:linear-gradient(135deg,color-mix(in srgb,var(--md-sys-color-secondary-container) 82%,transparent),color-mix(in srgb,var(--md-sys-color-tertiary-container) 44%,transparent));border:1px solid color-mix(in srgb,var(--md-sys-color-secondary) 22%,transparent)}.form-actions__hint-icon{flex:0 0 auto;font-size:18px;width:18px;height:18px;line-height:18px}.form-actions.position-split .desktop-actions{width:100%;justify-content:space-between;align-items:center}.desktop-actions-group{display:flex;flex-wrap:wrap;gap:.5rem;align-items:center}.desktop-actions-group.primary-group{justify-content:flex-end}.desktop-actions-group.secondary-group{opacity:.94}.form-actions .mdc-button{letter-spacing:.01em;border-radius:999px}.form-actions.editorial-visual-context .mdc-button{border-radius:var(--editorial-button-radius, 999px);font-family:var(--editorial-body-font-family, inherit)}.form-actions .mdc-button.size-small{min-height:36px;padding-inline:12px;font-size:.82rem}.form-actions .mdc-button.size-medium{min-height:44px;padding-inline:18px}.form-actions .mdc-button.size-large{min-height:50px;padding-inline:24px;font-size:.98rem}.form-actions .mat-mdc-raised-button,.form-actions .mat-mdc-unelevated-button{box-shadow:none}.form-actions .mat-mdc-raised-button:not([disabled]),.form-actions .mat-mdc-unelevated-button:not([disabled]){--mdc-protected-button-container-color: var(--md-sys-color-primary);--mdc-protected-button-label-text-color: var(--md-sys-color-on-primary);--mdc-filled-button-container-color: var(--md-sys-color-primary);--mdc-filled-button-label-text-color: var(--md-sys-color-on-primary)}.form-actions .mat-mdc-raised-button[disabled],.form-actions .mat-mdc-unelevated-button[disabled]{opacity:.55}.form-actions .mat-mdc-outlined-button,.form-actions .mat-mdc-button-base.mat-mdc-outlined-button{background:transparent}.form-actions.editorial-visual-context .mat-mdc-raised-button,.form-actions.editorial-visual-context .mat-mdc-unelevated-button{--mdc-protected-button-container-color: var(--editorial-cta-primary, var(--md-sys-color-primary));--mdc-protected-button-label-text-color: var(--editorial-cta-primary-text, var(--md-sys-color-on-primary));--mdc-filled-button-container-color: var(--editorial-cta-primary, var(--md-sys-color-primary));--mdc-filled-button-label-text-color: var(--editorial-cta-primary-text, var(--md-sys-color-on-primary));box-shadow:var(--editorial-floating-shadow, var(--editorial-card-shadow, none))}.form-actions.editorial-visual-context .mat-mdc-outlined-button{--mdc-outlined-button-outline-color: var(--editorial-border-color, var(--md-sys-color-outline-variant));--mdc-outlined-button-label-text-color: var( --editorial-cta-secondary-text, var(--editorial-text-primary, var(--md-sys-color-on-surface)) );background:color-mix(in srgb,var(--editorial-cta-secondary, var(--editorial-surface-secondary, var(--md-sys-color-surface-container-low))) 72%,transparent)}.form-actions.editorial-visual-context .mat-mdc-outlined-button.tone-primary,.form-actions.editorial-visual-context .mat-mdc-outlined-button.tone-accent{--mdc-outlined-button-label-text-color: var(--editorial-cta-primary, var(--md-sys-color-primary));background:transparent}.form-actions button .mat-icon{font-size:20px;width:20px;height:20px;line-height:1;display:inline-flex;align-items:center;justify-content:center}.form-actions button .mat-icon+span{margin-left:8px}@media(max-width:768px){.form-actions{padding:.9rem;border-radius:16px}.form-actions.mobile-menu-active .desktop-actions{display:none}.form-actions.mobile-menu-active .mobile-actions{display:flex;align-items:center;gap:.5rem}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { 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: "component", type: i4.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { 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: MatMenuModule }, { kind: "component", type: i6.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i6.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i6.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }] });
653
890
  }
654
891
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisFormActionsComponent, decorators: [{
655
892
  type: Component,
@@ -660,7 +897,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
660
897
  MatTooltipModule,
661
898
  MatMenuModule,
662
899
  PraxisIconDirective,
663
- ], template: "<div\n class=\"form-actions\"\n [ngClass]=\"viewClassMap\"\n [ngStyle]=\"actions?.containerStyles\"\n>\n @if (submitError) {\n <div class=\"alert alert--error\" role=\"alert\" aria-live=\"polite\">\n <mat-icon class=\"alert__icon\" aria-hidden=\"true\">error_outline</mat-icon>\n <div>\n <h4 class=\"alert__title\">Erro ao salvar</h4>\n <p class=\"alert__text\">{{ submitError }}</p>\n </div>\n </div>\n }\n <div class=\"desktop-actions\">\n @if (isSplitLayout()) {\n <div class=\"desktop-actions-group secondary-group\">\n @for (button of getSecondaryButtons(); track button.id) {\n @if (button.visible) {\n @switch (button.variant || 'raised') {\n @case ('stroked') {\n <button mat-stroked-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('flat') {\n <button mat-flat-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('fab') {\n <button mat-fab\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @default {\n <button mat-raised-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n }\n }\n }\n </div>\n <div class=\"desktop-actions-group primary-group\">\n @for (button of getPrimaryButtons(); track button.id) {\n @if (button.visible) {\n @switch (button.variant || 'raised') {\n @case ('stroked') {\n <button mat-stroked-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('flat') {\n <button mat-flat-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('fab') {\n <button mat-fab\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @default {\n <button mat-raised-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n }\n }\n }\n </div>\n } @else {\n @for (button of getActionButtons(); track button.id) {\n @if (button.visible) {\n @switch (button.variant || 'raised') {\n @case ('stroked') {\n <button mat-stroked-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('flat') {\n <button mat-flat-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('fab') {\n <button mat-fab\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @default {\n <button mat-raised-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n }\n }\n }\n }\n </div>\n @if (actions?.mobile?.collapseToMenu) {\n <div class=\"mobile-actions\">\n @for (button of getVisibleButtons(); track button.id) {\n @switch (button.variant || 'raised') {\n @case ('stroked') {\n <button mat-stroked-button [type]=\"button.type || 'button'\" [color]=\"getMatColor(button)\" [disabled]=\"isActionButtonDisabled(button)\" [attr.aria-busy]=\"button.loading ? 'true' : null\" [matTooltip]=\"button.tooltip\" (click)=\"onActionButtonClick(button, $event)\" [attr.aria-label]=\"button.label\" [ngClass]=\"getButtonNgClass(button)\" [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('flat') {\n <button mat-flat-button [type]=\"button.type || 'button'\" [color]=\"getMatColor(button)\" [disabled]=\"isActionButtonDisabled(button)\" [attr.aria-busy]=\"button.loading ? 'true' : null\" [matTooltip]=\"button.tooltip\" (click)=\"onActionButtonClick(button, $event)\" [attr.aria-label]=\"button.label\" [ngClass]=\"getButtonNgClass(button)\" [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('fab') {\n <button mat-fab [type]=\"button.type || 'button'\" [color]=\"getMatColor(button)\" [disabled]=\"isActionButtonDisabled(button)\" [attr.aria-busy]=\"button.loading ? 'true' : null\" [matTooltip]=\"button.tooltip\" (click)=\"onActionButtonClick(button, $event)\" [attr.aria-label]=\"button.label\" [ngClass]=\"getButtonNgClass(button)\" [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon aria-hidden=\"true\">{{ button.icon }}</mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @default {\n <button mat-raised-button [type]=\"button.type || 'button'\" [color]=\"getMatColor(button)\" [disabled]=\"isActionButtonDisabled(button)\" [attr.aria-busy]=\"button.loading ? 'true' : null\" [matTooltip]=\"button.tooltip\" (click)=\"onActionButtonClick(button, $event)\" [attr.aria-label]=\"button.label\" [ngClass]=\"getButtonNgClass(button)\" [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon aria-hidden=\"true\">{{ button.icon }}</mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n }\n }\n @if (getCollapsedButtons().length > 0) {\n <button\n mat-icon-button\n [matMenuTriggerFor]=\"actionsMenu\"\n aria-label=\"More actions\"\n >\n <mat-icon>more_vert</mat-icon>\n </button>\n <mat-menu #actionsMenu=\"matMenu\">\n @for (button of getCollapsedButtons(); track button.id) {\n <button\n mat-menu-item\n (click)=\"onActionButtonClick(button, $event)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\"\n >\n @if (button.icon) { <mat-icon [praxisIcon]=\"button.icon\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n </mat-menu>\n }\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";.form-actions{display:flex;justify-content:flex-end;align-items:center;gap:.75rem;padding:1rem 1.1rem;z-index:1;font-family:inherit;border-radius:18px;background:var(--pfx-form-footer-surface, color-mix(in srgb, var(--md-sys-color-surface-container) 78%, var(--md-sys-color-surface-container-low) 22%));border:1px solid var(--pfx-form-footer-border, color-mix(in srgb, var(--md-sys-color-outline-variant) 76%, transparent))}.form-actions.with-divider{border-top:1px solid var(--pfx-form-footer-border, color-mix(in srgb, var(--md-sys-color-outline-variant) 76%, transparent))}.form-actions.editorial-visual-context{font-family:var(--editorial-body-font-family, inherit)}.form-actions.editorial-visual-context.with-divider{border-top:var(--editorial-card-border-width, 1px) solid color-mix(in srgb,var(--editorial-border-color, var(--md-sys-color-outline-variant)) 72%,transparent)}.form-actions.sticky{position:sticky;bottom:0}.form-actions.position-left{justify-content:flex-start}.form-actions.position-center{justify-content:center}.form-actions.position-right{justify-content:flex-end}.form-actions.position-justified,.form-actions.position-split{justify-content:space-between}.form-actions.orientation-vertical .desktop-actions{display:flex;flex-direction:column}.form-actions.orientation-horizontal .desktop-actions{display:flex;flex-direction:row}.form-actions.spacing-compact{gap:.25rem}.form-actions.spacing-normal{gap:.5rem}.form-actions.spacing-spacious{gap:1rem}.form-actions.spacing-compact .desktop-actions{gap:.25rem}.form-actions.spacing-normal .desktop-actions{gap:.5rem}.form-actions.spacing-spacious .desktop-actions{gap:1rem}.form-actions.loading{pointer-events:none;opacity:.7}.mobile-actions{display:none}.desktop-actions{display:flex;flex-wrap:wrap;gap:.5rem}.form-actions.position-split .desktop-actions{width:100%;justify-content:space-between;align-items:center}.desktop-actions-group{display:flex;flex-wrap:wrap;gap:.5rem;align-items:center}.desktop-actions-group.primary-group{justify-content:flex-end}.desktop-actions-group.secondary-group{opacity:.94}.form-actions .mdc-button{letter-spacing:.01em;border-radius:999px}.form-actions.editorial-visual-context .mdc-button{border-radius:var(--editorial-button-radius, 999px);font-family:var(--editorial-body-font-family, inherit)}.form-actions .mdc-button.size-small{min-height:36px;padding-inline:12px;font-size:.82rem}.form-actions .mdc-button.size-medium{min-height:44px;padding-inline:18px}.form-actions .mdc-button.size-large{min-height:50px;padding-inline:24px;font-size:.98rem}.form-actions .mat-mdc-raised-button,.form-actions .mat-mdc-unelevated-button{box-shadow:none}.form-actions .mat-mdc-raised-button:not([disabled]),.form-actions .mat-mdc-unelevated-button:not([disabled]){--mdc-protected-button-container-color: var(--md-sys-color-primary);--mdc-protected-button-label-text-color: var(--md-sys-color-on-primary);--mdc-filled-button-container-color: var(--md-sys-color-primary);--mdc-filled-button-label-text-color: var(--md-sys-color-on-primary)}.form-actions .mat-mdc-raised-button[disabled],.form-actions .mat-mdc-unelevated-button[disabled]{opacity:.55}.form-actions .mat-mdc-outlined-button,.form-actions .mat-mdc-button-base.mat-mdc-outlined-button{background:transparent}.form-actions.editorial-visual-context .mat-mdc-raised-button,.form-actions.editorial-visual-context .mat-mdc-unelevated-button{--mdc-protected-button-container-color: var(--editorial-cta-primary, var(--md-sys-color-primary));--mdc-protected-button-label-text-color: var(--editorial-cta-primary-text, var(--md-sys-color-on-primary));--mdc-filled-button-container-color: var(--editorial-cta-primary, var(--md-sys-color-primary));--mdc-filled-button-label-text-color: var(--editorial-cta-primary-text, var(--md-sys-color-on-primary));box-shadow:var(--editorial-floating-shadow, var(--editorial-card-shadow, none))}.form-actions.editorial-visual-context .mat-mdc-outlined-button{--mdc-outlined-button-outline-color: var(--editorial-border-color, var(--md-sys-color-outline-variant));--mdc-outlined-button-label-text-color: var( --editorial-cta-secondary-text, var(--editorial-text-primary, var(--md-sys-color-on-surface)) );background:color-mix(in srgb,var(--editorial-cta-secondary, var(--editorial-surface-secondary, var(--md-sys-color-surface-container-low))) 72%,transparent)}.form-actions.editorial-visual-context .mat-mdc-outlined-button.tone-primary,.form-actions.editorial-visual-context .mat-mdc-outlined-button.tone-accent{--mdc-outlined-button-label-text-color: var(--editorial-cta-primary, var(--md-sys-color-primary));background:transparent}.form-actions button .mat-icon{font-size:20px;width:20px;height:20px;line-height:1;display:inline-flex;align-items:center;justify-content:center}.form-actions button .mat-icon+span{margin-left:8px}@media(max-width:768px){.form-actions{padding:.9rem;border-radius:16px}.form-actions.mobile-menu-active .desktop-actions{display:none}.form-actions.mobile-menu-active .mobile-actions{display:flex;align-items:center;gap:.5rem}}\n"] }]
900
+ ], providers: [providePraxisI18nConfig(PRAXIS_DYNAMIC_FORM_I18N_CONFIG)], template: "<div\n class=\"form-actions\"\n [ngClass]=\"viewClassMap\"\n [ngStyle]=\"actions?.containerStyles\"\n>\n @if (submitError) {\n <div class=\"alert alert--error\" role=\"alert\" aria-live=\"polite\">\n <mat-icon class=\"alert__icon\" aria-hidden=\"true\">error_outline</mat-icon>\n <div>\n <h4 class=\"alert__title\">Erro ao salvar</h4>\n <p class=\"alert__text\">{{ submitError }}</p>\n </div>\n </div>\n }\n @if (shouldShowInvalidSubmitHint()) {\n <div class=\"form-actions__hint\" role=\"status\" aria-live=\"polite\">\n <mat-icon class=\"form-actions__hint-icon\" aria-hidden=\"true\">info</mat-icon>\n <span>{{ getInvalidSubmitHint() }}</span>\n </div>\n }\n <div class=\"desktop-actions\">\n @if (isSplitLayout()) {\n <div class=\"desktop-actions-group secondary-group\">\n @for (button of getSecondaryButtons(); track button.id) {\n @if (button.visible) {\n @switch (button.variant || 'raised') {\n @case ('stroked') {\n <button mat-stroked-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('flat') {\n <button mat-flat-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('fab') {\n <button mat-fab\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @default {\n <button mat-raised-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n }\n }\n }\n </div>\n <div class=\"desktop-actions-group primary-group\">\n @for (button of getPrimaryButtons(); track button.id) {\n @if (button.visible) {\n @switch (button.variant || 'raised') {\n @case ('stroked') {\n <button mat-stroked-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('flat') {\n <button mat-flat-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('fab') {\n <button mat-fab\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @default {\n <button mat-raised-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n }\n }\n }\n </div>\n } @else {\n @for (button of getActionButtons(); track button.id) {\n @if (button.visible) {\n @switch (button.variant || 'raised') {\n @case ('stroked') {\n <button mat-stroked-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('flat') {\n <button mat-flat-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('fab') {\n <button mat-fab\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @default {\n <button mat-raised-button\n [type]=\"button.type || 'button'\"\n [color]=\"getMatColor(button)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [attr.aria-busy]=\"button.loading ? 'true' : null\"\n [matTooltip]=\"button.tooltip\"\n (click)=\"onActionButtonClick(button, $event)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n }\n }\n }\n }\n </div>\n @if (actions?.mobile?.collapseToMenu) {\n <div class=\"mobile-actions\">\n @for (button of getVisibleButtons(); track button.id) {\n @switch (button.variant || 'raised') {\n @case ('stroked') {\n <button mat-stroked-button [type]=\"button.type || 'button'\" [color]=\"getMatColor(button)\" [disabled]=\"isActionButtonDisabled(button)\" [attr.aria-busy]=\"button.loading ? 'true' : null\" [matTooltip]=\"button.tooltip\" (click)=\"onActionButtonClick(button, $event)\" [attr.aria-label]=\"button.label\" [ngClass]=\"getButtonNgClass(button)\" [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('flat') {\n <button mat-flat-button [type]=\"button.type || 'button'\" [color]=\"getMatColor(button)\" [disabled]=\"isActionButtonDisabled(button)\" [attr.aria-busy]=\"button.loading ? 'true' : null\" [matTooltip]=\"button.tooltip\" (click)=\"onActionButtonClick(button, $event)\" [attr.aria-label]=\"button.label\" [ngClass]=\"getButtonNgClass(button)\" [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon [praxisIcon]=\"button.icon\" aria-hidden=\"true\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @case ('fab') {\n <button mat-fab [type]=\"button.type || 'button'\" [color]=\"getMatColor(button)\" [disabled]=\"isActionButtonDisabled(button)\" [attr.aria-busy]=\"button.loading ? 'true' : null\" [matTooltip]=\"button.tooltip\" (click)=\"onActionButtonClick(button, $event)\" [attr.aria-label]=\"button.label\" [ngClass]=\"getButtonNgClass(button)\" [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon aria-hidden=\"true\">{{ button.icon }}</mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n @default {\n <button mat-raised-button [type]=\"button.type || 'button'\" [color]=\"getMatColor(button)\" [disabled]=\"isActionButtonDisabled(button)\" [attr.aria-busy]=\"button.loading ? 'true' : null\" [matTooltip]=\"button.tooltip\" (click)=\"onActionButtonClick(button, $event)\" [attr.aria-label]=\"button.label\" [ngClass]=\"getButtonNgClass(button)\" [ngStyle]=\"getButtonStyles(button)\">\n @if (button.loading) { <mat-icon class=\"loading-icon\" aria-hidden=\"true\">autorenew</mat-icon> }\n @if (button.icon && !button.loading) { <mat-icon aria-hidden=\"true\">{{ button.icon }}</mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n }\n }\n @if (getCollapsedButtons().length > 0) {\n <button\n mat-icon-button\n [matMenuTriggerFor]=\"actionsMenu\"\n aria-label=\"More actions\"\n >\n <mat-icon>more_vert</mat-icon>\n </button>\n <mat-menu #actionsMenu=\"matMenu\">\n @for (button of getCollapsedButtons(); track button.id) {\n <button\n mat-menu-item\n (click)=\"onActionButtonClick(button, $event)\"\n [disabled]=\"isActionButtonDisabled(button)\"\n [ngClass]=\"getButtonNgClass(button)\"\n [ngStyle]=\"getButtonStyles(button)\"\n >\n @if (button.icon) { <mat-icon [praxisIcon]=\"button.icon\"></mat-icon> }\n <span>{{ button.label }}</span>\n </button>\n }\n </mat-menu>\n }\n </div>\n }\n</div>\n", styles: ["@charset \"UTF-8\";.form-actions{display:flex;justify-content:flex-end;align-items:center;gap:.75rem;padding:1rem 1.1rem;z-index:1;font-family:inherit;border-radius:18px;background:var(--pfx-form-footer-surface, color-mix(in srgb, var(--md-sys-color-surface-container) 78%, var(--md-sys-color-surface-container-low) 22%));border:1px solid var(--pfx-form-footer-border, color-mix(in srgb, var(--md-sys-color-outline-variant) 76%, transparent))}.form-actions.with-divider{border-top:1px solid var(--pfx-form-footer-border, color-mix(in srgb, var(--md-sys-color-outline-variant) 76%, transparent))}.form-actions.editorial-visual-context{font-family:var(--editorial-body-font-family, inherit)}.form-actions.editorial-visual-context.with-divider{border-top:var(--editorial-card-border-width, 1px) solid color-mix(in srgb,var(--editorial-border-color, var(--md-sys-color-outline-variant)) 72%,transparent)}.form-actions.sticky{position:sticky;bottom:0}.form-actions.position-left{justify-content:flex-start}.form-actions.position-center{justify-content:center}.form-actions.position-right{justify-content:flex-end}.form-actions.position-justified,.form-actions.position-split{justify-content:space-between}.form-actions.orientation-vertical .desktop-actions{display:flex;flex-direction:column}.form-actions.orientation-horizontal .desktop-actions{display:flex;flex-direction:row}.form-actions.spacing-compact{gap:.25rem}.form-actions.spacing-normal{gap:.5rem}.form-actions.spacing-spacious{gap:1rem}.form-actions.spacing-compact .desktop-actions{gap:.25rem}.form-actions.spacing-normal .desktop-actions{gap:.5rem}.form-actions.spacing-spacious .desktop-actions{gap:1rem}.form-actions.loading{pointer-events:none;opacity:.7}.mobile-actions{display:none}.desktop-actions{display:flex;flex-wrap:wrap;gap:.5rem}.form-actions__hint{display:inline-flex;align-items:center;gap:.45rem;max-width:min(100%,32rem);padding:.5rem .65rem;border-radius:8px;font-size:.84rem;line-height:1.25;color:var(--md-sys-color-on-secondary-container);background:linear-gradient(135deg,color-mix(in srgb,var(--md-sys-color-secondary-container) 82%,transparent),color-mix(in srgb,var(--md-sys-color-tertiary-container) 44%,transparent));border:1px solid color-mix(in srgb,var(--md-sys-color-secondary) 22%,transparent)}.form-actions__hint-icon{flex:0 0 auto;font-size:18px;width:18px;height:18px;line-height:18px}.form-actions.position-split .desktop-actions{width:100%;justify-content:space-between;align-items:center}.desktop-actions-group{display:flex;flex-wrap:wrap;gap:.5rem;align-items:center}.desktop-actions-group.primary-group{justify-content:flex-end}.desktop-actions-group.secondary-group{opacity:.94}.form-actions .mdc-button{letter-spacing:.01em;border-radius:999px}.form-actions.editorial-visual-context .mdc-button{border-radius:var(--editorial-button-radius, 999px);font-family:var(--editorial-body-font-family, inherit)}.form-actions .mdc-button.size-small{min-height:36px;padding-inline:12px;font-size:.82rem}.form-actions .mdc-button.size-medium{min-height:44px;padding-inline:18px}.form-actions .mdc-button.size-large{min-height:50px;padding-inline:24px;font-size:.98rem}.form-actions .mat-mdc-raised-button,.form-actions .mat-mdc-unelevated-button{box-shadow:none}.form-actions .mat-mdc-raised-button:not([disabled]),.form-actions .mat-mdc-unelevated-button:not([disabled]){--mdc-protected-button-container-color: var(--md-sys-color-primary);--mdc-protected-button-label-text-color: var(--md-sys-color-on-primary);--mdc-filled-button-container-color: var(--md-sys-color-primary);--mdc-filled-button-label-text-color: var(--md-sys-color-on-primary)}.form-actions .mat-mdc-raised-button[disabled],.form-actions .mat-mdc-unelevated-button[disabled]{opacity:.55}.form-actions .mat-mdc-outlined-button,.form-actions .mat-mdc-button-base.mat-mdc-outlined-button{background:transparent}.form-actions.editorial-visual-context .mat-mdc-raised-button,.form-actions.editorial-visual-context .mat-mdc-unelevated-button{--mdc-protected-button-container-color: var(--editorial-cta-primary, var(--md-sys-color-primary));--mdc-protected-button-label-text-color: var(--editorial-cta-primary-text, var(--md-sys-color-on-primary));--mdc-filled-button-container-color: var(--editorial-cta-primary, var(--md-sys-color-primary));--mdc-filled-button-label-text-color: var(--editorial-cta-primary-text, var(--md-sys-color-on-primary));box-shadow:var(--editorial-floating-shadow, var(--editorial-card-shadow, none))}.form-actions.editorial-visual-context .mat-mdc-outlined-button{--mdc-outlined-button-outline-color: var(--editorial-border-color, var(--md-sys-color-outline-variant));--mdc-outlined-button-label-text-color: var( --editorial-cta-secondary-text, var(--editorial-text-primary, var(--md-sys-color-on-surface)) );background:color-mix(in srgb,var(--editorial-cta-secondary, var(--editorial-surface-secondary, var(--md-sys-color-surface-container-low))) 72%,transparent)}.form-actions.editorial-visual-context .mat-mdc-outlined-button.tone-primary,.form-actions.editorial-visual-context .mat-mdc-outlined-button.tone-accent{--mdc-outlined-button-label-text-color: var(--editorial-cta-primary, var(--md-sys-color-primary));background:transparent}.form-actions button .mat-icon{font-size:20px;width:20px;height:20px;line-height:1;display:inline-flex;align-items:center;justify-content:center}.form-actions button .mat-icon+span{margin-left:8px}@media(max-width:768px){.form-actions{padding:.9rem;border-radius:16px}.form-actions.mobile-menu-active .desktop-actions{display:none}.form-actions.mobile-menu-active .mobile-actions{display:flex;align-items:center;gap:.5rem}}\n"] }]
664
901
  }], ctorParameters: () => [{ type: i1.KeyboardShortcutService }], propDecorators: { actions: [{
665
902
  type: Input
666
903
  }], editorialVisualContext: [{
@@ -671,6 +908,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
671
908
  type: Input
672
909
  }], submitError: [{
673
910
  type: Input
911
+ }], invalidRequiredFieldLabels: [{
912
+ type: Input
674
913
  }], formId: [{
675
914
  type: Input
676
915
  }], actionOverrides: [{
@@ -3633,20 +3872,6 @@ function deepEqual(left, right) {
3633
3872
  JSON.stringify(stripUndefinedDeep(cloneJson(right)));
3634
3873
  }
3635
3874
 
3636
- const PRAXIS_DYNAMIC_FORM_I18N_NAMESPACE = 'praxisDynamicForm';
3637
- const PRAXIS_DYNAMIC_FORM_I18N_CONFIG = {
3638
- namespaces: {
3639
- [PRAXIS_DYNAMIC_FORM_I18N_NAMESPACE]: {
3640
- 'pt-BR': {
3641
- 'settings.title': 'Configuração do Formulário',
3642
- },
3643
- 'en-US': {
3644
- 'settings.title': 'Form Configuration',
3645
- },
3646
- },
3647
- },
3648
- };
3649
-
3650
3875
  class FormLayoutService {
3651
3876
  storage;
3652
3877
  constructor(storage) {
@@ -5052,6 +5277,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
5052
5277
  args: [{ providedIn: 'root' }]
5053
5278
  }] });
5054
5279
 
5280
+ const FORM_CONFIG_LOAD_TIMEOUT_MS = 1200;
5281
+ const FORM_INPUT_PREFS_LOAD_TIMEOUT_MS = 1200;
5282
+ const SCHEMA_GLOBAL_CONFIG_READY_TIMEOUT_MS = 1200;
5055
5283
  const PRAXIS_DYNAMIC_FORM_FALLBACK_LOGGER = new LoggerService(createCorporateLoggerConfig('dev'), [new ConsoleLoggerSink()]);
5056
5284
  class PraxisDynamicForm {
5057
5285
  crud;
@@ -5086,6 +5314,11 @@ class PraxisDynamicForm {
5086
5314
  i18n = inject(PraxisI18nService);
5087
5315
  DEBUG = typeof window !== 'undefined' &&
5088
5316
  Boolean(window['__PRAXIS_DEBUG__']);
5317
+ effectiveActionsCache;
5318
+ effectiveActionsCacheBase;
5319
+ effectiveActionsCacheOverride;
5320
+ invalidRequiredFieldLabelsCache = [];
5321
+ invalidRequiredFieldLabelsSignature = '';
5089
5322
  resourcePath;
5090
5323
  resourceId;
5091
5324
  initialValue;
@@ -5097,6 +5330,8 @@ class PraxisDynamicForm {
5097
5330
  editorialContext = null;
5098
5331
  mode = 'create';
5099
5332
  config = { sections: [] };
5333
+ /** Optional host-level overrides for the rendered form action bar. */
5334
+ actions;
5100
5335
  /**
5101
5336
  * Fonte de schema para construir o formulário.
5102
5337
  * 'resource' (padrão) usa o schema do recurso (/{resource}/schemas -> .../all response)
@@ -5177,6 +5412,47 @@ class PraxisDynamicForm {
5177
5412
  get effectiveFormIsValid() {
5178
5413
  return !this.entityHydrationPending && this.form.valid;
5179
5414
  }
5415
+ get invalidRequiredFieldLabels() {
5416
+ const signatureParts = [];
5417
+ const labels = [];
5418
+ const fieldMetadata = this.config?.fieldMetadata || [];
5419
+ for (const field of fieldMetadata) {
5420
+ if (!field?.name || this.fieldVisibility[field.name] === false) {
5421
+ continue;
5422
+ }
5423
+ const control = this.form.get(field.name);
5424
+ if (!control || !control.invalid) {
5425
+ continue;
5426
+ }
5427
+ const isRequired = control.hasError('required') ||
5428
+ control.hasError('requiredTrue') ||
5429
+ field.required === true ||
5430
+ field.validators?.required === true ||
5431
+ field.requiredTrue === true ||
5432
+ field.validators?.requiredTrue === true;
5433
+ signatureParts.push([
5434
+ field.name,
5435
+ control.status,
5436
+ isRequired ? 'required' : 'optional',
5437
+ control.touched ? 'touched' : 'untouched',
5438
+ control.dirty ? 'dirty' : 'pristine',
5439
+ String(control.value ?? ''),
5440
+ ].join(':'));
5441
+ if (isRequired) {
5442
+ labels.push(this.resolveFieldLabel(field));
5443
+ }
5444
+ if (labels.length >= 3) {
5445
+ break;
5446
+ }
5447
+ }
5448
+ const signature = signatureParts.join('|');
5449
+ if (signature === this.invalidRequiredFieldLabelsSignature) {
5450
+ return this.invalidRequiredFieldLabelsCache;
5451
+ }
5452
+ this.invalidRequiredFieldLabelsSignature = signature;
5453
+ this.invalidRequiredFieldLabelsCache = labels;
5454
+ return this.invalidRequiredFieldLabelsCache;
5455
+ }
5180
5456
  /** Value propagated to DynamicFieldLoader; null allows per-field metadata */
5181
5457
  get presentationForLoader() {
5182
5458
  return this.mode === 'view'
@@ -5248,6 +5524,12 @@ class PraxisDynamicForm {
5248
5524
  currentDnD;
5249
5525
  // Cache to provide referential stability for [fields] binding (avoids re-renders)
5250
5526
  columnFieldsCache = new Map();
5527
+ rowClassesCache = new Map();
5528
+ rowStylesCache = new Map();
5529
+ sectionClassesCache = new Map();
5530
+ sectionStylesCache = new Map();
5531
+ columnClassesCache = new Map();
5532
+ columnStylesCache = new Map();
5251
5533
  // De-duplication guard for field metadata patches (avoid repeated apply/save/bridge)
5252
5534
  lastFieldPatchSig = new Map();
5253
5535
  lastFieldPatchAt = new Map();
@@ -5290,7 +5572,42 @@ class PraxisDynamicForm {
5290
5572
  return (this.config?.formBlocksBeforeActionsPlacement || 'afterSections');
5291
5573
  }
5292
5574
  get actionPlacement() {
5293
- return (this.config.actions?.placement || 'afterSections');
5575
+ return (this.effectiveActions?.placement || 'afterSections');
5576
+ }
5577
+ get effectiveActions() {
5578
+ const base = this.config?.actions;
5579
+ const override = this.actions;
5580
+ if (!override) {
5581
+ this.effectiveActionsCache = undefined;
5582
+ this.effectiveActionsCacheBase = undefined;
5583
+ this.effectiveActionsCacheOverride = undefined;
5584
+ return base;
5585
+ }
5586
+ if (this.effectiveActionsCache !== undefined &&
5587
+ this.effectiveActionsCacheBase === base &&
5588
+ this.effectiveActionsCacheOverride === override) {
5589
+ return this.effectiveActionsCache;
5590
+ }
5591
+ this.effectiveActionsCache = {
5592
+ ...base,
5593
+ ...override,
5594
+ submit: {
5595
+ ...base?.submit,
5596
+ ...override.submit,
5597
+ },
5598
+ cancel: {
5599
+ ...base?.cancel,
5600
+ ...override.cancel,
5601
+ },
5602
+ reset: {
5603
+ ...base?.reset,
5604
+ ...override.reset,
5605
+ },
5606
+ custom: override.custom ?? base?.custom,
5607
+ };
5608
+ this.effectiveActionsCacheBase = base;
5609
+ this.effectiveActionsCacheOverride = override;
5610
+ return this.effectiveActionsCache;
5294
5611
  }
5295
5612
  /**
5296
5613
  * Precedence for editorial rich-content interpolation:
@@ -5856,14 +6173,20 @@ class PraxisDynamicForm {
5856
6173
  return;
5857
6174
  if (this.isInitialized || this.isLoading)
5858
6175
  return;
5859
- if (this.formId && this.resourcePath) {
6176
+ if (this.formId && this.hasSchemaBackedRuntime()) {
5860
6177
  void this.initializeForm();
5861
6178
  return;
5862
6179
  }
5863
- if (!this.resourcePath && this.hasRenderableConfig()) {
6180
+ if (!this.hasSchemaBackedRuntime() && this.hasRenderableConfig()) {
5864
6181
  this.initializeStandaloneConfig();
5865
6182
  }
5866
6183
  }
6184
+ hasExplicitSchemaUrl() {
6185
+ return String(this.schemaUrl || '').trim().length > 0;
6186
+ }
6187
+ hasSchemaBackedRuntime() {
6188
+ return !!this.resourcePath || this.hasExplicitSchemaUrl();
6189
+ }
5867
6190
  hasRenderableConfig(config = this.config) {
5868
6191
  return ((config?.sections?.length ?? 0) > 0 ||
5869
6192
  (config?.fieldMetadata?.length ?? 0) > 0 ||
@@ -5891,7 +6214,7 @@ class PraxisDynamicForm {
5891
6214
  this.cdr.detectChanges();
5892
6215
  }
5893
6216
  reloadForModeChange() {
5894
- if (!this.inputsLoaded || !this.formId || !this.resourcePath)
6217
+ if (!this.inputsLoaded || !this.formId || !this.hasSchemaBackedRuntime())
5895
6218
  return;
5896
6219
  if (this.isLoading)
5897
6220
  return;
@@ -5928,6 +6251,21 @@ class PraxisDynamicForm {
5928
6251
  try {
5929
6252
  const inputsKey = this.formInputsKey();
5930
6253
  if (inputsKey) {
6254
+ let inputsLoadSettled = false;
6255
+ let inputsLoadTimeout = null;
6256
+ const finishInputsLoad = () => {
6257
+ if (inputsLoadSettled) {
6258
+ return;
6259
+ }
6260
+ inputsLoadSettled = true;
6261
+ if (inputsLoadTimeout) {
6262
+ clearTimeout(inputsLoadTimeout);
6263
+ inputsLoadTimeout = null;
6264
+ }
6265
+ this.inputsLoaded = true;
6266
+ this.tryInitializeForm();
6267
+ };
6268
+ inputsLoadTimeout = setTimeout(finishInputsLoad, FORM_INPUT_PREFS_LOAD_TIMEOUT_MS);
5931
6269
  this.asyncConfigStorage.loadConfig(inputsKey).pipe(take(1)).subscribe({
5932
6270
  next: (inputsPref) => {
5933
6271
  if (inputsPref && typeof inputsPref === 'object') {
@@ -5937,14 +6275,8 @@ class PraxisDynamicForm {
5937
6275
  }
5938
6276
  }
5939
6277
  },
5940
- error: () => {
5941
- this.inputsLoaded = true;
5942
- this.tryInitializeForm();
5943
- },
5944
- complete: () => {
5945
- this.inputsLoaded = true;
5946
- this.tryInitializeForm();
5947
- },
6278
+ error: finishInputsLoad,
6279
+ complete: finishInputsLoad,
5948
6280
  });
5949
6281
  }
5950
6282
  else {
@@ -5999,7 +6331,7 @@ class PraxisDynamicForm {
5999
6331
  });
6000
6332
  // Initialize form based on the new flow
6001
6333
  if (this.formId &&
6002
- this.resourcePath &&
6334
+ this.hasSchemaBackedRuntime() &&
6003
6335
  !this.isInitialized &&
6004
6336
  !this.isLoading) {
6005
6337
  this.tryInitializeForm();
@@ -6059,7 +6391,7 @@ class PraxisDynamicForm {
6059
6391
  this.debugLog('[PDF] ngOnChanges schemaUrl=', this.schemaUrl);
6060
6392
  this.schemaCache = null;
6061
6393
  this.lastSchemaMeta = undefined;
6062
- if (this.formId && this.resourcePath && !this.isLoading) {
6394
+ if (this.formId && this.hasSchemaBackedRuntime() && !this.isLoading) {
6063
6395
  this.retryInitialization();
6064
6396
  }
6065
6397
  }
@@ -6078,7 +6410,9 @@ class PraxisDynamicForm {
6078
6410
  || (this.config?.fieldMetadata?.length ?? 0) > 0;
6079
6411
  if (hasRenderableConfig) {
6080
6412
  // External config updates must rebuild even when a resourcePath exists.
6081
- shouldRebuildFromExternalConfig = !this.isLoading;
6413
+ const shouldDeferToSchemaInitialization = this.hasSchemaBackedRuntime() && !!this.formId && !this.isInitialized;
6414
+ shouldRebuildFromExternalConfig =
6415
+ !this.isLoading && !shouldDeferToSchemaInitialization;
6082
6416
  }
6083
6417
  }
6084
6418
  else {
@@ -6097,7 +6431,7 @@ class PraxisDynamicForm {
6097
6431
  this.initializationStatus = 'success';
6098
6432
  }
6099
6433
  // On external config changes, if we have base and resourcePath configured, verify in background
6100
- if (this.resourcePath &&
6434
+ if (this.hasSchemaBackedRuntime() &&
6101
6435
  (this.config?.sections?.length ?? 0) > 0 &&
6102
6436
  this.formId &&
6103
6437
  (externalConfigChanged || !!changes['presentationModeGlobal'])) {
@@ -6159,8 +6493,8 @@ class PraxisDynamicForm {
6159
6493
  return;
6160
6494
  }
6161
6495
  // Validação obrigatória para cenários corporativos
6162
- if (!this.resourcePath || !this.formId) {
6163
- const error = new Error(`Form initialization failed: ${!this.formId ? 'formId' : 'resourcePath'} is required for corporate form management`);
6496
+ if (!this.formId || !this.hasSchemaBackedRuntime()) {
6497
+ const error = new Error(`Form initialization failed: ${!this.formId ? 'formId' : 'resourcePath or schemaUrl'} is required for corporate form management`);
6164
6498
  this.handleInitializationError('config-load', error, {
6165
6499
  formId: this.formId,
6166
6500
  resourcePath: this.resourcePath,
@@ -6180,16 +6514,23 @@ class PraxisDynamicForm {
6180
6514
  this.warnMissingId();
6181
6515
  return;
6182
6516
  }
6183
- let localConfig = await firstValueFrom(this.asyncConfigStorage.loadConfig(configKey)).catch(() => null);
6517
+ let localConfig = await this.loadConfigFailOpen(configKey);
6184
6518
  this.debugLog('[PDF] initializeForm:flow', localConfig ? 'LOCAL_CONFIG' : 'SERVER_DEFAULT');
6185
6519
  if (localConfig) {
6186
- // Flow 1: Has local config - load it and verify-only with server (ETag)
6520
+ // Flow 1: Has local config - load it, then refresh
6521
+ // server-authoritative field semantics from the current schema.
6187
6522
  this.debugLog('🔄 Loading saved form configuration');
6188
- this.config = localConfig;
6523
+ this.config = normalizeFormConfig$1(localConfig);
6524
+ await this.reconcileLoadedConfigWithServerSchema();
6189
6525
  this.emitLoadingState('config', 'success', 'Configuração carregada.');
6190
- // Verify server schema version in background (do not apply body here)
6526
+ // Verify server schema version in background for outdated UX metadata.
6191
6527
  this.verifyServerSchemaVersion().catch(() => { });
6192
6528
  }
6529
+ else if (this.hasRenderableConfig()) {
6530
+ // Flow 2: Host-provided config - reconcile local layout/fields with the server schema.
6531
+ this.debugLog('Creating form configuration from host config and server schema');
6532
+ await this.createConfigFromHostConfig();
6533
+ }
6193
6534
  else {
6194
6535
  // Flow 2: No local config - create default from server
6195
6536
  this.debugLog('🆕 Creating new form configuration from server');
@@ -6217,7 +6558,7 @@ class PraxisDynamicForm {
6217
6558
  this.errorLog('[PDF] initializeForm:error', error);
6218
6559
  const key = this.formConfigKey();
6219
6560
  const hasLocalConfig = key
6220
- ? (await firstValueFrom(this.asyncConfigStorage.loadConfig(key)).catch(() => null)) !== null
6561
+ ? (await this.loadConfigFailOpen(key)) !== null
6221
6562
  : false;
6222
6563
  this.handleInitializationError('form-build', error, {
6223
6564
  formId: this.formId,
@@ -6258,6 +6599,17 @@ class PraxisDynamicForm {
6258
6599
  }
6259
6600
  catch { }
6260
6601
  }
6602
+ loadConfigFailOpen(key) {
6603
+ return firstValueFrom(this.asyncConfigStorage
6604
+ .loadConfig(key)
6605
+ .pipe(timeout(FORM_CONFIG_LOAD_TIMEOUT_MS))).catch(() => null);
6606
+ }
6607
+ resolveFieldLabel(field) {
6608
+ const label = String(field?.label || '').trim();
6609
+ if (label)
6610
+ return label;
6611
+ return String(field.name || '').replace(/([a-z])([A-Z])/g, '$1 $2');
6612
+ }
6261
6613
  getErrorMessage(stage, error) {
6262
6614
  const messages = {
6263
6615
  'config-load': !this.formId
@@ -6330,6 +6682,65 @@ class PraxisDynamicForm {
6330
6682
  throw error; // Re-throw to stop initialization
6331
6683
  }
6332
6684
  }
6685
+ async createConfigFromHostConfig() {
6686
+ try {
6687
+ const serverDefs = await this.getSchemaWithCache();
6688
+ if (!serverDefs || serverDefs.length === 0) {
6689
+ throw new Error('No field metadata received from server');
6690
+ }
6691
+ const hostConfig = normalizeFormConfig$1(this.config);
6692
+ this.config = normalizeFormConfig$1(reconcileFormConfig(hostConfig, serverDefs));
6693
+ if (this.schemaRootHooks) {
6694
+ this.config.hooks = this.schemaRootHooks;
6695
+ }
6696
+ const configKey = this.formConfigKey();
6697
+ if (!this.config.metadata)
6698
+ this.config.metadata = {};
6699
+ if (this.lastSchemaMeta) {
6700
+ this.config.metadata.schemaId = this.lastSchemaMeta.schemaId;
6701
+ this.config.metadata.serverHash = this.lastSchemaMeta.serverHash;
6702
+ this.config.metadata.schemaContext = this.lastSchemaMeta.context;
6703
+ }
6704
+ this.persistFormConfig(configKey, this.config);
6705
+ this.columnFieldsCache.clear();
6706
+ }
6707
+ catch (error) {
6708
+ this.handleInitializationError('schema-fetch', error, {
6709
+ formId: this.formId,
6710
+ resourcePath: this.resourcePath,
6711
+ hasLocalConfig: false,
6712
+ });
6713
+ throw error;
6714
+ }
6715
+ }
6716
+ async reconcileLoadedConfigWithServerSchema() {
6717
+ try {
6718
+ const serverDefs = await this.getSchemaWithCache();
6719
+ if (!serverDefs || serverDefs.length === 0) {
6720
+ return;
6721
+ }
6722
+ const before = JSON.stringify(this.config);
6723
+ this.config = normalizeFormConfig$1(reconcileFormConfig(this.config, serverDefs));
6724
+ if (this.schemaRootHooks) {
6725
+ this.config.hooks = this.schemaRootHooks;
6726
+ }
6727
+ if (!this.config.metadata)
6728
+ this.config.metadata = {};
6729
+ if (this.lastSchemaMeta) {
6730
+ this.config.metadata.schemaId = this.lastSchemaMeta.schemaId;
6731
+ this.config.metadata.serverHash = this.lastSchemaMeta.serverHash;
6732
+ this.config.metadata.schemaContext = this.lastSchemaMeta.context;
6733
+ }
6734
+ const after = JSON.stringify(this.config);
6735
+ if (before !== after) {
6736
+ this.persistFormConfig(this.formConfigKey(), this.config);
6737
+ this.columnFieldsCache.clear();
6738
+ }
6739
+ }
6740
+ catch (error) {
6741
+ this.warnLog('[PraxisDynamicForm] Failed to reconcile saved form configuration with server schema', error);
6742
+ }
6743
+ }
6333
6744
  async syncWithServer() {
6334
6745
  // Deprecated by verify-only flow; keeping for manual reconcile paths
6335
6746
  try {
@@ -6439,7 +6850,7 @@ class PraxisDynamicForm {
6439
6850
  this.restoreFormRuntimeState(previousState, options);
6440
6851
  // Inicializar visibilidade padrão por campo
6441
6852
  for (const field of fieldMetadata) {
6442
- this.fieldVisibility[field.name] = true;
6853
+ this.fieldVisibility[field.name] = this.resolveFieldVisibility(field);
6443
6854
  }
6444
6855
  // Contexto de regras
6445
6856
  this.contextService.setAvailableFields(fieldMetadata);
@@ -6533,9 +6944,7 @@ class PraxisDynamicForm {
6533
6944
  if (!this.form || !Object.keys(this.form.controls || {}).length) {
6534
6945
  return null;
6535
6946
  }
6536
- const dirtyFields = Object.entries(this.form.controls)
6537
- .filter(([, control]) => control.dirty)
6538
- .map(([fieldName]) => fieldName);
6947
+ const dirtyFields = this.getDirtyFieldNames();
6539
6948
  const touchedFields = Object.entries(this.form.controls)
6540
6949
  .filter(([, control]) => control.touched)
6541
6950
  .map(([fieldName]) => fieldName);
@@ -6546,6 +6955,39 @@ class PraxisDynamicForm {
6546
6955
  context: this.buildCurrentFormRuntimeContext(),
6547
6956
  };
6548
6957
  }
6958
+ getDirtyFieldNames() {
6959
+ const dirtyFields = new Set();
6960
+ for (const [fieldName, control] of Object.entries(this.form.controls)) {
6961
+ if (control.dirty) {
6962
+ dirtyFields.add(fieldName);
6963
+ }
6964
+ this.collectDirtyFieldNames(control, fieldName, dirtyFields);
6965
+ }
6966
+ return Array.from(dirtyFields);
6967
+ }
6968
+ collectDirtyFieldNames(control, path, dirtyFields) {
6969
+ const childControls = control.controls;
6970
+ if (!childControls) {
6971
+ return;
6972
+ }
6973
+ if (Array.isArray(childControls)) {
6974
+ childControls.forEach((childControl, index) => {
6975
+ const childPath = `${path}.${index}`;
6976
+ if (childControl.dirty) {
6977
+ dirtyFields.add(childPath);
6978
+ }
6979
+ this.collectDirtyFieldNames(childControl, childPath, dirtyFields);
6980
+ });
6981
+ return;
6982
+ }
6983
+ for (const [childName, childControl] of Object.entries(childControls)) {
6984
+ const childPath = `${path}.${childName}`;
6985
+ if (childControl.dirty) {
6986
+ dirtyFields.add(childPath);
6987
+ }
6988
+ this.collectDirtyFieldNames(childControl, childPath, dirtyFields);
6989
+ }
6990
+ }
6549
6991
  buildCurrentFormRuntimeContext() {
6550
6992
  const resourcePath = (this.resourcePath || '').trim();
6551
6993
  return {
@@ -6778,7 +7220,7 @@ class PraxisDynamicForm {
6778
7220
  const readonlyOverride = overrides.readonly !== undefined ? overrides.readonly : overrides.readOnly;
6779
7221
  const disabledOverride = overrides.disabled;
6780
7222
  const meta = fieldByName.get(field.name);
6781
- const isVisible = this.resolveRuleVisibility(overrides);
7223
+ const isVisible = this.resolveFieldVisibility(meta || field, overrides);
6782
7224
  this.fieldVisibility[field.name] = isVisible;
6783
7225
  const baseDisabled = meta?.disabled === true || meta?.readOnly === true;
6784
7226
  const shouldDisable = (disabledOverride !== undefined
@@ -6861,7 +7303,16 @@ class PraxisDynamicForm {
6861
7303
  if (!section)
6862
7304
  return true;
6863
7305
  const props = this.getSectionRuleProps(section);
6864
- return this.resolveRuleVisibility(props, true);
7306
+ if (!this.resolveRuleVisibility(props, true)) {
7307
+ return false;
7308
+ }
7309
+ if (this.enableCustomization) {
7310
+ return true;
7311
+ }
7312
+ if (Array.isArray(section.rows) && section.rows.length > 0) {
7313
+ return section.rows.some((row) => this.isRowVisible(row));
7314
+ }
7315
+ return true;
6865
7316
  }
6866
7317
  getSectionTitle(section) {
6867
7318
  const props = this.getSectionRuleProps(section);
@@ -6979,6 +7430,7 @@ class PraxisDynamicForm {
6979
7430
  const emitAction = () => {
6980
7431
  this.customAction.emit({
6981
7432
  actionId,
7433
+ ...(action.globalAction ? { globalAction: action.globalAction } : {}),
6982
7434
  formData: this.form.value,
6983
7435
  isValid: this.form.valid,
6984
7436
  source: 'section-header',
@@ -7012,7 +7464,10 @@ class PraxisDynamicForm {
7012
7464
  };
7013
7465
  }
7014
7466
  getSectionHeaderActionEventId(action) {
7015
- return action.action?.trim() || action.id.trim();
7467
+ return (action.action?.trim() ||
7468
+ action.id.trim() ||
7469
+ action.globalAction?.actionId?.trim() ||
7470
+ '');
7016
7471
  }
7017
7472
  getSectionHeaderActionRuleKeys(section, action) {
7018
7473
  const logicalId = this.getSectionHeaderActionEventId(action);
@@ -7033,7 +7488,20 @@ class PraxisDynamicForm {
7033
7488
  if (!row)
7034
7489
  return true;
7035
7490
  const props = this.getRowRuleProps(row);
7036
- return this.resolveRuleVisibility(props, true);
7491
+ if (!this.resolveRuleVisibility(props, true)) {
7492
+ return false;
7493
+ }
7494
+ if (this.enableCustomization) {
7495
+ return true;
7496
+ }
7497
+ if (Array.isArray(row.columns) && row.columns.length > 0) {
7498
+ const columnsWithFields = row.columns.filter((column) => Array.isArray(column.fields) && column.fields.length > 0);
7499
+ if (columnsWithFields.length === 0) {
7500
+ return true;
7501
+ }
7502
+ return row.columns.some((column) => this.isColumnVisible(column));
7503
+ }
7504
+ return true;
7037
7505
  }
7038
7506
  getRowGap(row) {
7039
7507
  const props = this.getRowRuleProps(row);
@@ -7045,15 +7513,30 @@ class PraxisDynamicForm {
7045
7513
  }
7046
7514
  getRowClasses(row) {
7047
7515
  const props = this.getRowRuleProps(row);
7516
+ const key = row?.id || JSON.stringify(row?.columns?.map((column) => column.id || column.fields) || []);
7517
+ const signature = String(props.className || '');
7518
+ const cached = this.rowClassesCache.get(key);
7519
+ if (cached?.signature === signature) {
7520
+ return cached.value;
7521
+ }
7048
7522
  const classes = [];
7049
7523
  if (props.className)
7050
7524
  classes.push(props.className);
7525
+ this.rowClassesCache.set(key, { signature, value: classes });
7051
7526
  return classes;
7052
7527
  }
7053
7528
  getRowStyles(row) {
7054
7529
  const props = this.getRowRuleProps(row);
7530
+ const key = row?.id || JSON.stringify(row?.columns?.map((column) => column.id || column.fields) || []);
7531
+ const signature = JSON.stringify(props.style || {});
7532
+ const cached = this.rowStylesCache.get(key);
7533
+ if (cached?.signature === signature) {
7534
+ return cached.value;
7535
+ }
7055
7536
  const styles = { ...(props.style || {}) };
7056
- return Object.keys(styles).length ? styles : null;
7537
+ const value = Object.keys(styles).length ? styles : null;
7538
+ this.rowStylesCache.set(key, { signature, value });
7539
+ return value;
7057
7540
  }
7058
7541
  getSectionIcon(section) {
7059
7542
  const props = this.getSectionRuleProps(section);
@@ -7252,6 +7735,17 @@ class PraxisDynamicForm {
7252
7735
  return null;
7253
7736
  }
7254
7737
  getSectionClasses(section) {
7738
+ const key = section?.id || JSON.stringify(section?.rows?.map((row) => row.id) || []);
7739
+ const props = this.getSectionRuleProps(section);
7740
+ const signature = [
7741
+ this.getSectionAppearance(section) || '',
7742
+ section.className || '',
7743
+ props.className || '',
7744
+ ].join('|');
7745
+ const cached = this.sectionClassesCache.get(key);
7746
+ if (cached?.signature === signature) {
7747
+ return cached.value;
7748
+ }
7255
7749
  const classes = [];
7256
7750
  const appearance = this.getSectionAppearance(section);
7257
7751
  if (appearance)
@@ -7259,26 +7753,44 @@ class PraxisDynamicForm {
7259
7753
  const baseClass = section.className;
7260
7754
  if (baseClass)
7261
7755
  classes.push(baseClass);
7262
- const props = this.getSectionRuleProps(section);
7263
7756
  if (props.className)
7264
7757
  classes.push(props.className);
7758
+ this.sectionClassesCache.set(key, { signature, value: classes });
7265
7759
  return classes;
7266
7760
  }
7267
7761
  getSectionStyles(section) {
7268
7762
  const props = this.getSectionRuleProps(section);
7763
+ const key = section?.id || JSON.stringify(section?.rows?.map((row) => row.id) || []);
7764
+ const signature = JSON.stringify({
7765
+ styles: section?.styles || {},
7766
+ ruleStyle: props.style,
7767
+ borderColor: props.borderColor,
7768
+ borderStyle: props.borderStyle,
7769
+ borderWidth: props.borderWidth,
7770
+ appearance: this.getSectionAppearance(section) || '',
7771
+ editorial: this.isEditorialVisualContext(),
7772
+ });
7773
+ const cached = this.sectionStylesCache.get(key);
7774
+ if (cached?.signature === signature) {
7775
+ return cached.value;
7776
+ }
7269
7777
  const styles = { ...(section?.styles || {}) };
7270
7778
  if (props.style === null) {
7271
7779
  // null remove overrides e estilos herdados
7272
7780
  this.sanitizeSectionStylesForEditorialContext(section, styles);
7273
7781
  this.applyBorderPropsToStyle(styles, props);
7274
- return Object.keys(styles).length ? styles : null;
7782
+ const value = Object.keys(styles).length ? styles : null;
7783
+ this.sectionStylesCache.set(key, { signature, value });
7784
+ return value;
7275
7785
  }
7276
7786
  Object.entries(props.style || {}).forEach(([k, v]) => {
7277
7787
  styles[k] = v;
7278
7788
  });
7279
7789
  this.sanitizeSectionStylesForEditorialContext(section, styles);
7280
7790
  this.applyBorderPropsToStyle(styles, props);
7281
- return Object.keys(styles).length ? styles : null;
7791
+ const value = Object.keys(styles).length ? styles : null;
7792
+ this.sectionStylesCache.set(key, { signature, value });
7793
+ return value;
7282
7794
  }
7283
7795
  sanitizeSectionStylesForEditorialContext(section, styles) {
7284
7796
  if (!this.isEditorialVisualContext()) {
@@ -7343,6 +7855,19 @@ class PraxisDynamicForm {
7343
7855
  }
7344
7856
  return fallback;
7345
7857
  }
7858
+ resolveFieldVisibility(field, props) {
7859
+ const fallback = !this.isFieldMetadataHidden(field);
7860
+ return this.resolveRuleVisibility(props, fallback);
7861
+ }
7862
+ isFieldMetadataHidden(field) {
7863
+ if (!field || typeof field !== 'object') {
7864
+ return false;
7865
+ }
7866
+ if (field.visible === false) {
7867
+ return true;
7868
+ }
7869
+ return field.hidden === true;
7870
+ }
7346
7871
  toggleSectionCollapse(event, section) {
7347
7872
  event.stopPropagation();
7348
7873
  if (!this.isSectionCollapsible(section))
@@ -7369,30 +7894,10 @@ class PraxisDynamicForm {
7369
7894
  return `${rows.length} ${rowLabel}, ${fieldCount} ${fieldLabel}`;
7370
7895
  }
7371
7896
  getColumnFields(column) {
7372
- // Build a lightweight signature based on the ordered list of visible field names
7373
- // When the signature is unchanged, return the cached array reference to avoid
7374
- // triggering DynamicFieldLoader re-renders due to a new array instance.
7375
7897
  const key = column.id || JSON.stringify(column.fields);
7376
- const visibleNames = column.fields
7377
- .filter((name) => this.fieldVisibility[name] !== false)
7378
- .map((name) => {
7379
- const meta = (this.config.fieldMetadata || []).find((field) => field.name === name);
7380
- const overrides = this.fieldRuleProps[name] || {};
7381
- return `${name}:${JSON.stringify({
7382
- overrides,
7383
- meta: meta ? {
7384
- disabled: meta.disabled,
7385
- readonly: meta.readonly,
7386
- readOnly: meta.readOnly,
7387
- required: meta.required,
7388
- cssClass: meta.cssClass,
7389
- style: meta.style,
7390
- } : null,
7391
- })}`;
7392
- })
7393
- .join('|');
7898
+ const signature = this.getColumnFieldsSignature(column);
7394
7899
  const cached = this.columnFieldsCache.get(key);
7395
- if (cached && cached.signature === visibleNames) {
7900
+ if (cached?.signature === signature) {
7396
7901
  return cached.value;
7397
7902
  }
7398
7903
  const fieldMetadata = this.config.fieldMetadata || [];
@@ -7421,9 +7926,19 @@ class PraxisDynamicForm {
7421
7926
  }
7422
7927
  }
7423
7928
  }
7424
- this.columnFieldsCache.set(key, { signature: visibleNames, value: ordered });
7929
+ this.columnFieldsCache.set(key, { signature, value: ordered });
7425
7930
  return ordered;
7426
7931
  }
7932
+ getColumnFieldsSignature(column) {
7933
+ return column.fields
7934
+ .map((name) => {
7935
+ const visible = this.fieldVisibility[name] === false ? '0' : '1';
7936
+ const overrides = this.fieldRuleProps[name];
7937
+ const overrideState = overrides && Object.keys(overrides).length > 0 ? '1' : '0';
7938
+ return `${name}:${visible}:${overrideState}`;
7939
+ })
7940
+ .join('|');
7941
+ }
7427
7942
  getColumnRuleProps(column) {
7428
7943
  if (!column || !column.id)
7429
7944
  return {};
@@ -7537,6 +8052,19 @@ class PraxisDynamicForm {
7537
8052
  }
7538
8053
  getColumnClasses(column) {
7539
8054
  const effective = this.getEffectiveColumn(column);
8055
+ const key = column?.id || JSON.stringify(column?.fields || []);
8056
+ const signature = JSON.stringify({
8057
+ span: effective.span,
8058
+ offset: effective.offset,
8059
+ order: effective.order,
8060
+ hidden: effective.hidden,
8061
+ align: effective.align,
8062
+ className: effective.className,
8063
+ });
8064
+ const cached = this.columnClassesCache.get(key);
8065
+ if (cached?.signature === signature) {
8066
+ return cached.value;
8067
+ }
7540
8068
  const classes = [
7541
8069
  ...this.getColumnSpanClasses(effective),
7542
8070
  ...this.getColumnOffsetClasses(effective),
@@ -7549,6 +8077,7 @@ class PraxisDynamicForm {
7549
8077
  if (effective.className) {
7550
8078
  classes.push(effective.className);
7551
8079
  }
8080
+ this.columnClassesCache.set(key, { signature, value: classes });
7552
8081
  return classes;
7553
8082
  }
7554
8083
  getColumnSpanClasses(column) {
@@ -7640,8 +8169,16 @@ class PraxisDynamicForm {
7640
8169
  }
7641
8170
  getColumnStyles(column) {
7642
8171
  const props = this.getColumnRuleProps(column);
8172
+ const key = column?.id || JSON.stringify(column?.fields || []);
8173
+ const signature = JSON.stringify(props.style || {});
8174
+ const cached = this.columnStylesCache.get(key);
8175
+ if (cached?.signature === signature) {
8176
+ return cached.value;
8177
+ }
7643
8178
  const styles = { ...(props.style || {}) };
7644
- return Object.keys(styles).length ? styles : null;
8179
+ const value = Object.keys(styles).length ? styles : null;
8180
+ this.columnStylesCache.set(key, { signature, value });
8181
+ return value;
7645
8182
  }
7646
8183
  _getConfirmationMessage(actionId) {
7647
8184
  return this._getConfirmationMessageForAliases(actionId);
@@ -7702,7 +8239,7 @@ class PraxisDynamicForm {
7702
8239
  }
7703
8240
  }
7704
8241
  _executeAction(button) {
7705
- const actionId = button.id || button.action;
8242
+ const actionId = this.getFormActionEventId(button);
7706
8243
  if (!actionId)
7707
8244
  return;
7708
8245
  switch (actionId) {
@@ -7738,7 +8275,8 @@ class PraxisDynamicForm {
7738
8275
  default:
7739
8276
  // This is a custom action
7740
8277
  this.customAction.emit({
7741
- actionId: actionId,
8278
+ actionId,
8279
+ ...(button.globalAction ? { globalAction: button.globalAction } : {}),
7742
8280
  formData: this.form.value,
7743
8281
  isValid: this.form.valid,
7744
8282
  source: 'button',
@@ -7746,6 +8284,15 @@ class PraxisDynamicForm {
7746
8284
  break;
7747
8285
  }
7748
8286
  }
8287
+ getFormActionEventId(button) {
8288
+ const id = button.id?.trim();
8289
+ if (id === 'submit' || id === 'cancel' || id === 'reset')
8290
+ return id;
8291
+ return (button.action?.trim() ||
8292
+ id ||
8293
+ button.globalAction?.actionId?.trim() ||
8294
+ undefined);
8295
+ }
7749
8296
  async onSubmit() {
7750
8297
  if (this.submitting)
7751
8298
  return;
@@ -7822,11 +8369,15 @@ class PraxisDynamicForm {
7822
8369
  }
7823
8370
  catch { }
7824
8371
  // Snapshot after hooks potentially changed control values
7825
- const formData = normalizeSubmitPayload(this.form.getRawValue());
8372
+ const rawFormData = this.form.getRawValue();
8373
+ const formData = prepareSubmitPayload(rawFormData, this.config.fieldMetadata, {
8374
+ dirtyFields: this.getDirtyFieldNames(),
8375
+ });
7826
8376
  const operation = this.resolveSubmitOperation();
7827
8377
  this.formSubmit.emit({
7828
8378
  stage: 'before',
7829
8379
  formData,
8380
+ rawFormData,
7830
8381
  isValid: true,
7831
8382
  operation,
7832
8383
  entityId: this.resourceId ?? undefined,
@@ -7861,6 +8412,7 @@ class PraxisDynamicForm {
7861
8412
  this.formSubmit.emit({
7862
8413
  stage: 'after',
7863
8414
  formData,
8415
+ rawFormData,
7864
8416
  isValid: true,
7865
8417
  operation,
7866
8418
  entityId: this.resourceId ?? undefined,
@@ -7883,7 +8435,7 @@ class PraxisDynamicForm {
7883
8435
  catch { }
7884
8436
  // Hooks: afterSubmit
7885
8437
  try {
7886
- (async () => await this.runHooks('afterSubmit', { result, formData, operation }))();
8438
+ (async () => await this.runHooks('afterSubmit', { result, formData, rawFormData, operation }))();
7887
8439
  }
7888
8440
  catch { }
7889
8441
  // Comportamentos pós-salvar
@@ -7905,6 +8457,7 @@ class PraxisDynamicForm {
7905
8457
  this.formSubmit.emit({
7906
8458
  stage: 'error',
7907
8459
  formData,
8460
+ rawFormData,
7908
8461
  isValid: false,
7909
8462
  operation,
7910
8463
  entityId: this.resourceId ?? undefined,
@@ -7912,7 +8465,7 @@ class PraxisDynamicForm {
7912
8465
  });
7913
8466
  // Hook: onError (submit)
7914
8467
  try {
7915
- (async () => await this.runHooks('onError', { error, formData, operation }))();
8468
+ (async () => await this.runHooks('onError', { error, formData, rawFormData, operation }))();
7916
8469
  }
7917
8470
  catch { }
7918
8471
  // Mensagens de erro configuráveis
@@ -8616,6 +9169,7 @@ class PraxisDynamicForm {
8616
9169
  materialDesign: fieldMeta.materialDesign,
8617
9170
  clearButton: fieldMeta.clearButton,
8618
9171
  extra: fieldMeta.extra,
9172
+ optionSource: fieldMeta.optionSource,
8619
9173
  controlType,
8620
9174
  };
8621
9175
  // Seed específicos para NUMBER (numericTextBox)
@@ -9377,6 +9931,22 @@ class PraxisDynamicForm {
9377
9931
  });
9378
9932
  return out;
9379
9933
  }
9934
+ mergeMetadataObjectPatch(current, patch) {
9935
+ if (!patch || typeof patch !== 'object' || Array.isArray(patch)) {
9936
+ return patch;
9937
+ }
9938
+ const base = current && typeof current === 'object' && !Array.isArray(current)
9939
+ ? { ...current }
9940
+ : {};
9941
+ Object.entries(patch).forEach(([key, value]) => {
9942
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
9943
+ base[key] = this.mergeMetadataObjectPatch(base[key], value);
9944
+ return;
9945
+ }
9946
+ base[key] = value;
9947
+ });
9948
+ return base;
9949
+ }
9380
9950
  deleteMetadataPath(target, path) {
9381
9951
  if (!target || typeof path !== 'string' || !path.trim())
9382
9952
  return;
@@ -9568,6 +10138,9 @@ class PraxisDynamicForm {
9568
10138
  fm.dataAttributes = patch.dataAttributes;
9569
10139
  }
9570
10140
  }
10141
+ if (patch.optionSource !== undefined) {
10142
+ fm.optionSource = this.mergeMetadataObjectPatch(fm.optionSource, patch.optionSource);
10143
+ }
9571
10144
  // Generic extras passthrough: merge into fm.extra to allow component-specific appearance/behavior
9572
10145
  if (patch.extra !== undefined) {
9573
10146
  const currExtra = (fm.extra && typeof fm.extra === 'object') ? fm.extra : {};
@@ -11492,10 +12065,7 @@ class PraxisDynamicForm {
11492
12065
  this.debugLog('[PDF] schema:fetching', { url, mode: ctx.schemaType });
11493
12066
  }
11494
12067
  catch { }
11495
- try {
11496
- await this.global.ready();
11497
- }
11498
- catch { }
12068
+ await this.waitForGlobalConfigReadyForSchema();
11499
12069
  const disableCache = this.global.get('cache.disableSchemaCache');
11500
12070
  const adapter = disableCache
11501
12071
  ? new MemoryCacheAdapter()
@@ -11598,6 +12168,18 @@ class PraxisDynamicForm {
11598
12168
  const fromCfg = this.config?.behavior?.reactiveValidationDebounceMs;
11599
12169
  return typeof fromCfg === 'number' ? fromCfg : 150;
11600
12170
  }
12171
+ async waitForGlobalConfigReadyForSchema() {
12172
+ try {
12173
+ await Promise.race([
12174
+ this.global.ready(),
12175
+ new Promise((resolve) => setTimeout(resolve, SCHEMA_GLOBAL_CONFIG_READY_TIMEOUT_MS)),
12176
+ ]);
12177
+ }
12178
+ catch {
12179
+ // Schema loading is fail-open: global preferences may refine cache
12180
+ // behavior, but they must not block the canonical schema request.
12181
+ }
12182
+ }
11601
12183
  setupReactiveValidation() {
11602
12184
  if (!this.isReactiveValidationEnabled()) {
11603
12185
  this.reactiveValidate$ = undefined;
@@ -11700,7 +12282,7 @@ class PraxisDynamicForm {
11700
12282
  }
11701
12283
  }
11702
12284
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisDynamicForm, deps: [{ token: i1$2.GenericCrudService }, { token: i2.HttpClient }, { token: i1$3.FormBuilder }, { token: i0.ChangeDetectorRef }, { token: FormLayoutService }, { token: FormContextService }, { token: FormRulesService }, { token: i7.SettingsPanelService }, { token: i2$1.MatDialog }, { token: ASYNC_CONFIG_STORAGE }, { token: CONNECTION_STORAGE }, { token: i1$2.DynamicFormService }, { token: i9$1.MatSnackBar }, { token: CanvasStateService }, { token: DynamicFormLayoutService }, { token: i1$2.ErrorMessageService }, { token: i1$2.SchemaNormalizerService }, { token: i1$2.ComponentMetadataRegistry }, { token: i1$2.GlobalConfigService }, { token: i1$2.ComponentKeyService }, { token: i1$2.LoadingOrchestrator }, { token: i0.NgZone }, { token: API_URL }, { token: PRAXIS_LOADING_RENDERER, optional: true }, { token: i12.Router, optional: true }, { token: i12.ActivatedRoute, optional: true }, { token: i1$2.FormHooksRegistry, optional: true }, { token: FORM_HOOKS_PRESETS, optional: true }, { token: i1$2.LoggerService, optional: true }], target: i0.ɵɵFactoryTarget.Component });
11703
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PraxisDynamicForm, isStandalone: true, selector: "praxis-dynamic-form", inputs: { resourcePath: "resourcePath", resourceId: "resourceId", initialValue: "initialValue", editorialContext: "editorialContext", mode: "mode", config: "config", schemaSource: "schemaSource", schemaUrl: "schemaUrl", submitUrl: "submitUrl", submitMethod: "submitMethod", responseSchemaUrl: "responseSchemaUrl", apiEndpointKey: "apiEndpointKey", apiUrlEntry: "apiUrlEntry", enableCustomization: "enableCustomization", formId: "formId", componentInstanceId: "componentInstanceId", layout: "layout", backConfig: "backConfig", hooks: "hooks", removeEmptyContainersOnSave: "removeEmptyContainersOnSave", reactiveValidation: "reactiveValidation", reactiveValidationDebounceMs: "reactiveValidationDebounceMs", notifyIfOutdated: "notifyIfOutdated", snoozeMs: "snoozeMs", autoOpenSettingsOnOutdated: "autoOpenSettingsOnOutdated", readonlyModeGlobal: "readonlyModeGlobal", disabledModeGlobal: "disabledModeGlobal", presentationModeGlobal: "presentationModeGlobal", visibleGlobal: "visibleGlobal", customEndpoints: "customEndpoints" }, outputs: { formSubmit: "formSubmit", formCancel: "formCancel", formReset: "formReset", configChange: "configChange", formReady: "formReady", valueChange: "valueChange", syncCompleted: "syncCompleted", initializationError: "initializationError", loadingStateChange: "loadingStateChange", enableCustomizationChange: "enableCustomizationChange", customAction: "customAction", actionConfirmation: "actionConfirmation", schemaStatusChange: "schemaStatusChange", fieldRenderError: "fieldRenderError" }, providers: [providePraxisI18nConfig(PRAXIS_DYNAMIC_FORM_I18N_CONFIG)], viewQueries: [{ propertyName: "formHost", first: true, predicate: ["formHost"], descendants: true }, { propertyName: "fieldLoaders", predicate: DynamicFieldLoaderDirective, descendants: true }], usesOnChanges: true, ngImport: i0, template: "@if (isLoading) {\n<!-- Loading State -->\n<div class=\"form-loading\">\n <mat-progress-spinner diameter=\"40\"></mat-progress-spinner>\n <p>Carregando formul\u00E1rio...</p>\n</div>\n} @else if (initializationStatus === 'error') {\n<!-- Error State -->\n<div class=\"form-error\">\n <mat-icon color=\"warn\" [praxisIcon]=\"'error'\"></mat-icon>\n <h3>{{ getErrorTitle() }}</h3>\n <p>{{ currentErrorMessage }}</p>\n @if (isRecoverable) {\n <button mat-stroked-button (click)=\"retryInitialization()\">\n <mat-icon [praxisIcon]=\"'refresh'\"></mat-icon>\n Tentar Novamente\n </button>\n }\n <button mat-button (click)=\"showDetailedError()\" class=\"show-details\">\n Ver Detalhes T\u00E9cnicos\n </button>\n <!-- Permitir corre\u00E7\u00E3o do resourcePath diretamente do estado de erro -->\n <button mat-flat-button color=\"primary\" (click)=\"openQuickConnect()\" class=\"connect-action\">\n <mat-icon [praxisIcon]=\"'bolt'\"></mat-icon>\n Conectar a recurso\n </button>\n</div>\n} @else if (initializationStatus === 'success') {\n<!-- Inline banner for schema change (only when customization is enabled) -->\n@if (shouldShowOutdatedInline()) {\n<div class=\"pfx-form-info-banner\" role=\"status\" aria-live=\"polite\">\n <div class=\"text\">O schema do servidor mudou. Reconciliar agora?</div>\n <div class=\"actions\">\n <button mat-stroked-button color=\"primary\" (click)=\"openConfigEditor()\">\n <mat-icon [praxisIcon]=\"'sync'\"></mat-icon>\n Reconciliar\n </button>\n <button mat-button (click)=\"onSnoozeOutdated()\">Lembrar depois</button>\n <button mat-button (click)=\"onIgnoreOutdated()\">Ignorar</button>\n </div>\n</div>\n}\n\n<!-- Configuration Controls -->\n@if (shouldShowConfigControls && enableCustomization) {\n<div class=\"form-config-controls\">\n <praxis-ai-assistant [adapter]=\"aiAdapter\"></praxis-ai-assistant>\n <button type=\"button\" mat-icon-button (click)=\"openConfigEditor()\" [disabled]=\"isLoading\" class=\"config-button\"\n [matBadge]=\"schemaOutdated ? '!' : ''\" [matBadgeHidden]=\"!schemaOutdated\" matBadgeColor=\"warn\" matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configurar formul\u00E1rio'\">\n <mat-icon [praxisIcon]=\"'settings'\"></mat-icon>\n </button>\n <button type=\"button\" mat-icon-button (click)=\"disconnect()\" matTooltip=\"Desconectar da fonte de dados\"\n [disabled]=\"isLoading\">\n <mat-icon [praxisIcon]=\"'link_off'\"></mat-icon>\n </button>\n</div>\n}\n\n<!-- Form Content -->\n@if (!resourcePath && (!config.sections || config.sections.length === 0)) {\n<praxis-empty-state-card icon=\"link\" [title]=\"'Conecte o formul\u00E1rio \u00E0 fonte de dados'\"\n [description]=\"'Informe a rota do recurso da API para gerar automaticamente os campos do formul\u00E1rio.'\"\n [primaryAction]=\"{ label: 'Conectar \u00E0 fonte de dados', icon: 'bolt', action: openQuickConnect.bind(this) }\"></praxis-empty-state-card>\n}\n<form #formHost (ngSubmit)=\"onSubmit()\" [attr.aria-busy]=\"submitting ? 'true' : null\"\n [attr.aria-label]=\"'Formul\u00E1rio ' + (config.metadata?.version || '')\" [class.canvas-mode-enabled]=\"enableCustomization\"\n [class.presentation-mode]=\"effectivePresentation\" [class.readonly-mode]=\"effectiveReadonly\"\n [class.editorial-visual-context]=\"hasEditorialVisualContext()\"\n [class.pfx-mounting]=\"isMounting\" [formGroup]=\"form\"\n [ngClass]=\"{\n 'pres-compact': presentationVars.compact,\n 'pres-label-left': presentationVars.labelPosition === 'left',\n 'pres-label-above': presentationVars.labelPosition === 'above'\n }\" [style.--pfx-pres-label-align]=\"presentationVars.labelAlign\"\n [style.--pfx-pres-label-size]=\"presentationVars.labelSize\"\n [style.--pfx-pres-label-width]=\"presentationVars.labelWidth\"\n [style.--pfx-pres-row-gap]=\"presentationVars.density === 'compact' ? '6px' : (presentationVars.density === 'cozy' ? '8px' : '10px')\"\n [style.--pfx-pres-row-padding]=\"presentationVars.density === 'compact' ? '2px 0' : (presentationVars.density === 'cozy' ? '6px 0' : '8px 0')\"\n [style.--pfx-pres-value-align]=\"presentationVars.valueAlign\"\n [style.--pfx-pres-value-size]=\"presentationVars.valueSize\"\n [style.--pdx-form-mount-duration]=\"getMountDurationVar()\"\n [style.--pdx-form-mount-offset]=\"getMountOffsetVar()\"\n [style.--pdx-form-mount-stagger]=\"getMountStaggerVar()\"\n class=\"praxis-dynamic-form\">\n <praxis-canvas-toolbar (editMetadata)=\"openSelectedElementEditor()\" (selectPath)=\"onSelectPath($event)\"\n (moveUp)=\"onToolbarMove('up')\" (moveDown)=\"onToolbarMove('down')\" (toggleReadonly)=\"onToolbarToggleReadonly()\"\n (toggleRequired)=\"onToolbarToggleRequired()\" (toggleHidden)=\"onToolbarToggleHidden()\"\n (toggleDisabled)=\"onToolbarToggleDisabled()\" (requestClose)=\"onToolbarRequestClose()\"\n *ngIf=\"enableCustomization && selectedElement\" [selectedElement]=\"selectedElement\"\n [style.transform]=\"toolbarTransform\"></praxis-canvas-toolbar>\n\n @if (formBlocksBefore?.nodes?.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before\" data-editorial-placement=\"before\">\n <praxis-rich-content\n [document]=\"formBlocksBeforeDocument\"\n [context]=\"getEditorialRichContentContext()\"\n ></praxis-rich-content>\n </section>\n }\n\n @if (actionPlacement === 'top' && !(effectivePresentation || effectiveReadonly)) {\n <praxis-form-actions data-actions-placement=\"top\" [actions]=\"config.actions\" [isSubmitting]=\"submitting\"\n [formIsValid]=\"effectiveFormIsValid\" [submitError]=\"submitError\" [formId]=\"formId\" [actionOverrides]=\"actionRuleProps\"\n [editorialVisualContext]=\"hasEditorialVisualContext()\"\n (action)=\"onFormAction($event)\"></praxis-form-actions>\n }\n\n @for (section of config.sections; track (section.id ?? $index); let sectionIndex = $index;\n let last = $last) {\n <div class=\"section-drop-wrapper\" [attr.data-section-id]=\"section.id\">\n @if (isSectionVisible(section)) {\n <div #sectionEl class=\"form-section canvas-element\" data-canvas-type=\"section\" [attr.data-section-id]=\"section.id\"\n [attr.data-section-index]=\"sectionIndex\" (mouseenter)=\"onElementMouseEnter($event, 'section', section, sectionEl)\"\n (mouseleave)=\"onElementMouseLeave($event)\" (click)=\"onElementClick($event, 'section', section, sectionEl)\"\n [class.selected]=\"selectedElement?.domElement === sectionEl\"\n [class.hovered]=\"hoveredElement?.domElement === sectionEl && selectedElement?.domElement !== sectionEl\"\n [attr.data-section-appearance]=\"getSectionAppearance(section) || null\"\n [style.marginBottom.px]=\"!last ? getSectionGapBottom(section) : null\" [ngClass]=\"getSectionClasses(section)\"\n [ngStyle]=\"getSectionStyles(section)\">\n <div\n class=\"section-heading\"\n [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\"\n [class.step-appearance]=\"getSectionAppearance(section) === 'step'\"\n >\n <div class=\"section-heading-text\" [matTooltip]=\"getSectionHeaderTooltip(section) || null\"\n [matTooltipDisabled]=\"!getSectionHeaderTooltip(section)\">\n @if (getSectionStepLabel(section)) {\n <div class=\"section-step-label\" [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\">\n {{ getSectionStepLabel(section) }}\n </div>\n }\n @if (getSectionTitle(section)) {\n <h3 class=\"section-title\" [class.title-large]=\"getSectionTitleStyle(section) === 'titleLarge'\"\n [class.title-medium]=\"getSectionTitleStyle(section) === 'titleMedium'\"\n [class.title-small]=\"getSectionTitleStyle(section) === 'titleSmall'\"\n [class.headline-small]=\"getSectionTitleStyle(section) === 'headlineSmall'\"\n [style.marginBottom.px]=\"getSectionTitleGapBottom(section) ?? 20\" [style.color]=\"getSectionTitleColor(section) || null\"\n [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\"\n [attr.id]=\"sectionPanelId(section, sectionIndex) + '-title'\">\n @let sectionHeaderVisual = getSectionHeaderVisual(section);\n @let sectionHeaderAvatarSize = getSectionHeaderAvatarSize(section);\n @if (sectionHeaderVisual.kind === 'icon') {\n <mat-icon class=\"section-title-icon\" aria-hidden=\"true\" [praxisIcon]=\"sectionHeaderVisual.icon\"></mat-icon>\n }\n @if (sectionHeaderVisual.kind === 'image') {\n <img class=\"section-title-avatar section-title-avatar-image\" [class.size-sm]=\"sectionHeaderAvatarSize === 'sm'\"\n [class.size-md]=\"sectionHeaderAvatarSize === 'md'\" [class.size-lg]=\"sectionHeaderAvatarSize === 'lg'\"\n [src]=\"sectionHeaderVisual.src\" [alt]=\"sectionHeaderVisual.alt\" />\n }\n @if (sectionHeaderVisual.kind === 'initials') {\n <span class=\"section-title-avatar section-title-avatar-text\" [class.size-sm]=\"sectionHeaderAvatarSize === 'sm'\"\n [class.size-md]=\"sectionHeaderAvatarSize === 'md'\" [class.size-lg]=\"sectionHeaderAvatarSize === 'lg'\" aria-hidden=\"true\">\n {{ sectionHeaderVisual.text }}\n </span>\n @if (sectionHeaderVisual.ariaLabel) {\n <span class=\"section-title-avatar-sr-only\">{{ sectionHeaderVisual.ariaLabel }}</span>\n }\n }\n @if (sectionHeaderVisual.kind === 'placeholder') {\n <span class=\"section-title-avatar section-title-avatar-placeholder\" [class.size-sm]=\"sectionHeaderAvatarSize === 'sm'\"\n [class.size-md]=\"sectionHeaderAvatarSize === 'md'\" [class.size-lg]=\"sectionHeaderAvatarSize === 'lg'\" aria-hidden=\"true\">\n @if (sectionHeaderVisual.icon) {\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"sectionHeaderVisual.icon\"></mat-icon>\n }\n @if (sectionHeaderVisual.text) {\n <span>{{ sectionHeaderVisual.text }}</span>\n }\n </span>\n @if (sectionHeaderVisual.ariaLabel) {\n <span class=\"section-title-avatar-sr-only\">{{ sectionHeaderVisual.ariaLabel }}</span>\n }\n }\n {{ getSectionTitle(section) }}\n @if (enableCustomization) {\n <button type=\"button\" class=\"inline-edit-btn\" mat-icon-button (click)=\"openSectionEditor(section, 'title')\"\n aria-label=\"Editar t\u00EDtulo da se\u00E7\u00E3o\" matTooltip=\"Editar t\u00EDtulo\">\n <mat-icon>edit</mat-icon>\n </button>\n }\n </h3>\n }\n @if (getSectionDescription(section)) {\n <p class=\"section-description\" [class.body-large]=\"getSectionDescriptionStyle(section) === 'bodyLarge'\"\n [class.body-medium]=\"getSectionDescriptionStyle(section) === 'bodyMedium'\"\n [class.body-small]=\"getSectionDescriptionStyle(section) === 'bodySmall'\"\n [style.marginBottom.px]=\"getSectionDescriptionGapBottom(section) ?? 8\" [style.color]=\"getSectionDescriptionColor(section) || null\"\n [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\">\n {{ getSectionDescription(section) }}\n @if (enableCustomization) {\n <button type=\"button\" class=\"inline-edit-btn\" mat-icon-button\n (click)=\"openSectionEditor(section, 'description')\" aria-label=\"Editar descri\u00E7\u00E3o da se\u00E7\u00E3o\"\n matTooltip=\"Editar descri\u00E7\u00E3o\">\n <mat-icon>edit</mat-icon>\n </button>\n }\n </p>\n }\n </div>\n @if (getSectionHeaderActions(section).length) {\n <div class=\"section-heading-actions\" [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\">\n @for (action of getSectionHeaderActions(section); track action.id) {\n <button\n type=\"button\"\n class=\"section-heading-action-btn\"\n mat-icon-button\n [color]=\"getSectionHeaderActionColor(action)\"\n [disabled]=\"isSectionHeaderActionDisabled(action)\"\n [matTooltip]=\"getSectionHeaderActionTooltip(action)\"\n [attr.aria-label]=\"action.label\"\n [attr.aria-busy]=\"action.loading ? 'true' : null\"\n [ngClass]=\"getSectionHeaderActionNgClass(action)\"\n [ngStyle]=\"getSectionHeaderActionStyles(action)\"\n (click)=\"onSectionHeaderActionClick(section, action, $event)\"\n >\n @if (action.loading) {\n <mat-progress-spinner diameter=\"16\" mode=\"indeterminate\"></mat-progress-spinner>\n <span class=\"section-heading-action-sr-only\">{{ getSectionHeaderActionLoadingLabel(action) }}</span>\n } @else {\n <mat-icon [praxisIcon]=\"action.icon\"></mat-icon>\n }\n </button>\n }\n </div>\n }\n @if (isSectionCollapsible(section)) {\n <button type=\"button\" class=\"section-collapse-btn\" mat-icon-button\n (click)=\"toggleSectionCollapse($event, section)\" [attr.aria-expanded]=\"!isSectionCollapsed(section)\"\n [attr.aria-controls]=\"sectionPanelId(section, sectionIndex)\"\n [attr.aria-label]=\"isSectionCollapsed(section) ? 'Expandir se\u00E7\u00E3o' : 'Recolher se\u00E7\u00E3o'\">\n <mat-icon [praxisIcon]=\"isSectionCollapsed(section) ? 'expand_more' : 'expand_less'\"></mat-icon>\n </button>\n }\n </div>\n\n <div class=\"section-body\" [class.collapsed]=\"isSectionCollapsed(section)\"\n [attr.id]=\"sectionPanelId(section, sectionIndex)\"\n [attr.aria-labelledby]=\"getSectionTitle(section) ? sectionPanelId(section, sectionIndex) + '-title' : null\">\n @if (!isSectionCollapsed(section)) {\n @for (row of section.rows; track (row.id ?? $index); let rowIndex = $index) {\n @if (isRowVisible(row)) {\n <div class=\"row-drop-wrapper\" [attr.data-section-id]=\"section.id\">\n <div #rowEl class=\"form-row grid-12 canvas-element\" data-canvas-type=\"row\" [attr.data-row-index]=\"rowIndex\"\n [attr.data-section-id]=\"section.id\" [attr.data-row-id]=\"row.id\" [style.--pfx-grid-gap.px]=\"getRowGap(row)\"\n [style.--pfx-field-gap.px]=\"getRowRowGap(row)\"\n [style.--pfx-mount-index]=\"rowIndex\"\n [style.marginBottom.px]=\"rowIndex < section.rows.length - 1 ? (getRowRowGap(row) ?? null) : null\"\n [ngClass]=\"getRowClasses(row)\" [ngStyle]=\"getRowStyles(row)\"\n (mouseenter)=\"onElementMouseEnter($event, 'row', row, rowEl)\" (mouseleave)=\"onElementMouseLeave($event)\"\n (click)=\"onElementClick($event, 'row', row, rowEl)\" [class.selected]=\"selectedElement?.domElement === rowEl\"\n [class.hovered]=\"hoveredElement?.domElement === rowEl && selectedElement?.domElement !== rowEl\">\n @for (column of row.columns; track (column.id ?? $index); let colIndex = $index) {\n @if (isColumnVisible(column)) {\n <div class=\"column-drop-wrapper\" [attr.data-section-id]=\"section.id\" [attr.data-row-id]=\"row.id\">\n <div #colEl class=\"form-column canvas-element\" [ngClass]=\"getColumnClasses(column)\"\n [style.padding.px]=\"getColumnPadding(column)\" [style.--pfx-field-gap.px]=\"getRowRowGap(row)\"\n [ngStyle]=\"getColumnStyles(column)\" [attr.data-testid]=\"column.testId || null\"\n [attr.data-row-gap]=\"getRowRowGap(row)\" data-canvas-type=\"column\" [attr.data-column-index]=\"colIndex\"\n [attr.data-row-index]=\"rowIndex\" [attr.data-section-id]=\"section.id\" [attr.data-row-id]=\"row.id\"\n [attr.data-column-id]=\"column.id\" (mouseenter)=\"onElementMouseEnter($event, 'column', column, colEl)\"\n (mouseleave)=\"onElementMouseLeave($event)\" (click)=\"onElementClick($event, 'column', column, colEl)\"\n [class.selected]=\"selectedElement?.domElement === colEl\"\n [class.hovered]=\"hoveredElement?.domElement === colEl && selectedElement?.domElement !== colEl\">\n <ng-container dynamicFieldLoader [fields]=\"getColumnFields(column)\" [formGroup]=\"form\"\n [enableExternalControlBinding]=\"true\"\n [readonlyMode]=\"readonlyModeGlobal === null ? null : readonlyModeGlobal\"\n [disabledMode]=\"effectiveDisabledMode\"\n [presentationMode]=\"presentationForLoader\" [visible]=\"visibleGlobal === null ? null : visibleGlobal\"\n [canvasMode]=\"enableCustomization\" (canvasMouseEnter)=\"onFieldMouseEnter($event)\"\n (canvasMouseLeave)=\"onFieldMouseLeave($event)\" (canvasClick)=\"onFieldClick($event)\"\n (renderError)=\"onFieldRenderError($event)\">\n </ng-container>\n </div>\n </div>\n } }\n </div>\n </div>\n }\n }\n } @else {\n <div class=\"section-collapsed-placeholder\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'unfold_more'\"></mat-icon>\n <span>{{ getSectionCollapsedSummary(section) }}</span>\n </div>\n }\n @if (last && beforeActionsPlacement === 'insideLastSection' && formBlocksBeforeActions?.nodes?.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before-actions\" data-editorial-placement=\"beforeActions\">\n <praxis-rich-content\n [document]=\"formBlocksBeforeActionsDocument\"\n [context]=\"getEditorialRichContentContext()\"\n ></praxis-rich-content>\n </section>\n }\n @if (actionPlacement === 'insideLastSection' && last && !(effectivePresentation\n || effectiveReadonly)) {\n <praxis-form-actions data-actions-placement=\"insideLastSection\" [actions]=\"config.actions\"\n [isSubmitting]=\"submitting\" [formIsValid]=\"effectiveFormIsValid\" [submitError]=\"submitError\" [formId]=\"formId\"\n [actionOverrides]=\"actionRuleProps\" [editorialVisualContext]=\"hasEditorialVisualContext()\"\n (action)=\"onFormAction($event)\"></praxis-form-actions>\n }\n </div>\n <!-- Overlay de bloqueio durante submiss\u00E3o -->\n @if (submitting) {\n <div class=\"form-blocking-overlay\" aria-live=\"polite\">\n <mat-progress-spinner diameter=\"40\" color=\"primary\"></mat-progress-spinner>\n <p>{{ config.messages?.loading?.submit || 'Salvando...' }}</p>\n </div>\n }\n </div>\n\n @if (enableCustomization && selectedElement?.domElement === sectionEl) {\n <div class=\"add-section-container\">\n <div class=\"add-section-line\"></div>\n <button mat-fab color=\"primary\" aria-label=\"Adicionar nova se\u00E7\u00E3o\" (click)=\"addNewSectionAfter(sectionIndex)\"\n matTooltip=\"Adicionar nova se\u00E7\u00E3o aqui\">\n <mat-icon [praxisIcon]=\"'add'\"></mat-icon>\n </button>\n <div class=\"add-section-line\"></div>\n </div>\n }\n }\n </div>\n }\n\n @if (beforeActionsPlacement !== 'insideLastSection' && formBlocksBeforeActions?.nodes?.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before-actions\" data-editorial-placement=\"beforeActions\">\n <praxis-rich-content\n [document]=\"formBlocksBeforeActionsDocument\"\n [context]=\"getEditorialRichContentContext()\"\n ></praxis-rich-content>\n </section>\n }\n\n @if (actionPlacement === 'afterSections' && !(effectivePresentation || effectiveReadonly)) {\n <praxis-form-actions data-actions-placement=\"afterSections\" [actions]=\"config.actions\" [isSubmitting]=\"submitting\"\n [formIsValid]=\"effectiveFormIsValid\" [submitError]=\"submitError\" [formId]=\"formId\"\n [actionOverrides]=\"actionRuleProps\" [editorialVisualContext]=\"hasEditorialVisualContext()\"\n (action)=\"onFormAction($event)\"></praxis-form-actions>\n }\n\n @if (formBlocksAfter?.nodes?.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-after\" data-editorial-placement=\"after\">\n <praxis-rich-content\n [document]=\"formBlocksAfterDocument\"\n [context]=\"getEditorialRichContentContext()\"\n ></praxis-rich-content>\n </section>\n }\n</form>\n@if (!enableCustomization && mode === 'view') {\n<div class=\"ai-floating-assistant\">\n <praxis-ai-assistant [adapter]=\"aiAdapter\"></praxis-ai-assistant>\n</div>\n}\n}\n", styles: ["@charset \"UTF-8\";.span-xs-1{grid-column:span 1}.span-xs-2{grid-column:span 2}.span-xs-3{grid-column:span 3}.span-xs-4{grid-column:span 4}.span-xs-5{grid-column:span 5}.span-xs-6{grid-column:span 6}.span-xs-7{grid-column:span 7}.span-xs-8{grid-column:span 8}.span-xs-9{grid-column:span 9}.span-xs-10{grid-column:span 10}.span-xs-11{grid-column:span 11}.span-xs-12{grid-column:span 12}.offset-xs-0{margin-left:0%}.offset-xs-1{margin-left:calc(1 / 12 * 100%)}.offset-xs-2{margin-left:calc(2 / 12 * 100%)}.offset-xs-3{margin-left:25%}.offset-xs-4{margin-left:calc(4 / 12 * 100%)}.offset-xs-5{margin-left:calc(5 / 12 * 100%)}.offset-xs-6{margin-left:50%}.offset-xs-7{margin-left:calc(7 / 12 * 100%)}.offset-xs-8{margin-left:calc(8 / 12 * 100%)}.offset-xs-9{margin-left:75%}.offset-xs-10{margin-left:calc(10 / 12 * 100%)}.offset-xs-11{margin-left:calc(11 / 12 * 100%)}.order-xs--12{order:-12}.order-xs--11{order:-11}.order-xs--10{order:-10}.order-xs--9{order:-9}.order-xs--8{order:-8}.order-xs--7{order:-7}.order-xs--6{order:-6}.order-xs--5{order:-5}.order-xs--4{order:-4}.order-xs--3{order:-3}.order-xs--2{order:-2}.order-xs--1{order:-1}.order-xs-0{order:0}.order-xs-1{order:1}.order-xs-2{order:2}.order-xs-3{order:3}.order-xs-4{order:4}.order-xs-5{order:5}.order-xs-6{order:6}.order-xs-7{order:7}.order-xs-8{order:8}.order-xs-9{order:9}.order-xs-10{order:10}.order-xs-11{order:11}.order-xs-12{order:12}.hidden-xs{display:none}@media(min-width:600px){.span-sm-1{grid-column:span 1}.span-sm-2{grid-column:span 2}.span-sm-3{grid-column:span 3}.span-sm-4{grid-column:span 4}.span-sm-5{grid-column:span 5}.span-sm-6{grid-column:span 6}.span-sm-7{grid-column:span 7}.span-sm-8{grid-column:span 8}.span-sm-9{grid-column:span 9}.span-sm-10{grid-column:span 10}.span-sm-11{grid-column:span 11}.span-sm-12{grid-column:span 12}.offset-sm-0{margin-left:0%}.offset-sm-1{margin-left:calc(1 / 12 * 100%)}.offset-sm-2{margin-left:calc(2 / 12 * 100%)}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:calc(4 / 12 * 100%)}.offset-sm-5{margin-left:calc(5 / 12 * 100%)}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:calc(7 / 12 * 100%)}.offset-sm-8{margin-left:calc(8 / 12 * 100%)}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:calc(10 / 12 * 100%)}.offset-sm-11{margin-left:calc(11 / 12 * 100%)}.order-sm--12{order:-12}.order-sm--11{order:-11}.order-sm--10{order:-10}.order-sm--9{order:-9}.order-sm--8{order:-8}.order-sm--7{order:-7}.order-sm--6{order:-6}.order-sm--5{order:-5}.order-sm--4{order:-4}.order-sm--3{order:-3}.order-sm--2{order:-2}.order-sm--1{order:-1}.order-sm-0{order:0}.order-sm-1{order:1}.order-sm-2{order:2}.order-sm-3{order:3}.order-sm-4{order:4}.order-sm-5{order:5}.order-sm-6{order:6}.order-sm-7{order:7}.order-sm-8{order:8}.order-sm-9{order:9}.order-sm-10{order:10}.order-sm-11{order:11}.order-sm-12{order:12}.hidden-sm{display:none}}@media(min-width:900px){.span-md-1{grid-column:span 1}.span-md-2{grid-column:span 2}.span-md-3{grid-column:span 3}.span-md-4{grid-column:span 4}.span-md-5{grid-column:span 5}.span-md-6{grid-column:span 6}.span-md-7{grid-column:span 7}.span-md-8{grid-column:span 8}.span-md-9{grid-column:span 9}.span-md-10{grid-column:span 10}.span-md-11{grid-column:span 11}.span-md-12{grid-column:span 12}.offset-md-0{margin-left:0%}.offset-md-1{margin-left:calc(1 / 12 * 100%)}.offset-md-2{margin-left:calc(2 / 12 * 100%)}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:calc(4 / 12 * 100%)}.offset-md-5{margin-left:calc(5 / 12 * 100%)}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:calc(7 / 12 * 100%)}.offset-md-8{margin-left:calc(8 / 12 * 100%)}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:calc(10 / 12 * 100%)}.offset-md-11{margin-left:calc(11 / 12 * 100%)}.order-md--12{order:-12}.order-md--11{order:-11}.order-md--10{order:-10}.order-md--9{order:-9}.order-md--8{order:-8}.order-md--7{order:-7}.order-md--6{order:-6}.order-md--5{order:-5}.order-md--4{order:-4}.order-md--3{order:-3}.order-md--2{order:-2}.order-md--1{order:-1}.order-md-0{order:0}.order-md-1{order:1}.order-md-2{order:2}.order-md-3{order:3}.order-md-4{order:4}.order-md-5{order:5}.order-md-6{order:6}.order-md-7{order:7}.order-md-8{order:8}.order-md-9{order:9}.order-md-10{order:10}.order-md-11{order:11}.order-md-12{order:12}.hidden-md{display:none}}@media(min-width:1200px){.span-lg-1{grid-column:span 1}.span-lg-2{grid-column:span 2}.span-lg-3{grid-column:span 3}.span-lg-4{grid-column:span 4}.span-lg-5{grid-column:span 5}.span-lg-6{grid-column:span 6}.span-lg-7{grid-column:span 7}.span-lg-8{grid-column:span 8}.span-lg-9{grid-column:span 9}.span-lg-10{grid-column:span 10}.span-lg-11{grid-column:span 11}.span-lg-12{grid-column:span 12}.offset-lg-0{margin-left:0%}.offset-lg-1{margin-left:calc(1 / 12 * 100%)}.offset-lg-2{margin-left:calc(2 / 12 * 100%)}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:calc(4 / 12 * 100%)}.offset-lg-5{margin-left:calc(5 / 12 * 100%)}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:calc(7 / 12 * 100%)}.offset-lg-8{margin-left:calc(8 / 12 * 100%)}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:calc(10 / 12 * 100%)}.offset-lg-11{margin-left:calc(11 / 12 * 100%)}.order-lg--12{order:-12}.order-lg--11{order:-11}.order-lg--10{order:-10}.order-lg--9{order:-9}.order-lg--8{order:-8}.order-lg--7{order:-7}.order-lg--6{order:-6}.order-lg--5{order:-5}.order-lg--4{order:-4}.order-lg--3{order:-3}.order-lg--2{order:-2}.order-lg--1{order:-1}.order-lg-0{order:0}.order-lg-1{order:1}.order-lg-2{order:2}.order-lg-3{order:3}.order-lg-4{order:4}.order-lg-5{order:5}.order-lg-6{order:6}.order-lg-7{order:7}.order-lg-8{order:8}.order-lg-9{order:9}.order-lg-10{order:10}.order-lg-11{order:11}.order-lg-12{order:12}.hidden-lg{display:none}}@media(min-width:1536px){.span-xl-1{grid-column:span 1}.span-xl-2{grid-column:span 2}.span-xl-3{grid-column:span 3}.span-xl-4{grid-column:span 4}.span-xl-5{grid-column:span 5}.span-xl-6{grid-column:span 6}.span-xl-7{grid-column:span 7}.span-xl-8{grid-column:span 8}.span-xl-9{grid-column:span 9}.span-xl-10{grid-column:span 10}.span-xl-11{grid-column:span 11}.span-xl-12{grid-column:span 12}.offset-xl-0{margin-left:0%}.offset-xl-1{margin-left:calc(1 / 12 * 100%)}.offset-xl-2{margin-left:calc(2 / 12 * 100%)}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:calc(4 / 12 * 100%)}.offset-xl-5{margin-left:calc(5 / 12 * 100%)}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:calc(7 / 12 * 100%)}.offset-xl-8{margin-left:calc(8 / 12 * 100%)}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:calc(10 / 12 * 100%)}.offset-xl-11{margin-left:calc(11 / 12 * 100%)}.order-xl--12{order:-12}.order-xl--11{order:-11}.order-xl--10{order:-10}.order-xl--9{order:-9}.order-xl--8{order:-8}.order-xl--7{order:-7}.order-xl--6{order:-6}.order-xl--5{order:-5}.order-xl--4{order:-4}.order-xl--3{order:-3}.order-xl--2{order:-2}.order-xl--1{order:-1}.order-xl-0{order:0}.order-xl-1{order:1}.order-xl-2{order:2}.order-xl-3{order:3}.order-xl-4{order:4}.order-xl-5{order:5}.order-xl-6{order:6}.order-xl-7{order:7}.order-xl-8{order:8}.order-xl-9{order:9}.order-xl-10{order:10}.order-xl-11{order:11}.order-xl-12{order:12}.hidden-xl{display:none}}.canvas-mode-enabled{--canvas-hit: 14px}.canvas-mode-enabled .canvas-element{position:relative;z-index:0;border-radius:8px;outline:2px solid transparent;outline-offset:2px;transition:outline-color .2s ease,outline-style .2s ease}.canvas-mode-enabled .canvas-element:before{content:\"\";position:absolute;inset:calc(-1 * var(--canvas-hit));pointer-events:none;border-radius:inherit;background:transparent}.canvas-mode-enabled .canvas-element[data-canvas-type=section]{--outline-color: var(--md-sys-color-primary)}.canvas-mode-enabled .canvas-element[data-canvas-type=row]{--outline-color: var(--md-sys-color-secondary)}.canvas-mode-enabled .canvas-element[data-canvas-type=column],.canvas-mode-enabled .canvas-element[data-canvas-type=field]{--outline-color: var(--md-sys-color-tertiary)}.canvas-mode-enabled .canvas-element[data-canvas-type=actions]{--outline-color: var(--md-sys-color-primary)}.canvas-mode-enabled .canvas-element.hovered:not(.selected){outline-color:var(--outline-color, var(--md-sys-color-primary));outline-style:dashed}.canvas-mode-enabled .canvas-element.selected{outline-color:var(--outline-color, var(--md-sys-color-primary));outline-style:solid;box-shadow:0 0 0 2px var(--outline-color, var(--md-sys-color-primary))}.canvas-mode-enabled .canvas-element.hovered{z-index:var(--praxis-layer-authoring-hover, 300)}.canvas-mode-enabled .canvas-element.selected{z-index:var(--praxis-layer-authoring-selected, 320)}.section-drop-wrapper,.row-drop-wrapper,.column-drop-wrapper{display:contents}.add-section-container{display:flex;align-items:center;justify-content:center;padding:.5rem 0;margin:-.5rem 0;position:relative;z-index:var(--praxis-layer-authoring-insert, 280)}.add-section-container .add-section-line{flex-grow:1;height:1px;background:repeating-linear-gradient(90deg,var(--md-sys-color-outline-variant),var(--md-sys-color-outline-variant) 4px,transparent 4px,transparent 8px)}.add-section-container button{margin:0 1rem;transform:scale(.85)}:host{display:block;position:relative}.form-config-controls{position:sticky;top:10px;margin-left:auto;margin-bottom:10px;display:flex;align-items:center;gap:.35rem;z-index:60;background:color-mix(in srgb,var(--md-sys-color-surface) 92%,transparent);padding:6px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 82%,transparent);border-radius:999px;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);box-shadow:0 10px 22px #0f172a14;min-width:0;justify-content:flex-end;pointer-events:none;opacity:.88;transition:opacity .16s ease,box-shadow .16s ease,border-color .16s ease,transform .16s ease}.form-config-controls>*{pointer-events:auto}.praxis-dynamic-form:hover .form-config-controls,.praxis-dynamic-form:focus-within .form-config-controls{opacity:1;border-color:color-mix(in srgb,var(--md-sys-color-primary) 26%,var(--md-sys-color-outline-variant) 74%);box-shadow:0 14px 28px #0f172a1f;transform:translateY(-1px)}.form-config-controls .mat-icon-button{--mdc-icon-button-state-layer-size: 34px;width:34px;height:34px;color:var(--md-sys-color-on-surface-variant);background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 82%,transparent);border:1px solid transparent}.form-config-controls .mat-icon-button:hover{color:var(--md-sys-color-primary);background:var(--md-sys-color-primary-container);border-color:color-mix(in srgb,var(--md-sys-color-primary) 20%,transparent)}.ai-floating-assistant{position:sticky;right:0;bottom:12px;margin-top:14px;margin-left:auto;width:fit-content;z-index:70;pointer-events:none}.ai-floating-assistant praxis-ai-assistant{pointer-events:auto;display:inline-flex}.ai-floating-assistant .ai-trigger-btn{box-shadow:0 12px 28px #0f172a24}.config-button{color:var(--md-sys-color-primary)}.form-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3rem;text-align:center;color:var(--md-sys-color-on-surface);gap:1rem}.form-loading p{margin:0;font-size:.875rem;opacity:.7}.form-error{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3rem;text-align:center;color:var(--md-sys-color-error);gap:1rem;border:1px solid var(--md-sys-color-error);border-radius:8px;background-color:var(--md-sys-color-error-container);margin:1rem;box-shadow:0 12px 30px #7f1d1d1f}.form-error h3{margin:0;color:var(--md-sys-color-on-error-container)}.form-error p{margin:0;color:var(--md-sys-color-on-error-container);opacity:.8}.form-error button{margin-top:.5rem}.pfx-form-info-banner{margin-bottom:14px;padding:12px 14px;border-radius:16px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 18%,var(--md-sys-color-outline-variant) 82%);background:color-mix(in srgb,var(--md-sys-color-primary-container) 24%,var(--md-sys-color-surface) 76%);color:var(--md-sys-color-on-surface);display:flex;align-items:flex-start;justify-content:space-between;gap:14px}.pfx-form-info-banner .text{font-size:.92rem;line-height:1.5;font-weight:600}.pfx-form-info-banner .actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px}.praxis-dynamic-form{display:flex;flex-direction:column;transition:all .3s ease;position:relative;font-family:inherit;font-size:inherit;color:inherit;--pfx-editorial-form-surface: var( --pfx-form-section-surface, var(--md-sys-color-surface-container) );--pfx-editorial-form-surface-muted: var(--md-sys-color-surface-container-low);--pfx-editorial-form-border: var( --pfx-form-stroke, var(--md-sys-color-outline-variant) );--pfx-editorial-form-text: var(--md-sys-color-on-surface);--pfx-editorial-form-text-muted: var(--md-sys-color-on-surface-variant);--pfx-editorial-form-field-surface: color-mix( in srgb, var(--pfx-editorial-form-surface-muted) 76%, var(--pfx-editorial-form-surface) 24% );--pfx-editorial-form-field-outline: color-mix( in srgb, var(--pfx-editorial-form-border) 88%, transparent );--pfx-editorial-form-accent: var(--md-sys-color-primary);--pfx-editorial-form-accent-text: var(--md-sys-color-on-primary);--pfx-editorial-form-radius: var(--pfx-form-section-radius, 8px);--pfx-editorial-form-field-radius: var(--pfx-form-field-radius, 4px);--pfx-editorial-form-border-width: 1px;--pfx-editorial-form-field-border-width: 1px;--pfx-editorial-form-shadow: none;--pfx-form-shell-surface: color-mix( in srgb, var(--pfx-editorial-form-surface) 36%, transparent );--pfx-form-section-surface-flat: color-mix( in srgb, var(--pfx-editorial-form-surface) 78%, var(--pfx-editorial-form-surface-muted) 22% );--pfx-form-section-divider: color-mix( in srgb, var(--pfx-editorial-form-border) 68%, transparent );--pfx-form-label-strong: color-mix( in srgb, var(--pfx-editorial-form-text) 96%, white 4% );--pfx-form-label-muted: color-mix( in srgb, var(--pfx-editorial-form-text-muted) 92%, var(--pfx-editorial-form-text) 8% );--pfx-form-field-surface-rest: color-mix( in srgb, var(--pfx-editorial-form-field-surface) 82%, var(--pfx-editorial-form-surface) 18% );--pfx-form-field-surface-focus: color-mix( in srgb, var(--pfx-editorial-form-accent) 4%, var(--pfx-form-field-surface-rest) 96% );--pfx-form-field-min-height: 48px;--pfx-form-section-padding: clamp(1.1rem, 1.8vw, 1.5rem);--pfx-form-section-gap: 22px;--pfx-form-footer-surface: color-mix( in srgb, var(--pfx-editorial-form-surface) 70%, var(--pfx-editorial-form-surface-muted) 30% );--pfx-form-footer-border: color-mix( in srgb, var(--pfx-editorial-form-border) 76%, transparent )}.praxis-dynamic-form.editorial-visual-context{font-family:var(--editorial-body-font-family, inherit);font-size:var(--editorial-body-size, 1rem);color:var(--editorial-text-primary, var(--md-sys-color-on-surface));--pfx-editorial-form-surface: var( --editorial-surface-primary, var(--pfx-form-section-surface, var(--md-sys-color-surface-container)) );--pfx-editorial-form-surface-muted: var( --editorial-surface-secondary, var(--md-sys-color-surface-container-low) );--pfx-editorial-form-border: var( --editorial-border-color, var(--pfx-form-stroke, var(--md-sys-color-outline-variant)) );--pfx-editorial-form-text: var( --editorial-text-primary, var(--md-sys-color-on-surface) );--pfx-editorial-form-text-muted: var( --editorial-text-secondary, var(--md-sys-color-on-surface-variant) );--pfx-editorial-form-accent: var( --editorial-cta-primary, var(--editorial-accent, var(--md-sys-color-primary)) );--pfx-editorial-form-accent-text: var( --editorial-cta-primary-text, var(--editorial-accent-contrast, var(--md-sys-color-on-primary)) );--pfx-editorial-form-radius: var(--editorial-card-radius, 18px);--pfx-editorial-form-field-radius: var( --editorial-field-radius, var(--editorial-card-radius, 14px) );--pfx-editorial-form-border-width: var(--editorial-card-border-width, 1px);--pfx-editorial-form-field-border-width: var(--editorial-field-border-width, 1px);--pfx-editorial-form-shadow: var(--editorial-card-shadow, none);--md-sys-color-surface: var( --pfx-editorial-form-surface, var(--md-sys-color-surface) );--md-sys-color-surface-container: var( --pfx-editorial-form-surface, var(--md-sys-color-surface-container) );--md-sys-color-surface-container-low: var( --pfx-editorial-form-surface-muted, var(--md-sys-color-surface-container-low) );--md-sys-color-surface-variant: var( --pfx-editorial-form-field-surface, var(--md-sys-color-surface-variant) );--md-sys-color-outline: var( --pfx-editorial-form-field-outline, var(--md-sys-color-outline) );--md-sys-color-outline-variant: var( --pfx-editorial-form-border, var(--md-sys-color-outline-variant) );--md-sys-color-on-surface: var( --pfx-editorial-form-text, var(--md-sys-color-on-surface) );--md-sys-color-on-surface-variant: var( --pfx-editorial-form-text-muted, var(--md-sys-color-on-surface-variant) );--md-sys-color-primary: var( --pfx-editorial-form-accent, var(--md-sys-color-primary) );--md-sys-color-on-primary: var( --pfx-editorial-form-accent-text, var(--md-sys-color-on-primary) );--md-sys-color-on-surface-rgb: var( --editorial-text-primary-rgb, var(--md-sys-color-on-surface-rgb) );--mat-sys-on-surface: var( --pfx-editorial-form-text, var(--mat-sys-on-surface) );--mat-sys-on-surface-variant: var( --pfx-editorial-form-text-muted, var(--mat-sys-on-surface-variant) );--mat-sys-on-surface-rgb: var( --editorial-text-primary-rgb, var(--mat-sys-on-surface-rgb, var(--md-sys-color-on-surface-rgb)) );color:var(--pfx-editorial-form-text);color-scheme:light}.praxis-dynamic-form.presentation-mode .form-row,.praxis-dynamic-form.readonly-mode .form-row{gap:.5rem;margin-bottom:.5rem}.praxis-dynamic-form.presentation-mode .form-section,.praxis-dynamic-form.readonly-mode .form-section{padding:.75rem .875rem}.praxis-dynamic-form.pres-compact .form-row{gap:.35rem;margin-bottom:.35rem}.praxis-dynamic-form.pres-compact .form-section{padding:.5rem .75rem}.praxis-dynamic-form.pres-label-left .praxis-presentation{display:grid;grid-template-columns:var(--pfx-presentation-label-w, 220px) 1fr;align-items:baseline;column-gap:10px}.praxis-dynamic-form.pres-label-left .praxis-presentation__label{text-align:right;margin-bottom:0}.form-section{border:1px solid var(--pfx-form-section-divider);border-radius:var(--pfx-editorial-form-radius);padding:var(--pfx-form-section-padding);background:var(--pfx-form-section-surface-flat);box-shadow:none;transition:all .2s ease;position:relative}.praxis-dynamic-form.editorial-visual-context .form-section{border:var(--pfx-editorial-form-border-width) solid var(--pfx-editorial-form-border)!important;background:var(--pfx-editorial-form-surface)!important;box-shadow:var(--editorial-card-shadow, none)}.praxis-dynamic-form.editorial-visual-context .form-section.section-appearance-step{background-color:var(--pfx-editorial-form-surface)!important;background-image:linear-gradient(180deg,color-mix(in srgb,var(--editorial-accent, var(--md-sys-color-primary)) 6%,transparent) 0%,transparent 132px)!important;background-repeat:no-repeat!important}.section-drop-wrapper>.form-section{margin-bottom:var(--pfx-section-gap, 20px)}.section-drop-wrapper:last-of-type>.form-section{margin-bottom:0}.praxis-dynamic-form>praxis-form-actions[data-actions-placement=afterSections] .form-actions{margin-top:var(--pfx-actions-gap-top, var(--pfx-section-gap, 20px))}.section-title{margin:0 0 var(--pfx-section-title-mb, 12px) 0;font-family:var(--editorial-title-font-family, var(--editorial-body-font-family, inherit));font-size:var(--editorial-step-title-size, 1.12rem);font-weight:700;color:var(--pfx-form-label-strong)}.section-heading{display:flex;align-items:flex-start;gap:12px;margin-bottom:18px;padding-bottom:18px;border-bottom:1px solid var(--pfx-form-section-divider)}.section-heading.align-center{flex-direction:column;align-items:center;text-align:center}.section-heading.align-center .section-heading-text{display:grid;justify-items:center}.section-step-label{display:inline-flex;align-items:center;min-height:24px;padding:0 10px;margin:0 0 8px;border-radius:999px;background:color-mix(in srgb,var(--pfx-editorial-form-accent) 10%,transparent);color:color-mix(in srgb,var(--pfx-editorial-form-accent) 84%,var(--pfx-form-label-strong) 16%);font-size:.72rem;font-weight:800;font-family:var(--editorial-title-font-family, var(--editorial-body-font-family, inherit));letter-spacing:.08em;text-transform:uppercase}.section-heading-text{flex:1 1 auto;min-width:0}.section-heading-actions{display:inline-flex;align-items:center;align-self:flex-start;flex-wrap:wrap;gap:4px;margin-left:auto}.section-heading-actions.align-center{align-self:center;margin-left:0}.section-heading-action-btn{color:var(--pfx-form-label-muted)}.section-heading-action-btn .mat-mdc-progress-spinner,.section-heading-action-btn mat-progress-spinner{--mdc-circular-progress-active-indicator-color: currentColor}.section-heading-action-btn.loading{opacity:.72;pointer-events:none}.section-heading-action-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.section-collapse-btn{margin-left:4px;color:var(--pfx-form-label-muted)}.section-title.title-large{font:var(--mdc-typography-title-large, 500 22px/28px system-ui)}.section-title.title-medium{font:var(--mdc-typography-title-medium, 500 16px/24px system-ui)}.section-title.title-small{font:var(--mdc-typography-title-small, 500 14px/20px system-ui)}.section-title.headline-small{font:var(--mdc-typography-headline-small, 600 24px/32px system-ui)}.section-title.title-large,.section-title.title-medium,.section-title.title-small,.section-title.headline-small{font-family:var(--editorial-title-font-family, var(--editorial-body-font-family, inherit));font-weight:var(--editorial-title-weight, 600)}.section-description{margin:0;font-family:var(--editorial-body-font-family, inherit);font-size:.93rem;color:var(--pfx-form-label-muted);line-height:1.6}.section-description.body-large{font:var(--mdc-typography-body-large, 400 16px/24px system-ui)}.section-description.body-medium{font:var(--mdc-typography-body-medium, 400 14px/20px system-ui)}.section-description.body-small{font:var(--mdc-typography-body-small, 400 12px/16px system-ui)}.section-description.body-large,.section-description.body-medium,.section-description.body-small{font-family:var(--editorial-body-font-family, inherit);font-weight:var(--editorial-body-weight, 400)}.section-title.align-center,.section-description.align-center,.section-step-label.align-center{text-align:center}.form-section.section-appearance-plain{border-color:transparent;border-radius:0;padding:0;background:transparent}.form-section.section-appearance-plain .section-heading{margin-bottom:14px}.form-section.section-appearance-step{border-radius:var(--pfx-editorial-form-radius);padding:var(--pfx-form-section-padding);background:var(--pfx-form-section-surface-flat);border-color:var(--pfx-form-section-divider);box-shadow:none}.praxis-dynamic-form.editorial-visual-context .form-section.section-appearance-step{border-radius:var(--editorial-card-radius, 20px);box-shadow:none}.form-section.section-appearance-step .section-heading{margin-bottom:22px;padding-bottom:16px;border-bottom:1px solid var(--pfx-form-section-divider)}.form-section.section-appearance-step .section-title{font:var(--mdc-typography-title-large, 700 22px/28px system-ui);color:var(--pfx-form-label-strong);margin-bottom:8px}.form-section.section-appearance-step .section-description{color:var(--pfx-form-label-muted);max-width:60ch}.form-section.section-appearance-step .section-step-label{min-height:24px;padding:0 10px;margin-bottom:10px;box-shadow:none}.form-section.section-appearance-step .section-heading.align-center .section-description{max-width:52ch}.form-section.section-appearance-step .section-body{display:grid;gap:8px;padding-top:0}.form-section.section-appearance-step .form-row{margin-bottom:var(--pfx-field-gap, 1rem)}.form-section.section-appearance-step .form-column{gap:var(--pfx-field-gap, 12px)}.form-section .form-editorial-blocks{display:grid;gap:16px}.form-section .form-editorial-blocks[data-editorial-placement=beforeActions]{margin-top:24px;padding-top:18px;border-top:1px solid var(--pfx-form-section-divider)}.form-section.section-appearance-step .form-editorial-blocks[data-editorial-placement=beforeActions]{gap:18px}.form-section.section-appearance-step .form-editorial-blocks[data-editorial-placement=beforeActions]>*+*{margin-top:2px}.praxis-dynamic-form>praxis-form-actions[data-actions-placement=afterSections] .form-actions{margin-top:18px;padding-top:0}.praxis-dynamic-form>.form-editorial-blocks[data-editorial-placement=after]{margin-top:6px}:host-context(.mdc-theme-dark) .form-section.section-appearance-step,:host-context(.theme-dark) .form-section.section-appearance-step{border-color:color-mix(in srgb,var(--pfx-editorial-form-border) 82%,transparent);box-shadow:none}:host-context(.mdc-theme-dark) .form-section.section-appearance-step .section-title,:host-context(.theme-dark) .form-section.section-appearance-step .section-title{color:color-mix(in srgb,var(--pfx-editorial-form-text) 96%,white)}:host-context(.mdc-theme-dark) .form-section.section-appearance-step .section-description,:host-context(.theme-dark) .form-section.section-appearance-step .section-description{color:color-mix(in srgb,var(--pfx-editorial-form-text-muted) 86%,var(--pfx-editorial-form-text) 14%)}:host-context(.mdc-theme-dark) .section-step-label,:host-context(.theme-dark) .section-step-label{background:color-mix(in srgb,var(--md-sys-color-primary-container) 54%,transparent);color:color-mix(in srgb,var(--md-sys-color-primary) 88%,white)}:host-context(.mdc-theme-dark) .form-section .form-editorial-blocks[data-editorial-placement=beforeActions],:host-context(.theme-dark) .form-section .form-editorial-blocks[data-editorial-placement=beforeActions]{border-top-color:color-mix(in srgb,var(--md-sys-color-outline-variant) 90%,transparent)}.inline-edit-btn{margin-left:6px;vertical-align:middle;--mdc-icon-button-size: 28px;--mdc-icon-button-icon-size: 16px}.inline-edit-btn mat-icon{font-size:16px;width:16px;height:16px}.section-body.collapsed{border:1px dashed var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low);border-radius:6px;padding:8px 10px}.section-collapsed-placeholder{display:flex;align-items:center;gap:8px;color:var(--md-sys-color-on-surface-variant);font-size:.95rem}.section-collapsed-placeholder mat-icon{font-size:20px;width:20px;height:20px}.form-row{display:flex;gap:1.1rem;margin-bottom:var(--pfx-field-gap, 1.1rem);transition:all .2s ease;border-radius:6px;position:relative}.praxis-dynamic-form.pfx-mounting .form-row{opacity:0;transform:translateY(var(--pdx-form-mount-offset, 6px));animation:pdxFormMount var(--pdx-form-mount-duration, .16s) ease-out both;animation-delay:calc(var(--pfx-mount-index, 0) * var(--pdx-form-mount-stagger, 20ms))}@media(prefers-reduced-motion:reduce){.praxis-dynamic-form.pfx-mounting .form-row{animation:none;opacity:1;transform:none}}@keyframes pdxFormMount{to{opacity:1;transform:translateY(0)}}.praxis-dynamic-form .mat-mdc-form-field{width:100%;margin-bottom:var(--pfx-field-gap, 10px);font-family:inherit;--mdc-filled-text-field-container-color: var(--pfx-form-field-surface-rest);--mdc-filled-text-field-focus-active-indicator-color: var(--pfx-editorial-form-accent);--mdc-filled-text-field-active-indicator-color: var(--pfx-editorial-form-field-outline);--mdc-filled-text-field-hover-active-indicator-color: var(--pfx-editorial-form-accent);--mdc-filled-text-field-focus-label-text-color: var(--pfx-editorial-form-accent);--mdc-filled-text-field-label-text-color: var(--pfx-form-label-muted);--mdc-filled-text-field-input-text-color: var(--pfx-editorial-form-text);--mdc-filled-text-field-caret-color: var(--pfx-editorial-form-accent);--mdc-filled-text-field-input-text-placeholder-color: color-mix( in srgb, var(--pfx-editorial-form-text-muted) 82%, transparent );--mdc-outlined-text-field-outline-color: var(--pfx-editorial-form-field-outline);--mdc-outlined-text-field-hover-outline-color: var(--pfx-editorial-form-accent);--mdc-outlined-text-field-focus-outline-color: var(--pfx-editorial-form-accent);--mdc-outlined-text-field-focus-label-text-color: var(--pfx-editorial-form-accent);--mdc-outlined-text-field-label-text-color: var(--pfx-form-label-muted);--mdc-outlined-text-field-input-text-color: var(--pfx-editorial-form-text);--mdc-filled-text-field-container-shape: var(--pfx-editorial-form-field-radius);--mdc-outlined-text-field-container-shape: var(--pfx-editorial-form-field-radius);--mdc-filled-text-field-active-indicator-height: var(--pfx-editorial-form-field-border-width);--mdc-filled-text-field-focus-active-indicator-height: var(--pfx-editorial-form-field-border-width);--mdc-outlined-text-field-outline-width: var(--pfx-editorial-form-field-border-width);--mdc-outlined-text-field-focus-outline-width: var(--pfx-editorial-form-field-border-width);--mat-select-enabled-trigger-text-color: var(--pfx-editorial-form-text);--mat-select-enabled-arrow-color: var(--pfx-form-label-muted);--mat-form-field-enabled-select-arrow-color: var(--pfx-form-label-muted);--mat-form-field-focus-select-arrow-color: var(--pfx-editorial-form-accent);--mat-form-field-hover-state-layer-opacity: 0;--mat-form-field-focus-state-layer-opacity: 0}.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field{font-family:var(--editorial-body-font-family, inherit)}.praxis-dynamic-form .mat-mdc-text-field-wrapper,.praxis-dynamic-form .mdc-text-field{background:var(--pfx-form-field-surface-rest);border-radius:var(--pfx-editorial-form-field-radius);min-height:var(--pfx-form-field-min-height);transition:background-color .16s ease,box-shadow .16s ease,border-color .16s ease}.praxis-dynamic-form.editorial-visual-context .mat-mdc-text-field-wrapper,.praxis-dynamic-form.editorial-visual-context .mdc-text-field,.praxis-dynamic-form.editorial-visual-context .mdc-text-field--filled,.praxis-dynamic-form.editorial-visual-context .mdc-text-field--outlined{background-color:var(--pfx-form-field-surface-rest)!important;background:var(--pfx-form-field-surface-rest)!important}.praxis-dynamic-form .mat-mdc-form-field .mdc-floating-label,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-form-field-hint,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-form-field-error,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-form-field-required-marker,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-select-arrow,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-select-placeholder,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-form-field-icon-suffix,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-form-field-icon-prefix{color:var(--pfx-form-label-muted)}.praxis-dynamic-form .mat-mdc-input-element,.praxis-dynamic-form .mat-mdc-select-value-text,.praxis-dynamic-form .mat-mdc-form-field-infix,.praxis-dynamic-form .mat-mdc-select-min-line{color:var(--pfx-editorial-form-text)}.praxis-dynamic-form .mat-mdc-form-field:hover .mat-mdc-text-field-wrapper,.praxis-dynamic-form .mat-mdc-form-field:hover .mdc-text-field,.praxis-dynamic-form .mat-mdc-form-field.mat-focused .mat-mdc-text-field-wrapper,.praxis-dynamic-form .mat-mdc-form-field.mat-focused .mdc-text-field{background:var(--pfx-form-field-surface-focus)}.praxis-dynamic-form.editorial-visual-context .mat-mdc-input-element,.praxis-dynamic-form.editorial-visual-context .mat-mdc-select-value-text,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field-infix,.praxis-dynamic-form.editorial-visual-context .mat-mdc-select-min-line,.praxis-dynamic-form.editorial-visual-context .mdc-text-field__input{color:var(--pfx-editorial-form-text)!important;-webkit-text-fill-color:var(--pfx-editorial-form-text)!important}.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mdc-floating-label,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-form-field-hint,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-form-field-error,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-form-field-required-marker,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-select-arrow,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-select-placeholder,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-form-field-icon-suffix,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-form-field-icon-prefix{color:var(--pfx-editorial-form-text-muted)!important}.praxis-dynamic-form.editorial-visual-context .mat-mdc-floating-label,.praxis-dynamic-form.editorial-visual-context .mdc-floating-label,.praxis-dynamic-form.editorial-visual-context .mdc-text-field__input::placeholder{color:color-mix(in srgb,var(--pfx-editorial-form-text) 68%,transparent)!important}.praxis-dynamic-form.editorial-visual-context .mat-mdc-input-element::placeholder,.praxis-dynamic-form.editorial-visual-context textarea.mat-mdc-input-element::placeholder{color:color-mix(in srgb,var(--pfx-editorial-form-text) 58%,transparent)!important}.praxis-dynamic-form .mat-mdc-radio-button,.praxis-dynamic-form .mat-mdc-checkbox,.praxis-dynamic-form .mat-mdc-slide-toggle{--mdc-radio-selected-icon-color: var(--pfx-editorial-form-accent);--mdc-radio-selected-hover-icon-color: var(--pfx-editorial-form-accent);--mdc-radio-selected-focus-icon-color: var(--pfx-editorial-form-accent);--mdc-checkbox-selected-checkmark-color: var(--pfx-editorial-form-accent-text);--mdc-checkbox-selected-focus-icon-color: var(--pfx-editorial-form-accent);--mdc-checkbox-selected-hover-icon-color: var(--pfx-editorial-form-accent);--mdc-checkbox-selected-icon-color: var(--pfx-editorial-form-accent);--mdc-switch-selected-focus-state-layer-color: var(--pfx-editorial-form-accent);--mdc-switch-selected-handle-color: var(--pfx-editorial-form-accent);--mdc-switch-selected-track-color: color-mix(in srgb, var(--pfx-editorial-form-accent) 45%, #fff)}.praxis-dynamic-form [data-field-type=input],.praxis-dynamic-form [data-field-type=textarea],.praxis-dynamic-form [data-field-type=email],.praxis-dynamic-form [data-field-type=password],.praxis-dynamic-form [data-field-type=url],.praxis-dynamic-form [data-field-type=search],.praxis-dynamic-form [data-field-type=phone],.praxis-dynamic-form [data-field-type=numericTextBox],.praxis-dynamic-form [data-field-type=currency],.praxis-dynamic-form [data-field-type=cpfCnpj],.praxis-dynamic-form [data-field-type=date],.praxis-dynamic-form [data-field-type=dateInput],.praxis-dynamic-form [data-field-type=dateRange],.praxis-dynamic-form [data-field-type=dateTimeLocal],.praxis-dynamic-form [data-field-type=time],.praxis-dynamic-form [data-field-type=timePicker],.praxis-dynamic-form [data-field-type=timeRange],.praxis-dynamic-form [data-field-type=month],.praxis-dynamic-form [data-field-type=week],.praxis-dynamic-form [data-field-type=yearInput],.praxis-dynamic-form [data-field-type=select],.praxis-dynamic-form [data-field-type=multi-select],.praxis-dynamic-form [data-field-type=searchable-select],.praxis-dynamic-form [data-field-type=async-select],.praxis-dynamic-form [data-field-type=autocomplete],.praxis-dynamic-form [data-field-type=tree-select],.praxis-dynamic-form [data-field-type=multi-select-tree],.praxis-dynamic-form [data-field-type=priceRange],.praxis-dynamic-form [data-field-type=file-upload]{display:block;width:100%;min-width:0}.praxis-dynamic-form .mat-mdc-form-field-subscript-wrapper{min-height:var(--pfx-subscript-min-h, 22px)}.form-row:last-child{margin-bottom:0}.form-column{display:grid;align-content:start;gap:var(--pfx-field-gap, 10px);flex:1;min-width:0;transition:all .2s ease;border-radius:4px;position:relative}.form-row.grid-12{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:var(--pfx-grid-gap, 16px)}.align-start{align-self:flex-start}.align-center{align-self:center}.align-end{align-self:flex-end}.align-stretch{align-self:stretch}.form-blocking-overlay{position:absolute;inset:0;background:transparent;color:var(--md-sys-color-on-surface);backdrop-filter:blur(2px) saturate(103%);-webkit-backdrop-filter:blur(2px) saturate(103%);display:flex;align-items:center;justify-content:center;flex-direction:column;gap:12px;z-index:10;pointer-events:all}@media(max-width:768px){.form-row{flex-direction:column;gap:.5rem}.form-section{padding:1rem}.section-heading{gap:10px;margin-bottom:16px;padding-bottom:14px}}.section-title{display:flex;align-items:center;gap:8px}.section-title .section-title-icon{font-size:1.25em;line-height:1}.section-title-avatar{--_pfx-form-section-avatar-size: var(--pfx-form-section-avatar-size-md, var(--pfx-form-section-avatar-size, 32px));display:inline-flex;align-items:center;justify-content:center;width:var(--_pfx-form-section-avatar-size);height:var(--_pfx-form-section-avatar-size);border-radius:999px;flex:0 0 var(--_pfx-form-section-avatar-size);overflow:hidden}.section-title-avatar.size-sm{--_pfx-form-section-avatar-size: var(--pfx-form-section-avatar-size-sm, 24px)}.section-title-avatar.size-md{--_pfx-form-section-avatar-size: var(--pfx-form-section-avatar-size-md, var(--pfx-form-section-avatar-size, 32px))}.section-title-avatar.size-lg{--_pfx-form-section-avatar-size: var(--pfx-form-section-avatar-size-lg, 40px)}.section-title-avatar-image{object-fit:cover;border:1px solid color-mix(in srgb,var(--pfx-form-section-divider) 72%,transparent);background:var(--pfx-form-section-surface-flat)}.section-title-avatar-text,.section-title-avatar-placeholder{background:color-mix(in srgb,var(--pfx-editorial-form-accent) 14%,var(--pfx-form-section-surface-flat));color:color-mix(in srgb,var(--pfx-editorial-form-accent) 82%,var(--pfx-form-label-strong) 18%);font-size:calc(var(--_pfx-form-section-avatar-size) * .41);font-weight:800;letter-spacing:.04em;text-transform:uppercase}.section-title-avatar-placeholder mat-icon{font-size:calc(var(--_pfx-form-section-avatar-size) * .5625);width:calc(var(--_pfx-form-section-avatar-size) * .5625);height:calc(var(--_pfx-form-section-avatar-size) * .5625);line-height:calc(var(--_pfx-form-section-avatar-size) * .5625)}.section-title-avatar-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: DynamicFieldLoaderDirective, selector: "[dynamicFieldLoader]", inputs: ["fields", "formGroup", "enableExternalControlBinding", "itemTemplate", "debugTrace", "debugTraceLabel", "readonlyMode", "disabledMode", "presentationMode", "visible", "canvasMode"], outputs: ["componentsCreated", "fieldCreated", "fieldDestroyed", "renderError", "canvasMouseEnter", "canvasMouseLeave", "canvasClick"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { 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: "component", type: i4.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i16.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { 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: MatDialogModule }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "ngmodule", type: MatBadgeModule }, { kind: "directive", type: i18.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: CanvasToolbarComponent, selector: "praxis-canvas-toolbar", inputs: ["selectedElement"], outputs: ["editMetadata", "delete", "moveUp", "moveDown", "selectPath", "requestClose", "toggleReadonly", "toggleRequired", "toggleHidden", "toggleDisabled"] }, { kind: "component", type: PraxisFormActionsComponent, selector: "praxis-form-actions", inputs: ["actions", "editorialVisualContext", "isSubmitting", "formIsValid", "submitError", "formId", "actionOverrides"], outputs: ["action"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone"] }, { kind: "component", type: PraxisAiAssistantComponent, selector: "praxis-ai-assistant", inputs: ["adapter", "riskPolicy", "allowManualPatchEdit"] }, { kind: "component", type: PraxisRichContent, selector: "praxis-rich-content", inputs: ["document", "nodes", "context", "hostCapabilities", "layout", "rootClassName"] }] });
12285
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PraxisDynamicForm, isStandalone: true, selector: "praxis-dynamic-form", inputs: { resourcePath: "resourcePath", resourceId: "resourceId", initialValue: "initialValue", editorialContext: "editorialContext", mode: "mode", config: "config", actions: "actions", schemaSource: "schemaSource", schemaUrl: "schemaUrl", submitUrl: "submitUrl", submitMethod: "submitMethod", responseSchemaUrl: "responseSchemaUrl", apiEndpointKey: "apiEndpointKey", apiUrlEntry: "apiUrlEntry", enableCustomization: "enableCustomization", formId: "formId", componentInstanceId: "componentInstanceId", layout: "layout", backConfig: "backConfig", hooks: "hooks", removeEmptyContainersOnSave: "removeEmptyContainersOnSave", reactiveValidation: "reactiveValidation", reactiveValidationDebounceMs: "reactiveValidationDebounceMs", notifyIfOutdated: "notifyIfOutdated", snoozeMs: "snoozeMs", autoOpenSettingsOnOutdated: "autoOpenSettingsOnOutdated", readonlyModeGlobal: "readonlyModeGlobal", disabledModeGlobal: "disabledModeGlobal", presentationModeGlobal: "presentationModeGlobal", visibleGlobal: "visibleGlobal", customEndpoints: "customEndpoints" }, outputs: { formSubmit: "formSubmit", formCancel: "formCancel", formReset: "formReset", configChange: "configChange", formReady: "formReady", valueChange: "valueChange", syncCompleted: "syncCompleted", initializationError: "initializationError", loadingStateChange: "loadingStateChange", enableCustomizationChange: "enableCustomizationChange", customAction: "customAction", actionConfirmation: "actionConfirmation", schemaStatusChange: "schemaStatusChange", fieldRenderError: "fieldRenderError" }, providers: [providePraxisI18nConfig(PRAXIS_DYNAMIC_FORM_I18N_CONFIG)], viewQueries: [{ propertyName: "formHost", first: true, predicate: ["formHost"], descendants: true }, { propertyName: "fieldLoaders", predicate: DynamicFieldLoaderDirective, descendants: true }], usesOnChanges: true, ngImport: i0, template: "@if (isLoading) {\n<!-- Loading State -->\n<div class=\"form-loading\" role=\"status\" aria-live=\"polite\" aria-label=\"Carregando formul\u00E1rio\">\n <div class=\"form-loading-header\">\n <mat-progress-spinner diameter=\"32\"></mat-progress-spinner>\n <div class=\"form-loading-copy\">\n <p class=\"form-loading-title\">Carregando formul\u00E1rio...</p>\n <p class=\"form-loading-subtitle\">Preparando campos e op\u00E7\u00F5es do cadastro.</p>\n </div>\n </div>\n <div class=\"form-loading-skeleton\" aria-hidden=\"true\">\n <span class=\"form-loading-line form-loading-line-title\"></span>\n <span class=\"form-loading-line\"></span>\n <span class=\"form-loading-line form-loading-line-short\"></span>\n <span class=\"form-loading-field\"></span>\n <span class=\"form-loading-field form-loading-field-wide\"></span>\n </div>\n</div>\n} @else if (initializationStatus === 'error') {\n<!-- Error State -->\n<div class=\"form-error\">\n <mat-icon color=\"warn\" [praxisIcon]=\"'error'\"></mat-icon>\n <h3>{{ getErrorTitle() }}</h3>\n <p>{{ currentErrorMessage }}</p>\n @if (isRecoverable) {\n <button mat-stroked-button (click)=\"retryInitialization()\">\n <mat-icon [praxisIcon]=\"'refresh'\"></mat-icon>\n Tentar Novamente\n </button>\n }\n <button mat-button (click)=\"showDetailedError()\" class=\"show-details\">\n Ver Detalhes T\u00E9cnicos\n </button>\n <!-- Permitir corre\u00E7\u00E3o do resourcePath diretamente do estado de erro -->\n <button mat-flat-button color=\"primary\" (click)=\"openQuickConnect()\" class=\"connect-action\">\n <mat-icon [praxisIcon]=\"'bolt'\"></mat-icon>\n Conectar a recurso\n </button>\n</div>\n} @else if (initializationStatus === 'success') {\n<!-- Inline banner for schema change (only when customization is enabled) -->\n@if (shouldShowOutdatedInline()) {\n<div class=\"pfx-form-info-banner\" role=\"status\" aria-live=\"polite\">\n <div class=\"text\">O schema do servidor mudou. Reconciliar agora?</div>\n <div class=\"actions\">\n <button mat-stroked-button color=\"primary\" (click)=\"openConfigEditor()\">\n <mat-icon [praxisIcon]=\"'sync'\"></mat-icon>\n Reconciliar\n </button>\n <button mat-button (click)=\"onSnoozeOutdated()\">Lembrar depois</button>\n <button mat-button (click)=\"onIgnoreOutdated()\">Ignorar</button>\n </div>\n</div>\n}\n\n<!-- Configuration Controls -->\n@if (shouldShowConfigControls && enableCustomization) {\n<div class=\"form-config-controls\">\n <praxis-ai-assistant [adapter]=\"aiAdapter\"></praxis-ai-assistant>\n <button type=\"button\" mat-icon-button (click)=\"openConfigEditor()\" [disabled]=\"isLoading\" class=\"config-button\"\n [matBadge]=\"schemaOutdated ? '!' : ''\" [matBadgeHidden]=\"!schemaOutdated\" matBadgeColor=\"warn\" matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configurar formul\u00E1rio'\">\n <mat-icon [praxisIcon]=\"'settings'\"></mat-icon>\n </button>\n <button type=\"button\" mat-icon-button (click)=\"disconnect()\" matTooltip=\"Desconectar da fonte de dados\"\n [disabled]=\"isLoading\">\n <mat-icon [praxisIcon]=\"'link_off'\"></mat-icon>\n </button>\n</div>\n}\n\n<!-- Form Content -->\n@if (!resourcePath && (!config.sections || config.sections.length === 0)) {\n<praxis-empty-state-card icon=\"link\" [title]=\"'Conecte o formul\u00E1rio \u00E0 fonte de dados'\"\n [description]=\"'Informe a rota do recurso da API para gerar automaticamente os campos do formul\u00E1rio.'\"\n [primaryAction]=\"{ label: 'Conectar \u00E0 fonte de dados', icon: 'bolt', action: openQuickConnect.bind(this) }\"></praxis-empty-state-card>\n}\n<form #formHost (ngSubmit)=\"onSubmit()\" [attr.aria-busy]=\"submitting ? 'true' : null\"\n [attr.aria-label]=\"'Formul\u00E1rio ' + (config.metadata?.version || '')\" [class.canvas-mode-enabled]=\"enableCustomization\"\n [class.presentation-mode]=\"effectivePresentation\" [class.readonly-mode]=\"effectiveReadonly\"\n [class.editorial-visual-context]=\"hasEditorialVisualContext()\"\n [class.pfx-mounting]=\"isMounting\" [formGroup]=\"form\"\n [ngClass]=\"{\n 'pres-compact': presentationVars.compact,\n 'pres-label-left': presentationVars.labelPosition === 'left',\n 'pres-label-above': presentationVars.labelPosition === 'above'\n }\" [style.--pfx-pres-label-align]=\"presentationVars.labelAlign\"\n [style.--pfx-pres-label-size]=\"presentationVars.labelSize\"\n [style.--pfx-pres-label-width]=\"presentationVars.labelWidth\"\n [style.--pfx-pres-row-gap]=\"presentationVars.density === 'compact' ? '6px' : (presentationVars.density === 'cozy' ? '8px' : '10px')\"\n [style.--pfx-pres-row-padding]=\"presentationVars.density === 'compact' ? '2px 0' : (presentationVars.density === 'cozy' ? '6px 0' : '8px 0')\"\n [style.--pfx-pres-value-align]=\"presentationVars.valueAlign\"\n [style.--pfx-pres-value-size]=\"presentationVars.valueSize\"\n [style.--pdx-form-mount-duration]=\"getMountDurationVar()\"\n [style.--pdx-form-mount-offset]=\"getMountOffsetVar()\"\n [style.--pdx-form-mount-stagger]=\"getMountStaggerVar()\"\n class=\"praxis-dynamic-form\">\n <praxis-canvas-toolbar (editMetadata)=\"openSelectedElementEditor()\" (selectPath)=\"onSelectPath($event)\"\n (moveUp)=\"onToolbarMove('up')\" (moveDown)=\"onToolbarMove('down')\" (toggleReadonly)=\"onToolbarToggleReadonly()\"\n (toggleRequired)=\"onToolbarToggleRequired()\" (toggleHidden)=\"onToolbarToggleHidden()\"\n (toggleDisabled)=\"onToolbarToggleDisabled()\" (requestClose)=\"onToolbarRequestClose()\"\n *ngIf=\"enableCustomization && selectedElement\" [selectedElement]=\"selectedElement\"\n [style.transform]=\"toolbarTransform\"></praxis-canvas-toolbar>\n\n @if (formBlocksBefore?.nodes?.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before\" data-editorial-placement=\"before\">\n <praxis-rich-content\n [document]=\"formBlocksBeforeDocument\"\n [context]=\"getEditorialRichContentContext()\"\n ></praxis-rich-content>\n </section>\n }\n\n @if (actionPlacement === 'top' && !(effectivePresentation || effectiveReadonly)) {\n <praxis-form-actions data-actions-placement=\"top\" [actions]=\"effectiveActions\" [isSubmitting]=\"submitting\"\n [formIsValid]=\"effectiveFormIsValid\" [submitError]=\"submitError\" [formId]=\"formId\" [actionOverrides]=\"actionRuleProps\"\n [invalidRequiredFieldLabels]=\"invalidRequiredFieldLabels\"\n [editorialVisualContext]=\"hasEditorialVisualContext()\"\n (action)=\"onFormAction($event)\"></praxis-form-actions>\n }\n\n @for (section of config.sections; track (section.id ?? $index); let sectionIndex = $index;\n let last = $last) {\n <div class=\"section-drop-wrapper\" [attr.data-section-id]=\"section.id\">\n @if (isSectionVisible(section)) {\n <div #sectionEl class=\"form-section canvas-element\" data-canvas-type=\"section\" [attr.data-section-id]=\"section.id\"\n [attr.data-section-index]=\"sectionIndex\" (mouseenter)=\"onElementMouseEnter($event, 'section', section, sectionEl)\"\n (mouseleave)=\"onElementMouseLeave($event)\" (click)=\"onElementClick($event, 'section', section, sectionEl)\"\n [class.selected]=\"selectedElement?.domElement === sectionEl\"\n [class.hovered]=\"hoveredElement?.domElement === sectionEl && selectedElement?.domElement !== sectionEl\"\n [attr.data-section-appearance]=\"getSectionAppearance(section) || null\"\n [style.marginBottom.px]=\"!last ? getSectionGapBottom(section) : null\" [ngClass]=\"getSectionClasses(section)\"\n [ngStyle]=\"getSectionStyles(section)\">\n <div\n class=\"section-heading\"\n [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\"\n [class.step-appearance]=\"getSectionAppearance(section) === 'step'\"\n >\n <div class=\"section-heading-text\" [matTooltip]=\"getSectionHeaderTooltip(section) || null\"\n [matTooltipDisabled]=\"!getSectionHeaderTooltip(section)\">\n @if (getSectionStepLabel(section)) {\n <div class=\"section-step-label\" [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\">\n {{ getSectionStepLabel(section) }}\n </div>\n }\n @if (getSectionTitle(section)) {\n <h3 class=\"section-title\" [class.title-large]=\"getSectionTitleStyle(section) === 'titleLarge'\"\n [class.title-medium]=\"getSectionTitleStyle(section) === 'titleMedium'\"\n [class.title-small]=\"getSectionTitleStyle(section) === 'titleSmall'\"\n [class.headline-small]=\"getSectionTitleStyle(section) === 'headlineSmall'\"\n [style.marginBottom.px]=\"getSectionTitleGapBottom(section) ?? 20\" [style.color]=\"getSectionTitleColor(section) || null\"\n [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\"\n [attr.id]=\"sectionPanelId(section, sectionIndex) + '-title'\">\n @let sectionHeaderVisual = getSectionHeaderVisual(section);\n @let sectionHeaderAvatarSize = getSectionHeaderAvatarSize(section);\n @if (sectionHeaderVisual.kind === 'icon') {\n <mat-icon class=\"section-title-icon\" aria-hidden=\"true\" [praxisIcon]=\"sectionHeaderVisual.icon\"></mat-icon>\n }\n @if (sectionHeaderVisual.kind === 'image') {\n <img class=\"section-title-avatar section-title-avatar-image\" [class.size-sm]=\"sectionHeaderAvatarSize === 'sm'\"\n [class.size-md]=\"sectionHeaderAvatarSize === 'md'\" [class.size-lg]=\"sectionHeaderAvatarSize === 'lg'\"\n [src]=\"sectionHeaderVisual.src\" [alt]=\"sectionHeaderVisual.alt\" />\n }\n @if (sectionHeaderVisual.kind === 'initials') {\n <span class=\"section-title-avatar section-title-avatar-text\" [class.size-sm]=\"sectionHeaderAvatarSize === 'sm'\"\n [class.size-md]=\"sectionHeaderAvatarSize === 'md'\" [class.size-lg]=\"sectionHeaderAvatarSize === 'lg'\" aria-hidden=\"true\">\n {{ sectionHeaderVisual.text }}\n </span>\n @if (sectionHeaderVisual.ariaLabel) {\n <span class=\"section-title-avatar-sr-only\">{{ sectionHeaderVisual.ariaLabel }}</span>\n }\n }\n @if (sectionHeaderVisual.kind === 'placeholder') {\n <span class=\"section-title-avatar section-title-avatar-placeholder\" [class.size-sm]=\"sectionHeaderAvatarSize === 'sm'\"\n [class.size-md]=\"sectionHeaderAvatarSize === 'md'\" [class.size-lg]=\"sectionHeaderAvatarSize === 'lg'\" aria-hidden=\"true\">\n @if (sectionHeaderVisual.icon) {\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"sectionHeaderVisual.icon\"></mat-icon>\n }\n @if (sectionHeaderVisual.text) {\n <span>{{ sectionHeaderVisual.text }}</span>\n }\n </span>\n @if (sectionHeaderVisual.ariaLabel) {\n <span class=\"section-title-avatar-sr-only\">{{ sectionHeaderVisual.ariaLabel }}</span>\n }\n }\n {{ getSectionTitle(section) }}\n @if (enableCustomization) {\n <button type=\"button\" class=\"inline-edit-btn\" mat-icon-button (click)=\"openSectionEditor(section, 'title')\"\n aria-label=\"Editar t\u00EDtulo da se\u00E7\u00E3o\" matTooltip=\"Editar t\u00EDtulo\">\n <mat-icon>edit</mat-icon>\n </button>\n }\n </h3>\n }\n @if (getSectionDescription(section)) {\n <p class=\"section-description\" [class.body-large]=\"getSectionDescriptionStyle(section) === 'bodyLarge'\"\n [class.body-medium]=\"getSectionDescriptionStyle(section) === 'bodyMedium'\"\n [class.body-small]=\"getSectionDescriptionStyle(section) === 'bodySmall'\"\n [style.marginBottom.px]=\"getSectionDescriptionGapBottom(section) ?? 8\" [style.color]=\"getSectionDescriptionColor(section) || null\"\n [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\">\n {{ getSectionDescription(section) }}\n @if (enableCustomization) {\n <button type=\"button\" class=\"inline-edit-btn\" mat-icon-button\n (click)=\"openSectionEditor(section, 'description')\" aria-label=\"Editar descri\u00E7\u00E3o da se\u00E7\u00E3o\"\n matTooltip=\"Editar descri\u00E7\u00E3o\">\n <mat-icon>edit</mat-icon>\n </button>\n }\n </p>\n }\n </div>\n @if (getSectionHeaderActions(section).length) {\n <div class=\"section-heading-actions\" [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\">\n @for (action of getSectionHeaderActions(section); track action.id) {\n <button\n type=\"button\"\n class=\"section-heading-action-btn\"\n mat-icon-button\n [color]=\"getSectionHeaderActionColor(action)\"\n [disabled]=\"isSectionHeaderActionDisabled(action)\"\n [matTooltip]=\"getSectionHeaderActionTooltip(action)\"\n [attr.aria-label]=\"action.label\"\n [attr.aria-busy]=\"action.loading ? 'true' : null\"\n [ngClass]=\"getSectionHeaderActionNgClass(action)\"\n [ngStyle]=\"getSectionHeaderActionStyles(action)\"\n (click)=\"onSectionHeaderActionClick(section, action, $event)\"\n >\n @if (action.loading) {\n <mat-progress-spinner diameter=\"16\" mode=\"indeterminate\"></mat-progress-spinner>\n <span class=\"section-heading-action-sr-only\">{{ getSectionHeaderActionLoadingLabel(action) }}</span>\n } @else {\n <mat-icon [praxisIcon]=\"action.icon\"></mat-icon>\n }\n </button>\n }\n </div>\n }\n @if (isSectionCollapsible(section)) {\n <button type=\"button\" class=\"section-collapse-btn\" mat-icon-button\n (click)=\"toggleSectionCollapse($event, section)\" [attr.aria-expanded]=\"!isSectionCollapsed(section)\"\n [attr.aria-controls]=\"sectionPanelId(section, sectionIndex)\"\n [attr.aria-label]=\"isSectionCollapsed(section) ? 'Expandir se\u00E7\u00E3o' : 'Recolher se\u00E7\u00E3o'\">\n <mat-icon [praxisIcon]=\"isSectionCollapsed(section) ? 'expand_more' : 'expand_less'\"></mat-icon>\n </button>\n }\n </div>\n\n <div class=\"section-body\" [class.collapsed]=\"isSectionCollapsed(section)\"\n [attr.id]=\"sectionPanelId(section, sectionIndex)\"\n [attr.aria-labelledby]=\"getSectionTitle(section) ? sectionPanelId(section, sectionIndex) + '-title' : null\">\n @if (!isSectionCollapsed(section)) {\n @for (row of section.rows; track (row.id ?? $index); let rowIndex = $index) {\n @if (isRowVisible(row)) {\n <div class=\"row-drop-wrapper\" [attr.data-section-id]=\"section.id\">\n <div #rowEl class=\"form-row grid-12 canvas-element\" data-canvas-type=\"row\" [attr.data-row-index]=\"rowIndex\"\n [attr.data-section-id]=\"section.id\" [attr.data-row-id]=\"row.id\" [style.--pfx-grid-gap.px]=\"getRowGap(row)\"\n [style.--pfx-field-gap.px]=\"getRowRowGap(row)\"\n [style.--pfx-mount-index]=\"rowIndex\"\n [style.marginBottom.px]=\"rowIndex < section.rows.length - 1 ? (getRowRowGap(row) ?? null) : null\"\n [ngClass]=\"getRowClasses(row)\" [ngStyle]=\"getRowStyles(row)\"\n (mouseenter)=\"onElementMouseEnter($event, 'row', row, rowEl)\" (mouseleave)=\"onElementMouseLeave($event)\"\n (click)=\"onElementClick($event, 'row', row, rowEl)\" [class.selected]=\"selectedElement?.domElement === rowEl\"\n [class.hovered]=\"hoveredElement?.domElement === rowEl && selectedElement?.domElement !== rowEl\">\n @for (column of row.columns; track (column.id ?? $index); let colIndex = $index) {\n @if (isColumnVisible(column)) {\n <div class=\"column-drop-wrapper\" [attr.data-section-id]=\"section.id\" [attr.data-row-id]=\"row.id\">\n <div #colEl class=\"form-column canvas-element\" [ngClass]=\"getColumnClasses(column)\"\n [style.padding.px]=\"getColumnPadding(column)\" [style.--pfx-field-gap.px]=\"getRowRowGap(row)\"\n [ngStyle]=\"getColumnStyles(column)\" [attr.data-testid]=\"column.testId || null\"\n [attr.data-row-gap]=\"getRowRowGap(row)\" data-canvas-type=\"column\" [attr.data-column-index]=\"colIndex\"\n [attr.data-row-index]=\"rowIndex\" [attr.data-section-id]=\"section.id\" [attr.data-row-id]=\"row.id\"\n [attr.data-column-id]=\"column.id\" (mouseenter)=\"onElementMouseEnter($event, 'column', column, colEl)\"\n (mouseleave)=\"onElementMouseLeave($event)\" (click)=\"onElementClick($event, 'column', column, colEl)\"\n [class.selected]=\"selectedElement?.domElement === colEl\"\n [class.hovered]=\"hoveredElement?.domElement === colEl && selectedElement?.domElement !== colEl\">\n <ng-container dynamicFieldLoader [fields]=\"getColumnFields(column)\" [formGroup]=\"form\"\n [enableExternalControlBinding]=\"true\"\n [readonlyMode]=\"readonlyModeGlobal === null ? null : readonlyModeGlobal\"\n [disabledMode]=\"effectiveDisabledMode\"\n [presentationMode]=\"presentationForLoader\" [visible]=\"visibleGlobal === null ? null : visibleGlobal\"\n [canvasMode]=\"enableCustomization\" (canvasMouseEnter)=\"onFieldMouseEnter($event)\"\n (canvasMouseLeave)=\"onFieldMouseLeave($event)\" (canvasClick)=\"onFieldClick($event)\"\n (renderError)=\"onFieldRenderError($event)\">\n </ng-container>\n </div>\n </div>\n } }\n </div>\n </div>\n }\n }\n } @else {\n <div class=\"section-collapsed-placeholder\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'unfold_more'\"></mat-icon>\n <span>{{ getSectionCollapsedSummary(section) }}</span>\n </div>\n }\n @if (last && beforeActionsPlacement === 'insideLastSection' && formBlocksBeforeActions?.nodes?.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before-actions\" data-editorial-placement=\"beforeActions\">\n <praxis-rich-content\n [document]=\"formBlocksBeforeActionsDocument\"\n [context]=\"getEditorialRichContentContext()\"\n ></praxis-rich-content>\n </section>\n }\n @if (actionPlacement === 'insideLastSection' && last && !(effectivePresentation\n || effectiveReadonly)) {\n <praxis-form-actions data-actions-placement=\"insideLastSection\" [actions]=\"effectiveActions\"\n [isSubmitting]=\"submitting\" [formIsValid]=\"effectiveFormIsValid\" [submitError]=\"submitError\" [formId]=\"formId\"\n [actionOverrides]=\"actionRuleProps\" [invalidRequiredFieldLabels]=\"invalidRequiredFieldLabels\"\n [editorialVisualContext]=\"hasEditorialVisualContext()\"\n (action)=\"onFormAction($event)\"></praxis-form-actions>\n }\n </div>\n <!-- Overlay de bloqueio durante submiss\u00E3o -->\n @if (submitting) {\n <div class=\"form-blocking-overlay\" aria-live=\"polite\">\n <mat-progress-spinner diameter=\"40\" color=\"primary\"></mat-progress-spinner>\n <p>{{ config.messages?.loading?.submit || 'Salvando...' }}</p>\n </div>\n }\n </div>\n\n @if (enableCustomization && selectedElement?.domElement === sectionEl) {\n <div class=\"add-section-container\">\n <div class=\"add-section-line\"></div>\n <button mat-fab color=\"primary\" aria-label=\"Adicionar nova se\u00E7\u00E3o\" (click)=\"addNewSectionAfter(sectionIndex)\"\n matTooltip=\"Adicionar nova se\u00E7\u00E3o aqui\">\n <mat-icon [praxisIcon]=\"'add'\"></mat-icon>\n </button>\n <div class=\"add-section-line\"></div>\n </div>\n }\n }\n </div>\n }\n\n @if (beforeActionsPlacement !== 'insideLastSection' && formBlocksBeforeActions?.nodes?.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before-actions\" data-editorial-placement=\"beforeActions\">\n <praxis-rich-content\n [document]=\"formBlocksBeforeActionsDocument\"\n [context]=\"getEditorialRichContentContext()\"\n ></praxis-rich-content>\n </section>\n }\n\n @if (actionPlacement === 'afterSections' && !(effectivePresentation || effectiveReadonly)) {\n <praxis-form-actions data-actions-placement=\"afterSections\" [actions]=\"effectiveActions\" [isSubmitting]=\"submitting\"\n [formIsValid]=\"effectiveFormIsValid\" [submitError]=\"submitError\" [formId]=\"formId\"\n [actionOverrides]=\"actionRuleProps\" [invalidRequiredFieldLabels]=\"invalidRequiredFieldLabels\"\n [editorialVisualContext]=\"hasEditorialVisualContext()\"\n (action)=\"onFormAction($event)\"></praxis-form-actions>\n }\n\n @if (formBlocksAfter?.nodes?.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-after\" data-editorial-placement=\"after\">\n <praxis-rich-content\n [document]=\"formBlocksAfterDocument\"\n [context]=\"getEditorialRichContentContext()\"\n ></praxis-rich-content>\n </section>\n }\n</form>\n@if (!enableCustomization && mode === 'view') {\n<div class=\"ai-floating-assistant\">\n <praxis-ai-assistant [adapter]=\"aiAdapter\"></praxis-ai-assistant>\n</div>\n}\n}\n", styles: ["@charset \"UTF-8\";.span-xs-1{grid-column:span 1}.span-xs-2{grid-column:span 2}.span-xs-3{grid-column:span 3}.span-xs-4{grid-column:span 4}.span-xs-5{grid-column:span 5}.span-xs-6{grid-column:span 6}.span-xs-7{grid-column:span 7}.span-xs-8{grid-column:span 8}.span-xs-9{grid-column:span 9}.span-xs-10{grid-column:span 10}.span-xs-11{grid-column:span 11}.span-xs-12{grid-column:span 12}.offset-xs-0{margin-left:0%}.offset-xs-1{margin-left:calc(1 / 12 * 100%)}.offset-xs-2{margin-left:calc(2 / 12 * 100%)}.offset-xs-3{margin-left:25%}.offset-xs-4{margin-left:calc(4 / 12 * 100%)}.offset-xs-5{margin-left:calc(5 / 12 * 100%)}.offset-xs-6{margin-left:50%}.offset-xs-7{margin-left:calc(7 / 12 * 100%)}.offset-xs-8{margin-left:calc(8 / 12 * 100%)}.offset-xs-9{margin-left:75%}.offset-xs-10{margin-left:calc(10 / 12 * 100%)}.offset-xs-11{margin-left:calc(11 / 12 * 100%)}.order-xs--12{order:-12}.order-xs--11{order:-11}.order-xs--10{order:-10}.order-xs--9{order:-9}.order-xs--8{order:-8}.order-xs--7{order:-7}.order-xs--6{order:-6}.order-xs--5{order:-5}.order-xs--4{order:-4}.order-xs--3{order:-3}.order-xs--2{order:-2}.order-xs--1{order:-1}.order-xs-0{order:0}.order-xs-1{order:1}.order-xs-2{order:2}.order-xs-3{order:3}.order-xs-4{order:4}.order-xs-5{order:5}.order-xs-6{order:6}.order-xs-7{order:7}.order-xs-8{order:8}.order-xs-9{order:9}.order-xs-10{order:10}.order-xs-11{order:11}.order-xs-12{order:12}.hidden-xs{display:none}@media(min-width:600px){.span-sm-1{grid-column:span 1}.span-sm-2{grid-column:span 2}.span-sm-3{grid-column:span 3}.span-sm-4{grid-column:span 4}.span-sm-5{grid-column:span 5}.span-sm-6{grid-column:span 6}.span-sm-7{grid-column:span 7}.span-sm-8{grid-column:span 8}.span-sm-9{grid-column:span 9}.span-sm-10{grid-column:span 10}.span-sm-11{grid-column:span 11}.span-sm-12{grid-column:span 12}.offset-sm-0{margin-left:0%}.offset-sm-1{margin-left:calc(1 / 12 * 100%)}.offset-sm-2{margin-left:calc(2 / 12 * 100%)}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:calc(4 / 12 * 100%)}.offset-sm-5{margin-left:calc(5 / 12 * 100%)}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:calc(7 / 12 * 100%)}.offset-sm-8{margin-left:calc(8 / 12 * 100%)}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:calc(10 / 12 * 100%)}.offset-sm-11{margin-left:calc(11 / 12 * 100%)}.order-sm--12{order:-12}.order-sm--11{order:-11}.order-sm--10{order:-10}.order-sm--9{order:-9}.order-sm--8{order:-8}.order-sm--7{order:-7}.order-sm--6{order:-6}.order-sm--5{order:-5}.order-sm--4{order:-4}.order-sm--3{order:-3}.order-sm--2{order:-2}.order-sm--1{order:-1}.order-sm-0{order:0}.order-sm-1{order:1}.order-sm-2{order:2}.order-sm-3{order:3}.order-sm-4{order:4}.order-sm-5{order:5}.order-sm-6{order:6}.order-sm-7{order:7}.order-sm-8{order:8}.order-sm-9{order:9}.order-sm-10{order:10}.order-sm-11{order:11}.order-sm-12{order:12}.hidden-sm{display:none}}@media(min-width:900px){.span-md-1{grid-column:span 1}.span-md-2{grid-column:span 2}.span-md-3{grid-column:span 3}.span-md-4{grid-column:span 4}.span-md-5{grid-column:span 5}.span-md-6{grid-column:span 6}.span-md-7{grid-column:span 7}.span-md-8{grid-column:span 8}.span-md-9{grid-column:span 9}.span-md-10{grid-column:span 10}.span-md-11{grid-column:span 11}.span-md-12{grid-column:span 12}.offset-md-0{margin-left:0%}.offset-md-1{margin-left:calc(1 / 12 * 100%)}.offset-md-2{margin-left:calc(2 / 12 * 100%)}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:calc(4 / 12 * 100%)}.offset-md-5{margin-left:calc(5 / 12 * 100%)}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:calc(7 / 12 * 100%)}.offset-md-8{margin-left:calc(8 / 12 * 100%)}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:calc(10 / 12 * 100%)}.offset-md-11{margin-left:calc(11 / 12 * 100%)}.order-md--12{order:-12}.order-md--11{order:-11}.order-md--10{order:-10}.order-md--9{order:-9}.order-md--8{order:-8}.order-md--7{order:-7}.order-md--6{order:-6}.order-md--5{order:-5}.order-md--4{order:-4}.order-md--3{order:-3}.order-md--2{order:-2}.order-md--1{order:-1}.order-md-0{order:0}.order-md-1{order:1}.order-md-2{order:2}.order-md-3{order:3}.order-md-4{order:4}.order-md-5{order:5}.order-md-6{order:6}.order-md-7{order:7}.order-md-8{order:8}.order-md-9{order:9}.order-md-10{order:10}.order-md-11{order:11}.order-md-12{order:12}.hidden-md{display:none}}@media(min-width:1200px){.span-lg-1{grid-column:span 1}.span-lg-2{grid-column:span 2}.span-lg-3{grid-column:span 3}.span-lg-4{grid-column:span 4}.span-lg-5{grid-column:span 5}.span-lg-6{grid-column:span 6}.span-lg-7{grid-column:span 7}.span-lg-8{grid-column:span 8}.span-lg-9{grid-column:span 9}.span-lg-10{grid-column:span 10}.span-lg-11{grid-column:span 11}.span-lg-12{grid-column:span 12}.offset-lg-0{margin-left:0%}.offset-lg-1{margin-left:calc(1 / 12 * 100%)}.offset-lg-2{margin-left:calc(2 / 12 * 100%)}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:calc(4 / 12 * 100%)}.offset-lg-5{margin-left:calc(5 / 12 * 100%)}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:calc(7 / 12 * 100%)}.offset-lg-8{margin-left:calc(8 / 12 * 100%)}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:calc(10 / 12 * 100%)}.offset-lg-11{margin-left:calc(11 / 12 * 100%)}.order-lg--12{order:-12}.order-lg--11{order:-11}.order-lg--10{order:-10}.order-lg--9{order:-9}.order-lg--8{order:-8}.order-lg--7{order:-7}.order-lg--6{order:-6}.order-lg--5{order:-5}.order-lg--4{order:-4}.order-lg--3{order:-3}.order-lg--2{order:-2}.order-lg--1{order:-1}.order-lg-0{order:0}.order-lg-1{order:1}.order-lg-2{order:2}.order-lg-3{order:3}.order-lg-4{order:4}.order-lg-5{order:5}.order-lg-6{order:6}.order-lg-7{order:7}.order-lg-8{order:8}.order-lg-9{order:9}.order-lg-10{order:10}.order-lg-11{order:11}.order-lg-12{order:12}.hidden-lg{display:none}}@media(min-width:1536px){.span-xl-1{grid-column:span 1}.span-xl-2{grid-column:span 2}.span-xl-3{grid-column:span 3}.span-xl-4{grid-column:span 4}.span-xl-5{grid-column:span 5}.span-xl-6{grid-column:span 6}.span-xl-7{grid-column:span 7}.span-xl-8{grid-column:span 8}.span-xl-9{grid-column:span 9}.span-xl-10{grid-column:span 10}.span-xl-11{grid-column:span 11}.span-xl-12{grid-column:span 12}.offset-xl-0{margin-left:0%}.offset-xl-1{margin-left:calc(1 / 12 * 100%)}.offset-xl-2{margin-left:calc(2 / 12 * 100%)}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:calc(4 / 12 * 100%)}.offset-xl-5{margin-left:calc(5 / 12 * 100%)}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:calc(7 / 12 * 100%)}.offset-xl-8{margin-left:calc(8 / 12 * 100%)}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:calc(10 / 12 * 100%)}.offset-xl-11{margin-left:calc(11 / 12 * 100%)}.order-xl--12{order:-12}.order-xl--11{order:-11}.order-xl--10{order:-10}.order-xl--9{order:-9}.order-xl--8{order:-8}.order-xl--7{order:-7}.order-xl--6{order:-6}.order-xl--5{order:-5}.order-xl--4{order:-4}.order-xl--3{order:-3}.order-xl--2{order:-2}.order-xl--1{order:-1}.order-xl-0{order:0}.order-xl-1{order:1}.order-xl-2{order:2}.order-xl-3{order:3}.order-xl-4{order:4}.order-xl-5{order:5}.order-xl-6{order:6}.order-xl-7{order:7}.order-xl-8{order:8}.order-xl-9{order:9}.order-xl-10{order:10}.order-xl-11{order:11}.order-xl-12{order:12}.hidden-xl{display:none}}.canvas-mode-enabled{--canvas-hit: 14px}.canvas-mode-enabled .canvas-element{position:relative;z-index:0;border-radius:8px;outline:2px solid transparent;outline-offset:2px;transition:outline-color .2s ease,outline-style .2s ease}.canvas-mode-enabled .canvas-element:before{content:\"\";position:absolute;inset:calc(-1 * var(--canvas-hit));pointer-events:none;border-radius:inherit;background:transparent}.canvas-mode-enabled .canvas-element[data-canvas-type=section]{--outline-color: var(--md-sys-color-primary)}.canvas-mode-enabled .canvas-element[data-canvas-type=row]{--outline-color: var(--md-sys-color-secondary)}.canvas-mode-enabled .canvas-element[data-canvas-type=column],.canvas-mode-enabled .canvas-element[data-canvas-type=field]{--outline-color: var(--md-sys-color-tertiary)}.canvas-mode-enabled .canvas-element[data-canvas-type=actions]{--outline-color: var(--md-sys-color-primary)}.canvas-mode-enabled .canvas-element.hovered:not(.selected){outline-color:var(--outline-color, var(--md-sys-color-primary));outline-style:dashed}.canvas-mode-enabled .canvas-element.selected{outline-color:var(--outline-color, var(--md-sys-color-primary));outline-style:solid;box-shadow:0 0 0 2px var(--outline-color, var(--md-sys-color-primary))}.canvas-mode-enabled .canvas-element.hovered{z-index:var(--praxis-layer-authoring-hover, 300)}.canvas-mode-enabled .canvas-element.selected{z-index:var(--praxis-layer-authoring-selected, 320)}.section-drop-wrapper,.row-drop-wrapper,.column-drop-wrapper{display:contents}.add-section-container{display:flex;align-items:center;justify-content:center;padding:.5rem 0;margin:-.5rem 0;position:relative;z-index:var(--praxis-layer-authoring-insert, 280)}.add-section-container .add-section-line{flex-grow:1;height:1px;background:repeating-linear-gradient(90deg,var(--md-sys-color-outline-variant),var(--md-sys-color-outline-variant) 4px,transparent 4px,transparent 8px)}.add-section-container button{margin:0 1rem;transform:scale(.85)}:host{display:block;position:relative}.form-config-controls{position:sticky;top:10px;margin-left:auto;margin-bottom:10px;display:flex;align-items:center;gap:.35rem;z-index:60;background:color-mix(in srgb,var(--md-sys-color-surface) 92%,transparent);padding:6px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 82%,transparent);border-radius:999px;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);box-shadow:0 10px 22px #0f172a14;min-width:0;justify-content:flex-end;pointer-events:none;opacity:.88;transition:opacity .16s ease,box-shadow .16s ease,border-color .16s ease,transform .16s ease}.form-config-controls>*{pointer-events:auto}.praxis-dynamic-form:hover .form-config-controls,.praxis-dynamic-form:focus-within .form-config-controls{opacity:1;border-color:color-mix(in srgb,var(--md-sys-color-primary) 26%,var(--md-sys-color-outline-variant) 74%);box-shadow:0 14px 28px #0f172a1f;transform:translateY(-1px)}.form-config-controls .mat-icon-button{--mdc-icon-button-state-layer-size: 34px;width:34px;height:34px;color:var(--md-sys-color-on-surface-variant);background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 82%,transparent);border:1px solid transparent}.form-config-controls .mat-icon-button:hover{color:var(--md-sys-color-primary);background:var(--md-sys-color-primary-container);border-color:color-mix(in srgb,var(--md-sys-color-primary) 20%,transparent)}.ai-floating-assistant{position:sticky;right:0;bottom:12px;margin-top:14px;margin-left:auto;width:fit-content;z-index:70;pointer-events:none}.ai-floating-assistant praxis-ai-assistant{pointer-events:auto;display:inline-flex}.ai-floating-assistant .ai-trigger-btn{box-shadow:0 12px 28px #0f172a24}.config-button{color:var(--md-sys-color-primary)}.form-loading{display:flex;flex-direction:column;align-items:stretch;justify-content:center;padding:1.25rem;text-align:left;color:var(--md-sys-color-on-surface);gap:1rem;min-height:220px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 14%,var(--md-sys-color-outline-variant) 86%);border-radius:8px;background:linear-gradient(135deg,color-mix(in srgb,var(--md-sys-color-primary) 8%,transparent),transparent 42%),color-mix(in srgb,var(--md-sys-color-surface-container-low) 78%,var(--md-sys-color-surface) 22%);box-shadow:0 12px 28px #0f172a14}.form-loading-header{display:flex;align-items:center;gap:.85rem}.form-loading-copy{min-width:0}.form-loading p{margin:0}.form-loading-title{font-weight:700;color:var(--md-sys-color-on-surface)}.form-loading-subtitle{margin-top:.2rem;font-size:.875rem;color:var(--md-sys-color-on-surface-variant)}.form-loading-skeleton{display:grid;gap:.7rem}.form-loading-line,.form-loading-field{display:block;overflow:hidden;position:relative;border-radius:6px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high) 74%,var(--md-sys-color-primary) 6%)}.form-loading-line:after,.form-loading-field:after{content:\"\";position:absolute;inset:0;transform:translate(-100%);background:linear-gradient(90deg,transparent,color-mix(in srgb,var(--md-sys-color-primary) 16%,white 18%),transparent);animation:form-loading-shimmer 1.4s ease-in-out infinite}.form-loading-line{height:12px}.form-loading-line-title{width:min(42%,240px)}.form-loading-line-short{width:min(64%,360px)}.form-loading-field{height:44px}.form-loading-field-wide{height:72px}@keyframes form-loading-shimmer{to{transform:translate(100%)}}@media(prefers-reduced-motion:reduce){.form-loading-line:after,.form-loading-field:after{animation:none;transform:none;opacity:.28}}.form-error{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3rem;text-align:center;color:var(--md-sys-color-error);gap:1rem;border:1px solid var(--md-sys-color-error);border-radius:8px;background-color:var(--md-sys-color-error-container);margin:1rem;box-shadow:0 12px 30px #7f1d1d1f}.form-error h3{margin:0;color:var(--md-sys-color-on-error-container)}.form-error p{margin:0;color:var(--md-sys-color-on-error-container);opacity:.8}.form-error button{margin-top:.5rem}.pfx-form-info-banner{margin-bottom:14px;padding:12px 14px;border-radius:16px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 18%,var(--md-sys-color-outline-variant) 82%);background:color-mix(in srgb,var(--md-sys-color-primary-container) 24%,var(--md-sys-color-surface) 76%);color:var(--md-sys-color-on-surface);display:flex;align-items:flex-start;justify-content:space-between;gap:14px}.pfx-form-info-banner .text{font-size:.92rem;line-height:1.5;font-weight:600}.pfx-form-info-banner .actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px}.praxis-dynamic-form{display:flex;flex-direction:column;transition:all .3s ease;position:relative;font-family:inherit;font-size:inherit;color:inherit;--pfx-editorial-form-surface: var( --pfx-form-section-surface, var(--md-sys-color-surface-container) );--pfx-editorial-form-surface-muted: var(--md-sys-color-surface-container-low);--pfx-editorial-form-border: var( --pfx-form-stroke, var(--md-sys-color-outline-variant) );--pfx-editorial-form-text: var(--md-sys-color-on-surface);--pfx-editorial-form-text-muted: var(--md-sys-color-on-surface-variant);--pfx-editorial-form-field-surface: color-mix( in srgb, var(--pfx-editorial-form-surface-muted) 76%, var(--pfx-editorial-form-surface) 24% );--pfx-editorial-form-field-outline: color-mix( in srgb, var(--pfx-editorial-form-border) 88%, transparent );--pfx-editorial-form-accent: var(--md-sys-color-primary);--pfx-editorial-form-accent-text: var(--md-sys-color-on-primary);--pfx-editorial-form-radius: var(--pfx-form-section-radius, 8px);--pfx-editorial-form-field-radius: var(--pfx-form-field-radius, 4px);--pfx-editorial-form-border-width: 1px;--pfx-editorial-form-field-border-width: 1px;--pfx-editorial-form-shadow: none;--pfx-form-shell-surface: color-mix( in srgb, var(--pfx-editorial-form-surface) 36%, transparent );--pfx-form-section-surface-flat: color-mix( in srgb, var(--pfx-editorial-form-surface) 78%, var(--pfx-editorial-form-surface-muted) 22% );--pfx-form-section-divider: color-mix( in srgb, var(--pfx-editorial-form-border) 68%, transparent );--pfx-form-label-strong: color-mix( in srgb, var(--pfx-editorial-form-text) 96%, white 4% );--pfx-form-label-muted: color-mix( in srgb, var(--pfx-editorial-form-text-muted) 92%, var(--pfx-editorial-form-text) 8% );--pfx-form-field-surface-rest: color-mix( in srgb, var(--pfx-editorial-form-field-surface) 82%, var(--pfx-editorial-form-surface) 18% );--pfx-form-field-surface-focus: color-mix( in srgb, var(--pfx-editorial-form-accent) 4%, var(--pfx-form-field-surface-rest) 96% );--pfx-form-field-min-height: 48px;--pfx-form-section-padding: clamp(1.1rem, 1.8vw, 1.5rem);--pfx-form-section-gap: 22px;--pfx-form-footer-surface: color-mix( in srgb, var(--pfx-editorial-form-surface) 70%, var(--pfx-editorial-form-surface-muted) 30% );--pfx-form-footer-border: color-mix( in srgb, var(--pfx-editorial-form-border) 76%, transparent )}.praxis-dynamic-form.editorial-visual-context{font-family:var(--editorial-body-font-family, inherit);font-size:var(--editorial-body-size, 1rem);color:var(--editorial-text-primary, var(--md-sys-color-on-surface));--pfx-editorial-form-surface: var( --editorial-surface-primary, var(--pfx-form-section-surface, var(--md-sys-color-surface-container)) );--pfx-editorial-form-surface-muted: var( --editorial-surface-secondary, var(--md-sys-color-surface-container-low) );--pfx-editorial-form-border: var( --editorial-border-color, var(--pfx-form-stroke, var(--md-sys-color-outline-variant)) );--pfx-editorial-form-text: var( --editorial-text-primary, var(--md-sys-color-on-surface) );--pfx-editorial-form-text-muted: var( --editorial-text-secondary, var(--md-sys-color-on-surface-variant) );--pfx-editorial-form-accent: var( --editorial-cta-primary, var(--editorial-accent, var(--md-sys-color-primary)) );--pfx-editorial-form-accent-text: var( --editorial-cta-primary-text, var(--editorial-accent-contrast, var(--md-sys-color-on-primary)) );--pfx-editorial-form-radius: var(--editorial-card-radius, 18px);--pfx-editorial-form-field-radius: var( --editorial-field-radius, var(--editorial-card-radius, 14px) );--pfx-editorial-form-border-width: var(--editorial-card-border-width, 1px);--pfx-editorial-form-field-border-width: var(--editorial-field-border-width, 1px);--pfx-editorial-form-shadow: var(--editorial-card-shadow, none);--md-sys-color-surface: var( --pfx-editorial-form-surface, var(--md-sys-color-surface) );--md-sys-color-surface-container: var( --pfx-editorial-form-surface, var(--md-sys-color-surface-container) );--md-sys-color-surface-container-low: var( --pfx-editorial-form-surface-muted, var(--md-sys-color-surface-container-low) );--md-sys-color-surface-variant: var( --pfx-editorial-form-field-surface, var(--md-sys-color-surface-variant) );--md-sys-color-outline: var( --pfx-editorial-form-field-outline, var(--md-sys-color-outline) );--md-sys-color-outline-variant: var( --pfx-editorial-form-border, var(--md-sys-color-outline-variant) );--md-sys-color-on-surface: var( --pfx-editorial-form-text, var(--md-sys-color-on-surface) );--md-sys-color-on-surface-variant: var( --pfx-editorial-form-text-muted, var(--md-sys-color-on-surface-variant) );--md-sys-color-primary: var( --pfx-editorial-form-accent, var(--md-sys-color-primary) );--md-sys-color-on-primary: var( --pfx-editorial-form-accent-text, var(--md-sys-color-on-primary) );--md-sys-color-on-surface-rgb: var( --editorial-text-primary-rgb, var(--md-sys-color-on-surface-rgb) );--mat-sys-on-surface: var( --pfx-editorial-form-text, var(--mat-sys-on-surface) );--mat-sys-on-surface-variant: var( --pfx-editorial-form-text-muted, var(--mat-sys-on-surface-variant) );--mat-sys-on-surface-rgb: var( --editorial-text-primary-rgb, var(--mat-sys-on-surface-rgb, var(--md-sys-color-on-surface-rgb)) );color:var(--pfx-editorial-form-text);color-scheme:light}.praxis-dynamic-form.presentation-mode .form-row,.praxis-dynamic-form.readonly-mode .form-row{gap:.5rem;margin-bottom:.5rem}.praxis-dynamic-form.presentation-mode .form-section,.praxis-dynamic-form.readonly-mode .form-section{padding:.75rem .875rem}.praxis-dynamic-form.pres-compact .form-row{gap:.35rem;margin-bottom:.35rem}.praxis-dynamic-form.pres-compact .form-section{padding:.5rem .75rem}.praxis-dynamic-form.pres-label-left .praxis-presentation{display:grid;grid-template-columns:var(--pfx-presentation-label-w, 220px) 1fr;align-items:baseline;column-gap:10px}.praxis-dynamic-form.pres-label-left .praxis-presentation__label{text-align:right;margin-bottom:0}.form-section{border:1px solid var(--pfx-form-section-divider);border-radius:var(--pfx-editorial-form-radius);padding:var(--pfx-form-section-padding);background:var(--pfx-form-section-surface-flat);box-shadow:none;transition:all .2s ease;position:relative}.praxis-dynamic-form.editorial-visual-context .form-section{border:var(--pfx-editorial-form-border-width) solid var(--pfx-editorial-form-border)!important;background:var(--pfx-editorial-form-surface)!important;box-shadow:var(--editorial-card-shadow, none)}.praxis-dynamic-form.editorial-visual-context .form-section.section-appearance-step{background-color:var(--pfx-editorial-form-surface)!important;background-image:linear-gradient(180deg,color-mix(in srgb,var(--editorial-accent, var(--md-sys-color-primary)) 6%,transparent) 0%,transparent 132px)!important;background-repeat:no-repeat!important}.section-drop-wrapper>.form-section{margin-bottom:var(--pfx-section-gap, 20px)}.section-drop-wrapper:last-of-type>.form-section{margin-bottom:0}.praxis-dynamic-form>praxis-form-actions[data-actions-placement=afterSections] .form-actions{margin-top:var(--pfx-actions-gap-top, var(--pfx-section-gap, 20px))}.section-title{margin:0 0 var(--pfx-section-title-mb, 12px) 0;font-family:var(--editorial-title-font-family, var(--editorial-body-font-family, inherit));font-size:var(--editorial-step-title-size, 1.12rem);font-weight:700;color:var(--pfx-form-label-strong)}.section-heading{display:flex;align-items:flex-start;gap:12px;margin-bottom:18px;padding-bottom:18px;border-bottom:1px solid var(--pfx-form-section-divider)}.section-heading.align-center{flex-direction:column;align-items:center;text-align:center}.section-heading.align-center .section-heading-text{display:grid;justify-items:center}.section-step-label{display:inline-flex;align-items:center;min-height:24px;padding:0 10px;margin:0 0 8px;border-radius:999px;background:color-mix(in srgb,var(--pfx-editorial-form-accent) 10%,transparent);color:color-mix(in srgb,var(--pfx-editorial-form-accent) 84%,var(--pfx-form-label-strong) 16%);font-size:.72rem;font-weight:800;font-family:var(--editorial-title-font-family, var(--editorial-body-font-family, inherit));letter-spacing:.08em;text-transform:uppercase}.section-heading-text{flex:1 1 auto;min-width:0}.section-heading-actions{display:inline-flex;align-items:center;align-self:flex-start;flex-wrap:wrap;gap:4px;margin-left:auto}.section-heading-actions.align-center{align-self:center;margin-left:0}.section-heading-action-btn{color:var(--pfx-form-label-muted)}.section-heading-action-btn .mat-mdc-progress-spinner,.section-heading-action-btn mat-progress-spinner{--mdc-circular-progress-active-indicator-color: currentColor}.section-heading-action-btn.loading{opacity:.72;pointer-events:none}.section-heading-action-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.section-collapse-btn{margin-left:4px;color:var(--pfx-form-label-muted)}.section-title.title-large{font:var(--mdc-typography-title-large, 500 22px/28px system-ui)}.section-title.title-medium{font:var(--mdc-typography-title-medium, 500 16px/24px system-ui)}.section-title.title-small{font:var(--mdc-typography-title-small, 500 14px/20px system-ui)}.section-title.headline-small{font:var(--mdc-typography-headline-small, 600 24px/32px system-ui)}.section-title.title-large,.section-title.title-medium,.section-title.title-small,.section-title.headline-small{font-family:var(--editorial-title-font-family, var(--editorial-body-font-family, inherit));font-weight:var(--editorial-title-weight, 600)}.section-description{margin:0;font-family:var(--editorial-body-font-family, inherit);font-size:.93rem;color:var(--pfx-form-label-muted);line-height:1.6}.section-description.body-large{font:var(--mdc-typography-body-large, 400 16px/24px system-ui)}.section-description.body-medium{font:var(--mdc-typography-body-medium, 400 14px/20px system-ui)}.section-description.body-small{font:var(--mdc-typography-body-small, 400 12px/16px system-ui)}.section-description.body-large,.section-description.body-medium,.section-description.body-small{font-family:var(--editorial-body-font-family, inherit);font-weight:var(--editorial-body-weight, 400)}.section-title.align-center,.section-description.align-center,.section-step-label.align-center{text-align:center}.form-section.section-appearance-plain{border-color:transparent;border-radius:0;padding:0;background:transparent}.form-section.section-appearance-plain .section-heading{margin-bottom:14px}.form-section.section-appearance-step{border-radius:var(--pfx-editorial-form-radius);padding:var(--pfx-form-section-padding);background:var(--pfx-form-section-surface-flat);border-color:var(--pfx-form-section-divider);box-shadow:none}.praxis-dynamic-form.editorial-visual-context .form-section.section-appearance-step{border-radius:var(--editorial-card-radius, 20px);box-shadow:none}.form-section.section-appearance-step .section-heading{margin-bottom:22px;padding-bottom:16px;border-bottom:1px solid var(--pfx-form-section-divider)}.form-section.section-appearance-step .section-title{font:var(--mdc-typography-title-large, 700 22px/28px system-ui);color:var(--pfx-form-label-strong);margin-bottom:8px}.form-section.section-appearance-step .section-description{color:var(--pfx-form-label-muted);max-width:60ch}.form-section.section-appearance-step .section-step-label{min-height:24px;padding:0 10px;margin-bottom:10px;box-shadow:none}.form-section.section-appearance-step .section-heading.align-center .section-description{max-width:52ch}.form-section.section-appearance-step .section-body{display:grid;gap:8px;padding-top:0}.form-section.section-appearance-step .form-row{margin-bottom:var(--pfx-field-gap, 1rem)}.form-section.section-appearance-step .form-column{gap:var(--pfx-field-gap, 12px)}.form-section .form-editorial-blocks{display:grid;gap:16px}.form-section .form-editorial-blocks[data-editorial-placement=beforeActions]{margin-top:24px;padding-top:18px;border-top:1px solid var(--pfx-form-section-divider)}.form-section.section-appearance-step .form-editorial-blocks[data-editorial-placement=beforeActions]{gap:18px}.form-section.section-appearance-step .form-editorial-blocks[data-editorial-placement=beforeActions]>*+*{margin-top:2px}.praxis-dynamic-form>praxis-form-actions[data-actions-placement=afterSections] .form-actions{margin-top:18px;padding-top:0}.praxis-dynamic-form>.form-editorial-blocks[data-editorial-placement=after]{margin-top:6px}:host-context(.mdc-theme-dark) .form-section.section-appearance-step,:host-context(.theme-dark) .form-section.section-appearance-step{border-color:color-mix(in srgb,var(--pfx-editorial-form-border) 82%,transparent);box-shadow:none}:host-context(.mdc-theme-dark) .form-section.section-appearance-step .section-title,:host-context(.theme-dark) .form-section.section-appearance-step .section-title{color:color-mix(in srgb,var(--pfx-editorial-form-text) 96%,white)}:host-context(.mdc-theme-dark) .form-section.section-appearance-step .section-description,:host-context(.theme-dark) .form-section.section-appearance-step .section-description{color:color-mix(in srgb,var(--pfx-editorial-form-text-muted) 86%,var(--pfx-editorial-form-text) 14%)}:host-context(.mdc-theme-dark) .section-step-label,:host-context(.theme-dark) .section-step-label{background:color-mix(in srgb,var(--md-sys-color-primary-container) 54%,transparent);color:color-mix(in srgb,var(--md-sys-color-primary) 88%,white)}:host-context(.mdc-theme-dark) .form-section .form-editorial-blocks[data-editorial-placement=beforeActions],:host-context(.theme-dark) .form-section .form-editorial-blocks[data-editorial-placement=beforeActions]{border-top-color:color-mix(in srgb,var(--md-sys-color-outline-variant) 90%,transparent)}.inline-edit-btn{margin-left:6px;vertical-align:middle;--mdc-icon-button-size: 28px;--mdc-icon-button-icon-size: 16px}.inline-edit-btn mat-icon{font-size:16px;width:16px;height:16px}.section-body.collapsed{border:1px dashed var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low);border-radius:6px;padding:8px 10px}.section-collapsed-placeholder{display:flex;align-items:center;gap:8px;color:var(--md-sys-color-on-surface-variant);font-size:.95rem}.section-collapsed-placeholder mat-icon{font-size:20px;width:20px;height:20px}.form-row{display:flex;gap:1.1rem;margin-bottom:var(--pfx-field-gap, 1.1rem);transition:all .2s ease;border-radius:6px;position:relative}.praxis-dynamic-form.pfx-mounting .form-row{opacity:0;transform:translateY(var(--pdx-form-mount-offset, 6px));animation:pdxFormMount var(--pdx-form-mount-duration, .16s) ease-out both;animation-delay:calc(var(--pfx-mount-index, 0) * var(--pdx-form-mount-stagger, 20ms))}@media(prefers-reduced-motion:reduce){.praxis-dynamic-form.pfx-mounting .form-row{animation:none;opacity:1;transform:none}}@keyframes pdxFormMount{to{opacity:1;transform:translateY(0)}}.praxis-dynamic-form .mat-mdc-form-field{width:100%;margin-bottom:var(--pfx-field-gap, 10px);font-family:inherit;--mdc-filled-text-field-container-color: var(--pfx-form-field-surface-rest);--mdc-filled-text-field-focus-active-indicator-color: var(--pfx-editorial-form-accent);--mdc-filled-text-field-active-indicator-color: var(--pfx-editorial-form-field-outline);--mdc-filled-text-field-hover-active-indicator-color: var(--pfx-editorial-form-accent);--mdc-filled-text-field-focus-label-text-color: var(--pfx-editorial-form-accent);--mdc-filled-text-field-label-text-color: var(--pfx-form-label-muted);--mdc-filled-text-field-input-text-color: var(--pfx-editorial-form-text);--mdc-filled-text-field-caret-color: var(--pfx-editorial-form-accent);--mdc-filled-text-field-input-text-placeholder-color: color-mix( in srgb, var(--pfx-editorial-form-text-muted) 82%, transparent );--mdc-outlined-text-field-outline-color: var(--pfx-editorial-form-field-outline);--mdc-outlined-text-field-hover-outline-color: var(--pfx-editorial-form-accent);--mdc-outlined-text-field-focus-outline-color: var(--pfx-editorial-form-accent);--mdc-outlined-text-field-focus-label-text-color: var(--pfx-editorial-form-accent);--mdc-outlined-text-field-label-text-color: var(--pfx-form-label-muted);--mdc-outlined-text-field-input-text-color: var(--pfx-editorial-form-text);--mdc-filled-text-field-container-shape: var(--pfx-editorial-form-field-radius);--mdc-outlined-text-field-container-shape: var(--pfx-editorial-form-field-radius);--mdc-filled-text-field-active-indicator-height: var(--pfx-editorial-form-field-border-width);--mdc-filled-text-field-focus-active-indicator-height: var(--pfx-editorial-form-field-border-width);--mdc-outlined-text-field-outline-width: var(--pfx-editorial-form-field-border-width);--mdc-outlined-text-field-focus-outline-width: var(--pfx-editorial-form-field-border-width);--mat-select-enabled-trigger-text-color: var(--pfx-editorial-form-text);--mat-select-enabled-arrow-color: var(--pfx-form-label-muted);--mat-form-field-enabled-select-arrow-color: var(--pfx-form-label-muted);--mat-form-field-focus-select-arrow-color: var(--pfx-editorial-form-accent);--mat-form-field-hover-state-layer-opacity: 0;--mat-form-field-focus-state-layer-opacity: 0}.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field{font-family:var(--editorial-body-font-family, inherit)}.praxis-dynamic-form .mat-mdc-text-field-wrapper,.praxis-dynamic-form .mdc-text-field{background:var(--pfx-form-field-surface-rest);border-radius:var(--pfx-editorial-form-field-radius);min-height:var(--pfx-form-field-min-height);transition:background-color .16s ease,box-shadow .16s ease,border-color .16s ease}.praxis-dynamic-form.editorial-visual-context .mat-mdc-text-field-wrapper,.praxis-dynamic-form.editorial-visual-context .mdc-text-field,.praxis-dynamic-form.editorial-visual-context .mdc-text-field--filled,.praxis-dynamic-form.editorial-visual-context .mdc-text-field--outlined{background-color:var(--pfx-form-field-surface-rest)!important;background:var(--pfx-form-field-surface-rest)!important}.praxis-dynamic-form .mat-mdc-form-field .mdc-floating-label,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-form-field-hint,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-form-field-error,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-form-field-required-marker,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-select-arrow,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-select-placeholder,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-form-field-icon-suffix,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-form-field-icon-prefix{color:var(--pfx-form-label-muted)}.praxis-dynamic-form .mat-mdc-input-element,.praxis-dynamic-form .mat-mdc-select-value-text,.praxis-dynamic-form .mat-mdc-form-field-infix,.praxis-dynamic-form .mat-mdc-select-min-line{color:var(--pfx-editorial-form-text)}.praxis-dynamic-form .mat-mdc-form-field:hover .mat-mdc-text-field-wrapper,.praxis-dynamic-form .mat-mdc-form-field:hover .mdc-text-field,.praxis-dynamic-form .mat-mdc-form-field.mat-focused .mat-mdc-text-field-wrapper,.praxis-dynamic-form .mat-mdc-form-field.mat-focused .mdc-text-field{background:var(--pfx-form-field-surface-focus)}.praxis-dynamic-form.editorial-visual-context .mat-mdc-input-element,.praxis-dynamic-form.editorial-visual-context .mat-mdc-select-value-text,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field-infix,.praxis-dynamic-form.editorial-visual-context .mat-mdc-select-min-line,.praxis-dynamic-form.editorial-visual-context .mdc-text-field__input{color:var(--pfx-editorial-form-text)!important;-webkit-text-fill-color:var(--pfx-editorial-form-text)!important}.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mdc-floating-label,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-form-field-hint,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-form-field-error,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-form-field-required-marker,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-select-arrow,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-select-placeholder,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-form-field-icon-suffix,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-form-field-icon-prefix{color:var(--pfx-editorial-form-text-muted)!important}.praxis-dynamic-form.editorial-visual-context .mat-mdc-floating-label,.praxis-dynamic-form.editorial-visual-context .mdc-floating-label,.praxis-dynamic-form.editorial-visual-context .mdc-text-field__input::placeholder{color:color-mix(in srgb,var(--pfx-editorial-form-text) 68%,transparent)!important}.praxis-dynamic-form.editorial-visual-context .mat-mdc-input-element::placeholder,.praxis-dynamic-form.editorial-visual-context textarea.mat-mdc-input-element::placeholder{color:color-mix(in srgb,var(--pfx-editorial-form-text) 58%,transparent)!important}.praxis-dynamic-form .mat-mdc-radio-button,.praxis-dynamic-form .mat-mdc-checkbox,.praxis-dynamic-form .mat-mdc-slide-toggle{--mdc-radio-selected-icon-color: var(--pfx-editorial-form-accent);--mdc-radio-selected-hover-icon-color: var(--pfx-editorial-form-accent);--mdc-radio-selected-focus-icon-color: var(--pfx-editorial-form-accent);--mdc-checkbox-selected-checkmark-color: var(--pfx-editorial-form-accent-text);--mdc-checkbox-selected-focus-icon-color: var(--pfx-editorial-form-accent);--mdc-checkbox-selected-hover-icon-color: var(--pfx-editorial-form-accent);--mdc-checkbox-selected-icon-color: var(--pfx-editorial-form-accent);--mdc-switch-selected-focus-state-layer-color: var(--pfx-editorial-form-accent);--mdc-switch-selected-handle-color: var(--pfx-editorial-form-accent);--mdc-switch-selected-track-color: color-mix(in srgb, var(--pfx-editorial-form-accent) 45%, #fff)}.praxis-dynamic-form [data-field-type=input],.praxis-dynamic-form [data-field-type=textarea],.praxis-dynamic-form [data-field-type=email],.praxis-dynamic-form [data-field-type=password],.praxis-dynamic-form [data-field-type=url],.praxis-dynamic-form [data-field-type=search],.praxis-dynamic-form [data-field-type=phone],.praxis-dynamic-form [data-field-type=numericTextBox],.praxis-dynamic-form [data-field-type=currency],.praxis-dynamic-form [data-field-type=cpfCnpj],.praxis-dynamic-form [data-field-type=date],.praxis-dynamic-form [data-field-type=dateInput],.praxis-dynamic-form [data-field-type=dateRange],.praxis-dynamic-form [data-field-type=dateTimeLocal],.praxis-dynamic-form [data-field-type=time],.praxis-dynamic-form [data-field-type=timePicker],.praxis-dynamic-form [data-field-type=timeRange],.praxis-dynamic-form [data-field-type=month],.praxis-dynamic-form [data-field-type=week],.praxis-dynamic-form [data-field-type=yearInput],.praxis-dynamic-form [data-field-type=select],.praxis-dynamic-form [data-field-type=multi-select],.praxis-dynamic-form [data-field-type=searchable-select],.praxis-dynamic-form [data-field-type=async-select],.praxis-dynamic-form [data-field-type=autocomplete],.praxis-dynamic-form [data-field-type=tree-select],.praxis-dynamic-form [data-field-type=multi-select-tree],.praxis-dynamic-form [data-field-type=priceRange],.praxis-dynamic-form [data-field-type=file-upload]{display:block;width:100%;min-width:0}.praxis-dynamic-form .mat-mdc-form-field-subscript-wrapper{min-height:var(--pfx-subscript-min-h, 22px)}.form-row:last-child{margin-bottom:0}.form-column{display:grid;align-content:start;gap:var(--pfx-field-gap, 10px);flex:1;min-width:0;transition:all .2s ease;border-radius:4px;position:relative}.form-row.grid-12{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:var(--pfx-grid-gap, 16px)}.align-start{align-self:flex-start}.align-center{align-self:center}.align-end{align-self:flex-end}.align-stretch{align-self:stretch}.form-blocking-overlay{position:absolute;inset:0;background:transparent;color:var(--md-sys-color-on-surface);backdrop-filter:blur(2px) saturate(103%);-webkit-backdrop-filter:blur(2px) saturate(103%);display:flex;align-items:center;justify-content:center;flex-direction:column;gap:12px;z-index:10;pointer-events:all}@media(max-width:768px){.form-row{flex-direction:column;gap:.5rem}.form-section{padding:1rem}.section-heading{gap:10px;margin-bottom:16px;padding-bottom:14px}}.section-title{display:flex;align-items:center;gap:8px}.section-title .section-title-icon{font-size:1.25em;line-height:1}.section-title-avatar{--_pfx-form-section-avatar-size: var(--pfx-form-section-avatar-size-md, var(--pfx-form-section-avatar-size, 32px));display:inline-flex;align-items:center;justify-content:center;width:var(--_pfx-form-section-avatar-size);height:var(--_pfx-form-section-avatar-size);border-radius:999px;flex:0 0 var(--_pfx-form-section-avatar-size);overflow:hidden}.section-title-avatar.size-sm{--_pfx-form-section-avatar-size: var(--pfx-form-section-avatar-size-sm, 24px)}.section-title-avatar.size-md{--_pfx-form-section-avatar-size: var(--pfx-form-section-avatar-size-md, var(--pfx-form-section-avatar-size, 32px))}.section-title-avatar.size-lg{--_pfx-form-section-avatar-size: var(--pfx-form-section-avatar-size-lg, 40px)}.section-title-avatar-image{object-fit:cover;border:1px solid color-mix(in srgb,var(--pfx-form-section-divider) 72%,transparent);background:var(--pfx-form-section-surface-flat)}.section-title-avatar-text,.section-title-avatar-placeholder{background:color-mix(in srgb,var(--pfx-editorial-form-accent) 14%,var(--pfx-form-section-surface-flat));color:color-mix(in srgb,var(--pfx-editorial-form-accent) 82%,var(--pfx-form-label-strong) 18%);font-size:calc(var(--_pfx-form-section-avatar-size) * .41);font-weight:800;letter-spacing:.04em;text-transform:uppercase}.section-title-avatar-placeholder mat-icon{font-size:calc(var(--_pfx-form-section-avatar-size) * .5625);width:calc(var(--_pfx-form-section-avatar-size) * .5625);height:calc(var(--_pfx-form-section-avatar-size) * .5625);line-height:calc(var(--_pfx-form-section-avatar-size) * .5625)}.section-title-avatar-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: DynamicFieldLoaderDirective, selector: "[dynamicFieldLoader]", inputs: ["fields", "formGroup", "enableExternalControlBinding", "itemTemplate", "debugTrace", "debugTraceLabel", "readonlyMode", "disabledMode", "presentationMode", "visible", "canvasMode"], outputs: ["componentsCreated", "fieldCreated", "fieldDestroyed", "renderError", "canvasMouseEnter", "canvasMouseLeave", "canvasClick"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { 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: "component", type: i4.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i16.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { 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: MatDialogModule }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "ngmodule", type: MatBadgeModule }, { kind: "directive", type: i18.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: CanvasToolbarComponent, selector: "praxis-canvas-toolbar", inputs: ["selectedElement"], outputs: ["editMetadata", "delete", "moveUp", "moveDown", "selectPath", "requestClose", "toggleReadonly", "toggleRequired", "toggleHidden", "toggleDisabled"] }, { kind: "component", type: PraxisFormActionsComponent, selector: "praxis-form-actions", inputs: ["actions", "editorialVisualContext", "isSubmitting", "formIsValid", "submitError", "invalidRequiredFieldLabels", "formId", "actionOverrides"], outputs: ["action"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone"] }, { kind: "component", type: PraxisAiAssistantComponent, selector: "praxis-ai-assistant", inputs: ["adapter", "riskPolicy", "allowManualPatchEdit"] }, { kind: "component", type: PraxisRichContent, selector: "praxis-rich-content", inputs: ["document", "nodes", "context", "hostCapabilities", "layout", "rootClassName"] }] });
11704
12286
  }
11705
12287
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisDynamicForm, decorators: [{
11706
12288
  type: Component,
@@ -11721,7 +12303,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
11721
12303
  EmptyStateCardComponent,
11722
12304
  PraxisAiAssistantComponent,
11723
12305
  PraxisRichContent,
11724
- ], template: "@if (isLoading) {\n<!-- Loading State -->\n<div class=\"form-loading\">\n <mat-progress-spinner diameter=\"40\"></mat-progress-spinner>\n <p>Carregando formul\u00E1rio...</p>\n</div>\n} @else if (initializationStatus === 'error') {\n<!-- Error State -->\n<div class=\"form-error\">\n <mat-icon color=\"warn\" [praxisIcon]=\"'error'\"></mat-icon>\n <h3>{{ getErrorTitle() }}</h3>\n <p>{{ currentErrorMessage }}</p>\n @if (isRecoverable) {\n <button mat-stroked-button (click)=\"retryInitialization()\">\n <mat-icon [praxisIcon]=\"'refresh'\"></mat-icon>\n Tentar Novamente\n </button>\n }\n <button mat-button (click)=\"showDetailedError()\" class=\"show-details\">\n Ver Detalhes T\u00E9cnicos\n </button>\n <!-- Permitir corre\u00E7\u00E3o do resourcePath diretamente do estado de erro -->\n <button mat-flat-button color=\"primary\" (click)=\"openQuickConnect()\" class=\"connect-action\">\n <mat-icon [praxisIcon]=\"'bolt'\"></mat-icon>\n Conectar a recurso\n </button>\n</div>\n} @else if (initializationStatus === 'success') {\n<!-- Inline banner for schema change (only when customization is enabled) -->\n@if (shouldShowOutdatedInline()) {\n<div class=\"pfx-form-info-banner\" role=\"status\" aria-live=\"polite\">\n <div class=\"text\">O schema do servidor mudou. Reconciliar agora?</div>\n <div class=\"actions\">\n <button mat-stroked-button color=\"primary\" (click)=\"openConfigEditor()\">\n <mat-icon [praxisIcon]=\"'sync'\"></mat-icon>\n Reconciliar\n </button>\n <button mat-button (click)=\"onSnoozeOutdated()\">Lembrar depois</button>\n <button mat-button (click)=\"onIgnoreOutdated()\">Ignorar</button>\n </div>\n</div>\n}\n\n<!-- Configuration Controls -->\n@if (shouldShowConfigControls && enableCustomization) {\n<div class=\"form-config-controls\">\n <praxis-ai-assistant [adapter]=\"aiAdapter\"></praxis-ai-assistant>\n <button type=\"button\" mat-icon-button (click)=\"openConfigEditor()\" [disabled]=\"isLoading\" class=\"config-button\"\n [matBadge]=\"schemaOutdated ? '!' : ''\" [matBadgeHidden]=\"!schemaOutdated\" matBadgeColor=\"warn\" matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configurar formul\u00E1rio'\">\n <mat-icon [praxisIcon]=\"'settings'\"></mat-icon>\n </button>\n <button type=\"button\" mat-icon-button (click)=\"disconnect()\" matTooltip=\"Desconectar da fonte de dados\"\n [disabled]=\"isLoading\">\n <mat-icon [praxisIcon]=\"'link_off'\"></mat-icon>\n </button>\n</div>\n}\n\n<!-- Form Content -->\n@if (!resourcePath && (!config.sections || config.sections.length === 0)) {\n<praxis-empty-state-card icon=\"link\" [title]=\"'Conecte o formul\u00E1rio \u00E0 fonte de dados'\"\n [description]=\"'Informe a rota do recurso da API para gerar automaticamente os campos do formul\u00E1rio.'\"\n [primaryAction]=\"{ label: 'Conectar \u00E0 fonte de dados', icon: 'bolt', action: openQuickConnect.bind(this) }\"></praxis-empty-state-card>\n}\n<form #formHost (ngSubmit)=\"onSubmit()\" [attr.aria-busy]=\"submitting ? 'true' : null\"\n [attr.aria-label]=\"'Formul\u00E1rio ' + (config.metadata?.version || '')\" [class.canvas-mode-enabled]=\"enableCustomization\"\n [class.presentation-mode]=\"effectivePresentation\" [class.readonly-mode]=\"effectiveReadonly\"\n [class.editorial-visual-context]=\"hasEditorialVisualContext()\"\n [class.pfx-mounting]=\"isMounting\" [formGroup]=\"form\"\n [ngClass]=\"{\n 'pres-compact': presentationVars.compact,\n 'pres-label-left': presentationVars.labelPosition === 'left',\n 'pres-label-above': presentationVars.labelPosition === 'above'\n }\" [style.--pfx-pres-label-align]=\"presentationVars.labelAlign\"\n [style.--pfx-pres-label-size]=\"presentationVars.labelSize\"\n [style.--pfx-pres-label-width]=\"presentationVars.labelWidth\"\n [style.--pfx-pres-row-gap]=\"presentationVars.density === 'compact' ? '6px' : (presentationVars.density === 'cozy' ? '8px' : '10px')\"\n [style.--pfx-pres-row-padding]=\"presentationVars.density === 'compact' ? '2px 0' : (presentationVars.density === 'cozy' ? '6px 0' : '8px 0')\"\n [style.--pfx-pres-value-align]=\"presentationVars.valueAlign\"\n [style.--pfx-pres-value-size]=\"presentationVars.valueSize\"\n [style.--pdx-form-mount-duration]=\"getMountDurationVar()\"\n [style.--pdx-form-mount-offset]=\"getMountOffsetVar()\"\n [style.--pdx-form-mount-stagger]=\"getMountStaggerVar()\"\n class=\"praxis-dynamic-form\">\n <praxis-canvas-toolbar (editMetadata)=\"openSelectedElementEditor()\" (selectPath)=\"onSelectPath($event)\"\n (moveUp)=\"onToolbarMove('up')\" (moveDown)=\"onToolbarMove('down')\" (toggleReadonly)=\"onToolbarToggleReadonly()\"\n (toggleRequired)=\"onToolbarToggleRequired()\" (toggleHidden)=\"onToolbarToggleHidden()\"\n (toggleDisabled)=\"onToolbarToggleDisabled()\" (requestClose)=\"onToolbarRequestClose()\"\n *ngIf=\"enableCustomization && selectedElement\" [selectedElement]=\"selectedElement\"\n [style.transform]=\"toolbarTransform\"></praxis-canvas-toolbar>\n\n @if (formBlocksBefore?.nodes?.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before\" data-editorial-placement=\"before\">\n <praxis-rich-content\n [document]=\"formBlocksBeforeDocument\"\n [context]=\"getEditorialRichContentContext()\"\n ></praxis-rich-content>\n </section>\n }\n\n @if (actionPlacement === 'top' && !(effectivePresentation || effectiveReadonly)) {\n <praxis-form-actions data-actions-placement=\"top\" [actions]=\"config.actions\" [isSubmitting]=\"submitting\"\n [formIsValid]=\"effectiveFormIsValid\" [submitError]=\"submitError\" [formId]=\"formId\" [actionOverrides]=\"actionRuleProps\"\n [editorialVisualContext]=\"hasEditorialVisualContext()\"\n (action)=\"onFormAction($event)\"></praxis-form-actions>\n }\n\n @for (section of config.sections; track (section.id ?? $index); let sectionIndex = $index;\n let last = $last) {\n <div class=\"section-drop-wrapper\" [attr.data-section-id]=\"section.id\">\n @if (isSectionVisible(section)) {\n <div #sectionEl class=\"form-section canvas-element\" data-canvas-type=\"section\" [attr.data-section-id]=\"section.id\"\n [attr.data-section-index]=\"sectionIndex\" (mouseenter)=\"onElementMouseEnter($event, 'section', section, sectionEl)\"\n (mouseleave)=\"onElementMouseLeave($event)\" (click)=\"onElementClick($event, 'section', section, sectionEl)\"\n [class.selected]=\"selectedElement?.domElement === sectionEl\"\n [class.hovered]=\"hoveredElement?.domElement === sectionEl && selectedElement?.domElement !== sectionEl\"\n [attr.data-section-appearance]=\"getSectionAppearance(section) || null\"\n [style.marginBottom.px]=\"!last ? getSectionGapBottom(section) : null\" [ngClass]=\"getSectionClasses(section)\"\n [ngStyle]=\"getSectionStyles(section)\">\n <div\n class=\"section-heading\"\n [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\"\n [class.step-appearance]=\"getSectionAppearance(section) === 'step'\"\n >\n <div class=\"section-heading-text\" [matTooltip]=\"getSectionHeaderTooltip(section) || null\"\n [matTooltipDisabled]=\"!getSectionHeaderTooltip(section)\">\n @if (getSectionStepLabel(section)) {\n <div class=\"section-step-label\" [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\">\n {{ getSectionStepLabel(section) }}\n </div>\n }\n @if (getSectionTitle(section)) {\n <h3 class=\"section-title\" [class.title-large]=\"getSectionTitleStyle(section) === 'titleLarge'\"\n [class.title-medium]=\"getSectionTitleStyle(section) === 'titleMedium'\"\n [class.title-small]=\"getSectionTitleStyle(section) === 'titleSmall'\"\n [class.headline-small]=\"getSectionTitleStyle(section) === 'headlineSmall'\"\n [style.marginBottom.px]=\"getSectionTitleGapBottom(section) ?? 20\" [style.color]=\"getSectionTitleColor(section) || null\"\n [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\"\n [attr.id]=\"sectionPanelId(section, sectionIndex) + '-title'\">\n @let sectionHeaderVisual = getSectionHeaderVisual(section);\n @let sectionHeaderAvatarSize = getSectionHeaderAvatarSize(section);\n @if (sectionHeaderVisual.kind === 'icon') {\n <mat-icon class=\"section-title-icon\" aria-hidden=\"true\" [praxisIcon]=\"sectionHeaderVisual.icon\"></mat-icon>\n }\n @if (sectionHeaderVisual.kind === 'image') {\n <img class=\"section-title-avatar section-title-avatar-image\" [class.size-sm]=\"sectionHeaderAvatarSize === 'sm'\"\n [class.size-md]=\"sectionHeaderAvatarSize === 'md'\" [class.size-lg]=\"sectionHeaderAvatarSize === 'lg'\"\n [src]=\"sectionHeaderVisual.src\" [alt]=\"sectionHeaderVisual.alt\" />\n }\n @if (sectionHeaderVisual.kind === 'initials') {\n <span class=\"section-title-avatar section-title-avatar-text\" [class.size-sm]=\"sectionHeaderAvatarSize === 'sm'\"\n [class.size-md]=\"sectionHeaderAvatarSize === 'md'\" [class.size-lg]=\"sectionHeaderAvatarSize === 'lg'\" aria-hidden=\"true\">\n {{ sectionHeaderVisual.text }}\n </span>\n @if (sectionHeaderVisual.ariaLabel) {\n <span class=\"section-title-avatar-sr-only\">{{ sectionHeaderVisual.ariaLabel }}</span>\n }\n }\n @if (sectionHeaderVisual.kind === 'placeholder') {\n <span class=\"section-title-avatar section-title-avatar-placeholder\" [class.size-sm]=\"sectionHeaderAvatarSize === 'sm'\"\n [class.size-md]=\"sectionHeaderAvatarSize === 'md'\" [class.size-lg]=\"sectionHeaderAvatarSize === 'lg'\" aria-hidden=\"true\">\n @if (sectionHeaderVisual.icon) {\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"sectionHeaderVisual.icon\"></mat-icon>\n }\n @if (sectionHeaderVisual.text) {\n <span>{{ sectionHeaderVisual.text }}</span>\n }\n </span>\n @if (sectionHeaderVisual.ariaLabel) {\n <span class=\"section-title-avatar-sr-only\">{{ sectionHeaderVisual.ariaLabel }}</span>\n }\n }\n {{ getSectionTitle(section) }}\n @if (enableCustomization) {\n <button type=\"button\" class=\"inline-edit-btn\" mat-icon-button (click)=\"openSectionEditor(section, 'title')\"\n aria-label=\"Editar t\u00EDtulo da se\u00E7\u00E3o\" matTooltip=\"Editar t\u00EDtulo\">\n <mat-icon>edit</mat-icon>\n </button>\n }\n </h3>\n }\n @if (getSectionDescription(section)) {\n <p class=\"section-description\" [class.body-large]=\"getSectionDescriptionStyle(section) === 'bodyLarge'\"\n [class.body-medium]=\"getSectionDescriptionStyle(section) === 'bodyMedium'\"\n [class.body-small]=\"getSectionDescriptionStyle(section) === 'bodySmall'\"\n [style.marginBottom.px]=\"getSectionDescriptionGapBottom(section) ?? 8\" [style.color]=\"getSectionDescriptionColor(section) || null\"\n [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\">\n {{ getSectionDescription(section) }}\n @if (enableCustomization) {\n <button type=\"button\" class=\"inline-edit-btn\" mat-icon-button\n (click)=\"openSectionEditor(section, 'description')\" aria-label=\"Editar descri\u00E7\u00E3o da se\u00E7\u00E3o\"\n matTooltip=\"Editar descri\u00E7\u00E3o\">\n <mat-icon>edit</mat-icon>\n </button>\n }\n </p>\n }\n </div>\n @if (getSectionHeaderActions(section).length) {\n <div class=\"section-heading-actions\" [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\">\n @for (action of getSectionHeaderActions(section); track action.id) {\n <button\n type=\"button\"\n class=\"section-heading-action-btn\"\n mat-icon-button\n [color]=\"getSectionHeaderActionColor(action)\"\n [disabled]=\"isSectionHeaderActionDisabled(action)\"\n [matTooltip]=\"getSectionHeaderActionTooltip(action)\"\n [attr.aria-label]=\"action.label\"\n [attr.aria-busy]=\"action.loading ? 'true' : null\"\n [ngClass]=\"getSectionHeaderActionNgClass(action)\"\n [ngStyle]=\"getSectionHeaderActionStyles(action)\"\n (click)=\"onSectionHeaderActionClick(section, action, $event)\"\n >\n @if (action.loading) {\n <mat-progress-spinner diameter=\"16\" mode=\"indeterminate\"></mat-progress-spinner>\n <span class=\"section-heading-action-sr-only\">{{ getSectionHeaderActionLoadingLabel(action) }}</span>\n } @else {\n <mat-icon [praxisIcon]=\"action.icon\"></mat-icon>\n }\n </button>\n }\n </div>\n }\n @if (isSectionCollapsible(section)) {\n <button type=\"button\" class=\"section-collapse-btn\" mat-icon-button\n (click)=\"toggleSectionCollapse($event, section)\" [attr.aria-expanded]=\"!isSectionCollapsed(section)\"\n [attr.aria-controls]=\"sectionPanelId(section, sectionIndex)\"\n [attr.aria-label]=\"isSectionCollapsed(section) ? 'Expandir se\u00E7\u00E3o' : 'Recolher se\u00E7\u00E3o'\">\n <mat-icon [praxisIcon]=\"isSectionCollapsed(section) ? 'expand_more' : 'expand_less'\"></mat-icon>\n </button>\n }\n </div>\n\n <div class=\"section-body\" [class.collapsed]=\"isSectionCollapsed(section)\"\n [attr.id]=\"sectionPanelId(section, sectionIndex)\"\n [attr.aria-labelledby]=\"getSectionTitle(section) ? sectionPanelId(section, sectionIndex) + '-title' : null\">\n @if (!isSectionCollapsed(section)) {\n @for (row of section.rows; track (row.id ?? $index); let rowIndex = $index) {\n @if (isRowVisible(row)) {\n <div class=\"row-drop-wrapper\" [attr.data-section-id]=\"section.id\">\n <div #rowEl class=\"form-row grid-12 canvas-element\" data-canvas-type=\"row\" [attr.data-row-index]=\"rowIndex\"\n [attr.data-section-id]=\"section.id\" [attr.data-row-id]=\"row.id\" [style.--pfx-grid-gap.px]=\"getRowGap(row)\"\n [style.--pfx-field-gap.px]=\"getRowRowGap(row)\"\n [style.--pfx-mount-index]=\"rowIndex\"\n [style.marginBottom.px]=\"rowIndex < section.rows.length - 1 ? (getRowRowGap(row) ?? null) : null\"\n [ngClass]=\"getRowClasses(row)\" [ngStyle]=\"getRowStyles(row)\"\n (mouseenter)=\"onElementMouseEnter($event, 'row', row, rowEl)\" (mouseleave)=\"onElementMouseLeave($event)\"\n (click)=\"onElementClick($event, 'row', row, rowEl)\" [class.selected]=\"selectedElement?.domElement === rowEl\"\n [class.hovered]=\"hoveredElement?.domElement === rowEl && selectedElement?.domElement !== rowEl\">\n @for (column of row.columns; track (column.id ?? $index); let colIndex = $index) {\n @if (isColumnVisible(column)) {\n <div class=\"column-drop-wrapper\" [attr.data-section-id]=\"section.id\" [attr.data-row-id]=\"row.id\">\n <div #colEl class=\"form-column canvas-element\" [ngClass]=\"getColumnClasses(column)\"\n [style.padding.px]=\"getColumnPadding(column)\" [style.--pfx-field-gap.px]=\"getRowRowGap(row)\"\n [ngStyle]=\"getColumnStyles(column)\" [attr.data-testid]=\"column.testId || null\"\n [attr.data-row-gap]=\"getRowRowGap(row)\" data-canvas-type=\"column\" [attr.data-column-index]=\"colIndex\"\n [attr.data-row-index]=\"rowIndex\" [attr.data-section-id]=\"section.id\" [attr.data-row-id]=\"row.id\"\n [attr.data-column-id]=\"column.id\" (mouseenter)=\"onElementMouseEnter($event, 'column', column, colEl)\"\n (mouseleave)=\"onElementMouseLeave($event)\" (click)=\"onElementClick($event, 'column', column, colEl)\"\n [class.selected]=\"selectedElement?.domElement === colEl\"\n [class.hovered]=\"hoveredElement?.domElement === colEl && selectedElement?.domElement !== colEl\">\n <ng-container dynamicFieldLoader [fields]=\"getColumnFields(column)\" [formGroup]=\"form\"\n [enableExternalControlBinding]=\"true\"\n [readonlyMode]=\"readonlyModeGlobal === null ? null : readonlyModeGlobal\"\n [disabledMode]=\"effectiveDisabledMode\"\n [presentationMode]=\"presentationForLoader\" [visible]=\"visibleGlobal === null ? null : visibleGlobal\"\n [canvasMode]=\"enableCustomization\" (canvasMouseEnter)=\"onFieldMouseEnter($event)\"\n (canvasMouseLeave)=\"onFieldMouseLeave($event)\" (canvasClick)=\"onFieldClick($event)\"\n (renderError)=\"onFieldRenderError($event)\">\n </ng-container>\n </div>\n </div>\n } }\n </div>\n </div>\n }\n }\n } @else {\n <div class=\"section-collapsed-placeholder\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'unfold_more'\"></mat-icon>\n <span>{{ getSectionCollapsedSummary(section) }}</span>\n </div>\n }\n @if (last && beforeActionsPlacement === 'insideLastSection' && formBlocksBeforeActions?.nodes?.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before-actions\" data-editorial-placement=\"beforeActions\">\n <praxis-rich-content\n [document]=\"formBlocksBeforeActionsDocument\"\n [context]=\"getEditorialRichContentContext()\"\n ></praxis-rich-content>\n </section>\n }\n @if (actionPlacement === 'insideLastSection' && last && !(effectivePresentation\n || effectiveReadonly)) {\n <praxis-form-actions data-actions-placement=\"insideLastSection\" [actions]=\"config.actions\"\n [isSubmitting]=\"submitting\" [formIsValid]=\"effectiveFormIsValid\" [submitError]=\"submitError\" [formId]=\"formId\"\n [actionOverrides]=\"actionRuleProps\" [editorialVisualContext]=\"hasEditorialVisualContext()\"\n (action)=\"onFormAction($event)\"></praxis-form-actions>\n }\n </div>\n <!-- Overlay de bloqueio durante submiss\u00E3o -->\n @if (submitting) {\n <div class=\"form-blocking-overlay\" aria-live=\"polite\">\n <mat-progress-spinner diameter=\"40\" color=\"primary\"></mat-progress-spinner>\n <p>{{ config.messages?.loading?.submit || 'Salvando...' }}</p>\n </div>\n }\n </div>\n\n @if (enableCustomization && selectedElement?.domElement === sectionEl) {\n <div class=\"add-section-container\">\n <div class=\"add-section-line\"></div>\n <button mat-fab color=\"primary\" aria-label=\"Adicionar nova se\u00E7\u00E3o\" (click)=\"addNewSectionAfter(sectionIndex)\"\n matTooltip=\"Adicionar nova se\u00E7\u00E3o aqui\">\n <mat-icon [praxisIcon]=\"'add'\"></mat-icon>\n </button>\n <div class=\"add-section-line\"></div>\n </div>\n }\n }\n </div>\n }\n\n @if (beforeActionsPlacement !== 'insideLastSection' && formBlocksBeforeActions?.nodes?.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before-actions\" data-editorial-placement=\"beforeActions\">\n <praxis-rich-content\n [document]=\"formBlocksBeforeActionsDocument\"\n [context]=\"getEditorialRichContentContext()\"\n ></praxis-rich-content>\n </section>\n }\n\n @if (actionPlacement === 'afterSections' && !(effectivePresentation || effectiveReadonly)) {\n <praxis-form-actions data-actions-placement=\"afterSections\" [actions]=\"config.actions\" [isSubmitting]=\"submitting\"\n [formIsValid]=\"effectiveFormIsValid\" [submitError]=\"submitError\" [formId]=\"formId\"\n [actionOverrides]=\"actionRuleProps\" [editorialVisualContext]=\"hasEditorialVisualContext()\"\n (action)=\"onFormAction($event)\"></praxis-form-actions>\n }\n\n @if (formBlocksAfter?.nodes?.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-after\" data-editorial-placement=\"after\">\n <praxis-rich-content\n [document]=\"formBlocksAfterDocument\"\n [context]=\"getEditorialRichContentContext()\"\n ></praxis-rich-content>\n </section>\n }\n</form>\n@if (!enableCustomization && mode === 'view') {\n<div class=\"ai-floating-assistant\">\n <praxis-ai-assistant [adapter]=\"aiAdapter\"></praxis-ai-assistant>\n</div>\n}\n}\n", styles: ["@charset \"UTF-8\";.span-xs-1{grid-column:span 1}.span-xs-2{grid-column:span 2}.span-xs-3{grid-column:span 3}.span-xs-4{grid-column:span 4}.span-xs-5{grid-column:span 5}.span-xs-6{grid-column:span 6}.span-xs-7{grid-column:span 7}.span-xs-8{grid-column:span 8}.span-xs-9{grid-column:span 9}.span-xs-10{grid-column:span 10}.span-xs-11{grid-column:span 11}.span-xs-12{grid-column:span 12}.offset-xs-0{margin-left:0%}.offset-xs-1{margin-left:calc(1 / 12 * 100%)}.offset-xs-2{margin-left:calc(2 / 12 * 100%)}.offset-xs-3{margin-left:25%}.offset-xs-4{margin-left:calc(4 / 12 * 100%)}.offset-xs-5{margin-left:calc(5 / 12 * 100%)}.offset-xs-6{margin-left:50%}.offset-xs-7{margin-left:calc(7 / 12 * 100%)}.offset-xs-8{margin-left:calc(8 / 12 * 100%)}.offset-xs-9{margin-left:75%}.offset-xs-10{margin-left:calc(10 / 12 * 100%)}.offset-xs-11{margin-left:calc(11 / 12 * 100%)}.order-xs--12{order:-12}.order-xs--11{order:-11}.order-xs--10{order:-10}.order-xs--9{order:-9}.order-xs--8{order:-8}.order-xs--7{order:-7}.order-xs--6{order:-6}.order-xs--5{order:-5}.order-xs--4{order:-4}.order-xs--3{order:-3}.order-xs--2{order:-2}.order-xs--1{order:-1}.order-xs-0{order:0}.order-xs-1{order:1}.order-xs-2{order:2}.order-xs-3{order:3}.order-xs-4{order:4}.order-xs-5{order:5}.order-xs-6{order:6}.order-xs-7{order:7}.order-xs-8{order:8}.order-xs-9{order:9}.order-xs-10{order:10}.order-xs-11{order:11}.order-xs-12{order:12}.hidden-xs{display:none}@media(min-width:600px){.span-sm-1{grid-column:span 1}.span-sm-2{grid-column:span 2}.span-sm-3{grid-column:span 3}.span-sm-4{grid-column:span 4}.span-sm-5{grid-column:span 5}.span-sm-6{grid-column:span 6}.span-sm-7{grid-column:span 7}.span-sm-8{grid-column:span 8}.span-sm-9{grid-column:span 9}.span-sm-10{grid-column:span 10}.span-sm-11{grid-column:span 11}.span-sm-12{grid-column:span 12}.offset-sm-0{margin-left:0%}.offset-sm-1{margin-left:calc(1 / 12 * 100%)}.offset-sm-2{margin-left:calc(2 / 12 * 100%)}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:calc(4 / 12 * 100%)}.offset-sm-5{margin-left:calc(5 / 12 * 100%)}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:calc(7 / 12 * 100%)}.offset-sm-8{margin-left:calc(8 / 12 * 100%)}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:calc(10 / 12 * 100%)}.offset-sm-11{margin-left:calc(11 / 12 * 100%)}.order-sm--12{order:-12}.order-sm--11{order:-11}.order-sm--10{order:-10}.order-sm--9{order:-9}.order-sm--8{order:-8}.order-sm--7{order:-7}.order-sm--6{order:-6}.order-sm--5{order:-5}.order-sm--4{order:-4}.order-sm--3{order:-3}.order-sm--2{order:-2}.order-sm--1{order:-1}.order-sm-0{order:0}.order-sm-1{order:1}.order-sm-2{order:2}.order-sm-3{order:3}.order-sm-4{order:4}.order-sm-5{order:5}.order-sm-6{order:6}.order-sm-7{order:7}.order-sm-8{order:8}.order-sm-9{order:9}.order-sm-10{order:10}.order-sm-11{order:11}.order-sm-12{order:12}.hidden-sm{display:none}}@media(min-width:900px){.span-md-1{grid-column:span 1}.span-md-2{grid-column:span 2}.span-md-3{grid-column:span 3}.span-md-4{grid-column:span 4}.span-md-5{grid-column:span 5}.span-md-6{grid-column:span 6}.span-md-7{grid-column:span 7}.span-md-8{grid-column:span 8}.span-md-9{grid-column:span 9}.span-md-10{grid-column:span 10}.span-md-11{grid-column:span 11}.span-md-12{grid-column:span 12}.offset-md-0{margin-left:0%}.offset-md-1{margin-left:calc(1 / 12 * 100%)}.offset-md-2{margin-left:calc(2 / 12 * 100%)}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:calc(4 / 12 * 100%)}.offset-md-5{margin-left:calc(5 / 12 * 100%)}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:calc(7 / 12 * 100%)}.offset-md-8{margin-left:calc(8 / 12 * 100%)}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:calc(10 / 12 * 100%)}.offset-md-11{margin-left:calc(11 / 12 * 100%)}.order-md--12{order:-12}.order-md--11{order:-11}.order-md--10{order:-10}.order-md--9{order:-9}.order-md--8{order:-8}.order-md--7{order:-7}.order-md--6{order:-6}.order-md--5{order:-5}.order-md--4{order:-4}.order-md--3{order:-3}.order-md--2{order:-2}.order-md--1{order:-1}.order-md-0{order:0}.order-md-1{order:1}.order-md-2{order:2}.order-md-3{order:3}.order-md-4{order:4}.order-md-5{order:5}.order-md-6{order:6}.order-md-7{order:7}.order-md-8{order:8}.order-md-9{order:9}.order-md-10{order:10}.order-md-11{order:11}.order-md-12{order:12}.hidden-md{display:none}}@media(min-width:1200px){.span-lg-1{grid-column:span 1}.span-lg-2{grid-column:span 2}.span-lg-3{grid-column:span 3}.span-lg-4{grid-column:span 4}.span-lg-5{grid-column:span 5}.span-lg-6{grid-column:span 6}.span-lg-7{grid-column:span 7}.span-lg-8{grid-column:span 8}.span-lg-9{grid-column:span 9}.span-lg-10{grid-column:span 10}.span-lg-11{grid-column:span 11}.span-lg-12{grid-column:span 12}.offset-lg-0{margin-left:0%}.offset-lg-1{margin-left:calc(1 / 12 * 100%)}.offset-lg-2{margin-left:calc(2 / 12 * 100%)}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:calc(4 / 12 * 100%)}.offset-lg-5{margin-left:calc(5 / 12 * 100%)}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:calc(7 / 12 * 100%)}.offset-lg-8{margin-left:calc(8 / 12 * 100%)}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:calc(10 / 12 * 100%)}.offset-lg-11{margin-left:calc(11 / 12 * 100%)}.order-lg--12{order:-12}.order-lg--11{order:-11}.order-lg--10{order:-10}.order-lg--9{order:-9}.order-lg--8{order:-8}.order-lg--7{order:-7}.order-lg--6{order:-6}.order-lg--5{order:-5}.order-lg--4{order:-4}.order-lg--3{order:-3}.order-lg--2{order:-2}.order-lg--1{order:-1}.order-lg-0{order:0}.order-lg-1{order:1}.order-lg-2{order:2}.order-lg-3{order:3}.order-lg-4{order:4}.order-lg-5{order:5}.order-lg-6{order:6}.order-lg-7{order:7}.order-lg-8{order:8}.order-lg-9{order:9}.order-lg-10{order:10}.order-lg-11{order:11}.order-lg-12{order:12}.hidden-lg{display:none}}@media(min-width:1536px){.span-xl-1{grid-column:span 1}.span-xl-2{grid-column:span 2}.span-xl-3{grid-column:span 3}.span-xl-4{grid-column:span 4}.span-xl-5{grid-column:span 5}.span-xl-6{grid-column:span 6}.span-xl-7{grid-column:span 7}.span-xl-8{grid-column:span 8}.span-xl-9{grid-column:span 9}.span-xl-10{grid-column:span 10}.span-xl-11{grid-column:span 11}.span-xl-12{grid-column:span 12}.offset-xl-0{margin-left:0%}.offset-xl-1{margin-left:calc(1 / 12 * 100%)}.offset-xl-2{margin-left:calc(2 / 12 * 100%)}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:calc(4 / 12 * 100%)}.offset-xl-5{margin-left:calc(5 / 12 * 100%)}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:calc(7 / 12 * 100%)}.offset-xl-8{margin-left:calc(8 / 12 * 100%)}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:calc(10 / 12 * 100%)}.offset-xl-11{margin-left:calc(11 / 12 * 100%)}.order-xl--12{order:-12}.order-xl--11{order:-11}.order-xl--10{order:-10}.order-xl--9{order:-9}.order-xl--8{order:-8}.order-xl--7{order:-7}.order-xl--6{order:-6}.order-xl--5{order:-5}.order-xl--4{order:-4}.order-xl--3{order:-3}.order-xl--2{order:-2}.order-xl--1{order:-1}.order-xl-0{order:0}.order-xl-1{order:1}.order-xl-2{order:2}.order-xl-3{order:3}.order-xl-4{order:4}.order-xl-5{order:5}.order-xl-6{order:6}.order-xl-7{order:7}.order-xl-8{order:8}.order-xl-9{order:9}.order-xl-10{order:10}.order-xl-11{order:11}.order-xl-12{order:12}.hidden-xl{display:none}}.canvas-mode-enabled{--canvas-hit: 14px}.canvas-mode-enabled .canvas-element{position:relative;z-index:0;border-radius:8px;outline:2px solid transparent;outline-offset:2px;transition:outline-color .2s ease,outline-style .2s ease}.canvas-mode-enabled .canvas-element:before{content:\"\";position:absolute;inset:calc(-1 * var(--canvas-hit));pointer-events:none;border-radius:inherit;background:transparent}.canvas-mode-enabled .canvas-element[data-canvas-type=section]{--outline-color: var(--md-sys-color-primary)}.canvas-mode-enabled .canvas-element[data-canvas-type=row]{--outline-color: var(--md-sys-color-secondary)}.canvas-mode-enabled .canvas-element[data-canvas-type=column],.canvas-mode-enabled .canvas-element[data-canvas-type=field]{--outline-color: var(--md-sys-color-tertiary)}.canvas-mode-enabled .canvas-element[data-canvas-type=actions]{--outline-color: var(--md-sys-color-primary)}.canvas-mode-enabled .canvas-element.hovered:not(.selected){outline-color:var(--outline-color, var(--md-sys-color-primary));outline-style:dashed}.canvas-mode-enabled .canvas-element.selected{outline-color:var(--outline-color, var(--md-sys-color-primary));outline-style:solid;box-shadow:0 0 0 2px var(--outline-color, var(--md-sys-color-primary))}.canvas-mode-enabled .canvas-element.hovered{z-index:var(--praxis-layer-authoring-hover, 300)}.canvas-mode-enabled .canvas-element.selected{z-index:var(--praxis-layer-authoring-selected, 320)}.section-drop-wrapper,.row-drop-wrapper,.column-drop-wrapper{display:contents}.add-section-container{display:flex;align-items:center;justify-content:center;padding:.5rem 0;margin:-.5rem 0;position:relative;z-index:var(--praxis-layer-authoring-insert, 280)}.add-section-container .add-section-line{flex-grow:1;height:1px;background:repeating-linear-gradient(90deg,var(--md-sys-color-outline-variant),var(--md-sys-color-outline-variant) 4px,transparent 4px,transparent 8px)}.add-section-container button{margin:0 1rem;transform:scale(.85)}:host{display:block;position:relative}.form-config-controls{position:sticky;top:10px;margin-left:auto;margin-bottom:10px;display:flex;align-items:center;gap:.35rem;z-index:60;background:color-mix(in srgb,var(--md-sys-color-surface) 92%,transparent);padding:6px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 82%,transparent);border-radius:999px;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);box-shadow:0 10px 22px #0f172a14;min-width:0;justify-content:flex-end;pointer-events:none;opacity:.88;transition:opacity .16s ease,box-shadow .16s ease,border-color .16s ease,transform .16s ease}.form-config-controls>*{pointer-events:auto}.praxis-dynamic-form:hover .form-config-controls,.praxis-dynamic-form:focus-within .form-config-controls{opacity:1;border-color:color-mix(in srgb,var(--md-sys-color-primary) 26%,var(--md-sys-color-outline-variant) 74%);box-shadow:0 14px 28px #0f172a1f;transform:translateY(-1px)}.form-config-controls .mat-icon-button{--mdc-icon-button-state-layer-size: 34px;width:34px;height:34px;color:var(--md-sys-color-on-surface-variant);background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 82%,transparent);border:1px solid transparent}.form-config-controls .mat-icon-button:hover{color:var(--md-sys-color-primary);background:var(--md-sys-color-primary-container);border-color:color-mix(in srgb,var(--md-sys-color-primary) 20%,transparent)}.ai-floating-assistant{position:sticky;right:0;bottom:12px;margin-top:14px;margin-left:auto;width:fit-content;z-index:70;pointer-events:none}.ai-floating-assistant praxis-ai-assistant{pointer-events:auto;display:inline-flex}.ai-floating-assistant .ai-trigger-btn{box-shadow:0 12px 28px #0f172a24}.config-button{color:var(--md-sys-color-primary)}.form-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3rem;text-align:center;color:var(--md-sys-color-on-surface);gap:1rem}.form-loading p{margin:0;font-size:.875rem;opacity:.7}.form-error{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3rem;text-align:center;color:var(--md-sys-color-error);gap:1rem;border:1px solid var(--md-sys-color-error);border-radius:8px;background-color:var(--md-sys-color-error-container);margin:1rem;box-shadow:0 12px 30px #7f1d1d1f}.form-error h3{margin:0;color:var(--md-sys-color-on-error-container)}.form-error p{margin:0;color:var(--md-sys-color-on-error-container);opacity:.8}.form-error button{margin-top:.5rem}.pfx-form-info-banner{margin-bottom:14px;padding:12px 14px;border-radius:16px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 18%,var(--md-sys-color-outline-variant) 82%);background:color-mix(in srgb,var(--md-sys-color-primary-container) 24%,var(--md-sys-color-surface) 76%);color:var(--md-sys-color-on-surface);display:flex;align-items:flex-start;justify-content:space-between;gap:14px}.pfx-form-info-banner .text{font-size:.92rem;line-height:1.5;font-weight:600}.pfx-form-info-banner .actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px}.praxis-dynamic-form{display:flex;flex-direction:column;transition:all .3s ease;position:relative;font-family:inherit;font-size:inherit;color:inherit;--pfx-editorial-form-surface: var( --pfx-form-section-surface, var(--md-sys-color-surface-container) );--pfx-editorial-form-surface-muted: var(--md-sys-color-surface-container-low);--pfx-editorial-form-border: var( --pfx-form-stroke, var(--md-sys-color-outline-variant) );--pfx-editorial-form-text: var(--md-sys-color-on-surface);--pfx-editorial-form-text-muted: var(--md-sys-color-on-surface-variant);--pfx-editorial-form-field-surface: color-mix( in srgb, var(--pfx-editorial-form-surface-muted) 76%, var(--pfx-editorial-form-surface) 24% );--pfx-editorial-form-field-outline: color-mix( in srgb, var(--pfx-editorial-form-border) 88%, transparent );--pfx-editorial-form-accent: var(--md-sys-color-primary);--pfx-editorial-form-accent-text: var(--md-sys-color-on-primary);--pfx-editorial-form-radius: var(--pfx-form-section-radius, 8px);--pfx-editorial-form-field-radius: var(--pfx-form-field-radius, 4px);--pfx-editorial-form-border-width: 1px;--pfx-editorial-form-field-border-width: 1px;--pfx-editorial-form-shadow: none;--pfx-form-shell-surface: color-mix( in srgb, var(--pfx-editorial-form-surface) 36%, transparent );--pfx-form-section-surface-flat: color-mix( in srgb, var(--pfx-editorial-form-surface) 78%, var(--pfx-editorial-form-surface-muted) 22% );--pfx-form-section-divider: color-mix( in srgb, var(--pfx-editorial-form-border) 68%, transparent );--pfx-form-label-strong: color-mix( in srgb, var(--pfx-editorial-form-text) 96%, white 4% );--pfx-form-label-muted: color-mix( in srgb, var(--pfx-editorial-form-text-muted) 92%, var(--pfx-editorial-form-text) 8% );--pfx-form-field-surface-rest: color-mix( in srgb, var(--pfx-editorial-form-field-surface) 82%, var(--pfx-editorial-form-surface) 18% );--pfx-form-field-surface-focus: color-mix( in srgb, var(--pfx-editorial-form-accent) 4%, var(--pfx-form-field-surface-rest) 96% );--pfx-form-field-min-height: 48px;--pfx-form-section-padding: clamp(1.1rem, 1.8vw, 1.5rem);--pfx-form-section-gap: 22px;--pfx-form-footer-surface: color-mix( in srgb, var(--pfx-editorial-form-surface) 70%, var(--pfx-editorial-form-surface-muted) 30% );--pfx-form-footer-border: color-mix( in srgb, var(--pfx-editorial-form-border) 76%, transparent )}.praxis-dynamic-form.editorial-visual-context{font-family:var(--editorial-body-font-family, inherit);font-size:var(--editorial-body-size, 1rem);color:var(--editorial-text-primary, var(--md-sys-color-on-surface));--pfx-editorial-form-surface: var( --editorial-surface-primary, var(--pfx-form-section-surface, var(--md-sys-color-surface-container)) );--pfx-editorial-form-surface-muted: var( --editorial-surface-secondary, var(--md-sys-color-surface-container-low) );--pfx-editorial-form-border: var( --editorial-border-color, var(--pfx-form-stroke, var(--md-sys-color-outline-variant)) );--pfx-editorial-form-text: var( --editorial-text-primary, var(--md-sys-color-on-surface) );--pfx-editorial-form-text-muted: var( --editorial-text-secondary, var(--md-sys-color-on-surface-variant) );--pfx-editorial-form-accent: var( --editorial-cta-primary, var(--editorial-accent, var(--md-sys-color-primary)) );--pfx-editorial-form-accent-text: var( --editorial-cta-primary-text, var(--editorial-accent-contrast, var(--md-sys-color-on-primary)) );--pfx-editorial-form-radius: var(--editorial-card-radius, 18px);--pfx-editorial-form-field-radius: var( --editorial-field-radius, var(--editorial-card-radius, 14px) );--pfx-editorial-form-border-width: var(--editorial-card-border-width, 1px);--pfx-editorial-form-field-border-width: var(--editorial-field-border-width, 1px);--pfx-editorial-form-shadow: var(--editorial-card-shadow, none);--md-sys-color-surface: var( --pfx-editorial-form-surface, var(--md-sys-color-surface) );--md-sys-color-surface-container: var( --pfx-editorial-form-surface, var(--md-sys-color-surface-container) );--md-sys-color-surface-container-low: var( --pfx-editorial-form-surface-muted, var(--md-sys-color-surface-container-low) );--md-sys-color-surface-variant: var( --pfx-editorial-form-field-surface, var(--md-sys-color-surface-variant) );--md-sys-color-outline: var( --pfx-editorial-form-field-outline, var(--md-sys-color-outline) );--md-sys-color-outline-variant: var( --pfx-editorial-form-border, var(--md-sys-color-outline-variant) );--md-sys-color-on-surface: var( --pfx-editorial-form-text, var(--md-sys-color-on-surface) );--md-sys-color-on-surface-variant: var( --pfx-editorial-form-text-muted, var(--md-sys-color-on-surface-variant) );--md-sys-color-primary: var( --pfx-editorial-form-accent, var(--md-sys-color-primary) );--md-sys-color-on-primary: var( --pfx-editorial-form-accent-text, var(--md-sys-color-on-primary) );--md-sys-color-on-surface-rgb: var( --editorial-text-primary-rgb, var(--md-sys-color-on-surface-rgb) );--mat-sys-on-surface: var( --pfx-editorial-form-text, var(--mat-sys-on-surface) );--mat-sys-on-surface-variant: var( --pfx-editorial-form-text-muted, var(--mat-sys-on-surface-variant) );--mat-sys-on-surface-rgb: var( --editorial-text-primary-rgb, var(--mat-sys-on-surface-rgb, var(--md-sys-color-on-surface-rgb)) );color:var(--pfx-editorial-form-text);color-scheme:light}.praxis-dynamic-form.presentation-mode .form-row,.praxis-dynamic-form.readonly-mode .form-row{gap:.5rem;margin-bottom:.5rem}.praxis-dynamic-form.presentation-mode .form-section,.praxis-dynamic-form.readonly-mode .form-section{padding:.75rem .875rem}.praxis-dynamic-form.pres-compact .form-row{gap:.35rem;margin-bottom:.35rem}.praxis-dynamic-form.pres-compact .form-section{padding:.5rem .75rem}.praxis-dynamic-form.pres-label-left .praxis-presentation{display:grid;grid-template-columns:var(--pfx-presentation-label-w, 220px) 1fr;align-items:baseline;column-gap:10px}.praxis-dynamic-form.pres-label-left .praxis-presentation__label{text-align:right;margin-bottom:0}.form-section{border:1px solid var(--pfx-form-section-divider);border-radius:var(--pfx-editorial-form-radius);padding:var(--pfx-form-section-padding);background:var(--pfx-form-section-surface-flat);box-shadow:none;transition:all .2s ease;position:relative}.praxis-dynamic-form.editorial-visual-context .form-section{border:var(--pfx-editorial-form-border-width) solid var(--pfx-editorial-form-border)!important;background:var(--pfx-editorial-form-surface)!important;box-shadow:var(--editorial-card-shadow, none)}.praxis-dynamic-form.editorial-visual-context .form-section.section-appearance-step{background-color:var(--pfx-editorial-form-surface)!important;background-image:linear-gradient(180deg,color-mix(in srgb,var(--editorial-accent, var(--md-sys-color-primary)) 6%,transparent) 0%,transparent 132px)!important;background-repeat:no-repeat!important}.section-drop-wrapper>.form-section{margin-bottom:var(--pfx-section-gap, 20px)}.section-drop-wrapper:last-of-type>.form-section{margin-bottom:0}.praxis-dynamic-form>praxis-form-actions[data-actions-placement=afterSections] .form-actions{margin-top:var(--pfx-actions-gap-top, var(--pfx-section-gap, 20px))}.section-title{margin:0 0 var(--pfx-section-title-mb, 12px) 0;font-family:var(--editorial-title-font-family, var(--editorial-body-font-family, inherit));font-size:var(--editorial-step-title-size, 1.12rem);font-weight:700;color:var(--pfx-form-label-strong)}.section-heading{display:flex;align-items:flex-start;gap:12px;margin-bottom:18px;padding-bottom:18px;border-bottom:1px solid var(--pfx-form-section-divider)}.section-heading.align-center{flex-direction:column;align-items:center;text-align:center}.section-heading.align-center .section-heading-text{display:grid;justify-items:center}.section-step-label{display:inline-flex;align-items:center;min-height:24px;padding:0 10px;margin:0 0 8px;border-radius:999px;background:color-mix(in srgb,var(--pfx-editorial-form-accent) 10%,transparent);color:color-mix(in srgb,var(--pfx-editorial-form-accent) 84%,var(--pfx-form-label-strong) 16%);font-size:.72rem;font-weight:800;font-family:var(--editorial-title-font-family, var(--editorial-body-font-family, inherit));letter-spacing:.08em;text-transform:uppercase}.section-heading-text{flex:1 1 auto;min-width:0}.section-heading-actions{display:inline-flex;align-items:center;align-self:flex-start;flex-wrap:wrap;gap:4px;margin-left:auto}.section-heading-actions.align-center{align-self:center;margin-left:0}.section-heading-action-btn{color:var(--pfx-form-label-muted)}.section-heading-action-btn .mat-mdc-progress-spinner,.section-heading-action-btn mat-progress-spinner{--mdc-circular-progress-active-indicator-color: currentColor}.section-heading-action-btn.loading{opacity:.72;pointer-events:none}.section-heading-action-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.section-collapse-btn{margin-left:4px;color:var(--pfx-form-label-muted)}.section-title.title-large{font:var(--mdc-typography-title-large, 500 22px/28px system-ui)}.section-title.title-medium{font:var(--mdc-typography-title-medium, 500 16px/24px system-ui)}.section-title.title-small{font:var(--mdc-typography-title-small, 500 14px/20px system-ui)}.section-title.headline-small{font:var(--mdc-typography-headline-small, 600 24px/32px system-ui)}.section-title.title-large,.section-title.title-medium,.section-title.title-small,.section-title.headline-small{font-family:var(--editorial-title-font-family, var(--editorial-body-font-family, inherit));font-weight:var(--editorial-title-weight, 600)}.section-description{margin:0;font-family:var(--editorial-body-font-family, inherit);font-size:.93rem;color:var(--pfx-form-label-muted);line-height:1.6}.section-description.body-large{font:var(--mdc-typography-body-large, 400 16px/24px system-ui)}.section-description.body-medium{font:var(--mdc-typography-body-medium, 400 14px/20px system-ui)}.section-description.body-small{font:var(--mdc-typography-body-small, 400 12px/16px system-ui)}.section-description.body-large,.section-description.body-medium,.section-description.body-small{font-family:var(--editorial-body-font-family, inherit);font-weight:var(--editorial-body-weight, 400)}.section-title.align-center,.section-description.align-center,.section-step-label.align-center{text-align:center}.form-section.section-appearance-plain{border-color:transparent;border-radius:0;padding:0;background:transparent}.form-section.section-appearance-plain .section-heading{margin-bottom:14px}.form-section.section-appearance-step{border-radius:var(--pfx-editorial-form-radius);padding:var(--pfx-form-section-padding);background:var(--pfx-form-section-surface-flat);border-color:var(--pfx-form-section-divider);box-shadow:none}.praxis-dynamic-form.editorial-visual-context .form-section.section-appearance-step{border-radius:var(--editorial-card-radius, 20px);box-shadow:none}.form-section.section-appearance-step .section-heading{margin-bottom:22px;padding-bottom:16px;border-bottom:1px solid var(--pfx-form-section-divider)}.form-section.section-appearance-step .section-title{font:var(--mdc-typography-title-large, 700 22px/28px system-ui);color:var(--pfx-form-label-strong);margin-bottom:8px}.form-section.section-appearance-step .section-description{color:var(--pfx-form-label-muted);max-width:60ch}.form-section.section-appearance-step .section-step-label{min-height:24px;padding:0 10px;margin-bottom:10px;box-shadow:none}.form-section.section-appearance-step .section-heading.align-center .section-description{max-width:52ch}.form-section.section-appearance-step .section-body{display:grid;gap:8px;padding-top:0}.form-section.section-appearance-step .form-row{margin-bottom:var(--pfx-field-gap, 1rem)}.form-section.section-appearance-step .form-column{gap:var(--pfx-field-gap, 12px)}.form-section .form-editorial-blocks{display:grid;gap:16px}.form-section .form-editorial-blocks[data-editorial-placement=beforeActions]{margin-top:24px;padding-top:18px;border-top:1px solid var(--pfx-form-section-divider)}.form-section.section-appearance-step .form-editorial-blocks[data-editorial-placement=beforeActions]{gap:18px}.form-section.section-appearance-step .form-editorial-blocks[data-editorial-placement=beforeActions]>*+*{margin-top:2px}.praxis-dynamic-form>praxis-form-actions[data-actions-placement=afterSections] .form-actions{margin-top:18px;padding-top:0}.praxis-dynamic-form>.form-editorial-blocks[data-editorial-placement=after]{margin-top:6px}:host-context(.mdc-theme-dark) .form-section.section-appearance-step,:host-context(.theme-dark) .form-section.section-appearance-step{border-color:color-mix(in srgb,var(--pfx-editorial-form-border) 82%,transparent);box-shadow:none}:host-context(.mdc-theme-dark) .form-section.section-appearance-step .section-title,:host-context(.theme-dark) .form-section.section-appearance-step .section-title{color:color-mix(in srgb,var(--pfx-editorial-form-text) 96%,white)}:host-context(.mdc-theme-dark) .form-section.section-appearance-step .section-description,:host-context(.theme-dark) .form-section.section-appearance-step .section-description{color:color-mix(in srgb,var(--pfx-editorial-form-text-muted) 86%,var(--pfx-editorial-form-text) 14%)}:host-context(.mdc-theme-dark) .section-step-label,:host-context(.theme-dark) .section-step-label{background:color-mix(in srgb,var(--md-sys-color-primary-container) 54%,transparent);color:color-mix(in srgb,var(--md-sys-color-primary) 88%,white)}:host-context(.mdc-theme-dark) .form-section .form-editorial-blocks[data-editorial-placement=beforeActions],:host-context(.theme-dark) .form-section .form-editorial-blocks[data-editorial-placement=beforeActions]{border-top-color:color-mix(in srgb,var(--md-sys-color-outline-variant) 90%,transparent)}.inline-edit-btn{margin-left:6px;vertical-align:middle;--mdc-icon-button-size: 28px;--mdc-icon-button-icon-size: 16px}.inline-edit-btn mat-icon{font-size:16px;width:16px;height:16px}.section-body.collapsed{border:1px dashed var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low);border-radius:6px;padding:8px 10px}.section-collapsed-placeholder{display:flex;align-items:center;gap:8px;color:var(--md-sys-color-on-surface-variant);font-size:.95rem}.section-collapsed-placeholder mat-icon{font-size:20px;width:20px;height:20px}.form-row{display:flex;gap:1.1rem;margin-bottom:var(--pfx-field-gap, 1.1rem);transition:all .2s ease;border-radius:6px;position:relative}.praxis-dynamic-form.pfx-mounting .form-row{opacity:0;transform:translateY(var(--pdx-form-mount-offset, 6px));animation:pdxFormMount var(--pdx-form-mount-duration, .16s) ease-out both;animation-delay:calc(var(--pfx-mount-index, 0) * var(--pdx-form-mount-stagger, 20ms))}@media(prefers-reduced-motion:reduce){.praxis-dynamic-form.pfx-mounting .form-row{animation:none;opacity:1;transform:none}}@keyframes pdxFormMount{to{opacity:1;transform:translateY(0)}}.praxis-dynamic-form .mat-mdc-form-field{width:100%;margin-bottom:var(--pfx-field-gap, 10px);font-family:inherit;--mdc-filled-text-field-container-color: var(--pfx-form-field-surface-rest);--mdc-filled-text-field-focus-active-indicator-color: var(--pfx-editorial-form-accent);--mdc-filled-text-field-active-indicator-color: var(--pfx-editorial-form-field-outline);--mdc-filled-text-field-hover-active-indicator-color: var(--pfx-editorial-form-accent);--mdc-filled-text-field-focus-label-text-color: var(--pfx-editorial-form-accent);--mdc-filled-text-field-label-text-color: var(--pfx-form-label-muted);--mdc-filled-text-field-input-text-color: var(--pfx-editorial-form-text);--mdc-filled-text-field-caret-color: var(--pfx-editorial-form-accent);--mdc-filled-text-field-input-text-placeholder-color: color-mix( in srgb, var(--pfx-editorial-form-text-muted) 82%, transparent );--mdc-outlined-text-field-outline-color: var(--pfx-editorial-form-field-outline);--mdc-outlined-text-field-hover-outline-color: var(--pfx-editorial-form-accent);--mdc-outlined-text-field-focus-outline-color: var(--pfx-editorial-form-accent);--mdc-outlined-text-field-focus-label-text-color: var(--pfx-editorial-form-accent);--mdc-outlined-text-field-label-text-color: var(--pfx-form-label-muted);--mdc-outlined-text-field-input-text-color: var(--pfx-editorial-form-text);--mdc-filled-text-field-container-shape: var(--pfx-editorial-form-field-radius);--mdc-outlined-text-field-container-shape: var(--pfx-editorial-form-field-radius);--mdc-filled-text-field-active-indicator-height: var(--pfx-editorial-form-field-border-width);--mdc-filled-text-field-focus-active-indicator-height: var(--pfx-editorial-form-field-border-width);--mdc-outlined-text-field-outline-width: var(--pfx-editorial-form-field-border-width);--mdc-outlined-text-field-focus-outline-width: var(--pfx-editorial-form-field-border-width);--mat-select-enabled-trigger-text-color: var(--pfx-editorial-form-text);--mat-select-enabled-arrow-color: var(--pfx-form-label-muted);--mat-form-field-enabled-select-arrow-color: var(--pfx-form-label-muted);--mat-form-field-focus-select-arrow-color: var(--pfx-editorial-form-accent);--mat-form-field-hover-state-layer-opacity: 0;--mat-form-field-focus-state-layer-opacity: 0}.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field{font-family:var(--editorial-body-font-family, inherit)}.praxis-dynamic-form .mat-mdc-text-field-wrapper,.praxis-dynamic-form .mdc-text-field{background:var(--pfx-form-field-surface-rest);border-radius:var(--pfx-editorial-form-field-radius);min-height:var(--pfx-form-field-min-height);transition:background-color .16s ease,box-shadow .16s ease,border-color .16s ease}.praxis-dynamic-form.editorial-visual-context .mat-mdc-text-field-wrapper,.praxis-dynamic-form.editorial-visual-context .mdc-text-field,.praxis-dynamic-form.editorial-visual-context .mdc-text-field--filled,.praxis-dynamic-form.editorial-visual-context .mdc-text-field--outlined{background-color:var(--pfx-form-field-surface-rest)!important;background:var(--pfx-form-field-surface-rest)!important}.praxis-dynamic-form .mat-mdc-form-field .mdc-floating-label,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-form-field-hint,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-form-field-error,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-form-field-required-marker,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-select-arrow,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-select-placeholder,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-form-field-icon-suffix,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-form-field-icon-prefix{color:var(--pfx-form-label-muted)}.praxis-dynamic-form .mat-mdc-input-element,.praxis-dynamic-form .mat-mdc-select-value-text,.praxis-dynamic-form .mat-mdc-form-field-infix,.praxis-dynamic-form .mat-mdc-select-min-line{color:var(--pfx-editorial-form-text)}.praxis-dynamic-form .mat-mdc-form-field:hover .mat-mdc-text-field-wrapper,.praxis-dynamic-form .mat-mdc-form-field:hover .mdc-text-field,.praxis-dynamic-form .mat-mdc-form-field.mat-focused .mat-mdc-text-field-wrapper,.praxis-dynamic-form .mat-mdc-form-field.mat-focused .mdc-text-field{background:var(--pfx-form-field-surface-focus)}.praxis-dynamic-form.editorial-visual-context .mat-mdc-input-element,.praxis-dynamic-form.editorial-visual-context .mat-mdc-select-value-text,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field-infix,.praxis-dynamic-form.editorial-visual-context .mat-mdc-select-min-line,.praxis-dynamic-form.editorial-visual-context .mdc-text-field__input{color:var(--pfx-editorial-form-text)!important;-webkit-text-fill-color:var(--pfx-editorial-form-text)!important}.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mdc-floating-label,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-form-field-hint,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-form-field-error,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-form-field-required-marker,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-select-arrow,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-select-placeholder,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-form-field-icon-suffix,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-form-field-icon-prefix{color:var(--pfx-editorial-form-text-muted)!important}.praxis-dynamic-form.editorial-visual-context .mat-mdc-floating-label,.praxis-dynamic-form.editorial-visual-context .mdc-floating-label,.praxis-dynamic-form.editorial-visual-context .mdc-text-field__input::placeholder{color:color-mix(in srgb,var(--pfx-editorial-form-text) 68%,transparent)!important}.praxis-dynamic-form.editorial-visual-context .mat-mdc-input-element::placeholder,.praxis-dynamic-form.editorial-visual-context textarea.mat-mdc-input-element::placeholder{color:color-mix(in srgb,var(--pfx-editorial-form-text) 58%,transparent)!important}.praxis-dynamic-form .mat-mdc-radio-button,.praxis-dynamic-form .mat-mdc-checkbox,.praxis-dynamic-form .mat-mdc-slide-toggle{--mdc-radio-selected-icon-color: var(--pfx-editorial-form-accent);--mdc-radio-selected-hover-icon-color: var(--pfx-editorial-form-accent);--mdc-radio-selected-focus-icon-color: var(--pfx-editorial-form-accent);--mdc-checkbox-selected-checkmark-color: var(--pfx-editorial-form-accent-text);--mdc-checkbox-selected-focus-icon-color: var(--pfx-editorial-form-accent);--mdc-checkbox-selected-hover-icon-color: var(--pfx-editorial-form-accent);--mdc-checkbox-selected-icon-color: var(--pfx-editorial-form-accent);--mdc-switch-selected-focus-state-layer-color: var(--pfx-editorial-form-accent);--mdc-switch-selected-handle-color: var(--pfx-editorial-form-accent);--mdc-switch-selected-track-color: color-mix(in srgb, var(--pfx-editorial-form-accent) 45%, #fff)}.praxis-dynamic-form [data-field-type=input],.praxis-dynamic-form [data-field-type=textarea],.praxis-dynamic-form [data-field-type=email],.praxis-dynamic-form [data-field-type=password],.praxis-dynamic-form [data-field-type=url],.praxis-dynamic-form [data-field-type=search],.praxis-dynamic-form [data-field-type=phone],.praxis-dynamic-form [data-field-type=numericTextBox],.praxis-dynamic-form [data-field-type=currency],.praxis-dynamic-form [data-field-type=cpfCnpj],.praxis-dynamic-form [data-field-type=date],.praxis-dynamic-form [data-field-type=dateInput],.praxis-dynamic-form [data-field-type=dateRange],.praxis-dynamic-form [data-field-type=dateTimeLocal],.praxis-dynamic-form [data-field-type=time],.praxis-dynamic-form [data-field-type=timePicker],.praxis-dynamic-form [data-field-type=timeRange],.praxis-dynamic-form [data-field-type=month],.praxis-dynamic-form [data-field-type=week],.praxis-dynamic-form [data-field-type=yearInput],.praxis-dynamic-form [data-field-type=select],.praxis-dynamic-form [data-field-type=multi-select],.praxis-dynamic-form [data-field-type=searchable-select],.praxis-dynamic-form [data-field-type=async-select],.praxis-dynamic-form [data-field-type=autocomplete],.praxis-dynamic-form [data-field-type=tree-select],.praxis-dynamic-form [data-field-type=multi-select-tree],.praxis-dynamic-form [data-field-type=priceRange],.praxis-dynamic-form [data-field-type=file-upload]{display:block;width:100%;min-width:0}.praxis-dynamic-form .mat-mdc-form-field-subscript-wrapper{min-height:var(--pfx-subscript-min-h, 22px)}.form-row:last-child{margin-bottom:0}.form-column{display:grid;align-content:start;gap:var(--pfx-field-gap, 10px);flex:1;min-width:0;transition:all .2s ease;border-radius:4px;position:relative}.form-row.grid-12{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:var(--pfx-grid-gap, 16px)}.align-start{align-self:flex-start}.align-center{align-self:center}.align-end{align-self:flex-end}.align-stretch{align-self:stretch}.form-blocking-overlay{position:absolute;inset:0;background:transparent;color:var(--md-sys-color-on-surface);backdrop-filter:blur(2px) saturate(103%);-webkit-backdrop-filter:blur(2px) saturate(103%);display:flex;align-items:center;justify-content:center;flex-direction:column;gap:12px;z-index:10;pointer-events:all}@media(max-width:768px){.form-row{flex-direction:column;gap:.5rem}.form-section{padding:1rem}.section-heading{gap:10px;margin-bottom:16px;padding-bottom:14px}}.section-title{display:flex;align-items:center;gap:8px}.section-title .section-title-icon{font-size:1.25em;line-height:1}.section-title-avatar{--_pfx-form-section-avatar-size: var(--pfx-form-section-avatar-size-md, var(--pfx-form-section-avatar-size, 32px));display:inline-flex;align-items:center;justify-content:center;width:var(--_pfx-form-section-avatar-size);height:var(--_pfx-form-section-avatar-size);border-radius:999px;flex:0 0 var(--_pfx-form-section-avatar-size);overflow:hidden}.section-title-avatar.size-sm{--_pfx-form-section-avatar-size: var(--pfx-form-section-avatar-size-sm, 24px)}.section-title-avatar.size-md{--_pfx-form-section-avatar-size: var(--pfx-form-section-avatar-size-md, var(--pfx-form-section-avatar-size, 32px))}.section-title-avatar.size-lg{--_pfx-form-section-avatar-size: var(--pfx-form-section-avatar-size-lg, 40px)}.section-title-avatar-image{object-fit:cover;border:1px solid color-mix(in srgb,var(--pfx-form-section-divider) 72%,transparent);background:var(--pfx-form-section-surface-flat)}.section-title-avatar-text,.section-title-avatar-placeholder{background:color-mix(in srgb,var(--pfx-editorial-form-accent) 14%,var(--pfx-form-section-surface-flat));color:color-mix(in srgb,var(--pfx-editorial-form-accent) 82%,var(--pfx-form-label-strong) 18%);font-size:calc(var(--_pfx-form-section-avatar-size) * .41);font-weight:800;letter-spacing:.04em;text-transform:uppercase}.section-title-avatar-placeholder mat-icon{font-size:calc(var(--_pfx-form-section-avatar-size) * .5625);width:calc(var(--_pfx-form-section-avatar-size) * .5625);height:calc(var(--_pfx-form-section-avatar-size) * .5625);line-height:calc(var(--_pfx-form-section-avatar-size) * .5625)}.section-title-avatar-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"] }]
12306
+ ], template: "@if (isLoading) {\n<!-- Loading State -->\n<div class=\"form-loading\" role=\"status\" aria-live=\"polite\" aria-label=\"Carregando formul\u00E1rio\">\n <div class=\"form-loading-header\">\n <mat-progress-spinner diameter=\"32\"></mat-progress-spinner>\n <div class=\"form-loading-copy\">\n <p class=\"form-loading-title\">Carregando formul\u00E1rio...</p>\n <p class=\"form-loading-subtitle\">Preparando campos e op\u00E7\u00F5es do cadastro.</p>\n </div>\n </div>\n <div class=\"form-loading-skeleton\" aria-hidden=\"true\">\n <span class=\"form-loading-line form-loading-line-title\"></span>\n <span class=\"form-loading-line\"></span>\n <span class=\"form-loading-line form-loading-line-short\"></span>\n <span class=\"form-loading-field\"></span>\n <span class=\"form-loading-field form-loading-field-wide\"></span>\n </div>\n</div>\n} @else if (initializationStatus === 'error') {\n<!-- Error State -->\n<div class=\"form-error\">\n <mat-icon color=\"warn\" [praxisIcon]=\"'error'\"></mat-icon>\n <h3>{{ getErrorTitle() }}</h3>\n <p>{{ currentErrorMessage }}</p>\n @if (isRecoverable) {\n <button mat-stroked-button (click)=\"retryInitialization()\">\n <mat-icon [praxisIcon]=\"'refresh'\"></mat-icon>\n Tentar Novamente\n </button>\n }\n <button mat-button (click)=\"showDetailedError()\" class=\"show-details\">\n Ver Detalhes T\u00E9cnicos\n </button>\n <!-- Permitir corre\u00E7\u00E3o do resourcePath diretamente do estado de erro -->\n <button mat-flat-button color=\"primary\" (click)=\"openQuickConnect()\" class=\"connect-action\">\n <mat-icon [praxisIcon]=\"'bolt'\"></mat-icon>\n Conectar a recurso\n </button>\n</div>\n} @else if (initializationStatus === 'success') {\n<!-- Inline banner for schema change (only when customization is enabled) -->\n@if (shouldShowOutdatedInline()) {\n<div class=\"pfx-form-info-banner\" role=\"status\" aria-live=\"polite\">\n <div class=\"text\">O schema do servidor mudou. Reconciliar agora?</div>\n <div class=\"actions\">\n <button mat-stroked-button color=\"primary\" (click)=\"openConfigEditor()\">\n <mat-icon [praxisIcon]=\"'sync'\"></mat-icon>\n Reconciliar\n </button>\n <button mat-button (click)=\"onSnoozeOutdated()\">Lembrar depois</button>\n <button mat-button (click)=\"onIgnoreOutdated()\">Ignorar</button>\n </div>\n</div>\n}\n\n<!-- Configuration Controls -->\n@if (shouldShowConfigControls && enableCustomization) {\n<div class=\"form-config-controls\">\n <praxis-ai-assistant [adapter]=\"aiAdapter\"></praxis-ai-assistant>\n <button type=\"button\" mat-icon-button (click)=\"openConfigEditor()\" [disabled]=\"isLoading\" class=\"config-button\"\n [matBadge]=\"schemaOutdated ? '!' : ''\" [matBadgeHidden]=\"!schemaOutdated\" matBadgeColor=\"warn\" matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configurar formul\u00E1rio'\">\n <mat-icon [praxisIcon]=\"'settings'\"></mat-icon>\n </button>\n <button type=\"button\" mat-icon-button (click)=\"disconnect()\" matTooltip=\"Desconectar da fonte de dados\"\n [disabled]=\"isLoading\">\n <mat-icon [praxisIcon]=\"'link_off'\"></mat-icon>\n </button>\n</div>\n}\n\n<!-- Form Content -->\n@if (!resourcePath && (!config.sections || config.sections.length === 0)) {\n<praxis-empty-state-card icon=\"link\" [title]=\"'Conecte o formul\u00E1rio \u00E0 fonte de dados'\"\n [description]=\"'Informe a rota do recurso da API para gerar automaticamente os campos do formul\u00E1rio.'\"\n [primaryAction]=\"{ label: 'Conectar \u00E0 fonte de dados', icon: 'bolt', action: openQuickConnect.bind(this) }\"></praxis-empty-state-card>\n}\n<form #formHost (ngSubmit)=\"onSubmit()\" [attr.aria-busy]=\"submitting ? 'true' : null\"\n [attr.aria-label]=\"'Formul\u00E1rio ' + (config.metadata?.version || '')\" [class.canvas-mode-enabled]=\"enableCustomization\"\n [class.presentation-mode]=\"effectivePresentation\" [class.readonly-mode]=\"effectiveReadonly\"\n [class.editorial-visual-context]=\"hasEditorialVisualContext()\"\n [class.pfx-mounting]=\"isMounting\" [formGroup]=\"form\"\n [ngClass]=\"{\n 'pres-compact': presentationVars.compact,\n 'pres-label-left': presentationVars.labelPosition === 'left',\n 'pres-label-above': presentationVars.labelPosition === 'above'\n }\" [style.--pfx-pres-label-align]=\"presentationVars.labelAlign\"\n [style.--pfx-pres-label-size]=\"presentationVars.labelSize\"\n [style.--pfx-pres-label-width]=\"presentationVars.labelWidth\"\n [style.--pfx-pres-row-gap]=\"presentationVars.density === 'compact' ? '6px' : (presentationVars.density === 'cozy' ? '8px' : '10px')\"\n [style.--pfx-pres-row-padding]=\"presentationVars.density === 'compact' ? '2px 0' : (presentationVars.density === 'cozy' ? '6px 0' : '8px 0')\"\n [style.--pfx-pres-value-align]=\"presentationVars.valueAlign\"\n [style.--pfx-pres-value-size]=\"presentationVars.valueSize\"\n [style.--pdx-form-mount-duration]=\"getMountDurationVar()\"\n [style.--pdx-form-mount-offset]=\"getMountOffsetVar()\"\n [style.--pdx-form-mount-stagger]=\"getMountStaggerVar()\"\n class=\"praxis-dynamic-form\">\n <praxis-canvas-toolbar (editMetadata)=\"openSelectedElementEditor()\" (selectPath)=\"onSelectPath($event)\"\n (moveUp)=\"onToolbarMove('up')\" (moveDown)=\"onToolbarMove('down')\" (toggleReadonly)=\"onToolbarToggleReadonly()\"\n (toggleRequired)=\"onToolbarToggleRequired()\" (toggleHidden)=\"onToolbarToggleHidden()\"\n (toggleDisabled)=\"onToolbarToggleDisabled()\" (requestClose)=\"onToolbarRequestClose()\"\n *ngIf=\"enableCustomization && selectedElement\" [selectedElement]=\"selectedElement\"\n [style.transform]=\"toolbarTransform\"></praxis-canvas-toolbar>\n\n @if (formBlocksBefore?.nodes?.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before\" data-editorial-placement=\"before\">\n <praxis-rich-content\n [document]=\"formBlocksBeforeDocument\"\n [context]=\"getEditorialRichContentContext()\"\n ></praxis-rich-content>\n </section>\n }\n\n @if (actionPlacement === 'top' && !(effectivePresentation || effectiveReadonly)) {\n <praxis-form-actions data-actions-placement=\"top\" [actions]=\"effectiveActions\" [isSubmitting]=\"submitting\"\n [formIsValid]=\"effectiveFormIsValid\" [submitError]=\"submitError\" [formId]=\"formId\" [actionOverrides]=\"actionRuleProps\"\n [invalidRequiredFieldLabels]=\"invalidRequiredFieldLabels\"\n [editorialVisualContext]=\"hasEditorialVisualContext()\"\n (action)=\"onFormAction($event)\"></praxis-form-actions>\n }\n\n @for (section of config.sections; track (section.id ?? $index); let sectionIndex = $index;\n let last = $last) {\n <div class=\"section-drop-wrapper\" [attr.data-section-id]=\"section.id\">\n @if (isSectionVisible(section)) {\n <div #sectionEl class=\"form-section canvas-element\" data-canvas-type=\"section\" [attr.data-section-id]=\"section.id\"\n [attr.data-section-index]=\"sectionIndex\" (mouseenter)=\"onElementMouseEnter($event, 'section', section, sectionEl)\"\n (mouseleave)=\"onElementMouseLeave($event)\" (click)=\"onElementClick($event, 'section', section, sectionEl)\"\n [class.selected]=\"selectedElement?.domElement === sectionEl\"\n [class.hovered]=\"hoveredElement?.domElement === sectionEl && selectedElement?.domElement !== sectionEl\"\n [attr.data-section-appearance]=\"getSectionAppearance(section) || null\"\n [style.marginBottom.px]=\"!last ? getSectionGapBottom(section) : null\" [ngClass]=\"getSectionClasses(section)\"\n [ngStyle]=\"getSectionStyles(section)\">\n <div\n class=\"section-heading\"\n [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\"\n [class.step-appearance]=\"getSectionAppearance(section) === 'step'\"\n >\n <div class=\"section-heading-text\" [matTooltip]=\"getSectionHeaderTooltip(section) || null\"\n [matTooltipDisabled]=\"!getSectionHeaderTooltip(section)\">\n @if (getSectionStepLabel(section)) {\n <div class=\"section-step-label\" [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\">\n {{ getSectionStepLabel(section) }}\n </div>\n }\n @if (getSectionTitle(section)) {\n <h3 class=\"section-title\" [class.title-large]=\"getSectionTitleStyle(section) === 'titleLarge'\"\n [class.title-medium]=\"getSectionTitleStyle(section) === 'titleMedium'\"\n [class.title-small]=\"getSectionTitleStyle(section) === 'titleSmall'\"\n [class.headline-small]=\"getSectionTitleStyle(section) === 'headlineSmall'\"\n [style.marginBottom.px]=\"getSectionTitleGapBottom(section) ?? 20\" [style.color]=\"getSectionTitleColor(section) || null\"\n [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\"\n [attr.id]=\"sectionPanelId(section, sectionIndex) + '-title'\">\n @let sectionHeaderVisual = getSectionHeaderVisual(section);\n @let sectionHeaderAvatarSize = getSectionHeaderAvatarSize(section);\n @if (sectionHeaderVisual.kind === 'icon') {\n <mat-icon class=\"section-title-icon\" aria-hidden=\"true\" [praxisIcon]=\"sectionHeaderVisual.icon\"></mat-icon>\n }\n @if (sectionHeaderVisual.kind === 'image') {\n <img class=\"section-title-avatar section-title-avatar-image\" [class.size-sm]=\"sectionHeaderAvatarSize === 'sm'\"\n [class.size-md]=\"sectionHeaderAvatarSize === 'md'\" [class.size-lg]=\"sectionHeaderAvatarSize === 'lg'\"\n [src]=\"sectionHeaderVisual.src\" [alt]=\"sectionHeaderVisual.alt\" />\n }\n @if (sectionHeaderVisual.kind === 'initials') {\n <span class=\"section-title-avatar section-title-avatar-text\" [class.size-sm]=\"sectionHeaderAvatarSize === 'sm'\"\n [class.size-md]=\"sectionHeaderAvatarSize === 'md'\" [class.size-lg]=\"sectionHeaderAvatarSize === 'lg'\" aria-hidden=\"true\">\n {{ sectionHeaderVisual.text }}\n </span>\n @if (sectionHeaderVisual.ariaLabel) {\n <span class=\"section-title-avatar-sr-only\">{{ sectionHeaderVisual.ariaLabel }}</span>\n }\n }\n @if (sectionHeaderVisual.kind === 'placeholder') {\n <span class=\"section-title-avatar section-title-avatar-placeholder\" [class.size-sm]=\"sectionHeaderAvatarSize === 'sm'\"\n [class.size-md]=\"sectionHeaderAvatarSize === 'md'\" [class.size-lg]=\"sectionHeaderAvatarSize === 'lg'\" aria-hidden=\"true\">\n @if (sectionHeaderVisual.icon) {\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"sectionHeaderVisual.icon\"></mat-icon>\n }\n @if (sectionHeaderVisual.text) {\n <span>{{ sectionHeaderVisual.text }}</span>\n }\n </span>\n @if (sectionHeaderVisual.ariaLabel) {\n <span class=\"section-title-avatar-sr-only\">{{ sectionHeaderVisual.ariaLabel }}</span>\n }\n }\n {{ getSectionTitle(section) }}\n @if (enableCustomization) {\n <button type=\"button\" class=\"inline-edit-btn\" mat-icon-button (click)=\"openSectionEditor(section, 'title')\"\n aria-label=\"Editar t\u00EDtulo da se\u00E7\u00E3o\" matTooltip=\"Editar t\u00EDtulo\">\n <mat-icon>edit</mat-icon>\n </button>\n }\n </h3>\n }\n @if (getSectionDescription(section)) {\n <p class=\"section-description\" [class.body-large]=\"getSectionDescriptionStyle(section) === 'bodyLarge'\"\n [class.body-medium]=\"getSectionDescriptionStyle(section) === 'bodyMedium'\"\n [class.body-small]=\"getSectionDescriptionStyle(section) === 'bodySmall'\"\n [style.marginBottom.px]=\"getSectionDescriptionGapBottom(section) ?? 8\" [style.color]=\"getSectionDescriptionColor(section) || null\"\n [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\">\n {{ getSectionDescription(section) }}\n @if (enableCustomization) {\n <button type=\"button\" class=\"inline-edit-btn\" mat-icon-button\n (click)=\"openSectionEditor(section, 'description')\" aria-label=\"Editar descri\u00E7\u00E3o da se\u00E7\u00E3o\"\n matTooltip=\"Editar descri\u00E7\u00E3o\">\n <mat-icon>edit</mat-icon>\n </button>\n }\n </p>\n }\n </div>\n @if (getSectionHeaderActions(section).length) {\n <div class=\"section-heading-actions\" [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\">\n @for (action of getSectionHeaderActions(section); track action.id) {\n <button\n type=\"button\"\n class=\"section-heading-action-btn\"\n mat-icon-button\n [color]=\"getSectionHeaderActionColor(action)\"\n [disabled]=\"isSectionHeaderActionDisabled(action)\"\n [matTooltip]=\"getSectionHeaderActionTooltip(action)\"\n [attr.aria-label]=\"action.label\"\n [attr.aria-busy]=\"action.loading ? 'true' : null\"\n [ngClass]=\"getSectionHeaderActionNgClass(action)\"\n [ngStyle]=\"getSectionHeaderActionStyles(action)\"\n (click)=\"onSectionHeaderActionClick(section, action, $event)\"\n >\n @if (action.loading) {\n <mat-progress-spinner diameter=\"16\" mode=\"indeterminate\"></mat-progress-spinner>\n <span class=\"section-heading-action-sr-only\">{{ getSectionHeaderActionLoadingLabel(action) }}</span>\n } @else {\n <mat-icon [praxisIcon]=\"action.icon\"></mat-icon>\n }\n </button>\n }\n </div>\n }\n @if (isSectionCollapsible(section)) {\n <button type=\"button\" class=\"section-collapse-btn\" mat-icon-button\n (click)=\"toggleSectionCollapse($event, section)\" [attr.aria-expanded]=\"!isSectionCollapsed(section)\"\n [attr.aria-controls]=\"sectionPanelId(section, sectionIndex)\"\n [attr.aria-label]=\"isSectionCollapsed(section) ? 'Expandir se\u00E7\u00E3o' : 'Recolher se\u00E7\u00E3o'\">\n <mat-icon [praxisIcon]=\"isSectionCollapsed(section) ? 'expand_more' : 'expand_less'\"></mat-icon>\n </button>\n }\n </div>\n\n <div class=\"section-body\" [class.collapsed]=\"isSectionCollapsed(section)\"\n [attr.id]=\"sectionPanelId(section, sectionIndex)\"\n [attr.aria-labelledby]=\"getSectionTitle(section) ? sectionPanelId(section, sectionIndex) + '-title' : null\">\n @if (!isSectionCollapsed(section)) {\n @for (row of section.rows; track (row.id ?? $index); let rowIndex = $index) {\n @if (isRowVisible(row)) {\n <div class=\"row-drop-wrapper\" [attr.data-section-id]=\"section.id\">\n <div #rowEl class=\"form-row grid-12 canvas-element\" data-canvas-type=\"row\" [attr.data-row-index]=\"rowIndex\"\n [attr.data-section-id]=\"section.id\" [attr.data-row-id]=\"row.id\" [style.--pfx-grid-gap.px]=\"getRowGap(row)\"\n [style.--pfx-field-gap.px]=\"getRowRowGap(row)\"\n [style.--pfx-mount-index]=\"rowIndex\"\n [style.marginBottom.px]=\"rowIndex < section.rows.length - 1 ? (getRowRowGap(row) ?? null) : null\"\n [ngClass]=\"getRowClasses(row)\" [ngStyle]=\"getRowStyles(row)\"\n (mouseenter)=\"onElementMouseEnter($event, 'row', row, rowEl)\" (mouseleave)=\"onElementMouseLeave($event)\"\n (click)=\"onElementClick($event, 'row', row, rowEl)\" [class.selected]=\"selectedElement?.domElement === rowEl\"\n [class.hovered]=\"hoveredElement?.domElement === rowEl && selectedElement?.domElement !== rowEl\">\n @for (column of row.columns; track (column.id ?? $index); let colIndex = $index) {\n @if (isColumnVisible(column)) {\n <div class=\"column-drop-wrapper\" [attr.data-section-id]=\"section.id\" [attr.data-row-id]=\"row.id\">\n <div #colEl class=\"form-column canvas-element\" [ngClass]=\"getColumnClasses(column)\"\n [style.padding.px]=\"getColumnPadding(column)\" [style.--pfx-field-gap.px]=\"getRowRowGap(row)\"\n [ngStyle]=\"getColumnStyles(column)\" [attr.data-testid]=\"column.testId || null\"\n [attr.data-row-gap]=\"getRowRowGap(row)\" data-canvas-type=\"column\" [attr.data-column-index]=\"colIndex\"\n [attr.data-row-index]=\"rowIndex\" [attr.data-section-id]=\"section.id\" [attr.data-row-id]=\"row.id\"\n [attr.data-column-id]=\"column.id\" (mouseenter)=\"onElementMouseEnter($event, 'column', column, colEl)\"\n (mouseleave)=\"onElementMouseLeave($event)\" (click)=\"onElementClick($event, 'column', column, colEl)\"\n [class.selected]=\"selectedElement?.domElement === colEl\"\n [class.hovered]=\"hoveredElement?.domElement === colEl && selectedElement?.domElement !== colEl\">\n <ng-container dynamicFieldLoader [fields]=\"getColumnFields(column)\" [formGroup]=\"form\"\n [enableExternalControlBinding]=\"true\"\n [readonlyMode]=\"readonlyModeGlobal === null ? null : readonlyModeGlobal\"\n [disabledMode]=\"effectiveDisabledMode\"\n [presentationMode]=\"presentationForLoader\" [visible]=\"visibleGlobal === null ? null : visibleGlobal\"\n [canvasMode]=\"enableCustomization\" (canvasMouseEnter)=\"onFieldMouseEnter($event)\"\n (canvasMouseLeave)=\"onFieldMouseLeave($event)\" (canvasClick)=\"onFieldClick($event)\"\n (renderError)=\"onFieldRenderError($event)\">\n </ng-container>\n </div>\n </div>\n } }\n </div>\n </div>\n }\n }\n } @else {\n <div class=\"section-collapsed-placeholder\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'unfold_more'\"></mat-icon>\n <span>{{ getSectionCollapsedSummary(section) }}</span>\n </div>\n }\n @if (last && beforeActionsPlacement === 'insideLastSection' && formBlocksBeforeActions?.nodes?.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before-actions\" data-editorial-placement=\"beforeActions\">\n <praxis-rich-content\n [document]=\"formBlocksBeforeActionsDocument\"\n [context]=\"getEditorialRichContentContext()\"\n ></praxis-rich-content>\n </section>\n }\n @if (actionPlacement === 'insideLastSection' && last && !(effectivePresentation\n || effectiveReadonly)) {\n <praxis-form-actions data-actions-placement=\"insideLastSection\" [actions]=\"effectiveActions\"\n [isSubmitting]=\"submitting\" [formIsValid]=\"effectiveFormIsValid\" [submitError]=\"submitError\" [formId]=\"formId\"\n [actionOverrides]=\"actionRuleProps\" [invalidRequiredFieldLabels]=\"invalidRequiredFieldLabels\"\n [editorialVisualContext]=\"hasEditorialVisualContext()\"\n (action)=\"onFormAction($event)\"></praxis-form-actions>\n }\n </div>\n <!-- Overlay de bloqueio durante submiss\u00E3o -->\n @if (submitting) {\n <div class=\"form-blocking-overlay\" aria-live=\"polite\">\n <mat-progress-spinner diameter=\"40\" color=\"primary\"></mat-progress-spinner>\n <p>{{ config.messages?.loading?.submit || 'Salvando...' }}</p>\n </div>\n }\n </div>\n\n @if (enableCustomization && selectedElement?.domElement === sectionEl) {\n <div class=\"add-section-container\">\n <div class=\"add-section-line\"></div>\n <button mat-fab color=\"primary\" aria-label=\"Adicionar nova se\u00E7\u00E3o\" (click)=\"addNewSectionAfter(sectionIndex)\"\n matTooltip=\"Adicionar nova se\u00E7\u00E3o aqui\">\n <mat-icon [praxisIcon]=\"'add'\"></mat-icon>\n </button>\n <div class=\"add-section-line\"></div>\n </div>\n }\n }\n </div>\n }\n\n @if (beforeActionsPlacement !== 'insideLastSection' && formBlocksBeforeActions?.nodes?.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before-actions\" data-editorial-placement=\"beforeActions\">\n <praxis-rich-content\n [document]=\"formBlocksBeforeActionsDocument\"\n [context]=\"getEditorialRichContentContext()\"\n ></praxis-rich-content>\n </section>\n }\n\n @if (actionPlacement === 'afterSections' && !(effectivePresentation || effectiveReadonly)) {\n <praxis-form-actions data-actions-placement=\"afterSections\" [actions]=\"effectiveActions\" [isSubmitting]=\"submitting\"\n [formIsValid]=\"effectiveFormIsValid\" [submitError]=\"submitError\" [formId]=\"formId\"\n [actionOverrides]=\"actionRuleProps\" [invalidRequiredFieldLabels]=\"invalidRequiredFieldLabels\"\n [editorialVisualContext]=\"hasEditorialVisualContext()\"\n (action)=\"onFormAction($event)\"></praxis-form-actions>\n }\n\n @if (formBlocksAfter?.nodes?.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-after\" data-editorial-placement=\"after\">\n <praxis-rich-content\n [document]=\"formBlocksAfterDocument\"\n [context]=\"getEditorialRichContentContext()\"\n ></praxis-rich-content>\n </section>\n }\n</form>\n@if (!enableCustomization && mode === 'view') {\n<div class=\"ai-floating-assistant\">\n <praxis-ai-assistant [adapter]=\"aiAdapter\"></praxis-ai-assistant>\n</div>\n}\n}\n", styles: ["@charset \"UTF-8\";.span-xs-1{grid-column:span 1}.span-xs-2{grid-column:span 2}.span-xs-3{grid-column:span 3}.span-xs-4{grid-column:span 4}.span-xs-5{grid-column:span 5}.span-xs-6{grid-column:span 6}.span-xs-7{grid-column:span 7}.span-xs-8{grid-column:span 8}.span-xs-9{grid-column:span 9}.span-xs-10{grid-column:span 10}.span-xs-11{grid-column:span 11}.span-xs-12{grid-column:span 12}.offset-xs-0{margin-left:0%}.offset-xs-1{margin-left:calc(1 / 12 * 100%)}.offset-xs-2{margin-left:calc(2 / 12 * 100%)}.offset-xs-3{margin-left:25%}.offset-xs-4{margin-left:calc(4 / 12 * 100%)}.offset-xs-5{margin-left:calc(5 / 12 * 100%)}.offset-xs-6{margin-left:50%}.offset-xs-7{margin-left:calc(7 / 12 * 100%)}.offset-xs-8{margin-left:calc(8 / 12 * 100%)}.offset-xs-9{margin-left:75%}.offset-xs-10{margin-left:calc(10 / 12 * 100%)}.offset-xs-11{margin-left:calc(11 / 12 * 100%)}.order-xs--12{order:-12}.order-xs--11{order:-11}.order-xs--10{order:-10}.order-xs--9{order:-9}.order-xs--8{order:-8}.order-xs--7{order:-7}.order-xs--6{order:-6}.order-xs--5{order:-5}.order-xs--4{order:-4}.order-xs--3{order:-3}.order-xs--2{order:-2}.order-xs--1{order:-1}.order-xs-0{order:0}.order-xs-1{order:1}.order-xs-2{order:2}.order-xs-3{order:3}.order-xs-4{order:4}.order-xs-5{order:5}.order-xs-6{order:6}.order-xs-7{order:7}.order-xs-8{order:8}.order-xs-9{order:9}.order-xs-10{order:10}.order-xs-11{order:11}.order-xs-12{order:12}.hidden-xs{display:none}@media(min-width:600px){.span-sm-1{grid-column:span 1}.span-sm-2{grid-column:span 2}.span-sm-3{grid-column:span 3}.span-sm-4{grid-column:span 4}.span-sm-5{grid-column:span 5}.span-sm-6{grid-column:span 6}.span-sm-7{grid-column:span 7}.span-sm-8{grid-column:span 8}.span-sm-9{grid-column:span 9}.span-sm-10{grid-column:span 10}.span-sm-11{grid-column:span 11}.span-sm-12{grid-column:span 12}.offset-sm-0{margin-left:0%}.offset-sm-1{margin-left:calc(1 / 12 * 100%)}.offset-sm-2{margin-left:calc(2 / 12 * 100%)}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:calc(4 / 12 * 100%)}.offset-sm-5{margin-left:calc(5 / 12 * 100%)}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:calc(7 / 12 * 100%)}.offset-sm-8{margin-left:calc(8 / 12 * 100%)}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:calc(10 / 12 * 100%)}.offset-sm-11{margin-left:calc(11 / 12 * 100%)}.order-sm--12{order:-12}.order-sm--11{order:-11}.order-sm--10{order:-10}.order-sm--9{order:-9}.order-sm--8{order:-8}.order-sm--7{order:-7}.order-sm--6{order:-6}.order-sm--5{order:-5}.order-sm--4{order:-4}.order-sm--3{order:-3}.order-sm--2{order:-2}.order-sm--1{order:-1}.order-sm-0{order:0}.order-sm-1{order:1}.order-sm-2{order:2}.order-sm-3{order:3}.order-sm-4{order:4}.order-sm-5{order:5}.order-sm-6{order:6}.order-sm-7{order:7}.order-sm-8{order:8}.order-sm-9{order:9}.order-sm-10{order:10}.order-sm-11{order:11}.order-sm-12{order:12}.hidden-sm{display:none}}@media(min-width:900px){.span-md-1{grid-column:span 1}.span-md-2{grid-column:span 2}.span-md-3{grid-column:span 3}.span-md-4{grid-column:span 4}.span-md-5{grid-column:span 5}.span-md-6{grid-column:span 6}.span-md-7{grid-column:span 7}.span-md-8{grid-column:span 8}.span-md-9{grid-column:span 9}.span-md-10{grid-column:span 10}.span-md-11{grid-column:span 11}.span-md-12{grid-column:span 12}.offset-md-0{margin-left:0%}.offset-md-1{margin-left:calc(1 / 12 * 100%)}.offset-md-2{margin-left:calc(2 / 12 * 100%)}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:calc(4 / 12 * 100%)}.offset-md-5{margin-left:calc(5 / 12 * 100%)}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:calc(7 / 12 * 100%)}.offset-md-8{margin-left:calc(8 / 12 * 100%)}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:calc(10 / 12 * 100%)}.offset-md-11{margin-left:calc(11 / 12 * 100%)}.order-md--12{order:-12}.order-md--11{order:-11}.order-md--10{order:-10}.order-md--9{order:-9}.order-md--8{order:-8}.order-md--7{order:-7}.order-md--6{order:-6}.order-md--5{order:-5}.order-md--4{order:-4}.order-md--3{order:-3}.order-md--2{order:-2}.order-md--1{order:-1}.order-md-0{order:0}.order-md-1{order:1}.order-md-2{order:2}.order-md-3{order:3}.order-md-4{order:4}.order-md-5{order:5}.order-md-6{order:6}.order-md-7{order:7}.order-md-8{order:8}.order-md-9{order:9}.order-md-10{order:10}.order-md-11{order:11}.order-md-12{order:12}.hidden-md{display:none}}@media(min-width:1200px){.span-lg-1{grid-column:span 1}.span-lg-2{grid-column:span 2}.span-lg-3{grid-column:span 3}.span-lg-4{grid-column:span 4}.span-lg-5{grid-column:span 5}.span-lg-6{grid-column:span 6}.span-lg-7{grid-column:span 7}.span-lg-8{grid-column:span 8}.span-lg-9{grid-column:span 9}.span-lg-10{grid-column:span 10}.span-lg-11{grid-column:span 11}.span-lg-12{grid-column:span 12}.offset-lg-0{margin-left:0%}.offset-lg-1{margin-left:calc(1 / 12 * 100%)}.offset-lg-2{margin-left:calc(2 / 12 * 100%)}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:calc(4 / 12 * 100%)}.offset-lg-5{margin-left:calc(5 / 12 * 100%)}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:calc(7 / 12 * 100%)}.offset-lg-8{margin-left:calc(8 / 12 * 100%)}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:calc(10 / 12 * 100%)}.offset-lg-11{margin-left:calc(11 / 12 * 100%)}.order-lg--12{order:-12}.order-lg--11{order:-11}.order-lg--10{order:-10}.order-lg--9{order:-9}.order-lg--8{order:-8}.order-lg--7{order:-7}.order-lg--6{order:-6}.order-lg--5{order:-5}.order-lg--4{order:-4}.order-lg--3{order:-3}.order-lg--2{order:-2}.order-lg--1{order:-1}.order-lg-0{order:0}.order-lg-1{order:1}.order-lg-2{order:2}.order-lg-3{order:3}.order-lg-4{order:4}.order-lg-5{order:5}.order-lg-6{order:6}.order-lg-7{order:7}.order-lg-8{order:8}.order-lg-9{order:9}.order-lg-10{order:10}.order-lg-11{order:11}.order-lg-12{order:12}.hidden-lg{display:none}}@media(min-width:1536px){.span-xl-1{grid-column:span 1}.span-xl-2{grid-column:span 2}.span-xl-3{grid-column:span 3}.span-xl-4{grid-column:span 4}.span-xl-5{grid-column:span 5}.span-xl-6{grid-column:span 6}.span-xl-7{grid-column:span 7}.span-xl-8{grid-column:span 8}.span-xl-9{grid-column:span 9}.span-xl-10{grid-column:span 10}.span-xl-11{grid-column:span 11}.span-xl-12{grid-column:span 12}.offset-xl-0{margin-left:0%}.offset-xl-1{margin-left:calc(1 / 12 * 100%)}.offset-xl-2{margin-left:calc(2 / 12 * 100%)}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:calc(4 / 12 * 100%)}.offset-xl-5{margin-left:calc(5 / 12 * 100%)}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:calc(7 / 12 * 100%)}.offset-xl-8{margin-left:calc(8 / 12 * 100%)}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:calc(10 / 12 * 100%)}.offset-xl-11{margin-left:calc(11 / 12 * 100%)}.order-xl--12{order:-12}.order-xl--11{order:-11}.order-xl--10{order:-10}.order-xl--9{order:-9}.order-xl--8{order:-8}.order-xl--7{order:-7}.order-xl--6{order:-6}.order-xl--5{order:-5}.order-xl--4{order:-4}.order-xl--3{order:-3}.order-xl--2{order:-2}.order-xl--1{order:-1}.order-xl-0{order:0}.order-xl-1{order:1}.order-xl-2{order:2}.order-xl-3{order:3}.order-xl-4{order:4}.order-xl-5{order:5}.order-xl-6{order:6}.order-xl-7{order:7}.order-xl-8{order:8}.order-xl-9{order:9}.order-xl-10{order:10}.order-xl-11{order:11}.order-xl-12{order:12}.hidden-xl{display:none}}.canvas-mode-enabled{--canvas-hit: 14px}.canvas-mode-enabled .canvas-element{position:relative;z-index:0;border-radius:8px;outline:2px solid transparent;outline-offset:2px;transition:outline-color .2s ease,outline-style .2s ease}.canvas-mode-enabled .canvas-element:before{content:\"\";position:absolute;inset:calc(-1 * var(--canvas-hit));pointer-events:none;border-radius:inherit;background:transparent}.canvas-mode-enabled .canvas-element[data-canvas-type=section]{--outline-color: var(--md-sys-color-primary)}.canvas-mode-enabled .canvas-element[data-canvas-type=row]{--outline-color: var(--md-sys-color-secondary)}.canvas-mode-enabled .canvas-element[data-canvas-type=column],.canvas-mode-enabled .canvas-element[data-canvas-type=field]{--outline-color: var(--md-sys-color-tertiary)}.canvas-mode-enabled .canvas-element[data-canvas-type=actions]{--outline-color: var(--md-sys-color-primary)}.canvas-mode-enabled .canvas-element.hovered:not(.selected){outline-color:var(--outline-color, var(--md-sys-color-primary));outline-style:dashed}.canvas-mode-enabled .canvas-element.selected{outline-color:var(--outline-color, var(--md-sys-color-primary));outline-style:solid;box-shadow:0 0 0 2px var(--outline-color, var(--md-sys-color-primary))}.canvas-mode-enabled .canvas-element.hovered{z-index:var(--praxis-layer-authoring-hover, 300)}.canvas-mode-enabled .canvas-element.selected{z-index:var(--praxis-layer-authoring-selected, 320)}.section-drop-wrapper,.row-drop-wrapper,.column-drop-wrapper{display:contents}.add-section-container{display:flex;align-items:center;justify-content:center;padding:.5rem 0;margin:-.5rem 0;position:relative;z-index:var(--praxis-layer-authoring-insert, 280)}.add-section-container .add-section-line{flex-grow:1;height:1px;background:repeating-linear-gradient(90deg,var(--md-sys-color-outline-variant),var(--md-sys-color-outline-variant) 4px,transparent 4px,transparent 8px)}.add-section-container button{margin:0 1rem;transform:scale(.85)}:host{display:block;position:relative}.form-config-controls{position:sticky;top:10px;margin-left:auto;margin-bottom:10px;display:flex;align-items:center;gap:.35rem;z-index:60;background:color-mix(in srgb,var(--md-sys-color-surface) 92%,transparent);padding:6px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 82%,transparent);border-radius:999px;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);box-shadow:0 10px 22px #0f172a14;min-width:0;justify-content:flex-end;pointer-events:none;opacity:.88;transition:opacity .16s ease,box-shadow .16s ease,border-color .16s ease,transform .16s ease}.form-config-controls>*{pointer-events:auto}.praxis-dynamic-form:hover .form-config-controls,.praxis-dynamic-form:focus-within .form-config-controls{opacity:1;border-color:color-mix(in srgb,var(--md-sys-color-primary) 26%,var(--md-sys-color-outline-variant) 74%);box-shadow:0 14px 28px #0f172a1f;transform:translateY(-1px)}.form-config-controls .mat-icon-button{--mdc-icon-button-state-layer-size: 34px;width:34px;height:34px;color:var(--md-sys-color-on-surface-variant);background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 82%,transparent);border:1px solid transparent}.form-config-controls .mat-icon-button:hover{color:var(--md-sys-color-primary);background:var(--md-sys-color-primary-container);border-color:color-mix(in srgb,var(--md-sys-color-primary) 20%,transparent)}.ai-floating-assistant{position:sticky;right:0;bottom:12px;margin-top:14px;margin-left:auto;width:fit-content;z-index:70;pointer-events:none}.ai-floating-assistant praxis-ai-assistant{pointer-events:auto;display:inline-flex}.ai-floating-assistant .ai-trigger-btn{box-shadow:0 12px 28px #0f172a24}.config-button{color:var(--md-sys-color-primary)}.form-loading{display:flex;flex-direction:column;align-items:stretch;justify-content:center;padding:1.25rem;text-align:left;color:var(--md-sys-color-on-surface);gap:1rem;min-height:220px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 14%,var(--md-sys-color-outline-variant) 86%);border-radius:8px;background:linear-gradient(135deg,color-mix(in srgb,var(--md-sys-color-primary) 8%,transparent),transparent 42%),color-mix(in srgb,var(--md-sys-color-surface-container-low) 78%,var(--md-sys-color-surface) 22%);box-shadow:0 12px 28px #0f172a14}.form-loading-header{display:flex;align-items:center;gap:.85rem}.form-loading-copy{min-width:0}.form-loading p{margin:0}.form-loading-title{font-weight:700;color:var(--md-sys-color-on-surface)}.form-loading-subtitle{margin-top:.2rem;font-size:.875rem;color:var(--md-sys-color-on-surface-variant)}.form-loading-skeleton{display:grid;gap:.7rem}.form-loading-line,.form-loading-field{display:block;overflow:hidden;position:relative;border-radius:6px;background:color-mix(in srgb,var(--md-sys-color-surface-container-high) 74%,var(--md-sys-color-primary) 6%)}.form-loading-line:after,.form-loading-field:after{content:\"\";position:absolute;inset:0;transform:translate(-100%);background:linear-gradient(90deg,transparent,color-mix(in srgb,var(--md-sys-color-primary) 16%,white 18%),transparent);animation:form-loading-shimmer 1.4s ease-in-out infinite}.form-loading-line{height:12px}.form-loading-line-title{width:min(42%,240px)}.form-loading-line-short{width:min(64%,360px)}.form-loading-field{height:44px}.form-loading-field-wide{height:72px}@keyframes form-loading-shimmer{to{transform:translate(100%)}}@media(prefers-reduced-motion:reduce){.form-loading-line:after,.form-loading-field:after{animation:none;transform:none;opacity:.28}}.form-error{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3rem;text-align:center;color:var(--md-sys-color-error);gap:1rem;border:1px solid var(--md-sys-color-error);border-radius:8px;background-color:var(--md-sys-color-error-container);margin:1rem;box-shadow:0 12px 30px #7f1d1d1f}.form-error h3{margin:0;color:var(--md-sys-color-on-error-container)}.form-error p{margin:0;color:var(--md-sys-color-on-error-container);opacity:.8}.form-error button{margin-top:.5rem}.pfx-form-info-banner{margin-bottom:14px;padding:12px 14px;border-radius:16px;border:1px solid color-mix(in srgb,var(--md-sys-color-primary) 18%,var(--md-sys-color-outline-variant) 82%);background:color-mix(in srgb,var(--md-sys-color-primary-container) 24%,var(--md-sys-color-surface) 76%);color:var(--md-sys-color-on-surface);display:flex;align-items:flex-start;justify-content:space-between;gap:14px}.pfx-form-info-banner .text{font-size:.92rem;line-height:1.5;font-weight:600}.pfx-form-info-banner .actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px}.praxis-dynamic-form{display:flex;flex-direction:column;transition:all .3s ease;position:relative;font-family:inherit;font-size:inherit;color:inherit;--pfx-editorial-form-surface: var( --pfx-form-section-surface, var(--md-sys-color-surface-container) );--pfx-editorial-form-surface-muted: var(--md-sys-color-surface-container-low);--pfx-editorial-form-border: var( --pfx-form-stroke, var(--md-sys-color-outline-variant) );--pfx-editorial-form-text: var(--md-sys-color-on-surface);--pfx-editorial-form-text-muted: var(--md-sys-color-on-surface-variant);--pfx-editorial-form-field-surface: color-mix( in srgb, var(--pfx-editorial-form-surface-muted) 76%, var(--pfx-editorial-form-surface) 24% );--pfx-editorial-form-field-outline: color-mix( in srgb, var(--pfx-editorial-form-border) 88%, transparent );--pfx-editorial-form-accent: var(--md-sys-color-primary);--pfx-editorial-form-accent-text: var(--md-sys-color-on-primary);--pfx-editorial-form-radius: var(--pfx-form-section-radius, 8px);--pfx-editorial-form-field-radius: var(--pfx-form-field-radius, 4px);--pfx-editorial-form-border-width: 1px;--pfx-editorial-form-field-border-width: 1px;--pfx-editorial-form-shadow: none;--pfx-form-shell-surface: color-mix( in srgb, var(--pfx-editorial-form-surface) 36%, transparent );--pfx-form-section-surface-flat: color-mix( in srgb, var(--pfx-editorial-form-surface) 78%, var(--pfx-editorial-form-surface-muted) 22% );--pfx-form-section-divider: color-mix( in srgb, var(--pfx-editorial-form-border) 68%, transparent );--pfx-form-label-strong: color-mix( in srgb, var(--pfx-editorial-form-text) 96%, white 4% );--pfx-form-label-muted: color-mix( in srgb, var(--pfx-editorial-form-text-muted) 92%, var(--pfx-editorial-form-text) 8% );--pfx-form-field-surface-rest: color-mix( in srgb, var(--pfx-editorial-form-field-surface) 82%, var(--pfx-editorial-form-surface) 18% );--pfx-form-field-surface-focus: color-mix( in srgb, var(--pfx-editorial-form-accent) 4%, var(--pfx-form-field-surface-rest) 96% );--pfx-form-field-min-height: 48px;--pfx-form-section-padding: clamp(1.1rem, 1.8vw, 1.5rem);--pfx-form-section-gap: 22px;--pfx-form-footer-surface: color-mix( in srgb, var(--pfx-editorial-form-surface) 70%, var(--pfx-editorial-form-surface-muted) 30% );--pfx-form-footer-border: color-mix( in srgb, var(--pfx-editorial-form-border) 76%, transparent )}.praxis-dynamic-form.editorial-visual-context{font-family:var(--editorial-body-font-family, inherit);font-size:var(--editorial-body-size, 1rem);color:var(--editorial-text-primary, var(--md-sys-color-on-surface));--pfx-editorial-form-surface: var( --editorial-surface-primary, var(--pfx-form-section-surface, var(--md-sys-color-surface-container)) );--pfx-editorial-form-surface-muted: var( --editorial-surface-secondary, var(--md-sys-color-surface-container-low) );--pfx-editorial-form-border: var( --editorial-border-color, var(--pfx-form-stroke, var(--md-sys-color-outline-variant)) );--pfx-editorial-form-text: var( --editorial-text-primary, var(--md-sys-color-on-surface) );--pfx-editorial-form-text-muted: var( --editorial-text-secondary, var(--md-sys-color-on-surface-variant) );--pfx-editorial-form-accent: var( --editorial-cta-primary, var(--editorial-accent, var(--md-sys-color-primary)) );--pfx-editorial-form-accent-text: var( --editorial-cta-primary-text, var(--editorial-accent-contrast, var(--md-sys-color-on-primary)) );--pfx-editorial-form-radius: var(--editorial-card-radius, 18px);--pfx-editorial-form-field-radius: var( --editorial-field-radius, var(--editorial-card-radius, 14px) );--pfx-editorial-form-border-width: var(--editorial-card-border-width, 1px);--pfx-editorial-form-field-border-width: var(--editorial-field-border-width, 1px);--pfx-editorial-form-shadow: var(--editorial-card-shadow, none);--md-sys-color-surface: var( --pfx-editorial-form-surface, var(--md-sys-color-surface) );--md-sys-color-surface-container: var( --pfx-editorial-form-surface, var(--md-sys-color-surface-container) );--md-sys-color-surface-container-low: var( --pfx-editorial-form-surface-muted, var(--md-sys-color-surface-container-low) );--md-sys-color-surface-variant: var( --pfx-editorial-form-field-surface, var(--md-sys-color-surface-variant) );--md-sys-color-outline: var( --pfx-editorial-form-field-outline, var(--md-sys-color-outline) );--md-sys-color-outline-variant: var( --pfx-editorial-form-border, var(--md-sys-color-outline-variant) );--md-sys-color-on-surface: var( --pfx-editorial-form-text, var(--md-sys-color-on-surface) );--md-sys-color-on-surface-variant: var( --pfx-editorial-form-text-muted, var(--md-sys-color-on-surface-variant) );--md-sys-color-primary: var( --pfx-editorial-form-accent, var(--md-sys-color-primary) );--md-sys-color-on-primary: var( --pfx-editorial-form-accent-text, var(--md-sys-color-on-primary) );--md-sys-color-on-surface-rgb: var( --editorial-text-primary-rgb, var(--md-sys-color-on-surface-rgb) );--mat-sys-on-surface: var( --pfx-editorial-form-text, var(--mat-sys-on-surface) );--mat-sys-on-surface-variant: var( --pfx-editorial-form-text-muted, var(--mat-sys-on-surface-variant) );--mat-sys-on-surface-rgb: var( --editorial-text-primary-rgb, var(--mat-sys-on-surface-rgb, var(--md-sys-color-on-surface-rgb)) );color:var(--pfx-editorial-form-text);color-scheme:light}.praxis-dynamic-form.presentation-mode .form-row,.praxis-dynamic-form.readonly-mode .form-row{gap:.5rem;margin-bottom:.5rem}.praxis-dynamic-form.presentation-mode .form-section,.praxis-dynamic-form.readonly-mode .form-section{padding:.75rem .875rem}.praxis-dynamic-form.pres-compact .form-row{gap:.35rem;margin-bottom:.35rem}.praxis-dynamic-form.pres-compact .form-section{padding:.5rem .75rem}.praxis-dynamic-form.pres-label-left .praxis-presentation{display:grid;grid-template-columns:var(--pfx-presentation-label-w, 220px) 1fr;align-items:baseline;column-gap:10px}.praxis-dynamic-form.pres-label-left .praxis-presentation__label{text-align:right;margin-bottom:0}.form-section{border:1px solid var(--pfx-form-section-divider);border-radius:var(--pfx-editorial-form-radius);padding:var(--pfx-form-section-padding);background:var(--pfx-form-section-surface-flat);box-shadow:none;transition:all .2s ease;position:relative}.praxis-dynamic-form.editorial-visual-context .form-section{border:var(--pfx-editorial-form-border-width) solid var(--pfx-editorial-form-border)!important;background:var(--pfx-editorial-form-surface)!important;box-shadow:var(--editorial-card-shadow, none)}.praxis-dynamic-form.editorial-visual-context .form-section.section-appearance-step{background-color:var(--pfx-editorial-form-surface)!important;background-image:linear-gradient(180deg,color-mix(in srgb,var(--editorial-accent, var(--md-sys-color-primary)) 6%,transparent) 0%,transparent 132px)!important;background-repeat:no-repeat!important}.section-drop-wrapper>.form-section{margin-bottom:var(--pfx-section-gap, 20px)}.section-drop-wrapper:last-of-type>.form-section{margin-bottom:0}.praxis-dynamic-form>praxis-form-actions[data-actions-placement=afterSections] .form-actions{margin-top:var(--pfx-actions-gap-top, var(--pfx-section-gap, 20px))}.section-title{margin:0 0 var(--pfx-section-title-mb, 12px) 0;font-family:var(--editorial-title-font-family, var(--editorial-body-font-family, inherit));font-size:var(--editorial-step-title-size, 1.12rem);font-weight:700;color:var(--pfx-form-label-strong)}.section-heading{display:flex;align-items:flex-start;gap:12px;margin-bottom:18px;padding-bottom:18px;border-bottom:1px solid var(--pfx-form-section-divider)}.section-heading.align-center{flex-direction:column;align-items:center;text-align:center}.section-heading.align-center .section-heading-text{display:grid;justify-items:center}.section-step-label{display:inline-flex;align-items:center;min-height:24px;padding:0 10px;margin:0 0 8px;border-radius:999px;background:color-mix(in srgb,var(--pfx-editorial-form-accent) 10%,transparent);color:color-mix(in srgb,var(--pfx-editorial-form-accent) 84%,var(--pfx-form-label-strong) 16%);font-size:.72rem;font-weight:800;font-family:var(--editorial-title-font-family, var(--editorial-body-font-family, inherit));letter-spacing:.08em;text-transform:uppercase}.section-heading-text{flex:1 1 auto;min-width:0}.section-heading-actions{display:inline-flex;align-items:center;align-self:flex-start;flex-wrap:wrap;gap:4px;margin-left:auto}.section-heading-actions.align-center{align-self:center;margin-left:0}.section-heading-action-btn{color:var(--pfx-form-label-muted)}.section-heading-action-btn .mat-mdc-progress-spinner,.section-heading-action-btn mat-progress-spinner{--mdc-circular-progress-active-indicator-color: currentColor}.section-heading-action-btn.loading{opacity:.72;pointer-events:none}.section-heading-action-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.section-collapse-btn{margin-left:4px;color:var(--pfx-form-label-muted)}.section-title.title-large{font:var(--mdc-typography-title-large, 500 22px/28px system-ui)}.section-title.title-medium{font:var(--mdc-typography-title-medium, 500 16px/24px system-ui)}.section-title.title-small{font:var(--mdc-typography-title-small, 500 14px/20px system-ui)}.section-title.headline-small{font:var(--mdc-typography-headline-small, 600 24px/32px system-ui)}.section-title.title-large,.section-title.title-medium,.section-title.title-small,.section-title.headline-small{font-family:var(--editorial-title-font-family, var(--editorial-body-font-family, inherit));font-weight:var(--editorial-title-weight, 600)}.section-description{margin:0;font-family:var(--editorial-body-font-family, inherit);font-size:.93rem;color:var(--pfx-form-label-muted);line-height:1.6}.section-description.body-large{font:var(--mdc-typography-body-large, 400 16px/24px system-ui)}.section-description.body-medium{font:var(--mdc-typography-body-medium, 400 14px/20px system-ui)}.section-description.body-small{font:var(--mdc-typography-body-small, 400 12px/16px system-ui)}.section-description.body-large,.section-description.body-medium,.section-description.body-small{font-family:var(--editorial-body-font-family, inherit);font-weight:var(--editorial-body-weight, 400)}.section-title.align-center,.section-description.align-center,.section-step-label.align-center{text-align:center}.form-section.section-appearance-plain{border-color:transparent;border-radius:0;padding:0;background:transparent}.form-section.section-appearance-plain .section-heading{margin-bottom:14px}.form-section.section-appearance-step{border-radius:var(--pfx-editorial-form-radius);padding:var(--pfx-form-section-padding);background:var(--pfx-form-section-surface-flat);border-color:var(--pfx-form-section-divider);box-shadow:none}.praxis-dynamic-form.editorial-visual-context .form-section.section-appearance-step{border-radius:var(--editorial-card-radius, 20px);box-shadow:none}.form-section.section-appearance-step .section-heading{margin-bottom:22px;padding-bottom:16px;border-bottom:1px solid var(--pfx-form-section-divider)}.form-section.section-appearance-step .section-title{font:var(--mdc-typography-title-large, 700 22px/28px system-ui);color:var(--pfx-form-label-strong);margin-bottom:8px}.form-section.section-appearance-step .section-description{color:var(--pfx-form-label-muted);max-width:60ch}.form-section.section-appearance-step .section-step-label{min-height:24px;padding:0 10px;margin-bottom:10px;box-shadow:none}.form-section.section-appearance-step .section-heading.align-center .section-description{max-width:52ch}.form-section.section-appearance-step .section-body{display:grid;gap:8px;padding-top:0}.form-section.section-appearance-step .form-row{margin-bottom:var(--pfx-field-gap, 1rem)}.form-section.section-appearance-step .form-column{gap:var(--pfx-field-gap, 12px)}.form-section .form-editorial-blocks{display:grid;gap:16px}.form-section .form-editorial-blocks[data-editorial-placement=beforeActions]{margin-top:24px;padding-top:18px;border-top:1px solid var(--pfx-form-section-divider)}.form-section.section-appearance-step .form-editorial-blocks[data-editorial-placement=beforeActions]{gap:18px}.form-section.section-appearance-step .form-editorial-blocks[data-editorial-placement=beforeActions]>*+*{margin-top:2px}.praxis-dynamic-form>praxis-form-actions[data-actions-placement=afterSections] .form-actions{margin-top:18px;padding-top:0}.praxis-dynamic-form>.form-editorial-blocks[data-editorial-placement=after]{margin-top:6px}:host-context(.mdc-theme-dark) .form-section.section-appearance-step,:host-context(.theme-dark) .form-section.section-appearance-step{border-color:color-mix(in srgb,var(--pfx-editorial-form-border) 82%,transparent);box-shadow:none}:host-context(.mdc-theme-dark) .form-section.section-appearance-step .section-title,:host-context(.theme-dark) .form-section.section-appearance-step .section-title{color:color-mix(in srgb,var(--pfx-editorial-form-text) 96%,white)}:host-context(.mdc-theme-dark) .form-section.section-appearance-step .section-description,:host-context(.theme-dark) .form-section.section-appearance-step .section-description{color:color-mix(in srgb,var(--pfx-editorial-form-text-muted) 86%,var(--pfx-editorial-form-text) 14%)}:host-context(.mdc-theme-dark) .section-step-label,:host-context(.theme-dark) .section-step-label{background:color-mix(in srgb,var(--md-sys-color-primary-container) 54%,transparent);color:color-mix(in srgb,var(--md-sys-color-primary) 88%,white)}:host-context(.mdc-theme-dark) .form-section .form-editorial-blocks[data-editorial-placement=beforeActions],:host-context(.theme-dark) .form-section .form-editorial-blocks[data-editorial-placement=beforeActions]{border-top-color:color-mix(in srgb,var(--md-sys-color-outline-variant) 90%,transparent)}.inline-edit-btn{margin-left:6px;vertical-align:middle;--mdc-icon-button-size: 28px;--mdc-icon-button-icon-size: 16px}.inline-edit-btn mat-icon{font-size:16px;width:16px;height:16px}.section-body.collapsed{border:1px dashed var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low);border-radius:6px;padding:8px 10px}.section-collapsed-placeholder{display:flex;align-items:center;gap:8px;color:var(--md-sys-color-on-surface-variant);font-size:.95rem}.section-collapsed-placeholder mat-icon{font-size:20px;width:20px;height:20px}.form-row{display:flex;gap:1.1rem;margin-bottom:var(--pfx-field-gap, 1.1rem);transition:all .2s ease;border-radius:6px;position:relative}.praxis-dynamic-form.pfx-mounting .form-row{opacity:0;transform:translateY(var(--pdx-form-mount-offset, 6px));animation:pdxFormMount var(--pdx-form-mount-duration, .16s) ease-out both;animation-delay:calc(var(--pfx-mount-index, 0) * var(--pdx-form-mount-stagger, 20ms))}@media(prefers-reduced-motion:reduce){.praxis-dynamic-form.pfx-mounting .form-row{animation:none;opacity:1;transform:none}}@keyframes pdxFormMount{to{opacity:1;transform:translateY(0)}}.praxis-dynamic-form .mat-mdc-form-field{width:100%;margin-bottom:var(--pfx-field-gap, 10px);font-family:inherit;--mdc-filled-text-field-container-color: var(--pfx-form-field-surface-rest);--mdc-filled-text-field-focus-active-indicator-color: var(--pfx-editorial-form-accent);--mdc-filled-text-field-active-indicator-color: var(--pfx-editorial-form-field-outline);--mdc-filled-text-field-hover-active-indicator-color: var(--pfx-editorial-form-accent);--mdc-filled-text-field-focus-label-text-color: var(--pfx-editorial-form-accent);--mdc-filled-text-field-label-text-color: var(--pfx-form-label-muted);--mdc-filled-text-field-input-text-color: var(--pfx-editorial-form-text);--mdc-filled-text-field-caret-color: var(--pfx-editorial-form-accent);--mdc-filled-text-field-input-text-placeholder-color: color-mix( in srgb, var(--pfx-editorial-form-text-muted) 82%, transparent );--mdc-outlined-text-field-outline-color: var(--pfx-editorial-form-field-outline);--mdc-outlined-text-field-hover-outline-color: var(--pfx-editorial-form-accent);--mdc-outlined-text-field-focus-outline-color: var(--pfx-editorial-form-accent);--mdc-outlined-text-field-focus-label-text-color: var(--pfx-editorial-form-accent);--mdc-outlined-text-field-label-text-color: var(--pfx-form-label-muted);--mdc-outlined-text-field-input-text-color: var(--pfx-editorial-form-text);--mdc-filled-text-field-container-shape: var(--pfx-editorial-form-field-radius);--mdc-outlined-text-field-container-shape: var(--pfx-editorial-form-field-radius);--mdc-filled-text-field-active-indicator-height: var(--pfx-editorial-form-field-border-width);--mdc-filled-text-field-focus-active-indicator-height: var(--pfx-editorial-form-field-border-width);--mdc-outlined-text-field-outline-width: var(--pfx-editorial-form-field-border-width);--mdc-outlined-text-field-focus-outline-width: var(--pfx-editorial-form-field-border-width);--mat-select-enabled-trigger-text-color: var(--pfx-editorial-form-text);--mat-select-enabled-arrow-color: var(--pfx-form-label-muted);--mat-form-field-enabled-select-arrow-color: var(--pfx-form-label-muted);--mat-form-field-focus-select-arrow-color: var(--pfx-editorial-form-accent);--mat-form-field-hover-state-layer-opacity: 0;--mat-form-field-focus-state-layer-opacity: 0}.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field{font-family:var(--editorial-body-font-family, inherit)}.praxis-dynamic-form .mat-mdc-text-field-wrapper,.praxis-dynamic-form .mdc-text-field{background:var(--pfx-form-field-surface-rest);border-radius:var(--pfx-editorial-form-field-radius);min-height:var(--pfx-form-field-min-height);transition:background-color .16s ease,box-shadow .16s ease,border-color .16s ease}.praxis-dynamic-form.editorial-visual-context .mat-mdc-text-field-wrapper,.praxis-dynamic-form.editorial-visual-context .mdc-text-field,.praxis-dynamic-form.editorial-visual-context .mdc-text-field--filled,.praxis-dynamic-form.editorial-visual-context .mdc-text-field--outlined{background-color:var(--pfx-form-field-surface-rest)!important;background:var(--pfx-form-field-surface-rest)!important}.praxis-dynamic-form .mat-mdc-form-field .mdc-floating-label,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-form-field-hint,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-form-field-error,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-form-field-required-marker,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-select-arrow,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-select-placeholder,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-form-field-icon-suffix,.praxis-dynamic-form .mat-mdc-form-field .mat-mdc-form-field-icon-prefix{color:var(--pfx-form-label-muted)}.praxis-dynamic-form .mat-mdc-input-element,.praxis-dynamic-form .mat-mdc-select-value-text,.praxis-dynamic-form .mat-mdc-form-field-infix,.praxis-dynamic-form .mat-mdc-select-min-line{color:var(--pfx-editorial-form-text)}.praxis-dynamic-form .mat-mdc-form-field:hover .mat-mdc-text-field-wrapper,.praxis-dynamic-form .mat-mdc-form-field:hover .mdc-text-field,.praxis-dynamic-form .mat-mdc-form-field.mat-focused .mat-mdc-text-field-wrapper,.praxis-dynamic-form .mat-mdc-form-field.mat-focused .mdc-text-field{background:var(--pfx-form-field-surface-focus)}.praxis-dynamic-form.editorial-visual-context .mat-mdc-input-element,.praxis-dynamic-form.editorial-visual-context .mat-mdc-select-value-text,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field-infix,.praxis-dynamic-form.editorial-visual-context .mat-mdc-select-min-line,.praxis-dynamic-form.editorial-visual-context .mdc-text-field__input{color:var(--pfx-editorial-form-text)!important;-webkit-text-fill-color:var(--pfx-editorial-form-text)!important}.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mdc-floating-label,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-form-field-hint,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-form-field-error,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-form-field-required-marker,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-select-arrow,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-select-placeholder,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-form-field-icon-suffix,.praxis-dynamic-form.editorial-visual-context .mat-mdc-form-field .mat-mdc-form-field-icon-prefix{color:var(--pfx-editorial-form-text-muted)!important}.praxis-dynamic-form.editorial-visual-context .mat-mdc-floating-label,.praxis-dynamic-form.editorial-visual-context .mdc-floating-label,.praxis-dynamic-form.editorial-visual-context .mdc-text-field__input::placeholder{color:color-mix(in srgb,var(--pfx-editorial-form-text) 68%,transparent)!important}.praxis-dynamic-form.editorial-visual-context .mat-mdc-input-element::placeholder,.praxis-dynamic-form.editorial-visual-context textarea.mat-mdc-input-element::placeholder{color:color-mix(in srgb,var(--pfx-editorial-form-text) 58%,transparent)!important}.praxis-dynamic-form .mat-mdc-radio-button,.praxis-dynamic-form .mat-mdc-checkbox,.praxis-dynamic-form .mat-mdc-slide-toggle{--mdc-radio-selected-icon-color: var(--pfx-editorial-form-accent);--mdc-radio-selected-hover-icon-color: var(--pfx-editorial-form-accent);--mdc-radio-selected-focus-icon-color: var(--pfx-editorial-form-accent);--mdc-checkbox-selected-checkmark-color: var(--pfx-editorial-form-accent-text);--mdc-checkbox-selected-focus-icon-color: var(--pfx-editorial-form-accent);--mdc-checkbox-selected-hover-icon-color: var(--pfx-editorial-form-accent);--mdc-checkbox-selected-icon-color: var(--pfx-editorial-form-accent);--mdc-switch-selected-focus-state-layer-color: var(--pfx-editorial-form-accent);--mdc-switch-selected-handle-color: var(--pfx-editorial-form-accent);--mdc-switch-selected-track-color: color-mix(in srgb, var(--pfx-editorial-form-accent) 45%, #fff)}.praxis-dynamic-form [data-field-type=input],.praxis-dynamic-form [data-field-type=textarea],.praxis-dynamic-form [data-field-type=email],.praxis-dynamic-form [data-field-type=password],.praxis-dynamic-form [data-field-type=url],.praxis-dynamic-form [data-field-type=search],.praxis-dynamic-form [data-field-type=phone],.praxis-dynamic-form [data-field-type=numericTextBox],.praxis-dynamic-form [data-field-type=currency],.praxis-dynamic-form [data-field-type=cpfCnpj],.praxis-dynamic-form [data-field-type=date],.praxis-dynamic-form [data-field-type=dateInput],.praxis-dynamic-form [data-field-type=dateRange],.praxis-dynamic-form [data-field-type=dateTimeLocal],.praxis-dynamic-form [data-field-type=time],.praxis-dynamic-form [data-field-type=timePicker],.praxis-dynamic-form [data-field-type=timeRange],.praxis-dynamic-form [data-field-type=month],.praxis-dynamic-form [data-field-type=week],.praxis-dynamic-form [data-field-type=yearInput],.praxis-dynamic-form [data-field-type=select],.praxis-dynamic-form [data-field-type=multi-select],.praxis-dynamic-form [data-field-type=searchable-select],.praxis-dynamic-form [data-field-type=async-select],.praxis-dynamic-form [data-field-type=autocomplete],.praxis-dynamic-form [data-field-type=tree-select],.praxis-dynamic-form [data-field-type=multi-select-tree],.praxis-dynamic-form [data-field-type=priceRange],.praxis-dynamic-form [data-field-type=file-upload]{display:block;width:100%;min-width:0}.praxis-dynamic-form .mat-mdc-form-field-subscript-wrapper{min-height:var(--pfx-subscript-min-h, 22px)}.form-row:last-child{margin-bottom:0}.form-column{display:grid;align-content:start;gap:var(--pfx-field-gap, 10px);flex:1;min-width:0;transition:all .2s ease;border-radius:4px;position:relative}.form-row.grid-12{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:var(--pfx-grid-gap, 16px)}.align-start{align-self:flex-start}.align-center{align-self:center}.align-end{align-self:flex-end}.align-stretch{align-self:stretch}.form-blocking-overlay{position:absolute;inset:0;background:transparent;color:var(--md-sys-color-on-surface);backdrop-filter:blur(2px) saturate(103%);-webkit-backdrop-filter:blur(2px) saturate(103%);display:flex;align-items:center;justify-content:center;flex-direction:column;gap:12px;z-index:10;pointer-events:all}@media(max-width:768px){.form-row{flex-direction:column;gap:.5rem}.form-section{padding:1rem}.section-heading{gap:10px;margin-bottom:16px;padding-bottom:14px}}.section-title{display:flex;align-items:center;gap:8px}.section-title .section-title-icon{font-size:1.25em;line-height:1}.section-title-avatar{--_pfx-form-section-avatar-size: var(--pfx-form-section-avatar-size-md, var(--pfx-form-section-avatar-size, 32px));display:inline-flex;align-items:center;justify-content:center;width:var(--_pfx-form-section-avatar-size);height:var(--_pfx-form-section-avatar-size);border-radius:999px;flex:0 0 var(--_pfx-form-section-avatar-size);overflow:hidden}.section-title-avatar.size-sm{--_pfx-form-section-avatar-size: var(--pfx-form-section-avatar-size-sm, 24px)}.section-title-avatar.size-md{--_pfx-form-section-avatar-size: var(--pfx-form-section-avatar-size-md, var(--pfx-form-section-avatar-size, 32px))}.section-title-avatar.size-lg{--_pfx-form-section-avatar-size: var(--pfx-form-section-avatar-size-lg, 40px)}.section-title-avatar-image{object-fit:cover;border:1px solid color-mix(in srgb,var(--pfx-form-section-divider) 72%,transparent);background:var(--pfx-form-section-surface-flat)}.section-title-avatar-text,.section-title-avatar-placeholder{background:color-mix(in srgb,var(--pfx-editorial-form-accent) 14%,var(--pfx-form-section-surface-flat));color:color-mix(in srgb,var(--pfx-editorial-form-accent) 82%,var(--pfx-form-label-strong) 18%);font-size:calc(var(--_pfx-form-section-avatar-size) * .41);font-weight:800;letter-spacing:.04em;text-transform:uppercase}.section-title-avatar-placeholder mat-icon{font-size:calc(var(--_pfx-form-section-avatar-size) * .5625);width:calc(var(--_pfx-form-section-avatar-size) * .5625);height:calc(var(--_pfx-form-section-avatar-size) * .5625);line-height:calc(var(--_pfx-form-section-avatar-size) * .5625)}.section-title-avatar-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"] }]
11725
12307
  }], ctorParameters: () => [{ type: i1$2.GenericCrudService }, { type: i2.HttpClient }, { type: i1$3.FormBuilder }, { type: i0.ChangeDetectorRef }, { type: FormLayoutService }, { type: FormContextService }, { type: FormRulesService }, { type: i7.SettingsPanelService }, { type: i2$1.MatDialog }, { type: undefined, decorators: [{
11726
12308
  type: Inject,
11727
12309
  args: [ASYNC_CONFIG_STORAGE]
@@ -11761,6 +12343,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
11761
12343
  type: Input
11762
12344
  }], config: [{
11763
12345
  type: Input
12346
+ }], actions: [{
12347
+ type: Input
11764
12348
  }], schemaSource: [{
11765
12349
  type: Input
11766
12350
  }], schemaUrl: [{
@@ -14401,6 +14985,7 @@ const ACTIONS_EDITOR_I18N_CONFIG = {
14401
14985
  'global.section.essential': 'Essencial',
14402
14986
  'global.section.dialogOptional': 'Diálogo (opcional)',
14403
14987
  'global.validation.validJson': 'Informe um JSON válido.',
14988
+ 'global.validation.requiredField': 'Campo obrigatório.',
14404
14989
  'tabs.defaultButtons': 'Botões padrão',
14405
14990
  'tabs.customButtons': 'Botões customizados',
14406
14991
  'tabs.layout': 'Layout',
@@ -14487,6 +15072,7 @@ const ACTIONS_EDITOR_I18N_CONFIG = {
14487
15072
  'global.section.essential': 'Essential',
14488
15073
  'global.section.dialogOptional': 'Dialog (optional)',
14489
15074
  'global.validation.validJson': 'Provide valid JSON.',
15075
+ 'global.validation.requiredField': 'Required field.',
14490
15076
  'tabs.defaultButtons': 'Default buttons',
14491
15077
  'tabs.customButtons': 'Custom buttons',
14492
15078
  'tabs.layout': 'Layout',
@@ -14574,204 +15160,32 @@ const ACTIONS_EDITOR_I18N_CONFIG = {
14574
15160
 
14575
15161
  function buildGlobalActionCatalog(params) {
14576
15162
  const merged = new Map();
14577
- for (const spec of params.legacySpecs) {
14578
- const mapped = params.mapLegacySpec
14579
- ? params.mapLegacySpec(spec)
14580
- : { ...spec };
14581
- merged.set(mapped.id, mapped);
14582
- }
14583
15163
  const source = params.injectedCatalog.length
14584
15164
  ? params.injectedCatalog
14585
15165
  : params.fallbackCatalog;
14586
15166
  for (const entry of source) {
14587
- if (merged.has(entry.id))
14588
- continue;
14589
15167
  const mapped = params.mapCatalogEntry
14590
15168
  ? params.mapCatalogEntry(entry)
14591
15169
  : {
14592
15170
  id: entry.id,
14593
15171
  label: entry.label,
14594
15172
  description: entry.description,
15173
+ payloadSchema: entry.payloadSchema,
14595
15174
  };
14596
15175
  merged.set(mapped.id, mapped);
14597
15176
  }
14598
15177
  return Array.from(merged.values());
14599
15178
  }
14600
- function parseActionValue(value, catalog, customActionValue = '__custom__') {
14601
- const raw = String(value || '').trim();
14602
- if (!raw) {
14603
- return { id: customActionValue, param: '', isGlobal: false, raw: '' };
14604
- }
14605
- const [prefix, ...rest] = raw.split(':');
14606
- const candidate = catalog.find((item) => item.id === prefix);
14607
- if (candidate) {
14608
- return {
14609
- id: candidate.id,
14610
- param: rest.join(':'),
14611
- isGlobal: true,
14612
- raw,
14613
- };
14614
- }
14615
- const exact = catalog.find((item) => item.id === raw);
14616
- if (exact) {
14617
- return { id: exact.id, param: '', isGlobal: true, raw };
14618
- }
14619
- return { id: customActionValue, param: '', isGlobal: false, raw };
14620
- }
14621
- function getActionParamInfo(value, catalog, customActionValue = '__custom__') {
14622
- const parsed = parseActionValue(value, catalog, customActionValue);
14623
- const param = parsed.param || '';
14624
- const trimmed = param.trim();
14625
- const looksJson = (trimmed.startsWith('{') && trimmed.endsWith('}')) ||
14626
- (trimmed.startsWith('[') && trimmed.endsWith(']'));
14627
- if (looksJson) {
14628
- try {
14629
- const json = JSON.parse(trimmed);
14630
- if (json && typeof json === 'object') {
14631
- return { ...parsed, param, isJson: true, json };
14632
- }
14633
- }
14634
- catch {
14635
- // ignore malformed legacy value
14636
- }
14637
- }
14638
- return { ...parsed, param, isJson: false, json: undefined };
14639
- }
14640
- function normalizeSurfaceOpenPayload(payload) {
14641
- return {
14642
- presentation: payload?.presentation === 'drawer' ? 'drawer' : 'modal',
14643
- title: payload?.title,
14644
- subtitle: payload?.subtitle,
14645
- icon: payload?.icon,
14646
- size: payload?.size && Object.keys(payload.size).length
14647
- ? { ...payload.size }
14648
- : undefined,
14649
- widget: {
14650
- id: String(payload?.widget?.id || ''),
14651
- inputs: { ...(payload?.widget?.inputs || {}) },
14652
- bindingOrder: payload?.widget?.bindingOrder?.length
14653
- ? [...payload.widget.bindingOrder]
14654
- : undefined,
14655
- },
14656
- bindings: payload?.bindings?.length
14657
- ? payload.bindings.map((binding) => ({
14658
- ...binding,
14659
- }))
14660
- : [],
14661
- context: payload?.context &&
14662
- typeof payload.context === 'object' &&
14663
- !Array.isArray(payload.context)
14664
- ? { ...payload.context }
14665
- : undefined,
14666
- };
14667
- }
14668
- function serializeGlobalActionParam(actionId, values, existingJson) {
14669
- const payload = buildGlobalActionPayload(values, existingJson);
14670
- const preferJson = !!(existingJson && Object.keys(existingJson).length);
14671
- const primary = buildPrimaryParam(actionId, values, payload, preferJson);
14672
- if (primary)
14673
- return primary;
14674
- if (!payload || Object.keys(payload).length === 0)
14675
- return '';
14676
- return JSON.stringify(payload);
14677
- }
14678
- function isRequiredParamMissing(value, catalog, customActionValue = '__custom__') {
14679
- const parsed = parseActionValue(value, catalog, customActionValue);
14680
- const spec = catalog.find((item) => item.id === parsed.id);
14681
- if (!parsed.isGlobal || !spec?.param?.required)
14682
- return false;
14683
- return !String(parsed.param || '').trim();
14684
- }
14685
- function isEquivalentActionFieldValue(actual, draft, fieldType) {
14686
- if (fieldType === 'toggle') {
14687
- return Boolean(actual) === Boolean(draft);
14688
- }
14689
- if (fieldType === 'number') {
14690
- return String(actual ?? '') === String(draft ?? '');
14691
- }
14692
- return String(actual ?? '') === String(draft ?? '');
14693
- }
14694
- function parseMethodAndUrl(raw) {
14695
- const text = String(raw || '').trim();
14696
- if (!text)
14697
- return {};
14698
- const idx = text.indexOf(':');
14699
- if (idx > 0) {
14700
- return {
14701
- method: text.slice(0, idx).toUpperCase(),
14702
- url: text.slice(idx + 1),
14703
- };
14704
- }
14705
- return { url: text };
14706
- }
14707
- function stringifyJson(value) {
14708
- if (value === undefined || value === null || value === '')
14709
- return '';
14710
- if (typeof value === 'string')
14711
- return value;
14712
- try {
14713
- return JSON.stringify(value, null, 2);
14714
- }
14715
- catch {
14716
- return '';
14717
- }
15179
+ function isGlobalActionId(value, catalog) {
15180
+ const id = String(value || '').trim();
15181
+ return !!id && catalog.some((item) => item.id === id);
14718
15182
  }
14719
- function buildPrimaryParam(actionId, values, payload, preferJson) {
14720
- if (preferJson)
14721
- return '';
14722
- const onlyPayload = payload ? Object.keys(payload).length : 0;
14723
- const text = (value) => String(value || '').trim();
14724
- if (actionId === 'navigate' || actionId === 'openUrl') {
14725
- const url = text(values.url);
14726
- if (url && onlyPayload === 1)
14727
- return url;
14728
- }
14729
- if (actionId === 'showAlert' ||
14730
- actionId === 'confirm' ||
14731
- actionId === 'alert' ||
14732
- actionId === 'prompt') {
14733
- const message = text(values.message);
14734
- if (message && onlyPayload === 1)
14735
- return message;
14736
- }
14737
- if (actionId === 'download') {
14738
- const url = text(values.url);
14739
- if (url && onlyPayload === 1)
14740
- return url;
14741
- }
14742
- if (actionId === 'copyToClipboard') {
14743
- const copyText = text(values.text);
14744
- if (copyText && onlyPayload === 1)
14745
- return copyText;
14746
- }
14747
- if (actionId === 'saveFormDraft' ||
14748
- actionId === 'loadFormDraft' ||
14749
- actionId === 'clearFormDraft') {
14750
- const key = text(values.key);
14751
- if (key && onlyPayload === 1)
14752
- return key;
14753
- }
14754
- if (actionId === 'refreshOptions' || actionId === 'loadDependentData') {
14755
- const field = text(values.field);
14756
- if (field && onlyPayload === 1)
14757
- return field;
14758
- }
14759
- if (actionId === 'apiCall') {
14760
- const method = text(values.method);
14761
- const url = text(values.url);
14762
- if (url && onlyPayload <= 2) {
14763
- return method ? `${method}:${url}` : url;
14764
- }
14765
- }
14766
- return '';
14767
- }
14768
- function buildGlobalActionPayload(values, existingJson) {
15183
+ function normalizeGlobalActionPayload(values, existingPayload) {
14769
15184
  const payload = {};
14770
15185
  const hasText = (value) => {
14771
15186
  if (value === undefined || value === null)
14772
15187
  return false;
14773
- const text = String(value).trim();
14774
- return text.length > 0;
15188
+ return String(value).trim().length > 0;
14775
15189
  };
14776
15190
  const toText = (value) => hasText(value) ? String(value).trim() : undefined;
14777
15191
  const toNumber = (value) => {
@@ -14800,8 +15214,8 @@ function buildGlobalActionPayload(values, existingJson) {
14800
15214
  payload[key] = true;
14801
15215
  }
14802
15216
  else if (value === false &&
14803
- existingJson &&
14804
- Object.prototype.hasOwnProperty.call(existingJson, key)) {
15217
+ existingPayload &&
15218
+ Object.prototype.hasOwnProperty.call(existingPayload, key)) {
14805
15219
  payload[key] = false;
14806
15220
  }
14807
15221
  };
@@ -14839,101 +15253,55 @@ function buildGlobalActionPayload(values, existingJson) {
14839
15253
  ...(animationDuration !== undefined ? { duration: animationDuration } : {}),
14840
15254
  };
14841
15255
  }
14842
- const width = toText(values.width);
14843
- const height = toText(values.height);
14844
- const minWidth = toText(values.minWidth);
14845
- const maxWidth = toText(values.maxWidth);
14846
- const minHeight = toText(values.minHeight);
14847
- const maxHeight = toText(values.maxHeight);
14848
- if (width)
14849
- payload.width = width;
14850
- if (height)
14851
- payload.height = height;
14852
- if (minWidth)
14853
- payload.minWidth = minWidth;
14854
- if (maxWidth)
14855
- payload.maxWidth = maxWidth;
14856
- if (minHeight)
14857
- payload.minHeight = minHeight;
14858
- if (maxHeight)
14859
- payload.maxHeight = maxHeight;
15256
+ for (const key of ['width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight']) {
15257
+ const value = toText(values[key]);
15258
+ if (value)
15259
+ payload[key] = value;
15260
+ }
14860
15261
  includeBool('disableClose', values.disableClose);
14861
15262
  includeBool('hasBackdrop', values.hasBackdrop);
14862
15263
  includeBool('closeOnBackdropClick', values.closeOnBackdropClick);
14863
15264
  includeBool('autoFocus', values.autoFocus);
14864
15265
  includeBool('restoreFocus', values.restoreFocus);
14865
- const backdropClass = toText(values.backdropClass);
14866
- const panelClass = toText(values.panelClass);
14867
- if (backdropClass)
14868
- payload.backdropClass = backdropClass;
14869
- if (panelClass)
14870
- payload.panelClass = panelClass;
14871
- const autoFocusedElement = toText(values.autoFocusedElement);
14872
- if (autoFocusedElement)
14873
- payload.autoFocusedElement = autoFocusedElement;
14874
- const ariaRole = toText(values.ariaRole);
14875
- const ariaLabel = toText(values.ariaLabel);
14876
- const ariaLabelledBy = toText(values.ariaLabelledBy);
14877
- const ariaDescribedBy = toText(values.ariaDescribedBy);
14878
- if (ariaRole)
14879
- payload.ariaRole = ariaRole;
14880
- if (ariaLabel)
14881
- payload.ariaLabel = ariaLabel;
14882
- if (ariaLabelledBy)
14883
- payload.ariaLabelledBy = ariaLabelledBy;
14884
- if (ariaDescribedBy)
14885
- payload.ariaDescribedBy = ariaDescribedBy;
15266
+ for (const key of [
15267
+ 'backdropClass',
15268
+ 'panelClass',
15269
+ 'autoFocusedElement',
15270
+ 'ariaRole',
15271
+ 'ariaLabel',
15272
+ 'ariaLabelledBy',
15273
+ 'ariaDescribedBy',
15274
+ 'confirmLabel',
15275
+ 'cancelLabel',
15276
+ 'okLabel',
15277
+ 'placeholder',
15278
+ 'defaultValue',
15279
+ 'url',
15280
+ 'target',
15281
+ 'level',
15282
+ 'text',
15283
+ ]) {
15284
+ const value = toText(values[key]);
15285
+ if (value)
15286
+ payload[key] = value;
15287
+ }
14886
15288
  const styles = toJson(values.styles);
14887
15289
  if (styles)
14888
15290
  payload.styles = styles;
14889
- const confirmLabel = toText(values.confirmLabel);
14890
- if (confirmLabel)
14891
- payload.confirmLabel = confirmLabel;
14892
- const cancelLabel = toText(values.cancelLabel);
14893
- if (cancelLabel)
14894
- payload.cancelLabel = cancelLabel;
14895
- const okLabel = toText(values.okLabel);
14896
- if (okLabel)
14897
- payload.okLabel = okLabel;
14898
- const placeholder = toText(values.placeholder);
14899
- if (placeholder)
14900
- payload.placeholder = placeholder;
14901
- const defaultValue = toText(values.defaultValue);
14902
- if (defaultValue)
14903
- payload.defaultValue = defaultValue;
14904
- if (values.mode === true) {
15291
+ if (values.mode === true)
14905
15292
  payload.mode = 'dialog';
14906
- }
14907
- const url = toText(values.url);
14908
- if (url)
14909
- payload.url = url;
14910
- const target = toText(values.target);
14911
- if (target)
14912
- payload.target = target;
14913
15293
  includeBool('newTab', values.newTab);
14914
15294
  includeBool('replaceUrl', values.replaceUrl);
14915
- const method = toText(values.method);
14916
- if (method)
14917
- payload.method = method;
14918
- const bodySource = toText(values.bodySource);
14919
- if (bodySource)
14920
- payload.bodySource = bodySource;
15295
+ includeBool('useFieldValue', values.useFieldValue);
15296
+ const params = toJson(values.params);
15297
+ if (params !== undefined)
15298
+ payload.params = params;
14921
15299
  const headers = toJson(values.headers);
14922
- if (headers)
15300
+ if (headers !== undefined)
14923
15301
  payload.headers = headers;
14924
15302
  const body = toJson(values.body);
14925
- if (body)
15303
+ if (body !== undefined)
14926
15304
  payload.body = body;
14927
- const key = toText(values.key);
14928
- if (key)
14929
- payload.key = key;
14930
- const field = toText(values.field);
14931
- if (field)
14932
- payload.field = field;
14933
- const copyText = toText(values.text);
14934
- if (copyText)
14935
- payload.text = copyText;
14936
- includeBool('useFieldValue', values.useFieldValue);
14937
15305
  const contentType = toText(values.contentType);
14938
15306
  if (contentType)
14939
15307
  payload.contentType = contentType;
@@ -14952,6 +15320,65 @@ function buildGlobalActionPayload(values, existingJson) {
14952
15320
  }
14953
15321
  return payload;
14954
15322
  }
15323
+ function buildGlobalActionRef(actionId, values, existingPayload) {
15324
+ const payload = normalizeGlobalActionPayload(values, existingPayload);
15325
+ return Object.keys(payload).length
15326
+ ? { actionId, payload }
15327
+ : { actionId };
15328
+ }
15329
+ function isRequiredParamMissing(ref, catalog) {
15330
+ const spec = catalog.find((item) => item.id === ref?.actionId);
15331
+ return isRequiredGlobalActionPayloadMissing(ref, spec);
15332
+ }
15333
+ function isEquivalentActionFieldValue(actual, draft, fieldType) {
15334
+ if (fieldType === 'toggle') {
15335
+ return Boolean(actual) === Boolean(draft);
15336
+ }
15337
+ if (fieldType === 'number') {
15338
+ return String(actual ?? '') === String(draft ?? '');
15339
+ }
15340
+ return String(actual ?? '') === String(draft ?? '');
15341
+ }
15342
+ function stringifyJson(value) {
15343
+ if (value === undefined || value === null || value === '')
15344
+ return '';
15345
+ if (typeof value === 'string')
15346
+ return value;
15347
+ try {
15348
+ return JSON.stringify(value, null, 2);
15349
+ }
15350
+ catch {
15351
+ return '';
15352
+ }
15353
+ }
15354
+ function normalizeSurfaceOpenPayload(payload) {
15355
+ return {
15356
+ presentation: payload?.presentation === 'drawer' ? 'drawer' : 'modal',
15357
+ title: payload?.title,
15358
+ subtitle: payload?.subtitle,
15359
+ icon: payload?.icon,
15360
+ size: payload?.size && Object.keys(payload.size).length
15361
+ ? { ...payload.size }
15362
+ : undefined,
15363
+ widget: {
15364
+ id: String(payload?.widget?.id || ''),
15365
+ inputs: { ...(payload?.widget?.inputs || {}) },
15366
+ bindingOrder: payload?.widget?.bindingOrder?.length
15367
+ ? [...payload.widget.bindingOrder]
15368
+ : undefined,
15369
+ },
15370
+ bindings: payload?.bindings?.length
15371
+ ? payload.bindings.map((binding) => ({
15372
+ ...binding,
15373
+ }))
15374
+ : [],
15375
+ context: payload?.context &&
15376
+ typeof payload.context === 'object' &&
15377
+ !Array.isArray(payload.context)
15378
+ ? { ...payload.context }
15379
+ : undefined,
15380
+ };
15381
+ }
14955
15382
 
14956
15383
  let ActionsEditorComponent$1 = class ActionsEditorComponent {
14957
15384
  config;
@@ -14961,9 +15388,7 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
14961
15388
  customActionStyleErrors = new Map();
14962
15389
  globalActionCatalogSource = inject(GLOBAL_ACTION_CATALOG, { optional: true }) ?? [];
14963
15390
  i18n = inject(PraxisI18nService);
14964
- legacyActionSpecs = GLOBAL_ACTION_SPEC_CATALOG;
14965
15391
  globalActionCatalog = buildGlobalActionCatalog({
14966
- legacySpecs: this.legacyActionSpecs,
14967
15392
  injectedCatalog: getGlobalActionCatalog(this.globalActionCatalogSource),
14968
15393
  fallbackCatalog: PRAXIS_GLOBAL_ACTION_CATALOG,
14969
15394
  mapCatalogEntry: (entry) => this.mapCatalogEntryToActionSpec(entry),
@@ -15052,17 +15477,17 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
15052
15477
  getActionSpecById(id) {
15053
15478
  return this.globalActionCatalog.find((item) => item.id === id);
15054
15479
  }
15055
- getCustomActionSelectValue(value) {
15056
- const parsed = parseActionValue(value, this.globalActionCatalog, this.customActionValue);
15057
- return parsed.isGlobal ? parsed.id : this.customActionValue;
15480
+ getCustomActionSelectValue(action) {
15481
+ return action?.globalAction?.actionId || this.customActionValue;
15058
15482
  }
15059
- getCustomActionParam(value) {
15060
- const parsed = parseActionValue(value, this.globalActionCatalog, this.customActionValue);
15061
- return parsed.isGlobal ? parsed.param : '';
15483
+ getCustomActionParam(action) {
15484
+ const payload = action?.globalAction?.payload;
15485
+ if (payload === undefined || payload === null)
15486
+ return '';
15487
+ return typeof payload === 'string' ? payload : stringifyJson(payload);
15062
15488
  }
15063
- getCustomActionCustomValue(value) {
15064
- const parsed = parseActionValue(value, this.globalActionCatalog, this.customActionValue);
15065
- return parsed.isGlobal ? '' : parsed.raw;
15489
+ getCustomActionCustomValue(action) {
15490
+ return action?.globalAction ? '' : action?.action || '';
15066
15491
  }
15067
15492
  onCustomActionSelectChange(index, value) {
15068
15493
  const current = this.actions.custom?.[index];
@@ -15070,31 +15495,36 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
15070
15495
  return;
15071
15496
  this.clearActionFieldState(current);
15072
15497
  if (value === this.customActionValue) {
15073
- const parsed = parseActionValue(current.action, this.globalActionCatalog, this.customActionValue);
15074
- if (parsed.isGlobal) {
15075
- this.updateCustomAction(index, 'action', '');
15076
- }
15498
+ this.updateCustomActionRef(index, undefined);
15077
15499
  return;
15078
15500
  }
15079
- const parsed = parseActionValue(current.action, this.globalActionCatalog, this.customActionValue);
15080
- const param = parsed.isGlobal && parsed.id === value ? parsed.param : '';
15081
- this.updateCustomAction(index, 'action', param ? `${value}:${param}` : value);
15501
+ const payload = current.globalAction?.actionId === value ? current.globalAction.payload : undefined;
15502
+ this.updateCustomActionRef(index, payload !== undefined ? { actionId: value, payload } : { actionId: value });
15082
15503
  }
15083
15504
  onCustomActionParamChange(index, value) {
15084
15505
  const current = this.actions.custom?.[index];
15085
15506
  if (!current)
15086
15507
  return;
15087
- const parsed = parseActionValue(current.action, this.globalActionCatalog, this.customActionValue);
15088
- const id = parsed.isGlobal ? parsed.id : '';
15089
- this.updateCustomAction(index, 'action', id ? (value ? `${id}:${value}` : id) : value);
15508
+ const id = current.globalAction?.actionId;
15509
+ if (!id)
15510
+ return;
15511
+ const trimmed = String(value || '').trim();
15512
+ if (!trimmed) {
15513
+ this.updateCustomActionRef(index, { actionId: id });
15514
+ return;
15515
+ }
15516
+ try {
15517
+ this.updateCustomActionRef(index, { actionId: id, payload: JSON.parse(trimmed) });
15518
+ }
15519
+ catch {
15520
+ this.updateCustomActionRef(index, { actionId: id, payload: trimmed });
15521
+ }
15090
15522
  }
15091
15523
  onCustomActionCustomChange(index, value) {
15092
15524
  this.updateCustomAction(index, 'action', value);
15093
15525
  }
15094
- isGlobalActionId(id) {
15095
- if (!id)
15096
- return false;
15097
- return this.globalActionCatalog.some((item) => item.id === id);
15526
+ isCanonicalGlobalActionId(id) {
15527
+ return isGlobalActionId(id, this.globalActionCatalog);
15098
15528
  }
15099
15529
  getActionCatalogLabel(spec) {
15100
15530
  if (spec?.id === 'surface.open') {
@@ -15130,6 +15560,7 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
15130
15560
  id: entry.id,
15131
15561
  label: this.getActionCatalogLabel(entry),
15132
15562
  description: this.getActionCatalogDescription(entry),
15563
+ payloadSchema: entry.payloadSchema,
15133
15564
  param: hasPayloadSchema
15134
15565
  ? {
15135
15566
  label: this.tx('globalAction.payload.label', 'Payload (optional JSON)'),
@@ -15143,20 +15574,16 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
15143
15574
  };
15144
15575
  }
15145
15576
  getGlobalActionSchema(action) {
15146
- const parsed = parseActionValue(action.action, this.globalActionCatalog, this.customActionValue);
15147
- if (!parsed.isGlobal)
15148
- return undefined;
15149
- return getGlobalActionUiSchema(parsed.id);
15577
+ return getGlobalActionUiSchema(action.globalAction?.actionId);
15150
15578
  }
15151
15579
  getSurfaceOpenActionPayload(action) {
15152
- const info = getActionParamInfo(action.action, this.globalActionCatalog, this.customActionValue);
15153
- const payload = info.isJson && info.json && typeof info.json === 'object' && !Array.isArray(info.json)
15154
- ? info.json
15155
- : undefined;
15156
- return normalizeSurfaceOpenPayload(payload);
15580
+ return normalizeSurfaceOpenPayload(action.globalAction?.payload);
15157
15581
  }
15158
15582
  onSurfaceOpenActionPayloadChange(action, payload, index) {
15159
- this.updateCustomAction(index, 'action', `surface.open:${JSON.stringify(normalizeSurfaceOpenPayload(payload))}`);
15583
+ this.updateCustomActionRef(index, {
15584
+ actionId: 'surface.open',
15585
+ payload: normalizeSurfaceOpenPayload(payload),
15586
+ });
15160
15587
  }
15161
15588
  shouldShowGlobalActionField(action, field) {
15162
15589
  if (!field.dependsOnKey)
@@ -15167,6 +15594,18 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
15167
15594
  const values = this.collectGlobalActionFieldValues(action, schema.fields);
15168
15595
  return String(values[field.dependsOnKey] ?? '') === String(field.dependsOnValue ?? '');
15169
15596
  }
15597
+ isGlobalActionFieldRequired(action, field) {
15598
+ const actionId = action.globalAction?.actionId;
15599
+ const spec = this.globalActionCatalog.find((item) => item.id === actionId);
15600
+ return getRequiredGlobalActionPayloadKeys(actionId, spec).includes(field.key);
15601
+ }
15602
+ isGlobalActionFieldMissing(action, field) {
15603
+ if (!this.isGlobalActionFieldRequired(action, field))
15604
+ return false;
15605
+ if (!this.shouldShowGlobalActionField(action, field))
15606
+ return false;
15607
+ return !hasMeaningfulGlobalActionPayloadValue(this.getGlobalActionFieldValue(action, field));
15608
+ }
15170
15609
  hasActionFieldError(action, key) {
15171
15610
  const errors = this.actionFieldErrors.get(this.getActionDraftKey(action));
15172
15611
  return !!errors?.[key];
@@ -15178,48 +15617,19 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
15178
15617
  getGlobalActionFieldValue(action, field) {
15179
15618
  const draftKey = this.getActionDraftKey(action);
15180
15619
  const draft = this.actionFieldDrafts.get(draftKey)?.[field.key];
15181
- const info = getActionParamInfo(action.action, this.globalActionCatalog, this.customActionValue);
15182
- const raw = info.param || '';
15183
- const json = info.json || {};
15184
- const hasJson = info.isJson;
15620
+ const json = action.globalAction?.payload || {};
15185
15621
  let value;
15186
15622
  switch (field.key) {
15187
15623
  case 'message':
15188
- value = json.message ?? json.text ?? (hasJson ? '' : raw);
15624
+ value = json.message ?? json.text ?? '';
15189
15625
  break;
15190
15626
  case 'url':
15191
- if (json.url) {
15192
- value = json.url;
15193
- break;
15194
- }
15195
- if (!hasJson && info.id === 'apiCall') {
15196
- const parsed = parseMethodAndUrl(raw);
15197
- value = parsed.url || '';
15198
- break;
15199
- }
15200
- value = hasJson ? '' : raw;
15201
- break;
15202
- case 'method': {
15203
- if (json.method) {
15204
- value = String(json.method);
15205
- break;
15206
- }
15207
- if (!hasJson) {
15208
- const parsed = parseMethodAndUrl(raw);
15209
- value = parsed.method || '';
15210
- break;
15211
- }
15212
- value = '';
15213
- break;
15214
- }
15215
- case 'bodySource':
15216
- value = json.bodySource ?? '';
15627
+ value = json.url ?? '';
15217
15628
  break;
15629
+ case 'params':
15218
15630
  case 'headers':
15219
- value = stringifyJson(json.headers);
15220
- break;
15221
15631
  case 'body':
15222
- value = stringifyJson(json.body);
15632
+ value = stringifyJson(json[field.key]);
15223
15633
  break;
15224
15634
  case 'contentData':
15225
15635
  value = stringifyJson(json.content?.data);
@@ -15273,11 +15683,7 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
15273
15683
  value = json.target ?? '';
15274
15684
  break;
15275
15685
  case 'text':
15276
- value = json.text ?? (hasJson ? '' : raw);
15277
- break;
15278
- case 'key':
15279
- case 'field':
15280
- value = json[field.key] ?? (hasJson ? '' : raw);
15686
+ value = json.text ?? '';
15281
15687
  break;
15282
15688
  default:
15283
15689
  value = json[field.key] ?? '';
@@ -15294,8 +15700,8 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
15294
15700
  return value;
15295
15701
  }
15296
15702
  onGlobalActionFieldChange(action, field, value, index) {
15297
- const selectedId = this.getCustomActionSelectValue(action.action);
15298
- if (!this.isGlobalActionId(selectedId))
15703
+ const selectedId = this.getCustomActionSelectValue(action);
15704
+ if (!this.isCanonicalGlobalActionId(selectedId))
15299
15705
  return;
15300
15706
  const schema = getGlobalActionUiSchema(selectedId);
15301
15707
  if (!schema)
@@ -15319,9 +15725,7 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
15319
15725
  }
15320
15726
  const values = this.collectGlobalActionFieldValues(action, schema.fields);
15321
15727
  values[field.key] = value;
15322
- const info = getActionParamInfo(action.action, this.globalActionCatalog, this.customActionValue);
15323
- const param = serializeGlobalActionParam(selectedId, values, info.json);
15324
- this.updateCustomAction(index, 'action', param ? `${selectedId}:${param}` : selectedId);
15728
+ this.updateCustomActionRef(index, buildGlobalActionRef(selectedId, values, action.globalAction?.payload));
15325
15729
  }
15326
15730
  setActionFieldError(action, key, message) {
15327
15731
  const draftKey = this.getActionDraftKey(action);
@@ -15343,7 +15747,7 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
15343
15747
  }
15344
15748
  }
15345
15749
  getActionDraftKey(action) {
15346
- return String(action.id || action.action || action.label || 'action');
15750
+ return String(action.id || action.action || action.label || action.globalAction?.actionId || 'action');
15347
15751
  }
15348
15752
  setActionFieldDraft(action, key, value) {
15349
15753
  const draftKey = this.getActionDraftKey(action);
@@ -15407,6 +15811,25 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
15407
15811
  this.syncCustomActionStyleTexts();
15408
15812
  }
15409
15813
  }
15814
+ updateCustomActionRef(index, globalAction) {
15815
+ const customActions = [...(this.actions.custom || [])];
15816
+ const current = { ...customActions[index] };
15817
+ if (globalAction) {
15818
+ current.globalAction = globalAction;
15819
+ delete current.action;
15820
+ }
15821
+ else {
15822
+ delete current.globalAction;
15823
+ }
15824
+ customActions[index] = current;
15825
+ this.configChange.emit({
15826
+ ...this.config,
15827
+ actions: {
15828
+ ...this.actions,
15829
+ custom: customActions,
15830
+ },
15831
+ });
15832
+ }
15410
15833
  addCustomButton() {
15411
15834
  const newButton = {
15412
15835
  id: `custom_${Date.now()}`,
@@ -15575,10 +15998,10 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
15575
15998
  <div class="action-global-grid">
15576
15999
  @for (field of schema.fields; track field.key) {
15577
16000
  @if (shouldShowGlobalActionField(action, field)) {
15578
- @if (schema.id === 'showAlert' && field.key === 'message') {
16001
+ @if (schema.id === 'dialog.alert' && field.key === 'message') {
15579
16002
  <div class="action-section-title">{{ tx('global.section.essential', 'Essential') }}</div>
15580
16003
  }
15581
- @if (schema.id === 'showAlert' && field.key === 'title') {
16004
+ @if (schema.id === 'dialog.alert' && field.key === 'title') {
15582
16005
  <div class="action-section-title">{{ tx('global.section.dialogOptional', 'Dialog (optional)') }}</div>
15583
16006
  }
15584
16007
  @if (field.type === 'toggle') {
@@ -15609,6 +16032,7 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
15609
16032
  (ngModelChange)="onGlobalActionFieldChange(action, field, $event, index)"
15610
16033
  [ngModelOptions]="{ standalone: true }"
15611
16034
  [placeholder]="field.placeholder || ''"
16035
+ [required]="isGlobalActionFieldRequired(action, field)"
15612
16036
  ></textarea>
15613
16037
  @if (field.hint) {
15614
16038
  <button
@@ -15621,6 +16045,9 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
15621
16045
  <mat-icon>help_outline</mat-icon>
15622
16046
  </button>
15623
16047
  }
16048
+ @if (isGlobalActionFieldMissing(action, field)) {
16049
+ <mat-error aria-live="polite">{{ tx('global.validation.requiredField', 'Campo obrigatório.') }}</mat-error>
16050
+ }
15624
16051
  </mat-form-field>
15625
16052
  } @else if (field.type === 'json') {
15626
16053
  <mat-form-field>
@@ -15632,6 +16059,7 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
15632
16059
  (ngModelChange)="onGlobalActionFieldChange(action, field, $event, index)"
15633
16060
  [ngModelOptions]="{ standalone: true }"
15634
16061
  [placeholder]="field.placeholder || '{ }'"
16062
+ [required]="isGlobalActionFieldRequired(action, field)"
15635
16063
  ></textarea>
15636
16064
  <button
15637
16065
  mat-icon-button
@@ -15644,6 +16072,8 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
15644
16072
  </button>
15645
16073
  @if (hasActionFieldError(action, field.key)) {
15646
16074
  <mat-error>{{ getActionFieldError(action, field.key) }}</mat-error>
16075
+ } @else if (isGlobalActionFieldMissing(action, field)) {
16076
+ <mat-error aria-live="polite">{{ tx('global.validation.requiredField', 'Campo obrigatório.') }}</mat-error>
15647
16077
  }
15648
16078
  </mat-form-field>
15649
16079
  } @else if (field.type === 'select') {
@@ -15653,6 +16083,7 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
15653
16083
  [ngModel]="getGlobalActionFieldValue(action, field)"
15654
16084
  (ngModelChange)="onGlobalActionFieldChange(action, field, $event, index)"
15655
16085
  [ngModelOptions]="{ standalone: true }"
16086
+ [required]="isGlobalActionFieldRequired(action, field)"
15656
16087
  >
15657
16088
  @for (opt of field.options || []; track opt.value) {
15658
16089
  <mat-option [value]="opt.value">{{ opt.label }}</mat-option>
@@ -15669,6 +16100,9 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
15669
16100
  <mat-icon>help_outline</mat-icon>
15670
16101
  </button>
15671
16102
  }
16103
+ @if (isGlobalActionFieldMissing(action, field)) {
16104
+ <mat-error aria-live="polite">{{ tx('global.validation.requiredField', 'Campo obrigatório.') }}</mat-error>
16105
+ }
15672
16106
  </mat-form-field>
15673
16107
  } @else {
15674
16108
  <mat-form-field>
@@ -15680,6 +16114,7 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
15680
16114
  (ngModelChange)="onGlobalActionFieldChange(action, field, $event, index)"
15681
16115
  [ngModelOptions]="{ standalone: true }"
15682
16116
  [placeholder]="field.placeholder || ''"
16117
+ [required]="isGlobalActionFieldRequired(action, field)"
15683
16118
  />
15684
16119
  @if (field.hint) {
15685
16120
  <button
@@ -15692,6 +16127,9 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
15692
16127
  <mat-icon>help_outline</mat-icon>
15693
16128
  </button>
15694
16129
  }
16130
+ @if (isGlobalActionFieldMissing(action, field)) {
16131
+ <mat-error aria-live="polite">{{ tx('global.validation.requiredField', 'Campo obrigatório.') }}</mat-error>
16132
+ }
15695
16133
  </mat-form-field>
15696
16134
  }
15697
16135
  }
@@ -15966,7 +16404,7 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
15966
16404
  <mat-accordion multi>
15967
16405
  @for (
15968
16406
  customAction of actions.custom;
15969
- track customAction.id || customAction.action || $index;
16407
+ track customAction.id || customAction.globalAction?.actionId || customAction.action || $index;
15970
16408
  let i = $index
15971
16409
  ) {
15972
16410
  <div class="custom-action-panel-shell">
@@ -16023,7 +16461,7 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
16023
16461
  <mat-form-field>
16024
16462
  <mat-label>{{ tx('custom.action', 'Action') }}</mat-label>
16025
16463
  <mat-select
16026
- [value]="getCustomActionSelectValue(customAction.action)"
16464
+ [value]="getCustomActionSelectValue(customAction)"
16027
16465
  (selectionChange)="onCustomActionSelectChange(i, $event.value)"
16028
16466
  >
16029
16467
  <mat-option [value]="customActionValue">{{ tx('custom.action.custom', 'Custom') }}</mat-option>
@@ -16043,34 +16481,34 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
16043
16481
  <mat-icon>help_outline</mat-icon>
16044
16482
  </button>
16045
16483
  </mat-form-field>
16046
- @if (getCustomActionSelectValue(customAction.action) === customActionValue) {
16484
+ @if (getCustomActionSelectValue(customAction) === customActionValue) {
16047
16485
  <mat-form-field>
16048
16486
  <mat-label>{{ tx('custom.action', 'Action') }}</mat-label>
16049
16487
  <input
16050
16488
  matInput
16051
- [value]="getCustomActionCustomValue(customAction.action)"
16489
+ [value]="getCustomActionCustomValue(customAction)"
16052
16490
  (input)="onCustomActionCustomChange(i, $any($event.target).value)"
16053
16491
  [placeholder]="tx('custom.action.customPlaceholder', 'Example: custom_action, navigate, delete')"
16054
16492
  />
16055
16493
  </mat-form-field>
16056
16494
  } @else {
16057
- @if (isGlobalActionId(getCustomActionSelectValue(customAction.action))) {
16495
+ @if (isCanonicalGlobalActionId(getCustomActionSelectValue(customAction))) {
16058
16496
  <ng-container
16059
16497
  [ngTemplateOutlet]="globalActionFields"
16060
16498
  [ngTemplateOutletContext]="{ $implicit: customAction, index: i }"
16061
16499
  ></ng-container>
16062
16500
  } @else {
16063
- @if (getActionSpecById(getCustomActionSelectValue(customAction.action)); as actionSpec) {
16501
+ @if (getActionSpecById(getCustomActionSelectValue(customAction)); as actionSpec) {
16064
16502
  @if (actionSpec.param) {
16065
16503
  <mat-form-field>
16066
16504
  <mat-label>{{ actionSpec.param.label || tx('custom.parameter', 'Parameter') }}</mat-label>
16067
16505
  <input
16068
16506
  matInput
16069
- [value]="getCustomActionParam(customAction.action)"
16507
+ [value]="getCustomActionParam(customAction)"
16070
16508
  (input)="onCustomActionParamChange(i, $any($event.target).value)"
16071
16509
  [placeholder]="actionSpec.param.placeholder || ''"
16072
16510
  />
16073
- @if (actionSpec.param.required && !getCustomActionParam(customAction.action)) {
16511
+ @if (actionSpec.param.required && !getCustomActionParam(customAction)) {
16074
16512
  <mat-error>{{ tx('custom.parameterRequired', 'Required parameter.') }}</mat-error>
16075
16513
  }
16076
16514
  </mat-form-field>
@@ -16348,7 +16786,7 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
16348
16786
  </div>
16349
16787
  </mat-tab>
16350
16788
  </mat-tab-group>
16351
- `, isInline: true, styles: [".editor-container{padding:16px;display:flex;flex-direction:column;gap:16px}.action-fields{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:16px;align-items:center}.action-global-fields{grid-column:1 / -1;padding:12px 14px;border:1px dashed var(--md-sys-color-outline-variant, rgba(255, 255, 255, .12));border-radius:10px;background:var(--md-sys-color-surface-container-low)}.action-global-title{font-size:12px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--md-sys-color-on-surface-variant, #9aa0a6);margin-bottom:10px}.action-global-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px 16px}.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}.action-global-fields .mat-mdc-form-field-icon-suffix{align-self:center}.action-section-title{width:100%;margin:10px 0 2px;font-size:12px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--md-sys-color-on-surface-variant, #9aa0a6)}.add-button{margin-bottom:16px;align-self:flex-start}.custom-action-panel-shell{display:grid;gap:8px}.custom-action-panel-toolbar{display:flex;justify-content:flex-end}.custom-action-order-controls{display:inline-flex;align-items:center;gap:4px;justify-content:flex-end}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { 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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4$1.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i4$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7$1.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: MatSlideToggleModule }, { kind: "component", type: i6$2.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: MatSelectModule }, { kind: "component", type: i6$3.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$3.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i6$3.MatOptgroup, selector: "mat-optgroup", inputs: ["label", "disabled"], exportAs: ["matOptgroup"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i7$3.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i7$3.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: MatExpansionModule }, { kind: "directive", type: i3.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i3.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i3.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i3.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i3.MatExpansionPanelContent, selector: "ng-template[matExpansionPanelContent]" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { 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: MatTooltipModule }, { kind: "directive", type: i9.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: SurfaceOpenActionEditorComponent, selector: "praxis-surface-open-action-editor", inputs: ["value", "hostKind"], outputs: ["valueChange"] }] });
16789
+ `, isInline: true, styles: [".editor-container{padding:16px;display:flex;flex-direction:column;gap:16px}.action-fields{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:16px;align-items:center}.action-global-fields{grid-column:1 / -1;padding:12px 14px;border:1px dashed var(--md-sys-color-outline-variant, rgba(255, 255, 255, .12));border-radius:10px;background:var(--md-sys-color-surface-container-low)}.action-global-title{font-size:12px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--md-sys-color-on-surface-variant, #9aa0a6);margin-bottom:10px}.action-global-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px 16px}.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}.action-field-error{color:var(--md-sys-color-error, #ba1a1a);font-weight:500}.action-global-fields .mat-mdc-form-field-icon-suffix{align-self:center}.action-section-title{width:100%;margin:10px 0 2px;font-size:12px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--md-sys-color-on-surface-variant, #9aa0a6)}.add-button{margin-bottom:16px;align-self:flex-start}.custom-action-panel-shell{display:grid;gap:8px}.custom-action-panel-toolbar{display:flex;justify-content:flex-end}.custom-action-order-controls{display:inline-flex;align-items:center;gap:4px;justify-content:flex-end}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { 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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i4$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4$1.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i4$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7$1.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: MatSlideToggleModule }, { kind: "component", type: i6$2.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: MatSelectModule }, { kind: "component", type: i6$3.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$3.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i6$3.MatOptgroup, selector: "mat-optgroup", inputs: ["label", "disabled"], exportAs: ["matOptgroup"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i7$3.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i7$3.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: MatExpansionModule }, { kind: "directive", type: i3.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i3.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i3.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i3.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i3.MatExpansionPanelContent, selector: "ng-template[matExpansionPanelContent]" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { 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: MatTooltipModule }, { kind: "directive", type: i9.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: SurfaceOpenActionEditorComponent, selector: "praxis-surface-open-action-editor", inputs: ["value", "hostKind"], outputs: ["valueChange"] }] });
16352
16790
  };
16353
16791
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ActionsEditorComponent$1, decorators: [{
16354
16792
  type: Component,
@@ -16383,10 +16821,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
16383
16821
  <div class="action-global-grid">
16384
16822
  @for (field of schema.fields; track field.key) {
16385
16823
  @if (shouldShowGlobalActionField(action, field)) {
16386
- @if (schema.id === 'showAlert' && field.key === 'message') {
16824
+ @if (schema.id === 'dialog.alert' && field.key === 'message') {
16387
16825
  <div class="action-section-title">{{ tx('global.section.essential', 'Essential') }}</div>
16388
16826
  }
16389
- @if (schema.id === 'showAlert' && field.key === 'title') {
16827
+ @if (schema.id === 'dialog.alert' && field.key === 'title') {
16390
16828
  <div class="action-section-title">{{ tx('global.section.dialogOptional', 'Dialog (optional)') }}</div>
16391
16829
  }
16392
16830
  @if (field.type === 'toggle') {
@@ -16417,6 +16855,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
16417
16855
  (ngModelChange)="onGlobalActionFieldChange(action, field, $event, index)"
16418
16856
  [ngModelOptions]="{ standalone: true }"
16419
16857
  [placeholder]="field.placeholder || ''"
16858
+ [required]="isGlobalActionFieldRequired(action, field)"
16420
16859
  ></textarea>
16421
16860
  @if (field.hint) {
16422
16861
  <button
@@ -16429,6 +16868,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
16429
16868
  <mat-icon>help_outline</mat-icon>
16430
16869
  </button>
16431
16870
  }
16871
+ @if (isGlobalActionFieldMissing(action, field)) {
16872
+ <mat-error aria-live="polite">{{ tx('global.validation.requiredField', 'Campo obrigatório.') }}</mat-error>
16873
+ }
16432
16874
  </mat-form-field>
16433
16875
  } @else if (field.type === 'json') {
16434
16876
  <mat-form-field>
@@ -16440,6 +16882,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
16440
16882
  (ngModelChange)="onGlobalActionFieldChange(action, field, $event, index)"
16441
16883
  [ngModelOptions]="{ standalone: true }"
16442
16884
  [placeholder]="field.placeholder || '{ }'"
16885
+ [required]="isGlobalActionFieldRequired(action, field)"
16443
16886
  ></textarea>
16444
16887
  <button
16445
16888
  mat-icon-button
@@ -16452,6 +16895,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
16452
16895
  </button>
16453
16896
  @if (hasActionFieldError(action, field.key)) {
16454
16897
  <mat-error>{{ getActionFieldError(action, field.key) }}</mat-error>
16898
+ } @else if (isGlobalActionFieldMissing(action, field)) {
16899
+ <mat-error aria-live="polite">{{ tx('global.validation.requiredField', 'Campo obrigatório.') }}</mat-error>
16455
16900
  }
16456
16901
  </mat-form-field>
16457
16902
  } @else if (field.type === 'select') {
@@ -16461,6 +16906,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
16461
16906
  [ngModel]="getGlobalActionFieldValue(action, field)"
16462
16907
  (ngModelChange)="onGlobalActionFieldChange(action, field, $event, index)"
16463
16908
  [ngModelOptions]="{ standalone: true }"
16909
+ [required]="isGlobalActionFieldRequired(action, field)"
16464
16910
  >
16465
16911
  @for (opt of field.options || []; track opt.value) {
16466
16912
  <mat-option [value]="opt.value">{{ opt.label }}</mat-option>
@@ -16477,6 +16923,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
16477
16923
  <mat-icon>help_outline</mat-icon>
16478
16924
  </button>
16479
16925
  }
16926
+ @if (isGlobalActionFieldMissing(action, field)) {
16927
+ <mat-error aria-live="polite">{{ tx('global.validation.requiredField', 'Campo obrigatório.') }}</mat-error>
16928
+ }
16480
16929
  </mat-form-field>
16481
16930
  } @else {
16482
16931
  <mat-form-field>
@@ -16488,6 +16937,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
16488
16937
  (ngModelChange)="onGlobalActionFieldChange(action, field, $event, index)"
16489
16938
  [ngModelOptions]="{ standalone: true }"
16490
16939
  [placeholder]="field.placeholder || ''"
16940
+ [required]="isGlobalActionFieldRequired(action, field)"
16491
16941
  />
16492
16942
  @if (field.hint) {
16493
16943
  <button
@@ -16500,6 +16950,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
16500
16950
  <mat-icon>help_outline</mat-icon>
16501
16951
  </button>
16502
16952
  }
16953
+ @if (isGlobalActionFieldMissing(action, field)) {
16954
+ <mat-error aria-live="polite">{{ tx('global.validation.requiredField', 'Campo obrigatório.') }}</mat-error>
16955
+ }
16503
16956
  </mat-form-field>
16504
16957
  }
16505
16958
  }
@@ -16774,7 +17227,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
16774
17227
  <mat-accordion multi>
16775
17228
  @for (
16776
17229
  customAction of actions.custom;
16777
- track customAction.id || customAction.action || $index;
17230
+ track customAction.id || customAction.globalAction?.actionId || customAction.action || $index;
16778
17231
  let i = $index
16779
17232
  ) {
16780
17233
  <div class="custom-action-panel-shell">
@@ -16831,7 +17284,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
16831
17284
  <mat-form-field>
16832
17285
  <mat-label>{{ tx('custom.action', 'Action') }}</mat-label>
16833
17286
  <mat-select
16834
- [value]="getCustomActionSelectValue(customAction.action)"
17287
+ [value]="getCustomActionSelectValue(customAction)"
16835
17288
  (selectionChange)="onCustomActionSelectChange(i, $event.value)"
16836
17289
  >
16837
17290
  <mat-option [value]="customActionValue">{{ tx('custom.action.custom', 'Custom') }}</mat-option>
@@ -16851,34 +17304,34 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
16851
17304
  <mat-icon>help_outline</mat-icon>
16852
17305
  </button>
16853
17306
  </mat-form-field>
16854
- @if (getCustomActionSelectValue(customAction.action) === customActionValue) {
17307
+ @if (getCustomActionSelectValue(customAction) === customActionValue) {
16855
17308
  <mat-form-field>
16856
17309
  <mat-label>{{ tx('custom.action', 'Action') }}</mat-label>
16857
17310
  <input
16858
17311
  matInput
16859
- [value]="getCustomActionCustomValue(customAction.action)"
17312
+ [value]="getCustomActionCustomValue(customAction)"
16860
17313
  (input)="onCustomActionCustomChange(i, $any($event.target).value)"
16861
17314
  [placeholder]="tx('custom.action.customPlaceholder', 'Example: custom_action, navigate, delete')"
16862
17315
  />
16863
17316
  </mat-form-field>
16864
17317
  } @else {
16865
- @if (isGlobalActionId(getCustomActionSelectValue(customAction.action))) {
17318
+ @if (isCanonicalGlobalActionId(getCustomActionSelectValue(customAction))) {
16866
17319
  <ng-container
16867
17320
  [ngTemplateOutlet]="globalActionFields"
16868
17321
  [ngTemplateOutletContext]="{ $implicit: customAction, index: i }"
16869
17322
  ></ng-container>
16870
17323
  } @else {
16871
- @if (getActionSpecById(getCustomActionSelectValue(customAction.action)); as actionSpec) {
17324
+ @if (getActionSpecById(getCustomActionSelectValue(customAction)); as actionSpec) {
16872
17325
  @if (actionSpec.param) {
16873
17326
  <mat-form-field>
16874
17327
  <mat-label>{{ actionSpec.param.label || tx('custom.parameter', 'Parameter') }}</mat-label>
16875
17328
  <input
16876
17329
  matInput
16877
- [value]="getCustomActionParam(customAction.action)"
17330
+ [value]="getCustomActionParam(customAction)"
16878
17331
  (input)="onCustomActionParamChange(i, $any($event.target).value)"
16879
17332
  [placeholder]="actionSpec.param.placeholder || ''"
16880
17333
  />
16881
- @if (actionSpec.param.required && !getCustomActionParam(customAction.action)) {
17334
+ @if (actionSpec.param.required && !getCustomActionParam(customAction)) {
16882
17335
  <mat-error>{{ tx('custom.parameterRequired', 'Required parameter.') }}</mat-error>
16883
17336
  }
16884
17337
  </mat-form-field>
@@ -17156,7 +17609,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
17156
17609
  </div>
17157
17610
  </mat-tab>
17158
17611
  </mat-tab-group>
17159
- `, styles: [".editor-container{padding:16px;display:flex;flex-direction:column;gap:16px}.action-fields{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:16px;align-items:center}.action-global-fields{grid-column:1 / -1;padding:12px 14px;border:1px dashed var(--md-sys-color-outline-variant, rgba(255, 255, 255, .12));border-radius:10px;background:var(--md-sys-color-surface-container-low)}.action-global-title{font-size:12px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--md-sys-color-on-surface-variant, #9aa0a6);margin-bottom:10px}.action-global-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px 16px}.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}.action-global-fields .mat-mdc-form-field-icon-suffix{align-self:center}.action-section-title{width:100%;margin:10px 0 2px;font-size:12px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--md-sys-color-on-surface-variant, #9aa0a6)}.add-button{margin-bottom:16px;align-self:flex-start}.custom-action-panel-shell{display:grid;gap:8px}.custom-action-panel-toolbar{display:flex;justify-content:flex-end}.custom-action-order-controls{display:inline-flex;align-items:center;gap:4px;justify-content:flex-end}\n"] }]
17612
+ `, styles: [".editor-container{padding:16px;display:flex;flex-direction:column;gap:16px}.action-fields{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:16px;align-items:center}.action-global-fields{grid-column:1 / -1;padding:12px 14px;border:1px dashed var(--md-sys-color-outline-variant, rgba(255, 255, 255, .12));border-radius:10px;background:var(--md-sys-color-surface-container-low)}.action-global-title{font-size:12px;font-weight:600;letter-spacing:.02em;text-transform:uppercase;color:var(--md-sys-color-on-surface-variant, #9aa0a6);margin-bottom:10px}.action-global-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px 16px}.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}.action-field-error{color:var(--md-sys-color-error, #ba1a1a);font-weight:500}.action-global-fields .mat-mdc-form-field-icon-suffix{align-self:center}.action-section-title{width:100%;margin:10px 0 2px;font-size:12px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--md-sys-color-on-surface-variant, #9aa0a6)}.add-button{margin-bottom:16px;align-self:flex-start}.custom-action-panel-shell{display:grid;gap:8px}.custom-action-panel-toolbar{display:flex;justify-content:flex-end}.custom-action-order-controls{display:inline-flex;align-items:center;gap:4px;justify-content:flex-end}\n"] }]
17160
17613
  }], propDecorators: { config: [{
17161
17614
  type: Input
17162
17615
  }], configChange: [{
@@ -18518,6 +18971,9 @@ class PraxisDynamicFormConfigEditor {
18518
18971
  serverMeta;
18519
18972
  destroy$ = new Subject();
18520
18973
  hasProcessedInitialRuleState = false;
18974
+ jsonEditorIsValid = true;
18975
+ globalActionValidationIssues = [];
18976
+ globalActionCatalog;
18521
18977
  // Observables obrigatórios da interface SettingsValueProvider
18522
18978
  isDirty$ = new BehaviorSubject(false);
18523
18979
  isValid$ = new BehaviorSubject(true);
@@ -18534,11 +18990,12 @@ class PraxisDynamicFormConfigEditor {
18534
18990
  }
18535
18991
  console.debug(message);
18536
18992
  }
18537
- constructor(storage, configService, settingsPanel, cdr, injectedData) {
18993
+ constructor(storage, configService, settingsPanel, cdr, globalActionCatalogSource, injectedData) {
18538
18994
  this.storage = storage;
18539
18995
  this.configService = configService;
18540
18996
  this.settingsPanel = settingsPanel;
18541
18997
  this.cdr = cdr;
18998
+ this.globalActionCatalog = this.buildGlobalActionCatalog(globalActionCatalogSource);
18542
18999
  const data = injectedData;
18543
19000
  this.openedWithCanonicalDocument =
18544
19001
  data?.document?.kind === 'praxis.dynamic-form.editor' ||
@@ -18636,6 +19093,7 @@ class PraxisDynamicFormConfigEditor {
18636
19093
  }
18637
19094
  }
18638
19095
  catch { }
19096
+ this.updateValidityState();
18639
19097
  // Nota: isValid$ é atualizado em onJsonValidationChange quando necessário
18640
19098
  // Para outras validações futuras, verificar se não há validação JSON em andamento
18641
19099
  }
@@ -18702,7 +19160,8 @@ class PraxisDynamicFormConfigEditor {
18702
19160
  }
18703
19161
  onJsonValidationChange(result) {
18704
19162
  // Atualizar estado de validação baseado no resultado do JSON
18705
- this.isValid$.next(result.isValid);
19163
+ this.jsonEditorIsValid = result.isValid;
19164
+ this.updateValidityState();
18706
19165
  // Evitar marcar dirty apenas por validação (não há mudança real de config)
18707
19166
  if (this.isDirty$.value) {
18708
19167
  this.updateDirtyState('json-validate');
@@ -18726,6 +19185,41 @@ class PraxisDynamicFormConfigEditor {
18726
19185
  this.lastRulesSignature = this.computeRulesSignature(this.ruleBuilderState);
18727
19186
  this.updateDirtyState('layout-change');
18728
19187
  }
19188
+ updateValidityState() {
19189
+ this.globalActionValidationIssues = this.collectGlobalActionValidationIssues(this.editedConfig);
19190
+ this.isValid$.next(this.jsonEditorIsValid && this.globalActionValidationIssues.length === 0);
19191
+ }
19192
+ collectGlobalActionValidationIssues(config) {
19193
+ return validateGlobalActionRefs(this.collectGlobalActionValidationTargets(config));
19194
+ }
19195
+ collectGlobalActionValidationTargets(config) {
19196
+ const targets = [];
19197
+ const add = (ref, path) => {
19198
+ if (!ref)
19199
+ return;
19200
+ targets.push({
19201
+ ref,
19202
+ path,
19203
+ catalogEntry: this.findGlobalActionCatalogEntry(ref.actionId),
19204
+ });
19205
+ };
19206
+ const actions = config.actions;
19207
+ add(actions?.submit?.globalAction, 'actions.submit.globalAction');
19208
+ add(actions?.cancel?.globalAction, 'actions.cancel.globalAction');
19209
+ add(actions?.reset?.globalAction, 'actions.reset.globalAction');
19210
+ actions?.custom?.forEach((action, index) => add(action?.globalAction, `actions.custom[${index}].globalAction`));
19211
+ config.sections?.forEach((section, sectionIndex) => {
19212
+ section?.headerActions?.forEach((action, actionIndex) => add(action?.globalAction, `sections[${sectionIndex}].headerActions[${actionIndex}].globalAction`));
19213
+ });
19214
+ return targets;
19215
+ }
19216
+ findGlobalActionCatalogEntry(actionId) {
19217
+ return this.globalActionCatalog.find((entry) => entry.id === actionId);
19218
+ }
19219
+ buildGlobalActionCatalog(source) {
19220
+ const injected = getGlobalActionCatalog(source);
19221
+ return injected.length ? injected : PRAXIS_GLOBAL_ACTION_CATALOG;
19222
+ }
18729
19223
  onRulesChanged(state) {
18730
19224
  if (!this.hasProcessedInitialRuleState) {
18731
19225
  const hadInitialRules = this.initialConfig?.formRules?.length ||
@@ -18955,6 +19449,7 @@ class PraxisDynamicFormConfigEditor {
18955
19449
  file: FieldType.STRING,
18956
19450
  url: FieldType.URL,
18957
19451
  boolean: FieldType.BOOLEAN,
19452
+ array: FieldType.JSON,
18958
19453
  json: FieldType.JSON,
18959
19454
  };
18960
19455
  return mapping[dataType ?? 'text'];
@@ -19209,7 +19704,7 @@ class PraxisDynamicFormConfigEditor {
19209
19704
  toBackConfigSnapshot() {
19210
19705
  return structuredClone(this.backConfig || { returnTo: '', confirmOnDirty: true });
19211
19706
  }
19212
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisDynamicFormConfigEditor, deps: [{ token: ASYNC_CONFIG_STORAGE }, { token: FormConfigService }, { token: i7.SettingsPanelService }, { token: i0.ChangeDetectorRef }, { token: SETTINGS_PANEL_DATA, optional: true }], target: i0.ɵɵFactoryTarget.Component });
19707
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisDynamicFormConfigEditor, deps: [{ token: ASYNC_CONFIG_STORAGE }, { token: FormConfigService }, { token: i7.SettingsPanelService }, { token: i0.ChangeDetectorRef }, { token: GLOBAL_ACTION_CATALOG, optional: true }, { token: SETTINGS_PANEL_DATA, optional: true }], target: i0.ɵɵFactoryTarget.Component });
19213
19708
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PraxisDynamicFormConfigEditor, isStandalone: true, selector: "praxis-dynamic-form-config-editor", providers: [FormConfigService], viewQueries: [{ propertyName: "jsonEditor", first: true, predicate: JsonConfigEditorComponent, descendants: true }], ngImport: i0, template: "<mat-tab-group class=\"config-tabs\" data-testid=\"dynamic-form-config-editor-tabs\">\n <mat-tab label=\"Geral\">\n <div class=\"tab-content\" data-testid=\"config-tab-panel-geral\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Dados do formul\u00E1rio</h3>\n <p class=\"text-caption\">Defina o modo de dados e o contexto de edi\u00E7\u00E3o.</p>\n </div>\n </div>\n <div class=\"form-grid\">\n <div class=\"form-row\">\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Modo de dados</mat-label>\n <mat-select [(ngModel)]=\"inputMode\" (ngModelChange)=\"onInputModeChange()\">\n <mat-option value=\"create\">create</mat-option>\n <mat-option value=\"edit\">edit</mat-option>\n <mat-option value=\"view\">view</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div class=\"block-status\" [class.block-status--implicit]=\"!isBindingsBlockPersisted()\">\n <strong>Bindings</strong>\n <span *ngIf=\"isBindingsBlockPersisted(); else bindingsMissing\">`bindings.mode` ser\u00E1 persistido no documento can\u00F4nico.</span>\n <ng-template #bindingsMissing>Bloco ausente no documento can\u00F4nico. O valor exibido \u00E9 fallback visual e n\u00E3o ser\u00E1 salvo at\u00E9 voc\u00EA editar este campo.</ng-template>\n </div>\n <div class=\"section-help\">\n <span class=\"section-help__label\">Entenda:</span>\n <button\n mat-icon-button\n class=\"help-icon-button\"\n type=\"button\"\n [matTooltip]=\"'O mode controla o fluxo (create/edit/view). enableCustomization s\u00F3 libera edi\u00E7\u00E3o do layout e n\u00E3o altera o mode.'\"\n matTooltipPosition=\"above\"\n >\n <mat-icon>help_outline</mat-icon>\n </button>\n </div>\n </div>\n </mat-card-content>\n </mat-card>\n\n <mat-card\n class=\"section-card\"\n *ngIf=\"hasResolvedRuntimeContract()\"\n data-testid=\"config-tab-runtime-contract\"\n >\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Contrato Runtime Resolvido</h3>\n <p class=\"text-caption\">\n Visualiza\u00E7\u00E3o somente leitura do contrato backend resolvido pelo host atual.\n </p>\n </div>\n </div>\n <div class=\"block-status block-status--readonly\">\n <strong>Runtime</strong>\n <span>\n Esses valores v\u00EAm do host/runtime atual e n\u00E3o s\u00E3o persistidos em\n <code>bindings</code> nem em <code>contextSnapshot</code>.\n </span>\n </div>\n <div class=\"runtime-contract-grid\">\n <div class=\"runtime-contract-field\">\n <span class=\"runtime-contract-label\">Schema URL</span>\n <code class=\"runtime-contract-code\">{{ runtimeContract?.schemaUrl || '\u2014' }}</code>\n </div>\n <div class=\"runtime-contract-field\">\n <span class=\"runtime-contract-label\">Origem do schema</span>\n <strong>{{ describeRuntimeSource(runtimeContract?.schemaSource) }}</strong>\n </div>\n <div class=\"runtime-contract-field\">\n <span class=\"runtime-contract-label\">Submit method</span>\n <strong>{{ runtimeContract?.submitMethod || '\u2014' }}</strong>\n </div>\n <div class=\"runtime-contract-field\">\n <span class=\"runtime-contract-label\">Submit URL</span>\n <code class=\"runtime-contract-code\">{{ runtimeContract?.submitUrl || '\u2014' }}</code>\n </div>\n <div class=\"runtime-contract-field\">\n <span class=\"runtime-contract-label\">Origem do submit</span>\n <strong>{{ describeRuntimeSource(runtimeContract?.submitSource) }}</strong>\n </div>\n </div>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"Verifica\u00E7\u00E3o de Schema\">\n <div class=\"tab-content\" data-testid=\"config-tab-panel-schema\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Valida\u00E7\u00E3o de schema</h3>\n <p class=\"text-caption\">Controle avisos quando o schema do servidor mudar.</p>\n </div>\n </div>\n <div class=\"form-grid\">\n <div class=\"form-row\">\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Notificar quando desatualizado</mat-label>\n <mat-select [(ngModel)]=\"schemaPrefs.notifyIfOutdated\" (ngModelChange)=\"onSchemaPrefsChange('schema-prefs-notify')\">\n <mat-option value=\"both\">Banner + Snackbar</mat-option>\n <mat-option value=\"inline\">Somente Banner</mat-option>\n <mat-option value=\"snackbar\">Somente Snackbar</mat-option>\n <mat-option value=\"none\">Nenhum</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div class=\"form-row\">\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Intervalo de sil\u00EAncio (ms)</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"schemaPrefs.snoozeMs\" (ngModelChange)=\"onSchemaPrefsChange('schema-prefs-snooze')\" />\n </mat-form-field>\n </div>\n <div class=\"form-row\">\n <mat-slide-toggle [(ngModel)]=\"schemaPrefs.autoOpenSettingsOnOutdated\" (ngModelChange)=\"onSchemaPrefsChange('schema-prefs-auto-open')\">Abrir configura\u00E7\u00F5es\n automaticamente</mat-slide-toggle>\n </div>\n <div class=\"form-row\" *ngIf=\"serverMeta\">\n <div class=\"meta-card\">\n <div class=\"meta-row\">\n <span>Server hash</span>\n <strong>{{ serverMeta.serverHash || '\u2014' }}</strong>\n </div>\n <div class=\"meta-row\">\n <span>\u00DAltima verifica\u00E7\u00E3o</span>\n <strong>{{ serverMeta.lastVerifiedAt || '\u2014' }}</strong>\n </div>\n </div>\n </div>\n <div class=\"block-status\" [class.block-status--implicit]=\"!isSchemaPrefsBlockPersisted()\">\n <strong>Schema Prefs</strong>\n <span *ngIf=\"isSchemaPrefsBlockPersisted(); else schemaPrefsMissing\">`contextSnapshot.schemaPrefs` ser\u00E1 persistido no documento can\u00F4nico.</span>\n <ng-template #schemaPrefsMissing>Bloco ausente no documento can\u00F4nico. Os controles exibem fallback visual e n\u00E3o ser\u00E3o salvos at\u00E9 voc\u00EA editar.</ng-template>\n </div>\n </div>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"Layout\">\n <div class=\"tab-content tab-content--full\" data-testid=\"config-tab-panel-layout\">\n <div class=\"section-header section-header--compact\">\n <div>\n <h3>Layout do formul\u00E1rio</h3>\n <p class=\"text-caption\">Organize se\u00E7\u00F5es, linhas e campos com drag & drop.</p>\n </div>\n </div>\n <div class=\"section-body section-body--flex\">\n <praxis-layout-editor [config]=\"editedConfig\" (configChange)=\"onConfigChange($event)\"\n (select)=\"onLayoutSelect($event)\"></praxis-layout-editor>\n </div>\n </div>\n </mat-tab>\n <mat-tab label=\"Hooks\">\n <div class=\"tab-content\" data-testid=\"config-tab-panel-hooks\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Hooks de ciclo de vida</h3>\n <p class=\"text-caption\">Automatize a\u00E7\u00F5es nos eventos do formul\u00E1rio.</p>\n </div>\n </div>\n <praxis-hooks-editor [config]=\"editedConfig\" (configChange)=\"onConfigChange($event)\"></praxis-hooks-editor>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab *ngIf=\"isPresentationMode\" label=\"Modo Apresenta\u00E7\u00E3o\">\n <div class=\"tab-content\" data-testid=\"config-tab-panel-presentation\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Modo Apresenta\u00E7\u00E3o</h3>\n <p class=\"text-caption\">Configura\u00E7\u00F5es de exibi\u00E7\u00E3o aplicadas somente ao modo apresenta\u00E7\u00E3o.</p>\n </div>\n </div>\n\n <div class=\"presentation-layout\">\n <div class=\"presentation-controls\">\n <div class=\"block-status control-span\" [class.block-status--implicit]=\"!isPresentationBlockPersisted()\">\n <strong>Presentation</strong>\n <span *ngIf=\"isPresentationBlockPersisted(); else presentationMissing\">`contextSnapshot.presentation` ser\u00E1 persistido no documento can\u00F4nico.</span>\n <ng-template #presentationMissing>Bloco ausente no documento can\u00F4nico. A pr\u00E9-visualiza\u00E7\u00E3o usa fallback local e n\u00E3o ser\u00E1 salva at\u00E9 voc\u00EA editar.</ng-template>\n </div>\n <div class=\"control\">\n <label class=\"control__label\">Posi\u00E7\u00E3o do r\u00F3tulo</label>\n <mat-button-toggle-group [(ngModel)]=\"presentationPrefs.labelPosition\" (ngModelChange)=\"onPresentationPrefsChange('presentation-label-position')\" aria-label=\"Posi\u00E7\u00E3o do r\u00F3tulo\"\n class=\"toggle-group\">\n <mat-button-toggle value=\"above\">\n <mat-icon>view_stream</mat-icon>\n <span class=\"sr-only\">Acima</span>\n </mat-button-toggle>\n <mat-button-toggle value=\"left\">\n <mat-icon>view_column</mat-icon>\n <span class=\"sr-only\">\u00C0 esquerda</span>\n </mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n\n <div class=\"control\">\n <label class=\"control__label\">Densidade</label>\n <mat-button-toggle-group [(ngModel)]=\"presentationPrefs.density\" (ngModelChange)=\"onPresentationPrefsChange('presentation-density')\" aria-label=\"Densidade\"\n class=\"toggle-group\">\n <mat-button-toggle value=\"comfortable\">Confort\u00E1vel</mat-button-toggle>\n <mat-button-toggle value=\"cozy\">Intermedi\u00E1ria</mat-button-toggle>\n <mat-button-toggle value=\"compact\">Compacta</mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n\n <div class=\"control\">\n <label class=\"control__label\">Alinhamento do r\u00F3tulo</label>\n <mat-button-toggle-group [(ngModel)]=\"presentationPrefs.labelAlign\" (ngModelChange)=\"onPresentationPrefsChange('presentation-label-align')\" aria-label=\"Alinhamento do r\u00F3tulo\"\n class=\"toggle-group\">\n <mat-button-toggle value=\"start\"><mat-icon>format_align_left</mat-icon></mat-button-toggle>\n <mat-button-toggle value=\"center\"><mat-icon>format_align_center</mat-icon></mat-button-toggle>\n <mat-button-toggle value=\"end\"><mat-icon>format_align_right</mat-icon></mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n\n <div class=\"control\">\n <label class=\"control__label\">Alinhamento do valor</label>\n <mat-button-toggle-group [(ngModel)]=\"presentationPrefs.valueAlign\" (ngModelChange)=\"onPresentationPrefsChange('presentation-value-align')\" aria-label=\"Alinhamento do valor\"\n class=\"toggle-group\">\n <mat-button-toggle value=\"start\"><mat-icon>format_align_left</mat-icon></mat-button-toggle>\n <mat-button-toggle value=\"center\"><mat-icon>format_align_center</mat-icon></mat-button-toggle>\n <mat-button-toggle value=\"end\"><mat-icon>format_align_right</mat-icon></mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n\n <div class=\"control\">\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Tamanho do r\u00F3tulo</mat-label>\n <input matInput type=\"number\" min=\"10\" [(ngModel)]=\"presentationPrefs.labelFontSize\" (ngModelChange)=\"onPresentationPrefsChange('presentation-label-font-size')\"\n placeholder=\"ex.: 12\" />\n <span matSuffix>px</span>\n </mat-form-field>\n </div>\n\n <div class=\"control\">\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Tamanho do valor</mat-label>\n <input matInput type=\"number\" min=\"10\" [(ngModel)]=\"presentationPrefs.valueFontSize\" (ngModelChange)=\"onPresentationPrefsChange('presentation-value-font-size')\"\n placeholder=\"ex.: 16\" />\n <span matSuffix>px</span>\n </mat-form-field>\n </div>\n\n <div class=\"control\">\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Largura do r\u00F3tulo (label \u00E0 esquerda)</mat-label>\n <input matInput type=\"number\" min=\"60\" [(ngModel)]=\"presentationPrefs.labelWidth\" (ngModelChange)=\"onPresentationPrefsChange('presentation-label-width')\"\n placeholder=\"ex.: 140\" />\n <span matSuffix>px</span>\n </mat-form-field>\n </div>\n\n <div class=\"control control--inline\">\n <mat-slide-toggle [(ngModel)]=\"presentationPrefs.compact\" (ngModelChange)=\"onPresentationPrefsChange('presentation-compact')\">\n Modo compacto\n </mat-slide-toggle>\n <mat-checkbox [(ngModel)]=\"truncatePreview\">\n Truncar valores\n </mat-checkbox>\n </div>\n </div>\n\n <div class=\"presentation-preview\">\n <h4>Pr\u00E9\u2011visualiza\u00E7\u00E3o</h4>\n <div class=\"preview-card\">\n <div class=\"praxis-dynamic-form presentation-mode\" [ngClass]=\"{\n 'pres-compact': !!presentationPrefs.compact,\n 'pres-label-left': presentationPrefs.labelPosition === 'left',\n 'pres-label-above': presentationPrefs.labelPosition === 'above',\n 'pres-density-compact': presentationPrefs.density === 'compact',\n 'pres-density-cozy': presentationPrefs.density === 'cozy'\n }\" [style.--pfx-pres-label-size]=\"(presentationPrefs.labelFontSize || 12) + 'px'\"\n [style.--pfx-pres-value-size]=\"(presentationPrefs.valueFontSize || 16) + 'px'\"\n [style.--pfx-pres-label-width]=\"(presentationPrefs.labelWidth || 140) + 'px'\"\n [style.--pfx-pres-label-align]=\"presentationPrefs.labelAlign || 'start'\"\n [style.--pfx-pres-value-align]=\"presentationPrefs.valueAlign || 'start'\">\n <div class=\"preview-row\" *ngFor=\"let item of previewItems\">\n <span class=\"praxis-presentation__label\">{{ item.label }}</span>\n <span class=\"praxis-presentation__value\" [title]=\"truncatePreview ? item.value : null\"\n [style.maxWidth]=\"truncatePreview ? '280px' : null\"\n [style.overflow]=\"truncatePreview ? 'hidden' : null\"\n [style.textOverflow]=\"truncatePreview ? 'ellipsis' : null\"\n [style.whiteSpace]=\"truncatePreview ? 'nowrap' : 'normal'\">\n {{ item.value }}\n </span>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"section-help\">\n <span class=\"section-help__label\">Entenda:</span>\n <button\n mat-icon-button\n class=\"help-icon-button\"\n type=\"button\"\n [matTooltip]=\"'Ajustes aplicam somente ao Modo Apresenta\u00E7\u00E3o e s\u00E3o salvos por formul\u00E1rio (via CSS/Classes).'\"\n matTooltipPosition=\"above\"\n >\n <mat-icon>help_outline</mat-icon>\n </button>\n </div>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"Comportamento\">\n <div class=\"tab-content\" data-testid=\"config-tab-panel-comportamento\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Comportamento</h3>\n <p class=\"text-caption\">Ajuste valida\u00E7\u00F5es e respostas do formul\u00E1rio.</p>\n </div>\n </div>\n <praxis-behavior-editor [config]=\"editedConfig\" (configChange)=\"onConfigChange($event)\"></praxis-behavior-editor>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"Dicas e Tooltips\">\n <div class=\"tab-content tab-content--dense\" data-testid=\"config-tab-panel-hints\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Dicas dos modos</h3>\n <p class=\"text-caption\">Personalize textos de apoio para modos de dados e estados de UI.</p>\n </div>\n </div>\n <p class=\"text-caption\">Personalize os textos usados como tooltip/hint para modos de dados e modos globais de\n UI.</p>\n\n <div class=\"grid-2-cols grid-2-cols--compact\">\n <section>\n <h4>Modos de dados</h4>\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Criar</mat-label>\n <input matInput [(ngModel)]=\"editedConfig.hints!.dataModes.create\"\n (ngModelChange)=\"updateDirtyState('hints-data-create')\" placeholder=\"Ex.: Preencha os campos obrigat\u00F3rios.\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Editar</mat-label>\n <input matInput [(ngModel)]=\"editedConfig.hints!.dataModes.edit\"\n (ngModelChange)=\"updateDirtyState('hints-data-edit')\" placeholder=\"Ex.: Revise os dados antes de salvar.\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Visualizar</mat-label>\n <input matInput [(ngModel)]=\"editedConfig.hints!.dataModes.view\"\n (ngModelChange)=\"updateDirtyState('hints-data-view')\" placeholder=\"Ex.: Visualiza\u00E7\u00E3o somente leitura.\" />\n </mat-form-field>\n </section>\n\n <section>\n <h4>Modos de UI</h4>\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Apresenta\u00E7\u00E3o</mat-label>\n <textarea matInput rows=\"2\" [(ngModel)]=\"editedConfig.hints!.uiModes.presentation\"\n (ngModelChange)=\"updateDirtyState('hints-ui-presentation')\" placeholder=\"Ex.: Modo compacto para leitura r\u00E1pida.\"></textarea>\n </mat-form-field>\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Leitura (readonly)</mat-label>\n <textarea matInput rows=\"2\" [(ngModel)]=\"editedConfig.hints!.uiModes.readonly\"\n (ngModelChange)=\"updateDirtyState('hints-ui-readonly')\" placeholder=\"Ex.: Campos bloqueados para edi\u00E7\u00E3o.\"></textarea>\n </mat-form-field>\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Desabilitado</mat-label>\n <textarea matInput rows=\"2\" [(ngModel)]=\"editedConfig.hints!.uiModes.disabled\"\n (ngModelChange)=\"updateDirtyState('hints-ui-disabled')\" placeholder=\"Ex.: Fun\u00E7\u00E3o indispon\u00EDvel no momento.\"></textarea>\n </mat-form-field>\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Vis\u00EDvel</mat-label>\n <textarea matInput rows=\"2\" [(ngModel)]=\"editedConfig.hints!.uiModes.visible\"\n (ngModelChange)=\"updateDirtyState('hints-ui-visible')\" placeholder=\"Ex.: Campo exibido conforme permiss\u00F5es.\"></textarea>\n </mat-form-field>\n </section>\n </div>\n\n <div class=\"actions-row\">\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"restoreHintsDefaults()\">\n <mat-icon>restore</mat-icon>\n Restaurar padr\u00F5es\n </button>\n </div>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"A\u00E7\u00F5es\">\n <div class=\"tab-content\" data-testid=\"config-tab-panel-acoes\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>A\u00E7\u00F5es do formul\u00E1rio</h3>\n <p class=\"text-caption\">Configure bot\u00F5es padr\u00E3o e a\u00E7\u00F5es customizadas.</p>\n </div>\n </div>\n <praxis-actions-editor [config]=\"editedConfig\" (configChange)=\"onConfigChange($event)\"></praxis-actions-editor>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"Regras\">\n <div class=\"tab-content visual-builder-content\" data-testid=\"config-tab-panel-regras\">\n <div class=\"builder-header\">\n <div>\n <h3>Regras visuais</h3>\n <p class=\"text-caption\">Defina visibilidade, estilos e rea\u00E7\u00F5es baseadas em condi\u00E7\u00F5es.</p>\n </div>\n </div>\n <praxis-visual-builder [config]=\"ruleBuilderConfig\" [initialRules]=\"ruleBuilderState\"\n (rulesChanged)=\"onRulesChanged($event)\"></praxis-visual-builder>\n </div>\n </mat-tab>\n <mat-tab label=\"Cascatas\">\n <div class=\"tab-content\" data-testid=\"config-tab-panel-cascatas\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Depend\u00EAncias entre campos</h3>\n <p class=\"text-caption\">Configure cascatas e carregamento entre campos.</p>\n </div>\n </div>\n <praxis-cascade-manager-tab [fields]=\"editedConfig.fieldMetadata || []\"\n (apply)=\"onCascadeApply($event)\"></praxis-cascade-manager-tab>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"Mensagens\">\n <div class=\"tab-content\" data-testid=\"config-tab-panel-mensagens\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Mensagens e feedback</h3>\n <p class=\"text-caption\">Padronize textos de sucesso, erro e confirma\u00E7\u00F5es.</p>\n </div>\n </div>\n <praxis-messages-editor [config]=\"editedConfig\" (configChange)=\"onConfigChange($event)\"></praxis-messages-editor>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"Navega\u00E7\u00E3o\">\n <div class=\"tab-content\" data-testid=\"config-tab-panel-navegacao\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Navega\u00E7\u00E3o de retorno</h3>\n <p class=\"text-caption\">Defina o comportamento do bot\u00E3o Voltar em fluxos CRUD.</p>\n </div>\n </div>\n <div class=\"form-grid\">\n <div class=\"form-row\">\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Rota de retorno</mat-label>\n <input matInput id=\"returnTo\" [(ngModel)]=\"backConfig!.returnTo\" (ngModelChange)=\"onBackConfigChange()\" placeholder=\"/funcionarios\" />\n </mat-form-field>\n </div>\n <div class=\"form-row\">\n <mat-slide-toggle [(ngModel)]=\"backConfig!.confirmOnDirty\" (ngModelChange)=\"onBackConfigChange()\">\n Confirmar ao sair com altera\u00E7\u00F5es\n </mat-slide-toggle>\n </div>\n <div class=\"block-status\" [class.block-status--implicit]=\"!isBackConfigBlockPersisted()\">\n <strong>Back Config</strong>\n <span *ngIf=\"isBackConfigBlockPersisted(); else backConfigMissing\">`contextSnapshot.backConfig` ser\u00E1 persistido no documento can\u00F4nico.</span>\n <ng-template #backConfigMissing>Bloco ausente no documento can\u00F4nico. Os valores exibidos s\u00E3o fallback visual e n\u00E3o ser\u00E3o salvos at\u00E9 voc\u00EA editar.</ng-template>\n </div>\n </div>\n <div class=\"section-help\">\n <span class=\"section-help__label\">Observa\u00E7\u00E3o:</span>\n <button\n mat-icon-button\n class=\"help-icon-button\"\n type=\"button\"\n [matTooltip]=\"'Afeta o fluxo de navega\u00E7\u00E3o do CRUD e pode ser sobreposto por metadados do recurso.'\"\n matTooltipPosition=\"above\"\n >\n <mat-icon>help_outline</mat-icon>\n </button>\n </div>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"JSON\">\n <div class=\"tab-content\" data-testid=\"config-tab-panel-json\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Configura\u00E7\u00E3o JSON</h3>\n <p class=\"text-caption\">Edi\u00E7\u00E3o avan\u00E7ada do documento can\u00F4nico de autoria do formul\u00E1rio.</p>\n </div>\n </div>\n <form-json-config-editor [document]=\"jsonDocument\" (documentChange)=\"onJsonConfigChange($event)\"\n (validationChange)=\"onJsonValidationChange($event)\" (editorEvent)=\"onJsonEditorEvent($event)\">\n </form-json-config-editor>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n</mat-tab-group>\n", styles: [":host{display:block;height:100%}.config-tabs{height:100%}::ng-deep .mat-mdc-tab-group.config-tabs{display:flex;flex-direction:column}::ng-deep .mat-mdc-tab-group.config-tabs .mat-mdc-tab-body-wrapper{flex:1;min-height:0}::ng-deep .mat-mdc-tab-group.config-tabs .mat-mdc-tab-body-content{height:100%;overflow:auto;display:flex;flex-direction:column;padding:0!important}.tab-content{padding:16px 20px 20px;flex:1}.form-row{display:block;margin-bottom:12px}.form-grid{display:grid;gap:12px}.section-help{display:inline-flex;align-items:center;gap:8px;color:var(--md-sys-color-on-surface-variant);font-size:12px}.section-help__label{font-weight:500}.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}.help-icon-button mat-icon{font-size:18px;width:18px;height:18px}.grid-2-cols{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:16px}.grid-2-cols--compact{gap:12px}.w-100{width:100%}.visual-builder-content{padding:0!important;height:100%;overflow:hidden;display:flex;flex-direction:column}.visual-builder-content>praxis-visual-builder{flex:1;min-height:0}.json-field{width:100%}.presentation-layout{display:flex;gap:20px;align-items:flex-start}.presentation-controls{flex:1 1 60%;display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:14px 18px}.control{display:flex;flex-direction:column}.control-span{grid-column:1/-1}.block-status{grid-column:1/-1;display:flex;gap:8px;align-items:flex-start;padding:10px 12px;border-radius:10px;background:color-mix(in srgb,var(--md-sys-color-primary-container) 35%,transparent);color:var(--md-sys-color-on-surface);font-size:12px;line-height:1.45}.block-status strong{min-width:112px;font-size:11px;letter-spacing:.04em;text-transform:uppercase;color:var(--md-sys-color-on-surface-variant)}.block-status--implicit{background:color-mix(in srgb,var(--md-sys-color-surface-variant) 42%,transparent);border:1px dashed var(--md-sys-color-outline)}.block-status--readonly{margin-bottom:12px}.block-status code{font-family:var(--md-ref-typeface-mono, \"Courier New\", monospace);font-size:11px}.control__label{font-size:12px;color:var(--md-sys-color-on-surface-variant);margin-bottom:6px}.toggle-group{width:100%}.control--inline{grid-column:1/-1;display:flex;gap:16px;align-items:center}.presentation-preview{flex:0 0 360px;position:sticky;top:16px}.preview-card{border:1px solid var(--md-sys-color-outline);border-radius:12px;padding:14px;background:var(--md-sys-color-surface-container)}.preview-row{display:flex;gap:8px;padding:6px 0;border-bottom:1px dashed var(--md-sys-color-outline-variant)}.preview-row:last-child{border-bottom:none}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.presentation-preview .praxis-presentation__label{color:var(--md-sys-color-on-surface-variant)}.presentation-preview .praxis-presentation__value{color:var(--md-sys-color-on-surface)}.presentation-preview .presentation-mode.pres-density-cozy .preview-row{padding:6px 0}.presentation-preview .presentation-mode.pres-density-compact .preview-row,.presentation-preview .presentation-mode.pres-compact .preview-row{padding:2px 0}.presentation-preview .presentation-mode.pres-label-left .preview-row{display:grid;grid-template-columns:var(--pfx-pres-label-width, 140px) 1fr;align-items:baseline;column-gap:10px}.presentation-preview .presentation-mode.pres-label-left .praxis-presentation__label{margin:0;text-align:var(--pfx-pres-label-align, start)}.presentation-preview .presentation-mode .praxis-presentation__value{text-align:var(--pfx-pres-value-align, start)}.presentation-preview h4{margin:0 0 8px;color:var(--md-sys-color-on-surface);font-size:1rem}.section-card{border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container);box-shadow:var(--md-sys-elevation-level1, none)}.section-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:12px}.tab-content--dense{padding:12px 16px 16px}.tab-content--dense .section-header,.tab-content--dense .form-row{margin-bottom:8px}.section-header h3{margin:0;font-size:1.05rem;color:var(--md-sys-color-on-surface)}.text-caption{margin:6px 0 0;font-size:.9rem;color:var(--md-sys-color-on-surface-variant)}.section-header--compact{margin-bottom:8px}.section-body{display:block}.section-body--flex{flex:1;min-height:0;display:flex;flex-direction:column}.builder-header{padding:12px 16px;border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface)}.builder-header h3{margin:0;font-size:1rem;color:var(--md-sys-color-on-surface)}.hint{margin:8px 0 0;padding:10px 12px;border-radius:10px;border:1px dashed var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface-variant)}.meta-card{display:grid;gap:6px;padding:10px 12px;border-radius:10px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface)}.meta-row{display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:.9rem;color:var(--md-sys-color-on-surface-variant)}.meta-row strong{color:var(--md-sys-color-on-surface);font-weight:600}.runtime-contract-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px}.runtime-contract-field{display:grid;gap:6px;padding:12px;border-radius:10px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface)}.runtime-contract-label{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.runtime-contract-code{display:block;font-family:var(--md-ref-typeface-mono, \"Courier New\", monospace);font-size:12px;line-height:1.45;color:var(--md-sys-color-on-surface);word-break:break-word;white-space:normal}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { 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: FormsModule }, { 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.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i7$3.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i7$3.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: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i8.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i8.MatCardContent, selector: "mat-card-content" }, { 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: MatFormFieldModule }, { kind: "component", type: i4$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6$3.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$3.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7$1.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: MatCheckboxModule }, { kind: "component", type: i12$1.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i6$2.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: MatButtonToggleModule }, { kind: "directive", type: i7$2.MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "disabledInteractive", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "component", type: i7$2.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i9.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: JsonConfigEditorComponent, selector: "form-json-config-editor", inputs: ["config", "document"], outputs: ["configChange", "documentChange", "validationChange", "editorEvent"] }, { kind: "component", type: LayoutEditorComponent, selector: "praxis-layout-editor", inputs: ["config"], outputs: ["configChange", "select"] }, { kind: "component", type: BehaviorEditorComponent, selector: "praxis-behavior-editor", inputs: ["config"], outputs: ["configChange"] }, { kind: "component", type: ActionsEditorComponent$1, selector: "praxis-actions-editor", inputs: ["config"], outputs: ["configChange"] }, { kind: "component", type: MessagesEditorComponent, selector: "praxis-messages-editor", inputs: ["config"], outputs: ["configChange"] }, { kind: "component", type: HooksEditorComponent, selector: "praxis-hooks-editor", inputs: ["config"], outputs: ["configChange"] }, { kind: "component", type: PraxisVisualBuilder, selector: "praxis-visual-builder", inputs: ["config", "initialRules"], outputs: ["rulesChanged", "exportRequested", "importRequested"] }, { kind: "component", type: CascadeManagerTabComponent, selector: "praxis-cascade-manager-tab", inputs: ["fields", "connections"], outputs: ["apply", "cancel"] }] });
19214
19709
  }
19215
19710
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisDynamicFormConfigEditor, decorators: [{
@@ -19242,6 +19737,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
19242
19737
  args: [ASYNC_CONFIG_STORAGE]
19243
19738
  }] }, { type: FormConfigService }, { type: i7.SettingsPanelService }, { type: i0.ChangeDetectorRef }, { type: undefined, decorators: [{
19244
19739
  type: Optional
19740
+ }, {
19741
+ type: Inject,
19742
+ args: [GLOBAL_ACTION_CATALOG]
19743
+ }] }, { type: undefined, decorators: [{
19744
+ type: Optional
19245
19745
  }, {
19246
19746
  type: Inject,
19247
19747
  args: [SETTINGS_PANEL_DATA]
@@ -19438,11 +19938,11 @@ class PraxisFilterForm {
19438
19938
  return `col-${i}-${col.fields.join(',')}`;
19439
19939
  }
19440
19940
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisFilterForm, deps: [{ token: i1$2.DynamicFormService }], target: i0.ɵɵFactoryTarget.Component });
19441
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PraxisFilterForm, isStandalone: true, selector: "praxis-filter-form", inputs: { config: "config", formId: "formId", resourcePath: "resourcePath", mode: "mode" }, outputs: { formReady: "formReady", valueChange: "valueChange", submit: "submit", requestSearch: "requestSearch", validityChange: "validityChange" }, usesOnChanges: true, ngImport: i0, template: "<form [formGroup]=\"form\" class=\"praxis-filter-form\" (ngSubmit)=\"onFormSubmit()\">\n @if (hasLayout) { @for (section of config.sections; track section?.id ??\n $index) {\n <div class=\"filter-section\">\n @if (section.title) {\n <h3>{{ section.title }}</h3>\n } @for (row of section.rows; track $index) {\n <div class=\"filter-row\">\n @for ( column of row.columns; track (column?.fields?.join(',') ?? $index)\n ) {\n <div class=\"filter-column\">\n <ng-container\n dynamicFieldLoader\n [fields]=\"getColumnFields(column)\"\n [formGroup]=\"form\"\n [enableExternalControlBinding]=\"true\"\n ></ng-container>\n </div>\n }\n </div>\n }\n </div>\n } } @else {\n <div class=\"filter-row\">\n @for (field of fieldMetadata; track field?.name ?? $index) {\n <div class=\"filter-column\">\n <ng-container\n dynamicFieldLoader\n [fields]=\"[field]\"\n [formGroup]=\"form\"\n [enableExternalControlBinding]=\"true\"\n ></ng-container>\n </div>\n }\n </div>\n }\n <!-- Bot\u00E3o de submit invis\u00EDvel para garantir submit ao pressionar Enter -->\n <button\n type=\"submit\"\n class=\"hidden-submit\"\n aria-hidden=\"true\"\n tabindex=\"-1\"\n ></button>\n</form>\n", styles: [":host{display:block}.filter-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px 16px}.filter-column{min-width:0}.hidden-submit{display:none;width:0;height:0;padding:0;border:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: DynamicFieldLoaderDirective, selector: "[dynamicFieldLoader]", inputs: ["fields", "formGroup", "enableExternalControlBinding", "itemTemplate", "debugTrace", "debugTraceLabel", "readonlyMode", "disabledMode", "presentationMode", "visible", "canvasMode"], outputs: ["componentsCreated", "fieldCreated", "fieldDestroyed", "renderError", "canvasMouseEnter", "canvasMouseLeave", "canvasClick"] }] });
19941
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PraxisFilterForm, isStandalone: true, selector: "praxis-filter-form", inputs: { config: "config", formId: "formId", resourcePath: "resourcePath", mode: "mode" }, outputs: { formReady: "formReady", valueChange: "valueChange", submit: "submit", requestSearch: "requestSearch", validityChange: "validityChange" }, usesOnChanges: true, ngImport: i0, template: "<form\n [formGroup]=\"form\"\n class=\"praxis-filter-form\"\n (change)=\"$event.stopPropagation()\"\n (ngSubmit)=\"onFormSubmit(); $event.preventDefault(); $event.stopPropagation()\"\n>\n @if (hasLayout) { @for (section of config.sections; track section?.id ??\n $index) {\n <div class=\"filter-section\">\n @if (section.title) {\n <h3>{{ section.title }}</h3>\n } @for (row of section.rows; track $index) {\n <div class=\"filter-row\">\n @for ( column of row.columns; track (column?.fields?.join(',') ?? $index)\n ) {\n <div class=\"filter-column\">\n <ng-container\n dynamicFieldLoader\n [fields]=\"getColumnFields(column)\"\n [formGroup]=\"form\"\n [enableExternalControlBinding]=\"true\"\n ></ng-container>\n </div>\n }\n </div>\n }\n </div>\n } } @else {\n <div class=\"filter-row\">\n @for (field of fieldMetadata; track field?.name ?? $index) {\n <div class=\"filter-column\">\n <ng-container\n dynamicFieldLoader\n [fields]=\"[field]\"\n [formGroup]=\"form\"\n [enableExternalControlBinding]=\"true\"\n ></ng-container>\n </div>\n }\n </div>\n }\n <!-- Bot\u00E3o de submit invis\u00EDvel para garantir submit ao pressionar Enter -->\n <button\n type=\"submit\"\n class=\"hidden-submit\"\n aria-hidden=\"true\"\n tabindex=\"-1\"\n ></button>\n</form>\n", styles: [":host{display:block}.filter-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px 16px}.filter-column{min-width:0}.hidden-submit{display:none;width:0;height:0;padding:0;border:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: DynamicFieldLoaderDirective, selector: "[dynamicFieldLoader]", inputs: ["fields", "formGroup", "enableExternalControlBinding", "itemTemplate", "debugTrace", "debugTraceLabel", "readonlyMode", "disabledMode", "presentationMode", "visible", "canvasMode"], outputs: ["componentsCreated", "fieldCreated", "fieldDestroyed", "renderError", "canvasMouseEnter", "canvasMouseLeave", "canvasClick"] }] });
19442
19942
  }
19443
19943
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisFilterForm, decorators: [{
19444
19944
  type: Component,
19445
- args: [{ selector: 'praxis-filter-form', standalone: true, imports: [CommonModule, ReactiveFormsModule, DynamicFieldLoaderDirective], template: "<form [formGroup]=\"form\" class=\"praxis-filter-form\" (ngSubmit)=\"onFormSubmit()\">\n @if (hasLayout) { @for (section of config.sections; track section?.id ??\n $index) {\n <div class=\"filter-section\">\n @if (section.title) {\n <h3>{{ section.title }}</h3>\n } @for (row of section.rows; track $index) {\n <div class=\"filter-row\">\n @for ( column of row.columns; track (column?.fields?.join(',') ?? $index)\n ) {\n <div class=\"filter-column\">\n <ng-container\n dynamicFieldLoader\n [fields]=\"getColumnFields(column)\"\n [formGroup]=\"form\"\n [enableExternalControlBinding]=\"true\"\n ></ng-container>\n </div>\n }\n </div>\n }\n </div>\n } } @else {\n <div class=\"filter-row\">\n @for (field of fieldMetadata; track field?.name ?? $index) {\n <div class=\"filter-column\">\n <ng-container\n dynamicFieldLoader\n [fields]=\"[field]\"\n [formGroup]=\"form\"\n [enableExternalControlBinding]=\"true\"\n ></ng-container>\n </div>\n }\n </div>\n }\n <!-- Bot\u00E3o de submit invis\u00EDvel para garantir submit ao pressionar Enter -->\n <button\n type=\"submit\"\n class=\"hidden-submit\"\n aria-hidden=\"true\"\n tabindex=\"-1\"\n ></button>\n</form>\n", styles: [":host{display:block}.filter-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px 16px}.filter-column{min-width:0}.hidden-submit{display:none;width:0;height:0;padding:0;border:0}\n"] }]
19945
+ args: [{ selector: 'praxis-filter-form', standalone: true, imports: [CommonModule, ReactiveFormsModule, DynamicFieldLoaderDirective], template: "<form\n [formGroup]=\"form\"\n class=\"praxis-filter-form\"\n (change)=\"$event.stopPropagation()\"\n (ngSubmit)=\"onFormSubmit(); $event.preventDefault(); $event.stopPropagation()\"\n>\n @if (hasLayout) { @for (section of config.sections; track section?.id ??\n $index) {\n <div class=\"filter-section\">\n @if (section.title) {\n <h3>{{ section.title }}</h3>\n } @for (row of section.rows; track $index) {\n <div class=\"filter-row\">\n @for ( column of row.columns; track (column?.fields?.join(',') ?? $index)\n ) {\n <div class=\"filter-column\">\n <ng-container\n dynamicFieldLoader\n [fields]=\"getColumnFields(column)\"\n [formGroup]=\"form\"\n [enableExternalControlBinding]=\"true\"\n ></ng-container>\n </div>\n }\n </div>\n }\n </div>\n } } @else {\n <div class=\"filter-row\">\n @for (field of fieldMetadata; track field?.name ?? $index) {\n <div class=\"filter-column\">\n <ng-container\n dynamicFieldLoader\n [fields]=\"[field]\"\n [formGroup]=\"form\"\n [enableExternalControlBinding]=\"true\"\n ></ng-container>\n </div>\n }\n </div>\n }\n <!-- Bot\u00E3o de submit invis\u00EDvel para garantir submit ao pressionar Enter -->\n <button\n type=\"submit\"\n class=\"hidden-submit\"\n aria-hidden=\"true\"\n tabindex=\"-1\"\n ></button>\n</form>\n", styles: [":host{display:block}.filter-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px 16px}.filter-column{min-width:0}.hidden-submit{display:none;width:0;height:0;padding:0;border:0}\n"] }]
19446
19946
  }], ctorParameters: () => [{ type: i1$2.DynamicFormService }], propDecorators: { config: [{
19447
19947
  type: Input,
19448
19948
  args: [{ required: true }]
@@ -19610,6 +20110,7 @@ const PRAXIS_DYNAMIC_FORM_COMPONENT_METADATA = {
19610
20110
  inputs: [
19611
20111
  { name: 'resourcePath', type: 'string', label: 'Recurso', description: 'Recurso base para CRUD/schemas' },
19612
20112
  { name: 'resourceId', type: 'string | number', label: 'ID do Registro', description: 'Identificador do registro' },
20113
+ { name: 'initialValue', type: 'Record<string, unknown> | null', label: 'Valor inicial', description: 'Valores iniciais locais aplicados antes da interacao do usuario' },
19613
20114
  { name: 'editorialContext', type: 'Record<string, unknown> | null', label: 'Contexto editorial', description: 'Contexto compartilhado usado para resolver bindings do rich content hospedado no form' },
19614
20115
  { name: 'mode', type: "'create' | 'edit' | 'view'", default: 'create', label: 'Modo', description: 'Modo do formulario' },
19615
20116
  {
@@ -20056,9 +20557,7 @@ class SectionEditorComponent {
20056
20557
  isBusy$ = new BehaviorSubject(false);
20057
20558
  globalActionCatalogSource = inject(GLOBAL_ACTION_CATALOG, { optional: true }) ?? [];
20058
20559
  i18n = inject(PraxisI18nService);
20059
- legacyActionSpecs = GLOBAL_ACTION_SPEC_CATALOG;
20060
20560
  globalActionCatalog = buildGlobalActionCatalog({
20061
- legacySpecs: this.legacyActionSpecs,
20062
20561
  injectedCatalog: getGlobalActionCatalog(this.globalActionCatalogSource),
20063
20562
  fallbackCatalog: PRAXIS_GLOBAL_ACTION_CATALOG,
20064
20563
  mapCatalogEntry: (entry) => this.mapCatalogEntryToActionSpec(entry),
@@ -20165,63 +20664,66 @@ class SectionEditorComponent {
20165
20664
  getHeaderActionSpecById(id) {
20166
20665
  return this.globalActionCatalog.find((item) => item.id === id);
20167
20666
  }
20168
- getHeaderActionSelectValue(value) {
20169
- const parsed = parseActionValue(value, this.globalActionCatalog, this.customActionValue);
20170
- return parsed.isGlobal ? parsed.id : this.customActionValue;
20667
+ getHeaderActionSelectValue(value, globalAction) {
20668
+ return globalAction?.actionId || this.customActionValue;
20171
20669
  }
20172
- getHeaderActionParam(value) {
20173
- const parsed = parseActionValue(value, this.globalActionCatalog, this.customActionValue);
20174
- return parsed.isGlobal ? parsed.param : '';
20670
+ getHeaderActionParam(value, globalAction) {
20671
+ const payload = globalAction?.payload;
20672
+ if (payload === undefined || payload === null)
20673
+ return '';
20674
+ return typeof payload === 'string' ? payload : JSON.stringify(payload, null, 2);
20175
20675
  }
20176
- getHeaderActionCustomValue(value) {
20177
- const parsed = parseActionValue(value, this.globalActionCatalog, this.customActionValue);
20178
- return parsed.isGlobal ? '' : parsed.raw;
20676
+ getHeaderActionCustomValue(value, globalAction) {
20677
+ return globalAction ? '' : value || '';
20179
20678
  }
20180
- getHeaderGlobalActionSchema(value) {
20181
- const parsed = parseActionValue(value, this.globalActionCatalog, this.customActionValue);
20182
- if (!parsed.isGlobal)
20183
- return undefined;
20184
- return getGlobalActionUiSchema(parsed.id);
20679
+ getHeaderGlobalActionSchema(value, globalAction) {
20680
+ return getGlobalActionUiSchema(globalAction?.actionId);
20185
20681
  }
20186
- getHeaderSurfaceOpenActionPayload(value) {
20187
- const info = getActionParamInfo(value, this.globalActionCatalog, this.customActionValue);
20188
- const payload = info.isJson && info.json && typeof info.json === 'object' && !Array.isArray(info.json)
20189
- ? info.json
20190
- : undefined;
20191
- return normalizeSurfaceOpenPayload(payload);
20682
+ getHeaderSurfaceOpenActionPayload(value, globalAction) {
20683
+ return normalizeSurfaceOpenPayload(globalAction?.payload);
20192
20684
  }
20193
- isHeaderActionParamMissing(value) {
20194
- return isRequiredParamMissing(value, this.globalActionCatalog, this.customActionValue);
20685
+ isHeaderActionParamMissing(value, globalAction) {
20686
+ return isRequiredParamMissing(globalAction, this.globalActionCatalog);
20195
20687
  }
20196
20688
  onHeaderActionSelectChange(index, value) {
20197
20689
  const group = this.headerActionsArray.at(index);
20198
20690
  if (!group)
20199
20691
  return;
20200
- const currentValue = String(group.get('action')?.value || '');
20201
20692
  if (value === this.customActionValue) {
20202
- const parsed = parseActionValue(currentValue, this.globalActionCatalog, this.customActionValue);
20203
- if (parsed.isGlobal) {
20204
- group.get('action')?.setValue('');
20205
- }
20693
+ group.get('globalAction')?.setValue(undefined);
20206
20694
  return;
20207
20695
  }
20208
- const parsed = parseActionValue(currentValue, this.globalActionCatalog, this.customActionValue);
20209
- const param = parsed.isGlobal && parsed.id === value ? parsed.param : '';
20210
- group.get('action')?.setValue(param ? `${value}:${param}` : value);
20696
+ const current = group.get('globalAction')?.value;
20697
+ const payload = current?.actionId === value ? current.payload : undefined;
20698
+ group.get('globalAction')?.setValue(payload !== undefined ? { actionId: value, payload } : { actionId: value });
20699
+ group.get('action')?.setValue('');
20211
20700
  }
20212
20701
  onHeaderActionParamChange(index, value) {
20213
20702
  const group = this.headerActionsArray.at(index);
20214
20703
  if (!group)
20215
20704
  return;
20216
- const parsed = parseActionValue(String(group.get('action')?.value || ''), this.globalActionCatalog, this.customActionValue);
20217
- const id = parsed.isGlobal ? parsed.id : '';
20218
- group.get('action')?.setValue(id ? (value ? `${id}:${value}` : id) : value);
20705
+ const current = group.get('globalAction')?.value;
20706
+ const id = current?.actionId;
20707
+ if (!id)
20708
+ return;
20709
+ const trimmed = String(value || '').trim();
20710
+ if (!trimmed) {
20711
+ group.get('globalAction')?.setValue({ actionId: id });
20712
+ return;
20713
+ }
20714
+ try {
20715
+ group.get('globalAction')?.setValue({ actionId: id, payload: JSON.parse(trimmed) });
20716
+ }
20717
+ catch {
20718
+ group.get('globalAction')?.setValue({ actionId: id, payload: trimmed });
20719
+ }
20219
20720
  }
20220
20721
  onHeaderActionCustomChange(index, value) {
20221
20722
  const group = this.headerActionsArray.at(index);
20222
20723
  if (!group)
20223
20724
  return;
20224
20725
  group.get('action')?.setValue(value);
20726
+ group.get('globalAction')?.setValue(undefined);
20225
20727
  }
20226
20728
  async pickHeaderActionIcon(index) {
20227
20729
  const group = this.headerActionsArray.at(index);
@@ -20314,7 +20816,11 @@ class SectionEditorComponent {
20314
20816
  const group = this.headerActionsArray.at(index);
20315
20817
  if (!group)
20316
20818
  return;
20317
- group.get('action')?.setValue(`surface.open:${JSON.stringify(normalizeSurfaceOpenPayload(payload))}`);
20819
+ group.get('globalAction')?.setValue({
20820
+ actionId: 'surface.open',
20821
+ payload: normalizeSurfaceOpenPayload(payload),
20822
+ });
20823
+ group.get('action')?.setValue('');
20318
20824
  }
20319
20825
  async pickIcon() {
20320
20826
  const current = this.form.value.icon;
@@ -20532,6 +21038,7 @@ class SectionEditorComponent {
20532
21038
  label: [action?.label ?? ''],
20533
21039
  icon: [action?.icon ?? ''],
20534
21040
  action: [action?.action ?? ''],
21041
+ globalAction: [action?.globalAction],
20535
21042
  tooltip: [action?.tooltip ?? ''],
20536
21043
  color: [action?.color ?? ''],
20537
21044
  visible: [action?.visible ?? true],
@@ -20575,6 +21082,7 @@ class SectionEditorComponent {
20575
21082
  icon,
20576
21083
  };
20577
21084
  const action = this.normalizeOptionalString(item?.action);
21085
+ const globalAction = this.normalizeHeaderGlobalAction(item?.globalAction);
20578
21086
  const tooltip = this.normalizeOptionalString(item?.tooltip);
20579
21087
  const normalizedColor = color === 'primary' ||
20580
21088
  color === 'accent' ||
@@ -20584,8 +21092,12 @@ class SectionEditorComponent {
20584
21092
  : undefined;
20585
21093
  const className = this.normalizeOptionalString(item?.className);
20586
21094
  const style = this.parseHeaderActionStyle(item?.styleJson ?? item?.style);
20587
- if (action !== undefined)
21095
+ if (globalAction !== undefined) {
21096
+ nextAction.globalAction = globalAction;
21097
+ }
21098
+ else if (action !== undefined) {
20588
21099
  nextAction.action = action;
21100
+ }
20589
21101
  if (tooltip !== undefined)
20590
21102
  nextAction.tooltip = tooltip;
20591
21103
  if (normalizedColor !== undefined)
@@ -20628,6 +21140,15 @@ class SectionEditorComponent {
20628
21140
  }
20629
21141
  return spec?.description || '';
20630
21142
  }
21143
+ normalizeHeaderGlobalAction(value) {
21144
+ if (!value || typeof value !== 'object')
21145
+ return undefined;
21146
+ const actionId = this.normalizeOptionalString(value.actionId);
21147
+ if (!actionId || !isGlobalActionId(actionId, this.globalActionCatalog))
21148
+ return undefined;
21149
+ const payload = value.payload;
21150
+ return payload === undefined ? { actionId } : { actionId, payload };
21151
+ }
20631
21152
  headerActionStyleValidator = (control) => {
20632
21153
  const rawValue = control.value;
20633
21154
  if (rawValue === null || rawValue === undefined || rawValue === '') {
@@ -20987,7 +21508,7 @@ class SectionEditorComponent {
20987
21508
  <mat-form-field appearance="fill">
20988
21509
  <mat-label>{{ tx('headerActions.fields.action', 'Ação') }}</mat-label>
20989
21510
  <mat-select
20990
- [ngModel]="getHeaderActionSelectValue(actionGroup.get('action')?.value)"
21511
+ [ngModel]="getHeaderActionSelectValue(actionGroup.get('action')?.value, actionGroup.get('globalAction')?.value)"
20991
21512
  (ngModelChange)="onHeaderActionSelectChange(index, $event)"
20992
21513
  [ngModelOptions]="{ standalone: true }"
20993
21514
  >
@@ -21011,23 +21532,23 @@ class SectionEditorComponent {
21011
21532
  </button>
21012
21533
  </mat-form-field>
21013
21534
 
21014
- @if (getHeaderActionSelectValue(actionGroup.get('action')?.value) === customActionValue) {
21535
+ @if (getHeaderActionSelectValue(actionGroup.get('action')?.value, actionGroup.get('globalAction')?.value) === customActionValue) {
21015
21536
  <mat-form-field appearance="fill">
21016
21537
  <mat-label>{{ tx('headerActions.fields.customAction', 'Action customizada') }}</mat-label>
21017
21538
  <input
21018
21539
  matInput
21019
- [ngModel]="getHeaderActionCustomValue(actionGroup.get('action')?.value)"
21540
+ [ngModel]="getHeaderActionCustomValue(actionGroup.get('action')?.value, actionGroup.get('globalAction')?.value)"
21020
21541
  (ngModelChange)="onHeaderActionCustomChange(index, $event)"
21021
21542
  [ngModelOptions]="{ standalone: true }"
21022
21543
  [placeholder]="tx('headerActions.placeholders.action', 'Opcional. Se vazio, usa o ID')"
21023
21544
  />
21024
21545
  </mat-form-field>
21025
- } @else if (getHeaderActionSpecById(getHeaderActionSelectValue(actionGroup.get('action')?.value)); as actionSpec) {
21026
- @if (getHeaderGlobalActionSchema(actionGroup.get('action')?.value); as schema) {
21546
+ } @else if (getHeaderActionSpecById(getHeaderActionSelectValue(actionGroup.get('action')?.value, actionGroup.get('globalAction')?.value)); as actionSpec) {
21547
+ @if (getHeaderGlobalActionSchema(actionGroup.get('action')?.value, actionGroup.get('globalAction')?.value); as schema) {
21027
21548
  @if (schema.editorMode === 'surface-open') {
21028
21549
  <div class="row-1">
21029
21550
  <praxis-surface-open-action-editor
21030
- [value]="getHeaderSurfaceOpenActionPayload(actionGroup.get('action')?.value)"
21551
+ [value]="getHeaderSurfaceOpenActionPayload(actionGroup.get('action')?.value, actionGroup.get('globalAction')?.value)"
21031
21552
  hostKind="form"
21032
21553
  (valueChange)="onHeaderSurfaceOpenActionPayloadChange(index, $event)"
21033
21554
  ></praxis-surface-open-action-editor>
@@ -21037,7 +21558,7 @@ class SectionEditorComponent {
21037
21558
  <mat-label>{{ actionSpec.param.label || tx('headerActions.fields.actionParam', 'Parâmetro') }}</mat-label>
21038
21559
  <input
21039
21560
  matInput
21040
- [ngModel]="getHeaderActionParam(actionGroup.get('action')?.value)"
21561
+ [ngModel]="getHeaderActionParam(actionGroup.get('action')?.value, actionGroup.get('globalAction')?.value)"
21041
21562
  (ngModelChange)="onHeaderActionParamChange(index, $event)"
21042
21563
  [ngModelOptions]="{ standalone: true }"
21043
21564
  [placeholder]="actionSpec.param.placeholder || ''"
@@ -21046,7 +21567,7 @@ class SectionEditorComponent {
21046
21567
  @if (actionSpec.param.hint) {
21047
21568
  <mat-hint>{{ actionSpec.param.hint }}</mat-hint>
21048
21569
  }
21049
- @if (actionSpec.param.required && isHeaderActionParamMissing(actionGroup.get('action')?.value)) {
21570
+ @if (actionSpec.param.required && isHeaderActionParamMissing(actionGroup.get('action')?.value, actionGroup.get('globalAction')?.value)) {
21050
21571
  <mat-error>{{ tx('headerActions.validation.parameterRequired', 'Parâmetro obrigatório.') }}</mat-error>
21051
21572
  }
21052
21573
  </mat-form-field>
@@ -21056,7 +21577,7 @@ class SectionEditorComponent {
21056
21577
  <mat-label>{{ actionSpec.param.label || tx('headerActions.fields.actionParam', 'Parâmetro') }}</mat-label>
21057
21578
  <input
21058
21579
  matInput
21059
- [ngModel]="getHeaderActionParam(actionGroup.get('action')?.value)"
21580
+ [ngModel]="getHeaderActionParam(actionGroup.get('action')?.value, actionGroup.get('globalAction')?.value)"
21060
21581
  (ngModelChange)="onHeaderActionParamChange(index, $event)"
21061
21582
  [ngModelOptions]="{ standalone: true }"
21062
21583
  [placeholder]="actionSpec.param.placeholder || ''"
@@ -21065,7 +21586,7 @@ class SectionEditorComponent {
21065
21586
  @if (actionSpec.param.hint) {
21066
21587
  <mat-hint>{{ actionSpec.param.hint }}</mat-hint>
21067
21588
  }
21068
- @if (actionSpec.param.required && isHeaderActionParamMissing(actionGroup.get('action')?.value)) {
21589
+ @if (actionSpec.param.required && isHeaderActionParamMissing(actionGroup.get('action')?.value, actionGroup.get('globalAction')?.value)) {
21069
21590
  <mat-error>{{ tx('headerActions.validation.parameterRequired', 'Parâmetro obrigatório.') }}</mat-error>
21070
21591
  }
21071
21592
  </mat-form-field>
@@ -21078,7 +21599,7 @@ class SectionEditorComponent {
21078
21599
  </mat-form-field>
21079
21600
  </div>
21080
21601
 
21081
- @if (getHeaderActionSelectValue(actionGroup.get('action')?.value) !== customActionValue) {
21602
+ @if (getHeaderActionSelectValue(actionGroup.get('action')?.value, actionGroup.get('globalAction')?.value) !== customActionValue) {
21082
21603
  <div class="header-action-callout">
21083
21604
  <div class="header-action-callout-title">
21084
21605
  {{ tx('headerActions.callout.globalTitle', 'Ação global do app') }}
@@ -21659,7 +22180,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
21659
22180
  <mat-form-field appearance="fill">
21660
22181
  <mat-label>{{ tx('headerActions.fields.action', 'Ação') }}</mat-label>
21661
22182
  <mat-select
21662
- [ngModel]="getHeaderActionSelectValue(actionGroup.get('action')?.value)"
22183
+ [ngModel]="getHeaderActionSelectValue(actionGroup.get('action')?.value, actionGroup.get('globalAction')?.value)"
21663
22184
  (ngModelChange)="onHeaderActionSelectChange(index, $event)"
21664
22185
  [ngModelOptions]="{ standalone: true }"
21665
22186
  >
@@ -21683,23 +22204,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
21683
22204
  </button>
21684
22205
  </mat-form-field>
21685
22206
 
21686
- @if (getHeaderActionSelectValue(actionGroup.get('action')?.value) === customActionValue) {
22207
+ @if (getHeaderActionSelectValue(actionGroup.get('action')?.value, actionGroup.get('globalAction')?.value) === customActionValue) {
21687
22208
  <mat-form-field appearance="fill">
21688
22209
  <mat-label>{{ tx('headerActions.fields.customAction', 'Action customizada') }}</mat-label>
21689
22210
  <input
21690
22211
  matInput
21691
- [ngModel]="getHeaderActionCustomValue(actionGroup.get('action')?.value)"
22212
+ [ngModel]="getHeaderActionCustomValue(actionGroup.get('action')?.value, actionGroup.get('globalAction')?.value)"
21692
22213
  (ngModelChange)="onHeaderActionCustomChange(index, $event)"
21693
22214
  [ngModelOptions]="{ standalone: true }"
21694
22215
  [placeholder]="tx('headerActions.placeholders.action', 'Opcional. Se vazio, usa o ID')"
21695
22216
  />
21696
22217
  </mat-form-field>
21697
- } @else if (getHeaderActionSpecById(getHeaderActionSelectValue(actionGroup.get('action')?.value)); as actionSpec) {
21698
- @if (getHeaderGlobalActionSchema(actionGroup.get('action')?.value); as schema) {
22218
+ } @else if (getHeaderActionSpecById(getHeaderActionSelectValue(actionGroup.get('action')?.value, actionGroup.get('globalAction')?.value)); as actionSpec) {
22219
+ @if (getHeaderGlobalActionSchema(actionGroup.get('action')?.value, actionGroup.get('globalAction')?.value); as schema) {
21699
22220
  @if (schema.editorMode === 'surface-open') {
21700
22221
  <div class="row-1">
21701
22222
  <praxis-surface-open-action-editor
21702
- [value]="getHeaderSurfaceOpenActionPayload(actionGroup.get('action')?.value)"
22223
+ [value]="getHeaderSurfaceOpenActionPayload(actionGroup.get('action')?.value, actionGroup.get('globalAction')?.value)"
21703
22224
  hostKind="form"
21704
22225
  (valueChange)="onHeaderSurfaceOpenActionPayloadChange(index, $event)"
21705
22226
  ></praxis-surface-open-action-editor>
@@ -21709,7 +22230,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
21709
22230
  <mat-label>{{ actionSpec.param.label || tx('headerActions.fields.actionParam', 'Parâmetro') }}</mat-label>
21710
22231
  <input
21711
22232
  matInput
21712
- [ngModel]="getHeaderActionParam(actionGroup.get('action')?.value)"
22233
+ [ngModel]="getHeaderActionParam(actionGroup.get('action')?.value, actionGroup.get('globalAction')?.value)"
21713
22234
  (ngModelChange)="onHeaderActionParamChange(index, $event)"
21714
22235
  [ngModelOptions]="{ standalone: true }"
21715
22236
  [placeholder]="actionSpec.param.placeholder || ''"
@@ -21718,7 +22239,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
21718
22239
  @if (actionSpec.param.hint) {
21719
22240
  <mat-hint>{{ actionSpec.param.hint }}</mat-hint>
21720
22241
  }
21721
- @if (actionSpec.param.required && isHeaderActionParamMissing(actionGroup.get('action')?.value)) {
22242
+ @if (actionSpec.param.required && isHeaderActionParamMissing(actionGroup.get('action')?.value, actionGroup.get('globalAction')?.value)) {
21722
22243
  <mat-error>{{ tx('headerActions.validation.parameterRequired', 'Parâmetro obrigatório.') }}</mat-error>
21723
22244
  }
21724
22245
  </mat-form-field>
@@ -21728,7 +22249,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
21728
22249
  <mat-label>{{ actionSpec.param.label || tx('headerActions.fields.actionParam', 'Parâmetro') }}</mat-label>
21729
22250
  <input
21730
22251
  matInput
21731
- [ngModel]="getHeaderActionParam(actionGroup.get('action')?.value)"
22252
+ [ngModel]="getHeaderActionParam(actionGroup.get('action')?.value, actionGroup.get('globalAction')?.value)"
21732
22253
  (ngModelChange)="onHeaderActionParamChange(index, $event)"
21733
22254
  [ngModelOptions]="{ standalone: true }"
21734
22255
  [placeholder]="actionSpec.param.placeholder || ''"
@@ -21737,7 +22258,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
21737
22258
  @if (actionSpec.param.hint) {
21738
22259
  <mat-hint>{{ actionSpec.param.hint }}</mat-hint>
21739
22260
  }
21740
- @if (actionSpec.param.required && isHeaderActionParamMissing(actionGroup.get('action')?.value)) {
22261
+ @if (actionSpec.param.required && isHeaderActionParamMissing(actionGroup.get('action')?.value, actionGroup.get('globalAction')?.value)) {
21741
22262
  <mat-error>{{ tx('headerActions.validation.parameterRequired', 'Parâmetro obrigatório.') }}</mat-error>
21742
22263
  }
21743
22264
  </mat-form-field>
@@ -21750,7 +22271,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
21750
22271
  </mat-form-field>
21751
22272
  </div>
21752
22273
 
21753
- @if (getHeaderActionSelectValue(actionGroup.get('action')?.value) !== customActionValue) {
22274
+ @if (getHeaderActionSelectValue(actionGroup.get('action')?.value, actionGroup.get('globalAction')?.value) !== customActionValue) {
21754
22275
  <div class="header-action-callout">
21755
22276
  <div class="header-action-callout-title">
21756
22277
  {{ tx('headerActions.callout.globalTitle', 'Ação global do app') }}
@@ -23114,6 +23635,12 @@ const FORM_COMPONENT_AI_CAPABILITIES = {
23114
23635
  valueKind: 'string',
23115
23636
  description: 'ID do registro atual (string ou number).',
23116
23637
  },
23638
+ {
23639
+ path: 'inputs.initialValue',
23640
+ category: 'inputs',
23641
+ valueKind: 'object',
23642
+ description: 'Valores iniciais locais para campos host-authored ou complementares.',
23643
+ },
23117
23644
  {
23118
23645
  path: 'inputs.mode',
23119
23646
  category: 'inputs',
@@ -23514,6 +24041,787 @@ const FORM_COMPONENT_AI_CAPABILITIES = {
23514
24041
  ],
23515
24042
  };
23516
24043
 
24044
+ /**
24045
+ * Manifesto de authoring canônico para o componente praxis-dynamic-form.
24046
+ * Este arquivo define as operações permitidas pela IA para editar formulários.
24047
+ *
24048
+ * @version 1.4.0
24049
+ * @status COMPLIANT - Alinhado com contrato v2, gate de aceitação e semântica local/schema-backed.
24050
+ */
24051
+ const PRAXIS_DYNAMIC_FORM_AUTHORING_MANIFEST = {
24052
+ schemaVersion: '1.0.0',
24053
+ componentId: 'praxis-dynamic-form',
24054
+ ownerPackage: '@praxisui/dynamic-form',
24055
+ configSchemaId: 'FormConfig',
24056
+ manifestVersion: '1.4.0',
24057
+ runtimeInputs: [
24058
+ { name: 'config', type: 'FormConfig', description: 'Configuração completa do formulário' },
24059
+ { name: 'resourcePath', type: 'string', description: 'Caminho do recurso (API)' },
24060
+ { name: 'mode', type: 'string', allowedValues: ['create', 'edit', 'view'], description: 'Modo de operação' },
24061
+ { name: 'enableCustomization', type: 'boolean', description: 'Habilita edição visual' }
24062
+ ],
24063
+ editableTargets: [
24064
+ /**
24065
+ * 'field' é o target genérico para operações de metadado (label, placeholder, etc.).
24066
+ * Para distinguir schema-backed de local, use 'schemaBackedField' ou 'localField'.
24067
+ */
24068
+ { kind: 'field', resolver: 'field-by-name-or-label', description: 'Campos do formulário (metadados schema-backed ou local; use para ops de propriedade)' },
24069
+ { kind: 'schemaBackedField', resolver: 'field-by-name-or-label', description: 'Campos com origem no schema do backend; não removíveis via authoring' },
24070
+ { kind: 'localField', resolver: 'field-by-name', description: 'Campos locais com source:"local"; adicionados/removidos via authoring' },
24071
+ { kind: 'section', resolver: 'section-by-id-or-title', description: 'Seções de layout identificadas por id estável' },
24072
+ { kind: 'row', resolver: 'row-by-id-in-section', description: 'Linhas dentro de seções, identificadas por id estável' },
24073
+ { kind: 'column', resolver: 'column-by-id-in-row', description: 'Colunas dentro de linhas, identificadas por id estável' },
24074
+ /**
24075
+ * 'layoutPlacement' representa a posição de um campo no layout (seção+linha+coluna).
24076
+ * Usado conceitualmente por layout.field.move; declarado como target para futuras ops
24077
+ * de reposicionamento granular (ex: layout.placement.swap).
24078
+ */
24079
+ { kind: 'layoutPlacement', resolver: 'layout-placement-by-field', description: 'Posicionamento de campo no layout; target para ops de reposicionamento' },
24080
+ { kind: 'formAction', resolver: 'action-by-id-or-label', description: 'Ações da toolbar (submit, cancel, reset)' },
24081
+ { kind: 'formRule', resolver: 'rule-by-id-or-name', description: 'Regras de visibilidade e validação' },
24082
+ /**
24083
+ * 'message' declara suporte a mensagens inline de formulário (info, warning, error).
24084
+ * Declarado aqui para conformidade com o gate; ops de criação/remoção de messages
24085
+ * serão adicionadas na próxima versão do manifesto.
24086
+ */
24087
+ { kind: 'message', resolver: 'message-by-id', description: 'Mensagens inline do formulário (info, warning, error); ops em desenvolvimento' }
24088
+ ],
24089
+ operations: [
24090
+ // ─── FIELD PROPERTY OPERATIONS ───────────────────────────────────────────
24091
+ {
24092
+ operationId: 'field.label.set',
24093
+ title: 'Alterar rótulo do campo',
24094
+ scope: 'field',
24095
+ targetKind: 'field',
24096
+ target: { kind: 'field', resolver: 'field-by-name-or-label', ambiguityPolicy: 'fail', required: true },
24097
+ inputSchema: {
24098
+ type: 'object',
24099
+ required: ['label'],
24100
+ properties: { label: { type: 'string', minLength: 1 } }
24101
+ },
24102
+ effects: [{ kind: 'merge-by-key', path: 'fieldMetadata[]', key: 'name' }],
24103
+ validators: ['field-exists'],
24104
+ affectedPaths: ['fieldMetadata[].label'],
24105
+ submissionImpact: false,
24106
+ preconditions: ['config-initialized', 'target-exists']
24107
+ },
24108
+ {
24109
+ operationId: 'field.placeholder.set',
24110
+ title: 'Alterar placeholder do campo',
24111
+ scope: 'field',
24112
+ targetKind: 'field',
24113
+ target: { kind: 'field', resolver: 'field-by-name-or-label', ambiguityPolicy: 'fail', required: true },
24114
+ inputSchema: {
24115
+ type: 'object',
24116
+ required: ['placeholder'],
24117
+ properties: { placeholder: { type: 'string' } }
24118
+ },
24119
+ effects: [{ kind: 'merge-by-key', path: 'fieldMetadata[]', key: 'name' }],
24120
+ validators: ['field-exists'],
24121
+ affectedPaths: ['fieldMetadata[].placeholder'],
24122
+ submissionImpact: false,
24123
+ preconditions: ['config-initialized', 'target-exists']
24124
+ },
24125
+ {
24126
+ operationId: 'field.required.set',
24127
+ title: 'Definir obrigatoriedade',
24128
+ scope: 'field',
24129
+ targetKind: 'field',
24130
+ target: { kind: 'field', resolver: 'field-by-name-or-label', ambiguityPolicy: 'fail', required: true },
24131
+ inputSchema: {
24132
+ type: 'object',
24133
+ required: ['required'],
24134
+ properties: { required: { type: 'boolean' } }
24135
+ },
24136
+ effects: [{ kind: 'merge-by-key', path: 'fieldMetadata[]', key: 'name' }],
24137
+ validators: ['field-exists'],
24138
+ affectedPaths: ['fieldMetadata[].required'],
24139
+ submissionImpact: false,
24140
+ preconditions: ['config-initialized', 'target-exists']
24141
+ },
24142
+ {
24143
+ operationId: 'field.readOnly.set',
24144
+ title: 'Definir campo como somente leitura',
24145
+ scope: 'field',
24146
+ targetKind: 'field',
24147
+ target: { kind: 'field', resolver: 'field-by-name-or-label', ambiguityPolicy: 'fail', required: true },
24148
+ inputSchema: {
24149
+ type: 'object',
24150
+ required: ['readOnly'],
24151
+ properties: { readOnly: { type: 'boolean' } }
24152
+ },
24153
+ effects: [{ kind: 'merge-by-key', path: 'fieldMetadata[]', key: 'name' }],
24154
+ validators: ['field-exists'],
24155
+ affectedPaths: ['fieldMetadata[].readOnly'],
24156
+ submissionImpact: false,
24157
+ preconditions: ['config-initialized', 'target-exists']
24158
+ },
24159
+ {
24160
+ operationId: 'field.disabled.set',
24161
+ title: 'Definir campo como desabilitado',
24162
+ scope: 'field',
24163
+ targetKind: 'field',
24164
+ target: { kind: 'field', resolver: 'field-by-name-or-label', ambiguityPolicy: 'fail', required: true },
24165
+ inputSchema: {
24166
+ type: 'object',
24167
+ required: ['disabled'],
24168
+ properties: { disabled: { type: 'boolean' } }
24169
+ },
24170
+ effects: [{ kind: 'merge-by-key', path: 'fieldMetadata[]', key: 'name' }],
24171
+ validators: ['field-exists'],
24172
+ affectedPaths: ['fieldMetadata[].disabled'],
24173
+ submissionImpact: false,
24174
+ preconditions: ['config-initialized', 'target-exists']
24175
+ },
24176
+ {
24177
+ operationId: 'field.hidden.set',
24178
+ title: 'Definir visibilidade inicial',
24179
+ scope: 'field',
24180
+ targetKind: 'field',
24181
+ target: { kind: 'field', resolver: 'field-by-name-or-label', ambiguityPolicy: 'fail', required: true },
24182
+ inputSchema: {
24183
+ type: 'object',
24184
+ required: ['hidden'],
24185
+ properties: { hidden: { type: 'boolean' } }
24186
+ },
24187
+ effects: [{ kind: 'merge-by-key', path: 'fieldMetadata[]', key: 'name' }],
24188
+ validators: ['field-exists'],
24189
+ affectedPaths: ['fieldMetadata[].hidden'],
24190
+ submissionImpact: false,
24191
+ preconditions: ['config-initialized', 'target-exists']
24192
+ },
24193
+ {
24194
+ operationId: 'field.controlType.set',
24195
+ title: 'Alterar tipo de controle',
24196
+ scope: 'field',
24197
+ targetKind: 'field',
24198
+ target: { kind: 'field', resolver: 'field-by-name-or-label', ambiguityPolicy: 'fail', required: true },
24199
+ inputSchema: {
24200
+ type: 'object',
24201
+ required: ['controlType'],
24202
+ properties: { controlType: { type: 'string' } }
24203
+ },
24204
+ effects: [{ kind: 'merge-by-key', path: 'fieldMetadata[]', key: 'name' }],
24205
+ destructive: true,
24206
+ requiresConfirmation: true,
24207
+ validators: ['field-exists', 'control-type-compatible'],
24208
+ affectedPaths: ['fieldMetadata[].controlType'],
24209
+ submissionImpact: false,
24210
+ preconditions: ['config-initialized', 'target-exists']
24211
+ },
24212
+ {
24213
+ operationId: 'field.optionSource.set',
24214
+ title: 'Definir fonte de opções do campo',
24215
+ scope: 'field',
24216
+ targetKind: 'field',
24217
+ target: { kind: 'field', resolver: 'field-by-name-or-label', ambiguityPolicy: 'fail', required: true },
24218
+ inputSchema: {
24219
+ type: 'object',
24220
+ required: ['optionSource'],
24221
+ properties: {
24222
+ optionSource: {
24223
+ type: 'object',
24224
+ required: ['type'],
24225
+ properties: {
24226
+ type: { enum: ['static', 'remote', 'context'] },
24227
+ items: { type: 'array', description: 'Opções estáticas (type: static)' },
24228
+ resourcePath: { type: 'string', description: 'URL da API (type: remote)' },
24229
+ contextKey: { type: 'string', description: 'Chave de contexto (type: context)' }
24230
+ },
24231
+ if: { properties: { type: { const: 'static' } } },
24232
+ then: { required: ['items'] },
24233
+ else: {
24234
+ if: { properties: { type: { const: 'remote' } } },
24235
+ then: { required: ['resourcePath'] },
24236
+ else: { required: ['contextKey'] }
24237
+ }
24238
+ }
24239
+ }
24240
+ },
24241
+ effects: [{ kind: 'merge-by-key', path: 'fieldMetadata[]', key: 'name' }],
24242
+ validators: ['field-exists', 'option-source-valid'],
24243
+ affectedPaths: ['fieldMetadata[].optionSource'],
24244
+ submissionImpact: false,
24245
+ preconditions: ['config-initialized', 'target-exists']
24246
+ },
24247
+ // ─── LOCAL FIELD OPERATIONS ───────────────────────────────────────────────
24248
+ {
24249
+ operationId: 'field.local.add',
24250
+ title: 'Adicionar campo local',
24251
+ scope: 'global',
24252
+ targetKind: 'localField',
24253
+ target: { kind: 'localField', resolver: 'field-by-name', ambiguityPolicy: 'fail', required: false },
24254
+ inputSchema: {
24255
+ type: 'object',
24256
+ required: ['name', 'label', 'controlType', 'source'],
24257
+ properties: {
24258
+ name: { type: 'string' },
24259
+ label: { type: 'string' },
24260
+ controlType: { type: 'string' },
24261
+ /**
24262
+ * source:'local' é OBRIGATÓRIO e distingue campos locais de campos schema-backed.
24263
+ * O backend usa este discriminador para aplicar regras de ciclo de vida diferentes.
24264
+ */
24265
+ source: { const: 'local', description: 'Obrigatório. Discriminador que distingue campos locais de schema-backed.' },
24266
+ /**
24267
+ * transient:true significa que o valor não deve persistir entre navegações nem
24268
+ * ser incluído em snapshots. É independente de submitPolicy.
24269
+ */
24270
+ transient: { type: 'boolean', default: false, description: 'Se true, o valor não persiste entre navegações.' },
24271
+ /**
24272
+ * submitPolicy controla como o valor é tratado no payload de submissão.
24273
+ * VALORES VÁLIDOS: 'omit' | 'include' | 'includeWhenDirty'
24274
+ * NOTA: 'transient' NÃO é um valor válido aqui — use a propriedade transient:boolean acima.
24275
+ */
24276
+ submitPolicy: { enum: ['omit', 'include', 'includeWhenDirty'], default: 'omit' }
24277
+ }
24278
+ },
24279
+ effects: [{ kind: 'append-unique', path: 'fieldMetadata[]', key: 'name' }],
24280
+ validators: ['field-name-unique', 'local-schema-name-no-collision'],
24281
+ affectedPaths: ['fieldMetadata[]'],
24282
+ submissionImpact: true,
24283
+ preconditions: ['config-initialized']
24284
+ },
24285
+ {
24286
+ operationId: 'field.local.remove',
24287
+ title: 'Remover campo local',
24288
+ scope: 'field',
24289
+ targetKind: 'localField',
24290
+ target: { kind: 'localField', resolver: 'field-by-name', ambiguityPolicy: 'fail', required: true },
24291
+ inputSchema: { type: 'object', properties: {} },
24292
+ effects: [
24293
+ { kind: 'remove-by-key', path: 'fieldMetadata[]', key: 'name' },
24294
+ /**
24295
+ * Efeito cascata: remove a referência do campo de todos os slots de layout.
24296
+ * Sem isso, o layout mantém referências órfãs que causam erros de renderização.
24297
+ */
24298
+ {
24299
+ kind: 'compile-domain-patch',
24300
+ handler: 'form-layout-field-cleanup',
24301
+ handlerContract: {
24302
+ reads: ['fieldMetadata[].name', 'sections[].rows[].columns[].fields'],
24303
+ writes: ['sections[].rows[].columns[].fields'],
24304
+ identityKeys: ['fieldMetadata[].name', 'sections[].id', 'sections[].rows[].id', 'sections[].rows[].columns[].id'],
24305
+ inputSchema: { type: 'object', required: ['target.name'], properties: { 'target.name': { type: 'string' } } },
24306
+ failureModes: ['target-field-not-found', 'target-field-not-local', 'layout-reference-conflict'],
24307
+ description: 'Remove every layout placement that references the removed local field by fieldMetadata[].name.'
24308
+ }
24309
+ }
24310
+ ],
24311
+ destructive: true,
24312
+ requiresConfirmation: true,
24313
+ validators: ['field-exists', 'field-is-local'],
24314
+ affectedPaths: ['fieldMetadata[]', 'sections[].rows[].columns[].fields'],
24315
+ submissionImpact: true,
24316
+ preconditions: ['config-initialized', 'target-exists']
24317
+ },
24318
+ {
24319
+ operationId: 'field.submitPolicy.set',
24320
+ title: 'Definir política de submissão do campo',
24321
+ scope: 'field',
24322
+ targetKind: 'localField',
24323
+ target: { kind: 'localField', resolver: 'field-by-name', ambiguityPolicy: 'fail', required: true },
24324
+ inputSchema: {
24325
+ type: 'object',
24326
+ required: ['submitPolicy'],
24327
+ properties: {
24328
+ submitPolicy: { enum: ['omit', 'include', 'includeWhenDirty'] }
24329
+ }
24330
+ },
24331
+ effects: [{ kind: 'merge-by-key', path: 'fieldMetadata[]', key: 'name' }],
24332
+ validators: ['field-exists', 'field-is-local', 'editor-round-trip-preserve'],
24333
+ affectedPaths: ['fieldMetadata[].submitPolicy'],
24334
+ submissionImpact: true,
24335
+ preconditions: ['config-initialized', 'target-exists']
24336
+ },
24337
+ // ─── LAYOUT OPERATIONS ────────────────────────────────────────────────────
24338
+ {
24339
+ operationId: 'layout.section.add',
24340
+ title: 'Adicionar nova seção',
24341
+ scope: 'global',
24342
+ targetKind: 'section',
24343
+ target: { kind: 'section', resolver: 'section-by-id-or-title', ambiguityPolicy: 'fail', required: false },
24344
+ inputSchema: {
24345
+ type: 'object',
24346
+ required: ['id', 'title'],
24347
+ properties: {
24348
+ id: { type: 'string' },
24349
+ title: { type: 'string' },
24350
+ icon: { type: 'string' },
24351
+ collapsible: { type: 'boolean' }
24352
+ }
24353
+ },
24354
+ effects: [{ kind: 'append-unique', path: 'sections[]', key: 'id' }],
24355
+ validators: ['section-id-unique'],
24356
+ affectedPaths: ['sections[]'],
24357
+ submissionImpact: false,
24358
+ preconditions: ['config-initialized']
24359
+ },
24360
+ {
24361
+ operationId: 'layout.section.remove',
24362
+ title: 'Remover seção',
24363
+ scope: 'section',
24364
+ targetKind: 'section',
24365
+ target: { kind: 'section', resolver: 'section-by-id-or-title', ambiguityPolicy: 'fail', required: true },
24366
+ inputSchema: { type: 'object', properties: {} },
24367
+ effects: [
24368
+ { kind: 'remove-by-key', path: 'sections[]', key: 'id' },
24369
+ /**
24370
+ * Cascata: remove todas as linhas, colunas e referências de campos da seção excluída.
24371
+ * Os registros em fieldMetadata NÃO são removidos — apenas as posições de layout.
24372
+ * O validator field-exists-in-layout sinalizará campos sem posição após esta operação.
24373
+ */
24374
+ {
24375
+ kind: 'compile-domain-patch',
24376
+ handler: 'form-layout-section-cleanup',
24377
+ handlerContract: {
24378
+ reads: ['sections[].id', 'sections[].rows[].id', 'sections[].rows[].columns[].id', 'sections[].rows[].columns[].fields'],
24379
+ writes: ['sections[]'],
24380
+ identityKeys: ['sections[].id'],
24381
+ inputSchema: { type: 'object', required: ['target.id'], properties: { 'target.id': { type: 'string' } } },
24382
+ failureModes: ['target-section-not-found', 'ambiguous-section-target'],
24383
+ description: 'Removes the target section and its nested rows, columns, and field placements without deleting fieldMetadata records.'
24384
+ }
24385
+ }
24386
+ ],
24387
+ destructive: true,
24388
+ requiresConfirmation: true,
24389
+ validators: ['section-exists'],
24390
+ affectedPaths: ['sections[]', 'sections[].rows[]', 'sections[].rows[].columns[]', 'sections[].rows[].columns[].fields'],
24391
+ submissionImpact: false,
24392
+ preconditions: ['config-initialized', 'target-exists']
24393
+ },
24394
+ {
24395
+ operationId: 'layout.row.add',
24396
+ title: 'Adicionar linha numa seção',
24397
+ scope: 'section',
24398
+ targetKind: 'section',
24399
+ target: { kind: 'section', resolver: 'section-by-id-or-title', ambiguityPolicy: 'fail', required: true },
24400
+ inputSchema: {
24401
+ type: 'object',
24402
+ required: ['id'],
24403
+ properties: {
24404
+ id: { type: 'string' },
24405
+ columns: { type: 'number', minimum: 1, maximum: 12, default: 1 }
24406
+ }
24407
+ },
24408
+ effects: [{ kind: 'append-unique', path: 'sections[].rows[]', key: 'id' }],
24409
+ validators: ['section-exists', 'row-id-unique-in-section'],
24410
+ affectedPaths: ['sections[].rows[]'],
24411
+ submissionImpact: false,
24412
+ preconditions: ['config-initialized', 'target-exists']
24413
+ },
24414
+ {
24415
+ operationId: 'layout.column.add',
24416
+ title: 'Adicionar coluna numa linha',
24417
+ scope: 'row',
24418
+ targetKind: 'row',
24419
+ target: { kind: 'row', resolver: 'row-by-id-in-section', ambiguityPolicy: 'fail', required: true },
24420
+ inputSchema: {
24421
+ type: 'object',
24422
+ required: ['id'],
24423
+ properties: {
24424
+ id: { type: 'string' },
24425
+ span: { type: 'number', minimum: 1, maximum: 12, default: 6 }
24426
+ }
24427
+ },
24428
+ effects: [{ kind: 'append-unique', path: 'sections[].rows[].columns[]', key: 'id' }],
24429
+ validators: ['row-exists', 'column-id-unique-in-row'],
24430
+ affectedPaths: ['sections[].rows[].columns[]'],
24431
+ submissionImpact: false,
24432
+ preconditions: ['config-initialized', 'target-exists']
24433
+ },
24434
+ {
24435
+ operationId: 'layout.field.move',
24436
+ title: 'Mover campo no layout',
24437
+ scope: 'field',
24438
+ targetKind: 'field',
24439
+ target: { kind: 'field', resolver: 'field-by-name-or-label', ambiguityPolicy: 'fail', required: true },
24440
+ inputSchema: {
24441
+ type: 'object',
24442
+ required: ['targetSectionId', 'targetRowId', 'targetColumnId'],
24443
+ properties: {
24444
+ targetSectionId: { type: 'string' },
24445
+ targetRowId: { type: 'string' },
24446
+ targetColumnId: { type: 'string' },
24447
+ index: { type: 'number' }
24448
+ }
24449
+ },
24450
+ effects: [{
24451
+ kind: 'compile-domain-patch',
24452
+ handler: 'form-layout-reconciler',
24453
+ handlerContract: {
24454
+ reads: [
24455
+ 'fieldMetadata[].name',
24456
+ 'sections[].id',
24457
+ 'sections[].rows[].id',
24458
+ 'sections[].rows[].columns[].id',
24459
+ 'sections[].rows[].columns[].fields'
24460
+ ],
24461
+ writes: ['sections[].rows[].columns[].fields'],
24462
+ identityKeys: ['fieldMetadata[].name', 'sections[].id', 'sections[].rows[].id', 'sections[].rows[].columns[].id'],
24463
+ inputSchema: {
24464
+ type: 'object',
24465
+ required: ['target.name', 'targetSectionId', 'targetRowId', 'targetColumnId'],
24466
+ properties: {
24467
+ 'target.name': { type: 'string' },
24468
+ targetSectionId: { type: 'string' },
24469
+ targetRowId: { type: 'string' },
24470
+ targetColumnId: { type: 'string' },
24471
+ index: { type: 'number' }
24472
+ }
24473
+ },
24474
+ failureModes: ['target-field-not-found', 'target-layout-location-not-found', 'ambiguous-field-target', 'duplicate-field-placement'],
24475
+ description: 'Moves a field placement by stable section, row, column, and field ids; index is insertion position only.'
24476
+ }
24477
+ }],
24478
+ validators: ['field-exists', 'layout-target-exists', 'no-index-as-identity', 'field-exists-in-layout'],
24479
+ affectedPaths: ['sections[].rows[].columns[].fields'],
24480
+ submissionImpact: false,
24481
+ preconditions: ['config-initialized', 'target-exists']
24482
+ },
24483
+ // ─── RULE OPERATIONS ──────────────────────────────────────────────────────
24484
+ {
24485
+ operationId: 'rule.visibility.add',
24486
+ title: 'Adicionar regra de visibilidade',
24487
+ scope: 'global',
24488
+ targetKind: 'formRule',
24489
+ target: { kind: 'formRule', resolver: 'rule-by-id-or-name', ambiguityPolicy: 'fail', required: false },
24490
+ inputSchema: {
24491
+ type: 'object',
24492
+ required: ['id', 'type', 'targets', 'condition'],
24493
+ properties: {
24494
+ id: { type: 'string' },
24495
+ name: { type: 'string' },
24496
+ /** Discriminador de tipo armazenado no item formRules[]; distingue regras de visibilidade das de validação. */
24497
+ type: { const: 'visibility', description: 'Discriminador de tipo da regra; obrigatório para distinção em formRules[]' },
24498
+ targetType: { enum: ['field', 'section', 'row', 'column', 'action'], default: 'field' },
24499
+ targets: { type: 'array', items: { type: 'string' }, minItems: 1 },
24500
+ condition: { type: 'object', description: 'JsonLogic AST' }
24501
+ }
24502
+ },
24503
+ effects: [{ kind: 'append-unique', path: 'formRules[]', key: 'id' }],
24504
+ validators: ['rule-id-unique', 'rule-target-refs-exist', 'json-logic-valid'],
24505
+ affectedPaths: ['formRules[]'],
24506
+ submissionImpact: false,
24507
+ preconditions: ['config-initialized']
24508
+ },
24509
+ {
24510
+ operationId: 'rule.validation.add',
24511
+ title: 'Adicionar regra de validação',
24512
+ scope: 'global',
24513
+ targetKind: 'formRule',
24514
+ target: { kind: 'formRule', resolver: 'rule-by-id-or-name', ambiguityPolicy: 'fail', required: false },
24515
+ inputSchema: {
24516
+ type: 'object',
24517
+ required: ['id', 'type', 'targets', 'condition', 'message'],
24518
+ properties: {
24519
+ id: { type: 'string' },
24520
+ name: { type: 'string' },
24521
+ /** Discriminador de tipo armazenado no item formRules[]; distingue regras de validação das de visibilidade. */
24522
+ type: { const: 'validation', description: 'Discriminador de tipo da regra; obrigatório para distinção em formRules[]' },
24523
+ /**
24524
+ * targetType espelha o campo equivalente de rule.visibility.add.
24525
+ * Regras de validação podem se aplicar a campos, seções ou ações da toolbar.
24526
+ */
24527
+ targetType: { enum: ['field', 'section', 'row', 'column', 'action'], default: 'field' },
24528
+ targets: { type: 'array', items: { type: 'string' }, minItems: 1 },
24529
+ condition: { type: 'object', description: 'JsonLogic AST; quando true, exibe mensagem de erro' },
24530
+ message: { type: 'string', description: 'Mensagem de erro a exibir ao usuário' },
24531
+ level: { enum: ['error', 'warning'], default: 'error' }
24532
+ }
24533
+ },
24534
+ effects: [{ kind: 'append-unique', path: 'formRules[]', key: 'id' }],
24535
+ validators: ['rule-id-unique', 'rule-target-refs-exist', 'json-logic-valid'],
24536
+ affectedPaths: ['formRules[]'],
24537
+ submissionImpact: false,
24538
+ preconditions: ['config-initialized']
24539
+ },
24540
+ {
24541
+ operationId: 'rule.remove',
24542
+ title: 'Remover regra',
24543
+ scope: 'rule',
24544
+ targetKind: 'formRule',
24545
+ target: { kind: 'formRule', resolver: 'rule-by-id-or-name', ambiguityPolicy: 'fail', required: true },
24546
+ inputSchema: { type: 'object', properties: {} },
24547
+ effects: [{ kind: 'remove-by-key', path: 'formRules[]', key: 'id' }],
24548
+ destructive: true,
24549
+ requiresConfirmation: true,
24550
+ validators: ['rule-exists'],
24551
+ affectedPaths: ['formRules[]'],
24552
+ submissionImpact: false,
24553
+ preconditions: ['config-initialized', 'target-exists']
24554
+ },
24555
+ // ─── ACTION OPERATIONS ────────────────────────────────────────────────────
24556
+ {
24557
+ operationId: 'action.submit.configure',
24558
+ title: 'Configurar botão de envio',
24559
+ scope: 'global',
24560
+ targetKind: 'formAction',
24561
+ target: { kind: 'formAction', resolver: 'action-by-id-or-label', ambiguityPolicy: 'fail', required: false },
24562
+ inputSchema: {
24563
+ type: 'object',
24564
+ minProperties: 1,
24565
+ properties: {
24566
+ label: { type: 'string' },
24567
+ icon: { type: 'string' },
24568
+ visible: { type: 'boolean' },
24569
+ color: { enum: ['primary', 'accent', 'warn', 'basic'] }
24570
+ }
24571
+ },
24572
+ effects: [{ kind: 'merge-object', path: 'actions.submit' }],
24573
+ validators: ['editor-round-trip-preserve'],
24574
+ affectedPaths: ['actions.submit'],
24575
+ submissionImpact: false,
24576
+ preconditions: ['config-initialized']
24577
+ },
24578
+ {
24579
+ operationId: 'action.cancel.configure',
24580
+ title: 'Configurar botão de cancelar',
24581
+ scope: 'global',
24582
+ targetKind: 'formAction',
24583
+ target: { kind: 'formAction', resolver: 'action-by-id-or-label', ambiguityPolicy: 'fail', required: false },
24584
+ inputSchema: {
24585
+ type: 'object',
24586
+ minProperties: 1,
24587
+ properties: {
24588
+ label: { type: 'string' },
24589
+ icon: { type: 'string' },
24590
+ visible: { type: 'boolean' },
24591
+ color: { enum: ['primary', 'accent', 'warn', 'basic'] },
24592
+ navigateTo: { type: 'string' }
24593
+ }
24594
+ },
24595
+ effects: [{ kind: 'merge-object', path: 'actions.cancel' }],
24596
+ validators: ['editor-round-trip-preserve'],
24597
+ affectedPaths: ['actions.cancel'],
24598
+ submissionImpact: false,
24599
+ preconditions: ['config-initialized']
24600
+ },
24601
+ {
24602
+ operationId: 'action.reset.configure',
24603
+ title: 'Configurar botão de reset',
24604
+ scope: 'global',
24605
+ targetKind: 'formAction',
24606
+ target: { kind: 'formAction', resolver: 'action-by-id-or-label', ambiguityPolicy: 'fail', required: false },
24607
+ inputSchema: {
24608
+ type: 'object',
24609
+ minProperties: 1,
24610
+ properties: {
24611
+ label: { type: 'string' },
24612
+ icon: { type: 'string' },
24613
+ visible: { type: 'boolean' },
24614
+ color: { enum: ['primary', 'accent', 'warn', 'basic'] },
24615
+ confirmBeforeReset: { type: 'boolean' }
24616
+ }
24617
+ },
24618
+ effects: [{ kind: 'merge-object', path: 'actions.reset' }],
24619
+ validators: ['editor-round-trip-preserve'],
24620
+ affectedPaths: ['actions.reset'],
24621
+ submissionImpact: false,
24622
+ preconditions: ['config-initialized']
24623
+ }
24624
+ ],
24625
+ validators: [
24626
+ {
24627
+ validatorId: 'field-exists',
24628
+ level: 'error',
24629
+ code: 'DF001',
24630
+ description: 'O campo alvo deve existir em fieldMetadata.'
24631
+ },
24632
+ {
24633
+ validatorId: 'field-name-unique',
24634
+ level: 'error',
24635
+ code: 'DF002',
24636
+ description: 'Nomes de campos devem ser únicos em fieldMetadata.'
24637
+ },
24638
+ {
24639
+ validatorId: 'section-id-unique',
24640
+ level: 'error',
24641
+ code: 'DF003',
24642
+ description: 'IDs de seções devem ser únicos em sections[].'
24643
+ },
24644
+ {
24645
+ validatorId: 'section-exists',
24646
+ level: 'error',
24647
+ code: 'DF004',
24648
+ description: 'A seção alvo deve existir em sections[].'
24649
+ },
24650
+ {
24651
+ validatorId: 'field-exists-in-layout',
24652
+ level: 'warning',
24653
+ code: 'DF005',
24654
+ description: 'Campo definido em fieldMetadata mas ausente no layout.'
24655
+ },
24656
+ {
24657
+ validatorId: 'local-schema-name-no-collision',
24658
+ level: 'error',
24659
+ code: 'DF006',
24660
+ description: 'Nome de campo local não pode colidir com campo schema-backed existente.'
24661
+ },
24662
+ {
24663
+ validatorId: 'field-is-local',
24664
+ level: 'error',
24665
+ code: 'DF007',
24666
+ description: 'Operação restrita a campos locais (source:"local"). Campo schema-backed não pode ser removido ou ter submitPolicy alterada.'
24667
+ },
24668
+ {
24669
+ validatorId: 'rule-id-unique',
24670
+ level: 'error',
24671
+ code: 'DF008',
24672
+ description: 'IDs de regras devem ser únicos em formRules[].'
24673
+ },
24674
+ {
24675
+ validatorId: 'rule-exists',
24676
+ level: 'error',
24677
+ code: 'DF009',
24678
+ description: 'A regra alvo deve existir em formRules[].'
24679
+ },
24680
+ {
24681
+ validatorId: 'rule-target-refs-exist',
24682
+ level: 'error',
24683
+ code: 'DF010',
24684
+ description: 'Todos os targets da regra devem referenciar campos, seções ou ações existentes.'
24685
+ },
24686
+ {
24687
+ validatorId: 'json-logic-valid',
24688
+ level: 'error',
24689
+ code: 'DF011',
24690
+ description: 'A expressão JsonLogic deve ser um AST válido (suporte canônico à spec JsonLogic).'
24691
+ },
24692
+ {
24693
+ validatorId: 'layout-target-exists',
24694
+ level: 'error',
24695
+ code: 'DF012',
24696
+ description: 'A seção/linha/coluna de destino devem existir antes de mover o campo.'
24697
+ },
24698
+ {
24699
+ validatorId: 'no-index-as-identity',
24700
+ level: 'error',
24701
+ code: 'DF013',
24702
+ description: 'Operações de layout não devem usar índice de array como identidade canônica; use IDs estáveis.'
24703
+ },
24704
+ {
24705
+ validatorId: 'row-exists',
24706
+ level: 'error',
24707
+ code: 'DF014',
24708
+ description: 'A linha alvo deve existir na seção especificada.'
24709
+ },
24710
+ {
24711
+ validatorId: 'row-id-unique-in-section',
24712
+ level: 'error',
24713
+ code: 'DF015',
24714
+ description: 'O id da nova linha deve ser único dentro da seção alvo.'
24715
+ },
24716
+ {
24717
+ validatorId: 'column-id-unique-in-row',
24718
+ level: 'error',
24719
+ code: 'DF016',
24720
+ description: 'O id da nova coluna deve ser único dentro da linha alvo.'
24721
+ },
24722
+ {
24723
+ validatorId: 'control-type-compatible',
24724
+ level: 'error',
24725
+ code: 'DF017',
24726
+ description: 'O tipo de controle deve ser compatível com o tipo de dado do campo.'
24727
+ },
24728
+ {
24729
+ validatorId: 'option-source-valid',
24730
+ level: 'error',
24731
+ code: 'DF018',
24732
+ description: 'A fonte de opções deve ser válida para o tipo de controle especificado.'
24733
+ },
24734
+ {
24735
+ validatorId: 'editor-round-trip-preserve',
24736
+ level: 'error',
24737
+ code: 'DF019',
24738
+ description: 'Edição não deve corromper propriedades críticas para o round-trip do editor. Aplicado após operações que afetam submitPolicy ou source de campos locais.'
24739
+ }
24740
+ ],
24741
+ roundTripRequirements: [
24742
+ 'strip-undefined',
24743
+ 'normalize-json-logic',
24744
+ 'stable-field-order',
24745
+ 'preserve-local-field-source',
24746
+ 'preserve-section-row-column-ids'
24747
+ ],
24748
+ examples: [
24749
+ {
24750
+ id: 'add-local-transient-field',
24751
+ request: 'Adicionar campo auxiliar local "notasInternas" que não deve aparecer no payload de envio',
24752
+ operationId: 'field.local.add',
24753
+ params: {
24754
+ name: 'notasInternas',
24755
+ label: 'Notas Internas',
24756
+ controlType: 'textarea',
24757
+ source: 'local',
24758
+ transient: true,
24759
+ submitPolicy: 'omit'
24760
+ },
24761
+ isPositive: true
24762
+ },
24763
+ {
24764
+ id: 'relabel-schema-backed-field',
24765
+ request: 'Mudar o label do campo email para "E-mail Corporativo"',
24766
+ operationId: 'field.label.set',
24767
+ target: 'email',
24768
+ params: { label: 'E-mail Corporativo' },
24769
+ isPositive: true
24770
+ },
24771
+ {
24772
+ id: 'move-field-to-column',
24773
+ request: 'Mover campo telefone para a primeira coluna da seção de contato',
24774
+ operationId: 'layout.field.move',
24775
+ target: 'telefone',
24776
+ params: { targetSectionId: 'secao-contato', targetRowId: 'row-1', targetColumnId: 'col-1' },
24777
+ isPositive: true
24778
+ },
24779
+ {
24780
+ id: 'add-visibility-rule',
24781
+ request: 'Ocultar o campo cpf quando o tipo de pessoa for jurídica',
24782
+ operationId: 'rule.visibility.add',
24783
+ params: {
24784
+ id: 'ocultar-cpf-pj',
24785
+ name: 'Ocultar CPF para PJ',
24786
+ targetType: 'field',
24787
+ targets: ['cpf'],
24788
+ condition: { '==': [{ var: 'tipoPessoa' }, 'juridica'] }
24789
+ },
24790
+ isPositive: true
24791
+ },
24792
+ {
24793
+ id: 'reject-unknown-field',
24794
+ request: 'Alterar o label do campo "campoInexistente"',
24795
+ operationId: 'field.label.set',
24796
+ target: 'campoInexistente',
24797
+ params: { label: 'Novo Label' },
24798
+ isPositive: false
24799
+ },
24800
+ {
24801
+ id: 'require-confirmation-remove-field',
24802
+ request: 'Remover o campo auxiliar "notasInternas"',
24803
+ operationId: 'field.local.remove',
24804
+ target: 'notasInternas',
24805
+ isPositive: true
24806
+ },
24807
+ {
24808
+ id: 'require-confirmation-remove-section',
24809
+ request: 'Remover a seção "Dados Adicionais"',
24810
+ operationId: 'layout.section.remove',
24811
+ target: 'secao-dados-adicionais',
24812
+ isPositive: true
24813
+ },
24814
+ {
24815
+ id: 'round-trip-no-drift',
24816
+ request: 'Salvar e reabrir o formulário editado sem drift de configuração',
24817
+ operationId: 'field.label.set',
24818
+ target: 'nome',
24819
+ params: { label: 'Nome Completo' },
24820
+ isPositive: true
24821
+ }
24822
+ ]
24823
+ };
24824
+
23517
24825
  /*
23518
24826
  * Public API Surface of praxis-dynamic-form
23519
24827
  */
@@ -23522,4 +24830,4 @@ const FORM_COMPONENT_AI_CAPABILITIES = {
23522
24830
  * Generated bundle index. Do not edit.
23523
24831
  */
23524
24832
 
23525
- export { ActionsEditorComponent, CanvasStateService, CanvasToolbarComponent, ColumnEditorComponent, DynamicFormLayoutService, FORM_AI_CAPABILITIES, FORM_COMPONENT_AI_CAPABILITIES, FieldConfiguratorComponent, FieldEditorComponent, FormConfigService, FormContextService, FormLayoutService, JsonConfigEditorComponent, LayoutColorToken, LayoutEditorComponent, LayoutPrefsService, PRAXIS_DYNAMIC_FORM_COMPONENT_METADATA, PRAXIS_FILTER_FORM_COMPONENT_METADATA, PraxisDynamicForm, PraxisDynamicFormConfigEditor, PraxisFilterForm, PraxisFormActionsComponent, RowConfiguratorComponent, RowEditorComponent, SETTINGS_PANEL_DYNAMIC_FORM_PROVIDER, SectionEditorComponent, TASK_PRESETS, applyVisibilityRules, buildDynamicFormApplyPlan, createDynamicFormAuthoringDocument, formLayoutRulesToBuilderState, getFormAiCatalog, getFormCapabilities, getFormEnum, isRuleSatisfied, normalizeDateArrays, normalizeDynamicFormAuthoringDocument, parseLegacyOrDynamicFormDocument, providePraxisDynamicFormMetadata, providePraxisFilterFormMetadata, provideSettingsPanelDynamicForm, ruleBuilderStateToFormLayoutRules, serializeDynamicFormAuthoringDocument, stripLegacyFieldMetadata, toCanonicalDynamicFormConfig, validateDynamicFormAuthoringDocument, validateDynamicFormAuthoringInput };
24833
+ export { ActionsEditorComponent, CanvasStateService, CanvasToolbarComponent, ColumnEditorComponent, DynamicFormLayoutService, FORM_AI_CAPABILITIES, FORM_COMPONENT_AI_CAPABILITIES, FieldConfiguratorComponent, FieldEditorComponent, FormConfigService, FormContextService, FormLayoutService, JsonConfigEditorComponent, LayoutColorToken, LayoutEditorComponent, LayoutPrefsService, PRAXIS_DYNAMIC_FORM_AUTHORING_MANIFEST, PRAXIS_DYNAMIC_FORM_COMPONENT_METADATA, PRAXIS_FILTER_FORM_COMPONENT_METADATA, PraxisDynamicForm, PraxisDynamicFormConfigEditor, PraxisFilterForm, PraxisFormActionsComponent, RowConfiguratorComponent, RowEditorComponent, SETTINGS_PANEL_DYNAMIC_FORM_PROVIDER, SectionEditorComponent, TASK_PRESETS, applyVisibilityRules, buildDynamicFormApplyPlan, createDynamicFormAuthoringDocument, formLayoutRulesToBuilderState, getFormAiCatalog, getFormCapabilities, getFormEnum, isRuleSatisfied, normalizeDateArrays, normalizeDynamicFormAuthoringDocument, parseLegacyOrDynamicFormDocument, providePraxisDynamicFormMetadata, providePraxisFilterFormMetadata, provideSettingsPanelDynamicForm, ruleBuilderStateToFormLayoutRules, serializeDynamicFormAuthoringDocument, stripLegacyFieldMetadata, toCanonicalDynamicFormConfig, validateDynamicFormAuthoringDocument, validateDynamicFormAuthoringInput };