@acorex/platform 21.0.0-next.5 → 21.0.0-next.51

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.
Files changed (124) hide show
  1. package/fesm2022/acorex-platform-auth.mjs +281 -23
  2. package/fesm2022/acorex-platform-auth.mjs.map +1 -1
  3. package/fesm2022/acorex-platform-common-common-settings.provider-Bi1RYif5.mjs +163 -0
  4. package/fesm2022/acorex-platform-common-common-settings.provider-Bi1RYif5.mjs.map +1 -0
  5. package/fesm2022/acorex-platform-common.mjs +1047 -263
  6. package/fesm2022/acorex-platform-common.mjs.map +1 -1
  7. package/fesm2022/acorex-platform-core.mjs +1138 -510
  8. package/fesm2022/acorex-platform-core.mjs.map +1 -1
  9. package/fesm2022/acorex-platform-domain.mjs +557 -826
  10. package/fesm2022/acorex-platform-domain.mjs.map +1 -1
  11. package/fesm2022/acorex-platform-layout-builder.mjs +804 -186
  12. package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
  13. package/fesm2022/acorex-platform-layout-components-binding-expression-editor-popup.component-CXEdvDTf.mjs +121 -0
  14. package/fesm2022/acorex-platform-layout-components-binding-expression-editor-popup.component-CXEdvDTf.mjs.map +1 -0
  15. package/fesm2022/acorex-platform-layout-components.mjs +6208 -2344
  16. package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
  17. package/fesm2022/acorex-platform-layout-designer.mjs +456 -204
  18. package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
  19. package/fesm2022/acorex-platform-layout-entity.mjs +18632 -10286
  20. package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
  21. package/fesm2022/acorex-platform-layout-views.mjs +538 -168
  22. package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
  23. package/fesm2022/acorex-platform-layout-widget-core.mjs +720 -456
  24. package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
  25. 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
  26. package/fesm2022/acorex-platform-layout-widgets-button-widget-designer.component-Dy7jF-oD.mjs.map +1 -0
  27. package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-CDYAGBku.mjs +103 -0
  28. package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-CDYAGBku.mjs.map +1 -0
  29. package/fesm2022/{acorex-platform-layout-widgets-image-preview.popup-V31OpYah.mjs → acorex-platform-layout-widgets-image-preview.popup-C_EPAvCU.mjs} +6 -7
  30. package/fesm2022/acorex-platform-layout-widgets-image-preview.popup-C_EPAvCU.mjs.map +1 -0
  31. package/fesm2022/{acorex-platform-layout-widgets-page-widget-designer.component-BtZMBxYp.mjs → acorex-platform-layout-widgets-page-widget-designer.component-D10yO28c.mjs} +12 -12
  32. package/fesm2022/acorex-platform-layout-widgets-page-widget-designer.component-D10yO28c.mjs.map +1 -0
  33. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-BGO75IMz.mjs +116 -0
  34. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-BGO75IMz.mjs.map +1 -0
  35. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-edit-popup.component-Ck7-wpT2.mjs → acorex-platform-layout-widgets-tabular-data-edit-popup.component-DmzNTYiS.mjs} +6 -6
  36. package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-DmzNTYiS.mjs.map +1 -0
  37. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-view-popup.component-y8vjUiVs.mjs → acorex-platform-layout-widgets-tabular-data-view-popup.component-BNG_588B.mjs} +5 -5
  38. package/fesm2022/acorex-platform-layout-widgets-tabular-data-view-popup.component-BNG_588B.mjs.map +1 -0
  39. 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
  40. package/fesm2022/acorex-platform-layout-widgets-text-block-widget-designer.component-Vo4fWHtX.mjs.map +1 -0
  41. package/fesm2022/acorex-platform-layout-widgets.mjs +8728 -4269
  42. package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
  43. package/fesm2022/acorex-platform-native.mjs +8 -7
  44. package/fesm2022/acorex-platform-native.mjs.map +1 -1
  45. package/fesm2022/acorex-platform-runtime.mjs +391 -166
  46. package/fesm2022/acorex-platform-runtime.mjs.map +1 -1
  47. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-Cx1lLUaR.mjs +160 -0
  48. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-Cx1lLUaR.mjs.map +1 -0
  49. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-AOrcgjDF.mjs +120 -0
  50. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-AOrcgjDF.mjs.map +1 -0
  51. package/fesm2022/{acorex-platform-themes-default-entity-master-single-view.component-eMBby9k4.mjs → acorex-platform-themes-default-entity-master-single-view.component-BfCeUU5F.mjs} +19 -26
  52. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-BfCeUU5F.mjs.map +1 -0
  53. package/fesm2022/{acorex-platform-themes-default-error-401.component-cfREo88K.mjs → acorex-platform-themes-default-error-401.component-C7EYJzSr.mjs} +4 -4
  54. package/fesm2022/acorex-platform-themes-default-error-401.component-C7EYJzSr.mjs.map +1 -0
  55. package/fesm2022/{acorex-platform-themes-default-error-404.component-CdCV5ZoA.mjs → acorex-platform-themes-default-error-404.component-7MVLMwIa.mjs} +4 -4
  56. package/fesm2022/acorex-platform-themes-default-error-404.component-7MVLMwIa.mjs.map +1 -0
  57. package/fesm2022/acorex-platform-themes-default-error-offline.component-DR6G8gPC.mjs +19 -0
  58. package/fesm2022/acorex-platform-themes-default-error-offline.component-DR6G8gPC.mjs.map +1 -0
  59. package/fesm2022/acorex-platform-themes-default.mjs +1836 -67
  60. package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
  61. package/fesm2022/{acorex-platform-themes-shared-icon-chooser-column.component-C0EpfU2k.mjs → acorex-platform-themes-shared-icon-chooser-column.component-CqkWJYdv.mjs} +6 -6
  62. package/fesm2022/acorex-platform-themes-shared-icon-chooser-column.component-CqkWJYdv.mjs.map +1 -0
  63. package/fesm2022/{acorex-platform-themes-shared-icon-chooser-view.component-9W52W6Nu.mjs → acorex-platform-themes-shared-icon-chooser-view.component-BOTuLdWN.mjs} +6 -6
  64. package/fesm2022/acorex-platform-themes-shared-icon-chooser-view.component-BOTuLdWN.mjs.map +1 -0
  65. package/fesm2022/{acorex-platform-themes-shared-settings.provider-DSs1o1M6.mjs → acorex-platform-themes-shared-settings.provider-DK6R87Lf.mjs} +24 -25
  66. package/fesm2022/acorex-platform-themes-shared-settings.provider-DK6R87Lf.mjs.map +1 -0
  67. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-D566Kdvy.mjs +94 -0
  68. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-D566Kdvy.mjs.map +1 -0
  69. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-D7-rCGl7.mjs +86 -0
  70. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-D7-rCGl7.mjs.map +1 -0
  71. package/fesm2022/acorex-platform-themes-shared.mjs +674 -573
  72. package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
  73. package/fesm2022/acorex-platform-workflow.mjs +1715 -535
  74. package/fesm2022/acorex-platform-workflow.mjs.map +1 -1
  75. package/fesm2022/acorex-platform.mjs.map +1 -1
  76. package/package.json +37 -37
  77. package/{auth/index.d.ts → types/acorex-platform-auth.d.ts} +241 -4
  78. package/{common/index.d.ts → types/acorex-platform-common.d.ts} +598 -80
  79. package/{core/index.d.ts → types/acorex-platform-core.d.ts} +595 -132
  80. package/{domain/index.d.ts → types/acorex-platform-domain.d.ts} +744 -412
  81. package/{layout/builder/index.d.ts → types/acorex-platform-layout-builder.d.ts} +193 -48
  82. package/types/acorex-platform-layout-components.d.ts +2979 -0
  83. package/{layout/designer/index.d.ts → types/acorex-platform-layout-designer.d.ts} +96 -18
  84. package/{layout/entity/index.d.ts → types/acorex-platform-layout-entity.d.ts} +1601 -261
  85. package/{layout/views/index.d.ts → types/acorex-platform-layout-views.d.ts} +116 -55
  86. package/{layout/widget-core/index.d.ts → types/acorex-platform-layout-widget-core.d.ts} +272 -124
  87. package/{layout/widgets/index.d.ts → types/acorex-platform-layout-widgets.d.ts} +1055 -157
  88. package/{native/index.d.ts → types/acorex-platform-native.d.ts} +0 -7
  89. package/types/acorex-platform-runtime.d.ts +571 -0
  90. package/{themes/default/index.d.ts → types/acorex-platform-themes-default.d.ts} +122 -5
  91. package/{themes/shared/index.d.ts → types/acorex-platform-themes-shared.d.ts} +1 -1
  92. package/types/acorex-platform-workflow.d.ts +1884 -0
  93. package/fesm2022/acorex-platform-common-common-settings.provider-zhqNP3xb.mjs +0 -71
  94. package/fesm2022/acorex-platform-common-common-settings.provider-zhqNP3xb.mjs.map +0 -1
  95. package/fesm2022/acorex-platform-layout-widgets-button-widget-designer.component-C3VoBb_b.mjs.map +0 -1
  96. package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-CxrsI6Hn.mjs +0 -135
  97. package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-CxrsI6Hn.mjs.map +0 -1
  98. package/fesm2022/acorex-platform-layout-widgets-image-preview.popup-V31OpYah.mjs.map +0 -1
  99. package/fesm2022/acorex-platform-layout-widgets-page-widget-designer.component-BtZMBxYp.mjs.map +0 -1
  100. package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-Ck7-wpT2.mjs.map +0 -1
  101. package/fesm2022/acorex-platform-layout-widgets-tabular-data-view-popup.component-y8vjUiVs.mjs.map +0 -1
  102. package/fesm2022/acorex-platform-layout-widgets-text-block-widget-designer.component-Df1BFkSa.mjs.map +0 -1
  103. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-VIGuU5M4.mjs +0 -157
  104. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-VIGuU5M4.mjs.map +0 -1
  105. package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-DyDa_hyd.mjs +0 -1542
  106. package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-DyDa_hyd.mjs.map +0 -1
  107. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-Ua3ZA5hk.mjs +0 -101
  108. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-Ua3ZA5hk.mjs.map +0 -1
  109. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-eMBby9k4.mjs.map +0 -1
  110. package/fesm2022/acorex-platform-themes-default-error-401.component-cfREo88K.mjs.map +0 -1
  111. package/fesm2022/acorex-platform-themes-default-error-404.component-CdCV5ZoA.mjs.map +0 -1
  112. package/fesm2022/acorex-platform-themes-default-error-offline.component-E7SzBcAt.mjs +0 -19
  113. package/fesm2022/acorex-platform-themes-default-error-offline.component-E7SzBcAt.mjs.map +0 -1
  114. package/fesm2022/acorex-platform-themes-shared-icon-chooser-column.component-C0EpfU2k.mjs.map +0 -1
  115. package/fesm2022/acorex-platform-themes-shared-icon-chooser-view.component-9W52W6Nu.mjs.map +0 -1
  116. package/fesm2022/acorex-platform-themes-shared-settings.provider-DSs1o1M6.mjs.map +0 -1
  117. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-DTnfRy5f.mjs +0 -65
  118. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-DTnfRy5f.mjs.map +0 -1
  119. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-DY0JtT1v.mjs +0 -64
  120. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-DY0JtT1v.mjs.map +0 -1
  121. package/layout/components/index.d.ts +0 -1669
  122. package/runtime/index.d.ts +0 -307
  123. package/workflow/index.d.ts +0 -1808
  124. /package/{index.d.ts → types/acorex-platform.d.ts} +0 -0
