@praxisui/crud 8.0.0-beta.0 → 8.0.0-beta.2
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 +7 -0
- package/fesm2022/praxisui-crud.mjs +74 -3
- package/index.d.ts +3 -0
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -246,6 +246,13 @@ sequenceDiagram
|
|
|
246
246
|
5. **Modal e drawer nao sao equivalentes internamente**: o ramo modal usa `DynamicFormDialogHostComponent`; o ramo drawer depende do adapter provido pelo host.
|
|
247
247
|
6. **Save e delete geram refetch automatico da lista**: esse side effect faz parte do contrato atual do shell CRUD.
|
|
248
248
|
|
|
249
|
+
Nota de fronteira para formularios:
|
|
250
|
+
|
|
251
|
+
- `@praxisui/crud` nao redefine payload de formulario nem filtra campos locais.
|
|
252
|
+
- Campos locais/transientes pertencem ao contrato de `@praxisui/dynamic-form` via `fieldMetadata[].source`, `fieldMetadata[].transient` e `fieldMetadata[].submitPolicy`.
|
|
253
|
+
- No fluxo modal, `DynamicFormDialogHostComponent` recebe `formSubmit.formData` ja filtrado para persistencia. Valores completos de UI ficam em `formSubmit.rawFormData`.
|
|
254
|
+
- `actions[].form.initialValue` e `inputs` continuam sendo seed/contexto de abertura, nao campos automaticamente persistiveis.
|
|
255
|
+
|
|
249
256
|
## Documentacao Tecnica da Lib
|
|
250
257
|
|
|
251
258
|
- `projects/praxis-crud/docs/host-crud-runtime-and-openmode.md`
|
|
@@ -4412,7 +4412,7 @@ class PraxisCrudComponent {
|
|
|
4412
4412
|
/>
|
|
4413
4413
|
}
|
|
4414
4414
|
}
|
|
4415
|
-
`, isInline: true, styles: [":host{display:block;width:100%;min-width:0;max-width:100%}\n"], dependencies: [{ kind: "component", type: PraxisTable, selector: "praxis-table", inputs: ["config", "resourcePath", "data", "tableId", "componentInstanceId", "title", "subtitle", "icon", "autoDelete", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "crudContext", "filterCriteria", "queryContext", "enableCustomization", "dense"], outputs: ["rowClick", "rowDoubleClick", "rowExpansionChange", "rowAction", "toolbarAction", "bulkAction", "columnReorder", "columnReorderAttempt", "beforeDelete", "afterDelete", "deleteError", "beforeBulkDelete", "afterBulkDelete", "bulkDeleteError", "schemaStatusChange", "metadataChange", "loadingStateChange", "collectionLinksChange"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone"] }] });
|
|
4415
|
+
`, isInline: true, styles: [":host{display:block;width:100%;min-width:0;max-width:100%}\n"], dependencies: [{ kind: "component", type: PraxisTable, selector: "praxis-table", inputs: ["config", "resourcePath", "data", "tableId", "componentInstanceId", "title", "subtitle", "icon", "autoDelete", "notifyIfOutdated", "snoozeMs", "autoOpenSettingsOnOutdated", "crudContext", "filterCriteria", "queryContext", "enableCustomization", "dense"], outputs: ["rowClick", "rowDoubleClick", "rowExpansionChange", "rowAction", "toolbarAction", "bulkAction", "columnReorder", "columnReorderAttempt", "beforeDelete", "afterDelete", "deleteError", "beforeBulkDelete", "afterBulkDelete", "bulkDeleteError", "schemaStatusChange", "metadataChange", "loadingStateChange", "collectionLinksChange", "selectionChange"] }, { kind: "component", type: EmptyStateCardComponent, selector: "praxis-empty-state-card", inputs: ["icon", "title", "description", "primaryAction", "secondaryActions", "inline", "tone"] }] });
|
|
4416
4416
|
}
|
|
4417
4417
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisCrudComponent, decorators: [{
|
|
4418
4418
|
type: Component,
|
|
@@ -4505,6 +4505,7 @@ class DynamicFormDialogHostComponent {
|
|
|
4505
4505
|
submitMethod;
|
|
4506
4506
|
apiEndpointKey;
|
|
4507
4507
|
apiUrlEntry;
|
|
4508
|
+
formActions;
|
|
4508
4509
|
mode = 'create';
|
|
4509
4510
|
backConfig;
|
|
4510
4511
|
idField = 'id';
|
|
@@ -4560,6 +4561,7 @@ class DynamicFormDialogHostComponent {
|
|
|
4560
4561
|
this.apiUrlEntry = this.data.inputs?.['apiUrlEntry'] ?? null;
|
|
4561
4562
|
const act = this.data.action?.action;
|
|
4562
4563
|
this.mode = act === 'edit' ? 'edit' : act === 'view' ? 'view' : 'create';
|
|
4564
|
+
this.formActions = this.resolveFormActions();
|
|
4563
4565
|
// Back config: defaults from metadata/action, overridden by saved per-form config
|
|
4564
4566
|
const defaults = (this.data.action?.back || this.data.metadata?.defaults?.back) || {};
|
|
4565
4567
|
this.backDefaults = defaults;
|
|
@@ -4616,6 +4618,47 @@ class DynamicFormDialogHostComponent {
|
|
|
4616
4618
|
}
|
|
4617
4619
|
return Object.keys(explicit).length ? explicit : null;
|
|
4618
4620
|
}
|
|
4621
|
+
resolveFormActions() {
|
|
4622
|
+
if (this.mode === 'view') {
|
|
4623
|
+
return undefined;
|
|
4624
|
+
}
|
|
4625
|
+
const submitLabel = this.resolveSubmitLabel();
|
|
4626
|
+
if (!submitLabel) {
|
|
4627
|
+
return undefined;
|
|
4628
|
+
}
|
|
4629
|
+
return {
|
|
4630
|
+
showSaveButton: true,
|
|
4631
|
+
showCancelButton: false,
|
|
4632
|
+
showResetButton: false,
|
|
4633
|
+
submit: {
|
|
4634
|
+
id: 'submit',
|
|
4635
|
+
type: 'submit',
|
|
4636
|
+
color: 'primary',
|
|
4637
|
+
visible: true,
|
|
4638
|
+
label: submitLabel,
|
|
4639
|
+
},
|
|
4640
|
+
};
|
|
4641
|
+
}
|
|
4642
|
+
resolveSubmitLabel() {
|
|
4643
|
+
const action = this.data.action ?? {};
|
|
4644
|
+
const explicit = stringOrUndefined(action.form?.submitLabel ??
|
|
4645
|
+
action.submitLabel ??
|
|
4646
|
+
action.form?.actions?.submit?.label ??
|
|
4647
|
+
action.form?.actions?.submitButtonLabel);
|
|
4648
|
+
if (explicit) {
|
|
4649
|
+
return explicit;
|
|
4650
|
+
}
|
|
4651
|
+
const actionLabel = stringOrUndefined(action.label);
|
|
4652
|
+
if (this.mode === 'create') {
|
|
4653
|
+
return deriveCreateSubmitLabel(actionLabel);
|
|
4654
|
+
}
|
|
4655
|
+
if (this.mode === 'edit') {
|
|
4656
|
+
return actionLabel && !/^editar\b/i.test(actionLabel)
|
|
4657
|
+
? actionLabel
|
|
4658
|
+
: 'Salvar alterações';
|
|
4659
|
+
}
|
|
4660
|
+
return undefined;
|
|
4661
|
+
}
|
|
4619
4662
|
ngOnInit() {
|
|
4620
4663
|
// Carregar estado salvo (se habilitado)
|
|
4621
4664
|
if (this.rememberState && this.stateKey) {
|
|
@@ -4640,8 +4683,11 @@ class DynamicFormDialogHostComponent {
|
|
|
4640
4683
|
}
|
|
4641
4684
|
}
|
|
4642
4685
|
onSave(result) {
|
|
4686
|
+
const stage = getFormSubmitStage(result);
|
|
4687
|
+
if (stage === 'before' || stage === 'error') {
|
|
4688
|
+
return;
|
|
4689
|
+
}
|
|
4643
4690
|
if (this.modal.closeOnSave === false) {
|
|
4644
|
-
// Não fechar: manter aberto e opcionalmente salvar estado atual
|
|
4645
4691
|
this.saveState();
|
|
4646
4692
|
return;
|
|
4647
4693
|
}
|
|
@@ -4779,11 +4825,12 @@ class DynamicFormDialogHostComponent {
|
|
|
4779
4825
|
[apiUrlEntry]="apiUrlEntry"
|
|
4780
4826
|
[presentationModeGlobal]="mode === 'view' ? true : null"
|
|
4781
4827
|
[backConfig]="backConfig"
|
|
4828
|
+
[actions]="formActions"
|
|
4782
4829
|
(formSubmit)="onSave($event)"
|
|
4783
4830
|
(formCancel)="onCancel()"
|
|
4784
4831
|
></praxis-dynamic-form>
|
|
4785
4832
|
</mat-dialog-content>
|
|
4786
|
-
`, 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: i2.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$1.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", "initialValue", "editorialContext", "mode", "config", "schemaSource", "schemaUrl", "submitUrl", "submitMethod", "responseSchemaUrl", "apiEndpointKey", "apiUrlEntry", "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"] }] });
|
|
4833
|
+
`, 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: i2.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$1.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", "initialValue", "editorialContext", "mode", "config", "actions", "schemaSource", "schemaUrl", "submitUrl", "submitMethod", "responseSchemaUrl", "apiEndpointKey", "apiUrlEntry", "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"] }] });
|
|
4787
4834
|
}
|
|
4788
4835
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: DynamicFormDialogHostComponent, decorators: [{
|
|
4789
4836
|
type: Component,
|
|
@@ -4841,6 +4888,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
4841
4888
|
[apiUrlEntry]="apiUrlEntry"
|
|
4842
4889
|
[presentationModeGlobal]="mode === 'view' ? true : null"
|
|
4843
4890
|
[backConfig]="backConfig"
|
|
4891
|
+
[actions]="formActions"
|
|
4844
4892
|
(formSubmit)="onSave($event)"
|
|
4845
4893
|
(formCancel)="onCancel()"
|
|
4846
4894
|
></praxis-dynamic-form>
|
|
@@ -4859,6 +4907,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
4859
4907
|
type: ViewChild,
|
|
4860
4908
|
args: [PraxisDynamicForm]
|
|
4861
4909
|
}] } });
|
|
4910
|
+
function getFormSubmitStage(result) {
|
|
4911
|
+
if (!result || typeof result !== 'object' || !('stage' in result)) {
|
|
4912
|
+
return null;
|
|
4913
|
+
}
|
|
4914
|
+
const stage = result.stage;
|
|
4915
|
+
return stage === 'before' || stage === 'after' || stage === 'error'
|
|
4916
|
+
? stage
|
|
4917
|
+
: null;
|
|
4918
|
+
}
|
|
4919
|
+
function stringOrUndefined(value) {
|
|
4920
|
+
const text = String(value ?? '').trim();
|
|
4921
|
+
return text || undefined;
|
|
4922
|
+
}
|
|
4923
|
+
function deriveCreateSubmitLabel(actionLabel) {
|
|
4924
|
+
if (!actionLabel) {
|
|
4925
|
+
return 'Criar';
|
|
4926
|
+
}
|
|
4927
|
+
const match = actionLabel.match(/^(novo|nova|adicionar|incluir|criar)\s+(.+)$/i);
|
|
4928
|
+
if (match?.[2]) {
|
|
4929
|
+
return `Criar ${match[2].trim()}`;
|
|
4930
|
+
}
|
|
4931
|
+
return actionLabel;
|
|
4932
|
+
}
|
|
4862
4933
|
|
|
4863
4934
|
var dynamicFormDialogHost_component = /*#__PURE__*/Object.freeze({
|
|
4864
4935
|
__proto__: null,
|
package/index.d.ts
CHANGED
|
@@ -336,12 +336,15 @@ declare class DynamicFormDialogHostComponent implements OnInit {
|
|
|
336
336
|
submitMethod?: string | null;
|
|
337
337
|
apiEndpointKey?: ApiEndpoint | string | null;
|
|
338
338
|
apiUrlEntry?: ApiUrlEntry | null;
|
|
339
|
+
formActions?: FormConfig['actions'];
|
|
339
340
|
mode: 'create' | 'edit' | 'view';
|
|
340
341
|
backConfig?: BackConfig;
|
|
341
342
|
private idField;
|
|
342
343
|
texts: Record<string, string>;
|
|
343
344
|
constructor(dialogRef: DialogRef<DynamicFormDialogHostComponent>, data: any, dialogService: DialogService, crud: GenericCrudService<any>, configStorage: AsyncConfigStorage);
|
|
344
345
|
private extractInitialValue;
|
|
346
|
+
private resolveFormActions;
|
|
347
|
+
private resolveSubmitLabel;
|
|
345
348
|
ngOnInit(): void;
|
|
346
349
|
onSave(result: unknown): void;
|
|
347
350
|
onCancel(): void;
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praxisui/crud",
|
|
3
|
-
"version": "8.0.0-beta.
|
|
3
|
+
"version": "8.0.0-beta.2",
|
|
4
4
|
"description": "CRUD building blocks for Praxis UI: integrates dynamic forms and tables with unified configuration and services.",
|
|
5
5
|
"peerDependencies": {
|
|
6
6
|
"@angular/common": "^20.1.0",
|
|
7
7
|
"@angular/core": "^20.1.0",
|
|
8
|
-
"@praxisui/dynamic-form": "^8.0.0-beta.
|
|
9
|
-
"@praxisui/table": "^8.0.0-beta.
|
|
10
|
-
"@praxisui/core": "^8.0.0-beta.
|
|
11
|
-
"@praxisui/dynamic-fields": "^8.0.0-beta.
|
|
12
|
-
"@praxisui/settings-panel": "^8.0.0-beta.
|
|
8
|
+
"@praxisui/dynamic-form": "^8.0.0-beta.2",
|
|
9
|
+
"@praxisui/table": "^8.0.0-beta.2",
|
|
10
|
+
"@praxisui/core": "^8.0.0-beta.2",
|
|
11
|
+
"@praxisui/dynamic-fields": "^8.0.0-beta.2",
|
|
12
|
+
"@praxisui/settings-panel": "^8.0.0-beta.2"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"tslib": "^2.3.0"
|