@praxisui/dynamic-form 1.0.0-beta.60 → 1.0.0-beta.62
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 +119 -16
- package/fesm2022/praxisui-dynamic-form.mjs +1461 -326
- package/fesm2022/praxisui-dynamic-form.mjs.map +1 -1
- package/index.d.ts +164 -9
- 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';
|
|
@@ -685,7 +685,7 @@ function generateRulePropertyWhenFalseCapabilities(targetType, properties) {
|
|
|
685
685
|
return result;
|
|
686
686
|
}
|
|
687
687
|
const FORM_AI_CAPABILITIES = {
|
|
688
|
-
version: 'v1.
|
|
688
|
+
version: 'v1.8', // Adds a semantic before-actions editorial slot.
|
|
689
689
|
enums: ENUMS$1,
|
|
690
690
|
targets: [
|
|
691
691
|
'praxis-dynamic-form',
|
|
@@ -710,8 +710,8 @@ const FORM_AI_CAPABILITIES = {
|
|
|
710
710
|
'Campos function, Specifications complexas ou handlers (ex: hooks, conditionalRequired, transformValueFunction) NÃO são serializáveis e exigem revisão de segurança ou devem ser fornecidos pelo host.',
|
|
711
711
|
'Expressões string (e.g., em rules.condition) são perigosas e devem ser parseadas com segurança. Não utilize JS puro. Prefira um DSL simples e validado.',
|
|
712
712
|
'Paths marcados como "critical" exigem confirmação explícita antes de alteração.',
|
|
713
|
-
'A surface editorial do FormConfig usa formBlocksBefore
|
|
714
|
-
'
|
|
713
|
+
'A surface editorial do FormConfig usa formBlocksBefore, formBlocksBeforeActions e formBlocksAfter com WidgetDefinition e não participa do formData.',
|
|
714
|
+
'formBlocksBeforeActions representa um slot semântico após as seções e antes da área principal de ações, preservando compatibilidade de formBlocksAfter.',
|
|
715
715
|
],
|
|
716
716
|
capabilities: [
|
|
717
717
|
// =============================================================================
|
|
@@ -880,12 +880,50 @@ const FORM_AI_CAPABILITIES = {
|
|
|
880
880
|
valueKind: 'array',
|
|
881
881
|
description: 'Ordem explicita de binding dos inputs do widget editorial.',
|
|
882
882
|
},
|
|
883
|
+
{
|
|
884
|
+
path: 'formBlocksBeforeActions',
|
|
885
|
+
category: 'editorial',
|
|
886
|
+
valueKind: 'array',
|
|
887
|
+
description: 'Lista de WidgetDefinition renderizados depois das seções e antes da área principal de ações.',
|
|
888
|
+
safetyNotes: 'Ideal para blocos contextuais próximos ao preenchimento, sem contaminar formData.',
|
|
889
|
+
},
|
|
890
|
+
{
|
|
891
|
+
path: 'formBlocksBeforeActions[]',
|
|
892
|
+
category: 'editorial',
|
|
893
|
+
valueKind: 'object',
|
|
894
|
+
description: 'WidgetDefinition editorial hospedado antes da área principal de ações.',
|
|
895
|
+
},
|
|
896
|
+
{
|
|
897
|
+
path: 'formBlocksBeforeActions[].id',
|
|
898
|
+
category: 'editorial',
|
|
899
|
+
valueKind: 'string',
|
|
900
|
+
description: 'ID do widget registrado no ComponentMetadataRegistry.',
|
|
901
|
+
},
|
|
902
|
+
{
|
|
903
|
+
path: 'formBlocksBeforeActions[].inputs',
|
|
904
|
+
category: 'editorial',
|
|
905
|
+
valueKind: 'object',
|
|
906
|
+
description: 'Inputs serializaveis do widget editorial.',
|
|
907
|
+
},
|
|
908
|
+
{
|
|
909
|
+
path: 'formBlocksBeforeActions[].outputs',
|
|
910
|
+
category: 'editorial',
|
|
911
|
+
valueKind: 'object',
|
|
912
|
+
description: 'Mapeamento de outputs do widget editorial.',
|
|
913
|
+
safetyNotes: 'Use string "emit" para reencaminhar saida via widgetEvent quando aplicavel.',
|
|
914
|
+
},
|
|
915
|
+
{
|
|
916
|
+
path: 'formBlocksBeforeActions[].bindingOrder',
|
|
917
|
+
category: 'editorial',
|
|
918
|
+
valueKind: 'array',
|
|
919
|
+
description: 'Ordem explicita de binding dos inputs do widget editorial.',
|
|
920
|
+
},
|
|
883
921
|
{
|
|
884
922
|
path: 'formBlocksAfter',
|
|
885
923
|
category: 'editorial',
|
|
886
924
|
valueKind: 'array',
|
|
887
|
-
description: 'Lista de WidgetDefinition renderizados depois do formulario.',
|
|
888
|
-
safetyNotes: 'Blocos editoriais nao entram em formData.',
|
|
925
|
+
description: 'Lista de WidgetDefinition renderizados depois do formulario inteiro, incluindo a área principal de ações.',
|
|
926
|
+
safetyNotes: 'Blocos editoriais nao entram em formData e este slot é mais adequado para conteúdo de fechamento.',
|
|
889
927
|
},
|
|
890
928
|
{
|
|
891
929
|
path: 'formBlocksAfter[]',
|
|
@@ -985,6 +1023,21 @@ const FORM_AI_CAPABILITIES = {
|
|
|
985
1023
|
valueKind: 'number',
|
|
986
1024
|
description: 'Espaçamento vertical (em px) abaixo da descrição da seção.',
|
|
987
1025
|
},
|
|
1026
|
+
{
|
|
1027
|
+
path: 'sections[].appearance',
|
|
1028
|
+
category: 'appearance',
|
|
1029
|
+
valueKind: 'enum',
|
|
1030
|
+
allowedValues: ['card', 'plain', 'step'],
|
|
1031
|
+
description: 'Preset visual do container/cabeçalho da seção.',
|
|
1032
|
+
intentExamples: ['deixar a seção como step', 'remover card da seção'],
|
|
1033
|
+
},
|
|
1034
|
+
{
|
|
1035
|
+
path: 'sections[].stepLabel',
|
|
1036
|
+
category: 'content',
|
|
1037
|
+
valueKind: 'string',
|
|
1038
|
+
description: 'Rótulo curto do passo exibido antes do título da seção.',
|
|
1039
|
+
intentExamples: ['mostrar etapa 2', 'exibir badge numérico na seção'],
|
|
1040
|
+
},
|
|
988
1041
|
{
|
|
989
1042
|
path: 'sections[].titleStyle',
|
|
990
1043
|
category: 'appearance',
|
|
@@ -2356,6 +2409,8 @@ const TASK_PRESETS = {
|
|
|
2356
2409
|
'sections[].gapBottom',
|
|
2357
2410
|
'sections[].titleGapBottom',
|
|
2358
2411
|
'sections[].descriptionGapBottom',
|
|
2412
|
+
'sections[].appearance',
|
|
2413
|
+
'sections[].stepLabel',
|
|
2359
2414
|
'sections[].titleStyle',
|
|
2360
2415
|
'sections[].descriptionStyle',
|
|
2361
2416
|
'sections[].titleColor',
|
|
@@ -2759,6 +2814,463 @@ class FormAiAdapter extends BaseAiAdapter {
|
|
|
2759
2814
|
}
|
|
2760
2815
|
}
|
|
2761
2816
|
|
|
2817
|
+
/**
|
|
2818
|
+
* Deep clone and normalize a form configuration ensuring required collections
|
|
2819
|
+
* are always present to avoid unintended mutations.
|
|
2820
|
+
*/
|
|
2821
|
+
function normalizeFormConfig(config) {
|
|
2822
|
+
const cloned = config ? structuredClone(config) : createDefaultFormConfig();
|
|
2823
|
+
// Apply canonical normalization from @praxisui/core first (aliases → canônico)
|
|
2824
|
+
const canonical = normalizeFormConfig$1(cloned);
|
|
2825
|
+
const withCollections = ensureIds({
|
|
2826
|
+
...createDefaultFormConfig(),
|
|
2827
|
+
...canonical,
|
|
2828
|
+
sections: canonical.sections ?? [],
|
|
2829
|
+
fieldMetadata: canonical.fieldMetadata ?? [],
|
|
2830
|
+
formBlocksBefore: canonical.formBlocksBefore ?? [],
|
|
2831
|
+
formBlocksBeforeActions: canonical.formBlocksBeforeActions ?? [],
|
|
2832
|
+
formBlocksBeforeActionsPlacement: canonical.formBlocksBeforeActionsPlacement ?? 'afterSections',
|
|
2833
|
+
formBlocksAfter: canonical.formBlocksAfter ?? [],
|
|
2834
|
+
});
|
|
2835
|
+
return applyDefaultSpans(withCollections);
|
|
2836
|
+
}
|
|
2837
|
+
/**
|
|
2838
|
+
* Apply default grid spans when not provided by the config.
|
|
2839
|
+
* Rule: for each row, if none of its columns declares span.md,
|
|
2840
|
+
* distribute equally across 12 columns on md+; keep xs/sm stacked (12).
|
|
2841
|
+
*/
|
|
2842
|
+
function applyDefaultSpans(config) {
|
|
2843
|
+
const next = structuredClone(config);
|
|
2844
|
+
for (const section of next.sections || []) {
|
|
2845
|
+
for (const row of section.rows || []) {
|
|
2846
|
+
const cols = row.columns || [];
|
|
2847
|
+
if (!cols.length)
|
|
2848
|
+
continue;
|
|
2849
|
+
const hasAnyMdSpan = cols.some((c) => c.span?.md != null);
|
|
2850
|
+
if (hasAnyMdSpan)
|
|
2851
|
+
continue;
|
|
2852
|
+
const per = Math.max(1, Math.floor(12 / cols.length));
|
|
2853
|
+
for (const col of cols) {
|
|
2854
|
+
const span = col.span || {};
|
|
2855
|
+
span.xs = 12;
|
|
2856
|
+
span.sm = 12;
|
|
2857
|
+
span.md = per;
|
|
2858
|
+
span.lg = span.lg ?? per;
|
|
2859
|
+
span.xl = span.xl ?? per;
|
|
2860
|
+
col.span = span;
|
|
2861
|
+
}
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2864
|
+
return next;
|
|
2865
|
+
}
|
|
2866
|
+
|
|
2867
|
+
const DOCUMENT_KIND = 'praxis.dynamic-form.editor';
|
|
2868
|
+
const DOCUMENT_VERSION = 1;
|
|
2869
|
+
const VALID_MODES = ['create', 'edit', 'view'];
|
|
2870
|
+
const VALID_NOTIFY_VALUES = ['inline', 'snackbar', 'both', 'none'];
|
|
2871
|
+
const VALID_LABEL_POSITIONS = ['above', 'left'];
|
|
2872
|
+
const VALID_DENSITIES = ['comfortable', 'cozy', 'compact'];
|
|
2873
|
+
const VALID_ALIGNMENTS = ['start', 'center', 'end'];
|
|
2874
|
+
function createDynamicFormAuthoringDocument(source) {
|
|
2875
|
+
return normalizeDynamicFormAuthoringDocument({
|
|
2876
|
+
kind: DOCUMENT_KIND,
|
|
2877
|
+
version: DOCUMENT_VERSION,
|
|
2878
|
+
config: asFormConfig(source?.config),
|
|
2879
|
+
bindings: source?.bindings,
|
|
2880
|
+
contextSnapshot: source?.contextSnapshot,
|
|
2881
|
+
});
|
|
2882
|
+
}
|
|
2883
|
+
function parseLegacyOrDynamicFormDocument(raw) {
|
|
2884
|
+
const obj = asRecord(raw);
|
|
2885
|
+
if (obj.kind === DOCUMENT_KIND && obj.version === DOCUMENT_VERSION) {
|
|
2886
|
+
return normalizeDynamicFormAuthoringDocument({
|
|
2887
|
+
kind: DOCUMENT_KIND,
|
|
2888
|
+
version: DOCUMENT_VERSION,
|
|
2889
|
+
config: asFormConfig(obj.config),
|
|
2890
|
+
bindings: obj.bindings,
|
|
2891
|
+
contextSnapshot: obj.contextSnapshot,
|
|
2892
|
+
});
|
|
2893
|
+
}
|
|
2894
|
+
if (looksLikeLegacyEditorPayload(obj)) {
|
|
2895
|
+
return createDynamicFormAuthoringDocument({
|
|
2896
|
+
config: extractLegacyFormConfig(obj),
|
|
2897
|
+
bindings: {
|
|
2898
|
+
mode: normalizeMode(obj.inputsPatch?.mode),
|
|
2899
|
+
},
|
|
2900
|
+
contextSnapshot: {
|
|
2901
|
+
backConfig: normalizeBackConfig(obj.backConfig),
|
|
2902
|
+
presentation: normalizePresentation(obj.presentation),
|
|
2903
|
+
schemaPrefs: normalizeSchemaPrefs(obj.schemaPrefs),
|
|
2904
|
+
},
|
|
2905
|
+
});
|
|
2906
|
+
}
|
|
2907
|
+
return createDynamicFormAuthoringDocument({
|
|
2908
|
+
config: raw,
|
|
2909
|
+
});
|
|
2910
|
+
}
|
|
2911
|
+
function normalizeDynamicFormAuthoringDocument(doc) {
|
|
2912
|
+
const normalizedConfig = normalizeDynamicFormConfig(doc?.config);
|
|
2913
|
+
return {
|
|
2914
|
+
kind: DOCUMENT_KIND,
|
|
2915
|
+
version: DOCUMENT_VERSION,
|
|
2916
|
+
config: normalizedConfig,
|
|
2917
|
+
bindings: normalizeBindings(doc?.bindings),
|
|
2918
|
+
contextSnapshot: normalizeContextSnapshot(doc?.contextSnapshot),
|
|
2919
|
+
};
|
|
2920
|
+
}
|
|
2921
|
+
function validateDynamicFormAuthoringDocument(doc, context) {
|
|
2922
|
+
return validateDynamicFormAuthoringInput(doc, context);
|
|
2923
|
+
}
|
|
2924
|
+
function validateDynamicFormAuthoringInput(raw, context) {
|
|
2925
|
+
const diagnostics = [];
|
|
2926
|
+
const obj = asRecord(raw);
|
|
2927
|
+
const rawConfig = asRecord(obj.config);
|
|
2928
|
+
const rawBindings = asRecord(obj.bindings);
|
|
2929
|
+
const rawContextSnapshot = asRecord(obj.contextSnapshot);
|
|
2930
|
+
if (Object.prototype.hasOwnProperty.call(obj, 'kind') &&
|
|
2931
|
+
obj.kind !== DOCUMENT_KIND) {
|
|
2932
|
+
diagnostics.push(errorDiagnostic('dynamicForm.document.kind.invalid', `kind must be ${DOCUMENT_KIND}`, 'kind'));
|
|
2933
|
+
}
|
|
2934
|
+
if (Object.prototype.hasOwnProperty.call(obj, 'version') &&
|
|
2935
|
+
obj.version !== DOCUMENT_VERSION) {
|
|
2936
|
+
diagnostics.push(errorDiagnostic('dynamicForm.document.version.invalid', `version must be ${DOCUMENT_VERSION}`, 'version'));
|
|
2937
|
+
}
|
|
2938
|
+
if (!Array.isArray(rawConfig.sections)) {
|
|
2939
|
+
diagnostics.push(errorDiagnostic('dynamicForm.config.sections.invalid', 'config.sections must be an array', 'config.sections'));
|
|
2940
|
+
}
|
|
2941
|
+
if (!Array.isArray(rawConfig.fieldMetadata)) {
|
|
2942
|
+
diagnostics.push(errorDiagnostic('dynamicForm.config.fieldMetadata.invalid', 'config.fieldMetadata must be an array', 'config.fieldMetadata'));
|
|
2943
|
+
}
|
|
2944
|
+
if (Object.prototype.hasOwnProperty.call(rawBindings, 'mode') &&
|
|
2945
|
+
rawBindings.mode != null &&
|
|
2946
|
+
!VALID_MODES.includes(rawBindings.mode)) {
|
|
2947
|
+
diagnostics.push(errorDiagnostic('dynamicForm.bindings.mode.invalid', 'bindings.mode must be create, edit or view', 'bindings.mode'));
|
|
2948
|
+
}
|
|
2949
|
+
const rawSchemaPrefs = asRecord(rawContextSnapshot.schemaPrefs);
|
|
2950
|
+
if (Object.prototype.hasOwnProperty.call(rawSchemaPrefs, 'notifyIfOutdated') &&
|
|
2951
|
+
rawSchemaPrefs.notifyIfOutdated != null &&
|
|
2952
|
+
!VALID_NOTIFY_VALUES.includes(rawSchemaPrefs.notifyIfOutdated)) {
|
|
2953
|
+
diagnostics.push(errorDiagnostic('dynamicForm.contextSnapshot.schemaPrefs.notifyIfOutdated.invalid', 'schemaPrefs.notifyIfOutdated must be inline, snackbar, both or none', 'contextSnapshot.schemaPrefs.notifyIfOutdated'));
|
|
2954
|
+
}
|
|
2955
|
+
if (Object.prototype.hasOwnProperty.call(rawSchemaPrefs, 'snoozeMs') &&
|
|
2956
|
+
rawSchemaPrefs.snoozeMs != null &&
|
|
2957
|
+
(!Number.isFinite(rawSchemaPrefs.snoozeMs) || rawSchemaPrefs.snoozeMs < 0)) {
|
|
2958
|
+
diagnostics.push(errorDiagnostic('dynamicForm.contextSnapshot.schemaPrefs.snoozeMs.invalid', 'schemaPrefs.snoozeMs must be a non-negative number', 'contextSnapshot.schemaPrefs.snoozeMs'));
|
|
2959
|
+
}
|
|
2960
|
+
const rawPresentation = asRecord(rawContextSnapshot.presentation);
|
|
2961
|
+
if (Object.prototype.hasOwnProperty.call(rawPresentation, 'labelPosition') &&
|
|
2962
|
+
rawPresentation.labelPosition != null &&
|
|
2963
|
+
!VALID_LABEL_POSITIONS.includes(rawPresentation.labelPosition)) {
|
|
2964
|
+
diagnostics.push(errorDiagnostic('dynamicForm.contextSnapshot.presentation.labelPosition.invalid', 'presentation.labelPosition must be above or left', 'contextSnapshot.presentation.labelPosition'));
|
|
2965
|
+
}
|
|
2966
|
+
if (Object.prototype.hasOwnProperty.call(rawPresentation, 'density') &&
|
|
2967
|
+
rawPresentation.density != null &&
|
|
2968
|
+
!VALID_DENSITIES.includes(rawPresentation.density)) {
|
|
2969
|
+
diagnostics.push(errorDiagnostic('dynamicForm.contextSnapshot.presentation.density.invalid', 'presentation.density must be comfortable, cozy or compact', 'contextSnapshot.presentation.density'));
|
|
2970
|
+
}
|
|
2971
|
+
['labelAlign', 'valueAlign'].forEach((key) => {
|
|
2972
|
+
const value = rawPresentation[key];
|
|
2973
|
+
if (Object.prototype.hasOwnProperty.call(rawPresentation, key) &&
|
|
2974
|
+
value != null &&
|
|
2975
|
+
!VALID_ALIGNMENTS.includes(value)) {
|
|
2976
|
+
diagnostics.push(errorDiagnostic(`dynamicForm.contextSnapshot.presentation.${key}.invalid`, `presentation.${key} must be start, center or end`, `contextSnapshot.presentation.${key}`));
|
|
2977
|
+
}
|
|
2978
|
+
});
|
|
2979
|
+
[
|
|
2980
|
+
'labelFontSize',
|
|
2981
|
+
'valueFontSize',
|
|
2982
|
+
'labelWidth',
|
|
2983
|
+
].forEach((key) => {
|
|
2984
|
+
const value = rawPresentation[key];
|
|
2985
|
+
if (Object.prototype.hasOwnProperty.call(rawPresentation, key) &&
|
|
2986
|
+
value != null &&
|
|
2987
|
+
(!Number.isFinite(value) || value < 0)) {
|
|
2988
|
+
diagnostics.push(errorDiagnostic(`dynamicForm.contextSnapshot.presentation.${key}.invalid`, `presentation.${key} must be a non-negative number or null`, `contextSnapshot.presentation.${key}`));
|
|
2989
|
+
}
|
|
2990
|
+
});
|
|
2991
|
+
const normalized = normalizeDynamicFormAuthoringDocument(parseLegacyOrDynamicFormDocument(raw));
|
|
2992
|
+
validateNormalizedDynamicFormAuthoringDocument(normalized, diagnostics, context);
|
|
2993
|
+
return dedupeDiagnostics(diagnostics);
|
|
2994
|
+
}
|
|
2995
|
+
function validateNormalizedDynamicFormAuthoringDocument(normalized, diagnostics, context) {
|
|
2996
|
+
if (context?.server?.schemaHash) {
|
|
2997
|
+
const configHash = trimString(normalized.config?.metadata?.serverHash);
|
|
2998
|
+
if (!configHash) {
|
|
2999
|
+
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'));
|
|
3000
|
+
}
|
|
3001
|
+
else if (configHash !== context.server.schemaHash) {
|
|
3002
|
+
diagnostics.push(warnDiagnostic('dynamicForm.config.metadata.serverHash.diverges-from-server', 'config.metadata.serverHash snapshot diverges from current server schema hash', 'config.metadata.serverHash'));
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
function toCanonicalDynamicFormConfig(doc, _context) {
|
|
3007
|
+
const normalized = normalizeDynamicFormAuthoringDocument(doc);
|
|
3008
|
+
const canonical = cloneJson(normalized.config);
|
|
3009
|
+
delete canonical.presentation;
|
|
3010
|
+
return stripUndefinedDeep(canonical);
|
|
3011
|
+
}
|
|
3012
|
+
function buildDynamicFormApplyPlan(doc, runtime, options) {
|
|
3013
|
+
const normalized = normalizeDynamicFormAuthoringDocument(doc);
|
|
3014
|
+
const diagnostics = validateDynamicFormAuthoringInput(doc, {
|
|
3015
|
+
server: {
|
|
3016
|
+
schemaHash: runtime?.server?.schemaHash,
|
|
3017
|
+
},
|
|
3018
|
+
});
|
|
3019
|
+
const bindingsPatch = normalizeBindings(normalized.bindings);
|
|
3020
|
+
const contextSnapshot = normalizeContextSnapshot(normalized.contextSnapshot);
|
|
3021
|
+
const diff = {
|
|
3022
|
+
modeChanged: normalizeMode(runtime?.currentBindings?.mode) !==
|
|
3023
|
+
normalizeMode(bindingsPatch?.mode),
|
|
3024
|
+
presentationChanged: !deepEqual(runtime?.currentContextSnapshot?.presentation, contextSnapshot?.presentation),
|
|
3025
|
+
schemaPrefsChanged: !deepEqual(runtime?.currentContextSnapshot?.schemaPrefs, contextSnapshot?.schemaPrefs),
|
|
3026
|
+
backConfigChanged: !deepEqual(runtime?.currentContextSnapshot?.backConfig, contextSnapshot?.backConfig),
|
|
3027
|
+
};
|
|
3028
|
+
return {
|
|
3029
|
+
canonicalConfig: toCanonicalDynamicFormConfig(normalized, {
|
|
3030
|
+
mode: bindingsPatch?.mode,
|
|
3031
|
+
}),
|
|
3032
|
+
bindingsPatch,
|
|
3033
|
+
contextSnapshot,
|
|
3034
|
+
persistence: {
|
|
3035
|
+
saveConfig: options?.saveConfig === true,
|
|
3036
|
+
saveBindings: options?.saveBindings === true,
|
|
3037
|
+
saveContextSnapshot: options?.saveContextSnapshot === true,
|
|
3038
|
+
},
|
|
3039
|
+
runtime: {
|
|
3040
|
+
rebindMode: diff.modeChanged ||
|
|
3041
|
+
(options?.replaceContext === true &&
|
|
3042
|
+
!bindingsPatch?.mode &&
|
|
3043
|
+
!!runtime?.currentBindings?.mode),
|
|
3044
|
+
clearMode: options?.replaceContext === true &&
|
|
3045
|
+
!bindingsPatch?.mode &&
|
|
3046
|
+
!!runtime?.currentBindings?.mode,
|
|
3047
|
+
rebuildForm: true,
|
|
3048
|
+
refreshPresentation: !!contextSnapshot?.presentation,
|
|
3049
|
+
clearPresentation: options?.replaceContext === true &&
|
|
3050
|
+
!contextSnapshot?.presentation &&
|
|
3051
|
+
!!runtime?.currentContextSnapshot?.presentation,
|
|
3052
|
+
refreshSchemaState: !!contextSnapshot?.schemaPrefs,
|
|
3053
|
+
clearSchemaState: options?.replaceContext === true &&
|
|
3054
|
+
!contextSnapshot?.schemaPrefs &&
|
|
3055
|
+
!!runtime?.currentContextSnapshot?.schemaPrefs,
|
|
3056
|
+
refreshBackNavigation: !!contextSnapshot?.backConfig,
|
|
3057
|
+
clearBackNavigation: options?.replaceContext === true &&
|
|
3058
|
+
!contextSnapshot?.backConfig &&
|
|
3059
|
+
!!runtime?.currentContextSnapshot?.backConfig,
|
|
3060
|
+
},
|
|
3061
|
+
metadata: {
|
|
3062
|
+
attachSchemaSnapshot: options?.attachSchemaSnapshot === true,
|
|
3063
|
+
},
|
|
3064
|
+
diff,
|
|
3065
|
+
diagnostics,
|
|
3066
|
+
};
|
|
3067
|
+
}
|
|
3068
|
+
function dedupeDiagnostics(diagnostics) {
|
|
3069
|
+
const seen = new Set();
|
|
3070
|
+
return diagnostics.filter((diagnostic) => {
|
|
3071
|
+
const key = `${diagnostic.level}|${diagnostic.code}|${diagnostic.path || ''}`;
|
|
3072
|
+
if (seen.has(key)) {
|
|
3073
|
+
return false;
|
|
3074
|
+
}
|
|
3075
|
+
seen.add(key);
|
|
3076
|
+
return true;
|
|
3077
|
+
});
|
|
3078
|
+
}
|
|
3079
|
+
function serializeDynamicFormAuthoringDocument(doc) {
|
|
3080
|
+
const normalized = normalizeDynamicFormAuthoringDocument(doc);
|
|
3081
|
+
return stripUndefinedDeep({
|
|
3082
|
+
kind: DOCUMENT_KIND,
|
|
3083
|
+
version: DOCUMENT_VERSION,
|
|
3084
|
+
config: normalized.config,
|
|
3085
|
+
bindings: normalized.bindings,
|
|
3086
|
+
contextSnapshot: normalized.contextSnapshot,
|
|
3087
|
+
});
|
|
3088
|
+
}
|
|
3089
|
+
function normalizeDynamicFormConfig(config) {
|
|
3090
|
+
const normalized = normalizeFormConfig(asFormConfig(config));
|
|
3091
|
+
const next = cloneJson(normalized);
|
|
3092
|
+
next.fieldMetadata = stripLegacyFieldMetadata$1(next.fieldMetadata);
|
|
3093
|
+
delete next.presentation;
|
|
3094
|
+
if (Array.isArray(next.formRules) &&
|
|
3095
|
+
next.formRules.length === 0) {
|
|
3096
|
+
delete next.formRules;
|
|
3097
|
+
}
|
|
3098
|
+
if (isEmptyRuleBuilderState$1(next.formRulesState)) {
|
|
3099
|
+
delete next.formRulesState;
|
|
3100
|
+
}
|
|
3101
|
+
return next;
|
|
3102
|
+
}
|
|
3103
|
+
function normalizeBindings(bindings) {
|
|
3104
|
+
if (!bindings || typeof bindings !== 'object')
|
|
3105
|
+
return undefined;
|
|
3106
|
+
return stripUndefinedShallow({
|
|
3107
|
+
mode: normalizeMode(bindings.mode),
|
|
3108
|
+
});
|
|
3109
|
+
}
|
|
3110
|
+
function normalizeContextSnapshot(snapshot) {
|
|
3111
|
+
if (!snapshot || typeof snapshot !== 'object')
|
|
3112
|
+
return undefined;
|
|
3113
|
+
return stripUndefinedShallow({
|
|
3114
|
+
backConfig: normalizeBackConfig(snapshot.backConfig),
|
|
3115
|
+
presentation: normalizePresentation(snapshot.presentation),
|
|
3116
|
+
schemaPrefs: normalizeSchemaPrefs(snapshot.schemaPrefs),
|
|
3117
|
+
});
|
|
3118
|
+
}
|
|
3119
|
+
function normalizeBackConfig(value) {
|
|
3120
|
+
if (!value || typeof value !== 'object')
|
|
3121
|
+
return undefined;
|
|
3122
|
+
return stripUndefinedDeep(cloneJson(value));
|
|
3123
|
+
}
|
|
3124
|
+
function normalizePresentation(value) {
|
|
3125
|
+
if (!value || typeof value !== 'object')
|
|
3126
|
+
return undefined;
|
|
3127
|
+
return stripUndefinedShallow({
|
|
3128
|
+
labelPosition: value.labelPosition === 'left' ? 'left' : value.labelPosition === 'above' ? 'above' : undefined,
|
|
3129
|
+
labelFontSize: normalizeNullableNumber(value.labelFontSize),
|
|
3130
|
+
valueFontSize: normalizeNullableNumber(value.valueFontSize),
|
|
3131
|
+
compact: normalizeBoolean(value.compact),
|
|
3132
|
+
density: value.density === 'comfortable' ||
|
|
3133
|
+
value.density === 'cozy' ||
|
|
3134
|
+
value.density === 'compact'
|
|
3135
|
+
? value.density
|
|
3136
|
+
: undefined,
|
|
3137
|
+
labelWidth: normalizeNullableNumber(value.labelWidth),
|
|
3138
|
+
labelAlign: normalizeAlignment(value.labelAlign),
|
|
3139
|
+
valueAlign: normalizeAlignment(value.valueAlign),
|
|
3140
|
+
});
|
|
3141
|
+
}
|
|
3142
|
+
function normalizeSchemaPrefs(value) {
|
|
3143
|
+
if (!value || typeof value !== 'object')
|
|
3144
|
+
return undefined;
|
|
3145
|
+
return stripUndefinedShallow({
|
|
3146
|
+
notifyIfOutdated: value.notifyIfOutdated === 'inline' ||
|
|
3147
|
+
value.notifyIfOutdated === 'snackbar' ||
|
|
3148
|
+
value.notifyIfOutdated === 'both' ||
|
|
3149
|
+
value.notifyIfOutdated === 'none'
|
|
3150
|
+
? value.notifyIfOutdated
|
|
3151
|
+
: undefined,
|
|
3152
|
+
snoozeMs: normalizeNonNegativeNumber(value.snoozeMs),
|
|
3153
|
+
autoOpenSettingsOnOutdated: normalizeBoolean(value.autoOpenSettingsOnOutdated),
|
|
3154
|
+
});
|
|
3155
|
+
}
|
|
3156
|
+
function looksLikeLegacyEditorPayload(obj) {
|
|
3157
|
+
return (Object.prototype.hasOwnProperty.call(obj, 'formConfig') ||
|
|
3158
|
+
Object.prototype.hasOwnProperty.call(obj, 'inputsPatch') ||
|
|
3159
|
+
Object.prototype.hasOwnProperty.call(obj, 'backConfig') ||
|
|
3160
|
+
Object.prototype.hasOwnProperty.call(obj, 'presentation') ||
|
|
3161
|
+
Object.prototype.hasOwnProperty.call(obj, 'schemaPrefs'));
|
|
3162
|
+
}
|
|
3163
|
+
function extractLegacyFormConfig(obj) {
|
|
3164
|
+
if (obj.formConfig && typeof obj.formConfig === 'object') {
|
|
3165
|
+
return asFormConfig(obj.formConfig);
|
|
3166
|
+
}
|
|
3167
|
+
if (obj.config && typeof obj.config === 'object') {
|
|
3168
|
+
return asFormConfig(obj.config);
|
|
3169
|
+
}
|
|
3170
|
+
return asFormConfig(obj);
|
|
3171
|
+
}
|
|
3172
|
+
function asFormConfig(raw) {
|
|
3173
|
+
if (!raw || typeof raw !== 'object') {
|
|
3174
|
+
return { sections: [], fieldMetadata: [] };
|
|
3175
|
+
}
|
|
3176
|
+
return raw;
|
|
3177
|
+
}
|
|
3178
|
+
function asRecord(value) {
|
|
3179
|
+
return value && typeof value === 'object'
|
|
3180
|
+
? value
|
|
3181
|
+
: {};
|
|
3182
|
+
}
|
|
3183
|
+
function normalizeMode(value) {
|
|
3184
|
+
return value === 'create' || value === 'edit' || value === 'view'
|
|
3185
|
+
? value
|
|
3186
|
+
: undefined;
|
|
3187
|
+
}
|
|
3188
|
+
function normalizeNullableNumber(value) {
|
|
3189
|
+
if (value === null)
|
|
3190
|
+
return null;
|
|
3191
|
+
if (value === '')
|
|
3192
|
+
return null;
|
|
3193
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
3194
|
+
}
|
|
3195
|
+
function normalizeNonNegativeNumber(value) {
|
|
3196
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0
|
|
3197
|
+
? value
|
|
3198
|
+
: undefined;
|
|
3199
|
+
}
|
|
3200
|
+
function normalizeBoolean(value) {
|
|
3201
|
+
return typeof value === 'boolean' ? value : undefined;
|
|
3202
|
+
}
|
|
3203
|
+
function normalizeAlignment(value) {
|
|
3204
|
+
return value === 'start' || value === 'center' || value === 'end'
|
|
3205
|
+
? value
|
|
3206
|
+
: undefined;
|
|
3207
|
+
}
|
|
3208
|
+
function stripLegacyFieldMetadata$1(arr) {
|
|
3209
|
+
if (!Array.isArray(arr))
|
|
3210
|
+
return arr;
|
|
3211
|
+
return arr.map((item) => {
|
|
3212
|
+
const copy = { ...(item || {}) };
|
|
3213
|
+
delete copy.endpoint;
|
|
3214
|
+
return copy;
|
|
3215
|
+
});
|
|
3216
|
+
}
|
|
3217
|
+
function isEmptyRuleBuilderState$1(state) {
|
|
3218
|
+
try {
|
|
3219
|
+
const nodesEmpty = !state?.nodes || Object.keys(state.nodes).length === 0;
|
|
3220
|
+
const rootsEmpty = !state?.rootNodes || state.rootNodes.length === 0;
|
|
3221
|
+
return nodesEmpty && rootsEmpty;
|
|
3222
|
+
}
|
|
3223
|
+
catch {
|
|
3224
|
+
return false;
|
|
3225
|
+
}
|
|
3226
|
+
}
|
|
3227
|
+
function errorDiagnostic(code, message, path) {
|
|
3228
|
+
return { level: 'error', code, message, path };
|
|
3229
|
+
}
|
|
3230
|
+
function warnDiagnostic(code, message, path) {
|
|
3231
|
+
return { level: 'warning', code, message, path };
|
|
3232
|
+
}
|
|
3233
|
+
function infoDiagnostic(code, message, path) {
|
|
3234
|
+
return { level: 'info', code, message, path };
|
|
3235
|
+
}
|
|
3236
|
+
function cloneJson(value) {
|
|
3237
|
+
return value == null ? value : JSON.parse(JSON.stringify(value));
|
|
3238
|
+
}
|
|
3239
|
+
function trimString(value) {
|
|
3240
|
+
if (typeof value !== 'string')
|
|
3241
|
+
return undefined;
|
|
3242
|
+
const trimmed = value.trim();
|
|
3243
|
+
return trimmed ? trimmed : undefined;
|
|
3244
|
+
}
|
|
3245
|
+
function stripUndefinedShallow(value) {
|
|
3246
|
+
const entries = Object.entries(value).filter(([, item]) => item !== undefined);
|
|
3247
|
+
if (!entries.length)
|
|
3248
|
+
return undefined;
|
|
3249
|
+
return Object.fromEntries(entries);
|
|
3250
|
+
}
|
|
3251
|
+
function stripUndefinedDeep(value) {
|
|
3252
|
+
if (Array.isArray(value)) {
|
|
3253
|
+
return value
|
|
3254
|
+
.map((item) => stripUndefinedDeep(item))
|
|
3255
|
+
.filter((item) => item !== undefined);
|
|
3256
|
+
}
|
|
3257
|
+
if (!value || typeof value !== 'object') {
|
|
3258
|
+
return value;
|
|
3259
|
+
}
|
|
3260
|
+
const result = {};
|
|
3261
|
+
Object.entries(value).forEach(([key, item]) => {
|
|
3262
|
+
const normalized = stripUndefinedDeep(item);
|
|
3263
|
+
if (normalized !== undefined) {
|
|
3264
|
+
result[key] = normalized;
|
|
3265
|
+
}
|
|
3266
|
+
});
|
|
3267
|
+
return result;
|
|
3268
|
+
}
|
|
3269
|
+
function deepEqual(left, right) {
|
|
3270
|
+
return JSON.stringify(stripUndefinedDeep(cloneJson(left))) ===
|
|
3271
|
+
JSON.stringify(stripUndefinedDeep(cloneJson(right)));
|
|
3272
|
+
}
|
|
3273
|
+
|
|
2762
3274
|
class FormLayoutService {
|
|
2763
3275
|
storage;
|
|
2764
3276
|
constructor(storage) {
|
|
@@ -4117,7 +4629,7 @@ class PraxisDynamicForm {
|
|
|
4117
4629
|
resourcePath;
|
|
4118
4630
|
resourceId;
|
|
4119
4631
|
/**
|
|
4120
|
-
* Shared editorial context for widgets hosted
|
|
4632
|
+
* Shared editorial context for widgets hosted around the form.
|
|
4121
4633
|
* Treat this input as immutable: in-place mutations do not invalidate the cached
|
|
4122
4634
|
* widget context snapshot used to avoid re-binding on every change detection pass.
|
|
4123
4635
|
*/
|
|
@@ -4218,7 +4730,7 @@ class PraxisDynamicForm {
|
|
|
4218
4730
|
schemaStatusChange = new EventEmitter();
|
|
4219
4731
|
/** Forwarded from DynamicFieldLoader to allow host-side observability. */
|
|
4220
4732
|
fieldRenderError = new EventEmitter();
|
|
4221
|
-
/** Re-emits events from editorial widgets hosted before
|
|
4733
|
+
/** Re-emits events from editorial widgets hosted before, before actions, or after the form. */
|
|
4222
4734
|
widgetEvent = new EventEmitter();
|
|
4223
4735
|
// Estado interno para UX
|
|
4224
4736
|
isLoading = false;
|
|
@@ -4267,6 +4779,15 @@ class PraxisDynamicForm {
|
|
|
4267
4779
|
get formBlocksAfter() {
|
|
4268
4780
|
return Array.isArray(this.config?.formBlocksAfter) ? this.config.formBlocksAfter : [];
|
|
4269
4781
|
}
|
|
4782
|
+
get formBlocksBeforeActions() {
|
|
4783
|
+
return Array.isArray(this.config?.formBlocksBeforeActions) ? this.config.formBlocksBeforeActions : [];
|
|
4784
|
+
}
|
|
4785
|
+
get beforeActionsPlacement() {
|
|
4786
|
+
return (this.config?.formBlocksBeforeActionsPlacement || 'afterSections');
|
|
4787
|
+
}
|
|
4788
|
+
get actionPlacement() {
|
|
4789
|
+
return (this.config.actions?.placement || 'afterSections');
|
|
4790
|
+
}
|
|
4270
4791
|
/**
|
|
4271
4792
|
* Precedence for editorial widget interpolation:
|
|
4272
4793
|
* 1. form runtime context
|
|
@@ -4858,17 +5379,23 @@ class PraxisDynamicForm {
|
|
|
4858
5379
|
}
|
|
4859
5380
|
}
|
|
4860
5381
|
if (changes['config'] || changes['presentationModeGlobal']) {
|
|
5382
|
+
let shouldRebuildFromExternalConfig = false;
|
|
4861
5383
|
if (changes['config']?.currentValue) {
|
|
4862
5384
|
this.config = normalizeFormConfig$1(changes['config'].currentValue);
|
|
4863
5385
|
this.logRowGapsSnapshot('config@input');
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
5386
|
+
const hasRenderableConfig = (this.config?.sections?.length ?? 0) > 0
|
|
5387
|
+
|| (this.config?.fieldMetadata?.length ?? 0) > 0;
|
|
5388
|
+
if (hasRenderableConfig) {
|
|
5389
|
+
// External config updates must rebuild even when a resourcePath exists.
|
|
5390
|
+
shouldRebuildFromExternalConfig = !this.isLoading;
|
|
4869
5391
|
}
|
|
4870
5392
|
}
|
|
4871
5393
|
this.applyPresentationVars();
|
|
5394
|
+
if (shouldRebuildFromExternalConfig) {
|
|
5395
|
+
this.buildFormFromConfig();
|
|
5396
|
+
this.isInitialized = true;
|
|
5397
|
+
this.initializationStatus = 'success';
|
|
5398
|
+
}
|
|
4872
5399
|
// On external config changes, if we have base and resourcePath configured, verify in background
|
|
4873
5400
|
if (this.resourcePath && (this.config?.sections?.length ?? 0) > 0 && this.formId) {
|
|
4874
5401
|
this.verifyServerSchemaVersion().catch(() => { });
|
|
@@ -5482,6 +6009,14 @@ class PraxisDynamicForm {
|
|
|
5482
6009
|
const props = this.getSectionRuleProps(section);
|
|
5483
6010
|
return props.description !== undefined ? props.description : section.description;
|
|
5484
6011
|
}
|
|
6012
|
+
getSectionAppearance(section) {
|
|
6013
|
+
const props = this.getSectionRuleProps(section);
|
|
6014
|
+
return props.appearance ?? section.appearance;
|
|
6015
|
+
}
|
|
6016
|
+
getSectionStepLabel(section) {
|
|
6017
|
+
const props = this.getSectionRuleProps(section);
|
|
6018
|
+
return props.stepLabel ?? section.stepLabel;
|
|
6019
|
+
}
|
|
5485
6020
|
getSectionTitleColor(section) {
|
|
5486
6021
|
const props = this.getSectionRuleProps(section);
|
|
5487
6022
|
return props.titleColor !== undefined ? props.titleColor : section.titleColor;
|
|
@@ -5562,6 +6097,9 @@ class PraxisDynamicForm {
|
|
|
5562
6097
|
}
|
|
5563
6098
|
getSectionClasses(section) {
|
|
5564
6099
|
const classes = [];
|
|
6100
|
+
const appearance = this.getSectionAppearance(section);
|
|
6101
|
+
if (appearance)
|
|
6102
|
+
classes.push(`section-appearance-${appearance}`);
|
|
5565
6103
|
const baseClass = section.className;
|
|
5566
6104
|
if (baseClass)
|
|
5567
6105
|
classes.push(baseClass);
|
|
@@ -6125,6 +6663,11 @@ class PraxisDynamicForm {
|
|
|
6125
6663
|
}
|
|
6126
6664
|
async openConfigEditor() {
|
|
6127
6665
|
const initialConfig = normalizeFormConfig$1(this.config);
|
|
6666
|
+
const initialDocument = createDynamicFormAuthoringDocument({
|
|
6667
|
+
config: initialConfig,
|
|
6668
|
+
bindings: { mode: this.mode },
|
|
6669
|
+
contextSnapshot: this.captureCurrentContextSnapshot(),
|
|
6670
|
+
});
|
|
6128
6671
|
const { PraxisDynamicFormConfigEditor } = await Promise.resolve().then(function () { return index; });
|
|
6129
6672
|
const ref = this.settingsPanel.open({
|
|
6130
6673
|
id: `form.${this.formId}`,
|
|
@@ -6133,68 +6676,28 @@ class PraxisDynamicForm {
|
|
|
6133
6676
|
content: {
|
|
6134
6677
|
component: PraxisDynamicFormConfigEditor,
|
|
6135
6678
|
inputs: {
|
|
6136
|
-
|
|
6137
|
-
backConfig: this.backConfig,
|
|
6679
|
+
document: initialDocument,
|
|
6138
6680
|
formId: this.formId,
|
|
6139
6681
|
componentKeyId: this.componentKeyId(),
|
|
6140
|
-
|
|
6682
|
+
presentationMode: this.presentationModeGlobal === true,
|
|
6141
6683
|
},
|
|
6142
6684
|
},
|
|
6143
6685
|
});
|
|
6144
|
-
ref.applied$.pipe(takeUntil(this.destroy$)).subscribe(
|
|
6686
|
+
ref.applied$.pipe(takeUntil(this.destroy$)).subscribe((cfg) => {
|
|
6145
6687
|
try {
|
|
6146
6688
|
this.debugLog('[PDF] settingsPanel.applied$', { hasCfg: !!cfg });
|
|
6147
6689
|
}
|
|
6148
6690
|
catch { }
|
|
6149
6691
|
if (cfg) {
|
|
6150
|
-
const
|
|
6151
|
-
const
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
if (backKey) {
|
|
6160
|
-
this.asyncConfigStorage.saveConfig(backKey, backCfg).subscribe();
|
|
6161
|
-
this.backConfig = backCfg;
|
|
6162
|
-
}
|
|
6163
|
-
}
|
|
6164
|
-
if (presCfg) {
|
|
6165
|
-
const presKey = this.formPresKey(persistMode);
|
|
6166
|
-
if (presKey) {
|
|
6167
|
-
this.asyncConfigStorage.saveConfig(presKey, presCfg).subscribe();
|
|
6168
|
-
}
|
|
6169
|
-
// Aplicar imediatamente as preferências de apresentação no formulário
|
|
6170
|
-
this.applyPresentationVars();
|
|
6171
|
-
}
|
|
6172
|
-
if (schemaPrefs) {
|
|
6173
|
-
const prefsKey = this.getPrefsKey();
|
|
6174
|
-
if (prefsKey) {
|
|
6175
|
-
this.asyncConfigStorage.saveConfig(prefsKey, schemaPrefs).subscribe();
|
|
6176
|
-
this.resolveSchemaPrefs();
|
|
6177
|
-
}
|
|
6178
|
-
}
|
|
6179
|
-
if (inputsPatch && typeof inputsPatch === 'object') {
|
|
6180
|
-
if (typeof inputsPatch.mode === 'string') {
|
|
6181
|
-
const mv = inputsPatch.mode;
|
|
6182
|
-
if (mv === 'create' || mv === 'edit' || mv === 'view') {
|
|
6183
|
-
this.mode = mv;
|
|
6184
|
-
const inputsKey = this.formInputsKey();
|
|
6185
|
-
if (inputsKey) {
|
|
6186
|
-
const prev = await firstValueFrom(this.asyncConfigStorage.loadConfig(inputsKey)).catch(() => ({}));
|
|
6187
|
-
this.asyncConfigStorage.saveConfig(inputsKey, { ...prev, mode: mv }).subscribe();
|
|
6188
|
-
}
|
|
6189
|
-
}
|
|
6190
|
-
}
|
|
6191
|
-
}
|
|
6192
|
-
this.configChange.emit(this.config);
|
|
6193
|
-
this.buildFormFromConfig();
|
|
6194
|
-
const patchMode = typeof inputsPatch?.mode === 'string' ? inputsPatch.mode : undefined;
|
|
6195
|
-
if (patchMode && patchMode !== persistMode) {
|
|
6196
|
-
this.reloadForModeChange();
|
|
6197
|
-
}
|
|
6692
|
+
const doc = parseLegacyOrDynamicFormDocument(cfg);
|
|
6693
|
+
const plan = buildDynamicFormApplyPlan(doc, this.buildDynamicFormEditorRuntimeContext(), {
|
|
6694
|
+
saveConfig: false,
|
|
6695
|
+
saveBindings: true,
|
|
6696
|
+
saveContextSnapshot: true,
|
|
6697
|
+
attachSchemaSnapshot: true,
|
|
6698
|
+
replaceContext: true,
|
|
6699
|
+
});
|
|
6700
|
+
this.executeDynamicFormEditorApplyPlan(plan, 'settings-applied');
|
|
6198
6701
|
}
|
|
6199
6702
|
});
|
|
6200
6703
|
// Handle reset from Settings Panel: clear persisted config so globals/user defaults apply
|
|
@@ -6203,70 +6706,23 @@ class PraxisDynamicForm {
|
|
|
6203
6706
|
this.debugLog('[PDF] settingsPanel.reset$');
|
|
6204
6707
|
}
|
|
6205
6708
|
catch { }
|
|
6206
|
-
|
|
6207
|
-
const key = this.formConfigKey();
|
|
6208
|
-
if (key)
|
|
6209
|
-
this.asyncConfigStorage.clearConfig(key).subscribe();
|
|
6210
|
-
}
|
|
6211
|
-
catch { }
|
|
6709
|
+
this.clearDynamicFormEditorPersistence();
|
|
6212
6710
|
});
|
|
6213
|
-
ref.saved$.pipe(takeUntil(this.destroy$)).subscribe(
|
|
6711
|
+
ref.saved$.pipe(takeUntil(this.destroy$)).subscribe((cfg) => {
|
|
6214
6712
|
try {
|
|
6215
6713
|
this.debugLog('[PDF] settingsPanel.saved$', { hasCfg: !!cfg });
|
|
6216
6714
|
}
|
|
6217
6715
|
catch { }
|
|
6218
6716
|
if (cfg) {
|
|
6219
|
-
const
|
|
6220
|
-
const
|
|
6221
|
-
|
|
6222
|
-
|
|
6223
|
-
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
this.persistFormConfig(key, this.config);
|
|
6229
|
-
if (backCfg) {
|
|
6230
|
-
const backKey = this.crudBackKey();
|
|
6231
|
-
if (backKey) {
|
|
6232
|
-
this.asyncConfigStorage.saveConfig(backKey, backCfg).subscribe();
|
|
6233
|
-
this.backConfig = backCfg;
|
|
6234
|
-
}
|
|
6235
|
-
}
|
|
6236
|
-
if (presCfg) {
|
|
6237
|
-
const presKey = this.formPresKey(persistMode);
|
|
6238
|
-
if (presKey) {
|
|
6239
|
-
this.asyncConfigStorage.saveConfig(presKey, presCfg).subscribe();
|
|
6240
|
-
}
|
|
6241
|
-
// Aplicar imediatamente as preferências de apresentação no formulário
|
|
6242
|
-
this.applyPresentationVars();
|
|
6243
|
-
}
|
|
6244
|
-
if (schemaPrefs) {
|
|
6245
|
-
const prefsKey = this.getPrefsKey();
|
|
6246
|
-
if (prefsKey) {
|
|
6247
|
-
this.asyncConfigStorage.saveConfig(prefsKey, schemaPrefs).subscribe();
|
|
6248
|
-
this.resolveSchemaPrefs();
|
|
6249
|
-
}
|
|
6250
|
-
}
|
|
6251
|
-
if (inputsPatch && typeof inputsPatch === 'object') {
|
|
6252
|
-
if (typeof inputsPatch.mode === 'string') {
|
|
6253
|
-
const mv = inputsPatch.mode;
|
|
6254
|
-
if (mv === 'create' || mv === 'edit' || mv === 'view') {
|
|
6255
|
-
this.mode = mv;
|
|
6256
|
-
const inputsKey = this.formInputsKey();
|
|
6257
|
-
if (inputsKey) {
|
|
6258
|
-
const prev = await firstValueFrom(this.asyncConfigStorage.loadConfig(inputsKey)).catch(() => ({}));
|
|
6259
|
-
this.asyncConfigStorage.saveConfig(inputsKey, { ...prev, mode: mv }).subscribe();
|
|
6260
|
-
}
|
|
6261
|
-
}
|
|
6262
|
-
}
|
|
6263
|
-
}
|
|
6264
|
-
this.configChange.emit(this.config);
|
|
6265
|
-
this.buildFormFromConfig();
|
|
6266
|
-
const patchMode = typeof inputsPatch?.mode === 'string' ? inputsPatch.mode : undefined;
|
|
6267
|
-
if (patchMode && patchMode !== persistMode) {
|
|
6268
|
-
this.reloadForModeChange();
|
|
6269
|
-
}
|
|
6717
|
+
const doc = parseLegacyOrDynamicFormDocument(cfg);
|
|
6718
|
+
const plan = buildDynamicFormApplyPlan(doc, this.buildDynamicFormEditorRuntimeContext(), {
|
|
6719
|
+
saveConfig: true,
|
|
6720
|
+
saveBindings: true,
|
|
6721
|
+
saveContextSnapshot: true,
|
|
6722
|
+
attachSchemaSnapshot: true,
|
|
6723
|
+
replaceContext: true,
|
|
6724
|
+
});
|
|
6725
|
+
this.executeDynamicFormEditorApplyPlan(plan, 'settings-saved');
|
|
6270
6726
|
}
|
|
6271
6727
|
});
|
|
6272
6728
|
ref.reset$.pipe(takeUntil(this.destroy$)).subscribe(() => {
|
|
@@ -6274,9 +6730,14 @@ class PraxisDynamicForm {
|
|
|
6274
6730
|
this.debugLog('[PDF] settingsPanel.reset$ (second handler)');
|
|
6275
6731
|
}
|
|
6276
6732
|
catch { }
|
|
6277
|
-
this.
|
|
6278
|
-
|
|
6279
|
-
|
|
6733
|
+
const resetPlan = buildDynamicFormApplyPlan(initialDocument, this.buildDynamicFormEditorRuntimeContext(), {
|
|
6734
|
+
saveConfig: false,
|
|
6735
|
+
saveBindings: false,
|
|
6736
|
+
saveContextSnapshot: false,
|
|
6737
|
+
attachSchemaSnapshot: true,
|
|
6738
|
+
replaceContext: true,
|
|
6739
|
+
});
|
|
6740
|
+
this.executeDynamicFormEditorApplyPlan(resetPlan, 'settings-reset');
|
|
6280
6741
|
});
|
|
6281
6742
|
}
|
|
6282
6743
|
// --- Canvas Interaction Methods ---
|
|
@@ -6780,6 +7241,7 @@ class PraxisDynamicForm {
|
|
|
6780
7241
|
name: fieldMeta.name,
|
|
6781
7242
|
label: fieldMeta.label,
|
|
6782
7243
|
placeholder: fieldMeta.placeholder,
|
|
7244
|
+
description: fieldMeta.description,
|
|
6783
7245
|
hint: fieldMeta.hint,
|
|
6784
7246
|
// Canonical constraints only (no legacy)
|
|
6785
7247
|
required: fieldMeta.required,
|
|
@@ -7144,6 +7606,87 @@ class PraxisDynamicForm {
|
|
|
7144
7606
|
seed.layout = fmAny.layout;
|
|
7145
7607
|
if (fmAny.labelPosition !== undefined)
|
|
7146
7608
|
seed.labelPosition = fmAny.labelPosition;
|
|
7609
|
+
if (fmAny.selectionMode !== undefined)
|
|
7610
|
+
seed.selectionMode = fmAny.selectionMode;
|
|
7611
|
+
if (fmAny.variant !== undefined)
|
|
7612
|
+
seed.variant = fmAny.variant;
|
|
7613
|
+
if (fmAny.density !== undefined)
|
|
7614
|
+
seed.density = fmAny.density;
|
|
7615
|
+
}
|
|
7616
|
+
// CHECKBOX
|
|
7617
|
+
if (ctU === 'CHECKBOX') {
|
|
7618
|
+
const fmAny = fieldMeta;
|
|
7619
|
+
const src = fmAny.checkboxOptions || fmAny.options;
|
|
7620
|
+
if (src) {
|
|
7621
|
+
try {
|
|
7622
|
+
seed.options = JSON.stringify(src, null, 2);
|
|
7623
|
+
}
|
|
7624
|
+
catch {
|
|
7625
|
+
seed.options = src;
|
|
7626
|
+
}
|
|
7627
|
+
try {
|
|
7628
|
+
this.debugLog('[PDF][EditorSeed] options prepared (CHECKBOX)', {
|
|
7629
|
+
fieldName,
|
|
7630
|
+
controlType,
|
|
7631
|
+
valueType: typeof src,
|
|
7632
|
+
countOrLength: Array.isArray(src) ? src.length : (typeof src === 'string' ? src.length : undefined),
|
|
7633
|
+
});
|
|
7634
|
+
}
|
|
7635
|
+
catch { }
|
|
7636
|
+
}
|
|
7637
|
+
if (fmAny.optionLabelKey !== undefined)
|
|
7638
|
+
seed.optionLabelKey = fmAny.optionLabelKey;
|
|
7639
|
+
if (fmAny.optionValueKey !== undefined)
|
|
7640
|
+
seed.optionValueKey = fmAny.optionValueKey;
|
|
7641
|
+
if (fmAny.resourcePath !== undefined || fmAny.endpoint !== undefined) {
|
|
7642
|
+
seed.resourcePath = fmAny.resourcePath ?? fmAny.endpoint;
|
|
7643
|
+
}
|
|
7644
|
+
if (fmAny.filterCriteria !== undefined) {
|
|
7645
|
+
try {
|
|
7646
|
+
seed.filterCriteria = JSON.stringify(fmAny.filterCriteria, null, 2);
|
|
7647
|
+
}
|
|
7648
|
+
catch {
|
|
7649
|
+
seed.filterCriteria = fmAny.filterCriteria;
|
|
7650
|
+
}
|
|
7651
|
+
}
|
|
7652
|
+
if (fmAny.searchable !== undefined)
|
|
7653
|
+
seed.searchable = fmAny.searchable;
|
|
7654
|
+
if (fmAny.selectAll !== undefined)
|
|
7655
|
+
seed.selectAll = fmAny.selectAll;
|
|
7656
|
+
if (fmAny.maxSelections !== undefined)
|
|
7657
|
+
seed.maxSelections = fmAny.maxSelections;
|
|
7658
|
+
if (fmAny.layout !== undefined)
|
|
7659
|
+
seed.layout = fmAny.layout;
|
|
7660
|
+
if (fmAny.labelPosition !== undefined)
|
|
7661
|
+
seed.labelPosition = fmAny.labelPosition;
|
|
7662
|
+
if (fmAny.color !== undefined)
|
|
7663
|
+
seed.color = fmAny.color;
|
|
7664
|
+
if (fmAny.indeterminate !== undefined)
|
|
7665
|
+
seed.indeterminate = !!fmAny.indeterminate;
|
|
7666
|
+
if (fmAny.selectionMode !== undefined)
|
|
7667
|
+
seed.selectionMode = fmAny.selectionMode;
|
|
7668
|
+
if (fmAny.variant !== undefined)
|
|
7669
|
+
seed.variant = fmAny.variant;
|
|
7670
|
+
if (fmAny.density !== undefined)
|
|
7671
|
+
seed.density = fmAny.density;
|
|
7672
|
+
if (fmAny.requiredChecked !== undefined)
|
|
7673
|
+
seed.requiredChecked = !!fmAny.requiredChecked;
|
|
7674
|
+
else if (fmAny.validators?.requiredChecked !== undefined)
|
|
7675
|
+
seed.requiredChecked = !!fmAny.validators.requiredChecked;
|
|
7676
|
+
if (fmAny.links !== undefined) {
|
|
7677
|
+
try {
|
|
7678
|
+
seed.links = JSON.stringify(fmAny.links, null, 2);
|
|
7679
|
+
}
|
|
7680
|
+
catch {
|
|
7681
|
+
seed.links = fmAny.links;
|
|
7682
|
+
}
|
|
7683
|
+
}
|
|
7684
|
+
if (fmAny.linkText !== undefined)
|
|
7685
|
+
seed.linkText = fmAny.linkText;
|
|
7686
|
+
if (fmAny.linkUrl !== undefined)
|
|
7687
|
+
seed.linkUrl = fmAny.linkUrl;
|
|
7688
|
+
if (fmAny.linkTarget !== undefined)
|
|
7689
|
+
seed.linkTarget = fmAny.linkTarget;
|
|
7147
7690
|
}
|
|
7148
7691
|
// BUTTON_TOGGLE
|
|
7149
7692
|
if (ctU === 'BUTTON_TOGGLE') {
|
|
@@ -8194,6 +8737,12 @@ class PraxisDynamicForm {
|
|
|
8194
8737
|
const norm = normalizeOptions(patch.options) ?? [];
|
|
8195
8738
|
fm.checkboxOptions = norm;
|
|
8196
8739
|
}
|
|
8740
|
+
if (patch.selectionMode !== undefined)
|
|
8741
|
+
fm.selectionMode = patch.selectionMode;
|
|
8742
|
+
if (patch.variant !== undefined)
|
|
8743
|
+
fm.variant = patch.variant;
|
|
8744
|
+
if (patch.density !== undefined)
|
|
8745
|
+
fm.density = patch.density;
|
|
8197
8746
|
if (patch.layout !== undefined)
|
|
8198
8747
|
fm.layout = patch.layout;
|
|
8199
8748
|
if (patch.labelPosition !== undefined)
|
|
@@ -8202,6 +8751,24 @@ class PraxisDynamicForm {
|
|
|
8202
8751
|
fm.color = patch.color;
|
|
8203
8752
|
if (patch.indeterminate !== undefined)
|
|
8204
8753
|
fm.indeterminate = !!patch.indeterminate;
|
|
8754
|
+
if (patch.requiredChecked !== undefined) {
|
|
8755
|
+
fm.requiredChecked = !!patch.requiredChecked;
|
|
8756
|
+
fm.validators = { ...(fm.validators || {}), requiredChecked: !!patch.requiredChecked };
|
|
8757
|
+
}
|
|
8758
|
+
if (patch.links !== undefined) {
|
|
8759
|
+
const links = patch.links;
|
|
8760
|
+
if (typeof links === 'string') {
|
|
8761
|
+
try {
|
|
8762
|
+
fm.links = JSON.parse(links);
|
|
8763
|
+
}
|
|
8764
|
+
catch {
|
|
8765
|
+
fm.links = links;
|
|
8766
|
+
}
|
|
8767
|
+
}
|
|
8768
|
+
else {
|
|
8769
|
+
fm.links = links;
|
|
8770
|
+
}
|
|
8771
|
+
}
|
|
8205
8772
|
if (patch.linkText !== undefined)
|
|
8206
8773
|
fm.linkText = patch.linkText;
|
|
8207
8774
|
if (patch.linkUrl !== undefined)
|
|
@@ -8209,6 +8776,20 @@ class PraxisDynamicForm {
|
|
|
8209
8776
|
if (patch.linkTarget !== undefined)
|
|
8210
8777
|
fm.linkTarget = patch.linkTarget;
|
|
8211
8778
|
}
|
|
8779
|
+
if (fctApply === 'RADIO') {
|
|
8780
|
+
if (patch.selectionMode !== undefined)
|
|
8781
|
+
fm.selectionMode = patch.selectionMode;
|
|
8782
|
+
if (patch.variant !== undefined)
|
|
8783
|
+
fm.variant = patch.variant;
|
|
8784
|
+
if (patch.density !== undefined)
|
|
8785
|
+
fm.density = patch.density;
|
|
8786
|
+
if (patch.layout !== undefined)
|
|
8787
|
+
fm.layout = patch.layout;
|
|
8788
|
+
if (patch.labelPosition !== undefined)
|
|
8789
|
+
fm.labelPosition = patch.labelPosition;
|
|
8790
|
+
if (patch.color !== undefined)
|
|
8791
|
+
fm.color = patch.color;
|
|
8792
|
+
}
|
|
8212
8793
|
// TOGGLE
|
|
8213
8794
|
if (fctApply === 'TOGGLE') {
|
|
8214
8795
|
if (patch.color !== undefined)
|
|
@@ -9199,41 +9780,272 @@ class PraxisDynamicForm {
|
|
|
9199
9780
|
// 5. Trigger change detection to be safe.
|
|
9200
9781
|
this.cdr.detectChanges();
|
|
9201
9782
|
}
|
|
9202
|
-
ngOnDestroy() {
|
|
9203
|
-
this.destroy$.next();
|
|
9204
|
-
this.destroy$.complete();
|
|
9205
|
-
this.schemaCache = null;
|
|
9206
|
-
this.isInitialized = false;
|
|
9207
|
-
if (this.mountAnimationTimer) {
|
|
9208
|
-
clearTimeout(this.mountAnimationTimer);
|
|
9209
|
-
this.mountAnimationTimer = null;
|
|
9783
|
+
ngOnDestroy() {
|
|
9784
|
+
this.destroy$.next();
|
|
9785
|
+
this.destroy$.complete();
|
|
9786
|
+
this.schemaCache = null;
|
|
9787
|
+
this.isInitialized = false;
|
|
9788
|
+
if (this.mountAnimationTimer) {
|
|
9789
|
+
clearTimeout(this.mountAnimationTimer);
|
|
9790
|
+
this.mountAnimationTimer = null;
|
|
9791
|
+
}
|
|
9792
|
+
}
|
|
9793
|
+
// TrackBy functions for performance optimization
|
|
9794
|
+
trackBySection(index, section) {
|
|
9795
|
+
return section.id;
|
|
9796
|
+
}
|
|
9797
|
+
trackByRow(index, row) {
|
|
9798
|
+
return row.id;
|
|
9799
|
+
}
|
|
9800
|
+
trackByColumn(index, column) {
|
|
9801
|
+
return column.id;
|
|
9802
|
+
}
|
|
9803
|
+
applyConfigFromAdapter(nextConfig) {
|
|
9804
|
+
const raw = nextConfig;
|
|
9805
|
+
const isCanonicalDocument = !!raw &&
|
|
9806
|
+
typeof raw === 'object' &&
|
|
9807
|
+
raw.kind === 'praxis.dynamic-form.editor';
|
|
9808
|
+
const isLegacyEditorPayload = !!raw &&
|
|
9809
|
+
typeof raw === 'object' &&
|
|
9810
|
+
(Object.prototype.hasOwnProperty.call(raw, 'formConfig') ||
|
|
9811
|
+
Object.prototype.hasOwnProperty.call(raw, 'inputsPatch') ||
|
|
9812
|
+
Object.prototype.hasOwnProperty.call(raw, 'backConfig') ||
|
|
9813
|
+
Object.prototype.hasOwnProperty.call(raw, 'presentation') ||
|
|
9814
|
+
Object.prototype.hasOwnProperty.call(raw, 'schemaPrefs'));
|
|
9815
|
+
const doc = isCanonicalDocument || isLegacyEditorPayload
|
|
9816
|
+
? parseLegacyOrDynamicFormDocument(nextConfig)
|
|
9817
|
+
: createDynamicFormAuthoringDocument({
|
|
9818
|
+
config: nextConfig,
|
|
9819
|
+
bindings: { mode: this.mode },
|
|
9820
|
+
contextSnapshot: this.captureCurrentContextSnapshot(),
|
|
9821
|
+
});
|
|
9822
|
+
const plan = buildDynamicFormApplyPlan(doc, this.buildDynamicFormEditorRuntimeContext(), {
|
|
9823
|
+
saveConfig: false,
|
|
9824
|
+
saveBindings: false,
|
|
9825
|
+
saveContextSnapshot: false,
|
|
9826
|
+
attachSchemaSnapshot: true,
|
|
9827
|
+
replaceContext: isCanonicalDocument,
|
|
9828
|
+
});
|
|
9829
|
+
this.executeDynamicFormEditorApplyPlan(plan, 'adapter-apply');
|
|
9830
|
+
}
|
|
9831
|
+
buildDynamicFormEditorRuntimeContext() {
|
|
9832
|
+
return {
|
|
9833
|
+
currentBindings: {
|
|
9834
|
+
mode: this.mode,
|
|
9835
|
+
},
|
|
9836
|
+
currentContextSnapshot: this.captureCurrentContextSnapshot(),
|
|
9837
|
+
server: {
|
|
9838
|
+
schemaHash: this.lastSchemaMeta?.serverHash ||
|
|
9839
|
+
this.schemaState.meta?.serverHash ||
|
|
9840
|
+
this.config?.metadata?.serverHash,
|
|
9841
|
+
},
|
|
9842
|
+
};
|
|
9843
|
+
}
|
|
9844
|
+
captureCurrentContextSnapshot() {
|
|
9845
|
+
return {
|
|
9846
|
+
backConfig: this.backConfig ? structuredClone(this.backConfig) : undefined,
|
|
9847
|
+
presentation: this.captureCurrentPresentationSnapshot(),
|
|
9848
|
+
schemaPrefs: this.captureCurrentSchemaPrefsSnapshot(),
|
|
9849
|
+
};
|
|
9850
|
+
}
|
|
9851
|
+
captureCurrentPresentationSnapshot() {
|
|
9852
|
+
return {
|
|
9853
|
+
labelPosition: this.presentationVars.labelPosition,
|
|
9854
|
+
labelFontSize: this.parsePresentationCssNumber(this.presentationVars.labelSize),
|
|
9855
|
+
valueFontSize: this.parsePresentationCssNumber(this.presentationVars.valueSize),
|
|
9856
|
+
compact: this.presentationVars.compact,
|
|
9857
|
+
density: this.presentationVars.density,
|
|
9858
|
+
labelWidth: this.parsePresentationCssNumber(this.presentationVars.labelWidth),
|
|
9859
|
+
labelAlign: this.presentationVars.labelAlign,
|
|
9860
|
+
valueAlign: this.presentationVars.valueAlign,
|
|
9861
|
+
};
|
|
9862
|
+
}
|
|
9863
|
+
captureCurrentSchemaPrefsSnapshot() {
|
|
9864
|
+
return {
|
|
9865
|
+
notifyIfOutdated: this.notifyIfOutdated,
|
|
9866
|
+
snoozeMs: this.snoozeMs,
|
|
9867
|
+
autoOpenSettingsOnOutdated: this.autoOpenSettingsOnOutdated,
|
|
9868
|
+
};
|
|
9869
|
+
}
|
|
9870
|
+
executeDynamicFormEditorApplyPlan(plan, trigger) {
|
|
9871
|
+
if (plan.diagnostics.some((item) => item.level === 'error')) {
|
|
9872
|
+
this.warnLog('[PraxisDynamicForm] Editor document validation failed', {
|
|
9873
|
+
diagnostics: plan.diagnostics,
|
|
9874
|
+
});
|
|
9875
|
+
return;
|
|
9876
|
+
}
|
|
9877
|
+
const nextMode = plan.bindingsPatch?.mode;
|
|
9878
|
+
const previousMode = this.mode;
|
|
9879
|
+
const targetMode = nextMode || (plan.runtime?.clearMode ? 'create' : undefined);
|
|
9880
|
+
if (targetMode) {
|
|
9881
|
+
this.mode = targetMode;
|
|
9882
|
+
if (plan.persistence?.saveBindings) {
|
|
9883
|
+
const inputsKey = this.formInputsKey();
|
|
9884
|
+
if (inputsKey) {
|
|
9885
|
+
if (plan.runtime?.clearMode) {
|
|
9886
|
+
this.asyncConfigStorage.clearConfig(inputsKey).subscribe();
|
|
9887
|
+
}
|
|
9888
|
+
else {
|
|
9889
|
+
this.asyncConfigStorage
|
|
9890
|
+
.loadConfig(inputsKey)
|
|
9891
|
+
.pipe(take(1))
|
|
9892
|
+
.subscribe((prev) => {
|
|
9893
|
+
this.asyncConfigStorage
|
|
9894
|
+
.saveConfig(inputsKey, { ...(prev || {}), mode: targetMode })
|
|
9895
|
+
.subscribe();
|
|
9896
|
+
});
|
|
9897
|
+
}
|
|
9898
|
+
}
|
|
9899
|
+
}
|
|
9900
|
+
}
|
|
9901
|
+
const contextSnapshot = plan.contextSnapshot;
|
|
9902
|
+
if (plan.runtime?.refreshBackNavigation && contextSnapshot?.backConfig) {
|
|
9903
|
+
this.backConfig = contextSnapshot.backConfig;
|
|
9904
|
+
if (plan.persistence?.saveContextSnapshot) {
|
|
9905
|
+
const backKey = this.crudBackKey();
|
|
9906
|
+
if (backKey) {
|
|
9907
|
+
this.asyncConfigStorage
|
|
9908
|
+
.saveConfig(backKey, contextSnapshot.backConfig)
|
|
9909
|
+
.subscribe();
|
|
9910
|
+
}
|
|
9911
|
+
}
|
|
9912
|
+
}
|
|
9913
|
+
else if (plan.runtime?.clearBackNavigation) {
|
|
9914
|
+
this.backConfig = undefined;
|
|
9915
|
+
if (plan.persistence?.saveContextSnapshot) {
|
|
9916
|
+
const backKey = this.crudBackKey();
|
|
9917
|
+
if (backKey) {
|
|
9918
|
+
this.asyncConfigStorage.clearConfig(backKey).subscribe();
|
|
9919
|
+
}
|
|
9920
|
+
}
|
|
9921
|
+
}
|
|
9922
|
+
if (plan.runtime?.refreshPresentation && contextSnapshot?.presentation) {
|
|
9923
|
+
if (plan.persistence?.saveContextSnapshot) {
|
|
9924
|
+
const presKey = this.formPresKey(this.resolveModeKey(targetMode || previousMode));
|
|
9925
|
+
if (presKey) {
|
|
9926
|
+
this.asyncConfigStorage
|
|
9927
|
+
.saveConfig(presKey, contextSnapshot.presentation)
|
|
9928
|
+
.subscribe();
|
|
9929
|
+
}
|
|
9930
|
+
}
|
|
9931
|
+
this.applyPresentationConfig(contextSnapshot.presentation);
|
|
9932
|
+
}
|
|
9933
|
+
else if (plan.runtime?.clearPresentation) {
|
|
9934
|
+
if (plan.persistence?.saveContextSnapshot) {
|
|
9935
|
+
this.clearStoredPresentationSnapshots();
|
|
9936
|
+
}
|
|
9937
|
+
this.resetPresentationVarsToDefaults();
|
|
9938
|
+
}
|
|
9939
|
+
if (plan.runtime?.refreshSchemaState && contextSnapshot?.schemaPrefs) {
|
|
9940
|
+
this.notifyIfOutdated = contextSnapshot.schemaPrefs.notifyIfOutdated || 'both';
|
|
9941
|
+
this.snoozeMs = contextSnapshot.schemaPrefs.snoozeMs ?? this.snoozeMs;
|
|
9942
|
+
this.autoOpenSettingsOnOutdated =
|
|
9943
|
+
contextSnapshot.schemaPrefs.autoOpenSettingsOnOutdated === true;
|
|
9944
|
+
if (plan.persistence?.saveContextSnapshot) {
|
|
9945
|
+
const prefsKey = this.getPrefsKey();
|
|
9946
|
+
if (prefsKey) {
|
|
9947
|
+
this.asyncConfigStorage
|
|
9948
|
+
.saveConfig(prefsKey, contextSnapshot.schemaPrefs)
|
|
9949
|
+
.subscribe();
|
|
9950
|
+
}
|
|
9951
|
+
}
|
|
9952
|
+
void this.resolveSchemaPrefs();
|
|
9953
|
+
}
|
|
9954
|
+
else if (plan.runtime?.clearSchemaState) {
|
|
9955
|
+
this.resetSchemaPrefsToDefaults();
|
|
9956
|
+
if (plan.persistence?.saveContextSnapshot) {
|
|
9957
|
+
const prefsKey = this.getPrefsKey();
|
|
9958
|
+
if (prefsKey) {
|
|
9959
|
+
this.asyncConfigStorage.clearConfig(prefsKey).subscribe();
|
|
9960
|
+
}
|
|
9961
|
+
}
|
|
9962
|
+
}
|
|
9963
|
+
const nextConfig = this.attachRuntimeMetadataToDynamicFormConfig(plan.canonicalConfig, plan.metadata?.attachSchemaSnapshot === true);
|
|
9964
|
+
this.config = normalizeFormConfig$1(nextConfig);
|
|
9965
|
+
if (plan.persistence?.saveConfig) {
|
|
9966
|
+
const key = this.formConfigKey(this.resolveModeKey(targetMode || this.mode));
|
|
9967
|
+
this.persistFormConfig(key, this.config);
|
|
9968
|
+
}
|
|
9969
|
+
this.configChange.emit(this.config);
|
|
9970
|
+
if (plan.runtime?.rebuildForm) {
|
|
9971
|
+
this.buildFormFromConfig();
|
|
9972
|
+
}
|
|
9973
|
+
if (plan.runtime?.rebindMode && targetMode && targetMode !== previousMode) {
|
|
9974
|
+
this.reloadForModeChange();
|
|
9975
|
+
}
|
|
9976
|
+
this.cdr.detectChanges();
|
|
9977
|
+
try {
|
|
9978
|
+
this.debugLog('[PDF] executeDynamicFormEditorApplyPlan()', { trigger, plan });
|
|
9979
|
+
}
|
|
9980
|
+
catch { }
|
|
9981
|
+
}
|
|
9982
|
+
attachRuntimeMetadataToDynamicFormConfig(cfg, attachSchemaSnapshot) {
|
|
9983
|
+
if (!attachSchemaSnapshot) {
|
|
9984
|
+
return cfg;
|
|
9985
|
+
}
|
|
9986
|
+
const next = structuredClone(cfg);
|
|
9987
|
+
const schemaHash = this.lastSchemaMeta?.serverHash || this.schemaState.meta?.serverHash;
|
|
9988
|
+
if (!schemaHash) {
|
|
9989
|
+
return next;
|
|
9210
9990
|
}
|
|
9991
|
+
next.metadata = {
|
|
9992
|
+
...(next.metadata || {}),
|
|
9993
|
+
serverHash: schemaHash,
|
|
9994
|
+
};
|
|
9995
|
+
return next;
|
|
9211
9996
|
}
|
|
9212
|
-
|
|
9213
|
-
|
|
9214
|
-
|
|
9997
|
+
parsePresentationCssNumber(value) {
|
|
9998
|
+
if (!value)
|
|
9999
|
+
return null;
|
|
10000
|
+
const parsed = Number.parseFloat(value);
|
|
10001
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
9215
10002
|
}
|
|
9216
|
-
|
|
9217
|
-
|
|
10003
|
+
clearDynamicFormEditorPersistence(modes) {
|
|
10004
|
+
const inputsKey = this.formInputsKey();
|
|
10005
|
+
if (inputsKey) {
|
|
10006
|
+
this.asyncConfigStorage.clearConfig(inputsKey).subscribe();
|
|
10007
|
+
}
|
|
10008
|
+
const prefsKey = this.getPrefsKey();
|
|
10009
|
+
if (prefsKey) {
|
|
10010
|
+
this.asyncConfigStorage.clearConfig(prefsKey).subscribe();
|
|
10011
|
+
}
|
|
10012
|
+
const backKey = this.crudBackKey();
|
|
10013
|
+
if (backKey) {
|
|
10014
|
+
this.asyncConfigStorage.clearConfig(backKey).subscribe();
|
|
10015
|
+
}
|
|
10016
|
+
const scopedModes = Array.from(new Set((modes && modes.length ? modes : ['create', 'edit', 'view']).map((mode) => this.resolveModeKey(mode))));
|
|
10017
|
+
this.clearStoredPresentationSnapshots(scopedModes);
|
|
10018
|
+
scopedModes.forEach((mode) => {
|
|
10019
|
+
const key = this.formConfigKey(mode);
|
|
10020
|
+
if (key) {
|
|
10021
|
+
this.asyncConfigStorage.clearConfig(key).subscribe();
|
|
10022
|
+
}
|
|
10023
|
+
});
|
|
9218
10024
|
}
|
|
9219
|
-
|
|
9220
|
-
|
|
10025
|
+
clearStoredPresentationSnapshots(modes = ['create', 'edit', 'view']) {
|
|
10026
|
+
modes.forEach((mode) => {
|
|
10027
|
+
const presKey = this.formPresKey(mode);
|
|
10028
|
+
if (presKey) {
|
|
10029
|
+
this.asyncConfigStorage.clearConfig(presKey).subscribe();
|
|
10030
|
+
}
|
|
10031
|
+
});
|
|
9221
10032
|
}
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
|
|
9226
|
-
|
|
9227
|
-
|
|
9228
|
-
|
|
9229
|
-
|
|
9230
|
-
|
|
9231
|
-
|
|
10033
|
+
resetPresentationVarsToDefaults() {
|
|
10034
|
+
this.presentationVars = {
|
|
10035
|
+
labelPosition: 'above',
|
|
10036
|
+
labelSize: '0.78rem',
|
|
10037
|
+
valueSize: '1rem',
|
|
10038
|
+
compact: false,
|
|
10039
|
+
density: 'comfortable',
|
|
10040
|
+
labelWidth: '140px',
|
|
10041
|
+
labelAlign: 'start',
|
|
10042
|
+
valueAlign: 'start',
|
|
9232
10043
|
};
|
|
9233
|
-
|
|
9234
|
-
|
|
9235
|
-
this.
|
|
9236
|
-
this.
|
|
10044
|
+
}
|
|
10045
|
+
resetSchemaPrefsToDefaults() {
|
|
10046
|
+
this.notifyIfOutdated = 'both';
|
|
10047
|
+
this.snoozeMs = 24 * 60 * 60 * 1000;
|
|
10048
|
+
this.autoOpenSettingsOnOutdated = false;
|
|
9237
10049
|
}
|
|
9238
10050
|
// Public method for template access
|
|
9239
10051
|
retryInitialization() {
|
|
@@ -9522,7 +10334,7 @@ class PraxisDynamicForm {
|
|
|
9522
10334
|
}
|
|
9523
10335
|
}
|
|
9524
10336
|
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 });
|
|
9525
|
-
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 ((config.actions?.placement || 'afterSections') === '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 ((config.actions?.placement || 'afterSections') === '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 ((config.actions?.placement || 'afterSections') === '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"] }] });
|
|
10337
|
+
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 [attr.data-section-appearance]=\"getSectionAppearance(section) || null\"\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 (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}.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:600;color:var(--md-sys-color-on-surface)}.section-heading{display:flex;align-items:flex-start;gap:8px;margin-bottom:8px}.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(--md-sys-color-primary-container) 68%,transparent);color: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(--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,.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:20px;background:var(--pfx-form-section-surface, var(--md-sys-color-surface));border-color:color-mix(in srgb,var(--md-sys-color-outline-variant) 80%,transparent);box-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:18px}.form-section.section-appearance-step .section-title{font:var(--mdc-typography-headline-small, 600 24px/32px system-ui);color:var(--md-sys-color-on-surface);margin-bottom:10px}.form-section.section-appearance-step .section-description{color:color-mix(in srgb,var(--md-sys-color-on-surface-variant) 92%,var(--md-sys-color-on-surface) 8%);max-width:60ch}.form-section.section-appearance-step .section-body{padding-top:4px}.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(--md-sys-color-outline-variant) 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(--md-sys-color-on-surface) 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(--md-sys-color-on-surface-variant) 86%,var(--md-sys-color-on-surface) 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)}.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"] }] });
|
|
9526
10338
|
}
|
|
9527
10339
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisDynamicForm, decorators: [{
|
|
9528
10340
|
type: Component,
|
|
@@ -9543,7 +10355,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
9543
10355
|
PraxisFormActionsComponent,
|
|
9544
10356
|
EmptyStateCardComponent,
|
|
9545
10357
|
PraxisAiAssistantComponent,
|
|
9546
|
-
], 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 ((config.actions?.placement || 'afterSections') === '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 ((config.actions?.placement || 'afterSections') === '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 ((config.actions?.placement || 'afterSections') === '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"] }]
|
|
10358
|
+
], 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 [attr.data-section-appearance]=\"getSectionAppearance(section) || null\"\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 (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}.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:600;color:var(--md-sys-color-on-surface)}.section-heading{display:flex;align-items:flex-start;gap:8px;margin-bottom:8px}.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(--md-sys-color-primary-container) 68%,transparent);color: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(--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,.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:20px;background:var(--pfx-form-section-surface, var(--md-sys-color-surface));border-color:color-mix(in srgb,var(--md-sys-color-outline-variant) 80%,transparent);box-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:18px}.form-section.section-appearance-step .section-title{font:var(--mdc-typography-headline-small, 600 24px/32px system-ui);color:var(--md-sys-color-on-surface);margin-bottom:10px}.form-section.section-appearance-step .section-description{color:color-mix(in srgb,var(--md-sys-color-on-surface-variant) 92%,var(--md-sys-color-on-surface) 8%);max-width:60ch}.form-section.section-appearance-step .section-body{padding-top:4px}.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(--md-sys-color-outline-variant) 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(--md-sys-color-on-surface) 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(--md-sys-color-on-surface-variant) 86%,var(--md-sys-color-on-surface) 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)}.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"] }]
|
|
9547
10359
|
}], 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: [{
|
|
9548
10360
|
type: Inject,
|
|
9549
10361
|
args: [ASYNC_CONFIG_STORAGE]
|
|
@@ -9733,12 +10545,15 @@ class JsonConfigEditorComponent {
|
|
|
9733
10545
|
cdr;
|
|
9734
10546
|
configService;
|
|
9735
10547
|
config = null;
|
|
10548
|
+
document = null;
|
|
9736
10549
|
configChange = new EventEmitter();
|
|
10550
|
+
documentChange = new EventEmitter();
|
|
9737
10551
|
validationChange = new EventEmitter();
|
|
9738
10552
|
editorEvent = new EventEmitter();
|
|
9739
10553
|
jsonText = '';
|
|
9740
10554
|
isValidJson = true;
|
|
9741
10555
|
jsonError = '';
|
|
10556
|
+
diagnostics = [];
|
|
9742
10557
|
destroy$ = new Subject();
|
|
9743
10558
|
jsonTextChanges$ = new Subject();
|
|
9744
10559
|
constructor(cdr, configService) {
|
|
@@ -9749,9 +10564,10 @@ class JsonConfigEditorComponent {
|
|
|
9749
10564
|
.subscribe((text) => this.validateJson(text));
|
|
9750
10565
|
}
|
|
9751
10566
|
ngOnInit() {
|
|
9752
|
-
const
|
|
10567
|
+
const doc = this.document ??
|
|
10568
|
+
parseLegacyOrDynamicFormDocument(this.config || this.configService.currentConfig);
|
|
9753
10569
|
try {
|
|
9754
|
-
this.jsonText = JSON.stringify(
|
|
10570
|
+
this.jsonText = JSON.stringify(serializeDynamicFormAuthoringDocument(doc), null, 2);
|
|
9755
10571
|
}
|
|
9756
10572
|
catch {
|
|
9757
10573
|
this.jsonText = '{}';
|
|
@@ -9770,12 +10586,20 @@ class JsonConfigEditorComponent {
|
|
|
9770
10586
|
return;
|
|
9771
10587
|
}
|
|
9772
10588
|
try {
|
|
9773
|
-
const
|
|
9774
|
-
|
|
9775
|
-
|
|
10589
|
+
const parsed = JSON.parse(this.jsonText);
|
|
10590
|
+
const diagnostics = validateDynamicFormAuthoringInput(parsed);
|
|
10591
|
+
const newDocument = parseLegacyOrDynamicFormDocument(parsed);
|
|
10592
|
+
this.configService.loadConfig(newDocument.config);
|
|
10593
|
+
this.documentChange.emit(newDocument);
|
|
10594
|
+
this.configChange.emit(newDocument.config);
|
|
9776
10595
|
this.editorEvent.emit({
|
|
9777
10596
|
type: 'apply',
|
|
9778
|
-
payload: {
|
|
10597
|
+
payload: {
|
|
10598
|
+
isValid: !diagnostics.some((item) => item.level === 'error'),
|
|
10599
|
+
document: newDocument,
|
|
10600
|
+
config: newDocument.config,
|
|
10601
|
+
diagnostics,
|
|
10602
|
+
},
|
|
9779
10603
|
});
|
|
9780
10604
|
}
|
|
9781
10605
|
catch {
|
|
@@ -9792,10 +10616,17 @@ class JsonConfigEditorComponent {
|
|
|
9792
10616
|
}
|
|
9793
10617
|
try {
|
|
9794
10618
|
const parsed = JSON.parse(this.jsonText);
|
|
9795
|
-
|
|
10619
|
+
const diagnostics = validateDynamicFormAuthoringInput(parsed);
|
|
10620
|
+
const document = parseLegacyOrDynamicFormDocument(parsed);
|
|
10621
|
+
this.jsonText = JSON.stringify(serializeDynamicFormAuthoringDocument(document), null, 2);
|
|
9796
10622
|
this.editorEvent.emit({
|
|
9797
10623
|
type: 'format',
|
|
9798
|
-
payload: {
|
|
10624
|
+
payload: {
|
|
10625
|
+
isValid: !diagnostics.some((item) => item.level === 'error'),
|
|
10626
|
+
document,
|
|
10627
|
+
config: document.config,
|
|
10628
|
+
diagnostics,
|
|
10629
|
+
},
|
|
9799
10630
|
});
|
|
9800
10631
|
this.cdr.markForCheck();
|
|
9801
10632
|
}
|
|
@@ -9807,39 +10638,54 @@ class JsonConfigEditorComponent {
|
|
|
9807
10638
|
}
|
|
9808
10639
|
}
|
|
9809
10640
|
updateJsonFromConfig(config) {
|
|
9810
|
-
this.
|
|
10641
|
+
this.updateJsonFromDocument(parseLegacyOrDynamicFormDocument(config || { sections: [] }));
|
|
10642
|
+
}
|
|
10643
|
+
updateJsonFromDocument(document) {
|
|
10644
|
+
this.jsonText = JSON.stringify(serializeDynamicFormAuthoringDocument(document), null, 2);
|
|
9811
10645
|
this.validateJson(this.jsonText);
|
|
9812
10646
|
}
|
|
9813
10647
|
/**
|
|
9814
10648
|
* Força atualização do JSON com a configuração atual
|
|
9815
10649
|
*/
|
|
9816
10650
|
refreshJson() {
|
|
9817
|
-
const
|
|
9818
|
-
|
|
10651
|
+
const document = this.document ??
|
|
10652
|
+
parseLegacyOrDynamicFormDocument(this.config || this.configService.currentConfig);
|
|
10653
|
+
this.updateJsonFromDocument(document);
|
|
9819
10654
|
}
|
|
9820
10655
|
getCurrentConfig() {
|
|
10656
|
+
return this.getCurrentDocument()?.config || null;
|
|
10657
|
+
}
|
|
10658
|
+
getCurrentDocument() {
|
|
9821
10659
|
if (!this.isValidJson) {
|
|
9822
10660
|
return null;
|
|
9823
10661
|
}
|
|
9824
10662
|
try {
|
|
9825
|
-
return JSON.parse(this.jsonText);
|
|
10663
|
+
return parseLegacyOrDynamicFormDocument(JSON.parse(this.jsonText));
|
|
9826
10664
|
}
|
|
9827
10665
|
catch {
|
|
9828
10666
|
return null;
|
|
9829
10667
|
}
|
|
9830
10668
|
}
|
|
9831
10669
|
hasChanges() {
|
|
9832
|
-
|
|
10670
|
+
const baseline = this.document ??
|
|
10671
|
+
(this.config
|
|
10672
|
+
? parseLegacyOrDynamicFormDocument(this.config)
|
|
10673
|
+
: null);
|
|
10674
|
+
if (!baseline) {
|
|
9833
10675
|
return false;
|
|
9834
10676
|
}
|
|
9835
|
-
const
|
|
9836
|
-
if (!
|
|
10677
|
+
const currentDocument = this.getCurrentDocument();
|
|
10678
|
+
if (!currentDocument) {
|
|
9837
10679
|
return false;
|
|
9838
10680
|
}
|
|
9839
|
-
return JSON.stringify(
|
|
10681
|
+
return (JSON.stringify(serializeDynamicFormAuthoringDocument(baseline)) !==
|
|
10682
|
+
JSON.stringify(serializeDynamicFormAuthoringDocument(currentDocument)));
|
|
9840
10683
|
}
|
|
9841
10684
|
validateJson(text) {
|
|
9842
|
-
const result = {
|
|
10685
|
+
const result = {
|
|
10686
|
+
isValid: false,
|
|
10687
|
+
diagnostics: [],
|
|
10688
|
+
};
|
|
9843
10689
|
if (!text.trim()) {
|
|
9844
10690
|
result.error = 'JSON não pode estar vazio';
|
|
9845
10691
|
this.updateValidationState(result);
|
|
@@ -9847,16 +10693,17 @@ class JsonConfigEditorComponent {
|
|
|
9847
10693
|
}
|
|
9848
10694
|
try {
|
|
9849
10695
|
const parsed = JSON.parse(text);
|
|
9850
|
-
|
|
9851
|
-
|
|
9852
|
-
|
|
9853
|
-
|
|
9854
|
-
|
|
9855
|
-
|
|
9856
|
-
|
|
9857
|
-
|
|
9858
|
-
result.
|
|
9859
|
-
|
|
10696
|
+
const document = parseLegacyOrDynamicFormDocument(parsed);
|
|
10697
|
+
const diagnostics = validateDynamicFormAuthoringInput(parsed);
|
|
10698
|
+
const hasErrors = diagnostics.some((item) => item.level === 'error');
|
|
10699
|
+
result.isValid = !hasErrors;
|
|
10700
|
+
result.document = document;
|
|
10701
|
+
result.config = document.config;
|
|
10702
|
+
result.diagnostics = diagnostics;
|
|
10703
|
+
if (hasErrors) {
|
|
10704
|
+
result.error =
|
|
10705
|
+
diagnostics.find((item) => item.level === 'error')?.message ||
|
|
10706
|
+
'Documento de autoria inválido';
|
|
9860
10707
|
}
|
|
9861
10708
|
this.updateValidationState(result);
|
|
9862
10709
|
}
|
|
@@ -9869,6 +10716,7 @@ class JsonConfigEditorComponent {
|
|
|
9869
10716
|
updateValidationState(result) {
|
|
9870
10717
|
this.isValidJson = result.isValid;
|
|
9871
10718
|
this.jsonError = result.error || '';
|
|
10719
|
+
this.diagnostics = result.diagnostics || [];
|
|
9872
10720
|
this.validationChange.emit(result);
|
|
9873
10721
|
this.editorEvent.emit({ type: 'validation', payload: result });
|
|
9874
10722
|
this.cdr.markForCheck();
|
|
@@ -9910,7 +10758,7 @@ class JsonConfigEditorComponent {
|
|
|
9910
10758
|
}
|
|
9911
10759
|
}
|
|
9912
10760
|
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 });
|
|
9913
|
-
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: `
|
|
10761
|
+
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: `
|
|
9914
10762
|
<div class="json-config-editor">
|
|
9915
10763
|
<mat-card class="educational-card">
|
|
9916
10764
|
<mat-card-header>
|
|
@@ -9919,8 +10767,13 @@ class JsonConfigEditorComponent {
|
|
|
9919
10767
|
</mat-card-header>
|
|
9920
10768
|
<mat-card-content>
|
|
9921
10769
|
<p>
|
|
9922
|
-
<strong>Para usuários avançados:</strong> Edite
|
|
9923
|
-
|
|
10770
|
+
<strong>Para usuários avançados:</strong> Edite o documento
|
|
10771
|
+
canônico de autoria do formulário diretamente em JSON.
|
|
10772
|
+
</p>
|
|
10773
|
+
<p>
|
|
10774
|
+
O editor JSON aceita apenas o envelope canônico
|
|
10775
|
+
<code>DynamicFormAuthoringDocument</code>. Payloads legados devem
|
|
10776
|
+
ser migrados ou aplicados por APIs de compatibilidade.
|
|
9924
10777
|
</p>
|
|
9925
10778
|
<div class="badges">
|
|
9926
10779
|
<span class="badge" [class.ok]="isValidJson" [class.err]="!isValidJson">
|
|
@@ -9978,9 +10831,28 @@ class JsonConfigEditorComponent {
|
|
|
9978
10831
|
>JSON inválido: {{ jsonError }}</mat-error
|
|
9979
10832
|
>
|
|
9980
10833
|
</mat-form-field>
|
|
10834
|
+
<div
|
|
10835
|
+
class="diagnostics-panel"
|
|
10836
|
+
*ngIf="diagnostics.length"
|
|
10837
|
+
aria-live="polite"
|
|
10838
|
+
role="status"
|
|
10839
|
+
>
|
|
10840
|
+
<div class="diagnostics-title">Diagnósticos do documento</div>
|
|
10841
|
+
<div
|
|
10842
|
+
class="diagnostic-item"
|
|
10843
|
+
*ngFor="let diagnostic of diagnostics"
|
|
10844
|
+
[class.diagnostic-item--error]="diagnostic.level === 'error'"
|
|
10845
|
+
[class.diagnostic-item--warning]="diagnostic.level === 'warning'"
|
|
10846
|
+
[class.diagnostic-item--info]="diagnostic.level === 'info'"
|
|
10847
|
+
>
|
|
10848
|
+
<strong>{{ diagnostic.level | uppercase }}</strong>
|
|
10849
|
+
<span>{{ diagnostic.message }}</span>
|
|
10850
|
+
<code *ngIf="diagnostic.path">{{ diagnostic.path }}</code>
|
|
10851
|
+
</div>
|
|
10852
|
+
</div>
|
|
9981
10853
|
</div>
|
|
9982
10854
|
</div>
|
|
9983
|
-
`, 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 });
|
|
10855
|
+
`, 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 });
|
|
9984
10856
|
}
|
|
9985
10857
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: JsonConfigEditorComponent, decorators: [{
|
|
9986
10858
|
type: Component,
|
|
@@ -10001,8 +10873,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
10001
10873
|
</mat-card-header>
|
|
10002
10874
|
<mat-card-content>
|
|
10003
10875
|
<p>
|
|
10004
|
-
<strong>Para usuários avançados:</strong> Edite
|
|
10005
|
-
|
|
10876
|
+
<strong>Para usuários avançados:</strong> Edite o documento
|
|
10877
|
+
canônico de autoria do formulário diretamente em JSON.
|
|
10878
|
+
</p>
|
|
10879
|
+
<p>
|
|
10880
|
+
O editor JSON aceita apenas o envelope canônico
|
|
10881
|
+
<code>DynamicFormAuthoringDocument</code>. Payloads legados devem
|
|
10882
|
+
ser migrados ou aplicados por APIs de compatibilidade.
|
|
10006
10883
|
</p>
|
|
10007
10884
|
<div class="badges">
|
|
10008
10885
|
<span class="badge" [class.ok]="isValidJson" [class.err]="!isValidJson">
|
|
@@ -10060,13 +10937,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
10060
10937
|
>JSON inválido: {{ jsonError }}</mat-error
|
|
10061
10938
|
>
|
|
10062
10939
|
</mat-form-field>
|
|
10940
|
+
<div
|
|
10941
|
+
class="diagnostics-panel"
|
|
10942
|
+
*ngIf="diagnostics.length"
|
|
10943
|
+
aria-live="polite"
|
|
10944
|
+
role="status"
|
|
10945
|
+
>
|
|
10946
|
+
<div class="diagnostics-title">Diagnósticos do documento</div>
|
|
10947
|
+
<div
|
|
10948
|
+
class="diagnostic-item"
|
|
10949
|
+
*ngFor="let diagnostic of diagnostics"
|
|
10950
|
+
[class.diagnostic-item--error]="diagnostic.level === 'error'"
|
|
10951
|
+
[class.diagnostic-item--warning]="diagnostic.level === 'warning'"
|
|
10952
|
+
[class.diagnostic-item--info]="diagnostic.level === 'info'"
|
|
10953
|
+
>
|
|
10954
|
+
<strong>{{ diagnostic.level | uppercase }}</strong>
|
|
10955
|
+
<span>{{ diagnostic.message }}</span>
|
|
10956
|
+
<code *ngIf="diagnostic.path">{{ diagnostic.path }}</code>
|
|
10957
|
+
</div>
|
|
10958
|
+
</div>
|
|
10063
10959
|
</div>
|
|
10064
10960
|
</div>
|
|
10065
|
-
`, 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"] }]
|
|
10961
|
+
`, 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"] }]
|
|
10066
10962
|
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: FormConfigService }], propDecorators: { config: [{
|
|
10067
10963
|
type: Input
|
|
10964
|
+
}], document: [{
|
|
10965
|
+
type: Input
|
|
10068
10966
|
}], configChange: [{
|
|
10069
10967
|
type: Output
|
|
10968
|
+
}], documentChange: [{
|
|
10969
|
+
type: Output
|
|
10070
10970
|
}], validationChange: [{
|
|
10071
10971
|
type: Output
|
|
10072
10972
|
}], editorEvent: [{
|
|
@@ -12083,8 +12983,8 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
12083
12983
|
configChange = new EventEmitter();
|
|
12084
12984
|
containerStylesText = '';
|
|
12085
12985
|
globalActionCatalogSource = inject(GLOBAL_ACTION_CATALOG, { optional: true }) ?? [];
|
|
12086
|
-
globalActionCatalog = getGlobalActionCatalog(this.globalActionCatalogSource);
|
|
12087
12986
|
legacyActionSpecs = GLOBAL_ACTION_SPEC_CATALOG;
|
|
12987
|
+
globalActionCatalog = this.buildGlobalActionCatalog();
|
|
12088
12988
|
customActionValue = '__custom__';
|
|
12089
12989
|
actionFieldDrafts = new Map();
|
|
12090
12990
|
actionFieldErrors = new Map();
|
|
@@ -12150,7 +13050,7 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
12150
13050
|
this.containerStylesText = styles ? JSON.stringify(styles, null, 2) : '';
|
|
12151
13051
|
}
|
|
12152
13052
|
getActionSpecById(id) {
|
|
12153
|
-
return this.
|
|
13053
|
+
return this.globalActionCatalog.find((item) => item.id === id);
|
|
12154
13054
|
}
|
|
12155
13055
|
getCustomActionSelectValue(value) {
|
|
12156
13056
|
const parsed = this.parseActionValue(value);
|
|
@@ -12168,6 +13068,7 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
12168
13068
|
const current = this.actions.custom?.[index];
|
|
12169
13069
|
if (!current)
|
|
12170
13070
|
return;
|
|
13071
|
+
this.clearActionFieldState(current);
|
|
12171
13072
|
if (value === this.customActionValue) {
|
|
12172
13073
|
const parsed = this.parseActionValue(current.action);
|
|
12173
13074
|
if (parsed.isGlobal) {
|
|
@@ -12216,6 +13117,38 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
12216
13117
|
return false;
|
|
12217
13118
|
return this.globalActionCatalog.some((item) => item.id === id);
|
|
12218
13119
|
}
|
|
13120
|
+
buildGlobalActionCatalog() {
|
|
13121
|
+
const merged = new Map();
|
|
13122
|
+
for (const spec of this.legacyActionSpecs) {
|
|
13123
|
+
merged.set(spec.id, spec);
|
|
13124
|
+
}
|
|
13125
|
+
const injectedCatalog = getGlobalActionCatalog(this.globalActionCatalogSource);
|
|
13126
|
+
const fallbackCatalog = injectedCatalog.length ? injectedCatalog : PRAXIS_GLOBAL_ACTION_CATALOG;
|
|
13127
|
+
for (const entry of fallbackCatalog) {
|
|
13128
|
+
if (merged.has(entry.id))
|
|
13129
|
+
continue;
|
|
13130
|
+
merged.set(entry.id, this.mapCatalogEntryToActionSpec(entry));
|
|
13131
|
+
}
|
|
13132
|
+
return Array.from(merged.values());
|
|
13133
|
+
}
|
|
13134
|
+
mapCatalogEntryToActionSpec(entry) {
|
|
13135
|
+
const hasPayloadSchema = !!entry.payloadSchema;
|
|
13136
|
+
return {
|
|
13137
|
+
id: entry.id,
|
|
13138
|
+
label: entry.label,
|
|
13139
|
+
description: entry.description || '',
|
|
13140
|
+
param: hasPayloadSchema
|
|
13141
|
+
? {
|
|
13142
|
+
label: 'Payload (JSON opcional)',
|
|
13143
|
+
placeholder: entry.payloadSchema?.example
|
|
13144
|
+
? JSON.stringify(entry.payloadSchema.example)
|
|
13145
|
+
: '{ }',
|
|
13146
|
+
hint: 'Use JSON quando a ação global do app exigir payload estruturado.',
|
|
13147
|
+
required: !!entry.payloadSchema?.required?.length,
|
|
13148
|
+
}
|
|
13149
|
+
: undefined,
|
|
13150
|
+
};
|
|
13151
|
+
}
|
|
12219
13152
|
getGlobalActionSchema(action) {
|
|
12220
13153
|
const parsed = this.parseActionValue(action.action);
|
|
12221
13154
|
if (!parsed.isGlobal)
|
|
@@ -12240,83 +13173,122 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
12240
13173
|
return errors?.[key] || '';
|
|
12241
13174
|
}
|
|
12242
13175
|
getGlobalActionFieldValue(action, field) {
|
|
12243
|
-
|
|
12244
|
-
|
|
12245
|
-
if (draft !== undefined)
|
|
12246
|
-
return draft;
|
|
12247
|
-
}
|
|
13176
|
+
const draftKey = this.getActionDraftKey(action);
|
|
13177
|
+
const draft = this.actionFieldDrafts.get(draftKey)?.[field.key];
|
|
12248
13178
|
const info = this.getActionParamInfo(action.action);
|
|
12249
13179
|
const raw = info.param || '';
|
|
12250
13180
|
const json = info.json || {};
|
|
12251
13181
|
const hasJson = info.isJson;
|
|
13182
|
+
let value;
|
|
12252
13183
|
switch (field.key) {
|
|
12253
13184
|
case 'message':
|
|
12254
|
-
|
|
13185
|
+
value = json.message ?? json.text ?? (hasJson ? '' : raw);
|
|
13186
|
+
break;
|
|
12255
13187
|
case 'url':
|
|
12256
|
-
if (json.url)
|
|
12257
|
-
|
|
13188
|
+
if (json.url) {
|
|
13189
|
+
value = json.url;
|
|
13190
|
+
break;
|
|
13191
|
+
}
|
|
12258
13192
|
if (!hasJson && info.id === 'apiCall') {
|
|
12259
13193
|
const parsed = this.parseMethodAndUrl(raw);
|
|
12260
|
-
|
|
13194
|
+
value = parsed.url || '';
|
|
13195
|
+
break;
|
|
12261
13196
|
}
|
|
12262
|
-
|
|
13197
|
+
value = hasJson ? '' : raw;
|
|
13198
|
+
break;
|
|
12263
13199
|
case 'method': {
|
|
12264
|
-
if (json.method)
|
|
12265
|
-
|
|
13200
|
+
if (json.method) {
|
|
13201
|
+
value = String(json.method);
|
|
13202
|
+
break;
|
|
13203
|
+
}
|
|
12266
13204
|
if (!hasJson) {
|
|
12267
13205
|
const parsed = this.parseMethodAndUrl(raw);
|
|
12268
|
-
|
|
13206
|
+
value = parsed.method || '';
|
|
13207
|
+
break;
|
|
12269
13208
|
}
|
|
12270
|
-
|
|
13209
|
+
value = '';
|
|
13210
|
+
break;
|
|
12271
13211
|
}
|
|
12272
13212
|
case 'bodySource':
|
|
12273
|
-
|
|
13213
|
+
value = json.bodySource ?? '';
|
|
13214
|
+
break;
|
|
12274
13215
|
case 'headers':
|
|
12275
|
-
|
|
13216
|
+
value = this.stringifyJson(json.headers);
|
|
13217
|
+
break;
|
|
12276
13218
|
case 'body':
|
|
12277
|
-
|
|
13219
|
+
value = this.stringifyJson(json.body);
|
|
13220
|
+
break;
|
|
12278
13221
|
case 'contentData':
|
|
12279
|
-
|
|
13222
|
+
value = this.stringifyJson(json.content?.data);
|
|
13223
|
+
break;
|
|
12280
13224
|
case 'styles':
|
|
12281
|
-
|
|
13225
|
+
value = this.stringifyJson(json.styles);
|
|
13226
|
+
break;
|
|
12282
13227
|
case 'positionTop':
|
|
12283
|
-
|
|
13228
|
+
value = json.position?.top ?? '';
|
|
13229
|
+
break;
|
|
12284
13230
|
case 'positionRight':
|
|
12285
|
-
|
|
13231
|
+
value = json.position?.right ?? '';
|
|
13232
|
+
break;
|
|
12286
13233
|
case 'positionBottom':
|
|
12287
|
-
|
|
13234
|
+
value = json.position?.bottom ?? '';
|
|
13235
|
+
break;
|
|
12288
13236
|
case 'positionLeft':
|
|
12289
|
-
|
|
13237
|
+
value = json.position?.left ?? '';
|
|
13238
|
+
break;
|
|
12290
13239
|
case 'animationType':
|
|
12291
|
-
|
|
13240
|
+
value = json.animation?.type ?? '';
|
|
13241
|
+
break;
|
|
12292
13242
|
case 'animationDirection':
|
|
12293
|
-
|
|
13243
|
+
value = json.animation?.direction ?? '';
|
|
13244
|
+
break;
|
|
12294
13245
|
case 'animationDuration':
|
|
12295
|
-
|
|
13246
|
+
value = json.animation?.duration ?? '';
|
|
13247
|
+
break;
|
|
12296
13248
|
case 'contentType':
|
|
12297
|
-
|
|
13249
|
+
value = json.contentType ?? json.content?.type ?? '';
|
|
13250
|
+
break;
|
|
12298
13251
|
case 'templateId':
|
|
12299
|
-
|
|
13252
|
+
value = json.templateId ?? '';
|
|
13253
|
+
break;
|
|
12300
13254
|
case 'componentRef':
|
|
12301
|
-
|
|
13255
|
+
value = json.componentRef ?? '';
|
|
13256
|
+
break;
|
|
12302
13257
|
case 'safeHtml':
|
|
12303
|
-
|
|
13258
|
+
value = json.safeHtml ?? '';
|
|
13259
|
+
break;
|
|
12304
13260
|
case 'mode':
|
|
12305
|
-
|
|
13261
|
+
value = json.mode === 'dialog' || json.useDialog === true;
|
|
13262
|
+
break;
|
|
12306
13263
|
case 'newTab':
|
|
12307
|
-
|
|
13264
|
+
value = json.newTab ?? false;
|
|
13265
|
+
break;
|
|
12308
13266
|
case 'replaceUrl':
|
|
12309
|
-
|
|
13267
|
+
value = json.replaceUrl ?? false;
|
|
13268
|
+
break;
|
|
12310
13269
|
case 'target':
|
|
12311
|
-
|
|
13270
|
+
value = json.target ?? '';
|
|
13271
|
+
break;
|
|
12312
13272
|
case 'text':
|
|
12313
|
-
|
|
13273
|
+
value = json.text ?? (hasJson ? '' : raw);
|
|
13274
|
+
break;
|
|
12314
13275
|
case 'key':
|
|
12315
13276
|
case 'field':
|
|
12316
|
-
|
|
13277
|
+
value = json[field.key] ?? (hasJson ? '' : raw);
|
|
13278
|
+
break;
|
|
12317
13279
|
default:
|
|
12318
|
-
|
|
13280
|
+
value = json[field.key] ?? '';
|
|
13281
|
+
break;
|
|
13282
|
+
}
|
|
13283
|
+
if (draft !== undefined) {
|
|
13284
|
+
if (this.isEquivalentActionFieldValue(value, draft, field.type)) {
|
|
13285
|
+
this.clearActionFieldDraft(action, field.key);
|
|
13286
|
+
}
|
|
13287
|
+
else {
|
|
13288
|
+
return draft;
|
|
13289
|
+
}
|
|
12319
13290
|
}
|
|
13291
|
+
return value;
|
|
12320
13292
|
}
|
|
12321
13293
|
onGlobalActionFieldChange(action, field, value, index) {
|
|
12322
13294
|
const selectedId = this.getCustomActionSelectValue(action.action);
|
|
@@ -12325,12 +13297,9 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
12325
13297
|
const schema = getGlobalActionUiSchema(selectedId);
|
|
12326
13298
|
if (!schema)
|
|
12327
13299
|
return;
|
|
13300
|
+
this.setActionFieldDraft(action, field.key, value);
|
|
12328
13301
|
if (field.type === 'json') {
|
|
12329
13302
|
const raw = String(value || '');
|
|
12330
|
-
const draftKey = this.getActionDraftKey(action);
|
|
12331
|
-
const drafts = this.actionFieldDrafts.get(draftKey) || {};
|
|
12332
|
-
drafts[field.key] = raw;
|
|
12333
|
-
this.actionFieldDrafts.set(draftKey, drafts);
|
|
12334
13303
|
if (raw.trim()) {
|
|
12335
13304
|
try {
|
|
12336
13305
|
JSON.parse(raw);
|
|
@@ -12417,6 +13386,39 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
12417
13386
|
getActionDraftKey(action) {
|
|
12418
13387
|
return String(action.id || action.action || action.label || 'action');
|
|
12419
13388
|
}
|
|
13389
|
+
setActionFieldDraft(action, key, value) {
|
|
13390
|
+
const draftKey = this.getActionDraftKey(action);
|
|
13391
|
+
const drafts = this.actionFieldDrafts.get(draftKey) || {};
|
|
13392
|
+
drafts[key] = value;
|
|
13393
|
+
this.actionFieldDrafts.set(draftKey, drafts);
|
|
13394
|
+
}
|
|
13395
|
+
clearActionFieldDraft(action, key) {
|
|
13396
|
+
const draftKey = this.getActionDraftKey(action);
|
|
13397
|
+
const drafts = this.actionFieldDrafts.get(draftKey);
|
|
13398
|
+
if (!drafts)
|
|
13399
|
+
return;
|
|
13400
|
+
delete drafts[key];
|
|
13401
|
+
if (Object.keys(drafts).length) {
|
|
13402
|
+
this.actionFieldDrafts.set(draftKey, drafts);
|
|
13403
|
+
}
|
|
13404
|
+
else {
|
|
13405
|
+
this.actionFieldDrafts.delete(draftKey);
|
|
13406
|
+
}
|
|
13407
|
+
}
|
|
13408
|
+
clearActionFieldState(action) {
|
|
13409
|
+
const draftKey = this.getActionDraftKey(action);
|
|
13410
|
+
this.actionFieldDrafts.delete(draftKey);
|
|
13411
|
+
this.actionFieldErrors.delete(draftKey);
|
|
13412
|
+
}
|
|
13413
|
+
isEquivalentActionFieldValue(actual, draft, fieldType) {
|
|
13414
|
+
if (fieldType === 'toggle') {
|
|
13415
|
+
return Boolean(actual) === Boolean(draft);
|
|
13416
|
+
}
|
|
13417
|
+
if (fieldType === 'number') {
|
|
13418
|
+
return String(actual ?? '') === String(draft ?? '');
|
|
13419
|
+
}
|
|
13420
|
+
return String(actual ?? '') === String(draft ?? '');
|
|
13421
|
+
}
|
|
12420
13422
|
collectGlobalActionFieldValues(action, fields) {
|
|
12421
13423
|
const values = {};
|
|
12422
13424
|
fields.forEach((field) => {
|
|
@@ -13159,7 +14161,7 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
13159
14161
|
<mat-accordion multi>
|
|
13160
14162
|
@for (
|
|
13161
14163
|
customAction of actions.custom;
|
|
13162
|
-
track customAction;
|
|
14164
|
+
track customAction.id || customAction.action || $index;
|
|
13163
14165
|
let i = $index
|
|
13164
14166
|
) {
|
|
13165
14167
|
<mat-expansion-panel>
|
|
@@ -13827,7 +14829,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
13827
14829
|
<mat-accordion multi>
|
|
13828
14830
|
@for (
|
|
13829
14831
|
customAction of actions.custom;
|
|
13830
|
-
track customAction;
|
|
14832
|
+
track customAction.id || customAction.action || $index;
|
|
13831
14833
|
let i = $index
|
|
13832
14834
|
) {
|
|
13833
14835
|
<mat-expansion-panel>
|
|
@@ -14877,54 +15879,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
14877
15879
|
type: Output
|
|
14878
15880
|
}] } });
|
|
14879
15881
|
|
|
14880
|
-
/**
|
|
14881
|
-
* Deep clone and normalize a form configuration ensuring required collections
|
|
14882
|
-
* are always present to avoid unintended mutations.
|
|
14883
|
-
*/
|
|
14884
|
-
function normalizeFormConfig(config) {
|
|
14885
|
-
const cloned = config ? structuredClone(config) : createDefaultFormConfig();
|
|
14886
|
-
// Apply canonical normalization from @praxisui/core first (aliases → canônico)
|
|
14887
|
-
const canonical = normalizeFormConfig$1(cloned);
|
|
14888
|
-
const withCollections = ensureIds({
|
|
14889
|
-
...createDefaultFormConfig(),
|
|
14890
|
-
...canonical,
|
|
14891
|
-
sections: canonical.sections ?? [],
|
|
14892
|
-
fieldMetadata: canonical.fieldMetadata ?? [],
|
|
14893
|
-
formBlocksBefore: canonical.formBlocksBefore ?? [],
|
|
14894
|
-
formBlocksAfter: canonical.formBlocksAfter ?? [],
|
|
14895
|
-
});
|
|
14896
|
-
return applyDefaultSpans(withCollections);
|
|
14897
|
-
}
|
|
14898
|
-
/**
|
|
14899
|
-
* Apply default grid spans when not provided by the config.
|
|
14900
|
-
* Rule: for each row, if none of its columns declares span.md,
|
|
14901
|
-
* distribute equally across 12 columns on md+; keep xs/sm stacked (12).
|
|
14902
|
-
*/
|
|
14903
|
-
function applyDefaultSpans(config) {
|
|
14904
|
-
const next = structuredClone(config);
|
|
14905
|
-
for (const section of next.sections || []) {
|
|
14906
|
-
for (const row of section.rows || []) {
|
|
14907
|
-
const cols = row.columns || [];
|
|
14908
|
-
if (!cols.length)
|
|
14909
|
-
continue;
|
|
14910
|
-
const hasAnyMdSpan = cols.some((c) => c.span?.md != null);
|
|
14911
|
-
if (hasAnyMdSpan)
|
|
14912
|
-
continue;
|
|
14913
|
-
const per = Math.max(1, Math.floor(12 / cols.length));
|
|
14914
|
-
for (const col of cols) {
|
|
14915
|
-
const span = col.span || {};
|
|
14916
|
-
span.xs = 12;
|
|
14917
|
-
span.sm = 12;
|
|
14918
|
-
span.md = per;
|
|
14919
|
-
span.lg = span.lg ?? per;
|
|
14920
|
-
span.xl = span.xl ?? per;
|
|
14921
|
-
col.span = span;
|
|
14922
|
-
}
|
|
14923
|
-
}
|
|
14924
|
-
}
|
|
14925
|
-
return next;
|
|
14926
|
-
}
|
|
14927
|
-
|
|
14928
15882
|
/**
|
|
14929
15883
|
* Converts a RuleBuilderState produced by the visual builder into
|
|
14930
15884
|
* an array of FormLayoutRule objects understood by the dynamic form.
|
|
@@ -15310,11 +16264,15 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15310
16264
|
ruleBuilderState;
|
|
15311
16265
|
currentRules = [];
|
|
15312
16266
|
initialConfig;
|
|
16267
|
+
initialDocument;
|
|
16268
|
+
openedWithCanonicalDocument;
|
|
15313
16269
|
backConfig;
|
|
16270
|
+
includeBackConfigBlock = false;
|
|
15314
16271
|
formId;
|
|
15315
16272
|
componentKeyId;
|
|
15316
16273
|
// Input patch: controls like mode (dados)
|
|
15317
16274
|
inputMode = 'create';
|
|
16275
|
+
includeBindingsBlock = false;
|
|
15318
16276
|
editorMode;
|
|
15319
16277
|
// Indica se o editor foi aberto com o formulário em modo apresentação
|
|
15320
16278
|
isPresentationMode = false;
|
|
@@ -15350,6 +16308,8 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15350
16308
|
snoozeMs: 24 * 60 * 60 * 1000,
|
|
15351
16309
|
autoOpenSettingsOnOutdated: false,
|
|
15352
16310
|
};
|
|
16311
|
+
includePresentationBlock = false;
|
|
16312
|
+
includeSchemaPrefsBlock = false;
|
|
15353
16313
|
// Read-only current server meta (if any)
|
|
15354
16314
|
serverMeta;
|
|
15355
16315
|
destroy$ = new Subject();
|
|
@@ -15376,11 +16336,18 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15376
16336
|
this.settingsPanel = settingsPanel;
|
|
15377
16337
|
this.cdr = cdr;
|
|
15378
16338
|
const data = injectedData;
|
|
15379
|
-
|
|
15380
|
-
|
|
16339
|
+
this.openedWithCanonicalDocument =
|
|
16340
|
+
data?.document?.kind === 'praxis.dynamic-form.editor' ||
|
|
16341
|
+
data?.kind === 'praxis.dynamic-form.editor';
|
|
16342
|
+
const initialDocument = parseLegacyOrDynamicFormDocument(data?.document ?? injectedData);
|
|
16343
|
+
this.initialDocument = initialDocument;
|
|
16344
|
+
const formCfg = initialDocument.config;
|
|
16345
|
+
this.backConfig = initialDocument.contextSnapshot?.backConfig;
|
|
16346
|
+
this.includeBackConfigBlock = Object.prototype.hasOwnProperty.call(initialDocument.contextSnapshot || {}, 'backConfig');
|
|
15381
16347
|
this.formId = data?.formId;
|
|
15382
16348
|
this.componentKeyId = data?.componentKeyId;
|
|
15383
|
-
this.
|
|
16349
|
+
this.includeBindingsBlock = Object.prototype.hasOwnProperty.call(initialDocument.bindings || {}, 'mode');
|
|
16350
|
+
this.inputMode = this.normalizeMode(initialDocument.bindings?.mode);
|
|
15384
16351
|
this.editorMode = this.inputMode;
|
|
15385
16352
|
// Flag de contexto do host (praxis-dynamic-form)
|
|
15386
16353
|
this.isPresentationMode = !!data?.presentationMode;
|
|
@@ -15394,9 +16361,11 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15394
16361
|
});
|
|
15395
16362
|
}
|
|
15396
16363
|
catch { }
|
|
15397
|
-
|
|
15398
|
-
|
|
15399
|
-
|
|
16364
|
+
this.backConfig = this.createBackConfigState(this.backConfig);
|
|
16365
|
+
this.includePresentationBlock = Object.prototype.hasOwnProperty.call(initialDocument.contextSnapshot || {}, 'presentation');
|
|
16366
|
+
this.presentationPrefs = this.mergePresentationPrefs(initialDocument.contextSnapshot?.presentation);
|
|
16367
|
+
this.includeSchemaPrefsBlock = Object.prototype.hasOwnProperty.call(initialDocument.contextSnapshot || {}, 'schemaPrefs');
|
|
16368
|
+
this.schemaPrefs = this.mergeSchemaPrefs(initialDocument.contextSnapshot?.schemaPrefs);
|
|
15400
16369
|
this.editedConfig = structuredClone(this.initialConfig);
|
|
15401
16370
|
this.configService.loadConfig(structuredClone(this.initialConfig));
|
|
15402
16371
|
}
|
|
@@ -15416,11 +16385,19 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15416
16385
|
this.isBusy$.next(true);
|
|
15417
16386
|
try {
|
|
15418
16387
|
this.editedConfig = structuredClone(this.initialConfig);
|
|
16388
|
+
this.includeBackConfigBlock = Object.prototype.hasOwnProperty.call(this.initialDocument.contextSnapshot || {}, 'backConfig');
|
|
16389
|
+
this.backConfig = this.createBackConfigState(this.initialDocument.contextSnapshot?.backConfig);
|
|
16390
|
+
this.includeBindingsBlock = Object.prototype.hasOwnProperty.call(this.initialDocument.bindings || {}, 'mode');
|
|
16391
|
+
this.inputMode = this.normalizeMode(this.initialDocument.bindings?.mode);
|
|
16392
|
+
this.includePresentationBlock = Object.prototype.hasOwnProperty.call(this.initialDocument.contextSnapshot || {}, 'presentation');
|
|
16393
|
+
this.presentationPrefs = this.mergePresentationPrefs(this.initialDocument.contextSnapshot?.presentation);
|
|
16394
|
+
this.includeSchemaPrefsBlock = Object.prototype.hasOwnProperty.call(this.initialDocument.contextSnapshot || {}, 'schemaPrefs');
|
|
16395
|
+
this.schemaPrefs = this.mergeSchemaPrefs(this.initialDocument.contextSnapshot?.schemaPrefs);
|
|
15419
16396
|
try {
|
|
15420
16397
|
this.debugLog('[FormConfigEditor] reset()');
|
|
15421
16398
|
}
|
|
15422
16399
|
catch { }
|
|
15423
|
-
this.jsonEditor?.
|
|
16400
|
+
this.jsonEditor?.updateJsonFromDocument(this.buildAuthoringDocument());
|
|
15424
16401
|
this.ruleBuilderState =
|
|
15425
16402
|
this.editedConfig.formRulesState ||
|
|
15426
16403
|
formLayoutRulesToBuilderState(this.editedConfig.formRules || []);
|
|
@@ -15436,11 +16413,13 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15436
16413
|
}
|
|
15437
16414
|
// Public: referenced in template for (ngModelChange) handlers
|
|
15438
16415
|
updateDirtyState(reason = 'unknown') {
|
|
15439
|
-
|
|
15440
|
-
const
|
|
15441
|
-
const b = this.stripLegacy(structuredClone(this.editedConfig));
|
|
16416
|
+
const a = serializeDynamicFormAuthoringDocument(this.initialDocument);
|
|
16417
|
+
const b = serializeDynamicFormAuthoringDocument(this.buildAuthoringDocument());
|
|
15442
16418
|
const hasChanges = stableStringify(a) !== stableStringify(b);
|
|
15443
16419
|
this.isDirty$.next(hasChanges);
|
|
16420
|
+
if (!reason.startsWith('json-')) {
|
|
16421
|
+
this.jsonEditor?.updateJsonFromDocument(this.buildAuthoringDocument());
|
|
16422
|
+
}
|
|
15444
16423
|
try {
|
|
15445
16424
|
if (hasChanges) {
|
|
15446
16425
|
const diffs = diffObjects(a, b, [], 12);
|
|
@@ -15455,51 +16434,62 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15455
16434
|
// Para outras validações futuras, verificar se não há validação JSON em andamento
|
|
15456
16435
|
}
|
|
15457
16436
|
getSettingsValue() {
|
|
15458
|
-
const
|
|
16437
|
+
const document = this.buildAuthoringDocument();
|
|
15459
16438
|
try {
|
|
15460
|
-
this.debugLog('[FormConfigEditor] getSettingsValue()', { sections:
|
|
16439
|
+
this.debugLog('[FormConfigEditor] getSettingsValue()', { sections: document.config.sections?.length || 0 });
|
|
15461
16440
|
}
|
|
15462
16441
|
catch { }
|
|
15463
|
-
return
|
|
15464
|
-
formConfig: sanitized,
|
|
15465
|
-
backConfig: this.backConfig,
|
|
15466
|
-
presentation: this.presentationPrefs,
|
|
15467
|
-
inputsPatch: { mode: this.inputMode },
|
|
15468
|
-
schemaPrefs: this.schemaPrefs,
|
|
15469
|
-
};
|
|
16442
|
+
return serializeDynamicFormAuthoringDocument(document);
|
|
15470
16443
|
}
|
|
15471
16444
|
onSave() {
|
|
15472
16445
|
this.isBusy$.next(true);
|
|
15473
16446
|
try {
|
|
15474
|
-
const
|
|
16447
|
+
const document = this.buildAuthoringDocument();
|
|
15475
16448
|
try {
|
|
15476
|
-
this.debugLog('[FormConfigEditor] onSave()', { sections:
|
|
16449
|
+
this.debugLog('[FormConfigEditor] onSave()', { sections: document.config.sections?.length || 0 });
|
|
15477
16450
|
}
|
|
15478
16451
|
catch { }
|
|
15479
|
-
return
|
|
15480
|
-
formConfig: sanitized,
|
|
15481
|
-
backConfig: this.backConfig,
|
|
15482
|
-
presentation: this.presentationPrefs,
|
|
15483
|
-
inputsPatch: { mode: this.inputMode },
|
|
15484
|
-
schemaPrefs: this.schemaPrefs,
|
|
15485
|
-
};
|
|
16452
|
+
return serializeDynamicFormAuthoringDocument(document);
|
|
15486
16453
|
}
|
|
15487
16454
|
finally {
|
|
15488
16455
|
this.isBusy$.next(false);
|
|
15489
16456
|
}
|
|
15490
16457
|
}
|
|
15491
16458
|
// (Resumo/ações rápidas removidos da aba Geral conforme orientação)
|
|
15492
|
-
onJsonConfigChange(
|
|
15493
|
-
|
|
16459
|
+
onJsonConfigChange(newValue) {
|
|
16460
|
+
const isCanonicalDocument = newValue?.kind ===
|
|
16461
|
+
'praxis.dynamic-form.editor';
|
|
16462
|
+
const document = isCanonicalDocument
|
|
16463
|
+
? newValue
|
|
16464
|
+
: parseLegacyOrDynamicFormDocument(newValue);
|
|
16465
|
+
this.editedConfig = this.stripLegacy(document.config);
|
|
16466
|
+
this.includeBackConfigBlock = isCanonicalDocument
|
|
16467
|
+
? Object.prototype.hasOwnProperty.call(document.contextSnapshot || {}, 'backConfig')
|
|
16468
|
+
: this.includeBackConfigBlock || !!document.contextSnapshot?.backConfig;
|
|
16469
|
+
this.backConfig = this.createBackConfigState(isCanonicalDocument
|
|
16470
|
+
? document.contextSnapshot?.backConfig
|
|
16471
|
+
: document.contextSnapshot?.backConfig || this.backConfig);
|
|
16472
|
+
this.includeBindingsBlock = isCanonicalDocument
|
|
16473
|
+
? Object.prototype.hasOwnProperty.call(document.bindings || {}, 'mode')
|
|
16474
|
+
: this.includeBindingsBlock || !!document.bindings?.mode;
|
|
16475
|
+
this.inputMode = this.normalizeMode(document.bindings?.mode);
|
|
16476
|
+
this.includePresentationBlock = isCanonicalDocument
|
|
16477
|
+
? Object.prototype.hasOwnProperty.call(document.contextSnapshot || {}, 'presentation')
|
|
16478
|
+
: this.includePresentationBlock || !!document.contextSnapshot?.presentation;
|
|
16479
|
+
this.presentationPrefs = this.mergePresentationPrefs(document.contextSnapshot?.presentation);
|
|
16480
|
+
this.includeSchemaPrefsBlock = isCanonicalDocument
|
|
16481
|
+
? Object.prototype.hasOwnProperty.call(document.contextSnapshot || {}, 'schemaPrefs')
|
|
16482
|
+
: this.includeSchemaPrefsBlock || !!document.contextSnapshot?.schemaPrefs;
|
|
16483
|
+
this.schemaPrefs = this.mergeSchemaPrefs(document.contextSnapshot?.schemaPrefs);
|
|
15494
16484
|
this.ensureHints();
|
|
15495
16485
|
try {
|
|
15496
16486
|
this.debugLog('[FormConfigEditor] onJsonConfigChange()', { sections: this.editedConfig.sections?.length || 0 });
|
|
15497
16487
|
}
|
|
15498
16488
|
catch { }
|
|
15499
|
-
this.ruleBuilderConfig = this.createRuleBuilderConfig(
|
|
16489
|
+
this.ruleBuilderConfig = this.createRuleBuilderConfig(document.config.fieldMetadata || [], document.config.sections || [], document.config.actions);
|
|
15500
16490
|
this.ruleBuilderState =
|
|
15501
|
-
|
|
15502
|
-
formLayoutRulesToBuilderState(
|
|
16491
|
+
document.config.formRulesState ||
|
|
16492
|
+
formLayoutRulesToBuilderState(document.config.formRules || []);
|
|
15503
16493
|
this.currentRules = ruleBuilderStateToFormLayoutRules(this.ruleBuilderState);
|
|
15504
16494
|
this.lastRulesSignature = this.computeRulesSignature(this.ruleBuilderState);
|
|
15505
16495
|
this.updateDirtyState('json-change');
|
|
@@ -15576,6 +16566,37 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15576
16566
|
this.currentRules = rules;
|
|
15577
16567
|
this.updateDirtyState('rules-change');
|
|
15578
16568
|
}
|
|
16569
|
+
get jsonDocument() {
|
|
16570
|
+
return this.buildAuthoringDocument();
|
|
16571
|
+
}
|
|
16572
|
+
onBackConfigChange() {
|
|
16573
|
+
this.includeBackConfigBlock = true;
|
|
16574
|
+
this.updateDirtyState('back-config-change');
|
|
16575
|
+
}
|
|
16576
|
+
isBindingsBlockPersisted() {
|
|
16577
|
+
return this.includeBindingsBlock;
|
|
16578
|
+
}
|
|
16579
|
+
isPresentationBlockPersisted() {
|
|
16580
|
+
return this.includePresentationBlock;
|
|
16581
|
+
}
|
|
16582
|
+
isSchemaPrefsBlockPersisted() {
|
|
16583
|
+
return this.includeSchemaPrefsBlock;
|
|
16584
|
+
}
|
|
16585
|
+
isBackConfigBlockPersisted() {
|
|
16586
|
+
return this.includeBackConfigBlock;
|
|
16587
|
+
}
|
|
16588
|
+
onInputModeChange() {
|
|
16589
|
+
this.includeBindingsBlock = true;
|
|
16590
|
+
this.updateDirtyState('input-mode-change');
|
|
16591
|
+
}
|
|
16592
|
+
onPresentationPrefsChange(reason) {
|
|
16593
|
+
this.includePresentationBlock = true;
|
|
16594
|
+
this.updateDirtyState(reason);
|
|
16595
|
+
}
|
|
16596
|
+
onSchemaPrefsChange(reason) {
|
|
16597
|
+
this.includeSchemaPrefsBlock = true;
|
|
16598
|
+
this.updateDirtyState(reason);
|
|
16599
|
+
}
|
|
15579
16600
|
createRuleBuilderConfig(fieldMetadata, sections = [], actions) {
|
|
15580
16601
|
const mapped = this.mapMetadataToSchema(fieldMetadata, sections, actions);
|
|
15581
16602
|
return {
|
|
@@ -15749,7 +16770,7 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15749
16770
|
this.editedConfig = { ...this.editedConfig, fieldMetadata: next };
|
|
15750
16771
|
// Keep JSON editor view in sync when open
|
|
15751
16772
|
try {
|
|
15752
|
-
this.jsonEditor?.
|
|
16773
|
+
this.jsonEditor?.updateJsonFromDocument(this.buildAuthoringDocument());
|
|
15753
16774
|
}
|
|
15754
16775
|
catch { }
|
|
15755
16776
|
this.updateDirtyState();
|
|
@@ -15787,7 +16808,7 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15787
16808
|
// Defaults are injected by normalization on save/apply; keep a local reset to blank to let defaults win.
|
|
15788
16809
|
current.hints = undefined;
|
|
15789
16810
|
// Keep JSON editor in sync
|
|
15790
|
-
this.jsonEditor?.
|
|
16811
|
+
this.jsonEditor?.updateJsonFromDocument(this.buildAuthoringDocument());
|
|
15791
16812
|
this.updateDirtyState('hints-restore');
|
|
15792
16813
|
}
|
|
15793
16814
|
catch { }
|
|
@@ -15797,7 +16818,11 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15797
16818
|
if (!key)
|
|
15798
16819
|
return;
|
|
15799
16820
|
try {
|
|
15800
|
-
const
|
|
16821
|
+
const hasDocumentPresentation = this.openedWithCanonicalDocument ||
|
|
16822
|
+
!!this.initialDocument.contextSnapshot?.presentation;
|
|
16823
|
+
const pres = hasDocumentPresentation
|
|
16824
|
+
? null
|
|
16825
|
+
: await firstValueFrom(this.storage.loadConfig(`form-pres:${key}:${this.editorMode}`)).catch(() => null);
|
|
15801
16826
|
if (pres) {
|
|
15802
16827
|
this.presentationPrefs = {
|
|
15803
16828
|
labelPosition: pres.labelPosition === 'left' ? 'left' : 'above',
|
|
@@ -15810,7 +16835,11 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15810
16835
|
valueAlign: pres.valueAlign || 'start',
|
|
15811
16836
|
};
|
|
15812
16837
|
}
|
|
15813
|
-
const
|
|
16838
|
+
const hasDocumentSchemaPrefs = this.openedWithCanonicalDocument ||
|
|
16839
|
+
!!this.initialDocument.contextSnapshot?.schemaPrefs;
|
|
16840
|
+
const prefs = hasDocumentSchemaPrefs
|
|
16841
|
+
? null
|
|
16842
|
+
: await firstValueFrom(this.storage.loadConfig(`form-schema-prefs:${key}`)).catch(() => null);
|
|
15814
16843
|
if (prefs && typeof prefs === 'object') {
|
|
15815
16844
|
const allowed = {};
|
|
15816
16845
|
const n = prefs.notifyIfOutdated;
|
|
@@ -15858,8 +16887,75 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15858
16887
|
return value;
|
|
15859
16888
|
return 'create';
|
|
15860
16889
|
}
|
|
16890
|
+
buildAuthoringDocument() {
|
|
16891
|
+
const contextSnapshot = stripUndefinedContextSnapshot({
|
|
16892
|
+
backConfig: this.includeBackConfigBlock
|
|
16893
|
+
? this.toBackConfigSnapshot()
|
|
16894
|
+
: undefined,
|
|
16895
|
+
presentation: this.includePresentationBlock
|
|
16896
|
+
? this.toPresentationSnapshot()
|
|
16897
|
+
: undefined,
|
|
16898
|
+
schemaPrefs: this.includeSchemaPrefsBlock
|
|
16899
|
+
? this.toSchemaPrefsSnapshot()
|
|
16900
|
+
: undefined,
|
|
16901
|
+
});
|
|
16902
|
+
return createDynamicFormAuthoringDocument({
|
|
16903
|
+
config: this.stripLegacy(this.editedConfig),
|
|
16904
|
+
bindings: this.includeBindingsBlock
|
|
16905
|
+
? {
|
|
16906
|
+
mode: this.inputMode,
|
|
16907
|
+
}
|
|
16908
|
+
: undefined,
|
|
16909
|
+
contextSnapshot,
|
|
16910
|
+
});
|
|
16911
|
+
}
|
|
16912
|
+
createBackConfigState(snapshot) {
|
|
16913
|
+
return (structuredClone(snapshot) ||
|
|
16914
|
+
{ returnTo: '', confirmOnDirty: true });
|
|
16915
|
+
}
|
|
16916
|
+
mergePresentationPrefs(snapshot) {
|
|
16917
|
+
return {
|
|
16918
|
+
labelPosition: snapshot?.labelPosition === 'left' ? 'left' : 'above',
|
|
16919
|
+
labelFontSize: snapshot?.labelFontSize ?? null,
|
|
16920
|
+
valueFontSize: snapshot?.valueFontSize ?? null,
|
|
16921
|
+
compact: snapshot?.compact === true,
|
|
16922
|
+
density: snapshot?.density || 'comfortable',
|
|
16923
|
+
labelWidth: snapshot?.labelWidth ?? 140,
|
|
16924
|
+
labelAlign: snapshot?.labelAlign || 'start',
|
|
16925
|
+
valueAlign: snapshot?.valueAlign || 'start',
|
|
16926
|
+
};
|
|
16927
|
+
}
|
|
16928
|
+
mergeSchemaPrefs(snapshot) {
|
|
16929
|
+
return {
|
|
16930
|
+
notifyIfOutdated: snapshot?.notifyIfOutdated || 'both',
|
|
16931
|
+
snoozeMs: snapshot?.snoozeMs ?? 24 * 60 * 60 * 1000,
|
|
16932
|
+
autoOpenSettingsOnOutdated: snapshot?.autoOpenSettingsOnOutdated === true,
|
|
16933
|
+
};
|
|
16934
|
+
}
|
|
16935
|
+
toPresentationSnapshot() {
|
|
16936
|
+
return {
|
|
16937
|
+
labelPosition: this.presentationPrefs.labelPosition,
|
|
16938
|
+
labelFontSize: this.presentationPrefs.labelFontSize ?? null,
|
|
16939
|
+
valueFontSize: this.presentationPrefs.valueFontSize ?? null,
|
|
16940
|
+
compact: this.presentationPrefs.compact === true,
|
|
16941
|
+
density: this.presentationPrefs.density,
|
|
16942
|
+
labelWidth: this.presentationPrefs.labelWidth ?? null,
|
|
16943
|
+
labelAlign: this.presentationPrefs.labelAlign,
|
|
16944
|
+
valueAlign: this.presentationPrefs.valueAlign,
|
|
16945
|
+
};
|
|
16946
|
+
}
|
|
16947
|
+
toSchemaPrefsSnapshot() {
|
|
16948
|
+
return {
|
|
16949
|
+
notifyIfOutdated: this.schemaPrefs.notifyIfOutdated,
|
|
16950
|
+
snoozeMs: this.schemaPrefs.snoozeMs,
|
|
16951
|
+
autoOpenSettingsOnOutdated: this.schemaPrefs.autoOpenSettingsOnOutdated === true,
|
|
16952
|
+
};
|
|
16953
|
+
}
|
|
16954
|
+
toBackConfigSnapshot() {
|
|
16955
|
+
return structuredClone(this.backConfig || { returnTo: '', confirmOnDirty: true });
|
|
16956
|
+
}
|
|
15861
16957
|
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 });
|
|
15862
|
-
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"] }] });
|
|
16958
|
+
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"] }] });
|
|
15863
16959
|
}
|
|
15864
16960
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisDynamicFormConfigEditor, decorators: [{
|
|
15865
16961
|
type: Component,
|
|
@@ -15885,7 +16981,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
15885
16981
|
HooksEditorComponent,
|
|
15886
16982
|
PraxisVisualBuilder,
|
|
15887
16983
|
CascadeManagerTabComponent,
|
|
15888
|
-
], 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"] }]
|
|
16984
|
+
], 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"] }]
|
|
15889
16985
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
15890
16986
|
type: Inject,
|
|
15891
16987
|
args: [ASYNC_CONFIG_STORAGE]
|
|
@@ -15909,6 +17005,12 @@ function omit(obj, keys) {
|
|
|
15909
17005
|
}
|
|
15910
17006
|
return out;
|
|
15911
17007
|
}
|
|
17008
|
+
function stripUndefinedContextSnapshot(snapshot) {
|
|
17009
|
+
const result = Object.fromEntries(Object.entries(snapshot).filter(([, value]) => value !== undefined));
|
|
17010
|
+
return Object.keys(result).length
|
|
17011
|
+
? result
|
|
17012
|
+
: undefined;
|
|
17013
|
+
}
|
|
15912
17014
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
15913
17015
|
function hasFieldMetadata(cfg) {
|
|
15914
17016
|
return cfg && Array.isArray(cfg.fieldMetadata);
|
|
@@ -16131,7 +17233,7 @@ const PRAXIS_DYNAMIC_FORM_COMPONENT_METADATA = {
|
|
|
16131
17233
|
{ name: 'customAction', type: 'FormCustomActionEvent', description: 'Emite quando uma ação customizada é executada' },
|
|
16132
17234
|
{ name: 'actionConfirmation', type: 'FormActionConfirmationEvent', description: 'Resultado de confirmações de ações' },
|
|
16133
17235
|
{ name: 'schemaStatusChange', type: '{ outdated: boolean; serverHash?: string; lastVerifiedAt?: string; formId?: string }', description: 'Emite quando o status do schema muda' },
|
|
16134
|
-
{ name: 'widgetEvent', type: '{ placement: "before" | "after"; sourceId: string; output?: string; payload?: any }', label: 'Evento editorial', description: 'Reemite eventos dos widgets editoriais hospedados antes
|
|
17236
|
+
{ name: 'widgetEvent', type: '{ placement: "before" | "beforeActions" | "after"; sourceId: string; output?: string; payload?: any }', label: 'Evento editorial', description: 'Reemite eventos dos widgets editoriais hospedados antes, antes das ações ou depois do formulário' },
|
|
16135
17237
|
],
|
|
16136
17238
|
actions: [
|
|
16137
17239
|
{
|
|
@@ -16291,6 +17393,8 @@ class SectionEditorComponent {
|
|
|
16291
17393
|
title: [this.section.title, Validators.required],
|
|
16292
17394
|
icon: [this.section.icon ?? ''],
|
|
16293
17395
|
description: [this.section.description],
|
|
17396
|
+
appearance: [this.section.appearance ?? 'card'],
|
|
17397
|
+
stepLabel: [this.section.stepLabel ?? ''],
|
|
16294
17398
|
gapBottom: [this.section.gapBottom ?? 0],
|
|
16295
17399
|
titleGapBottom: [this.section.titleGapBottom ?? 20],
|
|
16296
17400
|
descriptionGapBottom: [this.section.descriptionGapBottom ?? 8],
|
|
@@ -16502,6 +17606,14 @@ class SectionEditorComponent {
|
|
|
16502
17606
|
</mat-form-field>
|
|
16503
17607
|
|
|
16504
17608
|
<div class="row-3">
|
|
17609
|
+
<mat-form-field appearance="fill">
|
|
17610
|
+
<mat-label>Aparência</mat-label>
|
|
17611
|
+
<mat-select formControlName="appearance">
|
|
17612
|
+
<mat-option value="card">Card</mat-option>
|
|
17613
|
+
<mat-option value="plain">Plain</mat-option>
|
|
17614
|
+
<mat-option value="step">Step</mat-option>
|
|
17615
|
+
</mat-select>
|
|
17616
|
+
</mat-form-field>
|
|
16505
17617
|
<mat-form-field appearance="fill">
|
|
16506
17618
|
<mat-label>Fonte do título</mat-label>
|
|
16507
17619
|
<mat-select [value]="section.titleStyle || 'titleMedium'" (valueChange)="onTitleStyleChange($event)">
|
|
@@ -16519,6 +17631,13 @@ class SectionEditorComponent {
|
|
|
16519
17631
|
<mat-option value="bodySmall">Corpo pequeno</mat-option>
|
|
16520
17632
|
</mat-select>
|
|
16521
17633
|
</mat-form-field>
|
|
17634
|
+
<mat-form-field appearance="fill">
|
|
17635
|
+
<mat-label>Rótulo do passo</mat-label>
|
|
17636
|
+
<input matInput formControlName="stepLabel" placeholder="ex.: 1, 2, A" />
|
|
17637
|
+
</mat-form-field>
|
|
17638
|
+
</div>
|
|
17639
|
+
|
|
17640
|
+
<div class="row-3">
|
|
16522
17641
|
<mat-form-field appearance="fill">
|
|
16523
17642
|
<mat-label>Alinhamento do cabeçalho</mat-label>
|
|
16524
17643
|
<mat-select formControlName="headerAlign">
|
|
@@ -16707,6 +17826,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
16707
17826
|
</mat-form-field>
|
|
16708
17827
|
|
|
16709
17828
|
<div class="row-3">
|
|
17829
|
+
<mat-form-field appearance="fill">
|
|
17830
|
+
<mat-label>Aparência</mat-label>
|
|
17831
|
+
<mat-select formControlName="appearance">
|
|
17832
|
+
<mat-option value="card">Card</mat-option>
|
|
17833
|
+
<mat-option value="plain">Plain</mat-option>
|
|
17834
|
+
<mat-option value="step">Step</mat-option>
|
|
17835
|
+
</mat-select>
|
|
17836
|
+
</mat-form-field>
|
|
16710
17837
|
<mat-form-field appearance="fill">
|
|
16711
17838
|
<mat-label>Fonte do título</mat-label>
|
|
16712
17839
|
<mat-select [value]="section.titleStyle || 'titleMedium'" (valueChange)="onTitleStyleChange($event)">
|
|
@@ -16724,6 +17851,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
16724
17851
|
<mat-option value="bodySmall">Corpo pequeno</mat-option>
|
|
16725
17852
|
</mat-select>
|
|
16726
17853
|
</mat-form-field>
|
|
17854
|
+
<mat-form-field appearance="fill">
|
|
17855
|
+
<mat-label>Rótulo do passo</mat-label>
|
|
17856
|
+
<input matInput formControlName="stepLabel" placeholder="ex.: 1, 2, A" />
|
|
17857
|
+
</mat-form-field>
|
|
17858
|
+
</div>
|
|
17859
|
+
|
|
17860
|
+
<div class="row-3">
|
|
16727
17861
|
<mat-form-field appearance="fill">
|
|
16728
17862
|
<mat-label>Alinhamento do cabeçalho</mat-label>
|
|
16729
17863
|
<mat-select formControlName="headerAlign">
|
|
@@ -17906,6 +19040,7 @@ const FORM_COMPONENT_AI_CAPABILITIES = {
|
|
|
17906
19040
|
'inputs.config.presentation e uma extensao de runtime aplicada pelo componente.',
|
|
17907
19041
|
'Preferir FormConfig (sections/fieldMetadata) em vez do layout legado (fieldsets).',
|
|
17908
19042
|
'Use inputs.editorialContext apenas para override de contexto editorial do host; a precedencia final e runtime < config.editorialContext < host.',
|
|
19043
|
+
'widgets editoriais hospedados pelo form podem ser renderizados em tres slots semanticos: before, beforeActions e after.',
|
|
17909
19044
|
],
|
|
17910
19045
|
capabilities: [
|
|
17911
19046
|
// =============================================================================
|
|
@@ -18300,7 +19435,7 @@ const FORM_COMPONENT_AI_CAPABILITIES = {
|
|
|
18300
19435
|
path: 'outputs.widgetEvent.placement',
|
|
18301
19436
|
category: 'outputs',
|
|
18302
19437
|
valueKind: 'enum',
|
|
18303
|
-
allowedValues: ['before', 'after'],
|
|
19438
|
+
allowedValues: ['before', 'beforeActions', 'after'],
|
|
18304
19439
|
description: 'Posicao do widget editorial no host do form.',
|
|
18305
19440
|
},
|
|
18306
19441
|
{
|
|
@@ -18332,5 +19467,5 @@ const FORM_COMPONENT_AI_CAPABILITIES = {
|
|
|
18332
19467
|
* Generated bundle index. Do not edit.
|
|
18333
19468
|
*/
|
|
18334
19469
|
|
|
18335
|
-
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 };
|
|
19470
|
+
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 };
|
|
18336
19471
|
//# sourceMappingURL=praxisui-dynamic-form.mjs.map
|