@praxisui/crud 9.0.4-rc.8 → 9.0.4
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 +41 -1
- package/ai/component-registry.json +59 -12
- package/fesm2022/praxisui-crud.mjs +692 -170
- package/package.json +7 -7
- package/types/praxisui-crud-drawer-adapter.d.ts +3 -1
- package/types/praxisui-crud.d.ts +66 -15
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, InjectionToken, inject, input, signal, computed, effect, ChangeDetectionStrategy, Component, EventEmitter, DestroyRef, ChangeDetectorRef, Injector, ViewChild, Output, Input, Inject, ViewEncapsulation, ENVIRONMENT_INITIALIZER } from '@angular/core';
|
|
2
|
+
import { Injectable, InjectionToken, inject, input, signal, computed, effect, ChangeDetectionStrategy, Component, EventEmitter, DestroyRef, ChangeDetectorRef, Injector, ViewChild, Output, Input, Optional, Inject, ViewEncapsulation, ENVIRONMENT_INITIALIZER } from '@angular/core';
|
|
3
3
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
4
4
|
import { HttpClient } from '@angular/common/http';
|
|
5
5
|
import { Router, ActivatedRoute, RouterLink } from '@angular/router';
|
|
6
6
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
|
7
7
|
import { firstValueFrom, BehaviorSubject, Subscription } from 'rxjs';
|
|
8
8
|
import * as i2$1 from '@praxisui/core';
|
|
9
|
-
import { ASYNC_CONFIG_STORAGE, GlobalConfigService, CrudOperationResolutionService, fillUndefined, SETTINGS_PANEL_DATA, PraxisI18nService, PraxisIconDirective, providePraxisI18nConfig, createDefaultTableConfig, EnterpriseRuntimeContextService, GLOBAL_SURFACE_SERVICE, SurfaceOutletRegistryService, ComponentKeyService, ResourceDiscoveryService, ResourceActionOpenAdapterService, ResourceSurfaceOpenAdapterService, translateUnavailableWorkflowMessage, EmptyStateCardComponent, RESOURCE_DISCOVERY_I18N_CONFIG, PraxisIconButtonComponent, PraxisResourceIdentityComponent, GenericCrudService, ComponentMetadataRegistry } from '@praxisui/core';
|
|
9
|
+
import { ASYNC_CONFIG_STORAGE, GlobalConfigService, CrudOperationResolutionService, LoggerService, SurfaceNavigationError, fillUndefined, SETTINGS_PANEL_DATA, PraxisI18nService, PraxisIconDirective, providePraxisI18nConfig, createDefaultTableConfig, EnterpriseRuntimeContextService, GLOBAL_SURFACE_SERVICE, SurfaceOutletRegistryService, ComponentKeyService, ResourceDiscoveryService, ResourceActionOpenAdapterService, ResourceSurfaceOpenAdapterService, normalizeSurfaceOperationContext, translateUnavailableWorkflowMessage, isSurfaceNavigationError, translateSurfaceNavigationRejected, EmptyStateCardComponent, RESOURCE_DISCOVERY_I18N_CONFIG, SURFACE_DRAWER_REF, SURFACE_DRAWER_CONTENT_DATA, PraxisIconButtonComponent, PraxisResourceIdentityComponent, GenericCrudService, ComponentMetadataRegistry } from '@praxisui/core';
|
|
10
10
|
import { SettingsPanelService } from '@praxisui/settings-panel';
|
|
11
11
|
import { PraxisTableInlineAuthoringEditorComponent, PraxisTable } from '@praxisui/table';
|
|
12
12
|
import { ConfirmDialogComponent } from '@praxisui/dynamic-fields';
|
|
@@ -97,12 +97,44 @@ function debugCrudLauncher(message, ...data) {
|
|
|
97
97
|
}
|
|
98
98
|
catch { }
|
|
99
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Captures the interaction origin before metadata, capability, or lazy-host
|
|
102
|
+
* work yields the event loop. Material can restore focus to an explicit
|
|
103
|
+
* element, which is essential when the action originated in a closing menu.
|
|
104
|
+
*/
|
|
105
|
+
function captureCrudActionFocusOrigin() {
|
|
106
|
+
if (typeof document === 'undefined') {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
const active = document.activeElement;
|
|
110
|
+
if (!(active instanceof HTMLElement) || active === document.body || !active.isConnected) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
return active;
|
|
114
|
+
}
|
|
115
|
+
function isRuntimeFocusOrigin(value) {
|
|
116
|
+
return typeof HTMLElement !== 'undefined'
|
|
117
|
+
&& value instanceof HTMLElement
|
|
118
|
+
&& typeof value.focus === 'function';
|
|
119
|
+
}
|
|
120
|
+
function resolveCrudRestoreFocus(configured, actionFocusOrigin) {
|
|
121
|
+
if (configured === false || typeof configured === 'string') {
|
|
122
|
+
return configured;
|
|
123
|
+
}
|
|
124
|
+
if (typeof HTMLElement !== 'undefined' && configured instanceof HTMLElement) {
|
|
125
|
+
return configured;
|
|
126
|
+
}
|
|
127
|
+
return actionFocusOrigin ?? true;
|
|
128
|
+
}
|
|
100
129
|
class CrudLauncherService {
|
|
101
130
|
router = inject(Router);
|
|
102
131
|
dialog = inject(DialogService);
|
|
103
132
|
storage = inject(ASYNC_CONFIG_STORAGE);
|
|
104
133
|
global = inject(GlobalConfigService);
|
|
105
134
|
operationResolver = inject(CrudOperationResolutionService);
|
|
135
|
+
logger = inject(LoggerService, { optional: true });
|
|
136
|
+
dialogHostPromise;
|
|
137
|
+
nextSurfaceFrameId = 0;
|
|
106
138
|
drawerAdapter = (() => {
|
|
107
139
|
try {
|
|
108
140
|
return inject(CRUD_DRAWER_ADAPTER);
|
|
@@ -112,6 +144,18 @@ class CrudLauncherService {
|
|
|
112
144
|
}
|
|
113
145
|
})();
|
|
114
146
|
async launch(action, row, metadata, componentKeyId, drawerCallbacks, runtime) {
|
|
147
|
+
// Capture before every await below. A toolbar/row menu closes before the
|
|
148
|
+
// form host is resolved, so the browser's later activeElement is not a
|
|
149
|
+
// reliable representation of the user-facing action trigger.
|
|
150
|
+
// A responsive host can replace the menu trigger while the menu is
|
|
151
|
+
// closing. Keep the original runtime-only reference even when detached:
|
|
152
|
+
// the overlay host resolves its stable data-praxis-focus-key on close.
|
|
153
|
+
const actionFocusOrigin = isRuntimeFocusOrigin(runtime?.focusOrigin)
|
|
154
|
+
? runtime.focusOrigin
|
|
155
|
+
: captureCrudActionFocusOrigin();
|
|
156
|
+
const prefetchedDialogHost = this.isLikelyOverlayAction(action, metadata)
|
|
157
|
+
? this.loadDialogHost()
|
|
158
|
+
: undefined;
|
|
115
159
|
// Carregar overrides de CRUD (se houver) e mesclar em uma cópia local
|
|
116
160
|
const merged = await this.mergeCrudOverrides(metadata, action, componentKeyId || undefined);
|
|
117
161
|
merged.action = this.normalizeActionForLaunch(merged.action);
|
|
@@ -137,21 +181,71 @@ class CrudLauncherService {
|
|
|
137
181
|
row[idField] !== undefined) {
|
|
138
182
|
inputs[idField] = row[idField];
|
|
139
183
|
}
|
|
140
|
-
const resourceIdentity = actionForLaunch.action === 'edit'
|
|
184
|
+
const resourceIdentity = actionForLaunch.action === 'edit' || actionForLaunch.action === 'view'
|
|
141
185
|
? runtime?.resourceIdentity ?? null
|
|
142
186
|
: null;
|
|
187
|
+
const contextIdentity = runtime?.contextIdentity ?? null;
|
|
188
|
+
const dialogData = {
|
|
189
|
+
action: actionForLaunch,
|
|
190
|
+
row,
|
|
191
|
+
metadata: merged.metadata,
|
|
192
|
+
inputs,
|
|
193
|
+
presentation: mode === 'drawer' ? 'drawer' : 'modal',
|
|
194
|
+
resourceIdentity,
|
|
195
|
+
contextIdentity,
|
|
196
|
+
};
|
|
197
|
+
const modalCfg = { ...(merged.metadata.defaults?.modal || {}) };
|
|
198
|
+
const restoreFocus = resolveCrudRestoreFocus(modalCfg.restoreFocus, actionFocusOrigin);
|
|
199
|
+
if (mode === 'drawer' && runtime?.surfaceRuntime?.push) {
|
|
200
|
+
const frameId = this.buildSurfaceFrameId(actionForLaunch);
|
|
201
|
+
const frameRef = await runtime.surfaceRuntime.push({
|
|
202
|
+
id: frameId,
|
|
203
|
+
title: this.resolveSurfaceFrameTitle(actionForLaunch, merged.metadata),
|
|
204
|
+
titleIcon: stringOrUndefined$1(actionForLaunch['icon']),
|
|
205
|
+
subtitle: this.resolveSurfaceFrameSubtitle(actionForLaunch, merged.metadata),
|
|
206
|
+
...(actionFocusOrigin
|
|
207
|
+
? { returnFocusTo: actionFocusOrigin }
|
|
208
|
+
: {}),
|
|
209
|
+
content: {
|
|
210
|
+
component: await (prefetchedDialogHost ?? this.loadDialogHost()),
|
|
211
|
+
inputs: { data: dialogData },
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
if (!frameRef) {
|
|
215
|
+
const error = new SurfaceNavigationError('SURFACE_SESSION_NAVIGATION_REJECTED', 'push', frameId);
|
|
216
|
+
this.logger?.warn('Surface session rejected nested CRUD frame.', {
|
|
217
|
+
context: {
|
|
218
|
+
lib: '@praxisui/crud',
|
|
219
|
+
component: 'CrudLauncherService',
|
|
220
|
+
actionId: `crud.${String(actionForLaunch.action || 'unknown')}`,
|
|
221
|
+
},
|
|
222
|
+
data: {
|
|
223
|
+
code: error.code,
|
|
224
|
+
operation: error.operation,
|
|
225
|
+
frameId: error.frameId,
|
|
226
|
+
formId: actionForLaunch.formId,
|
|
227
|
+
openMode: mode,
|
|
228
|
+
},
|
|
229
|
+
throttleKey: `${error.code}:crud:${frameId}`,
|
|
230
|
+
});
|
|
231
|
+
throw error;
|
|
232
|
+
}
|
|
233
|
+
this.bindSurfaceFrameLifecycle(frameRef, drawerCallbacks);
|
|
234
|
+
return { mode };
|
|
235
|
+
}
|
|
143
236
|
if (mode === 'drawer' && this.drawerAdapter) {
|
|
144
237
|
await Promise.resolve(this.drawerAdapter.open({
|
|
145
238
|
action: actionForLaunch,
|
|
146
239
|
metadata: merged.metadata,
|
|
147
240
|
inputs,
|
|
148
241
|
resourceIdentity,
|
|
242
|
+
contextIdentity,
|
|
243
|
+
restoreFocus,
|
|
149
244
|
onClose: drawerCallbacks?.onClose,
|
|
150
245
|
onResult: drawerCallbacks?.onResult,
|
|
151
246
|
}));
|
|
152
247
|
return { mode };
|
|
153
248
|
}
|
|
154
|
-
const modalCfg = { ...(merged.metadata.defaults?.modal || {}) };
|
|
155
249
|
debugCrudLauncher('[CRUD:Launcher] opening dialog with:', {
|
|
156
250
|
action: merged.action.action,
|
|
157
251
|
formId: actionForLaunch.formId,
|
|
@@ -177,12 +271,12 @@ class CrudLauncherService {
|
|
|
177
271
|
backdropClasses.push('pfx-transparent-backdrop');
|
|
178
272
|
}
|
|
179
273
|
const mergedBackdropClasses = mergeClassList(backdropClasses, modalCfg.backdropClass);
|
|
180
|
-
const ref = await this.dialog.openAsync(() =>
|
|
274
|
+
const ref = await this.dialog.openAsync(() => prefetchedDialogHost ?? this.loadDialogHost(), {
|
|
181
275
|
...modalCfg,
|
|
182
276
|
panelClass: panelClasses,
|
|
183
277
|
backdropClass: mergedBackdropClasses,
|
|
184
278
|
autoFocus: modalCfg.autoFocus ?? true,
|
|
185
|
-
restoreFocus
|
|
279
|
+
restoreFocus,
|
|
186
280
|
minWidth: drawerMode
|
|
187
281
|
? (modalCfg.minWidth ?? DEFAULT_CRUD_DRAWER_MODAL_CONFIG.minWidth)
|
|
188
282
|
: '360px',
|
|
@@ -201,13 +295,9 @@ class CrudLauncherService {
|
|
|
201
295
|
position: dialogPosition,
|
|
202
296
|
ariaLabelledBy: 'crudDialogTitle',
|
|
203
297
|
data: {
|
|
204
|
-
|
|
205
|
-
row,
|
|
206
|
-
metadata: merged.metadata,
|
|
207
|
-
inputs,
|
|
298
|
+
...dialogData,
|
|
208
299
|
presentation: drawerMode ? 'drawer' : 'modal',
|
|
209
300
|
dialogPosition,
|
|
210
|
-
resourceIdentity,
|
|
211
301
|
},
|
|
212
302
|
});
|
|
213
303
|
if (drawerMode) {
|
|
@@ -256,6 +346,13 @@ class CrudLauncherService {
|
|
|
256
346
|
return 'route';
|
|
257
347
|
}
|
|
258
348
|
}
|
|
349
|
+
isLikelyOverlayAction(action, metadata) {
|
|
350
|
+
const configuredMode = action.openMode ?? metadata.defaults?.openMode;
|
|
351
|
+
return configuredMode === 'drawer' || configuredMode === 'modal';
|
|
352
|
+
}
|
|
353
|
+
loadDialogHost() {
|
|
354
|
+
return this.dialogHostPromise ??= Promise.resolve().then(function () { return dynamicFormDialogHost_component; }).then((module) => module.DynamicFormDialogHostComponent);
|
|
355
|
+
}
|
|
259
356
|
buildRoute(action, row, metadata) {
|
|
260
357
|
let route = action.route;
|
|
261
358
|
const query = {};
|
|
@@ -468,8 +565,51 @@ class CrudLauncherService {
|
|
|
468
565
|
preset: 'groupedCommand',
|
|
469
566
|
persistence: 'transient',
|
|
470
567
|
schemaType: 'request',
|
|
568
|
+
groupedCommand: {
|
|
569
|
+
partialRowStrategy: 'fill-compatible',
|
|
570
|
+
},
|
|
471
571
|
};
|
|
472
572
|
}
|
|
573
|
+
buildSurfaceFrameId(action) {
|
|
574
|
+
this.nextSurfaceFrameId += 1;
|
|
575
|
+
return `crud:${action.action}:${action.formId || 'form'}:${this.nextSurfaceFrameId}`;
|
|
576
|
+
}
|
|
577
|
+
resolveSurfaceFrameTitle(action, metadata) {
|
|
578
|
+
return stringOrUndefined$1(action['label'])
|
|
579
|
+
?? stringOrUndefined$1(metadata.form?.['title'])
|
|
580
|
+
?? stringOrUndefined$1(metadata.table?.['title'])
|
|
581
|
+
?? (action.action === 'create'
|
|
582
|
+
? 'Adicionar registro'
|
|
583
|
+
: action.action === 'view'
|
|
584
|
+
? 'Consultar registro'
|
|
585
|
+
: 'Editar registro');
|
|
586
|
+
}
|
|
587
|
+
resolveSurfaceFrameSubtitle(action, metadata) {
|
|
588
|
+
return stringOrUndefined$1(action['description'])
|
|
589
|
+
?? stringOrUndefined$1(action['tooltip'])
|
|
590
|
+
?? stringOrUndefined$1(metadata.form?.['description'])
|
|
591
|
+
?? stringOrUndefined$1(metadata.table?.['subtitle']);
|
|
592
|
+
}
|
|
593
|
+
bindSurfaceFrameLifecycle(frameRef, callbacks) {
|
|
594
|
+
let semanticResultHandled = false;
|
|
595
|
+
const handleResult = (value) => {
|
|
596
|
+
if (semanticResultHandled)
|
|
597
|
+
return;
|
|
598
|
+
const result = toCrudDrawerResult(value);
|
|
599
|
+
if (!result || result.type === 'close')
|
|
600
|
+
return;
|
|
601
|
+
semanticResultHandled = true;
|
|
602
|
+
callbacks?.onResult?.(result);
|
|
603
|
+
};
|
|
604
|
+
frameRef.result$?.pipe(take(1)).subscribe(handleResult);
|
|
605
|
+
frameRef.closed$.pipe(take(1)).subscribe((value) => {
|
|
606
|
+
handleResult(value);
|
|
607
|
+
callbacks?.onClose?.();
|
|
608
|
+
if (!semanticResultHandled) {
|
|
609
|
+
callbacks?.onResult?.({ type: 'close' });
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
}
|
|
473
613
|
async mergeCrudOverrides(metadata, action, componentKeyId) {
|
|
474
614
|
try {
|
|
475
615
|
if (!componentKeyId)
|
|
@@ -551,6 +691,10 @@ function toCrudDrawerResult(value) {
|
|
|
551
691
|
? result
|
|
552
692
|
: undefined;
|
|
553
693
|
}
|
|
694
|
+
function stringOrUndefined$1(value) {
|
|
695
|
+
const text = String(value ?? '').trim();
|
|
696
|
+
return text || undefined;
|
|
697
|
+
}
|
|
554
698
|
|
|
555
699
|
const DOCUMENT_KIND = 'praxis.crud.editor';
|
|
556
700
|
const DOCUMENT_VERSION = 1;
|
|
@@ -888,7 +1032,7 @@ const PRAXIS_CRUD_RUNTIME_I18N_CONFIG = {
|
|
|
888
1032
|
'crud.emptyState.primaryAction': 'Configurar metadados',
|
|
889
1033
|
'crud.table.emptyState.initial.title': 'Sem registros em {label}',
|
|
890
1034
|
'crud.table.emptyState.initial.titleFallback': 'Nenhum registro disponível.',
|
|
891
|
-
'crud.table.emptyState.initial.
|
|
1035
|
+
'crud.table.emptyState.initial.descriptionWithToolbarAction': 'Use "{action}" na barra da tabela para adicionar o primeiro registro quando houver informações para cadastrar.',
|
|
892
1036
|
'crud.table.emptyState.filtered.title': 'Nenhum resultado encontrado.',
|
|
893
1037
|
'crud.table.emptyState.filtered.description': 'Revise os filtros ou ajuste o termo de busca.',
|
|
894
1038
|
'crud.preferences.resetSuccess': 'Overrides de CRUD redefinidos',
|
|
@@ -896,7 +1040,12 @@ const PRAXIS_CRUD_RUNTIME_I18N_CONFIG = {
|
|
|
896
1040
|
'crud.actions.view': 'Ver',
|
|
897
1041
|
'crud.actions.edit': 'Editar',
|
|
898
1042
|
'crud.actions.delete': 'Excluir',
|
|
1043
|
+
'crud.actions.default': 'ação',
|
|
1044
|
+
'crud.actions.opening': 'Abrindo {action}…',
|
|
1045
|
+
'crud.surface.openFailed': 'Não foi possível abrir {title}. Tente novamente ou confirme se esse recurso continua disponível.',
|
|
899
1046
|
'crud.dialog.recordContextLabel': 'Registro em edição',
|
|
1047
|
+
'crud.dialog.viewRecordContextLabel': 'Registro em consulta',
|
|
1048
|
+
'crud.dialog.relatedContextLabel': 'Vinculado a',
|
|
900
1049
|
'crud.delete.confirmMessage': 'Esta ação não pode ser desfeita. Deseja continuar?',
|
|
901
1050
|
'crud.delete.cancel': 'Cancelar',
|
|
902
1051
|
},
|
|
@@ -906,7 +1055,7 @@ const PRAXIS_CRUD_RUNTIME_I18N_CONFIG = {
|
|
|
906
1055
|
'crud.emptyState.primaryAction': 'Configure metadata',
|
|
907
1056
|
'crud.table.emptyState.initial.title': 'No records in {label}',
|
|
908
1057
|
'crud.table.emptyState.initial.titleFallback': 'No records available.',
|
|
909
|
-
'crud.table.emptyState.initial.
|
|
1058
|
+
'crud.table.emptyState.initial.descriptionWithToolbarAction': 'Use "{action}" in the table toolbar to add the first record when there is information to register.',
|
|
910
1059
|
'crud.table.emptyState.filtered.title': 'No results found.',
|
|
911
1060
|
'crud.table.emptyState.filtered.description': 'Review the filters or adjust the search term.',
|
|
912
1061
|
'crud.preferences.resetSuccess': 'CRUD overrides reset',
|
|
@@ -914,7 +1063,12 @@ const PRAXIS_CRUD_RUNTIME_I18N_CONFIG = {
|
|
|
914
1063
|
'crud.actions.view': 'View',
|
|
915
1064
|
'crud.actions.edit': 'Edit',
|
|
916
1065
|
'crud.actions.delete': 'Delete',
|
|
1066
|
+
'crud.actions.default': 'action',
|
|
1067
|
+
'crud.actions.opening': 'Opening {action}…',
|
|
1068
|
+
'crud.surface.openFailed': 'Could not open {title}. Try again or confirm that this resource is still available.',
|
|
917
1069
|
'crud.dialog.recordContextLabel': 'Record being edited',
|
|
1070
|
+
'crud.dialog.viewRecordContextLabel': 'Record being viewed',
|
|
1071
|
+
'crud.dialog.relatedContextLabel': 'Related to',
|
|
918
1072
|
'crud.delete.confirmMessage': 'This action cannot be undone. Do you want to continue?',
|
|
919
1073
|
'crud.delete.cancel': 'Cancel',
|
|
920
1074
|
},
|
|
@@ -3643,6 +3797,8 @@ class PraxisCrudComponent {
|
|
|
3643
3797
|
metadata;
|
|
3644
3798
|
crudId;
|
|
3645
3799
|
componentInstanceId;
|
|
3800
|
+
/** Controls whether the CRUD's internal table trusts host input or stored user configuration. */
|
|
3801
|
+
tableConfigPersistenceStrategy = 'local-first';
|
|
3646
3802
|
context;
|
|
3647
3803
|
enableCustomization = false;
|
|
3648
3804
|
/** Capability publica exigida para authoring governado do CRUD e da tabela interna. */
|
|
@@ -3654,6 +3810,8 @@ class PraxisCrudComponent {
|
|
|
3654
3810
|
afterDelete = new EventEmitter();
|
|
3655
3811
|
error = new EventEmitter();
|
|
3656
3812
|
rowClick = new EventEmitter();
|
|
3813
|
+
/** Emits the canonical table row-action envelope before the CRUD handles its surface/action. */
|
|
3814
|
+
rowAction = new EventEmitter();
|
|
3657
3815
|
selectionChange = new EventEmitter();
|
|
3658
3816
|
tableRuntimeConfigChange = new EventEmitter();
|
|
3659
3817
|
crudAuthoringDocumentApplied = new EventEmitter();
|
|
@@ -3664,6 +3822,7 @@ class PraxisCrudComponent {
|
|
|
3664
3822
|
tableQueryContext = null;
|
|
3665
3823
|
tableFilterCriteria = {};
|
|
3666
3824
|
tableCrudContext;
|
|
3825
|
+
openingActionLabel = signal(null, ...(ngDevMode ? [{ debugName: "openingActionLabel" }] : /* istanbul ignore next */ []));
|
|
3667
3826
|
launcher = inject(CrudLauncherService);
|
|
3668
3827
|
http = inject(HttpClient);
|
|
3669
3828
|
destroyRef = inject(DestroyRef);
|
|
@@ -3723,6 +3882,10 @@ class PraxisCrudComponent {
|
|
|
3723
3882
|
collectionCapabilitiesRequestSeq = 0;
|
|
3724
3883
|
currentAuthoringDocument;
|
|
3725
3884
|
selectedRow = null;
|
|
3885
|
+
onTableRowAction(event) {
|
|
3886
|
+
this.rowAction.emit(event);
|
|
3887
|
+
void this.onAction(event.action || '', event.row, event);
|
|
3888
|
+
}
|
|
3726
3889
|
getResourceDiscovery() {
|
|
3727
3890
|
const assigned = this.resourceDiscovery;
|
|
3728
3891
|
return assigned ?? (this.resourceDiscoveryInstance ??= this.injector.get(ResourceDiscoveryService));
|
|
@@ -3815,8 +3978,11 @@ class PraxisCrudComponent {
|
|
|
3815
3978
|
}
|
|
3816
3979
|
}
|
|
3817
3980
|
async onAction(action, row, runtimeEvent) {
|
|
3981
|
+
if (this.openingActionLabel()) {
|
|
3982
|
+
return;
|
|
3983
|
+
}
|
|
3984
|
+
this.openingActionLabel.set(this.resolveOpeningActionLabel(action, runtimeEvent));
|
|
3818
3985
|
try {
|
|
3819
|
-
document.activeElement?.blur();
|
|
3820
3986
|
const normalizedAction = this.normalizeCrudActionName(action);
|
|
3821
3987
|
const contextualRow = row ?? this.resolveSelectedRowForAction(normalizedAction);
|
|
3822
3988
|
let actionMeta = this.resolvedMetadata.actions?.find((candidate) => this.normalizeCrudActionName(candidate.action) === normalizedAction);
|
|
@@ -3842,6 +4008,7 @@ class PraxisCrudComponent {
|
|
|
3842
4008
|
action: this.normalizeCrudActionName(actionMeta?.action ?? normalizedAction),
|
|
3843
4009
|
};
|
|
3844
4010
|
const resourceIdentity = runtimeEvent?.resourceIdentity ?? null;
|
|
4011
|
+
const contextIdentity = this.resolveTaskScopeIdentity();
|
|
3845
4012
|
const handledByDuplicateDraft = await this.tryHandleCanonicalDuplicateDraftAction(effectiveAction, contextualRow);
|
|
3846
4013
|
if (handledByDuplicateDraft) {
|
|
3847
4014
|
return;
|
|
@@ -3890,6 +4057,9 @@ class PraxisCrudComponent {
|
|
|
3890
4057
|
: this.collectionCapabilities,
|
|
3891
4058
|
links: this.resolveCrudRuntimeLinks(effectiveAction.action, contextualRow),
|
|
3892
4059
|
resourceIdentity,
|
|
4060
|
+
contextIdentity,
|
|
4061
|
+
focusOrigin: runtimeEvent?.focusOrigin ?? null,
|
|
4062
|
+
surfaceRuntime: this.resolveSurfaceRuntime(),
|
|
3893
4063
|
});
|
|
3894
4064
|
this.afterOpen.emit({ mode, action: effectiveAction.action });
|
|
3895
4065
|
if (mode === 'drawer') {
|
|
@@ -3920,7 +4090,32 @@ class PraxisCrudComponent {
|
|
|
3920
4090
|
}
|
|
3921
4091
|
catch (err) {
|
|
3922
4092
|
this.error.emit(err);
|
|
4093
|
+
this.notifySurfaceNavigationFailure(err);
|
|
4094
|
+
}
|
|
4095
|
+
finally {
|
|
4096
|
+
this.openingActionLabel.set(null);
|
|
4097
|
+
}
|
|
4098
|
+
}
|
|
4099
|
+
resolveTaskScopeIdentity() {
|
|
4100
|
+
const operation = normalizeSurfaceOperationContext(this.context?.['operation']);
|
|
4101
|
+
return operation?.taskScope?.identity ?? null;
|
|
4102
|
+
}
|
|
4103
|
+
getOpeningActionStatus(actionLabel) {
|
|
4104
|
+
return this.txWithParams('crud.actions.opening', 'Abrindo {action}…', { action: actionLabel });
|
|
4105
|
+
}
|
|
4106
|
+
resolveOpeningActionLabel(action, runtimeEvent) {
|
|
4107
|
+
const configuredLabel = String(runtimeEvent?.actionConfig?.label || '').trim();
|
|
4108
|
+
if (configuredLabel) {
|
|
4109
|
+
return configuredLabel;
|
|
4110
|
+
}
|
|
4111
|
+
const normalized = this.normalizeCrudActionName(action);
|
|
4112
|
+
if (normalized === 'create'
|
|
4113
|
+
|| normalized === 'view'
|
|
4114
|
+
|| normalized === 'edit'
|
|
4115
|
+
|| normalized === 'delete') {
|
|
4116
|
+
return this.getCrudActionLabel(normalized);
|
|
3923
4117
|
}
|
|
4118
|
+
return String(action || '').trim() || this.tx('crud.actions.default', 'ação');
|
|
3924
4119
|
}
|
|
3925
4120
|
hasExplicitOpenBinding(action) {
|
|
3926
4121
|
const mode = action.openMode;
|
|
@@ -4132,6 +4327,7 @@ class PraxisCrudComponent {
|
|
|
4132
4327
|
}, {
|
|
4133
4328
|
capabilities: this.collectionCapabilities,
|
|
4134
4329
|
links: this.tableCollectionLinks,
|
|
4330
|
+
surfaceRuntime: this.resolveSurfaceRuntime(),
|
|
4135
4331
|
});
|
|
4136
4332
|
this.afterOpen.emit({ mode, action: normalizedAction });
|
|
4137
4333
|
if (mode !== 'drawer' && ref) {
|
|
@@ -4151,6 +4347,14 @@ class PraxisCrudComponent {
|
|
|
4151
4347
|
}
|
|
4152
4348
|
this.table.refetch();
|
|
4153
4349
|
}
|
|
4350
|
+
resolveSurfaceRuntime() {
|
|
4351
|
+
const candidate = this.context?.['surfaceRuntime'];
|
|
4352
|
+
if (!candidate || typeof candidate !== 'object') {
|
|
4353
|
+
return null;
|
|
4354
|
+
}
|
|
4355
|
+
const runtime = candidate;
|
|
4356
|
+
return typeof runtime.push === 'function' ? runtime : null;
|
|
4357
|
+
}
|
|
4154
4358
|
tryRefreshMaterializedLocalData() {
|
|
4155
4359
|
const readUrl = this.resolveMaterializedReadUrl();
|
|
4156
4360
|
if (!readUrl || this.resolveResourcePath(this.resolvedMetadata)) {
|
|
@@ -4316,6 +4520,17 @@ class PraxisCrudComponent {
|
|
|
4316
4520
|
const first = Array.isArray(event?.selectedRows) ? event.selectedRows[0] : null;
|
|
4317
4521
|
return this.isRecord(first) ? first : null;
|
|
4318
4522
|
}
|
|
4523
|
+
/**
|
|
4524
|
+
* Keeps the DOM trigger exclusively in transient runtime context. Discovery
|
|
4525
|
+
* payloads remain JSON-safe and can still be authored, inspected and
|
|
4526
|
+
* persisted independently from the browser that opened the surface.
|
|
4527
|
+
*/
|
|
4528
|
+
resolveRuntimeFocusOrigin(event) {
|
|
4529
|
+
const focusOrigin = event?.focusOrigin;
|
|
4530
|
+
return focusOrigin && typeof focusOrigin.focus === 'function'
|
|
4531
|
+
? focusOrigin
|
|
4532
|
+
: undefined;
|
|
4533
|
+
}
|
|
4319
4534
|
extractDiscoveryActionConfig(event) {
|
|
4320
4535
|
const config = event?.actionConfig;
|
|
4321
4536
|
return this.isRecord(config) && typeof config['resourceKey'] === 'string'
|
|
@@ -4335,9 +4550,6 @@ class PraxisCrudComponent {
|
|
|
4335
4550
|
return false;
|
|
4336
4551
|
}
|
|
4337
4552
|
const providedSurface = this.resolveProvidedSurface(normalizedAction, this.extractDiscoveryActionConfig(runtimeEvent));
|
|
4338
|
-
if (!providedSurface && !this.isDiscoveryManagedCrudAction(normalizedAction)) {
|
|
4339
|
-
return false;
|
|
4340
|
-
}
|
|
4341
4553
|
const catalog = providedSurface
|
|
4342
4554
|
? null
|
|
4343
4555
|
: await this.resolveDiscoveredSurfaceCatalog(normalizedAction, row);
|
|
@@ -4358,11 +4570,18 @@ class PraxisCrudComponent {
|
|
|
4358
4570
|
endpointKey: this.resolvedMetadata?.resource?.endpointKey,
|
|
4359
4571
|
apiUrlEntry: this.resolveDiscoveryApiEntry(),
|
|
4360
4572
|
group: catalog?.group ?? null,
|
|
4573
|
+
presentation: 'drawer',
|
|
4574
|
+
title: surface.title,
|
|
4575
|
+
subtitle: surface.description ?? undefined,
|
|
4576
|
+
parentIdentity: runtimeEvent?.resourceIdentity ?? null,
|
|
4361
4577
|
});
|
|
4362
4578
|
}
|
|
4363
|
-
catch {
|
|
4364
|
-
|
|
4579
|
+
catch (error) {
|
|
4580
|
+
this.error.emit(error);
|
|
4581
|
+
this.snack.open(this.txWithParams('crud.surface.openFailed', 'Não foi possível abrir {title}. Tente novamente ou confirme se esse recurso continua disponível.', { title: surface.title || surface.id }), undefined, { duration: 4500 });
|
|
4582
|
+
return true;
|
|
4365
4583
|
}
|
|
4584
|
+
const focusOrigin = this.resolveRuntimeFocusOrigin(runtimeEvent);
|
|
4366
4585
|
let openPromise;
|
|
4367
4586
|
try {
|
|
4368
4587
|
const surfaceContext = {
|
|
@@ -4381,23 +4600,29 @@ class PraxisCrudComponent {
|
|
|
4381
4600
|
action: normalizedAction,
|
|
4382
4601
|
resourcePath,
|
|
4383
4602
|
},
|
|
4603
|
+
...(focusOrigin ? { focusOrigin } : {}),
|
|
4384
4604
|
},
|
|
4385
4605
|
};
|
|
4386
4606
|
const handledInline = await this.surfaceOutlets.tryActivate(payload, surfaceContext);
|
|
4387
4607
|
openPromise = handledInline ? Promise.resolve(undefined) : Promise.resolve(this.surfaceService.open(payload, surfaceContext));
|
|
4388
4608
|
}
|
|
4389
|
-
catch {
|
|
4609
|
+
catch (error) {
|
|
4610
|
+
if (this.notifySurfaceNavigationFailure(error)) {
|
|
4611
|
+
this.error.emit(error);
|
|
4612
|
+
return true;
|
|
4613
|
+
}
|
|
4390
4614
|
return false;
|
|
4391
4615
|
}
|
|
4392
|
-
this.afterOpen.emit({
|
|
4393
|
-
mode: this.mapSurfacePresentationToCrudMode(payload?.presentation),
|
|
4394
|
-
action,
|
|
4395
|
-
});
|
|
4396
4616
|
try {
|
|
4397
4617
|
this.bindDiscoveredSurfaceLifecycle(await openPromise);
|
|
4618
|
+
this.afterOpen.emit({
|
|
4619
|
+
mode: this.mapSurfacePresentationToCrudMode(payload?.presentation),
|
|
4620
|
+
action,
|
|
4621
|
+
});
|
|
4398
4622
|
}
|
|
4399
4623
|
catch (err) {
|
|
4400
4624
|
this.error.emit(err);
|
|
4625
|
+
this.notifySurfaceNavigationFailure(err);
|
|
4401
4626
|
}
|
|
4402
4627
|
return true;
|
|
4403
4628
|
}
|
|
@@ -4437,17 +4662,37 @@ class PraxisCrudComponent {
|
|
|
4437
4662
|
}
|
|
4438
4663
|
let payload;
|
|
4439
4664
|
try {
|
|
4665
|
+
const resourceId = this.resolveRowResourceId(row);
|
|
4440
4666
|
payload = this.getActionOpenAdapter().toPayload(discoveredAction, {
|
|
4441
4667
|
resourcePath,
|
|
4442
|
-
resourceId
|
|
4668
|
+
resourceId,
|
|
4443
4669
|
endpointKey: this.resolvedMetadata?.resource?.endpointKey,
|
|
4444
4670
|
apiUrlEntry: this.resolveDiscoveryApiEntry(),
|
|
4445
4671
|
group: catalog?.group ?? null,
|
|
4446
4672
|
});
|
|
4673
|
+
const resourceIdentity = runtimeEvent?.resourceIdentity ?? null;
|
|
4674
|
+
if (resourceId != null && resourceIdentity) {
|
|
4675
|
+
payload.context = {
|
|
4676
|
+
...(payload.context || {}),
|
|
4677
|
+
operation: {
|
|
4678
|
+
...(payload.context?.operation || {}),
|
|
4679
|
+
subject: {
|
|
4680
|
+
resourceKey: discoveredAction.resourceKey,
|
|
4681
|
+
resourceId,
|
|
4682
|
+
identity: resourceIdentity,
|
|
4683
|
+
},
|
|
4684
|
+
},
|
|
4685
|
+
};
|
|
4686
|
+
}
|
|
4447
4687
|
}
|
|
4448
|
-
catch {
|
|
4688
|
+
catch (error) {
|
|
4689
|
+
if (this.notifySurfaceNavigationFailure(error)) {
|
|
4690
|
+
this.error.emit(error);
|
|
4691
|
+
return true;
|
|
4692
|
+
}
|
|
4449
4693
|
return false;
|
|
4450
4694
|
}
|
|
4695
|
+
const focusOrigin = this.resolveRuntimeFocusOrigin(runtimeEvent);
|
|
4451
4696
|
let openPromise;
|
|
4452
4697
|
try {
|
|
4453
4698
|
openPromise = Promise.resolve(this.surfaceService.open(payload, {
|
|
@@ -4466,19 +4711,35 @@ class PraxisCrudComponent {
|
|
|
4466
4711
|
action: normalizedAction,
|
|
4467
4712
|
resourcePath,
|
|
4468
4713
|
},
|
|
4714
|
+
...(focusOrigin ? { focusOrigin } : {}),
|
|
4469
4715
|
},
|
|
4470
4716
|
}));
|
|
4471
4717
|
}
|
|
4472
|
-
catch {
|
|
4718
|
+
catch (error) {
|
|
4719
|
+
if (this.notifySurfaceNavigationFailure(error)) {
|
|
4720
|
+
this.error.emit(error);
|
|
4721
|
+
return true;
|
|
4722
|
+
}
|
|
4473
4723
|
return false;
|
|
4474
4724
|
}
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4725
|
+
try {
|
|
4726
|
+
this.handleDiscoveredSurfaceResult(await openPromise);
|
|
4727
|
+
this.afterOpen.emit({
|
|
4728
|
+
mode: this.mapSurfacePresentationToCrudMode(payload?.presentation),
|
|
4729
|
+
action,
|
|
4730
|
+
});
|
|
4731
|
+
}
|
|
4732
|
+
catch (err) {
|
|
4733
|
+
this.error.emit(err);
|
|
4734
|
+
this.notifySurfaceNavigationFailure(err);
|
|
4735
|
+
}
|
|
4736
|
+
return true;
|
|
4737
|
+
}
|
|
4738
|
+
notifySurfaceNavigationFailure(error) {
|
|
4739
|
+
if (!isSurfaceNavigationError(error)) {
|
|
4740
|
+
return false;
|
|
4741
|
+
}
|
|
4742
|
+
this.snack.open(translateSurfaceNavigationRejected(this.i18n), undefined, { duration: 4500 });
|
|
4482
4743
|
return true;
|
|
4483
4744
|
}
|
|
4484
4745
|
async resolveDiscoveredActionCatalog(row) {
|
|
@@ -4521,13 +4782,13 @@ class PraxisCrudComponent {
|
|
|
4521
4782
|
}
|
|
4522
4783
|
return catalogCandidate;
|
|
4523
4784
|
}
|
|
4524
|
-
resolveProvidedSurface(
|
|
4785
|
+
resolveProvidedSurface(_action, candidate) {
|
|
4525
4786
|
if (!candidate || typeof candidate !== 'object') {
|
|
4526
4787
|
return null;
|
|
4527
4788
|
}
|
|
4528
4789
|
const catalogCandidate = candidate;
|
|
4529
4790
|
const normalizedId = String(catalogCandidate.id || '').trim().toLowerCase();
|
|
4530
|
-
if (!normalizedId
|
|
4791
|
+
if (!normalizedId) {
|
|
4531
4792
|
return null;
|
|
4532
4793
|
}
|
|
4533
4794
|
const surface = catalogCandidate;
|
|
@@ -4543,6 +4804,10 @@ class PraxisCrudComponent {
|
|
|
4543
4804
|
const candidates = surfaces
|
|
4544
4805
|
.filter((surface) => surface.availability?.allowed !== false)
|
|
4545
4806
|
.sort((left, right) => (left.order ?? 0) - (right.order ?? 0));
|
|
4807
|
+
const exact = candidates.find((surface) => String(surface.id || '').trim().toLowerCase() === action);
|
|
4808
|
+
if (exact) {
|
|
4809
|
+
return exact;
|
|
4810
|
+
}
|
|
4546
4811
|
const preferredIds = this.getPreferredSurfaceIdsForCrudAction(action);
|
|
4547
4812
|
const preferred = candidates.find((surface) => preferredIds.includes(String(surface.id || '').trim().toLowerCase()));
|
|
4548
4813
|
if (preferred) {
|
|
@@ -5004,7 +5269,8 @@ class PraxisCrudComponent {
|
|
|
5004
5269
|
return [];
|
|
5005
5270
|
}
|
|
5006
5271
|
return (capabilities.actions || [])
|
|
5007
|
-
.filter((action) => action.scope === 'COLLECTION'
|
|
5272
|
+
.filter((action) => action.scope === 'COLLECTION' &&
|
|
5273
|
+
this.isCatalogActionAvailable(capabilities, action.id))
|
|
5008
5274
|
.sort((left, right) => (left.order ?? 0) - (right.order ?? 0))
|
|
5009
5275
|
.map((action) => ({
|
|
5010
5276
|
action: action.id,
|
|
@@ -5025,28 +5291,49 @@ class PraxisCrudComponent {
|
|
|
5025
5291
|
appearance: 'outlined',
|
|
5026
5292
|
position: 'end',
|
|
5027
5293
|
action: action.id,
|
|
5028
|
-
disabled: action.
|
|
5294
|
+
disabled: !this.isCatalogActionAvailable(capabilities, action.id),
|
|
5029
5295
|
tooltip: action.description ||
|
|
5030
|
-
(action.
|
|
5031
|
-
? translateUnavailableWorkflowMessage(this.i18n, action.availability)
|
|
5296
|
+
(!this.isCatalogActionAvailable(capabilities, action.id)
|
|
5297
|
+
? translateUnavailableWorkflowMessage(this.i18n, capabilities.operations?.[action.id]?.availability || action.availability)
|
|
5032
5298
|
: undefined),
|
|
5033
5299
|
}));
|
|
5034
5300
|
}
|
|
5035
5301
|
supportsCreateCapability(snapshot) {
|
|
5036
|
-
|
|
5037
|
-
|
|
5302
|
+
const operationAvailability = this.resolveOperationAvailability(snapshot, 'create');
|
|
5303
|
+
if (operationAvailability !== null) {
|
|
5304
|
+
return operationAvailability;
|
|
5305
|
+
}
|
|
5306
|
+
return (this.hasCanonicalOperation(snapshot, 'create') ||
|
|
5038
5307
|
snapshot.surfaces.some((surface) => surface.scope === 'COLLECTION' &&
|
|
5039
5308
|
this.isWritableCrudSurface(surface) &&
|
|
5040
5309
|
surface.availability?.allowed !== false));
|
|
5041
5310
|
}
|
|
5042
5311
|
supportsViewCapability(snapshot) {
|
|
5043
|
-
|
|
5312
|
+
const operationAvailability = this.resolveOperationAvailability(snapshot, 'view');
|
|
5313
|
+
return operationAvailability ?? this.hasCanonicalOperation(snapshot, 'byId');
|
|
5044
5314
|
}
|
|
5045
5315
|
supportsEditCapability(snapshot) {
|
|
5046
|
-
|
|
5316
|
+
const operationAvailability = this.resolveOperationAvailability(snapshot, 'edit');
|
|
5317
|
+
return operationAvailability ?? this.hasCanonicalOperation(snapshot, 'update');
|
|
5047
5318
|
}
|
|
5048
5319
|
supportsDeleteCapability(snapshot) {
|
|
5049
|
-
|
|
5320
|
+
const operationAvailability = this.resolveOperationAvailability(snapshot, 'delete');
|
|
5321
|
+
return operationAvailability ?? this.hasCanonicalOperation(snapshot, 'delete');
|
|
5322
|
+
}
|
|
5323
|
+
resolveOperationAvailability(snapshot, operationId) {
|
|
5324
|
+
const operation = snapshot.operations?.[operationId];
|
|
5325
|
+
if (!operation) {
|
|
5326
|
+
return null;
|
|
5327
|
+
}
|
|
5328
|
+
return operation.supported === true && operation.availability?.allowed !== false;
|
|
5329
|
+
}
|
|
5330
|
+
isCatalogActionAvailable(snapshot, actionId) {
|
|
5331
|
+
const action = (snapshot.actions || []).find((candidate) => candidate.id === actionId);
|
|
5332
|
+
if (action?.availability?.allowed === false) {
|
|
5333
|
+
return false;
|
|
5334
|
+
}
|
|
5335
|
+
const operationAvailability = this.resolveOperationAvailability(snapshot, actionId);
|
|
5336
|
+
return operationAvailability ?? true;
|
|
5050
5337
|
}
|
|
5051
5338
|
hasCanonicalOperation(snapshot, operation) {
|
|
5052
5339
|
return snapshot.canonicalOperations?.[operation] === true;
|
|
@@ -5137,9 +5424,7 @@ class PraxisCrudComponent {
|
|
|
5137
5424
|
const title = resourceLabel
|
|
5138
5425
|
? this.txWithParams('crud.table.emptyState.initial.title', 'Sem registros em {label}', { label: resourceLabel })
|
|
5139
5426
|
: this.tx('crud.table.emptyState.initial.titleFallback', 'Nenhum registro disponível.');
|
|
5140
|
-
const actionId = String(createAction.action || createAction.id || 'create').trim() || 'create';
|
|
5141
5427
|
const actionLabel = String(createAction.label || this.getCrudActionLabel('create')).trim();
|
|
5142
|
-
const actionIcon = String(createAction.icon || 'add').trim();
|
|
5143
5428
|
config.behavior = {
|
|
5144
5429
|
...behavior,
|
|
5145
5430
|
emptyState: {
|
|
@@ -5147,15 +5432,10 @@ class PraxisCrudComponent {
|
|
|
5147
5432
|
contexts: {
|
|
5148
5433
|
initial: {
|
|
5149
5434
|
title,
|
|
5150
|
-
description: this.
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
action: actionId,
|
|
5155
|
-
icon: actionIcon,
|
|
5156
|
-
primary: true,
|
|
5157
|
-
},
|
|
5158
|
-
],
|
|
5435
|
+
description: this.txWithParams('crud.table.emptyState.initial.descriptionWithToolbarAction', 'Use "{action}" na barra da tabela para adicionar o primeiro registro quando houver informações para cadastrar.', { action: actionLabel }),
|
|
5436
|
+
// The create action is always materialized in the visible collection toolbar.
|
|
5437
|
+
// Keep the generated empty state instructional so it does not duplicate that CTA.
|
|
5438
|
+
actions: [],
|
|
5159
5439
|
},
|
|
5160
5440
|
filtered: {
|
|
5161
5441
|
title: this.tx('crud.table.emptyState.filtered.title', 'Nenhum resultado encontrado.'),
|
|
@@ -5187,42 +5467,51 @@ class PraxisCrudComponent {
|
|
|
5187
5467
|
return Object.entries(params).reduce((current, [name, value]) => current.split(`{${name}}`).join(value), text);
|
|
5188
5468
|
}
|
|
5189
5469
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisCrudComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5190
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisCrudComponent, isStandalone: true, selector: "praxis-crud", inputs: { metadata: "metadata", crudId: "crudId", componentInstanceId: "componentInstanceId", context: "context", enableCustomization: "enableCustomization", authoringCapability: "authoringCapability" }, outputs: { configureRequested: "configureRequested", afterOpen: "afterOpen", afterClose: "afterClose", afterSave: "afterSave", afterDelete: "afterDelete", error: "error", rowClick: "rowClick", selectionChange: "selectionChange", tableRuntimeConfigChange: "tableRuntimeConfigChange", crudAuthoringDocumentApplied: "crudAuthoringDocumentApplied", crudAuthoringDocumentSaved: "crudAuthoringDocumentSaved" }, providers: [
|
|
5470
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisCrudComponent, isStandalone: true, selector: "praxis-crud", inputs: { metadata: "metadata", crudId: "crudId", componentInstanceId: "componentInstanceId", tableConfigPersistenceStrategy: "tableConfigPersistenceStrategy", context: "context", enableCustomization: "enableCustomization", authoringCapability: "authoringCapability" }, outputs: { configureRequested: "configureRequested", afterOpen: "afterOpen", afterClose: "afterClose", afterSave: "afterSave", afterDelete: "afterDelete", error: "error", rowClick: "rowClick", rowAction: "rowAction", selectionChange: "selectionChange", tableRuntimeConfigChange: "tableRuntimeConfigChange", crudAuthoringDocumentApplied: "crudAuthoringDocumentApplied", crudAuthoringDocumentSaved: "crudAuthoringDocumentSaved" }, providers: [
|
|
5191
5471
|
providePraxisI18nConfig(RESOURCE_DISCOVERY_I18N_CONFIG),
|
|
5192
5472
|
providePraxisI18nConfig(PRAXIS_CRUD_RUNTIME_I18N_CONFIG),
|
|
5193
5473
|
], viewQueries: [{ propertyName: "table", first: true, predicate: PraxisTable, descendants: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
[filterCriteria]="tableFilterCriteria"
|
|
5201
|
-
[tableId]="crudId || 'default'"
|
|
5202
|
-
[crudContext]="tableCrudContext"
|
|
5203
|
-
[enableCustomization]="enableCustomization"
|
|
5204
|
-
[authoringCapability]="authoringCapability"
|
|
5205
|
-
(rowClick)="onTableRowClick($event)"
|
|
5206
|
-
(selectionChange)="onTableSelectionChange($event)"
|
|
5207
|
-
(rowAction)="onAction($event.action, $event.row, $event)"
|
|
5208
|
-
(toolbarAction)="onToolbarAction($event)"
|
|
5209
|
-
(bulkAction)="onBulkAction($event)"
|
|
5210
|
-
(collectionLinksChange)="onCollectionLinksChange($event)"
|
|
5211
|
-
(reset)="onResetPreferences()"
|
|
5212
|
-
(metadataChange)="onTableMetadataChange()"
|
|
5213
|
-
(loadingStateChange)="onTableLoadingStateChange($event)"
|
|
5214
|
-
></praxis-table>
|
|
5215
|
-
} @else {
|
|
5216
|
-
@if (isCustomizationAvailable()) {
|
|
5217
|
-
<praxis-empty-state-card
|
|
5218
|
-
icon="table_rows"
|
|
5219
|
-
[title]="getEmptyStateTitle()"
|
|
5220
|
-
[description]="getEmptyStateDescription()"
|
|
5221
|
-
[primaryAction]="{ label: getEmptyStatePrimaryAction(), icon: 'bolt', action: onConfigureRequested.bind(this) }"
|
|
5222
|
-
/>
|
|
5474
|
+
<div class="praxis-crud-runtime" [attr.aria-busy]="openingActionLabel() ? 'true' : null">
|
|
5475
|
+
@if (openingActionLabel(); as actionLabel) {
|
|
5476
|
+
<div class="praxis-crud-opening-status" role="status" aria-live="polite" data-testid="praxis-crud-opening-status">
|
|
5477
|
+
<span class="praxis-crud-opening-status__spinner" aria-hidden="true"></span>
|
|
5478
|
+
<span>{{ getOpeningActionStatus(actionLabel) }}</span>
|
|
5479
|
+
</div>
|
|
5223
5480
|
}
|
|
5224
|
-
|
|
5225
|
-
|
|
5481
|
+
@if (shouldRenderTable(resolvedMetadata)) {
|
|
5482
|
+
<praxis-table
|
|
5483
|
+
[config]="tableConfigForBinding"
|
|
5484
|
+
[resourcePath]="resolveResourcePath(resolvedMetadata)"
|
|
5485
|
+
[data]="resolveTableData(resolvedMetadata)"
|
|
5486
|
+
[queryContext]="tableQueryContext"
|
|
5487
|
+
[filterCriteria]="tableFilterCriteria"
|
|
5488
|
+
[tableId]="crudId || 'default'"
|
|
5489
|
+
[configPersistenceStrategy]="tableConfigPersistenceStrategy"
|
|
5490
|
+
[crudContext]="tableCrudContext"
|
|
5491
|
+
[enableCustomization]="enableCustomization"
|
|
5492
|
+
[authoringCapability]="authoringCapability"
|
|
5493
|
+
(rowClick)="onTableRowClick($event)"
|
|
5494
|
+
(selectionChange)="onTableSelectionChange($event)"
|
|
5495
|
+
(rowAction)="onTableRowAction($event)"
|
|
5496
|
+
(toolbarAction)="onToolbarAction($event)"
|
|
5497
|
+
(bulkAction)="onBulkAction($event)"
|
|
5498
|
+
(collectionLinksChange)="onCollectionLinksChange($event)"
|
|
5499
|
+
(reset)="onResetPreferences()"
|
|
5500
|
+
(metadataChange)="onTableMetadataChange()"
|
|
5501
|
+
(loadingStateChange)="onTableLoadingStateChange($event)"
|
|
5502
|
+
></praxis-table>
|
|
5503
|
+
} @else {
|
|
5504
|
+
@if (isCustomizationAvailable()) {
|
|
5505
|
+
<praxis-empty-state-card
|
|
5506
|
+
icon="table_rows"
|
|
5507
|
+
[title]="getEmptyStateTitle()"
|
|
5508
|
+
[description]="getEmptyStateDescription()"
|
|
5509
|
+
[primaryAction]="{ label: getEmptyStatePrimaryAction(), icon: 'bolt', action: onConfigureRequested.bind(this) }"
|
|
5510
|
+
/>
|
|
5511
|
+
}
|
|
5512
|
+
}
|
|
5513
|
+
</div>
|
|
5514
|
+
`, isInline: true, styles: [":host{display:block;width:100%;min-width:0;max-width:100%}.praxis-crud-runtime{position:relative;min-width:0}.praxis-crud-opening-status{position:absolute;z-index:6;top:.7rem;right:.7rem;display:inline-flex;align-items:center;gap:.55rem;min-height:2.25rem;max-width:min(24rem,calc(100% - 1.4rem));padding:.48rem .72rem;border:1px solid color-mix(in srgb,var(--md-sys-color-primary, #3f51b5) 32%,transparent);border-radius:999px;color:var(--md-sys-color-on-surface, currentColor);background:color-mix(in srgb,var(--md-sys-color-surface-container-high, #fff) 94%,transparent);box-shadow:0 10px 28px #0f172a29;font-size:.82rem;font-weight:650;line-height:1.25;pointer-events:none}.praxis-crud-opening-status__spinner{width:.95rem;height:.95rem;flex:0 0 auto;border:2px solid color-mix(in srgb,var(--md-sys-color-primary, #3f51b5) 26%,transparent);border-top-color:var(--md-sys-color-primary, #3f51b5);border-radius:50%;animation:praxis-crud-opening-spin .72s linear infinite}@keyframes praxis-crud-opening-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.praxis-crud-opening-status__spinner{animation:none;border-top-color:currentColor}}\n"], dependencies: [{ kind: "component", type: PraxisTable, selector: "praxis-table", inputs: ["config", "resourcePath", "data", "tableId", "componentInstanceId", "configPersistenceStrategy", "title", "subtitle", "icon", "autoDelete", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "crudContext", "filterCriteria", "queryContext", "aiContext", "aiAssistantVoiceInputMode", "aiAssistantVoiceLanguage", "horizontalScroll", "enableCustomization", "authoringCapability", "dense"], outputs: ["rowClick", "widgetEvent", "resourceEvent", "rowDoubleClick", "rowExpansionChange", "rowAction", "toolbarAction", "bulkAction", "exportAction", "columnReorder", "columnReorderAttempt", "columnResize", "beforeDelete", "afterDelete", "deleteError", "beforeBulkDelete", "afterBulkDelete", "bulkDeleteError", "schemaStatusChange", "configChange", "metadataChange", "loadingStateChange", "collectionLinksChange", "selectionChange"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone", "variant", "alignment", "density", "iconContainer"] }] });
|
|
5226
5515
|
}
|
|
5227
5516
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisCrudComponent, decorators: [{
|
|
5228
5517
|
type: Component,
|
|
@@ -5230,38 +5519,47 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
5230
5519
|
providePraxisI18nConfig(RESOURCE_DISCOVERY_I18N_CONFIG),
|
|
5231
5520
|
providePraxisI18nConfig(PRAXIS_CRUD_RUNTIME_I18N_CONFIG),
|
|
5232
5521
|
], template: `
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
[filterCriteria]="tableFilterCriteria"
|
|
5240
|
-
[tableId]="crudId || 'default'"
|
|
5241
|
-
[crudContext]="tableCrudContext"
|
|
5242
|
-
[enableCustomization]="enableCustomization"
|
|
5243
|
-
[authoringCapability]="authoringCapability"
|
|
5244
|
-
(rowClick)="onTableRowClick($event)"
|
|
5245
|
-
(selectionChange)="onTableSelectionChange($event)"
|
|
5246
|
-
(rowAction)="onAction($event.action, $event.row, $event)"
|
|
5247
|
-
(toolbarAction)="onToolbarAction($event)"
|
|
5248
|
-
(bulkAction)="onBulkAction($event)"
|
|
5249
|
-
(collectionLinksChange)="onCollectionLinksChange($event)"
|
|
5250
|
-
(reset)="onResetPreferences()"
|
|
5251
|
-
(metadataChange)="onTableMetadataChange()"
|
|
5252
|
-
(loadingStateChange)="onTableLoadingStateChange($event)"
|
|
5253
|
-
></praxis-table>
|
|
5254
|
-
} @else {
|
|
5255
|
-
@if (isCustomizationAvailable()) {
|
|
5256
|
-
<praxis-empty-state-card
|
|
5257
|
-
icon="table_rows"
|
|
5258
|
-
[title]="getEmptyStateTitle()"
|
|
5259
|
-
[description]="getEmptyStateDescription()"
|
|
5260
|
-
[primaryAction]="{ label: getEmptyStatePrimaryAction(), icon: 'bolt', action: onConfigureRequested.bind(this) }"
|
|
5261
|
-
/>
|
|
5522
|
+
<div class="praxis-crud-runtime" [attr.aria-busy]="openingActionLabel() ? 'true' : null">
|
|
5523
|
+
@if (openingActionLabel(); as actionLabel) {
|
|
5524
|
+
<div class="praxis-crud-opening-status" role="status" aria-live="polite" data-testid="praxis-crud-opening-status">
|
|
5525
|
+
<span class="praxis-crud-opening-status__spinner" aria-hidden="true"></span>
|
|
5526
|
+
<span>{{ getOpeningActionStatus(actionLabel) }}</span>
|
|
5527
|
+
</div>
|
|
5262
5528
|
}
|
|
5263
|
-
|
|
5264
|
-
|
|
5529
|
+
@if (shouldRenderTable(resolvedMetadata)) {
|
|
5530
|
+
<praxis-table
|
|
5531
|
+
[config]="tableConfigForBinding"
|
|
5532
|
+
[resourcePath]="resolveResourcePath(resolvedMetadata)"
|
|
5533
|
+
[data]="resolveTableData(resolvedMetadata)"
|
|
5534
|
+
[queryContext]="tableQueryContext"
|
|
5535
|
+
[filterCriteria]="tableFilterCriteria"
|
|
5536
|
+
[tableId]="crudId || 'default'"
|
|
5537
|
+
[configPersistenceStrategy]="tableConfigPersistenceStrategy"
|
|
5538
|
+
[crudContext]="tableCrudContext"
|
|
5539
|
+
[enableCustomization]="enableCustomization"
|
|
5540
|
+
[authoringCapability]="authoringCapability"
|
|
5541
|
+
(rowClick)="onTableRowClick($event)"
|
|
5542
|
+
(selectionChange)="onTableSelectionChange($event)"
|
|
5543
|
+
(rowAction)="onTableRowAction($event)"
|
|
5544
|
+
(toolbarAction)="onToolbarAction($event)"
|
|
5545
|
+
(bulkAction)="onBulkAction($event)"
|
|
5546
|
+
(collectionLinksChange)="onCollectionLinksChange($event)"
|
|
5547
|
+
(reset)="onResetPreferences()"
|
|
5548
|
+
(metadataChange)="onTableMetadataChange()"
|
|
5549
|
+
(loadingStateChange)="onTableLoadingStateChange($event)"
|
|
5550
|
+
></praxis-table>
|
|
5551
|
+
} @else {
|
|
5552
|
+
@if (isCustomizationAvailable()) {
|
|
5553
|
+
<praxis-empty-state-card
|
|
5554
|
+
icon="table_rows"
|
|
5555
|
+
[title]="getEmptyStateTitle()"
|
|
5556
|
+
[description]="getEmptyStateDescription()"
|
|
5557
|
+
[primaryAction]="{ label: getEmptyStatePrimaryAction(), icon: 'bolt', action: onConfigureRequested.bind(this) }"
|
|
5558
|
+
/>
|
|
5559
|
+
}
|
|
5560
|
+
}
|
|
5561
|
+
</div>
|
|
5562
|
+
`, styles: [":host{display:block;width:100%;min-width:0;max-width:100%}.praxis-crud-runtime{position:relative;min-width:0}.praxis-crud-opening-status{position:absolute;z-index:6;top:.7rem;right:.7rem;display:inline-flex;align-items:center;gap:.55rem;min-height:2.25rem;max-width:min(24rem,calc(100% - 1.4rem));padding:.48rem .72rem;border:1px solid color-mix(in srgb,var(--md-sys-color-primary, #3f51b5) 32%,transparent);border-radius:999px;color:var(--md-sys-color-on-surface, currentColor);background:color-mix(in srgb,var(--md-sys-color-surface-container-high, #fff) 94%,transparent);box-shadow:0 10px 28px #0f172a29;font-size:.82rem;font-weight:650;line-height:1.25;pointer-events:none}.praxis-crud-opening-status__spinner{width:.95rem;height:.95rem;flex:0 0 auto;border:2px solid color-mix(in srgb,var(--md-sys-color-primary, #3f51b5) 26%,transparent);border-top-color:var(--md-sys-color-primary, #3f51b5);border-radius:50%;animation:praxis-crud-opening-spin .72s linear infinite}@keyframes praxis-crud-opening-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.praxis-crud-opening-status__spinner{animation:none;border-top-color:currentColor}}\n"] }]
|
|
5265
5563
|
}], ctorParameters: () => [], propDecorators: { metadata: [{
|
|
5266
5564
|
type: Input,
|
|
5267
5565
|
args: [{ required: true }]
|
|
@@ -5270,6 +5568,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
5270
5568
|
args: [{ required: true }]
|
|
5271
5569
|
}], componentInstanceId: [{
|
|
5272
5570
|
type: Input
|
|
5571
|
+
}], tableConfigPersistenceStrategy: [{
|
|
5572
|
+
type: Input
|
|
5273
5573
|
}], context: [{
|
|
5274
5574
|
type: Input
|
|
5275
5575
|
}], enableCustomization: [{
|
|
@@ -5290,6 +5590,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
5290
5590
|
type: Output
|
|
5291
5591
|
}], rowClick: [{
|
|
5292
5592
|
type: Output
|
|
5593
|
+
}], rowAction: [{
|
|
5594
|
+
type: Output
|
|
5293
5595
|
}], selectionChange: [{
|
|
5294
5596
|
type: Output
|
|
5295
5597
|
}], tableRuntimeConfigChange: [{
|
|
@@ -5326,10 +5628,10 @@ function debugCrudHost(message, data) {
|
|
|
5326
5628
|
}
|
|
5327
5629
|
class DynamicFormDialogHostComponent {
|
|
5328
5630
|
dialogRef;
|
|
5329
|
-
data;
|
|
5330
5631
|
dialogService;
|
|
5331
5632
|
crud;
|
|
5332
5633
|
configStorage;
|
|
5634
|
+
surfaceRef;
|
|
5333
5635
|
formComp;
|
|
5334
5636
|
modal = {};
|
|
5335
5637
|
presentation = 'modal';
|
|
@@ -5339,12 +5641,14 @@ class DynamicFormDialogHostComponent {
|
|
|
5339
5641
|
rememberState = false;
|
|
5340
5642
|
stateKey;
|
|
5341
5643
|
backDefaults = {};
|
|
5644
|
+
terminalClose = false;
|
|
5342
5645
|
destroyRef = inject(DestroyRef);
|
|
5343
5646
|
i18n = inject(PraxisI18nService);
|
|
5344
5647
|
resourcePath;
|
|
5345
5648
|
resourceId;
|
|
5346
5649
|
initialValue;
|
|
5347
5650
|
resourceIdentity = null;
|
|
5651
|
+
contextIdentity = null;
|
|
5348
5652
|
schemaUrl;
|
|
5349
5653
|
submitUrl;
|
|
5350
5654
|
submitMethod;
|
|
@@ -5354,9 +5658,12 @@ class DynamicFormDialogHostComponent {
|
|
|
5354
5658
|
formConfig = {};
|
|
5355
5659
|
formActions;
|
|
5356
5660
|
formConfigPersistenceStrategy = 'input-first';
|
|
5661
|
+
fieldIconPolicy = 'presentation-only';
|
|
5357
5662
|
mode = 'create';
|
|
5358
5663
|
backConfig;
|
|
5359
5664
|
idField = 'id';
|
|
5665
|
+
isSurfaceFrame;
|
|
5666
|
+
data;
|
|
5360
5667
|
texts = {
|
|
5361
5668
|
title: 'Formulário',
|
|
5362
5669
|
close: 'Fechar',
|
|
@@ -5364,19 +5671,32 @@ class DynamicFormDialogHostComponent {
|
|
|
5364
5671
|
maximizeLabel: 'Maximizar',
|
|
5365
5672
|
restoreLabel: 'Restaurar',
|
|
5366
5673
|
recordContextLabel: translateCrudRuntimeText(this.i18n, 'crud.dialog.recordContextLabel', 'Registro em edição'),
|
|
5674
|
+
viewRecordContextLabel: translateCrudRuntimeText(this.i18n, 'crud.dialog.viewRecordContextLabel', 'Registro em consulta'),
|
|
5675
|
+
relatedContextLabel: translateCrudRuntimeText(this.i18n, 'crud.dialog.relatedContextLabel', 'Vinculado a'),
|
|
5367
5676
|
discardTitle: 'Descartar alterações?',
|
|
5368
5677
|
discardMessage: 'Você tem alterações não salvas. Deseja fechar assim mesmo?',
|
|
5369
5678
|
discardConfirm: 'Descartar',
|
|
5370
5679
|
discardCancel: 'Cancelar',
|
|
5371
5680
|
};
|
|
5372
|
-
constructor(dialogRef,
|
|
5681
|
+
constructor(dialogRef, dialogData, dialogService, crud, configStorage, surfaceRef = null, surfaceContentData = null) {
|
|
5373
5682
|
this.dialogRef = dialogRef;
|
|
5374
|
-
this.data = data;
|
|
5375
5683
|
this.dialogService = dialogService;
|
|
5376
5684
|
this.crud = crud;
|
|
5377
5685
|
this.configStorage = configStorage;
|
|
5378
|
-
this.
|
|
5379
|
-
this.
|
|
5686
|
+
this.surfaceRef = surfaceRef;
|
|
5687
|
+
this.data = dialogData ?? surfaceContentData?.['data'] ?? {};
|
|
5688
|
+
this.isSurfaceFrame = !!this.surfaceRef;
|
|
5689
|
+
if (!this.dialogRef && !this.surfaceRef) {
|
|
5690
|
+
throw new Error('DynamicFormDialogHostComponent requires a MatDialogRef or an active SurfaceDrawerRef.');
|
|
5691
|
+
}
|
|
5692
|
+
if (this.dialogRef) {
|
|
5693
|
+
this.dialogRef.disableClose = true;
|
|
5694
|
+
}
|
|
5695
|
+
this.presentation = this.isSurfaceFrame
|
|
5696
|
+
? 'surface-frame'
|
|
5697
|
+
: this.data.presentation === 'drawer'
|
|
5698
|
+
? 'drawer'
|
|
5699
|
+
: 'modal';
|
|
5380
5700
|
// i18n
|
|
5381
5701
|
this.texts = {
|
|
5382
5702
|
...this.texts,
|
|
@@ -5414,9 +5734,12 @@ class DynamicFormDialogHostComponent {
|
|
|
5414
5734
|
this.apiUrlEntry = this.data.inputs?.['apiUrlEntry'] ?? null;
|
|
5415
5735
|
const act = this.data.action?.action;
|
|
5416
5736
|
this.mode = act === 'edit' ? 'edit' : act === 'view' ? 'view' : 'create';
|
|
5417
|
-
this.resourceIdentity = this.mode
|
|
5737
|
+
this.resourceIdentity = this.mode !== 'create' && this.data.resourceIdentity
|
|
5418
5738
|
? this.data.resourceIdentity
|
|
5419
5739
|
: null;
|
|
5740
|
+
this.contextIdentity = this.data.contextIdentity
|
|
5741
|
+
? this.data.contextIdentity
|
|
5742
|
+
: null;
|
|
5420
5743
|
this.formConfig = this.resolveFormConfig();
|
|
5421
5744
|
this.layoutPolicy = this.resolveLayoutPolicy();
|
|
5422
5745
|
this.formActions = this.resolveFormActions();
|
|
@@ -5433,14 +5756,14 @@ class DynamicFormDialogHostComponent {
|
|
|
5433
5756
|
backConfig: this.backConfig,
|
|
5434
5757
|
});
|
|
5435
5758
|
// Esc
|
|
5436
|
-
if (!this.modal.disableCloseOnEsc) {
|
|
5759
|
+
if (this.dialogRef && !this.modal.disableCloseOnEsc) {
|
|
5437
5760
|
this.dialogRef
|
|
5438
5761
|
.keydownEvents()
|
|
5439
5762
|
.pipe(filter((e) => e.key === 'Escape'), takeUntilDestroyed(this.destroyRef))
|
|
5440
5763
|
.subscribe(() => this.onCancel());
|
|
5441
5764
|
}
|
|
5442
5765
|
// Backdrop
|
|
5443
|
-
if (!this.modal.disableCloseOnBackdrop) {
|
|
5766
|
+
if (this.dialogRef && !this.modal.disableCloseOnBackdrop) {
|
|
5444
5767
|
this.dialogRef
|
|
5445
5768
|
.backdropClick()
|
|
5446
5769
|
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
@@ -5448,10 +5771,36 @@ class DynamicFormDialogHostComponent {
|
|
|
5448
5771
|
}
|
|
5449
5772
|
// Salvar estado ao fechar, se aplicável
|
|
5450
5773
|
this.dialogRef
|
|
5451
|
-
|
|
5774
|
+
?.afterClosed()
|
|
5452
5775
|
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
5453
5776
|
.subscribe(() => this.saveState());
|
|
5454
5777
|
}
|
|
5778
|
+
get resourceIdentityLabel() {
|
|
5779
|
+
return this.mode === 'view'
|
|
5780
|
+
? this.texts['viewRecordContextLabel']
|
|
5781
|
+
: this.texts['recordContextLabel'];
|
|
5782
|
+
}
|
|
5783
|
+
get showContextIdentity() {
|
|
5784
|
+
return !!this.contextIdentity && !this.sameIdentity(this.contextIdentity, this.resourceIdentity);
|
|
5785
|
+
}
|
|
5786
|
+
sameIdentity(left, right) {
|
|
5787
|
+
if (!left || !right)
|
|
5788
|
+
return false;
|
|
5789
|
+
const leftKey = left.key;
|
|
5790
|
+
const rightKey = right.key;
|
|
5791
|
+
const leftLabel = this.identityLabel(left);
|
|
5792
|
+
const rightLabel = this.identityLabel(right);
|
|
5793
|
+
if (leftKey && rightKey) {
|
|
5794
|
+
return leftKey.field === rightKey.field
|
|
5795
|
+
&& String(leftKey.value) === String(rightKey.value)
|
|
5796
|
+
&& !!leftLabel
|
|
5797
|
+
&& leftLabel === rightLabel;
|
|
5798
|
+
}
|
|
5799
|
+
return !!leftLabel && leftLabel === rightLabel;
|
|
5800
|
+
}
|
|
5801
|
+
identityLabel(identity) {
|
|
5802
|
+
return String(identity.displayLabel || identity.title?.value || '').trim();
|
|
5803
|
+
}
|
|
5455
5804
|
extractInitialValue(inputs) {
|
|
5456
5805
|
if (!inputs || typeof inputs !== 'object') {
|
|
5457
5806
|
return null;
|
|
@@ -5488,6 +5837,22 @@ class DynamicFormDialogHostComponent {
|
|
|
5488
5837
|
deriveModeTitle(this.mode, this.resolveResourceTitle()) ||
|
|
5489
5838
|
this.texts.title;
|
|
5490
5839
|
}
|
|
5840
|
+
get dialogSubtitle() {
|
|
5841
|
+
const action = this.data.action ?? {};
|
|
5842
|
+
const form = this.formConfig;
|
|
5843
|
+
const resource = this.data.metadata?.resource ?? {};
|
|
5844
|
+
const table = this.data.metadata?.table ?? {};
|
|
5845
|
+
return stringOrUndefined(action.description ??
|
|
5846
|
+
action.tooltip ??
|
|
5847
|
+
form['description'] ??
|
|
5848
|
+
resource.description ??
|
|
5849
|
+
table.subtitle ??
|
|
5850
|
+
table.description);
|
|
5851
|
+
}
|
|
5852
|
+
get dialogIcon() {
|
|
5853
|
+
const action = this.data.action ?? {};
|
|
5854
|
+
return stringOrUndefined(action.icon ?? this.modal.titleIcon ?? this.modal.icon);
|
|
5855
|
+
}
|
|
5491
5856
|
resolveActionDialogTitle() {
|
|
5492
5857
|
const actionLabel = stringOrUndefined(this.data.action?.label);
|
|
5493
5858
|
if (!actionLabel) {
|
|
@@ -5518,7 +5883,7 @@ class DynamicFormDialogHostComponent {
|
|
|
5518
5883
|
if (this.mode === 'view') {
|
|
5519
5884
|
return undefined;
|
|
5520
5885
|
}
|
|
5521
|
-
if (this.formConfig?.actions) {
|
|
5886
|
+
if (this.formConfig?.actions?.submit) {
|
|
5522
5887
|
return undefined;
|
|
5523
5888
|
}
|
|
5524
5889
|
const submitLabel = this.resolveSubmitLabel();
|
|
@@ -5590,6 +5955,13 @@ class DynamicFormDialogHostComponent {
|
|
|
5590
5955
|
});
|
|
5591
5956
|
}
|
|
5592
5957
|
}
|
|
5958
|
+
ngAfterViewInit() {
|
|
5959
|
+
if (!this.surfaceRef?.setCanLeave) {
|
|
5960
|
+
return;
|
|
5961
|
+
}
|
|
5962
|
+
this.surfaceRef.updateTitle?.(this.dialogTitle);
|
|
5963
|
+
this.surfaceRef.setCanLeave(() => this.canLeaveSurfaceFrame());
|
|
5964
|
+
}
|
|
5593
5965
|
onSave(result) {
|
|
5594
5966
|
const stage = getFormSubmitStage(result);
|
|
5595
5967
|
if (stage === 'before' || stage === 'error') {
|
|
@@ -5599,9 +5971,29 @@ class DynamicFormDialogHostComponent {
|
|
|
5599
5971
|
this.saveState();
|
|
5600
5972
|
return;
|
|
5601
5973
|
}
|
|
5602
|
-
this.
|
|
5974
|
+
this.terminalClose = true;
|
|
5975
|
+
this.formComp?.form.markAsPristine();
|
|
5976
|
+
if (this.surfaceRef) {
|
|
5977
|
+
this.surfaceRef.emitResult?.({ type: 'save', data: result });
|
|
5978
|
+
this.surfaceRef.close?.({ type: 'save', data: result });
|
|
5979
|
+
}
|
|
5980
|
+
else {
|
|
5981
|
+
this.dialogRef?.close({ type: 'save', data: result });
|
|
5982
|
+
}
|
|
5603
5983
|
}
|
|
5604
5984
|
onCancel() {
|
|
5985
|
+
if (this.surfaceRef) {
|
|
5986
|
+
void this.canLeaveSurfaceFrame().then((allowed) => {
|
|
5987
|
+
if (!allowed)
|
|
5988
|
+
return;
|
|
5989
|
+
// `close()` on a nested frame consults the registered guard again.
|
|
5990
|
+
// Mark this user-confirmed transition so both root and nested frames
|
|
5991
|
+
// share one prompt and one close result.
|
|
5992
|
+
this.terminalClose = true;
|
|
5993
|
+
this.surfaceRef?.close?.({ type: 'close' });
|
|
5994
|
+
});
|
|
5995
|
+
return;
|
|
5996
|
+
}
|
|
5605
5997
|
const dirty = this.formComp?.form.dirty;
|
|
5606
5998
|
const backCfg = (this.data.action?.back || this.data.metadata?.defaults?.back) || {};
|
|
5607
5999
|
const confirm = backCfg.confirmOnDirty ?? true;
|
|
@@ -5619,12 +6011,32 @@ class DynamicFormDialogHostComponent {
|
|
|
5619
6011
|
ref
|
|
5620
6012
|
.afterClosed()
|
|
5621
6013
|
.pipe(filter((confirmed) => !!confirmed), takeUntilDestroyed(this.destroyRef))
|
|
5622
|
-
.subscribe(() => this.dialogRef
|
|
6014
|
+
.subscribe(() => this.dialogRef?.close({ type: 'close' }));
|
|
5623
6015
|
}
|
|
5624
6016
|
else {
|
|
5625
|
-
this.dialogRef
|
|
6017
|
+
this.dialogRef?.close({ type: 'close' });
|
|
5626
6018
|
}
|
|
5627
6019
|
}
|
|
6020
|
+
async canLeaveSurfaceFrame() {
|
|
6021
|
+
if (this.terminalClose || !this.formComp?.form.dirty) {
|
|
6022
|
+
return true;
|
|
6023
|
+
}
|
|
6024
|
+
const backCfg = (this.data.action?.back || this.data.metadata?.defaults?.back) || {};
|
|
6025
|
+
if (backCfg.confirmOnDirty === false) {
|
|
6026
|
+
return true;
|
|
6027
|
+
}
|
|
6028
|
+
const ref = this.dialogService.open(ConfirmDialogComponent, {
|
|
6029
|
+
data: {
|
|
6030
|
+
title: this.texts.discardTitle,
|
|
6031
|
+
message: this.texts.discardMessage,
|
|
6032
|
+
confirmText: this.texts.discardConfirm,
|
|
6033
|
+
cancelText: this.texts.discardCancel,
|
|
6034
|
+
type: 'warning',
|
|
6035
|
+
},
|
|
6036
|
+
autoFocus: false,
|
|
6037
|
+
});
|
|
6038
|
+
return !!(await firstValueFrom(ref.afterClosed().pipe(take(1))));
|
|
6039
|
+
}
|
|
5628
6040
|
toggleMaximize(initial = false) {
|
|
5629
6041
|
const pane = this.resolveOverlayPane();
|
|
5630
6042
|
if (!initial && !this.maximized && pane) {
|
|
@@ -5638,7 +6050,12 @@ class DynamicFormDialogHostComponent {
|
|
|
5638
6050
|
const height = this.maximized
|
|
5639
6051
|
? `calc(100dvh - ${2 * (gap ?? 0)}px)`
|
|
5640
6052
|
: this.initialSize.height;
|
|
5641
|
-
this.
|
|
6053
|
+
if (this.surfaceRef) {
|
|
6054
|
+
this.surfaceRef.updateSize?.(this.maximized ? 'full' : 'default');
|
|
6055
|
+
}
|
|
6056
|
+
else {
|
|
6057
|
+
this.dialogRef?.updateSize(width, height);
|
|
6058
|
+
}
|
|
5642
6059
|
this.updateHostPosition();
|
|
5643
6060
|
if (pane && pane.classList.contains('pfx-dialog-pane')) {
|
|
5644
6061
|
if (this.presentation === 'drawer') {
|
|
@@ -5695,6 +6112,9 @@ class DynamicFormDialogHostComponent {
|
|
|
5695
6112
|
startMaximized: this.modal.startMaximized,
|
|
5696
6113
|
fullscreenBreakpoint: this.modal.fullscreenBreakpoint,
|
|
5697
6114
|
});
|
|
6115
|
+
if (this.surfaceRef) {
|
|
6116
|
+
return;
|
|
6117
|
+
}
|
|
5698
6118
|
let shouldMax = false;
|
|
5699
6119
|
if (saved && typeof saved.maximized === 'boolean') {
|
|
5700
6120
|
shouldMax = !!saved.maximized;
|
|
@@ -5709,16 +6129,19 @@ class DynamicFormDialogHostComponent {
|
|
|
5709
6129
|
this.toggleMaximize(true);
|
|
5710
6130
|
}
|
|
5711
6131
|
else if (this.initialSize.width || this.initialSize.height) {
|
|
5712
|
-
this.dialogRef
|
|
6132
|
+
this.dialogRef?.updateSize(this.initialSize.width, this.initialSize.height);
|
|
5713
6133
|
this.updateHostPosition();
|
|
5714
6134
|
}
|
|
5715
6135
|
}
|
|
5716
6136
|
updateHostPosition() {
|
|
6137
|
+
if (this.surfaceRef) {
|
|
6138
|
+
return;
|
|
6139
|
+
}
|
|
5717
6140
|
if (this.presentation !== 'drawer') {
|
|
5718
|
-
this.dialogRef
|
|
6141
|
+
this.dialogRef?.updatePosition();
|
|
5719
6142
|
return;
|
|
5720
6143
|
}
|
|
5721
|
-
this.dialogRef
|
|
6144
|
+
this.dialogRef?.updatePosition(this.maximized
|
|
5722
6145
|
? this.resolveMaximizedDrawerPosition()
|
|
5723
6146
|
: this.initialPosition);
|
|
5724
6147
|
}
|
|
@@ -5733,15 +6156,28 @@ class DynamicFormDialogHostComponent {
|
|
|
5733
6156
|
: { top: offset };
|
|
5734
6157
|
return { ...horizontal, ...vertical };
|
|
5735
6158
|
}
|
|
5736
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicFormDialogHostComponent, deps: [{ token: MatDialogRef }, { token: MAT_DIALOG_DATA }, { token: DialogService }, { token: i2$1.GenericCrudService }, { token: ASYNC_CONFIG_STORAGE }], target: i0.ɵɵFactoryTarget.Component });
|
|
5737
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: DynamicFormDialogHostComponent, isStandalone: true, selector: "praxis-dynamic-form-dialog-host", host: { properties: { "attr.data-density": "modal.density || \"default\"", "attr.data-presentation": "presentation", "class.praxis-drawer": "presentation === \"drawer\"" }, classAttribute: "praxis-dialog" }, providers: [
|
|
6159
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicFormDialogHostComponent, deps: [{ token: MatDialogRef, optional: true }, { token: MAT_DIALOG_DATA, optional: true }, { token: DialogService }, { token: i2$1.GenericCrudService }, { token: ASYNC_CONFIG_STORAGE }, { token: SURFACE_DRAWER_REF, optional: true }, { token: SURFACE_DRAWER_CONTENT_DATA, optional: true }], target: i0.ɵɵFactoryTarget.Component });
|
|
6160
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: DynamicFormDialogHostComponent, isStandalone: true, selector: "praxis-dynamic-form-dialog-host", host: { properties: { "attr.data-density": "modal.density || \"default\"", "attr.data-presentation": "presentation", "class.praxis-drawer": "presentation === \"drawer\"", "class.praxis-surface-frame": "isSurfaceFrame" }, classAttribute: "praxis-dialog" }, providers: [
|
|
5738
6161
|
GenericCrudService,
|
|
5739
6162
|
providePraxisI18nConfig(PRAXIS_CRUD_RUNTIME_I18N_CONFIG),
|
|
5740
6163
|
], viewQueries: [{ propertyName: "formComp", first: true, predicate: PraxisDynamicForm, descendants: true }], ngImport: i0, template: `
|
|
6164
|
+
@if (!isSurfaceFrame) {
|
|
5741
6165
|
<div mat-dialog-title class="dialog-header">
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
6166
|
+
@if (dialogIcon; as icon) {
|
|
6167
|
+
<span class="dialog-title-icon" aria-hidden="true">
|
|
6168
|
+
<mat-icon [praxisIcon]="icon"></mat-icon>
|
|
6169
|
+
</span>
|
|
6170
|
+
}
|
|
6171
|
+
<span class="dialog-heading">
|
|
6172
|
+
<h2 id="crudDialogTitle" class="dialog-title">
|
|
6173
|
+
{{ dialogTitle }}
|
|
6174
|
+
</h2>
|
|
6175
|
+
@if (dialogSubtitle; as subtitle) {
|
|
6176
|
+
<span id="crudDialogDescription" class="dialog-subtitle">
|
|
6177
|
+
{{ subtitle }}
|
|
6178
|
+
</span>
|
|
6179
|
+
}
|
|
6180
|
+
</span>
|
|
5745
6181
|
<span class="spacer"></span>
|
|
5746
6182
|
@if (modal.canMaximize) {
|
|
5747
6183
|
<button
|
|
@@ -5761,22 +6197,43 @@ class DynamicFormDialogHostComponent {
|
|
|
5761
6197
|
cdkFocusInitial
|
|
5762
6198
|
></button>
|
|
5763
6199
|
</div>
|
|
6200
|
+
}
|
|
5764
6201
|
|
|
5765
6202
|
<mat-dialog-content
|
|
5766
6203
|
class="dialog-content"
|
|
5767
|
-
aria-labelledby="crudDialogTitle"
|
|
6204
|
+
[attr.aria-labelledby]="isSurfaceFrame ? null : 'crudDialogTitle'"
|
|
6205
|
+
[attr.aria-describedby]="
|
|
6206
|
+
!isSurfaceFrame && dialogSubtitle ? 'crudDialogDescription' : null
|
|
6207
|
+
"
|
|
5768
6208
|
>
|
|
5769
|
-
@if (resourceIdentity) {
|
|
5770
|
-
<div class="crud-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
6209
|
+
@if (showContextIdentity || resourceIdentity) {
|
|
6210
|
+
<div class="crud-operation-context" data-testid="crud-operation-context">
|
|
6211
|
+
@if (showContextIdentity) {
|
|
6212
|
+
<div class="crud-resource-identity" data-testid="crud-context-identity">
|
|
6213
|
+
<span class="crud-resource-identity__label" aria-hidden="true">
|
|
6214
|
+
{{ texts.relatedContextLabel }}
|
|
6215
|
+
</span>
|
|
6216
|
+
<praxis-resource-identity
|
|
6217
|
+
[identity]="contextIdentity"
|
|
6218
|
+
density="compact"
|
|
6219
|
+
[showKeyLabel]="true"
|
|
6220
|
+
[ariaLabel]="texts.relatedContextLabel"
|
|
6221
|
+
></praxis-resource-identity>
|
|
6222
|
+
</div>
|
|
6223
|
+
}
|
|
6224
|
+
@if (resourceIdentity) {
|
|
6225
|
+
<div class="crud-resource-identity" data-testid="crud-resource-identity">
|
|
6226
|
+
<span class="crud-resource-identity__label" aria-hidden="true">
|
|
6227
|
+
{{ resourceIdentityLabel }}
|
|
6228
|
+
</span>
|
|
6229
|
+
<praxis-resource-identity
|
|
6230
|
+
[identity]="resourceIdentity"
|
|
6231
|
+
density="compact"
|
|
6232
|
+
[showKeyLabel]="true"
|
|
6233
|
+
[ariaLabel]="resourceIdentityLabel"
|
|
6234
|
+
></praxis-resource-identity>
|
|
6235
|
+
</div>
|
|
6236
|
+
}
|
|
5780
6237
|
</div>
|
|
5781
6238
|
}
|
|
5782
6239
|
<praxis-dynamic-form
|
|
@@ -5794,19 +6251,22 @@ class DynamicFormDialogHostComponent {
|
|
|
5794
6251
|
[configPersistenceStrategy]="formConfigPersistenceStrategy"
|
|
5795
6252
|
[layoutPolicy]="layoutPolicy"
|
|
5796
6253
|
[presentationModeGlobal]="mode === 'view' ? true : null"
|
|
6254
|
+
[fieldIconPolicy]="fieldIconPolicy"
|
|
5797
6255
|
[backConfig]="backConfig"
|
|
5798
6256
|
[actions]="formActions"
|
|
5799
6257
|
(formSubmit)="onSave($event)"
|
|
5800
6258
|
(formCancel)="onCancel()"
|
|
5801
6259
|
></praxis-dynamic-form>
|
|
5802
6260
|
</mat-dialog-content>
|
|
5803
|
-
`, isInline: true, styles: ["praxis-dynamic-form-dialog-host{--dlg-header-h: 56px;--dlg-footer-h: 56px;--dlg-pad: 16px;display:flex;flex-direction:column;height:100%;overflow:hidden}praxis-dynamic-form-dialog-host[data-density=compact]{--dlg-header-h: 44px;--dlg-footer-h: 44px;--dlg-pad: 12px}praxis-dynamic-form-dialog-host .dialog-header{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:var(--dlg-pad);
|
|
6261
|
+
`, isInline: true, styles: ["praxis-dynamic-form-dialog-host{--dlg-header-h: 56px;--dlg-footer-h: 56px;--dlg-pad: 16px;display:flex;flex-direction:column;height:100%;overflow:hidden}praxis-dynamic-form-dialog-host[data-density=compact]{--dlg-header-h: 44px;--dlg-footer-h: 44px;--dlg-pad: 12px}praxis-dynamic-form-dialog-host .dialog-header{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:var(--dlg-pad);min-height:var(--dlg-header-h);padding:10px var(--dlg-pad);margin:0;background:var(--md-sys-color-surface-container-high);border-bottom:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host .dialog-title{margin:0;font:inherit;font-weight:600;color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host .dialog-heading{display:grid;min-width:0;gap:2px}praxis-dynamic-form-dialog-host .dialog-title-icon{display:inline-grid;flex:0 0 auto;width:36px;height:36px;place-items:center;border-radius:10px;color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary) 12%,transparent)}praxis-dynamic-form-dialog-host .dialog-title-icon mat-icon{width:22px;height:22px;font-size:22px}praxis-dynamic-form-dialog-host .dialog-subtitle{display:-webkit-box;overflow:hidden;color:var(--md-sys-color-on-surface-variant);font-size:.82rem;font-weight:400;line-height:1.35;-webkit-box-orient:vertical;-webkit-line-clamp:2}praxis-dynamic-form-dialog-host .spacer{flex:1}praxis-dynamic-form-dialog-host .dialog-content{flex:1 1 auto;overflow:auto;padding:var(--dlg-pad);max-height:calc(100svh - var(--dlg-header-h) - 32px)}praxis-dynamic-form-dialog-host .crud-operation-context{position:sticky;top:0;z-index:1;display:grid;gap:.75rem;margin-block-end:var(--dlg-pad);padding-block:.5rem .75rem;background:var(--pfx-form-surface, var(--md-sys-color-surface));border-bottom:1px solid var(--md-sys-color-outline-variant)}praxis-dynamic-form-dialog-host .crud-resource-identity{display:grid;gap:.25rem;min-width:0}praxis-dynamic-form-dialog-host .crud-resource-identity__label{color:var(--md-sys-color-on-surface-variant);font:var(--md-sys-typescale-label-medium, 500 .75rem/1rem inherit)}praxis-dynamic-form-dialog-host.praxis-drawer{--dlg-header-h: 72px;width:100%;min-width:0;max-width:100vw;height:100dvh;background:var(--pfx-form-surface, var(--md-sys-color-surface));color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-header{background:color-mix(in srgb,var(--md-sys-color-surface-container-high, var(--md-sys-color-surface)),transparent 4%)}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content{display:flex;flex-direction:column;max-height:none;min-height:0;padding:clamp(12px,2.4vw,24px)}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content>praxis-dynamic-form{display:block;flex:1 1 auto;min-height:0}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content>praxis-dynamic-form>.praxis-dynamic-form{min-height:100%}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content>praxis-dynamic-form>.praxis-dynamic-form>praxis-form-actions[data-actions-placement=afterSections]{margin-top:auto;padding-top:var(--pfx-actions-gap-top, var(--pfx-section-gap, 20px))}praxis-dynamic-form-dialog-host.praxis-surface-frame .dialog-content{max-height:none;min-height:0;padding:clamp(12px,2.4vw,24px)}praxis-dynamic-form-dialog-host .dialog-header button.praxis-icon-button{color:var(--md-sys-color-on-surface-variant)}praxis-dynamic-form-dialog-host .dialog-header button.praxis-icon-button:hover{color:var(--md-sys-color-primary)}praxis-dynamic-form-dialog-host .dialog-footer{position:sticky;bottom:0;z-index:1;padding:var(--dlg-pad)}.pfx-blur-backdrop{background-color:var(--pfx-backdrop, rgba(15, 23, 42, .42))!important;backdrop-filter:blur(var(--pfx-backdrop-blur, 10px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 10px)) saturate(110%)}.pfx-transparent-backdrop{background-color:transparent!important}.cdk-overlay-pane.pfx-dialog-pane{overflow:hidden;transition:width .2s ease,height .2s ease,margin .2s ease}.cdk-overlay-pane.pfx-dialog-pane .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-dialog-pane .mdc-dialog__surface{display:flex;flex-direction:column;width:100%;min-width:0;height:100%;max-height:inherit;overflow:hidden;background:var(--pfx-form-surface, var(--md-sys-color-surface))!important;border:1px solid var(--pfx-form-stroke, var(--md-sys-color-outline-variant))!important}.cdk-overlay-pane.pfx-dialog-pane.pfx-dialog-frosted .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-dialog-pane.pfx-dialog-frosted .mdc-dialog__surface{backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.cdk-overlay-pane.pfx-drawer-pane{margin:0!important;border-radius:0!important;box-shadow:var(--md-sys-elevation-level3, 0 24px 80px rgba(15, 23, 42, .28)),0 0 0 1px color-mix(in srgb,var(--md-sys-color-outline, #64748b) 24%,transparent)}.cdk-overlay-pane.pfx-drawer-pane.pfx-drawer-maximized{margin:var(--pfx-drawer-edge-gap, 8px)!important}.cdk-overlay-pane.pfx-drawer-pane .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-drawer-pane .mdc-dialog__surface{min-width:0!important;border-radius:0!important}\n"], dependencies: [{ kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i6.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisResourceIdentityComponent, selector: "praxis-resource-identity", inputs: ["identity", "emptyTitle", "density", "showMetadataLabels", "showKeyLabel", "ariaLabel"] }, { kind: "component", type: PraxisDynamicForm, selector: "praxis-dynamic-form", inputs: ["resourcePath", "resourceId", "initialValue", "editorialContext", "mode", "config", "actions", "schemaSource", "schemaUrl", "readUrl", "submitUrl", "submitMethod", "submitIdempotencyKey", "submitCorrelationId", "submitResourceVersion", "responseSchemaUrl", "apiEndpointKey", "apiUrlEntry", "enableCustomization", "showAiAssistant", "formId", "componentInstanceId", "configPersistenceStrategy", "layout", "generatedLayoutPreset", "layoutPolicy", "backConfig", "hooks", "removeEmptyContainersOnSave", "reactiveValidation", "reactiveValidationDebounceMs", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "readonlyModeGlobal", "disabledModeGlobal", "presentationModeGlobal", "visibleGlobal", "fieldIconPolicy", "domainRules", "customEndpoints"], outputs: ["formSubmit", "formCancel", "formReset", "configChange", "configPatchChange", "formReady", "valueChange", "syncCompleted", "initializationError", "loadingStateChange", "enableCustomizationChange", "customAction", "actionConfirmation", "schemaStatusChange", "fieldRenderError", "ruleDiagnosticsChange"] }], encapsulation: i0.ViewEncapsulation.None });
|
|
5804
6262
|
}
|
|
5805
6263
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicFormDialogHostComponent, decorators: [{
|
|
5806
6264
|
type: Component,
|
|
5807
6265
|
args: [{ selector: 'praxis-dynamic-form-dialog-host', standalone: true, imports: [
|
|
5808
6266
|
MatDialogModule,
|
|
6267
|
+
MatIconModule,
|
|
5809
6268
|
PraxisIconButtonComponent,
|
|
6269
|
+
PraxisIconDirective,
|
|
5810
6270
|
PraxisResourceIdentityComponent,
|
|
5811
6271
|
PraxisDynamicForm
|
|
5812
6272
|
], encapsulation: ViewEncapsulation.None, providers: [
|
|
@@ -5817,11 +6277,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
5817
6277
|
'[attr.data-density]': 'modal.density || "default"',
|
|
5818
6278
|
'[attr.data-presentation]': 'presentation',
|
|
5819
6279
|
'[class.praxis-drawer]': 'presentation === "drawer"',
|
|
6280
|
+
'[class.praxis-surface-frame]': 'isSurfaceFrame',
|
|
5820
6281
|
}, template: `
|
|
6282
|
+
@if (!isSurfaceFrame) {
|
|
5821
6283
|
<div mat-dialog-title class="dialog-header">
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
6284
|
+
@if (dialogIcon; as icon) {
|
|
6285
|
+
<span class="dialog-title-icon" aria-hidden="true">
|
|
6286
|
+
<mat-icon [praxisIcon]="icon"></mat-icon>
|
|
6287
|
+
</span>
|
|
6288
|
+
}
|
|
6289
|
+
<span class="dialog-heading">
|
|
6290
|
+
<h2 id="crudDialogTitle" class="dialog-title">
|
|
6291
|
+
{{ dialogTitle }}
|
|
6292
|
+
</h2>
|
|
6293
|
+
@if (dialogSubtitle; as subtitle) {
|
|
6294
|
+
<span id="crudDialogDescription" class="dialog-subtitle">
|
|
6295
|
+
{{ subtitle }}
|
|
6296
|
+
</span>
|
|
6297
|
+
}
|
|
6298
|
+
</span>
|
|
5825
6299
|
<span class="spacer"></span>
|
|
5826
6300
|
@if (modal.canMaximize) {
|
|
5827
6301
|
<button
|
|
@@ -5841,22 +6315,43 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
5841
6315
|
cdkFocusInitial
|
|
5842
6316
|
></button>
|
|
5843
6317
|
</div>
|
|
6318
|
+
}
|
|
5844
6319
|
|
|
5845
6320
|
<mat-dialog-content
|
|
5846
6321
|
class="dialog-content"
|
|
5847
|
-
aria-labelledby="crudDialogTitle"
|
|
6322
|
+
[attr.aria-labelledby]="isSurfaceFrame ? null : 'crudDialogTitle'"
|
|
6323
|
+
[attr.aria-describedby]="
|
|
6324
|
+
!isSurfaceFrame && dialogSubtitle ? 'crudDialogDescription' : null
|
|
6325
|
+
"
|
|
5848
6326
|
>
|
|
5849
|
-
@if (resourceIdentity) {
|
|
5850
|
-
<div class="crud-
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
6327
|
+
@if (showContextIdentity || resourceIdentity) {
|
|
6328
|
+
<div class="crud-operation-context" data-testid="crud-operation-context">
|
|
6329
|
+
@if (showContextIdentity) {
|
|
6330
|
+
<div class="crud-resource-identity" data-testid="crud-context-identity">
|
|
6331
|
+
<span class="crud-resource-identity__label" aria-hidden="true">
|
|
6332
|
+
{{ texts.relatedContextLabel }}
|
|
6333
|
+
</span>
|
|
6334
|
+
<praxis-resource-identity
|
|
6335
|
+
[identity]="contextIdentity"
|
|
6336
|
+
density="compact"
|
|
6337
|
+
[showKeyLabel]="true"
|
|
6338
|
+
[ariaLabel]="texts.relatedContextLabel"
|
|
6339
|
+
></praxis-resource-identity>
|
|
6340
|
+
</div>
|
|
6341
|
+
}
|
|
6342
|
+
@if (resourceIdentity) {
|
|
6343
|
+
<div class="crud-resource-identity" data-testid="crud-resource-identity">
|
|
6344
|
+
<span class="crud-resource-identity__label" aria-hidden="true">
|
|
6345
|
+
{{ resourceIdentityLabel }}
|
|
6346
|
+
</span>
|
|
6347
|
+
<praxis-resource-identity
|
|
6348
|
+
[identity]="resourceIdentity"
|
|
6349
|
+
density="compact"
|
|
6350
|
+
[showKeyLabel]="true"
|
|
6351
|
+
[ariaLabel]="resourceIdentityLabel"
|
|
6352
|
+
></praxis-resource-identity>
|
|
6353
|
+
</div>
|
|
6354
|
+
}
|
|
5860
6355
|
</div>
|
|
5861
6356
|
}
|
|
5862
6357
|
<praxis-dynamic-form
|
|
@@ -5874,22 +6369,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
5874
6369
|
[configPersistenceStrategy]="formConfigPersistenceStrategy"
|
|
5875
6370
|
[layoutPolicy]="layoutPolicy"
|
|
5876
6371
|
[presentationModeGlobal]="mode === 'view' ? true : null"
|
|
6372
|
+
[fieldIconPolicy]="fieldIconPolicy"
|
|
5877
6373
|
[backConfig]="backConfig"
|
|
5878
6374
|
[actions]="formActions"
|
|
5879
6375
|
(formSubmit)="onSave($event)"
|
|
5880
6376
|
(formCancel)="onCancel()"
|
|
5881
6377
|
></praxis-dynamic-form>
|
|
5882
6378
|
</mat-dialog-content>
|
|
5883
|
-
`, styles: ["praxis-dynamic-form-dialog-host{--dlg-header-h: 56px;--dlg-footer-h: 56px;--dlg-pad: 16px;display:flex;flex-direction:column;height:100%;overflow:hidden}praxis-dynamic-form-dialog-host[data-density=compact]{--dlg-header-h: 44px;--dlg-footer-h: 44px;--dlg-pad: 12px}praxis-dynamic-form-dialog-host .dialog-header{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:var(--dlg-pad);
|
|
6379
|
+
`, styles: ["praxis-dynamic-form-dialog-host{--dlg-header-h: 56px;--dlg-footer-h: 56px;--dlg-pad: 16px;display:flex;flex-direction:column;height:100%;overflow:hidden}praxis-dynamic-form-dialog-host[data-density=compact]{--dlg-header-h: 44px;--dlg-footer-h: 44px;--dlg-pad: 12px}praxis-dynamic-form-dialog-host .dialog-header{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:var(--dlg-pad);min-height:var(--dlg-header-h);padding:10px var(--dlg-pad);margin:0;background:var(--md-sys-color-surface-container-high);border-bottom:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host .dialog-title{margin:0;font:inherit;font-weight:600;color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host .dialog-heading{display:grid;min-width:0;gap:2px}praxis-dynamic-form-dialog-host .dialog-title-icon{display:inline-grid;flex:0 0 auto;width:36px;height:36px;place-items:center;border-radius:10px;color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary) 12%,transparent)}praxis-dynamic-form-dialog-host .dialog-title-icon mat-icon{width:22px;height:22px;font-size:22px}praxis-dynamic-form-dialog-host .dialog-subtitle{display:-webkit-box;overflow:hidden;color:var(--md-sys-color-on-surface-variant);font-size:.82rem;font-weight:400;line-height:1.35;-webkit-box-orient:vertical;-webkit-line-clamp:2}praxis-dynamic-form-dialog-host .spacer{flex:1}praxis-dynamic-form-dialog-host .dialog-content{flex:1 1 auto;overflow:auto;padding:var(--dlg-pad);max-height:calc(100svh - var(--dlg-header-h) - 32px)}praxis-dynamic-form-dialog-host .crud-operation-context{position:sticky;top:0;z-index:1;display:grid;gap:.75rem;margin-block-end:var(--dlg-pad);padding-block:.5rem .75rem;background:var(--pfx-form-surface, var(--md-sys-color-surface));border-bottom:1px solid var(--md-sys-color-outline-variant)}praxis-dynamic-form-dialog-host .crud-resource-identity{display:grid;gap:.25rem;min-width:0}praxis-dynamic-form-dialog-host .crud-resource-identity__label{color:var(--md-sys-color-on-surface-variant);font:var(--md-sys-typescale-label-medium, 500 .75rem/1rem inherit)}praxis-dynamic-form-dialog-host.praxis-drawer{--dlg-header-h: 72px;width:100%;min-width:0;max-width:100vw;height:100dvh;background:var(--pfx-form-surface, var(--md-sys-color-surface));color:var(--md-sys-color-on-surface)}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-header{background:color-mix(in srgb,var(--md-sys-color-surface-container-high, var(--md-sys-color-surface)),transparent 4%)}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content{display:flex;flex-direction:column;max-height:none;min-height:0;padding:clamp(12px,2.4vw,24px)}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content>praxis-dynamic-form{display:block;flex:1 1 auto;min-height:0}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content>praxis-dynamic-form>.praxis-dynamic-form{min-height:100%}praxis-dynamic-form-dialog-host.praxis-drawer .dialog-content>praxis-dynamic-form>.praxis-dynamic-form>praxis-form-actions[data-actions-placement=afterSections]{margin-top:auto;padding-top:var(--pfx-actions-gap-top, var(--pfx-section-gap, 20px))}praxis-dynamic-form-dialog-host.praxis-surface-frame .dialog-content{max-height:none;min-height:0;padding:clamp(12px,2.4vw,24px)}praxis-dynamic-form-dialog-host .dialog-header button.praxis-icon-button{color:var(--md-sys-color-on-surface-variant)}praxis-dynamic-form-dialog-host .dialog-header button.praxis-icon-button:hover{color:var(--md-sys-color-primary)}praxis-dynamic-form-dialog-host .dialog-footer{position:sticky;bottom:0;z-index:1;padding:var(--dlg-pad)}.pfx-blur-backdrop{background-color:var(--pfx-backdrop, rgba(15, 23, 42, .42))!important;backdrop-filter:blur(var(--pfx-backdrop-blur, 10px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 10px)) saturate(110%)}.pfx-transparent-backdrop{background-color:transparent!important}.cdk-overlay-pane.pfx-dialog-pane{overflow:hidden;transition:width .2s ease,height .2s ease,margin .2s ease}.cdk-overlay-pane.pfx-dialog-pane .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-dialog-pane .mdc-dialog__surface{display:flex;flex-direction:column;width:100%;min-width:0;height:100%;max-height:inherit;overflow:hidden;background:var(--pfx-form-surface, var(--md-sys-color-surface))!important;border:1px solid var(--pfx-form-stroke, var(--md-sys-color-outline-variant))!important}.cdk-overlay-pane.pfx-dialog-pane.pfx-dialog-frosted .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-dialog-pane.pfx-dialog-frosted .mdc-dialog__surface{backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}.cdk-overlay-pane.pfx-drawer-pane{margin:0!important;border-radius:0!important;box-shadow:var(--md-sys-elevation-level3, 0 24px 80px rgba(15, 23, 42, .28)),0 0 0 1px color-mix(in srgb,var(--md-sys-color-outline, #64748b) 24%,transparent)}.cdk-overlay-pane.pfx-drawer-pane.pfx-drawer-maximized{margin:var(--pfx-drawer-edge-gap, 8px)!important}.cdk-overlay-pane.pfx-drawer-pane .mat-mdc-dialog-surface,.cdk-overlay-pane.pfx-drawer-pane .mdc-dialog__surface{min-width:0!important;border-radius:0!important}\n"] }]
|
|
5884
6380
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
6381
|
+
type: Optional
|
|
6382
|
+
}, {
|
|
5885
6383
|
type: Inject,
|
|
5886
6384
|
args: [MatDialogRef]
|
|
5887
6385
|
}] }, { type: undefined, decorators: [{
|
|
6386
|
+
type: Optional
|
|
6387
|
+
}, {
|
|
5888
6388
|
type: Inject,
|
|
5889
6389
|
args: [MAT_DIALOG_DATA]
|
|
5890
6390
|
}] }, { type: DialogService }, { type: i2$1.GenericCrudService }, { type: undefined, decorators: [{
|
|
5891
6391
|
type: Inject,
|
|
5892
6392
|
args: [ASYNC_CONFIG_STORAGE]
|
|
6393
|
+
}] }, { type: undefined, decorators: [{
|
|
6394
|
+
type: Optional
|
|
6395
|
+
}, {
|
|
6396
|
+
type: Inject,
|
|
6397
|
+
args: [SURFACE_DRAWER_REF]
|
|
6398
|
+
}] }, { type: undefined, decorators: [{
|
|
6399
|
+
type: Optional
|
|
6400
|
+
}, {
|
|
6401
|
+
type: Inject,
|
|
6402
|
+
args: [SURFACE_DRAWER_CONTENT_DATA]
|
|
5893
6403
|
}] }], propDecorators: { formComp: [{
|
|
5894
6404
|
type: ViewChild,
|
|
5895
6405
|
args: [PraxisDynamicForm]
|
|
@@ -6119,6 +6629,12 @@ const PRAXIS_CRUD_COMPONENT_METADATA = {
|
|
|
6119
6629
|
type: 'string',
|
|
6120
6630
|
description: 'Identificador opcional para múltiplas instâncias na mesma rota.',
|
|
6121
6631
|
},
|
|
6632
|
+
{
|
|
6633
|
+
name: 'tableConfigPersistenceStrategy',
|
|
6634
|
+
type: "'local-first' | 'input-first' | 'volatile'",
|
|
6635
|
+
default: 'local-first',
|
|
6636
|
+
description: 'Política da tabela interna. Use input-first para contratos governados autoritativos e volatile para previews que não devem ler nem gravar preferências locais.',
|
|
6637
|
+
},
|
|
6122
6638
|
{
|
|
6123
6639
|
name: 'context',
|
|
6124
6640
|
type: 'Record<string, unknown>',
|
|
@@ -6198,6 +6714,11 @@ const PRAXIS_CRUD_COMPONENT_METADATA = {
|
|
|
6198
6714
|
type: '{ row: unknown; index: number }',
|
|
6199
6715
|
description: 'Encaminha o clique de linha da tabela interna para composição master-detail e seleção local.',
|
|
6200
6716
|
},
|
|
6717
|
+
{
|
|
6718
|
+
name: 'rowAction',
|
|
6719
|
+
type: 'CrudActionRuntimeEvent',
|
|
6720
|
+
description: 'Encaminha a ação contextual da linha com row, action e resourceIdentity para composição governada.',
|
|
6721
|
+
},
|
|
6201
6722
|
{
|
|
6202
6723
|
name: 'selectionChange',
|
|
6203
6724
|
type: 'unknown',
|
|
@@ -6694,6 +7215,7 @@ const PRAXIS_CRUD_AUTHORING_MANIFEST = {
|
|
|
6694
7215
|
{ name: 'metadata', type: 'CrudMetadata | string', description: 'Canonical CRUD metadata or serialized metadata document.' },
|
|
6695
7216
|
{ name: 'crudId', type: 'string', description: 'Stable CRUD instance id used for table/form identity and persistence.' },
|
|
6696
7217
|
{ name: 'componentInstanceId', type: 'string', description: 'Optional stable host instance id for multiple CRUD widgets on the same route.' },
|
|
7218
|
+
{ name: 'tableConfigPersistenceStrategy', type: "'local-first' | 'input-first' | 'volatile'", description: 'Persistence precedence forwarded to the internal Praxis Table. Governed previews should use input-first or volatile explicitly.' },
|
|
6697
7219
|
{ name: 'context', type: 'Record<string, unknown>', description: 'Opaque host context used for authoring seeds and launcher inputs.' },
|
|
6698
7220
|
{ name: 'enableCustomization', type: 'boolean', description: 'Explicit host opt-in for CRUD authoring surfaces.' },
|
|
6699
7221
|
{ name: 'authoringCapability', type: 'string | null', description: 'Public runtime capability required before CRUD and delegated table authoring can open.' },
|