@praxisui/manual-form 1.0.0-beta.7 → 2.0.0-beta.0
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 +201 -24
- package/fesm2022/praxisui-manual-form.mjs +3672 -218
- package/fesm2022/praxisui-manual-form.mjs.map +1 -1
- package/index.d.ts +242 -17
- package/package.json +10 -9
|
@@ -1,14 +1,35 @@
|
|
|
1
|
-
import { ensureIds, DynamicFormService,
|
|
1
|
+
import { ensureIds, DynamicFormService, ASYNC_CONFIG_STORAGE, RULE_PROPERTY_SCHEMA, deepMerge, resolveControlTypeAlias, FieldControlType, normalizeControlTypeKey, FormHooksRegistry, ComponentKeyService, FIELD_SELECTOR_REGISTRY_DISABLE_DEFAULTS, DEFAULT_FIELD_SELECTOR_CONTROL_TYPE_MAP, FieldSelectorRegistry, API_URL } from '@praxisui/core';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { inject, Optional, Inject, Injectable, input, output, ChangeDetectionStrategy, Component, Input, InjectionToken, isDevMode, ChangeDetectorRef, DestroyRef, ContentChildren,
|
|
4
|
-
import { BehaviorSubject, debounceTime } from 'rxjs';
|
|
5
|
-
import
|
|
6
|
-
import { CommonModule } from '@angular/common';
|
|
7
|
-
import
|
|
3
|
+
import { inject, Optional, Inject, Injectable, input, output, ChangeDetectionStrategy, Component, Input, InjectionToken, isDevMode, EventEmitter, HostListener, ViewChild, Output, ChangeDetectorRef, DestroyRef, PLATFORM_ID, effect, ContentChildren, signal, computed, Directive } from '@angular/core';
|
|
4
|
+
import { BehaviorSubject, debounceTime, fromEvent, of } from 'rxjs';
|
|
5
|
+
import { take } from 'rxjs/operators';
|
|
6
|
+
import { CommonModule, isPlatformBrowser } from '@angular/common';
|
|
7
|
+
import { ActivatedRoute } from '@angular/router';
|
|
8
|
+
import { BaseAiAdapter, PraxisAiAssistantComponent } from '@praxisui/ai';
|
|
9
|
+
import * as i1 from '@angular/forms';
|
|
8
10
|
import { FormGroupDirective, FormGroup, FormControl, Validators, ReactiveFormsModule, FormControlName, ControlContainer, FormsModule } from '@angular/forms';
|
|
9
11
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
10
|
-
import
|
|
12
|
+
import { Overlay } from '@angular/cdk/overlay';
|
|
13
|
+
import { ComponentPortal } from '@angular/cdk/portal';
|
|
14
|
+
import * as i10 from '@praxisui/settings-panel';
|
|
11
15
|
import { SettingsPanelService, SETTINGS_PANEL_DATA, SETTINGS_PANEL_REF } from '@praxisui/settings-panel';
|
|
16
|
+
import * as i2 from '@angular/material/tabs';
|
|
17
|
+
import { MatTabsModule } from '@angular/material/tabs';
|
|
18
|
+
import * as i3 from '@angular/material/form-field';
|
|
19
|
+
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
20
|
+
import * as i4 from '@angular/material/input';
|
|
21
|
+
import { MatInputModule } from '@angular/material/input';
|
|
22
|
+
import * as i5 from '@angular/material/select';
|
|
23
|
+
import { MatSelectModule } from '@angular/material/select';
|
|
24
|
+
import * as i6 from '@angular/material/slide-toggle';
|
|
25
|
+
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
|
26
|
+
import * as i7 from '@angular/material/button';
|
|
27
|
+
import { MatButtonModule } from '@angular/material/button';
|
|
28
|
+
import * as i8 from '@angular/material/icon';
|
|
29
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
30
|
+
import * as i9 from '@angular/material/tooltip';
|
|
31
|
+
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
32
|
+
import { CascadeManagerTabComponent } from '@praxisui/metadata-editor';
|
|
12
33
|
|
|
13
34
|
function deepClone(value) {
|
|
14
35
|
if (typeof globalThis?.structuredClone === 'function') {
|
|
@@ -109,6 +130,14 @@ class ManualFormInstance {
|
|
|
109
130
|
get fieldMetadataChanges$() {
|
|
110
131
|
return this.fieldMetadata$.asObservable();
|
|
111
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* Emits after field metadata has been applied to the runtime state.
|
|
135
|
+
* Fires on patchFieldMetadata(), replaceConfig(), resetToSeed(), and
|
|
136
|
+
* persisted config loads (when metadata is replaced).
|
|
137
|
+
*/
|
|
138
|
+
metadataChanges() {
|
|
139
|
+
return this.fieldMetadata$.asObservable();
|
|
140
|
+
}
|
|
112
141
|
get currentConfig() {
|
|
113
142
|
return this.formConfig$.value;
|
|
114
143
|
}
|
|
@@ -147,7 +176,7 @@ class ManualFormInstance {
|
|
|
147
176
|
if (!current) {
|
|
148
177
|
return;
|
|
149
178
|
}
|
|
150
|
-
const next = this.
|
|
179
|
+
const next = this.applyJsonMergePatch(current, patch);
|
|
151
180
|
this.fieldMetadata$.value.set(fieldName, next);
|
|
152
181
|
this.fieldMetadata$.next(new Map(this.fieldMetadata$.value));
|
|
153
182
|
const configClone = deepClone(this.formConfig$.value);
|
|
@@ -191,7 +220,7 @@ class ManualFormInstance {
|
|
|
191
220
|
}
|
|
192
221
|
/**
|
|
193
222
|
* Persists the current configuration and, optionally, the provided form value
|
|
194
|
-
* using
|
|
223
|
+
* using AsyncConfigStorage. By default, stores the FormGroup raw value.
|
|
195
224
|
*/
|
|
196
225
|
saveDraft(value = null) {
|
|
197
226
|
if (!this.storage) {
|
|
@@ -208,7 +237,7 @@ class ManualFormInstance {
|
|
|
208
237
|
},
|
|
209
238
|
version: this.seed.version,
|
|
210
239
|
};
|
|
211
|
-
this.storage.saveConfig(key, payload);
|
|
240
|
+
this.storage.saveConfig(key, payload).pipe(take(1)).subscribe({ error: () => { } });
|
|
212
241
|
}
|
|
213
242
|
publishConfig(config) {
|
|
214
243
|
this.formConfig$.next(config);
|
|
@@ -266,18 +295,37 @@ class ManualFormInstance {
|
|
|
266
295
|
/* ignore */
|
|
267
296
|
}
|
|
268
297
|
}
|
|
269
|
-
/**
|
|
270
|
-
|
|
271
|
-
|
|
298
|
+
/**
|
|
299
|
+
* JSON Merge Patch semantics for metadata patches.
|
|
300
|
+
* `null` remove a propriedade-alvo e objetos vazios são podados.
|
|
301
|
+
*/
|
|
302
|
+
applyJsonMergePatch(target, patch) {
|
|
303
|
+
if (patch == null || typeof patch !== 'object' || Array.isArray(patch)) {
|
|
272
304
|
return { ...target };
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
305
|
+
}
|
|
306
|
+
const out = Array.isArray(target)
|
|
307
|
+
? [...target]
|
|
308
|
+
: { ...target };
|
|
309
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
310
|
+
if (value === null) {
|
|
311
|
+
delete out[key];
|
|
312
|
+
continue;
|
|
277
313
|
}
|
|
278
|
-
|
|
279
|
-
out[
|
|
314
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
315
|
+
const merged = this.applyJsonMergePatch(out[key] && typeof out[key] === 'object' && !Array.isArray(out[key])
|
|
316
|
+
? out[key]
|
|
317
|
+
: {}, value);
|
|
318
|
+
if (merged &&
|
|
319
|
+
typeof merged === 'object' &&
|
|
320
|
+
!Array.isArray(merged) &&
|
|
321
|
+
!Object.keys(merged).length) {
|
|
322
|
+
delete out[key];
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
out[key] = merged;
|
|
326
|
+
continue;
|
|
280
327
|
}
|
|
328
|
+
out[key] = value;
|
|
281
329
|
}
|
|
282
330
|
return out;
|
|
283
331
|
}
|
|
@@ -293,35 +341,42 @@ class ManualFormInstanceFactory {
|
|
|
293
341
|
this.storage = storage;
|
|
294
342
|
}
|
|
295
343
|
create(seed, options = {}, externalForm) {
|
|
296
|
-
const
|
|
297
|
-
const baseConfig = persisted?.config ?? deepClone(seed.config);
|
|
344
|
+
const baseConfig = deepClone(seed.config);
|
|
298
345
|
const instance = new ManualFormInstance(seed.formId, seed, baseConfig, this.dynamicFormService, this.storage, options, externalForm);
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
346
|
+
if (this.storage) {
|
|
347
|
+
const key = buildStorageKey(seed.formId, options);
|
|
348
|
+
this.storage
|
|
349
|
+
.loadConfig(key)
|
|
350
|
+
.pipe(take(1))
|
|
351
|
+
.subscribe((persisted) => {
|
|
352
|
+
if (!persisted)
|
|
353
|
+
return;
|
|
354
|
+
if (persisted.config) {
|
|
355
|
+
instance.replaceConfig(persisted.config);
|
|
356
|
+
}
|
|
357
|
+
if (persisted.value) {
|
|
358
|
+
instance.form.patchValue(persisted.value, { emitEvent: false });
|
|
359
|
+
}
|
|
360
|
+
});
|
|
302
361
|
}
|
|
303
362
|
return instance;
|
|
304
363
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
return undefined;
|
|
308
|
-
}
|
|
309
|
-
const key = buildStorageKey(formId, options);
|
|
310
|
-
return this.storage.loadConfig(key) ?? undefined;
|
|
311
|
-
}
|
|
312
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: ManualFormInstanceFactory, deps: [{ token: CONFIG_STORAGE, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
313
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: ManualFormInstanceFactory, providedIn: 'root' });
|
|
364
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFormInstanceFactory, deps: [{ token: ASYNC_CONFIG_STORAGE, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
365
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFormInstanceFactory, providedIn: 'root' });
|
|
314
366
|
}
|
|
315
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
367
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFormInstanceFactory, decorators: [{
|
|
316
368
|
type: Injectable,
|
|
317
369
|
args: [{ providedIn: 'root' }]
|
|
318
370
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
319
371
|
type: Optional
|
|
320
372
|
}, {
|
|
321
373
|
type: Inject,
|
|
322
|
-
args: [
|
|
374
|
+
args: [ASYNC_CONFIG_STORAGE]
|
|
323
375
|
}] }] });
|
|
324
376
|
function buildStorageKey(formId, options = {}) {
|
|
377
|
+
if (options.storageKey) {
|
|
378
|
+
return String(options.storageKey);
|
|
379
|
+
}
|
|
325
380
|
const parts = ['manual-form', options.namespace, options.tenantId, options.profileId, formId]
|
|
326
381
|
.filter(Boolean)
|
|
327
382
|
.map((segment) => String(segment));
|
|
@@ -334,7 +389,7 @@ class ManualFormHeaderComponent {
|
|
|
334
389
|
description = input(...(ngDevMode ? [undefined, { debugName: "description" }] : []));
|
|
335
390
|
saveLabel = input('Salvar customizações', ...(ngDevMode ? [{ debugName: "saveLabel" }] : []));
|
|
336
391
|
resetLabel = input('Restaurar padrão', ...(ngDevMode ? [{ debugName: "resetLabel" }] : []));
|
|
337
|
-
|
|
392
|
+
enableCustomization = input(false, ...(ngDevMode ? [{ debugName: "enableCustomization" }] : []));
|
|
338
393
|
editFormLabel = input('Editar formulário', ...(ngDevMode ? [{ debugName: "editFormLabel" }] : []));
|
|
339
394
|
save = output();
|
|
340
395
|
reset = output();
|
|
@@ -358,8 +413,8 @@ class ManualFormHeaderComponent {
|
|
|
358
413
|
this.firstSection?.description ||
|
|
359
414
|
undefined);
|
|
360
415
|
}
|
|
361
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.
|
|
362
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.
|
|
416
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFormHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
417
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: ManualFormHeaderComponent, isStandalone: true, selector: "praxis-manual-form-header", inputs: { instance: { classPropertyName: "instance", publicName: "instance", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, saveLabel: { classPropertyName: "saveLabel", publicName: "saveLabel", isSignal: true, isRequired: false, transformFunction: null }, resetLabel: { classPropertyName: "resetLabel", publicName: "resetLabel", isSignal: true, isRequired: false, transformFunction: null }, enableCustomization: { classPropertyName: "enableCustomization", publicName: "enableCustomization", isSignal: true, isRequired: false, transformFunction: null }, editFormLabel: { classPropertyName: "editFormLabel", publicName: "editFormLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { save: "save", reset: "reset", editForm: "editForm" }, ngImport: i0, template: `
|
|
363
418
|
<header class="pdx-manual-form__header">
|
|
364
419
|
<div class="pdx-manual-form__title">
|
|
365
420
|
<h2>{{ displayTitle }}</h2>
|
|
@@ -370,16 +425,16 @@ class ManualFormHeaderComponent {
|
|
|
370
425
|
}
|
|
371
426
|
</div>
|
|
372
427
|
<div class="pdx-manual-form__header-actions">
|
|
373
|
-
@if (
|
|
374
|
-
<button type="button" (click)="editForm.emit()" [attr.aria-label]="editFormLabel()">{{ editFormLabel() }}</button>
|
|
428
|
+
@if (enableCustomization()) {
|
|
429
|
+
<button type="button" class="pdx-btn pdx-btn--ghost" (click)="editForm.emit()" [attr.aria-label]="editFormLabel()">{{ editFormLabel() }}</button>
|
|
375
430
|
}
|
|
376
|
-
<button type="button" (click)="reset.emit()" [attr.aria-label]="resetLabel()">{{ resetLabel() }}</button>
|
|
377
|
-
<button type="button" (click)="save.emit()" [attr.aria-label]="saveLabel()">{{ saveLabel() }}</button>
|
|
431
|
+
<button type="button" class="pdx-btn pdx-btn--ghost" (click)="reset.emit()" [attr.aria-label]="resetLabel()">{{ resetLabel() }}</button>
|
|
432
|
+
<button type="button" class="pdx-btn pdx-btn--primary" (click)="save.emit()" [attr.aria-label]="saveLabel()">{{ saveLabel() }}</button>
|
|
378
433
|
</div>
|
|
379
434
|
</header>
|
|
380
|
-
`, isInline: true, styles: [":host{display:block}.pdx-manual-form__header{display:flex;justify-content:space-between;align-items:center;gap:1rem;margin-bottom:1.5rem}.pdx-manual-form__title h2{margin:0;font-size:1.
|
|
435
|
+
`, isInline: true, styles: [":host{display:block}.pdx-manual-form__header{display:flex;justify-content:space-between;align-items:center;gap:1rem;margin-bottom:1.5rem}.pdx-manual-form__title h2{margin:0;font-size:1.25rem;font-weight:600;line-height:1.3}.pdx-manual-form__subtitle{margin:.25rem 0 0;color:var(--md-sys-color-on-surface-variant)}.pdx-manual-form__header-actions{display:flex;gap:.5rem;flex-wrap:wrap}.pdx-btn{min-height:36px;padding:0 14px;border-radius:8px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);cursor:pointer;transition:background-color .18s ease,border-color .18s ease,color .18s ease}.pdx-btn:hover{background:var(--md-sys-color-surface-container)}.pdx-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pdx-btn--ghost{background:transparent;border-color:var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface-variant)}.pdx-btn--primary{background:var(--md-sys-color-primary);border-color:var(--md-sys-color-primary);color:var(--md-sys-color-on-primary)}.pdx-btn--primary:hover{background:var(--md-sys-color-primary-container);border-color:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
381
436
|
}
|
|
382
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
437
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFormHeaderComponent, decorators: [{
|
|
383
438
|
type: Component,
|
|
384
439
|
args: [{ selector: 'praxis-manual-form-header', standalone: true, imports: [CommonModule], template: `
|
|
385
440
|
<header class="pdx-manual-form__header">
|
|
@@ -392,15 +447,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
392
447
|
}
|
|
393
448
|
</div>
|
|
394
449
|
<div class="pdx-manual-form__header-actions">
|
|
395
|
-
@if (
|
|
396
|
-
<button type="button" (click)="editForm.emit()" [attr.aria-label]="editFormLabel()">{{ editFormLabel() }}</button>
|
|
450
|
+
@if (enableCustomization()) {
|
|
451
|
+
<button type="button" class="pdx-btn pdx-btn--ghost" (click)="editForm.emit()" [attr.aria-label]="editFormLabel()">{{ editFormLabel() }}</button>
|
|
397
452
|
}
|
|
398
|
-
<button type="button" (click)="reset.emit()" [attr.aria-label]="resetLabel()">{{ resetLabel() }}</button>
|
|
399
|
-
<button type="button" (click)="save.emit()" [attr.aria-label]="saveLabel()">{{ saveLabel() }}</button>
|
|
453
|
+
<button type="button" class="pdx-btn pdx-btn--ghost" (click)="reset.emit()" [attr.aria-label]="resetLabel()">{{ resetLabel() }}</button>
|
|
454
|
+
<button type="button" class="pdx-btn pdx-btn--primary" (click)="save.emit()" [attr.aria-label]="saveLabel()">{{ saveLabel() }}</button>
|
|
400
455
|
</div>
|
|
401
456
|
</header>
|
|
402
|
-
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}.pdx-manual-form__header{display:flex;justify-content:space-between;align-items:center;gap:1rem;margin-bottom:1.5rem}.pdx-manual-form__title h2{margin:0;font-size:1.
|
|
403
|
-
}] });
|
|
457
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}.pdx-manual-form__header{display:flex;justify-content:space-between;align-items:center;gap:1rem;margin-bottom:1.5rem}.pdx-manual-form__title h2{margin:0;font-size:1.25rem;font-weight:600;line-height:1.3}.pdx-manual-form__subtitle{margin:.25rem 0 0;color:var(--md-sys-color-on-surface-variant)}.pdx-manual-form__header-actions{display:flex;gap:.5rem;flex-wrap:wrap}.pdx-btn{min-height:36px;padding:0 14px;border-radius:8px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);cursor:pointer;transition:background-color .18s ease,border-color .18s ease,color .18s ease}.pdx-btn:hover{background:var(--md-sys-color-surface-container)}.pdx-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pdx-btn--ghost{background:transparent;border-color:var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface-variant)}.pdx-btn--primary{background:var(--md-sys-color-primary);border-color:var(--md-sys-color-primary);color:var(--md-sys-color-on-primary)}.pdx-btn--primary:hover{background:var(--md-sys-color-primary-container);border-color:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}\n"] }]
|
|
458
|
+
}], propDecorators: { instance: [{ type: i0.Input, args: [{ isSignal: true, alias: "instance", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], saveLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "saveLabel", required: false }] }], resetLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "resetLabel", required: false }] }], enableCustomization: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableCustomization", required: false }] }], editFormLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "editFormLabel", required: false }] }], save: [{ type: i0.Output, args: ["save"] }], reset: [{ type: i0.Output, args: ["reset"] }], editForm: [{ type: i0.Output, args: ["editForm"] }] } });
|
|
404
459
|
|
|
405
460
|
class ManualFormActionsComponent {
|
|
406
461
|
actions = input(...(ngDevMode ? [undefined, { debugName: "actions" }] : []));
|
|
@@ -417,28 +472,28 @@ class ManualFormActionsComponent {
|
|
|
417
472
|
this.actionClick.emit(kind);
|
|
418
473
|
}
|
|
419
474
|
}
|
|
420
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.
|
|
421
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.
|
|
475
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFormActionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
476
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: ManualFormActionsComponent, isStandalone: true, selector: "praxis-manual-form-actions", inputs: { actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, trackByFn: { classPropertyName: "trackByFn", publicName: "trackByFn", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { actionClick: "actionClick" }, ngImport: i0, template: `
|
|
422
477
|
@if (actions(); as layout) {
|
|
423
478
|
<footer class="pdx-manual-form__actions">
|
|
424
479
|
<div class="pdx-manual-form__actions-secondary">
|
|
425
480
|
@if (layout.cancel; as cancel) {
|
|
426
481
|
@if (isVisible(cancel)) {
|
|
427
|
-
<button type="button" [disabled]="cancel.disabled" (click)="emit('cancel', cancel)">
|
|
482
|
+
<button type="button" class="pdx-btn pdx-btn--ghost" [disabled]="cancel.disabled" (click)="emit('cancel', cancel)">
|
|
428
483
|
{{ cancel.label || 'Cancelar' }}
|
|
429
484
|
</button>
|
|
430
485
|
}
|
|
431
486
|
}
|
|
432
487
|
@if (layout.reset; as resetBtn) {
|
|
433
488
|
@if (isVisible(resetBtn)) {
|
|
434
|
-
<button type="button" [disabled]="resetBtn.disabled" (click)="emit('reset', resetBtn)">
|
|
489
|
+
<button type="button" class="pdx-btn pdx-btn--ghost" [disabled]="resetBtn.disabled" (click)="emit('reset', resetBtn)">
|
|
435
490
|
{{ resetBtn.label || 'Resetar' }}
|
|
436
491
|
</button>
|
|
437
492
|
}
|
|
438
493
|
}
|
|
439
494
|
@for (action of layout.custom || []; track trackByFn ? trackByFn($index, action) : (action?.id || $index)) {
|
|
440
495
|
@if (isVisible(action)) {
|
|
441
|
-
<button type="button" [disabled]="action.disabled" (click)="emit(action.id || 'custom', action)">
|
|
496
|
+
<button type="button" class="pdx-btn pdx-btn--ghost" [disabled]="action.disabled" (click)="emit(action.id || 'custom', action)">
|
|
442
497
|
{{ action.label || action.id || 'Ação' }}
|
|
443
498
|
</button>
|
|
444
499
|
}
|
|
@@ -447,7 +502,7 @@ class ManualFormActionsComponent {
|
|
|
447
502
|
<div class="pdx-manual-form__actions-primary">
|
|
448
503
|
@if (layout.submit; as submitBtn) {
|
|
449
504
|
@if (isVisible(submitBtn)) {
|
|
450
|
-
<button type="submit" [disabled]="submitBtn.disabled" (click)="emit('submit', submitBtn)">
|
|
505
|
+
<button type="submit" class="pdx-btn pdx-btn--primary" [disabled]="submitBtn.disabled" (click)="emit('submit', submitBtn)">
|
|
451
506
|
{{ submitBtn.label || 'Salvar' }}
|
|
452
507
|
</button>
|
|
453
508
|
}
|
|
@@ -455,9 +510,9 @@ class ManualFormActionsComponent {
|
|
|
455
510
|
</div>
|
|
456
511
|
</footer>
|
|
457
512
|
}
|
|
458
|
-
`, isInline: true, styles: [":host{display:block}.pdx-manual-form__actions{display:flex;justify-content:space-between;align-items:center;gap:1rem;margin-top:2rem}.pdx-manual-form__actions-secondary,.pdx-manual-form__actions-primary{display:flex;gap:.5rem}
|
|
513
|
+
`, isInline: true, styles: [":host{display:block}.pdx-manual-form__actions{display:flex;justify-content:space-between;align-items:center;gap:1rem;margin-top:2rem;flex-wrap:wrap}.pdx-manual-form__actions-secondary,.pdx-manual-form__actions-primary{display:flex;gap:.5rem}.pdx-btn{min-height:36px;padding:0 14px;border-radius:8px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);cursor:pointer;transition:background-color .18s ease,border-color .18s ease,color .18s ease}.pdx-btn:hover{background:var(--md-sys-color-surface-container)}.pdx-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pdx-btn:disabled{opacity:.6;cursor:not-allowed}.pdx-btn--ghost{background:transparent;border-color:var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface-variant)}.pdx-btn--primary{background:var(--md-sys-color-primary);color:var(--md-sys-color-on-primary);border-color:var(--md-sys-color-primary)}.pdx-btn--primary:hover{background:var(--md-sys-color-primary-container);border-color:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
459
514
|
}
|
|
460
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
515
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFormActionsComponent, decorators: [{
|
|
461
516
|
type: Component,
|
|
462
517
|
args: [{ selector: 'praxis-manual-form-actions', standalone: true, imports: [CommonModule], template: `
|
|
463
518
|
@if (actions(); as layout) {
|
|
@@ -465,21 +520,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
465
520
|
<div class="pdx-manual-form__actions-secondary">
|
|
466
521
|
@if (layout.cancel; as cancel) {
|
|
467
522
|
@if (isVisible(cancel)) {
|
|
468
|
-
<button type="button" [disabled]="cancel.disabled" (click)="emit('cancel', cancel)">
|
|
523
|
+
<button type="button" class="pdx-btn pdx-btn--ghost" [disabled]="cancel.disabled" (click)="emit('cancel', cancel)">
|
|
469
524
|
{{ cancel.label || 'Cancelar' }}
|
|
470
525
|
</button>
|
|
471
526
|
}
|
|
472
527
|
}
|
|
473
528
|
@if (layout.reset; as resetBtn) {
|
|
474
529
|
@if (isVisible(resetBtn)) {
|
|
475
|
-
<button type="button" [disabled]="resetBtn.disabled" (click)="emit('reset', resetBtn)">
|
|
530
|
+
<button type="button" class="pdx-btn pdx-btn--ghost" [disabled]="resetBtn.disabled" (click)="emit('reset', resetBtn)">
|
|
476
531
|
{{ resetBtn.label || 'Resetar' }}
|
|
477
532
|
</button>
|
|
478
533
|
}
|
|
479
534
|
}
|
|
480
535
|
@for (action of layout.custom || []; track trackByFn ? trackByFn($index, action) : (action?.id || $index)) {
|
|
481
536
|
@if (isVisible(action)) {
|
|
482
|
-
<button type="button" [disabled]="action.disabled" (click)="emit(action.id || 'custom', action)">
|
|
537
|
+
<button type="button" class="pdx-btn pdx-btn--ghost" [disabled]="action.disabled" (click)="emit(action.id || 'custom', action)">
|
|
483
538
|
{{ action.label || action.id || 'Ação' }}
|
|
484
539
|
</button>
|
|
485
540
|
}
|
|
@@ -488,7 +543,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
488
543
|
<div class="pdx-manual-form__actions-primary">
|
|
489
544
|
@if (layout.submit; as submitBtn) {
|
|
490
545
|
@if (isVisible(submitBtn)) {
|
|
491
|
-
<button type="submit" [disabled]="submitBtn.disabled" (click)="emit('submit', submitBtn)">
|
|
546
|
+
<button type="submit" class="pdx-btn pdx-btn--primary" [disabled]="submitBtn.disabled" (click)="emit('submit', submitBtn)">
|
|
492
547
|
{{ submitBtn.label || 'Salvar' }}
|
|
493
548
|
</button>
|
|
494
549
|
}
|
|
@@ -496,10 +551,632 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
496
551
|
</div>
|
|
497
552
|
</footer>
|
|
498
553
|
}
|
|
499
|
-
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}.pdx-manual-form__actions{display:flex;justify-content:space-between;align-items:center;gap:1rem;margin-top:2rem}.pdx-manual-form__actions-secondary,.pdx-manual-form__actions-primary{display:flex;gap:.5rem}
|
|
500
|
-
}], propDecorators: { trackByFn: [{
|
|
554
|
+
`, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}.pdx-manual-form__actions{display:flex;justify-content:space-between;align-items:center;gap:1rem;margin-top:2rem;flex-wrap:wrap}.pdx-manual-form__actions-secondary,.pdx-manual-form__actions-primary{display:flex;gap:.5rem}.pdx-btn{min-height:36px;padding:0 14px;border-radius:8px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);cursor:pointer;transition:background-color .18s ease,border-color .18s ease,color .18s ease}.pdx-btn:hover{background:var(--md-sys-color-surface-container)}.pdx-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.pdx-btn:disabled{opacity:.6;cursor:not-allowed}.pdx-btn--ghost{background:transparent;border-color:var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface-variant)}.pdx-btn--primary{background:var(--md-sys-color-primary);color:var(--md-sys-color-on-primary);border-color:var(--md-sys-color-primary)}.pdx-btn--primary:hover{background:var(--md-sys-color-primary-container);border-color:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}\n"] }]
|
|
555
|
+
}], propDecorators: { actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], trackByFn: [{
|
|
501
556
|
type: Input
|
|
502
|
-
}] } });
|
|
557
|
+
}], actionClick: [{ type: i0.Output, args: ["actionClick"] }] } });
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Capabilities catalog for Manual Form (FormConfig-based).
|
|
561
|
+
* Focuses on the FormConfig structure managed by ManualFormInstance.
|
|
562
|
+
*/
|
|
563
|
+
const ENUMS = {
|
|
564
|
+
actionColor: ['primary', 'accent', 'warn', 'basic'],
|
|
565
|
+
actionSize: ['small', 'medium', 'large'],
|
|
566
|
+
actionVariant: ['raised', 'stroked', 'flat', 'fab'],
|
|
567
|
+
actionType: ['button', 'submit', 'reset'],
|
|
568
|
+
actionsPosition: ['left', 'center', 'right', 'justified', 'split'],
|
|
569
|
+
actionsMobilePosition: ['left', 'center', 'right', 'justified'],
|
|
570
|
+
actionsOrientation: ['horizontal', 'vertical'],
|
|
571
|
+
actionsSpacing: ['compact', 'normal', 'spacious'],
|
|
572
|
+
actionsPlacement: ['insideLastSection', 'afterSections', 'top'],
|
|
573
|
+
columnAlign: ['start', 'center', 'end', 'stretch'],
|
|
574
|
+
sectionTitleStyle: ['titleLarge', 'titleMedium', 'titleSmall', 'headlineSmall'],
|
|
575
|
+
sectionDescriptionStyle: ['bodyLarge', 'bodyMedium', 'bodySmall'],
|
|
576
|
+
headerAlign: ['start', 'center'],
|
|
577
|
+
schemaType: ['response', 'request'],
|
|
578
|
+
customizationSource: ['user', 'server', 'system'],
|
|
579
|
+
formRuleTargetType: ['field', 'section', 'action', 'row', 'column'],
|
|
580
|
+
formRuleContext: ['visibility', 'readOnly', 'style', 'validation', 'notification'],
|
|
581
|
+
mode: ['create', 'edit', 'view'],
|
|
582
|
+
schemaSource: ['resource', 'filter'],
|
|
583
|
+
};
|
|
584
|
+
function generateRulePropertyCapabilities(targetType, properties) {
|
|
585
|
+
const result = [];
|
|
586
|
+
properties.forEach((prop) => {
|
|
587
|
+
const valueKind = prop.type === 'object'
|
|
588
|
+
? 'object'
|
|
589
|
+
: prop.type === 'enum'
|
|
590
|
+
? 'enum'
|
|
591
|
+
: prop.type === 'boolean'
|
|
592
|
+
? 'boolean'
|
|
593
|
+
: prop.type === 'number'
|
|
594
|
+
? 'number'
|
|
595
|
+
: 'string';
|
|
596
|
+
const capability = {
|
|
597
|
+
path: `formRules[].effect.properties.${prop.name}`,
|
|
598
|
+
category: 'rules',
|
|
599
|
+
valueKind,
|
|
600
|
+
description: `Propriedade '${prop.label}' aplicada ao ${targetType}.`,
|
|
601
|
+
example: prop.type === 'boolean'
|
|
602
|
+
? 'true'
|
|
603
|
+
: prop.type === 'number'
|
|
604
|
+
? '123'
|
|
605
|
+
: prop.type === 'enum'
|
|
606
|
+
? (prop.enumValues?.[0]?.value || 'valor')
|
|
607
|
+
: 'valor',
|
|
608
|
+
safetyNotes: prop.type === 'object' ? 'O objeto deve seguir o schema esperado pelo componente alvo.' : undefined,
|
|
609
|
+
};
|
|
610
|
+
if (prop.enumValues) {
|
|
611
|
+
capability.allowedValues = prop.enumValues.map((ev) => ev.value);
|
|
612
|
+
}
|
|
613
|
+
result.push(capability);
|
|
614
|
+
});
|
|
615
|
+
return result;
|
|
616
|
+
}
|
|
617
|
+
function generateRulePropertyWhenFalseCapabilities(targetType, properties) {
|
|
618
|
+
const result = [];
|
|
619
|
+
properties.forEach((prop) => {
|
|
620
|
+
const valueKind = prop.type === 'object'
|
|
621
|
+
? 'object'
|
|
622
|
+
: prop.type === 'enum'
|
|
623
|
+
? 'enum'
|
|
624
|
+
: prop.type === 'boolean'
|
|
625
|
+
? 'boolean'
|
|
626
|
+
: prop.type === 'number'
|
|
627
|
+
? 'number'
|
|
628
|
+
: 'string';
|
|
629
|
+
const capability = {
|
|
630
|
+
path: `formRules[].effect.propertiesWhenFalse.${prop.name}`,
|
|
631
|
+
category: 'rules',
|
|
632
|
+
valueKind,
|
|
633
|
+
description: `Propriedade '${prop.label}' aplicada ao ${targetType} quando a condicao e falsa.`,
|
|
634
|
+
example: prop.type === 'boolean'
|
|
635
|
+
? 'false'
|
|
636
|
+
: prop.type === 'number'
|
|
637
|
+
? '0'
|
|
638
|
+
: prop.type === 'enum'
|
|
639
|
+
? (prop.enumValues?.[0]?.value || 'valor')
|
|
640
|
+
: 'valor',
|
|
641
|
+
safetyNotes: prop.type === 'object' ? 'O objeto deve seguir o schema esperado pelo componente alvo.' : undefined,
|
|
642
|
+
};
|
|
643
|
+
if (prop.enumValues) {
|
|
644
|
+
capability.allowedValues = prop.enumValues.map((ev) => ev.value);
|
|
645
|
+
}
|
|
646
|
+
result.push(capability);
|
|
647
|
+
});
|
|
648
|
+
return result;
|
|
649
|
+
}
|
|
650
|
+
const MANUAL_FORM_AI_CAPABILITIES = {
|
|
651
|
+
version: 'v1.3',
|
|
652
|
+
enums: ENUMS,
|
|
653
|
+
targets: [
|
|
654
|
+
'praxis-manual-form',
|
|
655
|
+
'praxis-manual-form-config-editor',
|
|
656
|
+
'praxis-manual-form-actions',
|
|
657
|
+
'praxis-manual-form-header',
|
|
658
|
+
],
|
|
659
|
+
notes: [
|
|
660
|
+
'ManualForm usa FormConfig interno para metadados e layout.',
|
|
661
|
+
'fieldMetadata[] deve seguir o catalogo base de FieldMetadata.',
|
|
662
|
+
'Arrays de layout e actions devem ser mesclados por id/label, evitando replace total.',
|
|
663
|
+
'ManualFormHeader usa metadata.title/metadata.description como fallback para titulo/descricao.',
|
|
664
|
+
'Se controlType nao tiver catalogo dedicado, trate como FieldMetadata base.',
|
|
665
|
+
],
|
|
666
|
+
capabilities: [
|
|
667
|
+
// --- Form metadata ---
|
|
668
|
+
{
|
|
669
|
+
path: 'metadata.version',
|
|
670
|
+
category: 'metadata',
|
|
671
|
+
valueKind: 'string',
|
|
672
|
+
description: 'Versao da configuracao do formulario.',
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
path: 'metadata.source',
|
|
676
|
+
category: 'metadata',
|
|
677
|
+
valueKind: 'enum',
|
|
678
|
+
allowedValues: ['local', 'server', 'default'],
|
|
679
|
+
description: 'Origem da configuracao.',
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
path: 'metadata.title',
|
|
683
|
+
category: 'metadata',
|
|
684
|
+
valueKind: 'string',
|
|
685
|
+
description: 'Titulo exibido no cabecalho quando formTitle nao for informado.',
|
|
686
|
+
},
|
|
687
|
+
{
|
|
688
|
+
path: 'metadata.description',
|
|
689
|
+
category: 'metadata',
|
|
690
|
+
valueKind: 'string',
|
|
691
|
+
description: 'Descricao exibida no cabecalho quando formDescription nao for informado.',
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
path: 'metadata.lastUpdated',
|
|
695
|
+
category: 'metadata',
|
|
696
|
+
valueKind: 'string',
|
|
697
|
+
description: 'Timestamp da ultima atualizacao.',
|
|
698
|
+
safetyNotes: 'Valor de auditoria; preferir preenchimento pelo host.',
|
|
699
|
+
},
|
|
700
|
+
{
|
|
701
|
+
path: 'metadata.schemaId',
|
|
702
|
+
category: 'metadata',
|
|
703
|
+
valueKind: 'string',
|
|
704
|
+
description: 'Identificador do schema usado na composicao.',
|
|
705
|
+
},
|
|
706
|
+
{
|
|
707
|
+
path: 'metadata.serverHash',
|
|
708
|
+
category: 'metadata',
|
|
709
|
+
valueKind: 'string',
|
|
710
|
+
description: 'Hash do schema/servidor para reconciliacao.',
|
|
711
|
+
},
|
|
712
|
+
{
|
|
713
|
+
path: 'metadata.schemaContext',
|
|
714
|
+
category: 'metadata',
|
|
715
|
+
valueKind: 'object',
|
|
716
|
+
description: 'Contexto do schema (path/operation/schemaType).',
|
|
717
|
+
},
|
|
718
|
+
{
|
|
719
|
+
path: 'metadata.schemaContext.path',
|
|
720
|
+
category: 'metadata',
|
|
721
|
+
valueKind: 'string',
|
|
722
|
+
description: 'Path do schema usado para compor o formulario.',
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
path: 'metadata.schemaContext.operation',
|
|
726
|
+
category: 'metadata',
|
|
727
|
+
valueKind: 'string',
|
|
728
|
+
description: 'Operacao do schema (ex: GET/POST).',
|
|
729
|
+
},
|
|
730
|
+
{
|
|
731
|
+
path: 'metadata.schemaContext.schemaType',
|
|
732
|
+
category: 'metadata',
|
|
733
|
+
valueKind: 'enum',
|
|
734
|
+
allowedValues: ENUMS.schemaType,
|
|
735
|
+
description: 'Tipo do schema (response/request).',
|
|
736
|
+
},
|
|
737
|
+
{
|
|
738
|
+
path: 'metadata.schemaContext.internal',
|
|
739
|
+
category: 'metadata',
|
|
740
|
+
valueKind: 'boolean',
|
|
741
|
+
description: 'Marca schema como interno.',
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
path: 'metadata.schemaContext.tenant',
|
|
745
|
+
category: 'metadata',
|
|
746
|
+
valueKind: 'string',
|
|
747
|
+
description: 'Tenant do schema.',
|
|
748
|
+
},
|
|
749
|
+
{
|
|
750
|
+
path: 'metadata.schemaContext.locale',
|
|
751
|
+
category: 'metadata',
|
|
752
|
+
valueKind: 'string',
|
|
753
|
+
description: 'Locale do schema.',
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
path: 'metadata.customizations',
|
|
757
|
+
category: 'metadata',
|
|
758
|
+
valueKind: 'array',
|
|
759
|
+
description: 'Log de customizacoes.',
|
|
760
|
+
safetyNotes: 'Usado para auditoria; nao gerar automaticamente.',
|
|
761
|
+
},
|
|
762
|
+
{
|
|
763
|
+
path: 'metadata.customizations[]',
|
|
764
|
+
category: 'metadata',
|
|
765
|
+
valueKind: 'object',
|
|
766
|
+
description: 'Registro de customizacao.',
|
|
767
|
+
},
|
|
768
|
+
{
|
|
769
|
+
path: 'metadata.customizations[].fieldName',
|
|
770
|
+
category: 'metadata',
|
|
771
|
+
valueKind: 'string',
|
|
772
|
+
description: 'Campo customizado.',
|
|
773
|
+
},
|
|
774
|
+
{
|
|
775
|
+
path: 'metadata.customizations[].property',
|
|
776
|
+
category: 'metadata',
|
|
777
|
+
valueKind: 'string',
|
|
778
|
+
description: 'Propriedade alterada.',
|
|
779
|
+
},
|
|
780
|
+
{
|
|
781
|
+
path: 'metadata.customizations[].oldValue',
|
|
782
|
+
category: 'metadata',
|
|
783
|
+
valueKind: 'object',
|
|
784
|
+
description: 'Valor anterior.',
|
|
785
|
+
},
|
|
786
|
+
{
|
|
787
|
+
path: 'metadata.customizations[].newValue',
|
|
788
|
+
category: 'metadata',
|
|
789
|
+
valueKind: 'object',
|
|
790
|
+
description: 'Novo valor.',
|
|
791
|
+
},
|
|
792
|
+
{
|
|
793
|
+
path: 'metadata.customizations[].timestamp',
|
|
794
|
+
category: 'metadata',
|
|
795
|
+
valueKind: 'string',
|
|
796
|
+
description: 'Timestamp da alteracao (ISO).',
|
|
797
|
+
},
|
|
798
|
+
{
|
|
799
|
+
path: 'metadata.customizations[].source',
|
|
800
|
+
category: 'metadata',
|
|
801
|
+
valueKind: 'enum',
|
|
802
|
+
allowedValues: ENUMS.customizationSource,
|
|
803
|
+
description: 'Origem da alteracao.',
|
|
804
|
+
},
|
|
805
|
+
// --- Field metadata ---
|
|
806
|
+
{
|
|
807
|
+
path: 'fieldMetadata',
|
|
808
|
+
category: 'fields',
|
|
809
|
+
valueKind: 'array',
|
|
810
|
+
critical: true,
|
|
811
|
+
description: 'Metadados dos campos (FieldMetadata).',
|
|
812
|
+
safetyNotes: 'Use o catalogo base FieldMetadata para detalhes de campos.',
|
|
813
|
+
},
|
|
814
|
+
{
|
|
815
|
+
path: 'fieldMetadata[]',
|
|
816
|
+
category: 'fields',
|
|
817
|
+
valueKind: 'object',
|
|
818
|
+
critical: true,
|
|
819
|
+
description: 'Item de metadados do campo (FieldMetadata).',
|
|
820
|
+
safetyNotes: 'Use o catalogo base FieldMetadata para detalhes de campos.',
|
|
821
|
+
},
|
|
822
|
+
// --- Layout ---
|
|
823
|
+
{ path: 'sections[]', category: 'layout', valueKind: 'object', description: 'Secao do formulario.' },
|
|
824
|
+
{ path: 'sections[].id', category: 'layout', valueKind: 'string', description: 'ID da secao.' },
|
|
825
|
+
{ path: 'sections[].title', category: 'layout', valueKind: 'string', description: 'Titulo da secao.' },
|
|
826
|
+
{ path: 'sections[].description', category: 'layout', valueKind: 'string', description: 'Descricao da secao.' },
|
|
827
|
+
{ path: 'sections[].icon', category: 'layout', valueKind: 'string', description: 'Icone da secao.' },
|
|
828
|
+
{ path: 'sections[].collapsible', category: 'layout', valueKind: 'boolean', description: 'Secao recolhivel.' },
|
|
829
|
+
{ path: 'sections[].collapsed', category: 'layout', valueKind: 'boolean', description: 'Estado inicial recolhido.' },
|
|
830
|
+
{ path: 'sections[].gapBottom', category: 'layout', valueKind: 'number', description: 'Espaco extra apos a secao (px).' },
|
|
831
|
+
{ path: 'sections[].titleGapBottom', category: 'layout', valueKind: 'number', description: 'Espaco abaixo do titulo (px).' },
|
|
832
|
+
{ path: 'sections[].descriptionGapBottom', category: 'layout', valueKind: 'number', description: 'Espaco abaixo da descricao (px).' },
|
|
833
|
+
{ path: 'sections[].titleStyle', category: 'layout', valueKind: 'enum', allowedValues: ENUMS.sectionTitleStyle, description: 'Preset tipografico do titulo.' },
|
|
834
|
+
{ path: 'sections[].descriptionStyle', category: 'layout', valueKind: 'enum', allowedValues: ENUMS.sectionDescriptionStyle, description: 'Preset tipografico da descricao.' },
|
|
835
|
+
{ path: 'sections[].titleColor', category: 'layout', valueKind: 'string', description: 'Cor do titulo.' },
|
|
836
|
+
{ path: 'sections[].descriptionColor', category: 'layout', valueKind: 'string', description: 'Cor da descricao.' },
|
|
837
|
+
{ path: 'sections[].headerAlign', category: 'layout', valueKind: 'enum', allowedValues: ENUMS.headerAlign, description: 'Alinhamento do header.' },
|
|
838
|
+
{ path: 'sections[].headerTooltip', category: 'layout', valueKind: 'string', description: 'Tooltip do header da secao.' },
|
|
839
|
+
{ path: 'sections[].rows[]', category: 'layout', valueKind: 'object', description: 'Linha da secao.' },
|
|
840
|
+
{ path: 'sections[].rows[].id', category: 'layout', valueKind: 'string', description: 'ID da linha.' },
|
|
841
|
+
{ path: 'sections[].rows[].title', category: 'layout', valueKind: 'string', description: 'Titulo da linha.' },
|
|
842
|
+
{ path: 'sections[].rows[].gap', category: 'layout', valueKind: 'number', description: 'Espaco horizontal entre colunas (px).' },
|
|
843
|
+
{ path: 'sections[].rows[].rowGap', category: 'layout', valueKind: 'number', description: 'Espaco vertical apos a linha (px).' },
|
|
844
|
+
{ path: 'sections[].rows[].columns[]', category: 'layout', valueKind: 'object', description: 'Coluna da linha.' },
|
|
845
|
+
{ path: 'sections[].rows[].columns[].id', category: 'layout', valueKind: 'string', description: 'ID da coluna.' },
|
|
846
|
+
{ path: 'sections[].rows[].columns[].title', category: 'layout', valueKind: 'string', description: 'Titulo da coluna.' },
|
|
847
|
+
{ path: 'sections[].rows[].columns[].fields', category: 'layout', valueKind: 'array', description: 'Campos associados a coluna.' },
|
|
848
|
+
{ path: 'sections[].rows[].columns[].fields[]', category: 'layout', valueKind: 'string', description: 'Nome do campo na coluna.', safetyNotes: 'Use nomes presentes em fieldMetadata[].name.' },
|
|
849
|
+
{ path: 'sections[].rows[].columns[].span', category: 'layout', valueKind: 'object', description: 'Span responsivo.' },
|
|
850
|
+
{ path: 'sections[].rows[].columns[].span.xs', category: 'layout', valueKind: 'number', description: 'Span xs (1-12).' },
|
|
851
|
+
{ path: 'sections[].rows[].columns[].span.sm', category: 'layout', valueKind: 'number', description: 'Span sm (1-12).' },
|
|
852
|
+
{ path: 'sections[].rows[].columns[].span.md', category: 'layout', valueKind: 'number', description: 'Span md (1-12).' },
|
|
853
|
+
{ path: 'sections[].rows[].columns[].span.lg', category: 'layout', valueKind: 'number', description: 'Span lg (1-12).' },
|
|
854
|
+
{ path: 'sections[].rows[].columns[].span.xl', category: 'layout', valueKind: 'number', description: 'Span xl (1-12).' },
|
|
855
|
+
{ path: 'sections[].rows[].columns[].offset', category: 'layout', valueKind: 'object', description: 'Offset responsivo.' },
|
|
856
|
+
{ path: 'sections[].rows[].columns[].offset.xs', category: 'layout', valueKind: 'number', description: 'Offset xs (1-12).' },
|
|
857
|
+
{ path: 'sections[].rows[].columns[].offset.sm', category: 'layout', valueKind: 'number', description: 'Offset sm (1-12).' },
|
|
858
|
+
{ path: 'sections[].rows[].columns[].offset.md', category: 'layout', valueKind: 'number', description: 'Offset md (1-12).' },
|
|
859
|
+
{ path: 'sections[].rows[].columns[].offset.lg', category: 'layout', valueKind: 'number', description: 'Offset lg (1-12).' },
|
|
860
|
+
{ path: 'sections[].rows[].columns[].offset.xl', category: 'layout', valueKind: 'number', description: 'Offset xl (1-12).' },
|
|
861
|
+
{ path: 'sections[].rows[].columns[].order', category: 'layout', valueKind: 'object', description: 'Order responsivo.' },
|
|
862
|
+
{ path: 'sections[].rows[].columns[].order.xs', category: 'layout', valueKind: 'number', description: 'Order xs.' },
|
|
863
|
+
{ path: 'sections[].rows[].columns[].order.sm', category: 'layout', valueKind: 'number', description: 'Order sm.' },
|
|
864
|
+
{ path: 'sections[].rows[].columns[].order.md', category: 'layout', valueKind: 'number', description: 'Order md.' },
|
|
865
|
+
{ path: 'sections[].rows[].columns[].order.lg', category: 'layout', valueKind: 'number', description: 'Order lg.' },
|
|
866
|
+
{ path: 'sections[].rows[].columns[].order.xl', category: 'layout', valueKind: 'number', description: 'Order xl.' },
|
|
867
|
+
{ path: 'sections[].rows[].columns[].hidden', category: 'layout', valueKind: 'object', description: 'Hidden responsivo.' },
|
|
868
|
+
{ path: 'sections[].rows[].columns[].hidden.xs', category: 'layout', valueKind: 'boolean', description: 'Ocultar no xs.' },
|
|
869
|
+
{ path: 'sections[].rows[].columns[].hidden.sm', category: 'layout', valueKind: 'boolean', description: 'Ocultar no sm.' },
|
|
870
|
+
{ path: 'sections[].rows[].columns[].hidden.md', category: 'layout', valueKind: 'boolean', description: 'Ocultar no md.' },
|
|
871
|
+
{ path: 'sections[].rows[].columns[].hidden.lg', category: 'layout', valueKind: 'boolean', description: 'Ocultar no lg.' },
|
|
872
|
+
{ path: 'sections[].rows[].columns[].hidden.xl', category: 'layout', valueKind: 'boolean', description: 'Ocultar no xl.' },
|
|
873
|
+
{ path: 'sections[].rows[].columns[].align', category: 'layout', valueKind: 'enum', allowedValues: ENUMS.columnAlign, description: 'Alinhamento da coluna.' },
|
|
874
|
+
{ path: 'sections[].rows[].columns[].padding', category: 'layout', valueKind: 'number', description: 'Padding da coluna (px).' },
|
|
875
|
+
{ path: 'sections[].rows[].columns[].className', category: 'layout', valueKind: 'string', description: 'Classe CSS da coluna.' },
|
|
876
|
+
{ path: 'sections[].rows[].columns[].testId', category: 'layout', valueKind: 'string', description: 'Test id da coluna.' },
|
|
877
|
+
// --- Actions ---
|
|
878
|
+
{ path: 'actions', category: 'actions', valueKind: 'object', description: 'Layout de acoes do formulario.' },
|
|
879
|
+
{ path: 'actions.position', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionsPosition, description: 'Posicao das acoes.' },
|
|
880
|
+
{ path: 'actions.orientation', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionsOrientation, description: 'Orientacao das acoes.' },
|
|
881
|
+
{ path: 'actions.spacing', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionsSpacing, description: 'Espacamento das acoes.' },
|
|
882
|
+
{ path: 'actions.sticky', category: 'actions', valueKind: 'boolean', description: 'Fixar barra de acoes.' },
|
|
883
|
+
{ path: 'actions.divider', category: 'actions', valueKind: 'boolean', description: 'Exibir divisor acima das acoes.' },
|
|
884
|
+
{ path: 'actions.placement', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionsPlacement, description: 'Posicionamento estrutural das acoes.' },
|
|
885
|
+
{ path: 'actions.containerClassName', category: 'actions', valueKind: 'string', description: 'Classe CSS do container de acoes.' },
|
|
886
|
+
{ path: 'actions.containerStyles', category: 'actions', valueKind: 'object', description: 'Estilos inline do container.' },
|
|
887
|
+
{ path: 'actions.mobile', category: 'actions', valueKind: 'object', description: 'Config para mobile.' },
|
|
888
|
+
{ path: 'actions.mobile.position', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionsMobilePosition, description: 'Posicao das acoes no mobile.' },
|
|
889
|
+
{ path: 'actions.mobile.orientation', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionsOrientation, description: 'Orientacao no mobile.' },
|
|
890
|
+
{ path: 'actions.mobile.collapseToMenu', category: 'actions', valueKind: 'boolean', description: 'Colapsar acoes em menu.' },
|
|
891
|
+
{ path: 'actions.showSaveButton', category: 'actions', valueKind: 'boolean', description: 'Legacy: mostrar botao salvar.' },
|
|
892
|
+
{ path: 'actions.submitButtonLabel', category: 'actions', valueKind: 'string', description: 'Legacy: texto do submit.' },
|
|
893
|
+
{ path: 'actions.showCancelButton', category: 'actions', valueKind: 'boolean', description: 'Legacy: mostrar cancelar.' },
|
|
894
|
+
{ path: 'actions.cancelButtonLabel', category: 'actions', valueKind: 'string', description: 'Legacy: texto do cancelar.' },
|
|
895
|
+
{ path: 'actions.showResetButton', category: 'actions', valueKind: 'boolean', description: 'Legacy: mostrar reset.' },
|
|
896
|
+
{ path: 'actions.resetButtonLabel', category: 'actions', valueKind: 'string', description: 'Legacy: texto do reset.' },
|
|
897
|
+
{ path: 'actions.submit', category: 'actions', valueKind: 'object', description: 'Botao submit.' },
|
|
898
|
+
{ path: 'actions.cancel', category: 'actions', valueKind: 'object', description: 'Botao cancelar.' },
|
|
899
|
+
{ path: 'actions.reset', category: 'actions', valueKind: 'object', description: 'Botao reset.' },
|
|
900
|
+
{ path: 'actions.custom', category: 'actions', valueKind: 'array', description: 'Botoes customizados.' },
|
|
901
|
+
{ path: 'actions.custom[]', category: 'actions', valueKind: 'object', description: 'Configuracao de botao customizado.', safetyNotes: 'Requer action handler registrado no host.' },
|
|
902
|
+
{ path: 'actions.submit.id', category: 'actions', valueKind: 'string', description: 'ID do botao.' },
|
|
903
|
+
{ path: 'actions.submit.visible', category: 'actions', valueKind: 'boolean', description: 'Visibilidade.' },
|
|
904
|
+
{ path: 'actions.submit.label', category: 'actions', valueKind: 'string', description: 'Label do botao.' },
|
|
905
|
+
{ path: 'actions.submit.icon', category: 'actions', valueKind: 'string', description: 'Icone do botao.' },
|
|
906
|
+
{ path: 'actions.submit.className', category: 'actions', valueKind: 'string', description: 'Classe CSS.' },
|
|
907
|
+
{ path: 'actions.submit.style', category: 'actions', valueKind: 'object', description: 'Estilo inline.' },
|
|
908
|
+
{ path: 'actions.submit.color', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionColor, description: 'Cor do botao.' },
|
|
909
|
+
{ path: 'actions.submit.disabled', category: 'actions', valueKind: 'boolean', description: 'Botao desabilitado.' },
|
|
910
|
+
{ path: 'actions.submit.type', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionType, description: 'Tipo do botao.' },
|
|
911
|
+
{ path: 'actions.submit.action', category: 'actions', valueKind: 'string', description: 'Evento custom.', safetyNotes: 'Requer handler registrado no host.' },
|
|
912
|
+
{ path: 'actions.submit.tooltip', category: 'actions', valueKind: 'string', description: 'Tooltip.' },
|
|
913
|
+
{ path: 'actions.submit.loading', category: 'actions', valueKind: 'boolean', description: 'Estado loading.' },
|
|
914
|
+
{ path: 'actions.submit.size', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionSize, description: 'Tamanho do botao.' },
|
|
915
|
+
{ path: 'actions.submit.variant', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionVariant, description: 'Variante visual.' },
|
|
916
|
+
{ path: 'actions.submit.shortcut', category: 'actions', valueKind: 'string', description: 'Atalho de teclado.' },
|
|
917
|
+
// Apply same structure for cancel/reset/custom buttons
|
|
918
|
+
{ path: 'actions.cancel.id', category: 'actions', valueKind: 'string', description: 'ID do botao cancelar.' },
|
|
919
|
+
{ path: 'actions.cancel.visible', category: 'actions', valueKind: 'boolean', description: 'Visibilidade do botao cancelar.' },
|
|
920
|
+
{ path: 'actions.cancel.label', category: 'actions', valueKind: 'string', description: 'Label do botao cancelar.' },
|
|
921
|
+
{ path: 'actions.cancel.icon', category: 'actions', valueKind: 'string', description: 'Icone do botao cancelar.' },
|
|
922
|
+
{ path: 'actions.cancel.className', category: 'actions', valueKind: 'string', description: 'Classe CSS do botao cancelar.' },
|
|
923
|
+
{ path: 'actions.cancel.style', category: 'actions', valueKind: 'object', description: 'Estilo inline do botao cancelar.' },
|
|
924
|
+
{ path: 'actions.cancel.color', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionColor, description: 'Cor do botao cancelar.' },
|
|
925
|
+
{ path: 'actions.cancel.disabled', category: 'actions', valueKind: 'boolean', description: 'Estado desabilitado do botao cancelar.' },
|
|
926
|
+
{ path: 'actions.cancel.type', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionType, description: 'Tipo HTML do botao cancelar.' },
|
|
927
|
+
{ path: 'actions.cancel.action', category: 'actions', valueKind: 'string', description: 'Acao customizada disparada pelo botao cancelar.', safetyNotes: 'Requer handler registrado no host.' },
|
|
928
|
+
{ path: 'actions.cancel.tooltip', category: 'actions', valueKind: 'string', description: 'Tooltip do botao cancelar.' },
|
|
929
|
+
{ path: 'actions.cancel.loading', category: 'actions', valueKind: 'boolean', description: 'Estado de loading do botao cancelar.' },
|
|
930
|
+
{ path: 'actions.cancel.size', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionSize, description: 'Tamanho visual do botao cancelar.' },
|
|
931
|
+
{ path: 'actions.cancel.variant', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionVariant, description: 'Variante visual do botao cancelar.' },
|
|
932
|
+
{ path: 'actions.cancel.shortcut', category: 'actions', valueKind: 'string', description: 'Atalho de teclado do botao cancelar.' },
|
|
933
|
+
{ path: 'actions.reset.id', category: 'actions', valueKind: 'string', description: 'ID do botao reset.' },
|
|
934
|
+
{ path: 'actions.reset.visible', category: 'actions', valueKind: 'boolean', description: 'Visibilidade do botao reset.' },
|
|
935
|
+
{ path: 'actions.reset.label', category: 'actions', valueKind: 'string', description: 'Label do botao reset.' },
|
|
936
|
+
{ path: 'actions.reset.icon', category: 'actions', valueKind: 'string', description: 'Icone do botao reset.' },
|
|
937
|
+
{ path: 'actions.reset.className', category: 'actions', valueKind: 'string', description: 'Classe CSS do botao reset.' },
|
|
938
|
+
{ path: 'actions.reset.style', category: 'actions', valueKind: 'object', description: 'Estilo inline do botao reset.' },
|
|
939
|
+
{ path: 'actions.reset.color', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionColor, description: 'Cor do botao reset.' },
|
|
940
|
+
{ path: 'actions.reset.disabled', category: 'actions', valueKind: 'boolean', description: 'Estado desabilitado do botao reset.' },
|
|
941
|
+
{ path: 'actions.reset.type', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionType, description: 'Tipo HTML do botao reset.' },
|
|
942
|
+
{ path: 'actions.reset.action', category: 'actions', valueKind: 'string', description: 'Acao customizada disparada pelo botao reset.', safetyNotes: 'Requer handler registrado no host.' },
|
|
943
|
+
{ path: 'actions.reset.tooltip', category: 'actions', valueKind: 'string', description: 'Tooltip do botao reset.' },
|
|
944
|
+
{ path: 'actions.reset.loading', category: 'actions', valueKind: 'boolean', description: 'Estado de loading do botao reset.' },
|
|
945
|
+
{ path: 'actions.reset.size', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionSize, description: 'Tamanho visual do botao reset.' },
|
|
946
|
+
{ path: 'actions.reset.variant', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionVariant, description: 'Variante visual do botao reset.' },
|
|
947
|
+
{ path: 'actions.reset.shortcut', category: 'actions', valueKind: 'string', description: 'Atalho de teclado do botao reset.' },
|
|
948
|
+
{ path: 'actions.custom[].id', category: 'actions', valueKind: 'string', description: 'ID do botao customizado.' },
|
|
949
|
+
{ path: 'actions.custom[].visible', category: 'actions', valueKind: 'boolean', description: 'Visibilidade do botao customizado.' },
|
|
950
|
+
{ path: 'actions.custom[].label', category: 'actions', valueKind: 'string', description: 'Label do botao customizado.' },
|
|
951
|
+
{ path: 'actions.custom[].icon', category: 'actions', valueKind: 'string', description: 'Icone do botao customizado.' },
|
|
952
|
+
{ path: 'actions.custom[].className', category: 'actions', valueKind: 'string', description: 'Classe CSS do botao customizado.' },
|
|
953
|
+
{ path: 'actions.custom[].style', category: 'actions', valueKind: 'object', description: 'Estilo inline do botao customizado.' },
|
|
954
|
+
{ path: 'actions.custom[].color', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionColor, description: 'Cor do botao customizado.' },
|
|
955
|
+
{ path: 'actions.custom[].disabled', category: 'actions', valueKind: 'boolean', description: 'Estado desabilitado do botao customizado.' },
|
|
956
|
+
{ path: 'actions.custom[].type', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionType, description: 'Tipo HTML do botao customizado.' },
|
|
957
|
+
{ path: 'actions.custom[].action', category: 'actions', valueKind: 'string', description: 'Acao customizada disparada pelo botao.', safetyNotes: 'Requer handler registrado no host.' },
|
|
958
|
+
{ path: 'actions.custom[].tooltip', category: 'actions', valueKind: 'string', description: 'Tooltip do botao customizado.' },
|
|
959
|
+
{ path: 'actions.custom[].loading', category: 'actions', valueKind: 'boolean', description: 'Estado de loading do botao customizado.' },
|
|
960
|
+
{ path: 'actions.custom[].size', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionSize, description: 'Tamanho visual do botao customizado.' },
|
|
961
|
+
{ path: 'actions.custom[].variant', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionVariant, description: 'Variante visual do botao customizado.' },
|
|
962
|
+
{ path: 'actions.custom[].shortcut', category: 'actions', valueKind: 'string', description: 'Atalho de teclado do botao customizado.' },
|
|
963
|
+
// --- Behavior ---
|
|
964
|
+
{ path: 'behavior', category: 'behavior', valueKind: 'object', description: 'Comportamento do formulario.' },
|
|
965
|
+
{ path: 'behavior.confirmOnUnsavedChanges', category: 'behavior', valueKind: 'boolean', description: 'Solicita confirmacao ao sair com alteracoes nao salvas.' },
|
|
966
|
+
{ path: 'behavior.trackHistory', category: 'behavior', valueKind: 'boolean', description: 'Mantem historico de alteracoes do formulario.' },
|
|
967
|
+
{ path: 'behavior.focusFirstError', category: 'behavior', valueKind: 'boolean', description: 'Move o foco para o primeiro campo com erro.' },
|
|
968
|
+
{ path: 'behavior.scrollToErrors', category: 'behavior', valueKind: 'boolean', description: 'Rola a tela ate a primeira area com erro de validacao.' },
|
|
969
|
+
{ path: 'behavior.clearAfterSave', category: 'behavior', valueKind: 'boolean', description: 'Limpa o formulario apos salvar com sucesso.' },
|
|
970
|
+
{ path: 'behavior.redirectAfterSave', category: 'behavior', valueKind: 'string', description: 'Destino de navegacao apos salvar com sucesso.' },
|
|
971
|
+
{ path: 'behavior.reactiveValidation', category: 'behavior', valueKind: 'boolean', description: 'Ativa validacao reativa durante a digitacao.' },
|
|
972
|
+
{ path: 'behavior.reactiveValidationDebounceMs', category: 'behavior', valueKind: 'number', description: 'Debounce em milissegundos para a validacao reativa.' },
|
|
973
|
+
// --- API ---
|
|
974
|
+
{ path: 'api', category: 'api', valueKind: 'object', description: 'Configuracao de API do formulario.' },
|
|
975
|
+
{ path: 'api.saveEndpoint', category: 'api', valueKind: 'string', description: 'Endpoint usado para persistir os dados do formulario.' },
|
|
976
|
+
{ path: 'api.loadEndpoint', category: 'api', valueKind: 'string', description: 'Endpoint usado para carregar os dados iniciais do formulario.' },
|
|
977
|
+
{ path: 'api.saveMethod', category: 'api', valueKind: 'enum', allowedValues: ['POST', 'PUT', 'PATCH'], description: 'Metodo HTTP usado na operacao de salvamento.' },
|
|
978
|
+
{ path: 'api.timeout', category: 'api', valueKind: 'number', description: 'Timeout da operacao de API em milissegundos.' },
|
|
979
|
+
{ path: 'api.headers', category: 'api', valueKind: 'object', description: 'Headers HTTP adicionais enviados nas operacoes de API.' },
|
|
980
|
+
{ path: 'api.idField', category: 'api', valueKind: 'string', description: 'Campo que representa o identificador primario no payload remoto.' },
|
|
981
|
+
{ path: 'api.beforeSave', category: 'api', valueKind: 'expression', description: 'Hook antes de salvar (nao serializavel).', safetyNotes: 'Deve ser fornecido pelo host.' },
|
|
982
|
+
{ path: 'api.afterLoad', category: 'api', valueKind: 'expression', description: 'Hook apos carregar (nao serializavel).', safetyNotes: 'Deve ser fornecido pelo host.' },
|
|
983
|
+
// --- Messages ---
|
|
984
|
+
{ path: 'messages', category: 'messages', valueKind: 'object', description: 'Mensagens e i18n do formulario.' },
|
|
985
|
+
{ path: 'messages.updateRegistrySuccess', category: 'messages', valueKind: 'string', description: 'Mensagem de sucesso ao atualizar registro.' },
|
|
986
|
+
{ path: 'messages.createRegistrySuccess', category: 'messages', valueKind: 'string', description: 'Mensagem de sucesso ao criar registro.' },
|
|
987
|
+
{ path: 'messages.updateRegistryError', category: 'messages', valueKind: 'string', description: 'Mensagem de erro ao atualizar registro.' },
|
|
988
|
+
{ path: 'messages.createRegistryError', category: 'messages', valueKind: 'string', description: 'Mensagem de erro ao criar registro.' },
|
|
989
|
+
{ path: 'messages.confirmations', category: 'messages', valueKind: 'object', description: 'Mensagens de confirmacao.' },
|
|
990
|
+
{ path: 'messages.confirmations.submit', category: 'messages', valueKind: 'string', description: 'Confirmacao de submit.' },
|
|
991
|
+
{ path: 'messages.confirmations.cancel', category: 'messages', valueKind: 'string', description: 'Confirmacao de cancel.' },
|
|
992
|
+
{ path: 'messages.confirmations.reset', category: 'messages', valueKind: 'string', description: 'Confirmacao de reset.' },
|
|
993
|
+
{ path: 'messages.confirmations.[actionId]', category: 'messages', valueKind: 'string', description: 'Confirmacao para acao custom.' },
|
|
994
|
+
{ path: 'messages.customActions', category: 'messages', valueKind: 'object', description: 'Mensagens por acao custom.' },
|
|
995
|
+
{ path: 'messages.customActions.[actionId]', category: 'messages', valueKind: 'object', description: 'Mensagens da acao custom (success/error/confirmation).' },
|
|
996
|
+
{ path: 'messages.customActions.[actionId].success', category: 'messages', valueKind: 'string', description: 'Sucesso para acao custom.' },
|
|
997
|
+
{ path: 'messages.customActions.[actionId].error', category: 'messages', valueKind: 'string', description: 'Erro para acao custom.' },
|
|
998
|
+
{ path: 'messages.customActions.[actionId].confirmation', category: 'messages', valueKind: 'string', description: 'Confirmacao para acao custom.' },
|
|
999
|
+
{ path: 'messages.loading', category: 'messages', valueKind: 'object', description: 'Mensagens de loading.' },
|
|
1000
|
+
{ path: 'messages.loading.submit', category: 'messages', valueKind: 'string', description: 'Mensagem de loading no submit.' },
|
|
1001
|
+
{ path: 'messages.loading.cancel', category: 'messages', valueKind: 'string', description: 'Mensagem de loading no cancel.' },
|
|
1002
|
+
{ path: 'messages.loading.reset', category: 'messages', valueKind: 'string', description: 'Mensagem de loading no reset.' },
|
|
1003
|
+
{ path: 'messages.loading.[actionId]', category: 'messages', valueKind: 'string', description: 'Mensagem de loading para acao custom.' },
|
|
1004
|
+
// --- Rules & Hooks ---
|
|
1005
|
+
{ path: 'formRules', category: 'rules', valueKind: 'array', description: 'Regras de layout/comportamento.' },
|
|
1006
|
+
{ path: 'formRules[].id', category: 'rules', valueKind: 'string', description: 'ID da regra.' },
|
|
1007
|
+
{ path: 'formRules[].name', category: 'rules', valueKind: 'string', description: 'Nome da regra.' },
|
|
1008
|
+
{ path: 'formRules[].description', category: 'rules', valueKind: 'string', description: 'Descricao da regra.' },
|
|
1009
|
+
{ path: 'formRules[].targetType', category: 'rules', valueKind: 'enum', allowedValues: ENUMS.formRuleTargetType, description: 'Tipo de alvo.' },
|
|
1010
|
+
{ path: 'formRules[].targets', category: 'rules', valueKind: 'array', description: 'IDs dos alvos canonicos.' },
|
|
1011
|
+
{ path: 'formRules[].targets[]', category: 'rules', valueKind: 'string', description: 'ID do alvo canonico.' },
|
|
1012
|
+
{ path: 'formRules[].targetFields', category: 'rules', valueKind: 'array', description: 'Alias legado para campos alvo.' },
|
|
1013
|
+
{ path: 'formRules[].targetFields[]', category: 'rules', valueKind: 'string', description: 'ID do campo alvo (alias legado).' },
|
|
1014
|
+
{ path: 'formRules[].context', category: 'rules', valueKind: 'enum', allowedValues: ENUMS.formRuleContext, description: 'Contexto legado da regra.' },
|
|
1015
|
+
{ path: 'formRules[].effect.condition', category: 'rules', valueKind: 'expression', description: 'Condicao da regra.' },
|
|
1016
|
+
{ path: 'formRules[].effect.properties', category: 'rules', valueKind: 'object', description: 'Propriedades aplicadas quando true.' },
|
|
1017
|
+
...generateRulePropertyCapabilities('field', RULE_PROPERTY_SCHEMA.field),
|
|
1018
|
+
...generateRulePropertyCapabilities('section', RULE_PROPERTY_SCHEMA.section),
|
|
1019
|
+
...generateRulePropertyCapabilities('action', RULE_PROPERTY_SCHEMA.action),
|
|
1020
|
+
...generateRulePropertyCapabilities('row', RULE_PROPERTY_SCHEMA.row),
|
|
1021
|
+
...generateRulePropertyCapabilities('column', RULE_PROPERTY_SCHEMA.column),
|
|
1022
|
+
{ path: 'formRules[].effect.propertiesWhenFalse', category: 'rules', valueKind: 'object', description: 'Propriedades aplicadas quando false.' },
|
|
1023
|
+
...generateRulePropertyWhenFalseCapabilities('field', RULE_PROPERTY_SCHEMA.field),
|
|
1024
|
+
...generateRulePropertyWhenFalseCapabilities('section', RULE_PROPERTY_SCHEMA.section),
|
|
1025
|
+
...generateRulePropertyWhenFalseCapabilities('action', RULE_PROPERTY_SCHEMA.action),
|
|
1026
|
+
...generateRulePropertyWhenFalseCapabilities('row', RULE_PROPERTY_SCHEMA.row),
|
|
1027
|
+
...generateRulePropertyWhenFalseCapabilities('column', RULE_PROPERTY_SCHEMA.column),
|
|
1028
|
+
{ path: 'formRulesState', category: 'rules', valueKind: 'object', description: 'Estado bruto do visual builder.' },
|
|
1029
|
+
{ path: 'hooks', category: 'hooks', valueKind: 'object', description: 'Declaracoes de hooks por estagio.' },
|
|
1030
|
+
{ path: 'hooks.beforeInit', category: 'hooks', valueKind: 'array', description: 'Hooks beforeInit.' },
|
|
1031
|
+
{ path: 'hooks.beforeInit[]', category: 'hooks', valueKind: 'array', description: 'Hooks beforeInit.' },
|
|
1032
|
+
{ path: 'hooks.beforeInit[].id', category: 'hooks', valueKind: 'string', description: 'ID do hook.' },
|
|
1033
|
+
{ path: 'hooks.beforeInit[].priority', category: 'hooks', valueKind: 'number', description: 'Prioridade do hook.' },
|
|
1034
|
+
{ path: 'hooks.beforeInit[].timeoutMs', category: 'hooks', valueKind: 'number', description: 'Timeout do hook.' },
|
|
1035
|
+
{ path: 'hooks.beforeInit[].args', category: 'hooks', valueKind: 'object', description: 'Args do hook.' },
|
|
1036
|
+
{ path: 'hooks.afterInit', category: 'hooks', valueKind: 'array', description: 'Hooks afterInit.' },
|
|
1037
|
+
{ path: 'hooks.afterInit[]', category: 'hooks', valueKind: 'array', description: 'Hooks afterInit.' },
|
|
1038
|
+
{ path: 'hooks.afterInit[].id', category: 'hooks', valueKind: 'string', description: 'ID do hook executado apos a inicializacao.' },
|
|
1039
|
+
{ path: 'hooks.afterInit[].priority', category: 'hooks', valueKind: 'number', description: 'Prioridade de execucao do hook apos a inicializacao.' },
|
|
1040
|
+
{ path: 'hooks.afterInit[].timeoutMs', category: 'hooks', valueKind: 'number', description: 'Timeout do hook apos a inicializacao em milissegundos.' },
|
|
1041
|
+
{ path: 'hooks.afterInit[].args', category: 'hooks', valueKind: 'object', description: 'Argumentos configurados para o hook apos a inicializacao.' },
|
|
1042
|
+
{ path: 'hooks.beforeValidate', category: 'hooks', valueKind: 'array', description: 'Hooks beforeValidate.' },
|
|
1043
|
+
{ path: 'hooks.beforeValidate[]', category: 'hooks', valueKind: 'array', description: 'Hooks beforeValidate.' },
|
|
1044
|
+
{ path: 'hooks.beforeValidate[].id', category: 'hooks', valueKind: 'string', description: 'ID do hook executado antes da validacao.' },
|
|
1045
|
+
{ path: 'hooks.beforeValidate[].priority', category: 'hooks', valueKind: 'number', description: 'Prioridade de execucao do hook antes da validacao.' },
|
|
1046
|
+
{ path: 'hooks.beforeValidate[].timeoutMs', category: 'hooks', valueKind: 'number', description: 'Timeout do hook antes da validacao em milissegundos.' },
|
|
1047
|
+
{ path: 'hooks.beforeValidate[].args', category: 'hooks', valueKind: 'object', description: 'Argumentos configurados para o hook antes da validacao.' },
|
|
1048
|
+
{ path: 'hooks.afterValidate', category: 'hooks', valueKind: 'array', description: 'Hooks afterValidate.' },
|
|
1049
|
+
{ path: 'hooks.afterValidate[]', category: 'hooks', valueKind: 'array', description: 'Hooks afterValidate.' },
|
|
1050
|
+
{ path: 'hooks.afterValidate[].id', category: 'hooks', valueKind: 'string', description: 'ID do hook executado apos a validacao.' },
|
|
1051
|
+
{ path: 'hooks.afterValidate[].priority', category: 'hooks', valueKind: 'number', description: 'Prioridade de execucao do hook apos a validacao.' },
|
|
1052
|
+
{ path: 'hooks.afterValidate[].timeoutMs', category: 'hooks', valueKind: 'number', description: 'Timeout do hook apos a validacao em milissegundos.' },
|
|
1053
|
+
{ path: 'hooks.afterValidate[].args', category: 'hooks', valueKind: 'object', description: 'Argumentos configurados para o hook apos a validacao.' },
|
|
1054
|
+
{ path: 'hooks.beforeSubmit', category: 'hooks', valueKind: 'array', description: 'Hooks beforeSubmit.' },
|
|
1055
|
+
{ path: 'hooks.beforeSubmit[]', category: 'hooks', valueKind: 'array', description: 'Hooks beforeSubmit.' },
|
|
1056
|
+
{ path: 'hooks.beforeSubmit[].id', category: 'hooks', valueKind: 'string', description: 'ID do hook executado antes do submit.' },
|
|
1057
|
+
{ path: 'hooks.beforeSubmit[].priority', category: 'hooks', valueKind: 'number', description: 'Prioridade de execucao do hook antes do submit.' },
|
|
1058
|
+
{ path: 'hooks.beforeSubmit[].timeoutMs', category: 'hooks', valueKind: 'number', description: 'Timeout do hook antes do submit em milissegundos.' },
|
|
1059
|
+
{ path: 'hooks.beforeSubmit[].args', category: 'hooks', valueKind: 'object', description: 'Argumentos configurados para o hook antes do submit.' },
|
|
1060
|
+
{ path: 'hooks.afterSubmit', category: 'hooks', valueKind: 'array', description: 'Hooks afterSubmit.' },
|
|
1061
|
+
{ path: 'hooks.afterSubmit[]', category: 'hooks', valueKind: 'array', description: 'Hooks afterSubmit.' },
|
|
1062
|
+
{ path: 'hooks.afterSubmit[].id', category: 'hooks', valueKind: 'string', description: 'ID do hook executado apos o submit.' },
|
|
1063
|
+
{ path: 'hooks.afterSubmit[].priority', category: 'hooks', valueKind: 'number', description: 'Prioridade de execucao do hook apos o submit.' },
|
|
1064
|
+
{ path: 'hooks.afterSubmit[].timeoutMs', category: 'hooks', valueKind: 'number', description: 'Timeout do hook apos o submit em milissegundos.' },
|
|
1065
|
+
{ path: 'hooks.afterSubmit[].args', category: 'hooks', valueKind: 'object', description: 'Argumentos configurados para o hook apos o submit.' },
|
|
1066
|
+
{ path: 'hooks.onError', category: 'hooks', valueKind: 'array', description: 'Hooks onError.' },
|
|
1067
|
+
{ path: 'hooks.onError[]', category: 'hooks', valueKind: 'array', description: 'Hooks onError.' },
|
|
1068
|
+
{ path: 'hooks.onError[].id', category: 'hooks', valueKind: 'string', description: 'ID do hook executado em cenarios de erro.' },
|
|
1069
|
+
{ path: 'hooks.onError[].priority', category: 'hooks', valueKind: 'number', description: 'Prioridade de execucao do hook em cenarios de erro.' },
|
|
1070
|
+
{ path: 'hooks.onError[].timeoutMs', category: 'hooks', valueKind: 'number', description: 'Timeout do hook de erro em milissegundos.' },
|
|
1071
|
+
{ path: 'hooks.onError[].args', category: 'hooks', valueKind: 'object', description: 'Argumentos configurados para o hook de erro.' },
|
|
1072
|
+
// --- Hints ---
|
|
1073
|
+
{ path: 'hints.dataModes.create', category: 'hints', valueKind: 'string', description: 'Texto de apoio para o modo create de dados.' },
|
|
1074
|
+
{ path: 'hints.dataModes.edit', category: 'hints', valueKind: 'string', description: 'Texto de apoio para o modo edit de dados.' },
|
|
1075
|
+
{ path: 'hints.dataModes.view', category: 'hints', valueKind: 'string', description: 'Texto de apoio para o modo view de dados.' },
|
|
1076
|
+
{ path: 'hints.uiModes.presentation', category: 'hints', valueKind: 'string', description: 'Texto de apoio para o modo de apresentacao da UI.' },
|
|
1077
|
+
{ path: 'hints.uiModes.readonly', category: 'hints', valueKind: 'string', description: 'Texto de apoio para o modo somente leitura.' },
|
|
1078
|
+
{ path: 'hints.uiModes.disabled', category: 'hints', valueKind: 'string', description: 'Texto de apoio para o modo desabilitado.' },
|
|
1079
|
+
{ path: 'hints.uiModes.visible', category: 'hints', valueKind: 'string', description: 'Texto de apoio para a visibilidade dos elementos da UI.' },
|
|
1080
|
+
],
|
|
1081
|
+
};
|
|
1082
|
+
|
|
1083
|
+
class ManualFormAiAdapter extends BaseAiAdapter {
|
|
1084
|
+
host;
|
|
1085
|
+
componentName = 'Manual Form';
|
|
1086
|
+
constructor(host) {
|
|
1087
|
+
super();
|
|
1088
|
+
this.host = host;
|
|
1089
|
+
}
|
|
1090
|
+
getCurrentConfig() {
|
|
1091
|
+
const current = this.host.instance?.currentConfig ?? { sections: [] };
|
|
1092
|
+
return this.cloneConfig(current);
|
|
1093
|
+
}
|
|
1094
|
+
getCapabilities() {
|
|
1095
|
+
return MANUAL_FORM_AI_CAPABILITIES.capabilities;
|
|
1096
|
+
}
|
|
1097
|
+
getRuntimeState() {
|
|
1098
|
+
const form = this.host.instance?.form;
|
|
1099
|
+
const fields = this.host.instance?.currentConfig?.fieldMetadata || [];
|
|
1100
|
+
return {
|
|
1101
|
+
hasInstance: !!this.host.instance,
|
|
1102
|
+
formId: this.safeFormId(),
|
|
1103
|
+
fieldsCount: fields.length,
|
|
1104
|
+
valid: form?.valid ?? false,
|
|
1105
|
+
dirty: form?.dirty ?? false,
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
createSnapshot() {
|
|
1109
|
+
return this.getCurrentConfig();
|
|
1110
|
+
}
|
|
1111
|
+
async restoreSnapshot(snapshot) {
|
|
1112
|
+
if (!snapshot)
|
|
1113
|
+
return;
|
|
1114
|
+
this.applyConfig(this.cloneConfig(snapshot));
|
|
1115
|
+
}
|
|
1116
|
+
async applyPatch(patch, _intent) {
|
|
1117
|
+
if (!this.host.instance) {
|
|
1118
|
+
return { success: false, error: 'Manual form instance not ready.' };
|
|
1119
|
+
}
|
|
1120
|
+
try {
|
|
1121
|
+
const current = this.getCurrentConfig();
|
|
1122
|
+
const next = this.smartMergeFormConfig(current, patch);
|
|
1123
|
+
this.applyConfig(next);
|
|
1124
|
+
return { success: true };
|
|
1125
|
+
}
|
|
1126
|
+
catch (error) {
|
|
1127
|
+
const message = error instanceof Error ? error.message : 'Unknown error applying patch';
|
|
1128
|
+
return { success: false, error: message };
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
applyConfig(config) {
|
|
1132
|
+
const apply = this.host.applyConfigFromAdapter;
|
|
1133
|
+
if (typeof apply === 'function') {
|
|
1134
|
+
apply.call(this.host, config);
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
if (this.host.instance) {
|
|
1138
|
+
this.host.instance.replaceConfig(config);
|
|
1139
|
+
this.host.metadataChange.emit(this.host.instance.currentConfig);
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
smartMergeFormConfig(base, patch) {
|
|
1143
|
+
const result = deepMerge(base, patch);
|
|
1144
|
+
if (patch.fieldMetadata && Array.isArray(patch.fieldMetadata)) {
|
|
1145
|
+
const originalFields = base.fieldMetadata || [];
|
|
1146
|
+
const patchFields = patch.fieldMetadata.filter((field) => field &&
|
|
1147
|
+
typeof field.name === 'string' &&
|
|
1148
|
+
field.name.trim().length > 0);
|
|
1149
|
+
const mergedFields = originalFields.map((orig) => {
|
|
1150
|
+
const match = patchFields.find((candidate) => candidate.name === orig.name);
|
|
1151
|
+
return match ? deepMerge(orig, match) : orig;
|
|
1152
|
+
});
|
|
1153
|
+
patchFields.forEach((incoming) => {
|
|
1154
|
+
if (!originalFields.find((orig) => orig.name === incoming.name)) {
|
|
1155
|
+
mergedFields.push(incoming);
|
|
1156
|
+
}
|
|
1157
|
+
});
|
|
1158
|
+
result.fieldMetadata = mergedFields;
|
|
1159
|
+
}
|
|
1160
|
+
return result;
|
|
1161
|
+
}
|
|
1162
|
+
cloneConfig(config) {
|
|
1163
|
+
try {
|
|
1164
|
+
return structuredClone(config);
|
|
1165
|
+
}
|
|
1166
|
+
catch {
|
|
1167
|
+
return JSON.parse(JSON.stringify(config));
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
safeFormId() {
|
|
1171
|
+
try {
|
|
1172
|
+
const idSignal = this.host.formId;
|
|
1173
|
+
return typeof idSignal === 'function' ? idSignal() : undefined;
|
|
1174
|
+
}
|
|
1175
|
+
catch {
|
|
1176
|
+
return undefined;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
503
1180
|
|
|
504
1181
|
const MANUAL_FORM_SELECTOR_TO_CONTROL_TYPE = new InjectionToken('MANUAL_FORM_SELECTOR_TO_CONTROL_TYPE');
|
|
505
1182
|
const MANUAL_FORM_CONSTRUCTOR_TO_CONTROL_TYPE = new InjectionToken('MANUAL_FORM_CONSTRUCTOR_TO_CONTROL_TYPE');
|
|
@@ -544,7 +1221,7 @@ class ManualFieldMetadataBridgeService {
|
|
|
544
1221
|
}
|
|
545
1222
|
return;
|
|
546
1223
|
}
|
|
547
|
-
const controlType = seed?.controlType;
|
|
1224
|
+
const controlType = resolveControlTypeAlias(seed?.controlType, FieldControlType.INPUT);
|
|
548
1225
|
const title = this.buildPanelTitle(fieldName, seed);
|
|
549
1226
|
const panelId = this.buildPanelId(instance, fieldName);
|
|
550
1227
|
let ref;
|
|
@@ -645,8 +1322,9 @@ class ManualFieldMetadataBridgeService {
|
|
|
645
1322
|
if (!controlType) {
|
|
646
1323
|
return '';
|
|
647
1324
|
}
|
|
648
|
-
return
|
|
649
|
-
.
|
|
1325
|
+
return normalizeControlTypeKey(controlType)
|
|
1326
|
+
.toLowerCase()
|
|
1327
|
+
.split('_')
|
|
650
1328
|
.map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1).toLowerCase())
|
|
651
1329
|
.join(' ');
|
|
652
1330
|
}
|
|
@@ -679,28 +1357,292 @@ class ManualFieldMetadataBridgeService {
|
|
|
679
1357
|
CHIP_INPUT: 'sell',
|
|
680
1358
|
CPF_CNPJ_INPUT: 'fingerprint',
|
|
681
1359
|
};
|
|
682
|
-
const key =
|
|
1360
|
+
const key = normalizeControlTypeKey(controlType);
|
|
683
1361
|
return map[key] || 'tune';
|
|
684
1362
|
}
|
|
685
1363
|
bindComponent(instance, fieldName, componentInstance) {
|
|
686
1364
|
instance.bindComponent(fieldName, componentInstance);
|
|
687
1365
|
}
|
|
688
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.
|
|
689
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.
|
|
1366
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFieldMetadataBridgeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1367
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFieldMetadataBridgeService, providedIn: 'root' });
|
|
1368
|
+
}
|
|
1369
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFieldMetadataBridgeService, decorators: [{
|
|
1370
|
+
type: Injectable,
|
|
1371
|
+
args: [{ providedIn: 'root' }]
|
|
1372
|
+
}] });
|
|
1373
|
+
|
|
1374
|
+
class ManualFieldKeyService {
|
|
1375
|
+
resolveFieldName(input, instance) {
|
|
1376
|
+
if (!instance) {
|
|
1377
|
+
return { status: 'missing' };
|
|
1378
|
+
}
|
|
1379
|
+
const fieldNames = (instance.currentConfig.fieldMetadata || [])
|
|
1380
|
+
.map((meta) => meta.name)
|
|
1381
|
+
.filter((name) => !!name);
|
|
1382
|
+
const raw = Array.isArray(input) ? input.join('.') : input;
|
|
1383
|
+
const normalized = String(raw || '').trim();
|
|
1384
|
+
if (!normalized) {
|
|
1385
|
+
return { status: 'missing' };
|
|
1386
|
+
}
|
|
1387
|
+
if (fieldNames.includes(normalized)) {
|
|
1388
|
+
return { status: 'ok', key: normalized };
|
|
1389
|
+
}
|
|
1390
|
+
const lastSegment = Array.isArray(input)
|
|
1391
|
+
? input[input.length - 1]
|
|
1392
|
+
: normalized.split('.').pop();
|
|
1393
|
+
if (!lastSegment) {
|
|
1394
|
+
return { status: 'missing' };
|
|
1395
|
+
}
|
|
1396
|
+
const matches = fieldNames.filter((name) => name === lastSegment || name.endsWith(`.${lastSegment}`));
|
|
1397
|
+
if (matches.length === 1) {
|
|
1398
|
+
return { status: 'ok', key: matches[0] };
|
|
1399
|
+
}
|
|
1400
|
+
if (matches.length > 1) {
|
|
1401
|
+
return { status: 'ambiguous', candidates: matches };
|
|
1402
|
+
}
|
|
1403
|
+
return { status: 'missing' };
|
|
1404
|
+
}
|
|
1405
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFieldKeyService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1406
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFieldKeyService, providedIn: 'root' });
|
|
690
1407
|
}
|
|
691
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
1408
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFieldKeyService, decorators: [{
|
|
692
1409
|
type: Injectable,
|
|
693
1410
|
args: [{ providedIn: 'root' }]
|
|
694
1411
|
}] });
|
|
695
1412
|
|
|
1413
|
+
class ManualFieldToolbarComponent {
|
|
1414
|
+
metadata = null;
|
|
1415
|
+
toggleRequired = new EventEmitter();
|
|
1416
|
+
toggleReadonly = new EventEmitter();
|
|
1417
|
+
toggleHidden = new EventEmitter();
|
|
1418
|
+
toggleDisabled = new EventEmitter();
|
|
1419
|
+
openEditor = new EventEmitter();
|
|
1420
|
+
requestClose = new EventEmitter();
|
|
1421
|
+
firstAction;
|
|
1422
|
+
focusFirstAction() {
|
|
1423
|
+
this.firstAction?.nativeElement?.focus();
|
|
1424
|
+
}
|
|
1425
|
+
get isRequired() {
|
|
1426
|
+
const meta = this.metadata;
|
|
1427
|
+
return !!meta?.required || !!meta?.validators?.required;
|
|
1428
|
+
}
|
|
1429
|
+
get isReadOnly() {
|
|
1430
|
+
return !!this.metadata?.readOnly;
|
|
1431
|
+
}
|
|
1432
|
+
onKeydown(event) {
|
|
1433
|
+
if (event.key === 'Escape' || event.key === 'Esc') {
|
|
1434
|
+
event.stopPropagation();
|
|
1435
|
+
event.preventDefault();
|
|
1436
|
+
this.requestClose.emit();
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFieldToolbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1440
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: ManualFieldToolbarComponent, isStandalone: true, selector: "praxis-manual-field-toolbar", inputs: { metadata: "metadata" }, outputs: { toggleRequired: "toggleRequired", toggleReadonly: "toggleReadonly", toggleHidden: "toggleHidden", toggleDisabled: "toggleDisabled", openEditor: "openEditor", requestClose: "requestClose" }, host: { listeners: { "keydown": "onKeydown($event)" } }, viewQueries: [{ propertyName: "firstAction", first: true, predicate: ["firstAction"], descendants: true, static: true }], ngImport: i0, template: `
|
|
1441
|
+
<div
|
|
1442
|
+
class="pdx-manual-field-toolbar"
|
|
1443
|
+
role="toolbar"
|
|
1444
|
+
aria-label="Acoes do campo"
|
|
1445
|
+
>
|
|
1446
|
+
<button
|
|
1447
|
+
#firstAction
|
|
1448
|
+
type="button"
|
|
1449
|
+
class="toolbar-btn"
|
|
1450
|
+
[attr.aria-pressed]="isRequired ? 'true' : 'false'"
|
|
1451
|
+
aria-label="Alternar obrigatorio"
|
|
1452
|
+
title="Alternar obrigatorio"
|
|
1453
|
+
(click)="toggleRequired.emit()"
|
|
1454
|
+
>
|
|
1455
|
+
<span class="material-symbols-outlined toolbar-icon" [class.toolbar-icon--filled]="isRequired">
|
|
1456
|
+
star
|
|
1457
|
+
</span>
|
|
1458
|
+
</button>
|
|
1459
|
+
<button
|
|
1460
|
+
type="button"
|
|
1461
|
+
class="toolbar-btn"
|
|
1462
|
+
[attr.aria-pressed]="isReadOnly ? 'true' : 'false'"
|
|
1463
|
+
aria-label="Alternar somente leitura"
|
|
1464
|
+
title="Alternar somente leitura"
|
|
1465
|
+
(click)="toggleReadonly.emit()"
|
|
1466
|
+
>
|
|
1467
|
+
<span class="material-symbols-outlined">
|
|
1468
|
+
{{ isReadOnly ? 'lock' : 'lock_open' }}
|
|
1469
|
+
</span>
|
|
1470
|
+
</button>
|
|
1471
|
+
<button
|
|
1472
|
+
type="button"
|
|
1473
|
+
class="toolbar-btn"
|
|
1474
|
+
[attr.aria-pressed]="metadata?.hidden ? 'true' : 'false'"
|
|
1475
|
+
aria-label="Alternar visibilidade"
|
|
1476
|
+
title="Alternar visibilidade"
|
|
1477
|
+
(click)="toggleHidden.emit()"
|
|
1478
|
+
>
|
|
1479
|
+
<span class="material-symbols-outlined">
|
|
1480
|
+
{{ metadata?.hidden ? 'visibility_off' : 'visibility' }}
|
|
1481
|
+
</span>
|
|
1482
|
+
</button>
|
|
1483
|
+
<button
|
|
1484
|
+
type="button"
|
|
1485
|
+
class="toolbar-btn"
|
|
1486
|
+
[attr.aria-pressed]="metadata?.disabled ? 'true' : 'false'"
|
|
1487
|
+
aria-label="Alternar desabilitado"
|
|
1488
|
+
title="Alternar desabilitado"
|
|
1489
|
+
(click)="toggleDisabled.emit()"
|
|
1490
|
+
>
|
|
1491
|
+
<span class="material-symbols-outlined">
|
|
1492
|
+
{{ metadata?.disabled ? 'toggle_on' : 'toggle_off' }}
|
|
1493
|
+
</span>
|
|
1494
|
+
</button>
|
|
1495
|
+
<button
|
|
1496
|
+
type="button"
|
|
1497
|
+
class="toolbar-btn toolbar-btn--accent"
|
|
1498
|
+
aria-label="Configurar campo"
|
|
1499
|
+
title="Configurar campo"
|
|
1500
|
+
(click)="openEditor.emit()"
|
|
1501
|
+
>
|
|
1502
|
+
<span class="material-symbols-outlined">tune</span>
|
|
1503
|
+
</button>
|
|
1504
|
+
<button
|
|
1505
|
+
type="button"
|
|
1506
|
+
class="toolbar-btn toolbar-btn--ghost"
|
|
1507
|
+
aria-label="Fechar"
|
|
1508
|
+
title="Fechar"
|
|
1509
|
+
(click)="requestClose.emit()"
|
|
1510
|
+
>
|
|
1511
|
+
<span class="material-symbols-outlined">close</span>
|
|
1512
|
+
</button>
|
|
1513
|
+
</div>
|
|
1514
|
+
`, isInline: true, styles: [":host{display:block;pointer-events:auto;font-family:inherit}.pdx-manual-field-toolbar{display:inline-flex;align-items:center;gap:6px;padding:6px;border-radius:12px;background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);box-shadow:var(--md-sys-elevation-level3);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}.toolbar-btn{appearance:none;border:0;background:transparent;color:var(--md-sys-color-on-surface);display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:10px;cursor:pointer;transition:background-color .12s ease,transform .12s ease}.toolbar-btn:hover{background:var(--md-sys-color-primary-container)}.toolbar-btn:active{transform:scale(.97)}.toolbar-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.toolbar-btn--accent{background:var(--md-sys-color-primary-container)}.toolbar-btn--ghost{opacity:.7}.material-symbols-outlined{font-size:18px;line-height:18px}.toolbar-icon{font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 20}.toolbar-icon--filled{font-variation-settings:\"FILL\" 1,\"wght\" 600,\"GRAD\" 0,\"opsz\" 20}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
1515
|
+
}
|
|
1516
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFieldToolbarComponent, decorators: [{
|
|
1517
|
+
type: Component,
|
|
1518
|
+
args: [{ selector: 'praxis-manual-field-toolbar', standalone: true, imports: [CommonModule], template: `
|
|
1519
|
+
<div
|
|
1520
|
+
class="pdx-manual-field-toolbar"
|
|
1521
|
+
role="toolbar"
|
|
1522
|
+
aria-label="Acoes do campo"
|
|
1523
|
+
>
|
|
1524
|
+
<button
|
|
1525
|
+
#firstAction
|
|
1526
|
+
type="button"
|
|
1527
|
+
class="toolbar-btn"
|
|
1528
|
+
[attr.aria-pressed]="isRequired ? 'true' : 'false'"
|
|
1529
|
+
aria-label="Alternar obrigatorio"
|
|
1530
|
+
title="Alternar obrigatorio"
|
|
1531
|
+
(click)="toggleRequired.emit()"
|
|
1532
|
+
>
|
|
1533
|
+
<span class="material-symbols-outlined toolbar-icon" [class.toolbar-icon--filled]="isRequired">
|
|
1534
|
+
star
|
|
1535
|
+
</span>
|
|
1536
|
+
</button>
|
|
1537
|
+
<button
|
|
1538
|
+
type="button"
|
|
1539
|
+
class="toolbar-btn"
|
|
1540
|
+
[attr.aria-pressed]="isReadOnly ? 'true' : 'false'"
|
|
1541
|
+
aria-label="Alternar somente leitura"
|
|
1542
|
+
title="Alternar somente leitura"
|
|
1543
|
+
(click)="toggleReadonly.emit()"
|
|
1544
|
+
>
|
|
1545
|
+
<span class="material-symbols-outlined">
|
|
1546
|
+
{{ isReadOnly ? 'lock' : 'lock_open' }}
|
|
1547
|
+
</span>
|
|
1548
|
+
</button>
|
|
1549
|
+
<button
|
|
1550
|
+
type="button"
|
|
1551
|
+
class="toolbar-btn"
|
|
1552
|
+
[attr.aria-pressed]="metadata?.hidden ? 'true' : 'false'"
|
|
1553
|
+
aria-label="Alternar visibilidade"
|
|
1554
|
+
title="Alternar visibilidade"
|
|
1555
|
+
(click)="toggleHidden.emit()"
|
|
1556
|
+
>
|
|
1557
|
+
<span class="material-symbols-outlined">
|
|
1558
|
+
{{ metadata?.hidden ? 'visibility_off' : 'visibility' }}
|
|
1559
|
+
</span>
|
|
1560
|
+
</button>
|
|
1561
|
+
<button
|
|
1562
|
+
type="button"
|
|
1563
|
+
class="toolbar-btn"
|
|
1564
|
+
[attr.aria-pressed]="metadata?.disabled ? 'true' : 'false'"
|
|
1565
|
+
aria-label="Alternar desabilitado"
|
|
1566
|
+
title="Alternar desabilitado"
|
|
1567
|
+
(click)="toggleDisabled.emit()"
|
|
1568
|
+
>
|
|
1569
|
+
<span class="material-symbols-outlined">
|
|
1570
|
+
{{ metadata?.disabled ? 'toggle_on' : 'toggle_off' }}
|
|
1571
|
+
</span>
|
|
1572
|
+
</button>
|
|
1573
|
+
<button
|
|
1574
|
+
type="button"
|
|
1575
|
+
class="toolbar-btn toolbar-btn--accent"
|
|
1576
|
+
aria-label="Configurar campo"
|
|
1577
|
+
title="Configurar campo"
|
|
1578
|
+
(click)="openEditor.emit()"
|
|
1579
|
+
>
|
|
1580
|
+
<span class="material-symbols-outlined">tune</span>
|
|
1581
|
+
</button>
|
|
1582
|
+
<button
|
|
1583
|
+
type="button"
|
|
1584
|
+
class="toolbar-btn toolbar-btn--ghost"
|
|
1585
|
+
aria-label="Fechar"
|
|
1586
|
+
title="Fechar"
|
|
1587
|
+
(click)="requestClose.emit()"
|
|
1588
|
+
>
|
|
1589
|
+
<span class="material-symbols-outlined">close</span>
|
|
1590
|
+
</button>
|
|
1591
|
+
</div>
|
|
1592
|
+
`, styles: [":host{display:block;pointer-events:auto;font-family:inherit}.pdx-manual-field-toolbar{display:inline-flex;align-items:center;gap:6px;padding:6px;border-radius:12px;background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);box-shadow:var(--md-sys-elevation-level3);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}.toolbar-btn{appearance:none;border:0;background:transparent;color:var(--md-sys-color-on-surface);display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:10px;cursor:pointer;transition:background-color .12s ease,transform .12s ease}.toolbar-btn:hover{background:var(--md-sys-color-primary-container)}.toolbar-btn:active{transform:scale(.97)}.toolbar-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.toolbar-btn--accent{background:var(--md-sys-color-primary-container)}.toolbar-btn--ghost{opacity:.7}.material-symbols-outlined{font-size:18px;line-height:18px}.toolbar-icon{font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 20}.toolbar-icon--filled{font-variation-settings:\"FILL\" 1,\"wght\" 600,\"GRAD\" 0,\"opsz\" 20}\n"] }]
|
|
1593
|
+
}], propDecorators: { metadata: [{
|
|
1594
|
+
type: Input
|
|
1595
|
+
}], toggleRequired: [{
|
|
1596
|
+
type: Output
|
|
1597
|
+
}], toggleReadonly: [{
|
|
1598
|
+
type: Output
|
|
1599
|
+
}], toggleHidden: [{
|
|
1600
|
+
type: Output
|
|
1601
|
+
}], toggleDisabled: [{
|
|
1602
|
+
type: Output
|
|
1603
|
+
}], openEditor: [{
|
|
1604
|
+
type: Output
|
|
1605
|
+
}], requestClose: [{
|
|
1606
|
+
type: Output
|
|
1607
|
+
}], firstAction: [{
|
|
1608
|
+
type: ViewChild,
|
|
1609
|
+
args: ['firstAction', { static: true }]
|
|
1610
|
+
}], onKeydown: [{
|
|
1611
|
+
type: HostListener,
|
|
1612
|
+
args: ['keydown', ['$event']]
|
|
1613
|
+
}] } });
|
|
1614
|
+
|
|
696
1615
|
class ManualFormComponent {
|
|
697
1616
|
instanceFactory = inject(ManualFormInstanceFactory);
|
|
698
1617
|
cdr = inject(ChangeDetectorRef);
|
|
699
1618
|
destroyRef = inject(DestroyRef);
|
|
700
1619
|
metadataBridge = inject(ManualFieldMetadataBridgeService);
|
|
701
1620
|
settingsPanel = inject(SettingsPanelService);
|
|
702
|
-
|
|
1621
|
+
hooksRegistry = inject(FormHooksRegistry, { optional: true });
|
|
1622
|
+
componentKeys = inject(ComponentKeyService);
|
|
1623
|
+
overlay = inject(Overlay);
|
|
1624
|
+
fieldKeyService = inject(ManualFieldKeyService);
|
|
1625
|
+
platformId = inject(PLATFORM_ID);
|
|
1626
|
+
route = (() => { try {
|
|
1627
|
+
return inject(ActivatedRoute);
|
|
1628
|
+
}
|
|
1629
|
+
catch {
|
|
1630
|
+
return undefined;
|
|
1631
|
+
} })();
|
|
1632
|
+
warnedMissingId = false;
|
|
1633
|
+
disableSelectorDefaults = inject(FIELD_SELECTOR_REGISTRY_DISABLE_DEFAULTS, { optional: true }) ?? false;
|
|
1634
|
+
selectorToControlType = inject(MANUAL_FORM_SELECTOR_TO_CONTROL_TYPE, { optional: true }) ??
|
|
1635
|
+
(this.disableSelectorDefaults ? {} : DEFAULT_SELECTOR_TO_CONTROL_TYPE);
|
|
703
1636
|
constructorToControlType = inject(MANUAL_FORM_CONSTRUCTOR_TO_CONTROL_TYPE, { optional: true }) ?? DEFAULT_CONSTRUCTOR_TO_CONTROL_TYPE;
|
|
1637
|
+
selectorRegistry = inject(FieldSelectorRegistry, { optional: true });
|
|
1638
|
+
toolbarOverlayRef;
|
|
1639
|
+
toolbarComponentRef;
|
|
1640
|
+
toolbarFieldKey;
|
|
1641
|
+
toolbarAnchor;
|
|
1642
|
+
toolbarSubscriptions = [];
|
|
1643
|
+
toolbarActionSubscriptions = [];
|
|
1644
|
+
metadataSubscription;
|
|
1645
|
+
outsideClickSubscription;
|
|
704
1646
|
formId = input.required(...(ngDevMode ? [{ debugName: "formId" }] : []));
|
|
705
1647
|
formTitle = input(...(ngDevMode ? [undefined, { debugName: "formTitle" }] : []));
|
|
706
1648
|
formDescription = input(...(ngDevMode ? [undefined, { debugName: "formDescription" }] : []));
|
|
@@ -708,8 +1650,9 @@ class ManualFormComponent {
|
|
|
708
1650
|
showHeader = input(true, ...(ngDevMode ? [{ debugName: "showHeader" }] : []));
|
|
709
1651
|
showActions = input(true, ...(ngDevMode ? [{ debugName: "showActions" }] : []));
|
|
710
1652
|
enableAutoSave = input(true, ...(ngDevMode ? [{ debugName: "enableAutoSave" }] : []));
|
|
1653
|
+
componentInstanceId = input(...(ngDevMode ? [undefined, { debugName: "componentInstanceId" }] : []));
|
|
711
1654
|
// Allows editor affordances (e.g., opening metadata editor on interactions)
|
|
712
|
-
|
|
1655
|
+
enableCustomization = input(false, ...(ngDevMode ? [{ debugName: "enableCustomization" }] : []));
|
|
713
1656
|
persistenceOptions = input(...(ngDevMode ? [undefined, { debugName: "persistenceOptions" }] : []));
|
|
714
1657
|
// When true, uses FormControlName.path (joined by '.') as FieldMetadata.name.
|
|
715
1658
|
// Requires nested path support (already present in DynamicFormService).
|
|
@@ -725,6 +1668,7 @@ class ManualFormComponent {
|
|
|
725
1668
|
hostFormGroupDirective = inject(FormGroupDirective, { self: true, optional: true });
|
|
726
1669
|
instance;
|
|
727
1670
|
resolvedActions;
|
|
1671
|
+
aiAdapter = new ManualFormAiAdapter(this);
|
|
728
1672
|
formGroup = new FormGroup({});
|
|
729
1673
|
registeredDirectives = [];
|
|
730
1674
|
constructor() {
|
|
@@ -747,17 +1691,35 @@ class ManualFormComponent {
|
|
|
747
1691
|
if (isDevMode())
|
|
748
1692
|
console.debug('[ManualForm] No host FormGroupDirective detected at construction (dynamic mode)');
|
|
749
1693
|
}
|
|
1694
|
+
effect(() => {
|
|
1695
|
+
const enabled = this.enableCustomization();
|
|
1696
|
+
if (!enabled) {
|
|
1697
|
+
this.closeToolbar();
|
|
1698
|
+
return;
|
|
1699
|
+
}
|
|
1700
|
+
if (this.instance && this.formControls) {
|
|
1701
|
+
const fields = this.collectFields();
|
|
1702
|
+
if (fields.length) {
|
|
1703
|
+
this.registerToolbarBindings(fields);
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
});
|
|
750
1707
|
}
|
|
751
1708
|
ngAfterContentInit() {
|
|
1709
|
+
this.syncHostFormGroupReference();
|
|
752
1710
|
if (this.formControls) {
|
|
753
1711
|
this.formControls.changes
|
|
754
1712
|
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
755
|
-
.subscribe(() => this.initialize(true));
|
|
1713
|
+
.subscribe(() => { void this.initialize(true); });
|
|
756
1714
|
}
|
|
757
|
-
queueMicrotask(() => this.initialize(false));
|
|
1715
|
+
queueMicrotask(() => { void this.initialize(false); });
|
|
758
1716
|
}
|
|
759
1717
|
ngOnDestroy() {
|
|
760
|
-
|
|
1718
|
+
this.toolbarSubscriptions.forEach((sub) => sub.unsubscribe());
|
|
1719
|
+
this.toolbarActionSubscriptions.forEach((sub) => sub.unsubscribe());
|
|
1720
|
+
this.metadataSubscription?.unsubscribe();
|
|
1721
|
+
this.toolbarOverlayRef?.dispose();
|
|
1722
|
+
this.outsideClickSubscription?.unsubscribe();
|
|
761
1723
|
}
|
|
762
1724
|
// =============================
|
|
763
1725
|
// ControlContainer interface
|
|
@@ -774,6 +1736,7 @@ class ManualFormComponent {
|
|
|
774
1736
|
return [];
|
|
775
1737
|
}
|
|
776
1738
|
addControl(dir) {
|
|
1739
|
+
this.syncHostFormGroupReference();
|
|
777
1740
|
const name = typeof dir.name === 'string' ? dir.name : String(dir.name);
|
|
778
1741
|
const pathSegs = Array.isArray(dir.path) && dir.path.length
|
|
779
1742
|
? dir.path
|
|
@@ -810,13 +1773,39 @@ class ManualFormComponent {
|
|
|
810
1773
|
dir.control = ctrl;
|
|
811
1774
|
return ctrl;
|
|
812
1775
|
}
|
|
1776
|
+
addFormGroup(dir) {
|
|
1777
|
+
this.syncHostFormGroupReference();
|
|
1778
|
+
if (this.hostFormGroupDirective && typeof this.hostFormGroupDirective.addFormGroup === 'function') {
|
|
1779
|
+
this.hostFormGroupDirective.addFormGroup(dir);
|
|
1780
|
+
return;
|
|
1781
|
+
}
|
|
1782
|
+
const path = this.getDirPath(dir);
|
|
1783
|
+
this.ensureGroupPath(path);
|
|
1784
|
+
}
|
|
1785
|
+
removeFormGroup(dir) {
|
|
1786
|
+
this.syncHostFormGroupReference();
|
|
1787
|
+
if (this.hostFormGroupDirective && typeof this.hostFormGroupDirective.removeFormGroup === 'function') {
|
|
1788
|
+
this.hostFormGroupDirective.removeFormGroup(dir);
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
getFormGroup(dir) {
|
|
1792
|
+
this.syncHostFormGroupReference();
|
|
1793
|
+
if (this.hostFormGroupDirective && typeof this.hostFormGroupDirective.getFormGroup === 'function') {
|
|
1794
|
+
return this.hostFormGroupDirective.getFormGroup(dir);
|
|
1795
|
+
}
|
|
1796
|
+
const path = this.getDirPath(dir);
|
|
1797
|
+
const control = this.formGroup.get(path);
|
|
1798
|
+
return control instanceof FormGroup ? control : this.ensureGroupPath(path);
|
|
1799
|
+
}
|
|
813
1800
|
removeControl(dir) {
|
|
1801
|
+
this.syncHostFormGroupReference();
|
|
814
1802
|
this.hostFormGroupDirective?.removeControl(dir);
|
|
815
1803
|
const idx = this.registeredDirectives.indexOf(dir);
|
|
816
1804
|
if (idx >= 0)
|
|
817
1805
|
this.registeredDirectives.splice(idx, 1);
|
|
818
1806
|
}
|
|
819
1807
|
getControl(dir) {
|
|
1808
|
+
this.syncHostFormGroupReference();
|
|
820
1809
|
return this.formGroup.get(dir.path);
|
|
821
1810
|
}
|
|
822
1811
|
updateModel(dir, value) {
|
|
@@ -842,15 +1831,20 @@ class ManualFormComponent {
|
|
|
842
1831
|
this.metadataChange.emit(this.instance.currentConfig);
|
|
843
1832
|
}
|
|
844
1833
|
}
|
|
845
|
-
handleSubmit() {
|
|
1834
|
+
async handleSubmit() {
|
|
846
1835
|
if (!this.instance) {
|
|
847
1836
|
return;
|
|
848
1837
|
}
|
|
849
1838
|
const value = this.formGroup.getRawValue();
|
|
1839
|
+
const beforeSubmitOk = await this.runHooks('beforeSubmit', { value });
|
|
1840
|
+
if (!beforeSubmitOk) {
|
|
1841
|
+
return;
|
|
1842
|
+
}
|
|
850
1843
|
if (this.enableAutoSave()) {
|
|
851
1844
|
this.instance.saveDraft(value);
|
|
852
1845
|
}
|
|
853
1846
|
this.submitted.emit({ value, instance: this.instance });
|
|
1847
|
+
await this.runHooks('afterSubmit', { value });
|
|
854
1848
|
}
|
|
855
1849
|
handleSave() {
|
|
856
1850
|
if (!this.instance) {
|
|
@@ -862,6 +1856,10 @@ class ManualFormComponent {
|
|
|
862
1856
|
this.saved.emit(this.instance);
|
|
863
1857
|
this.metadataChange.emit(this.instance.currentConfig);
|
|
864
1858
|
}
|
|
1859
|
+
/** True when host applied [formGroup] directly on <praxis-manual-form>. */
|
|
1860
|
+
isHostTyped() {
|
|
1861
|
+
return !!this.hostFormGroupDirective?.form;
|
|
1862
|
+
}
|
|
865
1863
|
handleReset() {
|
|
866
1864
|
if (!this.instance) {
|
|
867
1865
|
return;
|
|
@@ -870,12 +1868,39 @@ class ManualFormComponent {
|
|
|
870
1868
|
this.hostFormGroupDirective?.resetForm(this.instance.form.getRawValue());
|
|
871
1869
|
this.resetEvent.emit(this.instance);
|
|
872
1870
|
}
|
|
1871
|
+
async runHooks(stage, extras, configOverride) {
|
|
1872
|
+
const registry = this.hooksRegistry;
|
|
1873
|
+
const config = configOverride ?? this.instance?.currentConfig;
|
|
1874
|
+
if (!registry || !config?.hooks) {
|
|
1875
|
+
return true;
|
|
1876
|
+
}
|
|
1877
|
+
const declarations = config.hooks[stage];
|
|
1878
|
+
if (!declarations || declarations.length === 0) {
|
|
1879
|
+
return true;
|
|
1880
|
+
}
|
|
1881
|
+
try {
|
|
1882
|
+
const result = await registry.run(stage, { formGroup: this.formGroup, formConfig: config, extras }, declarations);
|
|
1883
|
+
if (result.stopped && stage !== 'onError' && config.hooks.onError?.length) {
|
|
1884
|
+
await registry.run('onError', { formGroup: this.formGroup, formConfig: config, extras: { ...extras, stopped: result.stopped } }, config.hooks.onError);
|
|
1885
|
+
}
|
|
1886
|
+
return !result.stopped;
|
|
1887
|
+
}
|
|
1888
|
+
catch {
|
|
1889
|
+
if (stage !== 'onError' && config.hooks.onError?.length) {
|
|
1890
|
+
try {
|
|
1891
|
+
await registry.run('onError', { formGroup: this.formGroup, formConfig: config, extras: { ...extras, error: 'hook-exception' } }, config.hooks.onError);
|
|
1892
|
+
}
|
|
1893
|
+
catch { }
|
|
1894
|
+
}
|
|
1895
|
+
return false;
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
873
1898
|
/**
|
|
874
1899
|
* Attempts to open the field metadata editor respecting the component's
|
|
875
|
-
*
|
|
1900
|
+
* enableCustomization input. No-ops when disabled or without an instance.
|
|
876
1901
|
*/
|
|
877
1902
|
tryOpenFieldEditor(fieldName) {
|
|
878
|
-
if (!this.
|
|
1903
|
+
if (!this.enableCustomization()) {
|
|
879
1904
|
return;
|
|
880
1905
|
}
|
|
881
1906
|
const instance = this.instance;
|
|
@@ -889,7 +1914,7 @@ class ManualFormComponent {
|
|
|
889
1914
|
}
|
|
890
1915
|
/** Opens a simple form-level editor listing the fields and their visibility. */
|
|
891
1916
|
async openFormEditor() {
|
|
892
|
-
if (!this.
|
|
1917
|
+
if (!this.enableCustomization() || !this.instance)
|
|
893
1918
|
return;
|
|
894
1919
|
let ManualFormConfigEditorComponent;
|
|
895
1920
|
try {
|
|
@@ -900,8 +1925,9 @@ class ManualFormComponent {
|
|
|
900
1925
|
return;
|
|
901
1926
|
}
|
|
902
1927
|
const formId = this.formId();
|
|
1928
|
+
const keyId = this.componentKeyId();
|
|
903
1929
|
this.settingsPanel.open({
|
|
904
|
-
id: formId ? `manual-form-editor.${formId}` : 'manual-form-editor',
|
|
1930
|
+
id: keyId ? `manual-form-editor.${keyId}` : (formId ? `manual-form-editor.${formId}` : 'manual-form-editor'),
|
|
905
1931
|
title: this.formTitle() || 'Editor do Formulário',
|
|
906
1932
|
titleIcon: 'tune',
|
|
907
1933
|
content: {
|
|
@@ -910,7 +1936,17 @@ class ManualFormComponent {
|
|
|
910
1936
|
},
|
|
911
1937
|
});
|
|
912
1938
|
}
|
|
913
|
-
|
|
1939
|
+
applyConfigFromAdapter(config) {
|
|
1940
|
+
if (!this.instance) {
|
|
1941
|
+
return;
|
|
1942
|
+
}
|
|
1943
|
+
this.instance.replaceConfig(config);
|
|
1944
|
+
this.resolvedActions = this.normalizeActions(config.actions ?? null);
|
|
1945
|
+
this.metadataChange.emit(this.instance.currentConfig);
|
|
1946
|
+
this.cdr.markForCheck();
|
|
1947
|
+
}
|
|
1948
|
+
async initialize(fromChanges) {
|
|
1949
|
+
this.syncHostFormGroupReference();
|
|
914
1950
|
if (!this.formId()) {
|
|
915
1951
|
return;
|
|
916
1952
|
}
|
|
@@ -921,11 +1957,22 @@ class ManualFormComponent {
|
|
|
921
1957
|
return;
|
|
922
1958
|
}
|
|
923
1959
|
const seed = this.createSeedFromFields(fields);
|
|
1960
|
+
const beforeInitOk = await this.runHooks('beforeInit', { seed }, seed.config);
|
|
1961
|
+
if (!beforeInitOk) {
|
|
1962
|
+
return;
|
|
1963
|
+
}
|
|
1964
|
+
const persistence = { ...(this.persistenceOptions() ?? {}) };
|
|
1965
|
+
const keyId = this.componentKeyId();
|
|
1966
|
+
if (keyId) {
|
|
1967
|
+
persistence.storageKey = `manual-form:${keyId}`;
|
|
1968
|
+
}
|
|
924
1969
|
// Create runtime instance and let it populate our existing FormGroup
|
|
925
|
-
this.instance = this.instanceFactory.create(seed,
|
|
1970
|
+
this.instance = this.instanceFactory.create(seed, persistence, this.formGroup);
|
|
926
1971
|
if (isDevMode())
|
|
927
1972
|
console.debug('[ManualForm] instance created with seed; current controls', Object.keys(this.formGroup.controls || {}));
|
|
928
1973
|
this.applyInstanceToTemplate(fields);
|
|
1974
|
+
this.registerToolbarBindings(fields);
|
|
1975
|
+
this.attachMetadataSync();
|
|
929
1976
|
// Auto-save value changes with debounce
|
|
930
1977
|
if (this.enableAutoSave() && this.instance) {
|
|
931
1978
|
this.instance.form.valueChanges
|
|
@@ -937,6 +1984,7 @@ class ManualFormComponent {
|
|
|
937
1984
|
catch { }
|
|
938
1985
|
});
|
|
939
1986
|
}
|
|
1987
|
+
await this.runHooks('afterInit', { seed });
|
|
940
1988
|
this.cdr.markForCheck();
|
|
941
1989
|
}
|
|
942
1990
|
collectFields() {
|
|
@@ -960,10 +2008,30 @@ class ManualFormComponent {
|
|
|
960
2008
|
control: dir.control,
|
|
961
2009
|
component,
|
|
962
2010
|
selector: this.resolveSelector(component, dir),
|
|
2011
|
+
element: this.resolveHostElement(component, dir),
|
|
963
2012
|
});
|
|
964
2013
|
}
|
|
965
2014
|
return result;
|
|
966
2015
|
}
|
|
2016
|
+
componentKeyId() {
|
|
2017
|
+
const key = this.componentKeys.buildComponentId({
|
|
2018
|
+
componentType: 'praxis-manual-form',
|
|
2019
|
+
componentId: this.formId(),
|
|
2020
|
+
instanceKey: this.componentInstanceId(),
|
|
2021
|
+
componentRef: this,
|
|
2022
|
+
route: this.route,
|
|
2023
|
+
requireComponentId: true,
|
|
2024
|
+
});
|
|
2025
|
+
if (!key)
|
|
2026
|
+
this.warnMissingId();
|
|
2027
|
+
return key;
|
|
2028
|
+
}
|
|
2029
|
+
warnMissingId() {
|
|
2030
|
+
if (this.warnedMissingId)
|
|
2031
|
+
return;
|
|
2032
|
+
this.warnedMissingId = true;
|
|
2033
|
+
console.warn('[ManualForm] formId is required for config persistence.');
|
|
2034
|
+
}
|
|
967
2035
|
resolveValueAccessor(dir) {
|
|
968
2036
|
const accessor = dir.valueAccessor;
|
|
969
2037
|
if (!accessor) {
|
|
@@ -986,6 +2054,10 @@ class ManualFormComponent {
|
|
|
986
2054
|
?? dir?._elementRef?.nativeElement;
|
|
987
2055
|
return element?.tagName?.toLowerCase();
|
|
988
2056
|
}
|
|
2057
|
+
resolveHostElement(component, dir) {
|
|
2058
|
+
return component?.elementRef?.nativeElement
|
|
2059
|
+
?? dir?._elementRef?.nativeElement;
|
|
2060
|
+
}
|
|
989
2061
|
createSeedFromFields(fields) {
|
|
990
2062
|
const metadataList = fields.map((field) => this.buildMetadataForField(field));
|
|
991
2063
|
const actions = this.normalizeActions(this.actions() ?? null);
|
|
@@ -1038,6 +2110,234 @@ class ManualFormComponent {
|
|
|
1038
2110
|
}
|
|
1039
2111
|
}
|
|
1040
2112
|
}
|
|
2113
|
+
registerToolbarBindings(fields) {
|
|
2114
|
+
this.toolbarSubscriptions.forEach((sub) => sub.unsubscribe());
|
|
2115
|
+
this.toolbarSubscriptions = [];
|
|
2116
|
+
if (!this.enableCustomization()) {
|
|
2117
|
+
return;
|
|
2118
|
+
}
|
|
2119
|
+
for (const field of fields) {
|
|
2120
|
+
const host = field.element;
|
|
2121
|
+
if (!host) {
|
|
2122
|
+
continue;
|
|
2123
|
+
}
|
|
2124
|
+
const clickSub = fromEvent(host, 'click')
|
|
2125
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
2126
|
+
.subscribe((event) => {
|
|
2127
|
+
this.openToolbarForField(field, false);
|
|
2128
|
+
});
|
|
2129
|
+
const keySub = fromEvent(host, 'keydown')
|
|
2130
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
2131
|
+
.subscribe((event) => {
|
|
2132
|
+
if (!this.isToolbarShortcut(event)) {
|
|
2133
|
+
return;
|
|
2134
|
+
}
|
|
2135
|
+
event.preventDefault();
|
|
2136
|
+
event.stopPropagation();
|
|
2137
|
+
this.openToolbarForField(field, true);
|
|
2138
|
+
});
|
|
2139
|
+
this.toolbarSubscriptions.push(clickSub, keySub);
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
isToolbarShortcut(event) {
|
|
2143
|
+
return event.key === 'F2' || (event.altKey && event.key === 'F10');
|
|
2144
|
+
}
|
|
2145
|
+
openToolbarForField(field, openedByKeyboard) {
|
|
2146
|
+
if (!this.enableCustomization()) {
|
|
2147
|
+
return;
|
|
2148
|
+
}
|
|
2149
|
+
const instance = this.instance;
|
|
2150
|
+
if (!instance || !field.element) {
|
|
2151
|
+
return;
|
|
2152
|
+
}
|
|
2153
|
+
const resolution = this.fieldKeyService.resolveFieldName(field.name, instance);
|
|
2154
|
+
if (resolution.status !== 'ok') {
|
|
2155
|
+
return;
|
|
2156
|
+
}
|
|
2157
|
+
const metadata = instance.getFieldMetadata(resolution.key);
|
|
2158
|
+
if (!metadata) {
|
|
2159
|
+
return;
|
|
2160
|
+
}
|
|
2161
|
+
if (!this.toolbarOverlayRef) {
|
|
2162
|
+
this.createToolbarOverlay(field.element);
|
|
2163
|
+
}
|
|
2164
|
+
else if (this.toolbarAnchor !== field.element) {
|
|
2165
|
+
this.updateToolbarAnchor(field.element);
|
|
2166
|
+
}
|
|
2167
|
+
this.toolbarFieldKey = resolution.key;
|
|
2168
|
+
this.toolbarAnchor = field.element;
|
|
2169
|
+
this.updateToolbarMetadata(metadata, openedByKeyboard);
|
|
2170
|
+
this.toolbarOverlayRef?.updatePosition();
|
|
2171
|
+
}
|
|
2172
|
+
createToolbarOverlay(anchor) {
|
|
2173
|
+
const positionStrategy = this.overlay
|
|
2174
|
+
.position()
|
|
2175
|
+
.flexibleConnectedTo(anchor)
|
|
2176
|
+
.withPositions([
|
|
2177
|
+
{
|
|
2178
|
+
originX: 'center',
|
|
2179
|
+
originY: 'top',
|
|
2180
|
+
overlayX: 'center',
|
|
2181
|
+
overlayY: 'bottom',
|
|
2182
|
+
offsetY: -10,
|
|
2183
|
+
},
|
|
2184
|
+
{
|
|
2185
|
+
originX: 'center',
|
|
2186
|
+
originY: 'bottom',
|
|
2187
|
+
overlayX: 'center',
|
|
2188
|
+
overlayY: 'top',
|
|
2189
|
+
offsetY: 10,
|
|
2190
|
+
},
|
|
2191
|
+
])
|
|
2192
|
+
.withPush(true)
|
|
2193
|
+
.withFlexibleDimensions(false)
|
|
2194
|
+
.withViewportMargin(8);
|
|
2195
|
+
this.toolbarOverlayRef = this.overlay.create({
|
|
2196
|
+
positionStrategy,
|
|
2197
|
+
scrollStrategy: this.overlay.scrollStrategies.reposition(),
|
|
2198
|
+
panelClass: 'pdx-manual-toolbar-panel',
|
|
2199
|
+
});
|
|
2200
|
+
this.toolbarOverlayRef.overlayElement.style.setProperty('z-index', 'var(--pdx-manual-toolbar-z, 2001)');
|
|
2201
|
+
this.toolbarOverlayRef
|
|
2202
|
+
.detachments()
|
|
2203
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
2204
|
+
.subscribe(() => this.resetToolbarState());
|
|
2205
|
+
const portal = new ComponentPortal(ManualFieldToolbarComponent);
|
|
2206
|
+
this.toolbarComponentRef = this.toolbarOverlayRef.attach(portal);
|
|
2207
|
+
this.bindToolbarActions();
|
|
2208
|
+
this.attachOutsideClickHandler();
|
|
2209
|
+
}
|
|
2210
|
+
updateToolbarAnchor(anchor) {
|
|
2211
|
+
if (!this.toolbarOverlayRef) {
|
|
2212
|
+
return;
|
|
2213
|
+
}
|
|
2214
|
+
const positionStrategy = this.overlay
|
|
2215
|
+
.position()
|
|
2216
|
+
.flexibleConnectedTo(anchor)
|
|
2217
|
+
.withPositions([
|
|
2218
|
+
{
|
|
2219
|
+
originX: 'center',
|
|
2220
|
+
originY: 'top',
|
|
2221
|
+
overlayX: 'center',
|
|
2222
|
+
overlayY: 'bottom',
|
|
2223
|
+
offsetY: -10,
|
|
2224
|
+
},
|
|
2225
|
+
{
|
|
2226
|
+
originX: 'center',
|
|
2227
|
+
originY: 'bottom',
|
|
2228
|
+
overlayX: 'center',
|
|
2229
|
+
overlayY: 'top',
|
|
2230
|
+
offsetY: 10,
|
|
2231
|
+
},
|
|
2232
|
+
])
|
|
2233
|
+
.withPush(true)
|
|
2234
|
+
.withFlexibleDimensions(false)
|
|
2235
|
+
.withViewportMargin(8);
|
|
2236
|
+
this.toolbarOverlayRef.updatePositionStrategy(positionStrategy);
|
|
2237
|
+
}
|
|
2238
|
+
bindToolbarActions() {
|
|
2239
|
+
this.toolbarActionSubscriptions.forEach((sub) => sub.unsubscribe());
|
|
2240
|
+
this.toolbarActionSubscriptions = [];
|
|
2241
|
+
if (!this.toolbarComponentRef) {
|
|
2242
|
+
return;
|
|
2243
|
+
}
|
|
2244
|
+
const instance = this.toolbarComponentRef.instance;
|
|
2245
|
+
this.toolbarActionSubscriptions.push(instance.toggleRequired.subscribe(() => this.toggleFieldFlag('required')), instance.toggleReadonly.subscribe(() => this.toggleFieldFlag('readOnly')), instance.toggleHidden.subscribe(() => this.toggleFieldFlag('hidden')), instance.toggleDisabled.subscribe(() => this.toggleFieldFlag('disabled')), instance.openEditor.subscribe(() => this.openToolbarEditor()), instance.requestClose.subscribe(() => this.closeToolbar()));
|
|
2246
|
+
}
|
|
2247
|
+
toggleFieldFlag(field) {
|
|
2248
|
+
if (!this.instance || !this.toolbarFieldKey) {
|
|
2249
|
+
return;
|
|
2250
|
+
}
|
|
2251
|
+
const current = this.instance.getFieldMetadata(this.toolbarFieldKey);
|
|
2252
|
+
if (!current) {
|
|
2253
|
+
return;
|
|
2254
|
+
}
|
|
2255
|
+
if (field === 'required') {
|
|
2256
|
+
const currentRequired = !!current.required || !!current.validators?.required;
|
|
2257
|
+
const nextRequired = !currentRequired;
|
|
2258
|
+
this.instance.patchFieldMetadata(this.toolbarFieldKey, {
|
|
2259
|
+
required: nextRequired,
|
|
2260
|
+
validators: { ...(current.validators ?? {}), required: nextRequired },
|
|
2261
|
+
});
|
|
2262
|
+
return;
|
|
2263
|
+
}
|
|
2264
|
+
const patch = {
|
|
2265
|
+
[field]: !current[field],
|
|
2266
|
+
};
|
|
2267
|
+
this.instance.patchFieldMetadata(this.toolbarFieldKey, patch);
|
|
2268
|
+
}
|
|
2269
|
+
openToolbarEditor() {
|
|
2270
|
+
if (!this.toolbarFieldKey || !this.instance) {
|
|
2271
|
+
return;
|
|
2272
|
+
}
|
|
2273
|
+
this.tryOpenFieldEditor(this.toolbarFieldKey);
|
|
2274
|
+
}
|
|
2275
|
+
updateToolbarMetadata(metadata, openedByKeyboard) {
|
|
2276
|
+
if (!this.toolbarComponentRef) {
|
|
2277
|
+
return;
|
|
2278
|
+
}
|
|
2279
|
+
this.toolbarComponentRef.setInput('metadata', metadata);
|
|
2280
|
+
this.toolbarComponentRef.changeDetectorRef.detectChanges();
|
|
2281
|
+
if (openedByKeyboard) {
|
|
2282
|
+
this.toolbarComponentRef.instance.focusFirstAction();
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
attachOutsideClickHandler() {
|
|
2286
|
+
this.outsideClickSubscription?.unsubscribe();
|
|
2287
|
+
if (!isPlatformBrowser(this.platformId) || typeof document === 'undefined') {
|
|
2288
|
+
return;
|
|
2289
|
+
}
|
|
2290
|
+
this.outsideClickSubscription = fromEvent(document, 'mousedown')
|
|
2291
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
2292
|
+
.subscribe((event) => {
|
|
2293
|
+
const target = event.target;
|
|
2294
|
+
if (!target) {
|
|
2295
|
+
return;
|
|
2296
|
+
}
|
|
2297
|
+
const overlayEl = this.toolbarOverlayRef?.overlayElement;
|
|
2298
|
+
if (overlayEl && overlayEl.contains(target)) {
|
|
2299
|
+
return;
|
|
2300
|
+
}
|
|
2301
|
+
if (this.toolbarAnchor && this.toolbarAnchor.contains(target)) {
|
|
2302
|
+
return;
|
|
2303
|
+
}
|
|
2304
|
+
this.closeToolbar();
|
|
2305
|
+
});
|
|
2306
|
+
}
|
|
2307
|
+
closeToolbar() {
|
|
2308
|
+
this.toolbarOverlayRef?.detach();
|
|
2309
|
+
this.toolbarOverlayRef?.dispose();
|
|
2310
|
+
this.toolbarOverlayRef = undefined;
|
|
2311
|
+
this.toolbarComponentRef = undefined;
|
|
2312
|
+
this.outsideClickSubscription?.unsubscribe();
|
|
2313
|
+
this.outsideClickSubscription = undefined;
|
|
2314
|
+
this.resetToolbarState();
|
|
2315
|
+
}
|
|
2316
|
+
resetToolbarState() {
|
|
2317
|
+
this.toolbarFieldKey = undefined;
|
|
2318
|
+
this.toolbarAnchor = undefined;
|
|
2319
|
+
}
|
|
2320
|
+
attachMetadataSync() {
|
|
2321
|
+
this.metadataSubscription?.unsubscribe();
|
|
2322
|
+
const instance = this.instance;
|
|
2323
|
+
if (!instance) {
|
|
2324
|
+
return;
|
|
2325
|
+
}
|
|
2326
|
+
this.metadataSubscription = instance
|
|
2327
|
+
.metadataChanges()
|
|
2328
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
2329
|
+
.subscribe((map) => {
|
|
2330
|
+
if (!this.toolbarFieldKey) {
|
|
2331
|
+
return;
|
|
2332
|
+
}
|
|
2333
|
+
const meta = map.get(this.toolbarFieldKey);
|
|
2334
|
+
if (!meta) {
|
|
2335
|
+
this.closeToolbar();
|
|
2336
|
+
return;
|
|
2337
|
+
}
|
|
2338
|
+
this.updateToolbarMetadata(meta, false);
|
|
2339
|
+
});
|
|
2340
|
+
}
|
|
1041
2341
|
buildMetadataForField(field) {
|
|
1042
2342
|
const existing = this.extractExistingMetadata(field.component);
|
|
1043
2343
|
const label = existing?.label ?? this.inferLabel(field, existing);
|
|
@@ -1051,18 +2351,34 @@ class ManualFormComponent {
|
|
|
1051
2351
|
validators: { ...existing?.validators, ...validators },
|
|
1052
2352
|
};
|
|
1053
2353
|
}
|
|
2354
|
+
syncHostFormGroupReference() {
|
|
2355
|
+
const hostForm = this.hostFormGroupDirective?.form;
|
|
2356
|
+
if (hostForm && this.formGroup !== hostForm) {
|
|
2357
|
+
this.formGroup = hostForm;
|
|
2358
|
+
if (isDevMode())
|
|
2359
|
+
console.debug('[ManualForm] Synced FormGroup from host FormGroupDirective');
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
1054
2362
|
getControlByPath(path) {
|
|
1055
2363
|
if (!Array.isArray(path) || path.length === 0)
|
|
1056
2364
|
return null;
|
|
1057
2365
|
return this.formGroup.get(path);
|
|
1058
2366
|
}
|
|
1059
|
-
|
|
1060
|
-
if (
|
|
1061
|
-
return;
|
|
1062
|
-
|
|
2367
|
+
getDirPath(dir) {
|
|
2368
|
+
if (Array.isArray(dir?.path) && dir.path.length) {
|
|
2369
|
+
return dir.path;
|
|
2370
|
+
}
|
|
2371
|
+
if (typeof dir?.name === 'string' && dir.name.length) {
|
|
2372
|
+
return [dir.name];
|
|
2373
|
+
}
|
|
2374
|
+
return [];
|
|
2375
|
+
}
|
|
2376
|
+
ensureGroupPath(path) {
|
|
1063
2377
|
let group = this.formGroup;
|
|
1064
|
-
|
|
1065
|
-
|
|
2378
|
+
if (!Array.isArray(path) || path.length === 0) {
|
|
2379
|
+
return group;
|
|
2380
|
+
}
|
|
2381
|
+
for (const seg of path) {
|
|
1066
2382
|
const existing = group.get(seg);
|
|
1067
2383
|
if (existing instanceof FormGroup) {
|
|
1068
2384
|
group = existing;
|
|
@@ -1073,6 +2389,13 @@ class ManualFormComponent {
|
|
|
1073
2389
|
group = next;
|
|
1074
2390
|
}
|
|
1075
2391
|
}
|
|
2392
|
+
return group;
|
|
2393
|
+
}
|
|
2394
|
+
ensureControlPath(path) {
|
|
2395
|
+
if (!Array.isArray(path) || path.length === 0)
|
|
2396
|
+
return;
|
|
2397
|
+
const leaf = path[path.length - 1];
|
|
2398
|
+
const group = this.ensureGroupPath(path.slice(0, -1));
|
|
1076
2399
|
if (!group.get(leaf)) {
|
|
1077
2400
|
group.addControl(leaf, new FormControl());
|
|
1078
2401
|
}
|
|
@@ -1112,8 +2435,13 @@ class ManualFormComponent {
|
|
|
1112
2435
|
}
|
|
1113
2436
|
inferControlType(field) {
|
|
1114
2437
|
const selector = field.selector?.toLowerCase();
|
|
1115
|
-
if (selector
|
|
1116
|
-
|
|
2438
|
+
if (selector) {
|
|
2439
|
+
const fromLocal = this.selectorToControlType[selector];
|
|
2440
|
+
if (fromLocal)
|
|
2441
|
+
return fromLocal;
|
|
2442
|
+
const fromRegistry = this.selectorRegistry?.resolve(selector);
|
|
2443
|
+
if (fromRegistry)
|
|
2444
|
+
return fromRegistry;
|
|
1117
2445
|
}
|
|
1118
2446
|
const ctorName = field.component?.constructor?.name ?? '';
|
|
1119
2447
|
for (const [key, value] of Object.entries(this.constructorToControlType)) {
|
|
@@ -1183,15 +2511,15 @@ class ManualFormComponent {
|
|
|
1183
2511
|
};
|
|
1184
2512
|
}
|
|
1185
2513
|
normalizeActions(source) {
|
|
1186
|
-
const base = deepClone(DEFAULT_ACTIONS);
|
|
2514
|
+
const base = deepClone(DEFAULT_ACTIONS$1);
|
|
1187
2515
|
if (!source) {
|
|
1188
2516
|
return base;
|
|
1189
2517
|
}
|
|
1190
2518
|
return {
|
|
1191
|
-
submit: mergeAction(base.submit, source.submit),
|
|
1192
|
-
cancel: mergeAction(base.cancel, source.cancel),
|
|
1193
|
-
reset: mergeAction(base.reset, source.reset),
|
|
1194
|
-
custom: source.custom ? source.custom.map(makeAction) : base.custom,
|
|
2519
|
+
submit: mergeAction$1(base.submit, source.submit),
|
|
2520
|
+
cancel: mergeAction$1(base.cancel, source.cancel),
|
|
2521
|
+
reset: mergeAction$1(base.reset, source.reset),
|
|
2522
|
+
custom: source.custom ? source.custom.map(makeAction$1) : base.custom,
|
|
1195
2523
|
position: source.position ?? base.position,
|
|
1196
2524
|
orientation: source.orientation ?? base.orientation,
|
|
1197
2525
|
spacing: source.spacing ?? base.spacing,
|
|
@@ -1208,37 +2536,38 @@ class ManualFormComponent {
|
|
|
1208
2536
|
resetButtonLabel: source.resetButtonLabel ?? base.resetButtonLabel,
|
|
1209
2537
|
};
|
|
1210
2538
|
}
|
|
1211
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.
|
|
1212
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.
|
|
2539
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2540
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: ManualFormComponent, isStandalone: true, selector: "praxis-manual-form", inputs: { formId: { classPropertyName: "formId", publicName: "formId", isSignal: true, isRequired: true, transformFunction: null }, formTitle: { classPropertyName: "formTitle", publicName: "formTitle", isSignal: true, isRequired: false, transformFunction: null }, formDescription: { classPropertyName: "formDescription", publicName: "formDescription", isSignal: true, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, showHeader: { classPropertyName: "showHeader", publicName: "showHeader", isSignal: true, isRequired: false, transformFunction: null }, showActions: { classPropertyName: "showActions", publicName: "showActions", isSignal: true, isRequired: false, transformFunction: null }, enableAutoSave: { classPropertyName: "enableAutoSave", publicName: "enableAutoSave", isSignal: true, isRequired: false, transformFunction: null }, componentInstanceId: { classPropertyName: "componentInstanceId", publicName: "componentInstanceId", isSignal: true, isRequired: false, transformFunction: null }, enableCustomization: { classPropertyName: "enableCustomization", publicName: "enableCustomization", isSignal: true, isRequired: false, transformFunction: null }, persistenceOptions: { classPropertyName: "persistenceOptions", publicName: "persistenceOptions", isSignal: true, isRequired: false, transformFunction: null }, usePathNames: { classPropertyName: "usePathNames", publicName: "usePathNames", isSignal: true, isRequired: false, transformFunction: null }, autoSaveDebounceMs: { classPropertyName: "autoSaveDebounceMs", publicName: "autoSaveDebounceMs", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { submitted: "submitted", saved: "saved", resetEvent: "reset", metadataChange: "metadataChange" }, providers: [
|
|
1213
2541
|
// Provide a ControlContainer at the host boundary so projected formControlName can resolve it
|
|
1214
2542
|
{ provide: ControlContainer, useExisting: ManualFormComponent },
|
|
1215
|
-
], queries: [{ propertyName: "formControls", predicate: FormControlName, descendants: true }], ngImport: i0, template: "<div class=\"pdx-manual-form\">\n @if (showHeader()) {\n
|
|
2543
|
+
], queries: [{ propertyName: "formControls", predicate: FormControlName, descendants: true }], ngImport: i0, template: "<div class=\"pdx-manual-form\">\n @if (showHeader()) {\n <praxis-manual-form-header [instance]=\"instance\" [title]=\"formTitle()\" [description]=\"formDescription()\"\n [enableCustomization]=\"enableCustomization()\" (editForm)=\"openFormEditor()\" (save)=\"handleSave()\"\n (reset)=\"handleReset()\"></praxis-manual-form-header>\n }\n\n @if (enableCustomization()) {\n <div class=\"pdx-manual-form__assistant\">\n <praxis-ai-assistant [adapter]=\"aiAdapter\"></praxis-ai-assistant>\n </div>\n }\n\n <form class=\"pdx-manual-form__form\" [formGroup]=\"formGroup\" (submit)=\"handleSubmit(); $event.preventDefault()\">\n <ng-content></ng-content>\n </form>\n\n @if (showActions() && resolvedActions) {\n <praxis-manual-form-actions [actions]=\"resolvedActions\"\n (actionClick)=\"handleAction($event)\"></praxis-manual-form-actions>\n }\n</div>\n", styles: [".pdx-manual-form{display:flex;flex-direction:column;gap:var(--pdx-manual-form-gap, 24px);color:var(--md-sys-color-on-surface)}.pdx-manual-form__form{display:grid;gap:var(--pdx-manual-form-field-gap, 16px);padding:var(--pdx-manual-form-padding, 20px);border-radius:var(--pdx-manual-form-radius, 16px);background:var(--pdx-manual-form-surface, var(--md-sys-color-surface-container));border:1px solid var(--pdx-manual-form-outline, var(--md-sys-color-outline-variant));box-shadow:var(--pdx-manual-form-shadow, var(--md-sys-elevation-level1, none))}.pdx-manual-form__form:focus-within{border-color:var(--pdx-manual-form-focus, var(--md-sys-color-primary));box-shadow:0 0 0 2px var(--md-sys-color-primary),var(--pdx-manual-form-shadow, var(--md-sys-elevation-level1, none))}.pdx-manual-form__assistant{display:flex;justify-content:flex-end}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: PraxisAiAssistantComponent, selector: "praxis-ai-assistant", inputs: ["adapter", "riskPolicy", "allowManualPatchEdit"] }, { kind: "component", type: ManualFormHeaderComponent, selector: "praxis-manual-form-header", inputs: ["instance", "title", "description", "saveLabel", "resetLabel", "enableCustomization", "editFormLabel"], outputs: ["save", "reset", "editForm"] }, { kind: "component", type: ManualFormActionsComponent, selector: "praxis-manual-form-actions", inputs: ["actions", "trackByFn"], outputs: ["actionClick"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1216
2544
|
}
|
|
1217
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
2545
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFormComponent, decorators: [{
|
|
1218
2546
|
type: Component,
|
|
1219
2547
|
args: [{ selector: 'praxis-manual-form', standalone: true, imports: [
|
|
1220
2548
|
CommonModule,
|
|
1221
2549
|
ReactiveFormsModule,
|
|
2550
|
+
PraxisAiAssistantComponent,
|
|
1222
2551
|
ManualFormHeaderComponent,
|
|
1223
2552
|
ManualFormActionsComponent,
|
|
1224
2553
|
], providers: [
|
|
1225
2554
|
// Provide a ControlContainer at the host boundary so projected formControlName can resolve it
|
|
1226
2555
|
{ provide: ControlContainer, useExisting: ManualFormComponent },
|
|
1227
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"pdx-manual-form\">\n @if (showHeader()) {\n
|
|
1228
|
-
}], ctorParameters: () => [], propDecorators: { formControls: [{
|
|
2556
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"pdx-manual-form\">\n @if (showHeader()) {\n <praxis-manual-form-header [instance]=\"instance\" [title]=\"formTitle()\" [description]=\"formDescription()\"\n [enableCustomization]=\"enableCustomization()\" (editForm)=\"openFormEditor()\" (save)=\"handleSave()\"\n (reset)=\"handleReset()\"></praxis-manual-form-header>\n }\n\n @if (enableCustomization()) {\n <div class=\"pdx-manual-form__assistant\">\n <praxis-ai-assistant [adapter]=\"aiAdapter\"></praxis-ai-assistant>\n </div>\n }\n\n <form class=\"pdx-manual-form__form\" [formGroup]=\"formGroup\" (submit)=\"handleSubmit(); $event.preventDefault()\">\n <ng-content></ng-content>\n </form>\n\n @if (showActions() && resolvedActions) {\n <praxis-manual-form-actions [actions]=\"resolvedActions\"\n (actionClick)=\"handleAction($event)\"></praxis-manual-form-actions>\n }\n</div>\n", styles: [".pdx-manual-form{display:flex;flex-direction:column;gap:var(--pdx-manual-form-gap, 24px);color:var(--md-sys-color-on-surface)}.pdx-manual-form__form{display:grid;gap:var(--pdx-manual-form-field-gap, 16px);padding:var(--pdx-manual-form-padding, 20px);border-radius:var(--pdx-manual-form-radius, 16px);background:var(--pdx-manual-form-surface, var(--md-sys-color-surface-container));border:1px solid var(--pdx-manual-form-outline, var(--md-sys-color-outline-variant));box-shadow:var(--pdx-manual-form-shadow, var(--md-sys-elevation-level1, none))}.pdx-manual-form__form:focus-within{border-color:var(--pdx-manual-form-focus, var(--md-sys-color-primary));box-shadow:0 0 0 2px var(--md-sys-color-primary),var(--pdx-manual-form-shadow, var(--md-sys-elevation-level1, none))}.pdx-manual-form__assistant{display:flex;justify-content:flex-end}\n"] }]
|
|
2557
|
+
}], ctorParameters: () => [], propDecorators: { formId: [{ type: i0.Input, args: [{ isSignal: true, alias: "formId", required: true }] }], formTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "formTitle", required: false }] }], formDescription: [{ type: i0.Input, args: [{ isSignal: true, alias: "formDescription", required: false }] }], actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: false }] }], showHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "showHeader", required: false }] }], showActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "showActions", required: false }] }], enableAutoSave: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableAutoSave", required: false }] }], componentInstanceId: [{ type: i0.Input, args: [{ isSignal: true, alias: "componentInstanceId", required: false }] }], enableCustomization: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableCustomization", required: false }] }], persistenceOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "persistenceOptions", required: false }] }], usePathNames: [{ type: i0.Input, args: [{ isSignal: true, alias: "usePathNames", required: false }] }], autoSaveDebounceMs: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoSaveDebounceMs", required: false }] }], submitted: [{ type: i0.Output, args: ["submitted"] }], saved: [{ type: i0.Output, args: ["saved"] }], resetEvent: [{ type: i0.Output, args: ["reset"] }], metadataChange: [{ type: i0.Output, args: ["metadataChange"] }], formControls: [{
|
|
1229
2558
|
type: ContentChildren,
|
|
1230
2559
|
args: [FormControlName, { descendants: true }]
|
|
1231
2560
|
}] } });
|
|
1232
|
-
const DEFAULT_ACTIONS = {
|
|
1233
|
-
submit: makeAction({ id: 'submit', label: 'Salvar', visible: true, type: 'submit', color: 'primary' }),
|
|
1234
|
-
cancel: makeAction({ id: 'cancel', label: 'Cancelar', visible: false, type: 'button' }),
|
|
1235
|
-
reset: makeAction({ id: 'reset', label: 'Restaurar', visible: true, type: 'reset' }),
|
|
2561
|
+
const DEFAULT_ACTIONS$1 = {
|
|
2562
|
+
submit: makeAction$1({ id: 'submit', label: 'Salvar', visible: true, type: 'submit', color: 'primary' }),
|
|
2563
|
+
cancel: makeAction$1({ id: 'cancel', label: 'Cancelar', visible: false, type: 'button' }),
|
|
2564
|
+
reset: makeAction$1({ id: 'reset', label: 'Restaurar', visible: true, type: 'reset' }),
|
|
1236
2565
|
custom: [],
|
|
1237
2566
|
position: 'right',
|
|
1238
2567
|
orientation: 'horizontal',
|
|
1239
2568
|
spacing: 'normal',
|
|
1240
2569
|
};
|
|
1241
|
-
function makeAction(action) {
|
|
2570
|
+
function makeAction$1(action) {
|
|
1242
2571
|
return {
|
|
1243
2572
|
id: action.id ?? 'action',
|
|
1244
2573
|
label: action.label ?? 'Ação',
|
|
@@ -1255,7 +2584,7 @@ function makeAction(action) {
|
|
|
1255
2584
|
shortcut: action.shortcut,
|
|
1256
2585
|
};
|
|
1257
2586
|
}
|
|
1258
|
-
function mergeAction(base, override) {
|
|
2587
|
+
function mergeAction$1(base, override) {
|
|
1259
2588
|
if (!override) {
|
|
1260
2589
|
return base;
|
|
1261
2590
|
}
|
|
@@ -1265,24 +2594,7 @@ function mergeAction(base, override) {
|
|
|
1265
2594
|
visible: override.visible !== undefined ? override.visible : base.visible,
|
|
1266
2595
|
};
|
|
1267
2596
|
}
|
|
1268
|
-
const DEFAULT_SELECTOR_TO_CONTROL_TYPE =
|
|
1269
|
-
'pdx-text-input': FieldControlType.INPUT,
|
|
1270
|
-
'pdx-material-textarea': FieldControlType.TEXTAREA,
|
|
1271
|
-
'pdx-number-input': FieldControlType.NUMERIC_TEXT_BOX,
|
|
1272
|
-
'pdx-material-currency': FieldControlType.CURRENCY_INPUT,
|
|
1273
|
-
'pdx-material-datepicker': FieldControlType.DATE_PICKER,
|
|
1274
|
-
'pdx-material-date-range': FieldControlType.DATE_RANGE,
|
|
1275
|
-
'pdx-material-timepicker': FieldControlType.TIME_PICKER,
|
|
1276
|
-
'pdx-material-colorpicker': FieldControlType.COLOR_PICKER,
|
|
1277
|
-
'pdx-material-select': FieldControlType.SELECT,
|
|
1278
|
-
'pdx-material-autocomplete': FieldControlType.AUTO_COMPLETE,
|
|
1279
|
-
'pdx-material-checkbox-group': FieldControlType.CHECKBOX,
|
|
1280
|
-
'pdx-material-radio-group': FieldControlType.RADIO,
|
|
1281
|
-
'pdx-material-slide-toggle': FieldControlType.TOGGLE,
|
|
1282
|
-
'pdx-material-slider': FieldControlType.SLIDER,
|
|
1283
|
-
'pdx-material-range-slider': FieldControlType.RANGE_SLIDER,
|
|
1284
|
-
'pdx-material-file-upload': FieldControlType.FILE_UPLOAD,
|
|
1285
|
-
};
|
|
2597
|
+
const DEFAULT_SELECTOR_TO_CONTROL_TYPE = DEFAULT_FIELD_SELECTOR_CONTROL_TYPE_MAP;
|
|
1286
2598
|
const DEFAULT_CONSTRUCTOR_TO_CONTROL_TYPE = {
|
|
1287
2599
|
TextInput: FieldControlType.INPUT,
|
|
1288
2600
|
Textarea: FieldControlType.TEXTAREA,
|
|
@@ -1308,6 +2620,30 @@ class ManualFormConfigEditorComponent {
|
|
|
1308
2620
|
all = [];
|
|
1309
2621
|
filtered = [];
|
|
1310
2622
|
onlyHidden = false;
|
|
2623
|
+
actionsModel = deepClone(DEFAULT_ACTIONS);
|
|
2624
|
+
messagesModel = {};
|
|
2625
|
+
behaviorModel = {};
|
|
2626
|
+
hintsModel = createDefaultHints();
|
|
2627
|
+
hooksText = createDefaultHooksText();
|
|
2628
|
+
hooksErrors = {};
|
|
2629
|
+
formRulesText = '';
|
|
2630
|
+
formRulesStateText = '';
|
|
2631
|
+
formRulesError = '';
|
|
2632
|
+
formRulesStateError = '';
|
|
2633
|
+
hookStages = [
|
|
2634
|
+
'beforeInit',
|
|
2635
|
+
'afterInit',
|
|
2636
|
+
'beforeValidate',
|
|
2637
|
+
'afterValidate',
|
|
2638
|
+
'beforeSubmit',
|
|
2639
|
+
'afterSubmit',
|
|
2640
|
+
'onError',
|
|
2641
|
+
];
|
|
2642
|
+
actionBlocks = [
|
|
2643
|
+
{ key: 'submit', label: 'Salvar' },
|
|
2644
|
+
{ key: 'cancel', label: 'Cancelar' },
|
|
2645
|
+
{ key: 'reset', label: 'Resetar' },
|
|
2646
|
+
];
|
|
1311
2647
|
// SettingsValueProvider observables
|
|
1312
2648
|
isDirty$ = new BehaviorSubject(false);
|
|
1313
2649
|
isValid$ = new BehaviorSubject(true);
|
|
@@ -1364,6 +2700,206 @@ class ManualFormConfigEditorComponent {
|
|
|
1364
2700
|
}
|
|
1365
2701
|
catch { }
|
|
1366
2702
|
}
|
|
2703
|
+
updateActionField(key, field, value) {
|
|
2704
|
+
const current = this.actionsModel[key];
|
|
2705
|
+
this.actionsModel = {
|
|
2706
|
+
...this.actionsModel,
|
|
2707
|
+
[key]: {
|
|
2708
|
+
...current,
|
|
2709
|
+
[field]: value,
|
|
2710
|
+
},
|
|
2711
|
+
};
|
|
2712
|
+
this.applyConfigPatch({ actions: this.actionsModel });
|
|
2713
|
+
}
|
|
2714
|
+
updateActionsLayout(field, value) {
|
|
2715
|
+
this.actionsModel = {
|
|
2716
|
+
...this.actionsModel,
|
|
2717
|
+
[field]: value,
|
|
2718
|
+
};
|
|
2719
|
+
this.applyConfigPatch({ actions: this.actionsModel });
|
|
2720
|
+
}
|
|
2721
|
+
addCustomAction() {
|
|
2722
|
+
const nextId = this.generateCustomId();
|
|
2723
|
+
const nextAction = makeAction({ id: nextId, label: 'Ação' });
|
|
2724
|
+
const custom = [...(this.actionsModel.custom || []), nextAction];
|
|
2725
|
+
this.actionsModel = { ...this.actionsModel, custom };
|
|
2726
|
+
this.applyConfigPatch({ actions: this.actionsModel });
|
|
2727
|
+
}
|
|
2728
|
+
removeCustomAction(action) {
|
|
2729
|
+
const custom = (this.actionsModel.custom || []).filter((item) => item !== action);
|
|
2730
|
+
this.actionsModel = { ...this.actionsModel, custom };
|
|
2731
|
+
this.applyConfigPatch({ actions: this.actionsModel });
|
|
2732
|
+
}
|
|
2733
|
+
updateCustomActionField(action, field, value) {
|
|
2734
|
+
const custom = (this.actionsModel.custom || []).map((item) => {
|
|
2735
|
+
if (item !== action)
|
|
2736
|
+
return item;
|
|
2737
|
+
return { ...item, [field]: value };
|
|
2738
|
+
});
|
|
2739
|
+
this.actionsModel = { ...this.actionsModel, custom };
|
|
2740
|
+
this.applyConfigPatch({ actions: this.actionsModel });
|
|
2741
|
+
}
|
|
2742
|
+
updateMessage(key, value) {
|
|
2743
|
+
this.messagesModel = {
|
|
2744
|
+
...this.messagesModel,
|
|
2745
|
+
[key]: value,
|
|
2746
|
+
};
|
|
2747
|
+
this.applyConfigPatch({ messages: this.messagesModel });
|
|
2748
|
+
}
|
|
2749
|
+
updateConfirmation(key, value) {
|
|
2750
|
+
this.messagesModel = {
|
|
2751
|
+
...this.messagesModel,
|
|
2752
|
+
confirmations: {
|
|
2753
|
+
...(this.messagesModel.confirmations || {}),
|
|
2754
|
+
[key]: value,
|
|
2755
|
+
},
|
|
2756
|
+
};
|
|
2757
|
+
this.applyConfigPatch({ messages: this.messagesModel });
|
|
2758
|
+
}
|
|
2759
|
+
updateLoading(key, value) {
|
|
2760
|
+
this.messagesModel = {
|
|
2761
|
+
...this.messagesModel,
|
|
2762
|
+
loading: {
|
|
2763
|
+
...(this.messagesModel.loading || {}),
|
|
2764
|
+
[key]: value,
|
|
2765
|
+
},
|
|
2766
|
+
};
|
|
2767
|
+
this.applyConfigPatch({ messages: this.messagesModel });
|
|
2768
|
+
}
|
|
2769
|
+
updateCustomMessage(action, kind, value) {
|
|
2770
|
+
const actionId = action.id || '';
|
|
2771
|
+
this.messagesModel = {
|
|
2772
|
+
...this.messagesModel,
|
|
2773
|
+
customActions: {
|
|
2774
|
+
...(this.messagesModel.customActions || {}),
|
|
2775
|
+
[actionId]: {
|
|
2776
|
+
...(this.messagesModel.customActions || {})[actionId],
|
|
2777
|
+
[kind]: value,
|
|
2778
|
+
},
|
|
2779
|
+
},
|
|
2780
|
+
};
|
|
2781
|
+
this.applyConfigPatch({ messages: this.messagesModel });
|
|
2782
|
+
}
|
|
2783
|
+
updateBehavior(key, value) {
|
|
2784
|
+
this.behaviorModel = {
|
|
2785
|
+
...this.behaviorModel,
|
|
2786
|
+
[key]: value,
|
|
2787
|
+
};
|
|
2788
|
+
this.applyConfigPatch({ behavior: this.behaviorModel });
|
|
2789
|
+
}
|
|
2790
|
+
updateHints(scope, key, value) {
|
|
2791
|
+
this.hintsModel = {
|
|
2792
|
+
...this.hintsModel,
|
|
2793
|
+
[scope]: {
|
|
2794
|
+
...this.hintsModel[scope],
|
|
2795
|
+
[key]: value,
|
|
2796
|
+
},
|
|
2797
|
+
};
|
|
2798
|
+
this.applyConfigPatch({ hints: this.hintsModel });
|
|
2799
|
+
}
|
|
2800
|
+
hookLabel(stage) {
|
|
2801
|
+
const map = {
|
|
2802
|
+
beforeInit: 'beforeInit',
|
|
2803
|
+
afterInit: 'afterInit',
|
|
2804
|
+
beforeValidate: 'beforeValidate',
|
|
2805
|
+
afterValidate: 'afterValidate',
|
|
2806
|
+
beforeSubmit: 'beforeSubmit',
|
|
2807
|
+
afterSubmit: 'afterSubmit',
|
|
2808
|
+
onError: 'onError',
|
|
2809
|
+
};
|
|
2810
|
+
return map[stage] || stage;
|
|
2811
|
+
}
|
|
2812
|
+
onHookTextChange(stage, value) {
|
|
2813
|
+
this.hooksText = { ...this.hooksText, [stage]: value };
|
|
2814
|
+
}
|
|
2815
|
+
resetHooks() {
|
|
2816
|
+
this.hooksText = buildHooksText(this.instance.currentConfig.hooks);
|
|
2817
|
+
this.hooksErrors = {};
|
|
2818
|
+
}
|
|
2819
|
+
applyHooks() {
|
|
2820
|
+
const next = {};
|
|
2821
|
+
const errors = {};
|
|
2822
|
+
for (const stage of this.hookStages) {
|
|
2823
|
+
const raw = (this.hooksText[stage] || '').trim();
|
|
2824
|
+
if (!raw)
|
|
2825
|
+
continue;
|
|
2826
|
+
try {
|
|
2827
|
+
const parsed = JSON.parse(raw);
|
|
2828
|
+
if (!Array.isArray(parsed)) {
|
|
2829
|
+
errors[stage] = 'Precisa ser um array JSON.';
|
|
2830
|
+
continue;
|
|
2831
|
+
}
|
|
2832
|
+
next[stage] = parsed;
|
|
2833
|
+
}
|
|
2834
|
+
catch {
|
|
2835
|
+
errors[stage] = 'JSON inválido.';
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
this.hooksErrors = errors;
|
|
2839
|
+
if (Object.keys(errors).length > 0) {
|
|
2840
|
+
return;
|
|
2841
|
+
}
|
|
2842
|
+
this.applyConfigPatch({ hooks: next });
|
|
2843
|
+
}
|
|
2844
|
+
onRulesTextChange(value) {
|
|
2845
|
+
this.formRulesText = value;
|
|
2846
|
+
}
|
|
2847
|
+
onRulesStateTextChange(value) {
|
|
2848
|
+
this.formRulesStateText = value;
|
|
2849
|
+
}
|
|
2850
|
+
applyRules() {
|
|
2851
|
+
this.formRulesError = '';
|
|
2852
|
+
this.formRulesStateError = '';
|
|
2853
|
+
let rules;
|
|
2854
|
+
let rulesState;
|
|
2855
|
+
const rulesRaw = this.formRulesText.trim();
|
|
2856
|
+
if (rulesRaw) {
|
|
2857
|
+
try {
|
|
2858
|
+
const parsed = JSON.parse(rulesRaw);
|
|
2859
|
+
if (!Array.isArray(parsed)) {
|
|
2860
|
+
this.formRulesError = 'formRules precisa ser um array JSON.';
|
|
2861
|
+
return;
|
|
2862
|
+
}
|
|
2863
|
+
rules = parsed;
|
|
2864
|
+
}
|
|
2865
|
+
catch {
|
|
2866
|
+
this.formRulesError = 'JSON inválido em formRules.';
|
|
2867
|
+
return;
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
const stateRaw = this.formRulesStateText.trim();
|
|
2871
|
+
if (stateRaw) {
|
|
2872
|
+
try {
|
|
2873
|
+
rulesState = JSON.parse(stateRaw);
|
|
2874
|
+
}
|
|
2875
|
+
catch {
|
|
2876
|
+
this.formRulesStateError = 'JSON inválido em formRulesState.';
|
|
2877
|
+
return;
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
this.applyConfigPatch({ formRules: rules, formRulesState: rulesState });
|
|
2881
|
+
}
|
|
2882
|
+
applyCascadePatch(patch) {
|
|
2883
|
+
const entries = Object.entries(patch || {});
|
|
2884
|
+
if (!entries.length) {
|
|
2885
|
+
return;
|
|
2886
|
+
}
|
|
2887
|
+
for (const [fieldName, fieldPatch] of entries) {
|
|
2888
|
+
try {
|
|
2889
|
+
this.instance.patchFieldMetadata(fieldName, fieldPatch);
|
|
2890
|
+
}
|
|
2891
|
+
catch { }
|
|
2892
|
+
}
|
|
2893
|
+
try {
|
|
2894
|
+
this.instance.saveDraft();
|
|
2895
|
+
this.refresh();
|
|
2896
|
+
this.isDirty$.next(true);
|
|
2897
|
+
}
|
|
2898
|
+
catch { }
|
|
2899
|
+
}
|
|
2900
|
+
get cascadeFields() {
|
|
2901
|
+
return this.instance.currentConfig.fieldMetadata || [];
|
|
2902
|
+
}
|
|
1367
2903
|
getSettingsValue() {
|
|
1368
2904
|
return {
|
|
1369
2905
|
fieldStates: (this.instance.currentConfig.fieldMetadata || []).map((f) => ({
|
|
@@ -1373,6 +2909,13 @@ class ManualFormConfigEditorComponent {
|
|
|
1373
2909
|
readOnly: !!f.readOnly,
|
|
1374
2910
|
disabled: !!f.disabled,
|
|
1375
2911
|
})),
|
|
2912
|
+
actions: deepClone(this.actionsModel),
|
|
2913
|
+
messages: deepClone(this.messagesModel),
|
|
2914
|
+
behavior: deepClone(this.behaviorModel),
|
|
2915
|
+
hints: deepClone(this.hintsModel),
|
|
2916
|
+
hooks: deepClone(this.instance.currentConfig.hooks || {}),
|
|
2917
|
+
formRules: deepClone(this.instance.currentConfig.formRules || []),
|
|
2918
|
+
formRulesState: deepClone(this.instance.currentConfig.formRulesState),
|
|
1376
2919
|
};
|
|
1377
2920
|
}
|
|
1378
2921
|
onSave() {
|
|
@@ -1383,127 +2926,2038 @@ class ManualFormConfigEditorComponent {
|
|
|
1383
2926
|
}
|
|
1384
2927
|
refresh() {
|
|
1385
2928
|
this.all = (this.instance.currentConfig.fieldMetadata || []).map(f => ({ ...f }));
|
|
2929
|
+
this.actionsModel = this.normalizeActions(this.instance.currentConfig.actions);
|
|
2930
|
+
this.messagesModel = deepClone(this.instance.currentConfig.messages || {});
|
|
2931
|
+
this.behaviorModel = deepClone(this.instance.currentConfig.behavior || {});
|
|
2932
|
+
this.hintsModel = createHints(this.instance.currentConfig.hints);
|
|
2933
|
+
this.hooksText = buildHooksText(this.instance.currentConfig.hooks);
|
|
2934
|
+
this.formRulesText = stringifyJson(this.instance.currentConfig.formRules, true);
|
|
2935
|
+
this.formRulesStateText = stringifyJson(this.instance.currentConfig.formRulesState, false);
|
|
1386
2936
|
this.applyFilter();
|
|
1387
2937
|
}
|
|
1388
|
-
|
|
1389
|
-
|
|
2938
|
+
normalizeActions(source) {
|
|
2939
|
+
const base = deepClone(DEFAULT_ACTIONS);
|
|
2940
|
+
if (!source) {
|
|
2941
|
+
return base;
|
|
2942
|
+
}
|
|
2943
|
+
return {
|
|
2944
|
+
submit: mergeAction(base.submit, source.submit),
|
|
2945
|
+
cancel: mergeAction(base.cancel, source.cancel),
|
|
2946
|
+
reset: mergeAction(base.reset, source.reset),
|
|
2947
|
+
custom: source.custom ? source.custom.map((item, index) => makeAction(item, index)) : base.custom,
|
|
2948
|
+
position: source.position ?? base.position,
|
|
2949
|
+
orientation: source.orientation ?? base.orientation,
|
|
2950
|
+
spacing: source.spacing ?? base.spacing,
|
|
2951
|
+
sticky: source.sticky ?? base.sticky,
|
|
2952
|
+
divider: source.divider ?? base.divider,
|
|
2953
|
+
placement: source.placement ?? base.placement,
|
|
2954
|
+
mobile: source.mobile ?? base.mobile,
|
|
2955
|
+
containerClassName: source.containerClassName ?? base.containerClassName,
|
|
2956
|
+
containerStyles: source.containerStyles ?? base.containerStyles,
|
|
2957
|
+
showSaveButton: source.showSaveButton ?? base.showSaveButton,
|
|
2958
|
+
submitButtonLabel: source.submitButtonLabel ?? base.submitButtonLabel,
|
|
2959
|
+
showCancelButton: source.showCancelButton ?? base.showCancelButton,
|
|
2960
|
+
cancelButtonLabel: source.cancelButtonLabel ?? base.cancelButtonLabel,
|
|
2961
|
+
showResetButton: source.showResetButton ?? base.showResetButton,
|
|
2962
|
+
resetButtonLabel: source.resetButtonLabel ?? base.resetButtonLabel,
|
|
2963
|
+
};
|
|
2964
|
+
}
|
|
2965
|
+
applyConfigPatch(patch) {
|
|
2966
|
+
try {
|
|
2967
|
+
const config = deepClone(this.instance.currentConfig);
|
|
2968
|
+
const next = { ...config, ...patch };
|
|
2969
|
+
this.instance.replaceConfig(next);
|
|
2970
|
+
this.instance.saveDraft();
|
|
2971
|
+
this.refresh();
|
|
2972
|
+
this.isDirty$.next(true);
|
|
2973
|
+
}
|
|
2974
|
+
catch { }
|
|
2975
|
+
}
|
|
2976
|
+
generateCustomId() {
|
|
2977
|
+
const existing = new Set((this.actionsModel.custom || []).map((item) => item.id).filter(Boolean));
|
|
2978
|
+
let counter = existing.size + 1;
|
|
2979
|
+
let next = `custom-${counter}`;
|
|
2980
|
+
while (existing.has(next)) {
|
|
2981
|
+
counter += 1;
|
|
2982
|
+
next = `custom-${counter}`;
|
|
2983
|
+
}
|
|
2984
|
+
return next;
|
|
2985
|
+
}
|
|
2986
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFormConfigEditorComponent, deps: [{ token: SETTINGS_PANEL_DATA }, { token: SETTINGS_PANEL_REF }], target: i0.ɵɵFactoryTarget.Component });
|
|
2987
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: ManualFormConfigEditorComponent, isStandalone: true, selector: "praxis-manual-form-config-editor", ngImport: i0, template: `
|
|
1390
2988
|
<div class="mf-editor">
|
|
1391
2989
|
<div class="mf-editor__toolbar">
|
|
1392
|
-
<
|
|
2990
|
+
<div class="mf-editor__title">
|
|
2991
|
+
<span>Configurações do formulário</span>
|
|
2992
|
+
<small class="mf-editor__subtitle">Campos, ações, mensagens e comportamento</small>
|
|
2993
|
+
</div>
|
|
1393
2994
|
<div class="spacer"></div>
|
|
1394
|
-
<button type="button" (click)="close()">Fechar</button>
|
|
2995
|
+
<button type="button" class="mf-btn" (click)="close()">Fechar</button>
|
|
1395
2996
|
</div>
|
|
1396
2997
|
|
|
1397
|
-
<
|
|
1398
|
-
<
|
|
1399
|
-
<div class="
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
<div class="col col--req">Obrigatório</div>
|
|
1404
|
-
<div class="col col--ro">Som. leitura</div>
|
|
1405
|
-
<div class="col col--dis">Desabilitado</div>
|
|
1406
|
-
</div>
|
|
1407
|
-
|
|
1408
|
-
<div class="mf-editor__row" *ngFor="let f of filtered">
|
|
1409
|
-
<div class="col col--name"><code>{{ f.name }}</code></div>
|
|
1410
|
-
<div class="col col--label">{{ f.label || f.name }}</div>
|
|
1411
|
-
<div class="col col--type">{{ f.controlType }}</div>
|
|
1412
|
-
<div class="col col--vis">
|
|
1413
|
-
<label class="toggle">
|
|
1414
|
-
<input type="checkbox" [checked]="!f.hidden" (change)="toggleVisibility(f, $any($event.target).checked)" />
|
|
1415
|
-
<span>{{ f.hidden ? 'Oculto' : 'Visível' }}</span>
|
|
2998
|
+
<mat-tab-group class="mf-tabs">
|
|
2999
|
+
<mat-tab label="Campos">
|
|
3000
|
+
<div class="mf-tab">
|
|
3001
|
+
<label class="mf-editor__filter">
|
|
3002
|
+
<input type="checkbox" [(ngModel)]="onlyHidden" (change)="applyFilter()" />
|
|
3003
|
+
<span>Mostrar apenas ocultos</span>
|
|
1416
3004
|
</label>
|
|
3005
|
+
<div class="mf-editor__list">
|
|
3006
|
+
<div class="mf-editor__row mf-editor__row--head">
|
|
3007
|
+
<div class="col col--name">Campo</div>
|
|
3008
|
+
<div class="col col--label">Rótulo</div>
|
|
3009
|
+
<div class="col col--type">Tipo</div>
|
|
3010
|
+
<div class="col col--vis">Visível</div>
|
|
3011
|
+
<div class="col col--req">Obrigatório</div>
|
|
3012
|
+
<div class="col col--ro">Som. leitura</div>
|
|
3013
|
+
<div class="col col--dis">Desabilitado</div>
|
|
3014
|
+
</div>
|
|
3015
|
+
|
|
3016
|
+
@for (f of filtered; track f.name) {
|
|
3017
|
+
<div class="mf-editor__row">
|
|
3018
|
+
<div class="col col--name"><code>{{ f.name }}</code></div>
|
|
3019
|
+
<div class="col col--label">{{ f.label || f.name }}</div>
|
|
3020
|
+
<div class="col col--type">{{ f.controlType }}</div>
|
|
3021
|
+
<div class="col col--vis">
|
|
3022
|
+
<label class="toggle">
|
|
3023
|
+
<input type="checkbox" [checked]="!f.hidden" (change)="toggleVisibility(f, $any($event.target).checked)" />
|
|
3024
|
+
<span>{{ f.hidden ? 'Oculto' : 'Visível' }}</span>
|
|
3025
|
+
</label>
|
|
3026
|
+
</div>
|
|
3027
|
+
<div class="col col--req">
|
|
3028
|
+
<label class="toggle">
|
|
3029
|
+
<input type="checkbox" [checked]="!!f.required" (change)="toggleRequired(f, $any($event.target).checked)" />
|
|
3030
|
+
<span>{{ f.required ? 'Sim' : 'Não' }}</span>
|
|
3031
|
+
</label>
|
|
3032
|
+
</div>
|
|
3033
|
+
<div class="col col--ro">
|
|
3034
|
+
<label class="toggle">
|
|
3035
|
+
<input type="checkbox" [checked]="!!f.readOnly" (change)="toggleReadOnly(f, $any($event.target).checked)" />
|
|
3036
|
+
<span>{{ f.readOnly ? 'Sim' : 'Não' }}</span>
|
|
3037
|
+
</label>
|
|
3038
|
+
</div>
|
|
3039
|
+
<div class="col col--dis">
|
|
3040
|
+
<label class="toggle">
|
|
3041
|
+
<input type="checkbox" [checked]="!!f.disabled" (change)="toggleDisabled(f, $any($event.target).checked)" />
|
|
3042
|
+
<span>{{ f.disabled ? 'Sim' : 'Não' }}</span>
|
|
3043
|
+
</label>
|
|
3044
|
+
</div>
|
|
3045
|
+
</div>
|
|
3046
|
+
}
|
|
3047
|
+
</div>
|
|
1417
3048
|
</div>
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
3049
|
+
</mat-tab>
|
|
3050
|
+
|
|
3051
|
+
<mat-tab label="Ações">
|
|
3052
|
+
<div class="mf-tab">
|
|
3053
|
+
<section class="mf-section">
|
|
3054
|
+
<div class="mf-section__header">
|
|
3055
|
+
<div>
|
|
3056
|
+
<h3>Botões padrão</h3>
|
|
3057
|
+
<p class="mf-help">Controle rótulos, visibilidade e estilo dos botões básicos.</p>
|
|
3058
|
+
</div>
|
|
3059
|
+
</div>
|
|
3060
|
+
<div class="mf-actions-grid">
|
|
3061
|
+
@for (block of actionBlocks; track block.key) {
|
|
3062
|
+
<div class="mf-action-card">
|
|
3063
|
+
<div class="mf-action-card__title">{{ block.label }}</div>
|
|
3064
|
+
<mat-slide-toggle
|
|
3065
|
+
[checked]="actionsModel[block.key].visible"
|
|
3066
|
+
(change)="updateActionField(block.key, 'visible', $event.checked)"
|
|
3067
|
+
>
|
|
3068
|
+
Visível
|
|
3069
|
+
</mat-slide-toggle>
|
|
3070
|
+
<mat-form-field appearance="outline">
|
|
3071
|
+
<mat-label>Label</mat-label>
|
|
3072
|
+
<input
|
|
3073
|
+
matInput
|
|
3074
|
+
[value]="actionsModel[block.key].label"
|
|
3075
|
+
(input)="updateActionField(block.key, 'label', $any($event.target).value)"
|
|
3076
|
+
/>
|
|
3077
|
+
</mat-form-field>
|
|
3078
|
+
<mat-form-field appearance="outline">
|
|
3079
|
+
<mat-label>Ícone</mat-label>
|
|
3080
|
+
<input
|
|
3081
|
+
matInput
|
|
3082
|
+
[value]="actionsModel[block.key].icon || ''"
|
|
3083
|
+
(input)="updateActionField(block.key, 'icon', $any($event.target).value)"
|
|
3084
|
+
/>
|
|
3085
|
+
</mat-form-field>
|
|
3086
|
+
<mat-form-field appearance="outline">
|
|
3087
|
+
<mat-label>Variante</mat-label>
|
|
3088
|
+
<mat-select
|
|
3089
|
+
[value]="actionsModel[block.key].variant || null"
|
|
3090
|
+
(selectionChange)="updateActionField(block.key, 'variant', $event.value)"
|
|
3091
|
+
>
|
|
3092
|
+
<mat-option [value]="null">Padrão</mat-option>
|
|
3093
|
+
<mat-option value="raised">Elevado</mat-option>
|
|
3094
|
+
<mat-option value="stroked">Contornado</mat-option>
|
|
3095
|
+
<mat-option value="flat">Plano</mat-option>
|
|
3096
|
+
<mat-option value="fab">Flutuante</mat-option>
|
|
3097
|
+
</mat-select>
|
|
3098
|
+
</mat-form-field>
|
|
3099
|
+
<mat-form-field appearance="outline">
|
|
3100
|
+
<mat-label>Cor</mat-label>
|
|
3101
|
+
<mat-select
|
|
3102
|
+
[value]="actionsModel[block.key].color || null"
|
|
3103
|
+
(selectionChange)="updateActionField(block.key, 'color', $event.value)"
|
|
3104
|
+
>
|
|
3105
|
+
<mat-option [value]="null">Padrão</mat-option>
|
|
3106
|
+
<mat-option value="primary">Primary</mat-option>
|
|
3107
|
+
<mat-option value="accent">Accent</mat-option>
|
|
3108
|
+
<mat-option value="warn">Warn</mat-option>
|
|
3109
|
+
<mat-option value="basic">Basic</mat-option>
|
|
3110
|
+
</mat-select>
|
|
3111
|
+
</mat-form-field>
|
|
3112
|
+
<mat-form-field appearance="outline">
|
|
3113
|
+
<mat-label>Atalho</mat-label>
|
|
3114
|
+
<input
|
|
3115
|
+
matInput
|
|
3116
|
+
[value]="actionsModel[block.key].shortcut || ''"
|
|
3117
|
+
(input)="updateActionField(block.key, 'shortcut', $any($event.target).value)"
|
|
3118
|
+
/>
|
|
3119
|
+
</mat-form-field>
|
|
3120
|
+
</div>
|
|
3121
|
+
}
|
|
3122
|
+
</div>
|
|
3123
|
+
</section>
|
|
3124
|
+
|
|
3125
|
+
<section class="mf-section">
|
|
3126
|
+
<div class="mf-section__header">
|
|
3127
|
+
<div>
|
|
3128
|
+
<h3>Layout das ações</h3>
|
|
3129
|
+
<p class="mf-help">Define alinhamento e disposição da barra de ações.</p>
|
|
3130
|
+
</div>
|
|
3131
|
+
</div>
|
|
3132
|
+
<div class="mf-actions-layout">
|
|
3133
|
+
<mat-form-field appearance="outline">
|
|
3134
|
+
<mat-label>Posição</mat-label>
|
|
3135
|
+
<mat-select
|
|
3136
|
+
[value]="actionsModel.position || null"
|
|
3137
|
+
(selectionChange)="updateActionsLayout('position', $event.value)"
|
|
3138
|
+
>
|
|
3139
|
+
<mat-option value="left">Esquerda</mat-option>
|
|
3140
|
+
<mat-option value="center">Centro</mat-option>
|
|
3141
|
+
<mat-option value="right">Direita</mat-option>
|
|
3142
|
+
<mat-option value="justified">Justificado</mat-option>
|
|
3143
|
+
<mat-option value="split">Split</mat-option>
|
|
3144
|
+
</mat-select>
|
|
3145
|
+
</mat-form-field>
|
|
3146
|
+
<mat-form-field appearance="outline">
|
|
3147
|
+
<mat-label>Orientação</mat-label>
|
|
3148
|
+
<mat-select
|
|
3149
|
+
[value]="actionsModel.orientation || null"
|
|
3150
|
+
(selectionChange)="updateActionsLayout('orientation', $event.value)"
|
|
3151
|
+
>
|
|
3152
|
+
<mat-option value="horizontal">Horizontal</mat-option>
|
|
3153
|
+
<mat-option value="vertical">Vertical</mat-option>
|
|
3154
|
+
</mat-select>
|
|
3155
|
+
</mat-form-field>
|
|
3156
|
+
<mat-form-field appearance="outline">
|
|
3157
|
+
<mat-label>Espaçamento</mat-label>
|
|
3158
|
+
<mat-select
|
|
3159
|
+
[value]="actionsModel.spacing || null"
|
|
3160
|
+
(selectionChange)="updateActionsLayout('spacing', $event.value)"
|
|
3161
|
+
>
|
|
3162
|
+
<mat-option value="compact">Compacto</mat-option>
|
|
3163
|
+
<mat-option value="normal">Normal</mat-option>
|
|
3164
|
+
<mat-option value="spacious">Espaçoso</mat-option>
|
|
3165
|
+
</mat-select>
|
|
3166
|
+
</mat-form-field>
|
|
3167
|
+
<mat-form-field appearance="outline">
|
|
3168
|
+
<mat-label>Posicionamento</mat-label>
|
|
3169
|
+
<mat-select
|
|
3170
|
+
[value]="actionsModel.placement || null"
|
|
3171
|
+
(selectionChange)="updateActionsLayout('placement', $event.value)"
|
|
3172
|
+
>
|
|
3173
|
+
<mat-option value="afterSections">Após seções</mat-option>
|
|
3174
|
+
<mat-option value="insideLastSection">Dentro da última seção</mat-option>
|
|
3175
|
+
<mat-option value="top">Topo</mat-option>
|
|
3176
|
+
</mat-select>
|
|
3177
|
+
</mat-form-field>
|
|
3178
|
+
<mat-slide-toggle
|
|
3179
|
+
[checked]="!!actionsModel.sticky"
|
|
3180
|
+
(change)="updateActionsLayout('sticky', $event.checked)"
|
|
3181
|
+
>
|
|
3182
|
+
Ações fixas
|
|
3183
|
+
</mat-slide-toggle>
|
|
3184
|
+
<mat-slide-toggle
|
|
3185
|
+
[checked]="!!actionsModel.divider"
|
|
3186
|
+
(change)="updateActionsLayout('divider', $event.checked)"
|
|
3187
|
+
>
|
|
3188
|
+
Mostrar divisor
|
|
3189
|
+
</mat-slide-toggle>
|
|
3190
|
+
</div>
|
|
3191
|
+
</section>
|
|
3192
|
+
|
|
3193
|
+
<section class="mf-section">
|
|
3194
|
+
<div class="mf-section__header mf-section__header--row">
|
|
3195
|
+
<div>
|
|
3196
|
+
<h3>Ações customizadas</h3>
|
|
3197
|
+
<p class="mf-help">Crie botões extras com evento próprio.</p>
|
|
3198
|
+
</div>
|
|
3199
|
+
<button mat-stroked-button type="button" (click)="addCustomAction()">
|
|
3200
|
+
<mat-icon>add</mat-icon>
|
|
3201
|
+
Adicionar ação
|
|
3202
|
+
</button>
|
|
3203
|
+
</div>
|
|
3204
|
+
<div class="mf-custom-actions">
|
|
3205
|
+
@if (actionsModel.custom?.length) {
|
|
3206
|
+
@for (action of actionsModel.custom || []; track action.id) {
|
|
3207
|
+
<div class="mf-custom-card">
|
|
3208
|
+
<div class="mf-custom-card__header">
|
|
3209
|
+
<div class="mf-custom-card__title">{{ action.label || action.id }}</div>
|
|
3210
|
+
<button mat-icon-button type="button" (click)="removeCustomAction(action)">
|
|
3211
|
+
<mat-icon>delete</mat-icon>
|
|
3212
|
+
</button>
|
|
3213
|
+
</div>
|
|
3214
|
+
<div class="mf-custom-card__grid">
|
|
3215
|
+
<mat-form-field appearance="outline">
|
|
3216
|
+
<mat-label>ID</mat-label>
|
|
3217
|
+
<input
|
|
3218
|
+
matInput
|
|
3219
|
+
[value]="action.id || ''"
|
|
3220
|
+
(input)="updateCustomActionField(action, 'id', $any($event.target).value)"
|
|
3221
|
+
/>
|
|
3222
|
+
</mat-form-field>
|
|
3223
|
+
<mat-form-field appearance="outline">
|
|
3224
|
+
<mat-label>Label</mat-label>
|
|
3225
|
+
<input
|
|
3226
|
+
matInput
|
|
3227
|
+
[value]="action.label || ''"
|
|
3228
|
+
(input)="updateCustomActionField(action, 'label', $any($event.target).value)"
|
|
3229
|
+
/>
|
|
3230
|
+
</mat-form-field>
|
|
3231
|
+
<mat-form-field appearance="outline">
|
|
3232
|
+
<mat-label>Evento (action)</mat-label>
|
|
3233
|
+
<input
|
|
3234
|
+
matInput
|
|
3235
|
+
[value]="action.action || ''"
|
|
3236
|
+
(input)="updateCustomActionField(action, 'action', $any($event.target).value)"
|
|
3237
|
+
/>
|
|
3238
|
+
</mat-form-field>
|
|
3239
|
+
<mat-form-field appearance="outline">
|
|
3240
|
+
<mat-label>Ícone</mat-label>
|
|
3241
|
+
<input
|
|
3242
|
+
matInput
|
|
3243
|
+
[value]="action.icon || ''"
|
|
3244
|
+
(input)="updateCustomActionField(action, 'icon', $any($event.target).value)"
|
|
3245
|
+
/>
|
|
3246
|
+
</mat-form-field>
|
|
3247
|
+
<mat-form-field appearance="outline">
|
|
3248
|
+
<mat-label>Variante</mat-label>
|
|
3249
|
+
<mat-select
|
|
3250
|
+
[value]="action.variant || null"
|
|
3251
|
+
(selectionChange)="updateCustomActionField(action, 'variant', $event.value)"
|
|
3252
|
+
>
|
|
3253
|
+
<mat-option [value]="null">Padrão</mat-option>
|
|
3254
|
+
<mat-option value="raised">Elevado</mat-option>
|
|
3255
|
+
<mat-option value="stroked">Contornado</mat-option>
|
|
3256
|
+
<mat-option value="flat">Plano</mat-option>
|
|
3257
|
+
<mat-option value="fab">Flutuante</mat-option>
|
|
3258
|
+
</mat-select>
|
|
3259
|
+
</mat-form-field>
|
|
3260
|
+
<mat-form-field appearance="outline">
|
|
3261
|
+
<mat-label>Cor</mat-label>
|
|
3262
|
+
<mat-select
|
|
3263
|
+
[value]="action.color || null"
|
|
3264
|
+
(selectionChange)="updateCustomActionField(action, 'color', $event.value)"
|
|
3265
|
+
>
|
|
3266
|
+
<mat-option [value]="null">Padrão</mat-option>
|
|
3267
|
+
<mat-option value="primary">Primary</mat-option>
|
|
3268
|
+
<mat-option value="accent">Accent</mat-option>
|
|
3269
|
+
<mat-option value="warn">Warn</mat-option>
|
|
3270
|
+
<mat-option value="basic">Basic</mat-option>
|
|
3271
|
+
</mat-select>
|
|
3272
|
+
</mat-form-field>
|
|
3273
|
+
<mat-form-field appearance="outline">
|
|
3274
|
+
<mat-label>Atalho</mat-label>
|
|
3275
|
+
<input
|
|
3276
|
+
matInput
|
|
3277
|
+
[value]="action.shortcut || ''"
|
|
3278
|
+
(input)="updateCustomActionField(action, 'shortcut', $any($event.target).value)"
|
|
3279
|
+
/>
|
|
3280
|
+
</mat-form-field>
|
|
3281
|
+
<mat-form-field appearance="outline">
|
|
3282
|
+
<mat-label>Tooltip</mat-label>
|
|
3283
|
+
<input
|
|
3284
|
+
matInput
|
|
3285
|
+
[value]="action.tooltip || ''"
|
|
3286
|
+
(input)="updateCustomActionField(action, 'tooltip', $any($event.target).value)"
|
|
3287
|
+
/>
|
|
3288
|
+
</mat-form-field>
|
|
3289
|
+
<mat-slide-toggle
|
|
3290
|
+
[checked]="action.visible !== false"
|
|
3291
|
+
(change)="updateCustomActionField(action, 'visible', $event.checked)"
|
|
3292
|
+
>
|
|
3293
|
+
Visível
|
|
3294
|
+
</mat-slide-toggle>
|
|
3295
|
+
</div>
|
|
3296
|
+
</div>
|
|
3297
|
+
}
|
|
3298
|
+
} @else {
|
|
3299
|
+
<p class="mf-empty">Nenhuma ação customizada cadastrada.</p>
|
|
3300
|
+
}
|
|
3301
|
+
</div>
|
|
3302
|
+
</section>
|
|
1423
3303
|
</div>
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
3304
|
+
</mat-tab>
|
|
3305
|
+
|
|
3306
|
+
<mat-tab label="Mensagens">
|
|
3307
|
+
<div class="mf-tab">
|
|
3308
|
+
<section class="mf-section">
|
|
3309
|
+
<div class="mf-section__header">
|
|
3310
|
+
<div>
|
|
3311
|
+
<h3>Feedback padrão</h3>
|
|
3312
|
+
<p class="mf-help">Mensagens de sucesso/erro exibidas pelo host.</p>
|
|
3313
|
+
</div>
|
|
3314
|
+
</div>
|
|
3315
|
+
<div class="mf-messages-grid">
|
|
3316
|
+
<mat-form-field appearance="outline">
|
|
3317
|
+
<mat-label>Sucesso ao criar</mat-label>
|
|
3318
|
+
<input
|
|
3319
|
+
matInput
|
|
3320
|
+
[value]="messagesModel.createRegistrySuccess || ''"
|
|
3321
|
+
(input)="updateMessage('createRegistrySuccess', $any($event.target).value)"
|
|
3322
|
+
/>
|
|
3323
|
+
</mat-form-field>
|
|
3324
|
+
<mat-form-field appearance="outline">
|
|
3325
|
+
<mat-label>Erro ao criar</mat-label>
|
|
3326
|
+
<input
|
|
3327
|
+
matInput
|
|
3328
|
+
[value]="messagesModel.createRegistryError || ''"
|
|
3329
|
+
(input)="updateMessage('createRegistryError', $any($event.target).value)"
|
|
3330
|
+
/>
|
|
3331
|
+
</mat-form-field>
|
|
3332
|
+
<mat-form-field appearance="outline">
|
|
3333
|
+
<mat-label>Sucesso ao atualizar</mat-label>
|
|
3334
|
+
<input
|
|
3335
|
+
matInput
|
|
3336
|
+
[value]="messagesModel.updateRegistrySuccess || ''"
|
|
3337
|
+
(input)="updateMessage('updateRegistrySuccess', $any($event.target).value)"
|
|
3338
|
+
/>
|
|
3339
|
+
</mat-form-field>
|
|
3340
|
+
<mat-form-field appearance="outline">
|
|
3341
|
+
<mat-label>Erro ao atualizar</mat-label>
|
|
3342
|
+
<input
|
|
3343
|
+
matInput
|
|
3344
|
+
[value]="messagesModel.updateRegistryError || ''"
|
|
3345
|
+
(input)="updateMessage('updateRegistryError', $any($event.target).value)"
|
|
3346
|
+
/>
|
|
3347
|
+
</mat-form-field>
|
|
3348
|
+
</div>
|
|
3349
|
+
</section>
|
|
3350
|
+
|
|
3351
|
+
<section class="mf-section">
|
|
3352
|
+
<div class="mf-section__header">
|
|
3353
|
+
<div>
|
|
3354
|
+
<h3>Confirmações</h3>
|
|
3355
|
+
<p class="mf-help">Textos mostrados antes de ações críticas.</p>
|
|
3356
|
+
</div>
|
|
3357
|
+
</div>
|
|
3358
|
+
<div class="mf-messages-grid">
|
|
3359
|
+
<mat-form-field appearance="outline">
|
|
3360
|
+
<mat-label>Enviar</mat-label>
|
|
3361
|
+
<input
|
|
3362
|
+
matInput
|
|
3363
|
+
[value]="messagesModel.confirmations?.submit || ''"
|
|
3364
|
+
(input)="updateConfirmation('submit', $any($event.target).value)"
|
|
3365
|
+
/>
|
|
3366
|
+
</mat-form-field>
|
|
3367
|
+
<mat-form-field appearance="outline">
|
|
3368
|
+
<mat-label>Cancelar</mat-label>
|
|
3369
|
+
<input
|
|
3370
|
+
matInput
|
|
3371
|
+
[value]="messagesModel.confirmations?.cancel || ''"
|
|
3372
|
+
(input)="updateConfirmation('cancel', $any($event.target).value)"
|
|
3373
|
+
/>
|
|
3374
|
+
</mat-form-field>
|
|
3375
|
+
<mat-form-field appearance="outline">
|
|
3376
|
+
<mat-label>Resetar</mat-label>
|
|
3377
|
+
<input
|
|
3378
|
+
matInput
|
|
3379
|
+
[value]="messagesModel.confirmations?.reset || ''"
|
|
3380
|
+
(input)="updateConfirmation('reset', $any($event.target).value)"
|
|
3381
|
+
/>
|
|
3382
|
+
</mat-form-field>
|
|
3383
|
+
</div>
|
|
3384
|
+
</section>
|
|
3385
|
+
|
|
3386
|
+
<section class="mf-section">
|
|
3387
|
+
<div class="mf-section__header">
|
|
3388
|
+
<div>
|
|
3389
|
+
<h3>Loading</h3>
|
|
3390
|
+
<p class="mf-help">Mensagens exibidas durante operações.</p>
|
|
3391
|
+
</div>
|
|
3392
|
+
</div>
|
|
3393
|
+
<div class="mf-messages-grid">
|
|
3394
|
+
<mat-form-field appearance="outline">
|
|
3395
|
+
<mat-label>Salvar</mat-label>
|
|
3396
|
+
<input
|
|
3397
|
+
matInput
|
|
3398
|
+
[value]="messagesModel.loading?.submit || ''"
|
|
3399
|
+
(input)="updateLoading('submit', $any($event.target).value)"
|
|
3400
|
+
/>
|
|
3401
|
+
</mat-form-field>
|
|
3402
|
+
<mat-form-field appearance="outline">
|
|
3403
|
+
<mat-label>Cancelar</mat-label>
|
|
3404
|
+
<input
|
|
3405
|
+
matInput
|
|
3406
|
+
[value]="messagesModel.loading?.cancel || ''"
|
|
3407
|
+
(input)="updateLoading('cancel', $any($event.target).value)"
|
|
3408
|
+
/>
|
|
3409
|
+
</mat-form-field>
|
|
3410
|
+
<mat-form-field appearance="outline">
|
|
3411
|
+
<mat-label>Resetar</mat-label>
|
|
3412
|
+
<input
|
|
3413
|
+
matInput
|
|
3414
|
+
[value]="messagesModel.loading?.reset || ''"
|
|
3415
|
+
(input)="updateLoading('reset', $any($event.target).value)"
|
|
3416
|
+
/>
|
|
3417
|
+
</mat-form-field>
|
|
3418
|
+
</div>
|
|
3419
|
+
</section>
|
|
3420
|
+
|
|
3421
|
+
<section class="mf-section">
|
|
3422
|
+
<div class="mf-section__header">
|
|
3423
|
+
<div>
|
|
3424
|
+
<h3>Mensagens por ação customizada</h3>
|
|
3425
|
+
<p class="mf-help">Personalize textos por ID de ação.</p>
|
|
3426
|
+
</div>
|
|
3427
|
+
</div>
|
|
3428
|
+
<div class="mf-custom-messages">
|
|
3429
|
+
@if (actionsModel.custom?.length) {
|
|
3430
|
+
@for (action of actionsModel.custom || []; track action.id) {
|
|
3431
|
+
<div class="mf-custom-message-card">
|
|
3432
|
+
<div class="mf-custom-card__title">{{ action.label || action.id }}</div>
|
|
3433
|
+
<div class="mf-custom-card__grid">
|
|
3434
|
+
<mat-form-field appearance="outline">
|
|
3435
|
+
<mat-label>Confirmação</mat-label>
|
|
3436
|
+
<input
|
|
3437
|
+
matInput
|
|
3438
|
+
[value]="messagesModel.customActions?.[action.id || '']?.confirmation || ''"
|
|
3439
|
+
(input)="updateCustomMessage(action, 'confirmation', $any($event.target).value)"
|
|
3440
|
+
/>
|
|
3441
|
+
</mat-form-field>
|
|
3442
|
+
<mat-form-field appearance="outline">
|
|
3443
|
+
<mat-label>Sucesso</mat-label>
|
|
3444
|
+
<input
|
|
3445
|
+
matInput
|
|
3446
|
+
[value]="messagesModel.customActions?.[action.id || '']?.success || ''"
|
|
3447
|
+
(input)="updateCustomMessage(action, 'success', $any($event.target).value)"
|
|
3448
|
+
/>
|
|
3449
|
+
</mat-form-field>
|
|
3450
|
+
<mat-form-field appearance="outline">
|
|
3451
|
+
<mat-label>Erro</mat-label>
|
|
3452
|
+
<input
|
|
3453
|
+
matInput
|
|
3454
|
+
[value]="messagesModel.customActions?.[action.id || '']?.error || ''"
|
|
3455
|
+
(input)="updateCustomMessage(action, 'error', $any($event.target).value)"
|
|
3456
|
+
/>
|
|
3457
|
+
</mat-form-field>
|
|
3458
|
+
</div>
|
|
3459
|
+
</div>
|
|
3460
|
+
}
|
|
3461
|
+
} @else {
|
|
3462
|
+
<p class="mf-empty">Cadastre ações customizadas para configurar mensagens específicas.</p>
|
|
3463
|
+
}
|
|
3464
|
+
</div>
|
|
3465
|
+
</section>
|
|
1429
3466
|
</div>
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
3467
|
+
</mat-tab>
|
|
3468
|
+
|
|
3469
|
+
<mat-tab label="Comportamento">
|
|
3470
|
+
<div class="mf-tab">
|
|
3471
|
+
<section class="mf-section">
|
|
3472
|
+
<div class="mf-section__header">
|
|
3473
|
+
<div>
|
|
3474
|
+
<h3>Comportamento do formulário</h3>
|
|
3475
|
+
<p class="mf-help">Preferências aplicadas pelo host ou runtime.</p>
|
|
3476
|
+
</div>
|
|
3477
|
+
</div>
|
|
3478
|
+
<div class="mf-note">Estas opções são persistidas no config; o host decide como aplicar.</div>
|
|
3479
|
+
<div class="mf-toggle-grid">
|
|
3480
|
+
<mat-slide-toggle
|
|
3481
|
+
[checked]="!!behaviorModel.confirmOnUnsavedChanges"
|
|
3482
|
+
(change)="updateBehavior('confirmOnUnsavedChanges', $event.checked)"
|
|
3483
|
+
>
|
|
3484
|
+
Confirmar ao sair com alterações não salvas
|
|
3485
|
+
</mat-slide-toggle>
|
|
3486
|
+
<mat-slide-toggle
|
|
3487
|
+
[checked]="!!behaviorModel.trackHistory"
|
|
3488
|
+
(change)="updateBehavior('trackHistory', $event.checked)"
|
|
3489
|
+
>
|
|
3490
|
+
Rastrear histórico de alterações
|
|
3491
|
+
</mat-slide-toggle>
|
|
3492
|
+
<mat-slide-toggle
|
|
3493
|
+
[checked]="!!behaviorModel.focusFirstError"
|
|
3494
|
+
(change)="updateBehavior('focusFirstError', $event.checked)"
|
|
3495
|
+
>
|
|
3496
|
+
Focar no primeiro erro ao submeter
|
|
3497
|
+
</mat-slide-toggle>
|
|
3498
|
+
<mat-slide-toggle
|
|
3499
|
+
[checked]="!!behaviorModel.scrollToErrors"
|
|
3500
|
+
(change)="updateBehavior('scrollToErrors', $event.checked)"
|
|
3501
|
+
>
|
|
3502
|
+
Rolar até os erros ao submeter
|
|
3503
|
+
</mat-slide-toggle>
|
|
3504
|
+
<mat-slide-toggle
|
|
3505
|
+
[checked]="!!behaviorModel.clearAfterSave"
|
|
3506
|
+
(change)="updateBehavior('clearAfterSave', $event.checked)"
|
|
3507
|
+
>
|
|
3508
|
+
Limpar formulário após salvar
|
|
3509
|
+
</mat-slide-toggle>
|
|
3510
|
+
<mat-slide-toggle
|
|
3511
|
+
[checked]="!!behaviorModel.reactiveValidation"
|
|
3512
|
+
(change)="updateBehavior('reactiveValidation', $event.checked)"
|
|
3513
|
+
>
|
|
3514
|
+
Validação reativa
|
|
3515
|
+
</mat-slide-toggle>
|
|
3516
|
+
</div>
|
|
3517
|
+
<div class="mf-actions-layout">
|
|
3518
|
+
<mat-form-field appearance="outline">
|
|
3519
|
+
<mat-label>Redirecionar após salvar (URL)</mat-label>
|
|
3520
|
+
<input
|
|
3521
|
+
matInput
|
|
3522
|
+
[value]="behaviorModel.redirectAfterSave || ''"
|
|
3523
|
+
(input)="updateBehavior('redirectAfterSave', $any($event.target).value)"
|
|
3524
|
+
/>
|
|
3525
|
+
</mat-form-field>
|
|
3526
|
+
<mat-form-field appearance="outline">
|
|
3527
|
+
<mat-label>Debounce validação reativa (ms)</mat-label>
|
|
3528
|
+
<input
|
|
3529
|
+
matInput
|
|
3530
|
+
type="number"
|
|
3531
|
+
min="0"
|
|
3532
|
+
[value]="behaviorModel.reactiveValidationDebounceMs || 0"
|
|
3533
|
+
(input)="updateBehavior('reactiveValidationDebounceMs', $any($event.target).valueAsNumber || 0)"
|
|
3534
|
+
/>
|
|
3535
|
+
</mat-form-field>
|
|
3536
|
+
</div>
|
|
3537
|
+
</section>
|
|
1435
3538
|
</div>
|
|
1436
|
-
</
|
|
1437
|
-
|
|
3539
|
+
</mat-tab>
|
|
3540
|
+
|
|
3541
|
+
<mat-tab label="Dicas">
|
|
3542
|
+
<div class="mf-tab">
|
|
3543
|
+
<section class="mf-section">
|
|
3544
|
+
<div class="mf-section__header">
|
|
3545
|
+
<div>
|
|
3546
|
+
<h3>Mensagens de modo</h3>
|
|
3547
|
+
<p class="mf-help">Textos auxiliares exibidos pelo host.</p>
|
|
3548
|
+
</div>
|
|
3549
|
+
</div>
|
|
3550
|
+
<div class="mf-note">Útil para i18n e orientação do usuário.</div>
|
|
3551
|
+
<div class="mf-messages-grid">
|
|
3552
|
+
<mat-form-field appearance="outline">
|
|
3553
|
+
<mat-label>Criar</mat-label>
|
|
3554
|
+
<input
|
|
3555
|
+
matInput
|
|
3556
|
+
[value]="hintsModel.dataModes.create"
|
|
3557
|
+
(input)="updateHints('dataModes', 'create', $any($event.target).value)"
|
|
3558
|
+
/>
|
|
3559
|
+
</mat-form-field>
|
|
3560
|
+
<mat-form-field appearance="outline">
|
|
3561
|
+
<mat-label>Editar</mat-label>
|
|
3562
|
+
<input
|
|
3563
|
+
matInput
|
|
3564
|
+
[value]="hintsModel.dataModes.edit"
|
|
3565
|
+
(input)="updateHints('dataModes', 'edit', $any($event.target).value)"
|
|
3566
|
+
/>
|
|
3567
|
+
</mat-form-field>
|
|
3568
|
+
<mat-form-field appearance="outline">
|
|
3569
|
+
<mat-label>Visualizar</mat-label>
|
|
3570
|
+
<input
|
|
3571
|
+
matInput
|
|
3572
|
+
[value]="hintsModel.dataModes.view"
|
|
3573
|
+
(input)="updateHints('dataModes', 'view', $any($event.target).value)"
|
|
3574
|
+
/>
|
|
3575
|
+
</mat-form-field>
|
|
3576
|
+
</div>
|
|
3577
|
+
</section>
|
|
3578
|
+
|
|
3579
|
+
<section class="mf-section">
|
|
3580
|
+
<div class="mf-section__header">
|
|
3581
|
+
<div>
|
|
3582
|
+
<h3>Dicas de UI</h3>
|
|
3583
|
+
<p class="mf-help">Ajuda contextual para estados do formulário.</p>
|
|
3584
|
+
</div>
|
|
3585
|
+
</div>
|
|
3586
|
+
<div class="mf-messages-grid">
|
|
3587
|
+
<mat-form-field appearance="outline">
|
|
3588
|
+
<mat-label>Apresentação</mat-label>
|
|
3589
|
+
<textarea
|
|
3590
|
+
matInput
|
|
3591
|
+
rows="2"
|
|
3592
|
+
[value]="hintsModel.uiModes.presentation"
|
|
3593
|
+
(input)="updateHints('uiModes', 'presentation', $any($event.target).value)"
|
|
3594
|
+
></textarea>
|
|
3595
|
+
</mat-form-field>
|
|
3596
|
+
<mat-form-field appearance="outline">
|
|
3597
|
+
<mat-label>Somente leitura</mat-label>
|
|
3598
|
+
<textarea
|
|
3599
|
+
matInput
|
|
3600
|
+
rows="2"
|
|
3601
|
+
[value]="hintsModel.uiModes.readonly"
|
|
3602
|
+
(input)="updateHints('uiModes', 'readonly', $any($event.target).value)"
|
|
3603
|
+
></textarea>
|
|
3604
|
+
</mat-form-field>
|
|
3605
|
+
<mat-form-field appearance="outline">
|
|
3606
|
+
<mat-label>Desabilitado</mat-label>
|
|
3607
|
+
<textarea
|
|
3608
|
+
matInput
|
|
3609
|
+
rows="2"
|
|
3610
|
+
[value]="hintsModel.uiModes.disabled"
|
|
3611
|
+
(input)="updateHints('uiModes', 'disabled', $any($event.target).value)"
|
|
3612
|
+
></textarea>
|
|
3613
|
+
</mat-form-field>
|
|
3614
|
+
<mat-form-field appearance="outline">
|
|
3615
|
+
<mat-label>Visível</mat-label>
|
|
3616
|
+
<textarea
|
|
3617
|
+
matInput
|
|
3618
|
+
rows="2"
|
|
3619
|
+
[value]="hintsModel.uiModes.visible"
|
|
3620
|
+
(input)="updateHints('uiModes', 'visible', $any($event.target).value)"
|
|
3621
|
+
></textarea>
|
|
3622
|
+
</mat-form-field>
|
|
3623
|
+
</div>
|
|
3624
|
+
</section>
|
|
3625
|
+
</div>
|
|
3626
|
+
</mat-tab>
|
|
3627
|
+
|
|
3628
|
+
<mat-tab label="Hooks">
|
|
3629
|
+
<div class="mf-tab">
|
|
3630
|
+
<section class="mf-section">
|
|
3631
|
+
<div class="mf-section__header">
|
|
3632
|
+
<div>
|
|
3633
|
+
<h3>Hooks de ciclo de vida</h3>
|
|
3634
|
+
<p class="mf-help">Defina ações automatizadas por estágio.</p>
|
|
3635
|
+
</div>
|
|
3636
|
+
<div class="mf-actions-row">
|
|
3637
|
+
<button mat-stroked-button type="button" (click)="resetHooks()">
|
|
3638
|
+
Restaurar
|
|
3639
|
+
</button>
|
|
3640
|
+
<button mat-flat-button color="primary" type="button" (click)="applyHooks()">
|
|
3641
|
+
Aplicar hooks
|
|
3642
|
+
</button>
|
|
3643
|
+
</div>
|
|
3644
|
+
</div>
|
|
3645
|
+
<div class="mf-note">Executa apenas se o host registrar hooks disponíveis.</div>
|
|
3646
|
+
<div class="mf-hooks-grid">
|
|
3647
|
+
@for (stage of hookStages; track stage) {
|
|
3648
|
+
<mat-form-field appearance="outline" class="mf-hook-field">
|
|
3649
|
+
<mat-label>{{ hookLabel(stage) }}</mat-label>
|
|
3650
|
+
<textarea
|
|
3651
|
+
matInput
|
|
3652
|
+
rows="4"
|
|
3653
|
+
[value]="hooksText[stage]"
|
|
3654
|
+
(input)="onHookTextChange(stage, $any($event.target).value)"
|
|
3655
|
+
placeholder='[{ "id": "notifySuccess", "priority": 0, "args": {} }]'
|
|
3656
|
+
></textarea>
|
|
3657
|
+
<button
|
|
3658
|
+
mat-icon-button
|
|
3659
|
+
matSuffix
|
|
3660
|
+
class="help-icon-button"
|
|
3661
|
+
type="button"
|
|
3662
|
+
[matTooltip]="'Array JSON de hooks para ' + stage + ' (id, priority, timeoutMs, args).'"
|
|
3663
|
+
matTooltipPosition="above"
|
|
3664
|
+
>
|
|
3665
|
+
<mat-icon>help_outline</mat-icon>
|
|
3666
|
+
</button>
|
|
3667
|
+
@if (hooksErrors[stage]) {
|
|
3668
|
+
<mat-error>{{ hooksErrors[stage] }}</mat-error>
|
|
3669
|
+
}
|
|
3670
|
+
</mat-form-field>
|
|
3671
|
+
}
|
|
3672
|
+
</div>
|
|
3673
|
+
</section>
|
|
3674
|
+
</div>
|
|
3675
|
+
</mat-tab>
|
|
3676
|
+
|
|
3677
|
+
<mat-tab label="Regras">
|
|
3678
|
+
<div class="mf-tab">
|
|
3679
|
+
<section class="mf-section">
|
|
3680
|
+
<div class="mf-section__header">
|
|
3681
|
+
<div>
|
|
3682
|
+
<h3>Regras de layout</h3>
|
|
3683
|
+
<p class="mf-help">Regras avançadas para visibilidade e comportamento.</p>
|
|
3684
|
+
</div>
|
|
3685
|
+
<div class="mf-actions-row">
|
|
3686
|
+
<button mat-flat-button color="primary" type="button" (click)="applyRules()">
|
|
3687
|
+
Aplicar regras
|
|
3688
|
+
</button>
|
|
3689
|
+
</div>
|
|
3690
|
+
</div>
|
|
3691
|
+
<div class="mf-note">Persistido no config; a execução depende do host.</div>
|
|
3692
|
+
<mat-form-field appearance="outline">
|
|
3693
|
+
<mat-label>formRules (JSON)</mat-label>
|
|
3694
|
+
<textarea
|
|
3695
|
+
matInput
|
|
3696
|
+
rows="6"
|
|
3697
|
+
[value]="formRulesText"
|
|
3698
|
+
(input)="onRulesTextChange($any($event.target).value)"
|
|
3699
|
+
placeholder='[{"id":"rule-1","name":"Obrigatoriedade","targetType":"field","targets":["campo"],"effect":{"condition":"{campo} != null","properties":{"required":true}}}]'
|
|
3700
|
+
></textarea>
|
|
3701
|
+
<button
|
|
3702
|
+
mat-icon-button
|
|
3703
|
+
matSuffix
|
|
3704
|
+
class="help-icon-button"
|
|
3705
|
+
type="button"
|
|
3706
|
+
[matTooltip]="'Array JSON com regras (id, targetType, targets, effect).'"
|
|
3707
|
+
matTooltipPosition="above"
|
|
3708
|
+
>
|
|
3709
|
+
<mat-icon>help_outline</mat-icon>
|
|
3710
|
+
</button>
|
|
3711
|
+
@if (formRulesError) {
|
|
3712
|
+
<mat-error>{{ formRulesError }}</mat-error>
|
|
3713
|
+
}
|
|
3714
|
+
</mat-form-field>
|
|
3715
|
+
<mat-form-field appearance="outline">
|
|
3716
|
+
<mat-label>formRulesState (JSON)</mat-label>
|
|
3717
|
+
<textarea
|
|
3718
|
+
matInput
|
|
3719
|
+
rows="6"
|
|
3720
|
+
[value]="formRulesStateText"
|
|
3721
|
+
(input)="onRulesStateTextChange($any($event.target).value)"
|
|
3722
|
+
placeholder='{"nodes":[],"edges":[]}'
|
|
3723
|
+
></textarea>
|
|
3724
|
+
<button
|
|
3725
|
+
mat-icon-button
|
|
3726
|
+
matSuffix
|
|
3727
|
+
class="help-icon-button"
|
|
3728
|
+
type="button"
|
|
3729
|
+
[matTooltip]="'Estado bruto do editor visual (opcional).'"
|
|
3730
|
+
matTooltipPosition="above"
|
|
3731
|
+
>
|
|
3732
|
+
<mat-icon>help_outline</mat-icon>
|
|
3733
|
+
</button>
|
|
3734
|
+
@if (formRulesStateError) {
|
|
3735
|
+
<mat-error>{{ formRulesStateError }}</mat-error>
|
|
3736
|
+
}
|
|
3737
|
+
</mat-form-field>
|
|
3738
|
+
<div class="mf-actions-row mf-actions-row--end">
|
|
3739
|
+
<button mat-flat-button color="primary" type="button" (click)="applyRules()">
|
|
3740
|
+
Aplicar regras
|
|
3741
|
+
</button>
|
|
3742
|
+
</div>
|
|
3743
|
+
</section>
|
|
3744
|
+
</div>
|
|
3745
|
+
</mat-tab>
|
|
3746
|
+
|
|
3747
|
+
<mat-tab label="Cascatas">
|
|
3748
|
+
<div class="mf-tab">
|
|
3749
|
+
<section class="mf-section mf-section--full">
|
|
3750
|
+
<div class="mf-section__header">
|
|
3751
|
+
<div>
|
|
3752
|
+
<h3>Dependências entre campos</h3>
|
|
3753
|
+
<p class="mf-help">Configure cascatas nativas baseadas em metadados.</p>
|
|
3754
|
+
</div>
|
|
3755
|
+
</div>
|
|
3756
|
+
<div class="mf-note">As alterações atualizam metadados e são salvas no draft.</div>
|
|
3757
|
+
<praxis-cascade-manager-tab
|
|
3758
|
+
[fields]="cascadeFields"
|
|
3759
|
+
(apply)="applyCascadePatch($event)"
|
|
3760
|
+
></praxis-cascade-manager-tab>
|
|
3761
|
+
</section>
|
|
3762
|
+
</div>
|
|
3763
|
+
</mat-tab>
|
|
3764
|
+
</mat-tab-group>
|
|
1438
3765
|
</div>
|
|
1439
|
-
`, isInline: true, styles: [".mf-editor{display:grid;gap:
|
|
3766
|
+
`, isInline: true, styles: [".mf-editor{display:grid;gap:16px;padding:16px;color:var(--md-sys-color-on-surface)}.mf-editor__toolbar{display:flex;align-items:center;gap:12px;padding:12px 14px;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;background:var(--md-sys-color-surface-container)}.mf-editor__title{display:flex;flex-direction:column;gap:2px;font-weight:600}.mf-editor__subtitle{font-weight:400;color:var(--md-sys-color-on-surface-variant)}.mf-editor__toolbar .spacer{flex:1}.mf-tabs{min-height:420px}.mf-tab{display:grid;gap:16px;padding:14px 6px 0}.mf-editor__list{display:grid;gap:8px;overflow-x:auto}.mf-editor__row{display:grid;grid-template-columns:2.2fr 2fr 1fr repeat(4,minmax(90px,.8fr));align-items:center;gap:8px;padding:10px 12px;border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;background:var(--md-sys-color-surface);min-width:720px}.mf-editor__row--head{font-weight:600;color:var(--md-sys-color-on-surface);background:var(--md-sys-color-surface-container)}.mf-editor__filter{display:inline-flex;align-items:center;gap:8px;font-weight:500}.mf-editor__filter input{accent-color:var(--md-sys-color-primary)}.col code{display:inline-block;font-family:inherit;font-size:.85rem;padding:2px 8px;border-radius:6px;background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface-variant)}.mf-btn{min-height:36px;padding:0 12px;border-radius:8px;border:1px solid var(--md-sys-color-outline);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);cursor:pointer;transition:background-color .18s ease,border-color .18s ease,color .18s ease}.mf-btn:hover{background:var(--md-sys-color-surface-container)}.mf-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.toggle{display:inline-flex;align-items:center;gap:6px;color:var(--md-sys-color-on-surface-variant)}.mf-section{display:grid;gap:12px;padding:16px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface)}.mf-section--full{padding:10px}.mf-section__header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.mf-section__header h3{margin:0;font-size:1.05rem;font-weight:600}.mf-help{margin:6px 0 0;font-size:.9rem;color:var(--md-sys-color-on-surface-variant)}.mf-note{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);font-size:.9rem}.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;margin-right:-4px}.help-icon-button mat-icon{font-size:18px;width:18px;height:18px}.mat-mdc-form-field-icon-suffix{align-self:center}.mf-actions-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px}.mf-action-card,.mf-custom-card,.mf-custom-message-card{display:grid;gap:10px;padding:14px;border-radius:10px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high)}.mf-action-card__title,.mf-custom-card__title{font-weight:600;color:var(--md-sys-color-on-surface)}.mf-actions-layout{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;align-items:center}.mf-toggle-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px;align-items:start}.mf-hooks-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px}.mf-hook-field textarea{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:.85rem}.mf-actions-row{display:inline-flex;gap:8px;align-items:center}.mf-actions-row--end{justify-content:flex-end;width:100%}.mf-custom-actions,.mf-custom-messages{display:grid;gap:12px}.mf-custom-card__header{display:flex;align-items:center;justify-content:space-between;gap:8px}.mf-custom-card__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;align-items:center}.mf-messages-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px}.mf-empty{margin:0;color:var(--md-sys-color-on-surface-variant)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i2.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i2.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: 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.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.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: MatSelectModule }, { kind: "component", type: i5.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: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i6.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: MatButtonModule }, { kind: "component", type: i7.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: i7.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i9.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: CascadeManagerTabComponent, selector: "praxis-cascade-manager-tab", inputs: ["fields", "connections"], outputs: ["apply", "cancel"] }] });
|
|
1440
3767
|
}
|
|
1441
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
3768
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFormConfigEditorComponent, decorators: [{
|
|
1442
3769
|
type: Component,
|
|
1443
|
-
args: [{ selector: 'praxis-manual-form-config-editor', standalone: true, imports: [
|
|
3770
|
+
args: [{ selector: 'praxis-manual-form-config-editor', standalone: true, imports: [
|
|
3771
|
+
CommonModule,
|
|
3772
|
+
FormsModule,
|
|
3773
|
+
MatTabsModule,
|
|
3774
|
+
MatFormFieldModule,
|
|
3775
|
+
MatInputModule,
|
|
3776
|
+
MatSelectModule,
|
|
3777
|
+
MatSlideToggleModule,
|
|
3778
|
+
MatButtonModule,
|
|
3779
|
+
MatIconModule,
|
|
3780
|
+
MatTooltipModule,
|
|
3781
|
+
CascadeManagerTabComponent,
|
|
3782
|
+
], template: `
|
|
1444
3783
|
<div class="mf-editor">
|
|
1445
3784
|
<div class="mf-editor__toolbar">
|
|
1446
|
-
<
|
|
3785
|
+
<div class="mf-editor__title">
|
|
3786
|
+
<span>Configurações do formulário</span>
|
|
3787
|
+
<small class="mf-editor__subtitle">Campos, ações, mensagens e comportamento</small>
|
|
3788
|
+
</div>
|
|
1447
3789
|
<div class="spacer"></div>
|
|
1448
|
-
<button type="button" (click)="close()">Fechar</button>
|
|
3790
|
+
<button type="button" class="mf-btn" (click)="close()">Fechar</button>
|
|
1449
3791
|
</div>
|
|
1450
3792
|
|
|
1451
|
-
<
|
|
1452
|
-
<
|
|
1453
|
-
<div class="
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
<div class="col col--req">Obrigatório</div>
|
|
1458
|
-
<div class="col col--ro">Som. leitura</div>
|
|
1459
|
-
<div class="col col--dis">Desabilitado</div>
|
|
1460
|
-
</div>
|
|
1461
|
-
|
|
1462
|
-
<div class="mf-editor__row" *ngFor="let f of filtered">
|
|
1463
|
-
<div class="col col--name"><code>{{ f.name }}</code></div>
|
|
1464
|
-
<div class="col col--label">{{ f.label || f.name }}</div>
|
|
1465
|
-
<div class="col col--type">{{ f.controlType }}</div>
|
|
1466
|
-
<div class="col col--vis">
|
|
1467
|
-
<label class="toggle">
|
|
1468
|
-
<input type="checkbox" [checked]="!f.hidden" (change)="toggleVisibility(f, $any($event.target).checked)" />
|
|
1469
|
-
<span>{{ f.hidden ? 'Oculto' : 'Visível' }}</span>
|
|
3793
|
+
<mat-tab-group class="mf-tabs">
|
|
3794
|
+
<mat-tab label="Campos">
|
|
3795
|
+
<div class="mf-tab">
|
|
3796
|
+
<label class="mf-editor__filter">
|
|
3797
|
+
<input type="checkbox" [(ngModel)]="onlyHidden" (change)="applyFilter()" />
|
|
3798
|
+
<span>Mostrar apenas ocultos</span>
|
|
1470
3799
|
</label>
|
|
3800
|
+
<div class="mf-editor__list">
|
|
3801
|
+
<div class="mf-editor__row mf-editor__row--head">
|
|
3802
|
+
<div class="col col--name">Campo</div>
|
|
3803
|
+
<div class="col col--label">Rótulo</div>
|
|
3804
|
+
<div class="col col--type">Tipo</div>
|
|
3805
|
+
<div class="col col--vis">Visível</div>
|
|
3806
|
+
<div class="col col--req">Obrigatório</div>
|
|
3807
|
+
<div class="col col--ro">Som. leitura</div>
|
|
3808
|
+
<div class="col col--dis">Desabilitado</div>
|
|
3809
|
+
</div>
|
|
3810
|
+
|
|
3811
|
+
@for (f of filtered; track f.name) {
|
|
3812
|
+
<div class="mf-editor__row">
|
|
3813
|
+
<div class="col col--name"><code>{{ f.name }}</code></div>
|
|
3814
|
+
<div class="col col--label">{{ f.label || f.name }}</div>
|
|
3815
|
+
<div class="col col--type">{{ f.controlType }}</div>
|
|
3816
|
+
<div class="col col--vis">
|
|
3817
|
+
<label class="toggle">
|
|
3818
|
+
<input type="checkbox" [checked]="!f.hidden" (change)="toggleVisibility(f, $any($event.target).checked)" />
|
|
3819
|
+
<span>{{ f.hidden ? 'Oculto' : 'Visível' }}</span>
|
|
3820
|
+
</label>
|
|
3821
|
+
</div>
|
|
3822
|
+
<div class="col col--req">
|
|
3823
|
+
<label class="toggle">
|
|
3824
|
+
<input type="checkbox" [checked]="!!f.required" (change)="toggleRequired(f, $any($event.target).checked)" />
|
|
3825
|
+
<span>{{ f.required ? 'Sim' : 'Não' }}</span>
|
|
3826
|
+
</label>
|
|
3827
|
+
</div>
|
|
3828
|
+
<div class="col col--ro">
|
|
3829
|
+
<label class="toggle">
|
|
3830
|
+
<input type="checkbox" [checked]="!!f.readOnly" (change)="toggleReadOnly(f, $any($event.target).checked)" />
|
|
3831
|
+
<span>{{ f.readOnly ? 'Sim' : 'Não' }}</span>
|
|
3832
|
+
</label>
|
|
3833
|
+
</div>
|
|
3834
|
+
<div class="col col--dis">
|
|
3835
|
+
<label class="toggle">
|
|
3836
|
+
<input type="checkbox" [checked]="!!f.disabled" (change)="toggleDisabled(f, $any($event.target).checked)" />
|
|
3837
|
+
<span>{{ f.disabled ? 'Sim' : 'Não' }}</span>
|
|
3838
|
+
</label>
|
|
3839
|
+
</div>
|
|
3840
|
+
</div>
|
|
3841
|
+
}
|
|
3842
|
+
</div>
|
|
1471
3843
|
</div>
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
3844
|
+
</mat-tab>
|
|
3845
|
+
|
|
3846
|
+
<mat-tab label="Ações">
|
|
3847
|
+
<div class="mf-tab">
|
|
3848
|
+
<section class="mf-section">
|
|
3849
|
+
<div class="mf-section__header">
|
|
3850
|
+
<div>
|
|
3851
|
+
<h3>Botões padrão</h3>
|
|
3852
|
+
<p class="mf-help">Controle rótulos, visibilidade e estilo dos botões básicos.</p>
|
|
3853
|
+
</div>
|
|
3854
|
+
</div>
|
|
3855
|
+
<div class="mf-actions-grid">
|
|
3856
|
+
@for (block of actionBlocks; track block.key) {
|
|
3857
|
+
<div class="mf-action-card">
|
|
3858
|
+
<div class="mf-action-card__title">{{ block.label }}</div>
|
|
3859
|
+
<mat-slide-toggle
|
|
3860
|
+
[checked]="actionsModel[block.key].visible"
|
|
3861
|
+
(change)="updateActionField(block.key, 'visible', $event.checked)"
|
|
3862
|
+
>
|
|
3863
|
+
Visível
|
|
3864
|
+
</mat-slide-toggle>
|
|
3865
|
+
<mat-form-field appearance="outline">
|
|
3866
|
+
<mat-label>Label</mat-label>
|
|
3867
|
+
<input
|
|
3868
|
+
matInput
|
|
3869
|
+
[value]="actionsModel[block.key].label"
|
|
3870
|
+
(input)="updateActionField(block.key, 'label', $any($event.target).value)"
|
|
3871
|
+
/>
|
|
3872
|
+
</mat-form-field>
|
|
3873
|
+
<mat-form-field appearance="outline">
|
|
3874
|
+
<mat-label>Ícone</mat-label>
|
|
3875
|
+
<input
|
|
3876
|
+
matInput
|
|
3877
|
+
[value]="actionsModel[block.key].icon || ''"
|
|
3878
|
+
(input)="updateActionField(block.key, 'icon', $any($event.target).value)"
|
|
3879
|
+
/>
|
|
3880
|
+
</mat-form-field>
|
|
3881
|
+
<mat-form-field appearance="outline">
|
|
3882
|
+
<mat-label>Variante</mat-label>
|
|
3883
|
+
<mat-select
|
|
3884
|
+
[value]="actionsModel[block.key].variant || null"
|
|
3885
|
+
(selectionChange)="updateActionField(block.key, 'variant', $event.value)"
|
|
3886
|
+
>
|
|
3887
|
+
<mat-option [value]="null">Padrão</mat-option>
|
|
3888
|
+
<mat-option value="raised">Elevado</mat-option>
|
|
3889
|
+
<mat-option value="stroked">Contornado</mat-option>
|
|
3890
|
+
<mat-option value="flat">Plano</mat-option>
|
|
3891
|
+
<mat-option value="fab">Flutuante</mat-option>
|
|
3892
|
+
</mat-select>
|
|
3893
|
+
</mat-form-field>
|
|
3894
|
+
<mat-form-field appearance="outline">
|
|
3895
|
+
<mat-label>Cor</mat-label>
|
|
3896
|
+
<mat-select
|
|
3897
|
+
[value]="actionsModel[block.key].color || null"
|
|
3898
|
+
(selectionChange)="updateActionField(block.key, 'color', $event.value)"
|
|
3899
|
+
>
|
|
3900
|
+
<mat-option [value]="null">Padrão</mat-option>
|
|
3901
|
+
<mat-option value="primary">Primary</mat-option>
|
|
3902
|
+
<mat-option value="accent">Accent</mat-option>
|
|
3903
|
+
<mat-option value="warn">Warn</mat-option>
|
|
3904
|
+
<mat-option value="basic">Basic</mat-option>
|
|
3905
|
+
</mat-select>
|
|
3906
|
+
</mat-form-field>
|
|
3907
|
+
<mat-form-field appearance="outline">
|
|
3908
|
+
<mat-label>Atalho</mat-label>
|
|
3909
|
+
<input
|
|
3910
|
+
matInput
|
|
3911
|
+
[value]="actionsModel[block.key].shortcut || ''"
|
|
3912
|
+
(input)="updateActionField(block.key, 'shortcut', $any($event.target).value)"
|
|
3913
|
+
/>
|
|
3914
|
+
</mat-form-field>
|
|
3915
|
+
</div>
|
|
3916
|
+
}
|
|
3917
|
+
</div>
|
|
3918
|
+
</section>
|
|
3919
|
+
|
|
3920
|
+
<section class="mf-section">
|
|
3921
|
+
<div class="mf-section__header">
|
|
3922
|
+
<div>
|
|
3923
|
+
<h3>Layout das ações</h3>
|
|
3924
|
+
<p class="mf-help">Define alinhamento e disposição da barra de ações.</p>
|
|
3925
|
+
</div>
|
|
3926
|
+
</div>
|
|
3927
|
+
<div class="mf-actions-layout">
|
|
3928
|
+
<mat-form-field appearance="outline">
|
|
3929
|
+
<mat-label>Posição</mat-label>
|
|
3930
|
+
<mat-select
|
|
3931
|
+
[value]="actionsModel.position || null"
|
|
3932
|
+
(selectionChange)="updateActionsLayout('position', $event.value)"
|
|
3933
|
+
>
|
|
3934
|
+
<mat-option value="left">Esquerda</mat-option>
|
|
3935
|
+
<mat-option value="center">Centro</mat-option>
|
|
3936
|
+
<mat-option value="right">Direita</mat-option>
|
|
3937
|
+
<mat-option value="justified">Justificado</mat-option>
|
|
3938
|
+
<mat-option value="split">Split</mat-option>
|
|
3939
|
+
</mat-select>
|
|
3940
|
+
</mat-form-field>
|
|
3941
|
+
<mat-form-field appearance="outline">
|
|
3942
|
+
<mat-label>Orientação</mat-label>
|
|
3943
|
+
<mat-select
|
|
3944
|
+
[value]="actionsModel.orientation || null"
|
|
3945
|
+
(selectionChange)="updateActionsLayout('orientation', $event.value)"
|
|
3946
|
+
>
|
|
3947
|
+
<mat-option value="horizontal">Horizontal</mat-option>
|
|
3948
|
+
<mat-option value="vertical">Vertical</mat-option>
|
|
3949
|
+
</mat-select>
|
|
3950
|
+
</mat-form-field>
|
|
3951
|
+
<mat-form-field appearance="outline">
|
|
3952
|
+
<mat-label>Espaçamento</mat-label>
|
|
3953
|
+
<mat-select
|
|
3954
|
+
[value]="actionsModel.spacing || null"
|
|
3955
|
+
(selectionChange)="updateActionsLayout('spacing', $event.value)"
|
|
3956
|
+
>
|
|
3957
|
+
<mat-option value="compact">Compacto</mat-option>
|
|
3958
|
+
<mat-option value="normal">Normal</mat-option>
|
|
3959
|
+
<mat-option value="spacious">Espaçoso</mat-option>
|
|
3960
|
+
</mat-select>
|
|
3961
|
+
</mat-form-field>
|
|
3962
|
+
<mat-form-field appearance="outline">
|
|
3963
|
+
<mat-label>Posicionamento</mat-label>
|
|
3964
|
+
<mat-select
|
|
3965
|
+
[value]="actionsModel.placement || null"
|
|
3966
|
+
(selectionChange)="updateActionsLayout('placement', $event.value)"
|
|
3967
|
+
>
|
|
3968
|
+
<mat-option value="afterSections">Após seções</mat-option>
|
|
3969
|
+
<mat-option value="insideLastSection">Dentro da última seção</mat-option>
|
|
3970
|
+
<mat-option value="top">Topo</mat-option>
|
|
3971
|
+
</mat-select>
|
|
3972
|
+
</mat-form-field>
|
|
3973
|
+
<mat-slide-toggle
|
|
3974
|
+
[checked]="!!actionsModel.sticky"
|
|
3975
|
+
(change)="updateActionsLayout('sticky', $event.checked)"
|
|
3976
|
+
>
|
|
3977
|
+
Ações fixas
|
|
3978
|
+
</mat-slide-toggle>
|
|
3979
|
+
<mat-slide-toggle
|
|
3980
|
+
[checked]="!!actionsModel.divider"
|
|
3981
|
+
(change)="updateActionsLayout('divider', $event.checked)"
|
|
3982
|
+
>
|
|
3983
|
+
Mostrar divisor
|
|
3984
|
+
</mat-slide-toggle>
|
|
3985
|
+
</div>
|
|
3986
|
+
</section>
|
|
3987
|
+
|
|
3988
|
+
<section class="mf-section">
|
|
3989
|
+
<div class="mf-section__header mf-section__header--row">
|
|
3990
|
+
<div>
|
|
3991
|
+
<h3>Ações customizadas</h3>
|
|
3992
|
+
<p class="mf-help">Crie botões extras com evento próprio.</p>
|
|
3993
|
+
</div>
|
|
3994
|
+
<button mat-stroked-button type="button" (click)="addCustomAction()">
|
|
3995
|
+
<mat-icon>add</mat-icon>
|
|
3996
|
+
Adicionar ação
|
|
3997
|
+
</button>
|
|
3998
|
+
</div>
|
|
3999
|
+
<div class="mf-custom-actions">
|
|
4000
|
+
@if (actionsModel.custom?.length) {
|
|
4001
|
+
@for (action of actionsModel.custom || []; track action.id) {
|
|
4002
|
+
<div class="mf-custom-card">
|
|
4003
|
+
<div class="mf-custom-card__header">
|
|
4004
|
+
<div class="mf-custom-card__title">{{ action.label || action.id }}</div>
|
|
4005
|
+
<button mat-icon-button type="button" (click)="removeCustomAction(action)">
|
|
4006
|
+
<mat-icon>delete</mat-icon>
|
|
4007
|
+
</button>
|
|
4008
|
+
</div>
|
|
4009
|
+
<div class="mf-custom-card__grid">
|
|
4010
|
+
<mat-form-field appearance="outline">
|
|
4011
|
+
<mat-label>ID</mat-label>
|
|
4012
|
+
<input
|
|
4013
|
+
matInput
|
|
4014
|
+
[value]="action.id || ''"
|
|
4015
|
+
(input)="updateCustomActionField(action, 'id', $any($event.target).value)"
|
|
4016
|
+
/>
|
|
4017
|
+
</mat-form-field>
|
|
4018
|
+
<mat-form-field appearance="outline">
|
|
4019
|
+
<mat-label>Label</mat-label>
|
|
4020
|
+
<input
|
|
4021
|
+
matInput
|
|
4022
|
+
[value]="action.label || ''"
|
|
4023
|
+
(input)="updateCustomActionField(action, 'label', $any($event.target).value)"
|
|
4024
|
+
/>
|
|
4025
|
+
</mat-form-field>
|
|
4026
|
+
<mat-form-field appearance="outline">
|
|
4027
|
+
<mat-label>Evento (action)</mat-label>
|
|
4028
|
+
<input
|
|
4029
|
+
matInput
|
|
4030
|
+
[value]="action.action || ''"
|
|
4031
|
+
(input)="updateCustomActionField(action, 'action', $any($event.target).value)"
|
|
4032
|
+
/>
|
|
4033
|
+
</mat-form-field>
|
|
4034
|
+
<mat-form-field appearance="outline">
|
|
4035
|
+
<mat-label>Ícone</mat-label>
|
|
4036
|
+
<input
|
|
4037
|
+
matInput
|
|
4038
|
+
[value]="action.icon || ''"
|
|
4039
|
+
(input)="updateCustomActionField(action, 'icon', $any($event.target).value)"
|
|
4040
|
+
/>
|
|
4041
|
+
</mat-form-field>
|
|
4042
|
+
<mat-form-field appearance="outline">
|
|
4043
|
+
<mat-label>Variante</mat-label>
|
|
4044
|
+
<mat-select
|
|
4045
|
+
[value]="action.variant || null"
|
|
4046
|
+
(selectionChange)="updateCustomActionField(action, 'variant', $event.value)"
|
|
4047
|
+
>
|
|
4048
|
+
<mat-option [value]="null">Padrão</mat-option>
|
|
4049
|
+
<mat-option value="raised">Elevado</mat-option>
|
|
4050
|
+
<mat-option value="stroked">Contornado</mat-option>
|
|
4051
|
+
<mat-option value="flat">Plano</mat-option>
|
|
4052
|
+
<mat-option value="fab">Flutuante</mat-option>
|
|
4053
|
+
</mat-select>
|
|
4054
|
+
</mat-form-field>
|
|
4055
|
+
<mat-form-field appearance="outline">
|
|
4056
|
+
<mat-label>Cor</mat-label>
|
|
4057
|
+
<mat-select
|
|
4058
|
+
[value]="action.color || null"
|
|
4059
|
+
(selectionChange)="updateCustomActionField(action, 'color', $event.value)"
|
|
4060
|
+
>
|
|
4061
|
+
<mat-option [value]="null">Padrão</mat-option>
|
|
4062
|
+
<mat-option value="primary">Primary</mat-option>
|
|
4063
|
+
<mat-option value="accent">Accent</mat-option>
|
|
4064
|
+
<mat-option value="warn">Warn</mat-option>
|
|
4065
|
+
<mat-option value="basic">Basic</mat-option>
|
|
4066
|
+
</mat-select>
|
|
4067
|
+
</mat-form-field>
|
|
4068
|
+
<mat-form-field appearance="outline">
|
|
4069
|
+
<mat-label>Atalho</mat-label>
|
|
4070
|
+
<input
|
|
4071
|
+
matInput
|
|
4072
|
+
[value]="action.shortcut || ''"
|
|
4073
|
+
(input)="updateCustomActionField(action, 'shortcut', $any($event.target).value)"
|
|
4074
|
+
/>
|
|
4075
|
+
</mat-form-field>
|
|
4076
|
+
<mat-form-field appearance="outline">
|
|
4077
|
+
<mat-label>Tooltip</mat-label>
|
|
4078
|
+
<input
|
|
4079
|
+
matInput
|
|
4080
|
+
[value]="action.tooltip || ''"
|
|
4081
|
+
(input)="updateCustomActionField(action, 'tooltip', $any($event.target).value)"
|
|
4082
|
+
/>
|
|
4083
|
+
</mat-form-field>
|
|
4084
|
+
<mat-slide-toggle
|
|
4085
|
+
[checked]="action.visible !== false"
|
|
4086
|
+
(change)="updateCustomActionField(action, 'visible', $event.checked)"
|
|
4087
|
+
>
|
|
4088
|
+
Visível
|
|
4089
|
+
</mat-slide-toggle>
|
|
4090
|
+
</div>
|
|
4091
|
+
</div>
|
|
4092
|
+
}
|
|
4093
|
+
} @else {
|
|
4094
|
+
<p class="mf-empty">Nenhuma ação customizada cadastrada.</p>
|
|
4095
|
+
}
|
|
4096
|
+
</div>
|
|
4097
|
+
</section>
|
|
1477
4098
|
</div>
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
4099
|
+
</mat-tab>
|
|
4100
|
+
|
|
4101
|
+
<mat-tab label="Mensagens">
|
|
4102
|
+
<div class="mf-tab">
|
|
4103
|
+
<section class="mf-section">
|
|
4104
|
+
<div class="mf-section__header">
|
|
4105
|
+
<div>
|
|
4106
|
+
<h3>Feedback padrão</h3>
|
|
4107
|
+
<p class="mf-help">Mensagens de sucesso/erro exibidas pelo host.</p>
|
|
4108
|
+
</div>
|
|
4109
|
+
</div>
|
|
4110
|
+
<div class="mf-messages-grid">
|
|
4111
|
+
<mat-form-field appearance="outline">
|
|
4112
|
+
<mat-label>Sucesso ao criar</mat-label>
|
|
4113
|
+
<input
|
|
4114
|
+
matInput
|
|
4115
|
+
[value]="messagesModel.createRegistrySuccess || ''"
|
|
4116
|
+
(input)="updateMessage('createRegistrySuccess', $any($event.target).value)"
|
|
4117
|
+
/>
|
|
4118
|
+
</mat-form-field>
|
|
4119
|
+
<mat-form-field appearance="outline">
|
|
4120
|
+
<mat-label>Erro ao criar</mat-label>
|
|
4121
|
+
<input
|
|
4122
|
+
matInput
|
|
4123
|
+
[value]="messagesModel.createRegistryError || ''"
|
|
4124
|
+
(input)="updateMessage('createRegistryError', $any($event.target).value)"
|
|
4125
|
+
/>
|
|
4126
|
+
</mat-form-field>
|
|
4127
|
+
<mat-form-field appearance="outline">
|
|
4128
|
+
<mat-label>Sucesso ao atualizar</mat-label>
|
|
4129
|
+
<input
|
|
4130
|
+
matInput
|
|
4131
|
+
[value]="messagesModel.updateRegistrySuccess || ''"
|
|
4132
|
+
(input)="updateMessage('updateRegistrySuccess', $any($event.target).value)"
|
|
4133
|
+
/>
|
|
4134
|
+
</mat-form-field>
|
|
4135
|
+
<mat-form-field appearance="outline">
|
|
4136
|
+
<mat-label>Erro ao atualizar</mat-label>
|
|
4137
|
+
<input
|
|
4138
|
+
matInput
|
|
4139
|
+
[value]="messagesModel.updateRegistryError || ''"
|
|
4140
|
+
(input)="updateMessage('updateRegistryError', $any($event.target).value)"
|
|
4141
|
+
/>
|
|
4142
|
+
</mat-form-field>
|
|
4143
|
+
</div>
|
|
4144
|
+
</section>
|
|
4145
|
+
|
|
4146
|
+
<section class="mf-section">
|
|
4147
|
+
<div class="mf-section__header">
|
|
4148
|
+
<div>
|
|
4149
|
+
<h3>Confirmações</h3>
|
|
4150
|
+
<p class="mf-help">Textos mostrados antes de ações críticas.</p>
|
|
4151
|
+
</div>
|
|
4152
|
+
</div>
|
|
4153
|
+
<div class="mf-messages-grid">
|
|
4154
|
+
<mat-form-field appearance="outline">
|
|
4155
|
+
<mat-label>Enviar</mat-label>
|
|
4156
|
+
<input
|
|
4157
|
+
matInput
|
|
4158
|
+
[value]="messagesModel.confirmations?.submit || ''"
|
|
4159
|
+
(input)="updateConfirmation('submit', $any($event.target).value)"
|
|
4160
|
+
/>
|
|
4161
|
+
</mat-form-field>
|
|
4162
|
+
<mat-form-field appearance="outline">
|
|
4163
|
+
<mat-label>Cancelar</mat-label>
|
|
4164
|
+
<input
|
|
4165
|
+
matInput
|
|
4166
|
+
[value]="messagesModel.confirmations?.cancel || ''"
|
|
4167
|
+
(input)="updateConfirmation('cancel', $any($event.target).value)"
|
|
4168
|
+
/>
|
|
4169
|
+
</mat-form-field>
|
|
4170
|
+
<mat-form-field appearance="outline">
|
|
4171
|
+
<mat-label>Resetar</mat-label>
|
|
4172
|
+
<input
|
|
4173
|
+
matInput
|
|
4174
|
+
[value]="messagesModel.confirmations?.reset || ''"
|
|
4175
|
+
(input)="updateConfirmation('reset', $any($event.target).value)"
|
|
4176
|
+
/>
|
|
4177
|
+
</mat-form-field>
|
|
4178
|
+
</div>
|
|
4179
|
+
</section>
|
|
4180
|
+
|
|
4181
|
+
<section class="mf-section">
|
|
4182
|
+
<div class="mf-section__header">
|
|
4183
|
+
<div>
|
|
4184
|
+
<h3>Loading</h3>
|
|
4185
|
+
<p class="mf-help">Mensagens exibidas durante operações.</p>
|
|
4186
|
+
</div>
|
|
4187
|
+
</div>
|
|
4188
|
+
<div class="mf-messages-grid">
|
|
4189
|
+
<mat-form-field appearance="outline">
|
|
4190
|
+
<mat-label>Salvar</mat-label>
|
|
4191
|
+
<input
|
|
4192
|
+
matInput
|
|
4193
|
+
[value]="messagesModel.loading?.submit || ''"
|
|
4194
|
+
(input)="updateLoading('submit', $any($event.target).value)"
|
|
4195
|
+
/>
|
|
4196
|
+
</mat-form-field>
|
|
4197
|
+
<mat-form-field appearance="outline">
|
|
4198
|
+
<mat-label>Cancelar</mat-label>
|
|
4199
|
+
<input
|
|
4200
|
+
matInput
|
|
4201
|
+
[value]="messagesModel.loading?.cancel || ''"
|
|
4202
|
+
(input)="updateLoading('cancel', $any($event.target).value)"
|
|
4203
|
+
/>
|
|
4204
|
+
</mat-form-field>
|
|
4205
|
+
<mat-form-field appearance="outline">
|
|
4206
|
+
<mat-label>Resetar</mat-label>
|
|
4207
|
+
<input
|
|
4208
|
+
matInput
|
|
4209
|
+
[value]="messagesModel.loading?.reset || ''"
|
|
4210
|
+
(input)="updateLoading('reset', $any($event.target).value)"
|
|
4211
|
+
/>
|
|
4212
|
+
</mat-form-field>
|
|
4213
|
+
</div>
|
|
4214
|
+
</section>
|
|
4215
|
+
|
|
4216
|
+
<section class="mf-section">
|
|
4217
|
+
<div class="mf-section__header">
|
|
4218
|
+
<div>
|
|
4219
|
+
<h3>Mensagens por ação customizada</h3>
|
|
4220
|
+
<p class="mf-help">Personalize textos por ID de ação.</p>
|
|
4221
|
+
</div>
|
|
4222
|
+
</div>
|
|
4223
|
+
<div class="mf-custom-messages">
|
|
4224
|
+
@if (actionsModel.custom?.length) {
|
|
4225
|
+
@for (action of actionsModel.custom || []; track action.id) {
|
|
4226
|
+
<div class="mf-custom-message-card">
|
|
4227
|
+
<div class="mf-custom-card__title">{{ action.label || action.id }}</div>
|
|
4228
|
+
<div class="mf-custom-card__grid">
|
|
4229
|
+
<mat-form-field appearance="outline">
|
|
4230
|
+
<mat-label>Confirmação</mat-label>
|
|
4231
|
+
<input
|
|
4232
|
+
matInput
|
|
4233
|
+
[value]="messagesModel.customActions?.[action.id || '']?.confirmation || ''"
|
|
4234
|
+
(input)="updateCustomMessage(action, 'confirmation', $any($event.target).value)"
|
|
4235
|
+
/>
|
|
4236
|
+
</mat-form-field>
|
|
4237
|
+
<mat-form-field appearance="outline">
|
|
4238
|
+
<mat-label>Sucesso</mat-label>
|
|
4239
|
+
<input
|
|
4240
|
+
matInput
|
|
4241
|
+
[value]="messagesModel.customActions?.[action.id || '']?.success || ''"
|
|
4242
|
+
(input)="updateCustomMessage(action, 'success', $any($event.target).value)"
|
|
4243
|
+
/>
|
|
4244
|
+
</mat-form-field>
|
|
4245
|
+
<mat-form-field appearance="outline">
|
|
4246
|
+
<mat-label>Erro</mat-label>
|
|
4247
|
+
<input
|
|
4248
|
+
matInput
|
|
4249
|
+
[value]="messagesModel.customActions?.[action.id || '']?.error || ''"
|
|
4250
|
+
(input)="updateCustomMessage(action, 'error', $any($event.target).value)"
|
|
4251
|
+
/>
|
|
4252
|
+
</mat-form-field>
|
|
4253
|
+
</div>
|
|
4254
|
+
</div>
|
|
4255
|
+
}
|
|
4256
|
+
} @else {
|
|
4257
|
+
<p class="mf-empty">Cadastre ações customizadas para configurar mensagens específicas.</p>
|
|
4258
|
+
}
|
|
4259
|
+
</div>
|
|
4260
|
+
</section>
|
|
1483
4261
|
</div>
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
4262
|
+
</mat-tab>
|
|
4263
|
+
|
|
4264
|
+
<mat-tab label="Comportamento">
|
|
4265
|
+
<div class="mf-tab">
|
|
4266
|
+
<section class="mf-section">
|
|
4267
|
+
<div class="mf-section__header">
|
|
4268
|
+
<div>
|
|
4269
|
+
<h3>Comportamento do formulário</h3>
|
|
4270
|
+
<p class="mf-help">Preferências aplicadas pelo host ou runtime.</p>
|
|
4271
|
+
</div>
|
|
4272
|
+
</div>
|
|
4273
|
+
<div class="mf-note">Estas opções são persistidas no config; o host decide como aplicar.</div>
|
|
4274
|
+
<div class="mf-toggle-grid">
|
|
4275
|
+
<mat-slide-toggle
|
|
4276
|
+
[checked]="!!behaviorModel.confirmOnUnsavedChanges"
|
|
4277
|
+
(change)="updateBehavior('confirmOnUnsavedChanges', $event.checked)"
|
|
4278
|
+
>
|
|
4279
|
+
Confirmar ao sair com alterações não salvas
|
|
4280
|
+
</mat-slide-toggle>
|
|
4281
|
+
<mat-slide-toggle
|
|
4282
|
+
[checked]="!!behaviorModel.trackHistory"
|
|
4283
|
+
(change)="updateBehavior('trackHistory', $event.checked)"
|
|
4284
|
+
>
|
|
4285
|
+
Rastrear histórico de alterações
|
|
4286
|
+
</mat-slide-toggle>
|
|
4287
|
+
<mat-slide-toggle
|
|
4288
|
+
[checked]="!!behaviorModel.focusFirstError"
|
|
4289
|
+
(change)="updateBehavior('focusFirstError', $event.checked)"
|
|
4290
|
+
>
|
|
4291
|
+
Focar no primeiro erro ao submeter
|
|
4292
|
+
</mat-slide-toggle>
|
|
4293
|
+
<mat-slide-toggle
|
|
4294
|
+
[checked]="!!behaviorModel.scrollToErrors"
|
|
4295
|
+
(change)="updateBehavior('scrollToErrors', $event.checked)"
|
|
4296
|
+
>
|
|
4297
|
+
Rolar até os erros ao submeter
|
|
4298
|
+
</mat-slide-toggle>
|
|
4299
|
+
<mat-slide-toggle
|
|
4300
|
+
[checked]="!!behaviorModel.clearAfterSave"
|
|
4301
|
+
(change)="updateBehavior('clearAfterSave', $event.checked)"
|
|
4302
|
+
>
|
|
4303
|
+
Limpar formulário após salvar
|
|
4304
|
+
</mat-slide-toggle>
|
|
4305
|
+
<mat-slide-toggle
|
|
4306
|
+
[checked]="!!behaviorModel.reactiveValidation"
|
|
4307
|
+
(change)="updateBehavior('reactiveValidation', $event.checked)"
|
|
4308
|
+
>
|
|
4309
|
+
Validação reativa
|
|
4310
|
+
</mat-slide-toggle>
|
|
4311
|
+
</div>
|
|
4312
|
+
<div class="mf-actions-layout">
|
|
4313
|
+
<mat-form-field appearance="outline">
|
|
4314
|
+
<mat-label>Redirecionar após salvar (URL)</mat-label>
|
|
4315
|
+
<input
|
|
4316
|
+
matInput
|
|
4317
|
+
[value]="behaviorModel.redirectAfterSave || ''"
|
|
4318
|
+
(input)="updateBehavior('redirectAfterSave', $any($event.target).value)"
|
|
4319
|
+
/>
|
|
4320
|
+
</mat-form-field>
|
|
4321
|
+
<mat-form-field appearance="outline">
|
|
4322
|
+
<mat-label>Debounce validação reativa (ms)</mat-label>
|
|
4323
|
+
<input
|
|
4324
|
+
matInput
|
|
4325
|
+
type="number"
|
|
4326
|
+
min="0"
|
|
4327
|
+
[value]="behaviorModel.reactiveValidationDebounceMs || 0"
|
|
4328
|
+
(input)="updateBehavior('reactiveValidationDebounceMs', $any($event.target).valueAsNumber || 0)"
|
|
4329
|
+
/>
|
|
4330
|
+
</mat-form-field>
|
|
4331
|
+
</div>
|
|
4332
|
+
</section>
|
|
1489
4333
|
</div>
|
|
1490
|
-
</
|
|
1491
|
-
|
|
4334
|
+
</mat-tab>
|
|
4335
|
+
|
|
4336
|
+
<mat-tab label="Dicas">
|
|
4337
|
+
<div class="mf-tab">
|
|
4338
|
+
<section class="mf-section">
|
|
4339
|
+
<div class="mf-section__header">
|
|
4340
|
+
<div>
|
|
4341
|
+
<h3>Mensagens de modo</h3>
|
|
4342
|
+
<p class="mf-help">Textos auxiliares exibidos pelo host.</p>
|
|
4343
|
+
</div>
|
|
4344
|
+
</div>
|
|
4345
|
+
<div class="mf-note">Útil para i18n e orientação do usuário.</div>
|
|
4346
|
+
<div class="mf-messages-grid">
|
|
4347
|
+
<mat-form-field appearance="outline">
|
|
4348
|
+
<mat-label>Criar</mat-label>
|
|
4349
|
+
<input
|
|
4350
|
+
matInput
|
|
4351
|
+
[value]="hintsModel.dataModes.create"
|
|
4352
|
+
(input)="updateHints('dataModes', 'create', $any($event.target).value)"
|
|
4353
|
+
/>
|
|
4354
|
+
</mat-form-field>
|
|
4355
|
+
<mat-form-field appearance="outline">
|
|
4356
|
+
<mat-label>Editar</mat-label>
|
|
4357
|
+
<input
|
|
4358
|
+
matInput
|
|
4359
|
+
[value]="hintsModel.dataModes.edit"
|
|
4360
|
+
(input)="updateHints('dataModes', 'edit', $any($event.target).value)"
|
|
4361
|
+
/>
|
|
4362
|
+
</mat-form-field>
|
|
4363
|
+
<mat-form-field appearance="outline">
|
|
4364
|
+
<mat-label>Visualizar</mat-label>
|
|
4365
|
+
<input
|
|
4366
|
+
matInput
|
|
4367
|
+
[value]="hintsModel.dataModes.view"
|
|
4368
|
+
(input)="updateHints('dataModes', 'view', $any($event.target).value)"
|
|
4369
|
+
/>
|
|
4370
|
+
</mat-form-field>
|
|
4371
|
+
</div>
|
|
4372
|
+
</section>
|
|
4373
|
+
|
|
4374
|
+
<section class="mf-section">
|
|
4375
|
+
<div class="mf-section__header">
|
|
4376
|
+
<div>
|
|
4377
|
+
<h3>Dicas de UI</h3>
|
|
4378
|
+
<p class="mf-help">Ajuda contextual para estados do formulário.</p>
|
|
4379
|
+
</div>
|
|
4380
|
+
</div>
|
|
4381
|
+
<div class="mf-messages-grid">
|
|
4382
|
+
<mat-form-field appearance="outline">
|
|
4383
|
+
<mat-label>Apresentação</mat-label>
|
|
4384
|
+
<textarea
|
|
4385
|
+
matInput
|
|
4386
|
+
rows="2"
|
|
4387
|
+
[value]="hintsModel.uiModes.presentation"
|
|
4388
|
+
(input)="updateHints('uiModes', 'presentation', $any($event.target).value)"
|
|
4389
|
+
></textarea>
|
|
4390
|
+
</mat-form-field>
|
|
4391
|
+
<mat-form-field appearance="outline">
|
|
4392
|
+
<mat-label>Somente leitura</mat-label>
|
|
4393
|
+
<textarea
|
|
4394
|
+
matInput
|
|
4395
|
+
rows="2"
|
|
4396
|
+
[value]="hintsModel.uiModes.readonly"
|
|
4397
|
+
(input)="updateHints('uiModes', 'readonly', $any($event.target).value)"
|
|
4398
|
+
></textarea>
|
|
4399
|
+
</mat-form-field>
|
|
4400
|
+
<mat-form-field appearance="outline">
|
|
4401
|
+
<mat-label>Desabilitado</mat-label>
|
|
4402
|
+
<textarea
|
|
4403
|
+
matInput
|
|
4404
|
+
rows="2"
|
|
4405
|
+
[value]="hintsModel.uiModes.disabled"
|
|
4406
|
+
(input)="updateHints('uiModes', 'disabled', $any($event.target).value)"
|
|
4407
|
+
></textarea>
|
|
4408
|
+
</mat-form-field>
|
|
4409
|
+
<mat-form-field appearance="outline">
|
|
4410
|
+
<mat-label>Visível</mat-label>
|
|
4411
|
+
<textarea
|
|
4412
|
+
matInput
|
|
4413
|
+
rows="2"
|
|
4414
|
+
[value]="hintsModel.uiModes.visible"
|
|
4415
|
+
(input)="updateHints('uiModes', 'visible', $any($event.target).value)"
|
|
4416
|
+
></textarea>
|
|
4417
|
+
</mat-form-field>
|
|
4418
|
+
</div>
|
|
4419
|
+
</section>
|
|
4420
|
+
</div>
|
|
4421
|
+
</mat-tab>
|
|
4422
|
+
|
|
4423
|
+
<mat-tab label="Hooks">
|
|
4424
|
+
<div class="mf-tab">
|
|
4425
|
+
<section class="mf-section">
|
|
4426
|
+
<div class="mf-section__header">
|
|
4427
|
+
<div>
|
|
4428
|
+
<h3>Hooks de ciclo de vida</h3>
|
|
4429
|
+
<p class="mf-help">Defina ações automatizadas por estágio.</p>
|
|
4430
|
+
</div>
|
|
4431
|
+
<div class="mf-actions-row">
|
|
4432
|
+
<button mat-stroked-button type="button" (click)="resetHooks()">
|
|
4433
|
+
Restaurar
|
|
4434
|
+
</button>
|
|
4435
|
+
<button mat-flat-button color="primary" type="button" (click)="applyHooks()">
|
|
4436
|
+
Aplicar hooks
|
|
4437
|
+
</button>
|
|
4438
|
+
</div>
|
|
4439
|
+
</div>
|
|
4440
|
+
<div class="mf-note">Executa apenas se o host registrar hooks disponíveis.</div>
|
|
4441
|
+
<div class="mf-hooks-grid">
|
|
4442
|
+
@for (stage of hookStages; track stage) {
|
|
4443
|
+
<mat-form-field appearance="outline" class="mf-hook-field">
|
|
4444
|
+
<mat-label>{{ hookLabel(stage) }}</mat-label>
|
|
4445
|
+
<textarea
|
|
4446
|
+
matInput
|
|
4447
|
+
rows="4"
|
|
4448
|
+
[value]="hooksText[stage]"
|
|
4449
|
+
(input)="onHookTextChange(stage, $any($event.target).value)"
|
|
4450
|
+
placeholder='[{ "id": "notifySuccess", "priority": 0, "args": {} }]'
|
|
4451
|
+
></textarea>
|
|
4452
|
+
<button
|
|
4453
|
+
mat-icon-button
|
|
4454
|
+
matSuffix
|
|
4455
|
+
class="help-icon-button"
|
|
4456
|
+
type="button"
|
|
4457
|
+
[matTooltip]="'Array JSON de hooks para ' + stage + ' (id, priority, timeoutMs, args).'"
|
|
4458
|
+
matTooltipPosition="above"
|
|
4459
|
+
>
|
|
4460
|
+
<mat-icon>help_outline</mat-icon>
|
|
4461
|
+
</button>
|
|
4462
|
+
@if (hooksErrors[stage]) {
|
|
4463
|
+
<mat-error>{{ hooksErrors[stage] }}</mat-error>
|
|
4464
|
+
}
|
|
4465
|
+
</mat-form-field>
|
|
4466
|
+
}
|
|
4467
|
+
</div>
|
|
4468
|
+
</section>
|
|
4469
|
+
</div>
|
|
4470
|
+
</mat-tab>
|
|
4471
|
+
|
|
4472
|
+
<mat-tab label="Regras">
|
|
4473
|
+
<div class="mf-tab">
|
|
4474
|
+
<section class="mf-section">
|
|
4475
|
+
<div class="mf-section__header">
|
|
4476
|
+
<div>
|
|
4477
|
+
<h3>Regras de layout</h3>
|
|
4478
|
+
<p class="mf-help">Regras avançadas para visibilidade e comportamento.</p>
|
|
4479
|
+
</div>
|
|
4480
|
+
<div class="mf-actions-row">
|
|
4481
|
+
<button mat-flat-button color="primary" type="button" (click)="applyRules()">
|
|
4482
|
+
Aplicar regras
|
|
4483
|
+
</button>
|
|
4484
|
+
</div>
|
|
4485
|
+
</div>
|
|
4486
|
+
<div class="mf-note">Persistido no config; a execução depende do host.</div>
|
|
4487
|
+
<mat-form-field appearance="outline">
|
|
4488
|
+
<mat-label>formRules (JSON)</mat-label>
|
|
4489
|
+
<textarea
|
|
4490
|
+
matInput
|
|
4491
|
+
rows="6"
|
|
4492
|
+
[value]="formRulesText"
|
|
4493
|
+
(input)="onRulesTextChange($any($event.target).value)"
|
|
4494
|
+
placeholder='[{"id":"rule-1","name":"Obrigatoriedade","targetType":"field","targets":["campo"],"effect":{"condition":"{campo} != null","properties":{"required":true}}}]'
|
|
4495
|
+
></textarea>
|
|
4496
|
+
<button
|
|
4497
|
+
mat-icon-button
|
|
4498
|
+
matSuffix
|
|
4499
|
+
class="help-icon-button"
|
|
4500
|
+
type="button"
|
|
4501
|
+
[matTooltip]="'Array JSON com regras (id, targetType, targets, effect).'"
|
|
4502
|
+
matTooltipPosition="above"
|
|
4503
|
+
>
|
|
4504
|
+
<mat-icon>help_outline</mat-icon>
|
|
4505
|
+
</button>
|
|
4506
|
+
@if (formRulesError) {
|
|
4507
|
+
<mat-error>{{ formRulesError }}</mat-error>
|
|
4508
|
+
}
|
|
4509
|
+
</mat-form-field>
|
|
4510
|
+
<mat-form-field appearance="outline">
|
|
4511
|
+
<mat-label>formRulesState (JSON)</mat-label>
|
|
4512
|
+
<textarea
|
|
4513
|
+
matInput
|
|
4514
|
+
rows="6"
|
|
4515
|
+
[value]="formRulesStateText"
|
|
4516
|
+
(input)="onRulesStateTextChange($any($event.target).value)"
|
|
4517
|
+
placeholder='{"nodes":[],"edges":[]}'
|
|
4518
|
+
></textarea>
|
|
4519
|
+
<button
|
|
4520
|
+
mat-icon-button
|
|
4521
|
+
matSuffix
|
|
4522
|
+
class="help-icon-button"
|
|
4523
|
+
type="button"
|
|
4524
|
+
[matTooltip]="'Estado bruto do editor visual (opcional).'"
|
|
4525
|
+
matTooltipPosition="above"
|
|
4526
|
+
>
|
|
4527
|
+
<mat-icon>help_outline</mat-icon>
|
|
4528
|
+
</button>
|
|
4529
|
+
@if (formRulesStateError) {
|
|
4530
|
+
<mat-error>{{ formRulesStateError }}</mat-error>
|
|
4531
|
+
}
|
|
4532
|
+
</mat-form-field>
|
|
4533
|
+
<div class="mf-actions-row mf-actions-row--end">
|
|
4534
|
+
<button mat-flat-button color="primary" type="button" (click)="applyRules()">
|
|
4535
|
+
Aplicar regras
|
|
4536
|
+
</button>
|
|
4537
|
+
</div>
|
|
4538
|
+
</section>
|
|
4539
|
+
</div>
|
|
4540
|
+
</mat-tab>
|
|
4541
|
+
|
|
4542
|
+
<mat-tab label="Cascatas">
|
|
4543
|
+
<div class="mf-tab">
|
|
4544
|
+
<section class="mf-section mf-section--full">
|
|
4545
|
+
<div class="mf-section__header">
|
|
4546
|
+
<div>
|
|
4547
|
+
<h3>Dependências entre campos</h3>
|
|
4548
|
+
<p class="mf-help">Configure cascatas nativas baseadas em metadados.</p>
|
|
4549
|
+
</div>
|
|
4550
|
+
</div>
|
|
4551
|
+
<div class="mf-note">As alterações atualizam metadados e são salvas no draft.</div>
|
|
4552
|
+
<praxis-cascade-manager-tab
|
|
4553
|
+
[fields]="cascadeFields"
|
|
4554
|
+
(apply)="applyCascadePatch($event)"
|
|
4555
|
+
></praxis-cascade-manager-tab>
|
|
4556
|
+
</section>
|
|
4557
|
+
</div>
|
|
4558
|
+
</mat-tab>
|
|
4559
|
+
</mat-tab-group>
|
|
1492
4560
|
</div>
|
|
1493
|
-
`, styles: [".mf-editor{display:grid;gap:
|
|
4561
|
+
`, styles: [".mf-editor{display:grid;gap:16px;padding:16px;color:var(--md-sys-color-on-surface)}.mf-editor__toolbar{display:flex;align-items:center;gap:12px;padding:12px 14px;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;background:var(--md-sys-color-surface-container)}.mf-editor__title{display:flex;flex-direction:column;gap:2px;font-weight:600}.mf-editor__subtitle{font-weight:400;color:var(--md-sys-color-on-surface-variant)}.mf-editor__toolbar .spacer{flex:1}.mf-tabs{min-height:420px}.mf-tab{display:grid;gap:16px;padding:14px 6px 0}.mf-editor__list{display:grid;gap:8px;overflow-x:auto}.mf-editor__row{display:grid;grid-template-columns:2.2fr 2fr 1fr repeat(4,minmax(90px,.8fr));align-items:center;gap:8px;padding:10px 12px;border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;background:var(--md-sys-color-surface);min-width:720px}.mf-editor__row--head{font-weight:600;color:var(--md-sys-color-on-surface);background:var(--md-sys-color-surface-container)}.mf-editor__filter{display:inline-flex;align-items:center;gap:8px;font-weight:500}.mf-editor__filter input{accent-color:var(--md-sys-color-primary)}.col code{display:inline-block;font-family:inherit;font-size:.85rem;padding:2px 8px;border-radius:6px;background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface-variant)}.mf-btn{min-height:36px;padding:0 12px;border-radius:8px;border:1px solid var(--md-sys-color-outline);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);cursor:pointer;transition:background-color .18s ease,border-color .18s ease,color .18s ease}.mf-btn:hover{background:var(--md-sys-color-surface-container)}.mf-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.toggle{display:inline-flex;align-items:center;gap:6px;color:var(--md-sys-color-on-surface-variant)}.mf-section{display:grid;gap:12px;padding:16px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface)}.mf-section--full{padding:10px}.mf-section__header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.mf-section__header h3{margin:0;font-size:1.05rem;font-weight:600}.mf-help{margin:6px 0 0;font-size:.9rem;color:var(--md-sys-color-on-surface-variant)}.mf-note{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);font-size:.9rem}.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;margin-right:-4px}.help-icon-button mat-icon{font-size:18px;width:18px;height:18px}.mat-mdc-form-field-icon-suffix{align-self:center}.mf-actions-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px}.mf-action-card,.mf-custom-card,.mf-custom-message-card{display:grid;gap:10px;padding:14px;border-radius:10px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high)}.mf-action-card__title,.mf-custom-card__title{font-weight:600;color:var(--md-sys-color-on-surface)}.mf-actions-layout{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;align-items:center}.mf-toggle-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px;align-items:start}.mf-hooks-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px}.mf-hook-field textarea{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:.85rem}.mf-actions-row{display:inline-flex;gap:8px;align-items:center}.mf-actions-row--end{justify-content:flex-end;width:100%}.mf-custom-actions,.mf-custom-messages{display:grid;gap:12px}.mf-custom-card__header{display:flex;align-items:center;justify-content:space-between;gap:8px}.mf-custom-card__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;align-items:center}.mf-messages-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px}.mf-empty{margin:0;color:var(--md-sys-color-on-surface-variant)}\n"] }]
|
|
1494
4562
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
1495
4563
|
type: Inject,
|
|
1496
4564
|
args: [SETTINGS_PANEL_DATA]
|
|
1497
|
-
}] }, { type:
|
|
4565
|
+
}] }, { type: i10.SettingsPanelRef, decorators: [{
|
|
1498
4566
|
type: Inject,
|
|
1499
4567
|
args: [SETTINGS_PANEL_REF]
|
|
1500
4568
|
}] }] });
|
|
4569
|
+
const DEFAULT_ACTIONS = {
|
|
4570
|
+
submit: makeAction({ id: 'submit', label: 'Salvar', visible: true, type: 'submit', color: 'primary' }),
|
|
4571
|
+
cancel: makeAction({ id: 'cancel', label: 'Cancelar', visible: false, type: 'button' }),
|
|
4572
|
+
reset: makeAction({ id: 'reset', label: 'Restaurar', visible: true, type: 'reset' }),
|
|
4573
|
+
custom: [],
|
|
4574
|
+
position: 'right',
|
|
4575
|
+
orientation: 'horizontal',
|
|
4576
|
+
spacing: 'normal',
|
|
4577
|
+
};
|
|
4578
|
+
function makeAction(action, index) {
|
|
4579
|
+
const fallbackId = typeof index === 'number' ? `custom-${index + 1}` : 'action';
|
|
4580
|
+
return {
|
|
4581
|
+
id: action.id ?? fallbackId,
|
|
4582
|
+
label: action.label ?? 'Ação',
|
|
4583
|
+
visible: action.visible !== false,
|
|
4584
|
+
type: action.type ?? 'button',
|
|
4585
|
+
color: action.color,
|
|
4586
|
+
disabled: action.disabled,
|
|
4587
|
+
icon: action.icon,
|
|
4588
|
+
action: action.action,
|
|
4589
|
+
tooltip: action.tooltip,
|
|
4590
|
+
variant: action.variant,
|
|
4591
|
+
size: action.size,
|
|
4592
|
+
loading: action.loading,
|
|
4593
|
+
shortcut: action.shortcut,
|
|
4594
|
+
};
|
|
4595
|
+
}
|
|
4596
|
+
function mergeAction(base, override) {
|
|
4597
|
+
if (!override) {
|
|
4598
|
+
return base;
|
|
4599
|
+
}
|
|
4600
|
+
return {
|
|
4601
|
+
...base,
|
|
4602
|
+
...override,
|
|
4603
|
+
visible: override.visible !== undefined ? override.visible : base.visible,
|
|
4604
|
+
};
|
|
4605
|
+
}
|
|
4606
|
+
function createDefaultHints() {
|
|
4607
|
+
return {
|
|
4608
|
+
dataModes: { create: '', edit: '', view: '' },
|
|
4609
|
+
uiModes: { presentation: '', readonly: '', disabled: '', visible: '' },
|
|
4610
|
+
};
|
|
4611
|
+
}
|
|
4612
|
+
function createHints(source) {
|
|
4613
|
+
if (!source) {
|
|
4614
|
+
return createDefaultHints();
|
|
4615
|
+
}
|
|
4616
|
+
return {
|
|
4617
|
+
dataModes: {
|
|
4618
|
+
create: source.dataModes?.create ?? '',
|
|
4619
|
+
edit: source.dataModes?.edit ?? '',
|
|
4620
|
+
view: source.dataModes?.view ?? '',
|
|
4621
|
+
},
|
|
4622
|
+
uiModes: {
|
|
4623
|
+
presentation: source.uiModes?.presentation ?? '',
|
|
4624
|
+
readonly: source.uiModes?.readonly ?? '',
|
|
4625
|
+
disabled: source.uiModes?.disabled ?? '',
|
|
4626
|
+
visible: source.uiModes?.visible ?? '',
|
|
4627
|
+
},
|
|
4628
|
+
};
|
|
4629
|
+
}
|
|
4630
|
+
function createDefaultHooksText() {
|
|
4631
|
+
return {
|
|
4632
|
+
beforeInit: '[]',
|
|
4633
|
+
afterInit: '[]',
|
|
4634
|
+
beforeValidate: '[]',
|
|
4635
|
+
afterValidate: '[]',
|
|
4636
|
+
beforeSubmit: '[]',
|
|
4637
|
+
afterSubmit: '[]',
|
|
4638
|
+
onError: '[]',
|
|
4639
|
+
};
|
|
4640
|
+
}
|
|
4641
|
+
function buildHooksText(source) {
|
|
4642
|
+
const base = createDefaultHooksText();
|
|
4643
|
+
if (!source)
|
|
4644
|
+
return base;
|
|
4645
|
+
for (const stage of Object.keys(base)) {
|
|
4646
|
+
const list = source[stage];
|
|
4647
|
+
base[stage] = list && list.length ? JSON.stringify(list, null, 2) : '[]';
|
|
4648
|
+
}
|
|
4649
|
+
return base;
|
|
4650
|
+
}
|
|
4651
|
+
function stringifyJson(value, pretty) {
|
|
4652
|
+
if (value === undefined)
|
|
4653
|
+
return '';
|
|
4654
|
+
try {
|
|
4655
|
+
return JSON.stringify(value, null, pretty ? 2 : 0);
|
|
4656
|
+
}
|
|
4657
|
+
catch {
|
|
4658
|
+
return '';
|
|
4659
|
+
}
|
|
4660
|
+
}
|
|
1501
4661
|
|
|
1502
4662
|
var manualFormConfigEditor_component = /*#__PURE__*/Object.freeze({
|
|
1503
4663
|
__proto__: null,
|
|
1504
4664
|
ManualFormConfigEditorComponent: ManualFormConfigEditorComponent
|
|
1505
4665
|
});
|
|
1506
4666
|
|
|
4667
|
+
let nextDocExampleId = 0;
|
|
4668
|
+
class ManualFormDocExampleComponent {
|
|
4669
|
+
title = input('Exemplo didatico', ...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
4670
|
+
subtitle = input(null, ...(ngDevMode ? [{ debugName: "subtitle" }] : []));
|
|
4671
|
+
level = input('Basico', ...(ngDevMode ? [{ debugName: "level" }] : []));
|
|
4672
|
+
bannerTitle = input(null, ...(ngDevMode ? [{ debugName: "bannerTitle" }] : []));
|
|
4673
|
+
bannerDescription = input(null, ...(ngDevMode ? [{ debugName: "bannerDescription" }] : []));
|
|
4674
|
+
templateCode = input('', ...(ngDevMode ? [{ debugName: "templateCode" }] : []));
|
|
4675
|
+
tsCode = input('', ...(ngDevMode ? [{ debugName: "tsCode" }] : []));
|
|
4676
|
+
configCode = input('', ...(ngDevMode ? [{ debugName: "configCode" }] : []));
|
|
4677
|
+
customizationEnabled = input(false, ...(ngDevMode ? [{ debugName: "customizationEnabled" }] : []));
|
|
4678
|
+
showCustomizationToggle = input(true, ...(ngDevMode ? [{ debugName: "showCustomizationToggle" }] : []));
|
|
4679
|
+
important = input(null, ...(ngDevMode ? [{ debugName: "important" }] : []));
|
|
4680
|
+
note = input(null, ...(ngDevMode ? [{ debugName: "note" }] : []));
|
|
4681
|
+
tip = input(null, ...(ngDevMode ? [{ debugName: "tip" }] : []));
|
|
4682
|
+
activeTab = signal('live', ...(ngDevMode ? [{ debugName: "activeTab" }] : []));
|
|
4683
|
+
copyStatus = signal('', ...(ngDevMode ? [{ debugName: "copyStatus" }] : []));
|
|
4684
|
+
tabs = [
|
|
4685
|
+
{ id: 'live', label: 'Live', hint: 'Renderizacao do exemplo' },
|
|
4686
|
+
{ id: 'template', label: 'Template', hint: 'Markup HTML do exemplo' },
|
|
4687
|
+
{ id: 'ts', label: 'TS', hint: 'Logica e estado do host' },
|
|
4688
|
+
{ id: 'config', label: 'Config', hint: 'JSON de configuracao do componente' },
|
|
4689
|
+
];
|
|
4690
|
+
selectedSnippet = computed(() => {
|
|
4691
|
+
switch (this.activeTab()) {
|
|
4692
|
+
case 'template':
|
|
4693
|
+
return this.templateCode();
|
|
4694
|
+
case 'ts':
|
|
4695
|
+
return this.tsCode();
|
|
4696
|
+
case 'config':
|
|
4697
|
+
return this.configCode();
|
|
4698
|
+
default:
|
|
4699
|
+
return '';
|
|
4700
|
+
}
|
|
4701
|
+
}, ...(ngDevMode ? [{ debugName: "selectedSnippet" }] : []));
|
|
4702
|
+
selectedSnippetLabel = computed(() => {
|
|
4703
|
+
switch (this.activeTab()) {
|
|
4704
|
+
case 'template':
|
|
4705
|
+
return 'Template';
|
|
4706
|
+
case 'ts':
|
|
4707
|
+
return 'TypeScript';
|
|
4708
|
+
case 'config':
|
|
4709
|
+
return 'Config';
|
|
4710
|
+
default:
|
|
4711
|
+
return 'Live';
|
|
4712
|
+
}
|
|
4713
|
+
}, ...(ngDevMode ? [{ debugName: "selectedSnippetLabel" }] : []));
|
|
4714
|
+
canCopySnippet = computed(() => this.selectedSnippet().trim().length > 0, ...(ngDevMode ? [{ debugName: "canCopySnippet" }] : []));
|
|
4715
|
+
tabChange = output();
|
|
4716
|
+
customizationEnabledChange = output();
|
|
4717
|
+
codeCopied = output();
|
|
4718
|
+
idPrefix = `pdx-manual-form-doc-example-${nextDocExampleId++}`;
|
|
4719
|
+
copyStatusTimer = null;
|
|
4720
|
+
ngOnDestroy() {
|
|
4721
|
+
this.clearCopyStatusTimer();
|
|
4722
|
+
}
|
|
4723
|
+
setTab(tab) {
|
|
4724
|
+
if (this.activeTab() === tab) {
|
|
4725
|
+
return;
|
|
4726
|
+
}
|
|
4727
|
+
this.activeTab.set(tab);
|
|
4728
|
+
this.setCopyStatus('');
|
|
4729
|
+
this.tabChange.emit(tab);
|
|
4730
|
+
}
|
|
4731
|
+
toggleCustomization() {
|
|
4732
|
+
this.customizationEnabledChange.emit(!this.customizationEnabled());
|
|
4733
|
+
}
|
|
4734
|
+
tabId(tab) {
|
|
4735
|
+
return `${this.idPrefix}-tab-${tab}`;
|
|
4736
|
+
}
|
|
4737
|
+
panelId(tab) {
|
|
4738
|
+
return `${this.idPrefix}-panel-${tab}`;
|
|
4739
|
+
}
|
|
4740
|
+
async copyActiveSnippet() {
|
|
4741
|
+
const tab = this.activeTab();
|
|
4742
|
+
const content = this.selectedSnippet();
|
|
4743
|
+
if (!content.trim()) {
|
|
4744
|
+
this.setCopyStatus('Sem conteudo para copiar');
|
|
4745
|
+
this.codeCopied.emit({ tab, success: false });
|
|
4746
|
+
return;
|
|
4747
|
+
}
|
|
4748
|
+
try {
|
|
4749
|
+
const clipboard = globalThis.navigator?.clipboard;
|
|
4750
|
+
if (!clipboard?.writeText) {
|
|
4751
|
+
this.setCopyStatus('Clipboard indisponivel');
|
|
4752
|
+
this.codeCopied.emit({ tab, success: false });
|
|
4753
|
+
return;
|
|
4754
|
+
}
|
|
4755
|
+
await clipboard.writeText(content);
|
|
4756
|
+
this.setCopyStatus('Copiado');
|
|
4757
|
+
this.codeCopied.emit({ tab, success: true });
|
|
4758
|
+
}
|
|
4759
|
+
catch {
|
|
4760
|
+
this.setCopyStatus('Falha ao copiar');
|
|
4761
|
+
this.codeCopied.emit({ tab, success: false });
|
|
4762
|
+
}
|
|
4763
|
+
}
|
|
4764
|
+
setCopyStatus(status) {
|
|
4765
|
+
this.copyStatus.set(status);
|
|
4766
|
+
this.clearCopyStatusTimer();
|
|
4767
|
+
if (status) {
|
|
4768
|
+
this.copyStatusTimer = setTimeout(() => this.copyStatus.set(''), 1800);
|
|
4769
|
+
}
|
|
4770
|
+
}
|
|
4771
|
+
clearCopyStatusTimer() {
|
|
4772
|
+
if (this.copyStatusTimer) {
|
|
4773
|
+
clearTimeout(this.copyStatusTimer);
|
|
4774
|
+
this.copyStatusTimer = null;
|
|
4775
|
+
}
|
|
4776
|
+
}
|
|
4777
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFormDocExampleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4778
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: ManualFormDocExampleComponent, isStandalone: true, selector: "praxis-manual-form-doc-example", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, level: { classPropertyName: "level", publicName: "level", isSignal: true, isRequired: false, transformFunction: null }, bannerTitle: { classPropertyName: "bannerTitle", publicName: "bannerTitle", isSignal: true, isRequired: false, transformFunction: null }, bannerDescription: { classPropertyName: "bannerDescription", publicName: "bannerDescription", isSignal: true, isRequired: false, transformFunction: null }, templateCode: { classPropertyName: "templateCode", publicName: "templateCode", isSignal: true, isRequired: false, transformFunction: null }, tsCode: { classPropertyName: "tsCode", publicName: "tsCode", isSignal: true, isRequired: false, transformFunction: null }, configCode: { classPropertyName: "configCode", publicName: "configCode", isSignal: true, isRequired: false, transformFunction: null }, customizationEnabled: { classPropertyName: "customizationEnabled", publicName: "customizationEnabled", isSignal: true, isRequired: false, transformFunction: null }, showCustomizationToggle: { classPropertyName: "showCustomizationToggle", publicName: "showCustomizationToggle", isSignal: true, isRequired: false, transformFunction: null }, important: { classPropertyName: "important", publicName: "important", isSignal: true, isRequired: false, transformFunction: null }, note: { classPropertyName: "note", publicName: "note", isSignal: true, isRequired: false, transformFunction: null }, tip: { classPropertyName: "tip", publicName: "tip", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { tabChange: "tabChange", customizationEnabledChange: "customizationEnabledChange", codeCopied: "codeCopied" }, ngImport: i0, template: "<section class=\"doc-example\" [attr.data-level]=\"level()\">\n @if (bannerTitle() || bannerDescription()) {\n <header class=\"doc-example__banner\">\n <div class=\"doc-example__banner-copy\">\n @if (bannerTitle()) {\n <h2>{{ bannerTitle() }}</h2>\n }\n @if (bannerDescription()) {\n <p>{{ bannerDescription() }}</p>\n }\n </div>\n <div class=\"doc-example__banner-art\">\n <ng-content select=\"[exampleBannerArtwork]\"></ng-content>\n </div>\n </header>\n }\n\n <div class=\"doc-example__summary\">\n <div class=\"doc-example__summary-main\">\n <span class=\"badge badge--level\">{{ level() }}</span>\n <h3>{{ title() }}</h3>\n @if (subtitle()) {\n <p>{{ subtitle() }}</p>\n }\n </div>\n\n @if (showCustomizationToggle()) {\n <button\n type=\"button\"\n class=\"mode-toggle\"\n [attr.aria-pressed]=\"customizationEnabled()\"\n [attr.aria-label]=\"customizationEnabled() ? 'Desligar modo de customizacao' : 'Ligar modo de customizacao'\"\n (click)=\"toggleCustomization()\"\n >\n {{ customizationEnabled() ? 'Customizacao: ligada' : 'Customizacao: desligada' }}\n </button>\n }\n </div>\n\n <p class=\"sr-only\" [attr.id]=\"idPrefix + '-tabs-help'\">\n Escolha entre visualizacao live, template, TypeScript ou config do exemplo.\n </p>\n\n <div class=\"doc-example__tabs\" role=\"tablist\" [attr.aria-describedby]=\"idPrefix + '-tabs-help'\">\n @for (tab of tabs; track tab.id) {\n <button\n type=\"button\"\n role=\"tab\"\n class=\"doc-example__tab\"\n [class.is-active]=\"activeTab() === tab.id\"\n [attr.id]=\"tabId(tab.id)\"\n [attr.aria-controls]=\"panelId(tab.id)\"\n [attr.aria-selected]=\"activeTab() === tab.id\"\n [attr.aria-label]=\"tab.label + ': ' + tab.hint\"\n (click)=\"setTab(tab.id)\"\n >\n {{ tab.label }}\n </button>\n }\n </div>\n\n <section\n class=\"doc-example__panel\"\n role=\"tabpanel\"\n [attr.id]=\"panelId(activeTab())\"\n [attr.aria-labelledby]=\"tabId(activeTab())\"\n >\n @if (activeTab() === 'live') {\n <div class=\"doc-example__live\">\n <ng-content select=\"[exampleLive]\"></ng-content>\n </div>\n } @else {\n <div class=\"doc-example__code-toolbar\">\n <span class=\"doc-example__code-label\">{{ selectedSnippetLabel() }}</span>\n <button type=\"button\" class=\"copy-btn\" [disabled]=\"!canCopySnippet()\" (click)=\"copyActiveSnippet()\">\n Copiar\n </button>\n @if (copyStatus()) {\n <span class=\"copy-status\" aria-live=\"polite\">{{ copyStatus() }}</span>\n }\n </div>\n <pre class=\"doc-example__code\"><code>{{ selectedSnippet() }}</code></pre>\n }\n </section>\n\n <ng-content select=\"[exampleDiagram]\"></ng-content>\n\n @if (important() || note() || tip()) {\n <div class=\"doc-example__callouts\">\n @if (important()) {\n <p class=\"callout callout--important\"><strong>IMPORTANTE:</strong> {{ important() }}</p>\n }\n @if (note()) {\n <p class=\"callout callout--note\"><strong>NOTA:</strong> {{ note() }}</p>\n }\n @if (tip()) {\n <p class=\"callout callout--tip\"><strong>DICA:</strong> {{ tip() }}</p>\n }\n </div>\n }\n</section>\n", styles: [":host{display:block}.doc-example{--doc-accent: #007a6f;--doc-surface: var(--md-sys-color-surface, #fff);--doc-outline: color-mix(in srgb, var(--doc-accent) 30%, #d6dde1);display:grid;gap:16px;padding:18px;border-radius:18px;border:1px solid var(--doc-outline);background:radial-gradient(140% 120% at 0% 0%,color-mix(in srgb,var(--doc-accent) 10%,transparent),transparent 55%),var(--doc-surface);box-shadow:0 10px 22px color-mix(in srgb,var(--doc-accent) 14%,transparent)}.doc-example__banner{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:14px;padding:16px;border-radius:14px;background:linear-gradient(130deg,color-mix(in srgb,var(--doc-accent) 22%,#10141a),color-mix(in srgb,var(--doc-accent) 10%,#1e2630));color:#f7fbff}.doc-example__banner-copy h2{margin:0 0 6px;font-size:1.35rem;line-height:1.2}.doc-example__banner-copy p{margin:0;color:color-mix(in srgb,#f7fbff 90%,transparent)}.doc-example__banner-art{align-self:center;justify-self:end}.doc-example__summary{display:flex;justify-content:space-between;gap:10px;align-items:flex-start;flex-wrap:wrap}.doc-example__summary-main{display:grid;gap:4px}.doc-example__summary-main h3{margin:0;font-size:1.1rem}.doc-example__summary-main p{margin:0;color:var(--md-sys-color-on-surface-variant, #454e59)}.badge{width:fit-content;padding:4px 10px;border-radius:999px;font-size:12px;font-weight:600;letter-spacing:.04em;text-transform:uppercase}.badge--level{background:color-mix(in srgb,var(--doc-accent) 14%,#ffffff);color:color-mix(in srgb,var(--doc-accent) 86%,#0f151b);border:1px solid color-mix(in srgb,var(--doc-accent) 40%,#cfd6dc)}.mode-toggle{min-height:36px;border-radius:10px;border:1px solid color-mix(in srgb,var(--doc-accent) 44%,#cad2d8);background:color-mix(in srgb,var(--doc-accent) 12%,#ffffff);color:color-mix(in srgb,var(--doc-accent) 84%,#182128);font-weight:600;padding:0 12px;cursor:pointer}.mode-toggle:focus-visible,.doc-example__tab:focus-visible,.copy-btn:focus-visible{outline:2px solid color-mix(in srgb,var(--doc-accent) 72%,#4d5b66);outline-offset:2px}.doc-example__tabs{display:flex;flex-wrap:wrap;gap:8px}.doc-example__tab{min-height:34px;padding:0 12px;border-radius:999px;border:1px solid color-mix(in srgb,var(--doc-accent) 30%,#cfd6dc);background:color-mix(in srgb,var(--doc-accent) 7%,#ffffff);color:var(--md-sys-color-on-surface, #171b21);font-weight:600;cursor:pointer}.doc-example__tab.is-active{border-color:color-mix(in srgb,var(--doc-accent) 62%,#8fa3b0);background:color-mix(in srgb,var(--doc-accent) 78%,#1a2732);color:#f4fbff}.doc-example__panel{border-radius:14px;border:1px solid color-mix(in srgb,var(--doc-accent) 24%,#d2d9de);background:color-mix(in srgb,var(--doc-accent) 5%,#ffffff);padding:14px}.doc-example__live{min-height:80px}.doc-example__code-toolbar{display:flex;align-items:center;gap:8px;margin-bottom:10px}.doc-example__code-label{font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--md-sys-color-on-surface-variant, #4f5861)}.copy-btn{min-height:30px;padding:0 10px;border-radius:8px;border:1px solid color-mix(in srgb,var(--doc-accent) 28%,#ccd3d8);background:#fff;cursor:pointer}.copy-btn:disabled{opacity:.6;cursor:not-allowed}.copy-status{color:color-mix(in srgb,var(--doc-accent) 84%,#1f2931);font-size:12px}.doc-example__code{margin:0;max-height:360px;overflow:auto;border-radius:10px;border:1px solid color-mix(in srgb,var(--doc-accent) 22%,#d3dbe0);background:#0f1218;color:#e9eef8;padding:12px;font-size:13px;line-height:1.55}.doc-example__callouts{display:grid;gap:10px}.callout{margin:0;padding:12px 14px;border-radius:10px;border-left:4px solid}.callout--important{border-left-color:#ff8f8f;background:#fff4f4}.callout--note{border-left-color:#91b3ff;background:#f1f6ff}.callout--tip{border-left-color:#84d8bf;background:#ecfff8}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}@media(max-width:768px){.doc-example{padding:14px}.doc-example__banner{grid-template-columns:1fr}.doc-example__banner-art{justify-self:start}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4779
|
+
}
|
|
4780
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFormDocExampleComponent, decorators: [{
|
|
4781
|
+
type: Component,
|
|
4782
|
+
args: [{ selector: 'praxis-manual-form-doc-example', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section class=\"doc-example\" [attr.data-level]=\"level()\">\n @if (bannerTitle() || bannerDescription()) {\n <header class=\"doc-example__banner\">\n <div class=\"doc-example__banner-copy\">\n @if (bannerTitle()) {\n <h2>{{ bannerTitle() }}</h2>\n }\n @if (bannerDescription()) {\n <p>{{ bannerDescription() }}</p>\n }\n </div>\n <div class=\"doc-example__banner-art\">\n <ng-content select=\"[exampleBannerArtwork]\"></ng-content>\n </div>\n </header>\n }\n\n <div class=\"doc-example__summary\">\n <div class=\"doc-example__summary-main\">\n <span class=\"badge badge--level\">{{ level() }}</span>\n <h3>{{ title() }}</h3>\n @if (subtitle()) {\n <p>{{ subtitle() }}</p>\n }\n </div>\n\n @if (showCustomizationToggle()) {\n <button\n type=\"button\"\n class=\"mode-toggle\"\n [attr.aria-pressed]=\"customizationEnabled()\"\n [attr.aria-label]=\"customizationEnabled() ? 'Desligar modo de customizacao' : 'Ligar modo de customizacao'\"\n (click)=\"toggleCustomization()\"\n >\n {{ customizationEnabled() ? 'Customizacao: ligada' : 'Customizacao: desligada' }}\n </button>\n }\n </div>\n\n <p class=\"sr-only\" [attr.id]=\"idPrefix + '-tabs-help'\">\n Escolha entre visualizacao live, template, TypeScript ou config do exemplo.\n </p>\n\n <div class=\"doc-example__tabs\" role=\"tablist\" [attr.aria-describedby]=\"idPrefix + '-tabs-help'\">\n @for (tab of tabs; track tab.id) {\n <button\n type=\"button\"\n role=\"tab\"\n class=\"doc-example__tab\"\n [class.is-active]=\"activeTab() === tab.id\"\n [attr.id]=\"tabId(tab.id)\"\n [attr.aria-controls]=\"panelId(tab.id)\"\n [attr.aria-selected]=\"activeTab() === tab.id\"\n [attr.aria-label]=\"tab.label + ': ' + tab.hint\"\n (click)=\"setTab(tab.id)\"\n >\n {{ tab.label }}\n </button>\n }\n </div>\n\n <section\n class=\"doc-example__panel\"\n role=\"tabpanel\"\n [attr.id]=\"panelId(activeTab())\"\n [attr.aria-labelledby]=\"tabId(activeTab())\"\n >\n @if (activeTab() === 'live') {\n <div class=\"doc-example__live\">\n <ng-content select=\"[exampleLive]\"></ng-content>\n </div>\n } @else {\n <div class=\"doc-example__code-toolbar\">\n <span class=\"doc-example__code-label\">{{ selectedSnippetLabel() }}</span>\n <button type=\"button\" class=\"copy-btn\" [disabled]=\"!canCopySnippet()\" (click)=\"copyActiveSnippet()\">\n Copiar\n </button>\n @if (copyStatus()) {\n <span class=\"copy-status\" aria-live=\"polite\">{{ copyStatus() }}</span>\n }\n </div>\n <pre class=\"doc-example__code\"><code>{{ selectedSnippet() }}</code></pre>\n }\n </section>\n\n <ng-content select=\"[exampleDiagram]\"></ng-content>\n\n @if (important() || note() || tip()) {\n <div class=\"doc-example__callouts\">\n @if (important()) {\n <p class=\"callout callout--important\"><strong>IMPORTANTE:</strong> {{ important() }}</p>\n }\n @if (note()) {\n <p class=\"callout callout--note\"><strong>NOTA:</strong> {{ note() }}</p>\n }\n @if (tip()) {\n <p class=\"callout callout--tip\"><strong>DICA:</strong> {{ tip() }}</p>\n }\n </div>\n }\n</section>\n", styles: [":host{display:block}.doc-example{--doc-accent: #007a6f;--doc-surface: var(--md-sys-color-surface, #fff);--doc-outline: color-mix(in srgb, var(--doc-accent) 30%, #d6dde1);display:grid;gap:16px;padding:18px;border-radius:18px;border:1px solid var(--doc-outline);background:radial-gradient(140% 120% at 0% 0%,color-mix(in srgb,var(--doc-accent) 10%,transparent),transparent 55%),var(--doc-surface);box-shadow:0 10px 22px color-mix(in srgb,var(--doc-accent) 14%,transparent)}.doc-example__banner{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:14px;padding:16px;border-radius:14px;background:linear-gradient(130deg,color-mix(in srgb,var(--doc-accent) 22%,#10141a),color-mix(in srgb,var(--doc-accent) 10%,#1e2630));color:#f7fbff}.doc-example__banner-copy h2{margin:0 0 6px;font-size:1.35rem;line-height:1.2}.doc-example__banner-copy p{margin:0;color:color-mix(in srgb,#f7fbff 90%,transparent)}.doc-example__banner-art{align-self:center;justify-self:end}.doc-example__summary{display:flex;justify-content:space-between;gap:10px;align-items:flex-start;flex-wrap:wrap}.doc-example__summary-main{display:grid;gap:4px}.doc-example__summary-main h3{margin:0;font-size:1.1rem}.doc-example__summary-main p{margin:0;color:var(--md-sys-color-on-surface-variant, #454e59)}.badge{width:fit-content;padding:4px 10px;border-radius:999px;font-size:12px;font-weight:600;letter-spacing:.04em;text-transform:uppercase}.badge--level{background:color-mix(in srgb,var(--doc-accent) 14%,#ffffff);color:color-mix(in srgb,var(--doc-accent) 86%,#0f151b);border:1px solid color-mix(in srgb,var(--doc-accent) 40%,#cfd6dc)}.mode-toggle{min-height:36px;border-radius:10px;border:1px solid color-mix(in srgb,var(--doc-accent) 44%,#cad2d8);background:color-mix(in srgb,var(--doc-accent) 12%,#ffffff);color:color-mix(in srgb,var(--doc-accent) 84%,#182128);font-weight:600;padding:0 12px;cursor:pointer}.mode-toggle:focus-visible,.doc-example__tab:focus-visible,.copy-btn:focus-visible{outline:2px solid color-mix(in srgb,var(--doc-accent) 72%,#4d5b66);outline-offset:2px}.doc-example__tabs{display:flex;flex-wrap:wrap;gap:8px}.doc-example__tab{min-height:34px;padding:0 12px;border-radius:999px;border:1px solid color-mix(in srgb,var(--doc-accent) 30%,#cfd6dc);background:color-mix(in srgb,var(--doc-accent) 7%,#ffffff);color:var(--md-sys-color-on-surface, #171b21);font-weight:600;cursor:pointer}.doc-example__tab.is-active{border-color:color-mix(in srgb,var(--doc-accent) 62%,#8fa3b0);background:color-mix(in srgb,var(--doc-accent) 78%,#1a2732);color:#f4fbff}.doc-example__panel{border-radius:14px;border:1px solid color-mix(in srgb,var(--doc-accent) 24%,#d2d9de);background:color-mix(in srgb,var(--doc-accent) 5%,#ffffff);padding:14px}.doc-example__live{min-height:80px}.doc-example__code-toolbar{display:flex;align-items:center;gap:8px;margin-bottom:10px}.doc-example__code-label{font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--md-sys-color-on-surface-variant, #4f5861)}.copy-btn{min-height:30px;padding:0 10px;border-radius:8px;border:1px solid color-mix(in srgb,var(--doc-accent) 28%,#ccd3d8);background:#fff;cursor:pointer}.copy-btn:disabled{opacity:.6;cursor:not-allowed}.copy-status{color:color-mix(in srgb,var(--doc-accent) 84%,#1f2931);font-size:12px}.doc-example__code{margin:0;max-height:360px;overflow:auto;border-radius:10px;border:1px solid color-mix(in srgb,var(--doc-accent) 22%,#d3dbe0);background:#0f1218;color:#e9eef8;padding:12px;font-size:13px;line-height:1.55}.doc-example__callouts{display:grid;gap:10px}.callout{margin:0;padding:12px 14px;border-radius:10px;border-left:4px solid}.callout--important{border-left-color:#ff8f8f;background:#fff4f4}.callout--note{border-left-color:#91b3ff;background:#f1f6ff}.callout--tip{border-left-color:#84d8bf;background:#ecfff8}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}@media(max-width:768px){.doc-example{padding:14px}.doc-example__banner{grid-template-columns:1fr}.doc-example__banner-art{justify-self:start}}\n"] }]
|
|
4783
|
+
}], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], level: [{ type: i0.Input, args: [{ isSignal: true, alias: "level", required: false }] }], bannerTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "bannerTitle", required: false }] }], bannerDescription: [{ type: i0.Input, args: [{ isSignal: true, alias: "bannerDescription", required: false }] }], templateCode: [{ type: i0.Input, args: [{ isSignal: true, alias: "templateCode", required: false }] }], tsCode: [{ type: i0.Input, args: [{ isSignal: true, alias: "tsCode", required: false }] }], configCode: [{ type: i0.Input, args: [{ isSignal: true, alias: "configCode", required: false }] }], customizationEnabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "customizationEnabled", required: false }] }], showCustomizationToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCustomizationToggle", required: false }] }], important: [{ type: i0.Input, args: [{ isSignal: true, alias: "important", required: false }] }], note: [{ type: i0.Input, args: [{ isSignal: true, alias: "note", required: false }] }], tip: [{ type: i0.Input, args: [{ isSignal: true, alias: "tip", required: false }] }], tabChange: [{ type: i0.Output, args: ["tabChange"] }], customizationEnabledChange: [{ type: i0.Output, args: ["customizationEnabledChange"] }], codeCopied: [{ type: i0.Output, args: ["codeCopied"] }] } });
|
|
4784
|
+
|
|
4785
|
+
class DocExampleMemoryStorage {
|
|
4786
|
+
map = new Map();
|
|
4787
|
+
loadConfig(key) {
|
|
4788
|
+
return of(this.map.get(key) ?? null);
|
|
4789
|
+
}
|
|
4790
|
+
saveConfig(key, config) {
|
|
4791
|
+
this.map.set(key, config);
|
|
4792
|
+
return of(void 0);
|
|
4793
|
+
}
|
|
4794
|
+
clearConfig(key) {
|
|
4795
|
+
this.map.delete(key);
|
|
4796
|
+
return of(void 0);
|
|
4797
|
+
}
|
|
4798
|
+
}
|
|
4799
|
+
class ManualFormDocExampleShowcaseComponent {
|
|
4800
|
+
customizationEnabled = signal(false, ...(ngDevMode ? [{ debugName: "customizationEnabled" }] : []));
|
|
4801
|
+
form = new FormGroup({
|
|
4802
|
+
nome: new FormControl('', { nonNullable: true, validators: [Validators.required] }),
|
|
4803
|
+
descricao: new FormControl('', { nonNullable: true }),
|
|
4804
|
+
});
|
|
4805
|
+
templateSnippet = `<praxis-manual-form [formGroup]="form" formId="doc-example-cargo">
|
|
4806
|
+
<label>
|
|
4807
|
+
<span>Nome</span>
|
|
4808
|
+
<input type="text" formControlName="nome" />
|
|
4809
|
+
</label>
|
|
4810
|
+
|
|
4811
|
+
<label>
|
|
4812
|
+
<span>Descricao</span>
|
|
4813
|
+
<textarea formControlName="descricao" rows="3"></textarea>
|
|
4814
|
+
</label>
|
|
4815
|
+
</praxis-manual-form>`;
|
|
4816
|
+
tsSnippet = `readonly customizationEnabled = signal(false);
|
|
4817
|
+
|
|
4818
|
+
readonly form = new FormGroup({
|
|
4819
|
+
nome: new FormControl('', { nonNullable: true, validators: [Validators.required] }),
|
|
4820
|
+
descricao: new FormControl('', { nonNullable: true }),
|
|
4821
|
+
});
|
|
4822
|
+
|
|
4823
|
+
setCustomizationEnabled(enabled: boolean): void {
|
|
4824
|
+
this.customizationEnabled.set(enabled);
|
|
4825
|
+
}`;
|
|
4826
|
+
configSnippet = `{
|
|
4827
|
+
"component": "praxis-manual-form-doc-example",
|
|
4828
|
+
"tabs": ["live", "template", "ts", "config"],
|
|
4829
|
+
"defaultTab": "live",
|
|
4830
|
+
"level": "Intermediario"
|
|
4831
|
+
}`;
|
|
4832
|
+
setCustomizationEnabled(enabled) {
|
|
4833
|
+
this.customizationEnabled.set(enabled);
|
|
4834
|
+
}
|
|
4835
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFormDocExampleShowcaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4836
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: ManualFormDocExampleShowcaseComponent, isStandalone: true, selector: "praxis-manual-form-doc-example-showcase", providers: [
|
|
4837
|
+
{ provide: API_URL, useValue: { default: { baseUrl: '/api' } } },
|
|
4838
|
+
{ provide: ASYNC_CONFIG_STORAGE, useFactory: () => new DocExampleMemoryStorage() },
|
|
4839
|
+
], ngImport: i0, template: `
|
|
4840
|
+
<praxis-manual-form-doc-example
|
|
4841
|
+
title="Cadastro de cargo com preview live"
|
|
4842
|
+
subtitle="Exemplo live com abas para implementação."
|
|
4843
|
+
level="Intermediario"
|
|
4844
|
+
bannerTitle="Explore o formulário antes da teoria"
|
|
4845
|
+
bannerDescription="Interaja no live e abra código apenas quando precisar."
|
|
4846
|
+
[templateCode]="templateSnippet"
|
|
4847
|
+
[tsCode]="tsSnippet"
|
|
4848
|
+
[configCode]="configSnippet"
|
|
4849
|
+
[customizationEnabled]="customizationEnabled()"
|
|
4850
|
+
important="Comece pelo live para reduzir friccao de onboarding."
|
|
4851
|
+
note="As abas preservam o estado atual do formulario."
|
|
4852
|
+
tip="Use o toggle para validar variacoes de host sem recarregar."
|
|
4853
|
+
(customizationEnabledChange)="setCustomizationEnabled($event)"
|
|
4854
|
+
>
|
|
4855
|
+
<div exampleBannerArtwork class="showcase-art" aria-hidden="true">
|
|
4856
|
+
<div class="showcase-art__chip">Live</div>
|
|
4857
|
+
<div class="showcase-art__chip">Template</div>
|
|
4858
|
+
<div class="showcase-art__chip">TS</div>
|
|
4859
|
+
<div class="showcase-art__chip">Config</div>
|
|
4860
|
+
</div>
|
|
4861
|
+
|
|
4862
|
+
<div exampleLive class="showcase-live">
|
|
4863
|
+
<p class="showcase-mode" [class.showcase-mode--enabled]="customizationEnabled()">
|
|
4864
|
+
Customizacao no host: {{ customizationEnabled() ? 'ligada' : 'desligada' }}
|
|
4865
|
+
</p>
|
|
4866
|
+
|
|
4867
|
+
<praxis-manual-form
|
|
4868
|
+
[formGroup]="form"
|
|
4869
|
+
formId="doc-example-cargo"
|
|
4870
|
+
formTitle="Cadastro de cargo"
|
|
4871
|
+
formDescription="Exemplo simples com campos nativos para documentacao."
|
|
4872
|
+
[showActions]="false"
|
|
4873
|
+
[enableCustomization]="false"
|
|
4874
|
+
>
|
|
4875
|
+
<label class="showcase-field">
|
|
4876
|
+
<span>Nome</span>
|
|
4877
|
+
<input type="text" formControlName="nome" placeholder="Ex.: Analista de Dados" />
|
|
4878
|
+
</label>
|
|
4879
|
+
|
|
4880
|
+
<label class="showcase-field">
|
|
4881
|
+
<span>Descricao</span>
|
|
4882
|
+
<textarea formControlName="descricao" rows="3" placeholder="Resumo das responsabilidades"></textarea>
|
|
4883
|
+
</label>
|
|
4884
|
+
</praxis-manual-form>
|
|
4885
|
+
</div>
|
|
4886
|
+
|
|
4887
|
+
<div exampleDiagram class="showcase-diagram">
|
|
4888
|
+
<ol>
|
|
4889
|
+
<li>Preencha o formulario e valide comportamento dos campos.</li>
|
|
4890
|
+
<li>Abra a aba necessaria: Template, TS ou Config.</li>
|
|
4891
|
+
<li>Alterne customizacao para comparar o comportamento do host.</li>
|
|
4892
|
+
</ol>
|
|
4893
|
+
</div>
|
|
4894
|
+
</praxis-manual-form-doc-example>
|
|
4895
|
+
`, isInline: true, styles: [":host{display:block}.showcase-art{display:flex;gap:6px;flex-wrap:wrap}.showcase-art__chip{border-radius:999px;padding:4px 8px;font-size:11px;font-weight:700;border:1px solid color-mix(in srgb,var(--md-sys-color-outline, #98a3af) 50%,#d7dde3);background:color-mix(in srgb,var(--md-sys-color-primary, #00796b) 14%,#ffffff);color:var(--md-sys-color-on-surface, #151b22)}.showcase-live{display:grid;gap:12px}.showcase-mode{margin:0;border-radius:10px;border:1px solid #d6dde4;background:#f6f8fb;padding:8px 10px;color:#3b4651;font-size:13px}.showcase-mode--enabled{border-color:#5ebdb0;background:#e9f9f5;color:#075146}.showcase-field{display:grid;gap:6px}.showcase-field span{font-size:13px;font-weight:600}.showcase-field input,.showcase-field textarea{width:100%;border-radius:8px;border:1px solid #c8d2dc;padding:9px 10px;font:inherit;color:#1a222b;background:#fff}.showcase-field input:focus-visible,.showcase-field textarea:focus-visible{outline:2px solid #2d7f73;outline-offset:1px}.showcase-diagram{border-radius:12px;border:1px dashed #bdd0db;background:#f8fbff;padding:12px 14px}.showcase-diagram ol{margin:0;padding-left:20px;display:grid;gap:6px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: ManualFormComponent, selector: "praxis-manual-form", inputs: ["formId", "formTitle", "formDescription", "actions", "showHeader", "showActions", "enableAutoSave", "componentInstanceId", "enableCustomization", "persistenceOptions", "usePathNames", "autoSaveDebounceMs"], outputs: ["submitted", "saved", "reset", "metadataChange"] }, { kind: "component", type: ManualFormDocExampleComponent, selector: "praxis-manual-form-doc-example", inputs: ["title", "subtitle", "level", "bannerTitle", "bannerDescription", "templateCode", "tsCode", "configCode", "customizationEnabled", "showCustomizationToggle", "important", "note", "tip"], outputs: ["tabChange", "customizationEnabledChange", "codeCopied"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4896
|
+
}
|
|
4897
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFormDocExampleShowcaseComponent, decorators: [{
|
|
4898
|
+
type: Component,
|
|
4899
|
+
args: [{ selector: 'praxis-manual-form-doc-example-showcase', standalone: true, imports: [CommonModule, ReactiveFormsModule, ManualFormComponent, ManualFormDocExampleComponent], template: `
|
|
4900
|
+
<praxis-manual-form-doc-example
|
|
4901
|
+
title="Cadastro de cargo com preview live"
|
|
4902
|
+
subtitle="Exemplo live com abas para implementação."
|
|
4903
|
+
level="Intermediario"
|
|
4904
|
+
bannerTitle="Explore o formulário antes da teoria"
|
|
4905
|
+
bannerDescription="Interaja no live e abra código apenas quando precisar."
|
|
4906
|
+
[templateCode]="templateSnippet"
|
|
4907
|
+
[tsCode]="tsSnippet"
|
|
4908
|
+
[configCode]="configSnippet"
|
|
4909
|
+
[customizationEnabled]="customizationEnabled()"
|
|
4910
|
+
important="Comece pelo live para reduzir friccao de onboarding."
|
|
4911
|
+
note="As abas preservam o estado atual do formulario."
|
|
4912
|
+
tip="Use o toggle para validar variacoes de host sem recarregar."
|
|
4913
|
+
(customizationEnabledChange)="setCustomizationEnabled($event)"
|
|
4914
|
+
>
|
|
4915
|
+
<div exampleBannerArtwork class="showcase-art" aria-hidden="true">
|
|
4916
|
+
<div class="showcase-art__chip">Live</div>
|
|
4917
|
+
<div class="showcase-art__chip">Template</div>
|
|
4918
|
+
<div class="showcase-art__chip">TS</div>
|
|
4919
|
+
<div class="showcase-art__chip">Config</div>
|
|
4920
|
+
</div>
|
|
4921
|
+
|
|
4922
|
+
<div exampleLive class="showcase-live">
|
|
4923
|
+
<p class="showcase-mode" [class.showcase-mode--enabled]="customizationEnabled()">
|
|
4924
|
+
Customizacao no host: {{ customizationEnabled() ? 'ligada' : 'desligada' }}
|
|
4925
|
+
</p>
|
|
4926
|
+
|
|
4927
|
+
<praxis-manual-form
|
|
4928
|
+
[formGroup]="form"
|
|
4929
|
+
formId="doc-example-cargo"
|
|
4930
|
+
formTitle="Cadastro de cargo"
|
|
4931
|
+
formDescription="Exemplo simples com campos nativos para documentacao."
|
|
4932
|
+
[showActions]="false"
|
|
4933
|
+
[enableCustomization]="false"
|
|
4934
|
+
>
|
|
4935
|
+
<label class="showcase-field">
|
|
4936
|
+
<span>Nome</span>
|
|
4937
|
+
<input type="text" formControlName="nome" placeholder="Ex.: Analista de Dados" />
|
|
4938
|
+
</label>
|
|
4939
|
+
|
|
4940
|
+
<label class="showcase-field">
|
|
4941
|
+
<span>Descricao</span>
|
|
4942
|
+
<textarea formControlName="descricao" rows="3" placeholder="Resumo das responsabilidades"></textarea>
|
|
4943
|
+
</label>
|
|
4944
|
+
</praxis-manual-form>
|
|
4945
|
+
</div>
|
|
4946
|
+
|
|
4947
|
+
<div exampleDiagram class="showcase-diagram">
|
|
4948
|
+
<ol>
|
|
4949
|
+
<li>Preencha o formulario e valide comportamento dos campos.</li>
|
|
4950
|
+
<li>Abra a aba necessaria: Template, TS ou Config.</li>
|
|
4951
|
+
<li>Alterne customizacao para comparar o comportamento do host.</li>
|
|
4952
|
+
</ol>
|
|
4953
|
+
</div>
|
|
4954
|
+
</praxis-manual-form-doc-example>
|
|
4955
|
+
`, providers: [
|
|
4956
|
+
{ provide: API_URL, useValue: { default: { baseUrl: '/api' } } },
|
|
4957
|
+
{ provide: ASYNC_CONFIG_STORAGE, useFactory: () => new DocExampleMemoryStorage() },
|
|
4958
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block}.showcase-art{display:flex;gap:6px;flex-wrap:wrap}.showcase-art__chip{border-radius:999px;padding:4px 8px;font-size:11px;font-weight:700;border:1px solid color-mix(in srgb,var(--md-sys-color-outline, #98a3af) 50%,#d7dde3);background:color-mix(in srgb,var(--md-sys-color-primary, #00796b) 14%,#ffffff);color:var(--md-sys-color-on-surface, #151b22)}.showcase-live{display:grid;gap:12px}.showcase-mode{margin:0;border-radius:10px;border:1px solid #d6dde4;background:#f6f8fb;padding:8px 10px;color:#3b4651;font-size:13px}.showcase-mode--enabled{border-color:#5ebdb0;background:#e9f9f5;color:#075146}.showcase-field{display:grid;gap:6px}.showcase-field span{font-size:13px;font-weight:600}.showcase-field input,.showcase-field textarea{width:100%;border-radius:8px;border:1px solid #c8d2dc;padding:9px 10px;font:inherit;color:#1a222b;background:#fff}.showcase-field input:focus-visible,.showcase-field textarea:focus-visible{outline:2px solid #2d7f73;outline-offset:1px}.showcase-diagram{border-radius:12px;border:1px dashed #bdd0db;background:#f8fbff;padding:12px 14px}.showcase-diagram ol{margin:0;padding-left:20px;display:grid;gap:6px}\n"] }]
|
|
4959
|
+
}] });
|
|
4960
|
+
|
|
1507
4961
|
class ManualFieldDirective {
|
|
1508
4962
|
templateRef;
|
|
1509
4963
|
viewContainer;
|
|
@@ -1531,10 +4985,10 @@ class ManualFieldDirective {
|
|
|
1531
4985
|
};
|
|
1532
4986
|
this.viewContainer.createEmbeddedView(this.templateRef, context);
|
|
1533
4987
|
}
|
|
1534
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.
|
|
1535
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.
|
|
4988
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFieldDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Directive });
|
|
4989
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.17", type: ManualFieldDirective, isStandalone: true, selector: "[praxisManualField]", inputs: { fieldName: ["praxisManualField", "fieldName"], instance: ["praxisManualFieldInstance", "instance"] }, usesOnChanges: true, ngImport: i0 });
|
|
1536
4990
|
}
|
|
1537
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
4991
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFieldDirective, decorators: [{
|
|
1538
4992
|
type: Directive,
|
|
1539
4993
|
args: [{
|
|
1540
4994
|
selector: '[praxisManualField]',
|
|
@@ -1550,7 +5004,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
1550
5004
|
|
|
1551
5005
|
/**
|
|
1552
5006
|
* Attach to any field inside <praxis-manual-form> to open the metadata editor
|
|
1553
|
-
* on double click, respecting the form's
|
|
5007
|
+
* on double click, respecting the form's enableCustomization state.
|
|
1554
5008
|
*
|
|
1555
5009
|
* Usage:
|
|
1556
5010
|
* <pdx-text-input pdxManualEdit="nome" ...></pdx-text-input>
|
|
@@ -1563,10 +5017,10 @@ class ManualFieldEditorOnDblclickDirective {
|
|
|
1563
5017
|
return;
|
|
1564
5018
|
this.manualForm?.tryOpenFieldEditor(this.fieldName);
|
|
1565
5019
|
}
|
|
1566
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.
|
|
1567
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.
|
|
5020
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFieldEditorOnDblclickDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
5021
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.17", type: ManualFieldEditorOnDblclickDirective, isStandalone: true, selector: "[pdxManualEdit]", inputs: { fieldName: ["pdxManualEdit", "fieldName"] }, host: { listeners: { "dblclick": "onDblClick()" } }, ngImport: i0 });
|
|
1568
5022
|
}
|
|
1569
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
5023
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ManualFieldEditorOnDblclickDirective, decorators: [{
|
|
1570
5024
|
type: Directive,
|
|
1571
5025
|
args: [{
|
|
1572
5026
|
selector: '[pdxManualEdit]',
|
|
@@ -1588,5 +5042,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
1588
5042
|
* Generated bundle index. Do not edit.
|
|
1589
5043
|
*/
|
|
1590
5044
|
|
|
1591
|
-
export { MANUAL_FORM_CONSTRUCTOR_TO_CONTROL_TYPE, MANUAL_FORM_SELECTOR_TO_CONTROL_TYPE, ManualFieldDirective, ManualFieldEditorOnDblclickDirective, ManualFieldMetadataBridgeService, ManualFormActionsComponent, ManualFormComponent, ManualFormConfigEditorComponent, ManualFormHeaderComponent, ManualFormInstance, ManualFormInstanceFactory, createManualFormSeed, toFieldMetadataMap };
|
|
5045
|
+
export { MANUAL_FORM_AI_CAPABILITIES, MANUAL_FORM_CONSTRUCTOR_TO_CONTROL_TYPE, MANUAL_FORM_SELECTOR_TO_CONTROL_TYPE, ManualFieldDirective, ManualFieldEditorOnDblclickDirective, ManualFieldKeyService, ManualFieldMetadataBridgeService, ManualFormActionsComponent, ManualFormComponent, ManualFormConfigEditorComponent, ManualFormDocExampleComponent, ManualFormDocExampleShowcaseComponent, ManualFormHeaderComponent, ManualFormInstance, ManualFormInstanceFactory, createManualFormSeed, toFieldMetadataMap };
|
|
1592
5046
|
//# sourceMappingURL=praxisui-manual-form.mjs.map
|