@praxisui/crud 1.0.0-beta.8 → 2.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +106 -42
- package/drawer-adapter/index.d.ts +81 -0
- package/drawer-adapter/package.json +3 -0
- package/fesm2022/praxisui-crud-drawer-adapter.mjs +18 -0
- package/fesm2022/praxisui-crud-drawer-adapter.mjs.map +1 -0
- package/fesm2022/praxisui-crud.mjs +579 -153
- package/fesm2022/praxisui-crud.mjs.map +1 -1
- package/index.d.ts +80 -20
- package/package.json +16 -11
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, InjectionToken, inject, EventEmitter, ViewChild, Output, Input, Component,
|
|
2
|
+
import { Injectable, InjectionToken, inject, EventEmitter, DestroyRef, ViewChild, Output, Input, Component, Inject, ENVIRONMENT_INITIALIZER } from '@angular/core';
|
|
3
|
+
import { Router, ActivatedRoute, RouterLink } from '@angular/router';
|
|
3
4
|
import { PraxisTable } from '@praxisui/table';
|
|
4
|
-
import {
|
|
5
|
+
import { firstValueFrom } from 'rxjs';
|
|
5
6
|
import * as i1 from '@angular/material/dialog';
|
|
6
7
|
import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
|
|
7
8
|
export { MAT_DIALOG_DATA as DIALOG_DATA } from '@angular/material/dialog';
|
|
8
9
|
import * as i2 from '@praxisui/core';
|
|
9
|
-
import {
|
|
10
|
+
import { ASYNC_CONFIG_STORAGE, GlobalConfigService, fillUndefined, createDefaultTableConfig, ComponentKeyService, EmptyStateCardComponent, PraxisIconDirective, GenericCrudService, ComponentMetadataRegistry } from '@praxisui/core';
|
|
10
11
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
11
12
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
|
12
13
|
import * as i1$1 from '@angular/common';
|
|
@@ -17,7 +18,7 @@ import * as i5 from '@angular/material/icon';
|
|
|
17
18
|
import { MatIconModule } from '@angular/material/icon';
|
|
18
19
|
import { PraxisDynamicForm } from '@praxisui/dynamic-form';
|
|
19
20
|
import { ConfirmDialogComponent } from '@praxisui/dynamic-fields';
|
|
20
|
-
import { filter } from 'rxjs/operators';
|
|
21
|
+
import { filter, take } from 'rxjs/operators';
|
|
21
22
|
|
|
22
23
|
class DialogService {
|
|
23
24
|
matDialog;
|
|
@@ -33,20 +34,28 @@ class DialogService {
|
|
|
33
34
|
const component = await loader();
|
|
34
35
|
return this.zone.run(() => this.matDialog.open(component, config));
|
|
35
36
|
}
|
|
36
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.
|
|
37
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.
|
|
37
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DialogService, deps: [{ token: i1.MatDialog }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
38
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DialogService, providedIn: 'root' });
|
|
38
39
|
}
|
|
39
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
40
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DialogService, decorators: [{
|
|
40
41
|
type: Injectable,
|
|
41
42
|
args: [{ providedIn: 'root' }]
|
|
42
43
|
}], ctorParameters: () => [{ type: i1.MatDialog }, { type: i0.NgZone }] });
|
|
43
44
|
|
|
44
|
-
|
|
45
|
+
function getCrudDrawerAdapterToken() {
|
|
46
|
+
const registryKey = '__PAX_CRUD_DRAWER_ADAPTER_TOKEN__';
|
|
47
|
+
const globalRegistry = globalThis;
|
|
48
|
+
if (!globalRegistry[registryKey]) {
|
|
49
|
+
globalRegistry[registryKey] = new InjectionToken('CRUD_DRAWER_ADAPTER');
|
|
50
|
+
}
|
|
51
|
+
return globalRegistry[registryKey];
|
|
52
|
+
}
|
|
53
|
+
const CRUD_DRAWER_ADAPTER = getCrudDrawerAdapterToken();
|
|
45
54
|
|
|
46
55
|
class CrudLauncherService {
|
|
47
56
|
router = inject(Router);
|
|
48
57
|
dialog = inject(DialogService);
|
|
49
|
-
storage = inject(
|
|
58
|
+
storage = inject(ASYNC_CONFIG_STORAGE);
|
|
50
59
|
global = inject(GlobalConfigService);
|
|
51
60
|
drawerAdapter = (() => {
|
|
52
61
|
try {
|
|
@@ -56,9 +65,9 @@ class CrudLauncherService {
|
|
|
56
65
|
return undefined;
|
|
57
66
|
}
|
|
58
67
|
})();
|
|
59
|
-
async launch(action, row, metadata) {
|
|
68
|
+
async launch(action, row, metadata, componentKeyId, drawerCallbacks) {
|
|
60
69
|
// Carregar overrides de CRUD (se houver) e mesclar em uma cópia local
|
|
61
|
-
const merged = this.mergeCrudOverrides(metadata, action);
|
|
70
|
+
const merged = await this.mergeCrudOverrides(metadata, action, componentKeyId || undefined);
|
|
62
71
|
const mode = this.resolveOpenMode(merged.action, merged.metadata);
|
|
63
72
|
console.debug('[CRUD:Launcher] mode=', mode, 'action=', action);
|
|
64
73
|
if (mode === 'route') {
|
|
@@ -75,7 +84,13 @@ class CrudLauncherService {
|
|
|
75
84
|
if (row && inputs[idField] === undefined && row[idField] !== undefined) {
|
|
76
85
|
inputs[idField] = row[idField];
|
|
77
86
|
}
|
|
78
|
-
await Promise.resolve(this.drawerAdapter.open({
|
|
87
|
+
await Promise.resolve(this.drawerAdapter.open({
|
|
88
|
+
action: merged.action,
|
|
89
|
+
metadata: merged.metadata,
|
|
90
|
+
inputs,
|
|
91
|
+
onClose: drawerCallbacks?.onClose,
|
|
92
|
+
onResult: drawerCallbacks?.onResult,
|
|
93
|
+
}));
|
|
79
94
|
return { mode };
|
|
80
95
|
}
|
|
81
96
|
if (!merged.action.formId) {
|
|
@@ -195,13 +210,18 @@ class CrudLauncherService {
|
|
|
195
210
|
});
|
|
196
211
|
return inputs;
|
|
197
212
|
}
|
|
198
|
-
mergeCrudOverrides(metadata, action) {
|
|
213
|
+
async mergeCrudOverrides(metadata, action, componentKeyId) {
|
|
199
214
|
try {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
215
|
+
if (!componentKeyId)
|
|
216
|
+
return { metadata, action };
|
|
217
|
+
const key = `crud-overrides:${componentKeyId}`;
|
|
218
|
+
let saved = null;
|
|
219
|
+
try {
|
|
220
|
+
saved = await firstValueFrom(this.storage.loadConfig(key));
|
|
221
|
+
}
|
|
222
|
+
catch { }
|
|
223
|
+
const overDefaults = saved?.defaults || {};
|
|
224
|
+
const overActions = saved?.actions || {};
|
|
205
225
|
// Merge defaults (shallow for defaults, shallow for modal/back)
|
|
206
226
|
let mergedDefaults = {
|
|
207
227
|
...(metadata.defaults || {}),
|
|
@@ -237,21 +257,29 @@ class CrudLauncherService {
|
|
|
237
257
|
return { metadata, action };
|
|
238
258
|
}
|
|
239
259
|
}
|
|
240
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.
|
|
241
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.
|
|
260
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: CrudLauncherService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
261
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: CrudLauncherService, providedIn: 'root' });
|
|
242
262
|
}
|
|
243
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
263
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: CrudLauncherService, decorators: [{
|
|
244
264
|
type: Injectable,
|
|
245
265
|
args: [{ providedIn: 'root' }]
|
|
246
266
|
}] });
|
|
247
267
|
|
|
248
|
-
function assertCrudMetadata(meta) {
|
|
268
|
+
function assertCrudMetadata(meta, options = {}) {
|
|
269
|
+
if (meta.component !== 'praxis-crud') {
|
|
270
|
+
throw new Error('Invalid component type for CRUD metadata');
|
|
271
|
+
}
|
|
272
|
+
if (!meta.table) {
|
|
273
|
+
throw new Error('Table config not provided for CRUD metadata');
|
|
274
|
+
}
|
|
249
275
|
meta.actions?.forEach((action) => {
|
|
250
276
|
const mode = action.openMode ?? meta.defaults?.openMode ?? 'route';
|
|
251
|
-
if (mode === 'route' && !action.route) {
|
|
277
|
+
if (!options.allowDeferredActionBindings && mode === 'route' && !action.route) {
|
|
252
278
|
throw new Error(`Route not provided for action ${action.action}`);
|
|
253
279
|
}
|
|
254
|
-
if (
|
|
280
|
+
if (!options.allowDeferredActionBindings &&
|
|
281
|
+
(mode === 'modal' || mode === 'drawer') &&
|
|
282
|
+
!action.formId) {
|
|
255
283
|
throw new Error(`formId not provided for action ${action.action}`);
|
|
256
284
|
}
|
|
257
285
|
action.params?.forEach((p) => {
|
|
@@ -263,13 +291,15 @@ function assertCrudMetadata(meta) {
|
|
|
263
291
|
}
|
|
264
292
|
|
|
265
293
|
class PraxisCrudComponent {
|
|
266
|
-
/** Habilita visual de debug para alinhamento/layouot */
|
|
267
|
-
debugLayout = false;
|
|
268
294
|
/** JSON inline ou chave/URL resolvida pelo MetadataResolver */
|
|
269
295
|
metadata;
|
|
296
|
+
/** Identificador obrigatório do CRUD (base para tabela/formulário) */
|
|
297
|
+
crudId;
|
|
298
|
+
/** Identificador opcional para instâncias múltiplas */
|
|
299
|
+
componentInstanceId;
|
|
270
300
|
context;
|
|
271
301
|
/** Encaminha o modo de edição de layout para a tabela interna */
|
|
272
|
-
|
|
302
|
+
enableCustomization = false;
|
|
273
303
|
/** CTA: usado pelo Builder para abrir configuração de metadados quando vazio */
|
|
274
304
|
configureRequested = new EventEmitter();
|
|
275
305
|
afterOpen = new EventEmitter();
|
|
@@ -277,13 +307,38 @@ class PraxisCrudComponent {
|
|
|
277
307
|
afterSave = new EventEmitter();
|
|
278
308
|
afterDelete = new EventEmitter();
|
|
279
309
|
error = new EventEmitter();
|
|
310
|
+
/**
|
|
311
|
+
* Emits the live PraxisTable configuration snapshot after runtime hydration or
|
|
312
|
+
* metadata refreshes. This reflects the effective table state in memory,
|
|
313
|
+
* deduplicated by structural JSON equality, and is intended for external hosts
|
|
314
|
+
* that need to mirror the table editor/runtime state.
|
|
315
|
+
*/
|
|
316
|
+
tableRuntimeConfigChange = new EventEmitter();
|
|
280
317
|
resolvedMetadata;
|
|
281
318
|
/** Configuração efetiva da tabela com melhorias automáticas (ex.: botão Adicionar). */
|
|
282
319
|
effectiveTableConfig;
|
|
320
|
+
/** Config passado ao PraxisTable — sempre definido (fallback seguro). */
|
|
321
|
+
tableConfigForBinding = createDefaultTableConfig();
|
|
283
322
|
launcher = inject(CrudLauncherService);
|
|
323
|
+
destroyRef = inject(DestroyRef);
|
|
284
324
|
table;
|
|
285
|
-
storage = inject(
|
|
325
|
+
storage = inject(ASYNC_CONFIG_STORAGE);
|
|
286
326
|
snack = inject(MatSnackBar);
|
|
327
|
+
global = (() => { try {
|
|
328
|
+
return inject(GlobalConfigService);
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
return undefined;
|
|
332
|
+
} })();
|
|
333
|
+
componentKeys = inject(ComponentKeyService);
|
|
334
|
+
route = (() => { try {
|
|
335
|
+
return inject(ActivatedRoute);
|
|
336
|
+
}
|
|
337
|
+
catch {
|
|
338
|
+
return undefined;
|
|
339
|
+
} })();
|
|
340
|
+
warnedMissingId = false;
|
|
341
|
+
lastEmittedTableRuntimeConfigJson = '';
|
|
287
342
|
/**
|
|
288
343
|
* Stable CRUD context passed to PraxisTable.
|
|
289
344
|
* Previously this was created via a getter, producing a new object each CD tick
|
|
@@ -292,9 +347,11 @@ class PraxisCrudComponent {
|
|
|
292
347
|
tableCrudContext;
|
|
293
348
|
onResetPreferences() {
|
|
294
349
|
try {
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
350
|
+
const keyId = this.componentKeyId();
|
|
351
|
+
if (!keyId)
|
|
352
|
+
return;
|
|
353
|
+
const key = `crud-overrides:${keyId}`;
|
|
354
|
+
void firstValueFrom(this.storage.clearConfig(key)).catch(() => { });
|
|
298
355
|
this.snack.open('Overrides de CRUD redefinidos', undefined, { duration: 2000 });
|
|
299
356
|
}
|
|
300
357
|
catch { }
|
|
@@ -302,12 +359,21 @@ class PraxisCrudComponent {
|
|
|
302
359
|
ngOnChanges(changes) {
|
|
303
360
|
if (changes['metadata']) {
|
|
304
361
|
try {
|
|
305
|
-
this.
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
362
|
+
const parsed = typeof this.metadata === 'string'
|
|
363
|
+
? JSON.parse(this.metadata)
|
|
364
|
+
: this.metadata;
|
|
365
|
+
this.resolvedMetadata = parsed;
|
|
366
|
+
// Runtime metadata must stay structurally valid, but route/formId can be
|
|
367
|
+
// completed later by launcher overrides, defaults or host policy.
|
|
368
|
+
assertCrudMetadata(this.resolvedMetadata, {
|
|
369
|
+
allowDeferredActionBindings: true,
|
|
370
|
+
});
|
|
310
371
|
this.effectiveTableConfig = this.buildEffectiveTableConfig(this.resolvedMetadata);
|
|
372
|
+
// Evitar passar undefined para [config], o que sobrescreve o default do PraxisTable
|
|
373
|
+
this.tableConfigForBinding =
|
|
374
|
+
this.effectiveTableConfig ||
|
|
375
|
+
(this.resolvedMetadata.table ??
|
|
376
|
+
createDefaultTableConfig());
|
|
311
377
|
// Build a stable table context when metadata changes
|
|
312
378
|
this.tableCrudContext = this.buildTableCrudContext(this.resolvedMetadata);
|
|
313
379
|
}
|
|
@@ -338,13 +404,39 @@ class PraxisCrudComponent {
|
|
|
338
404
|
}
|
|
339
405
|
}
|
|
340
406
|
const effectiveAction = (actionMeta || { action });
|
|
341
|
-
|
|
407
|
+
let drawerCloseEmitted = false;
|
|
408
|
+
const emitDrawerClose = () => {
|
|
409
|
+
if (drawerCloseEmitted)
|
|
410
|
+
return;
|
|
411
|
+
drawerCloseEmitted = true;
|
|
412
|
+
this.afterClose.emit();
|
|
413
|
+
};
|
|
414
|
+
const { mode, ref } = await this.launcher.launch(effectiveAction, row, this.resolvedMetadata, this.componentKeyId(), {
|
|
415
|
+
onClose: () => {
|
|
416
|
+
emitDrawerClose();
|
|
417
|
+
},
|
|
418
|
+
onResult: (result) => {
|
|
419
|
+
emitDrawerClose();
|
|
420
|
+
const data = (result?.data || {});
|
|
421
|
+
const idField = this.getIdField();
|
|
422
|
+
if (result?.type === 'save') {
|
|
423
|
+
const id = data?.[idField];
|
|
424
|
+
this.afterSave.emit({ id, data });
|
|
425
|
+
this.refreshTable();
|
|
426
|
+
}
|
|
427
|
+
if (result?.type === 'delete') {
|
|
428
|
+
const id = data?.[idField];
|
|
429
|
+
this.afterDelete.emit({ id });
|
|
430
|
+
this.refreshTable();
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
});
|
|
342
434
|
this.afterOpen.emit({ mode, action: effectiveAction.action });
|
|
343
435
|
if (ref) {
|
|
344
436
|
const idField = this.getIdField();
|
|
345
437
|
ref
|
|
346
438
|
.afterClosed()
|
|
347
|
-
.pipe(takeUntilDestroyed())
|
|
439
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
348
440
|
.subscribe((result) => {
|
|
349
441
|
this.afterClose.emit();
|
|
350
442
|
if (result?.type === 'save') {
|
|
@@ -369,6 +461,61 @@ class PraxisCrudComponent {
|
|
|
369
461
|
refreshTable() {
|
|
370
462
|
this.table.refetch();
|
|
371
463
|
}
|
|
464
|
+
getCurrentTableConfigSnapshot() {
|
|
465
|
+
const current = this.table?.config || this.tableConfigForBinding || this.effectiveTableConfig;
|
|
466
|
+
if (!current)
|
|
467
|
+
return null;
|
|
468
|
+
try {
|
|
469
|
+
return JSON.parse(JSON.stringify(current));
|
|
470
|
+
}
|
|
471
|
+
catch {
|
|
472
|
+
return current;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
onTableMetadataChange() {
|
|
476
|
+
this.emitTableRuntimeConfigSnapshot();
|
|
477
|
+
}
|
|
478
|
+
onTableLoadingStateChange(state) {
|
|
479
|
+
if (!state || state.status !== 'success')
|
|
480
|
+
return;
|
|
481
|
+
if (state.phase !== 'render' && state.phase !== 'data' && state.phase !== 'schema') {
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
this.emitTableRuntimeConfigSnapshot();
|
|
485
|
+
}
|
|
486
|
+
emitTableRuntimeConfigSnapshot() {
|
|
487
|
+
const snapshot = this.getCurrentTableConfigSnapshot();
|
|
488
|
+
if (!snapshot)
|
|
489
|
+
return;
|
|
490
|
+
try {
|
|
491
|
+
const nextJson = JSON.stringify(snapshot);
|
|
492
|
+
if (nextJson === this.lastEmittedTableRuntimeConfigJson)
|
|
493
|
+
return;
|
|
494
|
+
this.lastEmittedTableRuntimeConfigJson = nextJson;
|
|
495
|
+
}
|
|
496
|
+
catch {
|
|
497
|
+
// Fallback to emitting when serialization is not possible.
|
|
498
|
+
}
|
|
499
|
+
this.tableRuntimeConfigChange.emit(snapshot);
|
|
500
|
+
}
|
|
501
|
+
resolveResourcePath(meta) {
|
|
502
|
+
return (meta?.resource?.path ||
|
|
503
|
+
meta?.table?.resourcePath ||
|
|
504
|
+
'').trim();
|
|
505
|
+
}
|
|
506
|
+
resolveLocalData(meta) {
|
|
507
|
+
const localData = meta?.data;
|
|
508
|
+
return Array.isArray(localData) ? localData : null;
|
|
509
|
+
}
|
|
510
|
+
resolveTableData(meta) {
|
|
511
|
+
if (this.resolveResourcePath(meta).length > 0) {
|
|
512
|
+
return null;
|
|
513
|
+
}
|
|
514
|
+
return this.resolveLocalData(meta);
|
|
515
|
+
}
|
|
516
|
+
shouldRenderTable(meta) {
|
|
517
|
+
return this.resolveResourcePath(meta).length > 0 || Array.isArray(meta?.data);
|
|
518
|
+
}
|
|
372
519
|
getIdField() {
|
|
373
520
|
return this.resolvedMetadata?.resource?.idField || 'id';
|
|
374
521
|
}
|
|
@@ -383,45 +530,77 @@ class PraxisCrudComponent {
|
|
|
383
530
|
*/
|
|
384
531
|
buildEffectiveTableConfig(meta) {
|
|
385
532
|
const base = meta.table;
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
533
|
+
// Clonar base ou criar default para permitir injeção de melhorias
|
|
534
|
+
const cfg = base
|
|
535
|
+
? JSON.parse(JSON.stringify(base))
|
|
536
|
+
: createDefaultTableConfig();
|
|
537
|
+
let changed = false;
|
|
390
538
|
const ensureToolbar = () => {
|
|
391
539
|
if (!cfg.toolbar) {
|
|
392
540
|
cfg.toolbar = { visible: true, position: 'top' };
|
|
393
541
|
}
|
|
394
542
|
return cfg.toolbar;
|
|
395
543
|
};
|
|
396
|
-
//
|
|
544
|
+
// 1) Toolbar: injetar ação "Adicionar" se metadata declara create/add e toolbar não tiver
|
|
397
545
|
const hasToolbarAdd = (cfg.toolbar?.actions || []).some((a) => this.isAddLike(a));
|
|
398
|
-
// Procurar mapeamento de ação "adicionar/criar" nos metadados do CRUD
|
|
399
546
|
const addAction = (meta.actions || []).find((a) => this.isAddLike(a));
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
547
|
+
if (addAction) {
|
|
548
|
+
if (hasToolbarAdd) {
|
|
549
|
+
const tb = ensureToolbar();
|
|
550
|
+
tb.visible = true; // garantir visibilidade
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
const tb = ensureToolbar();
|
|
554
|
+
tb.visible = true;
|
|
555
|
+
if (!tb.actions)
|
|
556
|
+
tb.actions = [];
|
|
557
|
+
const injected = {
|
|
558
|
+
id: addAction.id || 'add',
|
|
559
|
+
label: addAction.label || 'Adicionar',
|
|
560
|
+
icon: addAction.icon || 'add',
|
|
561
|
+
type: 'button',
|
|
562
|
+
color: 'primary',
|
|
563
|
+
position: 'end',
|
|
564
|
+
action: addAction.action || 'add',
|
|
565
|
+
};
|
|
566
|
+
tb.actions.push(injected);
|
|
567
|
+
}
|
|
568
|
+
changed = true;
|
|
403
569
|
}
|
|
404
|
-
//
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
570
|
+
// 2) Row actions automáticas (apenas quando host não definiu explicitamente)
|
|
571
|
+
const hostDefinedRow = !!(base && base.actions && base.actions.row);
|
|
572
|
+
const crudDefaults = this.global.get('crud.defaults') || {};
|
|
573
|
+
const autoRow = crudDefaults.autoRowActions !== false; // default true
|
|
574
|
+
if (!hostDefinedRow && autoRow) {
|
|
575
|
+
const acts = (meta.actions || []);
|
|
576
|
+
const hasView = acts.some((a) => String(a.action).toLowerCase() === 'view');
|
|
577
|
+
const hasEdit = acts.some((a) => String(a.action).toLowerCase() === 'edit');
|
|
578
|
+
const includeDelete = !!crudDefaults.includeDeleteInRow && acts.some((a) => String(a.action).toLowerCase() === 'delete');
|
|
579
|
+
const rowActions = [];
|
|
580
|
+
if (hasView)
|
|
581
|
+
rowActions.push({ id: 'view', label: 'Ver', icon: 'visibility', action: 'view', tooltip: 'Ver' });
|
|
582
|
+
if (hasEdit)
|
|
583
|
+
rowActions.push({ id: 'edit', label: 'Editar', icon: 'edit', action: 'edit', tooltip: 'Editar' });
|
|
584
|
+
if (includeDelete)
|
|
585
|
+
rowActions.push({ id: 'delete', label: 'Excluir', icon: 'delete', action: 'delete', tooltip: 'Excluir' });
|
|
586
|
+
if (rowActions.length) {
|
|
587
|
+
cfg.actions = cfg.actions || {};
|
|
588
|
+
cfg.actions.row = {
|
|
589
|
+
enabled: true,
|
|
590
|
+
position: 'end',
|
|
591
|
+
width: cfg.actions?.row?.width || '120px',
|
|
592
|
+
display: crudDefaults.rowActionsDisplay || 'icons',
|
|
593
|
+
trigger: cfg.actions?.row?.trigger || 'hover',
|
|
594
|
+
actions: rowActions,
|
|
595
|
+
header: { label: cfg.actions?.row?.header?.label || '' },
|
|
596
|
+
};
|
|
597
|
+
changed = true;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
// Se nada foi alterado e havia base, não retornar para preservar semântica anterior
|
|
601
|
+
if (!changed) {
|
|
602
|
+
return base ? undefined : cfg;
|
|
409
603
|
}
|
|
410
|
-
// Injetar ação na toolbar
|
|
411
|
-
const tb = ensureToolbar();
|
|
412
|
-
tb.visible = true;
|
|
413
|
-
if (!tb.actions)
|
|
414
|
-
tb.actions = [];
|
|
415
|
-
const injected = {
|
|
416
|
-
id: addAction.id || 'add',
|
|
417
|
-
label: addAction.label || 'Adicionar',
|
|
418
|
-
icon: addAction.icon || 'add',
|
|
419
|
-
type: 'button',
|
|
420
|
-
color: 'primary',
|
|
421
|
-
position: 'end',
|
|
422
|
-
action: addAction.action || 'add',
|
|
423
|
-
};
|
|
424
|
-
tb.actions.push(injected);
|
|
425
604
|
return cfg;
|
|
426
605
|
}
|
|
427
606
|
/** Heurística leve para identificar ações do tipo "adicionar/criar" */
|
|
@@ -451,8 +630,9 @@ class PraxisCrudComponent {
|
|
|
451
630
|
? meta.actions
|
|
452
631
|
: [{ action: 'create' }, { action: 'view' }, { action: 'edit' }];
|
|
453
632
|
return {
|
|
454
|
-
tableId:
|
|
455
|
-
|
|
633
|
+
tableId: this.crudId || 'default',
|
|
634
|
+
componentKeyId: this.componentKeyId() || undefined,
|
|
635
|
+
resourcePath: this.resolveResourcePath(meta) || undefined,
|
|
456
636
|
defaults: meta.defaults,
|
|
457
637
|
actions: baseActions.map((a) => ({
|
|
458
638
|
action: a.action,
|
|
@@ -464,71 +644,97 @@ class PraxisCrudComponent {
|
|
|
464
644
|
idField: meta.resource?.idField || 'id',
|
|
465
645
|
};
|
|
466
646
|
}
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
647
|
+
componentKeyId() {
|
|
648
|
+
const key = this.componentKeys.buildComponentId({
|
|
649
|
+
componentType: 'praxis-crud',
|
|
650
|
+
componentId: this.crudId,
|
|
651
|
+
instanceKey: this.componentInstanceId,
|
|
652
|
+
componentRef: this,
|
|
653
|
+
route: this.route,
|
|
654
|
+
requireComponentId: true,
|
|
655
|
+
});
|
|
656
|
+
if (!key)
|
|
657
|
+
this.warnMissingId();
|
|
658
|
+
return key;
|
|
659
|
+
}
|
|
660
|
+
warnMissingId() {
|
|
661
|
+
if (this.warnedMissingId)
|
|
662
|
+
return;
|
|
663
|
+
this.warnedMissingId = true;
|
|
664
|
+
console.warn('[PraxisCrud] crudId is required for config persistence.');
|
|
665
|
+
}
|
|
666
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisCrudComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
667
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PraxisCrudComponent, isStandalone: true, selector: "praxis-crud", inputs: { metadata: "metadata", crudId: "crudId", componentInstanceId: "componentInstanceId", context: "context", enableCustomization: "enableCustomization" }, outputs: { configureRequested: "configureRequested", afterOpen: "afterOpen", afterClose: "afterClose", afterSave: "afterSave", afterDelete: "afterDelete", error: "error", tableRuntimeConfigChange: "tableRuntimeConfigChange" }, viewQueries: [{ propertyName: "table", first: true, predicate: PraxisTable, descendants: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
668
|
+
@if (shouldRenderTable(resolvedMetadata)) {
|
|
470
669
|
<praxis-table
|
|
471
|
-
[config]="
|
|
472
|
-
[resourcePath]="resolvedMetadata
|
|
473
|
-
[
|
|
670
|
+
[config]="tableConfigForBinding"
|
|
671
|
+
[resourcePath]="resolveResourcePath(resolvedMetadata)"
|
|
672
|
+
[data]="resolveTableData(resolvedMetadata)"
|
|
673
|
+
[tableId]="crudId || 'default'"
|
|
474
674
|
[crudContext]="tableCrudContext"
|
|
475
|
-
[
|
|
675
|
+
[enableCustomization]="enableCustomization"
|
|
476
676
|
(rowAction)="onAction($event.action, $event.row)"
|
|
477
677
|
(toolbarAction)="onAction($event.action)"
|
|
478
678
|
(reset)="onResetPreferences()"
|
|
479
|
-
|
|
679
|
+
(metadataChange)="onTableMetadataChange()"
|
|
680
|
+
(loadingStateChange)="onTableLoadingStateChange($event)"
|
|
480
681
|
></praxis-table>
|
|
481
682
|
} @else {
|
|
482
|
-
@if (
|
|
683
|
+
@if (enableCustomization) {
|
|
483
684
|
<praxis-empty-state-card
|
|
484
685
|
icon="table_rows"
|
|
485
686
|
[title]="'Conecte o CRUD a um recurso'"
|
|
486
|
-
[description]="'Informe os metadados (resourcePath / schema) para habilitar a tabela e ações.'"
|
|
687
|
+
[description]="'Informe os metadados (resourcePath / schema) ou forneça metadata.data para habilitar a tabela e ações.'"
|
|
487
688
|
[primaryAction]="{ label: 'Configurar metadados', icon: 'bolt', action: onConfigureRequested.bind(this) }"
|
|
488
689
|
/>
|
|
489
690
|
}
|
|
490
691
|
}
|
|
491
|
-
`, isInline: true, dependencies: [{ kind: "component", type: PraxisTable, selector: "praxis-table", inputs: ["config", "resourcePath", "
|
|
692
|
+
`, isInline: true, dependencies: [{ kind: "component", type: PraxisTable, selector: "praxis-table", inputs: ["config", "resourcePath", "data", "tableId", "componentInstanceId", "title", "subtitle", "icon", "autoDelete", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "crudContext", "dslFunctionRegistry", "enableCustomization", "dense"], outputs: ["rowClick", "rowDoubleClick", "rowExpansionChange", "rowAction", "toolbarAction", "bulkAction", "columnReorder", "columnReorderAttempt", "beforeDelete", "afterDelete", "deleteError", "beforeBulkDelete", "afterBulkDelete", "bulkDeleteError", "schemaStatusChange", "metadataChange", "loadingStateChange"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone"] }] });
|
|
492
693
|
}
|
|
493
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
694
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisCrudComponent, decorators: [{
|
|
494
695
|
type: Component,
|
|
495
696
|
args: [{
|
|
496
697
|
selector: 'praxis-crud',
|
|
497
698
|
standalone: true,
|
|
498
699
|
imports: [PraxisTable, EmptyStateCardComponent],
|
|
499
700
|
template: `
|
|
500
|
-
@if (
|
|
701
|
+
@if (shouldRenderTable(resolvedMetadata)) {
|
|
501
702
|
<praxis-table
|
|
502
|
-
[config]="
|
|
503
|
-
[resourcePath]="resolvedMetadata
|
|
504
|
-
[
|
|
703
|
+
[config]="tableConfigForBinding"
|
|
704
|
+
[resourcePath]="resolveResourcePath(resolvedMetadata)"
|
|
705
|
+
[data]="resolveTableData(resolvedMetadata)"
|
|
706
|
+
[tableId]="crudId || 'default'"
|
|
505
707
|
[crudContext]="tableCrudContext"
|
|
506
|
-
[
|
|
708
|
+
[enableCustomization]="enableCustomization"
|
|
507
709
|
(rowAction)="onAction($event.action, $event.row)"
|
|
508
710
|
(toolbarAction)="onAction($event.action)"
|
|
509
711
|
(reset)="onResetPreferences()"
|
|
510
|
-
|
|
712
|
+
(metadataChange)="onTableMetadataChange()"
|
|
713
|
+
(loadingStateChange)="onTableLoadingStateChange($event)"
|
|
511
714
|
></praxis-table>
|
|
512
715
|
} @else {
|
|
513
|
-
@if (
|
|
716
|
+
@if (enableCustomization) {
|
|
514
717
|
<praxis-empty-state-card
|
|
515
718
|
icon="table_rows"
|
|
516
719
|
[title]="'Conecte o CRUD a um recurso'"
|
|
517
|
-
[description]="'Informe os metadados (resourcePath / schema) para habilitar a tabela e ações.'"
|
|
720
|
+
[description]="'Informe os metadados (resourcePath / schema) ou forneça metadata.data para habilitar a tabela e ações.'"
|
|
518
721
|
[primaryAction]="{ label: 'Configurar metadados', icon: 'bolt', action: onConfigureRequested.bind(this) }"
|
|
519
722
|
/>
|
|
520
723
|
}
|
|
521
724
|
}
|
|
522
725
|
`,
|
|
523
726
|
}]
|
|
524
|
-
}], propDecorators: {
|
|
525
|
-
type: Input
|
|
526
|
-
|
|
727
|
+
}], propDecorators: { metadata: [{
|
|
728
|
+
type: Input,
|
|
729
|
+
args: [{ required: true }]
|
|
730
|
+
}], crudId: [{
|
|
527
731
|
type: Input,
|
|
528
732
|
args: [{ required: true }]
|
|
733
|
+
}], componentInstanceId: [{
|
|
734
|
+
type: Input
|
|
529
735
|
}], context: [{
|
|
530
736
|
type: Input
|
|
531
|
-
}],
|
|
737
|
+
}], enableCustomization: [{
|
|
532
738
|
type: Input
|
|
533
739
|
}], configureRequested: [{
|
|
534
740
|
type: Output
|
|
@@ -542,6 +748,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
542
748
|
type: Output
|
|
543
749
|
}], error: [{
|
|
544
750
|
type: Output
|
|
751
|
+
}], tableRuntimeConfigChange: [{
|
|
752
|
+
type: Output
|
|
545
753
|
}], table: [{
|
|
546
754
|
type: ViewChild,
|
|
547
755
|
args: [PraxisTable]
|
|
@@ -559,6 +767,7 @@ class DynamicFormDialogHostComponent {
|
|
|
559
767
|
initialSize = {};
|
|
560
768
|
rememberState = false;
|
|
561
769
|
stateKey;
|
|
770
|
+
backDefaults = {};
|
|
562
771
|
destroyRef = inject(DestroyRef);
|
|
563
772
|
resourcePath;
|
|
564
773
|
resourceId;
|
|
@@ -610,10 +819,8 @@ class DynamicFormDialogHostComponent {
|
|
|
610
819
|
this.mode = act === 'edit' ? 'edit' : act === 'view' ? 'view' : 'create';
|
|
611
820
|
// Back config: defaults from metadata/action, overridden by saved per-form config
|
|
612
821
|
const defaults = (this.data.action?.back || this.data.metadata?.defaults?.back) || {};
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
: null;
|
|
616
|
-
this.backConfig = { ...defaults, ...(saved || {}) };
|
|
822
|
+
this.backDefaults = defaults;
|
|
823
|
+
this.backConfig = { ...defaults };
|
|
617
824
|
console.debug('[CRUD:Host] constructed', {
|
|
618
825
|
action: this.data?.action,
|
|
619
826
|
resourcePath: this.resourcePath,
|
|
@@ -644,34 +851,25 @@ class DynamicFormDialogHostComponent {
|
|
|
644
851
|
}
|
|
645
852
|
ngOnInit() {
|
|
646
853
|
// Carregar estado salvo (se habilitado)
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
height: saved?.height ?? this.modal.height,
|
|
653
|
-
};
|
|
654
|
-
console.debug('[CRUD:Host] ngOnInit', {
|
|
655
|
-
initialSize: this.initialSize,
|
|
656
|
-
startMaximized: this.modal.startMaximized,
|
|
657
|
-
fullscreenBreakpoint: this.modal.fullscreenBreakpoint,
|
|
658
|
-
});
|
|
659
|
-
let shouldMax = false;
|
|
660
|
-
if (saved && typeof saved.maximized === 'boolean') {
|
|
661
|
-
shouldMax = !!saved.maximized;
|
|
854
|
+
if (this.rememberState && this.stateKey) {
|
|
855
|
+
this.configStorage
|
|
856
|
+
.loadConfig(this.stateKey)
|
|
857
|
+
.pipe(take(1))
|
|
858
|
+
.subscribe((saved) => this.applySavedState(saved || null));
|
|
662
859
|
}
|
|
663
860
|
else {
|
|
664
|
-
|
|
665
|
-
this.modal.startMaximized ||
|
|
666
|
-
(this.modal.fullscreenBreakpoint &&
|
|
667
|
-
window.innerWidth <= this.modal.fullscreenBreakpoint);
|
|
861
|
+
this.applySavedState(null);
|
|
668
862
|
}
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
863
|
+
// Carregar override de back config se houver formId
|
|
864
|
+
if (this.data.action?.formId) {
|
|
865
|
+
this.configStorage
|
|
866
|
+
.loadConfig(`crud-back:${this.data.action.formId}`)
|
|
867
|
+
.pipe(take(1))
|
|
868
|
+
.subscribe((saved) => {
|
|
869
|
+
if (saved && typeof saved === 'object') {
|
|
870
|
+
this.backConfig = { ...this.backDefaults, ...saved };
|
|
871
|
+
}
|
|
872
|
+
});
|
|
675
873
|
}
|
|
676
874
|
}
|
|
677
875
|
onSave(result) {
|
|
@@ -695,6 +893,7 @@ class DynamicFormDialogHostComponent {
|
|
|
695
893
|
cancelText: this.texts.discardCancel,
|
|
696
894
|
type: 'warning',
|
|
697
895
|
},
|
|
896
|
+
autoFocus: false,
|
|
698
897
|
});
|
|
699
898
|
ref
|
|
700
899
|
.afterClosed()
|
|
@@ -731,15 +930,46 @@ class DynamicFormDialogHostComponent {
|
|
|
731
930
|
const style = pane ? getComputedStyle(pane) : null;
|
|
732
931
|
const currentWidth = style?.width || this.initialSize.width;
|
|
733
932
|
const currentHeight = style?.height || this.initialSize.height;
|
|
734
|
-
this.configStorage
|
|
933
|
+
this.configStorage
|
|
934
|
+
.saveConfig(this.stateKey, {
|
|
735
935
|
maximized: this.maximized,
|
|
736
936
|
width: currentWidth,
|
|
737
937
|
height: currentHeight,
|
|
938
|
+
})
|
|
939
|
+
.pipe(take(1))
|
|
940
|
+
.subscribe({ error: () => { } });
|
|
941
|
+
}
|
|
942
|
+
applySavedState(saved) {
|
|
943
|
+
this.initialSize = {
|
|
944
|
+
width: saved?.width ?? this.modal.width,
|
|
945
|
+
height: saved?.height ?? this.modal.height,
|
|
946
|
+
};
|
|
947
|
+
console.debug('[CRUD:Host] ngOnInit', {
|
|
948
|
+
initialSize: this.initialSize,
|
|
949
|
+
startMaximized: this.modal.startMaximized,
|
|
950
|
+
fullscreenBreakpoint: this.modal.fullscreenBreakpoint,
|
|
738
951
|
});
|
|
952
|
+
let shouldMax = false;
|
|
953
|
+
if (saved && typeof saved.maximized === 'boolean') {
|
|
954
|
+
shouldMax = !!saved.maximized;
|
|
955
|
+
}
|
|
956
|
+
else {
|
|
957
|
+
shouldMax =
|
|
958
|
+
this.modal.startMaximized ||
|
|
959
|
+
(this.modal.fullscreenBreakpoint &&
|
|
960
|
+
window.innerWidth <= this.modal.fullscreenBreakpoint);
|
|
961
|
+
}
|
|
962
|
+
if (shouldMax) {
|
|
963
|
+
this.toggleMaximize(true);
|
|
964
|
+
}
|
|
965
|
+
else if (this.initialSize.width || this.initialSize.height) {
|
|
966
|
+
this.dialogRef.updateSize(this.initialSize.width, this.initialSize.height);
|
|
967
|
+
this.dialogRef.updatePosition();
|
|
968
|
+
}
|
|
739
969
|
}
|
|
740
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.
|
|
741
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.
|
|
742
|
-
<div class="dialog-header">
|
|
970
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicFormDialogHostComponent, deps: [{ token: MatDialogRef }, { token: MAT_DIALOG_DATA }, { token: DialogService }, { token: i2.GenericCrudService }, { token: ASYNC_CONFIG_STORAGE }], target: i0.ɵɵFactoryTarget.Component });
|
|
971
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: DynamicFormDialogHostComponent, isStandalone: true, selector: "praxis-dynamic-form-dialog-host", host: { properties: { "attr.data-density": "modal.density || \"default\"" }, classAttribute: "praxis-dialog" }, providers: [GenericCrudService], viewQueries: [{ propertyName: "formComp", first: true, predicate: PraxisDynamicForm, descendants: true }], ngImport: i0, template: `
|
|
972
|
+
<div mat-dialog-title class="dialog-header">
|
|
743
973
|
<h2 id="crudDialogTitle" class="dialog-title">
|
|
744
974
|
{{ data.action?.label || texts.title }}
|
|
745
975
|
</h2>
|
|
@@ -749,13 +979,9 @@ class DynamicFormDialogHostComponent {
|
|
|
749
979
|
mat-icon-button
|
|
750
980
|
type="button"
|
|
751
981
|
(click)="toggleMaximize()"
|
|
752
|
-
[attr.aria-label]="
|
|
753
|
-
maximized ? texts.restoreLabel : texts.maximizeLabel
|
|
754
|
-
"
|
|
982
|
+
[attr.aria-label]="maximized ? texts.restoreLabel : texts.maximizeLabel"
|
|
755
983
|
>
|
|
756
|
-
<mat-icon>
|
|
757
|
-
maximized ? 'close_fullscreen' : 'open_in_full'
|
|
758
|
-
}}</mat-icon>
|
|
984
|
+
<mat-icon [praxisIcon]="maximized ? 'close_fullscreen' : 'open_in_full'"></mat-icon>
|
|
759
985
|
</button>
|
|
760
986
|
}
|
|
761
987
|
<button
|
|
@@ -778,14 +1004,15 @@ class DynamicFormDialogHostComponent {
|
|
|
778
1004
|
[resourcePath]="resourcePath"
|
|
779
1005
|
[resourceId]="resourceId"
|
|
780
1006
|
[mode]="mode"
|
|
1007
|
+
[presentationModeGlobal]="mode === 'view' ? true : null"
|
|
781
1008
|
[backConfig]="backConfig"
|
|
782
1009
|
(formSubmit)="onSave($event)"
|
|
783
1010
|
(formCancel)="onCancel()"
|
|
784
1011
|
></praxis-dynamic-form>
|
|
785
1012
|
</mat-dialog-content>
|
|
786
|
-
`, isInline: true, styles: [":host{--dlg-header-h: 56px;--dlg-footer-h: 56px;--dlg-pad: 16px;display:flex;flex-direction:column;height:100%;overflow:hidden}:host([data-density=\"compact\"]){--dlg-header-h: 44px;--dlg-footer-h: 44px;--dlg-pad: 12px}.dialog-header{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:var(--dlg-pad);padding:0 var(--dlg-pad);height:var(--dlg-header-h);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)}.dialog-title{margin:0;font:inherit;font-weight:600;color:var(--md-sys-color-on-surface)}.spacer{flex:1}.dialog-content{overflow:auto;padding:var(--dlg-pad);max-height:calc(100svh - var(--dlg-header-h) - 32px)}.dialog-header button.mat-icon-button{color:var(--md-sys-color-on-surface-variant)}.dialog-header button.mat-icon-button:hover{color:var(--md-sys-color-primary);background:
|
|
1013
|
+
`, isInline: true, styles: [":host{--dlg-header-h: 56px;--dlg-footer-h: 56px;--dlg-pad: 16px;display:flex;flex-direction:column;height:100%;overflow:hidden}:host([data-density=\"compact\"]){--dlg-header-h: 44px;--dlg-footer-h: 44px;--dlg-pad: 12px}.dialog-header{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:var(--dlg-pad);padding:0 var(--dlg-pad);height:var(--dlg-header-h);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)}.dialog-title{margin:0;font:inherit;font-weight:600;color:var(--md-sys-color-on-surface)}.spacer{flex:1}.dialog-content{overflow:auto;padding:var(--dlg-pad);max-height:calc(100svh - var(--dlg-header-h) - 32px)}.dialog-header button.mat-icon-button{color:var(--md-sys-color-on-surface-variant)}.dialog-header button.mat-icon-button:hover{color:var(--md-sys-color-primary);background:var(--md-sys-color-primary-container)}.dialog-footer{position:sticky;bottom:0;z-index:1;padding:var(--dlg-pad)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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: MatButtonModule }, { kind: "component", type: i4.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: PraxisDynamicForm, selector: "praxis-dynamic-form", inputs: ["resourcePath", "resourceId", "editorialContext", "mode", "config", "schemaSource", "enableCustomization", "formId", "componentInstanceId", "layout", "backConfig", "hooks", "removeEmptyContainersOnSave", "reactiveValidation", "reactiveValidationDebounceMs", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "readonlyModeGlobal", "disabledModeGlobal", "presentationModeGlobal", "visibleGlobal", "customEndpoints"], outputs: ["formSubmit", "formCancel", "formReset", "configChange", "formReady", "valueChange", "syncCompleted", "initializationError", "loadingStateChange", "enableCustomizationChange", "customAction", "actionConfirmation", "schemaStatusChange", "fieldRenderError", "widgetEvent"] }] });
|
|
787
1014
|
}
|
|
788
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
1015
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicFormDialogHostComponent, decorators: [{
|
|
789
1016
|
type: Component,
|
|
790
1017
|
args: [{ selector: 'praxis-dynamic-form-dialog-host', standalone: true, imports: [
|
|
791
1018
|
CommonModule,
|
|
@@ -798,7 +1025,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
798
1025
|
class: 'praxis-dialog',
|
|
799
1026
|
'[attr.data-density]': 'modal.density || "default"',
|
|
800
1027
|
}, template: `
|
|
801
|
-
<div class="dialog-header">
|
|
1028
|
+
<div mat-dialog-title class="dialog-header">
|
|
802
1029
|
<h2 id="crudDialogTitle" class="dialog-title">
|
|
803
1030
|
{{ data.action?.label || texts.title }}
|
|
804
1031
|
</h2>
|
|
@@ -808,13 +1035,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
808
1035
|
mat-icon-button
|
|
809
1036
|
type="button"
|
|
810
1037
|
(click)="toggleMaximize()"
|
|
811
|
-
[attr.aria-label]="
|
|
812
|
-
maximized ? texts.restoreLabel : texts.maximizeLabel
|
|
813
|
-
"
|
|
1038
|
+
[attr.aria-label]="maximized ? texts.restoreLabel : texts.maximizeLabel"
|
|
814
1039
|
>
|
|
815
|
-
<mat-icon>
|
|
816
|
-
maximized ? 'close_fullscreen' : 'open_in_full'
|
|
817
|
-
}}</mat-icon>
|
|
1040
|
+
<mat-icon [praxisIcon]="maximized ? 'close_fullscreen' : 'open_in_full'"></mat-icon>
|
|
818
1041
|
</button>
|
|
819
1042
|
}
|
|
820
1043
|
<button
|
|
@@ -837,12 +1060,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
837
1060
|
[resourcePath]="resourcePath"
|
|
838
1061
|
[resourceId]="resourceId"
|
|
839
1062
|
[mode]="mode"
|
|
1063
|
+
[presentationModeGlobal]="mode === 'view' ? true : null"
|
|
840
1064
|
[backConfig]="backConfig"
|
|
841
1065
|
(formSubmit)="onSave($event)"
|
|
842
1066
|
(formCancel)="onCancel()"
|
|
843
1067
|
></praxis-dynamic-form>
|
|
844
1068
|
</mat-dialog-content>
|
|
845
|
-
`, styles: [":host{--dlg-header-h: 56px;--dlg-footer-h: 56px;--dlg-pad: 16px;display:flex;flex-direction:column;height:100%;overflow:hidden}:host([data-density=\"compact\"]){--dlg-header-h: 44px;--dlg-footer-h: 44px;--dlg-pad: 12px}.dialog-header{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:var(--dlg-pad);padding:0 var(--dlg-pad);height:var(--dlg-header-h);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)}.dialog-title{margin:0;font:inherit;font-weight:600;color:var(--md-sys-color-on-surface)}.spacer{flex:1}.dialog-content{overflow:auto;padding:var(--dlg-pad);max-height:calc(100svh - var(--dlg-header-h) - 32px)}.dialog-header button.mat-icon-button{color:var(--md-sys-color-on-surface-variant)}.dialog-header button.mat-icon-button:hover{color:var(--md-sys-color-primary);background:
|
|
1069
|
+
`, styles: [":host{--dlg-header-h: 56px;--dlg-footer-h: 56px;--dlg-pad: 16px;display:flex;flex-direction:column;height:100%;overflow:hidden}:host([data-density=\"compact\"]){--dlg-header-h: 44px;--dlg-footer-h: 44px;--dlg-pad: 12px}.dialog-header{position:sticky;top:0;z-index:1;display:flex;align-items:center;gap:var(--dlg-pad);padding:0 var(--dlg-pad);height:var(--dlg-header-h);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)}.dialog-title{margin:0;font:inherit;font-weight:600;color:var(--md-sys-color-on-surface)}.spacer{flex:1}.dialog-content{overflow:auto;padding:var(--dlg-pad);max-height:calc(100svh - var(--dlg-header-h) - 32px)}.dialog-header button.mat-icon-button{color:var(--md-sys-color-on-surface-variant)}.dialog-header button.mat-icon-button:hover{color:var(--md-sys-color-primary);background:var(--md-sys-color-primary-container)}.dialog-footer{position:sticky;bottom:0;z-index:1;padding:var(--dlg-pad)}\n"] }]
|
|
846
1070
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
847
1071
|
type: Inject,
|
|
848
1072
|
args: [MatDialogRef]
|
|
@@ -851,7 +1075,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
851
1075
|
args: [MAT_DIALOG_DATA]
|
|
852
1076
|
}] }, { type: DialogService }, { type: i2.GenericCrudService }, { type: undefined, decorators: [{
|
|
853
1077
|
type: Inject,
|
|
854
|
-
args: [
|
|
1078
|
+
args: [ASYNC_CONFIG_STORAGE]
|
|
855
1079
|
}] }], propDecorators: { formComp: [{
|
|
856
1080
|
type: ViewChild,
|
|
857
1081
|
args: [PraxisDynamicForm]
|
|
@@ -874,18 +1098,28 @@ const PRAXIS_CRUD_COMPONENT_METADATA = {
|
|
|
874
1098
|
{
|
|
875
1099
|
name: 'metadata',
|
|
876
1100
|
type: 'CrudMetadata | string',
|
|
877
|
-
description: 'Metadados de configuração do CRUD',
|
|
1101
|
+
description: 'Metadados de configuração do CRUD (fonte remota via resource.path ou local via metadata.data).',
|
|
1102
|
+
},
|
|
1103
|
+
{
|
|
1104
|
+
name: 'crudId',
|
|
1105
|
+
type: 'string',
|
|
1106
|
+
description: 'Identificador do CRUD (base para tabela/formulário e persistência)',
|
|
1107
|
+
},
|
|
1108
|
+
{
|
|
1109
|
+
name: 'componentInstanceId',
|
|
1110
|
+
type: 'string',
|
|
1111
|
+
description: 'Identificador opcional para múltiplas instâncias na mesma rota',
|
|
878
1112
|
},
|
|
879
1113
|
{
|
|
880
1114
|
name: 'context',
|
|
881
1115
|
type: 'Record<string, unknown>',
|
|
882
|
-
description: 'Contexto
|
|
1116
|
+
description: 'Contexto opaco do host. A implementacao atual o expõe como Input, mas nao o consome no launcher nem no tableCrudContext.',
|
|
883
1117
|
},
|
|
884
1118
|
{
|
|
885
|
-
name: '
|
|
1119
|
+
name: 'enableCustomization',
|
|
886
1120
|
type: 'boolean',
|
|
887
1121
|
default: false,
|
|
888
|
-
description: 'Habilita modo de
|
|
1122
|
+
description: 'Habilita modo de customização do layout',
|
|
889
1123
|
},
|
|
890
1124
|
],
|
|
891
1125
|
outputs: [
|
|
@@ -916,6 +1150,108 @@ const PRAXIS_CRUD_COMPONENT_METADATA = {
|
|
|
916
1150
|
description: 'Emitido quando CTA de configuração é acionado em modo edição',
|
|
917
1151
|
},
|
|
918
1152
|
],
|
|
1153
|
+
actions: [
|
|
1154
|
+
{
|
|
1155
|
+
id: 'create',
|
|
1156
|
+
label: 'Criar',
|
|
1157
|
+
icon: 'add',
|
|
1158
|
+
description: 'Emite evento ao abrir fluxo de criação',
|
|
1159
|
+
emit: 'afterOpen',
|
|
1160
|
+
payloadSchema: {
|
|
1161
|
+
type: 'object',
|
|
1162
|
+
properties: {
|
|
1163
|
+
action: { type: 'string', description: 'Ação executada' },
|
|
1164
|
+
mode: { type: 'string', description: 'Modo de abertura' },
|
|
1165
|
+
},
|
|
1166
|
+
required: ['action', 'mode'],
|
|
1167
|
+
example: { action: 'create', mode: 'route' },
|
|
1168
|
+
},
|
|
1169
|
+
scope: 'toolbar',
|
|
1170
|
+
},
|
|
1171
|
+
{
|
|
1172
|
+
id: 'view',
|
|
1173
|
+
label: 'Visualizar',
|
|
1174
|
+
icon: 'visibility',
|
|
1175
|
+
description: 'Emite evento ao abrir fluxo de visualização',
|
|
1176
|
+
emit: 'afterOpen',
|
|
1177
|
+
payloadSchema: {
|
|
1178
|
+
type: 'object',
|
|
1179
|
+
properties: {
|
|
1180
|
+
action: { type: 'string', description: 'Ação executada' },
|
|
1181
|
+
mode: { type: 'string', description: 'Modo de abertura' },
|
|
1182
|
+
},
|
|
1183
|
+
required: ['action', 'mode'],
|
|
1184
|
+
example: { action: 'view', mode: 'modal' },
|
|
1185
|
+
},
|
|
1186
|
+
scope: 'context',
|
|
1187
|
+
},
|
|
1188
|
+
{
|
|
1189
|
+
id: 'edit',
|
|
1190
|
+
label: 'Editar',
|
|
1191
|
+
icon: 'edit',
|
|
1192
|
+
description: 'Emite evento ao abrir fluxo de edição',
|
|
1193
|
+
emit: 'afterOpen',
|
|
1194
|
+
payloadSchema: {
|
|
1195
|
+
type: 'object',
|
|
1196
|
+
properties: {
|
|
1197
|
+
action: { type: 'string', description: 'Ação executada' },
|
|
1198
|
+
mode: { type: 'string', description: 'Modo de abertura' },
|
|
1199
|
+
},
|
|
1200
|
+
required: ['action', 'mode'],
|
|
1201
|
+
example: { action: 'edit', mode: 'drawer' },
|
|
1202
|
+
},
|
|
1203
|
+
scope: 'context',
|
|
1204
|
+
},
|
|
1205
|
+
{
|
|
1206
|
+
id: 'delete',
|
|
1207
|
+
label: 'Excluir',
|
|
1208
|
+
icon: 'delete',
|
|
1209
|
+
description: 'Emite evento após exclusão',
|
|
1210
|
+
emit: 'afterDelete',
|
|
1211
|
+
payloadSchema: {
|
|
1212
|
+
type: 'object',
|
|
1213
|
+
properties: {
|
|
1214
|
+
id: { type: 'string | number', description: 'ID do registro (string ou number)' },
|
|
1215
|
+
},
|
|
1216
|
+
required: ['id'],
|
|
1217
|
+
example: { id: '123' },
|
|
1218
|
+
},
|
|
1219
|
+
scope: 'context',
|
|
1220
|
+
},
|
|
1221
|
+
{
|
|
1222
|
+
id: 'save',
|
|
1223
|
+
label: 'Salvar',
|
|
1224
|
+
icon: 'save',
|
|
1225
|
+
description: 'Emite evento após salvar',
|
|
1226
|
+
emit: 'afterSave',
|
|
1227
|
+
payloadSchema: {
|
|
1228
|
+
type: 'object',
|
|
1229
|
+
properties: {
|
|
1230
|
+
id: { type: 'string | number', description: 'ID do registro (string ou number)' },
|
|
1231
|
+
data: { type: 'object', description: 'Dados salvos' },
|
|
1232
|
+
},
|
|
1233
|
+
required: ['id', 'data'],
|
|
1234
|
+
example: { id: '123', data: {} },
|
|
1235
|
+
},
|
|
1236
|
+
scope: 'shell',
|
|
1237
|
+
},
|
|
1238
|
+
{
|
|
1239
|
+
id: 'close',
|
|
1240
|
+
label: 'Fechar',
|
|
1241
|
+
icon: 'close',
|
|
1242
|
+
description: 'Emite evento ao fechar o diálogo',
|
|
1243
|
+
emit: 'afterClose',
|
|
1244
|
+
scope: 'shell',
|
|
1245
|
+
},
|
|
1246
|
+
{
|
|
1247
|
+
id: 'configure',
|
|
1248
|
+
label: 'Configurar',
|
|
1249
|
+
icon: 'tune',
|
|
1250
|
+
description: 'Emite evento ao abrir configuração',
|
|
1251
|
+
emit: 'configureRequested',
|
|
1252
|
+
scope: 'shell',
|
|
1253
|
+
},
|
|
1254
|
+
],
|
|
919
1255
|
tags: ['widget', 'crud', 'configurable', 'hasWizard', 'stable'],
|
|
920
1256
|
lib: '@praxisui/crud',
|
|
921
1257
|
};
|
|
@@ -939,8 +1275,8 @@ class CrudPageHeaderComponent {
|
|
|
939
1275
|
sticky = true;
|
|
940
1276
|
divider = true;
|
|
941
1277
|
returnTo;
|
|
942
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.
|
|
943
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.
|
|
1278
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: CrudPageHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1279
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: CrudPageHeaderComponent, isStandalone: true, selector: "praxis-crud-page-header", inputs: { title: "title", backLabel: "backLabel", showBack: "showBack", variant: "variant", sticky: "sticky", divider: "divider", returnTo: "returnTo" }, ngImport: i0, template: `
|
|
944
1280
|
<header
|
|
945
1281
|
class="crud-header"
|
|
946
1282
|
[class.sticky]="sticky"
|
|
@@ -966,9 +1302,9 @@ class CrudPageHeaderComponent {
|
|
|
966
1302
|
<ng-content></ng-content>
|
|
967
1303
|
</div>
|
|
968
1304
|
</header>
|
|
969
|
-
`, isInline: true, styles: [".crud-header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:8px 0;background:var(--md-sys-color-surface
|
|
1305
|
+
`, isInline: true, styles: [".crud-header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:8px 0;background:var(--md-sys-color-surface)}.crud-header.sticky{position:sticky;top:0;z-index:10;-webkit-backdrop-filter:saturate(110%);backdrop-filter:saturate(110%)}.crud-header.with-divider{border-bottom:1px solid var(--md-sys-color-outline-variant)}.left{display:flex;align-items:center;gap:8px;min-width:0}.right{display:flex;align-items:center;gap:8px}.title{margin:0;font-weight:600;color:var(--md-sys-color-on-surface, currentColor);font:var(--mdc-typography-title-large, 600 20px/28px system-ui);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.back-btn{display:inline-flex;align-items:center;gap:6px;text-decoration:none}.back-btn .mat-mdc-button{padding-left:0}.back-btn mat-icon{color:var(--md-sys-color-on-surface-variant, currentColor)}.back-btn .label{color:var(--md-sys-color-on-surface-variant, currentColor)}.back-btn.ghost{border-radius:20px;padding:4px 8px}.back-btn.ghost:hover{background:var(--md-sys-color-primary-container)}.back-btn.tonal{border-radius:20px;padding:4px 10px;background:var(--md-sys-color-surface-container);box-shadow:inset 0 0 0 1px var(--md-sys-color-outline-variant)}.back-btn.tonal:hover{background:var(--md-sys-color-primary-container)}.back-btn.outlined{border-radius:20px;padding:4px 10px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface)}.back-btn.outlined:hover{border-color:var(--md-sys-color-primary);background:var(--md-sys-color-primary-container)}@media(max-width:599px){.label.hide-on-narrow{display:none}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i4.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: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }] });
|
|
970
1306
|
}
|
|
971
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.
|
|
1307
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: CrudPageHeaderComponent, decorators: [{
|
|
972
1308
|
type: Component,
|
|
973
1309
|
args: [{ selector: 'praxis-crud-page-header', standalone: true, imports: [CommonModule, RouterLink, MatButtonModule, MatIconModule, PraxisIconDirective], template: `
|
|
974
1310
|
<header
|
|
@@ -996,7 +1332,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
996
1332
|
<ng-content></ng-content>
|
|
997
1333
|
</div>
|
|
998
1334
|
</header>
|
|
999
|
-
`, styles: [".crud-header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:8px 0;background:var(--md-sys-color-surface
|
|
1335
|
+
`, styles: [".crud-header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:8px 0;background:var(--md-sys-color-surface)}.crud-header.sticky{position:sticky;top:0;z-index:10;-webkit-backdrop-filter:saturate(110%);backdrop-filter:saturate(110%)}.crud-header.with-divider{border-bottom:1px solid var(--md-sys-color-outline-variant)}.left{display:flex;align-items:center;gap:8px;min-width:0}.right{display:flex;align-items:center;gap:8px}.title{margin:0;font-weight:600;color:var(--md-sys-color-on-surface, currentColor);font:var(--mdc-typography-title-large, 600 20px/28px system-ui);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.back-btn{display:inline-flex;align-items:center;gap:6px;text-decoration:none}.back-btn .mat-mdc-button{padding-left:0}.back-btn mat-icon{color:var(--md-sys-color-on-surface-variant, currentColor)}.back-btn .label{color:var(--md-sys-color-on-surface-variant, currentColor)}.back-btn.ghost{border-radius:20px;padding:4px 8px}.back-btn.ghost:hover{background:var(--md-sys-color-primary-container)}.back-btn.tonal{border-radius:20px;padding:4px 10px;background:var(--md-sys-color-surface-container);box-shadow:inset 0 0 0 1px var(--md-sys-color-outline-variant)}.back-btn.tonal:hover{background:var(--md-sys-color-primary-container)}.back-btn.outlined{border-radius:20px;padding:4px 10px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface)}.back-btn.outlined:hover{border-color:var(--md-sys-color-primary);background:var(--md-sys-color-primary-container)}@media(max-width:599px){.label.hide-on-narrow{display:none}}\n"] }]
|
|
1000
1336
|
}], propDecorators: { title: [{
|
|
1001
1337
|
type: Input
|
|
1002
1338
|
}], backLabel: [{
|
|
@@ -1013,6 +1349,96 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
1013
1349
|
type: Input
|
|
1014
1350
|
}] } });
|
|
1015
1351
|
|
|
1352
|
+
/**
|
|
1353
|
+
* Capabilities catalog for CrudMetadata (Praxis CRUD).
|
|
1354
|
+
*/
|
|
1355
|
+
const ENUMS = {
|
|
1356
|
+
openMode: ['route', 'modal', 'drawer'],
|
|
1357
|
+
paramTarget: ['routeParam', 'query', 'input'],
|
|
1358
|
+
headerVariant: ['ghost', 'tonal', 'outlined'],
|
|
1359
|
+
toolbarActionType: ['button', 'icon', 'fab', 'menu'],
|
|
1360
|
+
toolbarActionAppearance: ['filled', 'outlined', 'elevated', 'text', 'tonal'],
|
|
1361
|
+
toolbarActionPosition: ['start', 'end'],
|
|
1362
|
+
backStrategy: ['auto', 'close', 'navigate'],
|
|
1363
|
+
};
|
|
1364
|
+
const CRUD_AI_CAPABILITIES = {
|
|
1365
|
+
version: 'v1.0',
|
|
1366
|
+
enums: ENUMS,
|
|
1367
|
+
targets: ['praxis-crud', 'crud-view', 'generic-crud'],
|
|
1368
|
+
notes: [
|
|
1369
|
+
'CrudMetadata agrega TableConfig e FormConfig; use os catalogos de tabela e formulario para detalhes.',
|
|
1370
|
+
'actions[] herda campos de RowAction e ToolbarAction; combine com openMode/route/formId quando aplicavel.',
|
|
1371
|
+
],
|
|
1372
|
+
capabilities: [
|
|
1373
|
+
// --- Metadata root ---
|
|
1374
|
+
{
|
|
1375
|
+
path: 'component',
|
|
1376
|
+
category: 'meta',
|
|
1377
|
+
valueKind: 'enum',
|
|
1378
|
+
allowedValues: ['praxis-crud'],
|
|
1379
|
+
description: 'Identificador fixo do componente.',
|
|
1380
|
+
critical: true,
|
|
1381
|
+
},
|
|
1382
|
+
// --- Resource ---
|
|
1383
|
+
{ path: 'resource', category: 'resource', valueKind: 'object', description: 'Recurso principal do CRUD.' },
|
|
1384
|
+
{ path: 'resource.path', category: 'resource', valueKind: 'string', description: 'Endpoint base do recurso.' },
|
|
1385
|
+
{ path: 'resource.idField', category: 'resource', valueKind: 'string', description: 'Campo identificador do recurso.' },
|
|
1386
|
+
{ path: 'resource.endpointKey', category: 'resource', valueKind: 'string', description: 'Chave de endpoint (ApiEndpoint).' },
|
|
1387
|
+
// --- Table / Form ---
|
|
1388
|
+
{ path: 'table', category: 'table', valueKind: 'object', description: 'TableConfig completo (usar catalogo de tabela).' },
|
|
1389
|
+
{ path: 'form', category: 'form', valueKind: 'object', description: 'FormConfig completo (usar catalogo de formulario).' },
|
|
1390
|
+
// --- Defaults ---
|
|
1391
|
+
{ path: 'defaults', category: 'defaults', valueKind: 'object', description: 'Defaults aplicados a acoes e UI.' },
|
|
1392
|
+
{ path: 'defaults.openMode', category: 'defaults', valueKind: 'enum', allowedValues: ENUMS.openMode, description: 'Modo padrao de abertura (route/modal/drawer).' },
|
|
1393
|
+
{ path: 'defaults.modal', category: 'defaults', valueKind: 'object', description: 'Config de dialog (DialogConfig).' },
|
|
1394
|
+
{ path: 'defaults.back', category: 'navigation', valueKind: 'object', description: 'Config de retorno (BackConfig).' },
|
|
1395
|
+
{ path: 'defaults.back.strategy', category: 'navigation', valueKind: 'enum', allowedValues: ENUMS.backStrategy, description: 'Estrategia de retorno.' },
|
|
1396
|
+
{ path: 'defaults.back.returnTo', category: 'navigation', valueKind: 'string', description: 'Rota de retorno quando strategy=navigate.' },
|
|
1397
|
+
{ path: 'defaults.back.confirmOnDirty', category: 'navigation', valueKind: 'boolean', description: 'Confirmar ao sair com alteracoes.' },
|
|
1398
|
+
{ path: 'defaults.header', category: 'defaults', valueKind: 'object', description: 'Config do header do CRUD.' },
|
|
1399
|
+
{ path: 'defaults.header.showBack', category: 'defaults', valueKind: 'boolean', description: 'Exibir botao voltar.' },
|
|
1400
|
+
{ path: 'defaults.header.backLabel', category: 'defaults', valueKind: 'string', description: 'Label do botao voltar.' },
|
|
1401
|
+
{ path: 'defaults.header.variant', category: 'defaults', valueKind: 'enum', allowedValues: ENUMS.headerVariant, description: 'Variante visual do header.' },
|
|
1402
|
+
{ path: 'defaults.header.sticky', category: 'defaults', valueKind: 'boolean', description: 'Header fixo.' },
|
|
1403
|
+
{ path: 'defaults.header.breadcrumbs', category: 'defaults', valueKind: 'boolean', description: 'Exibir breadcrumbs.' },
|
|
1404
|
+
{ path: 'defaults.header.divider', category: 'defaults', valueKind: 'boolean', description: 'Exibir divisor no header.' },
|
|
1405
|
+
// --- Actions ---
|
|
1406
|
+
{ path: 'actions', category: 'actions', valueKind: 'array', description: 'Acoes disponiveis no CRUD.' },
|
|
1407
|
+
{ path: 'actions[].id', category: 'actions', valueKind: 'string', description: 'ID da acao (RowAction/ToolbarAction).' },
|
|
1408
|
+
{ path: 'actions[].label', category: 'actions', valueKind: 'string', description: 'Label da acao.' },
|
|
1409
|
+
{ path: 'actions[].icon', category: 'actions', valueKind: 'string', description: 'Icone da acao.' },
|
|
1410
|
+
{ path: 'actions[].color', category: 'actions', valueKind: 'string', description: 'Cor da acao.' },
|
|
1411
|
+
{ path: 'actions[].disabled', category: 'actions', valueKind: 'boolean', description: 'Acao desabilitada.' },
|
|
1412
|
+
{ path: 'actions[].action', category: 'actions', valueKind: 'string', description: 'Nome da acao emitida.' },
|
|
1413
|
+
{ path: 'actions[].tooltip', category: 'actions', valueKind: 'string', description: 'Tooltip da acao.' },
|
|
1414
|
+
{ path: 'actions[].requiresConfirmation', category: 'actions', valueKind: 'boolean', description: 'Exige confirmacao.' },
|
|
1415
|
+
{ path: 'actions[].visibleWhen', category: 'actions', valueKind: 'expression', description: 'Condicao de visibilidade (RowAction/ToolbarAction).' },
|
|
1416
|
+
{ path: 'actions[].separator', category: 'actions', valueKind: 'boolean', description: 'Separador apos a acao (RowAction).' },
|
|
1417
|
+
{ path: 'actions[].autoDelete', category: 'actions', valueKind: 'boolean', description: 'Habilita auto delete (RowAction).' },
|
|
1418
|
+
{ path: 'actions[].deleteEndpoint', category: 'actions', valueKind: 'string', description: 'Endpoint custom para delete (RowAction).' },
|
|
1419
|
+
{ path: 'actions[].type', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.toolbarActionType, description: 'Tipo de acao (ToolbarAction).' },
|
|
1420
|
+
{ path: 'actions[].appearance', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.toolbarActionAppearance, description: 'Aparencia do botao (ToolbarAction).' },
|
|
1421
|
+
{ path: 'actions[].position', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.toolbarActionPosition, description: 'Posicao da acao (ToolbarAction).' },
|
|
1422
|
+
{ path: 'actions[].order', category: 'actions', valueKind: 'number', description: 'Ordem de exibicao (ToolbarAction).' },
|
|
1423
|
+
{ path: 'actions[].shortcut', category: 'actions', valueKind: 'string', description: 'Atalho de teclado (ToolbarAction).' },
|
|
1424
|
+
{ path: 'actions[].children', category: 'actions', valueKind: 'array', description: 'Sub-acoes (menu).' },
|
|
1425
|
+
{ path: 'actions[].openMode', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.openMode, description: 'Modo de abertura da acao.' },
|
|
1426
|
+
{ path: 'actions[].route', category: 'actions', valueKind: 'string', description: 'Rota para openMode=route.' },
|
|
1427
|
+
{ path: 'actions[].formId', category: 'actions', valueKind: 'string', description: 'FormId canonico para modal/drawer; se ausente, o host precisa resolve-lo por override por acao antes da abertura.' },
|
|
1428
|
+
{ path: 'actions[].params', category: 'actions', valueKind: 'array', description: 'Mapeamento de parametros.' },
|
|
1429
|
+
{ path: 'actions[].params[].from', category: 'actions', valueKind: 'string', description: 'Origem do parametro.' },
|
|
1430
|
+
{ path: 'actions[].params[].to', category: 'actions', valueKind: 'enum', allowedValues: ENUMS.paramTarget, description: 'Destino do parametro.' },
|
|
1431
|
+
{ path: 'actions[].params[].name', category: 'actions', valueKind: 'string', description: 'Nome do parametro no destino.' },
|
|
1432
|
+
{ path: 'actions[].back', category: 'navigation', valueKind: 'object', description: 'BackConfig por acao.' },
|
|
1433
|
+
{ path: 'actions[].back.strategy', category: 'navigation', valueKind: 'enum', allowedValues: ENUMS.backStrategy, description: 'Estrategia de retorno da acao (auto, close, navigate).' },
|
|
1434
|
+
{ path: 'actions[].back.returnTo', category: 'navigation', valueKind: 'string', description: 'Rota ou destino usado quando a estrategia de retorno for navigate.' },
|
|
1435
|
+
{ path: 'actions[].back.confirmOnDirty', category: 'navigation', valueKind: 'boolean', description: 'Solicita confirmacao antes de sair com alteracoes nao salvas.' },
|
|
1436
|
+
// --- i18n ---
|
|
1437
|
+
{ path: 'i18n', category: 'i18n', valueKind: 'object', description: 'Mensagens i18n do CRUD.' },
|
|
1438
|
+
{ path: 'i18n.crudDialog', category: 'i18n', valueKind: 'object', description: 'Mapa de textos do dialog.' },
|
|
1439
|
+
],
|
|
1440
|
+
};
|
|
1441
|
+
|
|
1016
1442
|
/*
|
|
1017
1443
|
* Public API Surface of praxis-crud
|
|
1018
1444
|
*/
|
|
@@ -1021,5 +1447,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImpor
|
|
|
1021
1447
|
* Generated bundle index. Do not edit.
|
|
1022
1448
|
*/
|
|
1023
1449
|
|
|
1024
|
-
export {
|
|
1450
|
+
export { CRUD_AI_CAPABILITIES, CrudLauncherService, CrudPageHeaderComponent, DialogService, DynamicFormDialogHostComponent, PRAXIS_CRUD_COMPONENT_METADATA, PraxisCrudComponent, assertCrudMetadata, providePraxisCrudMetadata };
|
|
1025
1451
|
//# sourceMappingURL=praxisui-crud.mjs.map
|