@praxisui/settings-panel 0.0.1

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.
@@ -0,0 +1,1445 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, DestroyRef, ViewContainerRef, HostListener, ViewChild, ChangeDetectionStrategy, Component, InjectionToken, Injector, Injectable } from '@angular/core';
3
+ import { ComponentPortal } from '@angular/cdk/portal';
4
+ import * as i3 from '@angular/common';
5
+ import { CommonModule } from '@angular/common';
6
+ import * as i3$1 from '@angular/material/button';
7
+ import { MatButtonModule } from '@angular/material/button';
8
+ import * as i4 from '@angular/material/icon';
9
+ import { MatIconModule } from '@angular/material/icon';
10
+ import { PraxisIconDirective, GlobalConfigService, FieldControlType, IconPickerService } from '@praxisui/core';
11
+ import * as i5 from '@angular/material/tooltip';
12
+ import { MatTooltipModule } from '@angular/material/tooltip';
13
+ import { CdkTrapFocus } from '@angular/cdk/a11y';
14
+ import * as i6 from '@angular/material/progress-spinner';
15
+ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
16
+ import * as i1 from '@angular/material/dialog';
17
+ import { MatDialogModule } from '@angular/material/dialog';
18
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
19
+ import { isObservable, firstValueFrom, of, Subject, BehaviorSubject } from 'rxjs';
20
+ import { filter, switchMap } from 'rxjs/operators';
21
+ import { ConfirmDialogComponent } from '@praxisui/dynamic-fields';
22
+ import * as i1$1 from '@angular/cdk/overlay';
23
+ import * as i2 from '@angular/material/snack-bar';
24
+ import { MatSnackBarModule } from '@angular/material/snack-bar';
25
+ import * as i4$1 from '@angular/material/expansion';
26
+ import { MatExpansionModule } from '@angular/material/expansion';
27
+
28
+ class SettingsPanelComponent {
29
+ cdr;
30
+ dialog;
31
+ title = '';
32
+ titleIcon;
33
+ expanded = false;
34
+ ref;
35
+ contentRef;
36
+ static nextId = 0;
37
+ titleId = `praxis-settings-panel-title-${SettingsPanelComponent.nextId++}`;
38
+ isDirty = false;
39
+ isValid = true;
40
+ isBusy = false;
41
+ get canApply() {
42
+ return this.isDirty && this.isValid && !this.isBusy;
43
+ }
44
+ get canSave() {
45
+ return this.isDirty && this.isValid && !this.isBusy;
46
+ }
47
+ get disabledReason() {
48
+ if (this.isBusy) {
49
+ return 'Operação em andamento...';
50
+ }
51
+ if (!this.isValid) {
52
+ return 'O formulário contém erros que precisam ser corrigidos.';
53
+ }
54
+ if (!this.isDirty) {
55
+ return 'Nenhuma alteração para aplicar ou salvar.';
56
+ }
57
+ return '';
58
+ }
59
+ destroyRef = inject(DestroyRef);
60
+ contentHost;
61
+ constructor(cdr, dialog) {
62
+ this.cdr = cdr;
63
+ this.dialog = dialog;
64
+ }
65
+ attachContent(component, injector, ref) {
66
+ this.ref = ref;
67
+ this.contentRef = this.contentHost.createComponent(component, {
68
+ injector,
69
+ });
70
+ const instance = this.contentRef.instance;
71
+ if (instance.isDirty$) {
72
+ instance.isDirty$
73
+ .pipe(takeUntilDestroyed(this.destroyRef))
74
+ .subscribe((dirty) => {
75
+ this.isDirty = dirty;
76
+ try {
77
+ (console.log || console.debug)('[SettingsPanel] isDirty$ ->', dirty);
78
+ }
79
+ catch { }
80
+ this.cdr.markForCheck();
81
+ });
82
+ }
83
+ if (instance.isValid$) {
84
+ instance.isValid$
85
+ .pipe(takeUntilDestroyed(this.destroyRef))
86
+ .subscribe((valid) => {
87
+ this.isValid = valid;
88
+ this.cdr.markForCheck();
89
+ });
90
+ }
91
+ if (instance.isBusy$) {
92
+ instance.isBusy$
93
+ .pipe(takeUntilDestroyed(this.destroyRef))
94
+ .subscribe((busy) => {
95
+ this.isBusy = busy;
96
+ this.cdr.markForCheck();
97
+ });
98
+ }
99
+ // Generic output bridging: if the embedded component emits `selected`, save and close.
100
+ try {
101
+ const selected$ = instance?.selected;
102
+ if (selected$ && typeof selected$.subscribe === 'function') {
103
+ const obs = typeof selected$.pipe === 'function'
104
+ ? selected$.pipe(takeUntilDestroyed(this.destroyRef))
105
+ : selected$;
106
+ obs.subscribe((value) => {
107
+ try {
108
+ (console.log || console.debug)('[SettingsPanel] content.selected → apply()');
109
+ }
110
+ catch { }
111
+ // Apply emits value to the opener without closing the panel.
112
+ this.ref.apply(value);
113
+ });
114
+ }
115
+ }
116
+ catch { }
117
+ }
118
+ onReset() {
119
+ const dialogData = {
120
+ title: 'Redefinir Alterações',
121
+ message: 'Tem certeza de que deseja redefinir todas as alterações?',
122
+ confirmText: 'Redefinir',
123
+ cancelText: 'Cancelar',
124
+ type: 'warning',
125
+ icon: 'restart_alt',
126
+ };
127
+ this.dialog
128
+ .open(ConfirmDialogComponent, { data: dialogData })
129
+ .afterClosed()
130
+ .pipe(filter((confirmed) => confirmed))
131
+ .subscribe(() => {
132
+ this.contentRef?.instance?.reset?.();
133
+ this.ref.reset();
134
+ });
135
+ }
136
+ onApply() {
137
+ if (!this.canApply)
138
+ return;
139
+ const value = this.contentRef?.instance?.getSettingsValue?.();
140
+ try {
141
+ (console.log || console.debug)('[SettingsPanel] onApply()', { canApply: this.canApply });
142
+ }
143
+ catch { }
144
+ this.ref.apply(value);
145
+ }
146
+ onSave() {
147
+ if (!this.canSave)
148
+ return;
149
+ const instance = this.contentRef?.instance;
150
+ try {
151
+ const valuePreview = instance?.getSettingsValue?.();
152
+ const keys = valuePreview && typeof valuePreview === 'object' ? Object.keys(valuePreview) : undefined;
153
+ const keyTypes = valuePreview && typeof valuePreview === 'object'
154
+ ? Object.fromEntries(Object.entries(valuePreview).map(([k, v]) => [k, Array.isArray(v) ? `array(${v.length})` : (v === null ? 'null' : typeof v)]))
155
+ : undefined;
156
+ (console.log || console.debug)('[SettingsPanel] onSave()', { canSave: this.canSave, preview: { keysCount: Array.isArray(keys) ? keys.length : undefined, keyTypes } });
157
+ }
158
+ catch { }
159
+ const result = instance?.onSave?.();
160
+ if (isObservable(result)) {
161
+ firstValueFrom(result).then((value) => {
162
+ try {
163
+ const keys = value && typeof value === 'object' ? Object.keys(value) : undefined;
164
+ const keyTypes = value && typeof value === 'object'
165
+ ? Object.fromEntries(Object.entries(value).map(([k, v]) => [k, Array.isArray(v) ? `array(${v.length})` : (v === null ? 'null' : typeof v)]))
166
+ : undefined;
167
+ (console.log || console.debug)('[SettingsPanel] onSave(result:observable)', { keysCount: Array.isArray(keys) ? keys.length : undefined, keyTypes });
168
+ }
169
+ catch { }
170
+ this.ref.save(value);
171
+ });
172
+ }
173
+ else if (result instanceof Promise) {
174
+ result.then((value) => {
175
+ try {
176
+ const keys = value && typeof value === 'object' ? Object.keys(value) : undefined;
177
+ const keyTypes = value && typeof value === 'object'
178
+ ? Object.fromEntries(Object.entries(value).map(([k, v]) => [k, Array.isArray(v) ? `array(${v.length})` : (v === null ? 'null' : typeof v)]))
179
+ : undefined;
180
+ (console.log || console.debug)('[SettingsPanel] onSave(result:promise)', { keysCount: Array.isArray(keys) ? keys.length : undefined, keyTypes });
181
+ }
182
+ catch { }
183
+ this.ref.save(value);
184
+ });
185
+ }
186
+ else if (result !== undefined) {
187
+ try {
188
+ const keys = result && typeof result === 'object' ? Object.keys(result) : undefined;
189
+ const keyTypes = result && typeof result === 'object'
190
+ ? Object.fromEntries(Object.entries(result).map(([k, v]) => [k, Array.isArray(v) ? `array(${v.length})` : (v === null ? 'null' : typeof v)]))
191
+ : undefined;
192
+ (console.log || console.debug)('[SettingsPanel] onSave(result:value)', { keysCount: Array.isArray(keys) ? keys.length : undefined, keyTypes });
193
+ }
194
+ catch { }
195
+ this.ref.save(result);
196
+ }
197
+ else {
198
+ const value = instance?.getSettingsValue?.();
199
+ try {
200
+ const keys = value && typeof value === 'object' ? Object.keys(value) : undefined;
201
+ const keyTypes = value && typeof value === 'object'
202
+ ? Object.fromEntries(Object.entries(value).map(([k, v]) => [k, Array.isArray(v) ? `array(${v.length})` : (v === null ? 'null' : typeof v)]))
203
+ : undefined;
204
+ (console.log || console.debug)('[SettingsPanel] onSave(getSettingsValue)', { keysCount: Array.isArray(keys) ? keys.length : undefined, keyTypes });
205
+ }
206
+ catch { }
207
+ this.ref.save(value);
208
+ }
209
+ }
210
+ toggleExpand() {
211
+ this.expanded = !this.expanded;
212
+ this.cdr.markForCheck();
213
+ }
214
+ onCancel() {
215
+ of(this.isDirty)
216
+ .pipe(switchMap((dirty) => {
217
+ if (!dirty) {
218
+ try {
219
+ (console.log || console.debug)('[SettingsPanel] onCancel(): not dirty → close');
220
+ }
221
+ catch { }
222
+ return of(true);
223
+ }
224
+ try {
225
+ (console.log || console.debug)('[SettingsPanel] onCancel(): dirty → confirm dialog');
226
+ }
227
+ catch { }
228
+ const dialogData = {
229
+ title: 'Descartar Alterações',
230
+ message: 'Você tem alterações não salvas. Tem certeza de que deseja descartá-las?',
231
+ confirmText: 'Descartar',
232
+ cancelText: 'Continuar Editando',
233
+ type: 'warning',
234
+ icon: 'warning',
235
+ };
236
+ return this.dialog
237
+ .open(ConfirmDialogComponent, { data: dialogData })
238
+ .afterClosed();
239
+ }), filter((confirmed) => confirmed))
240
+ .subscribe(() => {
241
+ try {
242
+ (console.log || console.debug)('[SettingsPanel] close(confirm)');
243
+ }
244
+ catch { }
245
+ this.ref.close('cancel');
246
+ });
247
+ }
248
+ handleKeydown(event) {
249
+ if (event.key === 'Escape') {
250
+ event.preventDefault();
251
+ this.onCancel();
252
+ }
253
+ else if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) {
254
+ event.preventDefault();
255
+ this.onSave();
256
+ }
257
+ else if ((event.key === 's' || event.key === 'S') && (event.ctrlKey || event.metaKey)) {
258
+ event.preventDefault();
259
+ this.onSave();
260
+ }
261
+ }
262
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: SettingsPanelComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1.MatDialog }], target: i0.ɵɵFactoryTarget.Component });
263
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.4", type: SettingsPanelComponent, isStandalone: true, selector: "praxis-settings-panel", host: { listeners: { "document:keydown": "handleKeydown($event)" } }, viewQueries: [{ propertyName: "contentHost", first: true, predicate: ["contentHost"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: "<div\n class=\"settings-panel\"\n [class.expanded]=\"expanded\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"titleId\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n>\n <header class=\"settings-panel-header\">\n <h2 class=\"settings-panel-title\" [id]=\"titleId\">\n <mat-icon *ngIf=\"titleIcon\" class=\"title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n <span>{{ title }}</span>\n </h2>\n <span class=\"spacer\"></span>\n <button\n mat-icon-button\n type=\"button\"\n [attr.aria-label]=\"expanded ? 'Reduzir painel' : 'Expandir painel'\"\n [attr.aria-expanded]=\"expanded\"\n [matTooltip]=\"expanded ? 'Reduzir painel' : 'Expandir painel'\"\n (click)=\"toggleExpand()\"\n >\n <mat-icon [praxisIcon]=\"expanded ? 'close_fullscreen' : 'open_in_full'\"></mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n aria-label=\"Fechar\"\n matTooltip=\"Fechar\"\n (click)=\"onCancel()\"\n >\n <mat-icon [praxisIcon]=\"'close'\"></mat-icon>\n </button>\n </header>\n <div class=\"settings-panel-body\">\n <ng-template #contentHost></ng-template>\n </div>\n <footer class=\"settings-panel-footer\">\n <button mat-button type=\"button\" (click)=\"onReset()\" [disabled]=\"isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'restart_alt'\"></mat-icon>\n <span>Redefinir</span>\n </button>\n <span class=\"spacer\"></span>\n <button\n mat-button\n type=\"button\"\n (click)=\"onCancel()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'close'\"></mat-icon>\n <span>Cancelar</span>\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"onApply()\"\n [disabled]=\"!canApply\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canApply\"\n aria-busy=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'done'\"></mat-icon>\n <span>Aplicar</span>\n </ng-container>\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n (click)=\"onSave()\"\n [disabled]=\"!canSave\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canSave\"\n aria-busy=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'save'\"></mat-icon>\n <span>Salvar &amp; Fechar</span>\n </ng-container>\n </button>\n </footer>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100%}.settings-panel{display:grid;grid-template-rows:auto 1fr auto;grid-template-areas:\"header\" \"body\" \"footer\";height:100%;background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface);border-left:1px solid var(--md-sys-color-outline-variant);width:720px;transition:width .3s ease;overflow:hidden}.settings-panel.expanded{width:min(95vw,1200px)}.settings-panel-header{grid-area:header;display:flex;align-items:center;gap:8px;padding:0 16px;height:64px;border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:0 2px 6px var(--sicoob-shadow-low, rgba(0, 0, 0, .08));flex-shrink:0}.settings-panel-header .spacer{flex:1}.settings-panel-body{grid-area:body;overflow-y:auto;min-height:0;padding:8px 8px 24px;background:var(--md-sys-color-surface)}.settings-panel-content{display:block}.settings-panel-footer{grid-area:footer;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:0 -2px 6px var(--sicoob-shadow-low, rgba(0, 0, 0, .08));display:flex;align-items:center;padding:12px 16px;column-gap:12px;flex-shrink:0}.spacer{flex:1}.settings-panel-title{display:inline-flex;align-items:center;gap:8px;font-weight:700;letter-spacing:.2px;margin:0}.settings-panel-title mat-icon{color:var(--md-sys-color-primary)}.settings-panel-title .title-icon{width:20px;height:20px;font-size:20px}.settings-panel-footer button+button{margin-left:12px}.settings-panel-footer button{display:inline-flex;align-items:center}.settings-panel-footer button .mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.settings-panel-footer .mat-button-wrapper{display:inline-flex;align-items:center;gap:8px}.settings-panel-footer .mat-progress-spinner{margin-right:8px}.settings-panel-footer .mat-flat-button[color=primary]{font-weight:600}:host ::ng-deep .praxis-settings-panel-backdrop{background:var(--pfx-backdrop, rgba(0, 0, 0, .45));backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%)}.settings-panel .mat-divider{background-color:var(--md-sys-color-outline-variant)!important}.settings-panel .mat-expansion-panel{background:var(--md-sys-color-surface-container);border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;box-shadow:0 2px 8px var(--sicoob-shadow-low, rgba(0, 0, 0, .08))}.settings-panel .mat-expansion-panel-header{background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface)}.settings-panel .mat-expansion-panel-header .mat-expansion-indicator,.settings-panel .mat-expansion-panel-header .mat-expansion-indicator:after{color:var(--md-sys-color-on-surface-variant);border-color:var(--md-sys-color-on-surface-variant)}.settings-panel .mat-mdc-form-field{--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-secondary, var(--md-sys-color-primary));--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary)}:host ::ng-deep .praxis-settings-panel-pane{position:fixed!important;top:0!important;right:0!important;height:100vh!important;z-index:1000}:host ::ng-deep .praxis-settings-panel-pane .settings-panel{pointer-events:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$1.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: i3$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i6.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatDialogModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
264
+ }
265
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: SettingsPanelComponent, decorators: [{
266
+ type: Component,
267
+ args: [{ selector: 'praxis-settings-panel', standalone: true, imports: [
268
+ CommonModule,
269
+ MatButtonModule,
270
+ MatIconModule,
271
+ PraxisIconDirective,
272
+ MatTooltipModule,
273
+ CdkTrapFocus,
274
+ MatProgressSpinnerModule,
275
+ MatDialogModule,
276
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"settings-panel\"\n [class.expanded]=\"expanded\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"titleId\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n>\n <header class=\"settings-panel-header\">\n <h2 class=\"settings-panel-title\" [id]=\"titleId\">\n <mat-icon *ngIf=\"titleIcon\" class=\"title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n <span>{{ title }}</span>\n </h2>\n <span class=\"spacer\"></span>\n <button\n mat-icon-button\n type=\"button\"\n [attr.aria-label]=\"expanded ? 'Reduzir painel' : 'Expandir painel'\"\n [attr.aria-expanded]=\"expanded\"\n [matTooltip]=\"expanded ? 'Reduzir painel' : 'Expandir painel'\"\n (click)=\"toggleExpand()\"\n >\n <mat-icon [praxisIcon]=\"expanded ? 'close_fullscreen' : 'open_in_full'\"></mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n aria-label=\"Fechar\"\n matTooltip=\"Fechar\"\n (click)=\"onCancel()\"\n >\n <mat-icon [praxisIcon]=\"'close'\"></mat-icon>\n </button>\n </header>\n <div class=\"settings-panel-body\">\n <ng-template #contentHost></ng-template>\n </div>\n <footer class=\"settings-panel-footer\">\n <button mat-button type=\"button\" (click)=\"onReset()\" [disabled]=\"isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'restart_alt'\"></mat-icon>\n <span>Redefinir</span>\n </button>\n <span class=\"spacer\"></span>\n <button\n mat-button\n type=\"button\"\n (click)=\"onCancel()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'close'\"></mat-icon>\n <span>Cancelar</span>\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n (click)=\"onApply()\"\n [disabled]=\"!canApply\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canApply\"\n aria-busy=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'done'\"></mat-icon>\n <span>Aplicar</span>\n </ng-container>\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n (click)=\"onSave()\"\n [disabled]=\"!canSave\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canSave\"\n aria-busy=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'save'\"></mat-icon>\n <span>Salvar &amp; Fechar</span>\n </ng-container>\n </button>\n </footer>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100%}.settings-panel{display:grid;grid-template-rows:auto 1fr auto;grid-template-areas:\"header\" \"body\" \"footer\";height:100%;background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface);border-left:1px solid var(--md-sys-color-outline-variant);width:720px;transition:width .3s ease;overflow:hidden}.settings-panel.expanded{width:min(95vw,1200px)}.settings-panel-header{grid-area:header;display:flex;align-items:center;gap:8px;padding:0 16px;height:64px;border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:0 2px 6px var(--sicoob-shadow-low, rgba(0, 0, 0, .08));flex-shrink:0}.settings-panel-header .spacer{flex:1}.settings-panel-body{grid-area:body;overflow-y:auto;min-height:0;padding:8px 8px 24px;background:var(--md-sys-color-surface)}.settings-panel-content{display:block}.settings-panel-footer{grid-area:footer;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:0 -2px 6px var(--sicoob-shadow-low, rgba(0, 0, 0, .08));display:flex;align-items:center;padding:12px 16px;column-gap:12px;flex-shrink:0}.spacer{flex:1}.settings-panel-title{display:inline-flex;align-items:center;gap:8px;font-weight:700;letter-spacing:.2px;margin:0}.settings-panel-title mat-icon{color:var(--md-sys-color-primary)}.settings-panel-title .title-icon{width:20px;height:20px;font-size:20px}.settings-panel-footer button+button{margin-left:12px}.settings-panel-footer button{display:inline-flex;align-items:center}.settings-panel-footer button .mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.settings-panel-footer .mat-button-wrapper{display:inline-flex;align-items:center;gap:8px}.settings-panel-footer .mat-progress-spinner{margin-right:8px}.settings-panel-footer .mat-flat-button[color=primary]{font-weight:600}:host ::ng-deep .praxis-settings-panel-backdrop{background:var(--pfx-backdrop, rgba(0, 0, 0, .45));backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%)}.settings-panel .mat-divider{background-color:var(--md-sys-color-outline-variant)!important}.settings-panel .mat-expansion-panel{background:var(--md-sys-color-surface-container);border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;box-shadow:0 2px 8px var(--sicoob-shadow-low, rgba(0, 0, 0, .08))}.settings-panel .mat-expansion-panel-header{background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface)}.settings-panel .mat-expansion-panel-header .mat-expansion-indicator,.settings-panel .mat-expansion-panel-header .mat-expansion-indicator:after{color:var(--md-sys-color-on-surface-variant);border-color:var(--md-sys-color-on-surface-variant)}.settings-panel .mat-mdc-form-field{--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-secondary, var(--md-sys-color-primary));--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary)}:host ::ng-deep .praxis-settings-panel-pane{position:fixed!important;top:0!important;right:0!important;height:100vh!important;z-index:1000}:host ::ng-deep .praxis-settings-panel-pane .settings-panel{pointer-events:auto}\n"] }]
277
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1.MatDialog }], propDecorators: { contentHost: [{
278
+ type: ViewChild,
279
+ args: ['contentHost', { read: ViewContainerRef, static: true }]
280
+ }], handleKeydown: [{
281
+ type: HostListener,
282
+ args: ['document:keydown', ['$event']]
283
+ }] } });
284
+
285
+ class SettingsPanelRef {
286
+ overlayRef;
287
+ appliedSubject = new Subject();
288
+ savedSubject = new Subject();
289
+ resetSubject = new Subject();
290
+ closedSubject = new Subject();
291
+ applied$ = this.appliedSubject.asObservable();
292
+ saved$ = this.savedSubject.asObservable();
293
+ reset$ = this.resetSubject.asObservable();
294
+ closed$ = this.closedSubject.asObservable();
295
+ constructor(overlayRef) {
296
+ this.overlayRef = overlayRef;
297
+ }
298
+ apply(value) {
299
+ this.appliedSubject.next(value);
300
+ }
301
+ /**
302
+ * Emits the provided value on {@link saved$} and closes the panel.
303
+ *
304
+ * This should be called with the configuration object returned by the
305
+ * editor's `onSave()` method so that consumers can persist the new settings.
306
+ */
307
+ save(value) {
308
+ try {
309
+ const type = value == null ? 'nullish' : Array.isArray(value) ? 'array' : typeof value;
310
+ const keys = value && typeof value === 'object' ? Object.keys(value) : undefined;
311
+ const keyTypes = value && typeof value === 'object'
312
+ ? Object.fromEntries(Object.entries(value).map(([k, v]) => [k, Array.isArray(v) ? `array(${v.length})` : (v === null ? 'null' : typeof v)]))
313
+ : undefined;
314
+ (console.log || console.debug)('[SettingsPanelRef] save()', { type, keysCount: Array.isArray(keys) ? keys.length : undefined, keyTypes });
315
+ }
316
+ catch { }
317
+ this.savedSubject.next(value);
318
+ this.close('save');
319
+ }
320
+ reset() {
321
+ this.resetSubject.next();
322
+ }
323
+ updateSize(width) {
324
+ this.overlayRef.updateSize({ width });
325
+ }
326
+ close(reason = 'cancel') {
327
+ if (!this.overlayRef?.hasAttached()) {
328
+ return;
329
+ }
330
+ this.overlayRef.dispose();
331
+ this.closedSubject.next(reason);
332
+ this.complete();
333
+ }
334
+ complete() {
335
+ this.appliedSubject.complete();
336
+ this.savedSubject.complete();
337
+ this.resetSubject.complete();
338
+ this.closedSubject.complete();
339
+ }
340
+ }
341
+
342
+ const SETTINGS_PANEL_DATA = new InjectionToken('SETTINGS_PANEL_DATA');
343
+ const SETTINGS_PANEL_REF = new InjectionToken('SETTINGS_PANEL_REF');
344
+
345
+ class SettingsPanelService {
346
+ overlay;
347
+ injector;
348
+ currentRef;
349
+ constructor(overlay, injector) {
350
+ this.overlay = overlay;
351
+ this.injector = injector;
352
+ }
353
+ /**
354
+ * Opens a new settings panel. If another panel is already open it will be
355
+ * closed before the new one is created. Future improvements may reuse the
356
+ * same overlay when the provided id matches.
357
+ */
358
+ open(config) {
359
+ if (this.currentRef) {
360
+ this.currentRef.close('cancel');
361
+ }
362
+ const overlayConfig = {
363
+ hasBackdrop: true,
364
+ backdropClass: 'praxis-settings-panel-backdrop',
365
+ panelClass: 'praxis-settings-panel-pane',
366
+ positionStrategy: this.overlay.position().global().top('0').right('0'),
367
+ height: '100vh',
368
+ scrollStrategy: this.overlay.scrollStrategies.block(),
369
+ };
370
+ const overlayRef = this.overlay.create(overlayConfig);
371
+ const ref = new SettingsPanelRef(overlayRef);
372
+ const panelPortal = new ComponentPortal(SettingsPanelComponent);
373
+ const panelRef = overlayRef.attach(panelPortal);
374
+ panelRef.instance.title = config.title;
375
+ panelRef.instance.titleIcon = config.titleIcon;
376
+ const inputs = config.content.inputs;
377
+ try {
378
+ const dbg = inputs && inputs.page ? { hasPage: true, conns: Array.isArray(inputs.page.connections) ? inputs.page.connections.length : undefined } : { hasPage: false };
379
+ (console.log || console.debug)('[SettingsPanel] open()', { id: config.id, title: config.title, inputs: dbg });
380
+ }
381
+ catch { }
382
+ const injector = Injector.create({
383
+ providers: [
384
+ { provide: SETTINGS_PANEL_DATA, useValue: inputs },
385
+ { provide: SETTINGS_PANEL_REF, useValue: ref },
386
+ ],
387
+ parent: this.injector,
388
+ });
389
+ panelRef.instance.attachContent(config.content.component, injector, ref);
390
+ overlayRef.backdropClick().subscribe(() => ref.close('backdrop'));
391
+ overlayRef.keydownEvents().subscribe((event) => {
392
+ if (event.key === 'Escape') {
393
+ ref.close('esc');
394
+ }
395
+ });
396
+ ref.closed$.subscribe(() => {
397
+ if (this.currentRef === ref) {
398
+ this.currentRef = undefined;
399
+ }
400
+ });
401
+ this.currentRef = ref;
402
+ return ref;
403
+ }
404
+ close(reason = 'cancel') {
405
+ this.currentRef?.close(reason);
406
+ this.currentRef = undefined;
407
+ }
408
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: SettingsPanelService, deps: [{ token: i1$1.Overlay }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
409
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: SettingsPanelService, providedIn: 'root' });
410
+ }
411
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: SettingsPanelService, decorators: [{
412
+ type: Injectable,
413
+ args: [{ providedIn: 'root' }]
414
+ }], ctorParameters: () => [{ type: i1$1.Overlay }, { type: i0.Injector }] });
415
+
416
+ /**
417
+ * Admin helper to load, patch and persist GlobalConfig.
418
+ * Intended to be used by the Global Config Editor UI.
419
+ */
420
+ class GlobalConfigAdminService {
421
+ global = inject(GlobalConfigService);
422
+ /** Returns the effective merged configuration (providers + storage). */
423
+ getEffectiveConfig() {
424
+ // buildConfig is internal in service; use public getters instead
425
+ return {
426
+ crud: this.global.getCrud(),
427
+ dynamicFields: this.global.getDynamicFields(),
428
+ table: this.global.getTable(),
429
+ dialog: this.global.getDialog(),
430
+ };
431
+ }
432
+ /** Persist a partial update and notify listeners. */
433
+ save(partial) {
434
+ this.global.saveGlobalConfig(partial);
435
+ }
436
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: GlobalConfigAdminService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
437
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: GlobalConfigAdminService, providedIn: 'root' });
438
+ }
439
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: GlobalConfigAdminService, decorators: [{
440
+ type: Injectable,
441
+ args: [{ providedIn: 'root' }]
442
+ }] });
443
+
444
+ function safeName(path) {
445
+ return path.replace(/[^a-zA-Z0-9_]/g, '_');
446
+ }
447
+ /**
448
+ * Minimal FormConfig schema for the Global Config editor UI.
449
+ * This can be extended incrementally as new domains/settings are added.
450
+ */
451
+ function buildGlobalConfigFormConfig() {
452
+ const fields = [
453
+ // CRUD
454
+ {
455
+ name: safeName('crud.defaults.openMode'),
456
+ label: 'Modo de abertura padrão',
457
+ controlType: FieldControlType.SELECT,
458
+ selectOptions: [
459
+ { text: 'Rota', value: 'route' },
460
+ { text: 'Modal', value: 'modal' },
461
+ { text: 'Drawer', value: 'drawer' },
462
+ ],
463
+ hint: 'Aplicado quando ação/metadado não definem modo.',
464
+ dataAttributes: { globalPath: 'crud.defaults.openMode' },
465
+ },
466
+ {
467
+ name: safeName('crud.defaults.back.confirmOnDirty'),
468
+ label: 'Confirmar ao sair com alterações',
469
+ controlType: FieldControlType.TOGGLE,
470
+ dataAttributes: { globalPath: 'crud.defaults.back.confirmOnDirty' },
471
+ },
472
+ {
473
+ name: safeName('crud.defaults.back.strategy'),
474
+ label: 'Back: estratégia',
475
+ controlType: FieldControlType.SELECT,
476
+ selectOptions: [
477
+ { text: 'auto', value: 'auto' },
478
+ { text: 'close', value: 'close' },
479
+ { text: 'navigate', value: 'navigate' },
480
+ ],
481
+ dataAttributes: { globalPath: 'crud.defaults.back.strategy' },
482
+ },
483
+ {
484
+ name: safeName('crud.defaults.header.showBack'),
485
+ label: 'Header: mostrar voltar',
486
+ controlType: FieldControlType.TOGGLE,
487
+ dataAttributes: { globalPath: 'crud.defaults.header.showBack' },
488
+ },
489
+ {
490
+ name: safeName('crud.defaults.header.variant'),
491
+ label: 'Header: variante',
492
+ controlType: FieldControlType.SELECT,
493
+ selectOptions: [
494
+ { text: 'ghost', value: 'ghost' },
495
+ { text: 'tonal', value: 'tonal' },
496
+ { text: 'outlined', value: 'outlined' },
497
+ ],
498
+ dataAttributes: { globalPath: 'crud.defaults.header.variant' },
499
+ },
500
+ {
501
+ name: safeName('crud.defaults.header.sticky'),
502
+ label: 'Header: fixo (sticky)',
503
+ controlType: FieldControlType.TOGGLE,
504
+ dataAttributes: { globalPath: 'crud.defaults.header.sticky' },
505
+ },
506
+ {
507
+ name: safeName('crud.defaults.header.divider'),
508
+ label: 'Header: mostrar divisor',
509
+ controlType: FieldControlType.TOGGLE,
510
+ dataAttributes: { globalPath: 'crud.defaults.header.divider' },
511
+ },
512
+ {
513
+ name: safeName('crud.defaults.modal.width'),
514
+ label: 'Modal: largura (ex.: 900px)',
515
+ controlType: FieldControlType.INPUT,
516
+ dataAttributes: { globalPath: 'crud.defaults.modal.width' },
517
+ },
518
+ {
519
+ name: safeName('crud.defaults.modal.maxWidth'),
520
+ label: 'Modal: largura máxima (ex.: 96vw)',
521
+ controlType: FieldControlType.INPUT,
522
+ dataAttributes: { globalPath: 'crud.defaults.modal.maxWidth' },
523
+ },
524
+ {
525
+ name: safeName('crud.defaults.modal.backdropStyle'),
526
+ label: 'Modal: backdrop',
527
+ controlType: FieldControlType.SELECT,
528
+ selectOptions: [
529
+ { text: 'blur', value: 'blur' },
530
+ { text: 'dim', value: 'dim' },
531
+ { text: 'transparent', value: 'transparent' },
532
+ ],
533
+ dataAttributes: { globalPath: 'crud.defaults.modal.backdropStyle' },
534
+ },
535
+ // Dynamic Fields
536
+ {
537
+ name: safeName('dynamicFields.asyncSelect.loadOn'),
538
+ label: 'Async Select: loadOn',
539
+ controlType: FieldControlType.SELECT,
540
+ selectOptions: [
541
+ { text: 'open', value: 'open' },
542
+ { text: 'init', value: 'init' },
543
+ { text: 'none', value: 'none' },
544
+ ],
545
+ hint: 'Estratégia de primeira carga quando metadado não definir.',
546
+ dataAttributes: { globalPath: 'dynamicFields.asyncSelect.loadOn' },
547
+ },
548
+ {
549
+ name: safeName('dynamicFields.cascade.enable'),
550
+ label: 'Cascata nativa habilitada',
551
+ controlType: FieldControlType.TOGGLE,
552
+ dataAttributes: { globalPath: 'dynamicFields.cascade.enable' },
553
+ },
554
+ {
555
+ name: safeName('dynamicFields.cascade.loadOnChange'),
556
+ label: 'Cascata: loadOnChange',
557
+ controlType: FieldControlType.SELECT,
558
+ selectOptions: [
559
+ { text: 'respectLoadOn', value: 'respectLoadOn' },
560
+ { text: 'immediate', value: 'immediate' },
561
+ { text: 'manual', value: 'manual' },
562
+ ],
563
+ dataAttributes: { globalPath: 'dynamicFields.cascade.loadOnChange' },
564
+ },
565
+ {
566
+ name: safeName('dynamicFields.cascade.debounceMs'),
567
+ label: 'Cascata: debounce (ms)',
568
+ controlType: FieldControlType.NUMERIC_TEXT_BOX,
569
+ dataAttributes: { globalPath: 'dynamicFields.cascade.debounceMs' },
570
+ },
571
+ // Dynamic Fields async-select extra
572
+ {
573
+ name: safeName('dynamicFields.asyncSelect.pageSize'),
574
+ label: 'Async Select: pageSize',
575
+ controlType: FieldControlType.NUMERIC_TEXT_BOX,
576
+ dataAttributes: { globalPath: 'dynamicFields.asyncSelect.pageSize' },
577
+ },
578
+ {
579
+ name: safeName('dynamicFields.asyncSelect.useCursor'),
580
+ label: 'Async Select: usar cursor',
581
+ controlType: FieldControlType.TOGGLE,
582
+ dataAttributes: { globalPath: 'dynamicFields.asyncSelect.useCursor' },
583
+ },
584
+ // Table
585
+ {
586
+ name: safeName('table.behavior.pagination.enabled'),
587
+ label: 'Paginação habilitada',
588
+ controlType: FieldControlType.TOGGLE,
589
+ dataAttributes: { globalPath: 'table.behavior.pagination.enabled' },
590
+ },
591
+ {
592
+ name: safeName('table.behavior.pagination.pageSize'),
593
+ label: 'Tamanho da página',
594
+ controlType: FieldControlType.NUMERIC_TEXT_BOX,
595
+ dataAttributes: { globalPath: 'table.behavior.pagination.pageSize' },
596
+ },
597
+ {
598
+ name: safeName('table.behavior.filtering.debounceTime'),
599
+ label: 'Debounce do filtro (ms)',
600
+ controlType: FieldControlType.NUMERIC_TEXT_BOX,
601
+ dataAttributes: { globalPath: 'table.behavior.filtering.debounceTime' },
602
+ },
603
+ {
604
+ name: safeName('table.behavior.sorting.enabled'),
605
+ label: 'Ordenação habilitada',
606
+ controlType: FieldControlType.TOGGLE,
607
+ dataAttributes: { globalPath: 'table.behavior.sorting.enabled' },
608
+ },
609
+ {
610
+ name: safeName('table.behavior.selection.enabled'),
611
+ label: 'Seleção habilitada',
612
+ controlType: FieldControlType.TOGGLE,
613
+ dataAttributes: { globalPath: 'table.behavior.selection.enabled' },
614
+ },
615
+ {
616
+ name: safeName('table.behavior.pagination.strategy'),
617
+ label: 'Paginação: estratégia',
618
+ controlType: FieldControlType.SELECT,
619
+ selectOptions: [
620
+ { text: 'client', value: 'client' },
621
+ { text: 'server', value: 'server' },
622
+ ],
623
+ dataAttributes: { globalPath: 'table.behavior.pagination.strategy' },
624
+ },
625
+ {
626
+ name: safeName('table.behavior.sorting.strategy'),
627
+ label: 'Ordenação: estratégia',
628
+ controlType: FieldControlType.SELECT,
629
+ selectOptions: [
630
+ { text: 'client', value: 'client' },
631
+ { text: 'server', value: 'server' },
632
+ ],
633
+ dataAttributes: { globalPath: 'table.behavior.sorting.strategy' },
634
+ },
635
+ {
636
+ name: safeName('table.behavior.pagination.showFirstLastButtons'),
637
+ label: 'Paginação: botões Primeiro/Último',
638
+ controlType: FieldControlType.CHECKBOX,
639
+ dataAttributes: { globalPath: 'table.behavior.pagination.showFirstLastButtons' },
640
+ },
641
+ {
642
+ name: safeName('table.toolbar.visible'),
643
+ label: 'Toolbar visível',
644
+ controlType: FieldControlType.CHECKBOX,
645
+ dataAttributes: { globalPath: 'table.toolbar.visible' },
646
+ },
647
+ {
648
+ name: safeName('table.appearance.density'),
649
+ label: 'Aparência: densidade',
650
+ controlType: FieldControlType.SELECT,
651
+ selectOptions: [
652
+ { text: 'comfortable', value: 'comfortable' },
653
+ { text: 'cozy', value: 'cozy' },
654
+ { text: 'compact', value: 'compact' },
655
+ ],
656
+ dataAttributes: { globalPath: 'table.appearance.density' },
657
+ },
658
+ {
659
+ name: safeName('table.filteringUi.advancedOpenMode'),
660
+ label: 'Filtro: modo de abertura',
661
+ controlType: FieldControlType.SELECT,
662
+ selectOptions: [
663
+ { text: 'overlay', value: 'overlay' },
664
+ { text: 'modal', value: 'modal' },
665
+ { text: 'drawer', value: 'drawer' },
666
+ ],
667
+ dataAttributes: { globalPath: 'table.filteringUi.advancedOpenMode' },
668
+ },
669
+ {
670
+ name: safeName('table.filteringUi.overlayVariant'),
671
+ label: 'Filtro: overlay (visual)',
672
+ controlType: FieldControlType.SELECT,
673
+ selectOptions: [
674
+ { text: 'card', value: 'card' },
675
+ { text: 'frosted', value: 'frosted' },
676
+ ],
677
+ dataAttributes: { globalPath: 'table.filteringUi.overlayVariant' },
678
+ },
679
+ {
680
+ name: safeName('table.filteringUi.overlayBackdrop'),
681
+ label: 'Filtro: backdrop no overlay',
682
+ controlType: FieldControlType.CHECKBOX,
683
+ dataAttributes: { globalPath: 'table.filteringUi.overlayBackdrop' },
684
+ },
685
+ // Table messages - confirmations
686
+ { name: safeName('table.messages.actions.confirmations.delete'), label: 'Excluir — confirmação', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.confirmations.delete' } },
687
+ { name: safeName('table.messages.actions.confirmations.deleteMultiple'), label: 'Excluir (múltiplos) — confirmação', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.confirmations.deleteMultiple' } },
688
+ { name: safeName('table.messages.actions.confirmations.save'), label: 'Salvar — confirmação', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.confirmations.save' } },
689
+ { name: safeName('table.messages.actions.confirmations.cancel'), label: 'Cancelar — confirmação', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.confirmations.cancel' } },
690
+ { name: safeName('table.messages.actions.confirmations.export'), label: 'Exportar — confirmação', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.confirmations.export' } },
691
+ // Table messages - progress
692
+ { name: safeName('table.messages.actions.progress.delete'), label: 'Excluir — progresso', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.progress.delete' } },
693
+ { name: safeName('table.messages.actions.progress.deleteMultiple'), label: 'Excluir (múltiplos) — progresso', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.progress.deleteMultiple' } },
694
+ // Table messages - success
695
+ { name: safeName('table.messages.actions.success.delete'), label: 'Excluir — sucesso', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.success.delete' } },
696
+ { name: safeName('table.messages.actions.success.save'), label: 'Salvar — sucesso', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.success.save' } },
697
+ { name: safeName('table.messages.actions.success.export'), label: 'Exportar — sucesso', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.success.export' } },
698
+ { name: safeName('table.messages.actions.success.import'), label: 'Importar — sucesso', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.success.import' } },
699
+ // Table messages - errors
700
+ { name: safeName('table.messages.actions.errors.delete'), label: 'Excluir — erro', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.errors.delete' } },
701
+ { name: safeName('table.messages.actions.errors.save'), label: 'Salvar — erro', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.errors.save' } },
702
+ { name: safeName('table.messages.actions.errors.export'), label: 'Exportar — erro', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.errors.export' } },
703
+ { name: safeName('table.messages.actions.errors.network'), label: 'Rede — erro', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.errors.network' } },
704
+ { name: safeName('table.messages.actions.errors.permission'), label: 'Permissão — erro', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.errors.permission' } },
705
+ // Dialog — Defaults by type
706
+ {
707
+ name: safeName('dialog.defaults.confirm.ariaRole'),
708
+ label: 'Dialog Defaults: confirm — ariaRole',
709
+ controlType: FieldControlType.SELECT,
710
+ selectOptions: [
711
+ { text: 'dialog', value: 'dialog' },
712
+ { text: 'alertdialog', value: 'alertdialog' },
713
+ ],
714
+ dataAttributes: { globalPath: 'dialog.defaults.confirm.ariaRole' },
715
+ },
716
+ {
717
+ name: safeName('dialog.defaults.confirm.closeOnBackdropClick'),
718
+ label: 'Dialog Defaults: confirm — fechar ao clicar no backdrop',
719
+ controlType: FieldControlType.TOGGLE,
720
+ dataAttributes: { globalPath: 'dialog.defaults.confirm.closeOnBackdropClick' },
721
+ },
722
+ {
723
+ name: safeName('dialog.defaults.alert.ariaRole'),
724
+ label: 'Dialog Defaults: alert — ariaRole',
725
+ controlType: FieldControlType.SELECT,
726
+ selectOptions: [
727
+ { text: 'dialog', value: 'dialog' },
728
+ { text: 'alertdialog', value: 'alertdialog' },
729
+ ],
730
+ dataAttributes: { globalPath: 'dialog.defaults.alert.ariaRole' },
731
+ },
732
+ {
733
+ name: safeName('dialog.defaults.alert.closeOnBackdropClick'),
734
+ label: 'Dialog Defaults: alert — fechar ao clicar no backdrop',
735
+ controlType: FieldControlType.TOGGLE,
736
+ dataAttributes: { globalPath: 'dialog.defaults.alert.closeOnBackdropClick' },
737
+ },
738
+ {
739
+ name: safeName('dialog.defaults.prompt.ariaRole'),
740
+ label: 'Dialog Defaults: prompt — ariaRole',
741
+ controlType: FieldControlType.SELECT,
742
+ selectOptions: [
743
+ { text: 'dialog', value: 'dialog' },
744
+ { text: 'alertdialog', value: 'alertdialog' },
745
+ ],
746
+ dataAttributes: { globalPath: 'dialog.defaults.prompt.ariaRole' },
747
+ },
748
+ {
749
+ name: safeName('dialog.defaults.prompt.closeOnBackdropClick'),
750
+ label: 'Dialog Defaults: prompt — fechar ao clicar no backdrop',
751
+ controlType: FieldControlType.TOGGLE,
752
+ dataAttributes: { globalPath: 'dialog.defaults.prompt.closeOnBackdropClick' },
753
+ },
754
+ // Dialog — Variants per profile (danger, info, success, question, error)
755
+ // Each variant exposes commonly customized fields + JSON editors for actions/styles
756
+ // danger
757
+ { name: safeName('dialog.variants.danger.title'), label: 'Variant danger — título', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.danger.title' } },
758
+ { name: safeName('dialog.variants.danger.icon'), label: 'Variant danger — ícone', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.danger.icon' } },
759
+ { name: safeName('dialog.variants.danger.message'), label: 'Variant danger — mensagem', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.danger.message' } },
760
+ { name: safeName('dialog.variants.danger.ariaRole'), label: 'Variant danger — ariaRole', controlType: FieldControlType.SELECT, selectOptions: [{ text: 'dialog', value: 'dialog' }, { text: 'alertdialog', value: 'alertdialog' }], dataAttributes: { globalPath: 'dialog.variants.danger.ariaRole' } },
761
+ { name: safeName('dialog.variants.danger.closeOnBackdropClick'), label: 'Variant danger — fechar ao clicar no backdrop', controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.variants.danger.closeOnBackdropClick' } },
762
+ { name: safeName('dialog.variants.danger.panelClass'), label: 'Variant danger — panelClass', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.danger.panelClass' } },
763
+ { name: safeName('dialog.variants.danger.actions'), label: 'Variant danger — actions (JSON array)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.danger.actions', monospace: true }, hint: 'Ex.: [{"id":"cancel","text":"Cancelar","role":"secondary","close":true,"cssClass":"btn"},{"id":"confirm","text":"Excluir","role":"primary","close":true,"cssClass":"btn btn-danger","icon":"delete"}]' },
764
+ { name: safeName('dialog.variants.danger.styles'), label: 'Variant danger — styles (JSON)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.danger.styles', monospace: true } },
765
+ { name: safeName('dialog.variants.danger.animation'), label: 'Variant danger — animation (JSON/boolean)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.danger.animation', monospace: true } },
766
+ // info
767
+ { name: safeName('dialog.variants.info.title'), label: 'Variant info — título', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.info.title' } },
768
+ { name: safeName('dialog.variants.info.icon'), label: 'Variant info — ícone', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.info.icon' } },
769
+ { name: safeName('dialog.variants.info.message'), label: 'Variant info — mensagem', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.info.message' } },
770
+ { name: safeName('dialog.variants.info.ariaRole'), label: 'Variant info — ariaRole', controlType: FieldControlType.SELECT, selectOptions: [{ text: 'dialog', value: 'dialog' }, { text: 'alertdialog', value: 'alertdialog' }], dataAttributes: { globalPath: 'dialog.variants.info.ariaRole' } },
771
+ { name: safeName('dialog.variants.info.closeOnBackdropClick'), label: 'Variant info — fechar ao clicar no backdrop', controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.variants.info.closeOnBackdropClick' } },
772
+ { name: safeName('dialog.variants.info.panelClass'), label: 'Variant info — panelClass', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.info.panelClass' } },
773
+ { name: safeName('dialog.variants.info.actions'), label: 'Variant info — actions (JSON array)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.info.actions', monospace: true } },
774
+ { name: safeName('dialog.variants.info.styles'), label: 'Variant info — styles (JSON)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.info.styles', monospace: true } },
775
+ { name: safeName('dialog.variants.info.animation'), label: 'Variant info — animation (JSON/boolean)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.info.animation', monospace: true } },
776
+ // success
777
+ { name: safeName('dialog.variants.success.title'), label: 'Variant success — título', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.success.title' } },
778
+ { name: safeName('dialog.variants.success.icon'), label: 'Variant success — ícone', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.success.icon' } },
779
+ { name: safeName('dialog.variants.success.message'), label: 'Variant success — mensagem', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.success.message' } },
780
+ { name: safeName('dialog.variants.success.ariaRole'), label: 'Variant success — ariaRole', controlType: FieldControlType.SELECT, selectOptions: [{ text: 'dialog', value: 'dialog' }, { text: 'alertdialog', value: 'alertdialog' }], dataAttributes: { globalPath: 'dialog.variants.success.ariaRole' } },
781
+ { name: safeName('dialog.variants.success.closeOnBackdropClick'), label: 'Variant success — fechar ao clicar no backdrop', controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.variants.success.closeOnBackdropClick' } },
782
+ { name: safeName('dialog.variants.success.panelClass'), label: 'Variant success — panelClass', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.success.panelClass' } },
783
+ { name: safeName('dialog.variants.success.actions'), label: 'Variant success — actions (JSON array)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.success.actions', monospace: true } },
784
+ { name: safeName('dialog.variants.success.styles'), label: 'Variant success — styles (JSON)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.success.styles', monospace: true } },
785
+ { name: safeName('dialog.variants.success.animation'), label: 'Variant success — animation (JSON/boolean)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.success.animation', monospace: true } },
786
+ // question
787
+ { name: safeName('dialog.variants.question.title'), label: 'Variant question — título', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.question.title' } },
788
+ { name: safeName('dialog.variants.question.icon'), label: 'Variant question — ícone', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.question.icon' } },
789
+ { name: safeName('dialog.variants.question.message'), label: 'Variant question — mensagem', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.question.message' } },
790
+ { name: safeName('dialog.variants.question.ariaRole'), label: 'Variant question — ariaRole', controlType: FieldControlType.SELECT, selectOptions: [{ text: 'dialog', value: 'dialog' }, { text: 'alertdialog', value: 'alertdialog' }], dataAttributes: { globalPath: 'dialog.variants.question.ariaRole' } },
791
+ { name: safeName('dialog.variants.question.closeOnBackdropClick'), label: 'Variant question — fechar ao clicar no backdrop', controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.variants.question.closeOnBackdropClick' } },
792
+ { name: safeName('dialog.variants.question.panelClass'), label: 'Variant question — panelClass', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.question.panelClass' } },
793
+ { name: safeName('dialog.variants.question.actions'), label: 'Variant question — actions (JSON array)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.question.actions', monospace: true } },
794
+ { name: safeName('dialog.variants.question.styles'), label: 'Variant question — styles (JSON)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.question.styles', monospace: true } },
795
+ { name: safeName('dialog.variants.question.animation'), label: 'Variant question — animation (JSON/boolean)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.question.animation', monospace: true } },
796
+ // error
797
+ { name: safeName('dialog.variants.error.title'), label: 'Variant error — título', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.error.title' } },
798
+ { name: safeName('dialog.variants.error.icon'), label: 'Variant error — ícone', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.error.icon' } },
799
+ { name: safeName('dialog.variants.error.message'), label: 'Variant error — mensagem', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.error.message' } },
800
+ { name: safeName('dialog.variants.error.ariaRole'), label: 'Variant error — ariaRole', controlType: FieldControlType.SELECT, selectOptions: [{ text: 'dialog', value: 'dialog' }, { text: 'alertdialog', value: 'alertdialog' }], dataAttributes: { globalPath: 'dialog.variants.error.ariaRole' } },
801
+ { name: safeName('dialog.variants.error.closeOnBackdropClick'), label: 'Variant error — fechar ao clicar no backdrop', controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.variants.error.closeOnBackdropClick' } },
802
+ { name: safeName('dialog.variants.error.panelClass'), label: 'Variant error — panelClass', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.error.panelClass' } },
803
+ { name: safeName('dialog.variants.error.actions'), label: 'Variant error — actions (JSON array)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.error.actions', monospace: true } },
804
+ { name: safeName('dialog.variants.error.styles'), label: 'Variant error — styles (JSON)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.error.styles', monospace: true } },
805
+ { name: safeName('dialog.variants.error.animation'), label: 'Variant error — animation (JSON/boolean)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.error.animation', monospace: true } },
806
+ ];
807
+ const cfg = {
808
+ sections: [
809
+ {
810
+ id: 'crud',
811
+ title: 'CRUD',
812
+ rows: [
813
+ {
814
+ id: 'crud-row-1',
815
+ columns: [
816
+ { id: 'crud-col-1', fields: [
817
+ safeName('crud.defaults.openMode'),
818
+ safeName('crud.defaults.back.strategy'),
819
+ safeName('crud.defaults.back.confirmOnDirty'),
820
+ ] },
821
+ { id: 'crud-col-2', fields: [
822
+ safeName('crud.defaults.header.showBack'),
823
+ safeName('crud.defaults.header.variant'),
824
+ safeName('crud.defaults.header.sticky'),
825
+ safeName('crud.defaults.header.divider'),
826
+ ] },
827
+ { id: 'crud-col-3', fields: [
828
+ safeName('crud.defaults.modal.width'),
829
+ safeName('crud.defaults.modal.maxWidth'),
830
+ safeName('crud.defaults.modal.backdropStyle'),
831
+ ] },
832
+ ],
833
+ },
834
+ ],
835
+ },
836
+ {
837
+ id: 'dynamic-fields',
838
+ title: 'Dynamic Fields',
839
+ rows: [
840
+ {
841
+ id: 'df-row-1',
842
+ columns: [
843
+ { id: 'df-col-1', fields: [
844
+ safeName('dynamicFields.asyncSelect.loadOn'),
845
+ safeName('dynamicFields.asyncSelect.pageSize'),
846
+ safeName('dynamicFields.asyncSelect.useCursor'),
847
+ ] },
848
+ { id: 'df-col-2', fields: [
849
+ safeName('dynamicFields.cascade.enable'),
850
+ safeName('dynamicFields.cascade.loadOnChange'),
851
+ safeName('dynamicFields.cascade.debounceMs'),
852
+ ] },
853
+ ],
854
+ },
855
+ ],
856
+ },
857
+ {
858
+ id: 'table',
859
+ title: 'Tabela',
860
+ rows: [
861
+ {
862
+ id: 'tbl-row-1',
863
+ columns: [
864
+ { id: 'tbl-col-1', fields: [
865
+ safeName('table.toolbar.visible'),
866
+ safeName('table.appearance.density'),
867
+ safeName('table.filteringUi.advancedOpenMode'),
868
+ safeName('table.filteringUi.overlayVariant'),
869
+ safeName('table.filteringUi.overlayBackdrop'),
870
+ ] },
871
+ { id: 'tbl-col-2', fields: [
872
+ safeName('table.behavior.pagination.enabled'),
873
+ safeName('table.behavior.pagination.pageSize'),
874
+ safeName('table.behavior.pagination.showFirstLastButtons'),
875
+ safeName('table.behavior.pagination.strategy'),
876
+ ] },
877
+ { id: 'tbl-col-3', fields: [
878
+ safeName('table.behavior.filtering.debounceTime'),
879
+ safeName('table.behavior.sorting.enabled'),
880
+ safeName('table.behavior.sorting.strategy'),
881
+ safeName('table.behavior.selection.enabled'),
882
+ ] },
883
+ ],
884
+ },
885
+ {
886
+ id: 'tbl-row-messages',
887
+ columns: [
888
+ { id: 'tbl-msg-col-1', fields: [
889
+ safeName('table.messages.actions.confirmations.delete'),
890
+ safeName('table.messages.actions.confirmations.deleteMultiple'),
891
+ safeName('table.messages.actions.confirmations.save'),
892
+ safeName('table.messages.actions.confirmations.cancel'),
893
+ safeName('table.messages.actions.confirmations.export'),
894
+ ] },
895
+ { id: 'tbl-msg-col-2', fields: [
896
+ safeName('table.messages.actions.progress.delete'),
897
+ safeName('table.messages.actions.progress.deleteMultiple'),
898
+ safeName('table.messages.actions.success.delete'),
899
+ safeName('table.messages.actions.success.save'),
900
+ safeName('table.messages.actions.success.export'),
901
+ safeName('table.messages.actions.success.import'),
902
+ ] },
903
+ { id: 'tbl-msg-col-3', fields: [
904
+ safeName('table.messages.actions.errors.delete'),
905
+ safeName('table.messages.actions.errors.save'),
906
+ safeName('table.messages.actions.errors.export'),
907
+ safeName('table.messages.actions.errors.network'),
908
+ safeName('table.messages.actions.errors.permission'),
909
+ ] },
910
+ ],
911
+ },
912
+ ],
913
+ },
914
+ {
915
+ id: 'dialog',
916
+ title: 'Dialog',
917
+ rows: [
918
+ {
919
+ id: 'dlg-row-defaults',
920
+ columns: [
921
+ { id: 'dlg-def-col-1', fields: [
922
+ safeName('dialog.defaults.confirm.ariaRole'),
923
+ safeName('dialog.defaults.confirm.closeOnBackdropClick'),
924
+ ] },
925
+ { id: 'dlg-def-col-2', fields: [
926
+ safeName('dialog.defaults.alert.ariaRole'),
927
+ safeName('dialog.defaults.alert.closeOnBackdropClick'),
928
+ ] },
929
+ { id: 'dlg-def-col-3', fields: [
930
+ safeName('dialog.defaults.prompt.ariaRole'),
931
+ safeName('dialog.defaults.prompt.closeOnBackdropClick'),
932
+ ] },
933
+ ],
934
+ },
935
+ {
936
+ id: 'dlg-row-variants-danger',
937
+ columns: [
938
+ { id: 'dlg-var-danger-col-1', fields: [
939
+ safeName('dialog.variants.danger.title'),
940
+ safeName('dialog.variants.danger.icon'),
941
+ safeName('dialog.variants.danger.message'),
942
+ ] },
943
+ { id: 'dlg-var-danger-col-2', fields: [
944
+ safeName('dialog.variants.danger.ariaRole'),
945
+ safeName('dialog.variants.danger.closeOnBackdropClick'),
946
+ safeName('dialog.variants.danger.panelClass'),
947
+ ] },
948
+ { id: 'dlg-var-danger-col-3', fields: [
949
+ safeName('dialog.variants.danger.actions'),
950
+ safeName('dialog.variants.danger.styles'),
951
+ safeName('dialog.variants.danger.animation'),
952
+ ] },
953
+ ],
954
+ },
955
+ {
956
+ id: 'dlg-row-variants-info',
957
+ columns: [
958
+ { id: 'dlg-var-info-col-1', fields: [
959
+ safeName('dialog.variants.info.title'),
960
+ safeName('dialog.variants.info.icon'),
961
+ safeName('dialog.variants.info.message'),
962
+ ] },
963
+ { id: 'dlg-var-info-col-2', fields: [
964
+ safeName('dialog.variants.info.ariaRole'),
965
+ safeName('dialog.variants.info.closeOnBackdropClick'),
966
+ safeName('dialog.variants.info.panelClass'),
967
+ ] },
968
+ { id: 'dlg-var-info-col-3', fields: [
969
+ safeName('dialog.variants.info.actions'),
970
+ safeName('dialog.variants.info.styles'),
971
+ safeName('dialog.variants.info.animation'),
972
+ ] },
973
+ ],
974
+ },
975
+ {
976
+ id: 'dlg-row-variants-success',
977
+ columns: [
978
+ { id: 'dlg-var-success-col-1', fields: [
979
+ safeName('dialog.variants.success.title'),
980
+ safeName('dialog.variants.success.icon'),
981
+ safeName('dialog.variants.success.message'),
982
+ ] },
983
+ { id: 'dlg-var-success-col-2', fields: [
984
+ safeName('dialog.variants.success.ariaRole'),
985
+ safeName('dialog.variants.success.closeOnBackdropClick'),
986
+ safeName('dialog.variants.success.panelClass'),
987
+ ] },
988
+ { id: 'dlg-var-success-col-3', fields: [
989
+ safeName('dialog.variants.success.actions'),
990
+ safeName('dialog.variants.success.styles'),
991
+ safeName('dialog.variants.success.animation'),
992
+ ] },
993
+ ],
994
+ },
995
+ {
996
+ id: 'dlg-row-variants-question',
997
+ columns: [
998
+ { id: 'dlg-var-question-col-1', fields: [
999
+ safeName('dialog.variants.question.title'),
1000
+ safeName('dialog.variants.question.icon'),
1001
+ safeName('dialog.variants.question.message'),
1002
+ ] },
1003
+ { id: 'dlg-var-question-col-2', fields: [
1004
+ safeName('dialog.variants.question.ariaRole'),
1005
+ safeName('dialog.variants.question.closeOnBackdropClick'),
1006
+ safeName('dialog.variants.question.panelClass'),
1007
+ ] },
1008
+ { id: 'dlg-var-question-col-3', fields: [
1009
+ safeName('dialog.variants.question.actions'),
1010
+ safeName('dialog.variants.question.styles'),
1011
+ safeName('dialog.variants.question.animation'),
1012
+ ] },
1013
+ ],
1014
+ },
1015
+ {
1016
+ id: 'dlg-row-variants-error',
1017
+ columns: [
1018
+ { id: 'dlg-var-error-col-1', fields: [
1019
+ safeName('dialog.variants.error.title'),
1020
+ safeName('dialog.variants.error.icon'),
1021
+ safeName('dialog.variants.error.message'),
1022
+ ] },
1023
+ { id: 'dlg-var-error-col-2', fields: [
1024
+ safeName('dialog.variants.error.ariaRole'),
1025
+ safeName('dialog.variants.error.closeOnBackdropClick'),
1026
+ safeName('dialog.variants.error.panelClass'),
1027
+ ] },
1028
+ { id: 'dlg-var-error-col-3', fields: [
1029
+ safeName('dialog.variants.error.actions'),
1030
+ safeName('dialog.variants.error.styles'),
1031
+ safeName('dialog.variants.error.animation'),
1032
+ ] },
1033
+ ],
1034
+ },
1035
+ ],
1036
+ },
1037
+ ],
1038
+ actions: {
1039
+ placement: 'afterSections',
1040
+ // Hide internal form actions; Settings Panel footer handles Apply/Save
1041
+ submit: { visible: false, label: 'Salvar' },
1042
+ cancel: { visible: false, label: 'Cancelar' },
1043
+ reset: { visible: false, label: 'Redefinir', type: 'reset' },
1044
+ },
1045
+ fieldMetadata: fields,
1046
+ };
1047
+ return cfg;
1048
+ }
1049
+
1050
+ class GlobalConfigEditorComponent {
1051
+ admin;
1052
+ snack;
1053
+ formConfig;
1054
+ initialValues = {};
1055
+ currentValues = {};
1056
+ pathMap = {};
1057
+ bootstrappedSections = new Set();
1058
+ allSections = ['crud', 'dynamic-fields', 'table', 'dialog'];
1059
+ // SettingsPanel integration signals
1060
+ isDirty$ = new BehaviorSubject(false);
1061
+ isValid$ = new BehaviorSubject(true);
1062
+ isBusy$ = new BehaviorSubject(false);
1063
+ hostCrud;
1064
+ hostFields;
1065
+ hostTable;
1066
+ hostDialog;
1067
+ destroyRef = inject(DestroyRef);
1068
+ iconPicker = inject(IconPickerService);
1069
+ // Guardar instância do form da seção 'dialog' para atualizar controles diretamente
1070
+ dialogFormInst = null;
1071
+ dialogVariantKeys = ['danger', 'info', 'success', 'question', 'error'];
1072
+ constructor(admin, snack) {
1073
+ this.admin = admin;
1074
+ this.snack = snack;
1075
+ }
1076
+ ngOnInit() {
1077
+ const cfg = this.admin.getEffectiveConfig();
1078
+ const flat = this.flatten(cfg);
1079
+ this.formConfig = buildGlobalConfigFormConfig();
1080
+ // Build mapping from safeName -> dot path and set defaultValue on fieldMetadata
1081
+ this.pathMap = {};
1082
+ for (const fm of this.formConfig.fieldMetadata || []) {
1083
+ const da = fm.dataAttributes || {};
1084
+ const path = da.globalPath || fm.name;
1085
+ this.pathMap[fm.name] = path;
1086
+ const v = flat[path];
1087
+ if (v !== undefined)
1088
+ fm.defaultValue = v;
1089
+ }
1090
+ // Initial/current values in SAFE namespace (control names)
1091
+ this.initialValues = {};
1092
+ for (const safe of Object.keys(this.pathMap)) {
1093
+ const path = this.pathMap[safe];
1094
+ const v = flat[path];
1095
+ if (v !== undefined)
1096
+ this.initialValues[safe] = v;
1097
+ }
1098
+ this.currentValues = { ...this.initialValues };
1099
+ // Lazy-load PraxisDynamicForm to avoid circular dependency with settings-panel
1100
+ import('@praxisui/dynamic-form').then((m) => {
1101
+ const Comp = m.PraxisDynamicForm;
1102
+ const makeGroupConfig = (sectionId) => {
1103
+ const sec = (this.formConfig.sections || []).find((s) => s.id === sectionId);
1104
+ if (!sec)
1105
+ return { sections: [], fieldMetadata: [] };
1106
+ // Collect field names in this section
1107
+ const names = new Set();
1108
+ for (const row of sec.rows || []) {
1109
+ for (const col of row.columns || []) {
1110
+ for (const fname of col.fields || [])
1111
+ names.add(fname);
1112
+ }
1113
+ }
1114
+ const fms = (this.formConfig.fieldMetadata || []).filter((f) => names.has(f.name));
1115
+ return { sections: [sec], fieldMetadata: fms };
1116
+ };
1117
+ const groups = [
1118
+ { id: 'crud', host: this.hostCrud, cfg: makeGroupConfig('crud') },
1119
+ { id: 'dynamic-fields', host: this.hostFields, cfg: makeGroupConfig('dynamic-fields') },
1120
+ { id: 'table', host: this.hostTable, cfg: makeGroupConfig('table') },
1121
+ { id: 'dialog', host: this.hostDialog, cfg: makeGroupConfig('dialog') },
1122
+ ];
1123
+ for (const g of groups) {
1124
+ const ref = g.host.createComponent(Comp);
1125
+ ref.setInput('config', g.cfg);
1126
+ ref.setInput('mode', 'edit');
1127
+ ref.setInput('editModeEnabled', false);
1128
+ const inst = ref.instance;
1129
+ try {
1130
+ if (typeof inst.buildFormFromConfig === 'function')
1131
+ inst.buildFormFromConfig();
1132
+ }
1133
+ catch { }
1134
+ try {
1135
+ inst.onSubmit = () => { };
1136
+ }
1137
+ catch { }
1138
+ try {
1139
+ ref.changeDetectorRef.detectChanges();
1140
+ }
1141
+ catch { }
1142
+ if (inst?.valueChange?.subscribe) {
1143
+ inst.valueChange.subscribe((ev) => this.onValueChange(g.id, ev));
1144
+ }
1145
+ if (g.id === 'dialog') {
1146
+ // Guardar referência para facilitar setValue em campos de ícone
1147
+ this.dialogFormInst = inst;
1148
+ }
1149
+ this.destroyRef.onDestroy(() => { try {
1150
+ ref.destroy();
1151
+ }
1152
+ catch { } });
1153
+ }
1154
+ });
1155
+ }
1156
+ onValueChange(sectionId, ev) {
1157
+ this.isValid$.next(!!ev.isValid);
1158
+ // Mesclar valores por seção (cada form emite somente seu grupo)
1159
+ this.currentValues = { ...this.currentValues, ...ev.formData };
1160
+ // Durante o bootstrap, coletar baseline de todas as seções
1161
+ if (!this.hasBootstrappedAll()) {
1162
+ this.initialValues = { ...this.initialValues, ...ev.formData };
1163
+ this.bootstrappedSections.add(sectionId);
1164
+ if (this.hasBootstrappedAll()) {
1165
+ // Após todas as seções emitirem, marcar como limpo
1166
+ this.isDirty$.next(false);
1167
+ }
1168
+ return;
1169
+ }
1170
+ this.isDirty$.next(!this.shallowEqual(this.currentValues, this.initialValues));
1171
+ }
1172
+ hasBootstrappedAll() {
1173
+ return this.allSections.every((s) => this.bootstrappedSections.has(s));
1174
+ }
1175
+ // SettingsPanel expects these methods
1176
+ reset() {
1177
+ const flat = {};
1178
+ for (const safe of Object.keys(this.initialValues))
1179
+ flat[safe] = this.initialValues[safe];
1180
+ for (const fm of this.formConfig.fieldMetadata || []) {
1181
+ const v = flat[fm.name];
1182
+ fm.defaultValue = v;
1183
+ }
1184
+ this.currentValues = { ...this.initialValues };
1185
+ this.isDirty$.next(false);
1186
+ }
1187
+ getSettingsValue() {
1188
+ // Map SAFE keys back to dot-paths using pathMap
1189
+ const flat = {};
1190
+ for (const [safe, value] of Object.entries(this.currentValues)) {
1191
+ const path = this.pathMap[safe] || safe;
1192
+ flat[path] = value;
1193
+ }
1194
+ return this.toNested(flat);
1195
+ }
1196
+ onSave() {
1197
+ const partial = this.getSettingsValue();
1198
+ this.admin.save(partial);
1199
+ // Update baseline after save
1200
+ this.initialValues = { ...this.currentValues };
1201
+ this.isDirty$.next(false);
1202
+ try {
1203
+ this.snack.open('Configurações salvas', undefined, { duration: 2000 });
1204
+ }
1205
+ catch { }
1206
+ return partial;
1207
+ }
1208
+ // ===== Helpers de UX para ícones de Dialog (variants) =====
1209
+ safeName(path) {
1210
+ return path.replace(/[^a-zA-Z0-9_]/g, '_');
1211
+ }
1212
+ getVariantIcon(key) {
1213
+ const safe = this.safeName(`dialog.variants.${key}.icon`);
1214
+ return this.currentValues[safe];
1215
+ }
1216
+ async pickVariantIcon(key) {
1217
+ const safe = this.safeName(`dialog.variants.${key}.icon`);
1218
+ const current = this.currentValues[safe] || '';
1219
+ // Melhor UX: usar MatDialog (modal) em vez do Settings Panel para o picker
1220
+ const res = await this.iconPicker.openDialog({ value: current });
1221
+ if (res === undefined)
1222
+ return; // cancel
1223
+ this.setDialogControlValue(safe, res);
1224
+ }
1225
+ clearVariantIcon(key) {
1226
+ const safe = this.safeName(`dialog.variants.${key}.icon`);
1227
+ this.setDialogControlValue(safe, '');
1228
+ }
1229
+ setDialogControlValue(safeControlName, val) {
1230
+ const inst = this.dialogFormInst;
1231
+ try {
1232
+ const ctrl = inst?.form?.get?.(safeControlName);
1233
+ if (ctrl) {
1234
+ ctrl.setValue(val);
1235
+ return;
1236
+ }
1237
+ }
1238
+ catch { }
1239
+ // Fallback: atualiza cache local e marca como sujo
1240
+ this.currentValues[safeControlName] = val;
1241
+ this.isDirty$.next(!this.shallowEqual(this.currentValues, this.initialValues));
1242
+ }
1243
+ withDefaults(config, values) {
1244
+ const next = JSON.parse(JSON.stringify(config));
1245
+ for (const section of next.sections || []) {
1246
+ for (const row of section.rows || []) {
1247
+ for (const col of row.columns || []) {
1248
+ for (const field of col.fields || []) {
1249
+ const key = field.name;
1250
+ if (!key)
1251
+ continue;
1252
+ const v = values[key];
1253
+ if (v !== undefined) {
1254
+ field.defaultValue = v;
1255
+ }
1256
+ }
1257
+ }
1258
+ }
1259
+ }
1260
+ return next;
1261
+ }
1262
+ setByPath(target, dotPath, value) {
1263
+ const parts = dotPath.split('.');
1264
+ let ref = target;
1265
+ for (let i = 0; i < parts.length - 1; i++) {
1266
+ const p = parts[i];
1267
+ if (typeof ref[p] !== 'object' || ref[p] == null)
1268
+ ref[p] = {};
1269
+ ref = ref[p];
1270
+ }
1271
+ ref[parts[parts.length - 1]] = value;
1272
+ }
1273
+ flatten(obj, prefix = '') {
1274
+ const out = {};
1275
+ if (!obj || typeof obj !== 'object')
1276
+ return out;
1277
+ for (const key of Object.keys(obj)) {
1278
+ const val = obj[key];
1279
+ const path = prefix ? `${prefix}.${key}` : key;
1280
+ if (val && typeof val === 'object' && !Array.isArray(val)) {
1281
+ Object.assign(out, this.flatten(val, path));
1282
+ }
1283
+ else {
1284
+ out[path] = val;
1285
+ }
1286
+ }
1287
+ return out;
1288
+ }
1289
+ toNested(flat) {
1290
+ const out = {};
1291
+ for (const k of Object.keys(flat)) {
1292
+ const v = flat[k];
1293
+ if (v === undefined)
1294
+ continue;
1295
+ this.setByPath(out, k, v);
1296
+ }
1297
+ return out;
1298
+ }
1299
+ shallowEqual(a, b) {
1300
+ const ak = Object.keys(a);
1301
+ const bk = Object.keys(b);
1302
+ if (ak.length !== bk.length)
1303
+ return false;
1304
+ for (const k of ak) {
1305
+ if (a[k] !== b[k])
1306
+ return false;
1307
+ }
1308
+ return true;
1309
+ }
1310
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: GlobalConfigEditorComponent, deps: [{ token: GlobalConfigAdminService }, { token: i2.MatSnackBar }], target: i0.ɵɵFactoryTarget.Component });
1311
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.4", type: GlobalConfigEditorComponent, isStandalone: true, selector: "praxis-global-config-editor", viewQueries: [{ propertyName: "hostCrud", first: true, predicate: ["hostCrud"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "hostFields", first: true, predicate: ["hostFields"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "hostTable", first: true, predicate: ["hostTable"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "hostDialog", first: true, predicate: ["hostDialog"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: `
1312
+ <mat-accordion multi>
1313
+ <mat-expansion-panel [expanded]="true">
1314
+ <mat-expansion-panel-header>
1315
+ <mat-panel-title>CRUD</mat-panel-title>
1316
+ <mat-panel-description>Políticas globais de abertura, back e header</mat-panel-description>
1317
+ </mat-expansion-panel-header>
1318
+ <ng-template #hostCrud></ng-template>
1319
+ </mat-expansion-panel>
1320
+ <mat-expansion-panel>
1321
+ <mat-expansion-panel-header>
1322
+ <mat-panel-title>Dynamic Fields</mat-panel-title>
1323
+ <mat-panel-description>Async Select, cascata e paginação</mat-panel-description>
1324
+ </mat-expansion-panel-header>
1325
+ <ng-template #hostFields></ng-template>
1326
+ </mat-expansion-panel>
1327
+ <mat-expansion-panel>
1328
+ <mat-expansion-panel-header>
1329
+ <mat-panel-title>Tabela</mat-panel-title>
1330
+ <mat-panel-description>Toolbar, aparência e filtro avançado</mat-panel-description>
1331
+ </mat-expansion-panel-header>
1332
+ <ng-template #hostTable></ng-template>
1333
+ </mat-expansion-panel>
1334
+ <mat-expansion-panel>
1335
+ <mat-expansion-panel-header>
1336
+ <mat-panel-title>Dialog</mat-panel-title>
1337
+ <mat-panel-description>Defaults e variants (danger, info, success, question, error)</mat-panel-description>
1338
+ </mat-expansion-panel-header>
1339
+ <ng-template #hostDialog></ng-template>
1340
+ <!-- Icon UX helpers for Dialog variants -->
1341
+ <div class="dlg-icon-helpers">
1342
+ <div class="dlg-icon-helpers__head">Ícones por perfil (atalhos)</div>
1343
+ <div class="dlg-icon-helpers__row" *ngFor="let k of dialogVariantKeys">
1344
+ <div class="dlg-icon-helpers__label">{{ k }}</div>
1345
+ <mat-icon aria-hidden="true" [praxisIcon]="getVariantIcon(k) || 'mi:help_outline'" class="dlg-icon-helpers__preview"></mat-icon>
1346
+ <button mat-stroked-button color="primary" type="button" (click)="pickVariantIcon(k)" matTooltip="Escolher ícone para {{k}}">
1347
+ <mat-icon>search</mat-icon>
1348
+ Escolher ícone
1349
+ </button>
1350
+ <button mat-icon-button type="button" *ngIf="getVariantIcon(k)" (click)="clearVariantIcon(k)" matTooltip="Limpar ícone de {{k}}" aria-label="Limpar ícone">
1351
+ <mat-icon>backspace</mat-icon>
1352
+ </button>
1353
+ <span class="dlg-icon-helpers__value" *ngIf="getVariantIcon(k)">{{ getVariantIcon(k) }}</span>
1354
+ </div>
1355
+ <div class="dlg-icon-helpers__hint">Dica: O campo de texto acima permanece editável. Estes botões apenas facilitam a escolha/limpeza com preview.</div>
1356
+ </div>
1357
+ </mat-expansion-panel>
1358
+ </mat-accordion>
1359
+ `, isInline: true, styles: [".dlg-icon-helpers{margin:12px 16px 4px;padding:12px;border:1px dashed rgba(0,0,0,.1);border-radius:8px}.dlg-icon-helpers__head{font-weight:600;margin-bottom:8px;opacity:.8}.dlg-icon-helpers__row{display:flex;align-items:center;gap:12px;padding:6px 0}.dlg-icon-helpers__label{text-transform:capitalize;width:96px;opacity:.8}.dlg-icon-helpers__preview{width:24px;height:24px}.dlg-icon-helpers__value{font-family:monospace;font-size:12px;opacity:.8}.dlg-icon-helpers__hint{margin-top:8px;font-size:12px;opacity:.7}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "directive", type: i4$1.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i4$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i4$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i4$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i4$1.MatExpansionPanelDescription, selector: "mat-panel-description" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$1.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: i3$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }] });
1360
+ }
1361
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: GlobalConfigEditorComponent, decorators: [{
1362
+ type: Component,
1363
+ args: [{ selector: 'praxis-global-config-editor', standalone: true, imports: [CommonModule, MatSnackBarModule, MatExpansionModule, MatIconModule, MatButtonModule, MatTooltipModule, PraxisIconDirective], template: `
1364
+ <mat-accordion multi>
1365
+ <mat-expansion-panel [expanded]="true">
1366
+ <mat-expansion-panel-header>
1367
+ <mat-panel-title>CRUD</mat-panel-title>
1368
+ <mat-panel-description>Políticas globais de abertura, back e header</mat-panel-description>
1369
+ </mat-expansion-panel-header>
1370
+ <ng-template #hostCrud></ng-template>
1371
+ </mat-expansion-panel>
1372
+ <mat-expansion-panel>
1373
+ <mat-expansion-panel-header>
1374
+ <mat-panel-title>Dynamic Fields</mat-panel-title>
1375
+ <mat-panel-description>Async Select, cascata e paginação</mat-panel-description>
1376
+ </mat-expansion-panel-header>
1377
+ <ng-template #hostFields></ng-template>
1378
+ </mat-expansion-panel>
1379
+ <mat-expansion-panel>
1380
+ <mat-expansion-panel-header>
1381
+ <mat-panel-title>Tabela</mat-panel-title>
1382
+ <mat-panel-description>Toolbar, aparência e filtro avançado</mat-panel-description>
1383
+ </mat-expansion-panel-header>
1384
+ <ng-template #hostTable></ng-template>
1385
+ </mat-expansion-panel>
1386
+ <mat-expansion-panel>
1387
+ <mat-expansion-panel-header>
1388
+ <mat-panel-title>Dialog</mat-panel-title>
1389
+ <mat-panel-description>Defaults e variants (danger, info, success, question, error)</mat-panel-description>
1390
+ </mat-expansion-panel-header>
1391
+ <ng-template #hostDialog></ng-template>
1392
+ <!-- Icon UX helpers for Dialog variants -->
1393
+ <div class="dlg-icon-helpers">
1394
+ <div class="dlg-icon-helpers__head">Ícones por perfil (atalhos)</div>
1395
+ <div class="dlg-icon-helpers__row" *ngFor="let k of dialogVariantKeys">
1396
+ <div class="dlg-icon-helpers__label">{{ k }}</div>
1397
+ <mat-icon aria-hidden="true" [praxisIcon]="getVariantIcon(k) || 'mi:help_outline'" class="dlg-icon-helpers__preview"></mat-icon>
1398
+ <button mat-stroked-button color="primary" type="button" (click)="pickVariantIcon(k)" matTooltip="Escolher ícone para {{k}}">
1399
+ <mat-icon>search</mat-icon>
1400
+ Escolher ícone
1401
+ </button>
1402
+ <button mat-icon-button type="button" *ngIf="getVariantIcon(k)" (click)="clearVariantIcon(k)" matTooltip="Limpar ícone de {{k}}" aria-label="Limpar ícone">
1403
+ <mat-icon>backspace</mat-icon>
1404
+ </button>
1405
+ <span class="dlg-icon-helpers__value" *ngIf="getVariantIcon(k)">{{ getVariantIcon(k) }}</span>
1406
+ </div>
1407
+ <div class="dlg-icon-helpers__hint">Dica: O campo de texto acima permanece editável. Estes botões apenas facilitam a escolha/limpeza com preview.</div>
1408
+ </div>
1409
+ </mat-expansion-panel>
1410
+ </mat-accordion>
1411
+ `, styles: [".dlg-icon-helpers{margin:12px 16px 4px;padding:12px;border:1px dashed rgba(0,0,0,.1);border-radius:8px}.dlg-icon-helpers__head{font-weight:600;margin-bottom:8px;opacity:.8}.dlg-icon-helpers__row{display:flex;align-items:center;gap:12px;padding:6px 0}.dlg-icon-helpers__label{text-transform:capitalize;width:96px;opacity:.8}.dlg-icon-helpers__preview{width:24px;height:24px}.dlg-icon-helpers__value{font-family:monospace;font-size:12px;opacity:.8}.dlg-icon-helpers__hint{margin-top:8px;font-size:12px;opacity:.7}\n"] }]
1412
+ }], ctorParameters: () => [{ type: GlobalConfigAdminService }, { type: i2.MatSnackBar }], propDecorators: { hostCrud: [{
1413
+ type: ViewChild,
1414
+ args: ['hostCrud', { read: ViewContainerRef, static: true }]
1415
+ }], hostFields: [{
1416
+ type: ViewChild,
1417
+ args: ['hostFields', { read: ViewContainerRef, static: true }]
1418
+ }], hostTable: [{
1419
+ type: ViewChild,
1420
+ args: ['hostTable', { read: ViewContainerRef, static: true }]
1421
+ }], hostDialog: [{
1422
+ type: ViewChild,
1423
+ args: ['hostDialog', { read: ViewContainerRef, static: true }]
1424
+ }] } });
1425
+
1426
+ /**
1427
+ * Helper to open the Global Config Editor in a host application.
1428
+ * Usage:
1429
+ * openGlobalConfigEditor(settings);
1430
+ */
1431
+ function openGlobalConfigEditor(settings, opts) {
1432
+ settings.open({
1433
+ id: opts?.id ?? 'global-config',
1434
+ title: opts?.title ?? 'Configurações Globais',
1435
+ titleIcon: opts?.titleIcon ?? 'tune',
1436
+ content: { component: GlobalConfigEditorComponent, inputs: {} },
1437
+ });
1438
+ }
1439
+
1440
+ /**
1441
+ * Generated bundle index. Do not edit.
1442
+ */
1443
+
1444
+ export { GlobalConfigAdminService, GlobalConfigEditorComponent, SETTINGS_PANEL_DATA, SETTINGS_PANEL_REF, SettingsPanelComponent, SettingsPanelRef, SettingsPanelService, buildGlobalConfigFormConfig, openGlobalConfigEditor };
1445
+ //# sourceMappingURL=praxisui-settings-panel.mjs.map