@@ -1,13 +1,16 @@
1
- import * as i4 from '@angular/common';
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, Input } from '@angular/core';
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';
6
- import { cloneDeep, isNil, set, isEqual } from 'lodash-es';
7
+ import * as i4 from '@acorex/platform/core';
8
+ import { AXPHookService, AXPExpressionEvaluatorService, AXPComponentSlotModule, AXPContextStore } from '@acorex/platform/core';
9
+ import * as i1 from '@acorex/platform/layout/widget-core';
10
+ import { AXPWidgetSerializationHelper, AXPWidgetContainerComponent, AXPPageStatus, AXPWidgetCoreModule, AXPWidgetRegistryService } from '@acorex/platform/layout/widget-core';
11
+ import { cloneDeep, isNil, set, isEqual, merge } from 'lodash-es';
7
12
  import * as i2 from '@acorex/components/form';
8
13
  import { AXFormComponent, AXFormModule } from '@acorex/components/form';
9
- import * as i1 from '@acorex/platform/layout/widget-core';
10
- import { AXPWidgetContainerComponent, AXPPageStatus, AXPWidgetCoreModule } from '@acorex/platform/layout/widget-core';
11
14
  import { Subject, debounceTime, distinctUntilChanged, startWith } from 'rxjs';
12
15
  import * as i1$1 from '@acorex/components/button';
13
16
  import { AXButtonModule } from '@acorex/components/button';
@@ -16,9 +19,36 @@ 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 i5 from '@acorex/core/translation';
20
- import { AXTranslationModule } from '@acorex/core/translation';
21
- import { AXPExpressionEvaluatorService } from '@acorex/platform/core';
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
+ //#region ---- Imports ----
27
+ //#endregion
28
+ //#region ---- Before open ----
29
+ /**
30
+ * Runs after dialog options and context are prepared and **before** footer customization and popup open.
31
+ * Listeners may mutate {@link AXPLayoutBuilderDialogBeforeOpenPayload.context} by reference.
32
+ */
33
+ const AXP_LAYOUT_BUILDER_DIALOG_BEFORE_OPEN_HOOK_KEY = 'layout-builder.dialog.before-open';
34
+ //#endregion
35
+ //#region ---- Footer actions ----
36
+ /**
37
+ * Runs after builder-defined footer actions exist and **before** the dialog opens.
38
+ * Listeners receive the live `actions.footer.prefix` / `suffix` arrays (same references as the dialog)
39
+ * so they may push, splice, filter, or replace items. They may also mutate {@link AXPLayoutBuilderDialogFooterPayload.context} by reference.
40
+ */
41
+ const AXP_LAYOUT_BUILDER_DIALOG_CONFIG_HOOK_KEY = 'layout-builder.dialog.config';
42
+ //#endregion
43
+ //#region ---- Context updates (after open) ----
44
+ /**
45
+ * Runs whenever the dialog layout builder context changes (debounced upstream), **after** the popup is visible.
46
+ * Use for side effects that depend on live context (for example values updated by widgets after render).
47
+ * Payload mirrors `AXPDialogRendererComponent` semantics: {@link AXPLayoutBuilderDialogContextChangedPayload.getContext},
48
+ * {@link AXPLayoutBuilderDialogContextChangedPayload.patchContext}, and optional loading state.
49
+ */
50
+ const AXP_LAYOUT_BUILDER_DIALOG_CONTEXT_CHANGED_HOOK_KEY = 'layout-builder.dialog.context-changed';
51
+ //#endregion
22
52
 
23
53
  class AXPLayoutConversionService {
24
54
  constructor() {
@@ -158,6 +188,10 @@ class AXPLayoutConversionService {
158
188
  if (!editorWidget.mode) {
159
189
  editorWidget.mode = fieldMode;
160
190
  }
191
+ const hintOpts = field.description != null &&
192
+ (typeof field.description !== 'string' || field.description.trim().length > 0)
193
+ ? { hint: field.description, hintDisplayMode: 'note' }
194
+ : {};
161
195
  return {
162
196
  type: 'form-field',
163
197
  name: field.path,
@@ -165,8 +199,8 @@ class AXPLayoutConversionService {
165
199
  options: {
166
200
  label: field.title,
167
201
  badge: field.badge,
168
- description: field.description,
169
202
  showLabel: true,
203
+ ...hintOpts,
170
204
  },
171
205
  children: [editorWidget], // The editor widget becomes a child of form-field
172
206
  };
@@ -209,7 +243,7 @@ class AXPLayoutConversionService {
209
243
  path: formFieldNode.name || editorWidget.name || `field-${Date.now()}`,
210
244
  title: formFieldNode.options?.['label'],
211
245
  badge: formFieldNode.options?.['badge'],
212
- description: formFieldNode.options?.['description'],
246
+ description: formFieldNode.options?.['hint'],
213
247
  widget: editorWidget,
214
248
  mode: formFieldNode.mode,
215
249
  };
@@ -222,9 +256,16 @@ class AXPLayoutConversionService {
222
256
  const keyParts = [];
223
257
  keyParts.push(`groups:${formDefinition.groups.length}`);
224
258
  formDefinition.groups.forEach((group, groupIndex) => {
225
- keyParts.push(`g${groupIndex}:${group.name}:${group.parameters.length}`);
259
+ // Include group.mode so view vs edit (or mixed) layouts do not share a cached widget tree.
260
+ const groupModePart = group.mode ?? '_';
261
+ keyParts.push(`g${groupIndex}:${group.name}:${group.parameters.length}:${groupModePart}`);
262
+ keyParts.push(`gL${groupIndex}:${JSON.stringify(group.title ?? null)}:${JSON.stringify(group.description ?? null)}`);
226
263
  group.parameters.forEach((param, paramIndex) => {
227
- keyParts.push(`p${groupIndex}.${paramIndex}:${param.path}:${param.widget.type}`);
264
+ // Field mode must be part of the key; otherwise metadata forms that only differ by
265
+ // view/edit (same paths and widget types) incorrectly reuse the first cached tree.
266
+ const fieldModePart = param.mode ?? '_';
267
+ keyParts.push(`p${groupIndex}.${paramIndex}:${param.path}:${param.widget.type}:${fieldModePart}`);
268
+ keyParts.push(`pL${groupIndex}.${paramIndex}:${JSON.stringify(param.title ?? null)}:${JSON.stringify(param.description ?? null)}:${JSON.stringify(param.badge ?? null)}`);
228
269
  });
229
270
  });
230
271
  if (formDefinition.mode) {
@@ -285,10 +326,10 @@ class AXPLayoutConversionService {
285
326
  }
286
327
  return Math.abs(hash).toString(36); // Convert to base36 for shorter string
287
328
  }
288
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLayoutConversionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
289
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLayoutConversionService, providedIn: 'root' }); }
329
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutConversionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
330
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutConversionService, providedIn: 'root' }); }
290
331
  }
