@praxisui/manual-form 1.0.0-beta.4 → 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 +140 -13
- package/fesm2022/praxisui-manual-form.mjs +3639 -185
- package/fesm2022/praxisui-manual-form.mjs.map +1 -1
- package/index.d.ts +237 -12
- package/package.json +8 -8
|
@@ -1,14 +1,35 @@
|
|
|
1
|
-
import { ensureIds, DynamicFormService,
|
|
1
|
+
import { ensureIds, DynamicFormService, ASYNC_CONFIG_STORAGE, RULE_PROPERTY_SCHEMA, deepMerge, resolveControlTypeAlias, FieldControlType, normalizeControlTypeKey, FormHooksRegistry, ComponentKeyService, FIELD_SELECTOR_REGISTRY_DISABLE_DEFAULTS, DEFAULT_FIELD_SELECTOR_CONTROL_TYPE_MAP, FieldSelectorRegistry, API_URL } from '@praxisui/core';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { inject, Optional, Inject, Injectable, input, output, ChangeDetectionStrategy, Component, Input, InjectionToken, isDevMode, ChangeDetectorRef, DestroyRef, ContentChildren,
|
|
4
|
-
import { BehaviorSubject, debounceTime } from 'rxjs';
|
|
5
|
-
import
|
|
6
|
-
import { CommonModule } from '@angular/common';
|
|
7
|
-
import
|
|
3
|
+
import { inject, Optional, Inject, Injectable, input, output, ChangeDetectionStrategy, Component, Input, InjectionToken, isDevMode, EventEmitter, HostListener, ViewChild, Output, ChangeDetectorRef, DestroyRef, PLATFORM_ID, effect, ContentChildren, signal, computed, Directive } from '@angular/core';
|
|
4
|
+
import { BehaviorSubject, debounceTime, fromEvent, of } from 'rxjs';
|
|
5
|
+
import { take } from 'rxjs/operators';
|
|
6
|
+
import { CommonModule, isPlatformBrowser } from '@angular/common';
|
|
7
|
+
import { ActivatedRoute } from '@angular/router';
|
|
8
|
+
import { BaseAiAdapter, PraxisAiAssistantComponent } from '@praxisui/ai';
|
|
9
|
+
import * as i1 from '@angular/forms';
|
|
8
10
|
import { FormGroupDirective, FormGroup, FormControl, Validators, ReactiveFormsModule, FormControlName, ControlContainer, FormsModule } from '@angular/forms';
|
|
9
11
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
10
|
-
import
|
|
12
|
+
import { Overlay } from '@angular/cdk/overlay';
|
|
13
|
+
import { ComponentPortal } from '@angular/cdk/portal';
|
|
14
|
+
import * as i10 from '@praxisui/settings-panel';
|
|
11
15
|
import { SettingsPanelService, SETTINGS_PANEL_DATA, SETTINGS_PANEL_REF } from '@praxisui/settings-panel';
|
|
16
|
+
import * as i2 from '@angular/material/tabs';
|
|
17
|
+
import { MatTabsModule } from '@angular/material/tabs';
|
|
18
|
+
import * as i3 from '@angular/material/form-field';
|
|
19
|
+
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
20
|
+
import * as i4 from '@angular/material/input';
|
|
21
|
+
import { MatInputModule } from '@angular/material/input';
|
|
22
|
+
import * as i5 from '@angular/material/select';
|
|
23
|
+
import { MatSelectModule } from '@angular/material/select';
|
|
24
|
+
import * as i6 from '@angular/material/slide-toggle';
|
|
25
|
+
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
|
26
|
+
import * as i7 from '@angular/material/button';
|
|
27
|
+
import { MatButtonModule } from '@angular/material/button';
|
|
28
|
+
import * as i8 from '@angular/material/icon';
|
|
29
|
+
import { MatIconModule } from '@angular/material/icon';
|
|
30
|
+
import * as i9 from '@angular/material/tooltip';
|
|
31
|
+
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
32
|
+
import { CascadeManagerTabComponent } from '@praxisui/metadata-editor';
|
|
12
33
|
|
|
13
34
|
function deepClone(value) {
|
|
14
35
|
if (typeof globalThis?.structuredClone === 'function') {
|
|
@@ -109,6 +130,14 @@ class ManualFormInstance {
|
|
|
109
130
|
get fieldMetadataChanges$() {
|
|
110
131
|
return this.fieldMetadata$.asObservable();
|
|
111
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* Emits after field metadata has been applied to the runtime state.
|
|
135
|
+
* Fires on patchFieldMetadata(), replaceConfig(), resetToSeed(), and
|
|
136
|
+
* persisted config loads (when metadata is replaced).
|
|
137
|
+
*/
|
|
138
|
+
metadataChanges() {
|
|
139
|
+
return this.fieldMetadata$.asObservable();
|
|
140
|
+
}
|
|
112
141
|
get currentConfig() {
|
|
113
142
|
return this.formConfig$.value;
|
|
114
143
|
}
|
|
@@ -147,7 +176,7 @@ class ManualFormInstance {
|
|
|
147
176
|
if (!current) {
|
|
148
177
|
return;
|
|
149
178
|
}
|
|
150
|
-
const next = this.
|
|
179
|
+
const next = this.applyJsonMergePatch(current, patch);
|
|
151
180
|
this.fieldMetadata$.value.set(fieldName, next);
|
|
152
181
|
this.fieldMetadata$.next(new Map(this.fieldMetadata$.value));
|
|
153
182
|
const configClone = deepClone(this.formConfig$.value);
|
|
@@ -191,7 +220,7 @@ class ManualFormInstance {
|
|
|
191
220
|
}
|
|
192
221
|
/**
|
|
193
222
|
* Persists the current configuration and, optionally, the provided form value
|
|
194
|
-
* using
|
|
223
|
+
* using AsyncConfigStorage. By default, stores the FormGroup raw value.
|
|
195
224
|
*/
|
|
196
225
|
saveDraft(value = null) {
|
|
197
226
|
if (!this.storage) {
|
|
@@ -208,7 +237,7 @@ class ManualFormInstance {
|
|
|
208
237
|
},
|
|
209
238
|
version: this.seed.version,
|
|
210
239
|
};
|
|
211
|
-
this.storage.saveConfig(key, payload);
|
|
240
|
+
this.storage.saveConfig(key, payload).pipe(take(1)).subscribe({ error: () => { } });
|
|
212
241
|
}
|
|
213
242
|
publishConfig(config) {
|
|
214
243
|
this.formConfig$.next(config);
|
|
@@ -266,18 +295,37 @@ class ManualFormInstance {
|
|
|
266
295
|
/* ignore */
|
|
267
296
|
}
|
|
268
297
|
}
|
|
269
|
-
/**
|
|
270
|
-
|
|
271
|
-
|
|
298
|
+
/**
|
|
299
|
+
* JSON Merge Patch semantics for metadata patches.
|
|
300
|
+
* `null` remove a propriedade-alvo e objetos vazios são podados.
|
|
301
|
+
*/
|
|
302
|
+
applyJsonMergePatch(target, patch) {
|
|
303
|
+
if (patch == null || typeof patch !== 'object' || Array.isArray(patch)) {
|
|
272
304
|
return { ...target };
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
305
|
+
}
|
|
306
|
+
const out = Array.isArray(target)
|
|
307
|
+
? [...target]
|
|
308
|
+
: { ...target };
|
|
309
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
310
|
+
if (value === null) {
|
|
311
|
+
delete out[key];
|
|
312
|
+
continue;
|
|
277
313
|
}
|
|
278
|
-
|
|
279
|
-
out[
|
|
314
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
315
|
+
const merged = this.applyJsonMergePatch(out[key] && typeof out[key] === 'object' && !Array.isArray(out[key])
|
|
316
|
+
? out[key]
|
|
317
|
+
: {}, value);
|
|
318
|
+
if (merged &&
|
|
319
|
+
typeof merged === 'object' &&
|
|
320
|
+
!Array.isArray(merged) &&
|
|
321
|
+
!Object.keys(merged).length) {
|
|
322
|
+
delete out[key];
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
out[key] = merged;
|
|
326
|
+
continue;
|
|
280
327
|
}
|
|
328
|
+
out[key] = value;
|
|
281
329
|
}
|
|
282
330
|
return out;
|
|
283
331
|
}
|
|
@@ -293,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) {
|
|
@@ -862,6 +1856,10 @@ class ManualFormComponent {
|
|
|
862
1856
|
this.saved.emit(this.instance);
|
|
863
1857
|
this.metadataChange.emit(this.instance.currentConfig);
|
|
864
1858
|
}
|
|
1859
|
+
/** True when host applied [formGroup] directly on <praxis-manual-form>. */
|
|
1860
|
+
isHostTyped() {
|
|
1861
|
+
return !!this.hostFormGroupDirective?.form;
|
|
1862
|
+
}
|
|
865
1863
|
handleReset() {
|
|
866
1864
|
if (!this.instance) {
|
|
867
1865
|
return;
|
|
@@ -870,6 +1868,33 @@ class ManualFormComponent {
|
|
|
870
1868
|
this.hostFormGroupDirective?.resetForm(this.instance.form.getRawValue());
|
|
871
1869
|
this.resetEvent.emit(this.instance);
|
|
872
1870
|
}
|
|
1871
|
+
async runHooks(stage, extras, configOverride) {
|
|
1872
|
+
const registry = this.hooksRegistry;
|
|
1873
|
+
const config = configOverride ?? this.instance?.currentConfig;
|
|
1874
|
+
if (!registry || !config?.hooks) {
|
|
1875
|
+
return true;
|
|
1876
|
+
}
|
|
1877
|
+
const declarations = config.hooks[stage];
|
|
1878
|
+
if (!declarations || declarations.length === 0) {
|
|
1879
|
+
return true;
|
|
1880
|
+
}
|
|
1881
|
+
try {
|
|
1882
|
+
const result = await registry.run(stage, { formGroup: this.formGroup, formConfig: config, extras }, declarations);
|
|
1883
|
+
if (result.stopped && stage !== 'onError' && config.hooks.onError?.length) {
|
|
1884
|
+
await registry.run('onError', { formGroup: this.formGroup, formConfig: config, extras: { ...extras, stopped: result.stopped } }, config.hooks.onError);
|
|
1885
|
+
}
|
|
1886
|
+
return !result.stopped;
|
|
1887
|
+
}
|
|
1888
|
+
catch {
|
|
1889
|
+
if (stage !== 'onError' && config.hooks.onError?.length) {
|
|
1890
|
+
try {
|
|
1891
|
+
await registry.run('onError', { formGroup: this.formGroup, formConfig: config, extras: { ...extras, error: 'hook-exception' } }, config.hooks.onError);
|
|
1892
|
+
}
|
|
1893
|
+
catch { }
|
|
1894
|
+
}
|
|
1895
|
+
return false;
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
873
1898
|
/**
|
|
874
1899
|
* Attempts to open the field metadata editor respecting the component's
|
|
875
1900
|
* editModeEnabled input. No-ops when disabled or without an instance.
|
|
@@ -900,8 +1925,9 @@ class ManualFormComponent {
|
|
|
900
1925
|
return;
|
|
901
1926
|
}
|
|
902
1927
|
const formId = this.formId();
|
|
1928
|
+
const keyId = this.componentKeyId();
|
|
903
1929
|
this.settingsPanel.open({
|
|
904
|
-
id: formId ? `manual-form-editor.${formId}` : 'manual-form-editor',
|
|
1930
|
+
id: keyId ? `manual-form-editor.${keyId}` : (formId ? `manual-form-editor.${formId}` : 'manual-form-editor'),
|
|
905
1931
|
title: this.formTitle() || 'Editor do Formulário',
|
|
906
1932
|
titleIcon: 'tune',
|
|
907
1933
|
content: {
|
|
@@ -910,7 +1936,17 @@ class ManualFormComponent {
|
|
|
910
1936
|
},
|
|
911
1937
|
});
|
|
912
1938
|
}
|
|
913
|
-
|
|
1939
|
+
applyConfigFromAdapter(config) {
|
|
1940
|
+
if (!this.instance) {
|
|
1941
|
+
return;
|
|
1942
|
+
}
|
|
1943
|
+
this.instance.replaceConfig(config);
|
|
1944
|
+
this.resolvedActions = this.normalizeActions(config.actions ?? null);
|
|
1945
|
+
this.metadataChange.emit(this.instance.currentConfig);
|
|
1946
|
+
this.cdr.markForCheck();
|
|
1947
|
+
}
|
|
1948
|
+
async initialize(fromChanges) {
|
|
1949
|
+
this.syncHostFormGroupReference();
|
|
914
1950
|
if (!this.formId()) {
|
|
915
1951
|
return;
|
|
916
1952
|
}
|
|
@@ -921,11 +1957,22 @@ class ManualFormComponent {
|
|
|
921
1957
|
return;
|
|
922
1958
|
}
|
|
923
1959
|
const seed = this.createSeedFromFields(fields);
|
|
1960
|
+
const beforeInitOk = await this.runHooks('beforeInit', { seed }, seed.config);
|
|
1961
|
+
if (!beforeInitOk) {
|
|
1962
|
+
return;
|
|
1963
|
+
}
|
|
1964
|
+
const persistence = { ...(this.persistenceOptions() ?? {}) };
|
|
1965
|
+
const keyId = this.componentKeyId();
|
|
1966
|
+
if (keyId) {
|
|
1967
|
+
persistence.storageKey = `manual-form:${keyId}`;
|
|
1968
|
+
}
|
|
924
1969
|
// Create runtime instance and let it populate our existing FormGroup
|
|
925
|
-
this.instance = this.instanceFactory.create(seed,
|
|
1970
|
+
this.instance = this.instanceFactory.create(seed, persistence, this.formGroup);
|
|
926
1971
|
if (isDevMode())
|
|
927
1972
|
console.debug('[ManualForm] instance created with seed; current controls', Object.keys(this.formGroup.controls || {}));
|
|
928
1973
|
this.applyInstanceToTemplate(fields);
|
|
1974
|
+
this.registerToolbarBindings(fields);
|
|
1975
|
+
this.attachMetadataSync();
|
|
929
1976
|
// Auto-save value changes with debounce
|
|
930
1977
|
if (this.enableAutoSave() && this.instance) {
|
|
931
1978
|
this.instance.form.valueChanges
|
|
@@ -937,6 +1984,7 @@ class ManualFormComponent {
|
|
|
937
1984
|
catch { }
|
|
938
1985
|
});
|
|
939
1986
|
}
|
|
1987
|
+
await this.runHooks('afterInit', { seed });
|
|
940
1988
|
this.cdr.markForCheck();
|
|
941
1989
|
}
|
|
942
1990
|
collectFields() {
|
|
@@ -960,10 +2008,30 @@ class ManualFormComponent {
|
|
|
960
2008
|
control: dir.control,
|
|
961
2009
|
component,
|
|
962
2010
|
selector: this.resolveSelector(component, dir),
|
|
2011
|
+
element: this.resolveHostElement(component, dir),
|
|
963
2012
|
});
|
|
964
2013
|
}
|
|
965
2014
|
return result;
|
|
966
2015
|
}
|
|
2016
|
+
componentKeyId() {
|
|
2017
|
+
const key = this.componentKeys.buildComponentId({
|
|
2018
|
+
componentType: 'praxis-manual-form',
|
|
2019
|
+
componentId: this.formId(),
|
|
2020
|
+
instanceKey: this.componentInstanceId(),
|
|
2021
|
+
componentRef: this,
|
|
2022
|
+
route: this.route,
|
|
2023
|
+
requireComponentId: true,
|
|
2024
|
+
});
|
|
2025
|
+
if (!key)
|
|
2026
|
+
this.warnMissingId();
|
|
2027
|
+
return key;
|
|
2028
|
+
}
|
|
2029
|
+
warnMissingId() {
|
|
2030
|
+
if (this.warnedMissingId)
|
|
2031
|
+
return;
|
|
2032
|
+
this.warnedMissingId = true;
|
|
2033
|
+
console.warn('[ManualForm] formId is required for config persistence.');
|
|
2034
|
+
}
|
|
967
2035
|
resolveValueAccessor(dir) {
|
|
968
2036
|
const accessor = dir.valueAccessor;
|
|
969
2037
|
if (!accessor) {
|
|
@@ -986,6 +2054,10 @@ class ManualFormComponent {
|
|
|
986
2054
|
?? dir?._elementRef?.nativeElement;
|
|
987
2055
|
return element?.tagName?.toLowerCase();
|
|
988
2056
|
}
|
|
2057
|
+
resolveHostElement(component, dir) {
|
|
2058
|
+
return component?.elementRef?.nativeElement
|
|
2059
|
+
?? dir?._elementRef?.nativeElement;
|
|
2060
|
+
}
|
|
989
2061
|
createSeedFromFields(fields) {
|
|
990
2062
|
const metadataList = fields.map((field) => this.buildMetadataForField(field));
|
|
991
2063
|
const actions = this.normalizeActions(this.actions() ?? null);
|
|
@@ -1038,6 +2110,234 @@ class ManualFormComponent {
|
|
|
1038
2110
|
}
|
|
1039
2111
|
}
|
|
1040
2112
|
}
|
|
2113
|
+
registerToolbarBindings(fields) {
|
|
2114
|
+
this.toolbarSubscriptions.forEach((sub) => sub.unsubscribe());
|
|
2115
|
+
this.toolbarSubscriptions = [];
|
|
2116
|
+
if (!this.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
|
+
}
|
|
1041
2341
|
buildMetadataForField(field) {
|
|
1042
2342
|
const existing = this.extractExistingMetadata(field.component);
|
|
1043
2343
|
const label = existing?.label ?? this.inferLabel(field, existing);
|
|
@@ -1051,18 +2351,34 @@ class ManualFormComponent {
|
|
|
1051
2351
|
validators: { ...existing?.validators, ...validators },
|
|
1052
2352
|
};
|
|
1053
2353
|
}
|
|
2354
|
+
syncHostFormGroupReference() {
|
|
2355
|
+
const hostForm = this.hostFormGroupDirective?.form;
|
|
2356
|
+
if (hostForm && this.formGroup !== hostForm) {
|
|
2357
|
+
this.formGroup = hostForm;
|
|
2358
|
+
if (isDevMode())
|
|
2359
|
+
console.debug('[ManualForm] Synced FormGroup from host FormGroupDirective');
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
1054
2362
|
getControlByPath(path) {
|
|
1055
2363
|
if (!Array.isArray(path) || path.length === 0)
|
|
1056
2364
|
return null;
|
|
1057
2365
|
return this.formGroup.get(path);
|
|
1058
2366
|
}
|
|
1059
|
-
|
|
1060
|
-
if (
|
|
1061
|
-
return;
|
|
1062
|
-
|
|
2367
|
+
getDirPath(dir) {
|
|
2368
|
+
if (Array.isArray(dir?.path) && dir.path.length) {
|
|
2369
|
+
return dir.path;
|
|
2370
|
+
}
|
|
2371
|
+
if (typeof dir?.name === 'string' && dir.name.length) {
|
|
2372
|
+
return [dir.name];
|
|
2373
|
+
}
|
|
2374
|
+
return [];
|
|
2375
|
+
}
|
|
2376
|
+
ensureGroupPath(path) {
|
|
1063
2377
|
let group = this.formGroup;
|
|
1064
|
-
|
|
1065
|
-
|
|
2378
|
+
if (!Array.isArray(path) || path.length === 0) {
|
|
2379
|
+
return group;
|
|
2380
|
+
}
|
|
2381
|
+
for (const seg of path) {
|
|
1066
2382
|
const existing = group.get(seg);
|
|
1067
2383
|
if (existing instanceof FormGroup) {
|
|
1068
2384
|
group = existing;
|
|
@@ -1073,6 +2389,13 @@ class ManualFormComponent {
|
|
|
1073
2389
|
group = next;
|
|
1074
2390
|
}
|
|
1075
2391
|
}
|
|
2392
|
+
return group;
|
|
2393
|
+
}
|
|
2394
|
+
ensureControlPath(path) {
|
|
2395
|
+
if (!Array.isArray(path) || path.length === 0)
|
|
2396
|
+
return;
|
|
2397
|
+
const leaf = path[path.length - 1];
|
|
2398
|
+
const group = this.ensureGroupPath(path.slice(0, -1));
|
|
1076
2399
|
if (!group.get(leaf)) {
|
|
1077
2400
|
group.addControl(leaf, new FormControl());
|
|
1078
2401
|
}
|
|
@@ -1112,8 +2435,13 @@ class ManualFormComponent {
|
|
|
1112
2435
|
}
|
|
1113
2436
|
inferControlType(field) {
|
|
1114
2437
|
const selector = field.selector?.toLowerCase();
|
|
1115
|
-
if (selector
|
|
1116
|
-
|
|
2438
|
+
if (selector) {
|
|
2439
|
+
const fromLocal = this.selectorToControlType[selector];
|
|
2440
|
+
if (fromLocal)
|
|
2441
|
+
return fromLocal;
|
|
2442
|
+
const fromRegistry = this.selectorRegistry?.resolve(selector);
|
|
2443
|
+
if (fromRegistry)
|
|
2444
|
+
return fromRegistry;
|
|
1117
2445
|
}
|
|
1118
2446
|
const ctorName = field.component?.constructor?.name ?? '';
|
|
1119
2447
|
for (const [key, value] of Object.entries(this.constructorToControlType)) {
|
|
@@ -1183,15 +2511,15 @@ class ManualFormComponent {
|
|
|
1183
2511
|
};
|
|
1184
2512
|
}
|
|
1185
2513
|
normalizeActions(source) {
|
|
1186
|
-
const base = deepClone(DEFAULT_ACTIONS);
|
|
2514
|
+
const base = deepClone(DEFAULT_ACTIONS$1);
|
|
1187
2515
|
if (!source) {
|
|
1188
2516
|
return base;
|
|
1189
2517
|
}
|
|
1190
2518
|
return {
|
|
1191
|
-
submit: mergeAction(base.submit, source.submit),
|
|
1192
|
-
cancel: mergeAction(base.cancel, source.cancel),
|
|
1193
|
-
reset: mergeAction(base.reset, source.reset),
|
|
1194
|
-
custom: source.custom ? source.custom.map(makeAction) : base.custom,
|
|
2519
|
+
submit: mergeAction$1(base.submit, source.submit),
|
|
2520
|
+
cancel: mergeAction$1(base.cancel, source.cancel),
|
|
2521
|
+
reset: mergeAction$1(base.reset, source.reset),
|
|
2522
|
+
custom: source.custom ? source.custom.map(makeAction$1) : base.custom,
|
|
1195
2523
|
position: source.position ?? base.position,
|
|
1196
2524
|
orientation: source.orientation ?? base.orientation,
|
|
1197
2525
|
spacing: source.spacing ?? base.spacing,
|
|
@@ -1209,36 +2537,37 @@ class ManualFormComponent {
|
|
|
1209
2537
|
};
|
|
1210
2538
|
}
|
|
1211
2539
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: ManualFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1212
|
-
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: [
|
|
1213
2541
|
// Provide a ControlContainer at the host boundary so projected formControlName can resolve it
|
|
1214
2542
|
{ provide: ControlContainer, useExisting: ManualFormComponent },
|
|
1215
|
-
], queries: [{ propertyName: "formControls", predicate: FormControlName, descendants: true }], ngImport: i0, template: "<div class=\"pdx-manual-form\">\n @if (showHeader()) {\n
|
|
2543
|
+
], queries: [{ propertyName: "formControls", predicate: FormControlName, descendants: true }], ngImport: i0, template: "<div class=\"pdx-manual-form\">\n @if (showHeader()) {\n <praxis-manual-form-header [instance]=\"instance\" [title]=\"formTitle()\" [description]=\"formDescription()\"\n [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 });
|
|
1216
2544
|
}
|
|
1217
2545
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: ManualFormComponent, decorators: [{
|
|
1218
2546
|
type: Component,
|
|
1219
2547
|
args: [{ selector: 'praxis-manual-form', standalone: true, imports: [
|
|
1220
2548
|
CommonModule,
|
|
1221
2549
|
ReactiveFormsModule,
|
|
2550
|
+
PraxisAiAssistantComponent,
|
|
1222
2551
|
ManualFormHeaderComponent,
|
|
1223
2552
|
ManualFormActionsComponent,
|
|
1224
2553
|
], providers: [
|
|
1225
2554
|
// Provide a ControlContainer at the host boundary so projected formControlName can resolve it
|
|
1226
2555
|
{ provide: ControlContainer, useExisting: ManualFormComponent },
|
|
1227
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"pdx-manual-form\">\n @if (showHeader()) {\n
|
|
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"] }]
|
|
1228
2557
|
}], ctorParameters: () => [], propDecorators: { formControls: [{
|
|
1229
2558
|
type: ContentChildren,
|
|
1230
2559
|
args: [FormControlName, { descendants: true }]
|
|
1231
2560
|
}] } });
|
|
1232
|
-
const DEFAULT_ACTIONS = {
|
|
1233
|
-
submit: makeAction({ id: 'submit', label: 'Salvar', visible: true, type: 'submit', color: 'primary' }),
|
|
1234
|
-
cancel: makeAction({ id: 'cancel', label: 'Cancelar', visible: false, type: 'button' }),
|
|
1235
|
-
reset: makeAction({ id: 'reset', label: 'Restaurar', visible: true, type: 'reset' }),
|
|
2561
|
+
const DEFAULT_ACTIONS$1 = {
|
|
2562
|
+
submit: makeAction$1({ id: 'submit', label: 'Salvar', visible: true, type: 'submit', color: 'primary' }),
|
|
2563
|
+
cancel: makeAction$1({ id: 'cancel', label: 'Cancelar', visible: false, type: 'button' }),
|
|
2564
|
+
reset: makeAction$1({ id: 'reset', label: 'Restaurar', visible: true, type: 'reset' }),
|
|
1236
2565
|
custom: [],
|
|
1237
2566
|
position: 'right',
|
|
1238
2567
|
orientation: 'horizontal',
|
|
1239
2568
|
spacing: 'normal',
|
|
1240
2569
|
};
|
|
1241
|
-
function makeAction(action) {
|
|
2570
|
+
function makeAction$1(action) {
|
|
1242
2571
|
return {
|
|
1243
2572
|
id: action.id ?? 'action',
|
|
1244
2573
|
label: action.label ?? 'Ação',
|
|
@@ -1255,7 +2584,7 @@ function makeAction(action) {
|
|
|
1255
2584
|
shortcut: action.shortcut,
|
|
1256
2585
|
};
|
|
1257
2586
|
}
|
|
1258
|
-
function mergeAction(base, override) {
|
|
2587
|
+
function mergeAction$1(base, override) {
|
|
1259
2588
|
if (!override) {
|
|
1260
2589
|
return base;
|
|
1261
2590
|
}
|
|
@@ -1265,24 +2594,7 @@ function mergeAction(base, override) {
|
|
|
1265
2594
|
visible: override.visible !== undefined ? override.visible : base.visible,
|
|
1266
2595
|
};
|
|
1267
2596
|
}
|
|
1268
|
-
const DEFAULT_SELECTOR_TO_CONTROL_TYPE =
|
|
1269
|
-
'pdx-text-input': FieldControlType.INPUT,
|
|
1270
|
-
'pdx-material-textarea': FieldControlType.TEXTAREA,
|
|
1271
|
-
'pdx-number-input': FieldControlType.NUMERIC_TEXT_BOX,
|
|
1272
|
-
'pdx-material-currency': FieldControlType.CURRENCY_INPUT,
|
|
1273
|
-
'pdx-material-datepicker': FieldControlType.DATE_PICKER,
|
|
1274
|
-
'pdx-material-date-range': FieldControlType.DATE_RANGE,
|
|
1275
|
-
'pdx-material-timepicker': FieldControlType.TIME_PICKER,
|
|
1276
|
-
'pdx-material-colorpicker': FieldControlType.COLOR_PICKER,
|
|
1277
|
-
'pdx-material-select': FieldControlType.SELECT,
|
|
1278
|
-
'pdx-material-autocomplete': FieldControlType.AUTO_COMPLETE,
|
|
1279
|
-
'pdx-material-checkbox-group': FieldControlType.CHECKBOX,
|
|
1280
|
-
'pdx-material-radio-group': FieldControlType.RADIO,
|
|
1281
|
-
'pdx-material-slide-toggle': FieldControlType.TOGGLE,
|
|
1282
|
-
'pdx-material-slider': FieldControlType.SLIDER,
|
|
1283
|
-
'pdx-material-range-slider': FieldControlType.RANGE_SLIDER,
|
|
1284
|
-
'pdx-material-file-upload': FieldControlType.FILE_UPLOAD,
|
|
1285
|
-
};
|
|
2597
|
+
const DEFAULT_SELECTOR_TO_CONTROL_TYPE = DEFAULT_FIELD_SELECTOR_CONTROL_TYPE_MAP;
|
|
1286
2598
|
const DEFAULT_CONSTRUCTOR_TO_CONTROL_TYPE = {
|
|
1287
2599
|
TextInput: FieldControlType.INPUT,
|
|
1288
2600
|
Textarea: FieldControlType.TEXTAREA,
|
|
@@ -1308,6 +2620,30 @@ class ManualFormConfigEditorComponent {
|
|
|
1308
2620
|
all = [];
|
|
1309
2621
|
filtered = [];
|
|
1310
2622
|
onlyHidden = false;
|
|
2623
|
+
actionsModel = deepClone(DEFAULT_ACTIONS);
|
|
2624
|
+
messagesModel = {};
|
|
2625
|
+
behaviorModel = {};
|
|
2626
|
+
hintsModel = createDefaultHints();
|
|
2627
|
+
hooksText = createDefaultHooksText();
|
|
2628
|
+
hooksErrors = {};
|
|
2629
|
+
formRulesText = '';
|
|
2630
|
+
formRulesStateText = '';
|
|
2631
|
+
formRulesError = '';
|
|
2632
|
+
formRulesStateError = '';
|
|
2633
|
+
hookStages = [
|
|
2634
|
+
'beforeInit',
|
|
2635
|
+
'afterInit',
|
|
2636
|
+
'beforeValidate',
|
|
2637
|
+
'afterValidate',
|
|
2638
|
+
'beforeSubmit',
|
|
2639
|
+
'afterSubmit',
|
|
2640
|
+
'onError',
|
|
2641
|
+
];
|
|
2642
|
+
actionBlocks = [
|
|
2643
|
+
{ key: 'submit', label: 'Salvar' },
|
|
2644
|
+
{ key: 'cancel', label: 'Cancelar' },
|
|
2645
|
+
{ key: 'reset', label: 'Resetar' },
|
|
2646
|
+
];
|
|
1311
2647
|
// SettingsValueProvider observables
|
|
1312
2648
|
isDirty$ = new BehaviorSubject(false);
|
|
1313
2649
|
isValid$ = new BehaviorSubject(true);
|
|
@@ -1364,6 +2700,206 @@ class ManualFormConfigEditorComponent {
|
|
|
1364
2700
|
}
|
|
1365
2701
|
catch { }
|
|
1366
2702
|
}
|
|
2703
|
+
updateActionField(key, field, value) {
|
|
2704
|
+
const current = this.actionsModel[key];
|
|
2705
|
+
this.actionsModel = {
|
|
2706
|
+
...this.actionsModel,
|
|
2707
|
+
[key]: {
|
|
2708
|
+
...current,
|
|
2709
|
+
[field]: value,
|
|
2710
|
+
},
|
|
2711
|
+
};
|
|
2712
|
+
this.applyConfigPatch({ actions: this.actionsModel });
|
|
2713
|
+
}
|
|
2714
|
+
updateActionsLayout(field, value) {
|
|
2715
|
+
this.actionsModel = {
|
|
2716
|
+
...this.actionsModel,
|
|
2717
|
+
[field]: value,
|
|
2718
|
+
};
|
|
2719
|
+
this.applyConfigPatch({ actions: this.actionsModel });
|
|
2720
|
+
}
|
|
2721
|
+
addCustomAction() {
|
|
2722
|
+
const nextId = this.generateCustomId();
|
|
2723
|
+
const nextAction = makeAction({ id: nextId, label: 'Ação' });
|
|
2724
|
+
const custom = [...(this.actionsModel.custom || []), nextAction];
|
|
2725
|
+
this.actionsModel = { ...this.actionsModel, custom };
|
|
2726
|
+
this.applyConfigPatch({ actions: this.actionsModel });
|
|
2727
|
+
}
|
|
2728
|
+
removeCustomAction(action) {
|
|
2729
|
+
const custom = (this.actionsModel.custom || []).filter((item) => item !== action);
|
|
2730
|
+
this.actionsModel = { ...this.actionsModel, custom };
|
|
2731
|
+
this.applyConfigPatch({ actions: this.actionsModel });
|
|
2732
|
+
}
|
|
2733
|
+
updateCustomActionField(action, field, value) {
|
|
2734
|
+
const custom = (this.actionsModel.custom || []).map((item) => {
|
|
2735
|
+
if (item !== action)
|
|
2736
|
+
return item;
|
|
2737
|
+
return { ...item, [field]: value };
|
|
2738
|
+
});
|
|
2739
|
+
this.actionsModel = { ...this.actionsModel, custom };
|
|
2740
|
+
this.applyConfigPatch({ actions: this.actionsModel });
|
|
2741
|
+
}
|
|
2742
|
+
updateMessage(key, value) {
|
|
2743
|
+
this.messagesModel = {
|
|
2744
|
+
...this.messagesModel,
|
|
2745
|
+
[key]: value,
|
|
2746
|
+
};
|
|
2747
|
+
this.applyConfigPatch({ messages: this.messagesModel });
|
|
2748
|
+
}
|
|
2749
|
+
updateConfirmation(key, value) {
|
|
2750
|
+
this.messagesModel = {
|
|
2751
|
+
...this.messagesModel,
|
|
2752
|
+
confirmations: {
|
|
2753
|
+
...(this.messagesModel.confirmations || {}),
|
|
2754
|
+
[key]: value,
|
|
2755
|
+
},
|
|
2756
|
+
};
|
|
2757
|
+
this.applyConfigPatch({ messages: this.messagesModel });
|
|
2758
|
+
}
|
|
2759
|
+
updateLoading(key, value) {
|
|
2760
|
+
this.messagesModel = {
|
|
2761
|
+
...this.messagesModel,
|
|
2762
|
+
loading: {
|
|
2763
|
+
...(this.messagesModel.loading || {}),
|
|
2764
|
+
[key]: value,
|
|
2765
|
+
},
|
|
2766
|
+
};
|
|
2767
|
+
this.applyConfigPatch({ messages: this.messagesModel });
|
|
2768
|
+
}
|
|
2769
|
+
updateCustomMessage(action, kind, value) {
|
|
2770
|
+
const actionId = action.id || '';
|
|
2771
|
+
this.messagesModel = {
|
|
2772
|
+
...this.messagesModel,
|
|
2773
|
+
customActions: {
|
|
2774
|
+
...(this.messagesModel.customActions || {}),
|
|
2775
|
+
[actionId]: {
|
|
2776
|
+
...(this.messagesModel.customActions || {})[actionId],
|
|
2777
|
+
[kind]: value,
|
|
2778
|
+
},
|
|
2779
|
+
},
|
|
2780
|
+
};
|
|
2781
|
+
this.applyConfigPatch({ messages: this.messagesModel });
|
|
2782
|
+
}
|
|
2783
|
+
updateBehavior(key, value) {
|
|
2784
|
+
this.behaviorModel = {
|
|
2785
|
+
...this.behaviorModel,
|
|
2786
|
+
[key]: value,
|
|
2787
|
+
};
|
|
2788
|
+
this.applyConfigPatch({ behavior: this.behaviorModel });
|
|
2789
|
+
}
|
|
2790
|
+
updateHints(scope, key, value) {
|
|
2791
|
+
this.hintsModel = {
|
|
2792
|
+
...this.hintsModel,
|
|
2793
|
+
[scope]: {
|
|
2794
|
+
...this.hintsModel[scope],
|
|
2795
|
+
[key]: value,
|
|
2796
|
+
},
|
|
2797
|
+
};
|
|
2798
|
+
this.applyConfigPatch({ hints: this.hintsModel });
|
|
2799
|
+
}
|
|
2800
|
+
hookLabel(stage) {
|
|
2801
|
+
const map = {
|
|
2802
|
+
beforeInit: 'beforeInit',
|
|
2803
|
+
afterInit: 'afterInit',
|
|
2804
|
+
beforeValidate: 'beforeValidate',
|
|
2805
|
+
afterValidate: 'afterValidate',
|
|
2806
|
+
beforeSubmit: 'beforeSubmit',
|
|
2807
|
+
afterSubmit: 'afterSubmit',
|
|
2808
|
+
onError: 'onError',
|
|
2809
|
+
};
|
|
2810
|
+
return map[stage] || stage;
|
|
2811
|
+
}
|
|
2812
|
+
onHookTextChange(stage, value) {
|
|
2813
|
+
this.hooksText = { ...this.hooksText, [stage]: value };
|
|
2814
|
+
}
|
|
2815
|
+
resetHooks() {
|
|
2816
|
+
this.hooksText = buildHooksText(this.instance.currentConfig.hooks);
|
|
2817
|
+
this.hooksErrors = {};
|
|
2818
|
+
}
|
|
2819
|
+
applyHooks() {
|
|
2820
|
+
const next = {};
|
|
2821
|
+
const errors = {};
|
|
2822
|
+
for (const stage of this.hookStages) {
|
|
2823
|
+
const raw = (this.hooksText[stage] || '').trim();
|
|
2824
|
+
if (!raw)
|
|
2825
|
+
continue;
|
|
2826
|
+
try {
|
|
2827
|
+
const parsed = JSON.parse(raw);
|
|
2828
|
+
if (!Array.isArray(parsed)) {
|
|
2829
|
+
errors[stage] = 'Precisa ser um array JSON.';
|
|
2830
|
+
continue;
|
|
2831
|
+
}
|
|
2832
|
+
next[stage] = parsed;
|
|
2833
|
+
}
|
|
2834
|
+
catch {
|
|
2835
|
+
errors[stage] = 'JSON inválido.';
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
this.hooksErrors = errors;
|
|
2839
|
+
if (Object.keys(errors).length > 0) {
|
|
2840
|
+
return;
|
|
2841
|
+
}
|
|
2842
|
+
this.applyConfigPatch({ hooks: next });
|
|
2843
|
+
}
|
|
2844
|
+
onRulesTextChange(value) {
|
|
2845
|
+
this.formRulesText = value;
|
|
2846
|
+
}
|
|
2847
|
+
onRulesStateTextChange(value) {
|
|
2848
|
+
this.formRulesStateText = value;
|
|
2849
|
+
}
|
|
2850
|
+
applyRules() {
|
|
2851
|
+
this.formRulesError = '';
|
|
2852
|
+
this.formRulesStateError = '';
|
|
2853
|
+
let rules;
|
|
2854
|
+
let rulesState;
|
|
2855
|
+
const rulesRaw = this.formRulesText.trim();
|
|
2856
|
+
if (rulesRaw) {
|
|
2857
|
+
try {
|
|
2858
|
+
const parsed = JSON.parse(rulesRaw);
|
|
2859
|
+
if (!Array.isArray(parsed)) {
|
|
2860
|
+
this.formRulesError = 'formRules precisa ser um array JSON.';
|
|
2861
|
+
return;
|
|
2862
|
+
}
|
|
2863
|
+
rules = parsed;
|
|
2864
|
+
}
|
|
2865
|
+
catch {
|
|
2866
|
+
this.formRulesError = 'JSON inválido em formRules.';
|
|
2867
|
+
return;
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
const stateRaw = this.formRulesStateText.trim();
|
|
2871
|
+
if (stateRaw) {
|
|
2872
|
+
try {
|
|
2873
|
+
rulesState = JSON.parse(stateRaw);
|
|
2874
|
+
}
|
|
2875
|
+
catch {
|
|
2876
|
+
this.formRulesStateError = 'JSON inválido em formRulesState.';
|
|
2877
|
+
return;
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
this.applyConfigPatch({ formRules: rules, formRulesState: rulesState });
|
|
2881
|
+
}
|
|
2882
|
+
applyCascadePatch(patch) {
|
|
2883
|
+
const entries = Object.entries(patch || {});
|
|
2884
|
+
if (!entries.length) {
|
|
2885
|
+
return;
|
|
2886
|
+
}
|
|
2887
|
+
for (const [fieldName, fieldPatch] of entries) {
|
|
2888
|
+
try {
|
|
2889
|
+
this.instance.patchFieldMetadata(fieldName, fieldPatch);
|
|
2890
|
+
}
|
|
2891
|
+
catch { }
|
|
2892
|
+
}
|
|
2893
|
+
try {
|
|
2894
|
+
this.instance.saveDraft();
|
|
2895
|
+
this.refresh();
|
|
2896
|
+
this.isDirty$.next(true);
|
|
2897
|
+
}
|
|
2898
|
+
catch { }
|
|
2899
|
+
}
|
|
2900
|
+
get cascadeFields() {
|
|
2901
|
+
return this.instance.currentConfig.fieldMetadata || [];
|
|
2902
|
+
}
|
|
1367
2903
|
getSettingsValue() {
|
|
1368
2904
|
return {
|
|
1369
2905
|
fieldStates: (this.instance.currentConfig.fieldMetadata || []).map((f) => ({
|
|
@@ -1373,6 +2909,13 @@ class ManualFormConfigEditorComponent {
|
|
|
1373
2909
|
readOnly: !!f.readOnly,
|
|
1374
2910
|
disabled: !!f.disabled,
|
|
1375
2911
|
})),
|
|
2912
|
+
actions: deepClone(this.actionsModel),
|
|
2913
|
+
messages: deepClone(this.messagesModel),
|
|
2914
|
+
behavior: deepClone(this.behaviorModel),
|
|
2915
|
+
hints: deepClone(this.hintsModel),
|
|
2916
|
+
hooks: deepClone(this.instance.currentConfig.hooks || {}),
|
|
2917
|
+
formRules: deepClone(this.instance.currentConfig.formRules || []),
|
|
2918
|
+
formRulesState: deepClone(this.instance.currentConfig.formRulesState),
|
|
1376
2919
|
};
|
|
1377
2920
|
}
|
|
1378
2921
|
onSave() {
|
|
@@ -1383,127 +2926,2038 @@ class ManualFormConfigEditorComponent {
|
|
|
1383
2926
|
}
|
|
1384
2927
|
refresh() {
|
|
1385
2928
|
this.all = (this.instance.currentConfig.fieldMetadata || []).map(f => ({ ...f }));
|
|
2929
|
+
this.actionsModel = this.normalizeActions(this.instance.currentConfig.actions);
|
|
2930
|
+
this.messagesModel = deepClone(this.instance.currentConfig.messages || {});
|
|
2931
|
+
this.behaviorModel = deepClone(this.instance.currentConfig.behavior || {});
|
|
2932
|
+
this.hintsModel = createHints(this.instance.currentConfig.hints);
|
|
2933
|
+
this.hooksText = buildHooksText(this.instance.currentConfig.hooks);
|
|
2934
|
+
this.formRulesText = stringifyJson(this.instance.currentConfig.formRules, true);
|
|
2935
|
+
this.formRulesStateText = stringifyJson(this.instance.currentConfig.formRulesState, false);
|
|
1386
2936
|
this.applyFilter();
|
|
1387
2937
|
}
|
|
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
|
+
}
|
|
1388
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 });
|
|
1389
|
-
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: `
|
|
1390
2988
|
<div class="mf-editor">
|
|
1391
2989
|
<div class="mf-editor__toolbar">
|
|
1392
|
-
<
|
|
2990
|
+
<div class="mf-editor__title">
|
|
2991
|
+
<span>Configurações do formulário</span>
|
|
2992
|
+
<small class="mf-editor__subtitle">Campos, ações, mensagens e comportamento</small>
|
|
2993
|
+
</div>
|
|
1393
2994
|
<div class="spacer"></div>
|
|
1394
|
-
<button type="button" (click)="close()">Fechar</button>
|
|
2995
|
+
<button type="button" class="mf-btn" (click)="close()">Fechar</button>
|
|
1395
2996
|
</div>
|
|
1396
2997
|
|
|
1397
|
-
<
|
|
1398
|
-
<
|
|
1399
|
-
<div class="
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
<div class="col col--req">Obrigatório</div>
|
|
1404
|
-
<div class="col col--ro">Som. leitura</div>
|
|
1405
|
-
<div class="col col--dis">Desabilitado</div>
|
|
1406
|
-
</div>
|
|
1407
|
-
|
|
1408
|
-
<div class="mf-editor__row" *ngFor="let f of filtered">
|
|
1409
|
-
<div class="col col--name"><code>{{ f.name }}</code></div>
|
|
1410
|
-
<div class="col col--label">{{ f.label || f.name }}</div>
|
|
1411
|
-
<div class="col col--type">{{ f.controlType }}</div>
|
|
1412
|
-
<div class="col col--vis">
|
|
1413
|
-
<label class="toggle">
|
|
1414
|
-
<input type="checkbox" [checked]="!f.hidden" (change)="toggleVisibility(f, $any($event.target).checked)" />
|
|
1415
|
-
<span>{{ f.hidden ? 'Oculto' : 'Visível' }}</span>
|
|
2998
|
+
<mat-tab-group class="mf-tabs">
|
|
2999
|
+
<mat-tab label="Campos">
|
|
3000
|
+
<div class="mf-tab">
|
|
3001
|
+
<label class="mf-editor__filter">
|
|
3002
|
+
<input type="checkbox" [(ngModel)]="onlyHidden" (change)="applyFilter()" />
|
|
3003
|
+
<span>Mostrar apenas ocultos</span>
|
|
1416
3004
|
</label>
|
|
3005
|
+
<div class="mf-editor__list">
|
|
3006
|
+
<div class="mf-editor__row mf-editor__row--head">
|
|
3007
|
+
<div class="col col--name">Campo</div>
|
|
3008
|
+
<div class="col col--label">Rótulo</div>
|
|
3009
|
+
<div class="col col--type">Tipo</div>
|
|
3010
|
+
<div class="col col--vis">Visível</div>
|
|
3011
|
+
<div class="col col--req">Obrigatório</div>
|
|
3012
|
+
<div class="col col--ro">Som. leitura</div>
|
|
3013
|
+
<div class="col col--dis">Desabilitado</div>
|
|
3014
|
+
</div>
|
|
3015
|
+
|
|
3016
|
+
@for (f of filtered; track f.name) {
|
|
3017
|
+
<div class="mf-editor__row">
|
|
3018
|
+
<div class="col col--name"><code>{{ f.name }}</code></div>
|
|
3019
|
+
<div class="col col--label">{{ f.label || f.name }}</div>
|
|
3020
|
+
<div class="col col--type">{{ f.controlType }}</div>
|
|
3021
|
+
<div class="col col--vis">
|
|
3022
|
+
<label class="toggle">
|
|
3023
|
+
<input type="checkbox" [checked]="!f.hidden" (change)="toggleVisibility(f, $any($event.target).checked)" />
|
|
3024
|
+
<span>{{ f.hidden ? 'Oculto' : 'Visível' }}</span>
|
|
3025
|
+
</label>
|
|
3026
|
+
</div>
|
|
3027
|
+
<div class="col col--req">
|
|
3028
|
+
<label class="toggle">
|
|
3029
|
+
<input type="checkbox" [checked]="!!f.required" (change)="toggleRequired(f, $any($event.target).checked)" />
|
|
3030
|
+
<span>{{ f.required ? 'Sim' : 'Não' }}</span>
|
|
3031
|
+
</label>
|
|
3032
|
+
</div>
|
|
3033
|
+
<div class="col col--ro">
|
|
3034
|
+
<label class="toggle">
|
|
3035
|
+
<input type="checkbox" [checked]="!!f.readOnly" (change)="toggleReadOnly(f, $any($event.target).checked)" />
|
|
3036
|
+
<span>{{ f.readOnly ? 'Sim' : 'Não' }}</span>
|
|
3037
|
+
</label>
|
|
3038
|
+
</div>
|
|
3039
|
+
<div class="col col--dis">
|
|
3040
|
+
<label class="toggle">
|
|
3041
|
+
<input type="checkbox" [checked]="!!f.disabled" (change)="toggleDisabled(f, $any($event.target).checked)" />
|
|
3042
|
+
<span>{{ f.disabled ? 'Sim' : 'Não' }}</span>
|
|
3043
|
+
</label>
|
|
3044
|
+
</div>
|
|
3045
|
+
</div>
|
|
3046
|
+
}
|
|
3047
|
+
</div>
|
|
1417
3048
|
</div>
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
3049
|
+
</mat-tab>
|
|
3050
|
+
|
|
3051
|
+
<mat-tab label="Ações">
|
|
3052
|
+
<div class="mf-tab">
|
|
3053
|
+
<section class="mf-section">
|
|
3054
|
+
<div class="mf-section__header">
|
|
3055
|
+
<div>
|
|
3056
|
+
<h3>Botões padrão</h3>
|
|
3057
|
+
<p class="mf-help">Controle rótulos, visibilidade e estilo dos botões básicos.</p>
|
|
3058
|
+
</div>
|
|
3059
|
+
</div>
|
|
3060
|
+
<div class="mf-actions-grid">
|
|
3061
|
+
@for (block of actionBlocks; track block.key) {
|
|
3062
|
+
<div class="mf-action-card">
|
|
3063
|
+
<div class="mf-action-card__title">{{ block.label }}</div>
|
|
3064
|
+
<mat-slide-toggle
|
|
3065
|
+
[checked]="actionsModel[block.key].visible"
|
|
3066
|
+
(change)="updateActionField(block.key, 'visible', $event.checked)"
|
|
3067
|
+
>
|
|
3068
|
+
Visível
|
|
3069
|
+
</mat-slide-toggle>
|
|
3070
|
+
<mat-form-field appearance="outline">
|
|
3071
|
+
<mat-label>Label</mat-label>
|
|
3072
|
+
<input
|
|
3073
|
+
matInput
|
|
3074
|
+
[value]="actionsModel[block.key].label"
|
|
3075
|
+
(input)="updateActionField(block.key, 'label', $any($event.target).value)"
|
|
3076
|
+
/>
|
|
3077
|
+
</mat-form-field>
|
|
3078
|
+
<mat-form-field appearance="outline">
|
|
3079
|
+
<mat-label>Ícone</mat-label>
|
|
3080
|
+
<input
|
|
3081
|
+
matInput
|
|
3082
|
+
[value]="actionsModel[block.key].icon || ''"
|
|
3083
|
+
(input)="updateActionField(block.key, 'icon', $any($event.target).value)"
|
|
3084
|
+
/>
|
|
3085
|
+
</mat-form-field>
|
|
3086
|
+
<mat-form-field appearance="outline">
|
|
3087
|
+
<mat-label>Variante</mat-label>
|
|
3088
|
+
<mat-select
|
|
3089
|
+
[value]="actionsModel[block.key].variant || null"
|
|
3090
|
+
(selectionChange)="updateActionField(block.key, 'variant', $event.value)"
|
|
3091
|
+
>
|
|
3092
|
+
<mat-option [value]="null">Padrão</mat-option>
|
|
3093
|
+
<mat-option value="raised">Elevado</mat-option>
|
|
3094
|
+
<mat-option value="stroked">Contornado</mat-option>
|
|
3095
|
+
<mat-option value="flat">Plano</mat-option>
|
|
3096
|
+
<mat-option value="fab">Flutuante</mat-option>
|
|
3097
|
+
</mat-select>
|
|
3098
|
+
</mat-form-field>
|
|
3099
|
+
<mat-form-field appearance="outline">
|
|
3100
|
+
<mat-label>Cor</mat-label>
|
|
3101
|
+
<mat-select
|
|
3102
|
+
[value]="actionsModel[block.key].color || null"
|
|
3103
|
+
(selectionChange)="updateActionField(block.key, 'color', $event.value)"
|
|
3104
|
+
>
|
|
3105
|
+
<mat-option [value]="null">Padrão</mat-option>
|
|
3106
|
+
<mat-option value="primary">Primary</mat-option>
|
|
3107
|
+
<mat-option value="accent">Accent</mat-option>
|
|
3108
|
+
<mat-option value="warn">Warn</mat-option>
|
|
3109
|
+
<mat-option value="basic">Basic</mat-option>
|
|
3110
|
+
</mat-select>
|
|
3111
|
+
</mat-form-field>
|
|
3112
|
+
<mat-form-field appearance="outline">
|
|
3113
|
+
<mat-label>Atalho</mat-label>
|
|
3114
|
+
<input
|
|
3115
|
+
matInput
|
|
3116
|
+
[value]="actionsModel[block.key].shortcut || ''"
|
|
3117
|
+
(input)="updateActionField(block.key, 'shortcut', $any($event.target).value)"
|
|
3118
|
+
/>
|
|
3119
|
+
</mat-form-field>
|
|
3120
|
+
</div>
|
|
3121
|
+
}
|
|
3122
|
+
</div>
|
|
3123
|
+
</section>
|
|
3124
|
+
|
|
3125
|
+
<section class="mf-section">
|
|
3126
|
+
<div class="mf-section__header">
|
|
3127
|
+
<div>
|
|
3128
|
+
<h3>Layout das ações</h3>
|
|
3129
|
+
<p class="mf-help">Define alinhamento e disposição da barra de ações.</p>
|
|
3130
|
+
</div>
|
|
3131
|
+
</div>
|
|
3132
|
+
<div class="mf-actions-layout">
|
|
3133
|
+
<mat-form-field appearance="outline">
|
|
3134
|
+
<mat-label>Posição</mat-label>
|
|
3135
|
+
<mat-select
|
|
3136
|
+
[value]="actionsModel.position || null"
|
|
3137
|
+
(selectionChange)="updateActionsLayout('position', $event.value)"
|
|
3138
|
+
>
|
|
3139
|
+
<mat-option value="left">Esquerda</mat-option>
|
|
3140
|
+
<mat-option value="center">Centro</mat-option>
|
|
3141
|
+
<mat-option value="right">Direita</mat-option>
|
|
3142
|
+
<mat-option value="justified">Justificado</mat-option>
|
|
3143
|
+
<mat-option value="split">Split</mat-option>
|
|
3144
|
+
</mat-select>
|
|
3145
|
+
</mat-form-field>
|
|
3146
|
+
<mat-form-field appearance="outline">
|
|
3147
|
+
<mat-label>Orientação</mat-label>
|
|
3148
|
+
<mat-select
|
|
3149
|
+
[value]="actionsModel.orientation || null"
|
|
3150
|
+
(selectionChange)="updateActionsLayout('orientation', $event.value)"
|
|
3151
|
+
>
|
|
3152
|
+
<mat-option value="horizontal">Horizontal</mat-option>
|
|
3153
|
+
<mat-option value="vertical">Vertical</mat-option>
|
|
3154
|
+
</mat-select>
|
|
3155
|
+
</mat-form-field>
|
|
3156
|
+
<mat-form-field appearance="outline">
|
|
3157
|
+
<mat-label>Espaçamento</mat-label>
|
|
3158
|
+
<mat-select
|
|
3159
|
+
[value]="actionsModel.spacing || null"
|
|
3160
|
+
(selectionChange)="updateActionsLayout('spacing', $event.value)"
|
|
3161
|
+
>
|
|
3162
|
+
<mat-option value="compact">Compacto</mat-option>
|
|
3163
|
+
<mat-option value="normal">Normal</mat-option>
|
|
3164
|
+
<mat-option value="spacious">Espaçoso</mat-option>
|
|
3165
|
+
</mat-select>
|
|
3166
|
+
</mat-form-field>
|
|
3167
|
+
<mat-form-field appearance="outline">
|
|
3168
|
+
<mat-label>Posicionamento</mat-label>
|
|
3169
|
+
<mat-select
|
|
3170
|
+
[value]="actionsModel.placement || null"
|
|
3171
|
+
(selectionChange)="updateActionsLayout('placement', $event.value)"
|
|
3172
|
+
>
|
|
3173
|
+
<mat-option value="afterSections">Após seções</mat-option>
|
|
3174
|
+
<mat-option value="insideLastSection">Dentro da última seção</mat-option>
|
|
3175
|
+
<mat-option value="top">Topo</mat-option>
|
|
3176
|
+
</mat-select>
|
|
3177
|
+
</mat-form-field>
|
|
3178
|
+
<mat-slide-toggle
|
|
3179
|
+
[checked]="!!actionsModel.sticky"
|
|
3180
|
+
(change)="updateActionsLayout('sticky', $event.checked)"
|
|
3181
|
+
>
|
|
3182
|
+
Ações fixas
|
|
3183
|
+
</mat-slide-toggle>
|
|
3184
|
+
<mat-slide-toggle
|
|
3185
|
+
[checked]="!!actionsModel.divider"
|
|
3186
|
+
(change)="updateActionsLayout('divider', $event.checked)"
|
|
3187
|
+
>
|
|
3188
|
+
Mostrar divisor
|
|
3189
|
+
</mat-slide-toggle>
|
|
3190
|
+
</div>
|
|
3191
|
+
</section>
|
|
3192
|
+
|
|
3193
|
+
<section class="mf-section">
|
|
3194
|
+
<div class="mf-section__header mf-section__header--row">
|
|
3195
|
+
<div>
|
|
3196
|
+
<h3>Ações customizadas</h3>
|
|
3197
|
+
<p class="mf-help">Crie botões extras com evento próprio.</p>
|
|
3198
|
+
</div>
|
|
3199
|
+
<button mat-stroked-button type="button" (click)="addCustomAction()">
|
|
3200
|
+
<mat-icon>add</mat-icon>
|
|
3201
|
+
Adicionar ação
|
|
3202
|
+
</button>
|
|
3203
|
+
</div>
|
|
3204
|
+
<div class="mf-custom-actions">
|
|
3205
|
+
@if (actionsModel.custom?.length) {
|
|
3206
|
+
@for (action of actionsModel.custom || []; track action.id) {
|
|
3207
|
+
<div class="mf-custom-card">
|
|
3208
|
+
<div class="mf-custom-card__header">
|
|
3209
|
+
<div class="mf-custom-card__title">{{ action.label || action.id }}</div>
|
|
3210
|
+
<button mat-icon-button type="button" (click)="removeCustomAction(action)">
|
|
3211
|
+
<mat-icon>delete</mat-icon>
|
|
3212
|
+
</button>
|
|
3213
|
+
</div>
|
|
3214
|
+
<div class="mf-custom-card__grid">
|
|
3215
|
+
<mat-form-field appearance="outline">
|
|
3216
|
+
<mat-label>ID</mat-label>
|
|
3217
|
+
<input
|
|
3218
|
+
matInput
|
|
3219
|
+
[value]="action.id || ''"
|
|
3220
|
+
(input)="updateCustomActionField(action, 'id', $any($event.target).value)"
|
|
3221
|
+
/>
|
|
3222
|
+
</mat-form-field>
|
|
3223
|
+
<mat-form-field appearance="outline">
|
|
3224
|
+
<mat-label>Label</mat-label>
|
|
3225
|
+
<input
|
|
3226
|
+
matInput
|
|
3227
|
+
[value]="action.label || ''"
|
|
3228
|
+
(input)="updateCustomActionField(action, 'label', $any($event.target).value)"
|
|
3229
|
+
/>
|
|
3230
|
+
</mat-form-field>
|
|
3231
|
+
<mat-form-field appearance="outline">
|
|
3232
|
+
<mat-label>Evento (action)</mat-label>
|
|
3233
|
+
<input
|
|
3234
|
+
matInput
|
|
3235
|
+
[value]="action.action || ''"
|
|
3236
|
+
(input)="updateCustomActionField(action, 'action', $any($event.target).value)"
|
|
3237
|
+
/>
|
|
3238
|
+
</mat-form-field>
|
|
3239
|
+
<mat-form-field appearance="outline">
|
|
3240
|
+
<mat-label>Ícone</mat-label>
|
|
3241
|
+
<input
|
|
3242
|
+
matInput
|
|
3243
|
+
[value]="action.icon || ''"
|
|
3244
|
+
(input)="updateCustomActionField(action, 'icon', $any($event.target).value)"
|
|
3245
|
+
/>
|
|
3246
|
+
</mat-form-field>
|
|
3247
|
+
<mat-form-field appearance="outline">
|
|
3248
|
+
<mat-label>Variante</mat-label>
|
|
3249
|
+
<mat-select
|
|
3250
|
+
[value]="action.variant || null"
|
|
3251
|
+
(selectionChange)="updateCustomActionField(action, 'variant', $event.value)"
|
|
3252
|
+
>
|
|
3253
|
+
<mat-option [value]="null">Padrão</mat-option>
|
|
3254
|
+
<mat-option value="raised">Elevado</mat-option>
|
|
3255
|
+
<mat-option value="stroked">Contornado</mat-option>
|
|
3256
|
+
<mat-option value="flat">Plano</mat-option>
|
|
3257
|
+
<mat-option value="fab">Flutuante</mat-option>
|
|
3258
|
+
</mat-select>
|
|
3259
|
+
</mat-form-field>
|
|
3260
|
+
<mat-form-field appearance="outline">
|
|
3261
|
+
<mat-label>Cor</mat-label>
|
|
3262
|
+
<mat-select
|
|
3263
|
+
[value]="action.color || null"
|
|
3264
|
+
(selectionChange)="updateCustomActionField(action, 'color', $event.value)"
|
|
3265
|
+
>
|
|
3266
|
+
<mat-option [value]="null">Padrão</mat-option>
|
|
3267
|
+
<mat-option value="primary">Primary</mat-option>
|
|
3268
|
+
<mat-option value="accent">Accent</mat-option>
|
|
3269
|
+
<mat-option value="warn">Warn</mat-option>
|
|
3270
|
+
<mat-option value="basic">Basic</mat-option>
|
|
3271
|
+
</mat-select>
|
|
3272
|
+
</mat-form-field>
|
|
3273
|
+
<mat-form-field appearance="outline">
|
|
3274
|
+
<mat-label>Atalho</mat-label>
|
|
3275
|
+
<input
|
|
3276
|
+
matInput
|
|
3277
|
+
[value]="action.shortcut || ''"
|
|
3278
|
+
(input)="updateCustomActionField(action, 'shortcut', $any($event.target).value)"
|
|
3279
|
+
/>
|
|
3280
|
+
</mat-form-field>
|
|
3281
|
+
<mat-form-field appearance="outline">
|
|
3282
|
+
<mat-label>Tooltip</mat-label>
|
|
3283
|
+
<input
|
|
3284
|
+
matInput
|
|
3285
|
+
[value]="action.tooltip || ''"
|
|
3286
|
+
(input)="updateCustomActionField(action, 'tooltip', $any($event.target).value)"
|
|
3287
|
+
/>
|
|
3288
|
+
</mat-form-field>
|
|
3289
|
+
<mat-slide-toggle
|
|
3290
|
+
[checked]="action.visible !== false"
|
|
3291
|
+
(change)="updateCustomActionField(action, 'visible', $event.checked)"
|
|
3292
|
+
>
|
|
3293
|
+
Visível
|
|
3294
|
+
</mat-slide-toggle>
|
|
3295
|
+
</div>
|
|
3296
|
+
</div>
|
|
3297
|
+
}
|
|
3298
|
+
} @else {
|
|
3299
|
+
<p class="mf-empty">Nenhuma ação customizada cadastrada.</p>
|
|
3300
|
+
}
|
|
3301
|
+
</div>
|
|
3302
|
+
</section>
|
|
1423
3303
|
</div>
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
3304
|
+
</mat-tab>
|
|
3305
|
+
|
|
3306
|
+
<mat-tab label="Mensagens">
|
|
3307
|
+
<div class="mf-tab">
|
|
3308
|
+
<section class="mf-section">
|
|
3309
|
+
<div class="mf-section__header">
|
|
3310
|
+
<div>
|
|
3311
|
+
<h3>Feedback padrão</h3>
|
|
3312
|
+
<p class="mf-help">Mensagens de sucesso/erro exibidas pelo host.</p>
|
|
3313
|
+
</div>
|
|
3314
|
+
</div>
|
|
3315
|
+
<div class="mf-messages-grid">
|
|
3316
|
+
<mat-form-field appearance="outline">
|
|
3317
|
+
<mat-label>Sucesso ao criar</mat-label>
|
|
3318
|
+
<input
|
|
3319
|
+
matInput
|
|
3320
|
+
[value]="messagesModel.createRegistrySuccess || ''"
|
|
3321
|
+
(input)="updateMessage('createRegistrySuccess', $any($event.target).value)"
|
|
3322
|
+
/>
|
|
3323
|
+
</mat-form-field>
|
|
3324
|
+
<mat-form-field appearance="outline">
|
|
3325
|
+
<mat-label>Erro ao criar</mat-label>
|
|
3326
|
+
<input
|
|
3327
|
+
matInput
|
|
3328
|
+
[value]="messagesModel.createRegistryError || ''"
|
|
3329
|
+
(input)="updateMessage('createRegistryError', $any($event.target).value)"
|
|
3330
|
+
/>
|
|
3331
|
+
</mat-form-field>
|
|
3332
|
+
<mat-form-field appearance="outline">
|
|
3333
|
+
<mat-label>Sucesso ao atualizar</mat-label>
|
|
3334
|
+
<input
|
|
3335
|
+
matInput
|
|
3336
|
+
[value]="messagesModel.updateRegistrySuccess || ''"
|
|
3337
|
+
(input)="updateMessage('updateRegistrySuccess', $any($event.target).value)"
|
|
3338
|
+
/>
|
|
3339
|
+
</mat-form-field>
|
|
3340
|
+
<mat-form-field appearance="outline">
|
|
3341
|
+
<mat-label>Erro ao atualizar</mat-label>
|
|
3342
|
+
<input
|
|
3343
|
+
matInput
|
|
3344
|
+
[value]="messagesModel.updateRegistryError || ''"
|
|
3345
|
+
(input)="updateMessage('updateRegistryError', $any($event.target).value)"
|
|
3346
|
+
/>
|
|
3347
|
+
</mat-form-field>
|
|
3348
|
+
</div>
|
|
3349
|
+
</section>
|
|
3350
|
+
|
|
3351
|
+
<section class="mf-section">
|
|
3352
|
+
<div class="mf-section__header">
|
|
3353
|
+
<div>
|
|
3354
|
+
<h3>Confirmações</h3>
|
|
3355
|
+
<p class="mf-help">Textos mostrados antes de ações críticas.</p>
|
|
3356
|
+
</div>
|
|
3357
|
+
</div>
|
|
3358
|
+
<div class="mf-messages-grid">
|
|
3359
|
+
<mat-form-field appearance="outline">
|
|
3360
|
+
<mat-label>Enviar</mat-label>
|
|
3361
|
+
<input
|
|
3362
|
+
matInput
|
|
3363
|
+
[value]="messagesModel.confirmations?.submit || ''"
|
|
3364
|
+
(input)="updateConfirmation('submit', $any($event.target).value)"
|
|
3365
|
+
/>
|
|
3366
|
+
</mat-form-field>
|
|
3367
|
+
<mat-form-field appearance="outline">
|
|
3368
|
+
<mat-label>Cancelar</mat-label>
|
|
3369
|
+
<input
|
|
3370
|
+
matInput
|
|
3371
|
+
[value]="messagesModel.confirmations?.cancel || ''"
|
|
3372
|
+
(input)="updateConfirmation('cancel', $any($event.target).value)"
|
|
3373
|
+
/>
|
|
3374
|
+
</mat-form-field>
|
|
3375
|
+
<mat-form-field appearance="outline">
|
|
3376
|
+
<mat-label>Resetar</mat-label>
|
|
3377
|
+
<input
|
|
3378
|
+
matInput
|
|
3379
|
+
[value]="messagesModel.confirmations?.reset || ''"
|
|
3380
|
+
(input)="updateConfirmation('reset', $any($event.target).value)"
|
|
3381
|
+
/>
|
|
3382
|
+
</mat-form-field>
|
|
3383
|
+
</div>
|
|
3384
|
+
</section>
|
|
3385
|
+
|
|
3386
|
+
<section class="mf-section">
|
|
3387
|
+
<div class="mf-section__header">
|
|
3388
|
+
<div>
|
|
3389
|
+
<h3>Loading</h3>
|
|
3390
|
+
<p class="mf-help">Mensagens exibidas durante operações.</p>
|
|
3391
|
+
</div>
|
|
3392
|
+
</div>
|
|
3393
|
+
<div class="mf-messages-grid">
|
|
3394
|
+
<mat-form-field appearance="outline">
|
|
3395
|
+
<mat-label>Salvar</mat-label>
|
|
3396
|
+
<input
|
|
3397
|
+
matInput
|
|
3398
|
+
[value]="messagesModel.loading?.submit || ''"
|
|
3399
|
+
(input)="updateLoading('submit', $any($event.target).value)"
|
|
3400
|
+
/>
|
|
3401
|
+
</mat-form-field>
|
|
3402
|
+
<mat-form-field appearance="outline">
|
|
3403
|
+
<mat-label>Cancelar</mat-label>
|
|
3404
|
+
<input
|
|
3405
|
+
matInput
|
|
3406
|
+
[value]="messagesModel.loading?.cancel || ''"
|
|
3407
|
+
(input)="updateLoading('cancel', $any($event.target).value)"
|
|
3408
|
+
/>
|
|
3409
|
+
</mat-form-field>
|
|
3410
|
+
<mat-form-field appearance="outline">
|
|
3411
|
+
<mat-label>Resetar</mat-label>
|
|
3412
|
+
<input
|
|
3413
|
+
matInput
|
|
3414
|
+
[value]="messagesModel.loading?.reset || ''"
|
|
3415
|
+
(input)="updateLoading('reset', $any($event.target).value)"
|
|
3416
|
+
/>
|
|
3417
|
+
</mat-form-field>
|
|
3418
|
+
</div>
|
|
3419
|
+
</section>
|
|
3420
|
+
|
|
3421
|
+
<section class="mf-section">
|
|
3422
|
+
<div class="mf-section__header">
|
|
3423
|
+
<div>
|
|
3424
|
+
<h3>Mensagens por ação customizada</h3>
|
|
3425
|
+
<p class="mf-help">Personalize textos por ID de ação.</p>
|
|
3426
|
+
</div>
|
|
3427
|
+
</div>
|
|
3428
|
+
<div class="mf-custom-messages">
|
|
3429
|
+
@if (actionsModel.custom?.length) {
|
|
3430
|
+
@for (action of actionsModel.custom || []; track action.id) {
|
|
3431
|
+
<div class="mf-custom-message-card">
|
|
3432
|
+
<div class="mf-custom-card__title">{{ action.label || action.id }}</div>
|
|
3433
|
+
<div class="mf-custom-card__grid">
|
|
3434
|
+
<mat-form-field appearance="outline">
|
|
3435
|
+
<mat-label>Confirmação</mat-label>
|
|
3436
|
+
<input
|
|
3437
|
+
matInput
|
|
3438
|
+
[value]="messagesModel.customActions?.[action.id || '']?.confirmation || ''"
|
|
3439
|
+
(input)="updateCustomMessage(action, 'confirmation', $any($event.target).value)"
|
|
3440
|
+
/>
|
|
3441
|
+
</mat-form-field>
|
|
3442
|
+
<mat-form-field appearance="outline">
|
|
3443
|
+
<mat-label>Sucesso</mat-label>
|
|
3444
|
+
<input
|
|
3445
|
+
matInput
|
|
3446
|
+
[value]="messagesModel.customActions?.[action.id || '']?.success || ''"
|
|
3447
|
+
(input)="updateCustomMessage(action, 'success', $any($event.target).value)"
|
|
3448
|
+
/>
|
|
3449
|
+
</mat-form-field>
|
|
3450
|
+
<mat-form-field appearance="outline">
|
|
3451
|
+
<mat-label>Erro</mat-label>
|
|
3452
|
+
<input
|
|
3453
|
+
matInput
|
|
3454
|
+
[value]="messagesModel.customActions?.[action.id || '']?.error || ''"
|
|
3455
|
+
(input)="updateCustomMessage(action, 'error', $any($event.target).value)"
|
|
3456
|
+
/>
|
|
3457
|
+
</mat-form-field>
|
|
3458
|
+
</div>
|
|
3459
|
+
</div>
|
|
3460
|
+
}
|
|
3461
|
+
} @else {
|
|
3462
|
+
<p class="mf-empty">Cadastre ações customizadas para configurar mensagens específicas.</p>
|
|
3463
|
+
}
|
|
3464
|
+
</div>
|
|
3465
|
+
</section>
|
|
1429
3466
|
</div>
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
3467
|
+
</mat-tab>
|
|
3468
|
+
|
|
3469
|
+
<mat-tab label="Comportamento">
|
|
3470
|
+
<div class="mf-tab">
|
|
3471
|
+
<section class="mf-section">
|
|
3472
|
+
<div class="mf-section__header">
|
|
3473
|
+
<div>
|
|
3474
|
+
<h3>Comportamento do formulário</h3>
|
|
3475
|
+
<p class="mf-help">Preferências aplicadas pelo host ou runtime.</p>
|
|
3476
|
+
</div>
|
|
3477
|
+
</div>
|
|
3478
|
+
<div class="mf-note">Estas opções são persistidas no config; o host decide como aplicar.</div>
|
|
3479
|
+
<div class="mf-toggle-grid">
|
|
3480
|
+
<mat-slide-toggle
|
|
3481
|
+
[checked]="!!behaviorModel.confirmOnUnsavedChanges"
|
|
3482
|
+
(change)="updateBehavior('confirmOnUnsavedChanges', $event.checked)"
|
|
3483
|
+
>
|
|
3484
|
+
Confirmar ao sair com alterações não salvas
|
|
3485
|
+
</mat-slide-toggle>
|
|
3486
|
+
<mat-slide-toggle
|
|
3487
|
+
[checked]="!!behaviorModel.trackHistory"
|
|
3488
|
+
(change)="updateBehavior('trackHistory', $event.checked)"
|
|
3489
|
+
>
|
|
3490
|
+
Rastrear histórico de alterações
|
|
3491
|
+
</mat-slide-toggle>
|
|
3492
|
+
<mat-slide-toggle
|
|
3493
|
+
[checked]="!!behaviorModel.focusFirstError"
|
|
3494
|
+
(change)="updateBehavior('focusFirstError', $event.checked)"
|
|
3495
|
+
>
|
|
3496
|
+
Focar no primeiro erro ao submeter
|
|
3497
|
+
</mat-slide-toggle>
|
|
3498
|
+
<mat-slide-toggle
|
|
3499
|
+
[checked]="!!behaviorModel.scrollToErrors"
|
|
3500
|
+
(change)="updateBehavior('scrollToErrors', $event.checked)"
|
|
3501
|
+
>
|
|
3502
|
+
Rolar até os erros ao submeter
|
|
3503
|
+
</mat-slide-toggle>
|
|
3504
|
+
<mat-slide-toggle
|
|
3505
|
+
[checked]="!!behaviorModel.clearAfterSave"
|
|
3506
|
+
(change)="updateBehavior('clearAfterSave', $event.checked)"
|
|
3507
|
+
>
|
|
3508
|
+
Limpar formulário após salvar
|
|
3509
|
+
</mat-slide-toggle>
|
|
3510
|
+
<mat-slide-toggle
|
|
3511
|
+
[checked]="!!behaviorModel.reactiveValidation"
|
|
3512
|
+
(change)="updateBehavior('reactiveValidation', $event.checked)"
|
|
3513
|
+
>
|
|
3514
|
+
Validação reativa
|
|
3515
|
+
</mat-slide-toggle>
|
|
3516
|
+
</div>
|
|
3517
|
+
<div class="mf-actions-layout">
|
|
3518
|
+
<mat-form-field appearance="outline">
|
|
3519
|
+
<mat-label>Redirecionar após salvar (URL)</mat-label>
|
|
3520
|
+
<input
|
|
3521
|
+
matInput
|
|
3522
|
+
[value]="behaviorModel.redirectAfterSave || ''"
|
|
3523
|
+
(input)="updateBehavior('redirectAfterSave', $any($event.target).value)"
|
|
3524
|
+
/>
|
|
3525
|
+
</mat-form-field>
|
|
3526
|
+
<mat-form-field appearance="outline">
|
|
3527
|
+
<mat-label>Debounce validação reativa (ms)</mat-label>
|
|
3528
|
+
<input
|
|
3529
|
+
matInput
|
|
3530
|
+
type="number"
|
|
3531
|
+
min="0"
|
|
3532
|
+
[value]="behaviorModel.reactiveValidationDebounceMs || 0"
|
|
3533
|
+
(input)="updateBehavior('reactiveValidationDebounceMs', $any($event.target).valueAsNumber || 0)"
|
|
3534
|
+
/>
|
|
3535
|
+
</mat-form-field>
|
|
3536
|
+
</div>
|
|
3537
|
+
</section>
|
|
1435
3538
|
</div>
|
|
1436
|
-
</
|
|
1437
|
-
|
|
3539
|
+
</mat-tab>
|
|
3540
|
+
|
|
3541
|
+
<mat-tab label="Dicas">
|
|
3542
|
+
<div class="mf-tab">
|
|
3543
|
+
<section class="mf-section">
|
|
3544
|
+
<div class="mf-section__header">
|
|
3545
|
+
<div>
|
|
3546
|
+
<h3>Mensagens de modo</h3>
|
|
3547
|
+
<p class="mf-help">Textos auxiliares exibidos pelo host.</p>
|
|
3548
|
+
</div>
|
|
3549
|
+
</div>
|
|
3550
|
+
<div class="mf-note">Útil para i18n e orientação do usuário.</div>
|
|
3551
|
+
<div class="mf-messages-grid">
|
|
3552
|
+
<mat-form-field appearance="outline">
|
|
3553
|
+
<mat-label>Criar</mat-label>
|
|
3554
|
+
<input
|
|
3555
|
+
matInput
|
|
3556
|
+
[value]="hintsModel.dataModes.create"
|
|
3557
|
+
(input)="updateHints('dataModes', 'create', $any($event.target).value)"
|
|
3558
|
+
/>
|
|
3559
|
+
</mat-form-field>
|
|
3560
|
+
<mat-form-field appearance="outline">
|
|
3561
|
+
<mat-label>Editar</mat-label>
|
|
3562
|
+
<input
|
|
3563
|
+
matInput
|
|
3564
|
+
[value]="hintsModel.dataModes.edit"
|
|
3565
|
+
(input)="updateHints('dataModes', 'edit', $any($event.target).value)"
|
|
3566
|
+
/>
|
|
3567
|
+
</mat-form-field>
|
|
3568
|
+
<mat-form-field appearance="outline">
|
|
3569
|
+
<mat-label>Visualizar</mat-label>
|
|
3570
|
+
<input
|
|
3571
|
+
matInput
|
|
3572
|
+
[value]="hintsModel.dataModes.view"
|
|
3573
|
+
(input)="updateHints('dataModes', 'view', $any($event.target).value)"
|
|
3574
|
+
/>
|
|
3575
|
+
</mat-form-field>
|
|
3576
|
+
</div>
|
|
3577
|
+
</section>
|
|
3578
|
+
|
|
3579
|
+
<section class="mf-section">
|
|
3580
|
+
<div class="mf-section__header">
|
|
3581
|
+
<div>
|
|
3582
|
+
<h3>Dicas de UI</h3>
|
|
3583
|
+
<p class="mf-help">Ajuda contextual para estados do formulário.</p>
|
|
3584
|
+
</div>
|
|
3585
|
+
</div>
|
|
3586
|
+
<div class="mf-messages-grid">
|
|
3587
|
+
<mat-form-field appearance="outline">
|
|
3588
|
+
<mat-label>Apresentação</mat-label>
|
|
3589
|
+
<textarea
|
|
3590
|
+
matInput
|
|
3591
|
+
rows="2"
|
|
3592
|
+
[value]="hintsModel.uiModes.presentation"
|
|
3593
|
+
(input)="updateHints('uiModes', 'presentation', $any($event.target).value)"
|
|
3594
|
+
></textarea>
|
|
3595
|
+
</mat-form-field>
|
|
3596
|
+
<mat-form-field appearance="outline">
|
|
3597
|
+
<mat-label>Somente leitura</mat-label>
|
|
3598
|
+
<textarea
|
|
3599
|
+
matInput
|
|
3600
|
+
rows="2"
|
|
3601
|
+
[value]="hintsModel.uiModes.readonly"
|
|
3602
|
+
(input)="updateHints('uiModes', 'readonly', $any($event.target).value)"
|
|
3603
|
+
></textarea>
|
|
3604
|
+
</mat-form-field>
|
|
3605
|
+
<mat-form-field appearance="outline">
|
|
3606
|
+
<mat-label>Desabilitado</mat-label>
|
|
3607
|
+
<textarea
|
|
3608
|
+
matInput
|
|
3609
|
+
rows="2"
|
|
3610
|
+
[value]="hintsModel.uiModes.disabled"
|
|
3611
|
+
(input)="updateHints('uiModes', 'disabled', $any($event.target).value)"
|
|
3612
|
+
></textarea>
|
|
3613
|
+
</mat-form-field>
|
|
3614
|
+
<mat-form-field appearance="outline">
|
|
3615
|
+
<mat-label>Visível</mat-label>
|
|
3616
|
+
<textarea
|
|
3617
|
+
matInput
|
|
3618
|
+
rows="2"
|
|
3619
|
+
[value]="hintsModel.uiModes.visible"
|
|
3620
|
+
(input)="updateHints('uiModes', 'visible', $any($event.target).value)"
|
|
3621
|
+
></textarea>
|
|
3622
|
+
</mat-form-field>
|
|
3623
|
+
</div>
|
|
3624
|
+
</section>
|
|
3625
|
+
</div>
|
|
3626
|
+
</mat-tab>
|
|
3627
|
+
|
|
3628
|
+
<mat-tab label="Hooks">
|
|
3629
|
+
<div class="mf-tab">
|
|
3630
|
+
<section class="mf-section">
|
|
3631
|
+
<div class="mf-section__header">
|
|
3632
|
+
<div>
|
|
3633
|
+
<h3>Hooks de ciclo de vida</h3>
|
|
3634
|
+
<p class="mf-help">Defina ações automatizadas por estágio.</p>
|
|
3635
|
+
</div>
|
|
3636
|
+
<div class="mf-actions-row">
|
|
3637
|
+
<button mat-stroked-button type="button" (click)="resetHooks()">
|
|
3638
|
+
Restaurar
|
|
3639
|
+
</button>
|
|
3640
|
+
<button mat-flat-button color="primary" type="button" (click)="applyHooks()">
|
|
3641
|
+
Aplicar hooks
|
|
3642
|
+
</button>
|
|
3643
|
+
</div>
|
|
3644
|
+
</div>
|
|
3645
|
+
<div class="mf-note">Executa apenas se o host registrar hooks disponíveis.</div>
|
|
3646
|
+
<div class="mf-hooks-grid">
|
|
3647
|
+
@for (stage of hookStages; track stage) {
|
|
3648
|
+
<mat-form-field appearance="outline" class="mf-hook-field">
|
|
3649
|
+
<mat-label>{{ hookLabel(stage) }}</mat-label>
|
|
3650
|
+
<textarea
|
|
3651
|
+
matInput
|
|
3652
|
+
rows="4"
|
|
3653
|
+
[value]="hooksText[stage]"
|
|
3654
|
+
(input)="onHookTextChange(stage, $any($event.target).value)"
|
|
3655
|
+
placeholder='[{ "id": "notifySuccess", "priority": 0, "args": {} }]'
|
|
3656
|
+
></textarea>
|
|
3657
|
+
<button
|
|
3658
|
+
mat-icon-button
|
|
3659
|
+
matSuffix
|
|
3660
|
+
class="help-icon-button"
|
|
3661
|
+
type="button"
|
|
3662
|
+
[matTooltip]="'Array JSON de hooks para ' + stage + ' (id, priority, timeoutMs, args).'"
|
|
3663
|
+
matTooltipPosition="above"
|
|
3664
|
+
>
|
|
3665
|
+
<mat-icon>help_outline</mat-icon>
|
|
3666
|
+
</button>
|
|
3667
|
+
@if (hooksErrors[stage]) {
|
|
3668
|
+
<mat-error>{{ hooksErrors[stage] }}</mat-error>
|
|
3669
|
+
}
|
|
3670
|
+
</mat-form-field>
|
|
3671
|
+
}
|
|
3672
|
+
</div>
|
|
3673
|
+
</section>
|
|
3674
|
+
</div>
|
|
3675
|
+
</mat-tab>
|
|
3676
|
+
|
|
3677
|
+
<mat-tab label="Regras">
|
|
3678
|
+
<div class="mf-tab">
|
|
3679
|
+
<section class="mf-section">
|
|
3680
|
+
<div class="mf-section__header">
|
|
3681
|
+
<div>
|
|
3682
|
+
<h3>Regras de layout</h3>
|
|
3683
|
+
<p class="mf-help">Regras avançadas para visibilidade e comportamento.</p>
|
|
3684
|
+
</div>
|
|
3685
|
+
<div class="mf-actions-row">
|
|
3686
|
+
<button mat-flat-button color="primary" type="button" (click)="applyRules()">
|
|
3687
|
+
Aplicar regras
|
|
3688
|
+
</button>
|
|
3689
|
+
</div>
|
|
3690
|
+
</div>
|
|
3691
|
+
<div class="mf-note">Persistido no config; a execução depende do host.</div>
|
|
3692
|
+
<mat-form-field appearance="outline">
|
|
3693
|
+
<mat-label>formRules (JSON)</mat-label>
|
|
3694
|
+
<textarea
|
|
3695
|
+
matInput
|
|
3696
|
+
rows="6"
|
|
3697
|
+
[value]="formRulesText"
|
|
3698
|
+
(input)="onRulesTextChange($any($event.target).value)"
|
|
3699
|
+
placeholder='[{"id":"rule-1","name":"Obrigatoriedade","targetType":"field","targets":["campo"],"effect":{"condition":"{campo} != null","properties":{"required":true}}}]'
|
|
3700
|
+
></textarea>
|
|
3701
|
+
<button
|
|
3702
|
+
mat-icon-button
|
|
3703
|
+
matSuffix
|
|
3704
|
+
class="help-icon-button"
|
|
3705
|
+
type="button"
|
|
3706
|
+
[matTooltip]="'Array JSON com regras (id, targetType, targets, effect).'"
|
|
3707
|
+
matTooltipPosition="above"
|
|
3708
|
+
>
|
|
3709
|
+
<mat-icon>help_outline</mat-icon>
|
|
3710
|
+
</button>
|
|
3711
|
+
@if (formRulesError) {
|
|
3712
|
+
<mat-error>{{ formRulesError }}</mat-error>
|
|
3713
|
+
}
|
|
3714
|
+
</mat-form-field>
|
|
3715
|
+
<mat-form-field appearance="outline">
|
|
3716
|
+
<mat-label>formRulesState (JSON)</mat-label>
|
|
3717
|
+
<textarea
|
|
3718
|
+
matInput
|
|
3719
|
+
rows="6"
|
|
3720
|
+
[value]="formRulesStateText"
|
|
3721
|
+
(input)="onRulesStateTextChange($any($event.target).value)"
|
|
3722
|
+
placeholder='{"nodes":[],"edges":[]}'
|
|
3723
|
+
></textarea>
|
|
3724
|
+
<button
|
|
3725
|
+
mat-icon-button
|
|
3726
|
+
matSuffix
|
|
3727
|
+
class="help-icon-button"
|
|
3728
|
+
type="button"
|
|
3729
|
+
[matTooltip]="'Estado bruto do editor visual (opcional).'"
|
|
3730
|
+
matTooltipPosition="above"
|
|
3731
|
+
>
|
|
3732
|
+
<mat-icon>help_outline</mat-icon>
|
|
3733
|
+
</button>
|
|
3734
|
+
@if (formRulesStateError) {
|
|
3735
|
+
<mat-error>{{ formRulesStateError }}</mat-error>
|
|
3736
|
+
}
|
|
3737
|
+
</mat-form-field>
|
|
3738
|
+
<div class="mf-actions-row mf-actions-row--end">
|
|
3739
|
+
<button mat-flat-button color="primary" type="button" (click)="applyRules()">
|
|
3740
|
+
Aplicar regras
|
|
3741
|
+
</button>
|
|
3742
|
+
</div>
|
|
3743
|
+
</section>
|
|
3744
|
+
</div>
|
|
3745
|
+
</mat-tab>
|
|
3746
|
+
|
|
3747
|
+
<mat-tab label="Cascatas">
|
|
3748
|
+
<div class="mf-tab">
|
|
3749
|
+
<section class="mf-section mf-section--full">
|
|
3750
|
+
<div class="mf-section__header">
|
|
3751
|
+
<div>
|
|
3752
|
+
<h3>Dependências entre campos</h3>
|
|
3753
|
+
<p class="mf-help">Configure cascatas nativas baseadas em metadados.</p>
|
|
3754
|
+
</div>
|
|
3755
|
+
</div>
|
|
3756
|
+
<div class="mf-note">As alterações atualizam metadados e são salvas no draft.</div>
|
|
3757
|
+
<praxis-cascade-manager-tab
|
|
3758
|
+
[fields]="cascadeFields"
|
|
3759
|
+
(apply)="applyCascadePatch($event)"
|
|
3760
|
+
></praxis-cascade-manager-tab>
|
|
3761
|
+
</section>
|
|
3762
|
+
</div>
|
|
3763
|
+
</mat-tab>
|
|
3764
|
+
</mat-tab-group>
|
|
1438
3765
|
</div>
|
|
1439
|
-
`, isInline: true, styles: [".mf-editor{display:grid;gap:
|
|
3766
|
+
`, isInline: true, styles: [".mf-editor{display:grid;gap:16px;padding:16px;color:var(--md-sys-color-on-surface)}.mf-editor__toolbar{display:flex;align-items:center;gap:12px;padding:12px 14px;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;background:var(--md-sys-color-surface-container)}.mf-editor__title{display:flex;flex-direction:column;gap:2px;font-weight:600}.mf-editor__subtitle{font-weight:400;color:var(--md-sys-color-on-surface-variant)}.mf-editor__toolbar .spacer{flex:1}.mf-tabs{min-height:420px}.mf-tab{display:grid;gap:16px;padding:14px 6px 0}.mf-editor__list{display:grid;gap:8px;overflow-x:auto}.mf-editor__row{display:grid;grid-template-columns:2.2fr 2fr 1fr repeat(4,minmax(90px,.8fr));align-items:center;gap:8px;padding:10px 12px;border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;background:var(--md-sys-color-surface);min-width:720px}.mf-editor__row--head{font-weight:600;color:var(--md-sys-color-on-surface);background:var(--md-sys-color-surface-container)}.mf-editor__filter{display:inline-flex;align-items:center;gap:8px;font-weight:500}.mf-editor__filter input{accent-color:var(--md-sys-color-primary)}.col code{display:inline-block;font-family:inherit;font-size:.85rem;padding:2px 8px;border-radius:6px;background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface-variant)}.mf-btn{min-height:36px;padding:0 12px;border-radius:8px;border:1px solid var(--md-sys-color-outline);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);cursor:pointer;transition:background-color .18s ease,border-color .18s ease,color .18s ease}.mf-btn:hover{background:var(--md-sys-color-surface-container)}.mf-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.toggle{display:inline-flex;align-items:center;gap:6px;color:var(--md-sys-color-on-surface-variant)}.mf-section{display:grid;gap:12px;padding:16px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface)}.mf-section--full{padding:10px}.mf-section__header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.mf-section__header h3{margin:0;font-size:1.05rem;font-weight:600}.mf-help{margin:6px 0 0;font-size:.9rem;color:var(--md-sys-color-on-surface-variant)}.mf-note{padding:10px 12px;border-radius:10px;border:1px dashed var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface-variant);font-size:.9rem}.help-icon-button{--mdc-icon-button-state-layer-size: 28px;--mdc-icon-button-icon-size: 18px;width:28px;height:28px;padding:0;display:inline-flex;align-items:center;justify-content:center;margin-right:-4px}.help-icon-button mat-icon{font-size:18px;width:18px;height:18px}.mat-mdc-form-field-icon-suffix{align-self:center}.mf-actions-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px}.mf-action-card,.mf-custom-card,.mf-custom-message-card{display:grid;gap:10px;padding:14px;border-radius:10px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high)}.mf-action-card__title,.mf-custom-card__title{font-weight:600;color:var(--md-sys-color-on-surface)}.mf-actions-layout{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;align-items:center}.mf-toggle-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px;align-items:start}.mf-hooks-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px}.mf-hook-field textarea{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:.85rem}.mf-actions-row{display:inline-flex;gap:8px;align-items:center}.mf-actions-row--end{justify-content:flex-end;width:100%}.mf-custom-actions,.mf-custom-messages{display:grid;gap:12px}.mf-custom-card__header{display:flex;align-items:center;justify-content:space-between;gap:8px}.mf-custom-card__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;align-items:center}.mf-messages-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px}.mf-empty{margin:0;color:var(--md-sys-color-on-surface-variant)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i2.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i6.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i7.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i7.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i9.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: CascadeManagerTabComponent, selector: "praxis-cascade-manager-tab", inputs: ["fields", "connections"], outputs: ["apply", "cancel"] }] });
|
|
1440
3767
|
}
|
|
1441
3768
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: ManualFormConfigEditorComponent, decorators: [{
|
|
1442
3769
|
type: Component,
|
|
1443
|
-
args: [{ selector: 'praxis-manual-form-config-editor', standalone: true, imports: [
|
|
3770
|
+
args: [{ selector: 'praxis-manual-form-config-editor', standalone: true, imports: [
|
|
3771
|
+
CommonModule,
|
|
3772
|
+
FormsModule,
|
|
3773
|
+
MatTabsModule,
|
|
3774
|
+
MatFormFieldModule,
|
|
3775
|
+
MatInputModule,
|
|
3776
|
+
MatSelectModule,
|
|
3777
|
+
MatSlideToggleModule,
|
|
3778
|
+
MatButtonModule,
|
|
3779
|
+
MatIconModule,
|
|
3780
|
+
MatTooltipModule,
|
|
3781
|
+
CascadeManagerTabComponent,
|
|
3782
|
+
], template: `
|
|
1444
3783
|
<div class="mf-editor">
|
|
1445
3784
|
<div class="mf-editor__toolbar">
|
|
1446
|
-
<
|
|
3785
|
+
<div class="mf-editor__title">
|
|
3786
|
+
<span>Configurações do formulário</span>
|
|
3787
|
+
<small class="mf-editor__subtitle">Campos, ações, mensagens e comportamento</small>
|
|
3788
|
+
</div>
|
|
1447
3789
|
<div class="spacer"></div>
|
|
1448
|
-
<button type="button" (click)="close()">Fechar</button>
|
|
3790
|
+
<button type="button" class="mf-btn" (click)="close()">Fechar</button>
|
|
1449
3791
|
</div>
|
|
1450
3792
|
|
|
1451
|
-
<
|
|
1452
|
-
<
|
|
1453
|
-
<div class="
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
<div class="col col--req">Obrigatório</div>
|
|
1458
|
-
<div class="col col--ro">Som. leitura</div>
|
|
1459
|
-
<div class="col col--dis">Desabilitado</div>
|
|
1460
|
-
</div>
|
|
1461
|
-
|
|
1462
|
-
<div class="mf-editor__row" *ngFor="let f of filtered">
|
|
1463
|
-
<div class="col col--name"><code>{{ f.name }}</code></div>
|
|
1464
|
-
<div class="col col--label">{{ f.label || f.name }}</div>
|
|
1465
|
-
<div class="col col--type">{{ f.controlType }}</div>
|
|
1466
|
-
<div class="col col--vis">
|
|
1467
|
-
<label class="toggle">
|
|
1468
|
-
<input type="checkbox" [checked]="!f.hidden" (change)="toggleVisibility(f, $any($event.target).checked)" />
|
|
1469
|
-
<span>{{ f.hidden ? 'Oculto' : 'Visível' }}</span>
|
|
3793
|
+
<mat-tab-group class="mf-tabs">
|
|
3794
|
+
<mat-tab label="Campos">
|
|
3795
|
+
<div class="mf-tab">
|
|
3796
|
+
<label class="mf-editor__filter">
|
|
3797
|
+
<input type="checkbox" [(ngModel)]="onlyHidden" (change)="applyFilter()" />
|
|
3798
|
+
<span>Mostrar apenas ocultos</span>
|
|
1470
3799
|
</label>
|
|
3800
|
+
<div class="mf-editor__list">
|
|
3801
|
+
<div class="mf-editor__row mf-editor__row--head">
|
|
3802
|
+
<div class="col col--name">Campo</div>
|
|
3803
|
+
<div class="col col--label">Rótulo</div>
|
|
3804
|
+
<div class="col col--type">Tipo</div>
|
|
3805
|
+
<div class="col col--vis">Visível</div>
|
|
3806
|
+
<div class="col col--req">Obrigatório</div>
|
|
3807
|
+
<div class="col col--ro">Som. leitura</div>
|
|
3808
|
+
<div class="col col--dis">Desabilitado</div>
|
|
3809
|
+
</div>
|
|
3810
|
+
|
|
3811
|
+
@for (f of filtered; track f.name) {
|
|
3812
|
+
<div class="mf-editor__row">
|
|
3813
|
+
<div class="col col--name"><code>{{ f.name }}</code></div>
|
|
3814
|
+
<div class="col col--label">{{ f.label || f.name }}</div>
|
|
3815
|
+
<div class="col col--type">{{ f.controlType }}</div>
|
|
3816
|
+
<div class="col col--vis">
|
|
3817
|
+
<label class="toggle">
|
|
3818
|
+
<input type="checkbox" [checked]="!f.hidden" (change)="toggleVisibility(f, $any($event.target).checked)" />
|
|
3819
|
+
<span>{{ f.hidden ? 'Oculto' : 'Visível' }}</span>
|
|
3820
|
+
</label>
|
|
3821
|
+
</div>
|
|
3822
|
+
<div class="col col--req">
|
|
3823
|
+
<label class="toggle">
|
|
3824
|
+
<input type="checkbox" [checked]="!!f.required" (change)="toggleRequired(f, $any($event.target).checked)" />
|
|
3825
|
+
<span>{{ f.required ? 'Sim' : 'Não' }}</span>
|
|
3826
|
+
</label>
|
|
3827
|
+
</div>
|
|
3828
|
+
<div class="col col--ro">
|
|
3829
|
+
<label class="toggle">
|
|
3830
|
+
<input type="checkbox" [checked]="!!f.readOnly" (change)="toggleReadOnly(f, $any($event.target).checked)" />
|
|
3831
|
+
<span>{{ f.readOnly ? 'Sim' : 'Não' }}</span>
|
|
3832
|
+
</label>
|
|
3833
|
+
</div>
|
|
3834
|
+
<div class="col col--dis">
|
|
3835
|
+
<label class="toggle">
|
|
3836
|
+
<input type="checkbox" [checked]="!!f.disabled" (change)="toggleDisabled(f, $any($event.target).checked)" />
|
|
3837
|
+
<span>{{ f.disabled ? 'Sim' : 'Não' }}</span>
|
|
3838
|
+
</label>
|
|
3839
|
+
</div>
|
|
3840
|
+
</div>
|
|
3841
|
+
}
|
|
3842
|
+
</div>
|
|
1471
3843
|
</div>
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
3844
|
+
</mat-tab>
|
|
3845
|
+
|
|
3846
|
+
<mat-tab label="Ações">
|
|
3847
|
+
<div class="mf-tab">
|
|
3848
|
+
<section class="mf-section">
|
|
3849
|
+
<div class="mf-section__header">
|
|
3850
|
+
<div>
|
|
3851
|
+
<h3>Botões padrão</h3>
|
|
3852
|
+
<p class="mf-help">Controle rótulos, visibilidade e estilo dos botões básicos.</p>
|
|
3853
|
+
</div>
|
|
3854
|
+
</div>
|
|
3855
|
+
<div class="mf-actions-grid">
|
|
3856
|
+
@for (block of actionBlocks; track block.key) {
|
|
3857
|
+
<div class="mf-action-card">
|
|
3858
|
+
<div class="mf-action-card__title">{{ block.label }}</div>
|
|
3859
|
+
<mat-slide-toggle
|
|
3860
|
+
[checked]="actionsModel[block.key].visible"
|
|
3861
|
+
(change)="updateActionField(block.key, 'visible', $event.checked)"
|
|
3862
|
+
>
|
|
3863
|
+
Visível
|
|
3864
|
+
</mat-slide-toggle>
|
|
3865
|
+
<mat-form-field appearance="outline">
|
|
3866
|
+
<mat-label>Label</mat-label>
|
|
3867
|
+
<input
|
|
3868
|
+
matInput
|
|
3869
|
+
[value]="actionsModel[block.key].label"
|
|
3870
|
+
(input)="updateActionField(block.key, 'label', $any($event.target).value)"
|
|
3871
|
+
/>
|
|
3872
|
+
</mat-form-field>
|
|
3873
|
+
<mat-form-field appearance="outline">
|
|
3874
|
+
<mat-label>Ícone</mat-label>
|
|
3875
|
+
<input
|
|
3876
|
+
matInput
|
|
3877
|
+
[value]="actionsModel[block.key].icon || ''"
|
|
3878
|
+
(input)="updateActionField(block.key, 'icon', $any($event.target).value)"
|
|
3879
|
+
/>
|
|
3880
|
+
</mat-form-field>
|
|
3881
|
+
<mat-form-field appearance="outline">
|
|
3882
|
+
<mat-label>Variante</mat-label>
|
|
3883
|
+
<mat-select
|
|
3884
|
+
[value]="actionsModel[block.key].variant || null"
|
|
3885
|
+
(selectionChange)="updateActionField(block.key, 'variant', $event.value)"
|
|
3886
|
+
>
|
|
3887
|
+
<mat-option [value]="null">Padrão</mat-option>
|
|
3888
|
+
<mat-option value="raised">Elevado</mat-option>
|
|
3889
|
+
<mat-option value="stroked">Contornado</mat-option>
|
|
3890
|
+
<mat-option value="flat">Plano</mat-option>
|
|
3891
|
+
<mat-option value="fab">Flutuante</mat-option>
|
|
3892
|
+
</mat-select>
|
|
3893
|
+
</mat-form-field>
|
|
3894
|
+
<mat-form-field appearance="outline">
|
|
3895
|
+
<mat-label>Cor</mat-label>
|
|
3896
|
+
<mat-select
|
|
3897
|
+
[value]="actionsModel[block.key].color || null"
|
|
3898
|
+
(selectionChange)="updateActionField(block.key, 'color', $event.value)"
|
|
3899
|
+
>
|
|
3900
|
+
<mat-option [value]="null">Padrão</mat-option>
|
|
3901
|
+
<mat-option value="primary">Primary</mat-option>
|
|
3902
|
+
<mat-option value="accent">Accent</mat-option>
|
|
3903
|
+
<mat-option value="warn">Warn</mat-option>
|
|
3904
|
+
<mat-option value="basic">Basic</mat-option>
|
|
3905
|
+
</mat-select>
|
|
3906
|
+
</mat-form-field>
|
|
3907
|
+
<mat-form-field appearance="outline">
|
|
3908
|
+
<mat-label>Atalho</mat-label>
|
|
3909
|
+
<input
|
|
3910
|
+
matInput
|
|
3911
|
+
[value]="actionsModel[block.key].shortcut || ''"
|
|
3912
|
+
(input)="updateActionField(block.key, 'shortcut', $any($event.target).value)"
|
|
3913
|
+
/>
|
|
3914
|
+
</mat-form-field>
|
|
3915
|
+
</div>
|
|
3916
|
+
}
|
|
3917
|
+
</div>
|
|
3918
|
+
</section>
|
|
3919
|
+
|
|
3920
|
+
<section class="mf-section">
|
|
3921
|
+
<div class="mf-section__header">
|
|
3922
|
+
<div>
|
|
3923
|
+
<h3>Layout das ações</h3>
|
|
3924
|
+
<p class="mf-help">Define alinhamento e disposição da barra de ações.</p>
|
|
3925
|
+
</div>
|
|
3926
|
+
</div>
|
|
3927
|
+
<div class="mf-actions-layout">
|
|
3928
|
+
<mat-form-field appearance="outline">
|
|
3929
|
+
<mat-label>Posição</mat-label>
|
|
3930
|
+
<mat-select
|
|
3931
|
+
[value]="actionsModel.position || null"
|
|
3932
|
+
(selectionChange)="updateActionsLayout('position', $event.value)"
|
|
3933
|
+
>
|
|
3934
|
+
<mat-option value="left">Esquerda</mat-option>
|
|
3935
|
+
<mat-option value="center">Centro</mat-option>
|
|
3936
|
+
<mat-option value="right">Direita</mat-option>
|
|
3937
|
+
<mat-option value="justified">Justificado</mat-option>
|
|
3938
|
+
<mat-option value="split">Split</mat-option>
|
|
3939
|
+
</mat-select>
|
|
3940
|
+
</mat-form-field>
|
|
3941
|
+
<mat-form-field appearance="outline">
|
|
3942
|
+
<mat-label>Orientação</mat-label>
|
|
3943
|
+
<mat-select
|
|
3944
|
+
[value]="actionsModel.orientation || null"
|
|
3945
|
+
(selectionChange)="updateActionsLayout('orientation', $event.value)"
|
|
3946
|
+
>
|
|
3947
|
+
<mat-option value="horizontal">Horizontal</mat-option>
|
|
3948
|
+
<mat-option value="vertical">Vertical</mat-option>
|
|
3949
|
+
</mat-select>
|
|
3950
|
+
</mat-form-field>
|
|
3951
|
+
<mat-form-field appearance="outline">
|
|
3952
|
+
<mat-label>Espaçamento</mat-label>
|
|
3953
|
+
<mat-select
|
|
3954
|
+
[value]="actionsModel.spacing || null"
|
|
3955
|
+
(selectionChange)="updateActionsLayout('spacing', $event.value)"
|
|
3956
|
+
>
|
|
3957
|
+
<mat-option value="compact">Compacto</mat-option>
|
|
3958
|
+
<mat-option value="normal">Normal</mat-option>
|
|
3959
|
+
<mat-option value="spacious">Espaçoso</mat-option>
|
|
3960
|
+
</mat-select>
|
|
3961
|
+
</mat-form-field>
|
|
3962
|
+
<mat-form-field appearance="outline">
|
|
3963
|
+
<mat-label>Posicionamento</mat-label>
|
|
3964
|
+
<mat-select
|
|
3965
|
+
[value]="actionsModel.placement || null"
|
|
3966
|
+
(selectionChange)="updateActionsLayout('placement', $event.value)"
|
|
3967
|
+
>
|
|
3968
|
+
<mat-option value="afterSections">Após seções</mat-option>
|
|
3969
|
+
<mat-option value="insideLastSection">Dentro da última seção</mat-option>
|
|
3970
|
+
<mat-option value="top">Topo</mat-option>
|
|
3971
|
+
</mat-select>
|
|
3972
|
+
</mat-form-field>
|
|
3973
|
+
<mat-slide-toggle
|
|
3974
|
+
[checked]="!!actionsModel.sticky"
|
|
3975
|
+
(change)="updateActionsLayout('sticky', $event.checked)"
|
|
3976
|
+
>
|
|
3977
|
+
Ações fixas
|
|
3978
|
+
</mat-slide-toggle>
|
|
3979
|
+
<mat-slide-toggle
|
|
3980
|
+
[checked]="!!actionsModel.divider"
|
|
3981
|
+
(change)="updateActionsLayout('divider', $event.checked)"
|
|
3982
|
+
>
|
|
3983
|
+
Mostrar divisor
|
|
3984
|
+
</mat-slide-toggle>
|
|
3985
|
+
</div>
|
|
3986
|
+
</section>
|
|
3987
|
+
|
|
3988
|
+
<section class="mf-section">
|
|
3989
|
+
<div class="mf-section__header mf-section__header--row">
|
|
3990
|
+
<div>
|
|
3991
|
+
<h3>Ações customizadas</h3>
|
|
3992
|
+
<p class="mf-help">Crie botões extras com evento próprio.</p>
|
|
3993
|
+
</div>
|
|
3994
|
+
<button mat-stroked-button type="button" (click)="addCustomAction()">
|
|
3995
|
+
<mat-icon>add</mat-icon>
|
|
3996
|
+
Adicionar ação
|
|
3997
|
+
</button>
|
|
3998
|
+
</div>
|
|
3999
|
+
<div class="mf-custom-actions">
|
|
4000
|
+
@if (actionsModel.custom?.length) {
|
|
4001
|
+
@for (action of actionsModel.custom || []; track action.id) {
|
|
4002
|
+
<div class="mf-custom-card">
|
|
4003
|
+
<div class="mf-custom-card__header">
|
|
4004
|
+
<div class="mf-custom-card__title">{{ action.label || action.id }}</div>
|
|
4005
|
+
<button mat-icon-button type="button" (click)="removeCustomAction(action)">
|
|
4006
|
+
<mat-icon>delete</mat-icon>
|
|
4007
|
+
</button>
|
|
4008
|
+
</div>
|
|
4009
|
+
<div class="mf-custom-card__grid">
|
|
4010
|
+
<mat-form-field appearance="outline">
|
|
4011
|
+
<mat-label>ID</mat-label>
|
|
4012
|
+
<input
|
|
4013
|
+
matInput
|
|
4014
|
+
[value]="action.id || ''"
|
|
4015
|
+
(input)="updateCustomActionField(action, 'id', $any($event.target).value)"
|
|
4016
|
+
/>
|
|
4017
|
+
</mat-form-field>
|
|
4018
|
+
<mat-form-field appearance="outline">
|
|
4019
|
+
<mat-label>Label</mat-label>
|
|
4020
|
+
<input
|
|
4021
|
+
matInput
|
|
4022
|
+
[value]="action.label || ''"
|
|
4023
|
+
(input)="updateCustomActionField(action, 'label', $any($event.target).value)"
|
|
4024
|
+
/>
|
|
4025
|
+
</mat-form-field>
|
|
4026
|
+
<mat-form-field appearance="outline">
|
|
4027
|
+
<mat-label>Evento (action)</mat-label>
|
|
4028
|
+
<input
|
|
4029
|
+
matInput
|
|
4030
|
+
[value]="action.action || ''"
|
|
4031
|
+
(input)="updateCustomActionField(action, 'action', $any($event.target).value)"
|
|
4032
|
+
/>
|
|
4033
|
+
</mat-form-field>
|
|
4034
|
+
<mat-form-field appearance="outline">
|
|
4035
|
+
<mat-label>Ícone</mat-label>
|
|
4036
|
+
<input
|
|
4037
|
+
matInput
|
|
4038
|
+
[value]="action.icon || ''"
|
|
4039
|
+
(input)="updateCustomActionField(action, 'icon', $any($event.target).value)"
|
|
4040
|
+
/>
|
|
4041
|
+
</mat-form-field>
|
|
4042
|
+
<mat-form-field appearance="outline">
|
|
4043
|
+
<mat-label>Variante</mat-label>
|
|
4044
|
+
<mat-select
|
|
4045
|
+
[value]="action.variant || null"
|
|
4046
|
+
(selectionChange)="updateCustomActionField(action, 'variant', $event.value)"
|
|
4047
|
+
>
|
|
4048
|
+
<mat-option [value]="null">Padrão</mat-option>
|
|
4049
|
+
<mat-option value="raised">Elevado</mat-option>
|
|
4050
|
+
<mat-option value="stroked">Contornado</mat-option>
|
|
4051
|
+
<mat-option value="flat">Plano</mat-option>
|
|
4052
|
+
<mat-option value="fab">Flutuante</mat-option>
|
|
4053
|
+
</mat-select>
|
|
4054
|
+
</mat-form-field>
|
|
4055
|
+
<mat-form-field appearance="outline">
|
|
4056
|
+
<mat-label>Cor</mat-label>
|
|
4057
|
+
<mat-select
|
|
4058
|
+
[value]="action.color || null"
|
|
4059
|
+
(selectionChange)="updateCustomActionField(action, 'color', $event.value)"
|
|
4060
|
+
>
|
|
4061
|
+
<mat-option [value]="null">Padrão</mat-option>
|
|
4062
|
+
<mat-option value="primary">Primary</mat-option>
|
|
4063
|
+
<mat-option value="accent">Accent</mat-option>
|
|
4064
|
+
<mat-option value="warn">Warn</mat-option>
|
|
4065
|
+
<mat-option value="basic">Basic</mat-option>
|
|
4066
|
+
</mat-select>
|
|
4067
|
+
</mat-form-field>
|
|
4068
|
+
<mat-form-field appearance="outline">
|
|
4069
|
+
<mat-label>Atalho</mat-label>
|
|
4070
|
+
<input
|
|
4071
|
+
matInput
|
|
4072
|
+
[value]="action.shortcut || ''"
|
|
4073
|
+
(input)="updateCustomActionField(action, 'shortcut', $any($event.target).value)"
|
|
4074
|
+
/>
|
|
4075
|
+
</mat-form-field>
|
|
4076
|
+
<mat-form-field appearance="outline">
|
|
4077
|
+
<mat-label>Tooltip</mat-label>
|
|
4078
|
+
<input
|
|
4079
|
+
matInput
|
|
4080
|
+
[value]="action.tooltip || ''"
|
|
4081
|
+
(input)="updateCustomActionField(action, 'tooltip', $any($event.target).value)"
|
|
4082
|
+
/>
|
|
4083
|
+
</mat-form-field>
|
|
4084
|
+
<mat-slide-toggle
|
|
4085
|
+
[checked]="action.visible !== false"
|
|
4086
|
+
(change)="updateCustomActionField(action, 'visible', $event.checked)"
|
|
4087
|
+
>
|
|
4088
|
+
Visível
|
|
4089
|
+
</mat-slide-toggle>
|
|
4090
|
+
</div>
|
|
4091
|
+
</div>
|
|
4092
|
+
}
|
|
4093
|
+
} @else {
|
|
4094
|
+
<p class="mf-empty">Nenhuma ação customizada cadastrada.</p>
|
|
4095
|
+
}
|
|
4096
|
+
</div>
|
|
4097
|
+
</section>
|
|
1477
4098
|
</div>
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
4099
|
+
</mat-tab>
|
|
4100
|
+
|
|
4101
|
+
<mat-tab label="Mensagens">
|
|
4102
|
+
<div class="mf-tab">
|
|
4103
|
+
<section class="mf-section">
|
|
4104
|
+
<div class="mf-section__header">
|
|
4105
|
+
<div>
|
|
4106
|
+
<h3>Feedback padrão</h3>
|
|
4107
|
+
<p class="mf-help">Mensagens de sucesso/erro exibidas pelo host.</p>
|
|
4108
|
+
</div>
|
|
4109
|
+
</div>
|
|
4110
|
+
<div class="mf-messages-grid">
|
|
4111
|
+
<mat-form-field appearance="outline">
|
|
4112
|
+
<mat-label>Sucesso ao criar</mat-label>
|
|
4113
|
+
<input
|
|
4114
|
+
matInput
|
|
4115
|
+
[value]="messagesModel.createRegistrySuccess || ''"
|
|
4116
|
+
(input)="updateMessage('createRegistrySuccess', $any($event.target).value)"
|
|
4117
|
+
/>
|
|
4118
|
+
</mat-form-field>
|
|
4119
|
+
<mat-form-field appearance="outline">
|
|
4120
|
+
<mat-label>Erro ao criar</mat-label>
|
|
4121
|
+
<input
|
|
4122
|
+
matInput
|
|
4123
|
+
[value]="messagesModel.createRegistryError || ''"
|
|
4124
|
+
(input)="updateMessage('createRegistryError', $any($event.target).value)"
|
|
4125
|
+
/>
|
|
4126
|
+
</mat-form-field>
|
|
4127
|
+
<mat-form-field appearance="outline">
|
|
4128
|
+
<mat-label>Sucesso ao atualizar</mat-label>
|
|
4129
|
+
<input
|
|
4130
|
+
matInput
|
|
4131
|
+
[value]="messagesModel.updateRegistrySuccess || ''"
|
|
4132
|
+
(input)="updateMessage('updateRegistrySuccess', $any($event.target).value)"
|
|
4133
|
+
/>
|
|
4134
|
+
</mat-form-field>
|
|
4135
|
+
<mat-form-field appearance="outline">
|
|
4136
|
+
<mat-label>Erro ao atualizar</mat-label>
|
|
4137
|
+
<input
|
|
4138
|
+
matInput
|
|
4139
|
+
[value]="messagesModel.updateRegistryError || ''"
|
|
4140
|
+
(input)="updateMessage('updateRegistryError', $any($event.target).value)"
|
|
4141
|
+
/>
|
|
4142
|
+
</mat-form-field>
|
|
4143
|
+
</div>
|
|
4144
|
+
</section>
|
|
4145
|
+
|
|
4146
|
+
<section class="mf-section">
|
|
4147
|
+
<div class="mf-section__header">
|
|
4148
|
+
<div>
|
|
4149
|
+
<h3>Confirmações</h3>
|
|
4150
|
+
<p class="mf-help">Textos mostrados antes de ações críticas.</p>
|
|
4151
|
+
</div>
|
|
4152
|
+
</div>
|
|
4153
|
+
<div class="mf-messages-grid">
|
|
4154
|
+
<mat-form-field appearance="outline">
|
|
4155
|
+
<mat-label>Enviar</mat-label>
|
|
4156
|
+
<input
|
|
4157
|
+
matInput
|
|
4158
|
+
[value]="messagesModel.confirmations?.submit || ''"
|
|
4159
|
+
(input)="updateConfirmation('submit', $any($event.target).value)"
|
|
4160
|
+
/>
|
|
4161
|
+
</mat-form-field>
|
|
4162
|
+
<mat-form-field appearance="outline">
|
|
4163
|
+
<mat-label>Cancelar</mat-label>
|
|
4164
|
+
<input
|
|
4165
|
+
matInput
|
|
4166
|
+
[value]="messagesModel.confirmations?.cancel || ''"
|
|
4167
|
+
(input)="updateConfirmation('cancel', $any($event.target).value)"
|
|
4168
|
+
/>
|
|
4169
|
+
</mat-form-field>
|
|
4170
|
+
<mat-form-field appearance="outline">
|
|
4171
|
+
<mat-label>Resetar</mat-label>
|
|
4172
|
+
<input
|
|
4173
|
+
matInput
|
|
4174
|
+
[value]="messagesModel.confirmations?.reset || ''"
|
|
4175
|
+
(input)="updateConfirmation('reset', $any($event.target).value)"
|
|
4176
|
+
/>
|
|
4177
|
+
</mat-form-field>
|
|
4178
|
+
</div>
|
|
4179
|
+
</section>
|
|
4180
|
+
|
|
4181
|
+
<section class="mf-section">
|
|
4182
|
+
<div class="mf-section__header">
|
|
4183
|
+
<div>
|
|
4184
|
+
<h3>Loading</h3>
|
|
4185
|
+
<p class="mf-help">Mensagens exibidas durante operações.</p>
|
|
4186
|
+
</div>
|
|
4187
|
+
</div>
|
|
4188
|
+
<div class="mf-messages-grid">
|
|
4189
|
+
<mat-form-field appearance="outline">
|
|
4190
|
+
<mat-label>Salvar</mat-label>
|
|
4191
|
+
<input
|
|
4192
|
+
matInput
|
|
4193
|
+
[value]="messagesModel.loading?.submit || ''"
|
|
4194
|
+
(input)="updateLoading('submit', $any($event.target).value)"
|
|
4195
|
+
/>
|
|
4196
|
+
</mat-form-field>
|
|
4197
|
+
<mat-form-field appearance="outline">
|
|
4198
|
+
<mat-label>Cancelar</mat-label>
|
|
4199
|
+
<input
|
|
4200
|
+
matInput
|
|
4201
|
+
[value]="messagesModel.loading?.cancel || ''"
|
|
4202
|
+
(input)="updateLoading('cancel', $any($event.target).value)"
|
|
4203
|
+
/>
|
|
4204
|
+
</mat-form-field>
|
|
4205
|
+
<mat-form-field appearance="outline">
|
|
4206
|
+
<mat-label>Resetar</mat-label>
|
|
4207
|
+
<input
|
|
4208
|
+
matInput
|
|
4209
|
+
[value]="messagesModel.loading?.reset || ''"
|
|
4210
|
+
(input)="updateLoading('reset', $any($event.target).value)"
|
|
4211
|
+
/>
|
|
4212
|
+
</mat-form-field>
|
|
4213
|
+
</div>
|
|
4214
|
+
</section>
|
|
4215
|
+
|
|
4216
|
+
<section class="mf-section">
|
|
4217
|
+
<div class="mf-section__header">
|
|
4218
|
+
<div>
|
|
4219
|
+
<h3>Mensagens por ação customizada</h3>
|
|
4220
|
+
<p class="mf-help">Personalize textos por ID de ação.</p>
|
|
4221
|
+
</div>
|
|
4222
|
+
</div>
|
|
4223
|
+
<div class="mf-custom-messages">
|
|
4224
|
+
@if (actionsModel.custom?.length) {
|
|
4225
|
+
@for (action of actionsModel.custom || []; track action.id) {
|
|
4226
|
+
<div class="mf-custom-message-card">
|
|
4227
|
+
<div class="mf-custom-card__title">{{ action.label || action.id }}</div>
|
|
4228
|
+
<div class="mf-custom-card__grid">
|
|
4229
|
+
<mat-form-field appearance="outline">
|
|
4230
|
+
<mat-label>Confirmação</mat-label>
|
|
4231
|
+
<input
|
|
4232
|
+
matInput
|
|
4233
|
+
[value]="messagesModel.customActions?.[action.id || '']?.confirmation || ''"
|
|
4234
|
+
(input)="updateCustomMessage(action, 'confirmation', $any($event.target).value)"
|
|
4235
|
+
/>
|
|
4236
|
+
</mat-form-field>
|
|
4237
|
+
<mat-form-field appearance="outline">
|
|
4238
|
+
<mat-label>Sucesso</mat-label>
|
|
4239
|
+
<input
|
|
4240
|
+
matInput
|
|
4241
|
+
[value]="messagesModel.customActions?.[action.id || '']?.success || ''"
|
|
4242
|
+
(input)="updateCustomMessage(action, 'success', $any($event.target).value)"
|
|
4243
|
+
/>
|
|
4244
|
+
</mat-form-field>
|
|
4245
|
+
<mat-form-field appearance="outline">
|
|
4246
|
+
<mat-label>Erro</mat-label>
|
|
4247
|
+
<input
|
|
4248
|
+
matInput
|
|
4249
|
+
[value]="messagesModel.customActions?.[action.id || '']?.error || ''"
|
|
4250
|
+
(input)="updateCustomMessage(action, 'error', $any($event.target).value)"
|
|
4251
|
+
/>
|
|
4252
|
+
</mat-form-field>
|
|
4253
|
+
</div>
|
|
4254
|
+
</div>
|
|
4255
|
+
}
|
|
4256
|
+
} @else {
|
|
4257
|
+
<p class="mf-empty">Cadastre ações customizadas para configurar mensagens específicas.</p>
|
|
4258
|
+
}
|
|
4259
|
+
</div>
|
|
4260
|
+
</section>
|
|
1483
4261
|
</div>
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
4262
|
+
</mat-tab>
|
|
4263
|
+
|
|
4264
|
+
<mat-tab label="Comportamento">
|
|
4265
|
+
<div class="mf-tab">
|
|
4266
|
+
<section class="mf-section">
|
|
4267
|
+
<div class="mf-section__header">
|
|
4268
|
+
<div>
|
|
4269
|
+
<h3>Comportamento do formulário</h3>
|
|
4270
|
+
<p class="mf-help">Preferências aplicadas pelo host ou runtime.</p>
|
|
4271
|
+
</div>
|
|
4272
|
+
</div>
|
|
4273
|
+
<div class="mf-note">Estas opções são persistidas no config; o host decide como aplicar.</div>
|
|
4274
|
+
<div class="mf-toggle-grid">
|
|
4275
|
+
<mat-slide-toggle
|
|
4276
|
+
[checked]="!!behaviorModel.confirmOnUnsavedChanges"
|
|
4277
|
+
(change)="updateBehavior('confirmOnUnsavedChanges', $event.checked)"
|
|
4278
|
+
>
|
|
4279
|
+
Confirmar ao sair com alterações não salvas
|
|
4280
|
+
</mat-slide-toggle>
|
|
4281
|
+
<mat-slide-toggle
|
|
4282
|
+
[checked]="!!behaviorModel.trackHistory"
|
|
4283
|
+
(change)="updateBehavior('trackHistory', $event.checked)"
|
|
4284
|
+
>
|
|
4285
|
+
Rastrear histórico de alterações
|
|
4286
|
+
</mat-slide-toggle>
|
|
4287
|
+
<mat-slide-toggle
|
|
4288
|
+
[checked]="!!behaviorModel.focusFirstError"
|
|
4289
|
+
(change)="updateBehavior('focusFirstError', $event.checked)"
|
|
4290
|
+
>
|
|
4291
|
+
Focar no primeiro erro ao submeter
|
|
4292
|
+
</mat-slide-toggle>
|
|
4293
|
+
<mat-slide-toggle
|
|
4294
|
+
[checked]="!!behaviorModel.scrollToErrors"
|
|
4295
|
+
(change)="updateBehavior('scrollToErrors', $event.checked)"
|
|
4296
|
+
>
|
|
4297
|
+
Rolar até os erros ao submeter
|
|
4298
|
+
</mat-slide-toggle>
|
|
4299
|
+
<mat-slide-toggle
|
|
4300
|
+
[checked]="!!behaviorModel.clearAfterSave"
|
|
4301
|
+
(change)="updateBehavior('clearAfterSave', $event.checked)"
|
|
4302
|
+
>
|
|
4303
|
+
Limpar formulário após salvar
|
|
4304
|
+
</mat-slide-toggle>
|
|
4305
|
+
<mat-slide-toggle
|
|
4306
|
+
[checked]="!!behaviorModel.reactiveValidation"
|
|
4307
|
+
(change)="updateBehavior('reactiveValidation', $event.checked)"
|
|
4308
|
+
>
|
|
4309
|
+
Validação reativa
|
|
4310
|
+
</mat-slide-toggle>
|
|
4311
|
+
</div>
|
|
4312
|
+
<div class="mf-actions-layout">
|
|
4313
|
+
<mat-form-field appearance="outline">
|
|
4314
|
+
<mat-label>Redirecionar após salvar (URL)</mat-label>
|
|
4315
|
+
<input
|
|
4316
|
+
matInput
|
|
4317
|
+
[value]="behaviorModel.redirectAfterSave || ''"
|
|
4318
|
+
(input)="updateBehavior('redirectAfterSave', $any($event.target).value)"
|
|
4319
|
+
/>
|
|
4320
|
+
</mat-form-field>
|
|
4321
|
+
<mat-form-field appearance="outline">
|
|
4322
|
+
<mat-label>Debounce validação reativa (ms)</mat-label>
|
|
4323
|
+
<input
|
|
4324
|
+
matInput
|
|
4325
|
+
type="number"
|
|
4326
|
+
min="0"
|
|
4327
|
+
[value]="behaviorModel.reactiveValidationDebounceMs || 0"
|
|
4328
|
+
(input)="updateBehavior('reactiveValidationDebounceMs', $any($event.target).valueAsNumber || 0)"
|
|
4329
|
+
/>
|
|
4330
|
+
</mat-form-field>
|
|
4331
|
+
</div>
|
|
4332
|
+
</section>
|
|
1489
4333
|
</div>
|
|
1490
|
-
</
|
|
1491
|
-
|
|
4334
|
+
</mat-tab>
|
|
4335
|
+
|
|
4336
|
+
<mat-tab label="Dicas">
|
|
4337
|
+
<div class="mf-tab">
|
|
4338
|
+
<section class="mf-section">
|
|
4339
|
+
<div class="mf-section__header">
|
|
4340
|
+
<div>
|
|
4341
|
+
<h3>Mensagens de modo</h3>
|
|
4342
|
+
<p class="mf-help">Textos auxiliares exibidos pelo host.</p>
|
|
4343
|
+
</div>
|
|
4344
|
+
</div>
|
|
4345
|
+
<div class="mf-note">Útil para i18n e orientação do usuário.</div>
|
|
4346
|
+
<div class="mf-messages-grid">
|
|
4347
|
+
<mat-form-field appearance="outline">
|
|
4348
|
+
<mat-label>Criar</mat-label>
|
|
4349
|
+
<input
|
|
4350
|
+
matInput
|
|
4351
|
+
[value]="hintsModel.dataModes.create"
|
|
4352
|
+
(input)="updateHints('dataModes', 'create', $any($event.target).value)"
|
|
4353
|
+
/>
|
|
4354
|
+
</mat-form-field>
|
|
4355
|
+
<mat-form-field appearance="outline">
|
|
4356
|
+
<mat-label>Editar</mat-label>
|
|
4357
|
+
<input
|
|
4358
|
+
matInput
|
|
4359
|
+
[value]="hintsModel.dataModes.edit"
|
|
4360
|
+
(input)="updateHints('dataModes', 'edit', $any($event.target).value)"
|
|
4361
|
+
/>
|
|
4362
|
+
</mat-form-field>
|
|
4363
|
+
<mat-form-field appearance="outline">
|
|
4364
|
+
<mat-label>Visualizar</mat-label>
|
|
4365
|
+
<input
|
|
4366
|
+
matInput
|
|
4367
|
+
[value]="hintsModel.dataModes.view"
|
|
4368
|
+
(input)="updateHints('dataModes', 'view', $any($event.target).value)"
|
|
4369
|
+
/>
|
|
4370
|
+
</mat-form-field>
|
|
4371
|
+
</div>
|
|
4372
|
+
</section>
|
|
4373
|
+
|
|
4374
|
+
<section class="mf-section">
|
|
4375
|
+
<div class="mf-section__header">
|
|
4376
|
+
<div>
|
|
4377
|
+
<h3>Dicas de UI</h3>
|
|
4378
|
+
<p class="mf-help">Ajuda contextual para estados do formulário.</p>
|
|
4379
|
+
</div>
|
|
4380
|
+
</div>
|
|
4381
|
+
<div class="mf-messages-grid">
|
|
4382
|
+
<mat-form-field appearance="outline">
|
|
4383
|
+
<mat-label>Apresentação</mat-label>
|
|
4384
|
+
<textarea
|
|
4385
|
+
matInput
|
|
4386
|
+
rows="2"
|
|
4387
|
+
[value]="hintsModel.uiModes.presentation"
|
|
4388
|
+
(input)="updateHints('uiModes', 'presentation', $any($event.target).value)"
|
|
4389
|
+
></textarea>
|
|
4390
|
+
</mat-form-field>
|
|
4391
|
+
<mat-form-field appearance="outline">
|
|
4392
|
+
<mat-label>Somente leitura</mat-label>
|
|
4393
|
+
<textarea
|
|
4394
|
+
matInput
|
|
4395
|
+
rows="2"
|
|
4396
|
+
[value]="hintsModel.uiModes.readonly"
|
|
4397
|
+
(input)="updateHints('uiModes', 'readonly', $any($event.target).value)"
|
|
4398
|
+
></textarea>
|
|
4399
|
+
</mat-form-field>
|
|
4400
|
+
<mat-form-field appearance="outline">
|
|
4401
|
+
<mat-label>Desabilitado</mat-label>
|
|
4402
|
+
<textarea
|
|
4403
|
+
matInput
|
|
4404
|
+
rows="2"
|
|
4405
|
+
[value]="hintsModel.uiModes.disabled"
|
|
4406
|
+
(input)="updateHints('uiModes', 'disabled', $any($event.target).value)"
|
|
4407
|
+
></textarea>
|
|
4408
|
+
</mat-form-field>
|
|
4409
|
+
<mat-form-field appearance="outline">
|
|
4410
|
+
<mat-label>Visível</mat-label>
|
|
4411
|
+
<textarea
|
|
4412
|
+
matInput
|
|
4413
|
+
rows="2"
|
|
4414
|
+
[value]="hintsModel.uiModes.visible"
|
|
4415
|
+
(input)="updateHints('uiModes', 'visible', $any($event.target).value)"
|
|
4416
|
+
></textarea>
|
|
4417
|
+
</mat-form-field>
|
|
4418
|
+
</div>
|
|
4419
|
+
</section>
|
|
4420
|
+
</div>
|
|
4421
|
+
</mat-tab>
|
|
4422
|
+
|
|
4423
|
+
<mat-tab label="Hooks">
|
|
4424
|
+
<div class="mf-tab">
|
|
4425
|
+
<section class="mf-section">
|
|
4426
|
+
<div class="mf-section__header">
|
|
4427
|
+
<div>
|
|
4428
|
+
<h3>Hooks de ciclo de vida</h3>
|
|
4429
|
+
<p class="mf-help">Defina ações automatizadas por estágio.</p>
|
|
4430
|
+
</div>
|
|
4431
|
+
<div class="mf-actions-row">
|
|
4432
|
+
<button mat-stroked-button type="button" (click)="resetHooks()">
|
|
4433
|
+
Restaurar
|
|
4434
|
+
</button>
|
|
4435
|
+
<button mat-flat-button color="primary" type="button" (click)="applyHooks()">
|
|
4436
|
+
Aplicar hooks
|
|
4437
|
+
</button>
|
|
4438
|
+
</div>
|
|
4439
|
+
</div>
|
|
4440
|
+
<div class="mf-note">Executa apenas se o host registrar hooks disponíveis.</div>
|
|
4441
|
+
<div class="mf-hooks-grid">
|
|
4442
|
+
@for (stage of hookStages; track stage) {
|
|
4443
|
+
<mat-form-field appearance="outline" class="mf-hook-field">
|
|
4444
|
+
<mat-label>{{ hookLabel(stage) }}</mat-label>
|
|
4445
|
+
<textarea
|
|
4446
|
+
matInput
|
|
4447
|
+
rows="4"
|
|
4448
|
+
[value]="hooksText[stage]"
|
|
4449
|
+
(input)="onHookTextChange(stage, $any($event.target).value)"
|
|
4450
|
+
placeholder='[{ "id": "notifySuccess", "priority": 0, "args": {} }]'
|
|
4451
|
+
></textarea>
|
|
4452
|
+
<button
|
|
4453
|
+
mat-icon-button
|
|
4454
|
+
matSuffix
|
|
4455
|
+
class="help-icon-button"
|
|
4456
|
+
type="button"
|
|
4457
|
+
[matTooltip]="'Array JSON de hooks para ' + stage + ' (id, priority, timeoutMs, args).'"
|
|
4458
|
+
matTooltipPosition="above"
|
|
4459
|
+
>
|
|
4460
|
+
<mat-icon>help_outline</mat-icon>
|
|
4461
|
+
</button>
|
|
4462
|
+
@if (hooksErrors[stage]) {
|
|
4463
|
+
<mat-error>{{ hooksErrors[stage] }}</mat-error>
|
|
4464
|
+
}
|
|
4465
|
+
</mat-form-field>
|
|
4466
|
+
}
|
|
4467
|
+
</div>
|
|
4468
|
+
</section>
|
|
4469
|
+
</div>
|
|
4470
|
+
</mat-tab>
|
|
4471
|
+
|
|
4472
|
+
<mat-tab label="Regras">
|
|
4473
|
+
<div class="mf-tab">
|
|
4474
|
+
<section class="mf-section">
|
|
4475
|
+
<div class="mf-section__header">
|
|
4476
|
+
<div>
|
|
4477
|
+
<h3>Regras de layout</h3>
|
|
4478
|
+
<p class="mf-help">Regras avançadas para visibilidade e comportamento.</p>
|
|
4479
|
+
</div>
|
|
4480
|
+
<div class="mf-actions-row">
|
|
4481
|
+
<button mat-flat-button color="primary" type="button" (click)="applyRules()">
|
|
4482
|
+
Aplicar regras
|
|
4483
|
+
</button>
|
|
4484
|
+
</div>
|
|
4485
|
+
</div>
|
|
4486
|
+
<div class="mf-note">Persistido no config; a execução depende do host.</div>
|
|
4487
|
+
<mat-form-field appearance="outline">
|
|
4488
|
+
<mat-label>formRules (JSON)</mat-label>
|
|
4489
|
+
<textarea
|
|
4490
|
+
matInput
|
|
4491
|
+
rows="6"
|
|
4492
|
+
[value]="formRulesText"
|
|
4493
|
+
(input)="onRulesTextChange($any($event.target).value)"
|
|
4494
|
+
placeholder='[{"id":"rule-1","name":"Obrigatoriedade","targetType":"field","targets":["campo"],"effect":{"condition":"{campo} != null","properties":{"required":true}}}]'
|
|
4495
|
+
></textarea>
|
|
4496
|
+
<button
|
|
4497
|
+
mat-icon-button
|
|
4498
|
+
matSuffix
|
|
4499
|
+
class="help-icon-button"
|
|
4500
|
+
type="button"
|
|
4501
|
+
[matTooltip]="'Array JSON com regras (id, targetType, targets, effect).'"
|
|
4502
|
+
matTooltipPosition="above"
|
|
4503
|
+
>
|
|
4504
|
+
<mat-icon>help_outline</mat-icon>
|
|
4505
|
+
</button>
|
|
4506
|
+
@if (formRulesError) {
|
|
4507
|
+
<mat-error>{{ formRulesError }}</mat-error>
|
|
4508
|
+
}
|
|
4509
|
+
</mat-form-field>
|
|
4510
|
+
<mat-form-field appearance="outline">
|
|
4511
|
+
<mat-label>formRulesState (JSON)</mat-label>
|
|
4512
|
+
<textarea
|
|
4513
|
+
matInput
|
|
4514
|
+
rows="6"
|
|
4515
|
+
[value]="formRulesStateText"
|
|
4516
|
+
(input)="onRulesStateTextChange($any($event.target).value)"
|
|
4517
|
+
placeholder='{"nodes":[],"edges":[]}'
|
|
4518
|
+
></textarea>
|
|
4519
|
+
<button
|
|
4520
|
+
mat-icon-button
|
|
4521
|
+
matSuffix
|
|
4522
|
+
class="help-icon-button"
|
|
4523
|
+
type="button"
|
|
4524
|
+
[matTooltip]="'Estado bruto do editor visual (opcional).'"
|
|
4525
|
+
matTooltipPosition="above"
|
|
4526
|
+
>
|
|
4527
|
+
<mat-icon>help_outline</mat-icon>
|
|
4528
|
+
</button>
|
|
4529
|
+
@if (formRulesStateError) {
|
|
4530
|
+
<mat-error>{{ formRulesStateError }}</mat-error>
|
|
4531
|
+
}
|
|
4532
|
+
</mat-form-field>
|
|
4533
|
+
<div class="mf-actions-row mf-actions-row--end">
|
|
4534
|
+
<button mat-flat-button color="primary" type="button" (click)="applyRules()">
|
|
4535
|
+
Aplicar regras
|
|
4536
|
+
</button>
|
|
4537
|
+
</div>
|
|
4538
|
+
</section>
|
|
4539
|
+
</div>
|
|
4540
|
+
</mat-tab>
|
|
4541
|
+
|
|
4542
|
+
<mat-tab label="Cascatas">
|
|
4543
|
+
<div class="mf-tab">
|
|
4544
|
+
<section class="mf-section mf-section--full">
|
|
4545
|
+
<div class="mf-section__header">
|
|
4546
|
+
<div>
|
|
4547
|
+
<h3>Dependências entre campos</h3>
|
|
4548
|
+
<p class="mf-help">Configure cascatas nativas baseadas em metadados.</p>
|
|
4549
|
+
</div>
|
|
4550
|
+
</div>
|
|
4551
|
+
<div class="mf-note">As alterações atualizam metadados e são salvas no draft.</div>
|
|
4552
|
+
<praxis-cascade-manager-tab
|
|
4553
|
+
[fields]="cascadeFields"
|
|
4554
|
+
(apply)="applyCascadePatch($event)"
|
|
4555
|
+
></praxis-cascade-manager-tab>
|
|
4556
|
+
</section>
|
|
4557
|
+
</div>
|
|
4558
|
+
</mat-tab>
|
|
4559
|
+
</mat-tab-group>
|
|
1492
4560
|
</div>
|
|
1493
|
-
`, styles: [".mf-editor{display:grid;gap:
|
|
4561
|
+
`, styles: [".mf-editor{display:grid;gap:16px;padding:16px;color:var(--md-sys-color-on-surface)}.mf-editor__toolbar{display:flex;align-items:center;gap:12px;padding:12px 14px;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;background:var(--md-sys-color-surface-container)}.mf-editor__title{display:flex;flex-direction:column;gap:2px;font-weight:600}.mf-editor__subtitle{font-weight:400;color:var(--md-sys-color-on-surface-variant)}.mf-editor__toolbar .spacer{flex:1}.mf-tabs{min-height:420px}.mf-tab{display:grid;gap:16px;padding:14px 6px 0}.mf-editor__list{display:grid;gap:8px;overflow-x:auto}.mf-editor__row{display:grid;grid-template-columns:2.2fr 2fr 1fr repeat(4,minmax(90px,.8fr));align-items:center;gap:8px;padding:10px 12px;border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;background:var(--md-sys-color-surface);min-width:720px}.mf-editor__row--head{font-weight:600;color:var(--md-sys-color-on-surface);background:var(--md-sys-color-surface-container)}.mf-editor__filter{display:inline-flex;align-items:center;gap:8px;font-weight:500}.mf-editor__filter input{accent-color:var(--md-sys-color-primary)}.col code{display:inline-block;font-family:inherit;font-size:.85rem;padding:2px 8px;border-radius:6px;background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface-variant)}.mf-btn{min-height:36px;padding:0 12px;border-radius:8px;border:1px solid var(--md-sys-color-outline);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);cursor:pointer;transition:background-color .18s ease,border-color .18s ease,color .18s ease}.mf-btn:hover{background:var(--md-sys-color-surface-container)}.mf-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.toggle{display:inline-flex;align-items:center;gap:6px;color:var(--md-sys-color-on-surface-variant)}.mf-section{display:grid;gap:12px;padding:16px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface)}.mf-section--full{padding:10px}.mf-section__header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.mf-section__header h3{margin:0;font-size:1.05rem;font-weight:600}.mf-help{margin:6px 0 0;font-size:.9rem;color:var(--md-sys-color-on-surface-variant)}.mf-note{padding:10px 12px;border-radius:10px;border:1px dashed var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface-variant);font-size:.9rem}.help-icon-button{--mdc-icon-button-state-layer-size: 28px;--mdc-icon-button-icon-size: 18px;width:28px;height:28px;padding:0;display:inline-flex;align-items:center;justify-content:center;margin-right:-4px}.help-icon-button mat-icon{font-size:18px;width:18px;height:18px}.mat-mdc-form-field-icon-suffix{align-self:center}.mf-actions-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px}.mf-action-card,.mf-custom-card,.mf-custom-message-card{display:grid;gap:10px;padding:14px;border-radius:10px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high)}.mf-action-card__title,.mf-custom-card__title{font-weight:600;color:var(--md-sys-color-on-surface)}.mf-actions-layout{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;align-items:center}.mf-toggle-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px;align-items:start}.mf-hooks-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px}.mf-hook-field textarea{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:.85rem}.mf-actions-row{display:inline-flex;gap:8px;align-items:center}.mf-actions-row--end{justify-content:flex-end;width:100%}.mf-custom-actions,.mf-custom-messages{display:grid;gap:12px}.mf-custom-card__header{display:flex;align-items:center;justify-content:space-between;gap:8px}.mf-custom-card__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;align-items:center}.mf-messages-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px}.mf-empty{margin:0;color:var(--md-sys-color-on-surface-variant)}\n"] }]
|
|
1494
4562
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
1495
4563
|
type: Inject,
|
|
1496
4564
|
args: [SETTINGS_PANEL_DATA]
|
|
1497
|
-
}] }, { type:
|
|
4565
|
+
}] }, { type: i10.SettingsPanelRef, decorators: [{
|
|
1498
4566
|
type: Inject,
|
|
1499
4567
|
args: [SETTINGS_PANEL_REF]
|
|
1500
4568
|
}] }] });
|
|
4569
|
+
const DEFAULT_ACTIONS = {
|
|
4570
|
+
submit: makeAction({ id: 'submit', label: 'Salvar', visible: true, type: 'submit', color: 'primary' }),
|
|
4571
|
+
cancel: makeAction({ id: 'cancel', label: 'Cancelar', visible: false, type: 'button' }),
|
|
4572
|
+
reset: makeAction({ id: 'reset', label: 'Restaurar', visible: true, type: 'reset' }),
|
|
4573
|
+
custom: [],
|
|
4574
|
+
position: 'right',
|
|
4575
|
+
orientation: 'horizontal',
|
|
4576
|
+
spacing: 'normal',
|
|
4577
|
+
};
|
|
4578
|
+
function makeAction(action, index) {
|
|
4579
|
+
const fallbackId = typeof index === 'number' ? `custom-${index + 1}` : 'action';
|
|
4580
|
+
return {
|
|
4581
|
+
id: action.id ?? fallbackId,
|
|
4582
|
+
label: action.label ?? 'Ação',
|
|
4583
|
+
visible: action.visible !== false,
|
|
4584
|
+
type: action.type ?? 'button',
|
|
4585
|
+
color: action.color,
|
|
4586
|
+
disabled: action.disabled,
|
|
4587
|
+
icon: action.icon,
|
|
4588
|
+
action: action.action,
|
|
4589
|
+
tooltip: action.tooltip,
|
|
4590
|
+
variant: action.variant,
|
|
4591
|
+
size: action.size,
|
|
4592
|
+
loading: action.loading,
|
|
4593
|
+
shortcut: action.shortcut,
|
|
4594
|
+
};
|
|
4595
|
+
}
|
|
4596
|
+
function mergeAction(base, override) {
|
|
4597
|
+
if (!override) {
|
|
4598
|
+
return base;
|
|
4599
|
+
}
|
|
4600
|
+
return {
|
|
4601
|
+
...base,
|
|
4602
|
+
...override,
|
|
4603
|
+
visible: override.visible !== undefined ? override.visible : base.visible,
|
|
4604
|
+
};
|
|
4605
|
+
}
|
|
4606
|
+
function createDefaultHints() {
|
|
4607
|
+
return {
|
|
4608
|
+
dataModes: { create: '', edit: '', view: '' },
|
|
4609
|
+
uiModes: { presentation: '', readonly: '', disabled: '', visible: '' },
|
|
4610
|
+
};
|
|
4611
|
+
}
|
|
4612
|
+
function createHints(source) {
|
|
4613
|
+
if (!source) {
|
|
4614
|
+
return createDefaultHints();
|
|
4615
|
+
}
|
|
4616
|
+
return {
|
|
4617
|
+
dataModes: {
|
|
4618
|
+
create: source.dataModes?.create ?? '',
|
|
4619
|
+
edit: source.dataModes?.edit ?? '',
|
|
4620
|
+
view: source.dataModes?.view ?? '',
|
|
4621
|
+
},
|
|
4622
|
+
uiModes: {
|
|
4623
|
+
presentation: source.uiModes?.presentation ?? '',
|
|
4624
|
+
readonly: source.uiModes?.readonly ?? '',
|
|
4625
|
+
disabled: source.uiModes?.disabled ?? '',
|
|
4626
|
+
visible: source.uiModes?.visible ?? '',
|
|
4627
|
+
},
|
|
4628
|
+
};
|
|
4629
|
+
}
|
|
4630
|
+
function createDefaultHooksText() {
|
|
4631
|
+
return {
|
|
4632
|
+
beforeInit: '[]',
|
|
4633
|
+
afterInit: '[]',
|
|
4634
|
+
beforeValidate: '[]',
|
|
4635
|
+
afterValidate: '[]',
|
|
4636
|
+
beforeSubmit: '[]',
|
|
4637
|
+
afterSubmit: '[]',
|
|
4638
|
+
onError: '[]',
|
|
4639
|
+
};
|
|
4640
|
+
}
|
|
4641
|
+
function buildHooksText(source) {
|
|
4642
|
+
const base = createDefaultHooksText();
|
|
4643
|
+
if (!source)
|
|
4644
|
+
return base;
|
|
4645
|
+
for (const stage of Object.keys(base)) {
|
|
4646
|
+
const list = source[stage];
|
|
4647
|
+
base[stage] = list && list.length ? JSON.stringify(list, null, 2) : '[]';
|
|
4648
|
+
}
|
|
4649
|
+
return base;
|
|
4650
|
+
}
|
|
4651
|
+
function stringifyJson(value, pretty) {
|
|
4652
|
+
if (value === undefined)
|
|
4653
|
+
return '';
|
|
4654
|
+
try {
|
|
4655
|
+
return JSON.stringify(value, null, pretty ? 2 : 0);
|
|
4656
|
+
}
|
|
4657
|
+
catch {
|
|
4658
|
+
return '';
|
|
4659
|
+
}
|
|
4660
|
+
}
|
|
1501
4661
|
|
|
1502
4662
|
var manualFormConfigEditor_component = /*#__PURE__*/Object.freeze({
|
|
1503
4663
|
__proto__: null,
|
|
1504
4664
|
ManualFormConfigEditorComponent: ManualFormConfigEditorComponent
|
|
1505
4665
|
});
|
|
1506
4666
|
|
|
4667
|
+
let nextDocExampleId = 0;
|
|
4668
|
+
class ManualFormDocExampleComponent {
|
|
4669
|
+
title = input('Exemplo didatico', ...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
4670
|
+
subtitle = input(null, ...(ngDevMode ? [{ debugName: "subtitle" }] : []));
|
|
4671
|
+
level = input('Basico', ...(ngDevMode ? [{ debugName: "level" }] : []));
|
|
4672
|
+
bannerTitle = input(null, ...(ngDevMode ? [{ debugName: "bannerTitle" }] : []));
|
|
4673
|
+
bannerDescription = input(null, ...(ngDevMode ? [{ debugName: "bannerDescription" }] : []));
|
|
4674
|
+
templateCode = input('', ...(ngDevMode ? [{ debugName: "templateCode" }] : []));
|
|
4675
|
+
tsCode = input('', ...(ngDevMode ? [{ debugName: "tsCode" }] : []));
|
|
4676
|
+
configCode = input('', ...(ngDevMode ? [{ debugName: "configCode" }] : []));
|
|
4677
|
+
customizationEnabled = input(false, ...(ngDevMode ? [{ debugName: "customizationEnabled" }] : []));
|
|
4678
|
+
showCustomizationToggle = input(true, ...(ngDevMode ? [{ debugName: "showCustomizationToggle" }] : []));
|
|
4679
|
+
important = input(null, ...(ngDevMode ? [{ debugName: "important" }] : []));
|
|
4680
|
+
note = input(null, ...(ngDevMode ? [{ debugName: "note" }] : []));
|
|
4681
|
+
tip = input(null, ...(ngDevMode ? [{ debugName: "tip" }] : []));
|
|
4682
|
+
activeTab = signal('live', ...(ngDevMode ? [{ debugName: "activeTab" }] : []));
|
|
4683
|
+
copyStatus = signal('', ...(ngDevMode ? [{ debugName: "copyStatus" }] : []));
|
|
4684
|
+
tabs = [
|
|
4685
|
+
{ id: 'live', label: 'Live', hint: 'Renderizacao do exemplo' },
|
|
4686
|
+
{ id: 'template', label: 'Template', hint: 'Markup HTML do exemplo' },
|
|
4687
|
+
{ id: 'ts', label: 'TS', hint: 'Logica e estado do host' },
|
|
4688
|
+
{ id: 'config', label: 'Config', hint: 'JSON de configuracao do componente' },
|
|
4689
|
+
];
|
|
4690
|
+
selectedSnippet = computed(() => {
|
|
4691
|
+
switch (this.activeTab()) {
|
|
4692
|
+
case 'template':
|
|
4693
|
+
return this.templateCode();
|
|
4694
|
+
case 'ts':
|
|
4695
|
+
return this.tsCode();
|
|
4696
|
+
case 'config':
|
|
4697
|
+
return this.configCode();
|
|
4698
|
+
default:
|
|
4699
|
+
return '';
|
|
4700
|
+
}
|
|
4701
|
+
}, ...(ngDevMode ? [{ debugName: "selectedSnippet" }] : []));
|
|
4702
|
+
selectedSnippetLabel = computed(() => {
|
|
4703
|
+
switch (this.activeTab()) {
|
|
4704
|
+
case 'template':
|
|
4705
|
+
return 'Template';
|
|
4706
|
+
case 'ts':
|
|
4707
|
+
return 'TypeScript';
|
|
4708
|
+
case 'config':
|
|
4709
|
+
return 'Config';
|
|
4710
|
+
default:
|
|
4711
|
+
return 'Live';
|
|
4712
|
+
}
|
|
4713
|
+
}, ...(ngDevMode ? [{ debugName: "selectedSnippetLabel" }] : []));
|
|
4714
|
+
canCopySnippet = computed(() => this.selectedSnippet().trim().length > 0, ...(ngDevMode ? [{ debugName: "canCopySnippet" }] : []));
|
|
4715
|
+
tabChange = output();
|
|
4716
|
+
customizationEnabledChange = output();
|
|
4717
|
+
codeCopied = output();
|
|
4718
|
+
idPrefix = `pdx-manual-form-doc-example-${nextDocExampleId++}`;
|
|
4719
|
+
copyStatusTimer = null;
|
|
4720
|
+
ngOnDestroy() {
|
|
4721
|
+
this.clearCopyStatusTimer();
|
|
4722
|
+
}
|
|
4723
|
+
setTab(tab) {
|
|
4724
|
+
if (this.activeTab() === tab) {
|
|
4725
|
+
return;
|
|
4726
|
+
}
|
|
4727
|
+
this.activeTab.set(tab);
|
|
4728
|
+
this.setCopyStatus('');
|
|
4729
|
+
this.tabChange.emit(tab);
|
|
4730
|
+
}
|
|
4731
|
+
toggleCustomization() {
|
|
4732
|
+
this.customizationEnabledChange.emit(!this.customizationEnabled());
|
|
4733
|
+
}
|
|
4734
|
+
tabId(tab) {
|
|
4735
|
+
return `${this.idPrefix}-tab-${tab}`;
|
|
4736
|
+
}
|
|
4737
|
+
panelId(tab) {
|
|
4738
|
+
return `${this.idPrefix}-panel-${tab}`;
|
|
4739
|
+
}
|
|
4740
|
+
async copyActiveSnippet() {
|
|
4741
|
+
const tab = this.activeTab();
|
|
4742
|
+
const content = this.selectedSnippet();
|
|
4743
|
+
if (!content.trim()) {
|
|
4744
|
+
this.setCopyStatus('Sem conteudo para copiar');
|
|
4745
|
+
this.codeCopied.emit({ tab, success: false });
|
|
4746
|
+
return;
|
|
4747
|
+
}
|
|
4748
|
+
try {
|
|
4749
|
+
const clipboard = globalThis.navigator?.clipboard;
|
|
4750
|
+
if (!clipboard?.writeText) {
|
|
4751
|
+
this.setCopyStatus('Clipboard indisponivel');
|
|
4752
|
+
this.codeCopied.emit({ tab, success: false });
|
|
4753
|
+
return;
|
|
4754
|
+
}
|
|
4755
|
+
await clipboard.writeText(content);
|
|
4756
|
+
this.setCopyStatus('Copiado');
|
|
4757
|
+
this.codeCopied.emit({ tab, success: true });
|
|
4758
|
+
}
|
|
4759
|
+
catch {
|
|
4760
|
+
this.setCopyStatus('Falha ao copiar');
|
|
4761
|
+
this.codeCopied.emit({ tab, success: false });
|
|
4762
|
+
}
|
|
4763
|
+
}
|
|
4764
|
+
setCopyStatus(status) {
|
|
4765
|
+
this.copyStatus.set(status);
|
|
4766
|
+
this.clearCopyStatusTimer();
|
|
4767
|
+
if (status) {
|
|
4768
|
+
this.copyStatusTimer = setTimeout(() => this.copyStatus.set(''), 1800);
|
|
4769
|
+
}
|
|
4770
|
+
}
|
|
4771
|
+
clearCopyStatusTimer() {
|
|
4772
|
+
if (this.copyStatusTimer) {
|
|
4773
|
+
clearTimeout(this.copyStatusTimer);
|
|
4774
|
+
this.copyStatusTimer = null;
|
|
4775
|
+
}
|
|
4776
|
+
}
|
|
4777
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.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
|
+
|
|
1507
4961
|
class ManualFieldDirective {
|
|
1508
4962
|
templateRef;
|
|
1509
4963
|
viewContainer;
|
|
@@ -1588,5 +5042,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
1588
5042
|
* Generated bundle index. Do not edit.
|
|
1589
5043
|
*/
|
|
1590
5044
|
|
|
1591
|
-
export { MANUAL_FORM_CONSTRUCTOR_TO_CONTROL_TYPE, MANUAL_FORM_SELECTOR_TO_CONTROL_TYPE, ManualFieldDirective, ManualFieldEditorOnDblclickDirective, ManualFieldMetadataBridgeService, ManualFormActionsComponent, ManualFormComponent, ManualFormConfigEditorComponent, ManualFormHeaderComponent, ManualFormInstance, ManualFormInstanceFactory, createManualFormSeed, toFieldMetadataMap };
|
|
5045
|
+
export { MANUAL_FORM_AI_CAPABILITIES, MANUAL_FORM_CONSTRUCTOR_TO_CONTROL_TYPE, MANUAL_FORM_SELECTOR_TO_CONTROL_TYPE, ManualFieldDirective, ManualFieldEditorOnDblclickDirective, ManualFieldKeyService, ManualFieldMetadataBridgeService, ManualFormActionsComponent, ManualFormComponent, ManualFormConfigEditorComponent, ManualFormDocExampleComponent, ManualFormDocExampleShowcaseComponent, ManualFormHeaderComponent, ManualFormInstance, ManualFormInstanceFactory, createManualFormSeed, toFieldMetadataMap };
|
|
1592
5046
|
//# sourceMappingURL=praxisui-manual-form.mjs.map
|