@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.
- package/README.md +90 -6
- package/fesm2022/praxisui-dynamic-form.mjs +1836 -528
- package/index.d.ts +89 -13
- package/package.json +6 -6
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { EventEmitter, HostListener, Output, Input, Component, Optional, Inject, Injectable,
|
|
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,
|
|
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['
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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.
|
|
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.
|
|
6176
|
+
if (this.formId && this.hasSchemaBackedRuntime()) {
|
|
5860
6177
|
void this.initializeForm();
|
|
5861
6178
|
return;
|
|
5862
6179
|
}
|
|
5863
|
-
if (!this.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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] =
|
|
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 =
|
|
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.
|
|
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
|
-
|
|
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() ||
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
14601
|
-
const
|
|
14602
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
14804
|
-
Object.prototype.hasOwnProperty.call(
|
|
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
|
|
14843
|
-
|
|
14844
|
-
|
|
14845
|
-
|
|
14846
|
-
|
|
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
|
|
14866
|
-
|
|
14867
|
-
|
|
14868
|
-
|
|
14869
|
-
|
|
14870
|
-
|
|
14871
|
-
|
|
14872
|
-
|
|
14873
|
-
|
|
14874
|
-
|
|
14875
|
-
|
|
14876
|
-
|
|
14877
|
-
|
|
14878
|
-
|
|
14879
|
-
|
|
14880
|
-
|
|
14881
|
-
|
|
14882
|
-
|
|
14883
|
-
|
|
14884
|
-
|
|
14885
|
-
|
|
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
|
-
|
|
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
|
-
|
|
14916
|
-
|
|
14917
|
-
|
|
14918
|
-
|
|
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(
|
|
15056
|
-
|
|
15057
|
-
return parsed.isGlobal ? parsed.id : this.customActionValue;
|
|
15480
|
+
getCustomActionSelectValue(action) {
|
|
15481
|
+
return action?.globalAction?.actionId || this.customActionValue;
|
|
15058
15482
|
}
|
|
15059
|
-
getCustomActionParam(
|
|
15060
|
-
const
|
|
15061
|
-
|
|
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(
|
|
15064
|
-
|
|
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
|
-
|
|
15074
|
-
if (parsed.isGlobal) {
|
|
15075
|
-
this.updateCustomAction(index, 'action', '');
|
|
15076
|
-
}
|
|
15498
|
+
this.updateCustomActionRef(index, undefined);
|
|
15077
15499
|
return;
|
|
15078
15500
|
}
|
|
15079
|
-
const
|
|
15080
|
-
|
|
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
|
|
15088
|
-
|
|
15089
|
-
|
|
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
|
-
|
|
15095
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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 ??
|
|
15624
|
+
value = json.message ?? json.text ?? '';
|
|
15189
15625
|
break;
|
|
15190
15626
|
case 'url':
|
|
15191
|
-
|
|
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.
|
|
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 ??
|
|
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
|
|
15298
|
-
if (!this.
|
|
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
|
-
|
|
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 === '
|
|
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 === '
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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
|
|
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
|
|
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
|
|
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 === '
|
|
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 === '
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
20174
|
-
|
|
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
|
-
|
|
20178
|
-
return parsed.isGlobal ? '' : parsed.raw;
|
|
20676
|
+
getHeaderActionCustomValue(value, globalAction) {
|
|
20677
|
+
return globalAction ? '' : value || '';
|
|
20179
20678
|
}
|
|
20180
|
-
getHeaderGlobalActionSchema(value) {
|
|
20181
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
20203
|
-
if (parsed.isGlobal) {
|
|
20204
|
-
group.get('action')?.setValue('');
|
|
20205
|
-
}
|
|
20693
|
+
group.get('globalAction')?.setValue(undefined);
|
|
20206
20694
|
return;
|
|
20207
20695
|
}
|
|
20208
|
-
const
|
|
20209
|
-
const
|
|
20210
|
-
group.get('
|
|
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
|
|
20217
|
-
const id =
|
|
20218
|
-
|
|
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('
|
|
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 (
|
|
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 };
|