291
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLayoutConversionService, decorators: [{
332
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutConversionService, decorators: [{
292
333
  type: Injectable,
293
334
  args: [{
294
335
  providedIn: 'root',
@@ -361,17 +402,13 @@ function collectDefaultValues(node, context = {}, isTopLevel = true) {
361
402
  const result = isTopLevel ? cloneDeep(context) : context;
362
403
  // Check if this node has a defaultValue and a path
363
404
  // 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;
405
+ const defaultValue = node.defaultValue !== undefined ? node.defaultValue : node.options?.defaultValue;
367
406
  if (defaultValue !== undefined && !isNil(defaultValue) && node.path) {
368
407
  // Check if path exists in context using lodash get equivalent check
369
408
  const currentValue = getNestedValue(result, node.path);
370
409
  if (currentValue === undefined) {
371
410
  // 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);
411
+ const clonedValue = defaultValue instanceof Date ? new Date(defaultValue.getTime()) : cloneDeep(defaultValue);
375
412
  set(result, node.path, clonedValue);
376
413
  }
377
414
  }
@@ -402,17 +439,18 @@ function getNestedValue(obj, path) {
402
439
  class AXPLayoutBuilderService {
403
440
  constructor() {
404
441
  this.popupService = inject(AXPopupService);
442
+ this.hookService = inject(AXPHookService, { optional: true }) ?? undefined;
405
443
  }
406
444
  /**
407
445
  * Create a new layout builder
408
446
  */
409
447
  create() {
410
- return new LayoutBuilder(this.popupService);
448
+ return new LayoutBuilder(this.popupService, this.hookService);
411
449
  }
412
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLayoutBuilderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
413
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLayoutBuilderService, providedIn: 'root' }); }
450
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutBuilderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
451
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutBuilderService, providedIn: 'root' }); }
414
452
  }
415
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLayoutBuilderService, decorators: [{
453
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutBuilderService, decorators: [{
416
454
  type: Injectable,
417
455
  args: [{
418
456
  providedIn: 'root',
@@ -425,8 +463,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
425
463
  * Open/Closed: Extensible through container delegates
426
464
  */
427
465
  class LayoutBuilder {
428
- constructor(popupService) {
466
+ constructor(popupService, hookService) {
429
467
  this.popupService = popupService;
468
+ this.hookService = hookService;
430
469
  this.root = {
431
470
  children: [],
432
471
  mode: 'edit',
@@ -496,7 +535,7 @@ class LayoutBuilder {
496
535
  if (!this.popupService) {
497
536
  throw new Error('LayoutBuilder requires AXPopupService to create dialogs. Please inject it in the service constructor.');
498
537
  }
499
- const container = new DialogContainerBuilder(this.popupService);
538
+ const container = new DialogContainerBuilder(this.popupService, this.hookService);
500
539
  if (delegate) {
501
540
  delegate(container);
502
541
  }
@@ -521,11 +560,30 @@ class LayoutBuilder {
521
560
  return this;
522
561
  }
523
562
  build() {
524
- return {
525
- type: this.root.type,
526
- children: this.root.children,
527
- mode: this.root.mode,
563
+ const r = this.root;
564
+ const node = {
565
+ type: r.type,
566
+ ...(r.mode !== undefined ? { mode: r.mode } : {}),
567
+ ...(r.children !== undefined ? { children: r.children } : {}),
568
+ ...(r.options !== undefined ? { options: r.options } : {}),
569
+ ...(r.name !== undefined ? { name: r.name } : {}),
570
+ ...(r.path !== undefined ? { path: r.path } : {}),
571
+ ...(r.visible !== undefined ? { visible: r.visible } : {}),
572
+ ...(r.defaultValue !== undefined ? { defaultValue: r.defaultValue } : {}),
573
+ ...(r.triggers !== undefined ? { triggers: r.triggers } : {}),
574
+ ...(r.meta !== undefined ? { meta: r.meta } : {}),
575
+ ...(r.valueTransforms !== undefined ? { valueTransforms: r.valueTransforms } : {}),
528
576
  };
577
+ return node;
578
+ }
579
+ /**
580
+ * Converts the built widget node to JSON string
581
+ * @param options - Serialization options
582
+ * @returns JSON string representation of the widget node
583
+ */
584
+ toJson(options) {
585
+ const node = this.build();
586
+ return AXPWidgetSerializationHelper.toJson(node, options);
529
587
  }
530
588
  }
531
589
  //#endregion
@@ -585,6 +643,7 @@ class BaseContainerBuilder {
585
643
  'number-editor',
586
644
  'select-editor',
587
645
  'lookup-editor',
646
+ 'entity-definition-provider-editor',
588
647
  'selection-list-editor',
589
648
  'date-time-editor',
590
649
  'toggle-editor',
@@ -687,24 +746,30 @@ class BaseContainerMixin extends BaseContainerBuilder {
687
746
  class LayoutContainerMixin extends BaseContainerMixin {
688
747
  layout(value) {
689
748
  // Map layout intent to grid item sizing so containers like `form-field`
690
- // can span multiple columns inside grid/fieldset layouts.
749
+ // can span multiple columns/rows inside grid/fieldset layouts.
691
750
  if (!this.containerState.options)
692
751
  this.containerState.options = {};
693
752
  if (typeof value === 'number') {
694
- // Direct numeric shorthand → colSpan
695
753
  this.containerState.options.colSpan = value;
696
754
  }
697
755
  else if (value) {
698
- // Try to extract a reasonable colSpan from breakpoint positions
699
756
  const positions = value.positions;
700
757
  if (positions) {
701
- const colSpan = positions?.lg?.colSpan ??
702
- positions?.xl?.colSpan ??
703
- positions?.xxl?.colSpan ??
704
- positions?.md?.colSpan ??
705
- positions?.sm?.colSpan;
706
- if (colSpan != null) {
707
- this.containerState.options.colSpan = colSpan;
758
+ const placement = positions?.lg ?? positions?.xl ?? positions?.xxl ?? positions?.md ?? positions?.sm;
759
+ if (placement) {
760
+ const opts = this.containerState.options;
761
+ if (placement.colSpan != null)
762
+ opts.colSpan = placement.colSpan;
763
+ if (placement.colStart != null)
764
+ opts.colStart = placement.colStart;
765
+ if (placement.colEnd != null)
766
+ opts.colEnd = placement.colEnd;
767
+ if (placement.rowSpan != null)
768
+ opts.rowSpan = placement.rowSpan;
769
+ if (placement.rowStart != null)
770
+ opts.rowStart = placement.rowStart;
771
+ if (placement.rowEnd != null)
772
+ opts.rowEnd = placement.rowEnd;
708
773
  }
709
774
  }
710
775
  }
@@ -923,6 +988,7 @@ class WidgetContainerMixin extends ChildContainerMixin {
923
988
  'number-editor',
924
989
  'select-editor',
925
990
  'lookup-editor',
991
+ 'entity-definition-provider-editor',
926
992
  'selection-list-editor',
927
993
  'date-time-editor',
928
994
  'toggle-editor',
@@ -978,12 +1044,73 @@ class FlexContainerBuilder extends WidgetContainerMixin {
978
1044
  * Grid Container Builder - Liskov Substitution Principle
979
1045
  * Extends WidgetContainerMixin to inherit all common functionality
980
1046
  */
1047
+ /**
1048
+ * Extracts flat grid-item options from AXPGridLayoutOptions for grid-item-layout widget.
1049
+ * Uses first available breakpoint (lg, xl, md, sm).
1050
+ */
1051
+ /**
1052
+ * Deep-merges grid breakpoint buckets so sequential fluent calls (e.g. setColumns then setGap)
1053
+ * do not wipe sibling keys under options.grid.default.
1054
+ */
1055
+ function mergeAXPGridContainerOptions(prev, patch) {
1056
+ const next = { ...(prev ?? {}), ...patch };
1057
+ if (prev?.grid?.default || patch.grid?.default) {
1058
+ next.grid = {
1059
+ ...(prev?.grid ?? {}),
1060
+ ...(patch.grid ?? {}),
1061
+ default: {
1062
+ ...(prev?.grid?.default ?? {}),
1063
+ ...(patch.grid?.default ?? {}),
1064
+ },
1065
+ };
1066
+ }
1067
+ return next;
1068
+ }
1069
+ function toGridItemOptions(layoutOptions) {
1070
+ if (!layoutOptions?.positions)
1071
+ return { colSpan: 12 };
1072
+ const positions = layoutOptions.positions;
1073
+ const placement = positions['lg'] ?? positions['xl'] ?? positions['xxl'] ?? positions['md'] ?? positions['sm'];
1074
+ if (!placement)
1075
+ return { colSpan: 12 };
1076
+ const opts = {};
1077
+ if (placement['colSpan'] != null)
1078
+ opts['colSpan'] = placement['colSpan'];
1079
+ if (placement['colStart'] != null)
1080
+ opts['colStart'] = placement['colStart'];
1081
+ if (placement['colEnd'] != null)
1082
+ opts['colEnd'] = placement['colEnd'];
1083
+ if (placement['rowSpan'] != null)
1084
+ opts['rowSpan'] = placement['rowSpan'];
1085
+ if (placement['rowStart'] != null)
1086
+ opts['rowStart'] = placement['rowStart'];
1087
+ if (placement['rowEnd'] != null)
1088
+ opts['rowEnd'] = placement['rowEnd'];
1089
+ if (Object.keys(opts).length === 0)
1090
+ opts['colSpan'] = 12;
1091
+ return opts;
1092
+ }
981
1093
  class GridContainerBuilder extends WidgetContainerMixin {
982
1094
  constructor() {
983
1095
  super('grid-layout');
984
1096
  }
985
1097
  setOptions(options) {
986
- this.containerState.options = { ...this.containerState.options, ...options };
1098
+ this.containerState.options = mergeAXPGridContainerOptions(this.containerState.options, options);
1099
+ return this;
1100
+ }
1101
+ item(layoutOptions, delegate) {
1102
+ const fieldset = new FieldsetContainerBuilder();
1103
+ fieldset.withInheritanceContext(this.inheritanceContext);
1104
+ delegate(fieldset);
1105
+ const fieldsetNode = fieldset.build();
1106
+ const gridItemOptions = toGridItemOptions(layoutOptions);
1107
+ const gridItemNode = {
1108
+ type: 'grid-item-layout',
1109
+ options: gridItemOptions,
1110
+ children: [fieldsetNode],
1111
+ };
1112
+ this.ensureChildren();
1113
+ this.containerState.children.push(gridItemNode);
987
1114
  return this;
988
1115
  }
989
1116
  // Individual fluent methods for Grid
@@ -1155,10 +1282,34 @@ class FormFieldBuilder extends LayoutContainerMixin {
1155
1282
  child.type(type);
1156
1283
  child.name(finalName);
1157
1284
  child.path(widgetPath);
1158
- // Remove name from options since it's now in state
1159
- const { name: _, ...cleanOptions } = (options || {});
1285
+ // Extract extended properties from options (triggers, meta, valueTransforms, mode, visible, defaultValue)
1286
+ const { name: _, triggers, meta, valueTransforms, mode: extendedMode, visible: extendedVisible, defaultValue: extendedDefaultValue, children: extendedChildren, ...cleanOptions } = (options || {});
1160
1287
  child.withInheritanceContext(this.inheritanceContext);
1161
1288
  child.options(cleanOptions);
1289
+ // Apply extended properties if provided
1290
+ if (extendedMode !== undefined) {
1291
+ child.mode(extendedMode);
1292
+ }
1293
+ if (extendedVisible !== undefined) {
1294
+ child.visible(extendedVisible);
1295
+ }
1296
+ if (extendedDefaultValue !== undefined) {
1297
+ child.defaultValue(extendedDefaultValue);
1298
+ }
1299
+ // Set triggers, meta, and valueTransforms directly on widgetState
1300
+ // These are part of AXPWidgetNode but not handled by WidgetBuilder methods
1301
+ if (triggers !== undefined) {
1302
+ child.widgetState.triggers = triggers;
1303
+ }
1304
+ if (meta !== undefined) {
1305
+ child.widgetState.meta = meta;
1306
+ }
1307
+ if (valueTransforms !== undefined) {
1308
+ child.widgetState.valueTransforms = valueTransforms;
1309
+ }
1310
+ if (extendedChildren !== undefined) {
1311
+ child.widgetState.children = extendedChildren;
1312
+ }
1162
1313
  // IMPORTANT: Store the widget builder, don't build it yet!
1163
1314
  // This allows properties set after this method (like disabled, readonly) to be applied
1164
1315
  this.childWidget = child;
@@ -1390,7 +1541,7 @@ class ListWidgetBuilder extends WidgetContainerMixin {
1390
1541
  * Uses composition instead of inheritance for cleaner separation
1391
1542
  */
1392
1543
  class DialogContainerBuilder {
1393
- constructor(popupService) {
1544
+ constructor(popupService, hookService) {
1394
1545
  this.dialogState = {
1395
1546
  type: 'flex-layout', // This will be overridden when content layout exists
1396
1547
  children: [],
@@ -1413,6 +1564,7 @@ class DialogContainerBuilder {
1413
1564
  else {
1414
1565
  this.popupService = inject(AXPopupService);
1415
1566
  }
1567
+ this.hookService = hookService ?? inject(AXPHookService, { optional: true }) ?? undefined;
1416
1568
  }
1417
1569
  setOptions(options) {
1418
1570
  this.dialogState.dialogOptions = { ...this.dialogState.dialogOptions, ...options };
@@ -1452,6 +1604,15 @@ class DialogContainerBuilder {
1452
1604
  }
1453
1605
  return this;
1454
1606
  }
1607
+ onAction(handler) {
1608
+ this.dialogState.dialogOptions ??= {
1609
+ title: '',
1610
+ size: 'md',
1611
+ closeButton: false,
1612
+ };
1613
+ this.dialogState.dialogOptions.onAction = handler;
1614
+ return this;
1615
+ }
1455
1616
  addCustomAction(action) {
1456
1617
  // Add to actions based on position
1457
1618
  const position = action.position || 'suffix';
@@ -1498,20 +1659,42 @@ class DialogContainerBuilder {
1498
1659
  const dialogNode = this.build();
1499
1660
  // Import the dialog renderer component dynamically
1500
1661
  const { AXPDialogRendererComponent } = await Promise.resolve().then(function () { return dialogRenderer_component; });
1501
- // Collect default values from widget tree and merge into initial context
1502
- const initialContext = this.dialogState.dialogOptions?.context || {};
1503
- const contextWithDefaults = collectDefaultValues(dialogNode, initialContext);
1662
+ this.dialogState.dialogOptions ??= {};
1663
+ if (this.dialogState.dialogOptions.context == null || typeof this.dialogState.dialogOptions.context !== 'object') {
1664
+ this.dialogState.dialogOptions.context = {};
1665
+ }
1666
+ const initialContext = this.dialogState.dialogOptions.context;
1667
+ this.dialogState.actions ??= { footer: { prefix: [], suffix: [] } };
1668
+ this.dialogState.actions.footer ??= { prefix: [], suffix: [] };
1669
+ this.dialogState.actions.footer.prefix ??= [];
1670
+ this.dialogState.actions.footer.suffix ??= [];
1671
+ const hookService = this.hookService;
1672
+ if (hookService) {
1673
+ const beforePayload = {
1674
+ context: initialContext,
1675
+ dialogOptions: this.dialogState.dialogOptions,
1676
+ };
1677
+ await hookService.runAsync(AXP_LAYOUT_BUILDER_DIALOG_BEFORE_OPEN_HOOK_KEY, beforePayload);
1678
+ }
1504
1679
  // Create dialog configuration
1505
1680
  const dialogConfig = {
1506
1681
  title: this.dialogState.dialogOptions?.title || '',
1507
- message: this.dialogState.dialogOptions?.message,
1508
- context: contextWithDefaults,
1682
+ //TODO: why we need message?
1683
+ //message: this.dialogState.dialogOptions?.message,
1684
+ context: initialContext,
1509
1685
  definition: dialogNode,
1686
+ metadata: this.dialogState.dialogOptions.metadata,
1510
1687
  actions: this.dialogState.actions,
1688
+ onAction: this.dialogState.dialogOptions?.onAction,
1511
1689
  };
1690
+ //
1691
+ if (hookService) {
1692
+ await hookService.runAsync(AXP_LAYOUT_BUILDER_DIALOG_CONFIG_HOOK_KEY, dialogConfig);
1693
+ }
1512
1694
  // The Promise resolves when user clicks an action button
1513
1695
  return new Promise(async (resolve) => {
1514
- this.popupService.open(AXPDialogRendererComponent, {
1696
+ let flag = false;
1697
+ await this.popupService.open(AXPDialogRendererComponent, {
1515
1698
  title: dialogConfig.title,
1516
1699
  size: this.dialogState.dialogOptions?.size || 'md',
1517
1700
  closeButton: this.dialogState.dialogOptions?.closeButton || false,
@@ -1520,11 +1703,14 @@ class DialogContainerBuilder {
1520
1703
  data: {
1521
1704
  config: dialogConfig,
1522
1705
  callBack: (result) => {
1523
- // Resolve with the dialog reference when user clicks an action
1706
+ flag = true;
1524
1707
  resolve(result);
1525
1708
  },
1526
1709
  },
1527
1710
  });
1711
+ if (!flag) {
1712
+ resolve({ success: false });
1713
+ }
1528
1714
  });
1529
1715
  }
1530
1716
  }
@@ -1539,6 +1725,7 @@ class WidgetBuilder {
1539
1725
  this.widgetState = {
1540
1726
  type: 'widget',
1541
1727
  options: {},
1728
+ children: [],
1542
1729
  };
1543
1730
  this.inheritanceContext = {};
1544
1731
  if (name) {
@@ -1602,6 +1789,7 @@ class WidgetBuilder {
1602
1789
  this.widgetState.options = {};
1603
1790
  }
1604
1791
  this.widgetState.options['visible'] = condition;
1792
+ this.widgetState.visible = condition;
1605
1793
  this.inheritanceContext.visible = condition;
1606
1794
  return this;
1607
1795
  }
@@ -1633,6 +1821,10 @@ class WidgetBuilder {
1633
1821
  this.inheritanceContext.direction = direction;
1634
1822
  return this;
1635
1823
  }
1824
+ children(children) {
1825
+ this.widgetState.children = children;
1826
+ return this;
1827
+ }
1636
1828
  // Inheritance context methods
1637
1829
  withInheritanceContext(context) {
1638
1830
  this.inheritanceContext = mergeInheritanceContext(context);
@@ -1656,6 +1848,7 @@ class WidgetBuilder {
1656
1848
  }
1657
1849
  if (resolved.visible !== undefined) {
1658
1850
  this.widgetState.options['visible'] = resolved.visible;
1851
+ this.widgetState.visible = resolved.visible;
1659
1852
  }
1660
1853
  if (context.defaultValue !== undefined) {
1661
1854
  this.widgetState.defaultValue = context.defaultValue;
@@ -1666,14 +1859,29 @@ class WidgetBuilder {
1666
1859
  return { ...this.inheritanceContext };
1667
1860
  }
1668
1861
  build() {
1669
- return {
1862
+ const node = {
1670
1863
  name: this.widgetState.name,
1671
1864
  type: this.widgetState.type,
1672
1865
  options: this.widgetState.options,
1673
1866
  mode: this.widgetState.mode,
1674
1867
  path: this.widgetState.path,
1675
1868
  defaultValue: this.widgetState.defaultValue,
1869
+ children: this.widgetState.children,
1676
1870
  };
1871
+ // Add extended properties if they exist
1872
+ if (this.widgetState.triggers !== undefined) {
1873
+ node.triggers = this.widgetState.triggers;
1874
+ }
1875
+ if (this.widgetState.meta !== undefined) {
1876
+ node.meta = this.widgetState.meta;
1877
+ }
1878
+ if (this.widgetState.valueTransforms !== undefined) {
1879
+ node.valueTransforms = this.widgetState.valueTransforms;
1880
+ }
1881
+ if (this.widgetState.visible !== undefined) {
1882
+ node.visible = this.widgetState.visible;
1883
+ }
1884
+ return node;
1677
1885
  }
1678
1886
  }
1679
1887
  //#region ---- Action Builder Implementation ----
@@ -1687,7 +1895,6 @@ class ActionBuilder {
1687
1895
  }
1688
1896
  this.dialogBuilder['dialogState'].actions.footer.suffix.push({
1689
1897
  title: text || '@general:actions.cancel.title',
1690
- icon: 'fa-times',
1691
1898
  color: 'default',
1692
1899
  command: { name: 'cancel' },
1693
1900
  });
@@ -1699,17 +1906,25 @@ class ActionBuilder {
1699
1906
  }
1700
1907
  this.dialogBuilder['dialogState'].actions.footer.suffix.push({
1701
1908
  title: text || '@general:actions.submit.title',
1702
- icon: 'fa-check',
1703
1909
  color: 'primary',
1704
1910
  command: { name: 'submit', options: { validate: true } },
1705
1911
  });
1706
1912
  return this;
1707
1913
  }
1708
1914
  custom(action) {
1709
- if (!this.dialogBuilder['dialogState'].actions.footer.suffix) {
1710
- this.dialogBuilder['dialogState'].actions.footer.suffix = [];
1915
+ const position = action.position ?? 'suffix';
1916
+ if (position === 'prefix') {
1917
+ if (!this.dialogBuilder['dialogState'].actions.footer.prefix) {
1918
+ this.dialogBuilder['dialogState'].actions.footer.prefix = [];
1919
+ }
1920
+ this.dialogBuilder['dialogState'].actions.footer.prefix.push(action);
1921
+ }
1922
+ else {
1923
+ if (!this.dialogBuilder['dialogState'].actions.footer.suffix) {
1924
+ this.dialogBuilder['dialogState'].actions.footer.suffix = [];
1925
+ }
1926
+ this.dialogBuilder['dialogState'].actions.footer.suffix.push(action);
1711
1927
  }
1712
- this.dialogBuilder['dialogState'].actions.footer.suffix.push(action);
1713
1928
  return this;
1714
1929
  }
1715
1930
  }
@@ -1858,22 +2073,27 @@ class AXPLayoutRendererComponent {
1858
2073
  /**
1859
2074
  * Form definition containing groups and fields OR widget tree
1860
2075
  */
1861
- this.layout = input.required(...(ngDevMode ? [{ debugName: "layout" }] : []));
2076
+ this.layout = input.required(...(ngDevMode ? [{ debugName: "layout" }] : /* istanbul ignore next */ []));
1862
2077
  /**
1863
2078
  * Form context/model data
1864
2079
  */
1865
- this.context = model({}, ...(ngDevMode ? [{ debugName: "context" }] : []));
2080
+ this.context = model({}, ...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
1866
2081
  /**
1867
2082
  * Form appearance and density styling (normal, compact, spacious)
1868
2083
  */
1869
- this.look = input('fieldset', ...(ngDevMode ? [{ debugName: "look" }] : []));
2084
+ this.look = input('fieldset', ...(ngDevMode ? [{ debugName: "look" }] : /* istanbul ignore next */ []));
1870
2085
  /**
1871
2086
  * Default form mode. Can be overridden by section/group and field.
1872
2087
  */
1873
- this.mode = input('edit', ...(ngDevMode ? [{ debugName: "mode" }] : []));
2088
+ this.mode = input('edit', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
1874
2089
  //#endregion
1875
2090
  //#region ---- Widget Tree Conversion ----
1876
- this.widgetTree = signal(null, ...(ngDevMode ? [{ debugName: "widgetTree" }] : []));
2091
+ this.widgetTree = signal(null, ...(ngDevMode ? [{ debugName: "widgetTree" }] : /* istanbul ignore next */ []));
2092
+ /**
2093
+ * Prefer explicit {@link AXPWidgetNode.mode} on the root node (e.g. dialog flex `mode('view')`)
2094
+ * so nested widgets resolve view vs edit correctly; fall back to the layout `mode` input.
2095
+ */
2096
+ this.effectiveRenderMode = computed(() => this.widgetTree()?.mode ?? this.mode(), ...(ngDevMode ? [{ debugName: "effectiveRenderMode" }] : /* istanbul ignore next */ []));
1877
2097
  /**
1878
2098
  * Convert layout data to widget tree when inputs change
1879
2099
  */
@@ -1898,7 +2118,7 @@ class AXPLayoutRendererComponent {
1898
2118
  if (!isEqual(prev, tree)) {
1899
2119
  this.widgetTree.set(tree);
1900
2120
  }
1901
- }, ...(ngDevMode ? [{ debugName: "conversionEffect" }] : []));
2121
+ }, ...(ngDevMode ? [{ debugName: "conversionEffect" }] : /* istanbul ignore next */ []));
1902
2122
  //#endregion
1903
2123
  //#region ---- Outputs ----
1904
2124
  /**
@@ -1911,12 +2131,12 @@ class AXPLayoutRendererComponent {
1911
2131
  this.validityChange = output();
1912
2132
  //#endregion
1913
2133
  //#region ---- Properties ----
1914
- this.form = viewChild(AXFormComponent, ...(ngDevMode ? [{ debugName: "form" }] : []));
1915
- this.container = viewChild(AXPWidgetContainerComponent, ...(ngDevMode ? [{ debugName: "container" }] : []));
2134
+ this.form = viewChild(AXFormComponent, ...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
2135
+ this.container = viewChild(AXPWidgetContainerComponent, ...(ngDevMode ? [{ debugName: "container" }] : /* istanbul ignore next */ []));
1916
2136
  /**
1917
2137
  * Internal context signal for reactivity
1918
2138
  */
1919
- this.internalContext = signal({}, ...(ngDevMode ? [{ debugName: "internalContext" }] : []));
2139
+ this.internalContext = signal({}, ...(ngDevMode ? [{ debugName: "internalContext" }] : /* istanbul ignore next */ []));
1920
2140
  /**
1921
2141
  * Initial context for reset functionality
1922
2142
  */
@@ -1929,7 +2149,7 @@ class AXPLayoutRendererComponent {
1929
2149
  this.#contextSyncEffect = effect(() => {
1930
2150
  const ctx = this.context() ?? {};
1931
2151
  this.contextUpdateSubject.next(ctx);
1932
- }, ...(ngDevMode ? [{ debugName: "#contextSyncEffect" }] : []));
2152
+ }, ...(ngDevMode ? [{ debugName: "#contextSyncEffect" }] : /* istanbul ignore next */ []));
1933
2153
  /**
1934
2154
  * Effect to handle widget tree status changes
1935
2155
  */
@@ -1938,7 +2158,7 @@ class AXPLayoutRendererComponent {
1938
2158
  if (widgetTree) {
1939
2159
  this.container()?.builderService.setStatus(AXPPageStatus.Rendered);
1940
2160
  }
1941
- }, ...(ngDevMode ? [{ debugName: "#widgetStatusEffect" }] : []));
2161
+ }, ...(ngDevMode ? [{ debugName: "#widgetStatusEffect" }] : /* istanbul ignore next */ []));
1942
2162
  }
1943
2163
  //#endregion
1944
2164
  //#region ---- Lifecycle Methods ----
@@ -2078,40 +2298,67 @@ class AXPLayoutRendererComponent {
2078
2298
  isWidgetNode(data) {
2079
2299
  return data && typeof data === 'object' && 'type' in data && typeof data.type === 'string';
2080
2300
  }
2081
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLayoutRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2082
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", 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: `
2301
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2302
+ 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: `
2083
2303
  <ax-form>
2084
2304
  <axp-widgets-container [context]="internalContext()" (onContextChanged)="handleContextChanged($event)">
2085
2305
  @if (widgetTree()) {
2086
- <ng-container axp-widget-renderer [node]="widgetTree()!" [mode]="mode()"></ng-container>
2306
+ <ng-container
2307
+ axp-widget-renderer
2308
+ [node]="widgetTree()!"
2309
+ [mode]="effectiveRenderMode()"
2310
+ ></ng-container>
2087
2311
  }
2088
2312
  </axp-widgets-container>
2089
2313
  </ax-form>
2090
- `, isInline: true, styles: [":host{display:block;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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"], outputs: ["onValidate", "updateOnChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2314
+ `, 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 }); }
2091
2315
  }
2092
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLayoutRendererComponent, decorators: [{
2316
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutRendererComponent, decorators: [{
2093
2317
  type: Component,
2094
- args: [{ selector: 'axp-layout-renderer', standalone: true, imports: [CommonModule, AXPWidgetCoreModule, AXFormModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
2318
+ args: [{ selector: 'axp-layout-renderer', standalone: true, imports: [AXPWidgetCoreModule, AXFormModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
2095
2319
  <ax-form>
2096
2320
  <axp-widgets-container [context]="internalContext()" (onContextChanged)="handleContextChanged($event)">
2097
2321
  @if (widgetTree()) {
2098
- <ng-container axp-widget-renderer [node]="widgetTree()!" [mode]="mode()"></ng-container>
2322
+ <ng-container
2323
+ axp-widget-renderer
2324
+ [node]="widgetTree()!"
2325
+ [mode]="effectiveRenderMode()"
2326
+ ></ng-container>
2099
2327
  }
2100
2328
  </axp-widgets-container>
2101
2329
  </ax-form>
2102
2330
  `, styles: [":host{display:block;width:100%}\n"] }]
2103
2331
  }], 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 }] }] } });
2104
2332
 
2333
+ /** Registration key for {@link AXPPreviewWidgetFieldCommand}; lives alone so `LayoutBuilderModule` can reference it without static-importing the command implementation. */
2334
+ const AXP_PREVIEW_WIDGET_FIELD_COMMAND_KEY = 'Widget:Preview';
2335
+
2105
2336
  class LayoutBuilderModule {
2106
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: LayoutBuilderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
2107
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.12", ngImport: i0, type: LayoutBuilderModule, imports: [CommonModule, AXPLayoutRendererComponent], exports: [AXPLayoutRendererComponent] }); }
2108
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: LayoutBuilderModule, providers: [AXPLayoutBuilderService], imports: [CommonModule, AXPLayoutRendererComponent] }); }
2337
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LayoutBuilderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
2338
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: LayoutBuilderModule, imports: [CommonModule, AXPLayoutRendererComponent], exports: [AXPLayoutRendererComponent] }); }
2339
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LayoutBuilderModule, providers: [
2340
+ AXPLayoutBuilderService,
2341
+ provideCommandSetups([
2342
+ {
2343
+ key: AXP_PREVIEW_WIDGET_FIELD_COMMAND_KEY,
2344
+ command: () => Promise.resolve().then(function () { return previewWidgetField_command; }).then((c) => c.AXPPreviewWidgetFieldCommand),
2345
+ },
2346
+ ]),
2347
+ ], imports: [CommonModule, AXPLayoutRendererComponent] }); }
2109
2348
  }
2110
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: LayoutBuilderModule, decorators: [{
2349
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LayoutBuilderModule, decorators: [{
2111
2350
  type: NgModule,
2112
2351
  args: [{
2113
2352
  imports: [CommonModule, AXPLayoutRendererComponent],
2114
- providers: [AXPLayoutBuilderService],
2353
+ providers: [
2354
+ AXPLayoutBuilderService,
2355
+ provideCommandSetups([
2356
+ {
2357
+ key: AXP_PREVIEW_WIDGET_FIELD_COMMAND_KEY,
2358
+ command: () => Promise.resolve().then(function () { return previewWidgetField_command; }).then((c) => c.AXPPreviewWidgetFieldCommand),
2359
+ },
2360
+ ]),
2361
+ ],
2115
2362
  exports: [AXPLayoutRendererComponent],
2116
2363
  }]
2117
2364
  }] });
@@ -2123,17 +2370,25 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2123
2370
  super(...arguments);
2124
2371
  this.result = new EventEmitter();
2125
2372
  this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
2126
- this.context = signal({}, ...(ngDevMode ? [{ debugName: "context" }] : []));
2373
+ this.commandService = inject(AXPCommandService);
2374
+ this.hookService = inject(AXPHookService, { optional: true });
2375
+ this.context = signal({}, ...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
2127
2376
  // This will be set by the popup service automatically - same as dynamic-dialog
2128
2377
  this.callBack = () => { };
2129
- this.isDialogLoading = signal(false, ...(ngDevMode ? [{ debugName: "isDialogLoading" }] : []));
2378
+ this.isDialogLoading = signal(false, ...(ngDevMode ? [{ debugName: "isDialogLoading" }] : /* istanbul ignore next */ []));
2130
2379
  // Aggregated actions for footer rendering
2131
- this.footerPrefix = signal([], ...(ngDevMode ? [{ debugName: "footerPrefix" }] : []));
2132
- this.footerSuffix = signal([], ...(ngDevMode ? [{ debugName: "footerSuffix" }] : []));
2380
+ this.footerPrefix = signal([], ...(ngDevMode ? [{ debugName: "footerPrefix" }] : /* istanbul ignore next */ []));
2381
+ this.footerSuffix = signal([], ...(ngDevMode ? [{ debugName: "footerSuffix" }] : /* istanbul ignore next */ []));
2382
+ /**
2383
+ * Correlate layout context snapshots for distributed hooks (`layout-builder.dialog.context-changed`).
2384
+ */
2385
+ this.contextChangedHooksSessionKey = typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
2386
+ ? crypto.randomUUID()
2387
+ : `layout-dialog-ctx-${Date.now()}-${Math.random().toString(36).slice(2)}`;
2133
2388
  //#endregion
2134
2389
  //#region ---- View Accessors ----
2135
2390
  // Access the internal layout renderer to reach the widgets container injector
2136
- this.layoutRenderer = viewChild(AXPLayoutRendererComponent, ...(ngDevMode ? [{ debugName: "layoutRenderer" }] : []));
2391
+ this.layoutRenderer = viewChild(AXPLayoutRendererComponent, ...(ngDevMode ? [{ debugName: "layoutRenderer" }] : /* istanbul ignore next */ []));
2137
2392
  this.#eff = effect(() => {
2138
2393
  let count = 0;
2139
2394
  this.aggregateAndEvaluateActions();
@@ -2141,10 +2396,10 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2141
2396
  const renderer = this.layoutRenderer();
2142
2397
  const container = renderer?.getContainer();
2143
2398
  this.widgetCoreService = container?.builderService ?? null;
2144
- count = this.widgetCoreService.registeredWidgetsCount();
2399
+ count = this.widgetCoreService?.registeredWidgetsCount();
2145
2400
  }
2146
2401
  else {
2147
- count = this.widgetCoreService.registeredWidgetsCount();
2402
+ count = this.widgetCoreService?.registeredWidgetsCount();
2148
2403
  // Clear existing timer
2149
2404
  if (this.debounceTimer) {
2150
2405
  clearTimeout(this.debounceTimer);
@@ -2154,23 +2409,48 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2154
2409
  this.aggregateAndEvaluateActions();
2155
2410
  }, 200);
2156
2411
  }
2157
- }, ...(ngDevMode ? [{ debugName: "#eff" }] : []));
2412
+ }, ...(ngDevMode ? [{ debugName: "#eff" }] : /* istanbul ignore next */ []));
2158
2413
  }
