@praxisui/dynamic-form 1.0.0-beta.61 → 1.0.0-beta.63
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 +54 -5
- package/fesm2022/praxisui-dynamic-form.mjs +1455 -321
- package/fesm2022/praxisui-dynamic-form.mjs.map +1 -1
- package/index.d.ts +165 -5
- package/package.json +11 -10
|
@@ -22,7 +22,7 @@ import { MatBadgeModule } from '@angular/material/badge';
|
|
|
22
22
|
import { firstValueFrom, BehaviorSubject, Subject, debounceTime as debounceTime$1, takeUntil as takeUntil$1 } from 'rxjs';
|
|
23
23
|
import { take, takeUntil, debounceTime, finalize, map } from 'rxjs/operators';
|
|
24
24
|
import * as i1$2 from '@praxisui/core';
|
|
25
|
-
import { PraxisIconDirective, RULE_PROPERTY_SCHEMA, FIELD_METADATA_CAPABILITIES, deepMerge,
|
|
25
|
+
import { PraxisIconDirective, RULE_PROPERTY_SCHEMA, FIELD_METADATA_CAPABILITIES, deepMerge, createDefaultFormConfig, normalizeFormConfig as normalizeFormConfig$1, ensureIds, ASYNC_CONFIG_STORAGE, migrateFormLayoutRule, LoggerService, createCorporateLoggerConfig, ConsoleLoggerSink, ResourceQuickConnectComponent, mapFieldDefinitionsToMetadata, syncWithServerMetadata, PRAXIS_LOADING_CTX, normalizeControlTypeKey, resolveSpan, resolveOffset, resolveOrder, resolveHidden, buildSchemaId, fetchWithETag, resolveControlTypeAlias, getTextTransformer, MemoryCacheAdapter, LocalStorageCacheAdapter, SchemaMetadataClient, CONNECTION_STORAGE, PRAXIS_LOADING_RENDERER, FORM_HOOKS_PRESETS, DynamicWidgetLoaderDirective, EmptyStateCardComponent, isValidFormConfig, ComponentMetadataRegistry, FieldControlType, GLOBAL_ACTION_CATALOG, GLOBAL_ACTION_SPEC_CATALOG, getGlobalActionCatalog, PRAXIS_GLOBAL_ACTION_CATALOG, getGlobalActionUiSchema } from '@praxisui/core';
|
|
26
26
|
import * as i1 from '@praxisui/dynamic-fields';
|
|
27
27
|
import { getControlTypeCatalog, ConfirmDialogComponent, DynamicFieldLoaderDirective } from '@praxisui/dynamic-fields';
|
|
28
28
|
import { BaseAiAdapter, PraxisAiAssistantComponent } from '@praxisui/ai';
|
|
@@ -337,6 +337,17 @@ class PraxisFormActionsComponent {
|
|
|
337
337
|
.filter((btn) => btn.visible !== false)
|
|
338
338
|
.slice(1);
|
|
339
339
|
}
|
|
340
|
+
isSplitLayout() {
|
|
341
|
+
return (this.actions?.position || 'right') === 'split';
|
|
342
|
+
}
|
|
343
|
+
getPrimaryButtons() {
|
|
344
|
+
return this.getActionButtons().filter((button) => button.visible !== false
|
|
345
|
+
&& !this.isSecondaryButton(button));
|
|
346
|
+
}
|
|
347
|
+
getSecondaryButtons() {
|
|
348
|
+
return this.getActionButtons().filter((button) => button.visible !== false
|
|
349
|
+
&& this.isSecondaryButton(button));
|
|
350
|
+
}
|
|
340
351
|
onActionButtonClick(button, event) {
|
|
341
352
|
if (this.isSubmitting) {
|
|
342
353
|
event.preventDefault();
|
|
@@ -354,7 +365,10 @@ class PraxisFormActionsComponent {
|
|
|
354
365
|
}
|
|
355
366
|
getButtonNgClass(button) {
|
|
356
367
|
const className = button.className;
|
|
357
|
-
return
|
|
368
|
+
return {
|
|
369
|
+
...(className ? { [className]: true } : {}),
|
|
370
|
+
...(button.size ? { [`size-${button.size}`]: true } : {}),
|
|
371
|
+
};
|
|
358
372
|
}
|
|
359
373
|
getButtonStyles(button) {
|
|
360
374
|
const styles = button.style;
|
|
@@ -417,8 +431,12 @@ class PraxisFormActionsComponent {
|
|
|
417
431
|
this.shortcutDisposers.push(dispose);
|
|
418
432
|
});
|
|
419
433
|
}
|
|
434
|
+
isSecondaryButton(button) {
|
|
435
|
+
const actionId = (button.id || button.action || '').toLowerCase();
|
|
436
|
+
return actionId === 'cancel' || actionId === 'reset';
|
|
437
|
+
}
|
|
420
438
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisFormActionsComponent, deps: [{ token: i1.KeyboardShortcutService }], target: i0.ɵɵFactoryTarget.Component });
|
|
421
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PraxisFormActionsComponent, isStandalone: true, selector: "praxis-form-actions", inputs: { actions: "actions", 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 @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 </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:.5rem;padding:1rem;z-index:1}.form-actions.with-divider{border-top:1px solid var(--md-sys-color-outline-variant)}.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 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:.75rem}.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"] }] });
|
|
439
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PraxisFormActionsComponent, isStandalone: true, selector: "praxis-form-actions", inputs: { actions: "actions", 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:.5rem;padding:1rem;z-index:1}.form-actions.with-divider{border-top:1px solid var(--editorial-border-color, var(--md-sys-color-outline-variant))}.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}.desktop-actions-group.primary-group{justify-content:flex-end}.form-actions .mdc-button{border-radius:var(--editorial-button-radius, 999px)}.form-actions .mdc-button.size-small{min-height:36px;padding-inline:12px;font-size:.82rem}.form-actions .mdc-button.size-medium{min-height:42px;padding-inline:18px}.form-actions .mdc-button.size-large{min-height:48px;padding-inline:22px;font-size:.98rem}.form-actions .mat-mdc-raised-button,.form-actions .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))}.form-actions .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-primary, var(--md-sys-color-primary))}.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:.75rem}.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"] }] });
|
|
422
440
|
}
|
|
423
441
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisFormActionsComponent, decorators: [{
|
|
424
442
|
type: Component,
|
|
@@ -429,7 +447,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
429
447
|
MatTooltipModule,
|
|
430
448
|
MatMenuModule,
|
|
431
449
|
PraxisIconDirective,
|
|
432
|
-
], 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 @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 </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:.5rem;padding:1rem;z-index:1}.form-actions.with-divider{border-top:1px solid var(--md-sys-color-outline-variant)}.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 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:.75rem}.form-actions.mobile-menu-active .desktop-actions{display:none}.form-actions.mobile-menu-active .mobile-actions{display:flex;align-items:center;gap:.5rem}}\n"] }]
|
|
450
|
+
], 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:.5rem;padding:1rem;z-index:1}.form-actions.with-divider{border-top:1px solid var(--editorial-border-color, var(--md-sys-color-outline-variant))}.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}.desktop-actions-group.primary-group{justify-content:flex-end}.form-actions .mdc-button{border-radius:var(--editorial-button-radius, 999px)}.form-actions .mdc-button.size-small{min-height:36px;padding-inline:12px;font-size:.82rem}.form-actions .mdc-button.size-medium{min-height:42px;padding-inline:18px}.form-actions .mdc-button.size-large{min-height:48px;padding-inline:22px;font-size:.98rem}.form-actions .mat-mdc-raised-button,.form-actions .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))}.form-actions .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-primary, var(--md-sys-color-primary))}.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:.75rem}.form-actions.mobile-menu-active .desktop-actions{display:none}.form-actions.mobile-menu-active .mobile-actions{display:flex;align-items:center;gap:.5rem}}\n"] }]
|
|
433
451
|
}], ctorParameters: () => [{ type: i1.KeyboardShortcutService }], propDecorators: { actions: [{
|
|
434
452
|
type: Input
|
|
435
453
|
}], isSubmitting: [{
|
|
@@ -1023,6 +1041,21 @@ const FORM_AI_CAPABILITIES = {
|
|
|
1023
1041
|
valueKind: 'number',
|
|
1024
1042
|
description: 'Espaçamento vertical (em px) abaixo da descrição da seção.',
|
|
1025
1043
|
},
|
|
1044
|
+
{
|
|
1045
|
+
path: 'sections[].appearance',
|
|
1046
|
+
category: 'appearance',
|
|
1047
|
+
valueKind: 'enum',
|
|
1048
|
+
allowedValues: ['card', 'plain', 'step'],
|
|
1049
|
+
description: 'Preset visual do container/cabeçalho da seção.',
|
|
1050
|
+
intentExamples: ['deixar a seção como step', 'remover card da seção'],
|
|
1051
|
+
},
|
|
1052
|
+
{
|
|
1053
|
+
path: 'sections[].stepLabel',
|
|
1054
|
+
category: 'content',
|
|
1055
|
+
valueKind: 'string',
|
|
1056
|
+
description: 'Rótulo curto do passo exibido antes do título da seção.',
|
|
1057
|
+
intentExamples: ['mostrar etapa 2', 'exibir badge numérico na seção'],
|
|
1058
|
+
},
|
|
1026
1059
|
{
|
|
1027
1060
|
path: 'sections[].titleStyle',
|
|
1028
1061
|
category: 'appearance',
|
|
@@ -2394,6 +2427,8 @@ const TASK_PRESETS = {
|
|
|
2394
2427
|
'sections[].gapBottom',
|
|
2395
2428
|
'sections[].titleGapBottom',
|
|
2396
2429
|
'sections[].descriptionGapBottom',
|
|
2430
|
+
'sections[].appearance',
|
|
2431
|
+
'sections[].stepLabel',
|
|
2397
2432
|
'sections[].titleStyle',
|
|
2398
2433
|
'sections[].descriptionStyle',
|
|
2399
2434
|
'sections[].titleColor',
|
|
@@ -2797,6 +2832,463 @@ class FormAiAdapter extends BaseAiAdapter {
|
|
|
2797
2832
|
}
|
|
2798
2833
|
}
|
|
2799
2834
|
|
|
2835
|
+
/**
|
|
2836
|
+
* Deep clone and normalize a form configuration ensuring required collections
|
|
2837
|
+
* are always present to avoid unintended mutations.
|
|
2838
|
+
*/
|
|
2839
|
+
function normalizeFormConfig(config) {
|
|
2840
|
+
const cloned = config ? structuredClone(config) : createDefaultFormConfig();
|
|
2841
|
+
// Apply canonical normalization from @praxisui/core first (aliases → canônico)
|
|
2842
|
+
const canonical = normalizeFormConfig$1(cloned);
|
|
2843
|
+
const withCollections = ensureIds({
|
|
2844
|
+
...createDefaultFormConfig(),
|
|
2845
|
+
...canonical,
|
|
2846
|
+
sections: canonical.sections ?? [],
|
|
2847
|
+
fieldMetadata: canonical.fieldMetadata ?? [],
|
|
2848
|
+
formBlocksBefore: canonical.formBlocksBefore ?? [],
|
|
2849
|
+
formBlocksBeforeActions: canonical.formBlocksBeforeActions ?? [],
|
|
2850
|
+
formBlocksBeforeActionsPlacement: canonical.formBlocksBeforeActionsPlacement ?? 'afterSections',
|
|
2851
|
+
formBlocksAfter: canonical.formBlocksAfter ?? [],
|
|
2852
|
+
});
|
|
2853
|
+
return applyDefaultSpans(withCollections);
|
|
2854
|
+
}
|
|
2855
|
+
/**
|
|
2856
|
+
* Apply default grid spans when not provided by the config.
|
|
2857
|
+
* Rule: for each row, if none of its columns declares span.md,
|
|
2858
|
+
* distribute equally across 12 columns on md+; keep xs/sm stacked (12).
|
|
2859
|
+
*/
|
|
2860
|
+
function applyDefaultSpans(config) {
|
|
2861
|
+
const next = structuredClone(config);
|
|
2862
|
+
for (const section of next.sections || []) {
|
|
2863
|
+
for (const row of section.rows || []) {
|
|
2864
|
+
const cols = row.columns || [];
|
|
2865
|
+
if (!cols.length)
|
|
2866
|
+
continue;
|
|
2867
|
+
const hasAnyMdSpan = cols.some((c) => c.span?.md != null);
|
|
2868
|
+
if (hasAnyMdSpan)
|
|
2869
|
+
continue;
|
|
2870
|
+
const per = Math.max(1, Math.floor(12 / cols.length));
|
|
2871
|
+
for (const col of cols) {
|
|
2872
|
+
const span = col.span || {};
|
|
2873
|
+
span.xs = 12;
|
|
2874
|
+
span.sm = 12;
|
|
2875
|
+
span.md = per;
|
|
2876
|
+
span.lg = span.lg ?? per;
|
|
2877
|
+
span.xl = span.xl ?? per;
|
|
2878
|
+
col.span = span;
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
return next;
|
|
2883
|
+
}
|
|
2884
|
+
|
|
2885
|
+
const DOCUMENT_KIND = 'praxis.dynamic-form.editor';
|
|
2886
|
+
const DOCUMENT_VERSION = 1;
|
|
2887
|
+
const VALID_MODES = ['create', 'edit', 'view'];
|
|
2888
|
+
const VALID_NOTIFY_VALUES = ['inline', 'snackbar', 'both', 'none'];
|
|
2889
|
+
const VALID_LABEL_POSITIONS = ['above', 'left'];
|
|
2890
|
+
const VALID_DENSITIES = ['comfortable', 'cozy', 'compact'];
|
|
2891
|
+
const VALID_ALIGNMENTS = ['start', 'center', 'end'];
|
|
2892
|
+
function createDynamicFormAuthoringDocument(source) {
|
|
2893
|
+
return normalizeDynamicFormAuthoringDocument({
|
|
2894
|
+
kind: DOCUMENT_KIND,
|
|
2895
|
+
version: DOCUMENT_VERSION,
|
|
2896
|
+
config: asFormConfig(source?.config),
|
|
2897
|
+
bindings: source?.bindings,
|
|
2898
|
+
contextSnapshot: source?.contextSnapshot,
|
|
2899
|
+
});
|
|
2900
|
+
}
|
|
2901
|
+
function parseLegacyOrDynamicFormDocument(raw) {
|
|
2902
|
+
const obj = asRecord(raw);
|
|
2903
|
+
if (obj.kind === DOCUMENT_KIND && obj.version === DOCUMENT_VERSION) {
|
|
2904
|
+
return normalizeDynamicFormAuthoringDocument({
|
|
2905
|
+
kind: DOCUMENT_KIND,
|
|
2906
|
+
version: DOCUMENT_VERSION,
|
|
2907
|
+
config: asFormConfig(obj.config),
|
|
2908
|
+
bindings: obj.bindings,
|
|
2909
|
+
contextSnapshot: obj.contextSnapshot,
|
|
2910
|
+
});
|
|
2911
|
+
}
|
|
2912
|
+
if (looksLikeLegacyEditorPayload(obj)) {
|
|
2913
|
+
return createDynamicFormAuthoringDocument({
|
|
2914
|
+
config: extractLegacyFormConfig(obj),
|
|
2915
|
+
bindings: {
|
|
2916
|
+
mode: normalizeMode(obj.inputsPatch?.mode),
|
|
2917
|
+
},
|
|
2918
|
+
contextSnapshot: {
|
|
2919
|
+
backConfig: normalizeBackConfig(obj.backConfig),
|
|
2920
|
+
presentation: normalizePresentation(obj.presentation),
|
|
2921
|
+
schemaPrefs: normalizeSchemaPrefs(obj.schemaPrefs),
|
|
2922
|
+
},
|
|
2923
|
+
});
|
|
2924
|
+
}
|
|
2925
|
+
return createDynamicFormAuthoringDocument({
|
|
2926
|
+
config: raw,
|
|
2927
|
+
});
|
|
2928
|
+
}
|
|
2929
|
+
function normalizeDynamicFormAuthoringDocument(doc) {
|
|
2930
|
+
const normalizedConfig = normalizeDynamicFormConfig(doc?.config);
|
|
2931
|
+
return {
|
|
2932
|
+
kind: DOCUMENT_KIND,
|
|
2933
|
+
version: DOCUMENT_VERSION,
|
|
2934
|
+
config: normalizedConfig,
|
|
2935
|
+
bindings: normalizeBindings(doc?.bindings),
|
|
2936
|
+
contextSnapshot: normalizeContextSnapshot(doc?.contextSnapshot),
|
|
2937
|
+
};
|
|
2938
|
+
}
|
|
2939
|
+
function validateDynamicFormAuthoringDocument(doc, context) {
|
|
2940
|
+
return validateDynamicFormAuthoringInput(doc, context);
|
|
2941
|
+
}
|
|
2942
|
+
function validateDynamicFormAuthoringInput(raw, context) {
|
|
2943
|
+
const diagnostics = [];
|
|
2944
|
+
const obj = asRecord(raw);
|
|
2945
|
+
const rawConfig = asRecord(obj.config);
|
|
2946
|
+
const rawBindings = asRecord(obj.bindings);
|
|
2947
|
+
const rawContextSnapshot = asRecord(obj.contextSnapshot);
|
|
2948
|
+
if (Object.prototype.hasOwnProperty.call(obj, 'kind') &&
|
|
2949
|
+
obj.kind !== DOCUMENT_KIND) {
|
|
2950
|
+
diagnostics.push(errorDiagnostic('dynamicForm.document.kind.invalid', `kind must be ${DOCUMENT_KIND}`, 'kind'));
|
|
2951
|
+
}
|
|
2952
|
+
if (Object.prototype.hasOwnProperty.call(obj, 'version') &&
|
|
2953
|
+
obj.version !== DOCUMENT_VERSION) {
|
|
2954
|
+
diagnostics.push(errorDiagnostic('dynamicForm.document.version.invalid', `version must be ${DOCUMENT_VERSION}`, 'version'));
|
|
2955
|
+
}
|
|
2956
|
+
if (!Array.isArray(rawConfig.sections)) {
|
|
2957
|
+
diagnostics.push(errorDiagnostic('dynamicForm.config.sections.invalid', 'config.sections must be an array', 'config.sections'));
|
|
2958
|
+
}
|
|
2959
|
+
if (!Array.isArray(rawConfig.fieldMetadata)) {
|
|
2960
|
+
diagnostics.push(errorDiagnostic('dynamicForm.config.fieldMetadata.invalid', 'config.fieldMetadata must be an array', 'config.fieldMetadata'));
|
|
2961
|
+
}
|
|
2962
|
+
if (Object.prototype.hasOwnProperty.call(rawBindings, 'mode') &&
|
|
2963
|
+
rawBindings.mode != null &&
|
|
2964
|
+
!VALID_MODES.includes(rawBindings.mode)) {
|
|
2965
|
+
diagnostics.push(errorDiagnostic('dynamicForm.bindings.mode.invalid', 'bindings.mode must be create, edit or view', 'bindings.mode'));
|
|
2966
|
+
}
|
|
2967
|
+
const rawSchemaPrefs = asRecord(rawContextSnapshot.schemaPrefs);
|
|
2968
|
+
if (Object.prototype.hasOwnProperty.call(rawSchemaPrefs, 'notifyIfOutdated') &&
|
|
2969
|
+
rawSchemaPrefs.notifyIfOutdated != null &&
|
|
2970
|
+
!VALID_NOTIFY_VALUES.includes(rawSchemaPrefs.notifyIfOutdated)) {
|
|
2971
|
+
diagnostics.push(errorDiagnostic('dynamicForm.contextSnapshot.schemaPrefs.notifyIfOutdated.invalid', 'schemaPrefs.notifyIfOutdated must be inline, snackbar, both or none', 'contextSnapshot.schemaPrefs.notifyIfOutdated'));
|
|
2972
|
+
}
|
|
2973
|
+
if (Object.prototype.hasOwnProperty.call(rawSchemaPrefs, 'snoozeMs') &&
|
|
2974
|
+
rawSchemaPrefs.snoozeMs != null &&
|
|
2975
|
+
(!Number.isFinite(rawSchemaPrefs.snoozeMs) || rawSchemaPrefs.snoozeMs < 0)) {
|
|
2976
|
+
diagnostics.push(errorDiagnostic('dynamicForm.contextSnapshot.schemaPrefs.snoozeMs.invalid', 'schemaPrefs.snoozeMs must be a non-negative number', 'contextSnapshot.schemaPrefs.snoozeMs'));
|
|
2977
|
+
}
|
|
2978
|
+
const rawPresentation = asRecord(rawContextSnapshot.presentation);
|
|
2979
|
+
if (Object.prototype.hasOwnProperty.call(rawPresentation, 'labelPosition') &&
|
|
2980
|
+
rawPresentation.labelPosition != null &&
|
|
2981
|
+
!VALID_LABEL_POSITIONS.includes(rawPresentation.labelPosition)) {
|
|
2982
|
+
diagnostics.push(errorDiagnostic('dynamicForm.contextSnapshot.presentation.labelPosition.invalid', 'presentation.labelPosition must be above or left', 'contextSnapshot.presentation.labelPosition'));
|
|
2983
|
+
}
|
|
2984
|
+
if (Object.prototype.hasOwnProperty.call(rawPresentation, 'density') &&
|
|
2985
|
+
rawPresentation.density != null &&
|
|
2986
|
+
!VALID_DENSITIES.includes(rawPresentation.density)) {
|
|
2987
|
+
diagnostics.push(errorDiagnostic('dynamicForm.contextSnapshot.presentation.density.invalid', 'presentation.density must be comfortable, cozy or compact', 'contextSnapshot.presentation.density'));
|
|
2988
|
+
}
|
|
2989
|
+
['labelAlign', 'valueAlign'].forEach((key) => {
|
|
2990
|
+
const value = rawPresentation[key];
|
|
2991
|
+
if (Object.prototype.hasOwnProperty.call(rawPresentation, key) &&
|
|
2992
|
+
value != null &&
|
|
2993
|
+
!VALID_ALIGNMENTS.includes(value)) {
|
|
2994
|
+
diagnostics.push(errorDiagnostic(`dynamicForm.contextSnapshot.presentation.${key}.invalid`, `presentation.${key} must be start, center or end`, `contextSnapshot.presentation.${key}`));
|
|
2995
|
+
}
|
|
2996
|
+
});
|
|
2997
|
+
[
|
|
2998
|
+
'labelFontSize',
|
|
2999
|
+
'valueFontSize',
|
|
3000
|
+
'labelWidth',
|
|
3001
|
+
].forEach((key) => {
|
|
3002
|
+
const value = rawPresentation[key];
|
|
3003
|
+
if (Object.prototype.hasOwnProperty.call(rawPresentation, key) &&
|
|
3004
|
+
value != null &&
|
|
3005
|
+
(!Number.isFinite(value) || value < 0)) {
|
|
3006
|
+
diagnostics.push(errorDiagnostic(`dynamicForm.contextSnapshot.presentation.${key}.invalid`, `presentation.${key} must be a non-negative number or null`, `contextSnapshot.presentation.${key}`));
|
|
3007
|
+
}
|
|
3008
|
+
});
|
|
3009
|
+
const normalized = normalizeDynamicFormAuthoringDocument(parseLegacyOrDynamicFormDocument(raw));
|
|
3010
|
+
validateNormalizedDynamicFormAuthoringDocument(normalized, diagnostics, context);
|
|
3011
|
+
return dedupeDiagnostics(diagnostics);
|
|
3012
|
+
}
|
|
3013
|
+
function validateNormalizedDynamicFormAuthoringDocument(normalized, diagnostics, context) {
|
|
3014
|
+
if (context?.server?.schemaHash) {
|
|
3015
|
+
const configHash = trimString(normalized.config?.metadata?.serverHash);
|
|
3016
|
+
if (!configHash) {
|
|
3017
|
+
diagnostics.push(infoDiagnostic('dynamicForm.config.metadata.serverHash.snapshot-missing', 'No persisted schema snapshot is available for reconciliation with the current server schema hash', 'config.metadata.serverHash'));
|
|
3018
|
+
}
|
|
3019
|
+
else if (configHash !== context.server.schemaHash) {
|
|
3020
|
+
diagnostics.push(warnDiagnostic('dynamicForm.config.metadata.serverHash.diverges-from-server', 'config.metadata.serverHash snapshot diverges from current server schema hash', 'config.metadata.serverHash'));
|
|
3021
|
+
}
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
function toCanonicalDynamicFormConfig(doc, _context) {
|
|
3025
|
+
const normalized = normalizeDynamicFormAuthoringDocument(doc);
|
|
3026
|
+
const canonical = cloneJson(normalized.config);
|
|
3027
|
+
delete canonical.presentation;
|
|
3028
|
+
return stripUndefinedDeep(canonical);
|
|
3029
|
+
}
|
|
3030
|
+
function buildDynamicFormApplyPlan(doc, runtime, options) {
|
|
3031
|
+
const normalized = normalizeDynamicFormAuthoringDocument(doc);
|
|
3032
|
+
const diagnostics = validateDynamicFormAuthoringInput(doc, {
|
|
3033
|
+
server: {
|
|
3034
|
+
schemaHash: runtime?.server?.schemaHash,
|
|
3035
|
+
},
|
|
3036
|
+
});
|
|
3037
|
+
const bindingsPatch = normalizeBindings(normalized.bindings);
|
|
3038
|
+
const contextSnapshot = normalizeContextSnapshot(normalized.contextSnapshot);
|
|
3039
|
+
const diff = {
|
|
3040
|
+
modeChanged: normalizeMode(runtime?.currentBindings?.mode) !==
|
|
3041
|
+
normalizeMode(bindingsPatch?.mode),
|
|
3042
|
+
presentationChanged: !deepEqual(runtime?.currentContextSnapshot?.presentation, contextSnapshot?.presentation),
|
|
3043
|
+
schemaPrefsChanged: !deepEqual(runtime?.currentContextSnapshot?.schemaPrefs, contextSnapshot?.schemaPrefs),
|
|
3044
|
+
backConfigChanged: !deepEqual(runtime?.currentContextSnapshot?.backConfig, contextSnapshot?.backConfig),
|
|
3045
|
+
};
|
|
3046
|
+
return {
|
|
3047
|
+
canonicalConfig: toCanonicalDynamicFormConfig(normalized, {
|
|
3048
|
+
mode: bindingsPatch?.mode,
|
|
3049
|
+
}),
|
|
3050
|
+
bindingsPatch,
|
|
3051
|
+
contextSnapshot,
|
|
3052
|
+
persistence: {
|
|
3053
|
+
saveConfig: options?.saveConfig === true,
|
|
3054
|
+
saveBindings: options?.saveBindings === true,
|
|
3055
|
+
saveContextSnapshot: options?.saveContextSnapshot === true,
|
|
3056
|
+
},
|
|
3057
|
+
runtime: {
|
|
3058
|
+
rebindMode: diff.modeChanged ||
|
|
3059
|
+
(options?.replaceContext === true &&
|
|
3060
|
+
!bindingsPatch?.mode &&
|
|
3061
|
+
!!runtime?.currentBindings?.mode),
|
|
3062
|
+
clearMode: options?.replaceContext === true &&
|
|
3063
|
+
!bindingsPatch?.mode &&
|
|
3064
|
+
!!runtime?.currentBindings?.mode,
|
|
3065
|
+
rebuildForm: true,
|
|
3066
|
+
refreshPresentation: !!contextSnapshot?.presentation,
|
|
3067
|
+
clearPresentation: options?.replaceContext === true &&
|
|
3068
|
+
!contextSnapshot?.presentation &&
|
|
3069
|
+
!!runtime?.currentContextSnapshot?.presentation,
|
|
3070
|
+
refreshSchemaState: !!contextSnapshot?.schemaPrefs,
|
|
3071
|
+
clearSchemaState: options?.replaceContext === true &&
|
|
3072
|
+
!contextSnapshot?.schemaPrefs &&
|
|
3073
|
+
!!runtime?.currentContextSnapshot?.schemaPrefs,
|
|
3074
|
+
refreshBackNavigation: !!contextSnapshot?.backConfig,
|
|
3075
|
+
clearBackNavigation: options?.replaceContext === true &&
|
|
3076
|
+
!contextSnapshot?.backConfig &&
|
|
3077
|
+
!!runtime?.currentContextSnapshot?.backConfig,
|
|
3078
|
+
},
|
|
3079
|
+
metadata: {
|
|
3080
|
+
attachSchemaSnapshot: options?.attachSchemaSnapshot === true,
|
|
3081
|
+
},
|
|
3082
|
+
diff,
|
|
3083
|
+
diagnostics,
|
|
3084
|
+
};
|
|
3085
|
+
}
|
|
3086
|
+
function dedupeDiagnostics(diagnostics) {
|
|
3087
|
+
const seen = new Set();
|
|
3088
|
+
return diagnostics.filter((diagnostic) => {
|
|
3089
|
+
const key = `${diagnostic.level}|${diagnostic.code}|${diagnostic.path || ''}`;
|
|
3090
|
+
if (seen.has(key)) {
|
|
3091
|
+
return false;
|
|
3092
|
+
}
|
|
3093
|
+
seen.add(key);
|
|
3094
|
+
return true;
|
|
3095
|
+
});
|
|
3096
|
+
}
|
|
3097
|
+
function serializeDynamicFormAuthoringDocument(doc) {
|
|
3098
|
+
const normalized = normalizeDynamicFormAuthoringDocument(doc);
|
|
3099
|
+
return stripUndefinedDeep({
|
|
3100
|
+
kind: DOCUMENT_KIND,
|
|
3101
|
+
version: DOCUMENT_VERSION,
|
|
3102
|
+
config: normalized.config,
|
|
3103
|
+
bindings: normalized.bindings,
|
|
3104
|
+
contextSnapshot: normalized.contextSnapshot,
|
|
3105
|
+
});
|
|
3106
|
+
}
|
|
3107
|
+
function normalizeDynamicFormConfig(config) {
|
|
3108
|
+
const normalized = normalizeFormConfig(asFormConfig(config));
|
|
3109
|
+
const next = cloneJson(normalized);
|
|
3110
|
+
next.fieldMetadata = stripLegacyFieldMetadata$1(next.fieldMetadata);
|
|
3111
|
+
delete next.presentation;
|
|
3112
|
+
if (Array.isArray(next.formRules) &&
|
|
3113
|
+
next.formRules.length === 0) {
|
|
3114
|
+
delete next.formRules;
|
|
3115
|
+
}
|
|
3116
|
+
if (isEmptyRuleBuilderState$1(next.formRulesState)) {
|
|
3117
|
+
delete next.formRulesState;
|
|
3118
|
+
}
|
|
3119
|
+
return next;
|
|
3120
|
+
}
|
|
3121
|
+
function normalizeBindings(bindings) {
|
|
3122
|
+
if (!bindings || typeof bindings !== 'object')
|
|
3123
|
+
return undefined;
|
|
3124
|
+
return stripUndefinedShallow({
|
|
3125
|
+
mode: normalizeMode(bindings.mode),
|
|
3126
|
+
});
|
|
3127
|
+
}
|
|
3128
|
+
function normalizeContextSnapshot(snapshot) {
|
|
3129
|
+
if (!snapshot || typeof snapshot !== 'object')
|
|
3130
|
+
return undefined;
|
|
3131
|
+
return stripUndefinedShallow({
|
|
3132
|
+
backConfig: normalizeBackConfig(snapshot.backConfig),
|
|
3133
|
+
presentation: normalizePresentation(snapshot.presentation),
|
|
3134
|
+
schemaPrefs: normalizeSchemaPrefs(snapshot.schemaPrefs),
|
|
3135
|
+
});
|
|
3136
|
+
}
|
|
3137
|
+
function normalizeBackConfig(value) {
|
|
3138
|
+
if (!value || typeof value !== 'object')
|
|
3139
|
+
return undefined;
|
|
3140
|
+
return stripUndefinedDeep(cloneJson(value));
|
|
3141
|
+
}
|
|
3142
|
+
function normalizePresentation(value) {
|
|
3143
|
+
if (!value || typeof value !== 'object')
|
|
3144
|
+
return undefined;
|
|
3145
|
+
return stripUndefinedShallow({
|
|
3146
|
+
labelPosition: value.labelPosition === 'left' ? 'left' : value.labelPosition === 'above' ? 'above' : undefined,
|
|
3147
|
+
labelFontSize: normalizeNullableNumber(value.labelFontSize),
|
|
3148
|
+
valueFontSize: normalizeNullableNumber(value.valueFontSize),
|
|
3149
|
+
compact: normalizeBoolean(value.compact),
|
|
3150
|
+
density: value.density === 'comfortable' ||
|
|
3151
|
+
value.density === 'cozy' ||
|
|
3152
|
+
value.density === 'compact'
|
|
3153
|
+
? value.density
|
|
3154
|
+
: undefined,
|
|
3155
|
+
labelWidth: normalizeNullableNumber(value.labelWidth),
|
|
3156
|
+
labelAlign: normalizeAlignment(value.labelAlign),
|
|
3157
|
+
valueAlign: normalizeAlignment(value.valueAlign),
|
|
3158
|
+
});
|
|
3159
|
+
}
|
|
3160
|
+
function normalizeSchemaPrefs(value) {
|
|
3161
|
+
if (!value || typeof value !== 'object')
|
|
3162
|
+
return undefined;
|
|
3163
|
+
return stripUndefinedShallow({
|
|
3164
|
+
notifyIfOutdated: value.notifyIfOutdated === 'inline' ||
|
|
3165
|
+
value.notifyIfOutdated === 'snackbar' ||
|
|
3166
|
+
value.notifyIfOutdated === 'both' ||
|
|
3167
|
+
value.notifyIfOutdated === 'none'
|
|
3168
|
+
? value.notifyIfOutdated
|
|
3169
|
+
: undefined,
|
|
3170
|
+
snoozeMs: normalizeNonNegativeNumber(value.snoozeMs),
|
|
3171
|
+
autoOpenSettingsOnOutdated: normalizeBoolean(value.autoOpenSettingsOnOutdated),
|
|
3172
|
+
});
|
|
3173
|
+
}
|
|
3174
|
+
function looksLikeLegacyEditorPayload(obj) {
|
|
3175
|
+
return (Object.prototype.hasOwnProperty.call(obj, 'formConfig') ||
|
|
3176
|
+
Object.prototype.hasOwnProperty.call(obj, 'inputsPatch') ||
|
|
3177
|
+
Object.prototype.hasOwnProperty.call(obj, 'backConfig') ||
|
|
3178
|
+
Object.prototype.hasOwnProperty.call(obj, 'presentation') ||
|
|
3179
|
+
Object.prototype.hasOwnProperty.call(obj, 'schemaPrefs'));
|
|
3180
|
+
}
|
|
3181
|
+
function extractLegacyFormConfig(obj) {
|
|
3182
|
+
if (obj.formConfig && typeof obj.formConfig === 'object') {
|
|
3183
|
+
return asFormConfig(obj.formConfig);
|
|
3184
|
+
}
|
|
3185
|
+
if (obj.config && typeof obj.config === 'object') {
|
|
3186
|
+
return asFormConfig(obj.config);
|
|
3187
|
+
}
|
|
3188
|
+
return asFormConfig(obj);
|
|
3189
|
+
}
|
|
3190
|
+
function asFormConfig(raw) {
|
|
3191
|
+
if (!raw || typeof raw !== 'object') {
|
|
3192
|
+
return { sections: [], fieldMetadata: [] };
|
|
3193
|
+
}
|
|
3194
|
+
return raw;
|
|
3195
|
+
}
|
|
3196
|
+
function asRecord(value) {
|
|
3197
|
+
return value && typeof value === 'object'
|
|
3198
|
+
? value
|
|
3199
|
+
: {};
|
|
3200
|
+
}
|
|
3201
|
+
function normalizeMode(value) {
|
|
3202
|
+
return value === 'create' || value === 'edit' || value === 'view'
|
|
3203
|
+
? value
|
|
3204
|
+
: undefined;
|
|
3205
|
+
}
|
|
3206
|
+
function normalizeNullableNumber(value) {
|
|
3207
|
+
if (value === null)
|
|
3208
|
+
return null;
|
|
3209
|
+
if (value === '')
|
|
3210
|
+
return null;
|
|
3211
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
3212
|
+
}
|
|
3213
|
+
function normalizeNonNegativeNumber(value) {
|
|
3214
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0
|
|
3215
|
+
? value
|
|
3216
|
+
: undefined;
|
|
3217
|
+
}
|
|
3218
|
+
function normalizeBoolean(value) {
|
|
3219
|
+
return typeof value === 'boolean' ? value : undefined;
|
|
3220
|
+
}
|
|
3221
|
+
function normalizeAlignment(value) {
|
|
3222
|
+
return value === 'start' || value === 'center' || value === 'end'
|
|
3223
|
+
? value
|
|
3224
|
+
: undefined;
|
|
3225
|
+
}
|
|
3226
|
+
function stripLegacyFieldMetadata$1(arr) {
|
|
3227
|
+
if (!Array.isArray(arr))
|
|
3228
|
+
return arr;
|
|
3229
|
+
return arr.map((item) => {
|
|
3230
|
+
const copy = { ...(item || {}) };
|
|
3231
|
+
delete copy.endpoint;
|
|
3232
|
+
return copy;
|
|
3233
|
+
});
|
|
3234
|
+
}
|
|
3235
|
+
function isEmptyRuleBuilderState$1(state) {
|
|
3236
|
+
try {
|
|
3237
|
+
const nodesEmpty = !state?.nodes || Object.keys(state.nodes).length === 0;
|
|
3238
|
+
const rootsEmpty = !state?.rootNodes || state.rootNodes.length === 0;
|
|
3239
|
+
return nodesEmpty && rootsEmpty;
|
|
3240
|
+
}
|
|
3241
|
+
catch {
|
|
3242
|
+
return false;
|
|
3243
|
+
}
|
|
3244
|
+
}
|
|
3245
|
+
function errorDiagnostic(code, message, path) {
|
|
3246
|
+
return { level: 'error', code, message, path };
|
|
3247
|
+
}
|
|
3248
|
+
function warnDiagnostic(code, message, path) {
|
|
3249
|
+
return { level: 'warning', code, message, path };
|
|
3250
|
+
}
|
|
3251
|
+
function infoDiagnostic(code, message, path) {
|
|
3252
|
+
return { level: 'info', code, message, path };
|
|
3253
|
+
}
|
|
3254
|
+
function cloneJson(value) {
|
|
3255
|
+
return value == null ? value : JSON.parse(JSON.stringify(value));
|
|
3256
|
+
}
|
|
3257
|
+
function trimString(value) {
|
|
3258
|
+
if (typeof value !== 'string')
|
|
3259
|
+
return undefined;
|
|
3260
|
+
const trimmed = value.trim();
|
|
3261
|
+
return trimmed ? trimmed : undefined;
|
|
3262
|
+
}
|
|
3263
|
+
function stripUndefinedShallow(value) {
|
|
3264
|
+
const entries = Object.entries(value).filter(([, item]) => item !== undefined);
|
|
3265
|
+
if (!entries.length)
|
|
3266
|
+
return undefined;
|
|
3267
|
+
return Object.fromEntries(entries);
|
|
3268
|
+
}
|
|
3269
|
+
function stripUndefinedDeep(value) {
|
|
3270
|
+
if (Array.isArray(value)) {
|
|
3271
|
+
return value
|
|
3272
|
+
.map((item) => stripUndefinedDeep(item))
|
|
3273
|
+
.filter((item) => item !== undefined);
|
|
3274
|
+
}
|
|
3275
|
+
if (!value || typeof value !== 'object') {
|
|
3276
|
+
return value;
|
|
3277
|
+
}
|
|
3278
|
+
const result = {};
|
|
3279
|
+
Object.entries(value).forEach(([key, item]) => {
|
|
3280
|
+
const normalized = stripUndefinedDeep(item);
|
|
3281
|
+
if (normalized !== undefined) {
|
|
3282
|
+
result[key] = normalized;
|
|
3283
|
+
}
|
|
3284
|
+
});
|
|
3285
|
+
return result;
|
|
3286
|
+
}
|
|
3287
|
+
function deepEqual(left, right) {
|
|
3288
|
+
return JSON.stringify(stripUndefinedDeep(cloneJson(left))) ===
|
|
3289
|
+
JSON.stringify(stripUndefinedDeep(cloneJson(right)));
|
|
3290
|
+
}
|
|
3291
|
+
|
|
2800
3292
|
class FormLayoutService {
|
|
2801
3293
|
storage;
|
|
2802
3294
|
constructor(storage) {
|
|
@@ -4308,6 +4800,9 @@ class PraxisDynamicForm {
|
|
|
4308
4800
|
get formBlocksBeforeActions() {
|
|
4309
4801
|
return Array.isArray(this.config?.formBlocksBeforeActions) ? this.config.formBlocksBeforeActions : [];
|
|
4310
4802
|
}
|
|
4803
|
+
get beforeActionsPlacement() {
|
|
4804
|
+
return (this.config?.formBlocksBeforeActionsPlacement || 'afterSections');
|
|
4805
|
+
}
|
|
4311
4806
|
get actionPlacement() {
|
|
4312
4807
|
return (this.config.actions?.placement || 'afterSections');
|
|
4313
4808
|
}
|
|
@@ -4902,17 +5397,23 @@ class PraxisDynamicForm {
|
|
|
4902
5397
|
}
|
|
4903
5398
|
}
|
|
4904
5399
|
if (changes['config'] || changes['presentationModeGlobal']) {
|
|
5400
|
+
let shouldRebuildFromExternalConfig = false;
|
|
4905
5401
|
if (changes['config']?.currentValue) {
|
|
4906
5402
|
this.config = normalizeFormConfig$1(changes['config'].currentValue);
|
|
4907
5403
|
this.logRowGapsSnapshot('config@input');
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
5404
|
+
const hasRenderableConfig = (this.config?.sections?.length ?? 0) > 0
|
|
5405
|
+
|| (this.config?.fieldMetadata?.length ?? 0) > 0;
|
|
5406
|
+
if (hasRenderableConfig) {
|
|
5407
|
+
// External config updates must rebuild even when a resourcePath exists.
|
|
5408
|
+
shouldRebuildFromExternalConfig = !this.isLoading;
|
|
4913
5409
|
}
|
|
4914
5410
|
}
|
|
4915
5411
|
this.applyPresentationVars();
|
|
5412
|
+
if (shouldRebuildFromExternalConfig) {
|
|
5413
|
+
this.buildFormFromConfig();
|
|
5414
|
+
this.isInitialized = true;
|
|
5415
|
+
this.initializationStatus = 'success';
|
|
5416
|
+
}
|
|
4916
5417
|
// On external config changes, if we have base and resourcePath configured, verify in background
|
|
4917
5418
|
if (this.resourcePath && (this.config?.sections?.length ?? 0) > 0 && this.formId) {
|
|
4918
5419
|
this.verifyServerSchemaVersion().catch(() => { });
|
|
@@ -5526,6 +6027,14 @@ class PraxisDynamicForm {
|
|
|
5526
6027
|
const props = this.getSectionRuleProps(section);
|
|
5527
6028
|
return props.description !== undefined ? props.description : section.description;
|
|
5528
6029
|
}
|
|
6030
|
+
getSectionAppearance(section) {
|
|
6031
|
+
const props = this.getSectionRuleProps(section);
|
|
6032
|
+
return props.appearance ?? section.appearance;
|
|
6033
|
+
}
|
|
6034
|
+
getSectionStepLabel(section) {
|
|
6035
|
+
const props = this.getSectionRuleProps(section);
|
|
6036
|
+
return props.stepLabel ?? section.stepLabel;
|
|
6037
|
+
}
|
|
5529
6038
|
getSectionTitleColor(section) {
|
|
5530
6039
|
const props = this.getSectionRuleProps(section);
|
|
5531
6040
|
return props.titleColor !== undefined ? props.titleColor : section.titleColor;
|
|
@@ -5606,6 +6115,9 @@ class PraxisDynamicForm {
|
|
|
5606
6115
|
}
|
|
5607
6116
|
getSectionClasses(section) {
|
|
5608
6117
|
const classes = [];
|
|
6118
|
+
const appearance = this.getSectionAppearance(section);
|
|
6119
|
+
if (appearance)
|
|
6120
|
+
classes.push(`section-appearance-${appearance}`);
|
|
5609
6121
|
const baseClass = section.className;
|
|
5610
6122
|
if (baseClass)
|
|
5611
6123
|
classes.push(baseClass);
|
|
@@ -5619,15 +6131,42 @@ class PraxisDynamicForm {
|
|
|
5619
6131
|
const styles = { ...(section?.styles || {}) };
|
|
5620
6132
|
if (props.style === null) {
|
|
5621
6133
|
// null remove overrides e estilos herdados
|
|
6134
|
+
this.sanitizeSectionStylesForEditorialContext(section, styles);
|
|
5622
6135
|
this.applyBorderPropsToStyle(styles, props);
|
|
5623
6136
|
return Object.keys(styles).length ? styles : null;
|
|
5624
6137
|
}
|
|
5625
6138
|
Object.entries(props.style || {}).forEach(([k, v]) => {
|
|
5626
6139
|
styles[k] = v;
|
|
5627
6140
|
});
|
|
6141
|
+
this.sanitizeSectionStylesForEditorialContext(section, styles);
|
|
5628
6142
|
this.applyBorderPropsToStyle(styles, props);
|
|
5629
6143
|
return Object.keys(styles).length ? styles : null;
|
|
5630
6144
|
}
|
|
6145
|
+
sanitizeSectionStylesForEditorialContext(section, styles) {
|
|
6146
|
+
if (!this.isEditorialVisualContext()) {
|
|
6147
|
+
return;
|
|
6148
|
+
}
|
|
6149
|
+
if (this.getSectionAppearance(section) !== 'step') {
|
|
6150
|
+
return;
|
|
6151
|
+
}
|
|
6152
|
+
[
|
|
6153
|
+
'background',
|
|
6154
|
+
'backgroundColor',
|
|
6155
|
+
'background-color',
|
|
6156
|
+
'backgroundImage',
|
|
6157
|
+
'background-image',
|
|
6158
|
+
'boxShadow',
|
|
6159
|
+
'box-shadow',
|
|
6160
|
+
].forEach((key) => {
|
|
6161
|
+
delete styles[key];
|
|
6162
|
+
});
|
|
6163
|
+
}
|
|
6164
|
+
isEditorialVisualContext() {
|
|
6165
|
+
return !!(this.config?.editorialContext || this.editorialContext);
|
|
6166
|
+
}
|
|
6167
|
+
hasEditorialVisualContext() {
|
|
6168
|
+
return this.isEditorialVisualContext();
|
|
6169
|
+
}
|
|
5631
6170
|
applyBorderPropsToStyle(styles, props) {
|
|
5632
6171
|
if (props.borderColor !== undefined) {
|
|
5633
6172
|
if (props.borderColor === null) {
|
|
@@ -6169,6 +6708,11 @@ class PraxisDynamicForm {
|
|
|
6169
6708
|
}
|
|
6170
6709
|
async openConfigEditor() {
|
|
6171
6710
|
const initialConfig = normalizeFormConfig$1(this.config);
|
|
6711
|
+
const initialDocument = createDynamicFormAuthoringDocument({
|
|
6712
|
+
config: initialConfig,
|
|
6713
|
+
bindings: { mode: this.mode },
|
|
6714
|
+
contextSnapshot: this.captureCurrentContextSnapshot(),
|
|
6715
|
+
});
|
|
6172
6716
|
const { PraxisDynamicFormConfigEditor } = await Promise.resolve().then(function () { return index; });
|
|
6173
6717
|
const ref = this.settingsPanel.open({
|
|
6174
6718
|
id: `form.${this.formId}`,
|
|
@@ -6177,68 +6721,28 @@ class PraxisDynamicForm {
|
|
|
6177
6721
|
content: {
|
|
6178
6722
|
component: PraxisDynamicFormConfigEditor,
|
|
6179
6723
|
inputs: {
|
|
6180
|
-
|
|
6181
|
-
backConfig: this.backConfig,
|
|
6724
|
+
document: initialDocument,
|
|
6182
6725
|
formId: this.formId,
|
|
6183
6726
|
componentKeyId: this.componentKeyId(),
|
|
6184
|
-
|
|
6727
|
+
presentationMode: this.presentationModeGlobal === true,
|
|
6185
6728
|
},
|
|
6186
6729
|
},
|
|
6187
6730
|
});
|
|
6188
|
-
ref.applied$.pipe(takeUntil(this.destroy$)).subscribe(
|
|
6731
|
+
ref.applied$.pipe(takeUntil(this.destroy$)).subscribe((cfg) => {
|
|
6189
6732
|
try {
|
|
6190
6733
|
this.debugLog('[PDF] settingsPanel.applied$', { hasCfg: !!cfg });
|
|
6191
6734
|
}
|
|
6192
6735
|
catch { }
|
|
6193
6736
|
if (cfg) {
|
|
6194
|
-
const
|
|
6195
|
-
const
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
if (backKey) {
|
|
6204
|
-
this.asyncConfigStorage.saveConfig(backKey, backCfg).subscribe();
|
|
6205
|
-
this.backConfig = backCfg;
|
|
6206
|
-
}
|
|
6207
|
-
}
|
|
6208
|
-
if (presCfg) {
|
|
6209
|
-
const presKey = this.formPresKey(persistMode);
|
|
6210
|
-
if (presKey) {
|
|
6211
|
-
this.asyncConfigStorage.saveConfig(presKey, presCfg).subscribe();
|
|
6212
|
-
}
|
|
6213
|
-
// Aplicar imediatamente as preferências de apresentação no formulário
|
|
6214
|
-
this.applyPresentationVars();
|
|
6215
|
-
}
|
|
6216
|
-
if (schemaPrefs) {
|
|
6217
|
-
const prefsKey = this.getPrefsKey();
|
|
6218
|
-
if (prefsKey) {
|
|
6219
|
-
this.asyncConfigStorage.saveConfig(prefsKey, schemaPrefs).subscribe();
|
|
6220
|
-
this.resolveSchemaPrefs();
|
|
6221
|
-
}
|
|
6222
|
-
}
|
|
6223
|
-
if (inputsPatch && typeof inputsPatch === 'object') {
|
|
6224
|
-
if (typeof inputsPatch.mode === 'string') {
|
|
6225
|
-
const mv = inputsPatch.mode;
|
|
6226
|
-
if (mv === 'create' || mv === 'edit' || mv === 'view') {
|
|
6227
|
-
this.mode = mv;
|
|
6228
|
-
const inputsKey = this.formInputsKey();
|
|
6229
|
-
if (inputsKey) {
|
|
6230
|
-
const prev = await firstValueFrom(this.asyncConfigStorage.loadConfig(inputsKey)).catch(() => ({}));
|
|
6231
|
-
this.asyncConfigStorage.saveConfig(inputsKey, { ...prev, mode: mv }).subscribe();
|
|
6232
|
-
}
|
|
6233
|
-
}
|
|
6234
|
-
}
|
|
6235
|
-
}
|
|
6236
|
-
this.configChange.emit(this.config);
|
|
6237
|
-
this.buildFormFromConfig();
|
|
6238
|
-
const patchMode = typeof inputsPatch?.mode === 'string' ? inputsPatch.mode : undefined;
|
|
6239
|
-
if (patchMode && patchMode !== persistMode) {
|
|
6240
|
-
this.reloadForModeChange();
|
|
6241
|
-
}
|
|
6737
|
+
const doc = parseLegacyOrDynamicFormDocument(cfg);
|
|
6738
|
+
const plan = buildDynamicFormApplyPlan(doc, this.buildDynamicFormEditorRuntimeContext(), {
|
|
6739
|
+
saveConfig: false,
|
|
6740
|
+
saveBindings: true,
|
|
6741
|
+
saveContextSnapshot: true,
|
|
6742
|
+
attachSchemaSnapshot: true,
|
|
6743
|
+
replaceContext: true,
|
|
6744
|
+
});
|
|
6745
|
+
this.executeDynamicFormEditorApplyPlan(plan, 'settings-applied');
|
|
6242
6746
|
}
|
|
6243
6747
|
});
|
|
6244
6748
|
// Handle reset from Settings Panel: clear persisted config so globals/user defaults apply
|
|
@@ -6247,70 +6751,23 @@ class PraxisDynamicForm {
|
|
|
6247
6751
|
this.debugLog('[PDF] settingsPanel.reset$');
|
|
6248
6752
|
}
|
|
6249
6753
|
catch { }
|
|
6250
|
-
|
|
6251
|
-
const key = this.formConfigKey();
|
|
6252
|
-
if (key)
|
|
6253
|
-
this.asyncConfigStorage.clearConfig(key).subscribe();
|
|
6254
|
-
}
|
|
6255
|
-
catch { }
|
|
6754
|
+
this.clearDynamicFormEditorPersistence();
|
|
6256
6755
|
});
|
|
6257
|
-
ref.saved$.pipe(takeUntil(this.destroy$)).subscribe(
|
|
6756
|
+
ref.saved$.pipe(takeUntil(this.destroy$)).subscribe((cfg) => {
|
|
6258
6757
|
try {
|
|
6259
6758
|
this.debugLog('[PDF] settingsPanel.saved$', { hasCfg: !!cfg });
|
|
6260
6759
|
}
|
|
6261
6760
|
catch { }
|
|
6262
6761
|
if (cfg) {
|
|
6263
|
-
const
|
|
6264
|
-
const
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
this.persistFormConfig(key, this.config);
|
|
6273
|
-
if (backCfg) {
|
|
6274
|
-
const backKey = this.crudBackKey();
|
|
6275
|
-
if (backKey) {
|
|
6276
|
-
this.asyncConfigStorage.saveConfig(backKey, backCfg).subscribe();
|
|
6277
|
-
this.backConfig = backCfg;
|
|
6278
|
-
}
|
|
6279
|
-
}
|
|
6280
|
-
if (presCfg) {
|
|
6281
|
-
const presKey = this.formPresKey(persistMode);
|
|
6282
|
-
if (presKey) {
|
|
6283
|
-
this.asyncConfigStorage.saveConfig(presKey, presCfg).subscribe();
|
|
6284
|
-
}
|
|
6285
|
-
// Aplicar imediatamente as preferências de apresentação no formulário
|
|
6286
|
-
this.applyPresentationVars();
|
|
6287
|
-
}
|
|
6288
|
-
if (schemaPrefs) {
|
|
6289
|
-
const prefsKey = this.getPrefsKey();
|
|
6290
|
-
if (prefsKey) {
|
|
6291
|
-
this.asyncConfigStorage.saveConfig(prefsKey, schemaPrefs).subscribe();
|
|
6292
|
-
this.resolveSchemaPrefs();
|
|
6293
|
-
}
|
|
6294
|
-
}
|
|
6295
|
-
if (inputsPatch && typeof inputsPatch === 'object') {
|
|
6296
|
-
if (typeof inputsPatch.mode === 'string') {
|
|
6297
|
-
const mv = inputsPatch.mode;
|
|
6298
|
-
if (mv === 'create' || mv === 'edit' || mv === 'view') {
|
|
6299
|
-
this.mode = mv;
|
|
6300
|
-
const inputsKey = this.formInputsKey();
|
|
6301
|
-
if (inputsKey) {
|
|
6302
|
-
const prev = await firstValueFrom(this.asyncConfigStorage.loadConfig(inputsKey)).catch(() => ({}));
|
|
6303
|
-
this.asyncConfigStorage.saveConfig(inputsKey, { ...prev, mode: mv }).subscribe();
|
|
6304
|
-
}
|
|
6305
|
-
}
|
|
6306
|
-
}
|
|
6307
|
-
}
|
|
6308
|
-
this.configChange.emit(this.config);
|
|
6309
|
-
this.buildFormFromConfig();
|
|
6310
|
-
const patchMode = typeof inputsPatch?.mode === 'string' ? inputsPatch.mode : undefined;
|
|
6311
|
-
if (patchMode && patchMode !== persistMode) {
|
|
6312
|
-
this.reloadForModeChange();
|
|
6313
|
-
}
|
|
6762
|
+
const doc = parseLegacyOrDynamicFormDocument(cfg);
|
|
6763
|
+
const plan = buildDynamicFormApplyPlan(doc, this.buildDynamicFormEditorRuntimeContext(), {
|
|
6764
|
+
saveConfig: true,
|
|
6765
|
+
saveBindings: true,
|
|
6766
|
+
saveContextSnapshot: true,
|
|
6767
|
+
attachSchemaSnapshot: true,
|
|
6768
|
+
replaceContext: true,
|
|
6769
|
+
});
|
|
6770
|
+
this.executeDynamicFormEditorApplyPlan(plan, 'settings-saved');
|
|
6314
6771
|
}
|
|
6315
6772
|
});
|
|
6316
6773
|
ref.reset$.pipe(takeUntil(this.destroy$)).subscribe(() => {
|
|
@@ -6318,9 +6775,14 @@ class PraxisDynamicForm {
|
|
|
6318
6775
|
this.debugLog('[PDF] settingsPanel.reset$ (second handler)');
|
|
6319
6776
|
}
|
|
6320
6777
|
catch { }
|
|
6321
|
-
this.
|
|
6322
|
-
|
|
6323
|
-
|
|
6778
|
+
const resetPlan = buildDynamicFormApplyPlan(initialDocument, this.buildDynamicFormEditorRuntimeContext(), {
|
|
6779
|
+
saveConfig: false,
|
|
6780
|
+
saveBindings: false,
|
|
6781
|
+
saveContextSnapshot: false,
|
|
6782
|
+
attachSchemaSnapshot: true,
|
|
6783
|
+
replaceContext: true,
|
|
6784
|
+
});
|
|
6785
|
+
this.executeDynamicFormEditorApplyPlan(resetPlan, 'settings-reset');
|
|
6324
6786
|
});
|
|
6325
6787
|
}
|
|
6326
6788
|
// --- Canvas Interaction Methods ---
|
|
@@ -6824,6 +7286,7 @@ class PraxisDynamicForm {
|
|
|
6824
7286
|
name: fieldMeta.name,
|
|
6825
7287
|
label: fieldMeta.label,
|
|
6826
7288
|
placeholder: fieldMeta.placeholder,
|
|
7289
|
+
description: fieldMeta.description,
|
|
6827
7290
|
hint: fieldMeta.hint,
|
|
6828
7291
|
// Canonical constraints only (no legacy)
|
|
6829
7292
|
required: fieldMeta.required,
|
|
@@ -7188,6 +7651,87 @@ class PraxisDynamicForm {
|
|
|
7188
7651
|
seed.layout = fmAny.layout;
|
|
7189
7652
|
if (fmAny.labelPosition !== undefined)
|
|
7190
7653
|
seed.labelPosition = fmAny.labelPosition;
|
|
7654
|
+
if (fmAny.selectionMode !== undefined)
|
|
7655
|
+
seed.selectionMode = fmAny.selectionMode;
|
|
7656
|
+
if (fmAny.variant !== undefined)
|
|
7657
|
+
seed.variant = fmAny.variant;
|
|
7658
|
+
if (fmAny.density !== undefined)
|
|
7659
|
+
seed.density = fmAny.density;
|
|
7660
|
+
}
|
|
7661
|
+
// CHECKBOX
|
|
7662
|
+
if (ctU === 'CHECKBOX') {
|
|
7663
|
+
const fmAny = fieldMeta;
|
|
7664
|
+
const src = fmAny.checkboxOptions || fmAny.options;
|
|
7665
|
+
if (src) {
|
|
7666
|
+
try {
|
|
7667
|
+
seed.options = JSON.stringify(src, null, 2);
|
|
7668
|
+
}
|
|
7669
|
+
catch {
|
|
7670
|
+
seed.options = src;
|
|
7671
|
+
}
|
|
7672
|
+
try {
|
|
7673
|
+
this.debugLog('[PDF][EditorSeed] options prepared (CHECKBOX)', {
|
|
7674
|
+
fieldName,
|
|
7675
|
+
controlType,
|
|
7676
|
+
valueType: typeof src,
|
|
7677
|
+
countOrLength: Array.isArray(src) ? src.length : (typeof src === 'string' ? src.length : undefined),
|
|
7678
|
+
});
|
|
7679
|
+
}
|
|
7680
|
+
catch { }
|
|
7681
|
+
}
|
|
7682
|
+
if (fmAny.optionLabelKey !== undefined)
|
|
7683
|
+
seed.optionLabelKey = fmAny.optionLabelKey;
|
|
7684
|
+
if (fmAny.optionValueKey !== undefined)
|
|
7685
|
+
seed.optionValueKey = fmAny.optionValueKey;
|
|
7686
|
+
if (fmAny.resourcePath !== undefined || fmAny.endpoint !== undefined) {
|
|
7687
|
+
seed.resourcePath = fmAny.resourcePath ?? fmAny.endpoint;
|
|
7688
|
+
}
|
|
7689
|
+
if (fmAny.filterCriteria !== undefined) {
|
|
7690
|
+
try {
|
|
7691
|
+
seed.filterCriteria = JSON.stringify(fmAny.filterCriteria, null, 2);
|
|
7692
|
+
}
|
|
7693
|
+
catch {
|
|
7694
|
+
seed.filterCriteria = fmAny.filterCriteria;
|
|
7695
|
+
}
|
|
7696
|
+
}
|
|
7697
|
+
if (fmAny.searchable !== undefined)
|
|
7698
|
+
seed.searchable = fmAny.searchable;
|
|
7699
|
+
if (fmAny.selectAll !== undefined)
|
|
7700
|
+
seed.selectAll = fmAny.selectAll;
|
|
7701
|
+
if (fmAny.maxSelections !== undefined)
|
|
7702
|
+
seed.maxSelections = fmAny.maxSelections;
|
|
7703
|
+
if (fmAny.layout !== undefined)
|
|
7704
|
+
seed.layout = fmAny.layout;
|
|
7705
|
+
if (fmAny.labelPosition !== undefined)
|
|
7706
|
+
seed.labelPosition = fmAny.labelPosition;
|
|
7707
|
+
if (fmAny.color !== undefined)
|
|
7708
|
+
seed.color = fmAny.color;
|
|
7709
|
+
if (fmAny.indeterminate !== undefined)
|
|
7710
|
+
seed.indeterminate = !!fmAny.indeterminate;
|
|
7711
|
+
if (fmAny.selectionMode !== undefined)
|
|
7712
|
+
seed.selectionMode = fmAny.selectionMode;
|
|
7713
|
+
if (fmAny.variant !== undefined)
|
|
7714
|
+
seed.variant = fmAny.variant;
|
|
7715
|
+
if (fmAny.density !== undefined)
|
|
7716
|
+
seed.density = fmAny.density;
|
|
7717
|
+
if (fmAny.requiredChecked !== undefined)
|
|
7718
|
+
seed.requiredChecked = !!fmAny.requiredChecked;
|
|
7719
|
+
else if (fmAny.validators?.requiredChecked !== undefined)
|
|
7720
|
+
seed.requiredChecked = !!fmAny.validators.requiredChecked;
|
|
7721
|
+
if (fmAny.links !== undefined) {
|
|
7722
|
+
try {
|
|
7723
|
+
seed.links = JSON.stringify(fmAny.links, null, 2);
|
|
7724
|
+
}
|
|
7725
|
+
catch {
|
|
7726
|
+
seed.links = fmAny.links;
|
|
7727
|
+
}
|
|
7728
|
+
}
|
|
7729
|
+
if (fmAny.linkText !== undefined)
|
|
7730
|
+
seed.linkText = fmAny.linkText;
|
|
7731
|
+
if (fmAny.linkUrl !== undefined)
|
|
7732
|
+
seed.linkUrl = fmAny.linkUrl;
|
|
7733
|
+
if (fmAny.linkTarget !== undefined)
|
|
7734
|
+
seed.linkTarget = fmAny.linkTarget;
|
|
7191
7735
|
}
|
|
7192
7736
|
// BUTTON_TOGGLE
|
|
7193
7737
|
if (ctU === 'BUTTON_TOGGLE') {
|
|
@@ -8238,6 +8782,12 @@ class PraxisDynamicForm {
|
|
|
8238
8782
|
const norm = normalizeOptions(patch.options) ?? [];
|
|
8239
8783
|
fm.checkboxOptions = norm;
|
|
8240
8784
|
}
|
|
8785
|
+
if (patch.selectionMode !== undefined)
|
|
8786
|
+
fm.selectionMode = patch.selectionMode;
|
|
8787
|
+
if (patch.variant !== undefined)
|
|
8788
|
+
fm.variant = patch.variant;
|
|
8789
|
+
if (patch.density !== undefined)
|
|
8790
|
+
fm.density = patch.density;
|
|
8241
8791
|
if (patch.layout !== undefined)
|
|
8242
8792
|
fm.layout = patch.layout;
|
|
8243
8793
|
if (patch.labelPosition !== undefined)
|
|
@@ -8246,6 +8796,24 @@ class PraxisDynamicForm {
|
|
|
8246
8796
|
fm.color = patch.color;
|
|
8247
8797
|
if (patch.indeterminate !== undefined)
|
|
8248
8798
|
fm.indeterminate = !!patch.indeterminate;
|
|
8799
|
+
if (patch.requiredChecked !== undefined) {
|
|
8800
|
+
fm.requiredChecked = !!patch.requiredChecked;
|
|
8801
|
+
fm.validators = { ...(fm.validators || {}), requiredChecked: !!patch.requiredChecked };
|
|
8802
|
+
}
|
|
8803
|
+
if (patch.links !== undefined) {
|
|
8804
|
+
const links = patch.links;
|
|
8805
|
+
if (typeof links === 'string') {
|
|
8806
|
+
try {
|
|
8807
|
+
fm.links = JSON.parse(links);
|
|
8808
|
+
}
|
|
8809
|
+
catch {
|
|
8810
|
+
fm.links = links;
|
|
8811
|
+
}
|
|
8812
|
+
}
|
|
8813
|
+
else {
|
|
8814
|
+
fm.links = links;
|
|
8815
|
+
}
|
|
8816
|
+
}
|
|
8249
8817
|
if (patch.linkText !== undefined)
|
|
8250
8818
|
fm.linkText = patch.linkText;
|
|
8251
8819
|
if (patch.linkUrl !== undefined)
|
|
@@ -8253,6 +8821,20 @@ class PraxisDynamicForm {
|
|
|
8253
8821
|
if (patch.linkTarget !== undefined)
|
|
8254
8822
|
fm.linkTarget = patch.linkTarget;
|
|
8255
8823
|
}
|
|
8824
|
+
if (fctApply === 'RADIO') {
|
|
8825
|
+
if (patch.selectionMode !== undefined)
|
|
8826
|
+
fm.selectionMode = patch.selectionMode;
|
|
8827
|
+
if (patch.variant !== undefined)
|
|
8828
|
+
fm.variant = patch.variant;
|
|
8829
|
+
if (patch.density !== undefined)
|
|
8830
|
+
fm.density = patch.density;
|
|
8831
|
+
if (patch.layout !== undefined)
|
|
8832
|
+
fm.layout = patch.layout;
|
|
8833
|
+
if (patch.labelPosition !== undefined)
|
|
8834
|
+
fm.labelPosition = patch.labelPosition;
|
|
8835
|
+
if (patch.color !== undefined)
|
|
8836
|
+
fm.color = patch.color;
|
|
8837
|
+
}
|
|
8256
8838
|
// TOGGLE
|
|
8257
8839
|
if (fctApply === 'TOGGLE') {
|
|
8258
8840
|
if (patch.color !== undefined)
|
|
@@ -9243,41 +9825,272 @@ class PraxisDynamicForm {
|
|
|
9243
9825
|
// 5. Trigger change detection to be safe.
|
|
9244
9826
|
this.cdr.detectChanges();
|
|
9245
9827
|
}
|
|
9246
|
-
ngOnDestroy() {
|
|
9247
|
-
this.destroy$.next();
|
|
9248
|
-
this.destroy$.complete();
|
|
9249
|
-
this.schemaCache = null;
|
|
9250
|
-
this.isInitialized = false;
|
|
9251
|
-
if (this.mountAnimationTimer) {
|
|
9252
|
-
clearTimeout(this.mountAnimationTimer);
|
|
9253
|
-
this.mountAnimationTimer = null;
|
|
9828
|
+
ngOnDestroy() {
|
|
9829
|
+
this.destroy$.next();
|
|
9830
|
+
this.destroy$.complete();
|
|
9831
|
+
this.schemaCache = null;
|
|
9832
|
+
this.isInitialized = false;
|
|
9833
|
+
if (this.mountAnimationTimer) {
|
|
9834
|
+
clearTimeout(this.mountAnimationTimer);
|
|
9835
|
+
this.mountAnimationTimer = null;
|
|
9836
|
+
}
|
|
9837
|
+
}
|
|
9838
|
+
// TrackBy functions for performance optimization
|
|
9839
|
+
trackBySection(index, section) {
|
|
9840
|
+
return section.id;
|
|
9841
|
+
}
|
|
9842
|
+
trackByRow(index, row) {
|
|
9843
|
+
return row.id;
|
|
9844
|
+
}
|
|
9845
|
+
trackByColumn(index, column) {
|
|
9846
|
+
return column.id;
|
|
9847
|
+
}
|
|
9848
|
+
applyConfigFromAdapter(nextConfig) {
|
|
9849
|
+
const raw = nextConfig;
|
|
9850
|
+
const isCanonicalDocument = !!raw &&
|
|
9851
|
+
typeof raw === 'object' &&
|
|
9852
|
+
raw.kind === 'praxis.dynamic-form.editor';
|
|
9853
|
+
const isLegacyEditorPayload = !!raw &&
|
|
9854
|
+
typeof raw === 'object' &&
|
|
9855
|
+
(Object.prototype.hasOwnProperty.call(raw, 'formConfig') ||
|
|
9856
|
+
Object.prototype.hasOwnProperty.call(raw, 'inputsPatch') ||
|
|
9857
|
+
Object.prototype.hasOwnProperty.call(raw, 'backConfig') ||
|
|
9858
|
+
Object.prototype.hasOwnProperty.call(raw, 'presentation') ||
|
|
9859
|
+
Object.prototype.hasOwnProperty.call(raw, 'schemaPrefs'));
|
|
9860
|
+
const doc = isCanonicalDocument || isLegacyEditorPayload
|
|
9861
|
+
? parseLegacyOrDynamicFormDocument(nextConfig)
|
|
9862
|
+
: createDynamicFormAuthoringDocument({
|
|
9863
|
+
config: nextConfig,
|
|
9864
|
+
bindings: { mode: this.mode },
|
|
9865
|
+
contextSnapshot: this.captureCurrentContextSnapshot(),
|
|
9866
|
+
});
|
|
9867
|
+
const plan = buildDynamicFormApplyPlan(doc, this.buildDynamicFormEditorRuntimeContext(), {
|
|
9868
|
+
saveConfig: false,
|
|
9869
|
+
saveBindings: false,
|
|
9870
|
+
saveContextSnapshot: false,
|
|
9871
|
+
attachSchemaSnapshot: true,
|
|
9872
|
+
replaceContext: isCanonicalDocument,
|
|
9873
|
+
});
|
|
9874
|
+
this.executeDynamicFormEditorApplyPlan(plan, 'adapter-apply');
|
|
9875
|
+
}
|
|
9876
|
+
buildDynamicFormEditorRuntimeContext() {
|
|
9877
|
+
return {
|
|
9878
|
+
currentBindings: {
|
|
9879
|
+
mode: this.mode,
|
|
9880
|
+
},
|
|
9881
|
+
currentContextSnapshot: this.captureCurrentContextSnapshot(),
|
|
9882
|
+
server: {
|
|
9883
|
+
schemaHash: this.lastSchemaMeta?.serverHash ||
|
|
9884
|
+
this.schemaState.meta?.serverHash ||
|
|
9885
|
+
this.config?.metadata?.serverHash,
|
|
9886
|
+
},
|
|
9887
|
+
};
|
|
9888
|
+
}
|
|
9889
|
+
captureCurrentContextSnapshot() {
|
|
9890
|
+
return {
|
|
9891
|
+
backConfig: this.backConfig ? structuredClone(this.backConfig) : undefined,
|
|
9892
|
+
presentation: this.captureCurrentPresentationSnapshot(),
|
|
9893
|
+
schemaPrefs: this.captureCurrentSchemaPrefsSnapshot(),
|
|
9894
|
+
};
|
|
9895
|
+
}
|
|
9896
|
+
captureCurrentPresentationSnapshot() {
|
|
9897
|
+
return {
|
|
9898
|
+
labelPosition: this.presentationVars.labelPosition,
|
|
9899
|
+
labelFontSize: this.parsePresentationCssNumber(this.presentationVars.labelSize),
|
|
9900
|
+
valueFontSize: this.parsePresentationCssNumber(this.presentationVars.valueSize),
|
|
9901
|
+
compact: this.presentationVars.compact,
|
|
9902
|
+
density: this.presentationVars.density,
|
|
9903
|
+
labelWidth: this.parsePresentationCssNumber(this.presentationVars.labelWidth),
|
|
9904
|
+
labelAlign: this.presentationVars.labelAlign,
|
|
9905
|
+
valueAlign: this.presentationVars.valueAlign,
|
|
9906
|
+
};
|
|
9907
|
+
}
|
|
9908
|
+
captureCurrentSchemaPrefsSnapshot() {
|
|
9909
|
+
return {
|
|
9910
|
+
notifyIfOutdated: this.notifyIfOutdated,
|
|
9911
|
+
snoozeMs: this.snoozeMs,
|
|
9912
|
+
autoOpenSettingsOnOutdated: this.autoOpenSettingsOnOutdated,
|
|
9913
|
+
};
|
|
9914
|
+
}
|
|
9915
|
+
executeDynamicFormEditorApplyPlan(plan, trigger) {
|
|
9916
|
+
if (plan.diagnostics.some((item) => item.level === 'error')) {
|
|
9917
|
+
this.warnLog('[PraxisDynamicForm] Editor document validation failed', {
|
|
9918
|
+
diagnostics: plan.diagnostics,
|
|
9919
|
+
});
|
|
9920
|
+
return;
|
|
9921
|
+
}
|
|
9922
|
+
const nextMode = plan.bindingsPatch?.mode;
|
|
9923
|
+
const previousMode = this.mode;
|
|
9924
|
+
const targetMode = nextMode || (plan.runtime?.clearMode ? 'create' : undefined);
|
|
9925
|
+
if (targetMode) {
|
|
9926
|
+
this.mode = targetMode;
|
|
9927
|
+
if (plan.persistence?.saveBindings) {
|
|
9928
|
+
const inputsKey = this.formInputsKey();
|
|
9929
|
+
if (inputsKey) {
|
|
9930
|
+
if (plan.runtime?.clearMode) {
|
|
9931
|
+
this.asyncConfigStorage.clearConfig(inputsKey).subscribe();
|
|
9932
|
+
}
|
|
9933
|
+
else {
|
|
9934
|
+
this.asyncConfigStorage
|
|
9935
|
+
.loadConfig(inputsKey)
|
|
9936
|
+
.pipe(take(1))
|
|
9937
|
+
.subscribe((prev) => {
|
|
9938
|
+
this.asyncConfigStorage
|
|
9939
|
+
.saveConfig(inputsKey, { ...(prev || {}), mode: targetMode })
|
|
9940
|
+
.subscribe();
|
|
9941
|
+
});
|
|
9942
|
+
}
|
|
9943
|
+
}
|
|
9944
|
+
}
|
|
9945
|
+
}
|
|
9946
|
+
const contextSnapshot = plan.contextSnapshot;
|
|
9947
|
+
if (plan.runtime?.refreshBackNavigation && contextSnapshot?.backConfig) {
|
|
9948
|
+
this.backConfig = contextSnapshot.backConfig;
|
|
9949
|
+
if (plan.persistence?.saveContextSnapshot) {
|
|
9950
|
+
const backKey = this.crudBackKey();
|
|
9951
|
+
if (backKey) {
|
|
9952
|
+
this.asyncConfigStorage
|
|
9953
|
+
.saveConfig(backKey, contextSnapshot.backConfig)
|
|
9954
|
+
.subscribe();
|
|
9955
|
+
}
|
|
9956
|
+
}
|
|
9957
|
+
}
|
|
9958
|
+
else if (plan.runtime?.clearBackNavigation) {
|
|
9959
|
+
this.backConfig = undefined;
|
|
9960
|
+
if (plan.persistence?.saveContextSnapshot) {
|
|
9961
|
+
const backKey = this.crudBackKey();
|
|
9962
|
+
if (backKey) {
|
|
9963
|
+
this.asyncConfigStorage.clearConfig(backKey).subscribe();
|
|
9964
|
+
}
|
|
9965
|
+
}
|
|
9966
|
+
}
|
|
9967
|
+
if (plan.runtime?.refreshPresentation && contextSnapshot?.presentation) {
|
|
9968
|
+
if (plan.persistence?.saveContextSnapshot) {
|
|
9969
|
+
const presKey = this.formPresKey(this.resolveModeKey(targetMode || previousMode));
|
|
9970
|
+
if (presKey) {
|
|
9971
|
+
this.asyncConfigStorage
|
|
9972
|
+
.saveConfig(presKey, contextSnapshot.presentation)
|
|
9973
|
+
.subscribe();
|
|
9974
|
+
}
|
|
9975
|
+
}
|
|
9976
|
+
this.applyPresentationConfig(contextSnapshot.presentation);
|
|
9977
|
+
}
|
|
9978
|
+
else if (plan.runtime?.clearPresentation) {
|
|
9979
|
+
if (plan.persistence?.saveContextSnapshot) {
|
|
9980
|
+
this.clearStoredPresentationSnapshots();
|
|
9981
|
+
}
|
|
9982
|
+
this.resetPresentationVarsToDefaults();
|
|
9983
|
+
}
|
|
9984
|
+
if (plan.runtime?.refreshSchemaState && contextSnapshot?.schemaPrefs) {
|
|
9985
|
+
this.notifyIfOutdated = contextSnapshot.schemaPrefs.notifyIfOutdated || 'both';
|
|
9986
|
+
this.snoozeMs = contextSnapshot.schemaPrefs.snoozeMs ?? this.snoozeMs;
|
|
9987
|
+
this.autoOpenSettingsOnOutdated =
|
|
9988
|
+
contextSnapshot.schemaPrefs.autoOpenSettingsOnOutdated === true;
|
|
9989
|
+
if (plan.persistence?.saveContextSnapshot) {
|
|
9990
|
+
const prefsKey = this.getPrefsKey();
|
|
9991
|
+
if (prefsKey) {
|
|
9992
|
+
this.asyncConfigStorage
|
|
9993
|
+
.saveConfig(prefsKey, contextSnapshot.schemaPrefs)
|
|
9994
|
+
.subscribe();
|
|
9995
|
+
}
|
|
9996
|
+
}
|
|
9997
|
+
void this.resolveSchemaPrefs();
|
|
9998
|
+
}
|
|
9999
|
+
else if (plan.runtime?.clearSchemaState) {
|
|
10000
|
+
this.resetSchemaPrefsToDefaults();
|
|
10001
|
+
if (plan.persistence?.saveContextSnapshot) {
|
|
10002
|
+
const prefsKey = this.getPrefsKey();
|
|
10003
|
+
if (prefsKey) {
|
|
10004
|
+
this.asyncConfigStorage.clearConfig(prefsKey).subscribe();
|
|
10005
|
+
}
|
|
10006
|
+
}
|
|
10007
|
+
}
|
|
10008
|
+
const nextConfig = this.attachRuntimeMetadataToDynamicFormConfig(plan.canonicalConfig, plan.metadata?.attachSchemaSnapshot === true);
|
|
10009
|
+
this.config = normalizeFormConfig$1(nextConfig);
|
|
10010
|
+
if (plan.persistence?.saveConfig) {
|
|
10011
|
+
const key = this.formConfigKey(this.resolveModeKey(targetMode || this.mode));
|
|
10012
|
+
this.persistFormConfig(key, this.config);
|
|
10013
|
+
}
|
|
10014
|
+
this.configChange.emit(this.config);
|
|
10015
|
+
if (plan.runtime?.rebuildForm) {
|
|
10016
|
+
this.buildFormFromConfig();
|
|
10017
|
+
}
|
|
10018
|
+
if (plan.runtime?.rebindMode && targetMode && targetMode !== previousMode) {
|
|
10019
|
+
this.reloadForModeChange();
|
|
10020
|
+
}
|
|
10021
|
+
this.cdr.detectChanges();
|
|
10022
|
+
try {
|
|
10023
|
+
this.debugLog('[PDF] executeDynamicFormEditorApplyPlan()', { trigger, plan });
|
|
10024
|
+
}
|
|
10025
|
+
catch { }
|
|
10026
|
+
}
|
|
10027
|
+
attachRuntimeMetadataToDynamicFormConfig(cfg, attachSchemaSnapshot) {
|
|
10028
|
+
if (!attachSchemaSnapshot) {
|
|
10029
|
+
return cfg;
|
|
9254
10030
|
}
|
|
10031
|
+
const next = structuredClone(cfg);
|
|
10032
|
+
const schemaHash = this.lastSchemaMeta?.serverHash || this.schemaState.meta?.serverHash;
|
|
10033
|
+
if (!schemaHash) {
|
|
10034
|
+
return next;
|
|
10035
|
+
}
|
|
10036
|
+
next.metadata = {
|
|
10037
|
+
...(next.metadata || {}),
|
|
10038
|
+
serverHash: schemaHash,
|
|
10039
|
+
};
|
|
10040
|
+
return next;
|
|
9255
10041
|
}
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
|
|
10042
|
+
parsePresentationCssNumber(value) {
|
|
10043
|
+
if (!value)
|
|
10044
|
+
return null;
|
|
10045
|
+
const parsed = Number.parseFloat(value);
|
|
10046
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
9259
10047
|
}
|
|
9260
|
-
|
|
9261
|
-
|
|
10048
|
+
clearDynamicFormEditorPersistence(modes) {
|
|
10049
|
+
const inputsKey = this.formInputsKey();
|
|
10050
|
+
if (inputsKey) {
|
|
10051
|
+
this.asyncConfigStorage.clearConfig(inputsKey).subscribe();
|
|
10052
|
+
}
|
|
10053
|
+
const prefsKey = this.getPrefsKey();
|
|
10054
|
+
if (prefsKey) {
|
|
10055
|
+
this.asyncConfigStorage.clearConfig(prefsKey).subscribe();
|
|
10056
|
+
}
|
|
10057
|
+
const backKey = this.crudBackKey();
|
|
10058
|
+
if (backKey) {
|
|
10059
|
+
this.asyncConfigStorage.clearConfig(backKey).subscribe();
|
|
10060
|
+
}
|
|
10061
|
+
const scopedModes = Array.from(new Set((modes && modes.length ? modes : ['create', 'edit', 'view']).map((mode) => this.resolveModeKey(mode))));
|
|
10062
|
+
this.clearStoredPresentationSnapshots(scopedModes);
|
|
10063
|
+
scopedModes.forEach((mode) => {
|
|
10064
|
+
const key = this.formConfigKey(mode);
|
|
10065
|
+
if (key) {
|
|
10066
|
+
this.asyncConfigStorage.clearConfig(key).subscribe();
|
|
10067
|
+
}
|
|
10068
|
+
});
|
|
9262
10069
|
}
|
|
9263
|
-
|
|
9264
|
-
|
|
10070
|
+
clearStoredPresentationSnapshots(modes = ['create', 'edit', 'view']) {
|
|
10071
|
+
modes.forEach((mode) => {
|
|
10072
|
+
const presKey = this.formPresKey(mode);
|
|
10073
|
+
if (presKey) {
|
|
10074
|
+
this.asyncConfigStorage.clearConfig(presKey).subscribe();
|
|
10075
|
+
}
|
|
10076
|
+
});
|
|
9265
10077
|
}
|
|
9266
|
-
|
|
9267
|
-
|
|
9268
|
-
|
|
9269
|
-
|
|
9270
|
-
|
|
9271
|
-
|
|
9272
|
-
|
|
9273
|
-
|
|
9274
|
-
|
|
9275
|
-
|
|
10078
|
+
resetPresentationVarsToDefaults() {
|
|
10079
|
+
this.presentationVars = {
|
|
10080
|
+
labelPosition: 'above',
|
|
10081
|
+
labelSize: '0.78rem',
|
|
10082
|
+
valueSize: '1rem',
|
|
10083
|
+
compact: false,
|
|
10084
|
+
density: 'comfortable',
|
|
10085
|
+
labelWidth: '140px',
|
|
10086
|
+
labelAlign: 'start',
|
|
10087
|
+
valueAlign: 'start',
|
|
9276
10088
|
};
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
this.
|
|
9280
|
-
this.
|
|
10089
|
+
}
|
|
10090
|
+
resetSchemaPrefsToDefaults() {
|
|
10091
|
+
this.notifyIfOutdated = 'both';
|
|
10092
|
+
this.snoozeMs = 24 * 60 * 60 * 1000;
|
|
10093
|
+
this.autoOpenSettingsOnOutdated = false;
|
|
9281
10094
|
}
|
|
9282
10095
|
// Public method for template access
|
|
9283
10096
|
retryInitialization() {
|
|
@@ -9566,7 +10379,7 @@ class PraxisDynamicForm {
|
|
|
9566
10379
|
}
|
|
9567
10380
|
}
|
|
9568
10381
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisDynamicForm, deps: [{ token: i1$2.GenericCrudService }, { token: i1$3.FormBuilder }, { token: i0.ChangeDetectorRef }, { token: FormLayoutService }, { token: FormContextService }, { token: FormRulesService }, { token: i6$1.SettingsPanelService }, { token: i2.MatDialog }, { token: ASYNC_CONFIG_STORAGE }, { token: CONNECTION_STORAGE }, { token: i1$2.DynamicFormService }, { token: i8.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: PRAXIS_LOADING_RENDERER, optional: true }, { token: i11.Router, optional: true }, { token: i11.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 });
|
|
9569
|
-
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", editorialContext: "editorialContext", mode: "mode", config: "config", schemaSource: "schemaSource", editModeEnabled: "editModeEnabled", 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", editModeEnabledChange: "editModeEnabledChange", customAction: "customAction", actionConfirmation: "actionConfirmation", schemaStatusChange: "schemaStatusChange", fieldRenderError: "fieldRenderError", widgetEvent: "widgetEvent" }, 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 in edit mode) -->\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 && editModeEnabled) {\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]=\"editModeEnabled\"\n [class.presentation-mode]=\"effectivePresentation\" [class.readonly-mode]=\"effectiveReadonly\"\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=\"editModeEnabled && selectedElement\" [selectedElement]=\"selectedElement\"\n [style.transform]=\"toolbarTransform\"></praxis-canvas-toolbar>\n\n @if (formBlocksBefore.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before\" data-editorial-placement=\"before\">\n @for (block of formBlocksBefore; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('before', $event)\">\n </ng-container>\n }\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]=\"form.valid\" [submitError]=\"submitError\" [formId]=\"formId\" [actionOverrides]=\"actionRuleProps\"\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 [style.marginBottom.px]=\"!last ? getSectionGapBottom(section) : null\" [ngClass]=\"getSectionClasses(section)\"\n [ngStyle]=\"getSectionStyles(section)\">\n <div class=\"section-heading\">\n <div class=\"section-heading-text\" [matTooltip]=\"getSectionHeaderTooltip(section) || null\"\n [matTooltipDisabled]=\"!getSectionHeaderTooltip(section)\">\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 @if (getSectionIcon(section)) {\n <mat-icon class=\"section-title-icon\" aria-hidden=\"true\" [praxisIcon]=\"getSectionIcon(section)\"></mat-icon>\n }\n {{ getSectionTitle(section) }}\n @if (editModeEnabled) {\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 (editModeEnabled) {\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 (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 [readonlyMode]=\"readonlyModeGlobal === null ? null : readonlyModeGlobal\"\n [disabledMode]=\"disabledModeGlobal === null ? null : disabledModeGlobal\"\n [presentationMode]=\"presentationForLoader\" [visible]=\"visibleGlobal === null ? null : visibleGlobal\"\n [canvasMode]=\"editModeEnabled\" (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 && formBlocksBeforeActions.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before-actions\" data-editorial-placement=\"beforeActions\">\n @for (block of formBlocksBeforeActions; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('beforeActions', $event)\">\n </ng-container>\n }\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]=\"form.valid\" [submitError]=\"submitError\" [formId]=\"formId\"\n [actionOverrides]=\"actionRuleProps\"\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 (editModeEnabled && 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 (actionPlacement !== 'insideLastSection' && formBlocksBeforeActions.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before-actions\" data-editorial-placement=\"beforeActions\">\n @for (block of formBlocksBeforeActions; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('beforeActions', $event)\">\n </ng-container>\n }\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]=\"form.valid\" [submitError]=\"submitError\" [formId]=\"formId\"\n [actionOverrides]=\"actionRuleProps\"\n (action)=\"onFormAction($event)\"></praxis-form-actions>\n }\n\n @if (formBlocksAfter.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-after\" data-editorial-placement=\"after\">\n @for (block of formBlocksAfter; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('after', $event)\">\n </ng-container>\n }\n </section>\n }\n</form>\n@if (!editModeEnabled && 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\";:host{display:block;position:relative}.form-config-controls{position:absolute;top:4px;right:4px;display:flex;gap:.25rem;z-index:100;background:transparent;padding:0;border:0;min-width:0;justify-content:flex-end;pointer-events:none}.form-config-controls>*{pointer-events:auto}.form-config-controls .mat-icon-button{--mdc-icon-button-state-layer-size: 36px;width:36px;height:36px;color:var(--md-sys-color-on-surface-variant)}.form-config-controls .mat-icon-button:hover{color:var(--md-sys-color-primary);background:var(--md-sys-color-primary-container)}.ai-floating-assistant{position:absolute;top:0;left:0;z-index:2002;pointer-events:none}.ai-floating-assistant{inset:auto 12px 12px auto;z-index:120}.ai-floating-assistant praxis-ai-assistant{pointer-events:auto;display:inline-flex}.ai-floating-assistant .ai-trigger-btn{box-shadow:var(--md-sys-elevation-level2)}.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}.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}.praxis-dynamic-form{display:flex;flex-direction:column;transition:all .3s ease;position:relative}.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-stroke, var(--md-sys-color-outline-variant));border-radius:8px;padding:1rem;background:var(--pfx-form-section-surface, var(--md-sys-color-surface-container));transition:all .2s ease;position:relative}.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, 20px) 0;font-size:1.05rem;font-weight:500;color:var(--md-sys-color-on-surface)}.section-heading{display:flex;align-items:flex-start;gap:8px;margin-bottom:8px}.section-heading-text{flex:1 1 auto;min-width:0}.section-collapse-btn{margin-left:4px;color:var(--md-sys-color-on-surface-variant)}.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-description{margin:0 0 8px;font-size:.875rem;color:var(--md-sys-color-on-surface-variant)}.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-title.align-center,.section-description.align-center{text-align:center}.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:1rem;margin-bottom: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)}.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}.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}}.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}}.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:1000}.canvas-mode-enabled .canvas-element.selected{z-index:2000}.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:500}.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)}.section-title{display:flex;align-items:center;gap:8px}.section-title .section-title-icon{font-size:1.25em;line-height:1}\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: "directive", type: DynamicWidgetLoaderDirective, selector: "[dynamicWidgetLoader]", inputs: ["dynamicWidgetLoader", "context", "strictValidation", "autoWireOutputs"], outputs: ["widgetEvent"], exportAs: ["dynamicWidgetLoader"] }, { 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: i15.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: i17.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", "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"] }] });
|
|
10382
|
+
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", editorialContext: "editorialContext", mode: "mode", config: "config", schemaSource: "schemaSource", editModeEnabled: "editModeEnabled", 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", editModeEnabledChange: "editModeEnabledChange", customAction: "customAction", actionConfirmation: "actionConfirmation", schemaStatusChange: "schemaStatusChange", fieldRenderError: "fieldRenderError", widgetEvent: "widgetEvent" }, 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 in edit mode) -->\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 && editModeEnabled) {\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]=\"editModeEnabled\"\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=\"editModeEnabled && selectedElement\" [selectedElement]=\"selectedElement\"\n [style.transform]=\"toolbarTransform\"></praxis-canvas-toolbar>\n\n @if (formBlocksBefore.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before\" data-editorial-placement=\"before\">\n @for (block of formBlocksBefore; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('before', $event)\">\n </ng-container>\n }\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]=\"form.valid\" [submitError]=\"submitError\" [formId]=\"formId\" [actionOverrides]=\"actionRuleProps\"\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 @if (getSectionIcon(section)) {\n <mat-icon class=\"section-title-icon\" aria-hidden=\"true\" [praxisIcon]=\"getSectionIcon(section)\"></mat-icon>\n }\n {{ getSectionTitle(section) }}\n @if (editModeEnabled) {\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 (editModeEnabled) {\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 (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 [readonlyMode]=\"readonlyModeGlobal === null ? null : readonlyModeGlobal\"\n [disabledMode]=\"disabledModeGlobal === null ? null : disabledModeGlobal\"\n [presentationMode]=\"presentationForLoader\" [visible]=\"visibleGlobal === null ? null : visibleGlobal\"\n [canvasMode]=\"editModeEnabled\" (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.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before-actions\" data-editorial-placement=\"beforeActions\">\n @for (block of formBlocksBeforeActions; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('beforeActions', $event)\">\n </ng-container>\n }\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]=\"form.valid\" [submitError]=\"submitError\" [formId]=\"formId\"\n [actionOverrides]=\"actionRuleProps\"\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 (editModeEnabled && 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.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before-actions\" data-editorial-placement=\"beforeActions\">\n @for (block of formBlocksBeforeActions; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('beforeActions', $event)\">\n </ng-container>\n }\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]=\"form.valid\" [submitError]=\"submitError\" [formId]=\"formId\"\n [actionOverrides]=\"actionRuleProps\"\n (action)=\"onFormAction($event)\"></praxis-form-actions>\n }\n\n @if (formBlocksAfter.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-after\" data-editorial-placement=\"after\">\n @for (block of formBlocksAfter; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('after', $event)\">\n </ng-container>\n }\n </section>\n }\n</form>\n@if (!editModeEnabled && 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\";:host{display:block;position:relative}.form-config-controls{position:absolute;top:4px;right:4px;display:flex;gap:.25rem;z-index:100;background:transparent;padding:0;border:0;min-width:0;justify-content:flex-end;pointer-events:none}.form-config-controls>*{pointer-events:auto}.form-config-controls .mat-icon-button{--mdc-icon-button-state-layer-size: 36px;width:36px;height:36px;color:var(--md-sys-color-on-surface-variant)}.form-config-controls .mat-icon-button:hover{color:var(--md-sys-color-primary);background:var(--md-sys-color-primary-container)}.ai-floating-assistant{position:absolute;top:0;left:0;z-index:2002;pointer-events:none}.ai-floating-assistant{inset:auto 12px 12px auto;z-index:120}.ai-floating-assistant praxis-ai-assistant{pointer-events:auto;display:inline-flex}.ai-floating-assistant .ai-trigger-btn{box-shadow:var(--md-sys-elevation-level2)}.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}.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}.praxis-dynamic-form{display:flex;flex-direction:column;transition:all .3s ease;position:relative;--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-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( --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)) )}.praxis-dynamic-form.editorial-visual-context{--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-editorial-form-border);border-radius:var(--editorial-card-radius, 8px);padding:1rem;background:var(--pfx-editorial-form-surface);box-shadow:var(--editorial-card-shadow, none);transition:all .2s ease;position:relative}.praxis-dynamic-form.editorial-visual-context .form-section{border:1px 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, 20px) 0;font-size:var(--editorial-step-title-size, 1.05rem);font-weight:600;color:var(--pfx-editorial-form-text)}.section-heading{display:flex;align-items:flex-start;gap:8px;margin-bottom:8px}.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:30px;padding:0 12px;margin:0 0 10px;border-radius:999px;background:color-mix(in srgb,var(--editorial-cta-primary, var(--md-sys-color-primary-container)) 16%,transparent);color:var(--editorial-cta-primary, var(--md-sys-color-primary));font-size:.82rem;font-weight:700;letter-spacing:.01em}.section-heading-text{flex:1 1 auto;min-width:0}.section-collapse-btn{margin-left:4px;color:var(--pfx-editorial-form-text-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-description{margin:0 0 8px;font-size:var(--editorial-body-size, .875rem);color:var(--pfx-editorial-form-text-muted)}.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-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(--editorial-card-radius, 20px);padding:clamp(1rem,2vw,1.35rem);background-color:var(--pfx-editorial-form-surface);background-image:linear-gradient(180deg,color-mix(in srgb,var(--editorial-accent, var(--md-sys-color-primary)) 6%,transparent) 0%,transparent 132px);background-repeat:no-repeat;border-color:color-mix(in srgb,var(--pfx-editorial-form-border) 80%,transparent);box-shadow:var(--editorial-card-shadow, 0 12px 28px color-mix(in srgb, var(--md-sys-color-shadow, #000) 8%, transparent))}.form-section.section-appearance-step .section-heading{margin-bottom:20px;padding-bottom:18px;border-bottom:1px solid color-mix(in srgb,var(--editorial-border-color, var(--md-sys-color-outline-variant)) 72%,transparent)}.form-section.section-appearance-step .section-title{font:var(--mdc-typography-headline-small, 600 24px/32px system-ui);color:var(--pfx-editorial-form-text);margin-bottom:10px}.form-section.section-appearance-step .section-description{color:color-mix(in srgb,var(--pfx-editorial-form-text-muted) 92%,var(--pfx-editorial-form-text) 8%);max-width:60ch}.form-section.section-appearance-step .section-step-label{min-height:32px;padding:0 14px;margin-bottom:12px;box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--editorial-cta-primary, var(--md-sys-color-primary)) 12%,transparent)}.form-section.section-appearance-step .section-heading.align-center .section-description{max-width:52ch}.form-section.section-appearance-step .section-body{display:grid;gap:4px;padding-top:2px}.form-section.section-appearance-step .form-row{margin-bottom:.85rem}.form-section.section-appearance-step .form-column{gap:12px}.form-section .form-editorial-blocks{display:grid;gap:16px}.form-section .form-editorial-blocks[data-editorial-placement=beforeActions]{margin-top:26px;padding-top:22px;border-top:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 72%,transparent)}.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:14px;padding-top:10px}.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) 92%,transparent);box-shadow:0 18px 36px #00000038}: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:1rem;margin-bottom: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);--mdc-filled-text-field-container-color: var(--pfx-editorial-form-field-surface);--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-editorial-form-text-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-editorial-form-text-muted);--mdc-outlined-text-field-input-text-color: var(--pfx-editorial-form-text);--mat-select-enabled-trigger-text-color: var(--pfx-editorial-form-text);--mat-select-enabled-arrow-color: var(--pfx-editorial-form-text-muted);--mat-form-field-enabled-select-arrow-color: var(--pfx-editorial-form-text-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 .mat-mdc-text-field-wrapper,.praxis-dynamic-form .mdc-text-field{background:var(--pfx-editorial-form-field-surface)}.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-editorial-form-field-surface)!important;background:var(--pfx-editorial-form-field-surface)!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-editorial-form-text-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.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}.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}}.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}}.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:1000}.canvas-mode-enabled .canvas-element.selected{z-index:2000}.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:500}.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)}.section-title{display:flex;align-items:center;gap:8px}.section-title .section-title-icon{font-size:1.25em;line-height:1}\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: "directive", type: DynamicWidgetLoaderDirective, selector: "[dynamicWidgetLoader]", inputs: ["dynamicWidgetLoader", "context", "strictValidation", "autoWireOutputs"], outputs: ["widgetEvent"], exportAs: ["dynamicWidgetLoader"] }, { 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: i15.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: i17.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", "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"] }] });
|
|
9570
10383
|
}
|
|
9571
10384
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisDynamicForm, decorators: [{
|
|
9572
10385
|
type: Component,
|
|
@@ -9587,7 +10400,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
9587
10400
|
PraxisFormActionsComponent,
|
|
9588
10401
|
EmptyStateCardComponent,
|
|
9589
10402
|
PraxisAiAssistantComponent,
|
|
9590
|
-
], 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 in edit mode) -->\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 && editModeEnabled) {\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]=\"editModeEnabled\"\n [class.presentation-mode]=\"effectivePresentation\" [class.readonly-mode]=\"effectiveReadonly\"\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=\"editModeEnabled && selectedElement\" [selectedElement]=\"selectedElement\"\n [style.transform]=\"toolbarTransform\"></praxis-canvas-toolbar>\n\n @if (formBlocksBefore.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before\" data-editorial-placement=\"before\">\n @for (block of formBlocksBefore; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('before', $event)\">\n </ng-container>\n }\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]=\"form.valid\" [submitError]=\"submitError\" [formId]=\"formId\" [actionOverrides]=\"actionRuleProps\"\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 [style.marginBottom.px]=\"!last ? getSectionGapBottom(section) : null\" [ngClass]=\"getSectionClasses(section)\"\n [ngStyle]=\"getSectionStyles(section)\">\n <div class=\"section-heading\">\n <div class=\"section-heading-text\" [matTooltip]=\"getSectionHeaderTooltip(section) || null\"\n [matTooltipDisabled]=\"!getSectionHeaderTooltip(section)\">\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 @if (getSectionIcon(section)) {\n <mat-icon class=\"section-title-icon\" aria-hidden=\"true\" [praxisIcon]=\"getSectionIcon(section)\"></mat-icon>\n }\n {{ getSectionTitle(section) }}\n @if (editModeEnabled) {\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 (editModeEnabled) {\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 (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 [readonlyMode]=\"readonlyModeGlobal === null ? null : readonlyModeGlobal\"\n [disabledMode]=\"disabledModeGlobal === null ? null : disabledModeGlobal\"\n [presentationMode]=\"presentationForLoader\" [visible]=\"visibleGlobal === null ? null : visibleGlobal\"\n [canvasMode]=\"editModeEnabled\" (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 && formBlocksBeforeActions.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before-actions\" data-editorial-placement=\"beforeActions\">\n @for (block of formBlocksBeforeActions; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('beforeActions', $event)\">\n </ng-container>\n }\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]=\"form.valid\" [submitError]=\"submitError\" [formId]=\"formId\"\n [actionOverrides]=\"actionRuleProps\"\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 (editModeEnabled && 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 (actionPlacement !== 'insideLastSection' && formBlocksBeforeActions.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before-actions\" data-editorial-placement=\"beforeActions\">\n @for (block of formBlocksBeforeActions; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('beforeActions', $event)\">\n </ng-container>\n }\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]=\"form.valid\" [submitError]=\"submitError\" [formId]=\"formId\"\n [actionOverrides]=\"actionRuleProps\"\n (action)=\"onFormAction($event)\"></praxis-form-actions>\n }\n\n @if (formBlocksAfter.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-after\" data-editorial-placement=\"after\">\n @for (block of formBlocksAfter; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('after', $event)\">\n </ng-container>\n }\n </section>\n }\n</form>\n@if (!editModeEnabled && 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\";:host{display:block;position:relative}.form-config-controls{position:absolute;top:4px;right:4px;display:flex;gap:.25rem;z-index:100;background:transparent;padding:0;border:0;min-width:0;justify-content:flex-end;pointer-events:none}.form-config-controls>*{pointer-events:auto}.form-config-controls .mat-icon-button{--mdc-icon-button-state-layer-size: 36px;width:36px;height:36px;color:var(--md-sys-color-on-surface-variant)}.form-config-controls .mat-icon-button:hover{color:var(--md-sys-color-primary);background:var(--md-sys-color-primary-container)}.ai-floating-assistant{position:absolute;top:0;left:0;z-index:2002;pointer-events:none}.ai-floating-assistant{inset:auto 12px 12px auto;z-index:120}.ai-floating-assistant praxis-ai-assistant{pointer-events:auto;display:inline-flex}.ai-floating-assistant .ai-trigger-btn{box-shadow:var(--md-sys-elevation-level2)}.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}.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}.praxis-dynamic-form{display:flex;flex-direction:column;transition:all .3s ease;position:relative}.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-stroke, var(--md-sys-color-outline-variant));border-radius:8px;padding:1rem;background:var(--pfx-form-section-surface, var(--md-sys-color-surface-container));transition:all .2s ease;position:relative}.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, 20px) 0;font-size:1.05rem;font-weight:500;color:var(--md-sys-color-on-surface)}.section-heading{display:flex;align-items:flex-start;gap:8px;margin-bottom:8px}.section-heading-text{flex:1 1 auto;min-width:0}.section-collapse-btn{margin-left:4px;color:var(--md-sys-color-on-surface-variant)}.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-description{margin:0 0 8px;font-size:.875rem;color:var(--md-sys-color-on-surface-variant)}.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-title.align-center,.section-description.align-center{text-align:center}.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:1rem;margin-bottom: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)}.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}.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}}.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}}.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:1000}.canvas-mode-enabled .canvas-element.selected{z-index:2000}.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:500}.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)}.section-title{display:flex;align-items:center;gap:8px}.section-title .section-title-icon{font-size:1.25em;line-height:1}\n"] }]
|
|
10403
|
+
], 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 in edit mode) -->\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 && editModeEnabled) {\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]=\"editModeEnabled\"\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=\"editModeEnabled && selectedElement\" [selectedElement]=\"selectedElement\"\n [style.transform]=\"toolbarTransform\"></praxis-canvas-toolbar>\n\n @if (formBlocksBefore.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before\" data-editorial-placement=\"before\">\n @for (block of formBlocksBefore; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('before', $event)\">\n </ng-container>\n }\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]=\"form.valid\" [submitError]=\"submitError\" [formId]=\"formId\" [actionOverrides]=\"actionRuleProps\"\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 @if (getSectionIcon(section)) {\n <mat-icon class=\"section-title-icon\" aria-hidden=\"true\" [praxisIcon]=\"getSectionIcon(section)\"></mat-icon>\n }\n {{ getSectionTitle(section) }}\n @if (editModeEnabled) {\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 (editModeEnabled) {\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 (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 [readonlyMode]=\"readonlyModeGlobal === null ? null : readonlyModeGlobal\"\n [disabledMode]=\"disabledModeGlobal === null ? null : disabledModeGlobal\"\n [presentationMode]=\"presentationForLoader\" [visible]=\"visibleGlobal === null ? null : visibleGlobal\"\n [canvasMode]=\"editModeEnabled\" (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.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before-actions\" data-editorial-placement=\"beforeActions\">\n @for (block of formBlocksBeforeActions; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('beforeActions', $event)\">\n </ng-container>\n }\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]=\"form.valid\" [submitError]=\"submitError\" [formId]=\"formId\"\n [actionOverrides]=\"actionRuleProps\"\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 (editModeEnabled && 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.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before-actions\" data-editorial-placement=\"beforeActions\">\n @for (block of formBlocksBeforeActions; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('beforeActions', $event)\">\n </ng-container>\n }\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]=\"form.valid\" [submitError]=\"submitError\" [formId]=\"formId\"\n [actionOverrides]=\"actionRuleProps\"\n (action)=\"onFormAction($event)\"></praxis-form-actions>\n }\n\n @if (formBlocksAfter.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-after\" data-editorial-placement=\"after\">\n @for (block of formBlocksAfter; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('after', $event)\">\n </ng-container>\n }\n </section>\n }\n</form>\n@if (!editModeEnabled && 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\";:host{display:block;position:relative}.form-config-controls{position:absolute;top:4px;right:4px;display:flex;gap:.25rem;z-index:100;background:transparent;padding:0;border:0;min-width:0;justify-content:flex-end;pointer-events:none}.form-config-controls>*{pointer-events:auto}.form-config-controls .mat-icon-button{--mdc-icon-button-state-layer-size: 36px;width:36px;height:36px;color:var(--md-sys-color-on-surface-variant)}.form-config-controls .mat-icon-button:hover{color:var(--md-sys-color-primary);background:var(--md-sys-color-primary-container)}.ai-floating-assistant{position:absolute;top:0;left:0;z-index:2002;pointer-events:none}.ai-floating-assistant{inset:auto 12px 12px auto;z-index:120}.ai-floating-assistant praxis-ai-assistant{pointer-events:auto;display:inline-flex}.ai-floating-assistant .ai-trigger-btn{box-shadow:var(--md-sys-elevation-level2)}.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}.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}.praxis-dynamic-form{display:flex;flex-direction:column;transition:all .3s ease;position:relative;--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-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( --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)) )}.praxis-dynamic-form.editorial-visual-context{--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-editorial-form-border);border-radius:var(--editorial-card-radius, 8px);padding:1rem;background:var(--pfx-editorial-form-surface);box-shadow:var(--editorial-card-shadow, none);transition:all .2s ease;position:relative}.praxis-dynamic-form.editorial-visual-context .form-section{border:1px 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, 20px) 0;font-size:var(--editorial-step-title-size, 1.05rem);font-weight:600;color:var(--pfx-editorial-form-text)}.section-heading{display:flex;align-items:flex-start;gap:8px;margin-bottom:8px}.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:30px;padding:0 12px;margin:0 0 10px;border-radius:999px;background:color-mix(in srgb,var(--editorial-cta-primary, var(--md-sys-color-primary-container)) 16%,transparent);color:var(--editorial-cta-primary, var(--md-sys-color-primary));font-size:.82rem;font-weight:700;letter-spacing:.01em}.section-heading-text{flex:1 1 auto;min-width:0}.section-collapse-btn{margin-left:4px;color:var(--pfx-editorial-form-text-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-description{margin:0 0 8px;font-size:var(--editorial-body-size, .875rem);color:var(--pfx-editorial-form-text-muted)}.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-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(--editorial-card-radius, 20px);padding:clamp(1rem,2vw,1.35rem);background-color:var(--pfx-editorial-form-surface);background-image:linear-gradient(180deg,color-mix(in srgb,var(--editorial-accent, var(--md-sys-color-primary)) 6%,transparent) 0%,transparent 132px);background-repeat:no-repeat;border-color:color-mix(in srgb,var(--pfx-editorial-form-border) 80%,transparent);box-shadow:var(--editorial-card-shadow, 0 12px 28px color-mix(in srgb, var(--md-sys-color-shadow, #000) 8%, transparent))}.form-section.section-appearance-step .section-heading{margin-bottom:20px;padding-bottom:18px;border-bottom:1px solid color-mix(in srgb,var(--editorial-border-color, var(--md-sys-color-outline-variant)) 72%,transparent)}.form-section.section-appearance-step .section-title{font:var(--mdc-typography-headline-small, 600 24px/32px system-ui);color:var(--pfx-editorial-form-text);margin-bottom:10px}.form-section.section-appearance-step .section-description{color:color-mix(in srgb,var(--pfx-editorial-form-text-muted) 92%,var(--pfx-editorial-form-text) 8%);max-width:60ch}.form-section.section-appearance-step .section-step-label{min-height:32px;padding:0 14px;margin-bottom:12px;box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--editorial-cta-primary, var(--md-sys-color-primary)) 12%,transparent)}.form-section.section-appearance-step .section-heading.align-center .section-description{max-width:52ch}.form-section.section-appearance-step .section-body{display:grid;gap:4px;padding-top:2px}.form-section.section-appearance-step .form-row{margin-bottom:.85rem}.form-section.section-appearance-step .form-column{gap:12px}.form-section .form-editorial-blocks{display:grid;gap:16px}.form-section .form-editorial-blocks[data-editorial-placement=beforeActions]{margin-top:26px;padding-top:22px;border-top:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 72%,transparent)}.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:14px;padding-top:10px}.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) 92%,transparent);box-shadow:0 18px 36px #00000038}: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:1rem;margin-bottom: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);--mdc-filled-text-field-container-color: var(--pfx-editorial-form-field-surface);--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-editorial-form-text-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-editorial-form-text-muted);--mdc-outlined-text-field-input-text-color: var(--pfx-editorial-form-text);--mat-select-enabled-trigger-text-color: var(--pfx-editorial-form-text);--mat-select-enabled-arrow-color: var(--pfx-editorial-form-text-muted);--mat-form-field-enabled-select-arrow-color: var(--pfx-editorial-form-text-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 .mat-mdc-text-field-wrapper,.praxis-dynamic-form .mdc-text-field{background:var(--pfx-editorial-form-field-surface)}.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-editorial-form-field-surface)!important;background:var(--pfx-editorial-form-field-surface)!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-editorial-form-text-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.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}.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}}.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}}.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:1000}.canvas-mode-enabled .canvas-element.selected{z-index:2000}.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:500}.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)}.section-title{display:flex;align-items:center;gap:8px}.section-title .section-title-icon{font-size:1.25em;line-height:1}\n"] }]
|
|
9591
10404
|
}], ctorParameters: () => [{ type: i1$2.GenericCrudService }, { type: i1$3.FormBuilder }, { type: i0.ChangeDetectorRef }, { type: FormLayoutService }, { type: FormContextService }, { type: FormRulesService }, { type: i6$1.SettingsPanelService }, { type: i2.MatDialog }, { type: undefined, decorators: [{
|
|
9592
10405
|
type: Inject,
|
|
9593
10406
|
args: [ASYNC_CONFIG_STORAGE]
|
|
@@ -9777,12 +10590,15 @@ class JsonConfigEditorComponent {
|
|
|
9777
10590
|
cdr;
|
|
9778
10591
|
configService;
|
|
9779
10592
|
config = null;
|
|
10593
|
+
document = null;
|
|
9780
10594
|
configChange = new EventEmitter();
|
|
10595
|
+
documentChange = new EventEmitter();
|
|
9781
10596
|
validationChange = new EventEmitter();
|
|
9782
10597
|
editorEvent = new EventEmitter();
|
|
9783
10598
|
jsonText = '';
|
|
9784
10599
|
isValidJson = true;
|
|
9785
10600
|
jsonError = '';
|
|
10601
|
+
diagnostics = [];
|
|
9786
10602
|
destroy$ = new Subject();
|
|
9787
10603
|
jsonTextChanges$ = new Subject();
|
|
9788
10604
|
constructor(cdr, configService) {
|
|
@@ -9793,9 +10609,10 @@ class JsonConfigEditorComponent {
|
|
|
9793
10609
|
.subscribe((text) => this.validateJson(text));
|
|
9794
10610
|
}
|
|
9795
10611
|
ngOnInit() {
|
|
9796
|
-
const
|
|
10612
|
+
const doc = this.document ??
|
|
10613
|
+
parseLegacyOrDynamicFormDocument(this.config || this.configService.currentConfig);
|
|
9797
10614
|
try {
|
|
9798
|
-
this.jsonText = JSON.stringify(
|
|
10615
|
+
this.jsonText = JSON.stringify(serializeDynamicFormAuthoringDocument(doc), null, 2);
|
|
9799
10616
|
}
|
|
9800
10617
|
catch {
|
|
9801
10618
|
this.jsonText = '{}';
|
|
@@ -9814,12 +10631,20 @@ class JsonConfigEditorComponent {
|
|
|
9814
10631
|
return;
|
|
9815
10632
|
}
|
|
9816
10633
|
try {
|
|
9817
|
-
const
|
|
9818
|
-
|
|
9819
|
-
|
|
10634
|
+
const parsed = JSON.parse(this.jsonText);
|
|
10635
|
+
const diagnostics = validateDynamicFormAuthoringInput(parsed);
|
|
10636
|
+
const newDocument = parseLegacyOrDynamicFormDocument(parsed);
|
|
10637
|
+
this.configService.loadConfig(newDocument.config);
|
|
10638
|
+
this.documentChange.emit(newDocument);
|
|
10639
|
+
this.configChange.emit(newDocument.config);
|
|
9820
10640
|
this.editorEvent.emit({
|
|
9821
10641
|
type: 'apply',
|
|
9822
|
-
payload: {
|
|
10642
|
+
payload: {
|
|
10643
|
+
isValid: !diagnostics.some((item) => item.level === 'error'),
|
|
10644
|
+
document: newDocument,
|
|
10645
|
+
config: newDocument.config,
|
|
10646
|
+
diagnostics,
|
|
10647
|
+
},
|
|
9823
10648
|
});
|
|
9824
10649
|
}
|
|
9825
10650
|
catch {
|
|
@@ -9836,10 +10661,17 @@ class JsonConfigEditorComponent {
|
|
|
9836
10661
|
}
|
|
9837
10662
|
try {
|
|
9838
10663
|
const parsed = JSON.parse(this.jsonText);
|
|
9839
|
-
|
|
10664
|
+
const diagnostics = validateDynamicFormAuthoringInput(parsed);
|
|
10665
|
+
const document = parseLegacyOrDynamicFormDocument(parsed);
|
|
10666
|
+
this.jsonText = JSON.stringify(serializeDynamicFormAuthoringDocument(document), null, 2);
|
|
9840
10667
|
this.editorEvent.emit({
|
|
9841
10668
|
type: 'format',
|
|
9842
|
-
payload: {
|
|
10669
|
+
payload: {
|
|
10670
|
+
isValid: !diagnostics.some((item) => item.level === 'error'),
|
|
10671
|
+
document,
|
|
10672
|
+
config: document.config,
|
|
10673
|
+
diagnostics,
|
|
10674
|
+
},
|
|
9843
10675
|
});
|
|
9844
10676
|
this.cdr.markForCheck();
|
|
9845
10677
|
}
|
|
@@ -9851,39 +10683,54 @@ class JsonConfigEditorComponent {
|
|
|
9851
10683
|
}
|
|
9852
10684
|
}
|
|
9853
10685
|
updateJsonFromConfig(config) {
|
|
9854
|
-
this.
|
|
10686
|
+
this.updateJsonFromDocument(parseLegacyOrDynamicFormDocument(config || { sections: [] }));
|
|
10687
|
+
}
|
|
10688
|
+
updateJsonFromDocument(document) {
|
|
10689
|
+
this.jsonText = JSON.stringify(serializeDynamicFormAuthoringDocument(document), null, 2);
|
|
9855
10690
|
this.validateJson(this.jsonText);
|
|
9856
10691
|
}
|
|
9857
10692
|
/**
|
|
9858
10693
|
* Força atualização do JSON com a configuração atual
|
|
9859
10694
|
*/
|
|
9860
10695
|
refreshJson() {
|
|
9861
|
-
const
|
|
9862
|
-
|
|
10696
|
+
const document = this.document ??
|
|
10697
|
+
parseLegacyOrDynamicFormDocument(this.config || this.configService.currentConfig);
|
|
10698
|
+
this.updateJsonFromDocument(document);
|
|
9863
10699
|
}
|
|
9864
10700
|
getCurrentConfig() {
|
|
10701
|
+
return this.getCurrentDocument()?.config || null;
|
|
10702
|
+
}
|
|
10703
|
+
getCurrentDocument() {
|
|
9865
10704
|
if (!this.isValidJson) {
|
|
9866
10705
|
return null;
|
|
9867
10706
|
}
|
|
9868
10707
|
try {
|
|
9869
|
-
return JSON.parse(this.jsonText);
|
|
10708
|
+
return parseLegacyOrDynamicFormDocument(JSON.parse(this.jsonText));
|
|
9870
10709
|
}
|
|
9871
10710
|
catch {
|
|
9872
10711
|
return null;
|
|
9873
10712
|
}
|
|
9874
10713
|
}
|
|
9875
10714
|
hasChanges() {
|
|
9876
|
-
|
|
10715
|
+
const baseline = this.document ??
|
|
10716
|
+
(this.config
|
|
10717
|
+
? parseLegacyOrDynamicFormDocument(this.config)
|
|
10718
|
+
: null);
|
|
10719
|
+
if (!baseline) {
|
|
9877
10720
|
return false;
|
|
9878
10721
|
}
|
|
9879
|
-
const
|
|
9880
|
-
if (!
|
|
10722
|
+
const currentDocument = this.getCurrentDocument();
|
|
10723
|
+
if (!currentDocument) {
|
|
9881
10724
|
return false;
|
|
9882
10725
|
}
|
|
9883
|
-
return JSON.stringify(
|
|
10726
|
+
return (JSON.stringify(serializeDynamicFormAuthoringDocument(baseline)) !==
|
|
10727
|
+
JSON.stringify(serializeDynamicFormAuthoringDocument(currentDocument)));
|
|
9884
10728
|
}
|
|
9885
10729
|
validateJson(text) {
|
|
9886
|
-
const result = {
|
|
10730
|
+
const result = {
|
|
10731
|
+
isValid: false,
|
|
10732
|
+
diagnostics: [],
|
|
10733
|
+
};
|
|
9887
10734
|
if (!text.trim()) {
|
|
9888
10735
|
result.error = 'JSON não pode estar vazio';
|
|
9889
10736
|
this.updateValidationState(result);
|
|
@@ -9891,16 +10738,17 @@ class JsonConfigEditorComponent {
|
|
|
9891
10738
|
}
|
|
9892
10739
|
try {
|
|
9893
10740
|
const parsed = JSON.parse(text);
|
|
9894
|
-
|
|
9895
|
-
|
|
9896
|
-
|
|
9897
|
-
|
|
9898
|
-
|
|
9899
|
-
|
|
9900
|
-
|
|
9901
|
-
|
|
9902
|
-
result.
|
|
9903
|
-
|
|
10741
|
+
const document = parseLegacyOrDynamicFormDocument(parsed);
|
|
10742
|
+
const diagnostics = validateDynamicFormAuthoringInput(parsed);
|
|
10743
|
+
const hasErrors = diagnostics.some((item) => item.level === 'error');
|
|
10744
|
+
result.isValid = !hasErrors;
|
|
10745
|
+
result.document = document;
|
|
10746
|
+
result.config = document.config;
|
|
10747
|
+
result.diagnostics = diagnostics;
|
|
10748
|
+
if (hasErrors) {
|
|
10749
|
+
result.error =
|
|
10750
|
+
diagnostics.find((item) => item.level === 'error')?.message ||
|
|
10751
|
+
'Documento de autoria inválido';
|
|
9904
10752
|
}
|
|
9905
10753
|
this.updateValidationState(result);
|
|
9906
10754
|
}
|
|
@@ -9913,6 +10761,7 @@ class JsonConfigEditorComponent {
|
|
|
9913
10761
|
updateValidationState(result) {
|
|
9914
10762
|
this.isValidJson = result.isValid;
|
|
9915
10763
|
this.jsonError = result.error || '';
|
|
10764
|
+
this.diagnostics = result.diagnostics || [];
|
|
9916
10765
|
this.validationChange.emit(result);
|
|
9917
10766
|
this.editorEvent.emit({ type: 'validation', payload: result });
|
|
9918
10767
|
this.cdr.markForCheck();
|
|
@@ -9954,7 +10803,7 @@ class JsonConfigEditorComponent {
|
|
|
9954
10803
|
}
|
|
9955
10804
|
}
|
|
9956
10805
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: JsonConfigEditorComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: FormConfigService }], target: i0.ɵɵFactoryTarget.Component });
|
|
9957
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: JsonConfigEditorComponent, isStandalone: true, selector: "form-json-config-editor", inputs: { config: "config" }, outputs: { configChange: "configChange", validationChange: "validationChange", editorEvent: "editorEvent" }, ngImport: i0, template: `
|
|
10806
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: JsonConfigEditorComponent, isStandalone: true, selector: "form-json-config-editor", inputs: { config: "config", document: "document" }, outputs: { configChange: "configChange", documentChange: "documentChange", validationChange: "validationChange", editorEvent: "editorEvent" }, ngImport: i0, template: `
|
|
9958
10807
|
<div class="json-config-editor">
|
|
9959
10808
|
<mat-card class="educational-card">
|
|
9960
10809
|
<mat-card-header>
|
|
@@ -9963,8 +10812,13 @@ class JsonConfigEditorComponent {
|
|
|
9963
10812
|
</mat-card-header>
|
|
9964
10813
|
<mat-card-content>
|
|
9965
10814
|
<p>
|
|
9966
|
-
<strong>Para usuários avançados:</strong> Edite
|
|
9967
|
-
|
|
10815
|
+
<strong>Para usuários avançados:</strong> Edite o documento
|
|
10816
|
+
canônico de autoria do formulário diretamente em JSON.
|
|
10817
|
+
</p>
|
|
10818
|
+
<p>
|
|
10819
|
+
O editor JSON aceita apenas o envelope canônico
|
|
10820
|
+
<code>DynamicFormAuthoringDocument</code>. Payloads legados devem
|
|
10821
|
+
ser migrados ou aplicados por APIs de compatibilidade.
|
|
9968
10822
|
</p>
|
|
9969
10823
|
<div class="badges">
|
|
9970
10824
|
<span class="badge" [class.ok]="isValidJson" [class.err]="!isValidJson">
|
|
@@ -10022,9 +10876,28 @@ class JsonConfigEditorComponent {
|
|
|
10022
10876
|
>JSON inválido: {{ jsonError }}</mat-error
|
|
10023
10877
|
>
|
|
10024
10878
|
</mat-form-field>
|
|
10879
|
+
<div
|
|
10880
|
+
class="diagnostics-panel"
|
|
10881
|
+
*ngIf="diagnostics.length"
|
|
10882
|
+
aria-live="polite"
|
|
10883
|
+
role="status"
|
|
10884
|
+
>
|
|
10885
|
+
<div class="diagnostics-title">Diagnósticos do documento</div>
|
|
10886
|
+
<div
|
|
10887
|
+
class="diagnostic-item"
|
|
10888
|
+
*ngFor="let diagnostic of diagnostics"
|
|
10889
|
+
[class.diagnostic-item--error]="diagnostic.level === 'error'"
|
|
10890
|
+
[class.diagnostic-item--warning]="diagnostic.level === 'warning'"
|
|
10891
|
+
[class.diagnostic-item--info]="diagnostic.level === 'info'"
|
|
10892
|
+
>
|
|
10893
|
+
<strong>{{ diagnostic.level | uppercase }}</strong>
|
|
10894
|
+
<span>{{ diagnostic.message }}</span>
|
|
10895
|
+
<code *ngIf="diagnostic.path">{{ diagnostic.path }}</code>
|
|
10896
|
+
</div>
|
|
10897
|
+
</div>
|
|
10025
10898
|
</div>
|
|
10026
10899
|
</div>
|
|
10027
|
-
`, isInline: true, styles: [".json-config-editor{display:flex;flex-direction:column;height:100%}.educational-card{margin-bottom:24px;background-color:var(--md-sys-color-surface-container-low);border-left:4px solid var(--md-sys-color-primary)}.educational-card .mat-mdc-card-header{padding-bottom:8px}.card-icon{background-color:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container);font-size:20px;width:40px;height:40px;display:flex;align-items:center;justify-content:center}.educational-card .mat-mdc-card-title{font-size:1.1rem;font-weight:500;color:var(--md-sys-color-on-surface)}.educational-card .mat-mdc-card-content{color:var(--md-sys-color-on-surface-variant);line-height:1.5}.json-editor-section{flex:1;display:flex;flex-direction:column}.json-editor-toolbar{display:flex;gap:12px;margin-bottom:16px;padding:12px;background-color:var(--md-sys-color-surface-container-low);border-radius:8px;border:1px solid var(--md-sys-color-outline-variant)}.spacer{flex:1}.json-textarea-field{width:100%;flex:1}.badges{display:flex;gap:8px;margin-top:8px}.badge{padding:2px 8px;border-radius:999px;font-size:12px;border:1px solid var(--md-sys-color-outline-variant)}.badge.ok{background:var(--md-sys-color-primary-container)}.badge.err{background:var(--md-sys-color-error-container)}.badge.warn{background:var(--md-sys-color-secondary-container)}.json-textarea{font-family:Monaco,Menlo,Ubuntu Mono,Consolas,monospace!important;font-size:13px!important;line-height:1.4!important;height:100%!important;min-height:300px!important;white-space:pre!important;overflow-wrap:normal!important;overflow-x:auto!important;resize:none!important}.valid-hint{color:var(--md-sys-color-primary)!important}@media(max-width:768px){.json-editor-toolbar{flex-direction:column;gap:8px}.json-textarea{font-size:12px!important;min-height:300px!important}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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.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: 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: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i8$1.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i8$1.MatCardAvatar, selector: "[mat-card-avatar], [matCardAvatar]" }, { kind: "directive", type: i8$1.MatCardContent, selector: "mat-card-content" }, { kind: "component", type: i8$1.MatCardHeader, selector: "mat-card-header" }, { kind: "directive", type: i8$1.MatCardTitle, selector: "mat-card-title, [mat-card-title], [matCardTitle]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
10900
|
+
`, isInline: true, styles: [".json-config-editor{display:flex;flex-direction:column;height:100%}.educational-card{margin-bottom:24px;background-color:var(--md-sys-color-surface-container-low);border-left:4px solid var(--md-sys-color-primary)}.educational-card .mat-mdc-card-header{padding-bottom:8px}.card-icon{background-color:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container);font-size:20px;width:40px;height:40px;display:flex;align-items:center;justify-content:center}.educational-card .mat-mdc-card-title{font-size:1.1rem;font-weight:500;color:var(--md-sys-color-on-surface)}.educational-card .mat-mdc-card-content{color:var(--md-sys-color-on-surface-variant);line-height:1.5}.json-editor-section{flex:1;display:flex;flex-direction:column}.json-editor-toolbar{display:flex;gap:12px;margin-bottom:16px;padding:12px;background-color:var(--md-sys-color-surface-container-low);border-radius:8px;border:1px solid var(--md-sys-color-outline-variant)}.spacer{flex:1}.json-textarea-field{width:100%;flex:1}.badges{display:flex;gap:8px;margin-top:8px}.badge{padding:2px 8px;border-radius:999px;font-size:12px;border:1px solid var(--md-sys-color-outline-variant)}.badge.ok{background:var(--md-sys-color-primary-container)}.badge.err{background:var(--md-sys-color-error-container)}.badge.warn{background:var(--md-sys-color-secondary-container)}.json-textarea{font-family:Monaco,Menlo,Ubuntu Mono,Consolas,monospace!important;font-size:13px!important;line-height:1.4!important;height:100%!important;min-height:300px!important;white-space:pre!important;overflow-wrap:normal!important;overflow-x:auto!important;resize:none!important}.valid-hint{color:var(--md-sys-color-primary)!important}.diagnostics-panel{margin-top:12px;padding:12px;border-radius:8px;border:1px solid var(--md-sys-color-outline-variant);background-color:var(--md-sys-color-surface-container-low);display:grid;gap:8px}.diagnostics-title{font-weight:600;color:var(--md-sys-color-on-surface)}.diagnostic-item{display:grid;gap:2px;padding-left:10px;border-left:3px solid var(--md-sys-color-outline);color:var(--md-sys-color-on-surface-variant)}.diagnostic-item--error{border-left-color:var(--md-sys-color-error)}.diagnostic-item--warning{border-left-color:var(--md-sys-color-tertiary)}.diagnostic-item--info{border-left-color:var(--md-sys-color-primary)}.diagnostic-item code{font-size:12px}@media(max-width:768px){.json-editor-toolbar{flex-direction:column;gap:8px}.json-textarea{font-size:12px!important;min-height:300px!important}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: 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: 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: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i8$1.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i8$1.MatCardAvatar, selector: "[mat-card-avatar], [matCardAvatar]" }, { kind: "directive", type: i8$1.MatCardContent, selector: "mat-card-content" }, { kind: "component", type: i8$1.MatCardHeader, selector: "mat-card-header" }, { kind: "directive", type: i8$1.MatCardTitle, selector: "mat-card-title, [mat-card-title], [matCardTitle]" }, { kind: "pipe", type: i1$1.UpperCasePipe, name: "uppercase" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
10028
10901
|
}
|
|
10029
10902
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: JsonConfigEditorComponent, decorators: [{
|
|
10030
10903
|
type: Component,
|
|
@@ -10045,8 +10918,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
10045
10918
|
</mat-card-header>
|
|
10046
10919
|
<mat-card-content>
|
|
10047
10920
|
<p>
|
|
10048
|
-
<strong>Para usuários avançados:</strong> Edite
|
|
10049
|
-
|
|
10921
|
+
<strong>Para usuários avançados:</strong> Edite o documento
|
|
10922
|
+
canônico de autoria do formulário diretamente em JSON.
|
|
10923
|
+
</p>
|
|
10924
|
+
<p>
|
|
10925
|
+
O editor JSON aceita apenas o envelope canônico
|
|
10926
|
+
<code>DynamicFormAuthoringDocument</code>. Payloads legados devem
|
|
10927
|
+
ser migrados ou aplicados por APIs de compatibilidade.
|
|
10050
10928
|
</p>
|
|
10051
10929
|
<div class="badges">
|
|
10052
10930
|
<span class="badge" [class.ok]="isValidJson" [class.err]="!isValidJson">
|
|
@@ -10104,13 +10982,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
10104
10982
|
>JSON inválido: {{ jsonError }}</mat-error
|
|
10105
10983
|
>
|
|
10106
10984
|
</mat-form-field>
|
|
10985
|
+
<div
|
|
10986
|
+
class="diagnostics-panel"
|
|
10987
|
+
*ngIf="diagnostics.length"
|
|
10988
|
+
aria-live="polite"
|
|
10989
|
+
role="status"
|
|
10990
|
+
>
|
|
10991
|
+
<div class="diagnostics-title">Diagnósticos do documento</div>
|
|
10992
|
+
<div
|
|
10993
|
+
class="diagnostic-item"
|
|
10994
|
+
*ngFor="let diagnostic of diagnostics"
|
|
10995
|
+
[class.diagnostic-item--error]="diagnostic.level === 'error'"
|
|
10996
|
+
[class.diagnostic-item--warning]="diagnostic.level === 'warning'"
|
|
10997
|
+
[class.diagnostic-item--info]="diagnostic.level === 'info'"
|
|
10998
|
+
>
|
|
10999
|
+
<strong>{{ diagnostic.level | uppercase }}</strong>
|
|
11000
|
+
<span>{{ diagnostic.message }}</span>
|
|
11001
|
+
<code *ngIf="diagnostic.path">{{ diagnostic.path }}</code>
|
|
11002
|
+
</div>
|
|
11003
|
+
</div>
|
|
10107
11004
|
</div>
|
|
10108
11005
|
</div>
|
|
10109
|
-
`, styles: [".json-config-editor{display:flex;flex-direction:column;height:100%}.educational-card{margin-bottom:24px;background-color:var(--md-sys-color-surface-container-low);border-left:4px solid var(--md-sys-color-primary)}.educational-card .mat-mdc-card-header{padding-bottom:8px}.card-icon{background-color:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container);font-size:20px;width:40px;height:40px;display:flex;align-items:center;justify-content:center}.educational-card .mat-mdc-card-title{font-size:1.1rem;font-weight:500;color:var(--md-sys-color-on-surface)}.educational-card .mat-mdc-card-content{color:var(--md-sys-color-on-surface-variant);line-height:1.5}.json-editor-section{flex:1;display:flex;flex-direction:column}.json-editor-toolbar{display:flex;gap:12px;margin-bottom:16px;padding:12px;background-color:var(--md-sys-color-surface-container-low);border-radius:8px;border:1px solid var(--md-sys-color-outline-variant)}.spacer{flex:1}.json-textarea-field{width:100%;flex:1}.badges{display:flex;gap:8px;margin-top:8px}.badge{padding:2px 8px;border-radius:999px;font-size:12px;border:1px solid var(--md-sys-color-outline-variant)}.badge.ok{background:var(--md-sys-color-primary-container)}.badge.err{background:var(--md-sys-color-error-container)}.badge.warn{background:var(--md-sys-color-secondary-container)}.json-textarea{font-family:Monaco,Menlo,Ubuntu Mono,Consolas,monospace!important;font-size:13px!important;line-height:1.4!important;height:100%!important;min-height:300px!important;white-space:pre!important;overflow-wrap:normal!important;overflow-x:auto!important;resize:none!important}.valid-hint{color:var(--md-sys-color-primary)!important}@media(max-width:768px){.json-editor-toolbar{flex-direction:column;gap:8px}.json-textarea{font-size:12px!important;min-height:300px!important}}\n"] }]
|
|
11006
|
+
`, styles: [".json-config-editor{display:flex;flex-direction:column;height:100%}.educational-card{margin-bottom:24px;background-color:var(--md-sys-color-surface-container-low);border-left:4px solid var(--md-sys-color-primary)}.educational-card .mat-mdc-card-header{padding-bottom:8px}.card-icon{background-color:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container);font-size:20px;width:40px;height:40px;display:flex;align-items:center;justify-content:center}.educational-card .mat-mdc-card-title{font-size:1.1rem;font-weight:500;color:var(--md-sys-color-on-surface)}.educational-card .mat-mdc-card-content{color:var(--md-sys-color-on-surface-variant);line-height:1.5}.json-editor-section{flex:1;display:flex;flex-direction:column}.json-editor-toolbar{display:flex;gap:12px;margin-bottom:16px;padding:12px;background-color:var(--md-sys-color-surface-container-low);border-radius:8px;border:1px solid var(--md-sys-color-outline-variant)}.spacer{flex:1}.json-textarea-field{width:100%;flex:1}.badges{display:flex;gap:8px;margin-top:8px}.badge{padding:2px 8px;border-radius:999px;font-size:12px;border:1px solid var(--md-sys-color-outline-variant)}.badge.ok{background:var(--md-sys-color-primary-container)}.badge.err{background:var(--md-sys-color-error-container)}.badge.warn{background:var(--md-sys-color-secondary-container)}.json-textarea{font-family:Monaco,Menlo,Ubuntu Mono,Consolas,monospace!important;font-size:13px!important;line-height:1.4!important;height:100%!important;min-height:300px!important;white-space:pre!important;overflow-wrap:normal!important;overflow-x:auto!important;resize:none!important}.valid-hint{color:var(--md-sys-color-primary)!important}.diagnostics-panel{margin-top:12px;padding:12px;border-radius:8px;border:1px solid var(--md-sys-color-outline-variant);background-color:var(--md-sys-color-surface-container-low);display:grid;gap:8px}.diagnostics-title{font-weight:600;color:var(--md-sys-color-on-surface)}.diagnostic-item{display:grid;gap:2px;padding-left:10px;border-left:3px solid var(--md-sys-color-outline);color:var(--md-sys-color-on-surface-variant)}.diagnostic-item--error{border-left-color:var(--md-sys-color-error)}.diagnostic-item--warning{border-left-color:var(--md-sys-color-tertiary)}.diagnostic-item--info{border-left-color:var(--md-sys-color-primary)}.diagnostic-item code{font-size:12px}@media(max-width:768px){.json-editor-toolbar{flex-direction:column;gap:8px}.json-textarea{font-size:12px!important;min-height:300px!important}}\n"] }]
|
|
10110
11007
|
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: FormConfigService }], propDecorators: { config: [{
|
|
10111
11008
|
type: Input
|
|
11009
|
+
}], document: [{
|
|
11010
|
+
type: Input
|
|
10112
11011
|
}], configChange: [{
|
|
10113
11012
|
type: Output
|
|
11013
|
+
}], documentChange: [{
|
|
11014
|
+
type: Output
|
|
10114
11015
|
}], validationChange: [{
|
|
10115
11016
|
type: Output
|
|
10116
11017
|
}], editorEvent: [{
|
|
@@ -12127,8 +13028,8 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
12127
13028
|
configChange = new EventEmitter();
|
|
12128
13029
|
containerStylesText = '';
|
|
12129
13030
|
globalActionCatalogSource = inject(GLOBAL_ACTION_CATALOG, { optional: true }) ?? [];
|
|
12130
|
-
globalActionCatalog = getGlobalActionCatalog(this.globalActionCatalogSource);
|
|
12131
13031
|
legacyActionSpecs = GLOBAL_ACTION_SPEC_CATALOG;
|
|
13032
|
+
globalActionCatalog = this.buildGlobalActionCatalog();
|
|
12132
13033
|
customActionValue = '__custom__';
|
|
12133
13034
|
actionFieldDrafts = new Map();
|
|
12134
13035
|
actionFieldErrors = new Map();
|
|
@@ -12194,7 +13095,7 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
12194
13095
|
this.containerStylesText = styles ? JSON.stringify(styles, null, 2) : '';
|
|
12195
13096
|
}
|
|
12196
13097
|
getActionSpecById(id) {
|
|
12197
|
-
return this.
|
|
13098
|
+
return this.globalActionCatalog.find((item) => item.id === id);
|
|
12198
13099
|
}
|
|
12199
13100
|
getCustomActionSelectValue(value) {
|
|
12200
13101
|
const parsed = this.parseActionValue(value);
|
|
@@ -12212,6 +13113,7 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
12212
13113
|
const current = this.actions.custom?.[index];
|
|
12213
13114
|
if (!current)
|
|
12214
13115
|
return;
|
|
13116
|
+
this.clearActionFieldState(current);
|
|
12215
13117
|
if (value === this.customActionValue) {
|
|
12216
13118
|
const parsed = this.parseActionValue(current.action);
|
|
12217
13119
|
if (parsed.isGlobal) {
|
|
@@ -12260,6 +13162,38 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
12260
13162
|
return false;
|
|
12261
13163
|
return this.globalActionCatalog.some((item) => item.id === id);
|
|
12262
13164
|
}
|
|
13165
|
+
buildGlobalActionCatalog() {
|
|
13166
|
+
const merged = new Map();
|
|
13167
|
+
for (const spec of this.legacyActionSpecs) {
|
|
13168
|
+
merged.set(spec.id, spec);
|
|
13169
|
+
}
|
|
13170
|
+
const injectedCatalog = getGlobalActionCatalog(this.globalActionCatalogSource);
|
|
13171
|
+
const fallbackCatalog = injectedCatalog.length ? injectedCatalog : PRAXIS_GLOBAL_ACTION_CATALOG;
|
|
13172
|
+
for (const entry of fallbackCatalog) {
|
|
13173
|
+
if (merged.has(entry.id))
|
|
13174
|
+
continue;
|
|
13175
|
+
merged.set(entry.id, this.mapCatalogEntryToActionSpec(entry));
|
|
13176
|
+
}
|
|
13177
|
+
return Array.from(merged.values());
|
|
13178
|
+
}
|
|
13179
|
+
mapCatalogEntryToActionSpec(entry) {
|
|
13180
|
+
const hasPayloadSchema = !!entry.payloadSchema;
|
|
13181
|
+
return {
|
|
13182
|
+
id: entry.id,
|
|
13183
|
+
label: entry.label,
|
|
13184
|
+
description: entry.description || '',
|
|
13185
|
+
param: hasPayloadSchema
|
|
13186
|
+
? {
|
|
13187
|
+
label: 'Payload (JSON opcional)',
|
|
13188
|
+
placeholder: entry.payloadSchema?.example
|
|
13189
|
+
? JSON.stringify(entry.payloadSchema.example)
|
|
13190
|
+
: '{ }',
|
|
13191
|
+
hint: 'Use JSON quando a ação global do app exigir payload estruturado.',
|
|
13192
|
+
required: !!entry.payloadSchema?.required?.length,
|
|
13193
|
+
}
|
|
13194
|
+
: undefined,
|
|
13195
|
+
};
|
|
13196
|
+
}
|
|
12263
13197
|
getGlobalActionSchema(action) {
|
|
12264
13198
|
const parsed = this.parseActionValue(action.action);
|
|
12265
13199
|
if (!parsed.isGlobal)
|
|
@@ -12284,83 +13218,122 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
12284
13218
|
return errors?.[key] || '';
|
|
12285
13219
|
}
|
|
12286
13220
|
getGlobalActionFieldValue(action, field) {
|
|
12287
|
-
|
|
12288
|
-
|
|
12289
|
-
if (draft !== undefined)
|
|
12290
|
-
return draft;
|
|
12291
|
-
}
|
|
13221
|
+
const draftKey = this.getActionDraftKey(action);
|
|
13222
|
+
const draft = this.actionFieldDrafts.get(draftKey)?.[field.key];
|
|
12292
13223
|
const info = this.getActionParamInfo(action.action);
|
|
12293
13224
|
const raw = info.param || '';
|
|
12294
13225
|
const json = info.json || {};
|
|
12295
13226
|
const hasJson = info.isJson;
|
|
13227
|
+
let value;
|
|
12296
13228
|
switch (field.key) {
|
|
12297
13229
|
case 'message':
|
|
12298
|
-
|
|
13230
|
+
value = json.message ?? json.text ?? (hasJson ? '' : raw);
|
|
13231
|
+
break;
|
|
12299
13232
|
case 'url':
|
|
12300
|
-
if (json.url)
|
|
12301
|
-
|
|
13233
|
+
if (json.url) {
|
|
13234
|
+
value = json.url;
|
|
13235
|
+
break;
|
|
13236
|
+
}
|
|
12302
13237
|
if (!hasJson && info.id === 'apiCall') {
|
|
12303
13238
|
const parsed = this.parseMethodAndUrl(raw);
|
|
12304
|
-
|
|
13239
|
+
value = parsed.url || '';
|
|
13240
|
+
break;
|
|
12305
13241
|
}
|
|
12306
|
-
|
|
13242
|
+
value = hasJson ? '' : raw;
|
|
13243
|
+
break;
|
|
12307
13244
|
case 'method': {
|
|
12308
|
-
if (json.method)
|
|
12309
|
-
|
|
13245
|
+
if (json.method) {
|
|
13246
|
+
value = String(json.method);
|
|
13247
|
+
break;
|
|
13248
|
+
}
|
|
12310
13249
|
if (!hasJson) {
|
|
12311
13250
|
const parsed = this.parseMethodAndUrl(raw);
|
|
12312
|
-
|
|
13251
|
+
value = parsed.method || '';
|
|
13252
|
+
break;
|
|
12313
13253
|
}
|
|
12314
|
-
|
|
13254
|
+
value = '';
|
|
13255
|
+
break;
|
|
12315
13256
|
}
|
|
12316
13257
|
case 'bodySource':
|
|
12317
|
-
|
|
13258
|
+
value = json.bodySource ?? '';
|
|
13259
|
+
break;
|
|
12318
13260
|
case 'headers':
|
|
12319
|
-
|
|
13261
|
+
value = this.stringifyJson(json.headers);
|
|
13262
|
+
break;
|
|
12320
13263
|
case 'body':
|
|
12321
|
-
|
|
13264
|
+
value = this.stringifyJson(json.body);
|
|
13265
|
+
break;
|
|
12322
13266
|
case 'contentData':
|
|
12323
|
-
|
|
13267
|
+
value = this.stringifyJson(json.content?.data);
|
|
13268
|
+
break;
|
|
12324
13269
|
case 'styles':
|
|
12325
|
-
|
|
13270
|
+
value = this.stringifyJson(json.styles);
|
|
13271
|
+
break;
|
|
12326
13272
|
case 'positionTop':
|
|
12327
|
-
|
|
13273
|
+
value = json.position?.top ?? '';
|
|
13274
|
+
break;
|
|
12328
13275
|
case 'positionRight':
|
|
12329
|
-
|
|
13276
|
+
value = json.position?.right ?? '';
|
|
13277
|
+
break;
|
|
12330
13278
|
case 'positionBottom':
|
|
12331
|
-
|
|
13279
|
+
value = json.position?.bottom ?? '';
|
|
13280
|
+
break;
|
|
12332
13281
|
case 'positionLeft':
|
|
12333
|
-
|
|
13282
|
+
value = json.position?.left ?? '';
|
|
13283
|
+
break;
|
|
12334
13284
|
case 'animationType':
|
|
12335
|
-
|
|
13285
|
+
value = json.animation?.type ?? '';
|
|
13286
|
+
break;
|
|
12336
13287
|
case 'animationDirection':
|
|
12337
|
-
|
|
13288
|
+
value = json.animation?.direction ?? '';
|
|
13289
|
+
break;
|
|
12338
13290
|
case 'animationDuration':
|
|
12339
|
-
|
|
13291
|
+
value = json.animation?.duration ?? '';
|
|
13292
|
+
break;
|
|
12340
13293
|
case 'contentType':
|
|
12341
|
-
|
|
13294
|
+
value = json.contentType ?? json.content?.type ?? '';
|
|
13295
|
+
break;
|
|
12342
13296
|
case 'templateId':
|
|
12343
|
-
|
|
13297
|
+
value = json.templateId ?? '';
|
|
13298
|
+
break;
|
|
12344
13299
|
case 'componentRef':
|
|
12345
|
-
|
|
13300
|
+
value = json.componentRef ?? '';
|
|
13301
|
+
break;
|
|
12346
13302
|
case 'safeHtml':
|
|
12347
|
-
|
|
13303
|
+
value = json.safeHtml ?? '';
|
|
13304
|
+
break;
|
|
12348
13305
|
case 'mode':
|
|
12349
|
-
|
|
13306
|
+
value = json.mode === 'dialog' || json.useDialog === true;
|
|
13307
|
+
break;
|
|
12350
13308
|
case 'newTab':
|
|
12351
|
-
|
|
13309
|
+
value = json.newTab ?? false;
|
|
13310
|
+
break;
|
|
12352
13311
|
case 'replaceUrl':
|
|
12353
|
-
|
|
13312
|
+
value = json.replaceUrl ?? false;
|
|
13313
|
+
break;
|
|
12354
13314
|
case 'target':
|
|
12355
|
-
|
|
13315
|
+
value = json.target ?? '';
|
|
13316
|
+
break;
|
|
12356
13317
|
case 'text':
|
|
12357
|
-
|
|
13318
|
+
value = json.text ?? (hasJson ? '' : raw);
|
|
13319
|
+
break;
|
|
12358
13320
|
case 'key':
|
|
12359
13321
|
case 'field':
|
|
12360
|
-
|
|
13322
|
+
value = json[field.key] ?? (hasJson ? '' : raw);
|
|
13323
|
+
break;
|
|
12361
13324
|
default:
|
|
12362
|
-
|
|
13325
|
+
value = json[field.key] ?? '';
|
|
13326
|
+
break;
|
|
13327
|
+
}
|
|
13328
|
+
if (draft !== undefined) {
|
|
13329
|
+
if (this.isEquivalentActionFieldValue(value, draft, field.type)) {
|
|
13330
|
+
this.clearActionFieldDraft(action, field.key);
|
|
13331
|
+
}
|
|
13332
|
+
else {
|
|
13333
|
+
return draft;
|
|
13334
|
+
}
|
|
12363
13335
|
}
|
|
13336
|
+
return value;
|
|
12364
13337
|
}
|
|
12365
13338
|
onGlobalActionFieldChange(action, field, value, index) {
|
|
12366
13339
|
const selectedId = this.getCustomActionSelectValue(action.action);
|
|
@@ -12369,12 +13342,9 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
12369
13342
|
const schema = getGlobalActionUiSchema(selectedId);
|
|
12370
13343
|
if (!schema)
|
|
12371
13344
|
return;
|
|
13345
|
+
this.setActionFieldDraft(action, field.key, value);
|
|
12372
13346
|
if (field.type === 'json') {
|
|
12373
13347
|
const raw = String(value || '');
|
|
12374
|
-
const draftKey = this.getActionDraftKey(action);
|
|
12375
|
-
const drafts = this.actionFieldDrafts.get(draftKey) || {};
|
|
12376
|
-
drafts[field.key] = raw;
|
|
12377
|
-
this.actionFieldDrafts.set(draftKey, drafts);
|
|
12378
13348
|
if (raw.trim()) {
|
|
12379
13349
|
try {
|
|
12380
13350
|
JSON.parse(raw);
|
|
@@ -12461,6 +13431,39 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
12461
13431
|
getActionDraftKey(action) {
|
|
12462
13432
|
return String(action.id || action.action || action.label || 'action');
|
|
12463
13433
|
}
|
|
13434
|
+
setActionFieldDraft(action, key, value) {
|
|
13435
|
+
const draftKey = this.getActionDraftKey(action);
|
|
13436
|
+
const drafts = this.actionFieldDrafts.get(draftKey) || {};
|
|
13437
|
+
drafts[key] = value;
|
|
13438
|
+
this.actionFieldDrafts.set(draftKey, drafts);
|
|
13439
|
+
}
|
|
13440
|
+
clearActionFieldDraft(action, key) {
|
|
13441
|
+
const draftKey = this.getActionDraftKey(action);
|
|
13442
|
+
const drafts = this.actionFieldDrafts.get(draftKey);
|
|
13443
|
+
if (!drafts)
|
|
13444
|
+
return;
|
|
13445
|
+
delete drafts[key];
|
|
13446
|
+
if (Object.keys(drafts).length) {
|
|
13447
|
+
this.actionFieldDrafts.set(draftKey, drafts);
|
|
13448
|
+
}
|
|
13449
|
+
else {
|
|
13450
|
+
this.actionFieldDrafts.delete(draftKey);
|
|
13451
|
+
}
|
|
13452
|
+
}
|
|
13453
|
+
clearActionFieldState(action) {
|
|
13454
|
+
const draftKey = this.getActionDraftKey(action);
|
|
13455
|
+
this.actionFieldDrafts.delete(draftKey);
|
|
13456
|
+
this.actionFieldErrors.delete(draftKey);
|
|
13457
|
+
}
|
|
13458
|
+
isEquivalentActionFieldValue(actual, draft, fieldType) {
|
|
13459
|
+
if (fieldType === 'toggle') {
|
|
13460
|
+
return Boolean(actual) === Boolean(draft);
|
|
13461
|
+
}
|
|
13462
|
+
if (fieldType === 'number') {
|
|
13463
|
+
return String(actual ?? '') === String(draft ?? '');
|
|
13464
|
+
}
|
|
13465
|
+
return String(actual ?? '') === String(draft ?? '');
|
|
13466
|
+
}
|
|
12464
13467
|
collectGlobalActionFieldValues(action, fields) {
|
|
12465
13468
|
const values = {};
|
|
12466
13469
|
fields.forEach((field) => {
|
|
@@ -13203,7 +14206,7 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
13203
14206
|
<mat-accordion multi>
|
|
13204
14207
|
@for (
|
|
13205
14208
|
customAction of actions.custom;
|
|
13206
|
-
track customAction;
|
|
14209
|
+
track customAction.id || customAction.action || $index;
|
|
13207
14210
|
let i = $index
|
|
13208
14211
|
) {
|
|
13209
14212
|
<mat-expansion-panel>
|
|
@@ -13871,7 +14874,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
13871
14874
|
<mat-accordion multi>
|
|
13872
14875
|
@for (
|
|
13873
14876
|
customAction of actions.custom;
|
|
13874
|
-
track customAction;
|
|
14877
|
+
track customAction.id || customAction.action || $index;
|
|
13875
14878
|
let i = $index
|
|
13876
14879
|
) {
|
|
13877
14880
|
<mat-expansion-panel>
|
|
@@ -14921,55 +15924,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
14921
15924
|
type: Output
|
|
14922
15925
|
}] } });
|
|
14923
15926
|
|
|
14924
|
-
/**
|
|
14925
|
-
* Deep clone and normalize a form configuration ensuring required collections
|
|
14926
|
-
* are always present to avoid unintended mutations.
|
|
14927
|
-
*/
|
|
14928
|
-
function normalizeFormConfig(config) {
|
|
14929
|
-
const cloned = config ? structuredClone(config) : createDefaultFormConfig();
|
|
14930
|
-
// Apply canonical normalization from @praxisui/core first (aliases → canônico)
|
|
14931
|
-
const canonical = normalizeFormConfig$1(cloned);
|
|
14932
|
-
const withCollections = ensureIds({
|
|
14933
|
-
...createDefaultFormConfig(),
|
|
14934
|
-
...canonical,
|
|
14935
|
-
sections: canonical.sections ?? [],
|
|
14936
|
-
fieldMetadata: canonical.fieldMetadata ?? [],
|
|
14937
|
-
formBlocksBefore: canonical.formBlocksBefore ?? [],
|
|
14938
|
-
formBlocksBeforeActions: canonical.formBlocksBeforeActions ?? [],
|
|
14939
|
-
formBlocksAfter: canonical.formBlocksAfter ?? [],
|
|
14940
|
-
});
|
|
14941
|
-
return applyDefaultSpans(withCollections);
|
|
14942
|
-
}
|
|
14943
|
-
/**
|
|
14944
|
-
* Apply default grid spans when not provided by the config.
|
|
14945
|
-
* Rule: for each row, if none of its columns declares span.md,
|
|
14946
|
-
* distribute equally across 12 columns on md+; keep xs/sm stacked (12).
|
|
14947
|
-
*/
|
|
14948
|
-
function applyDefaultSpans(config) {
|
|
14949
|
-
const next = structuredClone(config);
|
|
14950
|
-
for (const section of next.sections || []) {
|
|
14951
|
-
for (const row of section.rows || []) {
|
|
14952
|
-
const cols = row.columns || [];
|
|
14953
|
-
if (!cols.length)
|
|
14954
|
-
continue;
|
|
14955
|
-
const hasAnyMdSpan = cols.some((c) => c.span?.md != null);
|
|
14956
|
-
if (hasAnyMdSpan)
|
|
14957
|
-
continue;
|
|
14958
|
-
const per = Math.max(1, Math.floor(12 / cols.length));
|
|
14959
|
-
for (const col of cols) {
|
|
14960
|
-
const span = col.span || {};
|
|
14961
|
-
span.xs = 12;
|
|
14962
|
-
span.sm = 12;
|
|
14963
|
-
span.md = per;
|
|
14964
|
-
span.lg = span.lg ?? per;
|
|
14965
|
-
span.xl = span.xl ?? per;
|
|
14966
|
-
col.span = span;
|
|
14967
|
-
}
|
|
14968
|
-
}
|
|
14969
|
-
}
|
|
14970
|
-
return next;
|
|
14971
|
-
}
|
|
14972
|
-
|
|
14973
15927
|
/**
|
|
14974
15928
|
* Converts a RuleBuilderState produced by the visual builder into
|
|
14975
15929
|
* an array of FormLayoutRule objects understood by the dynamic form.
|
|
@@ -15355,11 +16309,15 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15355
16309
|
ruleBuilderState;
|
|
15356
16310
|
currentRules = [];
|
|
15357
16311
|
initialConfig;
|
|
16312
|
+
initialDocument;
|
|
16313
|
+
openedWithCanonicalDocument;
|
|
15358
16314
|
backConfig;
|
|
16315
|
+
includeBackConfigBlock = false;
|
|
15359
16316
|
formId;
|
|
15360
16317
|
componentKeyId;
|
|
15361
16318
|
// Input patch: controls like mode (dados)
|
|
15362
16319
|
inputMode = 'create';
|
|
16320
|
+
includeBindingsBlock = false;
|
|
15363
16321
|
editorMode;
|
|
15364
16322
|
// Indica se o editor foi aberto com o formulário em modo apresentação
|
|
15365
16323
|
isPresentationMode = false;
|
|
@@ -15395,6 +16353,8 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15395
16353
|
snoozeMs: 24 * 60 * 60 * 1000,
|
|
15396
16354
|
autoOpenSettingsOnOutdated: false,
|
|
15397
16355
|
};
|
|
16356
|
+
includePresentationBlock = false;
|
|
16357
|
+
includeSchemaPrefsBlock = false;
|
|
15398
16358
|
// Read-only current server meta (if any)
|
|
15399
16359
|
serverMeta;
|
|
15400
16360
|
destroy$ = new Subject();
|
|
@@ -15421,11 +16381,18 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15421
16381
|
this.settingsPanel = settingsPanel;
|
|
15422
16382
|
this.cdr = cdr;
|
|
15423
16383
|
const data = injectedData;
|
|
15424
|
-
|
|
15425
|
-
|
|
16384
|
+
this.openedWithCanonicalDocument =
|
|
16385
|
+
data?.document?.kind === 'praxis.dynamic-form.editor' ||
|
|
16386
|
+
data?.kind === 'praxis.dynamic-form.editor';
|
|
16387
|
+
const initialDocument = parseLegacyOrDynamicFormDocument(data?.document ?? injectedData);
|
|
16388
|
+
this.initialDocument = initialDocument;
|
|
16389
|
+
const formCfg = initialDocument.config;
|
|
16390
|
+
this.backConfig = initialDocument.contextSnapshot?.backConfig;
|
|
16391
|
+
this.includeBackConfigBlock = Object.prototype.hasOwnProperty.call(initialDocument.contextSnapshot || {}, 'backConfig');
|
|
15426
16392
|
this.formId = data?.formId;
|
|
15427
16393
|
this.componentKeyId = data?.componentKeyId;
|
|
15428
|
-
this.
|
|
16394
|
+
this.includeBindingsBlock = Object.prototype.hasOwnProperty.call(initialDocument.bindings || {}, 'mode');
|
|
16395
|
+
this.inputMode = this.normalizeMode(initialDocument.bindings?.mode);
|
|
15429
16396
|
this.editorMode = this.inputMode;
|
|
15430
16397
|
// Flag de contexto do host (praxis-dynamic-form)
|
|
15431
16398
|
this.isPresentationMode = !!data?.presentationMode;
|
|
@@ -15439,9 +16406,11 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15439
16406
|
});
|
|
15440
16407
|
}
|
|
15441
16408
|
catch { }
|
|
15442
|
-
|
|
15443
|
-
|
|
15444
|
-
|
|
16409
|
+
this.backConfig = this.createBackConfigState(this.backConfig);
|
|
16410
|
+
this.includePresentationBlock = Object.prototype.hasOwnProperty.call(initialDocument.contextSnapshot || {}, 'presentation');
|
|
16411
|
+
this.presentationPrefs = this.mergePresentationPrefs(initialDocument.contextSnapshot?.presentation);
|
|
16412
|
+
this.includeSchemaPrefsBlock = Object.prototype.hasOwnProperty.call(initialDocument.contextSnapshot || {}, 'schemaPrefs');
|
|
16413
|
+
this.schemaPrefs = this.mergeSchemaPrefs(initialDocument.contextSnapshot?.schemaPrefs);
|
|
15445
16414
|
this.editedConfig = structuredClone(this.initialConfig);
|
|
15446
16415
|
this.configService.loadConfig(structuredClone(this.initialConfig));
|
|
15447
16416
|
}
|
|
@@ -15461,11 +16430,19 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15461
16430
|
this.isBusy$.next(true);
|
|
15462
16431
|
try {
|
|
15463
16432
|
this.editedConfig = structuredClone(this.initialConfig);
|
|
16433
|
+
this.includeBackConfigBlock = Object.prototype.hasOwnProperty.call(this.initialDocument.contextSnapshot || {}, 'backConfig');
|
|
16434
|
+
this.backConfig = this.createBackConfigState(this.initialDocument.contextSnapshot?.backConfig);
|
|
16435
|
+
this.includeBindingsBlock = Object.prototype.hasOwnProperty.call(this.initialDocument.bindings || {}, 'mode');
|
|
16436
|
+
this.inputMode = this.normalizeMode(this.initialDocument.bindings?.mode);
|
|
16437
|
+
this.includePresentationBlock = Object.prototype.hasOwnProperty.call(this.initialDocument.contextSnapshot || {}, 'presentation');
|
|
16438
|
+
this.presentationPrefs = this.mergePresentationPrefs(this.initialDocument.contextSnapshot?.presentation);
|
|
16439
|
+
this.includeSchemaPrefsBlock = Object.prototype.hasOwnProperty.call(this.initialDocument.contextSnapshot || {}, 'schemaPrefs');
|
|
16440
|
+
this.schemaPrefs = this.mergeSchemaPrefs(this.initialDocument.contextSnapshot?.schemaPrefs);
|
|
15464
16441
|
try {
|
|
15465
16442
|
this.debugLog('[FormConfigEditor] reset()');
|
|
15466
16443
|
}
|
|
15467
16444
|
catch { }
|
|
15468
|
-
this.jsonEditor?.
|
|
16445
|
+
this.jsonEditor?.updateJsonFromDocument(this.buildAuthoringDocument());
|
|
15469
16446
|
this.ruleBuilderState =
|
|
15470
16447
|
this.editedConfig.formRulesState ||
|
|
15471
16448
|
formLayoutRulesToBuilderState(this.editedConfig.formRules || []);
|
|
@@ -15481,11 +16458,13 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15481
16458
|
}
|
|
15482
16459
|
// Public: referenced in template for (ngModelChange) handlers
|
|
15483
16460
|
updateDirtyState(reason = 'unknown') {
|
|
15484
|
-
|
|
15485
|
-
const
|
|
15486
|
-
const b = this.stripLegacy(structuredClone(this.editedConfig));
|
|
16461
|
+
const a = serializeDynamicFormAuthoringDocument(this.initialDocument);
|
|
16462
|
+
const b = serializeDynamicFormAuthoringDocument(this.buildAuthoringDocument());
|
|
15487
16463
|
const hasChanges = stableStringify(a) !== stableStringify(b);
|
|
15488
16464
|
this.isDirty$.next(hasChanges);
|
|
16465
|
+
if (!reason.startsWith('json-')) {
|
|
16466
|
+
this.jsonEditor?.updateJsonFromDocument(this.buildAuthoringDocument());
|
|
16467
|
+
}
|
|
15489
16468
|
try {
|
|
15490
16469
|
if (hasChanges) {
|
|
15491
16470
|
const diffs = diffObjects(a, b, [], 12);
|
|
@@ -15500,51 +16479,62 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15500
16479
|
// Para outras validações futuras, verificar se não há validação JSON em andamento
|
|
15501
16480
|
}
|
|
15502
16481
|
getSettingsValue() {
|
|
15503
|
-
const
|
|
16482
|
+
const document = this.buildAuthoringDocument();
|
|
15504
16483
|
try {
|
|
15505
|
-
this.debugLog('[FormConfigEditor] getSettingsValue()', { sections:
|
|
16484
|
+
this.debugLog('[FormConfigEditor] getSettingsValue()', { sections: document.config.sections?.length || 0 });
|
|
15506
16485
|
}
|
|
15507
16486
|
catch { }
|
|
15508
|
-
return
|
|
15509
|
-
formConfig: sanitized,
|
|
15510
|
-
backConfig: this.backConfig,
|
|
15511
|
-
presentation: this.presentationPrefs,
|
|
15512
|
-
inputsPatch: { mode: this.inputMode },
|
|
15513
|
-
schemaPrefs: this.schemaPrefs,
|
|
15514
|
-
};
|
|
16487
|
+
return serializeDynamicFormAuthoringDocument(document);
|
|
15515
16488
|
}
|
|
15516
16489
|
onSave() {
|
|
15517
16490
|
this.isBusy$.next(true);
|
|
15518
16491
|
try {
|
|
15519
|
-
const
|
|
16492
|
+
const document = this.buildAuthoringDocument();
|
|
15520
16493
|
try {
|
|
15521
|
-
this.debugLog('[FormConfigEditor] onSave()', { sections:
|
|
16494
|
+
this.debugLog('[FormConfigEditor] onSave()', { sections: document.config.sections?.length || 0 });
|
|
15522
16495
|
}
|
|
15523
16496
|
catch { }
|
|
15524
|
-
return
|
|
15525
|
-
formConfig: sanitized,
|
|
15526
|
-
backConfig: this.backConfig,
|
|
15527
|
-
presentation: this.presentationPrefs,
|
|
15528
|
-
inputsPatch: { mode: this.inputMode },
|
|
15529
|
-
schemaPrefs: this.schemaPrefs,
|
|
15530
|
-
};
|
|
16497
|
+
return serializeDynamicFormAuthoringDocument(document);
|
|
15531
16498
|
}
|
|
15532
16499
|
finally {
|
|
15533
16500
|
this.isBusy$.next(false);
|
|
15534
16501
|
}
|
|
15535
16502
|
}
|
|
15536
16503
|
// (Resumo/ações rápidas removidos da aba Geral conforme orientação)
|
|
15537
|
-
onJsonConfigChange(
|
|
15538
|
-
|
|
16504
|
+
onJsonConfigChange(newValue) {
|
|
16505
|
+
const isCanonicalDocument = newValue?.kind ===
|
|
16506
|
+
'praxis.dynamic-form.editor';
|
|
16507
|
+
const document = isCanonicalDocument
|
|
16508
|
+
? newValue
|
|
16509
|
+
: parseLegacyOrDynamicFormDocument(newValue);
|
|
16510
|
+
this.editedConfig = this.stripLegacy(document.config);
|
|
16511
|
+
this.includeBackConfigBlock = isCanonicalDocument
|
|
16512
|
+
? Object.prototype.hasOwnProperty.call(document.contextSnapshot || {}, 'backConfig')
|
|
16513
|
+
: this.includeBackConfigBlock || !!document.contextSnapshot?.backConfig;
|
|
16514
|
+
this.backConfig = this.createBackConfigState(isCanonicalDocument
|
|
16515
|
+
? document.contextSnapshot?.backConfig
|
|
16516
|
+
: document.contextSnapshot?.backConfig || this.backConfig);
|
|
16517
|
+
this.includeBindingsBlock = isCanonicalDocument
|
|
16518
|
+
? Object.prototype.hasOwnProperty.call(document.bindings || {}, 'mode')
|
|
16519
|
+
: this.includeBindingsBlock || !!document.bindings?.mode;
|
|
16520
|
+
this.inputMode = this.normalizeMode(document.bindings?.mode);
|
|
16521
|
+
this.includePresentationBlock = isCanonicalDocument
|
|
16522
|
+
? Object.prototype.hasOwnProperty.call(document.contextSnapshot || {}, 'presentation')
|
|
16523
|
+
: this.includePresentationBlock || !!document.contextSnapshot?.presentation;
|
|
16524
|
+
this.presentationPrefs = this.mergePresentationPrefs(document.contextSnapshot?.presentation);
|
|
16525
|
+
this.includeSchemaPrefsBlock = isCanonicalDocument
|
|
16526
|
+
? Object.prototype.hasOwnProperty.call(document.contextSnapshot || {}, 'schemaPrefs')
|
|
16527
|
+
: this.includeSchemaPrefsBlock || !!document.contextSnapshot?.schemaPrefs;
|
|
16528
|
+
this.schemaPrefs = this.mergeSchemaPrefs(document.contextSnapshot?.schemaPrefs);
|
|
15539
16529
|
this.ensureHints();
|
|
15540
16530
|
try {
|
|
15541
16531
|
this.debugLog('[FormConfigEditor] onJsonConfigChange()', { sections: this.editedConfig.sections?.length || 0 });
|
|
15542
16532
|
}
|
|
15543
16533
|
catch { }
|
|
15544
|
-
this.ruleBuilderConfig = this.createRuleBuilderConfig(
|
|
16534
|
+
this.ruleBuilderConfig = this.createRuleBuilderConfig(document.config.fieldMetadata || [], document.config.sections || [], document.config.actions);
|
|
15545
16535
|
this.ruleBuilderState =
|
|
15546
|
-
|
|
15547
|
-
formLayoutRulesToBuilderState(
|
|
16536
|
+
document.config.formRulesState ||
|
|
16537
|
+
formLayoutRulesToBuilderState(document.config.formRules || []);
|
|
15548
16538
|
this.currentRules = ruleBuilderStateToFormLayoutRules(this.ruleBuilderState);
|
|
15549
16539
|
this.lastRulesSignature = this.computeRulesSignature(this.ruleBuilderState);
|
|
15550
16540
|
this.updateDirtyState('json-change');
|
|
@@ -15621,6 +16611,37 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15621
16611
|
this.currentRules = rules;
|
|
15622
16612
|
this.updateDirtyState('rules-change');
|
|
15623
16613
|
}
|
|
16614
|
+
get jsonDocument() {
|
|
16615
|
+
return this.buildAuthoringDocument();
|
|
16616
|
+
}
|
|
16617
|
+
onBackConfigChange() {
|
|
16618
|
+
this.includeBackConfigBlock = true;
|
|
16619
|
+
this.updateDirtyState('back-config-change');
|
|
16620
|
+
}
|
|
16621
|
+
isBindingsBlockPersisted() {
|
|
16622
|
+
return this.includeBindingsBlock;
|
|
16623
|
+
}
|
|
16624
|
+
isPresentationBlockPersisted() {
|
|
16625
|
+
return this.includePresentationBlock;
|
|
16626
|
+
}
|
|
16627
|
+
isSchemaPrefsBlockPersisted() {
|
|
16628
|
+
return this.includeSchemaPrefsBlock;
|
|
16629
|
+
}
|
|
16630
|
+
isBackConfigBlockPersisted() {
|
|
16631
|
+
return this.includeBackConfigBlock;
|
|
16632
|
+
}
|
|
16633
|
+
onInputModeChange() {
|
|
16634
|
+
this.includeBindingsBlock = true;
|
|
16635
|
+
this.updateDirtyState('input-mode-change');
|
|
16636
|
+
}
|
|
16637
|
+
onPresentationPrefsChange(reason) {
|
|
16638
|
+
this.includePresentationBlock = true;
|
|
16639
|
+
this.updateDirtyState(reason);
|
|
16640
|
+
}
|
|
16641
|
+
onSchemaPrefsChange(reason) {
|
|
16642
|
+
this.includeSchemaPrefsBlock = true;
|
|
16643
|
+
this.updateDirtyState(reason);
|
|
16644
|
+
}
|
|
15624
16645
|
createRuleBuilderConfig(fieldMetadata, sections = [], actions) {
|
|
15625
16646
|
const mapped = this.mapMetadataToSchema(fieldMetadata, sections, actions);
|
|
15626
16647
|
return {
|
|
@@ -15794,7 +16815,7 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15794
16815
|
this.editedConfig = { ...this.editedConfig, fieldMetadata: next };
|
|
15795
16816
|
// Keep JSON editor view in sync when open
|
|
15796
16817
|
try {
|
|
15797
|
-
this.jsonEditor?.
|
|
16818
|
+
this.jsonEditor?.updateJsonFromDocument(this.buildAuthoringDocument());
|
|
15798
16819
|
}
|
|
15799
16820
|
catch { }
|
|
15800
16821
|
this.updateDirtyState();
|
|
@@ -15832,7 +16853,7 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15832
16853
|
// Defaults are injected by normalization on save/apply; keep a local reset to blank to let defaults win.
|
|
15833
16854
|
current.hints = undefined;
|
|
15834
16855
|
// Keep JSON editor in sync
|
|
15835
|
-
this.jsonEditor?.
|
|
16856
|
+
this.jsonEditor?.updateJsonFromDocument(this.buildAuthoringDocument());
|
|
15836
16857
|
this.updateDirtyState('hints-restore');
|
|
15837
16858
|
}
|
|
15838
16859
|
catch { }
|
|
@@ -15842,7 +16863,11 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15842
16863
|
if (!key)
|
|
15843
16864
|
return;
|
|
15844
16865
|
try {
|
|
15845
|
-
const
|
|
16866
|
+
const hasDocumentPresentation = this.openedWithCanonicalDocument ||
|
|
16867
|
+
!!this.initialDocument.contextSnapshot?.presentation;
|
|
16868
|
+
const pres = hasDocumentPresentation
|
|
16869
|
+
? null
|
|
16870
|
+
: await firstValueFrom(this.storage.loadConfig(`form-pres:${key}:${this.editorMode}`)).catch(() => null);
|
|
15846
16871
|
if (pres) {
|
|
15847
16872
|
this.presentationPrefs = {
|
|
15848
16873
|
labelPosition: pres.labelPosition === 'left' ? 'left' : 'above',
|
|
@@ -15855,7 +16880,11 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15855
16880
|
valueAlign: pres.valueAlign || 'start',
|
|
15856
16881
|
};
|
|
15857
16882
|
}
|
|
15858
|
-
const
|
|
16883
|
+
const hasDocumentSchemaPrefs = this.openedWithCanonicalDocument ||
|
|
16884
|
+
!!this.initialDocument.contextSnapshot?.schemaPrefs;
|
|
16885
|
+
const prefs = hasDocumentSchemaPrefs
|
|
16886
|
+
? null
|
|
16887
|
+
: await firstValueFrom(this.storage.loadConfig(`form-schema-prefs:${key}`)).catch(() => null);
|
|
15859
16888
|
if (prefs && typeof prefs === 'object') {
|
|
15860
16889
|
const allowed = {};
|
|
15861
16890
|
const n = prefs.notifyIfOutdated;
|
|
@@ -15903,8 +16932,75 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15903
16932
|
return value;
|
|
15904
16933
|
return 'create';
|
|
15905
16934
|
}
|
|
16935
|
+
buildAuthoringDocument() {
|
|
16936
|
+
const contextSnapshot = stripUndefinedContextSnapshot({
|
|
16937
|
+
backConfig: this.includeBackConfigBlock
|
|
16938
|
+
? this.toBackConfigSnapshot()
|
|
16939
|
+
: undefined,
|
|
16940
|
+
presentation: this.includePresentationBlock
|
|
16941
|
+
? this.toPresentationSnapshot()
|
|
16942
|
+
: undefined,
|
|
16943
|
+
schemaPrefs: this.includeSchemaPrefsBlock
|
|
16944
|
+
? this.toSchemaPrefsSnapshot()
|
|
16945
|
+
: undefined,
|
|
16946
|
+
});
|
|
16947
|
+
return createDynamicFormAuthoringDocument({
|
|
16948
|
+
config: this.stripLegacy(this.editedConfig),
|
|
16949
|
+
bindings: this.includeBindingsBlock
|
|
16950
|
+
? {
|
|
16951
|
+
mode: this.inputMode,
|
|
16952
|
+
}
|
|
16953
|
+
: undefined,
|
|
16954
|
+
contextSnapshot,
|
|
16955
|
+
});
|
|
16956
|
+
}
|
|
16957
|
+
createBackConfigState(snapshot) {
|
|
16958
|
+
return (structuredClone(snapshot) ||
|
|
16959
|
+
{ returnTo: '', confirmOnDirty: true });
|
|
16960
|
+
}
|
|
16961
|
+
mergePresentationPrefs(snapshot) {
|
|
16962
|
+
return {
|
|
16963
|
+
labelPosition: snapshot?.labelPosition === 'left' ? 'left' : 'above',
|
|
16964
|
+
labelFontSize: snapshot?.labelFontSize ?? null,
|
|
16965
|
+
valueFontSize: snapshot?.valueFontSize ?? null,
|
|
16966
|
+
compact: snapshot?.compact === true,
|
|
16967
|
+
density: snapshot?.density || 'comfortable',
|
|
16968
|
+
labelWidth: snapshot?.labelWidth ?? 140,
|
|
16969
|
+
labelAlign: snapshot?.labelAlign || 'start',
|
|
16970
|
+
valueAlign: snapshot?.valueAlign || 'start',
|
|
16971
|
+
};
|
|
16972
|
+
}
|
|
16973
|
+
mergeSchemaPrefs(snapshot) {
|
|
16974
|
+
return {
|
|
16975
|
+
notifyIfOutdated: snapshot?.notifyIfOutdated || 'both',
|
|
16976
|
+
snoozeMs: snapshot?.snoozeMs ?? 24 * 60 * 60 * 1000,
|
|
16977
|
+
autoOpenSettingsOnOutdated: snapshot?.autoOpenSettingsOnOutdated === true,
|
|
16978
|
+
};
|
|
16979
|
+
}
|
|
16980
|
+
toPresentationSnapshot() {
|
|
16981
|
+
return {
|
|
16982
|
+
labelPosition: this.presentationPrefs.labelPosition,
|
|
16983
|
+
labelFontSize: this.presentationPrefs.labelFontSize ?? null,
|
|
16984
|
+
valueFontSize: this.presentationPrefs.valueFontSize ?? null,
|
|
16985
|
+
compact: this.presentationPrefs.compact === true,
|
|
16986
|
+
density: this.presentationPrefs.density,
|
|
16987
|
+
labelWidth: this.presentationPrefs.labelWidth ?? null,
|
|
16988
|
+
labelAlign: this.presentationPrefs.labelAlign,
|
|
16989
|
+
valueAlign: this.presentationPrefs.valueAlign,
|
|
16990
|
+
};
|
|
16991
|
+
}
|
|
16992
|
+
toSchemaPrefsSnapshot() {
|
|
16993
|
+
return {
|
|
16994
|
+
notifyIfOutdated: this.schemaPrefs.notifyIfOutdated,
|
|
16995
|
+
snoozeMs: this.schemaPrefs.snoozeMs,
|
|
16996
|
+
autoOpenSettingsOnOutdated: this.schemaPrefs.autoOpenSettingsOnOutdated === true,
|
|
16997
|
+
};
|
|
16998
|
+
}
|
|
16999
|
+
toBackConfigSnapshot() {
|
|
17000
|
+
return structuredClone(this.backConfig || { returnTo: '', confirmOnDirty: true });
|
|
17001
|
+
}
|
|
15906
17002
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisDynamicFormConfigEditor, deps: [{ token: ASYNC_CONFIG_STORAGE }, { token: FormConfigService }, { token: i6$1.SettingsPanelService }, { token: i0.ChangeDetectorRef }, { token: SETTINGS_PANEL_DATA, optional: true }], target: i0.ɵɵFactoryTarget.Component });
|
|
15907
|
-
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\">\n <mat-tab label=\"Geral\">\n <div class=\"tab-content\">\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\">\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=\"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). editModeEnabled 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 </div>\n </mat-tab>\n <mat-tab label=\"Verifica\u00E7\u00E3o de Schema\">\n <div class=\"tab-content\">\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\">\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\" />\n </mat-form-field>\n </div>\n <div class=\"form-row\">\n <mat-slide-toggle [(ngModel)]=\"schemaPrefs.autoOpenSettingsOnOutdated\">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>\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\">\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\">\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\">\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=\"control\">\n <label class=\"control__label\">Posi\u00E7\u00E3o do r\u00F3tulo</label>\n <mat-button-toggle-group [(ngModel)]=\"presentationPrefs.labelPosition\" 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\" 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\" 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\" 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\"\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\"\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\"\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\">\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\">\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\">\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\">\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\">\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\">\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\">\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\">\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\" placeholder=\"/funcionarios\" />\n </mat-form-field>\n </div>\n <div class=\"form-row\">\n <mat-slide-toggle [(ngModel)]=\"backConfig!.confirmOnDirty\">\n Confirmar ao sair com altera\u00E7\u00F5es\n </mat-slide-toggle>\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\">\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 schema do formul\u00E1rio.</p>\n </div>\n </div>\n <form-json-config-editor [config]=\"editedConfig\" (configChange)=\"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__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}\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$2.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i7$2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: 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$1.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i8$1.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: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6$4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6$4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i12.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$3.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "directive", type: i7$1.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$1.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"], outputs: ["configChange", "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"] }] });
|
|
17003
|
+
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). editModeEnabled 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 </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)}.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}\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$2.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i7$2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: 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$1.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i8$1.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: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6$4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6$4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i12.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$3.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "directive", type: i7$1.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$1.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"] }] });
|
|
15908
17004
|
}
|
|
15909
17005
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisDynamicFormConfigEditor, decorators: [{
|
|
15910
17006
|
type: Component,
|
|
@@ -15930,7 +17026,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
15930
17026
|
HooksEditorComponent,
|
|
15931
17027
|
PraxisVisualBuilder,
|
|
15932
17028
|
CascadeManagerTabComponent,
|
|
15933
|
-
], providers: [FormConfigService], template: "<mat-tab-group class=\"config-tabs\">\n <mat-tab label=\"Geral\">\n <div class=\"tab-content\">\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\">\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=\"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). editModeEnabled 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 </div>\n </mat-tab>\n <mat-tab label=\"Verifica\u00E7\u00E3o de Schema\">\n <div class=\"tab-content\">\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\">\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\" />\n </mat-form-field>\n </div>\n <div class=\"form-row\">\n <mat-slide-toggle [(ngModel)]=\"schemaPrefs.autoOpenSettingsOnOutdated\">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>\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\">\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\">\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\">\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=\"control\">\n <label class=\"control__label\">Posi\u00E7\u00E3o do r\u00F3tulo</label>\n <mat-button-toggle-group [(ngModel)]=\"presentationPrefs.labelPosition\" 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\" 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\" 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\" 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\"\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\"\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\"\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\">\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\">\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\">\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\">\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\">\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\">\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\">\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\">\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\" placeholder=\"/funcionarios\" />\n </mat-form-field>\n </div>\n <div class=\"form-row\">\n <mat-slide-toggle [(ngModel)]=\"backConfig!.confirmOnDirty\">\n Confirmar ao sair com altera\u00E7\u00F5es\n </mat-slide-toggle>\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\">\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 schema do formul\u00E1rio.</p>\n </div>\n </div>\n <form-json-config-editor [config]=\"editedConfig\" (configChange)=\"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__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}\n"] }]
|
|
17029
|
+
], providers: [FormConfigService], 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). editModeEnabled 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 </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)}.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}\n"] }]
|
|
15934
17030
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
15935
17031
|
type: Inject,
|
|
15936
17032
|
args: [ASYNC_CONFIG_STORAGE]
|
|
@@ -15954,6 +17050,12 @@ function omit(obj, keys) {
|
|
|
15954
17050
|
}
|
|
15955
17051
|
return out;
|
|
15956
17052
|
}
|
|
17053
|
+
function stripUndefinedContextSnapshot(snapshot) {
|
|
17054
|
+
const result = Object.fromEntries(Object.entries(snapshot).filter(([, value]) => value !== undefined));
|
|
17055
|
+
return Object.keys(result).length
|
|
17056
|
+
? result
|
|
17057
|
+
: undefined;
|
|
17058
|
+
}
|
|
15957
17059
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
15958
17060
|
function hasFieldMetadata(cfg) {
|
|
15959
17061
|
return cfg && Array.isArray(cfg.fieldMetadata);
|
|
@@ -16336,6 +17438,8 @@ class SectionEditorComponent {
|
|
|
16336
17438
|
title: [this.section.title, Validators.required],
|
|
16337
17439
|
icon: [this.section.icon ?? ''],
|
|
16338
17440
|
description: [this.section.description],
|
|
17441
|
+
appearance: [this.section.appearance ?? 'card'],
|
|
17442
|
+
stepLabel: [this.section.stepLabel ?? ''],
|
|
16339
17443
|
gapBottom: [this.section.gapBottom ?? 0],
|
|
16340
17444
|
titleGapBottom: [this.section.titleGapBottom ?? 20],
|
|
16341
17445
|
descriptionGapBottom: [this.section.descriptionGapBottom ?? 8],
|
|
@@ -16547,6 +17651,14 @@ class SectionEditorComponent {
|
|
|
16547
17651
|
</mat-form-field>
|
|
16548
17652
|
|
|
16549
17653
|
<div class="row-3">
|
|
17654
|
+
<mat-form-field appearance="fill">
|
|
17655
|
+
<mat-label>Aparência</mat-label>
|
|
17656
|
+
<mat-select formControlName="appearance">
|
|
17657
|
+
<mat-option value="card">Card</mat-option>
|
|
17658
|
+
<mat-option value="plain">Plain</mat-option>
|
|
17659
|
+
<mat-option value="step">Step</mat-option>
|
|
17660
|
+
</mat-select>
|
|
17661
|
+
</mat-form-field>
|
|
16550
17662
|
<mat-form-field appearance="fill">
|
|
16551
17663
|
<mat-label>Fonte do título</mat-label>
|
|
16552
17664
|
<mat-select [value]="section.titleStyle || 'titleMedium'" (valueChange)="onTitleStyleChange($event)">
|
|
@@ -16564,6 +17676,13 @@ class SectionEditorComponent {
|
|
|
16564
17676
|
<mat-option value="bodySmall">Corpo pequeno</mat-option>
|
|
16565
17677
|
</mat-select>
|
|
16566
17678
|
</mat-form-field>
|
|
17679
|
+
<mat-form-field appearance="fill">
|
|
17680
|
+
<mat-label>Rótulo do passo</mat-label>
|
|
17681
|
+
<input matInput formControlName="stepLabel" placeholder="ex.: 1, 2, A" />
|
|
17682
|
+
</mat-form-field>
|
|
17683
|
+
</div>
|
|
17684
|
+
|
|
17685
|
+
<div class="row-3">
|
|
16567
17686
|
<mat-form-field appearance="fill">
|
|
16568
17687
|
<mat-label>Alinhamento do cabeçalho</mat-label>
|
|
16569
17688
|
<mat-select formControlName="headerAlign">
|
|
@@ -16752,6 +17871,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
16752
17871
|
</mat-form-field>
|
|
16753
17872
|
|
|
16754
17873
|
<div class="row-3">
|
|
17874
|
+
<mat-form-field appearance="fill">
|
|
17875
|
+
<mat-label>Aparência</mat-label>
|
|
17876
|
+
<mat-select formControlName="appearance">
|
|
17877
|
+
<mat-option value="card">Card</mat-option>
|
|
17878
|
+
<mat-option value="plain">Plain</mat-option>
|
|
17879
|
+
<mat-option value="step">Step</mat-option>
|
|
17880
|
+
</mat-select>
|
|
17881
|
+
</mat-form-field>
|
|
16755
17882
|
<mat-form-field appearance="fill">
|
|
16756
17883
|
<mat-label>Fonte do título</mat-label>
|
|
16757
17884
|
<mat-select [value]="section.titleStyle || 'titleMedium'" (valueChange)="onTitleStyleChange($event)">
|
|
@@ -16769,6 +17896,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
16769
17896
|
<mat-option value="bodySmall">Corpo pequeno</mat-option>
|
|
16770
17897
|
</mat-select>
|
|
16771
17898
|
</mat-form-field>
|
|
17899
|
+
<mat-form-field appearance="fill">
|
|
17900
|
+
<mat-label>Rótulo do passo</mat-label>
|
|
17901
|
+
<input matInput formControlName="stepLabel" placeholder="ex.: 1, 2, A" />
|
|
17902
|
+
</mat-form-field>
|
|
17903
|
+
</div>
|
|
17904
|
+
|
|
17905
|
+
<div class="row-3">
|
|
16772
17906
|
<mat-form-field appearance="fill">
|
|
16773
17907
|
<mat-label>Alinhamento do cabeçalho</mat-label>
|
|
16774
17908
|
<mat-select formControlName="headerAlign">
|
|
@@ -18378,5 +19512,5 @@ const FORM_COMPONENT_AI_CAPABILITIES = {
|
|
|
18378
19512
|
* Generated bundle index. Do not edit.
|
|
18379
19513
|
*/
|
|
18380
19514
|
|
|
18381
|
-
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, PraxisDynamicForm, PraxisDynamicFormConfigEditor, PraxisFilterForm, PraxisFormActionsComponent, RowConfiguratorComponent, RowEditorComponent, SETTINGS_PANEL_DYNAMIC_FORM_PROVIDER, SectionEditorComponent, TASK_PRESETS, applyVisibilityRules, formLayoutRulesToBuilderState, getFormAiCatalog, getFormCapabilities, getFormEnum, isRuleSatisfied, normalizeDateArrays, providePraxisDynamicFormMetadata, provideSettingsPanelDynamicForm, ruleBuilderStateToFormLayoutRules, stripLegacyFieldMetadata };
|
|
19515
|
+
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, 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, provideSettingsPanelDynamicForm, ruleBuilderStateToFormLayoutRules, serializeDynamicFormAuthoringDocument, stripLegacyFieldMetadata, toCanonicalDynamicFormConfig, validateDynamicFormAuthoringDocument, validateDynamicFormAuthoringInput };
|
|
18382
19516
|
//# sourceMappingURL=praxisui-dynamic-form.mjs.map
|