@praxisui/dynamic-form 1.0.0-beta.61 → 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 +54 -5
- package/fesm2022/praxisui-dynamic-form.mjs +1407 -318
- package/fesm2022/praxisui-dynamic-form.mjs.map +1 -1
- package/index.d.ts +158 -5
- package/package.json +11 -10
|
@@ -22,7 +22,7 @@ import { MatBadgeModule } from '@angular/material/badge';
|
|
|
22
22
|
import { firstValueFrom, BehaviorSubject, Subject, debounceTime as debounceTime$1, takeUntil as takeUntil$1 } from 'rxjs';
|
|
23
23
|
import { take, takeUntil, debounceTime, finalize, map } from 'rxjs/operators';
|
|
24
24
|
import * as i1$2 from '@praxisui/core';
|
|
25
|
-
import { PraxisIconDirective, RULE_PROPERTY_SCHEMA, FIELD_METADATA_CAPABILITIES, deepMerge,
|
|
25
|
+
import { PraxisIconDirective, RULE_PROPERTY_SCHEMA, FIELD_METADATA_CAPABILITIES, deepMerge, createDefaultFormConfig, normalizeFormConfig as normalizeFormConfig$1, ensureIds, ASYNC_CONFIG_STORAGE, migrateFormLayoutRule, LoggerService, createCorporateLoggerConfig, ConsoleLoggerSink, ResourceQuickConnectComponent, mapFieldDefinitionsToMetadata, syncWithServerMetadata, PRAXIS_LOADING_CTX, normalizeControlTypeKey, resolveSpan, resolveOffset, resolveOrder, resolveHidden, buildSchemaId, fetchWithETag, resolveControlTypeAlias, getTextTransformer, MemoryCacheAdapter, LocalStorageCacheAdapter, SchemaMetadataClient, CONNECTION_STORAGE, PRAXIS_LOADING_RENDERER, FORM_HOOKS_PRESETS, DynamicWidgetLoaderDirective, EmptyStateCardComponent, isValidFormConfig, ComponentMetadataRegistry, FieldControlType, GLOBAL_ACTION_CATALOG, GLOBAL_ACTION_SPEC_CATALOG, getGlobalActionCatalog, PRAXIS_GLOBAL_ACTION_CATALOG, getGlobalActionUiSchema } from '@praxisui/core';
|
|
26
26
|
import * as i1 from '@praxisui/dynamic-fields';
|
|
27
27
|
import { getControlTypeCatalog, ConfirmDialogComponent, DynamicFieldLoaderDirective } from '@praxisui/dynamic-fields';
|
|
28
28
|
import { BaseAiAdapter, PraxisAiAssistantComponent } from '@praxisui/ai';
|
|
@@ -1023,6 +1023,21 @@ const FORM_AI_CAPABILITIES = {
|
|
|
1023
1023
|
valueKind: 'number',
|
|
1024
1024
|
description: 'Espaçamento vertical (em px) abaixo da descrição da seção.',
|
|
1025
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
|
+
},
|
|
1026
1041
|
{
|
|
1027
1042
|
path: 'sections[].titleStyle',
|
|
1028
1043
|
category: 'appearance',
|
|
@@ -2394,6 +2409,8 @@ const TASK_PRESETS = {
|
|
|
2394
2409
|
'sections[].gapBottom',
|
|
2395
2410
|
'sections[].titleGapBottom',
|
|
2396
2411
|
'sections[].descriptionGapBottom',
|
|
2412
|
+
'sections[].appearance',
|
|
2413
|
+
'sections[].stepLabel',
|
|
2397
2414
|
'sections[].titleStyle',
|
|
2398
2415
|
'sections[].descriptionStyle',
|
|
2399
2416
|
'sections[].titleColor',
|
|
@@ -2797,6 +2814,463 @@ class FormAiAdapter extends BaseAiAdapter {
|
|
|
2797
2814
|
}
|
|
2798
2815
|
}
|
|
2799
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
|
+
|
|
2800
3274
|
class FormLayoutService {
|
|
2801
3275
|
storage;
|
|
2802
3276
|
constructor(storage) {
|
|
@@ -4308,6 +4782,9 @@ class PraxisDynamicForm {
|
|
|
4308
4782
|
get formBlocksBeforeActions() {
|
|
4309
4783
|
return Array.isArray(this.config?.formBlocksBeforeActions) ? this.config.formBlocksBeforeActions : [];
|
|
4310
4784
|
}
|
|
4785
|
+
get beforeActionsPlacement() {
|
|
4786
|
+
return (this.config?.formBlocksBeforeActionsPlacement || 'afterSections');
|
|
4787
|
+
}
|
|
4311
4788
|
get actionPlacement() {
|
|
4312
4789
|
return (this.config.actions?.placement || 'afterSections');
|
|
4313
4790
|
}
|
|
@@ -4902,17 +5379,23 @@ class PraxisDynamicForm {
|
|
|
4902
5379
|
}
|
|
4903
5380
|
}
|
|
4904
5381
|
if (changes['config'] || changes['presentationModeGlobal']) {
|
|
5382
|
+
let shouldRebuildFromExternalConfig = false;
|
|
4905
5383
|
if (changes['config']?.currentValue) {
|
|
4906
5384
|
this.config = normalizeFormConfig$1(changes['config'].currentValue);
|
|
4907
5385
|
this.logRowGapsSnapshot('config@input');
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
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;
|
|
4913
5391
|
}
|
|
4914
5392
|
}
|
|
4915
5393
|
this.applyPresentationVars();
|
|
5394
|
+
if (shouldRebuildFromExternalConfig) {
|
|
5395
|
+
this.buildFormFromConfig();
|
|
5396
|
+
this.isInitialized = true;
|
|
5397
|
+
this.initializationStatus = 'success';
|
|
5398
|
+
}
|
|
4916
5399
|
// On external config changes, if we have base and resourcePath configured, verify in background
|
|
4917
5400
|
if (this.resourcePath && (this.config?.sections?.length ?? 0) > 0 && this.formId) {
|
|
4918
5401
|
this.verifyServerSchemaVersion().catch(() => { });
|
|
@@ -5526,6 +6009,14 @@ class PraxisDynamicForm {
|
|
|
5526
6009
|
const props = this.getSectionRuleProps(section);
|
|
5527
6010
|
return props.description !== undefined ? props.description : section.description;
|
|
5528
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
|
+
}
|
|
5529
6020
|
getSectionTitleColor(section) {
|
|
5530
6021
|
const props = this.getSectionRuleProps(section);
|
|
5531
6022
|
return props.titleColor !== undefined ? props.titleColor : section.titleColor;
|
|
@@ -5606,6 +6097,9 @@ class PraxisDynamicForm {
|
|
|
5606
6097
|
}
|
|
5607
6098
|
getSectionClasses(section) {
|
|
5608
6099
|
const classes = [];
|
|
6100
|
+
const appearance = this.getSectionAppearance(section);
|
|
6101
|
+
if (appearance)
|
|
6102
|
+
classes.push(`section-appearance-${appearance}`);
|
|
5609
6103
|
const baseClass = section.className;
|
|
5610
6104
|
if (baseClass)
|
|
5611
6105
|
classes.push(baseClass);
|
|
@@ -6169,6 +6663,11 @@ class PraxisDynamicForm {
|
|
|
6169
6663
|
}
|
|
6170
6664
|
async openConfigEditor() {
|
|
6171
6665
|
const initialConfig = normalizeFormConfig$1(this.config);
|
|
6666
|
+
const initialDocument = createDynamicFormAuthoringDocument({
|
|
6667
|
+
config: initialConfig,
|
|
6668
|
+
bindings: { mode: this.mode },
|
|
6669
|
+
contextSnapshot: this.captureCurrentContextSnapshot(),
|
|
6670
|
+
});
|
|
6172
6671
|
const { PraxisDynamicFormConfigEditor } = await Promise.resolve().then(function () { return index; });
|
|
6173
6672
|
const ref = this.settingsPanel.open({
|
|
6174
6673
|
id: `form.${this.formId}`,
|
|
@@ -6177,68 +6676,28 @@ class PraxisDynamicForm {
|
|
|
6177
6676
|
content: {
|
|
6178
6677
|
component: PraxisDynamicFormConfigEditor,
|
|
6179
6678
|
inputs: {
|
|
6180
|
-
|
|
6181
|
-
backConfig: this.backConfig,
|
|
6679
|
+
document: initialDocument,
|
|
6182
6680
|
formId: this.formId,
|
|
6183
6681
|
componentKeyId: this.componentKeyId(),
|
|
6184
|
-
|
|
6682
|
+
presentationMode: this.presentationModeGlobal === true,
|
|
6185
6683
|
},
|
|
6186
6684
|
},
|
|
6187
6685
|
});
|
|
6188
|
-
ref.applied$.pipe(takeUntil(this.destroy$)).subscribe(
|
|
6686
|
+
ref.applied$.pipe(takeUntil(this.destroy$)).subscribe((cfg) => {
|
|
6189
6687
|
try {
|
|
6190
6688
|
this.debugLog('[PDF] settingsPanel.applied$', { hasCfg: !!cfg });
|
|
6191
6689
|
}
|
|
6192
6690
|
catch { }
|
|
6193
6691
|
if (cfg) {
|
|
6194
|
-
const
|
|
6195
|
-
const
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
if (backKey) {
|
|
6204
|
-
this.asyncConfigStorage.saveConfig(backKey, backCfg).subscribe();
|
|
6205
|
-
this.backConfig = backCfg;
|
|
6206
|
-
}
|
|
6207
|
-
}
|
|
6208
|
-
if (presCfg) {
|
|
6209
|
-
const presKey = this.formPresKey(persistMode);
|
|
6210
|
-
if (presKey) {
|
|
6211
|
-
this.asyncConfigStorage.saveConfig(presKey, presCfg).subscribe();
|
|
6212
|
-
}
|
|
6213
|
-
// Aplicar imediatamente as preferências de apresentação no formulário
|
|
6214
|
-
this.applyPresentationVars();
|
|
6215
|
-
}
|
|
6216
|
-
if (schemaPrefs) {
|
|
6217
|
-
const prefsKey = this.getPrefsKey();
|
|
6218
|
-
if (prefsKey) {
|
|
6219
|
-
this.asyncConfigStorage.saveConfig(prefsKey, schemaPrefs).subscribe();
|
|
6220
|
-
this.resolveSchemaPrefs();
|
|
6221
|
-
}
|
|
6222
|
-
}
|
|
6223
|
-
if (inputsPatch && typeof inputsPatch === 'object') {
|
|
6224
|
-
if (typeof inputsPatch.mode === 'string') {
|
|
6225
|
-
const mv = inputsPatch.mode;
|
|
6226
|
-
if (mv === 'create' || mv === 'edit' || mv === 'view') {
|
|
6227
|
-
this.mode = mv;
|
|
6228
|
-
const inputsKey = this.formInputsKey();
|
|
6229
|
-
if (inputsKey) {
|
|
6230
|
-
const prev = await firstValueFrom(this.asyncConfigStorage.loadConfig(inputsKey)).catch(() => ({}));
|
|
6231
|
-
this.asyncConfigStorage.saveConfig(inputsKey, { ...prev, mode: mv }).subscribe();
|
|
6232
|
-
}
|
|
6233
|
-
}
|
|
6234
|
-
}
|
|
6235
|
-
}
|
|
6236
|
-
this.configChange.emit(this.config);
|
|
6237
|
-
this.buildFormFromConfig();
|
|
6238
|
-
const patchMode = typeof inputsPatch?.mode === 'string' ? inputsPatch.mode : undefined;
|
|
6239
|
-
if (patchMode && patchMode !== persistMode) {
|
|
6240
|
-
this.reloadForModeChange();
|
|
6241
|
-
}
|
|
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');
|
|
6242
6701
|
}
|
|
6243
6702
|
});
|
|
6244
6703
|
// Handle reset from Settings Panel: clear persisted config so globals/user defaults apply
|
|
@@ -6247,70 +6706,23 @@ class PraxisDynamicForm {
|
|
|
6247
6706
|
this.debugLog('[PDF] settingsPanel.reset$');
|
|
6248
6707
|
}
|
|
6249
6708
|
catch { }
|
|
6250
|
-
|
|
6251
|
-
const key = this.formConfigKey();
|
|
6252
|
-
if (key)
|
|
6253
|
-
this.asyncConfigStorage.clearConfig(key).subscribe();
|
|
6254
|
-
}
|
|
6255
|
-
catch { }
|
|
6709
|
+
this.clearDynamicFormEditorPersistence();
|
|
6256
6710
|
});
|
|
6257
|
-
ref.saved$.pipe(takeUntil(this.destroy$)).subscribe(
|
|
6711
|
+
ref.saved$.pipe(takeUntil(this.destroy$)).subscribe((cfg) => {
|
|
6258
6712
|
try {
|
|
6259
6713
|
this.debugLog('[PDF] settingsPanel.saved$', { hasCfg: !!cfg });
|
|
6260
6714
|
}
|
|
6261
6715
|
catch { }
|
|
6262
6716
|
if (cfg) {
|
|
6263
|
-
const
|
|
6264
|
-
const
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
this.persistFormConfig(key, this.config);
|
|
6273
|
-
if (backCfg) {
|
|
6274
|
-
const backKey = this.crudBackKey();
|
|
6275
|
-
if (backKey) {
|
|
6276
|
-
this.asyncConfigStorage.saveConfig(backKey, backCfg).subscribe();
|
|
6277
|
-
this.backConfig = backCfg;
|
|
6278
|
-
}
|
|
6279
|
-
}
|
|
6280
|
-
if (presCfg) {
|
|
6281
|
-
const presKey = this.formPresKey(persistMode);
|
|
6282
|
-
if (presKey) {
|
|
6283
|
-
this.asyncConfigStorage.saveConfig(presKey, presCfg).subscribe();
|
|
6284
|
-
}
|
|
6285
|
-
// Aplicar imediatamente as preferências de apresentação no formulário
|
|
6286
|
-
this.applyPresentationVars();
|
|
6287
|
-
}
|
|
6288
|
-
if (schemaPrefs) {
|
|
6289
|
-
const prefsKey = this.getPrefsKey();
|
|
6290
|
-
if (prefsKey) {
|
|
6291
|
-
this.asyncConfigStorage.saveConfig(prefsKey, schemaPrefs).subscribe();
|
|
6292
|
-
this.resolveSchemaPrefs();
|
|
6293
|
-
}
|
|
6294
|
-
}
|
|
6295
|
-
if (inputsPatch && typeof inputsPatch === 'object') {
|
|
6296
|
-
if (typeof inputsPatch.mode === 'string') {
|
|
6297
|
-
const mv = inputsPatch.mode;
|
|
6298
|
-
if (mv === 'create' || mv === 'edit' || mv === 'view') {
|
|
6299
|
-
this.mode = mv;
|
|
6300
|
-
const inputsKey = this.formInputsKey();
|
|
6301
|
-
if (inputsKey) {
|
|
6302
|
-
const prev = await firstValueFrom(this.asyncConfigStorage.loadConfig(inputsKey)).catch(() => ({}));
|
|
6303
|
-
this.asyncConfigStorage.saveConfig(inputsKey, { ...prev, mode: mv }).subscribe();
|
|
6304
|
-
}
|
|
6305
|
-
}
|
|
6306
|
-
}
|
|
6307
|
-
}
|
|
6308
|
-
this.configChange.emit(this.config);
|
|
6309
|
-
this.buildFormFromConfig();
|
|
6310
|
-
const patchMode = typeof inputsPatch?.mode === 'string' ? inputsPatch.mode : undefined;
|
|
6311
|
-
if (patchMode && patchMode !== persistMode) {
|
|
6312
|
-
this.reloadForModeChange();
|
|
6313
|
-
}
|
|
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');
|
|
6314
6726
|
}
|
|
6315
6727
|
});
|
|
6316
6728
|
ref.reset$.pipe(takeUntil(this.destroy$)).subscribe(() => {
|
|
@@ -6318,9 +6730,14 @@ class PraxisDynamicForm {
|
|
|
6318
6730
|
this.debugLog('[PDF] settingsPanel.reset$ (second handler)');
|
|
6319
6731
|
}
|
|
6320
6732
|
catch { }
|
|
6321
|
-
this.
|
|
6322
|
-
|
|
6323
|
-
|
|
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');
|
|
6324
6741
|
});
|
|
6325
6742
|
}
|
|
6326
6743
|
// --- Canvas Interaction Methods ---
|
|
@@ -6824,6 +7241,7 @@ class PraxisDynamicForm {
|
|
|
6824
7241
|
name: fieldMeta.name,
|
|
6825
7242
|
label: fieldMeta.label,
|
|
6826
7243
|
placeholder: fieldMeta.placeholder,
|
|
7244
|
+
description: fieldMeta.description,
|
|
6827
7245
|
hint: fieldMeta.hint,
|
|
6828
7246
|
// Canonical constraints only (no legacy)
|
|
6829
7247
|
required: fieldMeta.required,
|
|
@@ -7188,6 +7606,87 @@ class PraxisDynamicForm {
|
|
|
7188
7606
|
seed.layout = fmAny.layout;
|
|
7189
7607
|
if (fmAny.labelPosition !== undefined)
|
|
7190
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;
|
|
7191
7690
|
}
|
|
7192
7691
|
// BUTTON_TOGGLE
|
|
7193
7692
|
if (ctU === 'BUTTON_TOGGLE') {
|
|
@@ -8238,6 +8737,12 @@ class PraxisDynamicForm {
|
|
|
8238
8737
|
const norm = normalizeOptions(patch.options) ?? [];
|
|
8239
8738
|
fm.checkboxOptions = norm;
|
|
8240
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;
|
|
8241
8746
|
if (patch.layout !== undefined)
|
|
8242
8747
|
fm.layout = patch.layout;
|
|
8243
8748
|
if (patch.labelPosition !== undefined)
|
|
@@ -8246,6 +8751,24 @@ class PraxisDynamicForm {
|
|
|
8246
8751
|
fm.color = patch.color;
|
|
8247
8752
|
if (patch.indeterminate !== undefined)
|
|
8248
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
|
+
}
|
|
8249
8772
|
if (patch.linkText !== undefined)
|
|
8250
8773
|
fm.linkText = patch.linkText;
|
|
8251
8774
|
if (patch.linkUrl !== undefined)
|
|
@@ -8253,6 +8776,20 @@ class PraxisDynamicForm {
|
|
|
8253
8776
|
if (patch.linkTarget !== undefined)
|
|
8254
8777
|
fm.linkTarget = patch.linkTarget;
|
|
8255
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
|
+
}
|
|
8256
8793
|
// TOGGLE
|
|
8257
8794
|
if (fctApply === 'TOGGLE') {
|
|
8258
8795
|
if (patch.color !== undefined)
|
|
@@ -9243,41 +9780,272 @@ class PraxisDynamicForm {
|
|
|
9243
9780
|
// 5. Trigger change detection to be safe.
|
|
9244
9781
|
this.cdr.detectChanges();
|
|
9245
9782
|
}
|
|
9246
|
-
ngOnDestroy() {
|
|
9247
|
-
this.destroy$.next();
|
|
9248
|
-
this.destroy$.complete();
|
|
9249
|
-
this.schemaCache = null;
|
|
9250
|
-
this.isInitialized = false;
|
|
9251
|
-
if (this.mountAnimationTimer) {
|
|
9252
|
-
clearTimeout(this.mountAnimationTimer);
|
|
9253
|
-
this.mountAnimationTimer = null;
|
|
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;
|
|
9254
9985
|
}
|
|
9986
|
+
const next = structuredClone(cfg);
|
|
9987
|
+
const schemaHash = this.lastSchemaMeta?.serverHash || this.schemaState.meta?.serverHash;
|
|
9988
|
+
if (!schemaHash) {
|
|
9989
|
+
return next;
|
|
9990
|
+
}
|
|
9991
|
+
next.metadata = {
|
|
9992
|
+
...(next.metadata || {}),
|
|
9993
|
+
serverHash: schemaHash,
|
|
9994
|
+
};
|
|
9995
|
+
return next;
|
|
9255
9996
|
}
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
|
|
9997
|
+
parsePresentationCssNumber(value) {
|
|
9998
|
+
if (!value)
|
|
9999
|
+
return null;
|
|
10000
|
+
const parsed = Number.parseFloat(value);
|
|
10001
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
9259
10002
|
}
|
|
9260
|
-
|
|
9261
|
-
|
|
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
|
+
});
|
|
9262
10024
|
}
|
|
9263
|
-
|
|
9264
|
-
|
|
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
|
+
});
|
|
9265
10032
|
}
|
|
9266
|
-
|
|
9267
|
-
|
|
9268
|
-
|
|
9269
|
-
|
|
9270
|
-
|
|
9271
|
-
|
|
9272
|
-
|
|
9273
|
-
|
|
9274
|
-
|
|
9275
|
-
|
|
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',
|
|
9276
10043
|
};
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
this.
|
|
9280
|
-
this.
|
|
10044
|
+
}
|
|
10045
|
+
resetSchemaPrefsToDefaults() {
|
|
10046
|
+
this.notifyIfOutdated = 'both';
|
|
10047
|
+
this.snoozeMs = 24 * 60 * 60 * 1000;
|
|
10048
|
+
this.autoOpenSettingsOnOutdated = false;
|
|
9281
10049
|
}
|
|
9282
10050
|
// Public method for template access
|
|
9283
10051
|
retryInitialization() {
|
|
@@ -9566,7 +10334,7 @@ class PraxisDynamicForm {
|
|
|
9566
10334
|
}
|
|
9567
10335
|
}
|
|
9568
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 });
|
|
9569
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PraxisDynamicForm, isStandalone: true, selector: "praxis-dynamic-form", inputs: { resourcePath: "resourcePath", resourceId: "resourceId", editorialContext: "editorialContext", mode: "mode", config: "config", schemaSource: "schemaSource", editModeEnabled: "editModeEnabled", formId: "formId", componentInstanceId: "componentInstanceId", layout: "layout", backConfig: "backConfig", hooks: "hooks", removeEmptyContainersOnSave: "removeEmptyContainersOnSave", reactiveValidation: "reactiveValidation", reactiveValidationDebounceMs: "reactiveValidationDebounceMs", notifyIfOutdated: "notifyIfOutdated", snoozeMs: "snoozeMs", autoOpenSettingsOnOutdated: "autoOpenSettingsOnOutdated", readonlyModeGlobal: "readonlyModeGlobal", disabledModeGlobal: "disabledModeGlobal", presentationModeGlobal: "presentationModeGlobal", visibleGlobal: "visibleGlobal", customEndpoints: "customEndpoints" }, outputs: { formSubmit: "formSubmit", formCancel: "formCancel", formReset: "formReset", configChange: "configChange", formReady: "formReady", valueChange: "valueChange", syncCompleted: "syncCompleted", initializationError: "initializationError", loadingStateChange: "loadingStateChange", editModeEnabledChange: "editModeEnabledChange", customAction: "customAction", actionConfirmation: "actionConfirmation", schemaStatusChange: "schemaStatusChange", fieldRenderError: "fieldRenderError", widgetEvent: "widgetEvent" }, viewQueries: [{ propertyName: "formHost", first: true, predicate: ["formHost"], descendants: true }, { propertyName: "fieldLoaders", predicate: DynamicFieldLoaderDirective, descendants: true }], usesOnChanges: true, ngImport: i0, template: "@if (isLoading) {\n<!-- Loading State -->\n<div class=\"form-loading\">\n <mat-progress-spinner diameter=\"40\"></mat-progress-spinner>\n <p>Carregando formul\u00E1rio...</p>\n</div>\n} @else if (initializationStatus === 'error') {\n<!-- Error State -->\n<div class=\"form-error\">\n <mat-icon color=\"warn\" [praxisIcon]=\"'error'\"></mat-icon>\n <h3>{{ getErrorTitle() }}</h3>\n <p>{{ currentErrorMessage }}</p>\n @if (isRecoverable) {\n <button mat-stroked-button (click)=\"retryInitialization()\">\n <mat-icon [praxisIcon]=\"'refresh'\"></mat-icon>\n Tentar Novamente\n </button>\n }\n <button mat-button (click)=\"showDetailedError()\" class=\"show-details\">\n Ver Detalhes T\u00E9cnicos\n </button>\n <!-- Permitir corre\u00E7\u00E3o do resourcePath diretamente do estado de erro -->\n <button mat-flat-button color=\"primary\" (click)=\"openQuickConnect()\" class=\"connect-action\">\n <mat-icon [praxisIcon]=\"'bolt'\"></mat-icon>\n Conectar a recurso\n </button>\n</div>\n} @else if (initializationStatus === 'success') {\n<!-- Inline banner for schema change (only in edit mode) -->\n@if (shouldShowOutdatedInline()) {\n<div class=\"pfx-form-info-banner\" role=\"status\" aria-live=\"polite\">\n <div class=\"text\">O schema do servidor mudou. Reconciliar agora?</div>\n <div class=\"actions\">\n <button mat-stroked-button color=\"primary\" (click)=\"openConfigEditor()\">\n <mat-icon [praxisIcon]=\"'sync'\"></mat-icon>\n Reconciliar\n </button>\n <button mat-button (click)=\"onSnoozeOutdated()\">Lembrar depois</button>\n <button mat-button (click)=\"onIgnoreOutdated()\">Ignorar</button>\n </div>\n</div>\n}\n\n<!-- Configuration Controls -->\n@if (shouldShowConfigControls && editModeEnabled) {\n<div class=\"form-config-controls\">\n <praxis-ai-assistant [adapter]=\"aiAdapter\"></praxis-ai-assistant>\n <button type=\"button\" mat-icon-button (click)=\"openConfigEditor()\" [disabled]=\"isLoading\" class=\"config-button\"\n [matBadge]=\"schemaOutdated ? '!' : ''\" [matBadgeHidden]=\"!schemaOutdated\" matBadgeColor=\"warn\" matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configurar formul\u00E1rio'\">\n <mat-icon [praxisIcon]=\"'settings'\"></mat-icon>\n </button>\n <button type=\"button\" mat-icon-button (click)=\"disconnect()\" matTooltip=\"Desconectar da fonte de dados\"\n [disabled]=\"isLoading\">\n <mat-icon [praxisIcon]=\"'link_off'\"></mat-icon>\n </button>\n</div>\n}\n\n<!-- Form Content -->\n@if (!resourcePath && (!config.sections || config.sections.length === 0)) {\n<praxis-empty-state-card icon=\"link\" [title]=\"'Conecte o formul\u00E1rio \u00E0 fonte de dados'\"\n [description]=\"'Informe a rota do recurso da API para gerar automaticamente os campos do formul\u00E1rio.'\"\n [primaryAction]=\"{ label: 'Conectar \u00E0 fonte de dados', icon: 'bolt', action: openQuickConnect.bind(this) }\"></praxis-empty-state-card>\n}\n<form #formHost (ngSubmit)=\"onSubmit()\" [attr.aria-busy]=\"submitting ? 'true' : null\"\n [attr.aria-label]=\"'Formul\u00E1rio ' + (config.metadata?.version || '')\" [class.canvas-mode-enabled]=\"editModeEnabled\"\n [class.presentation-mode]=\"effectivePresentation\" [class.readonly-mode]=\"effectiveReadonly\"\n [class.pfx-mounting]=\"isMounting\" [formGroup]=\"form\"\n [ngClass]=\"{\n 'pres-compact': presentationVars.compact,\n 'pres-label-left': presentationVars.labelPosition === 'left',\n 'pres-label-above': presentationVars.labelPosition === 'above'\n }\" [style.--pfx-pres-label-align]=\"presentationVars.labelAlign\"\n [style.--pfx-pres-label-size]=\"presentationVars.labelSize\"\n [style.--pfx-pres-label-width]=\"presentationVars.labelWidth\"\n [style.--pfx-pres-row-gap]=\"presentationVars.density === 'compact' ? '6px' : (presentationVars.density === 'cozy' ? '8px' : '10px')\"\n [style.--pfx-pres-row-padding]=\"presentationVars.density === 'compact' ? '2px 0' : (presentationVars.density === 'cozy' ? '6px 0' : '8px 0')\"\n [style.--pfx-pres-value-align]=\"presentationVars.valueAlign\"\n [style.--pfx-pres-value-size]=\"presentationVars.valueSize\"\n [style.--pdx-form-mount-duration]=\"getMountDurationVar()\"\n [style.--pdx-form-mount-offset]=\"getMountOffsetVar()\"\n [style.--pdx-form-mount-stagger]=\"getMountStaggerVar()\"\n class=\"praxis-dynamic-form\">\n <praxis-canvas-toolbar (editMetadata)=\"openSelectedElementEditor()\" (selectPath)=\"onSelectPath($event)\"\n (moveUp)=\"onToolbarMove('up')\" (moveDown)=\"onToolbarMove('down')\" (toggleReadonly)=\"onToolbarToggleReadonly()\"\n (toggleRequired)=\"onToolbarToggleRequired()\" (toggleHidden)=\"onToolbarToggleHidden()\"\n (toggleDisabled)=\"onToolbarToggleDisabled()\" (requestClose)=\"onToolbarRequestClose()\"\n *ngIf=\"editModeEnabled && selectedElement\" [selectedElement]=\"selectedElement\"\n [style.transform]=\"toolbarTransform\"></praxis-canvas-toolbar>\n\n @if (formBlocksBefore.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before\" data-editorial-placement=\"before\">\n @for (block of formBlocksBefore; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('before', $event)\">\n </ng-container>\n }\n </section>\n }\n\n @if (actionPlacement === 'top' && !(effectivePresentation || effectiveReadonly)) {\n <praxis-form-actions data-actions-placement=\"top\" [actions]=\"config.actions\" [isSubmitting]=\"submitting\"\n [formIsValid]=\"form.valid\" [submitError]=\"submitError\" [formId]=\"formId\" [actionOverrides]=\"actionRuleProps\"\n (action)=\"onFormAction($event)\"></praxis-form-actions>\n }\n\n @for (section of config.sections; track (section.id ?? $index); let sectionIndex = $index;\n let last = $last) {\n <div class=\"section-drop-wrapper\" [attr.data-section-id]=\"section.id\">\n @if (isSectionVisible(section)) {\n <div #sectionEl class=\"form-section canvas-element\" data-canvas-type=\"section\" [attr.data-section-id]=\"section.id\"\n [attr.data-section-index]=\"sectionIndex\" (mouseenter)=\"onElementMouseEnter($event, 'section', section, sectionEl)\"\n (mouseleave)=\"onElementMouseLeave($event)\" (click)=\"onElementClick($event, 'section', section, sectionEl)\"\n [class.selected]=\"selectedElement?.domElement === sectionEl\"\n [class.hovered]=\"hoveredElement?.domElement === sectionEl && selectedElement?.domElement !== sectionEl\"\n [style.marginBottom.px]=\"!last ? getSectionGapBottom(section) : null\" [ngClass]=\"getSectionClasses(section)\"\n [ngStyle]=\"getSectionStyles(section)\">\n <div class=\"section-heading\">\n <div class=\"section-heading-text\" [matTooltip]=\"getSectionHeaderTooltip(section) || null\"\n [matTooltipDisabled]=\"!getSectionHeaderTooltip(section)\">\n @if (getSectionTitle(section)) {\n <h3 class=\"section-title\" [class.title-large]=\"getSectionTitleStyle(section) === 'titleLarge'\"\n [class.title-medium]=\"getSectionTitleStyle(section) === 'titleMedium'\"\n [class.title-small]=\"getSectionTitleStyle(section) === 'titleSmall'\"\n [class.headline-small]=\"getSectionTitleStyle(section) === 'headlineSmall'\"\n [style.marginBottom.px]=\"getSectionTitleGapBottom(section) ?? 20\" [style.color]=\"getSectionTitleColor(section) || null\"\n [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\"\n [attr.id]=\"sectionPanelId(section, sectionIndex) + '-title'\">\n @if (getSectionIcon(section)) {\n <mat-icon class=\"section-title-icon\" aria-hidden=\"true\" [praxisIcon]=\"getSectionIcon(section)\"></mat-icon>\n }\n {{ getSectionTitle(section) }}\n @if (editModeEnabled) {\n <button type=\"button\" class=\"inline-edit-btn\" mat-icon-button (click)=\"openSectionEditor(section, 'title')\"\n aria-label=\"Editar t\u00EDtulo da se\u00E7\u00E3o\" matTooltip=\"Editar t\u00EDtulo\">\n <mat-icon>edit</mat-icon>\n </button>\n }\n </h3>\n }\n @if (getSectionDescription(section)) {\n <p class=\"section-description\" [class.body-large]=\"getSectionDescriptionStyle(section) === 'bodyLarge'\"\n [class.body-medium]=\"getSectionDescriptionStyle(section) === 'bodyMedium'\"\n [class.body-small]=\"getSectionDescriptionStyle(section) === 'bodySmall'\"\n [style.marginBottom.px]=\"getSectionDescriptionGapBottom(section) ?? 8\" [style.color]=\"getSectionDescriptionColor(section) || null\"\n [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\">\n {{ getSectionDescription(section) }}\n @if (editModeEnabled) {\n <button type=\"button\" class=\"inline-edit-btn\" mat-icon-button\n (click)=\"openSectionEditor(section, 'description')\" aria-label=\"Editar descri\u00E7\u00E3o da se\u00E7\u00E3o\"\n matTooltip=\"Editar descri\u00E7\u00E3o\">\n <mat-icon>edit</mat-icon>\n </button>\n }\n </p>\n }\n </div>\n @if (isSectionCollapsible(section)) {\n <button type=\"button\" class=\"section-collapse-btn\" mat-icon-button\n (click)=\"toggleSectionCollapse($event, section)\" [attr.aria-expanded]=\"!isSectionCollapsed(section)\"\n [attr.aria-controls]=\"sectionPanelId(section, sectionIndex)\"\n [attr.aria-label]=\"isSectionCollapsed(section) ? 'Expandir se\u00E7\u00E3o' : 'Recolher se\u00E7\u00E3o'\">\n <mat-icon [praxisIcon]=\"isSectionCollapsed(section) ? 'expand_more' : 'expand_less'\"></mat-icon>\n </button>\n }\n </div>\n\n <div class=\"section-body\" [class.collapsed]=\"isSectionCollapsed(section)\"\n [attr.id]=\"sectionPanelId(section, sectionIndex)\"\n [attr.aria-labelledby]=\"getSectionTitle(section) ? sectionPanelId(section, sectionIndex) + '-title' : null\">\n @if (!isSectionCollapsed(section)) {\n @for (row of section.rows; track (row.id ?? $index); let rowIndex = $index) {\n @if (isRowVisible(row)) {\n <div class=\"row-drop-wrapper\" [attr.data-section-id]=\"section.id\">\n <div #rowEl class=\"form-row grid-12 canvas-element\" data-canvas-type=\"row\" [attr.data-row-index]=\"rowIndex\"\n [attr.data-section-id]=\"section.id\" [attr.data-row-id]=\"row.id\" [style.--pfx-grid-gap.px]=\"getRowGap(row)\"\n [style.--pfx-field-gap.px]=\"getRowRowGap(row)\"\n [style.--pfx-mount-index]=\"rowIndex\"\n [style.marginBottom.px]=\"rowIndex < section.rows.length - 1 ? (getRowRowGap(row) ?? null) : null\"\n [ngClass]=\"getRowClasses(row)\" [ngStyle]=\"getRowStyles(row)\"\n (mouseenter)=\"onElementMouseEnter($event, 'row', row, rowEl)\" (mouseleave)=\"onElementMouseLeave($event)\"\n (click)=\"onElementClick($event, 'row', row, rowEl)\" [class.selected]=\"selectedElement?.domElement === rowEl\"\n [class.hovered]=\"hoveredElement?.domElement === rowEl && selectedElement?.domElement !== rowEl\">\n @for (column of row.columns; track (column.id ?? $index); let colIndex = $index) {\n @if (isColumnVisible(column)) {\n <div class=\"column-drop-wrapper\" [attr.data-section-id]=\"section.id\" [attr.data-row-id]=\"row.id\">\n <div #colEl class=\"form-column canvas-element\" [ngClass]=\"getColumnClasses(column)\"\n [style.padding.px]=\"getColumnPadding(column)\" [style.--pfx-field-gap.px]=\"getRowRowGap(row)\"\n [ngStyle]=\"getColumnStyles(column)\" [attr.data-testid]=\"column.testId || null\"\n [attr.data-row-gap]=\"getRowRowGap(row)\" data-canvas-type=\"column\" [attr.data-column-index]=\"colIndex\"\n [attr.data-row-index]=\"rowIndex\" [attr.data-section-id]=\"section.id\" [attr.data-row-id]=\"row.id\"\n [attr.data-column-id]=\"column.id\" (mouseenter)=\"onElementMouseEnter($event, 'column', column, colEl)\"\n (mouseleave)=\"onElementMouseLeave($event)\" (click)=\"onElementClick($event, 'column', column, colEl)\"\n [class.selected]=\"selectedElement?.domElement === colEl\"\n [class.hovered]=\"hoveredElement?.domElement === colEl && selectedElement?.domElement !== colEl\">\n <ng-container dynamicFieldLoader [fields]=\"getColumnFields(column)\" [formGroup]=\"form\"\n [readonlyMode]=\"readonlyModeGlobal === null ? null : readonlyModeGlobal\"\n [disabledMode]=\"disabledModeGlobal === null ? null : disabledModeGlobal\"\n [presentationMode]=\"presentationForLoader\" [visible]=\"visibleGlobal === null ? null : visibleGlobal\"\n [canvasMode]=\"editModeEnabled\" (canvasMouseEnter)=\"onFieldMouseEnter($event)\"\n (canvasMouseLeave)=\"onFieldMouseLeave($event)\" (canvasClick)=\"onFieldClick($event)\"\n (renderError)=\"onFieldRenderError($event)\">\n </ng-container>\n </div>\n </div>\n } }\n </div>\n </div>\n }\n }\n } @else {\n <div class=\"section-collapsed-placeholder\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'unfold_more'\"></mat-icon>\n <span>{{ getSectionCollapsedSummary(section) }}</span>\n </div>\n }\n @if (last && formBlocksBeforeActions.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before-actions\" data-editorial-placement=\"beforeActions\">\n @for (block of formBlocksBeforeActions; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('beforeActions', $event)\">\n </ng-container>\n }\n </section>\n }\n @if (actionPlacement === 'insideLastSection' && last && !(effectivePresentation\n || effectiveReadonly)) {\n <praxis-form-actions data-actions-placement=\"insideLastSection\" [actions]=\"config.actions\"\n [isSubmitting]=\"submitting\" [formIsValid]=\"form.valid\" [submitError]=\"submitError\" [formId]=\"formId\"\n [actionOverrides]=\"actionRuleProps\"\n (action)=\"onFormAction($event)\"></praxis-form-actions>\n }\n </div>\n <!-- Overlay de bloqueio durante submiss\u00E3o -->\n @if (submitting) {\n <div class=\"form-blocking-overlay\" aria-live=\"polite\">\n <mat-progress-spinner diameter=\"40\" color=\"primary\"></mat-progress-spinner>\n <p>{{ config.messages?.loading?.submit || 'Salvando...' }}</p>\n </div>\n }\n </div>\n\n @if (editModeEnabled && selectedElement?.domElement === sectionEl) {\n <div class=\"add-section-container\">\n <div class=\"add-section-line\"></div>\n <button mat-fab color=\"primary\" aria-label=\"Adicionar nova se\u00E7\u00E3o\" (click)=\"addNewSectionAfter(sectionIndex)\"\n matTooltip=\"Adicionar nova se\u00E7\u00E3o aqui\">\n <mat-icon [praxisIcon]=\"'add'\"></mat-icon>\n </button>\n <div class=\"add-section-line\"></div>\n </div>\n }\n }\n </div>\n }\n\n @if (actionPlacement !== 'insideLastSection' && formBlocksBeforeActions.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before-actions\" data-editorial-placement=\"beforeActions\">\n @for (block of formBlocksBeforeActions; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('beforeActions', $event)\">\n </ng-container>\n }\n </section>\n }\n\n @if (actionPlacement === 'afterSections' && !(effectivePresentation || effectiveReadonly)) {\n <praxis-form-actions data-actions-placement=\"afterSections\" [actions]=\"config.actions\" [isSubmitting]=\"submitting\"\n [formIsValid]=\"form.valid\" [submitError]=\"submitError\" [formId]=\"formId\"\n [actionOverrides]=\"actionRuleProps\"\n (action)=\"onFormAction($event)\"></praxis-form-actions>\n }\n\n @if (formBlocksAfter.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-after\" data-editorial-placement=\"after\">\n @for (block of formBlocksAfter; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('after', $event)\">\n </ng-container>\n }\n </section>\n }\n</form>\n@if (!editModeEnabled && mode === 'view') {\n<div class=\"ai-floating-assistant\">\n <praxis-ai-assistant [adapter]=\"aiAdapter\"></praxis-ai-assistant>\n</div>\n}\n}\n", styles: ["@charset \"UTF-8\";:host{display:block;position:relative}.form-config-controls{position:absolute;top:4px;right:4px;display:flex;gap:.25rem;z-index:100;background:transparent;padding:0;border:0;min-width:0;justify-content:flex-end;pointer-events:none}.form-config-controls>*{pointer-events:auto}.form-config-controls .mat-icon-button{--mdc-icon-button-state-layer-size: 36px;width:36px;height:36px;color:var(--md-sys-color-on-surface-variant)}.form-config-controls .mat-icon-button:hover{color:var(--md-sys-color-primary);background:var(--md-sys-color-primary-container)}.ai-floating-assistant{position:absolute;top:0;left:0;z-index:2002;pointer-events:none}.ai-floating-assistant{inset:auto 12px 12px auto;z-index:120}.ai-floating-assistant praxis-ai-assistant{pointer-events:auto;display:inline-flex}.ai-floating-assistant .ai-trigger-btn{box-shadow:var(--md-sys-elevation-level2)}.config-button{color:var(--md-sys-color-primary)}.form-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3rem;text-align:center;color:var(--md-sys-color-on-surface);gap:1rem}.form-loading p{margin:0;font-size:.875rem;opacity:.7}.form-error{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3rem;text-align:center;color:var(--md-sys-color-error);gap:1rem;border:1px solid var(--md-sys-color-error);border-radius:8px;background-color:var(--md-sys-color-error-container);margin:1rem}.form-error h3{margin:0;color:var(--md-sys-color-on-error-container)}.form-error p{margin:0;color:var(--md-sys-color-on-error-container);opacity:.8}.form-error button{margin-top:.5rem}.praxis-dynamic-form{display:flex;flex-direction:column;transition:all .3s ease;position:relative}.praxis-dynamic-form.presentation-mode .form-row,.praxis-dynamic-form.readonly-mode .form-row{gap:.5rem;margin-bottom:.5rem}.praxis-dynamic-form.presentation-mode .form-section,.praxis-dynamic-form.readonly-mode .form-section{padding:.75rem .875rem}.praxis-dynamic-form.pres-compact .form-row{gap:.35rem;margin-bottom:.35rem}.praxis-dynamic-form.pres-compact .form-section{padding:.5rem .75rem}.praxis-dynamic-form.pres-label-left .praxis-presentation{display:grid;grid-template-columns:var(--pfx-presentation-label-w, 220px) 1fr;align-items:baseline;column-gap:10px}.praxis-dynamic-form.pres-label-left .praxis-presentation__label{text-align:right;margin-bottom:0}.form-section{border:1px solid var(--pfx-form-stroke, var(--md-sys-color-outline-variant));border-radius:8px;padding:1rem;background:var(--pfx-form-section-surface, var(--md-sys-color-surface-container));transition:all .2s ease;position:relative}.section-drop-wrapper>.form-section{margin-bottom:var(--pfx-section-gap, 20px)}.section-drop-wrapper:last-of-type>.form-section{margin-bottom:0}.praxis-dynamic-form>praxis-form-actions[data-actions-placement=afterSections] .form-actions{margin-top:var(--pfx-actions-gap-top, var(--pfx-section-gap, 20px))}.section-title{margin:0 0 var(--pfx-section-title-mb, 20px) 0;font-size:1.05rem;font-weight:500;color:var(--md-sys-color-on-surface)}.section-heading{display:flex;align-items:flex-start;gap:8px;margin-bottom:8px}.section-heading-text{flex:1 1 auto;min-width:0}.section-collapse-btn{margin-left:4px;color:var(--md-sys-color-on-surface-variant)}.section-title.title-large{font:var(--mdc-typography-title-large, 500 22px/28px system-ui)}.section-title.title-medium{font:var(--mdc-typography-title-medium, 500 16px/24px system-ui)}.section-title.title-small{font:var(--mdc-typography-title-small, 500 14px/20px system-ui)}.section-title.headline-small{font:var(--mdc-typography-headline-small, 600 24px/32px system-ui)}.section-description{margin:0 0 8px;font-size:.875rem;color:var(--md-sys-color-on-surface-variant)}.section-description.body-large{font:var(--mdc-typography-body-large, 400 16px/24px system-ui)}.section-description.body-medium{font:var(--mdc-typography-body-medium, 400 14px/20px system-ui)}.section-description.body-small{font:var(--mdc-typography-body-small, 400 12px/16px system-ui)}.section-title.align-center,.section-description.align-center{text-align:center}.inline-edit-btn{margin-left:6px;vertical-align:middle;--mdc-icon-button-size: 28px;--mdc-icon-button-icon-size: 16px}.inline-edit-btn mat-icon{font-size:16px;width:16px;height:16px}.section-body.collapsed{border:1px dashed var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low);border-radius:6px;padding:8px 10px}.section-collapsed-placeholder{display:flex;align-items:center;gap:8px;color:var(--md-sys-color-on-surface-variant);font-size:.95rem}.section-collapsed-placeholder mat-icon{font-size:20px;width:20px;height:20px}.form-row{display:flex;gap:1rem;margin-bottom:1rem;transition:all .2s ease;border-radius:6px;position:relative}.praxis-dynamic-form.pfx-mounting .form-row{opacity:0;transform:translateY(var(--pdx-form-mount-offset, 6px));animation:pdxFormMount var(--pdx-form-mount-duration, .16s) ease-out both;animation-delay:calc(var(--pfx-mount-index, 0) * var(--pdx-form-mount-stagger, 20ms))}@media(prefers-reduced-motion:reduce){.praxis-dynamic-form.pfx-mounting .form-row{animation:none;opacity:1;transform:none}}@keyframes pdxFormMount{to{opacity:1;transform:translateY(0)}}.praxis-dynamic-form .mat-mdc-form-field{width:100%;margin-bottom:var(--pfx-field-gap, 10px)}.praxis-dynamic-form [data-field-type=input],.praxis-dynamic-form [data-field-type=textarea],.praxis-dynamic-form [data-field-type=email],.praxis-dynamic-form [data-field-type=password],.praxis-dynamic-form [data-field-type=url],.praxis-dynamic-form [data-field-type=search],.praxis-dynamic-form [data-field-type=phone],.praxis-dynamic-form [data-field-type=numericTextBox],.praxis-dynamic-form [data-field-type=currency],.praxis-dynamic-form [data-field-type=cpfCnpj],.praxis-dynamic-form [data-field-type=date],.praxis-dynamic-form [data-field-type=dateInput],.praxis-dynamic-form [data-field-type=dateRange],.praxis-dynamic-form [data-field-type=dateTimeLocal],.praxis-dynamic-form [data-field-type=time],.praxis-dynamic-form [data-field-type=timePicker],.praxis-dynamic-form [data-field-type=timeRange],.praxis-dynamic-form [data-field-type=month],.praxis-dynamic-form [data-field-type=week],.praxis-dynamic-form [data-field-type=yearInput],.praxis-dynamic-form [data-field-type=select],.praxis-dynamic-form [data-field-type=multi-select],.praxis-dynamic-form [data-field-type=searchable-select],.praxis-dynamic-form [data-field-type=async-select],.praxis-dynamic-form [data-field-type=autocomplete],.praxis-dynamic-form [data-field-type=tree-select],.praxis-dynamic-form [data-field-type=multi-select-tree],.praxis-dynamic-form [data-field-type=priceRange],.praxis-dynamic-form [data-field-type=file-upload]{display:block;width:100%;min-width:0}.praxis-dynamic-form .mat-mdc-form-field-subscript-wrapper{min-height:var(--pfx-subscript-min-h, 22px)}.form-row:last-child{margin-bottom:0}.form-column{display:grid;align-content:start;gap:var(--pfx-field-gap, 10px);flex:1;min-width:0;transition:all .2s ease;border-radius:4px;position:relative}.form-row.grid-12{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:var(--pfx-grid-gap, 16px)}.align-start{align-self:flex-start}.align-center{align-self:center}.align-end{align-self:flex-end}.align-stretch{align-self:stretch}.span-xs-1{grid-column:span 1}.span-xs-2{grid-column:span 2}.span-xs-3{grid-column:span 3}.span-xs-4{grid-column:span 4}.span-xs-5{grid-column:span 5}.span-xs-6{grid-column:span 6}.span-xs-7{grid-column:span 7}.span-xs-8{grid-column:span 8}.span-xs-9{grid-column:span 9}.span-xs-10{grid-column:span 10}.span-xs-11{grid-column:span 11}.span-xs-12{grid-column:span 12}.offset-xs-0{margin-left:0%}.offset-xs-1{margin-left:calc(1 / 12 * 100%)}.offset-xs-2{margin-left:calc(2 / 12 * 100%)}.offset-xs-3{margin-left:25%}.offset-xs-4{margin-left:calc(4 / 12 * 100%)}.offset-xs-5{margin-left:calc(5 / 12 * 100%)}.offset-xs-6{margin-left:50%}.offset-xs-7{margin-left:calc(7 / 12 * 100%)}.offset-xs-8{margin-left:calc(8 / 12 * 100%)}.offset-xs-9{margin-left:75%}.offset-xs-10{margin-left:calc(10 / 12 * 100%)}.offset-xs-11{margin-left:calc(11 / 12 * 100%)}.order-xs--12{order:-12}.order-xs--11{order:-11}.order-xs--10{order:-10}.order-xs--9{order:-9}.order-xs--8{order:-8}.order-xs--7{order:-7}.order-xs--6{order:-6}.order-xs--5{order:-5}.order-xs--4{order:-4}.order-xs--3{order:-3}.order-xs--2{order:-2}.order-xs--1{order:-1}.order-xs-0{order:0}.order-xs-1{order:1}.order-xs-2{order:2}.order-xs-3{order:3}.order-xs-4{order:4}.order-xs-5{order:5}.order-xs-6{order:6}.order-xs-7{order:7}.order-xs-8{order:8}.order-xs-9{order:9}.order-xs-10{order:10}.order-xs-11{order:11}.order-xs-12{order:12}.hidden-xs{display:none}@media(min-width:600px){.span-sm-1{grid-column:span 1}.span-sm-2{grid-column:span 2}.span-sm-3{grid-column:span 3}.span-sm-4{grid-column:span 4}.span-sm-5{grid-column:span 5}.span-sm-6{grid-column:span 6}.span-sm-7{grid-column:span 7}.span-sm-8{grid-column:span 8}.span-sm-9{grid-column:span 9}.span-sm-10{grid-column:span 10}.span-sm-11{grid-column:span 11}.span-sm-12{grid-column:span 12}.offset-sm-0{margin-left:0%}.offset-sm-1{margin-left:calc(1 / 12 * 100%)}.offset-sm-2{margin-left:calc(2 / 12 * 100%)}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:calc(4 / 12 * 100%)}.offset-sm-5{margin-left:calc(5 / 12 * 100%)}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:calc(7 / 12 * 100%)}.offset-sm-8{margin-left:calc(8 / 12 * 100%)}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:calc(10 / 12 * 100%)}.offset-sm-11{margin-left:calc(11 / 12 * 100%)}.order-sm--12{order:-12}.order-sm--11{order:-11}.order-sm--10{order:-10}.order-sm--9{order:-9}.order-sm--8{order:-8}.order-sm--7{order:-7}.order-sm--6{order:-6}.order-sm--5{order:-5}.order-sm--4{order:-4}.order-sm--3{order:-3}.order-sm--2{order:-2}.order-sm--1{order:-1}.order-sm-0{order:0}.order-sm-1{order:1}.order-sm-2{order:2}.order-sm-3{order:3}.order-sm-4{order:4}.order-sm-5{order:5}.order-sm-6{order:6}.order-sm-7{order:7}.order-sm-8{order:8}.order-sm-9{order:9}.order-sm-10{order:10}.order-sm-11{order:11}.order-sm-12{order:12}.hidden-sm{display:none}}@media(min-width:900px){.span-md-1{grid-column:span 1}.span-md-2{grid-column:span 2}.span-md-3{grid-column:span 3}.span-md-4{grid-column:span 4}.span-md-5{grid-column:span 5}.span-md-6{grid-column:span 6}.span-md-7{grid-column:span 7}.span-md-8{grid-column:span 8}.span-md-9{grid-column:span 9}.span-md-10{grid-column:span 10}.span-md-11{grid-column:span 11}.span-md-12{grid-column:span 12}.offset-md-0{margin-left:0%}.offset-md-1{margin-left:calc(1 / 12 * 100%)}.offset-md-2{margin-left:calc(2 / 12 * 100%)}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:calc(4 / 12 * 100%)}.offset-md-5{margin-left:calc(5 / 12 * 100%)}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:calc(7 / 12 * 100%)}.offset-md-8{margin-left:calc(8 / 12 * 100%)}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:calc(10 / 12 * 100%)}.offset-md-11{margin-left:calc(11 / 12 * 100%)}.order-md--12{order:-12}.order-md--11{order:-11}.order-md--10{order:-10}.order-md--9{order:-9}.order-md--8{order:-8}.order-md--7{order:-7}.order-md--6{order:-6}.order-md--5{order:-5}.order-md--4{order:-4}.order-md--3{order:-3}.order-md--2{order:-2}.order-md--1{order:-1}.order-md-0{order:0}.order-md-1{order:1}.order-md-2{order:2}.order-md-3{order:3}.order-md-4{order:4}.order-md-5{order:5}.order-md-6{order:6}.order-md-7{order:7}.order-md-8{order:8}.order-md-9{order:9}.order-md-10{order:10}.order-md-11{order:11}.order-md-12{order:12}.hidden-md{display:none}}@media(min-width:1200px){.span-lg-1{grid-column:span 1}.span-lg-2{grid-column:span 2}.span-lg-3{grid-column:span 3}.span-lg-4{grid-column:span 4}.span-lg-5{grid-column:span 5}.span-lg-6{grid-column:span 6}.span-lg-7{grid-column:span 7}.span-lg-8{grid-column:span 8}.span-lg-9{grid-column:span 9}.span-lg-10{grid-column:span 10}.span-lg-11{grid-column:span 11}.span-lg-12{grid-column:span 12}.offset-lg-0{margin-left:0%}.offset-lg-1{margin-left:calc(1 / 12 * 100%)}.offset-lg-2{margin-left:calc(2 / 12 * 100%)}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:calc(4 / 12 * 100%)}.offset-lg-5{margin-left:calc(5 / 12 * 100%)}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:calc(7 / 12 * 100%)}.offset-lg-8{margin-left:calc(8 / 12 * 100%)}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:calc(10 / 12 * 100%)}.offset-lg-11{margin-left:calc(11 / 12 * 100%)}.order-lg--12{order:-12}.order-lg--11{order:-11}.order-lg--10{order:-10}.order-lg--9{order:-9}.order-lg--8{order:-8}.order-lg--7{order:-7}.order-lg--6{order:-6}.order-lg--5{order:-5}.order-lg--4{order:-4}.order-lg--3{order:-3}.order-lg--2{order:-2}.order-lg--1{order:-1}.order-lg-0{order:0}.order-lg-1{order:1}.order-lg-2{order:2}.order-lg-3{order:3}.order-lg-4{order:4}.order-lg-5{order:5}.order-lg-6{order:6}.order-lg-7{order:7}.order-lg-8{order:8}.order-lg-9{order:9}.order-lg-10{order:10}.order-lg-11{order:11}.order-lg-12{order:12}.hidden-lg{display:none}}@media(min-width:1536px){.span-xl-1{grid-column:span 1}.span-xl-2{grid-column:span 2}.span-xl-3{grid-column:span 3}.span-xl-4{grid-column:span 4}.span-xl-5{grid-column:span 5}.span-xl-6{grid-column:span 6}.span-xl-7{grid-column:span 7}.span-xl-8{grid-column:span 8}.span-xl-9{grid-column:span 9}.span-xl-10{grid-column:span 10}.span-xl-11{grid-column:span 11}.span-xl-12{grid-column:span 12}.offset-xl-0{margin-left:0%}.offset-xl-1{margin-left:calc(1 / 12 * 100%)}.offset-xl-2{margin-left:calc(2 / 12 * 100%)}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:calc(4 / 12 * 100%)}.offset-xl-5{margin-left:calc(5 / 12 * 100%)}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:calc(7 / 12 * 100%)}.offset-xl-8{margin-left:calc(8 / 12 * 100%)}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:calc(10 / 12 * 100%)}.offset-xl-11{margin-left:calc(11 / 12 * 100%)}.order-xl--12{order:-12}.order-xl--11{order:-11}.order-xl--10{order:-10}.order-xl--9{order:-9}.order-xl--8{order:-8}.order-xl--7{order:-7}.order-xl--6{order:-6}.order-xl--5{order:-5}.order-xl--4{order:-4}.order-xl--3{order:-3}.order-xl--2{order:-2}.order-xl--1{order:-1}.order-xl-0{order:0}.order-xl-1{order:1}.order-xl-2{order:2}.order-xl-3{order:3}.order-xl-4{order:4}.order-xl-5{order:5}.order-xl-6{order:6}.order-xl-7{order:7}.order-xl-8{order:8}.order-xl-9{order:9}.order-xl-10{order:10}.order-xl-11{order:11}.order-xl-12{order:12}.hidden-xl{display:none}}.form-blocking-overlay{position:absolute;inset:0;background:transparent;color:var(--md-sys-color-on-surface);backdrop-filter:blur(2px) saturate(103%);-webkit-backdrop-filter:blur(2px) saturate(103%);display:flex;align-items:center;justify-content:center;flex-direction:column;gap:12px;z-index:10;pointer-events:all}@media(max-width:768px){.form-row{flex-direction:column;gap:.5rem}.form-section{padding:1rem}}.canvas-mode-enabled{--canvas-hit: 14px}.canvas-mode-enabled .canvas-element{position:relative;z-index:0;border-radius:8px;outline:2px solid transparent;outline-offset:2px;transition:outline-color .2s ease,outline-style .2s ease}.canvas-mode-enabled .canvas-element:before{content:\"\";position:absolute;inset:calc(-1 * var(--canvas-hit));pointer-events:none;border-radius:inherit;background:transparent}.canvas-mode-enabled .canvas-element[data-canvas-type=section]{--outline-color: var(--md-sys-color-primary)}.canvas-mode-enabled .canvas-element[data-canvas-type=row]{--outline-color: var(--md-sys-color-secondary)}.canvas-mode-enabled .canvas-element[data-canvas-type=column],.canvas-mode-enabled .canvas-element[data-canvas-type=field]{--outline-color: var(--md-sys-color-tertiary)}.canvas-mode-enabled .canvas-element[data-canvas-type=actions]{--outline-color: var(--md-sys-color-primary)}.canvas-mode-enabled .canvas-element.hovered:not(.selected){outline-color:var(--outline-color, var(--md-sys-color-primary));outline-style:dashed}.canvas-mode-enabled .canvas-element.selected{outline-color:var(--outline-color, var(--md-sys-color-primary));outline-style:solid;box-shadow:0 0 0 2px var(--outline-color, var(--md-sys-color-primary))}.canvas-mode-enabled .canvas-element.hovered{z-index:1000}.canvas-mode-enabled .canvas-element.selected{z-index:2000}.section-drop-wrapper,.row-drop-wrapper,.column-drop-wrapper{display:contents}.add-section-container{display:flex;align-items:center;justify-content:center;padding:.5rem 0;margin:-.5rem 0;position:relative;z-index:500}.add-section-container .add-section-line{flex-grow:1;height:1px;background:repeating-linear-gradient(90deg,var(--md-sys-color-outline-variant),var(--md-sys-color-outline-variant) 4px,transparent 4px,transparent 8px)}.add-section-container button{margin:0 1rem;transform:scale(.85)}.section-title{display:flex;align-items:center;gap:8px}.section-title .section-title-icon{font-size:1.25em;line-height:1}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: DynamicFieldLoaderDirective, selector: "[dynamicFieldLoader]", inputs: ["fields", "formGroup", "enableExternalControlBinding", "itemTemplate", "debugTrace", "debugTraceLabel", "readonlyMode", "disabledMode", "presentationMode", "visible", "canvasMode"], outputs: ["componentsCreated", "fieldCreated", "fieldDestroyed", "renderError", "canvasMouseEnter", "canvasMouseLeave", "canvasClick"] }, { kind: "directive", type: DynamicWidgetLoaderDirective, selector: "[dynamicWidgetLoader]", inputs: ["dynamicWidgetLoader", "context", "strictValidation", "autoWireOutputs"], outputs: ["widgetEvent"], exportAs: ["dynamicWidgetLoader"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i15.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i9.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "ngmodule", type: MatBadgeModule }, { kind: "directive", type: i17.MatBadge, selector: "[matBadge]", inputs: ["matBadgeColor", "matBadgeOverlap", "matBadgeDisabled", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: CanvasToolbarComponent, selector: "praxis-canvas-toolbar", inputs: ["selectedElement"], outputs: ["editMetadata", "delete", "moveUp", "moveDown", "selectPath", "requestClose", "toggleReadonly", "toggleRequired", "toggleHidden", "toggleDisabled"] }, { kind: "component", type: PraxisFormActionsComponent, selector: "praxis-form-actions", inputs: ["actions", "isSubmitting", "formIsValid", "submitError", "formId", "actionOverrides"], outputs: ["action"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone"] }, { kind: "component", type: PraxisAiAssistantComponent, selector: "praxis-ai-assistant", inputs: ["adapter", "riskPolicy", "allowManualPatchEdit"] }] });
|
|
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"] }] });
|
|
9570
10338
|
}
|
|
9571
10339
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisDynamicForm, decorators: [{
|
|
9572
10340
|
type: Component,
|
|
@@ -9587,7 +10355,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
9587
10355
|
PraxisFormActionsComponent,
|
|
9588
10356
|
EmptyStateCardComponent,
|
|
9589
10357
|
PraxisAiAssistantComponent,
|
|
9590
|
-
], template: "@if (isLoading) {\n<!-- Loading State -->\n<div class=\"form-loading\">\n <mat-progress-spinner diameter=\"40\"></mat-progress-spinner>\n <p>Carregando formul\u00E1rio...</p>\n</div>\n} @else if (initializationStatus === 'error') {\n<!-- Error State -->\n<div class=\"form-error\">\n <mat-icon color=\"warn\" [praxisIcon]=\"'error'\"></mat-icon>\n <h3>{{ getErrorTitle() }}</h3>\n <p>{{ currentErrorMessage }}</p>\n @if (isRecoverable) {\n <button mat-stroked-button (click)=\"retryInitialization()\">\n <mat-icon [praxisIcon]=\"'refresh'\"></mat-icon>\n Tentar Novamente\n </button>\n }\n <button mat-button (click)=\"showDetailedError()\" class=\"show-details\">\n Ver Detalhes T\u00E9cnicos\n </button>\n <!-- Permitir corre\u00E7\u00E3o do resourcePath diretamente do estado de erro -->\n <button mat-flat-button color=\"primary\" (click)=\"openQuickConnect()\" class=\"connect-action\">\n <mat-icon [praxisIcon]=\"'bolt'\"></mat-icon>\n Conectar a recurso\n </button>\n</div>\n} @else if (initializationStatus === 'success') {\n<!-- Inline banner for schema change (only in edit mode) -->\n@if (shouldShowOutdatedInline()) {\n<div class=\"pfx-form-info-banner\" role=\"status\" aria-live=\"polite\">\n <div class=\"text\">O schema do servidor mudou. Reconciliar agora?</div>\n <div class=\"actions\">\n <button mat-stroked-button color=\"primary\" (click)=\"openConfigEditor()\">\n <mat-icon [praxisIcon]=\"'sync'\"></mat-icon>\n Reconciliar\n </button>\n <button mat-button (click)=\"onSnoozeOutdated()\">Lembrar depois</button>\n <button mat-button (click)=\"onIgnoreOutdated()\">Ignorar</button>\n </div>\n</div>\n}\n\n<!-- Configuration Controls -->\n@if (shouldShowConfigControls && editModeEnabled) {\n<div class=\"form-config-controls\">\n <praxis-ai-assistant [adapter]=\"aiAdapter\"></praxis-ai-assistant>\n <button type=\"button\" mat-icon-button (click)=\"openConfigEditor()\" [disabled]=\"isLoading\" class=\"config-button\"\n [matBadge]=\"schemaOutdated ? '!' : ''\" [matBadgeHidden]=\"!schemaOutdated\" matBadgeColor=\"warn\" matBadgeSize=\"small\"\n matBadgePosition=\"above after\"\n [matTooltip]=\"schemaOutdated ? 'Schema do servidor mudou \u2014 Reconciliar' : 'Configurar formul\u00E1rio'\">\n <mat-icon [praxisIcon]=\"'settings'\"></mat-icon>\n </button>\n <button type=\"button\" mat-icon-button (click)=\"disconnect()\" matTooltip=\"Desconectar da fonte de dados\"\n [disabled]=\"isLoading\">\n <mat-icon [praxisIcon]=\"'link_off'\"></mat-icon>\n </button>\n</div>\n}\n\n<!-- Form Content -->\n@if (!resourcePath && (!config.sections || config.sections.length === 0)) {\n<praxis-empty-state-card icon=\"link\" [title]=\"'Conecte o formul\u00E1rio \u00E0 fonte de dados'\"\n [description]=\"'Informe a rota do recurso da API para gerar automaticamente os campos do formul\u00E1rio.'\"\n [primaryAction]=\"{ label: 'Conectar \u00E0 fonte de dados', icon: 'bolt', action: openQuickConnect.bind(this) }\"></praxis-empty-state-card>\n}\n<form #formHost (ngSubmit)=\"onSubmit()\" [attr.aria-busy]=\"submitting ? 'true' : null\"\n [attr.aria-label]=\"'Formul\u00E1rio ' + (config.metadata?.version || '')\" [class.canvas-mode-enabled]=\"editModeEnabled\"\n [class.presentation-mode]=\"effectivePresentation\" [class.readonly-mode]=\"effectiveReadonly\"\n [class.pfx-mounting]=\"isMounting\" [formGroup]=\"form\"\n [ngClass]=\"{\n 'pres-compact': presentationVars.compact,\n 'pres-label-left': presentationVars.labelPosition === 'left',\n 'pres-label-above': presentationVars.labelPosition === 'above'\n }\" [style.--pfx-pres-label-align]=\"presentationVars.labelAlign\"\n [style.--pfx-pres-label-size]=\"presentationVars.labelSize\"\n [style.--pfx-pres-label-width]=\"presentationVars.labelWidth\"\n [style.--pfx-pres-row-gap]=\"presentationVars.density === 'compact' ? '6px' : (presentationVars.density === 'cozy' ? '8px' : '10px')\"\n [style.--pfx-pres-row-padding]=\"presentationVars.density === 'compact' ? '2px 0' : (presentationVars.density === 'cozy' ? '6px 0' : '8px 0')\"\n [style.--pfx-pres-value-align]=\"presentationVars.valueAlign\"\n [style.--pfx-pres-value-size]=\"presentationVars.valueSize\"\n [style.--pdx-form-mount-duration]=\"getMountDurationVar()\"\n [style.--pdx-form-mount-offset]=\"getMountOffsetVar()\"\n [style.--pdx-form-mount-stagger]=\"getMountStaggerVar()\"\n class=\"praxis-dynamic-form\">\n <praxis-canvas-toolbar (editMetadata)=\"openSelectedElementEditor()\" (selectPath)=\"onSelectPath($event)\"\n (moveUp)=\"onToolbarMove('up')\" (moveDown)=\"onToolbarMove('down')\" (toggleReadonly)=\"onToolbarToggleReadonly()\"\n (toggleRequired)=\"onToolbarToggleRequired()\" (toggleHidden)=\"onToolbarToggleHidden()\"\n (toggleDisabled)=\"onToolbarToggleDisabled()\" (requestClose)=\"onToolbarRequestClose()\"\n *ngIf=\"editModeEnabled && selectedElement\" [selectedElement]=\"selectedElement\"\n [style.transform]=\"toolbarTransform\"></praxis-canvas-toolbar>\n\n @if (formBlocksBefore.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before\" data-editorial-placement=\"before\">\n @for (block of formBlocksBefore; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('before', $event)\">\n </ng-container>\n }\n </section>\n }\n\n @if (actionPlacement === 'top' && !(effectivePresentation || effectiveReadonly)) {\n <praxis-form-actions data-actions-placement=\"top\" [actions]=\"config.actions\" [isSubmitting]=\"submitting\"\n [formIsValid]=\"form.valid\" [submitError]=\"submitError\" [formId]=\"formId\" [actionOverrides]=\"actionRuleProps\"\n (action)=\"onFormAction($event)\"></praxis-form-actions>\n }\n\n @for (section of config.sections; track (section.id ?? $index); let sectionIndex = $index;\n let last = $last) {\n <div class=\"section-drop-wrapper\" [attr.data-section-id]=\"section.id\">\n @if (isSectionVisible(section)) {\n <div #sectionEl class=\"form-section canvas-element\" data-canvas-type=\"section\" [attr.data-section-id]=\"section.id\"\n [attr.data-section-index]=\"sectionIndex\" (mouseenter)=\"onElementMouseEnter($event, 'section', section, sectionEl)\"\n (mouseleave)=\"onElementMouseLeave($event)\" (click)=\"onElementClick($event, 'section', section, sectionEl)\"\n [class.selected]=\"selectedElement?.domElement === sectionEl\"\n [class.hovered]=\"hoveredElement?.domElement === sectionEl && selectedElement?.domElement !== sectionEl\"\n [style.marginBottom.px]=\"!last ? getSectionGapBottom(section) : null\" [ngClass]=\"getSectionClasses(section)\"\n [ngStyle]=\"getSectionStyles(section)\">\n <div class=\"section-heading\">\n <div class=\"section-heading-text\" [matTooltip]=\"getSectionHeaderTooltip(section) || null\"\n [matTooltipDisabled]=\"!getSectionHeaderTooltip(section)\">\n @if (getSectionTitle(section)) {\n <h3 class=\"section-title\" [class.title-large]=\"getSectionTitleStyle(section) === 'titleLarge'\"\n [class.title-medium]=\"getSectionTitleStyle(section) === 'titleMedium'\"\n [class.title-small]=\"getSectionTitleStyle(section) === 'titleSmall'\"\n [class.headline-small]=\"getSectionTitleStyle(section) === 'headlineSmall'\"\n [style.marginBottom.px]=\"getSectionTitleGapBottom(section) ?? 20\" [style.color]=\"getSectionTitleColor(section) || null\"\n [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\"\n [attr.id]=\"sectionPanelId(section, sectionIndex) + '-title'\">\n @if (getSectionIcon(section)) {\n <mat-icon class=\"section-title-icon\" aria-hidden=\"true\" [praxisIcon]=\"getSectionIcon(section)\"></mat-icon>\n }\n {{ getSectionTitle(section) }}\n @if (editModeEnabled) {\n <button type=\"button\" class=\"inline-edit-btn\" mat-icon-button (click)=\"openSectionEditor(section, 'title')\"\n aria-label=\"Editar t\u00EDtulo da se\u00E7\u00E3o\" matTooltip=\"Editar t\u00EDtulo\">\n <mat-icon>edit</mat-icon>\n </button>\n }\n </h3>\n }\n @if (getSectionDescription(section)) {\n <p class=\"section-description\" [class.body-large]=\"getSectionDescriptionStyle(section) === 'bodyLarge'\"\n [class.body-medium]=\"getSectionDescriptionStyle(section) === 'bodyMedium'\"\n [class.body-small]=\"getSectionDescriptionStyle(section) === 'bodySmall'\"\n [style.marginBottom.px]=\"getSectionDescriptionGapBottom(section) ?? 8\" [style.color]=\"getSectionDescriptionColor(section) || null\"\n [class.align-center]=\"getSectionHeaderAlign(section) === 'center'\">\n {{ getSectionDescription(section) }}\n @if (editModeEnabled) {\n <button type=\"button\" class=\"inline-edit-btn\" mat-icon-button\n (click)=\"openSectionEditor(section, 'description')\" aria-label=\"Editar descri\u00E7\u00E3o da se\u00E7\u00E3o\"\n matTooltip=\"Editar descri\u00E7\u00E3o\">\n <mat-icon>edit</mat-icon>\n </button>\n }\n </p>\n }\n </div>\n @if (isSectionCollapsible(section)) {\n <button type=\"button\" class=\"section-collapse-btn\" mat-icon-button\n (click)=\"toggleSectionCollapse($event, section)\" [attr.aria-expanded]=\"!isSectionCollapsed(section)\"\n [attr.aria-controls]=\"sectionPanelId(section, sectionIndex)\"\n [attr.aria-label]=\"isSectionCollapsed(section) ? 'Expandir se\u00E7\u00E3o' : 'Recolher se\u00E7\u00E3o'\">\n <mat-icon [praxisIcon]=\"isSectionCollapsed(section) ? 'expand_more' : 'expand_less'\"></mat-icon>\n </button>\n }\n </div>\n\n <div class=\"section-body\" [class.collapsed]=\"isSectionCollapsed(section)\"\n [attr.id]=\"sectionPanelId(section, sectionIndex)\"\n [attr.aria-labelledby]=\"getSectionTitle(section) ? sectionPanelId(section, sectionIndex) + '-title' : null\">\n @if (!isSectionCollapsed(section)) {\n @for (row of section.rows; track (row.id ?? $index); let rowIndex = $index) {\n @if (isRowVisible(row)) {\n <div class=\"row-drop-wrapper\" [attr.data-section-id]=\"section.id\">\n <div #rowEl class=\"form-row grid-12 canvas-element\" data-canvas-type=\"row\" [attr.data-row-index]=\"rowIndex\"\n [attr.data-section-id]=\"section.id\" [attr.data-row-id]=\"row.id\" [style.--pfx-grid-gap.px]=\"getRowGap(row)\"\n [style.--pfx-field-gap.px]=\"getRowRowGap(row)\"\n [style.--pfx-mount-index]=\"rowIndex\"\n [style.marginBottom.px]=\"rowIndex < section.rows.length - 1 ? (getRowRowGap(row) ?? null) : null\"\n [ngClass]=\"getRowClasses(row)\" [ngStyle]=\"getRowStyles(row)\"\n (mouseenter)=\"onElementMouseEnter($event, 'row', row, rowEl)\" (mouseleave)=\"onElementMouseLeave($event)\"\n (click)=\"onElementClick($event, 'row', row, rowEl)\" [class.selected]=\"selectedElement?.domElement === rowEl\"\n [class.hovered]=\"hoveredElement?.domElement === rowEl && selectedElement?.domElement !== rowEl\">\n @for (column of row.columns; track (column.id ?? $index); let colIndex = $index) {\n @if (isColumnVisible(column)) {\n <div class=\"column-drop-wrapper\" [attr.data-section-id]=\"section.id\" [attr.data-row-id]=\"row.id\">\n <div #colEl class=\"form-column canvas-element\" [ngClass]=\"getColumnClasses(column)\"\n [style.padding.px]=\"getColumnPadding(column)\" [style.--pfx-field-gap.px]=\"getRowRowGap(row)\"\n [ngStyle]=\"getColumnStyles(column)\" [attr.data-testid]=\"column.testId || null\"\n [attr.data-row-gap]=\"getRowRowGap(row)\" data-canvas-type=\"column\" [attr.data-column-index]=\"colIndex\"\n [attr.data-row-index]=\"rowIndex\" [attr.data-section-id]=\"section.id\" [attr.data-row-id]=\"row.id\"\n [attr.data-column-id]=\"column.id\" (mouseenter)=\"onElementMouseEnter($event, 'column', column, colEl)\"\n (mouseleave)=\"onElementMouseLeave($event)\" (click)=\"onElementClick($event, 'column', column, colEl)\"\n [class.selected]=\"selectedElement?.domElement === colEl\"\n [class.hovered]=\"hoveredElement?.domElement === colEl && selectedElement?.domElement !== colEl\">\n <ng-container dynamicFieldLoader [fields]=\"getColumnFields(column)\" [formGroup]=\"form\"\n [readonlyMode]=\"readonlyModeGlobal === null ? null : readonlyModeGlobal\"\n [disabledMode]=\"disabledModeGlobal === null ? null : disabledModeGlobal\"\n [presentationMode]=\"presentationForLoader\" [visible]=\"visibleGlobal === null ? null : visibleGlobal\"\n [canvasMode]=\"editModeEnabled\" (canvasMouseEnter)=\"onFieldMouseEnter($event)\"\n (canvasMouseLeave)=\"onFieldMouseLeave($event)\" (canvasClick)=\"onFieldClick($event)\"\n (renderError)=\"onFieldRenderError($event)\">\n </ng-container>\n </div>\n </div>\n } }\n </div>\n </div>\n }\n }\n } @else {\n <div class=\"section-collapsed-placeholder\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'unfold_more'\"></mat-icon>\n <span>{{ getSectionCollapsedSummary(section) }}</span>\n </div>\n }\n @if (last && formBlocksBeforeActions.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before-actions\" data-editorial-placement=\"beforeActions\">\n @for (block of formBlocksBeforeActions; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('beforeActions', $event)\">\n </ng-container>\n }\n </section>\n }\n @if (actionPlacement === 'insideLastSection' && last && !(effectivePresentation\n || effectiveReadonly)) {\n <praxis-form-actions data-actions-placement=\"insideLastSection\" [actions]=\"config.actions\"\n [isSubmitting]=\"submitting\" [formIsValid]=\"form.valid\" [submitError]=\"submitError\" [formId]=\"formId\"\n [actionOverrides]=\"actionRuleProps\"\n (action)=\"onFormAction($event)\"></praxis-form-actions>\n }\n </div>\n <!-- Overlay de bloqueio durante submiss\u00E3o -->\n @if (submitting) {\n <div class=\"form-blocking-overlay\" aria-live=\"polite\">\n <mat-progress-spinner diameter=\"40\" color=\"primary\"></mat-progress-spinner>\n <p>{{ config.messages?.loading?.submit || 'Salvando...' }}</p>\n </div>\n }\n </div>\n\n @if (editModeEnabled && selectedElement?.domElement === sectionEl) {\n <div class=\"add-section-container\">\n <div class=\"add-section-line\"></div>\n <button mat-fab color=\"primary\" aria-label=\"Adicionar nova se\u00E7\u00E3o\" (click)=\"addNewSectionAfter(sectionIndex)\"\n matTooltip=\"Adicionar nova se\u00E7\u00E3o aqui\">\n <mat-icon [praxisIcon]=\"'add'\"></mat-icon>\n </button>\n <div class=\"add-section-line\"></div>\n </div>\n }\n }\n </div>\n }\n\n @if (actionPlacement !== 'insideLastSection' && formBlocksBeforeActions.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-before-actions\" data-editorial-placement=\"beforeActions\">\n @for (block of formBlocksBeforeActions; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('beforeActions', $event)\">\n </ng-container>\n }\n </section>\n }\n\n @if (actionPlacement === 'afterSections' && !(effectivePresentation || effectiveReadonly)) {\n <praxis-form-actions data-actions-placement=\"afterSections\" [actions]=\"config.actions\" [isSubmitting]=\"submitting\"\n [formIsValid]=\"form.valid\" [submitError]=\"submitError\" [formId]=\"formId\"\n [actionOverrides]=\"actionRuleProps\"\n (action)=\"onFormAction($event)\"></praxis-form-actions>\n }\n\n @if (formBlocksAfter.length) {\n <section class=\"form-editorial-blocks form-editorial-blocks-after\" data-editorial-placement=\"after\">\n @for (block of formBlocksAfter; track (block.id ?? $index)) {\n <ng-container\n [dynamicWidgetLoader]=\"block\"\n [context]=\"getEditorialWidgetContext()\"\n [strictValidation]=\"true\"\n [autoWireOutputs]=\"true\"\n (widgetEvent)=\"onEditorialWidgetEvent('after', $event)\">\n </ng-container>\n }\n </section>\n }\n</form>\n@if (!editModeEnabled && mode === 'view') {\n<div class=\"ai-floating-assistant\">\n <praxis-ai-assistant [adapter]=\"aiAdapter\"></praxis-ai-assistant>\n</div>\n}\n}\n", styles: ["@charset \"UTF-8\";:host{display:block;position:relative}.form-config-controls{position:absolute;top:4px;right:4px;display:flex;gap:.25rem;z-index:100;background:transparent;padding:0;border:0;min-width:0;justify-content:flex-end;pointer-events:none}.form-config-controls>*{pointer-events:auto}.form-config-controls .mat-icon-button{--mdc-icon-button-state-layer-size: 36px;width:36px;height:36px;color:var(--md-sys-color-on-surface-variant)}.form-config-controls .mat-icon-button:hover{color:var(--md-sys-color-primary);background:var(--md-sys-color-primary-container)}.ai-floating-assistant{position:absolute;top:0;left:0;z-index:2002;pointer-events:none}.ai-floating-assistant{inset:auto 12px 12px auto;z-index:120}.ai-floating-assistant praxis-ai-assistant{pointer-events:auto;display:inline-flex}.ai-floating-assistant .ai-trigger-btn{box-shadow:var(--md-sys-elevation-level2)}.config-button{color:var(--md-sys-color-primary)}.form-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3rem;text-align:center;color:var(--md-sys-color-on-surface);gap:1rem}.form-loading p{margin:0;font-size:.875rem;opacity:.7}.form-error{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:3rem;text-align:center;color:var(--md-sys-color-error);gap:1rem;border:1px solid var(--md-sys-color-error);border-radius:8px;background-color:var(--md-sys-color-error-container);margin:1rem}.form-error h3{margin:0;color:var(--md-sys-color-on-error-container)}.form-error p{margin:0;color:var(--md-sys-color-on-error-container);opacity:.8}.form-error button{margin-top:.5rem}.praxis-dynamic-form{display:flex;flex-direction:column;transition:all .3s ease;position:relative}.praxis-dynamic-form.presentation-mode .form-row,.praxis-dynamic-form.readonly-mode .form-row{gap:.5rem;margin-bottom:.5rem}.praxis-dynamic-form.presentation-mode .form-section,.praxis-dynamic-form.readonly-mode .form-section{padding:.75rem .875rem}.praxis-dynamic-form.pres-compact .form-row{gap:.35rem;margin-bottom:.35rem}.praxis-dynamic-form.pres-compact .form-section{padding:.5rem .75rem}.praxis-dynamic-form.pres-label-left .praxis-presentation{display:grid;grid-template-columns:var(--pfx-presentation-label-w, 220px) 1fr;align-items:baseline;column-gap:10px}.praxis-dynamic-form.pres-label-left .praxis-presentation__label{text-align:right;margin-bottom:0}.form-section{border:1px solid var(--pfx-form-stroke, var(--md-sys-color-outline-variant));border-radius:8px;padding:1rem;background:var(--pfx-form-section-surface, var(--md-sys-color-surface-container));transition:all .2s ease;position:relative}.section-drop-wrapper>.form-section{margin-bottom:var(--pfx-section-gap, 20px)}.section-drop-wrapper:last-of-type>.form-section{margin-bottom:0}.praxis-dynamic-form>praxis-form-actions[data-actions-placement=afterSections] .form-actions{margin-top:var(--pfx-actions-gap-top, var(--pfx-section-gap, 20px))}.section-title{margin:0 0 var(--pfx-section-title-mb, 20px) 0;font-size:1.05rem;font-weight:500;color:var(--md-sys-color-on-surface)}.section-heading{display:flex;align-items:flex-start;gap:8px;margin-bottom:8px}.section-heading-text{flex:1 1 auto;min-width:0}.section-collapse-btn{margin-left:4px;color:var(--md-sys-color-on-surface-variant)}.section-title.title-large{font:var(--mdc-typography-title-large, 500 22px/28px system-ui)}.section-title.title-medium{font:var(--mdc-typography-title-medium, 500 16px/24px system-ui)}.section-title.title-small{font:var(--mdc-typography-title-small, 500 14px/20px system-ui)}.section-title.headline-small{font:var(--mdc-typography-headline-small, 600 24px/32px system-ui)}.section-description{margin:0 0 8px;font-size:.875rem;color:var(--md-sys-color-on-surface-variant)}.section-description.body-large{font:var(--mdc-typography-body-large, 400 16px/24px system-ui)}.section-description.body-medium{font:var(--mdc-typography-body-medium, 400 14px/20px system-ui)}.section-description.body-small{font:var(--mdc-typography-body-small, 400 12px/16px system-ui)}.section-title.align-center,.section-description.align-center{text-align:center}.inline-edit-btn{margin-left:6px;vertical-align:middle;--mdc-icon-button-size: 28px;--mdc-icon-button-icon-size: 16px}.inline-edit-btn mat-icon{font-size:16px;width:16px;height:16px}.section-body.collapsed{border:1px dashed var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low);border-radius:6px;padding:8px 10px}.section-collapsed-placeholder{display:flex;align-items:center;gap:8px;color:var(--md-sys-color-on-surface-variant);font-size:.95rem}.section-collapsed-placeholder mat-icon{font-size:20px;width:20px;height:20px}.form-row{display:flex;gap:1rem;margin-bottom:1rem;transition:all .2s ease;border-radius:6px;position:relative}.praxis-dynamic-form.pfx-mounting .form-row{opacity:0;transform:translateY(var(--pdx-form-mount-offset, 6px));animation:pdxFormMount var(--pdx-form-mount-duration, .16s) ease-out both;animation-delay:calc(var(--pfx-mount-index, 0) * var(--pdx-form-mount-stagger, 20ms))}@media(prefers-reduced-motion:reduce){.praxis-dynamic-form.pfx-mounting .form-row{animation:none;opacity:1;transform:none}}@keyframes pdxFormMount{to{opacity:1;transform:translateY(0)}}.praxis-dynamic-form .mat-mdc-form-field{width:100%;margin-bottom:var(--pfx-field-gap, 10px)}.praxis-dynamic-form [data-field-type=input],.praxis-dynamic-form [data-field-type=textarea],.praxis-dynamic-form [data-field-type=email],.praxis-dynamic-form [data-field-type=password],.praxis-dynamic-form [data-field-type=url],.praxis-dynamic-form [data-field-type=search],.praxis-dynamic-form [data-field-type=phone],.praxis-dynamic-form [data-field-type=numericTextBox],.praxis-dynamic-form [data-field-type=currency],.praxis-dynamic-form [data-field-type=cpfCnpj],.praxis-dynamic-form [data-field-type=date],.praxis-dynamic-form [data-field-type=dateInput],.praxis-dynamic-form [data-field-type=dateRange],.praxis-dynamic-form [data-field-type=dateTimeLocal],.praxis-dynamic-form [data-field-type=time],.praxis-dynamic-form [data-field-type=timePicker],.praxis-dynamic-form [data-field-type=timeRange],.praxis-dynamic-form [data-field-type=month],.praxis-dynamic-form [data-field-type=week],.praxis-dynamic-form [data-field-type=yearInput],.praxis-dynamic-form [data-field-type=select],.praxis-dynamic-form [data-field-type=multi-select],.praxis-dynamic-form [data-field-type=searchable-select],.praxis-dynamic-form [data-field-type=async-select],.praxis-dynamic-form [data-field-type=autocomplete],.praxis-dynamic-form [data-field-type=tree-select],.praxis-dynamic-form [data-field-type=multi-select-tree],.praxis-dynamic-form [data-field-type=priceRange],.praxis-dynamic-form [data-field-type=file-upload]{display:block;width:100%;min-width:0}.praxis-dynamic-form .mat-mdc-form-field-subscript-wrapper{min-height:var(--pfx-subscript-min-h, 22px)}.form-row:last-child{margin-bottom:0}.form-column{display:grid;align-content:start;gap:var(--pfx-field-gap, 10px);flex:1;min-width:0;transition:all .2s ease;border-radius:4px;position:relative}.form-row.grid-12{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:var(--pfx-grid-gap, 16px)}.align-start{align-self:flex-start}.align-center{align-self:center}.align-end{align-self:flex-end}.align-stretch{align-self:stretch}.span-xs-1{grid-column:span 1}.span-xs-2{grid-column:span 2}.span-xs-3{grid-column:span 3}.span-xs-4{grid-column:span 4}.span-xs-5{grid-column:span 5}.span-xs-6{grid-column:span 6}.span-xs-7{grid-column:span 7}.span-xs-8{grid-column:span 8}.span-xs-9{grid-column:span 9}.span-xs-10{grid-column:span 10}.span-xs-11{grid-column:span 11}.span-xs-12{grid-column:span 12}.offset-xs-0{margin-left:0%}.offset-xs-1{margin-left:calc(1 / 12 * 100%)}.offset-xs-2{margin-left:calc(2 / 12 * 100%)}.offset-xs-3{margin-left:25%}.offset-xs-4{margin-left:calc(4 / 12 * 100%)}.offset-xs-5{margin-left:calc(5 / 12 * 100%)}.offset-xs-6{margin-left:50%}.offset-xs-7{margin-left:calc(7 / 12 * 100%)}.offset-xs-8{margin-left:calc(8 / 12 * 100%)}.offset-xs-9{margin-left:75%}.offset-xs-10{margin-left:calc(10 / 12 * 100%)}.offset-xs-11{margin-left:calc(11 / 12 * 100%)}.order-xs--12{order:-12}.order-xs--11{order:-11}.order-xs--10{order:-10}.order-xs--9{order:-9}.order-xs--8{order:-8}.order-xs--7{order:-7}.order-xs--6{order:-6}.order-xs--5{order:-5}.order-xs--4{order:-4}.order-xs--3{order:-3}.order-xs--2{order:-2}.order-xs--1{order:-1}.order-xs-0{order:0}.order-xs-1{order:1}.order-xs-2{order:2}.order-xs-3{order:3}.order-xs-4{order:4}.order-xs-5{order:5}.order-xs-6{order:6}.order-xs-7{order:7}.order-xs-8{order:8}.order-xs-9{order:9}.order-xs-10{order:10}.order-xs-11{order:11}.order-xs-12{order:12}.hidden-xs{display:none}@media(min-width:600px){.span-sm-1{grid-column:span 1}.span-sm-2{grid-column:span 2}.span-sm-3{grid-column:span 3}.span-sm-4{grid-column:span 4}.span-sm-5{grid-column:span 5}.span-sm-6{grid-column:span 6}.span-sm-7{grid-column:span 7}.span-sm-8{grid-column:span 8}.span-sm-9{grid-column:span 9}.span-sm-10{grid-column:span 10}.span-sm-11{grid-column:span 11}.span-sm-12{grid-column:span 12}.offset-sm-0{margin-left:0%}.offset-sm-1{margin-left:calc(1 / 12 * 100%)}.offset-sm-2{margin-left:calc(2 / 12 * 100%)}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:calc(4 / 12 * 100%)}.offset-sm-5{margin-left:calc(5 / 12 * 100%)}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:calc(7 / 12 * 100%)}.offset-sm-8{margin-left:calc(8 / 12 * 100%)}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:calc(10 / 12 * 100%)}.offset-sm-11{margin-left:calc(11 / 12 * 100%)}.order-sm--12{order:-12}.order-sm--11{order:-11}.order-sm--10{order:-10}.order-sm--9{order:-9}.order-sm--8{order:-8}.order-sm--7{order:-7}.order-sm--6{order:-6}.order-sm--5{order:-5}.order-sm--4{order:-4}.order-sm--3{order:-3}.order-sm--2{order:-2}.order-sm--1{order:-1}.order-sm-0{order:0}.order-sm-1{order:1}.order-sm-2{order:2}.order-sm-3{order:3}.order-sm-4{order:4}.order-sm-5{order:5}.order-sm-6{order:6}.order-sm-7{order:7}.order-sm-8{order:8}.order-sm-9{order:9}.order-sm-10{order:10}.order-sm-11{order:11}.order-sm-12{order:12}.hidden-sm{display:none}}@media(min-width:900px){.span-md-1{grid-column:span 1}.span-md-2{grid-column:span 2}.span-md-3{grid-column:span 3}.span-md-4{grid-column:span 4}.span-md-5{grid-column:span 5}.span-md-6{grid-column:span 6}.span-md-7{grid-column:span 7}.span-md-8{grid-column:span 8}.span-md-9{grid-column:span 9}.span-md-10{grid-column:span 10}.span-md-11{grid-column:span 11}.span-md-12{grid-column:span 12}.offset-md-0{margin-left:0%}.offset-md-1{margin-left:calc(1 / 12 * 100%)}.offset-md-2{margin-left:calc(2 / 12 * 100%)}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:calc(4 / 12 * 100%)}.offset-md-5{margin-left:calc(5 / 12 * 100%)}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:calc(7 / 12 * 100%)}.offset-md-8{margin-left:calc(8 / 12 * 100%)}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:calc(10 / 12 * 100%)}.offset-md-11{margin-left:calc(11 / 12 * 100%)}.order-md--12{order:-12}.order-md--11{order:-11}.order-md--10{order:-10}.order-md--9{order:-9}.order-md--8{order:-8}.order-md--7{order:-7}.order-md--6{order:-6}.order-md--5{order:-5}.order-md--4{order:-4}.order-md--3{order:-3}.order-md--2{order:-2}.order-md--1{order:-1}.order-md-0{order:0}.order-md-1{order:1}.order-md-2{order:2}.order-md-3{order:3}.order-md-4{order:4}.order-md-5{order:5}.order-md-6{order:6}.order-md-7{order:7}.order-md-8{order:8}.order-md-9{order:9}.order-md-10{order:10}.order-md-11{order:11}.order-md-12{order:12}.hidden-md{display:none}}@media(min-width:1200px){.span-lg-1{grid-column:span 1}.span-lg-2{grid-column:span 2}.span-lg-3{grid-column:span 3}.span-lg-4{grid-column:span 4}.span-lg-5{grid-column:span 5}.span-lg-6{grid-column:span 6}.span-lg-7{grid-column:span 7}.span-lg-8{grid-column:span 8}.span-lg-9{grid-column:span 9}.span-lg-10{grid-column:span 10}.span-lg-11{grid-column:span 11}.span-lg-12{grid-column:span 12}.offset-lg-0{margin-left:0%}.offset-lg-1{margin-left:calc(1 / 12 * 100%)}.offset-lg-2{margin-left:calc(2 / 12 * 100%)}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:calc(4 / 12 * 100%)}.offset-lg-5{margin-left:calc(5 / 12 * 100%)}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:calc(7 / 12 * 100%)}.offset-lg-8{margin-left:calc(8 / 12 * 100%)}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:calc(10 / 12 * 100%)}.offset-lg-11{margin-left:calc(11 / 12 * 100%)}.order-lg--12{order:-12}.order-lg--11{order:-11}.order-lg--10{order:-10}.order-lg--9{order:-9}.order-lg--8{order:-8}.order-lg--7{order:-7}.order-lg--6{order:-6}.order-lg--5{order:-5}.order-lg--4{order:-4}.order-lg--3{order:-3}.order-lg--2{order:-2}.order-lg--1{order:-1}.order-lg-0{order:0}.order-lg-1{order:1}.order-lg-2{order:2}.order-lg-3{order:3}.order-lg-4{order:4}.order-lg-5{order:5}.order-lg-6{order:6}.order-lg-7{order:7}.order-lg-8{order:8}.order-lg-9{order:9}.order-lg-10{order:10}.order-lg-11{order:11}.order-lg-12{order:12}.hidden-lg{display:none}}@media(min-width:1536px){.span-xl-1{grid-column:span 1}.span-xl-2{grid-column:span 2}.span-xl-3{grid-column:span 3}.span-xl-4{grid-column:span 4}.span-xl-5{grid-column:span 5}.span-xl-6{grid-column:span 6}.span-xl-7{grid-column:span 7}.span-xl-8{grid-column:span 8}.span-xl-9{grid-column:span 9}.span-xl-10{grid-column:span 10}.span-xl-11{grid-column:span 11}.span-xl-12{grid-column:span 12}.offset-xl-0{margin-left:0%}.offset-xl-1{margin-left:calc(1 / 12 * 100%)}.offset-xl-2{margin-left:calc(2 / 12 * 100%)}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:calc(4 / 12 * 100%)}.offset-xl-5{margin-left:calc(5 / 12 * 100%)}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:calc(7 / 12 * 100%)}.offset-xl-8{margin-left:calc(8 / 12 * 100%)}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:calc(10 / 12 * 100%)}.offset-xl-11{margin-left:calc(11 / 12 * 100%)}.order-xl--12{order:-12}.order-xl--11{order:-11}.order-xl--10{order:-10}.order-xl--9{order:-9}.order-xl--8{order:-8}.order-xl--7{order:-7}.order-xl--6{order:-6}.order-xl--5{order:-5}.order-xl--4{order:-4}.order-xl--3{order:-3}.order-xl--2{order:-2}.order-xl--1{order:-1}.order-xl-0{order:0}.order-xl-1{order:1}.order-xl-2{order:2}.order-xl-3{order:3}.order-xl-4{order:4}.order-xl-5{order:5}.order-xl-6{order:6}.order-xl-7{order:7}.order-xl-8{order:8}.order-xl-9{order:9}.order-xl-10{order:10}.order-xl-11{order:11}.order-xl-12{order:12}.hidden-xl{display:none}}.form-blocking-overlay{position:absolute;inset:0;background:transparent;color:var(--md-sys-color-on-surface);backdrop-filter:blur(2px) saturate(103%);-webkit-backdrop-filter:blur(2px) saturate(103%);display:flex;align-items:center;justify-content:center;flex-direction:column;gap:12px;z-index:10;pointer-events:all}@media(max-width:768px){.form-row{flex-direction:column;gap:.5rem}.form-section{padding:1rem}}.canvas-mode-enabled{--canvas-hit: 14px}.canvas-mode-enabled .canvas-element{position:relative;z-index:0;border-radius:8px;outline:2px solid transparent;outline-offset:2px;transition:outline-color .2s ease,outline-style .2s ease}.canvas-mode-enabled .canvas-element:before{content:\"\";position:absolute;inset:calc(-1 * var(--canvas-hit));pointer-events:none;border-radius:inherit;background:transparent}.canvas-mode-enabled .canvas-element[data-canvas-type=section]{--outline-color: var(--md-sys-color-primary)}.canvas-mode-enabled .canvas-element[data-canvas-type=row]{--outline-color: var(--md-sys-color-secondary)}.canvas-mode-enabled .canvas-element[data-canvas-type=column],.canvas-mode-enabled .canvas-element[data-canvas-type=field]{--outline-color: var(--md-sys-color-tertiary)}.canvas-mode-enabled .canvas-element[data-canvas-type=actions]{--outline-color: var(--md-sys-color-primary)}.canvas-mode-enabled .canvas-element.hovered:not(.selected){outline-color:var(--outline-color, var(--md-sys-color-primary));outline-style:dashed}.canvas-mode-enabled .canvas-element.selected{outline-color:var(--outline-color, var(--md-sys-color-primary));outline-style:solid;box-shadow:0 0 0 2px var(--outline-color, var(--md-sys-color-primary))}.canvas-mode-enabled .canvas-element.hovered{z-index:1000}.canvas-mode-enabled .canvas-element.selected{z-index:2000}.section-drop-wrapper,.row-drop-wrapper,.column-drop-wrapper{display:contents}.add-section-container{display:flex;align-items:center;justify-content:center;padding:.5rem 0;margin:-.5rem 0;position:relative;z-index:500}.add-section-container .add-section-line{flex-grow:1;height:1px;background:repeating-linear-gradient(90deg,var(--md-sys-color-outline-variant),var(--md-sys-color-outline-variant) 4px,transparent 4px,transparent 8px)}.add-section-container button{margin:0 1rem;transform:scale(.85)}.section-title{display:flex;align-items:center;gap:8px}.section-title .section-title-icon{font-size:1.25em;line-height:1}\n"] }]
|
|
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"] }]
|
|
9591
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: [{
|
|
9592
10360
|
type: Inject,
|
|
9593
10361
|
args: [ASYNC_CONFIG_STORAGE]
|
|
@@ -9777,12 +10545,15 @@ class JsonConfigEditorComponent {
|
|
|
9777
10545
|
cdr;
|
|
9778
10546
|
configService;
|
|
9779
10547
|
config = null;
|
|
10548
|
+
document = null;
|
|
9780
10549
|
configChange = new EventEmitter();
|
|
10550
|
+
documentChange = new EventEmitter();
|
|
9781
10551
|
validationChange = new EventEmitter();
|
|
9782
10552
|
editorEvent = new EventEmitter();
|
|
9783
10553
|
jsonText = '';
|
|
9784
10554
|
isValidJson = true;
|
|
9785
10555
|
jsonError = '';
|
|
10556
|
+
diagnostics = [];
|
|
9786
10557
|
destroy$ = new Subject();
|
|
9787
10558
|
jsonTextChanges$ = new Subject();
|
|
9788
10559
|
constructor(cdr, configService) {
|
|
@@ -9793,9 +10564,10 @@ class JsonConfigEditorComponent {
|
|
|
9793
10564
|
.subscribe((text) => this.validateJson(text));
|
|
9794
10565
|
}
|
|
9795
10566
|
ngOnInit() {
|
|
9796
|
-
const
|
|
10567
|
+
const doc = this.document ??
|
|
10568
|
+
parseLegacyOrDynamicFormDocument(this.config || this.configService.currentConfig);
|
|
9797
10569
|
try {
|
|
9798
|
-
this.jsonText = JSON.stringify(
|
|
10570
|
+
this.jsonText = JSON.stringify(serializeDynamicFormAuthoringDocument(doc), null, 2);
|
|
9799
10571
|
}
|
|
9800
10572
|
catch {
|
|
9801
10573
|
this.jsonText = '{}';
|
|
@@ -9814,12 +10586,20 @@ class JsonConfigEditorComponent {
|
|
|
9814
10586
|
return;
|
|
9815
10587
|
}
|
|
9816
10588
|
try {
|
|
9817
|
-
const
|
|
9818
|
-
|
|
9819
|
-
|
|
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);
|
|
9820
10595
|
this.editorEvent.emit({
|
|
9821
10596
|
type: 'apply',
|
|
9822
|
-
payload: {
|
|
10597
|
+
payload: {
|
|
10598
|
+
isValid: !diagnostics.some((item) => item.level === 'error'),
|
|
10599
|
+
document: newDocument,
|
|
10600
|
+
config: newDocument.config,
|
|
10601
|
+
diagnostics,
|
|
10602
|
+
},
|
|
9823
10603
|
});
|
|
9824
10604
|
}
|
|
9825
10605
|
catch {
|
|
@@ -9836,10 +10616,17 @@ class JsonConfigEditorComponent {
|
|
|
9836
10616
|
}
|
|
9837
10617
|
try {
|
|
9838
10618
|
const parsed = JSON.parse(this.jsonText);
|
|
9839
|
-
|
|
10619
|
+
const diagnostics = validateDynamicFormAuthoringInput(parsed);
|
|
10620
|
+
const document = parseLegacyOrDynamicFormDocument(parsed);
|
|
10621
|
+
this.jsonText = JSON.stringify(serializeDynamicFormAuthoringDocument(document), null, 2);
|
|
9840
10622
|
this.editorEvent.emit({
|
|
9841
10623
|
type: 'format',
|
|
9842
|
-
payload: {
|
|
10624
|
+
payload: {
|
|
10625
|
+
isValid: !diagnostics.some((item) => item.level === 'error'),
|
|
10626
|
+
document,
|
|
10627
|
+
config: document.config,
|
|
10628
|
+
diagnostics,
|
|
10629
|
+
},
|
|
9843
10630
|
});
|
|
9844
10631
|
this.cdr.markForCheck();
|
|
9845
10632
|
}
|
|
@@ -9851,39 +10638,54 @@ class JsonConfigEditorComponent {
|
|
|
9851
10638
|
}
|
|
9852
10639
|
}
|
|
9853
10640
|
updateJsonFromConfig(config) {
|
|
9854
|
-
this.
|
|
10641
|
+
this.updateJsonFromDocument(parseLegacyOrDynamicFormDocument(config || { sections: [] }));
|
|
10642
|
+
}
|
|
10643
|
+
updateJsonFromDocument(document) {
|
|
10644
|
+
this.jsonText = JSON.stringify(serializeDynamicFormAuthoringDocument(document), null, 2);
|
|
9855
10645
|
this.validateJson(this.jsonText);
|
|
9856
10646
|
}
|
|
9857
10647
|
/**
|
|
9858
10648
|
* Força atualização do JSON com a configuração atual
|
|
9859
10649
|
*/
|
|
9860
10650
|
refreshJson() {
|
|
9861
|
-
const
|
|
9862
|
-
|
|
10651
|
+
const document = this.document ??
|
|
10652
|
+
parseLegacyOrDynamicFormDocument(this.config || this.configService.currentConfig);
|
|
10653
|
+
this.updateJsonFromDocument(document);
|
|
9863
10654
|
}
|
|
9864
10655
|
getCurrentConfig() {
|
|
10656
|
+
return this.getCurrentDocument()?.config || null;
|
|
10657
|
+
}
|
|
10658
|
+
getCurrentDocument() {
|
|
9865
10659
|
if (!this.isValidJson) {
|
|
9866
10660
|
return null;
|
|
9867
10661
|
}
|
|
9868
10662
|
try {
|
|
9869
|
-
return JSON.parse(this.jsonText);
|
|
10663
|
+
return parseLegacyOrDynamicFormDocument(JSON.parse(this.jsonText));
|
|
9870
10664
|
}
|
|
9871
10665
|
catch {
|
|
9872
10666
|
return null;
|
|
9873
10667
|
}
|
|
9874
10668
|
}
|
|
9875
10669
|
hasChanges() {
|
|
9876
|
-
|
|
10670
|
+
const baseline = this.document ??
|
|
10671
|
+
(this.config
|
|
10672
|
+
? parseLegacyOrDynamicFormDocument(this.config)
|
|
10673
|
+
: null);
|
|
10674
|
+
if (!baseline) {
|
|
9877
10675
|
return false;
|
|
9878
10676
|
}
|
|
9879
|
-
const
|
|
9880
|
-
if (!
|
|
10677
|
+
const currentDocument = this.getCurrentDocument();
|
|
10678
|
+
if (!currentDocument) {
|
|
9881
10679
|
return false;
|
|
9882
10680
|
}
|
|
9883
|
-
return JSON.stringify(
|
|
10681
|
+
return (JSON.stringify(serializeDynamicFormAuthoringDocument(baseline)) !==
|
|
10682
|
+
JSON.stringify(serializeDynamicFormAuthoringDocument(currentDocument)));
|
|
9884
10683
|
}
|
|
9885
10684
|
validateJson(text) {
|
|
9886
|
-
const result = {
|
|
10685
|
+
const result = {
|
|
10686
|
+
isValid: false,
|
|
10687
|
+
diagnostics: [],
|
|
10688
|
+
};
|
|
9887
10689
|
if (!text.trim()) {
|
|
9888
10690
|
result.error = 'JSON não pode estar vazio';
|
|
9889
10691
|
this.updateValidationState(result);
|
|
@@ -9891,16 +10693,17 @@ class JsonConfigEditorComponent {
|
|
|
9891
10693
|
}
|
|
9892
10694
|
try {
|
|
9893
10695
|
const parsed = JSON.parse(text);
|
|
9894
|
-
|
|
9895
|
-
|
|
9896
|
-
|
|
9897
|
-
|
|
9898
|
-
|
|
9899
|
-
|
|
9900
|
-
|
|
9901
|
-
|
|
9902
|
-
result.
|
|
9903
|
-
|
|
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';
|
|
9904
10707
|
}
|
|
9905
10708
|
this.updateValidationState(result);
|
|
9906
10709
|
}
|
|
@@ -9913,6 +10716,7 @@ class JsonConfigEditorComponent {
|
|
|
9913
10716
|
updateValidationState(result) {
|
|
9914
10717
|
this.isValidJson = result.isValid;
|
|
9915
10718
|
this.jsonError = result.error || '';
|
|
10719
|
+
this.diagnostics = result.diagnostics || [];
|
|
9916
10720
|
this.validationChange.emit(result);
|
|
9917
10721
|
this.editorEvent.emit({ type: 'validation', payload: result });
|
|
9918
10722
|
this.cdr.markForCheck();
|
|
@@ -9954,7 +10758,7 @@ class JsonConfigEditorComponent {
|
|
|
9954
10758
|
}
|
|
9955
10759
|
}
|
|
9956
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 });
|
|
9957
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: JsonConfigEditorComponent, isStandalone: true, selector: "form-json-config-editor", inputs: { config: "config" }, outputs: { configChange: "configChange", validationChange: "validationChange", editorEvent: "editorEvent" }, ngImport: i0, template: `
|
|
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: `
|
|
9958
10762
|
<div class="json-config-editor">
|
|
9959
10763
|
<mat-card class="educational-card">
|
|
9960
10764
|
<mat-card-header>
|
|
@@ -9963,8 +10767,13 @@ class JsonConfigEditorComponent {
|
|
|
9963
10767
|
</mat-card-header>
|
|
9964
10768
|
<mat-card-content>
|
|
9965
10769
|
<p>
|
|
9966
|
-
<strong>Para usuários avançados:</strong> Edite
|
|
9967
|
-
|
|
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.
|
|
9968
10777
|
</p>
|
|
9969
10778
|
<div class="badges">
|
|
9970
10779
|
<span class="badge" [class.ok]="isValidJson" [class.err]="!isValidJson">
|
|
@@ -10022,9 +10831,28 @@ class JsonConfigEditorComponent {
|
|
|
10022
10831
|
>JSON inválido: {{ jsonError }}</mat-error
|
|
10023
10832
|
>
|
|
10024
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>
|
|
10025
10853
|
</div>
|
|
10026
10854
|
</div>
|
|
10027
|
-
`, isInline: true, styles: [".json-config-editor{display:flex;flex-direction:column;height:100%}.educational-card{margin-bottom:24px;background-color:var(--md-sys-color-surface-container-low);border-left:4px solid var(--md-sys-color-primary)}.educational-card .mat-mdc-card-header{padding-bottom:8px}.card-icon{background-color:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container);font-size:20px;width:40px;height:40px;display:flex;align-items:center;justify-content:center}.educational-card .mat-mdc-card-title{font-size:1.1rem;font-weight:500;color:var(--md-sys-color-on-surface)}.educational-card .mat-mdc-card-content{color:var(--md-sys-color-on-surface-variant);line-height:1.5}.json-editor-section{flex:1;display:flex;flex-direction:column}.json-editor-toolbar{display:flex;gap:12px;margin-bottom:16px;padding:12px;background-color:var(--md-sys-color-surface-container-low);border-radius:8px;border:1px solid var(--md-sys-color-outline-variant)}.spacer{flex:1}.json-textarea-field{width:100%;flex:1}.badges{display:flex;gap:8px;margin-top:8px}.badge{padding:2px 8px;border-radius:999px;font-size:12px;border:1px solid var(--md-sys-color-outline-variant)}.badge.ok{background:var(--md-sys-color-primary-container)}.badge.err{background:var(--md-sys-color-error-container)}.badge.warn{background:var(--md-sys-color-secondary-container)}.json-textarea{font-family:Monaco,Menlo,Ubuntu Mono,Consolas,monospace!important;font-size:13px!important;line-height:1.4!important;height:100%!important;min-height:300px!important;white-space:pre!important;overflow-wrap:normal!important;overflow-x:auto!important;resize:none!important}.valid-hint{color:var(--md-sys-color-primary)!important}@media(max-width:768px){.json-editor-toolbar{flex-direction:column;gap:8px}.json-textarea{font-size:12px!important;min-height:300px!important}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i8$1.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i8$1.MatCardAvatar, selector: "[mat-card-avatar], [matCardAvatar]" }, { kind: "directive", type: i8$1.MatCardContent, selector: "mat-card-content" }, { kind: "component", type: i8$1.MatCardHeader, selector: "mat-card-header" }, { kind: "directive", type: i8$1.MatCardTitle, selector: "mat-card-title, [mat-card-title], [matCardTitle]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
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 });
|
|
10028
10856
|
}
|
|
10029
10857
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: JsonConfigEditorComponent, decorators: [{
|
|
10030
10858
|
type: Component,
|
|
@@ -10045,8 +10873,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
10045
10873
|
</mat-card-header>
|
|
10046
10874
|
<mat-card-content>
|
|
10047
10875
|
<p>
|
|
10048
|
-
<strong>Para usuários avançados:</strong> Edite
|
|
10049
|
-
|
|
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.
|
|
10050
10883
|
</p>
|
|
10051
10884
|
<div class="badges">
|
|
10052
10885
|
<span class="badge" [class.ok]="isValidJson" [class.err]="!isValidJson">
|
|
@@ -10104,13 +10937,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
10104
10937
|
>JSON inválido: {{ jsonError }}</mat-error
|
|
10105
10938
|
>
|
|
10106
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>
|
|
10107
10959
|
</div>
|
|
10108
10960
|
</div>
|
|
10109
|
-
`, styles: [".json-config-editor{display:flex;flex-direction:column;height:100%}.educational-card{margin-bottom:24px;background-color:var(--md-sys-color-surface-container-low);border-left:4px solid var(--md-sys-color-primary)}.educational-card .mat-mdc-card-header{padding-bottom:8px}.card-icon{background-color:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container);font-size:20px;width:40px;height:40px;display:flex;align-items:center;justify-content:center}.educational-card .mat-mdc-card-title{font-size:1.1rem;font-weight:500;color:var(--md-sys-color-on-surface)}.educational-card .mat-mdc-card-content{color:var(--md-sys-color-on-surface-variant);line-height:1.5}.json-editor-section{flex:1;display:flex;flex-direction:column}.json-editor-toolbar{display:flex;gap:12px;margin-bottom:16px;padding:12px;background-color:var(--md-sys-color-surface-container-low);border-radius:8px;border:1px solid var(--md-sys-color-outline-variant)}.spacer{flex:1}.json-textarea-field{width:100%;flex:1}.badges{display:flex;gap:8px;margin-top:8px}.badge{padding:2px 8px;border-radius:999px;font-size:12px;border:1px solid var(--md-sys-color-outline-variant)}.badge.ok{background:var(--md-sys-color-primary-container)}.badge.err{background:var(--md-sys-color-error-container)}.badge.warn{background:var(--md-sys-color-secondary-container)}.json-textarea{font-family:Monaco,Menlo,Ubuntu Mono,Consolas,monospace!important;font-size:13px!important;line-height:1.4!important;height:100%!important;min-height:300px!important;white-space:pre!important;overflow-wrap:normal!important;overflow-x:auto!important;resize:none!important}.valid-hint{color:var(--md-sys-color-primary)!important}@media(max-width:768px){.json-editor-toolbar{flex-direction:column;gap:8px}.json-textarea{font-size:12px!important;min-height:300px!important}}\n"] }]
|
|
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"] }]
|
|
10110
10962
|
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: FormConfigService }], propDecorators: { config: [{
|
|
10111
10963
|
type: Input
|
|
10964
|
+
}], document: [{
|
|
10965
|
+
type: Input
|
|
10112
10966
|
}], configChange: [{
|
|
10113
10967
|
type: Output
|
|
10968
|
+
}], documentChange: [{
|
|
10969
|
+
type: Output
|
|
10114
10970
|
}], validationChange: [{
|
|
10115
10971
|
type: Output
|
|
10116
10972
|
}], editorEvent: [{
|
|
@@ -12127,8 +12983,8 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
12127
12983
|
configChange = new EventEmitter();
|
|
12128
12984
|
containerStylesText = '';
|
|
12129
12985
|
globalActionCatalogSource = inject(GLOBAL_ACTION_CATALOG, { optional: true }) ?? [];
|
|
12130
|
-
globalActionCatalog = getGlobalActionCatalog(this.globalActionCatalogSource);
|
|
12131
12986
|
legacyActionSpecs = GLOBAL_ACTION_SPEC_CATALOG;
|
|
12987
|
+
globalActionCatalog = this.buildGlobalActionCatalog();
|
|
12132
12988
|
customActionValue = '__custom__';
|
|
12133
12989
|
actionFieldDrafts = new Map();
|
|
12134
12990
|
actionFieldErrors = new Map();
|
|
@@ -12194,7 +13050,7 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
12194
13050
|
this.containerStylesText = styles ? JSON.stringify(styles, null, 2) : '';
|
|
12195
13051
|
}
|
|
12196
13052
|
getActionSpecById(id) {
|
|
12197
|
-
return this.
|
|
13053
|
+
return this.globalActionCatalog.find((item) => item.id === id);
|
|
12198
13054
|
}
|
|
12199
13055
|
getCustomActionSelectValue(value) {
|
|
12200
13056
|
const parsed = this.parseActionValue(value);
|
|
@@ -12212,6 +13068,7 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
12212
13068
|
const current = this.actions.custom?.[index];
|
|
12213
13069
|
if (!current)
|
|
12214
13070
|
return;
|
|
13071
|
+
this.clearActionFieldState(current);
|
|
12215
13072
|
if (value === this.customActionValue) {
|
|
12216
13073
|
const parsed = this.parseActionValue(current.action);
|
|
12217
13074
|
if (parsed.isGlobal) {
|
|
@@ -12260,6 +13117,38 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
12260
13117
|
return false;
|
|
12261
13118
|
return this.globalActionCatalog.some((item) => item.id === id);
|
|
12262
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
|
+
}
|
|
12263
13152
|
getGlobalActionSchema(action) {
|
|
12264
13153
|
const parsed = this.parseActionValue(action.action);
|
|
12265
13154
|
if (!parsed.isGlobal)
|
|
@@ -12284,83 +13173,122 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
12284
13173
|
return errors?.[key] || '';
|
|
12285
13174
|
}
|
|
12286
13175
|
getGlobalActionFieldValue(action, field) {
|
|
12287
|
-
|
|
12288
|
-
|
|
12289
|
-
if (draft !== undefined)
|
|
12290
|
-
return draft;
|
|
12291
|
-
}
|
|
13176
|
+
const draftKey = this.getActionDraftKey(action);
|
|
13177
|
+
const draft = this.actionFieldDrafts.get(draftKey)?.[field.key];
|
|
12292
13178
|
const info = this.getActionParamInfo(action.action);
|
|
12293
13179
|
const raw = info.param || '';
|
|
12294
13180
|
const json = info.json || {};
|
|
12295
13181
|
const hasJson = info.isJson;
|
|
13182
|
+
let value;
|
|
12296
13183
|
switch (field.key) {
|
|
12297
13184
|
case 'message':
|
|
12298
|
-
|
|
13185
|
+
value = json.message ?? json.text ?? (hasJson ? '' : raw);
|
|
13186
|
+
break;
|
|
12299
13187
|
case 'url':
|
|
12300
|
-
if (json.url)
|
|
12301
|
-
|
|
13188
|
+
if (json.url) {
|
|
13189
|
+
value = json.url;
|
|
13190
|
+
break;
|
|
13191
|
+
}
|
|
12302
13192
|
if (!hasJson && info.id === 'apiCall') {
|
|
12303
13193
|
const parsed = this.parseMethodAndUrl(raw);
|
|
12304
|
-
|
|
13194
|
+
value = parsed.url || '';
|
|
13195
|
+
break;
|
|
12305
13196
|
}
|
|
12306
|
-
|
|
13197
|
+
value = hasJson ? '' : raw;
|
|
13198
|
+
break;
|
|
12307
13199
|
case 'method': {
|
|
12308
|
-
if (json.method)
|
|
12309
|
-
|
|
13200
|
+
if (json.method) {
|
|
13201
|
+
value = String(json.method);
|
|
13202
|
+
break;
|
|
13203
|
+
}
|
|
12310
13204
|
if (!hasJson) {
|
|
12311
13205
|
const parsed = this.parseMethodAndUrl(raw);
|
|
12312
|
-
|
|
13206
|
+
value = parsed.method || '';
|
|
13207
|
+
break;
|
|
12313
13208
|
}
|
|
12314
|
-
|
|
13209
|
+
value = '';
|
|
13210
|
+
break;
|
|
12315
13211
|
}
|
|
12316
13212
|
case 'bodySource':
|
|
12317
|
-
|
|
13213
|
+
value = json.bodySource ?? '';
|
|
13214
|
+
break;
|
|
12318
13215
|
case 'headers':
|
|
12319
|
-
|
|
13216
|
+
value = this.stringifyJson(json.headers);
|
|
13217
|
+
break;
|
|
12320
13218
|
case 'body':
|
|
12321
|
-
|
|
13219
|
+
value = this.stringifyJson(json.body);
|
|
13220
|
+
break;
|
|
12322
13221
|
case 'contentData':
|
|
12323
|
-
|
|
13222
|
+
value = this.stringifyJson(json.content?.data);
|
|
13223
|
+
break;
|
|
12324
13224
|
case 'styles':
|
|
12325
|
-
|
|
13225
|
+
value = this.stringifyJson(json.styles);
|
|
13226
|
+
break;
|
|
12326
13227
|
case 'positionTop':
|
|
12327
|
-
|
|
13228
|
+
value = json.position?.top ?? '';
|
|
13229
|
+
break;
|
|
12328
13230
|
case 'positionRight':
|
|
12329
|
-
|
|
13231
|
+
value = json.position?.right ?? '';
|
|
13232
|
+
break;
|
|
12330
13233
|
case 'positionBottom':
|
|
12331
|
-
|
|
13234
|
+
value = json.position?.bottom ?? '';
|
|
13235
|
+
break;
|
|
12332
13236
|
case 'positionLeft':
|
|
12333
|
-
|
|
13237
|
+
value = json.position?.left ?? '';
|
|
13238
|
+
break;
|
|
12334
13239
|
case 'animationType':
|
|
12335
|
-
|
|
13240
|
+
value = json.animation?.type ?? '';
|
|
13241
|
+
break;
|
|
12336
13242
|
case 'animationDirection':
|
|
12337
|
-
|
|
13243
|
+
value = json.animation?.direction ?? '';
|
|
13244
|
+
break;
|
|
12338
13245
|
case 'animationDuration':
|
|
12339
|
-
|
|
13246
|
+
value = json.animation?.duration ?? '';
|
|
13247
|
+
break;
|
|
12340
13248
|
case 'contentType':
|
|
12341
|
-
|
|
13249
|
+
value = json.contentType ?? json.content?.type ?? '';
|
|
13250
|
+
break;
|
|
12342
13251
|
case 'templateId':
|
|
12343
|
-
|
|
13252
|
+
value = json.templateId ?? '';
|
|
13253
|
+
break;
|
|
12344
13254
|
case 'componentRef':
|
|
12345
|
-
|
|
13255
|
+
value = json.componentRef ?? '';
|
|
13256
|
+
break;
|
|
12346
13257
|
case 'safeHtml':
|
|
12347
|
-
|
|
13258
|
+
value = json.safeHtml ?? '';
|
|
13259
|
+
break;
|
|
12348
13260
|
case 'mode':
|
|
12349
|
-
|
|
13261
|
+
value = json.mode === 'dialog' || json.useDialog === true;
|
|
13262
|
+
break;
|
|
12350
13263
|
case 'newTab':
|
|
12351
|
-
|
|
13264
|
+
value = json.newTab ?? false;
|
|
13265
|
+
break;
|
|
12352
13266
|
case 'replaceUrl':
|
|
12353
|
-
|
|
13267
|
+
value = json.replaceUrl ?? false;
|
|
13268
|
+
break;
|
|
12354
13269
|
case 'target':
|
|
12355
|
-
|
|
13270
|
+
value = json.target ?? '';
|
|
13271
|
+
break;
|
|
12356
13272
|
case 'text':
|
|
12357
|
-
|
|
13273
|
+
value = json.text ?? (hasJson ? '' : raw);
|
|
13274
|
+
break;
|
|
12358
13275
|
case 'key':
|
|
12359
13276
|
case 'field':
|
|
12360
|
-
|
|
13277
|
+
value = json[field.key] ?? (hasJson ? '' : raw);
|
|
13278
|
+
break;
|
|
12361
13279
|
default:
|
|
12362
|
-
|
|
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
|
+
}
|
|
12363
13290
|
}
|
|
13291
|
+
return value;
|
|
12364
13292
|
}
|
|
12365
13293
|
onGlobalActionFieldChange(action, field, value, index) {
|
|
12366
13294
|
const selectedId = this.getCustomActionSelectValue(action.action);
|
|
@@ -12369,12 +13297,9 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
12369
13297
|
const schema = getGlobalActionUiSchema(selectedId);
|
|
12370
13298
|
if (!schema)
|
|
12371
13299
|
return;
|
|
13300
|
+
this.setActionFieldDraft(action, field.key, value);
|
|
12372
13301
|
if (field.type === 'json') {
|
|
12373
13302
|
const raw = String(value || '');
|
|
12374
|
-
const draftKey = this.getActionDraftKey(action);
|
|
12375
|
-
const drafts = this.actionFieldDrafts.get(draftKey) || {};
|
|
12376
|
-
drafts[field.key] = raw;
|
|
12377
|
-
this.actionFieldDrafts.set(draftKey, drafts);
|
|
12378
13303
|
if (raw.trim()) {
|
|
12379
13304
|
try {
|
|
12380
13305
|
JSON.parse(raw);
|
|
@@ -12461,6 +13386,39 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
12461
13386
|
getActionDraftKey(action) {
|
|
12462
13387
|
return String(action.id || action.action || action.label || 'action');
|
|
12463
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
|
+
}
|
|
12464
13422
|
collectGlobalActionFieldValues(action, fields) {
|
|
12465
13423
|
const values = {};
|
|
12466
13424
|
fields.forEach((field) => {
|
|
@@ -13203,7 +14161,7 @@ let ActionsEditorComponent$1 = class ActionsEditorComponent {
|
|
|
13203
14161
|
<mat-accordion multi>
|
|
13204
14162
|
@for (
|
|
13205
14163
|
customAction of actions.custom;
|
|
13206
|
-
track customAction;
|
|
14164
|
+
track customAction.id || customAction.action || $index;
|
|
13207
14165
|
let i = $index
|
|
13208
14166
|
) {
|
|
13209
14167
|
<mat-expansion-panel>
|
|
@@ -13871,7 +14829,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
13871
14829
|
<mat-accordion multi>
|
|
13872
14830
|
@for (
|
|
13873
14831
|
customAction of actions.custom;
|
|
13874
|
-
track customAction;
|
|
14832
|
+
track customAction.id || customAction.action || $index;
|
|
13875
14833
|
let i = $index
|
|
13876
14834
|
) {
|
|
13877
14835
|
<mat-expansion-panel>
|
|
@@ -14921,55 +15879,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
14921
15879
|
type: Output
|
|
14922
15880
|
}] } });
|
|
14923
15881
|
|
|
14924
|
-
/**
|
|
14925
|
-
* Deep clone and normalize a form configuration ensuring required collections
|
|
14926
|
-
* are always present to avoid unintended mutations.
|
|
14927
|
-
*/
|
|
14928
|
-
function normalizeFormConfig(config) {
|
|
14929
|
-
const cloned = config ? structuredClone(config) : createDefaultFormConfig();
|
|
14930
|
-
// Apply canonical normalization from @praxisui/core first (aliases → canônico)
|
|
14931
|
-
const canonical = normalizeFormConfig$1(cloned);
|
|
14932
|
-
const withCollections = ensureIds({
|
|
14933
|
-
...createDefaultFormConfig(),
|
|
14934
|
-
...canonical,
|
|
14935
|
-
sections: canonical.sections ?? [],
|
|
14936
|
-
fieldMetadata: canonical.fieldMetadata ?? [],
|
|
14937
|
-
formBlocksBefore: canonical.formBlocksBefore ?? [],
|
|
14938
|
-
formBlocksBeforeActions: canonical.formBlocksBeforeActions ?? [],
|
|
14939
|
-
formBlocksAfter: canonical.formBlocksAfter ?? [],
|
|
14940
|
-
});
|
|
14941
|
-
return applyDefaultSpans(withCollections);
|
|
14942
|
-
}
|
|
14943
|
-
/**
|
|
14944
|
-
* Apply default grid spans when not provided by the config.
|
|
14945
|
-
* Rule: for each row, if none of its columns declares span.md,
|
|
14946
|
-
* distribute equally across 12 columns on md+; keep xs/sm stacked (12).
|
|
14947
|
-
*/
|
|
14948
|
-
function applyDefaultSpans(config) {
|
|
14949
|
-
const next = structuredClone(config);
|
|
14950
|
-
for (const section of next.sections || []) {
|
|
14951
|
-
for (const row of section.rows || []) {
|
|
14952
|
-
const cols = row.columns || [];
|
|
14953
|
-
if (!cols.length)
|
|
14954
|
-
continue;
|
|
14955
|
-
const hasAnyMdSpan = cols.some((c) => c.span?.md != null);
|
|
14956
|
-
if (hasAnyMdSpan)
|
|
14957
|
-
continue;
|
|
14958
|
-
const per = Math.max(1, Math.floor(12 / cols.length));
|
|
14959
|
-
for (const col of cols) {
|
|
14960
|
-
const span = col.span || {};
|
|
14961
|
-
span.xs = 12;
|
|
14962
|
-
span.sm = 12;
|
|
14963
|
-
span.md = per;
|
|
14964
|
-
span.lg = span.lg ?? per;
|
|
14965
|
-
span.xl = span.xl ?? per;
|
|
14966
|
-
col.span = span;
|
|
14967
|
-
}
|
|
14968
|
-
}
|
|
14969
|
-
}
|
|
14970
|
-
return next;
|
|
14971
|
-
}
|
|
14972
|
-
|
|
14973
15882
|
/**
|
|
14974
15883
|
* Converts a RuleBuilderState produced by the visual builder into
|
|
14975
15884
|
* an array of FormLayoutRule objects understood by the dynamic form.
|
|
@@ -15355,11 +16264,15 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15355
16264
|
ruleBuilderState;
|
|
15356
16265
|
currentRules = [];
|
|
15357
16266
|
initialConfig;
|
|
16267
|
+
initialDocument;
|
|
16268
|
+
openedWithCanonicalDocument;
|
|
15358
16269
|
backConfig;
|
|
16270
|
+
includeBackConfigBlock = false;
|
|
15359
16271
|
formId;
|
|
15360
16272
|
componentKeyId;
|
|
15361
16273
|
// Input patch: controls like mode (dados)
|
|
15362
16274
|
inputMode = 'create';
|
|
16275
|
+
includeBindingsBlock = false;
|
|
15363
16276
|
editorMode;
|
|
15364
16277
|
// Indica se o editor foi aberto com o formulário em modo apresentação
|
|
15365
16278
|
isPresentationMode = false;
|
|
@@ -15395,6 +16308,8 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15395
16308
|
snoozeMs: 24 * 60 * 60 * 1000,
|
|
15396
16309
|
autoOpenSettingsOnOutdated: false,
|
|
15397
16310
|
};
|
|
16311
|
+
includePresentationBlock = false;
|
|
16312
|
+
includeSchemaPrefsBlock = false;
|
|
15398
16313
|
// Read-only current server meta (if any)
|
|
15399
16314
|
serverMeta;
|
|
15400
16315
|
destroy$ = new Subject();
|
|
@@ -15421,11 +16336,18 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15421
16336
|
this.settingsPanel = settingsPanel;
|
|
15422
16337
|
this.cdr = cdr;
|
|
15423
16338
|
const data = injectedData;
|
|
15424
|
-
|
|
15425
|
-
|
|
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');
|
|
15426
16347
|
this.formId = data?.formId;
|
|
15427
16348
|
this.componentKeyId = data?.componentKeyId;
|
|
15428
|
-
this.
|
|
16349
|
+
this.includeBindingsBlock = Object.prototype.hasOwnProperty.call(initialDocument.bindings || {}, 'mode');
|
|
16350
|
+
this.inputMode = this.normalizeMode(initialDocument.bindings?.mode);
|
|
15429
16351
|
this.editorMode = this.inputMode;
|
|
15430
16352
|
// Flag de contexto do host (praxis-dynamic-form)
|
|
15431
16353
|
this.isPresentationMode = !!data?.presentationMode;
|
|
@@ -15439,9 +16361,11 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15439
16361
|
});
|
|
15440
16362
|
}
|
|
15441
16363
|
catch { }
|
|
15442
|
-
|
|
15443
|
-
|
|
15444
|
-
|
|
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);
|
|
15445
16369
|
this.editedConfig = structuredClone(this.initialConfig);
|
|
15446
16370
|
this.configService.loadConfig(structuredClone(this.initialConfig));
|
|
15447
16371
|
}
|
|
@@ -15461,11 +16385,19 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15461
16385
|
this.isBusy$.next(true);
|
|
15462
16386
|
try {
|
|
15463
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);
|
|
15464
16396
|
try {
|
|
15465
16397
|
this.debugLog('[FormConfigEditor] reset()');
|
|
15466
16398
|
}
|
|
15467
16399
|
catch { }
|
|
15468
|
-
this.jsonEditor?.
|
|
16400
|
+
this.jsonEditor?.updateJsonFromDocument(this.buildAuthoringDocument());
|
|
15469
16401
|
this.ruleBuilderState =
|
|
15470
16402
|
this.editedConfig.formRulesState ||
|
|
15471
16403
|
formLayoutRulesToBuilderState(this.editedConfig.formRules || []);
|
|
@@ -15481,11 +16413,13 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15481
16413
|
}
|
|
15482
16414
|
// Public: referenced in template for (ngModelChange) handlers
|
|
15483
16415
|
updateDirtyState(reason = 'unknown') {
|
|
15484
|
-
|
|
15485
|
-
const
|
|
15486
|
-
const b = this.stripLegacy(structuredClone(this.editedConfig));
|
|
16416
|
+
const a = serializeDynamicFormAuthoringDocument(this.initialDocument);
|
|
16417
|
+
const b = serializeDynamicFormAuthoringDocument(this.buildAuthoringDocument());
|
|
15487
16418
|
const hasChanges = stableStringify(a) !== stableStringify(b);
|
|
15488
16419
|
this.isDirty$.next(hasChanges);
|
|
16420
|
+
if (!reason.startsWith('json-')) {
|
|
16421
|
+
this.jsonEditor?.updateJsonFromDocument(this.buildAuthoringDocument());
|
|
16422
|
+
}
|
|
15489
16423
|
try {
|
|
15490
16424
|
if (hasChanges) {
|
|
15491
16425
|
const diffs = diffObjects(a, b, [], 12);
|
|
@@ -15500,51 +16434,62 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15500
16434
|
// Para outras validações futuras, verificar se não há validação JSON em andamento
|
|
15501
16435
|
}
|
|
15502
16436
|
getSettingsValue() {
|
|
15503
|
-
const
|
|
16437
|
+
const document = this.buildAuthoringDocument();
|
|
15504
16438
|
try {
|
|
15505
|
-
this.debugLog('[FormConfigEditor] getSettingsValue()', { sections:
|
|
16439
|
+
this.debugLog('[FormConfigEditor] getSettingsValue()', { sections: document.config.sections?.length || 0 });
|
|
15506
16440
|
}
|
|
15507
16441
|
catch { }
|
|
15508
|
-
return
|
|
15509
|
-
formConfig: sanitized,
|
|
15510
|
-
backConfig: this.backConfig,
|
|
15511
|
-
presentation: this.presentationPrefs,
|
|
15512
|
-
inputsPatch: { mode: this.inputMode },
|
|
15513
|
-
schemaPrefs: this.schemaPrefs,
|
|
15514
|
-
};
|
|
16442
|
+
return serializeDynamicFormAuthoringDocument(document);
|
|
15515
16443
|
}
|
|
15516
16444
|
onSave() {
|
|
15517
16445
|
this.isBusy$.next(true);
|
|
15518
16446
|
try {
|
|
15519
|
-
const
|
|
16447
|
+
const document = this.buildAuthoringDocument();
|
|
15520
16448
|
try {
|
|
15521
|
-
this.debugLog('[FormConfigEditor] onSave()', { sections:
|
|
16449
|
+
this.debugLog('[FormConfigEditor] onSave()', { sections: document.config.sections?.length || 0 });
|
|
15522
16450
|
}
|
|
15523
16451
|
catch { }
|
|
15524
|
-
return
|
|
15525
|
-
formConfig: sanitized,
|
|
15526
|
-
backConfig: this.backConfig,
|
|
15527
|
-
presentation: this.presentationPrefs,
|
|
15528
|
-
inputsPatch: { mode: this.inputMode },
|
|
15529
|
-
schemaPrefs: this.schemaPrefs,
|
|
15530
|
-
};
|
|
16452
|
+
return serializeDynamicFormAuthoringDocument(document);
|
|
15531
16453
|
}
|
|
15532
16454
|
finally {
|
|
15533
16455
|
this.isBusy$.next(false);
|
|
15534
16456
|
}
|
|
15535
16457
|
}
|
|
15536
16458
|
// (Resumo/ações rápidas removidos da aba Geral conforme orientação)
|
|
15537
|
-
onJsonConfigChange(
|
|
15538
|
-
|
|
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);
|
|
15539
16484
|
this.ensureHints();
|
|
15540
16485
|
try {
|
|
15541
16486
|
this.debugLog('[FormConfigEditor] onJsonConfigChange()', { sections: this.editedConfig.sections?.length || 0 });
|
|
15542
16487
|
}
|
|
15543
16488
|
catch { }
|
|
15544
|
-
this.ruleBuilderConfig = this.createRuleBuilderConfig(
|
|
16489
|
+
this.ruleBuilderConfig = this.createRuleBuilderConfig(document.config.fieldMetadata || [], document.config.sections || [], document.config.actions);
|
|
15545
16490
|
this.ruleBuilderState =
|
|
15546
|
-
|
|
15547
|
-
formLayoutRulesToBuilderState(
|
|
16491
|
+
document.config.formRulesState ||
|
|
16492
|
+
formLayoutRulesToBuilderState(document.config.formRules || []);
|
|
15548
16493
|
this.currentRules = ruleBuilderStateToFormLayoutRules(this.ruleBuilderState);
|
|
15549
16494
|
this.lastRulesSignature = this.computeRulesSignature(this.ruleBuilderState);
|
|
15550
16495
|
this.updateDirtyState('json-change');
|
|
@@ -15621,6 +16566,37 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15621
16566
|
this.currentRules = rules;
|
|
15622
16567
|
this.updateDirtyState('rules-change');
|
|
15623
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
|
+
}
|
|
15624
16600
|
createRuleBuilderConfig(fieldMetadata, sections = [], actions) {
|
|
15625
16601
|
const mapped = this.mapMetadataToSchema(fieldMetadata, sections, actions);
|
|
15626
16602
|
return {
|
|
@@ -15794,7 +16770,7 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15794
16770
|
this.editedConfig = { ...this.editedConfig, fieldMetadata: next };
|
|
15795
16771
|
// Keep JSON editor view in sync when open
|
|
15796
16772
|
try {
|
|
15797
|
-
this.jsonEditor?.
|
|
16773
|
+
this.jsonEditor?.updateJsonFromDocument(this.buildAuthoringDocument());
|
|
15798
16774
|
}
|
|
15799
16775
|
catch { }
|
|
15800
16776
|
this.updateDirtyState();
|
|
@@ -15832,7 +16808,7 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15832
16808
|
// Defaults are injected by normalization on save/apply; keep a local reset to blank to let defaults win.
|
|
15833
16809
|
current.hints = undefined;
|
|
15834
16810
|
// Keep JSON editor in sync
|
|
15835
|
-
this.jsonEditor?.
|
|
16811
|
+
this.jsonEditor?.updateJsonFromDocument(this.buildAuthoringDocument());
|
|
15836
16812
|
this.updateDirtyState('hints-restore');
|
|
15837
16813
|
}
|
|
15838
16814
|
catch { }
|
|
@@ -15842,7 +16818,11 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15842
16818
|
if (!key)
|
|
15843
16819
|
return;
|
|
15844
16820
|
try {
|
|
15845
|
-
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);
|
|
15846
16826
|
if (pres) {
|
|
15847
16827
|
this.presentationPrefs = {
|
|
15848
16828
|
labelPosition: pres.labelPosition === 'left' ? 'left' : 'above',
|
|
@@ -15855,7 +16835,11 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15855
16835
|
valueAlign: pres.valueAlign || 'start',
|
|
15856
16836
|
};
|
|
15857
16837
|
}
|
|
15858
|
-
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);
|
|
15859
16843
|
if (prefs && typeof prefs === 'object') {
|
|
15860
16844
|
const allowed = {};
|
|
15861
16845
|
const n = prefs.notifyIfOutdated;
|
|
@@ -15903,8 +16887,75 @@ class PraxisDynamicFormConfigEditor {
|
|
|
15903
16887
|
return value;
|
|
15904
16888
|
return 'create';
|
|
15905
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
|
+
}
|
|
15906
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 });
|
|
15907
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PraxisDynamicFormConfigEditor, isStandalone: true, selector: "praxis-dynamic-form-config-editor", providers: [FormConfigService], viewQueries: [{ propertyName: "jsonEditor", first: true, predicate: JsonConfigEditorComponent, descendants: true }], ngImport: i0, template: "<mat-tab-group class=\"config-tabs\">\n <mat-tab label=\"Geral\">\n <div class=\"tab-content\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Dados do formul\u00E1rio</h3>\n <p class=\"text-caption\">Defina o modo de dados e o contexto de edi\u00E7\u00E3o.</p>\n </div>\n </div>\n <div class=\"form-grid\">\n <div class=\"form-row\">\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Modo de dados</mat-label>\n <mat-select [(ngModel)]=\"inputMode\">\n <mat-option value=\"create\">create</mat-option>\n <mat-option value=\"edit\">edit</mat-option>\n <mat-option value=\"view\">view</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div class=\"section-help\">\n <span class=\"section-help__label\">Entenda:</span>\n <button\n mat-icon-button\n class=\"help-icon-button\"\n type=\"button\"\n [matTooltip]=\"'O mode controla o fluxo (create/edit/view). editModeEnabled s\u00F3 libera edi\u00E7\u00E3o do layout e n\u00E3o altera o mode.'\"\n matTooltipPosition=\"above\"\n >\n <mat-icon>help_outline</mat-icon>\n </button>\n </div>\n </div>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"Verifica\u00E7\u00E3o de Schema\">\n <div class=\"tab-content\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Valida\u00E7\u00E3o de schema</h3>\n <p class=\"text-caption\">Controle avisos quando o schema do servidor mudar.</p>\n </div>\n </div>\n <div class=\"form-grid\">\n <div class=\"form-row\">\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Notificar quando desatualizado</mat-label>\n <mat-select [(ngModel)]=\"schemaPrefs.notifyIfOutdated\">\n <mat-option value=\"both\">Banner + Snackbar</mat-option>\n <mat-option value=\"inline\">Somente Banner</mat-option>\n <mat-option value=\"snackbar\">Somente Snackbar</mat-option>\n <mat-option value=\"none\">Nenhum</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div class=\"form-row\">\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Intervalo de sil\u00EAncio (ms)</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"schemaPrefs.snoozeMs\" />\n </mat-form-field>\n </div>\n <div class=\"form-row\">\n <mat-slide-toggle [(ngModel)]=\"schemaPrefs.autoOpenSettingsOnOutdated\">Abrir configura\u00E7\u00F5es\n automaticamente</mat-slide-toggle>\n </div>\n <div class=\"form-row\" *ngIf=\"serverMeta\">\n <div class=\"meta-card\">\n <div class=\"meta-row\">\n <span>Server hash</span>\n <strong>{{ serverMeta.serverHash || '\u2014' }}</strong>\n </div>\n <div class=\"meta-row\">\n <span>\u00DAltima verifica\u00E7\u00E3o</span>\n <strong>{{ serverMeta.lastVerifiedAt || '\u2014' }}</strong>\n </div>\n </div>\n </div>\n </div>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"Layout\">\n <div class=\"tab-content tab-content--full\">\n <div class=\"section-header section-header--compact\">\n <div>\n <h3>Layout do formul\u00E1rio</h3>\n <p class=\"text-caption\">Organize se\u00E7\u00F5es, linhas e campos com drag & drop.</p>\n </div>\n </div>\n <div class=\"section-body section-body--flex\">\n <praxis-layout-editor [config]=\"editedConfig\" (configChange)=\"onConfigChange($event)\"\n (select)=\"onLayoutSelect($event)\"></praxis-layout-editor>\n </div>\n </div>\n </mat-tab>\n <mat-tab label=\"Hooks\">\n <div class=\"tab-content\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Hooks de ciclo de vida</h3>\n <p class=\"text-caption\">Automatize a\u00E7\u00F5es nos eventos do formul\u00E1rio.</p>\n </div>\n </div>\n <praxis-hooks-editor [config]=\"editedConfig\" (configChange)=\"onConfigChange($event)\"></praxis-hooks-editor>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab *ngIf=\"isPresentationMode\" label=\"Modo Apresenta\u00E7\u00E3o\">\n <div class=\"tab-content\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Modo Apresenta\u00E7\u00E3o</h3>\n <p class=\"text-caption\">Configura\u00E7\u00F5es de exibi\u00E7\u00E3o aplicadas somente ao modo apresenta\u00E7\u00E3o.</p>\n </div>\n </div>\n\n <div class=\"presentation-layout\">\n <div class=\"presentation-controls\">\n <div class=\"control\">\n <label class=\"control__label\">Posi\u00E7\u00E3o do r\u00F3tulo</label>\n <mat-button-toggle-group [(ngModel)]=\"presentationPrefs.labelPosition\" aria-label=\"Posi\u00E7\u00E3o do r\u00F3tulo\"\n class=\"toggle-group\">\n <mat-button-toggle value=\"above\">\n <mat-icon>view_stream</mat-icon>\n <span class=\"sr-only\">Acima</span>\n </mat-button-toggle>\n <mat-button-toggle value=\"left\">\n <mat-icon>view_column</mat-icon>\n <span class=\"sr-only\">\u00C0 esquerda</span>\n </mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n\n <div class=\"control\">\n <label class=\"control__label\">Densidade</label>\n <mat-button-toggle-group [(ngModel)]=\"presentationPrefs.density\" aria-label=\"Densidade\"\n class=\"toggle-group\">\n <mat-button-toggle value=\"comfortable\">Confort\u00E1vel</mat-button-toggle>\n <mat-button-toggle value=\"cozy\">Intermedi\u00E1ria</mat-button-toggle>\n <mat-button-toggle value=\"compact\">Compacta</mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n\n <div class=\"control\">\n <label class=\"control__label\">Alinhamento do r\u00F3tulo</label>\n <mat-button-toggle-group [(ngModel)]=\"presentationPrefs.labelAlign\" aria-label=\"Alinhamento do r\u00F3tulo\"\n class=\"toggle-group\">\n <mat-button-toggle value=\"start\"><mat-icon>format_align_left</mat-icon></mat-button-toggle>\n <mat-button-toggle value=\"center\"><mat-icon>format_align_center</mat-icon></mat-button-toggle>\n <mat-button-toggle value=\"end\"><mat-icon>format_align_right</mat-icon></mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n\n <div class=\"control\">\n <label class=\"control__label\">Alinhamento do valor</label>\n <mat-button-toggle-group [(ngModel)]=\"presentationPrefs.valueAlign\" aria-label=\"Alinhamento do valor\"\n class=\"toggle-group\">\n <mat-button-toggle value=\"start\"><mat-icon>format_align_left</mat-icon></mat-button-toggle>\n <mat-button-toggle value=\"center\"><mat-icon>format_align_center</mat-icon></mat-button-toggle>\n <mat-button-toggle value=\"end\"><mat-icon>format_align_right</mat-icon></mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n\n <div class=\"control\">\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Tamanho do r\u00F3tulo</mat-label>\n <input matInput type=\"number\" min=\"10\" [(ngModel)]=\"presentationPrefs.labelFontSize\"\n placeholder=\"ex.: 12\" />\n <span matSuffix>px</span>\n </mat-form-field>\n </div>\n\n <div class=\"control\">\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Tamanho do valor</mat-label>\n <input matInput type=\"number\" min=\"10\" [(ngModel)]=\"presentationPrefs.valueFontSize\"\n placeholder=\"ex.: 16\" />\n <span matSuffix>px</span>\n </mat-form-field>\n </div>\n\n <div class=\"control\">\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Largura do r\u00F3tulo (label \u00E0 esquerda)</mat-label>\n <input matInput type=\"number\" min=\"60\" [(ngModel)]=\"presentationPrefs.labelWidth\"\n placeholder=\"ex.: 140\" />\n <span matSuffix>px</span>\n </mat-form-field>\n </div>\n\n <div class=\"control control--inline\">\n <mat-slide-toggle [(ngModel)]=\"presentationPrefs.compact\">\n Modo compacto\n </mat-slide-toggle>\n <mat-checkbox [(ngModel)]=\"truncatePreview\">\n Truncar valores\n </mat-checkbox>\n </div>\n </div>\n\n <div class=\"presentation-preview\">\n <h4>Pr\u00E9\u2011visualiza\u00E7\u00E3o</h4>\n <div class=\"preview-card\">\n <div class=\"praxis-dynamic-form presentation-mode\" [ngClass]=\"{\n 'pres-compact': !!presentationPrefs.compact,\n 'pres-label-left': presentationPrefs.labelPosition === 'left',\n 'pres-label-above': presentationPrefs.labelPosition === 'above',\n 'pres-density-compact': presentationPrefs.density === 'compact',\n 'pres-density-cozy': presentationPrefs.density === 'cozy'\n }\" [style.--pfx-pres-label-size]=\"(presentationPrefs.labelFontSize || 12) + 'px'\"\n [style.--pfx-pres-value-size]=\"(presentationPrefs.valueFontSize || 16) + 'px'\"\n [style.--pfx-pres-label-width]=\"(presentationPrefs.labelWidth || 140) + 'px'\"\n [style.--pfx-pres-label-align]=\"presentationPrefs.labelAlign || 'start'\"\n [style.--pfx-pres-value-align]=\"presentationPrefs.valueAlign || 'start'\">\n <div class=\"preview-row\" *ngFor=\"let item of previewItems\">\n <span class=\"praxis-presentation__label\">{{ item.label }}</span>\n <span class=\"praxis-presentation__value\" [title]=\"truncatePreview ? item.value : null\"\n [style.maxWidth]=\"truncatePreview ? '280px' : null\"\n [style.overflow]=\"truncatePreview ? 'hidden' : null\"\n [style.textOverflow]=\"truncatePreview ? 'ellipsis' : null\"\n [style.whiteSpace]=\"truncatePreview ? 'nowrap' : 'normal'\">\n {{ item.value }}\n </span>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"section-help\">\n <span class=\"section-help__label\">Entenda:</span>\n <button\n mat-icon-button\n class=\"help-icon-button\"\n type=\"button\"\n [matTooltip]=\"'Ajustes aplicam somente ao Modo Apresenta\u00E7\u00E3o e s\u00E3o salvos por formul\u00E1rio (via CSS/Classes).'\"\n matTooltipPosition=\"above\"\n >\n <mat-icon>help_outline</mat-icon>\n </button>\n </div>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"Comportamento\">\n <div class=\"tab-content\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Comportamento</h3>\n <p class=\"text-caption\">Ajuste valida\u00E7\u00F5es e respostas do formul\u00E1rio.</p>\n </div>\n </div>\n <praxis-behavior-editor [config]=\"editedConfig\" (configChange)=\"onConfigChange($event)\"></praxis-behavior-editor>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"Dicas e Tooltips\">\n <div class=\"tab-content tab-content--dense\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Dicas dos modos</h3>\n <p class=\"text-caption\">Personalize textos de apoio para modos de dados e estados de UI.</p>\n </div>\n </div>\n <p class=\"text-caption\">Personalize os textos usados como tooltip/hint para modos de dados e modos globais de\n UI.</p>\n\n <div class=\"grid-2-cols grid-2-cols--compact\">\n <section>\n <h4>Modos de dados</h4>\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Criar</mat-label>\n <input matInput [(ngModel)]=\"editedConfig.hints!.dataModes.create\"\n (ngModelChange)=\"updateDirtyState('hints-data-create')\" placeholder=\"Ex.: Preencha os campos obrigat\u00F3rios.\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Editar</mat-label>\n <input matInput [(ngModel)]=\"editedConfig.hints!.dataModes.edit\"\n (ngModelChange)=\"updateDirtyState('hints-data-edit')\" placeholder=\"Ex.: Revise os dados antes de salvar.\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Visualizar</mat-label>\n <input matInput [(ngModel)]=\"editedConfig.hints!.dataModes.view\"\n (ngModelChange)=\"updateDirtyState('hints-data-view')\" placeholder=\"Ex.: Visualiza\u00E7\u00E3o somente leitura.\" />\n </mat-form-field>\n </section>\n\n <section>\n <h4>Modos de UI</h4>\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Apresenta\u00E7\u00E3o</mat-label>\n <textarea matInput rows=\"2\" [(ngModel)]=\"editedConfig.hints!.uiModes.presentation\"\n (ngModelChange)=\"updateDirtyState('hints-ui-presentation')\" placeholder=\"Ex.: Modo compacto para leitura r\u00E1pida.\"></textarea>\n </mat-form-field>\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Leitura (readonly)</mat-label>\n <textarea matInput rows=\"2\" [(ngModel)]=\"editedConfig.hints!.uiModes.readonly\"\n (ngModelChange)=\"updateDirtyState('hints-ui-readonly')\" placeholder=\"Ex.: Campos bloqueados para edi\u00E7\u00E3o.\"></textarea>\n </mat-form-field>\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Desabilitado</mat-label>\n <textarea matInput rows=\"2\" [(ngModel)]=\"editedConfig.hints!.uiModes.disabled\"\n (ngModelChange)=\"updateDirtyState('hints-ui-disabled')\" placeholder=\"Ex.: Fun\u00E7\u00E3o indispon\u00EDvel no momento.\"></textarea>\n </mat-form-field>\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Vis\u00EDvel</mat-label>\n <textarea matInput rows=\"2\" [(ngModel)]=\"editedConfig.hints!.uiModes.visible\"\n (ngModelChange)=\"updateDirtyState('hints-ui-visible')\" placeholder=\"Ex.: Campo exibido conforme permiss\u00F5es.\"></textarea>\n </mat-form-field>\n </section>\n </div>\n\n <div class=\"actions-row\">\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"restoreHintsDefaults()\">\n <mat-icon>restore</mat-icon>\n Restaurar padr\u00F5es\n </button>\n </div>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"A\u00E7\u00F5es\">\n <div class=\"tab-content\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>A\u00E7\u00F5es do formul\u00E1rio</h3>\n <p class=\"text-caption\">Configure bot\u00F5es padr\u00E3o e a\u00E7\u00F5es customizadas.</p>\n </div>\n </div>\n <praxis-actions-editor [config]=\"editedConfig\" (configChange)=\"onConfigChange($event)\"></praxis-actions-editor>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"Regras\">\n <div class=\"tab-content visual-builder-content\">\n <div class=\"builder-header\">\n <div>\n <h3>Regras visuais</h3>\n <p class=\"text-caption\">Defina visibilidade, estilos e rea\u00E7\u00F5es baseadas em condi\u00E7\u00F5es.</p>\n </div>\n </div>\n <praxis-visual-builder [config]=\"ruleBuilderConfig\" [initialRules]=\"ruleBuilderState\"\n (rulesChanged)=\"onRulesChanged($event)\"></praxis-visual-builder>\n </div>\n </mat-tab>\n <mat-tab label=\"Cascatas\">\n <div class=\"tab-content\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Depend\u00EAncias entre campos</h3>\n <p class=\"text-caption\">Configure cascatas e carregamento entre campos.</p>\n </div>\n </div>\n <praxis-cascade-manager-tab [fields]=\"editedConfig.fieldMetadata || []\"\n (apply)=\"onCascadeApply($event)\"></praxis-cascade-manager-tab>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"Mensagens\">\n <div class=\"tab-content\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Mensagens e feedback</h3>\n <p class=\"text-caption\">Padronize textos de sucesso, erro e confirma\u00E7\u00F5es.</p>\n </div>\n </div>\n <praxis-messages-editor [config]=\"editedConfig\" (configChange)=\"onConfigChange($event)\"></praxis-messages-editor>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"Navega\u00E7\u00E3o\">\n <div class=\"tab-content\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Navega\u00E7\u00E3o de retorno</h3>\n <p class=\"text-caption\">Defina o comportamento do bot\u00E3o Voltar em fluxos CRUD.</p>\n </div>\n </div>\n <div class=\"form-grid\">\n <div class=\"form-row\">\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Rota de retorno</mat-label>\n <input matInput id=\"returnTo\" [(ngModel)]=\"backConfig!.returnTo\" placeholder=\"/funcionarios\" />\n </mat-form-field>\n </div>\n <div class=\"form-row\">\n <mat-slide-toggle [(ngModel)]=\"backConfig!.confirmOnDirty\">\n Confirmar ao sair com altera\u00E7\u00F5es\n </mat-slide-toggle>\n </div>\n </div>\n <div class=\"section-help\">\n <span class=\"section-help__label\">Observa\u00E7\u00E3o:</span>\n <button\n mat-icon-button\n class=\"help-icon-button\"\n type=\"button\"\n [matTooltip]=\"'Afeta o fluxo de navega\u00E7\u00E3o do CRUD e pode ser sobreposto por metadados do recurso.'\"\n matTooltipPosition=\"above\"\n >\n <mat-icon>help_outline</mat-icon>\n </button>\n </div>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"JSON\">\n <div class=\"tab-content\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Configura\u00E7\u00E3o JSON</h3>\n <p class=\"text-caption\">Edi\u00E7\u00E3o avan\u00E7ada do schema do formul\u00E1rio.</p>\n </div>\n </div>\n <form-json-config-editor [config]=\"editedConfig\" (configChange)=\"onJsonConfigChange($event)\"\n (validationChange)=\"onJsonValidationChange($event)\" (editorEvent)=\"onJsonEditorEvent($event)\">\n </form-json-config-editor>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n</mat-tab-group>\n", styles: [":host{display:block;height:100%}.config-tabs{height:100%}::ng-deep .mat-mdc-tab-group.config-tabs{display:flex;flex-direction:column}::ng-deep .mat-mdc-tab-group.config-tabs .mat-mdc-tab-body-wrapper{flex:1;min-height:0}::ng-deep .mat-mdc-tab-group.config-tabs .mat-mdc-tab-body-content{height:100%;overflow:auto;display:flex;flex-direction:column;padding:0!important}.tab-content{padding:16px 20px 20px;flex:1}.form-row{display:block;margin-bottom:12px}.form-grid{display:grid;gap:12px}.section-help{display:inline-flex;align-items:center;gap:8px;color:var(--md-sys-color-on-surface-variant);font-size:12px}.section-help__label{font-weight:500}.help-icon-button{--mdc-icon-button-state-layer-size: 28px;--mdc-icon-button-icon-size: 18px;width:28px;height:28px;padding:0;display:inline-flex;align-items:center;justify-content:center}.help-icon-button mat-icon{font-size:18px;width:18px;height:18px}.grid-2-cols{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:16px}.grid-2-cols--compact{gap:12px}.w-100{width:100%}.visual-builder-content{padding:0!important;height:100%;overflow:hidden;display:flex;flex-direction:column}.visual-builder-content>praxis-visual-builder{flex:1;min-height:0}.json-field{width:100%}.presentation-layout{display:flex;gap:20px;align-items:flex-start}.presentation-controls{flex:1 1 60%;display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:14px 18px}.control{display:flex;flex-direction:column}.control__label{font-size:12px;color:var(--md-sys-color-on-surface-variant);margin-bottom:6px}.toggle-group{width:100%}.control--inline{grid-column:1/-1;display:flex;gap:16px;align-items:center}.presentation-preview{flex:0 0 360px;position:sticky;top:16px}.preview-card{border:1px solid var(--md-sys-color-outline);border-radius:12px;padding:14px;background:var(--md-sys-color-surface-container)}.preview-row{display:flex;gap:8px;padding:6px 0;border-bottom:1px dashed var(--md-sys-color-outline-variant)}.preview-row:last-child{border-bottom:none}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.presentation-preview .praxis-presentation__label{color:var(--md-sys-color-on-surface-variant)}.presentation-preview .praxis-presentation__value{color:var(--md-sys-color-on-surface)}.presentation-preview .presentation-mode.pres-density-cozy .preview-row{padding:6px 0}.presentation-preview .presentation-mode.pres-density-compact .preview-row,.presentation-preview .presentation-mode.pres-compact .preview-row{padding:2px 0}.presentation-preview .presentation-mode.pres-label-left .preview-row{display:grid;grid-template-columns:var(--pfx-pres-label-width, 140px) 1fr;align-items:baseline;column-gap:10px}.presentation-preview .presentation-mode.pres-label-left .praxis-presentation__label{margin:0;text-align:var(--pfx-pres-label-align, start)}.presentation-preview .presentation-mode .praxis-presentation__value{text-align:var(--pfx-pres-value-align, start)}.presentation-preview h4{margin:0 0 8px;color:var(--md-sys-color-on-surface);font-size:1rem}.section-card{border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container);box-shadow:var(--md-sys-elevation-level1, none)}.section-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:12px}.tab-content--dense{padding:12px 16px 16px}.tab-content--dense .section-header,.tab-content--dense .form-row{margin-bottom:8px}.section-header h3{margin:0;font-size:1.05rem;color:var(--md-sys-color-on-surface)}.text-caption{margin:6px 0 0;font-size:.9rem;color:var(--md-sys-color-on-surface-variant)}.section-header--compact{margin-bottom:8px}.section-body{display:block}.section-body--flex{flex:1;min-height:0;display:flex;flex-direction:column}.builder-header{padding:12px 16px;border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface)}.builder-header h3{margin:0;font-size:1rem;color:var(--md-sys-color-on-surface)}.hint{margin:8px 0 0;padding:10px 12px;border-radius:10px;border:1px dashed var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface-variant)}.meta-card{display:grid;gap:6px;padding:10px 12px;border-radius:10px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface)}.meta-row{display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:.9rem;color:var(--md-sys-color-on-surface-variant)}.meta-row strong{color:var(--md-sys-color-on-surface);font-weight:600}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i7$2.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i7$2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatCardModule }, { kind: "component", type: i8$1.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i8$1.MatCardContent, selector: "mat-card-content" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6$4.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i6$4.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i12.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i6$3.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatButtonToggleModule }, { kind: "directive", type: i7$1.MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "disabledInteractive", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "component", type: i7$1.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled", "disabledInteractive"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i9.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: JsonConfigEditorComponent, selector: "form-json-config-editor", inputs: ["config"], outputs: ["configChange", "validationChange", "editorEvent"] }, { kind: "component", type: LayoutEditorComponent, selector: "praxis-layout-editor", inputs: ["config"], outputs: ["configChange", "select"] }, { kind: "component", type: BehaviorEditorComponent, selector: "praxis-behavior-editor", inputs: ["config"], outputs: ["configChange"] }, { kind: "component", type: ActionsEditorComponent$1, selector: "praxis-actions-editor", inputs: ["config"], outputs: ["configChange"] }, { kind: "component", type: MessagesEditorComponent, selector: "praxis-messages-editor", inputs: ["config"], outputs: ["configChange"] }, { kind: "component", type: HooksEditorComponent, selector: "praxis-hooks-editor", inputs: ["config"], outputs: ["configChange"] }, { kind: "component", type: PraxisVisualBuilder, selector: "praxis-visual-builder", inputs: ["config", "initialRules"], outputs: ["rulesChanged", "exportRequested", "importRequested"] }, { kind: "component", type: CascadeManagerTabComponent, selector: "praxis-cascade-manager-tab", inputs: ["fields", "connections"], outputs: ["apply", "cancel"] }] });
|
|
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"] }] });
|
|
15908
16959
|
}
|
|
15909
16960
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisDynamicFormConfigEditor, decorators: [{
|
|
15910
16961
|
type: Component,
|
|
@@ -15930,7 +16981,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
15930
16981
|
HooksEditorComponent,
|
|
15931
16982
|
PraxisVisualBuilder,
|
|
15932
16983
|
CascadeManagerTabComponent,
|
|
15933
|
-
], providers: [FormConfigService], template: "<mat-tab-group class=\"config-tabs\">\n <mat-tab label=\"Geral\">\n <div class=\"tab-content\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Dados do formul\u00E1rio</h3>\n <p class=\"text-caption\">Defina o modo de dados e o contexto de edi\u00E7\u00E3o.</p>\n </div>\n </div>\n <div class=\"form-grid\">\n <div class=\"form-row\">\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Modo de dados</mat-label>\n <mat-select [(ngModel)]=\"inputMode\">\n <mat-option value=\"create\">create</mat-option>\n <mat-option value=\"edit\">edit</mat-option>\n <mat-option value=\"view\">view</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div class=\"section-help\">\n <span class=\"section-help__label\">Entenda:</span>\n <button\n mat-icon-button\n class=\"help-icon-button\"\n type=\"button\"\n [matTooltip]=\"'O mode controla o fluxo (create/edit/view). editModeEnabled s\u00F3 libera edi\u00E7\u00E3o do layout e n\u00E3o altera o mode.'\"\n matTooltipPosition=\"above\"\n >\n <mat-icon>help_outline</mat-icon>\n </button>\n </div>\n </div>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"Verifica\u00E7\u00E3o de Schema\">\n <div class=\"tab-content\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Valida\u00E7\u00E3o de schema</h3>\n <p class=\"text-caption\">Controle avisos quando o schema do servidor mudar.</p>\n </div>\n </div>\n <div class=\"form-grid\">\n <div class=\"form-row\">\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Notificar quando desatualizado</mat-label>\n <mat-select [(ngModel)]=\"schemaPrefs.notifyIfOutdated\">\n <mat-option value=\"both\">Banner + Snackbar</mat-option>\n <mat-option value=\"inline\">Somente Banner</mat-option>\n <mat-option value=\"snackbar\">Somente Snackbar</mat-option>\n <mat-option value=\"none\">Nenhum</mat-option>\n </mat-select>\n </mat-form-field>\n </div>\n <div class=\"form-row\">\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Intervalo de sil\u00EAncio (ms)</mat-label>\n <input matInput type=\"number\" [(ngModel)]=\"schemaPrefs.snoozeMs\" />\n </mat-form-field>\n </div>\n <div class=\"form-row\">\n <mat-slide-toggle [(ngModel)]=\"schemaPrefs.autoOpenSettingsOnOutdated\">Abrir configura\u00E7\u00F5es\n automaticamente</mat-slide-toggle>\n </div>\n <div class=\"form-row\" *ngIf=\"serverMeta\">\n <div class=\"meta-card\">\n <div class=\"meta-row\">\n <span>Server hash</span>\n <strong>{{ serverMeta.serverHash || '\u2014' }}</strong>\n </div>\n <div class=\"meta-row\">\n <span>\u00DAltima verifica\u00E7\u00E3o</span>\n <strong>{{ serverMeta.lastVerifiedAt || '\u2014' }}</strong>\n </div>\n </div>\n </div>\n </div>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"Layout\">\n <div class=\"tab-content tab-content--full\">\n <div class=\"section-header section-header--compact\">\n <div>\n <h3>Layout do formul\u00E1rio</h3>\n <p class=\"text-caption\">Organize se\u00E7\u00F5es, linhas e campos com drag & drop.</p>\n </div>\n </div>\n <div class=\"section-body section-body--flex\">\n <praxis-layout-editor [config]=\"editedConfig\" (configChange)=\"onConfigChange($event)\"\n (select)=\"onLayoutSelect($event)\"></praxis-layout-editor>\n </div>\n </div>\n </mat-tab>\n <mat-tab label=\"Hooks\">\n <div class=\"tab-content\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Hooks de ciclo de vida</h3>\n <p class=\"text-caption\">Automatize a\u00E7\u00F5es nos eventos do formul\u00E1rio.</p>\n </div>\n </div>\n <praxis-hooks-editor [config]=\"editedConfig\" (configChange)=\"onConfigChange($event)\"></praxis-hooks-editor>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab *ngIf=\"isPresentationMode\" label=\"Modo Apresenta\u00E7\u00E3o\">\n <div class=\"tab-content\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Modo Apresenta\u00E7\u00E3o</h3>\n <p class=\"text-caption\">Configura\u00E7\u00F5es de exibi\u00E7\u00E3o aplicadas somente ao modo apresenta\u00E7\u00E3o.</p>\n </div>\n </div>\n\n <div class=\"presentation-layout\">\n <div class=\"presentation-controls\">\n <div class=\"control\">\n <label class=\"control__label\">Posi\u00E7\u00E3o do r\u00F3tulo</label>\n <mat-button-toggle-group [(ngModel)]=\"presentationPrefs.labelPosition\" aria-label=\"Posi\u00E7\u00E3o do r\u00F3tulo\"\n class=\"toggle-group\">\n <mat-button-toggle value=\"above\">\n <mat-icon>view_stream</mat-icon>\n <span class=\"sr-only\">Acima</span>\n </mat-button-toggle>\n <mat-button-toggle value=\"left\">\n <mat-icon>view_column</mat-icon>\n <span class=\"sr-only\">\u00C0 esquerda</span>\n </mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n\n <div class=\"control\">\n <label class=\"control__label\">Densidade</label>\n <mat-button-toggle-group [(ngModel)]=\"presentationPrefs.density\" aria-label=\"Densidade\"\n class=\"toggle-group\">\n <mat-button-toggle value=\"comfortable\">Confort\u00E1vel</mat-button-toggle>\n <mat-button-toggle value=\"cozy\">Intermedi\u00E1ria</mat-button-toggle>\n <mat-button-toggle value=\"compact\">Compacta</mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n\n <div class=\"control\">\n <label class=\"control__label\">Alinhamento do r\u00F3tulo</label>\n <mat-button-toggle-group [(ngModel)]=\"presentationPrefs.labelAlign\" aria-label=\"Alinhamento do r\u00F3tulo\"\n class=\"toggle-group\">\n <mat-button-toggle value=\"start\"><mat-icon>format_align_left</mat-icon></mat-button-toggle>\n <mat-button-toggle value=\"center\"><mat-icon>format_align_center</mat-icon></mat-button-toggle>\n <mat-button-toggle value=\"end\"><mat-icon>format_align_right</mat-icon></mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n\n <div class=\"control\">\n <label class=\"control__label\">Alinhamento do valor</label>\n <mat-button-toggle-group [(ngModel)]=\"presentationPrefs.valueAlign\" aria-label=\"Alinhamento do valor\"\n class=\"toggle-group\">\n <mat-button-toggle value=\"start\"><mat-icon>format_align_left</mat-icon></mat-button-toggle>\n <mat-button-toggle value=\"center\"><mat-icon>format_align_center</mat-icon></mat-button-toggle>\n <mat-button-toggle value=\"end\"><mat-icon>format_align_right</mat-icon></mat-button-toggle>\n </mat-button-toggle-group>\n </div>\n\n <div class=\"control\">\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Tamanho do r\u00F3tulo</mat-label>\n <input matInput type=\"number\" min=\"10\" [(ngModel)]=\"presentationPrefs.labelFontSize\"\n placeholder=\"ex.: 12\" />\n <span matSuffix>px</span>\n </mat-form-field>\n </div>\n\n <div class=\"control\">\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Tamanho do valor</mat-label>\n <input matInput type=\"number\" min=\"10\" [(ngModel)]=\"presentationPrefs.valueFontSize\"\n placeholder=\"ex.: 16\" />\n <span matSuffix>px</span>\n </mat-form-field>\n </div>\n\n <div class=\"control\">\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Largura do r\u00F3tulo (label \u00E0 esquerda)</mat-label>\n <input matInput type=\"number\" min=\"60\" [(ngModel)]=\"presentationPrefs.labelWidth\"\n placeholder=\"ex.: 140\" />\n <span matSuffix>px</span>\n </mat-form-field>\n </div>\n\n <div class=\"control control--inline\">\n <mat-slide-toggle [(ngModel)]=\"presentationPrefs.compact\">\n Modo compacto\n </mat-slide-toggle>\n <mat-checkbox [(ngModel)]=\"truncatePreview\">\n Truncar valores\n </mat-checkbox>\n </div>\n </div>\n\n <div class=\"presentation-preview\">\n <h4>Pr\u00E9\u2011visualiza\u00E7\u00E3o</h4>\n <div class=\"preview-card\">\n <div class=\"praxis-dynamic-form presentation-mode\" [ngClass]=\"{\n 'pres-compact': !!presentationPrefs.compact,\n 'pres-label-left': presentationPrefs.labelPosition === 'left',\n 'pres-label-above': presentationPrefs.labelPosition === 'above',\n 'pres-density-compact': presentationPrefs.density === 'compact',\n 'pres-density-cozy': presentationPrefs.density === 'cozy'\n }\" [style.--pfx-pres-label-size]=\"(presentationPrefs.labelFontSize || 12) + 'px'\"\n [style.--pfx-pres-value-size]=\"(presentationPrefs.valueFontSize || 16) + 'px'\"\n [style.--pfx-pres-label-width]=\"(presentationPrefs.labelWidth || 140) + 'px'\"\n [style.--pfx-pres-label-align]=\"presentationPrefs.labelAlign || 'start'\"\n [style.--pfx-pres-value-align]=\"presentationPrefs.valueAlign || 'start'\">\n <div class=\"preview-row\" *ngFor=\"let item of previewItems\">\n <span class=\"praxis-presentation__label\">{{ item.label }}</span>\n <span class=\"praxis-presentation__value\" [title]=\"truncatePreview ? item.value : null\"\n [style.maxWidth]=\"truncatePreview ? '280px' : null\"\n [style.overflow]=\"truncatePreview ? 'hidden' : null\"\n [style.textOverflow]=\"truncatePreview ? 'ellipsis' : null\"\n [style.whiteSpace]=\"truncatePreview ? 'nowrap' : 'normal'\">\n {{ item.value }}\n </span>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"section-help\">\n <span class=\"section-help__label\">Entenda:</span>\n <button\n mat-icon-button\n class=\"help-icon-button\"\n type=\"button\"\n [matTooltip]=\"'Ajustes aplicam somente ao Modo Apresenta\u00E7\u00E3o e s\u00E3o salvos por formul\u00E1rio (via CSS/Classes).'\"\n matTooltipPosition=\"above\"\n >\n <mat-icon>help_outline</mat-icon>\n </button>\n </div>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"Comportamento\">\n <div class=\"tab-content\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Comportamento</h3>\n <p class=\"text-caption\">Ajuste valida\u00E7\u00F5es e respostas do formul\u00E1rio.</p>\n </div>\n </div>\n <praxis-behavior-editor [config]=\"editedConfig\" (configChange)=\"onConfigChange($event)\"></praxis-behavior-editor>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"Dicas e Tooltips\">\n <div class=\"tab-content tab-content--dense\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Dicas dos modos</h3>\n <p class=\"text-caption\">Personalize textos de apoio para modos de dados e estados de UI.</p>\n </div>\n </div>\n <p class=\"text-caption\">Personalize os textos usados como tooltip/hint para modos de dados e modos globais de\n UI.</p>\n\n <div class=\"grid-2-cols grid-2-cols--compact\">\n <section>\n <h4>Modos de dados</h4>\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Criar</mat-label>\n <input matInput [(ngModel)]=\"editedConfig.hints!.dataModes.create\"\n (ngModelChange)=\"updateDirtyState('hints-data-create')\" placeholder=\"Ex.: Preencha os campos obrigat\u00F3rios.\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Editar</mat-label>\n <input matInput [(ngModel)]=\"editedConfig.hints!.dataModes.edit\"\n (ngModelChange)=\"updateDirtyState('hints-data-edit')\" placeholder=\"Ex.: Revise os dados antes de salvar.\" />\n </mat-form-field>\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Visualizar</mat-label>\n <input matInput [(ngModel)]=\"editedConfig.hints!.dataModes.view\"\n (ngModelChange)=\"updateDirtyState('hints-data-view')\" placeholder=\"Ex.: Visualiza\u00E7\u00E3o somente leitura.\" />\n </mat-form-field>\n </section>\n\n <section>\n <h4>Modos de UI</h4>\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Apresenta\u00E7\u00E3o</mat-label>\n <textarea matInput rows=\"2\" [(ngModel)]=\"editedConfig.hints!.uiModes.presentation\"\n (ngModelChange)=\"updateDirtyState('hints-ui-presentation')\" placeholder=\"Ex.: Modo compacto para leitura r\u00E1pida.\"></textarea>\n </mat-form-field>\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Leitura (readonly)</mat-label>\n <textarea matInput rows=\"2\" [(ngModel)]=\"editedConfig.hints!.uiModes.readonly\"\n (ngModelChange)=\"updateDirtyState('hints-ui-readonly')\" placeholder=\"Ex.: Campos bloqueados para edi\u00E7\u00E3o.\"></textarea>\n </mat-form-field>\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Desabilitado</mat-label>\n <textarea matInput rows=\"2\" [(ngModel)]=\"editedConfig.hints!.uiModes.disabled\"\n (ngModelChange)=\"updateDirtyState('hints-ui-disabled')\" placeholder=\"Ex.: Fun\u00E7\u00E3o indispon\u00EDvel no momento.\"></textarea>\n </mat-form-field>\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Vis\u00EDvel</mat-label>\n <textarea matInput rows=\"2\" [(ngModel)]=\"editedConfig.hints!.uiModes.visible\"\n (ngModelChange)=\"updateDirtyState('hints-ui-visible')\" placeholder=\"Ex.: Campo exibido conforme permiss\u00F5es.\"></textarea>\n </mat-form-field>\n </section>\n </div>\n\n <div class=\"actions-row\">\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"restoreHintsDefaults()\">\n <mat-icon>restore</mat-icon>\n Restaurar padr\u00F5es\n </button>\n </div>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"A\u00E7\u00F5es\">\n <div class=\"tab-content\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>A\u00E7\u00F5es do formul\u00E1rio</h3>\n <p class=\"text-caption\">Configure bot\u00F5es padr\u00E3o e a\u00E7\u00F5es customizadas.</p>\n </div>\n </div>\n <praxis-actions-editor [config]=\"editedConfig\" (configChange)=\"onConfigChange($event)\"></praxis-actions-editor>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"Regras\">\n <div class=\"tab-content visual-builder-content\">\n <div class=\"builder-header\">\n <div>\n <h3>Regras visuais</h3>\n <p class=\"text-caption\">Defina visibilidade, estilos e rea\u00E7\u00F5es baseadas em condi\u00E7\u00F5es.</p>\n </div>\n </div>\n <praxis-visual-builder [config]=\"ruleBuilderConfig\" [initialRules]=\"ruleBuilderState\"\n (rulesChanged)=\"onRulesChanged($event)\"></praxis-visual-builder>\n </div>\n </mat-tab>\n <mat-tab label=\"Cascatas\">\n <div class=\"tab-content\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Depend\u00EAncias entre campos</h3>\n <p class=\"text-caption\">Configure cascatas e carregamento entre campos.</p>\n </div>\n </div>\n <praxis-cascade-manager-tab [fields]=\"editedConfig.fieldMetadata || []\"\n (apply)=\"onCascadeApply($event)\"></praxis-cascade-manager-tab>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"Mensagens\">\n <div class=\"tab-content\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Mensagens e feedback</h3>\n <p class=\"text-caption\">Padronize textos de sucesso, erro e confirma\u00E7\u00F5es.</p>\n </div>\n </div>\n <praxis-messages-editor [config]=\"editedConfig\" (configChange)=\"onConfigChange($event)\"></praxis-messages-editor>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"Navega\u00E7\u00E3o\">\n <div class=\"tab-content\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Navega\u00E7\u00E3o de retorno</h3>\n <p class=\"text-caption\">Defina o comportamento do bot\u00E3o Voltar em fluxos CRUD.</p>\n </div>\n </div>\n <div class=\"form-grid\">\n <div class=\"form-row\">\n <mat-form-field appearance=\"fill\" class=\"w-100\">\n <mat-label>Rota de retorno</mat-label>\n <input matInput id=\"returnTo\" [(ngModel)]=\"backConfig!.returnTo\" placeholder=\"/funcionarios\" />\n </mat-form-field>\n </div>\n <div class=\"form-row\">\n <mat-slide-toggle [(ngModel)]=\"backConfig!.confirmOnDirty\">\n Confirmar ao sair com altera\u00E7\u00F5es\n </mat-slide-toggle>\n </div>\n </div>\n <div class=\"section-help\">\n <span class=\"section-help__label\">Observa\u00E7\u00E3o:</span>\n <button\n mat-icon-button\n class=\"help-icon-button\"\n type=\"button\"\n [matTooltip]=\"'Afeta o fluxo de navega\u00E7\u00E3o do CRUD e pode ser sobreposto por metadados do recurso.'\"\n matTooltipPosition=\"above\"\n >\n <mat-icon>help_outline</mat-icon>\n </button>\n </div>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n <mat-tab label=\"JSON\">\n <div class=\"tab-content\">\n <mat-card class=\"section-card\">\n <mat-card-content>\n <div class=\"section-header\">\n <div>\n <h3>Configura\u00E7\u00E3o JSON</h3>\n <p class=\"text-caption\">Edi\u00E7\u00E3o avan\u00E7ada do schema do formul\u00E1rio.</p>\n </div>\n </div>\n <form-json-config-editor [config]=\"editedConfig\" (configChange)=\"onJsonConfigChange($event)\"\n (validationChange)=\"onJsonValidationChange($event)\" (editorEvent)=\"onJsonEditorEvent($event)\">\n </form-json-config-editor>\n </mat-card-content>\n </mat-card>\n </div>\n </mat-tab>\n</mat-tab-group>\n", styles: [":host{display:block;height:100%}.config-tabs{height:100%}::ng-deep .mat-mdc-tab-group.config-tabs{display:flex;flex-direction:column}::ng-deep .mat-mdc-tab-group.config-tabs .mat-mdc-tab-body-wrapper{flex:1;min-height:0}::ng-deep .mat-mdc-tab-group.config-tabs .mat-mdc-tab-body-content{height:100%;overflow:auto;display:flex;flex-direction:column;padding:0!important}.tab-content{padding:16px 20px 20px;flex:1}.form-row{display:block;margin-bottom:12px}.form-grid{display:grid;gap:12px}.section-help{display:inline-flex;align-items:center;gap:8px;color:var(--md-sys-color-on-surface-variant);font-size:12px}.section-help__label{font-weight:500}.help-icon-button{--mdc-icon-button-state-layer-size: 28px;--mdc-icon-button-icon-size: 18px;width:28px;height:28px;padding:0;display:inline-flex;align-items:center;justify-content:center}.help-icon-button mat-icon{font-size:18px;width:18px;height:18px}.grid-2-cols{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:16px}.grid-2-cols--compact{gap:12px}.w-100{width:100%}.visual-builder-content{padding:0!important;height:100%;overflow:hidden;display:flex;flex-direction:column}.visual-builder-content>praxis-visual-builder{flex:1;min-height:0}.json-field{width:100%}.presentation-layout{display:flex;gap:20px;align-items:flex-start}.presentation-controls{flex:1 1 60%;display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:14px 18px}.control{display:flex;flex-direction:column}.control__label{font-size:12px;color:var(--md-sys-color-on-surface-variant);margin-bottom:6px}.toggle-group{width:100%}.control--inline{grid-column:1/-1;display:flex;gap:16px;align-items:center}.presentation-preview{flex:0 0 360px;position:sticky;top:16px}.preview-card{border:1px solid var(--md-sys-color-outline);border-radius:12px;padding:14px;background:var(--md-sys-color-surface-container)}.preview-row{display:flex;gap:8px;padding:6px 0;border-bottom:1px dashed var(--md-sys-color-outline-variant)}.preview-row:last-child{border-bottom:none}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.presentation-preview .praxis-presentation__label{color:var(--md-sys-color-on-surface-variant)}.presentation-preview .praxis-presentation__value{color:var(--md-sys-color-on-surface)}.presentation-preview .presentation-mode.pres-density-cozy .preview-row{padding:6px 0}.presentation-preview .presentation-mode.pres-density-compact .preview-row,.presentation-preview .presentation-mode.pres-compact .preview-row{padding:2px 0}.presentation-preview .presentation-mode.pres-label-left .preview-row{display:grid;grid-template-columns:var(--pfx-pres-label-width, 140px) 1fr;align-items:baseline;column-gap:10px}.presentation-preview .presentation-mode.pres-label-left .praxis-presentation__label{margin:0;text-align:var(--pfx-pres-label-align, start)}.presentation-preview .presentation-mode .praxis-presentation__value{text-align:var(--pfx-pres-value-align, start)}.presentation-preview h4{margin:0 0 8px;color:var(--md-sys-color-on-surface);font-size:1rem}.section-card{border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container);box-shadow:var(--md-sys-elevation-level1, none)}.section-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:12px}.tab-content--dense{padding:12px 16px 16px}.tab-content--dense .section-header,.tab-content--dense .form-row{margin-bottom:8px}.section-header h3{margin:0;font-size:1.05rem;color:var(--md-sys-color-on-surface)}.text-caption{margin:6px 0 0;font-size:.9rem;color:var(--md-sys-color-on-surface-variant)}.section-header--compact{margin-bottom:8px}.section-body{display:block}.section-body--flex{flex:1;min-height:0;display:flex;flex-direction:column}.builder-header{padding:12px 16px;border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface)}.builder-header h3{margin:0;font-size:1rem;color:var(--md-sys-color-on-surface)}.hint{margin:8px 0 0;padding:10px 12px;border-radius:10px;border:1px dashed var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface-variant)}.meta-card{display:grid;gap:6px;padding:10px 12px;border-radius:10px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface)}.meta-row{display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:.9rem;color:var(--md-sys-color-on-surface-variant)}.meta-row strong{color:var(--md-sys-color-on-surface);font-weight:600}\n"] }]
|
|
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"] }]
|
|
15934
16985
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
15935
16986
|
type: Inject,
|
|
15936
16987
|
args: [ASYNC_CONFIG_STORAGE]
|
|
@@ -15954,6 +17005,12 @@ function omit(obj, keys) {
|
|
|
15954
17005
|
}
|
|
15955
17006
|
return out;
|
|
15956
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
|
+
}
|
|
15957
17014
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
15958
17015
|
function hasFieldMetadata(cfg) {
|
|
15959
17016
|
return cfg && Array.isArray(cfg.fieldMetadata);
|
|
@@ -16336,6 +17393,8 @@ class SectionEditorComponent {
|
|
|
16336
17393
|
title: [this.section.title, Validators.required],
|
|
16337
17394
|
icon: [this.section.icon ?? ''],
|
|
16338
17395
|
description: [this.section.description],
|
|
17396
|
+
appearance: [this.section.appearance ?? 'card'],
|
|
17397
|
+
stepLabel: [this.section.stepLabel ?? ''],
|
|
16339
17398
|
gapBottom: [this.section.gapBottom ?? 0],
|
|
16340
17399
|
titleGapBottom: [this.section.titleGapBottom ?? 20],
|
|
16341
17400
|
descriptionGapBottom: [this.section.descriptionGapBottom ?? 8],
|
|
@@ -16547,6 +17606,14 @@ class SectionEditorComponent {
|
|
|
16547
17606
|
</mat-form-field>
|
|
16548
17607
|
|
|
16549
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>
|
|
16550
17617
|
<mat-form-field appearance="fill">
|
|
16551
17618
|
<mat-label>Fonte do título</mat-label>
|
|
16552
17619
|
<mat-select [value]="section.titleStyle || 'titleMedium'" (valueChange)="onTitleStyleChange($event)">
|
|
@@ -16564,6 +17631,13 @@ class SectionEditorComponent {
|
|
|
16564
17631
|
<mat-option value="bodySmall">Corpo pequeno</mat-option>
|
|
16565
17632
|
</mat-select>
|
|
16566
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">
|
|
16567
17641
|
<mat-form-field appearance="fill">
|
|
16568
17642
|
<mat-label>Alinhamento do cabeçalho</mat-label>
|
|
16569
17643
|
<mat-select formControlName="headerAlign">
|
|
@@ -16752,6 +17826,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
16752
17826
|
</mat-form-field>
|
|
16753
17827
|
|
|
16754
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>
|
|
16755
17837
|
<mat-form-field appearance="fill">
|
|
16756
17838
|
<mat-label>Fonte do título</mat-label>
|
|
16757
17839
|
<mat-select [value]="section.titleStyle || 'titleMedium'" (valueChange)="onTitleStyleChange($event)">
|
|
@@ -16769,6 +17851,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
16769
17851
|
<mat-option value="bodySmall">Corpo pequeno</mat-option>
|
|
16770
17852
|
</mat-select>
|
|
16771
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">
|
|
16772
17861
|
<mat-form-field appearance="fill">
|
|
16773
17862
|
<mat-label>Alinhamento do cabeçalho</mat-label>
|
|
16774
17863
|
<mat-select formControlName="headerAlign">
|
|
@@ -18378,5 +19467,5 @@ const FORM_COMPONENT_AI_CAPABILITIES = {
|
|
|
18378
19467
|
* Generated bundle index. Do not edit.
|
|
18379
19468
|
*/
|
|
18380
19469
|
|
|
18381
|
-
export { ActionsEditorComponent, CanvasStateService, CanvasToolbarComponent, ColumnEditorComponent, DynamicFormLayoutService, FORM_AI_CAPABILITIES, FORM_COMPONENT_AI_CAPABILITIES, FieldConfiguratorComponent, FieldEditorComponent, FormConfigService, FormContextService, FormLayoutService, JsonConfigEditorComponent, LayoutColorToken, LayoutEditorComponent, LayoutPrefsService, PRAXIS_DYNAMIC_FORM_COMPONENT_METADATA, PraxisDynamicForm, PraxisDynamicFormConfigEditor, PraxisFilterForm, PraxisFormActionsComponent, RowConfiguratorComponent, RowEditorComponent, SETTINGS_PANEL_DYNAMIC_FORM_PROVIDER, SectionEditorComponent, TASK_PRESETS, applyVisibilityRules, formLayoutRulesToBuilderState, getFormAiCatalog, getFormCapabilities, getFormEnum, isRuleSatisfied, normalizeDateArrays, providePraxisDynamicFormMetadata, provideSettingsPanelDynamicForm, ruleBuilderStateToFormLayoutRules, stripLegacyFieldMetadata };
|
|
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 };
|
|
18382
19471
|
//# sourceMappingURL=praxisui-dynamic-form.mjs.map
|