2159
2414
  //#endregion
2160
2415
  //#region ---- Lifecycle ----
2161
2416
  ngOnInit() {
2162
- // Initialize context with provided context
2163
2417
  this.context.set(this.config?.context || {});
2418
+ void this.invokeLayoutContextChangedHooks();
2164
2419
  }
2165
2420
  #eff;
2166
2421
  //#endregion
2167
2422
  handleContextChanged(event) {
2168
2423
  this.context.set(event);
2169
2424
  this.aggregateAndEvaluateActions();
2425
+ void this.invokeLayoutContextChangedHooks();
2170
2426
  }
2171
2427
  handleContextInitiated(event) {
2172
2428
  this.context.set(event);
2173
2429
  this.aggregateAndEvaluateActions();
2430
+ void this.invokeLayoutContextChangedHooks();
2431
+ }
2432
+ async invokeLayoutContextChangedHooks() {
2433
+ const meta = this.config?.metadata;
2434
+ if (!this.hookService) {
2435
+ return;
2436
+ }
2437
+ const payload = {
2438
+ sessionKey: this.contextChangedHooksSessionKey,
2439
+ getContext: () => (this.context() ?? {}),
2440
+ metadata: meta,
2441
+ patchContext: (partial) => {
2442
+ const merged = merge({}, this.context(), partial);
2443
+ this.context.set(merged);
2444
+ this.layoutRenderer()?.updateContext(merged);
2445
+ },
2446
+ setLoading: (loading) => this.isDialogLoading.set(loading),
2447
+ };
2448
+ try {
2449
+ await this.hookService.runAsync(AXP_LAYOUT_BUILDER_DIALOG_CONTEXT_CHANGED_HOOK_KEY, payload);
2450
+ }
2451
+ catch {
2452
+ // Hook providers are best-effort; avoid breaking the dialog lifecycle.
2453
+ }
2174
2454
  }
