@praxisui/manual-form 1.0.0-beta.30 → 1.0.0-beta.40
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 +125 -13
- package/fesm2022/praxisui-manual-form.mjs +3634 -184
- package/fesm2022/praxisui-manual-form.mjs.map +1 -1
- package/index.d.ts +235 -12
- package/package.json +5 -5
|
@@ -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';
|
|
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';
|
|
7
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,23 +341,27 @@ 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
|
-
if (!this.storage) {
|
|
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 });
|
|
364
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: ManualFormInstanceFactory, deps: [{ token: ASYNC_CONFIG_STORAGE, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
313
365
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: ManualFormInstanceFactory, providedIn: 'root' });
|
|
314
366
|
}
|
|
315
367
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: ManualFormInstanceFactory, decorators: [{
|
|
@@ -319,9 +371,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
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));
|
|
@@ -371,13 +426,13 @@ class ManualFormHeaderComponent {
|
|
|
371
426
|
</div>
|
|
372
427
|
<div class="pdx-manual-form__header-actions">
|
|
373
428
|
@if (editModeEnabled()) {
|
|
374
|
-
<button type="button" (click)="editForm.emit()" [attr.aria-label]="editFormLabel()">{{ editFormLabel() }}</button>
|
|
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
437
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: ManualFormHeaderComponent, decorators: [{
|
|
383
438
|
type: Component,
|
|
@@ -393,13 +448,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
393
448
|
</div>
|
|
394
449
|
<div class="pdx-manual-form__header-actions">
|
|
395
450
|
@if (editModeEnabled()) {
|
|
396
|
-
<button type="button" (click)="editForm.emit()" [attr.aria-label]="editFormLabel()">{{ editFormLabel() }}</button>
|
|
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.
|
|
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"] }]
|
|
403
458
|
}] });
|
|
404
459
|
|
|
405
460
|
class ManualFormActionsComponent {
|
|
@@ -424,21 +479,21 @@ class ManualFormActionsComponent {
|
|
|
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,7 +510,7 @@ 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
515
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: ManualFormActionsComponent, decorators: [{
|
|
461
516
|
type: Component,
|
|
@@ -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,11 +551,633 @@ 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}
|
|
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"] }]
|
|
500
555
|
}], propDecorators: { trackByFn: [{
|
|
501
556
|
type: Input
|
|
502
557
|
}] } });
|
|
503
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' },
|
|
919
|
+
{ path: 'actions.cancel.visible', category: 'actions', valueKind: 'boolean' },
|
|
920
|
+
{ path: 'actions.cancel.label', category: 'actions', valueKind: 'string' },
|
|
921
|
+
{ path: 'actions.cancel.icon', category: 'actions', valueKind: 'string' },
|
|
922
|
+
{ path: 'actions.cancel.className', category: 'actions', valueKind: 'string' },
|
|
923
|
+
{ path: 'actions.cancel.style', category: 'actions', valueKind: 'object' },
|
|
924
|
+
{ path: 'actions.cancel.color', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionColor },
|
|
925
|
+
{ path: 'actions.cancel.disabled', category: 'actions', valueKind: 'boolean' },
|
|
926
|
+
{ path: 'actions.cancel.type', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionType },
|
|
927
|
+
{ path: 'actions.cancel.action', category: 'actions', valueKind: 'string', safetyNotes: 'Requer handler registrado no host.' },
|
|
928
|
+
{ path: 'actions.cancel.tooltip', category: 'actions', valueKind: 'string' },
|
|
929
|
+
{ path: 'actions.cancel.loading', category: 'actions', valueKind: 'boolean' },
|
|
930
|
+
{ path: 'actions.cancel.size', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionSize },
|
|
931
|
+
{ path: 'actions.cancel.variant', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionVariant },
|
|
932
|
+
{ path: 'actions.cancel.shortcut', category: 'actions', valueKind: 'string' },
|
|
933
|
+
{ path: 'actions.reset.id', category: 'actions', valueKind: 'string' },
|
|
934
|
+
{ path: 'actions.reset.visible', category: 'actions', valueKind: 'boolean' },
|
|
935
|
+
{ path: 'actions.reset.label', category: 'actions', valueKind: 'string' },
|
|
936
|
+
{ path: 'actions.reset.icon', category: 'actions', valueKind: 'string' },
|
|
937
|
+
{ path: 'actions.reset.className', category: 'actions', valueKind: 'string' },
|
|
938
|
+
{ path: 'actions.reset.style', category: 'actions', valueKind: 'object' },
|
|
939
|
+
{ path: 'actions.reset.color', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionColor },
|
|
940
|
+
{ path: 'actions.reset.disabled', category: 'actions', valueKind: 'boolean' },
|
|
941
|
+
{ path: 'actions.reset.type', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionType },
|
|
942
|
+
{ path: 'actions.reset.action', category: 'actions', valueKind: 'string', safetyNotes: 'Requer handler registrado no host.' },
|
|
943
|
+
{ path: 'actions.reset.tooltip', category: 'actions', valueKind: 'string' },
|
|
944
|
+
{ path: 'actions.reset.loading', category: 'actions', valueKind: 'boolean' },
|
|
945
|
+
{ path: 'actions.reset.size', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionSize },
|
|
946
|
+
{ path: 'actions.reset.variant', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionVariant },
|
|
947
|
+
{ path: 'actions.reset.shortcut', category: 'actions', valueKind: 'string' },
|
|
948
|
+
{ path: 'actions.custom[].id', category: 'actions', valueKind: 'string' },
|
|
949
|
+
{ path: 'actions.custom[].visible', category: 'actions', valueKind: 'boolean' },
|
|
950
|
+
{ path: 'actions.custom[].label', category: 'actions', valueKind: 'string' },
|
|
951
|
+
{ path: 'actions.custom[].icon', category: 'actions', valueKind: 'string' },
|
|
952
|
+
{ path: 'actions.custom[].className', category: 'actions', valueKind: 'string' },
|
|
953
|
+
{ path: 'actions.custom[].style', category: 'actions', valueKind: 'object' },
|
|
954
|
+
{ path: 'actions.custom[].color', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionColor },
|
|
955
|
+
{ path: 'actions.custom[].disabled', category: 'actions', valueKind: 'boolean' },
|
|
956
|
+
{ path: 'actions.custom[].type', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionType },
|
|
957
|
+
{ path: 'actions.custom[].action', category: 'actions', valueKind: 'string', safetyNotes: 'Requer handler registrado no host.' },
|
|
958
|
+
{ path: 'actions.custom[].tooltip', category: 'actions', valueKind: 'string' },
|
|
959
|
+
{ path: 'actions.custom[].loading', category: 'actions', valueKind: 'boolean' },
|
|
960
|
+
{ path: 'actions.custom[].size', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionSize },
|
|
961
|
+
{ path: 'actions.custom[].variant', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.actionVariant },
|
|
962
|
+
{ path: 'actions.custom[].shortcut', category: 'actions', valueKind: 'string' },
|
|
963
|
+
// --- Behavior ---
|
|
964
|
+
{ path: 'behavior', category: 'behavior', valueKind: 'object', description: 'Comportamento do formulario.' },
|
|
965
|
+
{ path: 'behavior.confirmOnUnsavedChanges', category: 'behavior', valueKind: 'boolean' },
|
|
966
|
+
{ path: 'behavior.trackHistory', category: 'behavior', valueKind: 'boolean' },
|
|
967
|
+
{ path: 'behavior.focusFirstError', category: 'behavior', valueKind: 'boolean' },
|
|
968
|
+
{ path: 'behavior.scrollToErrors', category: 'behavior', valueKind: 'boolean' },
|
|
969
|
+
{ path: 'behavior.clearAfterSave', category: 'behavior', valueKind: 'boolean' },
|
|
970
|
+
{ path: 'behavior.redirectAfterSave', category: 'behavior', valueKind: 'string' },
|
|
971
|
+
{ path: 'behavior.reactiveValidation', category: 'behavior', valueKind: 'boolean' },
|
|
972
|
+
{ path: 'behavior.reactiveValidationDebounceMs', category: 'behavior', valueKind: 'number' },
|
|
973
|
+
// --- API ---
|
|
974
|
+
{ path: 'api', category: 'api', valueKind: 'object', description: 'Configuracao de API do formulario.' },
|
|
975
|
+
{ path: 'api.saveEndpoint', category: 'api', valueKind: 'string' },
|
|
976
|
+
{ path: 'api.loadEndpoint', category: 'api', valueKind: 'string' },
|
|
977
|
+
{ path: 'api.saveMethod', category: 'api', valueKind: 'enum', allowedValues: ['POST', 'PUT', 'PATCH'] },
|
|
978
|
+
{ path: 'api.timeout', category: 'api', valueKind: 'number' },
|
|
979
|
+
{ path: 'api.headers', category: 'api', valueKind: 'object' },
|
|
980
|
+
{ path: 'api.idField', category: 'api', valueKind: 'string' },
|
|
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' },
|
|
1039
|
+
{ path: 'hooks.afterInit[].priority', category: 'hooks', valueKind: 'number' },
|
|
1040
|
+
{ path: 'hooks.afterInit[].timeoutMs', category: 'hooks', valueKind: 'number' },
|
|
1041
|
+
{ path: 'hooks.afterInit[].args', category: 'hooks', valueKind: 'object' },
|
|
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' },
|
|
1045
|
+
{ path: 'hooks.beforeValidate[].priority', category: 'hooks', valueKind: 'number' },
|
|
1046
|
+
{ path: 'hooks.beforeValidate[].timeoutMs', category: 'hooks', valueKind: 'number' },
|
|
1047
|
+
{ path: 'hooks.beforeValidate[].args', category: 'hooks', valueKind: 'object' },
|
|
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' },
|
|
1051
|
+
{ path: 'hooks.afterValidate[].priority', category: 'hooks', valueKind: 'number' },
|
|
1052
|
+
{ path: 'hooks.afterValidate[].timeoutMs', category: 'hooks', valueKind: 'number' },
|
|
1053
|
+
{ path: 'hooks.afterValidate[].args', category: 'hooks', valueKind: 'object' },
|
|
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' },
|
|
1057
|
+
{ path: 'hooks.beforeSubmit[].priority', category: 'hooks', valueKind: 'number' },
|
|
1058
|
+
{ path: 'hooks.beforeSubmit[].timeoutMs', category: 'hooks', valueKind: 'number' },
|
|
1059
|
+
{ path: 'hooks.beforeSubmit[].args', category: 'hooks', valueKind: 'object' },
|
|
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' },
|
|
1063
|
+
{ path: 'hooks.afterSubmit[].priority', category: 'hooks', valueKind: 'number' },
|
|
1064
|
+
{ path: 'hooks.afterSubmit[].timeoutMs', category: 'hooks', valueKind: 'number' },
|
|
1065
|
+
{ path: 'hooks.afterSubmit[].args', category: 'hooks', valueKind: 'object' },
|
|
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' },
|
|
1069
|
+
{ path: 'hooks.onError[].priority', category: 'hooks', valueKind: 'number' },
|
|
1070
|
+
{ path: 'hooks.onError[].timeoutMs', category: 'hooks', valueKind: 'number' },
|
|
1071
|
+
{ path: 'hooks.onError[].args', category: 'hooks', valueKind: 'object' },
|
|
1072
|
+
// --- Hints ---
|
|
1073
|
+
{ path: 'hints.dataModes.create', category: 'hints', valueKind: 'string' },
|
|
1074
|
+
{ path: 'hints.dataModes.edit', category: 'hints', valueKind: 'string' },
|
|
1075
|
+
{ path: 'hints.dataModes.view', category: 'hints', valueKind: 'string' },
|
|
1076
|
+
{ path: 'hints.uiModes.presentation', category: 'hints', valueKind: 'string' },
|
|
1077
|
+
{ path: 'hints.uiModes.readonly', category: 'hints', valueKind: 'string' },
|
|
1078
|
+
{ path: 'hints.uiModes.disabled', category: 'hints', valueKind: 'string' },
|
|
1079
|
+
{ path: 'hints.uiModes.visible', category: 'hints', valueKind: 'string' },
|
|
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
|
+
}
|
|
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');
|
|
506
1183
|
|
|
@@ -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,7 +1357,7 @@ 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) {
|
|
@@ -693,14 +1371,278 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
693
1371
|
args: [{ providedIn: 'root' }]
|
|
694
1372
|
}] });
|
|
695
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.1.4", ngImport: i0, type: ManualFieldKeyService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1406
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: ManualFieldKeyService, providedIn: 'root' });
|
|
1407
|
+
}
|
|
1408
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: ManualFieldKeyService, decorators: [{
|
|
1409
|
+
type: Injectable,
|
|
1410
|
+
args: [{ providedIn: 'root' }]
|
|
1411
|
+
}] });
|
|
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.1.4", ngImport: i0, type: ManualFieldToolbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1440
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.4", 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.1.4", 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,6 +1650,7 @@ 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
|
editModeEnabled = input(false, ...(ngDevMode ? [{ debugName: "editModeEnabled" }] : []));
|
|
713
1656
|
persistenceOptions = input(...(ngDevMode ? [undefined, { debugName: "persistenceOptions" }] : []));
|
|
@@ -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.editModeEnabled();
|
|
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) {
|
|
@@ -874,6 +1868,33 @@ class ManualFormComponent {
|
|
|
874
1868
|
this.hostFormGroupDirective?.resetForm(this.instance.form.getRawValue());
|
|
875
1869
|
this.resetEvent.emit(this.instance);
|
|
876
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
|
+
}
|
|
877
1898
|
/**
|
|
878
1899
|
* Attempts to open the field metadata editor respecting the component's
|
|
879
1900
|
* editModeEnabled input. No-ops when disabled or without an instance.
|
|
@@ -904,8 +1925,9 @@ class ManualFormComponent {
|
|
|
904
1925
|
return;
|
|
905
1926
|
}
|
|
906
1927
|
const formId = this.formId();
|
|
1928
|
+
const keyId = this.componentKeyId();
|
|
907
1929
|
this.settingsPanel.open({
|
|
908
|
-
id: formId ? `manual-form-editor.${formId}` : 'manual-form-editor',
|
|
1930
|
+
id: keyId ? `manual-form-editor.${keyId}` : (formId ? `manual-form-editor.${formId}` : 'manual-form-editor'),
|
|
909
1931
|
title: this.formTitle() || 'Editor do Formulário',
|
|
910
1932
|
titleIcon: 'tune',
|
|
911
1933
|
content: {
|
|
@@ -914,7 +1936,17 @@ class ManualFormComponent {
|
|
|
914
1936
|
},
|
|
915
1937
|
});
|
|
916
1938
|
}
|
|
917
|
-
|
|
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();
|
|
918
1950
|
if (!this.formId()) {
|
|
919
1951
|
return;
|
|
920
1952
|
}
|
|
@@ -925,11 +1957,22 @@ class ManualFormComponent {
|
|
|
925
1957
|
return;
|
|
926
1958
|
}
|
|
927
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
|
+
}
|
|
928
1969
|
// Create runtime instance and let it populate our existing FormGroup
|
|
929
|
-
this.instance = this.instanceFactory.create(seed,
|
|
1970
|
+
this.instance = this.instanceFactory.create(seed, persistence, this.formGroup);
|
|
930
1971
|
if (isDevMode())
|
|
931
1972
|
console.debug('[ManualForm] instance created with seed; current controls', Object.keys(this.formGroup.controls || {}));
|
|
932
1973
|
this.applyInstanceToTemplate(fields);
|
|
1974
|
+
this.registerToolbarBindings(fields);
|
|
1975
|
+
this.attachMetadataSync();
|
|
933
1976
|
// Auto-save value changes with debounce
|
|
934
1977
|
if (this.enableAutoSave() && this.instance) {
|
|
935
1978
|
this.instance.form.valueChanges
|
|
@@ -941,6 +1984,7 @@ class ManualFormComponent {
|
|
|
941
1984
|
catch { }
|
|
942
1985
|
});
|
|
943
1986
|
}
|
|
1987
|
+
await this.runHooks('afterInit', { seed });
|
|
944
1988
|
this.cdr.markForCheck();
|
|
945
1989
|
}
|
|
946
1990
|
collectFields() {
|
|
@@ -964,10 +2008,30 @@ class ManualFormComponent {
|
|
|
964
2008
|
control: dir.control,
|
|
965
2009
|
component,
|
|
966
2010
|
selector: this.resolveSelector(component, dir),
|
|
2011
|
+
element: this.resolveHostElement(component, dir),
|
|
967
2012
|
});
|
|
968
2013
|
}
|
|
969
2014
|
return result;
|
|
970
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
|
+
}
|
|
971
2035
|
resolveValueAccessor(dir) {
|
|
972
2036
|
const accessor = dir.valueAccessor;
|
|
973
2037
|
if (!accessor) {
|
|
@@ -990,6 +2054,10 @@ class ManualFormComponent {
|
|
|
990
2054
|
?? dir?._elementRef?.nativeElement;
|
|
991
2055
|
return element?.tagName?.toLowerCase();
|
|
992
2056
|
}
|
|
2057
|
+
resolveHostElement(component, dir) {
|
|
2058
|
+
return component?.elementRef?.nativeElement
|
|
2059
|
+
?? dir?._elementRef?.nativeElement;
|
|
2060
|
+
}
|
|
993
2061
|
createSeedFromFields(fields) {
|
|
994
2062
|
const metadataList = fields.map((field) => this.buildMetadataForField(field));
|
|
995
2063
|
const actions = this.normalizeActions(this.actions() ?? null);
|
|
@@ -1042,6 +2110,234 @@ class ManualFormComponent {
|
|
|
1042
2110
|
}
|
|
1043
2111
|
}
|
|
1044
2112
|
}
|
|
2113
|
+
registerToolbarBindings(fields) {
|
|
2114
|
+
this.toolbarSubscriptions.forEach((sub) => sub.unsubscribe());
|
|
2115
|
+
this.toolbarSubscriptions = [];
|
|
2116
|
+
if (!this.editModeEnabled()) {
|
|
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.editModeEnabled()) {
|
|
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
|
+
}
|
|
1045
2341
|
buildMetadataForField(field) {
|
|
1046
2342
|
const existing = this.extractExistingMetadata(field.component);
|
|
1047
2343
|
const label = existing?.label ?? this.inferLabel(field, existing);
|
|
@@ -1055,18 +2351,34 @@ class ManualFormComponent {
|
|
|
1055
2351
|
validators: { ...existing?.validators, ...validators },
|
|
1056
2352
|
};
|
|
1057
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
|
+
}
|
|
1058
2362
|
getControlByPath(path) {
|
|
1059
2363
|
if (!Array.isArray(path) || path.length === 0)
|
|
1060
2364
|
return null;
|
|
1061
2365
|
return this.formGroup.get(path);
|
|
1062
2366
|
}
|
|
1063
|
-
|
|
1064
|
-
if (
|
|
1065
|
-
return;
|
|
1066
|
-
|
|
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) {
|
|
1067
2377
|
let group = this.formGroup;
|
|
1068
|
-
|
|
1069
|
-
|
|
2378
|
+
if (!Array.isArray(path) || path.length === 0) {
|
|
2379
|
+
return group;
|
|
2380
|
+
}
|
|
2381
|
+
for (const seg of path) {
|
|
1070
2382
|
const existing = group.get(seg);
|
|
1071
2383
|
if (existing instanceof FormGroup) {
|
|
1072
2384
|
group = existing;
|
|
@@ -1077,6 +2389,13 @@ class ManualFormComponent {
|
|
|
1077
2389
|
group = next;
|
|
1078
2390
|
}
|
|
1079
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));
|
|
1080
2399
|
if (!group.get(leaf)) {
|
|
1081
2400
|
group.addControl(leaf, new FormControl());
|
|
1082
2401
|
}
|
|
@@ -1116,8 +2435,13 @@ class ManualFormComponent {
|
|
|
1116
2435
|
}
|
|
1117
2436
|
inferControlType(field) {
|
|
1118
2437
|
const selector = field.selector?.toLowerCase();
|
|
1119
|
-
if (selector
|
|
1120
|
-
|
|
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;
|
|
1121
2445
|
}
|
|
1122
2446
|
const ctorName = field.component?.constructor?.name ?? '';
|
|
1123
2447
|
for (const [key, value] of Object.entries(this.constructorToControlType)) {
|
|
@@ -1187,15 +2511,15 @@ class ManualFormComponent {
|
|
|
1187
2511
|
};
|
|
1188
2512
|
}
|
|
1189
2513
|
normalizeActions(source) {
|
|
1190
|
-
const base = deepClone(DEFAULT_ACTIONS);
|
|
2514
|
+
const base = deepClone(DEFAULT_ACTIONS$1);
|
|
1191
2515
|
if (!source) {
|
|
1192
2516
|
return base;
|
|
1193
2517
|
}
|
|
1194
2518
|
return {
|
|
1195
|
-
submit: mergeAction(base.submit, source.submit),
|
|
1196
|
-
cancel: mergeAction(base.cancel, source.cancel),
|
|
1197
|
-
reset: mergeAction(base.reset, source.reset),
|
|
1198
|
-
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,
|
|
1199
2523
|
position: source.position ?? base.position,
|
|
1200
2524
|
orientation: source.orientation ?? base.orientation,
|
|
1201
2525
|
spacing: source.spacing ?? base.spacing,
|
|
@@ -1213,36 +2537,37 @@ class ManualFormComponent {
|
|
|
1213
2537
|
};
|
|
1214
2538
|
}
|
|
1215
2539
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: ManualFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1216
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.4", 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 }, editModeEnabled: { classPropertyName: "editModeEnabled", publicName: "editModeEnabled", 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: [
|
|
2540
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.4", 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 }, editModeEnabled: { classPropertyName: "editModeEnabled", publicName: "editModeEnabled", 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: [
|
|
1217
2541
|
// Provide a ControlContainer at the host boundary so projected formControlName can resolve it
|
|
1218
2542
|
{ provide: ControlContainer, useExisting: ManualFormComponent },
|
|
1219
|
-
], 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 [editModeEnabled]=\"editModeEnabled()\" (editForm)=\"openFormEditor()\" (save)=\"handleSave()\"\n (reset)=\"handleReset()\"></praxis-manual-form-header>\n }\n\n @if (editModeEnabled()) {\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", "editModeEnabled", "editFormLabel"], outputs: ["save", "reset", "editForm"] }, { kind: "component", type: ManualFormActionsComponent, selector: "praxis-manual-form-actions", inputs: ["actions", "trackByFn"], outputs: ["actionClick"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1220
2544
|
}
|
|
1221
2545
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: ManualFormComponent, decorators: [{
|
|
1222
2546
|
type: Component,
|
|
1223
2547
|
args: [{ selector: 'praxis-manual-form', standalone: true, imports: [
|
|
1224
2548
|
CommonModule,
|
|
1225
2549
|
ReactiveFormsModule,
|
|
2550
|
+
PraxisAiAssistantComponent,
|
|
1226
2551
|
ManualFormHeaderComponent,
|
|
1227
2552
|
ManualFormActionsComponent,
|
|
1228
2553
|
], providers: [
|
|
1229
2554
|
// Provide a ControlContainer at the host boundary so projected formControlName can resolve it
|
|
1230
2555
|
{ provide: ControlContainer, useExisting: ManualFormComponent },
|
|
1231
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"pdx-manual-form\">\n @if (showHeader()) {\n
|
|
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 [editModeEnabled]=\"editModeEnabled()\" (editForm)=\"openFormEditor()\" (save)=\"handleSave()\"\n (reset)=\"handleReset()\"></praxis-manual-form-header>\n }\n\n @if (editModeEnabled()) {\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"] }]
|
|
1232
2557
|
}], ctorParameters: () => [], propDecorators: { formControls: [{
|
|
1233
2558
|
type: ContentChildren,
|
|
1234
2559
|
args: [FormControlName, { descendants: true }]
|
|
1235
2560
|
}] } });
|
|
1236
|
-
const DEFAULT_ACTIONS = {
|
|
1237
|
-
submit: makeAction({ id: 'submit', label: 'Salvar', visible: true, type: 'submit', color: 'primary' }),
|
|
1238
|
-
cancel: makeAction({ id: 'cancel', label: 'Cancelar', visible: false, type: 'button' }),
|
|
1239
|
-
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' }),
|
|
1240
2565
|
custom: [],
|
|
1241
2566
|
position: 'right',
|
|
1242
2567
|
orientation: 'horizontal',
|
|
1243
2568
|
spacing: 'normal',
|
|
1244
2569
|
};
|
|
1245
|
-
function makeAction(action) {
|
|
2570
|
+
function makeAction$1(action) {
|
|
1246
2571
|
return {
|
|
1247
2572
|
id: action.id ?? 'action',
|
|
1248
2573
|
label: action.label ?? 'Ação',
|
|
@@ -1259,7 +2584,7 @@ function makeAction(action) {
|
|
|
1259
2584
|
shortcut: action.shortcut,
|
|
1260
2585
|
};
|
|
1261
2586
|
}
|
|
1262
|
-
function mergeAction(base, override) {
|
|
2587
|
+
function mergeAction$1(base, override) {
|
|
1263
2588
|
if (!override) {
|
|
1264
2589
|
return base;
|
|
1265
2590
|
}
|
|
@@ -1269,24 +2594,7 @@ function mergeAction(base, override) {
|
|
|
1269
2594
|
visible: override.visible !== undefined ? override.visible : base.visible,
|
|
1270
2595
|
};
|
|
1271
2596
|
}
|
|
1272
|
-
const DEFAULT_SELECTOR_TO_CONTROL_TYPE =
|
|
1273
|
-
'pdx-text-input': FieldControlType.INPUT,
|
|
1274
|
-
'pdx-material-textarea': FieldControlType.TEXTAREA,
|
|
1275
|
-
'pdx-number-input': FieldControlType.NUMERIC_TEXT_BOX,
|
|
1276
|
-
'pdx-material-currency': FieldControlType.CURRENCY_INPUT,
|
|
1277
|
-
'pdx-material-datepicker': FieldControlType.DATE_PICKER,
|
|
1278
|
-
'pdx-material-date-range': FieldControlType.DATE_RANGE,
|
|
1279
|
-
'pdx-material-timepicker': FieldControlType.TIME_PICKER,
|
|
1280
|
-
'pdx-material-colorpicker': FieldControlType.COLOR_PICKER,
|
|
1281
|
-
'pdx-material-select': FieldControlType.SELECT,
|
|
1282
|
-
'pdx-material-autocomplete': FieldControlType.AUTO_COMPLETE,
|
|
1283
|
-
'pdx-material-checkbox-group': FieldControlType.CHECKBOX,
|
|
1284
|
-
'pdx-material-radio-group': FieldControlType.RADIO,
|
|
1285
|
-
'pdx-material-slide-toggle': FieldControlType.TOGGLE,
|
|
1286
|
-
'pdx-material-slider': FieldControlType.SLIDER,
|
|
1287
|
-
'pdx-material-range-slider': FieldControlType.RANGE_SLIDER,
|
|
1288
|
-
'pdx-material-file-upload': FieldControlType.FILE_UPLOAD,
|
|
1289
|
-
};
|
|
2597
|
+
const DEFAULT_SELECTOR_TO_CONTROL_TYPE = DEFAULT_FIELD_SELECTOR_CONTROL_TYPE_MAP;
|
|
1290
2598
|
const DEFAULT_CONSTRUCTOR_TO_CONTROL_TYPE = {
|
|
1291
2599
|
TextInput: FieldControlType.INPUT,
|
|
1292
2600
|
Textarea: FieldControlType.TEXTAREA,
|
|
@@ -1312,6 +2620,30 @@ class ManualFormConfigEditorComponent {
|
|
|
1312
2620
|
all = [];
|
|
1313
2621
|
filtered = [];
|
|
1314
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
|
+
];
|
|
1315
2647
|
// SettingsValueProvider observables
|
|
1316
2648
|
isDirty$ = new BehaviorSubject(false);
|
|
1317
2649
|
isValid$ = new BehaviorSubject(true);
|
|
@@ -1368,6 +2700,206 @@ class ManualFormConfigEditorComponent {
|
|
|
1368
2700
|
}
|
|
1369
2701
|
catch { }
|
|
1370
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
|
+
}
|
|
1371
2903
|
getSettingsValue() {
|
|
1372
2904
|
return {
|
|
1373
2905
|
fieldStates: (this.instance.currentConfig.fieldMetadata || []).map((f) => ({
|
|
@@ -1377,6 +2909,13 @@ class ManualFormConfigEditorComponent {
|
|
|
1377
2909
|
readOnly: !!f.readOnly,
|
|
1378
2910
|
disabled: !!f.disabled,
|
|
1379
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),
|
|
1380
2919
|
};
|
|
1381
2920
|
}
|
|
1382
2921
|
onSave() {
|
|
@@ -1387,127 +2926,2038 @@ class ManualFormConfigEditorComponent {
|
|
|
1387
2926
|
}
|
|
1388
2927
|
refresh() {
|
|
1389
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);
|
|
1390
2936
|
this.applyFilter();
|
|
1391
2937
|
}
|
|
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
|
+
}
|
|
1392
2986
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: ManualFormConfigEditorComponent, deps: [{ token: SETTINGS_PANEL_DATA }, { token: SETTINGS_PANEL_REF }], target: i0.ɵɵFactoryTarget.Component });
|
|
1393
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
2987
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.4", type: ManualFormConfigEditorComponent, isStandalone: true, selector: "praxis-manual-form-config-editor", ngImport: i0, template: `
|
|
1394
2988
|
<div class="mf-editor">
|
|
1395
2989
|
<div class="mf-editor__toolbar">
|
|
1396
|
-
<
|
|
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>
|
|
1397
2994
|
<div class="spacer"></div>
|
|
1398
|
-
<button type="button" (click)="close()">Fechar</button>
|
|
2995
|
+
<button type="button" class="mf-btn" (click)="close()">Fechar</button>
|
|
1399
2996
|
</div>
|
|
1400
2997
|
|
|
1401
|
-
<
|
|
1402
|
-
<
|
|
1403
|
-
<div class="
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
<div class="col col--req">Obrigatório</div>
|
|
1408
|
-
<div class="col col--ro">Som. leitura</div>
|
|
1409
|
-
<div class="col col--dis">Desabilitado</div>
|
|
1410
|
-
</div>
|
|
1411
|
-
|
|
1412
|
-
<div class="mf-editor__row" *ngFor="let f of filtered">
|
|
1413
|
-
<div class="col col--name"><code>{{ f.name }}</code></div>
|
|
1414
|
-
<div class="col col--label">{{ f.label || f.name }}</div>
|
|
1415
|
-
<div class="col col--type">{{ f.controlType }}</div>
|
|
1416
|
-
<div class="col col--vis">
|
|
1417
|
-
<label class="toggle">
|
|
1418
|
-
<input type="checkbox" [checked]="!f.hidden" (change)="toggleVisibility(f, $any($event.target).checked)" />
|
|
1419
|
-
<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>
|
|
1420
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>
|
|
1421
3048
|
</div>
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
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>
|
|
1427
3303
|
</div>
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
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>
|
|
1433
3466
|
</div>
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
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>
|
|
1439
3538
|
</div>
|
|
1440
|
-
</
|
|
1441
|
-
|
|
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>
|
|
1442
3765
|
</div>
|
|
1443
|
-
`, 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"] }] });
|
|
1444
3767
|
}
|
|
1445
3768
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: ManualFormConfigEditorComponent, decorators: [{
|
|
1446
3769
|
type: Component,
|
|
1447
|
-
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: `
|
|
1448
3783
|
<div class="mf-editor">
|
|
1449
3784
|
<div class="mf-editor__toolbar">
|
|
1450
|
-
<
|
|
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>
|
|
1451
3789
|
<div class="spacer"></div>
|
|
1452
|
-
<button type="button" (click)="close()">Fechar</button>
|
|
3790
|
+
<button type="button" class="mf-btn" (click)="close()">Fechar</button>
|
|
1453
3791
|
</div>
|
|
1454
3792
|
|
|
1455
|
-
<
|
|
1456
|
-
<
|
|
1457
|
-
<div class="
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
<div class="col col--req">Obrigatório</div>
|
|
1462
|
-
<div class="col col--ro">Som. leitura</div>
|
|
1463
|
-
<div class="col col--dis">Desabilitado</div>
|
|
1464
|
-
</div>
|
|
1465
|
-
|
|
1466
|
-
<div class="mf-editor__row" *ngFor="let f of filtered">
|
|
1467
|
-
<div class="col col--name"><code>{{ f.name }}</code></div>
|
|
1468
|
-
<div class="col col--label">{{ f.label || f.name }}</div>
|
|
1469
|
-
<div class="col col--type">{{ f.controlType }}</div>
|
|
1470
|
-
<div class="col col--vis">
|
|
1471
|
-
<label class="toggle">
|
|
1472
|
-
<input type="checkbox" [checked]="!f.hidden" (change)="toggleVisibility(f, $any($event.target).checked)" />
|
|
1473
|
-
<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>
|
|
1474
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>
|
|
1475
3843
|
</div>
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
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>
|
|
1481
4098
|
</div>
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
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>
|
|
1487
4261
|
</div>
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
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>
|
|
1493
4333
|
</div>
|
|
1494
|
-
</
|
|
1495
|
-
|
|
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>
|
|
1496
4560
|
</div>
|
|
1497
|
-
`, 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"] }]
|
|
1498
4562
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
1499
4563
|
type: Inject,
|
|
1500
4564
|
args: [SETTINGS_PANEL_DATA]
|
|
1501
|
-
}] }, { type:
|
|
4565
|
+
}] }, { type: i10.SettingsPanelRef, decorators: [{
|
|
1502
4566
|
type: Inject,
|
|
1503
4567
|
args: [SETTINGS_PANEL_REF]
|
|
1504
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
|
+
}
|
|
1505
4661
|
|
|
1506
4662
|
var manualFormConfigEditor_component = /*#__PURE__*/Object.freeze({
|
|
1507
4663
|
__proto__: null,
|
|
1508
4664
|
ManualFormConfigEditorComponent: ManualFormConfigEditorComponent
|
|
1509
4665
|
});
|
|
1510
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.1.4", ngImport: i0, type: ManualFormDocExampleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4778
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.4", 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.1.4", 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
|
+
}] });
|
|
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.1.4", ngImport: i0, type: ManualFormDocExampleShowcaseComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4836
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.4", 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
|
+
[editModeEnabled]="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", "editModeEnabled", "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.1.4", 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
|
+
[editModeEnabled]="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
|
+
|
|
1511
4961
|
class ManualFieldDirective {
|
|
1512
4962
|
templateRef;
|
|
1513
4963
|
viewContainer;
|
|
@@ -1592,5 +5042,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
1592
5042
|
* Generated bundle index. Do not edit.
|
|
1593
5043
|
*/
|
|
1594
5044
|
|
|
1595
|
-
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 };
|
|
1596
5046
|
//# sourceMappingURL=praxisui-manual-form.mjs.map
|