@acorex/platform 21.0.0-next.7 → 21.0.0-next.70
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/fesm2022/acorex-platform-auth.mjs +281 -23
- package/fesm2022/acorex-platform-auth.mjs.map +1 -1
- package/fesm2022/acorex-platform-common-common-settings.provider-Bi1RYif5.mjs +163 -0
- package/fesm2022/acorex-platform-common-common-settings.provider-Bi1RYif5.mjs.map +1 -0
- package/fesm2022/acorex-platform-common.mjs +1370 -276
- package/fesm2022/acorex-platform-common.mjs.map +1 -1
- package/fesm2022/acorex-platform-core.mjs +1185 -514
- package/fesm2022/acorex-platform-core.mjs.map +1 -1
- package/fesm2022/acorex-platform-domain.mjs +557 -826
- package/fesm2022/acorex-platform-domain.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-builder.mjs +832 -189
- package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-components-binding-expression-editor-popup.component-CXEdvDTf.mjs +121 -0
- package/fesm2022/acorex-platform-layout-components-binding-expression-editor-popup.component-CXEdvDTf.mjs.map +1 -0
- package/fesm2022/acorex-platform-layout-components.mjs +6309 -1956
- package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-designer.mjs +456 -204
- package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-entity-attachments-page.component-D8iQnT-R.mjs +371 -0
- package/fesm2022/acorex-platform-layout-entity-attachments-page.component-D8iQnT-R.mjs.map +1 -0
- package/fesm2022/acorex-platform-layout-entity-file-list-popup.component-_yrP5SQe.mjs +100 -0
- package/fesm2022/acorex-platform-layout-entity-file-list-popup.component-_yrP5SQe.mjs.map +1 -0
- package/fesm2022/acorex-platform-layout-entity.mjs +22488 -10232
- package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-views.mjs +564 -170
- package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-widget-core.mjs +2084 -481
- package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
- package/fesm2022/{acorex-platform-layout-widgets-button-widget-designer.component-C3VoBb_b.mjs → acorex-platform-layout-widgets-button-widget-designer.component-Dy7jF-oD.mjs} +10 -10
- package/fesm2022/acorex-platform-layout-widgets-button-widget-designer.component-Dy7jF-oD.mjs.map +1 -0
- package/fesm2022/{acorex-platform-layout-widgets-image-preview.popup-V31OpYah.mjs → acorex-platform-layout-widgets-image-preview.popup-C_EPAvCU.mjs} +6 -7
- package/fesm2022/acorex-platform-layout-widgets-image-preview.popup-C_EPAvCU.mjs.map +1 -0
- package/fesm2022/{acorex-platform-layout-widgets-page-widget-designer.component-BtZMBxYp.mjs → acorex-platform-layout-widgets-page-widget-designer.component-D10yO28c.mjs} +12 -12
- package/fesm2022/acorex-platform-layout-widgets-page-widget-designer.component-D10yO28c.mjs.map +1 -0
- package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-J0zcGKBX.mjs +116 -0
- package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-J0zcGKBX.mjs.map +1 -0
- package/fesm2022/{acorex-platform-layout-widgets-tabular-data-edit-popup.component-Ck7-wpT2.mjs → acorex-platform-layout-widgets-tabular-data-edit-popup.component-BcpRkpJp.mjs} +6 -6
- package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-BcpRkpJp.mjs.map +1 -0
- package/fesm2022/{acorex-platform-layout-widgets-tabular-data-view-popup.component-y8vjUiVs.mjs → acorex-platform-layout-widgets-tabular-data-view-popup.component-DQtK4lxl.mjs} +5 -5
- package/fesm2022/acorex-platform-layout-widgets-tabular-data-view-popup.component-DQtK4lxl.mjs.map +1 -0
- package/fesm2022/{acorex-platform-layout-widgets-text-block-widget-designer.component-Df1BFkSa.mjs → acorex-platform-layout-widgets-text-block-widget-designer.component-Vo4fWHtX.mjs} +6 -6
- package/fesm2022/acorex-platform-layout-widgets-text-block-widget-designer.component-Vo4fWHtX.mjs.map +1 -0
- package/fesm2022/acorex-platform-layout-widgets.mjs +10326 -7981
- package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
- package/fesm2022/acorex-platform-native.mjs +8 -7
- package/fesm2022/acorex-platform-native.mjs.map +1 -1
- package/fesm2022/acorex-platform-runtime.mjs +391 -166
- package/fesm2022/acorex-platform-runtime.mjs.map +1 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-CWLfNqV0.mjs +160 -0
- package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-CWLfNqV0.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-C7cT82K2.mjs +120 -0
- package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-C7cT82K2.mjs.map +1 -0
- package/fesm2022/{acorex-platform-themes-default-entity-master-single-view.component-eMBby9k4.mjs → acorex-platform-themes-default-entity-master-single-view.component-Br9p5aXT.mjs} +21 -28
- package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-Br9p5aXT.mjs.map +1 -0
- package/fesm2022/{acorex-platform-themes-default-error-401.component-cfREo88K.mjs → acorex-platform-themes-default-error-401.component-C7EYJzSr.mjs} +4 -4
- package/fesm2022/acorex-platform-themes-default-error-401.component-C7EYJzSr.mjs.map +1 -0
- package/fesm2022/{acorex-platform-themes-default-error-404.component-CdCV5ZoA.mjs → acorex-platform-themes-default-error-404.component-7MVLMwIa.mjs} +4 -4
- package/fesm2022/acorex-platform-themes-default-error-404.component-7MVLMwIa.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-default-error-offline.component-DR6G8gPC.mjs +19 -0
- package/fesm2022/acorex-platform-themes-default-error-offline.component-DR6G8gPC.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-default.mjs +2283 -83
- package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
- package/fesm2022/{acorex-platform-themes-shared-icon-chooser-column.component-C0EpfU2k.mjs → acorex-platform-themes-shared-icon-chooser-column.component-CqkWJYdv.mjs} +6 -6
- package/fesm2022/acorex-platform-themes-shared-icon-chooser-column.component-CqkWJYdv.mjs.map +1 -0
- package/fesm2022/{acorex-platform-themes-shared-icon-chooser-view.component-9W52W6Nu.mjs → acorex-platform-themes-shared-icon-chooser-view.component-BOTuLdWN.mjs} +6 -6
- package/fesm2022/acorex-platform-themes-shared-icon-chooser-view.component-BOTuLdWN.mjs.map +1 -0
- package/fesm2022/{acorex-platform-themes-shared-settings.provider-DSs1o1M6.mjs → acorex-platform-themes-shared-settings.provider-BjuzSe0T.mjs} +52 -33
- package/fesm2022/acorex-platform-themes-shared-settings.provider-BjuzSe0T.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-D566Kdvy.mjs +94 -0
- package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-D566Kdvy.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-D7-rCGl7.mjs +86 -0
- package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-D7-rCGl7.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-shared.mjs +767 -609
- package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
- package/fesm2022/acorex-platform-workflow.mjs +978 -238
- package/fesm2022/acorex-platform-workflow.mjs.map +1 -1
- package/fesm2022/acorex-platform.mjs.map +1 -1
- package/package.json +40 -38
- package/{auth/index.d.ts → types/acorex-platform-auth.d.ts} +241 -4
- package/{common/index.d.ts → types/acorex-platform-common.d.ts} +822 -89
- package/{core/index.d.ts → types/acorex-platform-core.d.ts} +658 -133
- package/{domain/index.d.ts → types/acorex-platform-domain.d.ts} +744 -412
- package/{layout/builder/index.d.ts → types/acorex-platform-layout-builder.d.ts} +194 -49
- package/types/acorex-platform-layout-components.d.ts +3253 -0
- package/{layout/designer/index.d.ts → types/acorex-platform-layout-designer.d.ts} +96 -18
- package/types/acorex-platform-layout-entity.d.ts +4439 -0
- package/{layout/views/index.d.ts → types/acorex-platform-layout-views.d.ts} +179 -56
- package/{layout/widget-core/index.d.ts → types/acorex-platform-layout-widget-core.d.ts} +398 -127
- package/{layout/widgets/index.d.ts → types/acorex-platform-layout-widgets.d.ts} +1120 -501
- package/{native/index.d.ts → types/acorex-platform-native.d.ts} +0 -7
- package/types/acorex-platform-runtime.d.ts +571 -0
- package/{themes/default/index.d.ts → types/acorex-platform-themes-default.d.ts} +233 -6
- package/{themes/shared/index.d.ts → types/acorex-platform-themes-shared.d.ts} +24 -2
- package/{workflow/index.d.ts → types/acorex-platform-workflow.d.ts} +620 -617
- package/fesm2022/acorex-platform-common-common-settings.provider-zhqNP3xb.mjs +0 -71
- package/fesm2022/acorex-platform-common-common-settings.provider-zhqNP3xb.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-button-widget-designer.component-C3VoBb_b.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-CxrsI6Hn.mjs +0 -135
- package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-CxrsI6Hn.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-image-preview.popup-V31OpYah.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-page-widget-designer.component-BtZMBxYp.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-Ck7-wpT2.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-tabular-data-view-popup.component-y8vjUiVs.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-widgets-text-block-widget-designer.component-Df1BFkSa.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-VIGuU5M4.mjs +0 -157
- package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-VIGuU5M4.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-DfJEx_bs.mjs +0 -1542
- package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-DfJEx_bs.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-Ua3ZA5hk.mjs +0 -101
- package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-Ua3ZA5hk.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-eMBby9k4.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-error-401.component-cfREo88K.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-error-404.component-CdCV5ZoA.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-error-offline.component-E7SzBcAt.mjs +0 -19
- package/fesm2022/acorex-platform-themes-default-error-offline.component-E7SzBcAt.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-shared-icon-chooser-column.component-C0EpfU2k.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-shared-icon-chooser-view.component-9W52W6Nu.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-shared-settings.provider-DSs1o1M6.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-DTnfRy5f.mjs +0 -65
- package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-DTnfRy5f.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-DY0JtT1v.mjs +0 -64
- package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-DY0JtT1v.mjs.map +0 -1
- package/layout/components/index.d.ts +0 -1669
- package/layout/entity/index.d.ts +0 -2287
- package/runtime/index.d.ts +0 -307
- /package/{index.d.ts → types/acorex-platform.d.ts} +0 -0
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as i5 from '@angular/common';
|
|
2
2
|
import { CommonModule } from '@angular/common';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
|
-
import { Injectable, inject, input, model, signal, effect, output, viewChild, ChangeDetectionStrategy, Component, NgModule, EventEmitter, Output
|
|
4
|
+
import { Injectable, inject, input, model, signal, computed, effect, output, viewChild, ChangeDetectionStrategy, Component, NgModule, EventEmitter, Output } from '@angular/core';
|
|
5
|
+
import { provideCommandSetups, AXPCommandService } from '@acorex/platform/runtime';
|
|
5
6
|
import { AXPopupService } from '@acorex/components/popup';
|
|
7
|
+
import * as i4 from '@acorex/platform/core';
|
|
8
|
+
import { AXPHookService, AXPExpressionEvaluatorService, AXPComponentSlotModule, AXPContextStore } from '@acorex/platform/core';
|
|
6
9
|
import * as i1 from '@acorex/platform/layout/widget-core';
|
|
7
|
-
import { AXPWidgetSerializationHelper, AXPWidgetContainerComponent, AXPPageStatus, AXPWidgetCoreModule } from '@acorex/platform/layout/widget-core';
|
|
8
|
-
import { cloneDeep, isNil, set, isEqual } from 'lodash-es';
|
|
10
|
+
import { AXPWidgetSerializationHelper, AXPWidgetContainerComponent, AXPPageStatus, AXPWidgetCoreModule, AXPWidgetRegistryService } from '@acorex/platform/layout/widget-core';
|
|
11
|
+
import { cloneDeep, isNil, set, isEqual, merge } from 'lodash-es';
|
|
9
12
|
import * as i2 from '@acorex/components/form';
|
|
10
13
|
import { AXFormComponent, AXFormModule } from '@acorex/components/form';
|
|
11
14
|
import { Subject, debounceTime, distinctUntilChanged, startWith } from 'rxjs';
|
|
@@ -16,9 +19,47 @@ import { AXDecoratorModule } from '@acorex/components/decorators';
|
|
|
16
19
|
import * as i3 from '@acorex/components/loading';
|
|
17
20
|
import { AXLoadingModule } from '@acorex/components/loading';
|
|
18
21
|
import { AXBasePageComponent } from '@acorex/components/page';
|
|
19
|
-
import * as
|
|
20
|
-
import { AXTranslationModule } from '@acorex/core/translation';
|
|
21
|
-
import {
|
|
22
|
+
import * as i6 from '@acorex/core/translation';
|
|
23
|
+
import { AXTranslationModule, AXTranslationService } from '@acorex/core/translation';
|
|
24
|
+
import { AXP_ENTITY_DEFINITION_CRUD_SERVICE } from '@acorex/platform/domain';
|
|
25
|
+
|
|
26
|
+
/** Fallback {@link AXPDialogRef} when the popup is dismissed without a footer action (e.g. header close). */
|
|
27
|
+
function createDismissedDialogRef(context = () => ({})) {
|
|
28
|
+
return {
|
|
29
|
+
close: () => { },
|
|
30
|
+
context,
|
|
31
|
+
action: () => 'cancel',
|
|
32
|
+
setLoading: () => { },
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
|
|
37
|
+
//#region ---- Imports ----
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region ---- Before open ----
|
|
40
|
+
/**
|
|
41
|
+
* Runs after dialog options and context are prepared and **before** footer customization and popup open.
|
|
42
|
+
* Listeners may mutate {@link AXPLayoutBuilderDialogBeforeOpenPayload.context} by reference.
|
|
43
|
+
*/
|
|
44
|
+
const AXP_LAYOUT_BUILDER_DIALOG_BEFORE_OPEN_HOOK_KEY = 'layout-builder.dialog.before-open';
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region ---- Footer actions ----
|
|
47
|
+
/**
|
|
48
|
+
* Runs after builder-defined footer actions exist and **before** the dialog opens.
|
|
49
|
+
* Listeners receive the live `actions.footer.prefix` / `suffix` arrays (same references as the dialog)
|
|
50
|
+
* so they may push, splice, filter, or replace items. They may also mutate {@link AXPLayoutBuilderDialogFooterPayload.context} by reference.
|
|
51
|
+
*/
|
|
52
|
+
const AXP_LAYOUT_BUILDER_DIALOG_CONFIG_HOOK_KEY = 'layout-builder.dialog.config';
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region ---- Context updates (after open) ----
|
|
55
|
+
/**
|
|
56
|
+
* Runs whenever the dialog layout builder context changes (debounced upstream), **after** the popup is visible.
|
|
57
|
+
* Use for side effects that depend on live context (for example values updated by widgets after render).
|
|
58
|
+
* Payload mirrors `AXPDialogRendererComponent` semantics: {@link AXPLayoutBuilderDialogContextChangedPayload.getContext},
|
|
59
|
+
* {@link AXPLayoutBuilderDialogContextChangedPayload.patchContext}, and optional loading state.
|
|
60
|
+
*/
|
|
61
|
+
const AXP_LAYOUT_BUILDER_DIALOG_CONTEXT_CHANGED_HOOK_KEY = 'layout-builder.dialog.context-changed';
|
|
62
|
+
//#endregion
|
|
22
63
|
|
|
23
64
|
class AXPLayoutConversionService {
|
|
24
65
|
constructor() {
|
|
@@ -158,6 +199,10 @@ class AXPLayoutConversionService {
|
|
|
158
199
|
if (!editorWidget.mode) {
|
|
159
200
|
editorWidget.mode = fieldMode;
|
|
160
201
|
}
|
|
202
|
+
const hintOpts = field.description != null &&
|
|
203
|
+
(typeof field.description !== 'string' || field.description.trim().length > 0)
|
|
204
|
+
? { hint: field.description, hintDisplayMode: 'note' }
|
|
205
|
+
: {};
|
|
161
206
|
return {
|
|
162
207
|
type: 'form-field',
|
|
163
208
|
name: field.path,
|
|
@@ -165,8 +210,8 @@ class AXPLayoutConversionService {
|
|
|
165
210
|
options: {
|
|
166
211
|
label: field.title,
|
|
167
212
|
badge: field.badge,
|
|
168
|
-
description: field.description,
|
|
169
213
|
showLabel: true,
|
|
214
|
+
...hintOpts,
|
|
170
215
|
},
|
|
171
216
|
children: [editorWidget], // The editor widget becomes a child of form-field
|
|
172
217
|
};
|
|
@@ -209,7 +254,7 @@ class AXPLayoutConversionService {
|
|
|
209
254
|
path: formFieldNode.name || editorWidget.name || `field-${Date.now()}`,
|
|
210
255
|
title: formFieldNode.options?.['label'],
|
|
211
256
|
badge: formFieldNode.options?.['badge'],
|
|
212
|
-
description: formFieldNode.options?.['
|
|
257
|
+
description: formFieldNode.options?.['hint'],
|
|
213
258
|
widget: editorWidget,
|
|
214
259
|
mode: formFieldNode.mode,
|
|
215
260
|
};
|
|
@@ -222,9 +267,16 @@ class AXPLayoutConversionService {
|
|
|
222
267
|
const keyParts = [];
|
|
223
268
|
keyParts.push(`groups:${formDefinition.groups.length}`);
|
|
224
269
|
formDefinition.groups.forEach((group, groupIndex) => {
|
|
225
|
-
|
|
270
|
+
// Include group.mode so view vs edit (or mixed) layouts do not share a cached widget tree.
|
|
271
|
+
const groupModePart = group.mode ?? '_';
|
|
272
|
+
keyParts.push(`g${groupIndex}:${group.name}:${group.parameters.length}:${groupModePart}`);
|
|
273
|
+
keyParts.push(`gL${groupIndex}:${JSON.stringify(group.title ?? null)}:${JSON.stringify(group.description ?? null)}`);
|
|
226
274
|
group.parameters.forEach((param, paramIndex) => {
|
|
227
|
-
|
|
275
|
+
// Field mode must be part of the key; otherwise metadata forms that only differ by
|
|
276
|
+
// view/edit (same paths and widget types) incorrectly reuse the first cached tree.
|
|
277
|
+
const fieldModePart = param.mode ?? '_';
|
|
278
|
+
keyParts.push(`p${groupIndex}.${paramIndex}:${param.path}:${param.widget.type}:${fieldModePart}`);
|
|
279
|
+
keyParts.push(`pL${groupIndex}.${paramIndex}:${JSON.stringify(param.title ?? null)}:${JSON.stringify(param.description ?? null)}:${JSON.stringify(param.badge ?? null)}`);
|
|
228
280
|
});
|
|
229
281
|
});
|
|
230
282
|
if (formDefinition.mode) {
|
|
@@ -285,10 +337,10 @@ class AXPLayoutConversionService {
|
|
|
285
337
|
}
|
|
286
338
|
return Math.abs(hash).toString(36); // Convert to base36 for shorter string
|
|
287
339
|
}
|
|
288
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
289
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
340
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutConversionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
341
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutConversionService, providedIn: 'root' }); }
|
|
290
342
|
}
|
|
291
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
343
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutConversionService, decorators: [{
|
|
292
344
|
type: Injectable,
|
|
293
345
|
args: [{
|
|
294
346
|
providedIn: 'root',
|
|
@@ -361,17 +413,13 @@ function collectDefaultValues(node, context = {}, isTopLevel = true) {
|
|
|
361
413
|
const result = isTopLevel ? cloneDeep(context) : context;
|
|
362
414
|
// Check if this node has a defaultValue and a path
|
|
363
415
|
// Note: We check for both node.defaultValue and also look in node.options.defaultValue as fallback
|
|
364
|
-
const defaultValue = node.defaultValue !== undefined
|
|
365
|
-
? node.defaultValue
|
|
366
|
-
: node.options?.defaultValue;
|
|
416
|
+
const defaultValue = node.defaultValue !== undefined ? node.defaultValue : node.options?.defaultValue;
|
|
367
417
|
if (defaultValue !== undefined && !isNil(defaultValue) && node.path) {
|
|
368
418
|
// Check if path exists in context using lodash get equivalent check
|
|
369
419
|
const currentValue = getNestedValue(result, node.path);
|
|
370
420
|
if (currentValue === undefined) {
|
|
371
421
|
// Clone the defaultValue to avoid reference issues (especially for Date objects)
|
|
372
|
-
const clonedValue = defaultValue instanceof Date
|
|
373
|
-
? new Date(defaultValue.getTime())
|
|
374
|
-
: cloneDeep(defaultValue);
|
|
422
|
+
const clonedValue = defaultValue instanceof Date ? new Date(defaultValue.getTime()) : cloneDeep(defaultValue);
|
|
375
423
|
set(result, node.path, clonedValue);
|
|
376
424
|
}
|
|
377
425
|
}
|
|
@@ -402,17 +450,18 @@ function getNestedValue(obj, path) {
|
|
|
402
450
|
class AXPLayoutBuilderService {
|
|
403
451
|
constructor() {
|
|
404
452
|
this.popupService = inject(AXPopupService);
|
|
453
|
+
this.hookService = inject(AXPHookService, { optional: true }) ?? undefined;
|
|
405
454
|
}
|
|
406
455
|
/**
|
|
407
456
|
* Create a new layout builder
|
|
408
457
|
*/
|
|
409
458
|
create() {
|
|
410
|
-
return new LayoutBuilder(this.popupService);
|
|
459
|
+
return new LayoutBuilder(this.popupService, this.hookService);
|
|
411
460
|
}
|
|
412
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
413
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
461
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutBuilderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
462
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutBuilderService, providedIn: 'root' }); }
|
|
414
463
|
}
|
|
415
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
464
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutBuilderService, decorators: [{
|
|
416
465
|
type: Injectable,
|
|
417
466
|
args: [{
|
|
418
467
|
providedIn: 'root',
|
|
@@ -425,8 +474,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
|
|
|
425
474
|
* Open/Closed: Extensible through container delegates
|
|
426
475
|
*/
|
|
427
476
|
class LayoutBuilder {
|
|
428
|
-
constructor(popupService) {
|
|
477
|
+
constructor(popupService, hookService) {
|
|
429
478
|
this.popupService = popupService;
|
|
479
|
+
this.hookService = hookService;
|
|
430
480
|
this.root = {
|
|
431
481
|
children: [],
|
|
432
482
|
mode: 'edit',
|
|
@@ -453,7 +503,19 @@ class LayoutBuilder {
|
|
|
453
503
|
if (delegate) {
|
|
454
504
|
delegate(container);
|
|
455
505
|
}
|
|
456
|
-
|
|
506
|
+
const built = container.build();
|
|
507
|
+
// Step/dialog content usually calls flex() once; replace the default empty flex root instead of nesting flex inside flex.
|
|
508
|
+
if (this.root.type === 'flex-layout' &&
|
|
509
|
+
(!this.root.children || this.root.children.length === 0) &&
|
|
510
|
+
!this.root.options) {
|
|
511
|
+
this.root = built;
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
if (!this.root.children) {
|
|
515
|
+
this.root.children = [];
|
|
516
|
+
}
|
|
517
|
+
this.root.children.push(built);
|
|
518
|
+
}
|
|
457
519
|
return this;
|
|
458
520
|
}
|
|
459
521
|
panel(delegate) {
|
|
@@ -496,7 +558,7 @@ class LayoutBuilder {
|
|
|
496
558
|
if (!this.popupService) {
|
|
497
559
|
throw new Error('LayoutBuilder requires AXPopupService to create dialogs. Please inject it in the service constructor.');
|
|
498
560
|
}
|
|
499
|
-
const container = new DialogContainerBuilder(this.popupService);
|
|
561
|
+
const container = new DialogContainerBuilder(this.popupService, this.hookService);
|
|
500
562
|
if (delegate) {
|
|
501
563
|
delegate(container);
|
|
502
564
|
}
|
|
@@ -521,11 +583,21 @@ class LayoutBuilder {
|
|
|
521
583
|
return this;
|
|
522
584
|
}
|
|
523
585
|
build() {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
mode:
|
|
586
|
+
const r = this.root;
|
|
587
|
+
const node = {
|
|
588
|
+
type: r.type,
|
|
589
|
+
...(r.mode !== undefined ? { mode: r.mode } : {}),
|
|
590
|
+
...(r.children !== undefined ? { children: r.children } : {}),
|
|
591
|
+
...(r.options !== undefined ? { options: r.options } : {}),
|
|
592
|
+
...(r.name !== undefined ? { name: r.name } : {}),
|
|
593
|
+
...(r.path !== undefined ? { path: r.path } : {}),
|
|
594
|
+
...(r.visible !== undefined ? { visible: r.visible } : {}),
|
|
595
|
+
...(r.defaultValue !== undefined ? { defaultValue: r.defaultValue } : {}),
|
|
596
|
+
...(r.triggers !== undefined ? { triggers: r.triggers } : {}),
|
|
597
|
+
...(r.meta !== undefined ? { meta: r.meta } : {}),
|
|
598
|
+
...(r.valueTransforms !== undefined ? { valueTransforms: r.valueTransforms } : {}),
|
|
528
599
|
};
|
|
600
|
+
return node;
|
|
529
601
|
}
|
|
530
602
|
/**
|
|
531
603
|
* Converts the built widget node to JSON string
|
|
@@ -594,6 +666,7 @@ class BaseContainerBuilder {
|
|
|
594
666
|
'number-editor',
|
|
595
667
|
'select-editor',
|
|
596
668
|
'lookup-editor',
|
|
669
|
+
'entity-definition-provider-editor',
|
|
597
670
|
'selection-list-editor',
|
|
598
671
|
'date-time-editor',
|
|
599
672
|
'toggle-editor',
|
|
@@ -696,24 +769,30 @@ class BaseContainerMixin extends BaseContainerBuilder {
|
|
|
696
769
|
class LayoutContainerMixin extends BaseContainerMixin {
|
|
697
770
|
layout(value) {
|
|
698
771
|
// Map layout intent to grid item sizing so containers like `form-field`
|
|
699
|
-
// can span multiple columns inside grid/fieldset layouts.
|
|
772
|
+
// can span multiple columns/rows inside grid/fieldset layouts.
|
|
700
773
|
if (!this.containerState.options)
|
|
701
774
|
this.containerState.options = {};
|
|
702
775
|
if (typeof value === 'number') {
|
|
703
|
-
// Direct numeric shorthand → colSpan
|
|
704
776
|
this.containerState.options.colSpan = value;
|
|
705
777
|
}
|
|
706
778
|
else if (value) {
|
|
707
|
-
// Try to extract a reasonable colSpan from breakpoint positions
|
|
708
779
|
const positions = value.positions;
|
|
709
780
|
if (positions) {
|
|
710
|
-
const
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
781
|
+
const placement = positions?.lg ?? positions?.xl ?? positions?.xxl ?? positions?.md ?? positions?.sm;
|
|
782
|
+
if (placement) {
|
|
783
|
+
const opts = this.containerState.options;
|
|
784
|
+
if (placement.colSpan != null)
|
|
785
|
+
opts.colSpan = placement.colSpan;
|
|
786
|
+
if (placement.colStart != null)
|
|
787
|
+
opts.colStart = placement.colStart;
|
|
788
|
+
if (placement.colEnd != null)
|
|
789
|
+
opts.colEnd = placement.colEnd;
|
|
790
|
+
if (placement.rowSpan != null)
|
|
791
|
+
opts.rowSpan = placement.rowSpan;
|
|
792
|
+
if (placement.rowStart != null)
|
|
793
|
+
opts.rowStart = placement.rowStart;
|
|
794
|
+
if (placement.rowEnd != null)
|
|
795
|
+
opts.rowEnd = placement.rowEnd;
|
|
717
796
|
}
|
|
718
797
|
}
|
|
719
798
|
}
|
|
@@ -932,6 +1011,7 @@ class WidgetContainerMixin extends ChildContainerMixin {
|
|
|
932
1011
|
'number-editor',
|
|
933
1012
|
'select-editor',
|
|
934
1013
|
'lookup-editor',
|
|
1014
|
+
'entity-definition-provider-editor',
|
|
935
1015
|
'selection-list-editor',
|
|
936
1016
|
'date-time-editor',
|
|
937
1017
|
'toggle-editor',
|
|
@@ -987,12 +1067,73 @@ class FlexContainerBuilder extends WidgetContainerMixin {
|
|
|
987
1067
|
* Grid Container Builder - Liskov Substitution Principle
|
|
988
1068
|
* Extends WidgetContainerMixin to inherit all common functionality
|
|
989
1069
|
*/
|
|
1070
|
+
/**
|
|
1071
|
+
* Extracts flat grid-item options from AXPGridLayoutOptions for grid-item-layout widget.
|
|
1072
|
+
* Uses first available breakpoint (lg, xl, md, sm).
|
|
1073
|
+
*/
|
|
1074
|
+
/**
|
|
1075
|
+
* Deep-merges grid breakpoint buckets so sequential fluent calls (e.g. setColumns then setGap)
|
|
1076
|
+
* do not wipe sibling keys under options.grid.default.
|
|
1077
|
+
*/
|
|
1078
|
+
function mergeAXPGridContainerOptions(prev, patch) {
|
|
1079
|
+
const next = { ...(prev ?? {}), ...patch };
|
|
1080
|
+
if (prev?.grid?.default || patch.grid?.default) {
|
|
1081
|
+
next.grid = {
|
|
1082
|
+
...(prev?.grid ?? {}),
|
|
1083
|
+
...(patch.grid ?? {}),
|
|
1084
|
+
default: {
|
|
1085
|
+
...(prev?.grid?.default ?? {}),
|
|
1086
|
+
...(patch.grid?.default ?? {}),
|
|
1087
|
+
},
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
return next;
|
|
1091
|
+
}
|
|
1092
|
+
function toGridItemOptions(layoutOptions) {
|
|
1093
|
+
if (!layoutOptions?.positions)
|
|
1094
|
+
return { colSpan: 12 };
|
|
1095
|
+
const positions = layoutOptions.positions;
|
|
1096
|
+
const placement = positions['lg'] ?? positions['xl'] ?? positions['xxl'] ?? positions['md'] ?? positions['sm'];
|
|
1097
|
+
if (!placement)
|
|
1098
|
+
return { colSpan: 12 };
|
|
1099
|
+
const opts = {};
|
|
1100
|
+
if (placement['colSpan'] != null)
|
|
1101
|
+
opts['colSpan'] = placement['colSpan'];
|
|
1102
|
+
if (placement['colStart'] != null)
|
|
1103
|
+
opts['colStart'] = placement['colStart'];
|
|
1104
|
+
if (placement['colEnd'] != null)
|
|
1105
|
+
opts['colEnd'] = placement['colEnd'];
|
|
1106
|
+
if (placement['rowSpan'] != null)
|
|
1107
|
+
opts['rowSpan'] = placement['rowSpan'];
|
|
1108
|
+
if (placement['rowStart'] != null)
|
|
1109
|
+
opts['rowStart'] = placement['rowStart'];
|
|
1110
|
+
if (placement['rowEnd'] != null)
|
|
1111
|
+
opts['rowEnd'] = placement['rowEnd'];
|
|
1112
|
+
if (Object.keys(opts).length === 0)
|
|
1113
|
+
opts['colSpan'] = 12;
|
|
1114
|
+
return opts;
|
|
1115
|
+
}
|
|
990
1116
|
class GridContainerBuilder extends WidgetContainerMixin {
|
|
991
1117
|
constructor() {
|
|
992
1118
|
super('grid-layout');
|
|
993
1119
|
}
|
|
994
1120
|
setOptions(options) {
|
|
995
|
-
this.containerState.options =
|
|
1121
|
+
this.containerState.options = mergeAXPGridContainerOptions(this.containerState.options, options);
|
|
1122
|
+
return this;
|
|
1123
|
+
}
|
|
1124
|
+
item(layoutOptions, delegate) {
|
|
1125
|
+
const fieldset = new FieldsetContainerBuilder();
|
|
1126
|
+
fieldset.withInheritanceContext(this.inheritanceContext);
|
|
1127
|
+
delegate(fieldset);
|
|
1128
|
+
const fieldsetNode = fieldset.build();
|
|
1129
|
+
const gridItemOptions = toGridItemOptions(layoutOptions);
|
|
1130
|
+
const gridItemNode = {
|
|
1131
|
+
type: 'grid-item-layout',
|
|
1132
|
+
options: gridItemOptions,
|
|
1133
|
+
children: [fieldsetNode],
|
|
1134
|
+
};
|
|
1135
|
+
this.ensureChildren();
|
|
1136
|
+
this.containerState.children.push(gridItemNode);
|
|
996
1137
|
return this;
|
|
997
1138
|
}
|
|
998
1139
|
// Individual fluent methods for Grid
|
|
@@ -1164,10 +1305,34 @@ class FormFieldBuilder extends LayoutContainerMixin {
|
|
|
1164
1305
|
child.type(type);
|
|
1165
1306
|
child.name(finalName);
|
|
1166
1307
|
child.path(widgetPath);
|
|
1167
|
-
//
|
|
1168
|
-
const { name: _, ...cleanOptions } = (options || {});
|
|
1308
|
+
// Extract extended properties from options (triggers, meta, valueTransforms, mode, visible, defaultValue)
|
|
1309
|
+
const { name: _, triggers, meta, valueTransforms, mode: extendedMode, visible: extendedVisible, defaultValue: extendedDefaultValue, children: extendedChildren, ...cleanOptions } = (options || {});
|
|
1169
1310
|
child.withInheritanceContext(this.inheritanceContext);
|
|
1170
1311
|
child.options(cleanOptions);
|
|
1312
|
+
// Apply extended properties if provided
|
|
1313
|
+
if (extendedMode !== undefined) {
|
|
1314
|
+
child.mode(extendedMode);
|
|
1315
|
+
}
|
|
1316
|
+
if (extendedVisible !== undefined) {
|
|
1317
|
+
child.visible(extendedVisible);
|
|
1318
|
+
}
|
|
1319
|
+
if (extendedDefaultValue !== undefined) {
|
|
1320
|
+
child.defaultValue(extendedDefaultValue);
|
|
1321
|
+
}
|
|
1322
|
+
// Set triggers, meta, and valueTransforms directly on widgetState
|
|
1323
|
+
// These are part of AXPWidgetNode but not handled by WidgetBuilder methods
|
|
1324
|
+
if (triggers !== undefined) {
|
|
1325
|
+
child.widgetState.triggers = triggers;
|
|
1326
|
+
}
|
|
1327
|
+
if (meta !== undefined) {
|
|
1328
|
+
child.widgetState.meta = meta;
|
|
1329
|
+
}
|
|
1330
|
+
if (valueTransforms !== undefined) {
|
|
1331
|
+
child.widgetState.valueTransforms = valueTransforms;
|
|
1332
|
+
}
|
|
1333
|
+
if (extendedChildren !== undefined) {
|
|
1334
|
+
child.widgetState.children = extendedChildren;
|
|
1335
|
+
}
|
|
1171
1336
|
// IMPORTANT: Store the widget builder, don't build it yet!
|
|
1172
1337
|
// This allows properties set after this method (like disabled, readonly) to be applied
|
|
1173
1338
|
this.childWidget = child;
|
|
@@ -1399,7 +1564,7 @@ class ListWidgetBuilder extends WidgetContainerMixin {
|
|
|
1399
1564
|
* Uses composition instead of inheritance for cleaner separation
|
|
1400
1565
|
*/
|
|
1401
1566
|
class DialogContainerBuilder {
|
|
1402
|
-
constructor(popupService) {
|
|
1567
|
+
constructor(popupService, hookService) {
|
|
1403
1568
|
this.dialogState = {
|
|
1404
1569
|
type: 'flex-layout', // This will be overridden when content layout exists
|
|
1405
1570
|
children: [],
|
|
@@ -1422,6 +1587,7 @@ class DialogContainerBuilder {
|
|
|
1422
1587
|
else {
|
|
1423
1588
|
this.popupService = inject(AXPopupService);
|
|
1424
1589
|
}
|
|
1590
|
+
this.hookService = hookService ?? inject(AXPHookService, { optional: true }) ?? undefined;
|
|
1425
1591
|
}
|
|
1426
1592
|
setOptions(options) {
|
|
1427
1593
|
this.dialogState.dialogOptions = { ...this.dialogState.dialogOptions, ...options };
|
|
@@ -1461,6 +1627,15 @@ class DialogContainerBuilder {
|
|
|
1461
1627
|
}
|
|
1462
1628
|
return this;
|
|
1463
1629
|
}
|
|
1630
|
+
onAction(handler) {
|
|
1631
|
+
this.dialogState.dialogOptions ??= {
|
|
1632
|
+
title: '',
|
|
1633
|
+
size: 'md',
|
|
1634
|
+
closeButton: false,
|
|
1635
|
+
};
|
|
1636
|
+
this.dialogState.dialogOptions.onAction = handler;
|
|
1637
|
+
return this;
|
|
1638
|
+
}
|
|
1464
1639
|
addCustomAction(action) {
|
|
1465
1640
|
// Add to actions based on position
|
|
1466
1641
|
const position = action.position || 'suffix';
|
|
@@ -1507,20 +1682,42 @@ class DialogContainerBuilder {
|
|
|
1507
1682
|
const dialogNode = this.build();
|
|
1508
1683
|
// Import the dialog renderer component dynamically
|
|
1509
1684
|
const { AXPDialogRendererComponent } = await Promise.resolve().then(function () { return dialogRenderer_component; });
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1685
|
+
this.dialogState.dialogOptions ??= {};
|
|
1686
|
+
if (this.dialogState.dialogOptions.context == null || typeof this.dialogState.dialogOptions.context !== 'object') {
|
|
1687
|
+
this.dialogState.dialogOptions.context = {};
|
|
1688
|
+
}
|
|
1689
|
+
const initialContext = this.dialogState.dialogOptions.context;
|
|
1690
|
+
this.dialogState.actions ??= { footer: { prefix: [], suffix: [] } };
|
|
1691
|
+
this.dialogState.actions.footer ??= { prefix: [], suffix: [] };
|
|
1692
|
+
this.dialogState.actions.footer.prefix ??= [];
|
|
1693
|
+
this.dialogState.actions.footer.suffix ??= [];
|
|
1694
|
+
const hookService = this.hookService;
|
|
1695
|
+
if (hookService) {
|
|
1696
|
+
const beforePayload = {
|
|
1697
|
+
context: initialContext,
|
|
1698
|
+
dialogOptions: this.dialogState.dialogOptions,
|
|
1699
|
+
};
|
|
1700
|
+
await hookService.runAsync(AXP_LAYOUT_BUILDER_DIALOG_BEFORE_OPEN_HOOK_KEY, beforePayload);
|
|
1701
|
+
}
|
|
1513
1702
|
// Create dialog configuration
|
|
1514
1703
|
const dialogConfig = {
|
|
1515
1704
|
title: this.dialogState.dialogOptions?.title || '',
|
|
1516
|
-
|
|
1517
|
-
|
|
1705
|
+
//TODO: why we need message?
|
|
1706
|
+
//message: this.dialogState.dialogOptions?.message,
|
|
1707
|
+
context: initialContext,
|
|
1518
1708
|
definition: dialogNode,
|
|
1709
|
+
metadata: this.dialogState.dialogOptions.metadata,
|
|
1519
1710
|
actions: this.dialogState.actions,
|
|
1711
|
+
onAction: this.dialogState.dialogOptions?.onAction,
|
|
1520
1712
|
};
|
|
1713
|
+
//
|
|
1714
|
+
if (hookService) {
|
|
1715
|
+
await hookService.runAsync(AXP_LAYOUT_BUILDER_DIALOG_CONFIG_HOOK_KEY, dialogConfig);
|
|
1716
|
+
}
|
|
1521
1717
|
// The Promise resolves when user clicks an action button
|
|
1522
1718
|
return new Promise(async (resolve) => {
|
|
1523
|
-
|
|
1719
|
+
let flag = false;
|
|
1720
|
+
await this.popupService.open(AXPDialogRendererComponent, {
|
|
1524
1721
|
title: dialogConfig.title,
|
|
1525
1722
|
size: this.dialogState.dialogOptions?.size || 'md',
|
|
1526
1723
|
closeButton: this.dialogState.dialogOptions?.closeButton || false,
|
|
@@ -1529,11 +1726,14 @@ class DialogContainerBuilder {
|
|
|
1529
1726
|
data: {
|
|
1530
1727
|
config: dialogConfig,
|
|
1531
1728
|
callBack: (result) => {
|
|
1532
|
-
|
|
1729
|
+
flag = true;
|
|
1533
1730
|
resolve(result);
|
|
1534
1731
|
},
|
|
1535
1732
|
},
|
|
1536
1733
|
});
|
|
1734
|
+
if (!flag) {
|
|
1735
|
+
resolve(createDismissedDialogRef());
|
|
1736
|
+
}
|
|
1537
1737
|
});
|
|
1538
1738
|
}
|
|
1539
1739
|
}
|
|
@@ -1548,6 +1748,7 @@ class WidgetBuilder {
|
|
|
1548
1748
|
this.widgetState = {
|
|
1549
1749
|
type: 'widget',
|
|
1550
1750
|
options: {},
|
|
1751
|
+
children: [],
|
|
1551
1752
|
};
|
|
1552
1753
|
this.inheritanceContext = {};
|
|
1553
1754
|
if (name) {
|
|
@@ -1611,6 +1812,7 @@ class WidgetBuilder {
|
|
|
1611
1812
|
this.widgetState.options = {};
|
|
1612
1813
|
}
|
|
1613
1814
|
this.widgetState.options['visible'] = condition;
|
|
1815
|
+
this.widgetState.visible = condition;
|
|
1614
1816
|
this.inheritanceContext.visible = condition;
|
|
1615
1817
|
return this;
|
|
1616
1818
|
}
|
|
@@ -1642,6 +1844,10 @@ class WidgetBuilder {
|
|
|
1642
1844
|
this.inheritanceContext.direction = direction;
|
|
1643
1845
|
return this;
|
|
1644
1846
|
}
|
|
1847
|
+
children(children) {
|
|
1848
|
+
this.widgetState.children = children;
|
|
1849
|
+
return this;
|
|
1850
|
+
}
|
|
1645
1851
|
// Inheritance context methods
|
|
1646
1852
|
withInheritanceContext(context) {
|
|
1647
1853
|
this.inheritanceContext = mergeInheritanceContext(context);
|
|
@@ -1665,6 +1871,7 @@ class WidgetBuilder {
|
|
|
1665
1871
|
}
|
|
1666
1872
|
if (resolved.visible !== undefined) {
|
|
1667
1873
|
this.widgetState.options['visible'] = resolved.visible;
|
|
1874
|
+
this.widgetState.visible = resolved.visible;
|
|
1668
1875
|
}
|
|
1669
1876
|
if (context.defaultValue !== undefined) {
|
|
1670
1877
|
this.widgetState.defaultValue = context.defaultValue;
|
|
@@ -1675,14 +1882,29 @@ class WidgetBuilder {
|
|
|
1675
1882
|
return { ...this.inheritanceContext };
|
|
1676
1883
|
}
|
|
1677
1884
|
build() {
|
|
1678
|
-
|
|
1885
|
+
const node = {
|
|
1679
1886
|
name: this.widgetState.name,
|
|
1680
1887
|
type: this.widgetState.type,
|
|
1681
1888
|
options: this.widgetState.options,
|
|
1682
1889
|
mode: this.widgetState.mode,
|
|
1683
1890
|
path: this.widgetState.path,
|
|
1684
1891
|
defaultValue: this.widgetState.defaultValue,
|
|
1892
|
+
children: this.widgetState.children,
|
|
1685
1893
|
};
|
|
1894
|
+
// Add extended properties if they exist
|
|
1895
|
+
if (this.widgetState.triggers !== undefined) {
|
|
1896
|
+
node.triggers = this.widgetState.triggers;
|
|
1897
|
+
}
|
|
1898
|
+
if (this.widgetState.meta !== undefined) {
|
|
1899
|
+
node.meta = this.widgetState.meta;
|
|
1900
|
+
}
|
|
1901
|
+
if (this.widgetState.valueTransforms !== undefined) {
|
|
1902
|
+
node.valueTransforms = this.widgetState.valueTransforms;
|
|
1903
|
+
}
|
|
1904
|
+
if (this.widgetState.visible !== undefined) {
|
|
1905
|
+
node.visible = this.widgetState.visible;
|
|
1906
|
+
}
|
|
1907
|
+
return node;
|
|
1686
1908
|
}
|
|
1687
1909
|
}
|
|
1688
1910
|
//#region ---- Action Builder Implementation ----
|
|
@@ -1696,7 +1918,6 @@ class ActionBuilder {
|
|
|
1696
1918
|
}
|
|
1697
1919
|
this.dialogBuilder['dialogState'].actions.footer.suffix.push({
|
|
1698
1920
|
title: text || '@general:actions.cancel.title',
|
|
1699
|
-
icon: 'fa-times',
|
|
1700
1921
|
color: 'default',
|
|
1701
1922
|
command: { name: 'cancel' },
|
|
1702
1923
|
});
|
|
@@ -1708,17 +1929,25 @@ class ActionBuilder {
|
|
|
1708
1929
|
}
|
|
1709
1930
|
this.dialogBuilder['dialogState'].actions.footer.suffix.push({
|
|
1710
1931
|
title: text || '@general:actions.submit.title',
|
|
1711
|
-
icon: 'fa-check',
|
|
1712
1932
|
color: 'primary',
|
|
1713
1933
|
command: { name: 'submit', options: { validate: true } },
|
|
1714
1934
|
});
|
|
1715
1935
|
return this;
|
|
1716
1936
|
}
|
|
1717
1937
|
custom(action) {
|
|
1718
|
-
|
|
1719
|
-
|
|
1938
|
+
const position = action.position ?? 'suffix';
|
|
1939
|
+
if (position === 'prefix') {
|
|
1940
|
+
if (!this.dialogBuilder['dialogState'].actions.footer.prefix) {
|
|
1941
|
+
this.dialogBuilder['dialogState'].actions.footer.prefix = [];
|
|
1942
|
+
}
|
|
1943
|
+
this.dialogBuilder['dialogState'].actions.footer.prefix.push(action);
|
|
1944
|
+
}
|
|
1945
|
+
else {
|
|
1946
|
+
if (!this.dialogBuilder['dialogState'].actions.footer.suffix) {
|
|
1947
|
+
this.dialogBuilder['dialogState'].actions.footer.suffix = [];
|
|
1948
|
+
}
|
|
1949
|
+
this.dialogBuilder['dialogState'].actions.footer.suffix.push(action);
|
|
1720
1950
|
}
|
|
1721
|
-
this.dialogBuilder['dialogState'].actions.footer.suffix.push(action);
|
|
1722
1951
|
return this;
|
|
1723
1952
|
}
|
|
1724
1953
|
}
|
|
@@ -1867,22 +2096,27 @@ class AXPLayoutRendererComponent {
|
|
|
1867
2096
|
/**
|
|
1868
2097
|
* Form definition containing groups and fields OR widget tree
|
|
1869
2098
|
*/
|
|
1870
|
-
this.layout = input.required(...(ngDevMode ? [{ debugName: "layout" }] : []));
|
|
2099
|
+
this.layout = input.required(...(ngDevMode ? [{ debugName: "layout" }] : /* istanbul ignore next */ []));
|
|
1871
2100
|
/**
|
|
1872
2101
|
* Form context/model data
|
|
1873
2102
|
*/
|
|
1874
|
-
this.context = model({}, ...(ngDevMode ? [{ debugName: "context" }] : []));
|
|
2103
|
+
this.context = model({}, ...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
|
|
1875
2104
|
/**
|
|
1876
2105
|
* Form appearance and density styling (normal, compact, spacious)
|
|
1877
2106
|
*/
|
|
1878
|
-
this.look = input('fieldset', ...(ngDevMode ? [{ debugName: "look" }] : []));
|
|
2107
|
+
this.look = input('fieldset', ...(ngDevMode ? [{ debugName: "look" }] : /* istanbul ignore next */ []));
|
|
1879
2108
|
/**
|
|
1880
2109
|
* Default form mode. Can be overridden by section/group and field.
|
|
1881
2110
|
*/
|
|
1882
|
-
this.mode = input('edit', ...(ngDevMode ? [{ debugName: "mode" }] : []));
|
|
2111
|
+
this.mode = input('edit', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
|
|
1883
2112
|
//#endregion
|
|
1884
2113
|
//#region ---- Widget Tree Conversion ----
|
|
1885
|
-
this.widgetTree = signal(null, ...(ngDevMode ? [{ debugName: "widgetTree" }] : []));
|
|
2114
|
+
this.widgetTree = signal(null, ...(ngDevMode ? [{ debugName: "widgetTree" }] : /* istanbul ignore next */ []));
|
|
2115
|
+
/**
|
|
2116
|
+
* Prefer explicit {@link AXPWidgetNode.mode} on the root node (e.g. dialog flex `mode('view')`)
|
|
2117
|
+
* so nested widgets resolve view vs edit correctly; fall back to the layout `mode` input.
|
|
2118
|
+
*/
|
|
2119
|
+
this.effectiveRenderMode = computed(() => this.widgetTree()?.mode ?? this.mode(), ...(ngDevMode ? [{ debugName: "effectiveRenderMode" }] : /* istanbul ignore next */ []));
|
|
1886
2120
|
/**
|
|
1887
2121
|
* Convert layout data to widget tree when inputs change
|
|
1888
2122
|
*/
|
|
@@ -1907,7 +2141,7 @@ class AXPLayoutRendererComponent {
|
|
|
1907
2141
|
if (!isEqual(prev, tree)) {
|
|
1908
2142
|
this.widgetTree.set(tree);
|
|
1909
2143
|
}
|
|
1910
|
-
}, ...(ngDevMode ? [{ debugName: "conversionEffect" }] : []));
|
|
2144
|
+
}, ...(ngDevMode ? [{ debugName: "conversionEffect" }] : /* istanbul ignore next */ []));
|
|
1911
2145
|
//#endregion
|
|
1912
2146
|
//#region ---- Outputs ----
|
|
1913
2147
|
/**
|
|
@@ -1920,12 +2154,12 @@ class AXPLayoutRendererComponent {
|
|
|
1920
2154
|
this.validityChange = output();
|
|
1921
2155
|
//#endregion
|
|
1922
2156
|
//#region ---- Properties ----
|
|
1923
|
-
this.form = viewChild(AXFormComponent, ...(ngDevMode ? [{ debugName: "form" }] : []));
|
|
1924
|
-
this.container = viewChild(AXPWidgetContainerComponent, ...(ngDevMode ? [{ debugName: "container" }] : []));
|
|
2157
|
+
this.form = viewChild(AXFormComponent, ...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
|
|
2158
|
+
this.container = viewChild(AXPWidgetContainerComponent, ...(ngDevMode ? [{ debugName: "container" }] : /* istanbul ignore next */ []));
|
|
1925
2159
|
/**
|
|
1926
2160
|
* Internal context signal for reactivity
|
|
1927
2161
|
*/
|
|
1928
|
-
this.internalContext = signal({}, ...(ngDevMode ? [{ debugName: "internalContext" }] : []));
|
|
2162
|
+
this.internalContext = signal({}, ...(ngDevMode ? [{ debugName: "internalContext" }] : /* istanbul ignore next */ []));
|
|
1929
2163
|
/**
|
|
1930
2164
|
* Initial context for reset functionality
|
|
1931
2165
|
*/
|
|
@@ -1938,7 +2172,7 @@ class AXPLayoutRendererComponent {
|
|
|
1938
2172
|
this.#contextSyncEffect = effect(() => {
|
|
1939
2173
|
const ctx = this.context() ?? {};
|
|
1940
2174
|
this.contextUpdateSubject.next(ctx);
|
|
1941
|
-
}, ...(ngDevMode ? [{ debugName: "#contextSyncEffect" }] : []));
|
|
2175
|
+
}, ...(ngDevMode ? [{ debugName: "#contextSyncEffect" }] : /* istanbul ignore next */ []));
|
|
1942
2176
|
/**
|
|
1943
2177
|
* Effect to handle widget tree status changes
|
|
1944
2178
|
*/
|
|
@@ -1947,7 +2181,7 @@ class AXPLayoutRendererComponent {
|
|
|
1947
2181
|
if (widgetTree) {
|
|
1948
2182
|
this.container()?.builderService.setStatus(AXPPageStatus.Rendered);
|
|
1949
2183
|
}
|
|
1950
|
-
}, ...(ngDevMode ? [{ debugName: "#widgetStatusEffect" }] : []));
|
|
2184
|
+
}, ...(ngDevMode ? [{ debugName: "#widgetStatusEffect" }] : /* istanbul ignore next */ []));
|
|
1951
2185
|
}
|
|
1952
2186
|
//#endregion
|
|
1953
2187
|
//#region ---- Lifecycle Methods ----
|
|
@@ -2087,62 +2321,97 @@ class AXPLayoutRendererComponent {
|
|
|
2087
2321
|
isWidgetNode(data) {
|
|
2088
2322
|
return data && typeof data === 'object' && 'type' in data && typeof data.type === 'string';
|
|
2089
2323
|
}
|
|
2090
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
2091
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
2324
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2325
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPLayoutRendererComponent, isStandalone: true, selector: "axp-layout-renderer", inputs: { layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: true, transformFunction: null }, context: { classPropertyName: "context", publicName: "context", isSignal: true, isRequired: false, transformFunction: null }, look: { classPropertyName: "look", publicName: "look", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { context: "contextChange", contextInitiated: "contextInitiated", validityChange: "validityChange" }, viewQueries: [{ propertyName: "form", first: true, predicate: AXFormComponent, descendants: true, isSignal: true }, { propertyName: "container", first: true, predicate: AXPWidgetContainerComponent, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
2092
2326
|
<ax-form>
|
|
2093
2327
|
<axp-widgets-container [context]="internalContext()" (onContextChanged)="handleContextChanged($event)">
|
|
2094
2328
|
@if (widgetTree()) {
|
|
2095
|
-
<ng-container
|
|
2329
|
+
<ng-container
|
|
2330
|
+
axp-widget-renderer
|
|
2331
|
+
[node]="widgetTree()!"
|
|
2332
|
+
[mode]="effectiveRenderMode()"
|
|
2333
|
+
></ng-container>
|
|
2096
2334
|
}
|
|
2097
2335
|
</axp-widgets-container>
|
|
2098
2336
|
</ax-form>
|
|
2099
|
-
`, isInline: true, styles: [":host{display:block;width:100%}\n"], dependencies: [{ kind: "ngmodule", type:
|
|
2337
|
+
`, isInline: true, styles: [":host{display:block;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i1.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: i1.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged", "onLoad"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXFormModule }, { kind: "component", type: i2.AXFormComponent, selector: "ax-form", inputs: ["disabled", "readonly", "labelMode", "look", "messageStyle", "updateOn", "inUserInteractionActive"], outputs: ["onValidate", "updateOnChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2100
2338
|
}
|
|
2101
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2339
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutRendererComponent, decorators: [{
|
|
2102
2340
|
type: Component,
|
|
2103
|
-
args: [{ selector: 'axp-layout-renderer', standalone: true, imports: [
|
|
2341
|
+
args: [{ selector: 'axp-layout-renderer', standalone: true, imports: [AXPWidgetCoreModule, AXFormModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
2104
2342
|
<ax-form>
|
|
2105
2343
|
<axp-widgets-container [context]="internalContext()" (onContextChanged)="handleContextChanged($event)">
|
|
2106
2344
|
@if (widgetTree()) {
|
|
2107
|
-
<ng-container
|
|
2345
|
+
<ng-container
|
|
2346
|
+
axp-widget-renderer
|
|
2347
|
+
[node]="widgetTree()!"
|
|
2348
|
+
[mode]="effectiveRenderMode()"
|
|
2349
|
+
></ng-container>
|
|
2108
2350
|
}
|
|
2109
2351
|
</axp-widgets-container>
|
|
2110
2352
|
</ax-form>
|
|
2111
2353
|
`, styles: [":host{display:block;width:100%}\n"] }]
|
|
2112
2354
|
}], propDecorators: { layout: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout", required: true }] }], context: [{ type: i0.Input, args: [{ isSignal: true, alias: "context", required: false }] }, { type: i0.Output, args: ["contextChange"] }], look: [{ type: i0.Input, args: [{ isSignal: true, alias: "look", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], contextInitiated: [{ type: i0.Output, args: ["contextInitiated"] }], validityChange: [{ type: i0.Output, args: ["validityChange"] }], form: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AXFormComponent), { isSignal: true }] }], container: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AXPWidgetContainerComponent), { isSignal: true }] }] } });
|
|
2113
2355
|
|
|
2356
|
+
/** Registration key for {@link AXPPreviewWidgetFieldCommand}; lives alone so `LayoutBuilderModule` can reference it without static-importing the command implementation. */
|
|
2357
|
+
const AXP_PREVIEW_WIDGET_FIELD_COMMAND_KEY = 'Widget:Preview';
|
|
2358
|
+
|
|
2114
2359
|
class LayoutBuilderModule {
|
|
2115
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
2116
|
-
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "
|
|
2117
|
-
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "
|
|
2360
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LayoutBuilderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
|
|
2361
|
+
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: LayoutBuilderModule, imports: [CommonModule, AXPLayoutRendererComponent], exports: [AXPLayoutRendererComponent] }); }
|
|
2362
|
+
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LayoutBuilderModule, providers: [
|
|
2363
|
+
AXPLayoutBuilderService,
|
|
2364
|
+
provideCommandSetups([
|
|
2365
|
+
{
|
|
2366
|
+
key: AXP_PREVIEW_WIDGET_FIELD_COMMAND_KEY,
|
|
2367
|
+
command: () => Promise.resolve().then(function () { return previewWidgetField_command; }).then((c) => c.AXPPreviewWidgetFieldCommand),
|
|
2368
|
+
},
|
|
2369
|
+
]),
|
|
2370
|
+
], imports: [CommonModule, AXPLayoutRendererComponent] }); }
|
|
2118
2371
|
}
|
|
2119
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2372
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LayoutBuilderModule, decorators: [{
|
|
2120
2373
|
type: NgModule,
|
|
2121
2374
|
args: [{
|
|
2122
2375
|
imports: [CommonModule, AXPLayoutRendererComponent],
|
|
2123
|
-
providers: [
|
|
2376
|
+
providers: [
|
|
2377
|
+
AXPLayoutBuilderService,
|
|
2378
|
+
provideCommandSetups([
|
|
2379
|
+
{
|
|
2380
|
+
key: AXP_PREVIEW_WIDGET_FIELD_COMMAND_KEY,
|
|
2381
|
+
command: () => Promise.resolve().then(function () { return previewWidgetField_command; }).then((c) => c.AXPPreviewWidgetFieldCommand),
|
|
2382
|
+
},
|
|
2383
|
+
]),
|
|
2384
|
+
],
|
|
2124
2385
|
exports: [AXPLayoutRendererComponent],
|
|
2125
2386
|
}]
|
|
2126
2387
|
}] });
|
|
2127
2388
|
|
|
2128
|
-
//#endregion
|
|
2129
|
-
|
|
2130
2389
|
class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
2131
2390
|
constructor() {
|
|
2132
2391
|
super(...arguments);
|
|
2133
2392
|
this.result = new EventEmitter();
|
|
2134
2393
|
this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
|
|
2135
|
-
this.
|
|
2394
|
+
this.commandService = inject(AXPCommandService);
|
|
2395
|
+
this.hookService = inject(AXPHookService, { optional: true });
|
|
2396
|
+
/** Ensures `show()` resolves once when the dialog closes (footer action or header close). */
|
|
2397
|
+
this.callbackInvoked = false;
|
|
2398
|
+
this.context = signal({}, ...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
|
|
2136
2399
|
// This will be set by the popup service automatically - same as dynamic-dialog
|
|
2137
2400
|
this.callBack = () => { };
|
|
2138
|
-
this.isDialogLoading = signal(false, ...(ngDevMode ? [{ debugName: "isDialogLoading" }] : []));
|
|
2401
|
+
this.isDialogLoading = signal(false, ...(ngDevMode ? [{ debugName: "isDialogLoading" }] : /* istanbul ignore next */ []));
|
|
2139
2402
|
// Aggregated actions for footer rendering
|
|
2140
|
-
this.footerPrefix = signal([], ...(ngDevMode ? [{ debugName: "footerPrefix" }] : []));
|
|
2141
|
-
this.footerSuffix = signal([], ...(ngDevMode ? [{ debugName: "footerSuffix" }] : []));
|
|
2403
|
+
this.footerPrefix = signal([], ...(ngDevMode ? [{ debugName: "footerPrefix" }] : /* istanbul ignore next */ []));
|
|
2404
|
+
this.footerSuffix = signal([], ...(ngDevMode ? [{ debugName: "footerSuffix" }] : /* istanbul ignore next */ []));
|
|
2405
|
+
/**
|
|
2406
|
+
* Correlate layout context snapshots for distributed hooks (`layout-builder.dialog.context-changed`).
|
|
2407
|
+
*/
|
|
2408
|
+
this.contextChangedHooksSessionKey = typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
|
|
2409
|
+
? crypto.randomUUID()
|
|
2410
|
+
: `layout-dialog-ctx-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
2142
2411
|
//#endregion
|
|
2143
2412
|
//#region ---- View Accessors ----
|
|
2144
2413
|
// Access the internal layout renderer to reach the widgets container injector
|
|
2145
|
-
this.layoutRenderer = viewChild(AXPLayoutRendererComponent, ...(ngDevMode ? [{ debugName: "layoutRenderer" }] : []));
|
|
2414
|
+
this.layoutRenderer = viewChild(AXPLayoutRendererComponent, ...(ngDevMode ? [{ debugName: "layoutRenderer" }] : /* istanbul ignore next */ []));
|
|
2146
2415
|
this.#eff = effect(() => {
|
|
2147
2416
|
let count = 0;
|
|
2148
2417
|
this.aggregateAndEvaluateActions();
|
|
@@ -2150,10 +2419,10 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2150
2419
|
const renderer = this.layoutRenderer();
|
|
2151
2420
|
const container = renderer?.getContainer();
|
|
2152
2421
|
this.widgetCoreService = container?.builderService ?? null;
|
|
2153
|
-
count = this.widgetCoreService
|
|
2422
|
+
count = this.widgetCoreService?.registeredWidgetsCount();
|
|
2154
2423
|
}
|
|
2155
2424
|
else {
|
|
2156
|
-
count = this.widgetCoreService
|
|
2425
|
+
count = this.widgetCoreService?.registeredWidgetsCount();
|
|
2157
2426
|
// Clear existing timer
|
|
2158
2427
|
if (this.debounceTimer) {
|
|
2159
2428
|
clearTimeout(this.debounceTimer);
|
|
@@ -2163,23 +2432,48 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2163
2432
|
this.aggregateAndEvaluateActions();
|
|
2164
2433
|
}, 200);
|
|
2165
2434
|
}
|
|
2166
|
-
}, ...(ngDevMode ? [{ debugName: "#eff" }] : []));
|
|
2435
|
+
}, ...(ngDevMode ? [{ debugName: "#eff" }] : /* istanbul ignore next */ []));
|
|
2167
2436
|
}
|
|
2168
2437
|
//#endregion
|
|
2169
2438
|
//#region ---- Lifecycle ----
|
|
2170
2439
|
ngOnInit() {
|
|
2171
|
-
// Initialize context with provided context
|
|
2172
2440
|
this.context.set(this.config?.context || {});
|
|
2441
|
+
void this.invokeLayoutContextChangedHooks();
|
|
2173
2442
|
}
|
|
2174
2443
|
#eff;
|
|
2175
2444
|
//#endregion
|
|
2176
2445
|
handleContextChanged(event) {
|
|
2177
2446
|
this.context.set(event);
|
|
2178
2447
|
this.aggregateAndEvaluateActions();
|
|
2448
|
+
void this.invokeLayoutContextChangedHooks();
|
|
2179
2449
|
}
|
|
2180
2450
|
handleContextInitiated(event) {
|
|
2181
2451
|
this.context.set(event);
|
|
2182
2452
|
this.aggregateAndEvaluateActions();
|
|
2453
|
+
void this.invokeLayoutContextChangedHooks();
|
|
2454
|
+
}
|
|
2455
|
+
async invokeLayoutContextChangedHooks() {
|
|
2456
|
+
const meta = this.config?.metadata;
|
|
2457
|
+
if (!this.hookService) {
|
|
2458
|
+
return;
|
|
2459
|
+
}
|
|
2460
|
+
const payload = {
|
|
2461
|
+
sessionKey: this.contextChangedHooksSessionKey,
|
|
2462
|
+
getContext: () => (this.context() ?? {}),
|
|
2463
|
+
metadata: meta,
|
|
2464
|
+
patchContext: (partial) => {
|
|
2465
|
+
const merged = merge({}, this.context(), partial);
|
|
2466
|
+
this.context.set(merged);
|
|
2467
|
+
this.layoutRenderer()?.updateContext(merged);
|
|
2468
|
+
},
|
|
2469
|
+
setLoading: (loading) => this.isDialogLoading.set(loading),
|
|
2470
|
+
};
|
|
2471
|
+
try {
|
|
2472
|
+
await this.hookService.runAsync(AXP_LAYOUT_BUILDER_DIALOG_CONTEXT_CHANGED_HOOK_KEY, payload);
|
|
2473
|
+
}
|
|
2474
|
+
catch {
|
|
2475
|
+
// Hook providers are best-effort; avoid breaking the dialog lifecycle.
|
|
2476
|
+
}
|
|
2183
2477
|
}
|
|
2184
2478
|
footerPrefixActions() {
|
|
2185
2479
|
return this.footerPrefix();
|
|
@@ -2194,38 +2488,146 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2194
2488
|
return this.isDialogLoading();
|
|
2195
2489
|
}
|
|
2196
2490
|
async executeAction(action) {
|
|
2197
|
-
const cmd = action.command;
|
|
2198
|
-
if (cmd
|
|
2491
|
+
const cmd = this.resolveActionCommandName(action.command);
|
|
2492
|
+
if (this.shouldValidateBeforeAction(cmd)) {
|
|
2199
2493
|
const isValid = await this.layoutRenderer()?.validate();
|
|
2200
2494
|
if (!isValid?.result) {
|
|
2201
2495
|
return;
|
|
2202
2496
|
}
|
|
2203
2497
|
}
|
|
2204
|
-
|
|
2498
|
+
//TODO: matin, why we need this? maybe we can remove it?
|
|
2499
|
+
if (cmd?.startsWith('widget:')) {
|
|
2205
2500
|
const parsed = this.parseWidgetCommand(cmd);
|
|
2206
2501
|
if (parsed.widgetName && parsed.action) {
|
|
2207
|
-
await this.
|
|
2502
|
+
await this.invokeWidget(parsed.widgetName, parsed.action, {});
|
|
2208
2503
|
await this.aggregateAndEvaluateActions();
|
|
2209
2504
|
return;
|
|
2210
2505
|
}
|
|
2211
2506
|
}
|
|
2507
|
+
if (cmd && this.commandService.exists(cmd)) {
|
|
2508
|
+
const dialogRef = this.createDialogRef(cmd);
|
|
2509
|
+
const integration = (this.config.metadata ?? {});
|
|
2510
|
+
try {
|
|
2511
|
+
const cmdResult = await this.commandService.execute(cmd, { dialogRef, integration });
|
|
2512
|
+
if (!cmdResult?.success) {
|
|
2513
|
+
return;
|
|
2514
|
+
}
|
|
2515
|
+
if (this.shouldKeepDialogOpenAfterCommandResult(cmdResult)) {
|
|
2516
|
+
return;
|
|
2517
|
+
}
|
|
2518
|
+
this.resolveDialog(cmdResult);
|
|
2519
|
+
await this.closeWithOptionalSkipValidate(cmdResult);
|
|
2520
|
+
}
|
|
2521
|
+
catch (error) {
|
|
2522
|
+
console.error('Error executing action', cmd, error);
|
|
2523
|
+
}
|
|
2524
|
+
return;
|
|
2525
|
+
}
|
|
2526
|
+
const context = this.context();
|
|
2527
|
+
const onAction = this.config?.onAction;
|
|
2528
|
+
if (onAction) {
|
|
2529
|
+
const dialogRef = this.createDialogRef(cmd);
|
|
2530
|
+
try {
|
|
2531
|
+
this.isDialogLoading.set(true);
|
|
2532
|
+
const result = await Promise.resolve(onAction(dialogRef));
|
|
2533
|
+
if (this.shouldKeepDialogOpenAfterCommandResult(result)) {
|
|
2534
|
+
return;
|
|
2535
|
+
}
|
|
2536
|
+
this.resolveDialog(result);
|
|
2537
|
+
await this.closeWithOptionalSkipValidate(result);
|
|
2538
|
+
}
|
|
2539
|
+
catch {
|
|
2540
|
+
// Handler threw: stay open for retry, actions remain clickable
|
|
2541
|
+
}
|
|
2542
|
+
finally {
|
|
2543
|
+
this.isDialogLoading.set(false);
|
|
2544
|
+
}
|
|
2545
|
+
return;
|
|
2546
|
+
}
|
|
2212
2547
|
// Fallback: treat as regular dialog action (cancel/confirm/custom)
|
|
2213
|
-
const result = { context
|
|
2548
|
+
const result = { context, action: cmd };
|
|
2214
2549
|
this.dialogResult = result;
|
|
2215
2550
|
if (this.data) {
|
|
2216
2551
|
this.data.context = result.context;
|
|
2217
2552
|
this.data.action = result.action;
|
|
2218
2553
|
}
|
|
2219
|
-
this.
|
|
2220
|
-
|
|
2221
|
-
|
|
2554
|
+
this.resolveDialog({
|
|
2555
|
+
...this.createDialogRef(cmd),
|
|
2556
|
+
action: () => result.action,
|
|
2557
|
+
});
|
|
2558
|
+
// Without `onAction`, only the configured cancel action dismisses the dialog (not submit/custom).
|
|
2559
|
+
if (cmd === 'cancel') {
|
|
2560
|
+
await this.close(result);
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
/** Whether the layout form should be validated before running this footer command. */
|
|
2564
|
+
shouldValidateBeforeAction(cmd) {
|
|
2565
|
+
if (!cmd || cmd === 'cancel' || cmd === 'entity-form-done' || cmd === 'entity-form-next-step') {
|
|
2566
|
+
return false;
|
|
2567
|
+
}
|
|
2568
|
+
if (cmd.startsWith('widget:')) {
|
|
2569
|
+
return false;
|
|
2570
|
+
}
|
|
2571
|
+
if (this.commandService.exists(cmd)) {
|
|
2572
|
+
return false;
|
|
2573
|
+
}
|
|
2574
|
+
return true;
|
|
2575
|
+
}
|
|
2576
|
+
/** True when a footer handler or command result asks to leave the dialog open (`keepDialogOpen` on the result or `result.data`). */
|
|
2577
|
+
shouldKeepDialogOpenAfterCommandResult(result) {
|
|
2578
|
+
if (!result || typeof result !== 'object') {
|
|
2579
|
+
return false;
|
|
2580
|
+
}
|
|
2581
|
+
const top = result;
|
|
2582
|
+
if (top.keepDialogOpen === true) {
|
|
2583
|
+
return true;
|
|
2584
|
+
}
|
|
2585
|
+
if (top.data != null && typeof top.data === 'object' && 'keepDialogOpen' in top.data) {
|
|
2586
|
+
return top.data.keepDialogOpen === true;
|
|
2587
|
+
}
|
|
2588
|
+
return false;
|
|
2589
|
+
}
|
|
2590
|
+
/** Resolves the dialog `show()` promise exactly once. */
|
|
2591
|
+
resolveDialog(result) {
|
|
2592
|
+
if (this.callbackInvoked) {
|
|
2593
|
+
return;
|
|
2594
|
+
}
|
|
2595
|
+
this.callbackInvoked = true;
|
|
2596
|
+
this.callBack(result);
|
|
2597
|
+
}
|
|
2598
|
+
createDialogRef(actionCmd) {
|
|
2599
|
+
return {
|
|
2600
|
+
close: (res) => {
|
|
2601
|
+
void this.closeWithOptionalSkipValidate(res);
|
|
2222
2602
|
},
|
|
2223
2603
|
context: () => this.context(),
|
|
2224
|
-
action: () =>
|
|
2225
|
-
setLoading: (loading) =>
|
|
2226
|
-
|
|
2604
|
+
action: () => actionCmd,
|
|
2605
|
+
setLoading: (loading) => this.isDialogLoading.set(loading),
|
|
2606
|
+
patchContext: (partial) => {
|
|
2607
|
+
const merged = merge({}, this.context(), partial);
|
|
2608
|
+
this.context.set(merged);
|
|
2609
|
+
this.layoutRenderer()?.updateContext(merged);
|
|
2227
2610
|
},
|
|
2228
|
-
|
|
2611
|
+
invokeWidget: (widgetName, method, opts) => this.invokeWidget(widgetName, method, opts ?? {}),
|
|
2612
|
+
};
|
|
2613
|
+
}
|
|
2614
|
+
async closeWithOptionalSkipValidate(result) {
|
|
2615
|
+
if (result && typeof result === 'object' && result.skipValidate) {
|
|
2616
|
+
this.result.emit(result);
|
|
2617
|
+
await super.close(result);
|
|
2618
|
+
return;
|
|
2619
|
+
}
|
|
2620
|
+
await this.close(result);
|
|
2621
|
+
}
|
|
2622
|
+
/** Resolves footer/widget action command to a string (e.g. `cancel`, `submit`, `widget:...`). */
|
|
2623
|
+
resolveActionCommandName(command) {
|
|
2624
|
+
if (typeof command === 'string') {
|
|
2625
|
+
return command;
|
|
2626
|
+
}
|
|
2627
|
+
if (command && typeof command === 'object' && 'name' in command) {
|
|
2628
|
+
return command.name;
|
|
2629
|
+
}
|
|
2630
|
+
return undefined;
|
|
2229
2631
|
}
|
|
2230
2632
|
parseWidgetCommand(cmd) {
|
|
2231
2633
|
// Expected 'widget:<widgetName>.<action>'
|
|
@@ -2237,7 +2639,7 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2237
2639
|
return {};
|
|
2238
2640
|
return { widgetName: rest.slice(0, dot), action: rest.slice(dot + 1) };
|
|
2239
2641
|
}
|
|
2240
|
-
async
|
|
2642
|
+
async invokeWidget(widgetName, apiMethod, opts) {
|
|
2241
2643
|
if (!this.widgetCoreService)
|
|
2242
2644
|
return;
|
|
2243
2645
|
try {
|
|
@@ -2247,18 +2649,25 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2247
2649
|
if (typeof fn === 'function') {
|
|
2248
2650
|
await Promise.resolve(fn({
|
|
2249
2651
|
close: (result) => {
|
|
2250
|
-
this.
|
|
2652
|
+
void this.closeWithOptionalSkipValidate(result);
|
|
2251
2653
|
},
|
|
2252
2654
|
context: () => this.context(),
|
|
2253
2655
|
setLoading: (loading) => {
|
|
2254
|
-
this.isDialogLoading.set(loading);
|
|
2656
|
+
(opts.setLoading ?? ((v) => this.isDialogLoading.set(v)))(loading);
|
|
2255
2657
|
},
|
|
2256
2658
|
}));
|
|
2659
|
+
// Footer predicates (e.g. wizard step) must refresh when the widget advances outside executeAction (e.g. dialogRef.invokeWidget after entity-form continue).
|
|
2660
|
+
await this.aggregateAndEvaluateActions();
|
|
2257
2661
|
}
|
|
2258
2662
|
}
|
|
2259
|
-
catch {
|
|
2663
|
+
catch {
|
|
2664
|
+
//
|
|
2665
|
+
}
|
|
2260
2666
|
}
|
|
2261
2667
|
async close(result) {
|
|
2668
|
+
if (!this.callbackInvoked) {
|
|
2669
|
+
this.resolveDialog(this.createDialogRef('cancel'));
|
|
2670
|
+
}
|
|
2262
2671
|
if (result) {
|
|
2263
2672
|
const isValid = await this.layoutRenderer()?.validate();
|
|
2264
2673
|
if (isValid?.result) {
|
|
@@ -2306,6 +2715,7 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2306
2715
|
zone: 'footer',
|
|
2307
2716
|
placement,
|
|
2308
2717
|
scope: a.scope,
|
|
2718
|
+
predicateApiWidgetName: a.predicateApiWidgetName,
|
|
2309
2719
|
});
|
|
2310
2720
|
const prefix = (footer?.prefix || []).map((a) => mapOne(a, 'prefix'));
|
|
2311
2721
|
const suffix = (footer?.suffix || []).map((a) => mapOne(a, 'suffix'));
|
|
@@ -2315,16 +2725,18 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2315
2725
|
const out = [];
|
|
2316
2726
|
for (const a of actions) {
|
|
2317
2727
|
const parsed = typeof a.command === 'string' ? this.parseWidgetCommand(a.command) : {};
|
|
2318
|
-
const
|
|
2728
|
+
const widgetNameForApi = parsed.widgetName ?? a.predicateApiWidgetName;
|
|
2729
|
+
const api = widgetNameForApi ? await this.resolveApi(widgetNameForApi) : undefined;
|
|
2319
2730
|
const scope = {
|
|
2320
2731
|
api,
|
|
2321
|
-
widget: { name:
|
|
2732
|
+
widget: { name: widgetNameForApi },
|
|
2322
2733
|
dialog: { context: this.context() },
|
|
2323
2734
|
context: this.context(),
|
|
2324
2735
|
};
|
|
2325
2736
|
const disabled = await this.evalBool(a.disabled, scope);
|
|
2326
2737
|
const hidden = await this.evalBool(a.hidden, scope);
|
|
2327
|
-
|
|
2738
|
+
const resolvedTitle = (await this.evalActionTitle(a.title, scope)) ?? a.title;
|
|
2739
|
+
out.push({ ...a, disabled, hidden, title: resolvedTitle });
|
|
2328
2740
|
}
|
|
2329
2741
|
return out;
|
|
2330
2742
|
}
|
|
@@ -2342,6 +2754,25 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2342
2754
|
}
|
|
2343
2755
|
return value;
|
|
2344
2756
|
}
|
|
2757
|
+
/** Resolves footer action title when it contains {{ ... }} (e.g. wizard labels from dialog context). */
|
|
2758
|
+
async evalActionTitle(value, scope) {
|
|
2759
|
+
if (value == null || typeof value !== 'string' || !value.includes('{{')) {
|
|
2760
|
+
return value;
|
|
2761
|
+
}
|
|
2762
|
+
try {
|
|
2763
|
+
const result = await this.expressionEvaluator.evaluate(value, scope);
|
|
2764
|
+
if (typeof result === 'string' && result.length > 0) {
|
|
2765
|
+
return result;
|
|
2766
|
+
}
|
|
2767
|
+
if (result != null && result !== false) {
|
|
2768
|
+
return String(result);
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
catch {
|
|
2772
|
+
//
|
|
2773
|
+
}
|
|
2774
|
+
return value;
|
|
2775
|
+
}
|
|
2345
2776
|
async resolveApi(widgetName) {
|
|
2346
2777
|
try {
|
|
2347
2778
|
await this.widgetCoreService?.waitForWidget(widgetName, 2000);
|
|
@@ -2352,9 +2783,13 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2352
2783
|
return undefined;
|
|
2353
2784
|
}
|
|
2354
2785
|
}
|
|
2355
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
2356
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "
|
|
2786
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDialogRendererComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
2787
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPDialogRendererComponent, isStandalone: true, selector: "axp-dialog-renderer", outputs: { result: "result" }, providers: [AXPContextStore], viewQueries: [{ propertyName: "layoutRenderer", first: true, predicate: AXPLayoutRendererComponent, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
|
|
2788
|
+
<axp-component-slot name="dialog-header" [context]="context()"></axp-component-slot>
|
|
2357
2789
|
<div class="ax-p-4">
|
|
2790
|
+
<!-- @if (config.message) {
|
|
2791
|
+
<p class="ax-mb-4 ax-leading-relaxed">{{ config.message | translate | async }}</p>
|
|
2792
|
+
} -->
|
|
2358
2793
|
<axp-layout-renderer
|
|
2359
2794
|
[layout]="config.definition"
|
|
2360
2795
|
[context]="context()"
|
|
@@ -2364,46 +2799,57 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2364
2799
|
</axp-layout-renderer>
|
|
2365
2800
|
</div>
|
|
2366
2801
|
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
@for (action of footerSuffixActions(); track $index) {
|
|
2385
|
-
<ax-button
|
|
2386
|
-
[disabled]="action.disabled || isSubmitting()"
|
|
2387
|
-
[text]="(action.title | translate | async)!"
|
|
2388
|
-
[look]="'solid'"
|
|
2389
|
-
[color]="action.color"
|
|
2390
|
-
(onClick)="executeAction(action)"
|
|
2391
|
-
>
|
|
2392
|
-
@if (isFormLoading()) {
|
|
2393
|
-
<ax-loading></ax-loading>
|
|
2394
|
-
}
|
|
2395
|
-
@if (action.icon) {
|
|
2802
|
+
<!-- Custom footer slot: if it has content, default footer is hidden -->
|
|
2803
|
+
<axp-component-slot name="dialog-footer" #footerSlot="slot" [context]="context()"></axp-component-slot>
|
|
2804
|
+
@if (footerSlot.isEmpty()) {
|
|
2805
|
+
<ax-footer>
|
|
2806
|
+
<ax-prefix>
|
|
2807
|
+
<axp-component-slot name="dialog-footer-prefix" [context]="context()"></axp-component-slot>
|
|
2808
|
+
@for (action of footerPrefixActions(); track $index) {
|
|
2809
|
+
<ax-button
|
|
2810
|
+
[disabled]="action.disabled || isFormLoading()"
|
|
2811
|
+
[text]="(action.title | translate | async)!"
|
|
2812
|
+
[look]="'outline'"
|
|
2813
|
+
[color]="action.color"
|
|
2814
|
+
(onClick)="executeAction(action)"
|
|
2815
|
+
>
|
|
2816
|
+
@if (isFormLoading()) {
|
|
2817
|
+
<ax-loading></ax-loading>
|
|
2818
|
+
}
|
|
2396
2819
|
<ax-prefix>
|
|
2397
|
-
|
|
2820
|
+
@if (action.icon) {
|
|
2821
|
+
<ax-icon [icon]="action.icon"></ax-icon>
|
|
2822
|
+
}
|
|
2398
2823
|
</ax-prefix>
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2824
|
+
</ax-button>
|
|
2825
|
+
}
|
|
2826
|
+
</ax-prefix>
|
|
2827
|
+
<ax-suffix>
|
|
2828
|
+
@for (action of footerSuffixActions(); track $index) {
|
|
2829
|
+
<ax-button
|
|
2830
|
+
[disabled]="action.disabled || isSubmitting()"
|
|
2831
|
+
[text]="(action.title | translate | async)!"
|
|
2832
|
+
[look]="'solid'"
|
|
2833
|
+
[color]="action.color"
|
|
2834
|
+
(onClick)="executeAction(action)"
|
|
2835
|
+
>
|
|
2836
|
+
@if (isFormLoading()) {
|
|
2837
|
+
<ax-loading></ax-loading>
|
|
2838
|
+
}
|
|
2839
|
+
@if (action.icon) {
|
|
2840
|
+
<ax-prefix>
|
|
2841
|
+
<ax-icon [icon]="action.icon"></ax-icon>
|
|
2842
|
+
</ax-prefix>
|
|
2843
|
+
}
|
|
2844
|
+
</ax-button>
|
|
2845
|
+
}
|
|
2846
|
+
<axp-component-slot name="dialog-footer-suffix" [context]="context()"></axp-component-slot>
|
|
2847
|
+
</ax-suffix>
|
|
2848
|
+
</ax-footer>
|
|
2849
|
+
}
|
|
2850
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: AXPLayoutRendererComponent, selector: "axp-layout-renderer", inputs: ["layout", "context", "look", "mode"], outputs: ["contextChange", "contextInitiated", "validityChange"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1$1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2$1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i3.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXPComponentSlotModule }, { kind: "directive", type: i4.AXPComponentSlotDirective, selector: "axp-component-slot", inputs: ["name", "host", "context"], exportAs: ["slot"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2405
2851
|
}
|
|
2406
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
2852
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDialogRendererComponent, decorators: [{
|
|
2407
2853
|
type: Component,
|
|
2408
2854
|
args: [{
|
|
2409
2855
|
selector: 'axp-dialog-renderer',
|
|
@@ -2415,9 +2861,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
|
|
|
2415
2861
|
AXDecoratorModule,
|
|
2416
2862
|
AXLoadingModule,
|
|
2417
2863
|
AXTranslationModule,
|
|
2864
|
+
AXPComponentSlotModule,
|
|
2418
2865
|
],
|
|
2866
|
+
providers: [AXPContextStore],
|
|
2867
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
2419
2868
|
template: `
|
|
2869
|
+
<axp-component-slot name="dialog-header" [context]="context()"></axp-component-slot>
|
|
2420
2870
|
<div class="ax-p-4">
|
|
2871
|
+
<!-- @if (config.message) {
|
|
2872
|
+
<p class="ax-mb-4 ax-leading-relaxed">{{ config.message | translate | async }}</p>
|
|
2873
|
+
} -->
|
|
2421
2874
|
<axp-layout-renderer
|
|
2422
2875
|
[layout]="config.definition"
|
|
2423
2876
|
[context]="context()"
|
|
@@ -2427,48 +2880,57 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
|
|
|
2427
2880
|
</axp-layout-renderer>
|
|
2428
2881
|
</div>
|
|
2429
2882
|
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
@for (action of footerSuffixActions(); track $index) {
|
|
2448
|
-
<ax-button
|
|
2449
|
-
[disabled]="action.disabled || isSubmitting()"
|
|
2450
|
-
[text]="(action.title | translate | async)!"
|
|
2451
|
-
[look]="'solid'"
|
|
2452
|
-
[color]="action.color"
|
|
2453
|
-
(onClick)="executeAction(action)"
|
|
2454
|
-
>
|
|
2455
|
-
@if (isFormLoading()) {
|
|
2456
|
-
<ax-loading></ax-loading>
|
|
2457
|
-
}
|
|
2458
|
-
@if (action.icon) {
|
|
2883
|
+
<!-- Custom footer slot: if it has content, default footer is hidden -->
|
|
2884
|
+
<axp-component-slot name="dialog-footer" #footerSlot="slot" [context]="context()"></axp-component-slot>
|
|
2885
|
+
@if (footerSlot.isEmpty()) {
|
|
2886
|
+
<ax-footer>
|
|
2887
|
+
<ax-prefix>
|
|
2888
|
+
<axp-component-slot name="dialog-footer-prefix" [context]="context()"></axp-component-slot>
|
|
2889
|
+
@for (action of footerPrefixActions(); track $index) {
|
|
2890
|
+
<ax-button
|
|
2891
|
+
[disabled]="action.disabled || isFormLoading()"
|
|
2892
|
+
[text]="(action.title | translate | async)!"
|
|
2893
|
+
[look]="'outline'"
|
|
2894
|
+
[color]="action.color"
|
|
2895
|
+
(onClick)="executeAction(action)"
|
|
2896
|
+
>
|
|
2897
|
+
@if (isFormLoading()) {
|
|
2898
|
+
<ax-loading></ax-loading>
|
|
2899
|
+
}
|
|
2459
2900
|
<ax-prefix>
|
|
2460
|
-
|
|
2901
|
+
@if (action.icon) {
|
|
2902
|
+
<ax-icon [icon]="action.icon"></ax-icon>
|
|
2903
|
+
}
|
|
2461
2904
|
</ax-prefix>
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2905
|
+
</ax-button>
|
|
2906
|
+
}
|
|
2907
|
+
</ax-prefix>
|
|
2908
|
+
<ax-suffix>
|
|
2909
|
+
@for (action of footerSuffixActions(); track $index) {
|
|
2910
|
+
<ax-button
|
|
2911
|
+
[disabled]="action.disabled || isSubmitting()"
|
|
2912
|
+
[text]="(action.title | translate | async)!"
|
|
2913
|
+
[look]="'solid'"
|
|
2914
|
+
[color]="action.color"
|
|
2915
|
+
(onClick)="executeAction(action)"
|
|
2916
|
+
>
|
|
2917
|
+
@if (isFormLoading()) {
|
|
2918
|
+
<ax-loading></ax-loading>
|
|
2919
|
+
}
|
|
2920
|
+
@if (action.icon) {
|
|
2921
|
+
<ax-prefix>
|
|
2922
|
+
<ax-icon [icon]="action.icon"></ax-icon>
|
|
2923
|
+
</ax-prefix>
|
|
2924
|
+
}
|
|
2925
|
+
</ax-button>
|
|
2926
|
+
}
|
|
2927
|
+
<axp-component-slot name="dialog-footer-suffix" [context]="context()"></axp-component-slot>
|
|
2928
|
+
</ax-suffix>
|
|
2929
|
+
</ax-footer>
|
|
2930
|
+
}
|
|
2467
2931
|
`,
|
|
2468
2932
|
}]
|
|
2469
|
-
}], propDecorators: {
|
|
2470
|
-
type: Input
|
|
2471
|
-
}], result: [{
|
|
2933
|
+
}], propDecorators: { result: [{
|
|
2472
2934
|
type: Output
|
|
2473
2935
|
}], layoutRenderer: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AXPLayoutRendererComponent), { isSignal: true }] }] } });
|
|
2474
2936
|
|
|
@@ -2477,9 +2939,190 @@ var dialogRenderer_component = /*#__PURE__*/Object.freeze({
|
|
|
2477
2939
|
AXPDialogRendererComponent: AXPDialogRendererComponent
|
|
2478
2940
|
});
|
|
2479
2941
|
|
|
2942
|
+
//#region ---- Imports ----
|
|
2943
|
+
/**
|
|
2944
|
+
* `customWidget` only forwards keys from its options bag into the built node via `addSingleWidget`.
|
|
2945
|
+
* Designer / configurator persist `defaultValue` (and other extended fields) on the widget node root;
|
|
2946
|
+
* spreading `options` alone drops them, so preview never applied defaults.
|
|
2947
|
+
*/
|
|
2948
|
+
/**
|
|
2949
|
+
* Widget options are sometimes persisted with an extra nesting (`options.options`) when context
|
|
2950
|
+
* was merged incorrectly. Flatten so list/data-source resolution sees `dataSource` at the top level.
|
|
2951
|
+
*/
|
|
2952
|
+
function optionsBagForPreview(node) {
|
|
2953
|
+
const raw = (node.options ?? {});
|
|
2954
|
+
const inner = raw['options'];
|
|
2955
|
+
if (inner !== undefined && typeof inner === 'object' && !Array.isArray(inner)) {
|
|
2956
|
+
const { options: _nested, ...rest } = raw;
|
|
2957
|
+
return { ...rest, ...inner };
|
|
2958
|
+
}
|
|
2959
|
+
return { ...raw };
|
|
2960
|
+
}
|
|
2961
|
+
function extendedNodePropsForPreview(node) {
|
|
2962
|
+
const out = {};
|
|
2963
|
+
if (node.defaultValue !== undefined) {
|
|
2964
|
+
out['defaultValue'] = node.defaultValue;
|
|
2965
|
+
}
|
|
2966
|
+
if (node.triggers !== undefined) {
|
|
2967
|
+
out['triggers'] = node.triggers;
|
|
2968
|
+
}
|
|
2969
|
+
if (node.meta !== undefined) {
|
|
2970
|
+
out['meta'] = node.meta;
|
|
2971
|
+
}
|
|
2972
|
+
if (node.valueTransforms !== undefined) {
|
|
2973
|
+
out['valueTransforms'] = node.valueTransforms;
|
|
2974
|
+
}
|
|
2975
|
+
if (node.visible !== undefined) {
|
|
2976
|
+
out['visible'] = node.visible;
|
|
2977
|
+
}
|
|
2978
|
+
if (node.mode !== undefined) {
|
|
2979
|
+
out['mode'] = node.mode;
|
|
2980
|
+
}
|
|
2981
|
+
if (node.children !== undefined) {
|
|
2982
|
+
out['children'] = node.children;
|
|
2983
|
+
}
|
|
2984
|
+
return out;
|
|
2985
|
+
}
|
|
2986
|
+
//#endregion
|
|
2987
|
+
//#region ---- Command ----
|
|
2988
|
+
/**
|
|
2989
|
+
* Opens a dialog that previews a widget configuration (same behavior as the preview button on
|
|
2990
|
+
* `axp-widget-field-configurator`). Invoked from that component and from entity list actions.
|
|
2991
|
+
*/
|
|
2992
|
+
class AXPPreviewWidgetFieldCommand {
|
|
2993
|
+
constructor() {
|
|
2994
|
+
this.formBuilderService = inject(AXPLayoutBuilderService);
|
|
2995
|
+
this.widgetRegistry = inject(AXPWidgetRegistryService);
|
|
2996
|
+
this.translationService = inject(AXTranslationService);
|
|
2997
|
+
this.crudService = inject(AXP_ENTITY_DEFINITION_CRUD_SERVICE, { optional: true });
|
|
2998
|
+
}
|
|
2999
|
+
async execute(input) {
|
|
3000
|
+
try {
|
|
3001
|
+
const merged = this.mergeInvocation(input);
|
|
3002
|
+
const currentWidget = this.normalizeWidget(merged['widget'] ?? merged['interface']);
|
|
3003
|
+
if (!currentWidget?.type) {
|
|
3004
|
+
return {
|
|
3005
|
+
success: false,
|
|
3006
|
+
message: {
|
|
3007
|
+
text: (await this.translationService.translateAsync('@general:messages.invalid-data')) || 'Invalid data',
|
|
3008
|
+
},
|
|
3009
|
+
};
|
|
3010
|
+
}
|
|
3011
|
+
const fieldName = String(merged['fieldName'] ?? merged['name'] ?? 'Field');
|
|
3012
|
+
const rawTitle = (merged['fieldTitle'] ?? merged['title']);
|
|
3013
|
+
const fieldTitleLabel = this.resolveFieldTitleLabel(rawTitle, fieldName);
|
|
3014
|
+
const dialogTitle = (await this.resolveWidgetDisplayTitle(currentWidget.type)) || currentWidget.type || fieldTitleLabel;
|
|
3015
|
+
const previewWidgetOptions = {
|
|
3016
|
+
...optionsBagForPreview(currentWidget),
|
|
3017
|
+
name: fieldName,
|
|
3018
|
+
...extendedNodePropsForPreview(currentWidget),
|
|
3019
|
+
};
|
|
3020
|
+
const dialogOutcome = await this.formBuilderService
|
|
3021
|
+
.create()
|
|
3022
|
+
.dialog((dialog) => {
|
|
3023
|
+
dialog
|
|
3024
|
+
.setTitle(dialogTitle)
|
|
3025
|
+
.setSize('md')
|
|
3026
|
+
.setCloseButton(true)
|
|
3027
|
+
.setContext({})
|
|
3028
|
+
.content((layoutBuilder) => {
|
|
3029
|
+
layoutBuilder.formField(fieldTitleLabel, (formField) => {
|
|
3030
|
+
formField.customWidget(currentWidget.type, previewWidgetOptions);
|
|
3031
|
+
});
|
|
3032
|
+
})
|
|
3033
|
+
.setActions((actions) => actions.cancel('@general:actions.close.title'));
|
|
3034
|
+
})
|
|
3035
|
+
.show();
|
|
3036
|
+
const cancelled = this.isCancelDialogOutcome(dialogOutcome);
|
|
3037
|
+
return {
|
|
3038
|
+
success: !cancelled,
|
|
3039
|
+
message: { text: '' },
|
|
3040
|
+
};
|
|
3041
|
+
}
|
|
3042
|
+
catch (error) {
|
|
3043
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
3044
|
+
return {
|
|
3045
|
+
success: false,
|
|
3046
|
+
message: { text: message },
|
|
3047
|
+
};
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
mergeInvocation(input) {
|
|
3051
|
+
const contextOptions = input.__context__?.options;
|
|
3052
|
+
const ctxData = input.__context__?.data;
|
|
3053
|
+
const { __context__: _ctx, ...rest } = input;
|
|
3054
|
+
return {
|
|
3055
|
+
...(ctxData ?? {}),
|
|
3056
|
+
...(contextOptions ?? {}),
|
|
3057
|
+
...rest,
|
|
3058
|
+
};
|
|
3059
|
+
}
|
|
3060
|
+
normalizeWidget(raw) {
|
|
3061
|
+
if (raw == null)
|
|
3062
|
+
return null;
|
|
3063
|
+
if (typeof raw === 'string') {
|
|
3064
|
+
const t = raw.trim();
|
|
3065
|
+
return t ? { type: t, options: {} } : null;
|
|
3066
|
+
}
|
|
3067
|
+
if (typeof raw === 'object' && !Array.isArray(raw) && 'type' in raw) {
|
|
3068
|
+
const w = raw;
|
|
3069
|
+
return w.type ? cloneDeep(w) : null;
|
|
3070
|
+
}
|
|
3071
|
+
return null;
|
|
3072
|
+
}
|
|
3073
|
+
resolveFieldTitleLabel(raw, fallback) {
|
|
3074
|
+
let source = fallback;
|
|
3075
|
+
if (raw !== undefined && raw !== null) {
|
|
3076
|
+
if (typeof raw === 'string') {
|
|
3077
|
+
if (raw.trim() !== '') {
|
|
3078
|
+
source = raw;
|
|
3079
|
+
}
|
|
3080
|
+
}
|
|
3081
|
+
else if (typeof raw === 'object' && !Array.isArray(raw) && Object.keys(raw).length > 0) {
|
|
3082
|
+
source = raw;
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
return this.translationService.resolve(source);
|
|
3086
|
+
}
|
|
3087
|
+
isCancelDialogOutcome(outcome) {
|
|
3088
|
+
if (outcome == null) {
|
|
3089
|
+
return false;
|
|
3090
|
+
}
|
|
3091
|
+
const ref = outcome;
|
|
3092
|
+
if (typeof ref.action !== 'function') {
|
|
3093
|
+
return false;
|
|
3094
|
+
}
|
|
3095
|
+
return ref.action() === 'cancel';
|
|
3096
|
+
}
|
|
3097
|
+
async resolveWidgetDisplayTitle(widgetType) {
|
|
3098
|
+
const crud = this.crudService;
|
|
3099
|
+
if (crud) {
|
|
3100
|
+
const interfaces = await crud.listInterfaces();
|
|
3101
|
+
const iface = interfaces.find((d) => d.name === widgetType);
|
|
3102
|
+
return iface?.title ?? iface?.name;
|
|
3103
|
+
}
|
|
3104
|
+
const config = this.widgetRegistry.getOptional(widgetType);
|
|
3105
|
+
if (!config) {
|
|
3106
|
+
return undefined;
|
|
3107
|
+
}
|
|
3108
|
+
const resolved = this.translationService.resolve(config.title);
|
|
3109
|
+
return resolved || undefined;
|
|
3110
|
+
}
|
|
3111
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPreviewWidgetFieldCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3112
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPreviewWidgetFieldCommand }); }
|
|
3113
|
+
}
|
|
3114
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPreviewWidgetFieldCommand, decorators: [{
|
|
3115
|
+
type: Injectable
|
|
3116
|
+
}] });
|
|
3117
|
+
|
|
3118
|
+
var previewWidgetField_command = /*#__PURE__*/Object.freeze({
|
|
3119
|
+
__proto__: null,
|
|
3120
|
+
AXPPreviewWidgetFieldCommand: AXPPreviewWidgetFieldCommand
|
|
3121
|
+
});
|
|
3122
|
+
|
|
2480
3123
|
/**
|
|
2481
3124
|
* Generated bundle index. Do not edit.
|
|
2482
3125
|
*/
|
|
2483
3126
|
|
|
2484
|
-
export { AXPDialogRendererComponent, AXPLayoutBuilderService, AXPLayoutConversionService, AXPLayoutRendererComponent, LayoutBuilderModule };
|
|
3127
|
+
export { AXPDialogRendererComponent, AXPLayoutBuilderService, AXPLayoutConversionService, AXPLayoutRendererComponent, AXPPreviewWidgetFieldCommand, AXP_LAYOUT_BUILDER_DIALOG_BEFORE_OPEN_HOOK_KEY, AXP_LAYOUT_BUILDER_DIALOG_CONFIG_HOOK_KEY, AXP_LAYOUT_BUILDER_DIALOG_CONTEXT_CHANGED_HOOK_KEY, AXP_PREVIEW_WIDGET_FIELD_COMMAND_KEY, LayoutBuilderModule, createDismissedDialogRef };
|
|
2485
3128
|
//# sourceMappingURL=acorex-platform-layout-builder.mjs.map
|