2175
2455
  footerPrefixActions() {
2176
2456
  return this.footerPrefix();
@@ -2185,38 +2465,138 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2185
2465
  return this.isDialogLoading();
2186
2466
  }
2187
2467
  async executeAction(action) {
2188
- const cmd = action.command;
2189
- if (cmd !== 'cancel') {
2468
+ const cmd = this.resolveActionCommandName(action.command);
2469
+ if (this.shouldValidateBeforeAction(cmd)) {
2190
2470
  const isValid = await this.layoutRenderer()?.validate();
2191
2471
  if (!isValid?.result) {
2192
2472
  return;
2193
2473
  }
2194
2474
  }
2195
- if (typeof cmd === 'string' && cmd.startsWith('widget:')) {
2475
+ //TODO: matin, why we need this? maybe we can remove it?
2476
+ if (cmd?.startsWith('widget:')) {
2196
2477
  const parsed = this.parseWidgetCommand(cmd);
2197
2478
  if (parsed.widgetName && parsed.action) {
2198
- await this.executeWidgetApi(parsed.widgetName, parsed.action);
2479
+ await this.invokeWidget(parsed.widgetName, parsed.action, {});
2199
2480
  await this.aggregateAndEvaluateActions();
2200
2481
  return;
2201
2482
  }
2202
2483
  }
2484
+ if (cmd && this.commandService.exists(cmd)) {
2485
+ const dialogRef = this.createDialogRef(cmd);
2486
+ const integration = (this.config.metadata ?? {});
2487
+ try {
2488
+ const cmdResult = await this.commandService.execute(cmd, { dialogRef, integration });
2489
+ if (!cmdResult?.success) {
2490
+ return;
2491
+ }
2492
+ if (this.shouldKeepDialogOpenAfterCommandResult(cmdResult)) {
2493
+ return;
2494
+ }
2495
+ this.callBack(cmdResult);
2496
+ await this.closeWithOptionalSkipValidate(cmdResult);
2497
+ }
2498
+ catch (error) {
2499
+ console.error('Error executing action', cmd, error);
2500
+ }
2501
+ return;
2502
+ }
2503
+ const context = this.context();
2504
+ const onAction = this.config?.onAction;
2505
+ if (onAction) {
2506
+ const dialogRef = this.createDialogRef(cmd);
2507
+ try {
2508
+ this.isDialogLoading.set(true);
2509
+ const result = await Promise.resolve(onAction(dialogRef));
2510
+ if (this.shouldKeepDialogOpenAfterCommandResult(result)) {
2511
+ return;
2512
+ }
2513
+ this.callBack(result);
2514
+ await this.closeWithOptionalSkipValidate(result);
2515
+ }
2516
+ catch {
2517
+ // Handler threw: stay open for retry, actions remain clickable
2518
+ }
2519
+ finally {
2520
+ this.isDialogLoading.set(false);
2521
+ }
2522
+ return;
2523
+ }
2203
2524
  // Fallback: treat as regular dialog action (cancel/confirm/custom)
2204
- const result = { context: this.context(), action: cmd };
2525
+ const result = { context, action: cmd };
2205
2526
  this.dialogResult = result;
2206
2527
  if (this.data) {
2207
2528
  this.data.context = result.context;
2208
2529
  this.data.action = result.action;
2209
2530
  }
2210
2531
  this.callBack({
2211
- close: (result) => {
2212
- this.close(result);
2532
+ ...this.createDialogRef(cmd),
2533
+ action: () => result.action,
2534
+ });
2535
+ // Without `onAction`, only the configured cancel action dismisses the dialog (not submit/custom).
2536
+ if (cmd === 'cancel') {
2537
+ await this.close(result);
2538
+ }
2539
+ }
2540
+ /** Whether the layout form should be validated before running this footer command. */
2541
+ shouldValidateBeforeAction(cmd) {
2542
+ if (!cmd || cmd === 'cancel' || cmd === 'entity-form-done') {
2543
+ return false;
2544
+ }
2545
+ if (cmd.startsWith('widget:')) {
2546
+ return false;
2547
+ }
2548
+ if (this.commandService.exists(cmd)) {
2549
+ return false;
2550
+ }
2551
+ return true;
2552
+ }
2553
+ /** True when a footer handler or command result asks to leave the dialog open (`keepDialogOpen` on the result or `result.data`). */
2554
+ shouldKeepDialogOpenAfterCommandResult(result) {
2555
+ if (!result || typeof result !== 'object') {
2556
+ return false;
2557
+ }
2558
+ const top = result;
2559
+ if (top.keepDialogOpen === true) {
2560
+ return true;
2561
+ }
2562
+ if (top.data != null && typeof top.data === 'object' && 'keepDialogOpen' in top.data) {
2563
+ return top.data.keepDialogOpen === true;
2564
+ }
2565
+ return false;
2566
+ }
2567
+ createDialogRef(actionCmd) {
2568
+ return {
2569
+ close: (res) => {
2570
+ void this.closeWithOptionalSkipValidate(res);
2213
2571
  },
2214
2572
  context: () => this.context(),
2215
- action: () => result.action,
2216
- setLoading: (loading) => {
2217
- this.isDialogLoading.set(loading);
2573
+ action: () => actionCmd,
2574
+ setLoading: (loading) => this.isDialogLoading.set(loading),
2575
+ patchContext: (partial) => {
2576
+ const merged = merge({}, this.context(), partial);
2577
+ this.context.set(merged);
2578
+ this.layoutRenderer()?.updateContext(merged);
2218
2579
  },
2219
- });
2580
+ invokeWidget: (widgetName, method, opts) => this.invokeWidget(widgetName, method, opts ?? {}),
2581
+ };
2582
+ }
2583
+ async closeWithOptionalSkipValidate(result) {
2584
+ if (result && typeof result === 'object' && result.skipValidate) {
2585
+ this.result.emit(result);
2586
+ await super.close(result);
2587
+ return;
2588
+ }
2589
+ await this.close(result);
2590
+ }
2591
+ /** Resolves footer/widget action command to a string (e.g. `cancel`, `submit`, `widget:...`). */
2592
+ resolveActionCommandName(command) {
2593
+ if (typeof command === 'string') {
2594
+ return command;
2595
+ }
2596
+ if (command && typeof command === 'object' && 'name' in command) {
2597
+ return command.name;
2598
+ }
2599
+ return undefined;
2220
2600
  }
2221
2601
  parseWidgetCommand(cmd) {
2222
2602
  // Expected 'widget:<widgetName>.<action>'
@@ -2228,7 +2608,7 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2228
2608
  return {};
2229
2609
  return { widgetName: rest.slice(0, dot), action: rest.slice(dot + 1) };
2230
2610
  }
2231
- async executeWidgetApi(widgetName, apiMethod) {
2611
+ async invokeWidget(widgetName, apiMethod, opts) {
2232
2612
  if (!this.widgetCoreService)
2233
2613
  return;
2234
2614
  try {
@@ -2238,16 +2618,20 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2238
2618
  if (typeof fn === 'function') {
2239
2619
  await Promise.resolve(fn({
2240
2620
  close: (result) => {
2241
- this.close(result);
2621
+ void this.closeWithOptionalSkipValidate(result);
2242
2622
  },
2243
2623
  context: () => this.context(),
2244
2624
  setLoading: (loading) => {
2245
- this.isDialogLoading.set(loading);
2625
+ (opts.setLoading ?? ((v) => this.isDialogLoading.set(v)))(loading);
2246
2626
  },
2247
2627
  }));
2628
+ // Footer predicates (e.g. wizard step) must refresh when the widget advances outside executeAction (e.g. dialogRef.invokeWidget after entity-form continue).
2629
+ await this.aggregateAndEvaluateActions();
2248
2630
  }
2249
2631
  }
2250
- catch { }
2632
+ catch {
2633
+ //
2634
+ }
2251
2635
  }
2252
2636
  async close(result) {
2253
2637
  if (result) {
@@ -2297,6 +2681,7 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2297
2681
  zone: 'footer',
2298
2682
  placement,
2299
2683
  scope: a.scope,
2684
+ predicateApiWidgetName: a.predicateApiWidgetName,
2300
2685
  });
2301
2686
  const prefix = (footer?.prefix || []).map((a) => mapOne(a, 'prefix'));
2302
2687
  const suffix = (footer?.suffix || []).map((a) => mapOne(a, 'suffix'));
@@ -2306,16 +2691,18 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2306
2691
  const out = [];
2307
2692
  for (const a of actions) {
2308
2693
  const parsed = typeof a.command === 'string' ? this.parseWidgetCommand(a.command) : {};
2309
- const api = parsed.widgetName ? await this.resolveApi(parsed.widgetName) : undefined;
2694
+ const widgetNameForApi = parsed.widgetName ?? a.predicateApiWidgetName;
2695
+ const api = widgetNameForApi ? await this.resolveApi(widgetNameForApi) : undefined;
2310
2696
  const scope = {
2311
2697
  api,
2312
- widget: { name: parsed.widgetName },
2698
+ widget: { name: widgetNameForApi },
2313
2699
  dialog: { context: this.context() },
2314
2700
  context: this.context(),
2315
2701
  };
2316
2702
  const disabled = await this.evalBool(a.disabled, scope);
2317
2703
  const hidden = await this.evalBool(a.hidden, scope);
2318
- out.push({ ...a, disabled, hidden });
2704
+ const resolvedTitle = (await this.evalActionTitle(a.title, scope)) ?? a.title;
2705
+ out.push({ ...a, disabled, hidden, title: resolvedTitle });
2319
2706
  }
2320
2707
  return out;
2321
2708
  }
@@ -2333,6 +2720,25 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2333
2720
  }
2334
2721
  return value;
2335
2722
  }
2723
+ /** Resolves footer action title when it contains {{ ... }} (e.g. wizard labels from dialog context). */
2724
+ async evalActionTitle(value, scope) {
2725
+ if (value == null || typeof value !== 'string' || !value.includes('{{')) {
2726
+ return value;
2727
+ }
2728
+ try {
2729
+ const result = await this.expressionEvaluator.evaluate(value, scope);
2730
+ if (typeof result === 'string' && result.length > 0) {
2731
+ return result;
2732
+ }
2733
+ if (result != null && result !== false) {
2734
+ return String(result);
2735
+ }
2736
+ }
2737
+ catch {
2738
+ //
2739
+ }
2740
+ return value;
2741
+ }
2336
2742
  async resolveApi(widgetName) {
2337
2743
  try {
2338
2744
  await this.widgetCoreService?.waitForWidget(widgetName, 2000);
@@ -2343,9 +2749,13 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2343
2749
  return undefined;
2344
2750
  }
2345
2751
  }
2346
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPDialogRendererComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
2347
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", type: AXPDialogRendererComponent, isStandalone: true, selector: "axp-dialog-renderer", inputs: { config: "config" }, outputs: { result: "result" }, viewQueries: [{ propertyName: "layoutRenderer", first: true, predicate: AXPLayoutRendererComponent, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
2752
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDialogRendererComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
2753
+ 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: `
2754
+ <axp-component-slot name="dialog-header" [context]="context()"></axp-component-slot>
2348
2755
  <div class="ax-p-4">
2756
+ <!-- @if (config.message) {
2757
+ <p class="ax-mb-4 ax-leading-relaxed">{{ config.message | translate | async }}</p>
2758
+ } -->
2349
2759
  <axp-layout-renderer
2350
2760
  [layout]="config.definition"
2351
2761
  [context]="context()"
@@ -2355,46 +2765,57 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2355
2765
  </axp-layout-renderer>
2356
2766
  </div>
2357
2767
 
2358
- <ax-footer>
2359
- <ax-prefix>
2360
- @for (action of footerPrefixActions(); track $index) {
2361
- <ax-button
2362
- [disabled]="action.disabled || isFormLoading()"
2363
- [text]="(action.title | translate | async)!"
2364
- [look]="'outline'"
2365
- [color]="action.color"
2366
- (onClick)="executeAction(action)"
2367
- >
2368
- <ax-prefix>
2369
- <i class="{{ action.icon }}"></i>
2370
- </ax-prefix>
2371
- </ax-button>
2372
- }
2373
- </ax-prefix>
2374
- <ax-suffix>
2375
- @for (action of footerSuffixActions(); track $index) {
2376
- <ax-button
2377
- [disabled]="action.disabled || isSubmitting()"
2378
- [text]="(action.title | translate | async)!"
2379
- [look]="'solid'"
2380
- [color]="action.color"
2381
- (onClick)="executeAction(action)"
2382
- >
2383
- @if (isFormLoading()) {
2384
- <ax-loading></ax-loading>
2385
- }
2386
- @if (action.icon) {
2768
+ <!-- Custom footer slot: if it has content, default footer is hidden -->
2769
+ <axp-component-slot name="dialog-footer" #footerSlot="slot" [context]="context()"></axp-component-slot>
2770
+ @if (footerSlot.isEmpty()) {
2771
+ <ax-footer>
2772
+ <ax-prefix>
2773
+ <axp-component-slot name="dialog-footer-prefix" [context]="context()"></axp-component-slot>
2774
+ @for (action of footerPrefixActions(); track $index) {
2775
+ <ax-button
2776
+ [disabled]="action.disabled || isFormLoading()"
2777
+ [text]="(action.title | translate | async)!"
2778
+ [look]="'outline'"
2779
+ [color]="action.color"
2780
+ (onClick)="executeAction(action)"
2781
+ >
2782
+ @if (isFormLoading()) {
2783
+ <ax-loading></ax-loading>
2784
+ }
2387
2785
  <ax-prefix>
2388
- <ax-icon icon="{{ action.icon }}"></ax-icon>
2786
+ @if (action.icon) {
2787
+ <ax-icon [icon]="action.icon"></ax-icon>
2788
+ }
2389
2789
  </ax-prefix>
2390
- }
2391
- </ax-button>
2392
- }
2393
- </ax-suffix>
2394
- </ax-footer>
2395
- `, 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: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.AXTranslatorPipe, name: "translate" }] }); }
2790
+ </ax-button>
2791
+ }
2792
+ </ax-prefix>
2793
+ <ax-suffix>
2794
+ @for (action of footerSuffixActions(); track $index) {
2795
+ <ax-button
2796
+ [disabled]="action.disabled || isSubmitting()"
2797
+ [text]="(action.title | translate | async)!"
2798
+ [look]="'solid'"
2799
+ [color]="action.color"
2800
+ (onClick)="executeAction(action)"
2801
+ >
2802
+ @if (isFormLoading()) {
2803
+ <ax-loading></ax-loading>
2804
+ }
2805
+ @if (action.icon) {
2806
+ <ax-prefix>
2807
+ <ax-icon [icon]="action.icon"></ax-icon>
2808
+ </ax-prefix>
2809
+ }
2810
+ </ax-button>
2811
+ }
2812
+ <axp-component-slot name="dialog-footer-suffix" [context]="context()"></axp-component-slot>
2813
+ </ax-suffix>
2814
+ </ax-footer>
2815
+ }
2816
+ `, 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 }); }
2396
2817
  }
2397
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPDialogRendererComponent, decorators: [{
2818
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDialogRendererComponent, decorators: [{
2398
2819
  type: Component,
2399
2820
  args: [{
2400
2821
  selector: 'axp-dialog-renderer',
@@ -2406,9 +2827,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
2406
2827
  AXDecoratorModule,
2407
2828
  AXLoadingModule,
2408
2829
  AXTranslationModule,
2830
+ AXPComponentSlotModule,
2409
2831
  ],
2832
+ providers: [AXPContextStore],
2833
+ changeDetection: ChangeDetectionStrategy.OnPush,
2410
2834
  template: `
2835
+ <axp-component-slot name="dialog-header" [context]="context()"></axp-component-slot>
2411
2836
  <div class="ax-p-4">
2837
+ <!-- @if (config.message) {
2838
+ <p class="ax-mb-4 ax-leading-relaxed">{{ config.message | translate | async }}</p>
2839
+ } -->
2412
2840
  <axp-layout-renderer
2413
2841
  [layout]="config.definition"
2414
2842
  [context]="context()"
@@ -2418,48 +2846,57 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
2418
2846
  </axp-layout-renderer>
2419
2847
  </div>
2420
2848
 
2421
- <ax-footer>
2422
- <ax-prefix>
2423
- @for (action of footerPrefixActions(); track $index) {
2424
- <ax-button
2425
- [disabled]="action.disabled || isFormLoading()"
2426
- [text]="(action.title | translate | async)!"
2427
- [look]="'outline'"
2428
- [color]="action.color"
2429
- (onClick)="executeAction(action)"
2430
- >
2431
- <ax-prefix>
2432
- <i class="{{ action.icon }}"></i>
2433
- </ax-prefix>
2434
- </ax-button>
2435
- }
2436
- </ax-prefix>
2437
- <ax-suffix>
2438
- @for (action of footerSuffixActions(); track $index) {
2439
- <ax-button
2440
- [disabled]="action.disabled || isSubmitting()"
2441
- [text]="(action.title | translate | async)!"
2442
- [look]="'solid'"
2443
- [color]="action.color"
2444
- (onClick)="executeAction(action)"
2445
- >
2446
- @if (isFormLoading()) {
2447
- <ax-loading></ax-loading>
2448
- }
2449
- @if (action.icon) {
2849
+ <!-- Custom footer slot: if it has content, default footer is hidden -->
2850
+ <axp-component-slot name="dialog-footer" #footerSlot="slot" [context]="context()"></axp-component-slot>
2851
+ @if (footerSlot.isEmpty()) {
2852
+ <ax-footer>
2853
+ <ax-prefix>
2854
+ <axp-component-slot name="dialog-footer-prefix" [context]="context()"></axp-component-slot>
2855
+ @for (action of footerPrefixActions(); track $index) {
2856
+ <ax-button
2857
+ [disabled]="action.disabled || isFormLoading()"
2858
+ [text]="(action.title | translate | async)!"
2859
+ [look]="'outline'"
2860
+ [color]="action.color"
2861
+ (onClick)="executeAction(action)"
2862
+ >
2863
+ @if (isFormLoading()) {
2864
+ <ax-loading></ax-loading>
2865
+ }
2450
2866
  <ax-prefix>
2451
- <ax-icon icon="{{ action.icon }}"></ax-icon>
2867
+ @if (action.icon) {
2868
+ <ax-icon [icon]="action.icon"></ax-icon>
2869
+ }
2452
2870
  </ax-prefix>
2453
- }
2454
- </ax-button>
2455
- }
2456
- </ax-suffix>
2457
- </ax-footer>
2871
+ </ax-button>
2872
+ }
2873
+ </ax-prefix>
2874
+ <ax-suffix>
2875
+ @for (action of footerSuffixActions(); track $index) {
2876
+ <ax-button
2877
+ [disabled]="action.disabled || isSubmitting()"
2878
+ [text]="(action.title | translate | async)!"
2879
+ [look]="'solid'"
2880
+ [color]="action.color"
2881
+ (onClick)="executeAction(action)"
2882
+ >
2883
+ @if (isFormLoading()) {
2884
+ <ax-loading></ax-loading>
2885
+ }
2886
+ @if (action.icon) {
2887
+ <ax-prefix>
2888
+ <ax-icon [icon]="action.icon"></ax-icon>
2889
+ </ax-prefix>
2890
+ }
2891
+ </ax-button>
2892
+ }
2893
+ <axp-component-slot name="dialog-footer-suffix" [context]="context()"></axp-component-slot>
2894
+ </ax-suffix>
2895
+ </ax-footer>
2896
+ }
2458
2897
  `,
2459
2898
  }]
2460
- }], propDecorators: { config: [{
2461
- type: Input
2462
- }], result: [{
2899
+ }], propDecorators: { result: [{
2463
2900
  type: Output
2464
2901
  }], layoutRenderer: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AXPLayoutRendererComponent), { isSignal: true }] }] } });
2465
2902
 
@@ -2468,9 +2905,190 @@ var dialogRenderer_component = /*#__PURE__*/Object.freeze({
2468
2905
  AXPDialogRendererComponent: AXPDialogRendererComponent
2469
2906
  });
2470
2907
 
2908
+ //#region ---- Imports ----
2909
+ /**
2910
+ * `customWidget` only forwards keys from its options bag into the built node via `addSingleWidget`.
2911
+ * Designer / configurator persist `defaultValue` (and other extended fields) on the widget node root;
2912
+ * spreading `options` alone drops them, so preview never applied defaults.
2913
+ */
2914
+ /**
2915
+ * Widget options are sometimes persisted with an extra nesting (`options.options`) when context
2916
+ * was merged incorrectly. Flatten so list/data-source resolution sees `dataSource` at the top level.
2917
+ */
2918
+ function optionsBagForPreview(node) {
2919
+ const raw = (node.options ?? {});
2920
+ const inner = raw['options'];
2921
+ if (inner !== undefined && typeof inner === 'object' && !Array.isArray(inner)) {
2922
+ const { options: _nested, ...rest } = raw;
2923
+ return { ...rest, ...inner };
2924
+ }
2925
+ return { ...raw };
2926
+ }
2927
+ function extendedNodePropsForPreview(node) {
2928
+ const out = {};
2929
+ if (node.defaultValue !== undefined) {
2930
+ out['defaultValue'] = node.defaultValue;
2931
+ }
2932
+ if (node.triggers !== undefined) {
2933
+ out['triggers'] = node.triggers;
2934
+ }
2935
+ if (node.meta !== undefined) {
2936
+ out['meta'] = node.meta;
2937
+ }
2938
+ if (node.valueTransforms !== undefined) {
2939
+ out['valueTransforms'] = node.valueTransforms;
2940
+ }
2941
+ if (node.visible !== undefined) {
2942
+ out['visible'] = node.visible;
2943
+ }
2944
+ if (node.mode !== undefined) {
2945
+ out['mode'] = node.mode;
2946
+ }
2947
+ if (node.children !== undefined) {
2948
+ out['children'] = node.children;
2949
+ }
2950
+ return out;
2951
+ }
2952
+ //#endregion
2953
+ //#region ---- Command ----
2954
+ /**
2955
+ * Opens a dialog that previews a widget configuration (same behavior as the preview button on
2956
+ * `axp-widget-field-configurator`). Invoked from that component and from entity list actions.
2957
+ */
2958
+ class AXPPreviewWidgetFieldCommand {
2959
+ constructor() {
2960
+ this.formBuilderService = inject(AXPLayoutBuilderService);
2961
+ this.widgetRegistry = inject(AXPWidgetRegistryService);
2962
+ this.translationService = inject(AXTranslationService);
2963
+ this.crudService = inject(AXP_ENTITY_DEFINITION_CRUD_SERVICE, { optional: true });
2964
+ }
2965
+ async execute(input) {
2966
+ try {
2967
+ const merged = this.mergeInvocation(input);
2968
+ const currentWidget = this.normalizeWidget(merged['widget'] ?? merged['interface']);
2969
+ if (!currentWidget?.type) {
2970
+ return {
2971
+ success: false,
2972
+ message: {
2973
+ text: (await this.translationService.translateAsync('@general:messages.invalid-data')) || 'Invalid data',
2974
+ },
2975
+ };
2976
+ }
2977
+ const fieldName = String(merged['fieldName'] ?? merged['name'] ?? 'Field');
2978
+ const rawTitle = (merged['fieldTitle'] ?? merged['title']);
2979
+ const fieldTitleLabel = this.resolveFieldTitleLabel(rawTitle, fieldName);
2980
+ const dialogTitle = (await this.resolveWidgetDisplayTitle(currentWidget.type)) || currentWidget.type || fieldTitleLabel;
2981
+ const previewWidgetOptions = {
2982
+ ...optionsBagForPreview(currentWidget),
2983
+ name: fieldName,
2984
+ ...extendedNodePropsForPreview(currentWidget),
2985
+ };
2986
+ const dialogOutcome = await this.formBuilderService
2987
+ .create()
2988
+ .dialog((dialog) => {
2989
+ dialog
2990
+ .setTitle(dialogTitle)
2991
+ .setSize('md')
2992
+ .setCloseButton(true)
2993
+ .setContext({})
2994
+ .content((layoutBuilder) => {
2995
+ layoutBuilder.formField(fieldTitleLabel, (formField) => {
2996
+ formField.customWidget(currentWidget.type, previewWidgetOptions);
2997
+ });
2998
+ })
2999
+ .setActions((actions) => actions.cancel('@general:actions.close.title'));
3000
+ })
3001
+ .show();
3002
+ const cancelled = this.isCancelDialogOutcome(dialogOutcome);
3003
+ return {
3004
+ success: !cancelled,
3005
+ message: { text: '' },
3006
+ };
3007
+ }
3008
+ catch (error) {
3009
+ const message = error instanceof Error ? error.message : 'Unknown error';
3010
+ return {
3011
+ success: false,
3012
+ message: { text: message },
3013
+ };
3014
+ }
3015
+ }
3016
+ mergeInvocation(input) {
3017
+ const contextOptions = input.__context__?.options;
3018
+ const ctxData = input.__context__?.data;
3019
+ const { __context__: _ctx, ...rest } = input;
3020
+ return {
3021
+ ...(ctxData ?? {}),
3022
+ ...(contextOptions ?? {}),
3023
+ ...rest,
3024
+ };
3025
+ }
3026
+ normalizeWidget(raw) {
3027
+ if (raw == null)
3028
+ return null;
3029
+ if (typeof raw === 'string') {
3030
+ const t = raw.trim();
3031
+ return t ? { type: t, options: {} } : null;
3032
+ }
3033
+ if (typeof raw === 'object' && !Array.isArray(raw) && 'type' in raw) {
3034
+ const w = raw;
3035
+ return w.type ? cloneDeep(w) : null;
3036
+ }
3037
+ return null;
3038
+ }
3039
+ resolveFieldTitleLabel(raw, fallback) {
3040
+ let source = fallback;
3041
+ if (raw !== undefined && raw !== null) {
3042
+ if (typeof raw === 'string') {
3043
+ if (raw.trim() !== '') {
3044
+ source = raw;
3045
+ }
3046
+ }
3047
+ else if (typeof raw === 'object' && !Array.isArray(raw) && Object.keys(raw).length > 0) {
3048
+ source = raw;
3049
+ }
3050
+ }
3051
+ return this.translationService.resolve(source);
3052
+ }
3053
+ isCancelDialogOutcome(outcome) {
3054
+ if (outcome == null) {
3055
+ return false;
3056
+ }
3057
+ const ref = outcome;
3058
+ if (typeof ref.action !== 'function') {
3059
+ return false;
3060
+ }
3061
+ return ref.action() === 'cancel';
3062
+ }
3063
+ async resolveWidgetDisplayTitle(widgetType) {
3064
+ const crud = this.crudService;
3065
+ if (crud) {
3066
+ const interfaces = await crud.listInterfaces();
3067
+ const iface = interfaces.find((d) => d.name === widgetType);
3068
+ return iface?.title ?? iface?.name;
3069
+ }
3070
+ const config = this.widgetRegistry.getOptional(widgetType);
3071
+ if (!config) {
3072
+ return undefined;
3073
+ }
3074
+ const resolved = this.translationService.resolve(config.title);
3075
+ return resolved || undefined;
3076
+ }
3077
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPreviewWidgetFieldCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3078
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPreviewWidgetFieldCommand }); }
3079
+ }
3080
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPreviewWidgetFieldCommand, decorators: [{
3081
+ type: Injectable
3082
+ }] });
3083
+
3084
+ var previewWidgetField_command = /*#__PURE__*/Object.freeze({
3085
+ __proto__: null,
3086
+ AXPPreviewWidgetFieldCommand: AXPPreviewWidgetFieldCommand
3087
+ });
3088
+
2471
3089
  /**
2472
3090
  * Generated bundle index. Do not edit.
2473
3091
  */
2474
3092
 
2475
- export { AXPDialogRendererComponent, AXPLayoutBuilderService, AXPLayoutConversionService, AXPLayoutRendererComponent, LayoutBuilderModule };
3093
+ 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 };
2476
3094
  //# sourceMappingURL=acorex-platform-layout-builder.mjs.map