@acorex/platform 20.8.5 → 21.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/fesm2022/acorex-platform-auth.mjs +121 -27
  2. package/fesm2022/acorex-platform-auth.mjs.map +1 -1
  3. package/fesm2022/{acorex-platform-common-common-settings.provider-41RhWqb4.mjs → acorex-platform-common-common-settings.provider-G9XcXXOG.mjs} +4 -4
  4. package/fesm2022/acorex-platform-common-common-settings.provider-G9XcXXOG.mjs.map +1 -0
  5. package/fesm2022/acorex-platform-common.mjs +669 -268
  6. package/fesm2022/acorex-platform-common.mjs.map +1 -1
  7. package/fesm2022/acorex-platform-core.mjs +333 -140
  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 +539 -110
  12. package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
  13. package/fesm2022/{acorex-platform-layout-components-binding-expression-editor-popup.component-CSxCnzwU.mjs → acorex-platform-layout-components-binding-expression-editor-popup.component-CWV4XD36.mjs} +15 -15
  14. package/fesm2022/acorex-platform-layout-components-binding-expression-editor-popup.component-CWV4XD36.mjs.map +1 -0
  15. package/fesm2022/acorex-platform-layout-components.mjs +3285 -1035
  16. package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
  17. package/fesm2022/acorex-platform-layout-designer.mjs +488 -284
  18. package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
  19. package/fesm2022/acorex-platform-layout-entity.mjs +15955 -11978
  20. package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
  21. package/fesm2022/acorex-platform-layout-views.mjs +410 -170
  22. package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
  23. package/fesm2022/acorex-platform-layout-widget-core.mjs +548 -474
  24. package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
  25. package/fesm2022/{acorex-platform-layout-widgets-button-widget-designer.component-CPBzE96V.mjs → acorex-platform-layout-widgets-button-widget-designer.component-fLjWiSFE.mjs} +11 -11
  26. package/fesm2022/acorex-platform-layout-widgets-button-widget-designer.component-fLjWiSFE.mjs.map +1 -0
  27. package/fesm2022/{acorex-platform-layout-widgets-file-list-popup.component-Dtv6U3df.mjs → acorex-platform-layout-widgets-file-list-popup.component-3oRAKxTo.mjs} +22 -77
  28. package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-3oRAKxTo.mjs.map +1 -0
  29. package/fesm2022/{acorex-platform-layout-widgets-image-preview.popup-QxJfswhK.mjs → acorex-platform-layout-widgets-image-preview.popup-CazpERbX.mjs} +8 -9
  30. package/fesm2022/acorex-platform-layout-widgets-image-preview.popup-CazpERbX.mjs.map +1 -0
  31. package/fesm2022/{acorex-platform-layout-widgets-page-widget-designer.component-CVdssZBD.mjs → acorex-platform-layout-widgets-page-widget-designer.component-BQ4G6aYf.mjs} +17 -17
  32. package/fesm2022/acorex-platform-layout-widgets-page-widget-designer.component-BQ4G6aYf.mjs.map +1 -0
  33. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-DWuWxUF_.mjs +116 -0
  34. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-DWuWxUF_.mjs.map +1 -0
  35. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-edit-popup.component-DVaZN9QN.mjs → acorex-platform-layout-widgets-tabular-data-edit-popup.component-fV94u3t2.mjs} +25 -19
  36. package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-fV94u3t2.mjs.map +1 -0
  37. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-view-popup.component-DPGHgXa6.mjs → acorex-platform-layout-widgets-tabular-data-view-popup.component-DyuvQhgN.mjs} +9 -9
  38. package/fesm2022/acorex-platform-layout-widgets-tabular-data-view-popup.component-DyuvQhgN.mjs.map +1 -0
  39. package/fesm2022/{acorex-platform-layout-widgets-text-block-widget-designer.component-CdiNW691.mjs → acorex-platform-layout-widgets-text-block-widget-designer.component-EJMMdpIs.mjs} +7 -7
  40. package/fesm2022/acorex-platform-layout-widgets-text-block-widget-designer.component-EJMMdpIs.mjs.map +1 -0
  41. package/fesm2022/acorex-platform-layout-widgets.mjs +6396 -4058
  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 +328 -166
  46. package/fesm2022/acorex-platform-runtime.mjs.map +1 -1
  47. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-CkptOSO3.mjs +160 -0
  48. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-CkptOSO3.mjs.map +1 -0
  49. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-DXC2qtvK.mjs +120 -0
  50. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-DXC2qtvK.mjs.map +1 -0
  51. package/fesm2022/{acorex-platform-themes-default-entity-master-single-view.component-rGsMVAZj.mjs → acorex-platform-themes-default-entity-master-single-view.component-DYyunzKZ.mjs} +16 -23
  52. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-DYyunzKZ.mjs.map +1 -0
  53. package/fesm2022/acorex-platform-themes-default-error-401.component-C5lQECDP.mjs +31 -0
  54. package/fesm2022/acorex-platform-themes-default-error-401.component-C5lQECDP.mjs.map +1 -0
  55. package/fesm2022/acorex-platform-themes-default-error-404.component-D5wBXAB-.mjs +25 -0
  56. package/fesm2022/acorex-platform-themes-default-error-404.component-D5wBXAB-.mjs.map +1 -0
  57. package/fesm2022/acorex-platform-themes-default-error-offline.component-AhDiY3DI.mjs +19 -0
  58. package/fesm2022/acorex-platform-themes-default-error-offline.component-AhDiY3DI.mjs.map +1 -0
  59. package/fesm2022/acorex-platform-themes-default.mjs +1687 -69
  60. package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
  61. package/fesm2022/{acorex-platform-themes-shared-icon-chooser-column.component-TJ9PWHMY.mjs → acorex-platform-themes-shared-icon-chooser-column.component-QL2-ZUVg.mjs} +8 -8
  62. package/fesm2022/acorex-platform-themes-shared-icon-chooser-column.component-QL2-ZUVg.mjs.map +1 -0
  63. package/fesm2022/{acorex-platform-themes-shared-icon-chooser-view.component-BHcKkIx0.mjs → acorex-platform-themes-shared-icon-chooser-view.component-BXydqPt_.mjs} +8 -8
  64. package/fesm2022/acorex-platform-themes-shared-icon-chooser-view.component-BXydqPt_.mjs.map +1 -0
  65. package/fesm2022/{acorex-platform-themes-shared-settings.provider-DSs1o1M6.mjs → acorex-platform-themes-shared-settings.provider-D13QB3Hr.mjs} +2 -2
  66. package/fesm2022/acorex-platform-themes-shared-settings.provider-D13QB3Hr.mjs.map +1 -0
  67. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-Cb9iY6k9.mjs +88 -0
  68. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-Cb9iY6k9.mjs.map +1 -0
  69. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-KP4-BND5.mjs +80 -0
  70. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-KP4-BND5.mjs.map +1 -0
  71. package/fesm2022/acorex-platform-themes-shared.mjs +572 -465
  72. package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
  73. package/fesm2022/acorex-platform-workflow.mjs +276 -98
  74. package/fesm2022/acorex-platform-workflow.mjs.map +1 -1
  75. package/fesm2022/acorex-platform.mjs.map +1 -1
  76. package/package.json +33 -33
  77. package/{auth/index.d.ts → types/acorex-platform-auth.d.ts} +14 -2
  78. package/{common/index.d.ts → types/acorex-platform-common.d.ts} +261 -24
  79. package/{core/index.d.ts → types/acorex-platform-core.d.ts} +163 -42
  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} +137 -38
  82. package/{layout/components/index.d.ts → types/acorex-platform-layout-components.d.ts} +808 -131
  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} +686 -61
  85. package/{layout/views/index.d.ts → types/acorex-platform-layout-views.d.ts} +80 -47
  86. package/{layout/widget-core/index.d.ts → types/acorex-platform-layout-widget-core.d.ts} +274 -197
  87. package/{layout/widgets/index.d.ts → types/acorex-platform-layout-widgets.d.ts} +583 -104
  88. package/{native/index.d.ts → types/acorex-platform-native.d.ts} +0 -7
  89. package/types/acorex-platform-runtime.d.ts +565 -0
  90. package/{themes/default/index.d.ts → types/acorex-platform-themes-default.d.ts} +105 -4
  91. package/{themes/shared/index.d.ts → types/acorex-platform-themes-shared.d.ts} +14 -5
  92. package/{workflow/index.d.ts → types/acorex-platform-workflow.d.ts} +96 -81
  93. package/fesm2022/acorex-platform-common-common-settings.provider-41RhWqb4.mjs.map +0 -1
  94. package/fesm2022/acorex-platform-layout-components-binding-expression-editor-popup.component-CSxCnzwU.mjs.map +0 -1
  95. package/fesm2022/acorex-platform-layout-widgets-button-widget-designer.component-CPBzE96V.mjs.map +0 -1
  96. package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-Dtv6U3df.mjs.map +0 -1
  97. package/fesm2022/acorex-platform-layout-widgets-image-preview.popup-QxJfswhK.mjs.map +0 -1
  98. package/fesm2022/acorex-platform-layout-widgets-page-widget-designer.component-CVdssZBD.mjs.map +0 -1
  99. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-BCxE0RTB.mjs +0 -111
  100. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-BCxE0RTB.mjs.map +0 -1
  101. package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-DVaZN9QN.mjs.map +0 -1
  102. package/fesm2022/acorex-platform-layout-widgets-tabular-data-view-popup.component-DPGHgXa6.mjs.map +0 -1
  103. package/fesm2022/acorex-platform-layout-widgets-text-block-widget-designer.component-CdiNW691.mjs.map +0 -1
  104. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-D4hU2SCE.mjs +0 -160
  105. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-D4hU2SCE.mjs.map +0 -1
  106. package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-CLDoygoI.mjs +0 -1610
  107. package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-CLDoygoI.mjs.map +0 -1
  108. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-BVTklnzs.mjs +0 -120
  109. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-BVTklnzs.mjs.map +0 -1
  110. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-rGsMVAZj.mjs.map +0 -1
  111. package/fesm2022/acorex-platform-themes-default-error-401.component-53VB-PS_.mjs +0 -31
  112. package/fesm2022/acorex-platform-themes-default-error-401.component-53VB-PS_.mjs.map +0 -1
  113. package/fesm2022/acorex-platform-themes-default-error-404.component-DVF9soT5.mjs +0 -25
  114. package/fesm2022/acorex-platform-themes-default-error-404.component-DVF9soT5.mjs.map +0 -1
  115. package/fesm2022/acorex-platform-themes-default-error-offline.component-CwNNHzZn.mjs +0 -19
  116. package/fesm2022/acorex-platform-themes-default-error-offline.component-CwNNHzZn.mjs.map +0 -1
  117. package/fesm2022/acorex-platform-themes-shared-icon-chooser-column.component-TJ9PWHMY.mjs.map +0 -1
  118. package/fesm2022/acorex-platform-themes-shared-icon-chooser-view.component-BHcKkIx0.mjs.map +0 -1
  119. package/fesm2022/acorex-platform-themes-shared-settings.provider-DSs1o1M6.mjs.map +0 -1
  120. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-BUPs84MI.mjs +0 -65
  121. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-BUPs84MI.mjs.map +0 -1
  122. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-BotknoHn.mjs +0 -64
  123. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-BotknoHn.mjs.map +0 -1
  124. package/runtime/index.d.ts +0 -307
  125. /package/{index.d.ts → types/acorex-platform.d.ts} +0 -0
@@ -1,11 +1,14 @@
1
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 } 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 } 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';
@@ -17,9 +20,26 @@ import * as i3 from '@acorex/components/loading';
17
20
  import { AXLoadingModule } from '@acorex/components/loading';
18
21
  import { AXBasePageComponent } from '@acorex/components/page';
19
22
  import * as i6 from '@acorex/core/translation';
20
- import { AXTranslationModule } from '@acorex/core/translation';
21
- import * as i4 from '@acorex/platform/core';
22
- import { AXPExpressionEvaluatorService, AXPComponentSlotModule } from '@acorex/platform/core';
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
23
43
 
24
44
  class AXPLayoutConversionService {
25
45
  constructor() {
@@ -159,6 +179,10 @@ class AXPLayoutConversionService {
159
179
  if (!editorWidget.mode) {
160
180
  editorWidget.mode = fieldMode;
161
181
  }
182
+ const hintOpts = field.description != null &&
183
+ (typeof field.description !== 'string' || field.description.trim().length > 0)
184
+ ? { hint: field.description, hintDisplayMode: 'note' }
185
+ : {};
162
186
  return {
163
187
  type: 'form-field',
164
188
  name: field.path,
@@ -166,8 +190,8 @@ class AXPLayoutConversionService {
166
190
  options: {
167
191
  label: field.title,
168
192
  badge: field.badge,
169
- description: field.description,
170
193
  showLabel: true,
194
+ ...hintOpts,
171
195
  },
172
196
  children: [editorWidget], // The editor widget becomes a child of form-field
173
197
  };
@@ -210,7 +234,7 @@ class AXPLayoutConversionService {
210
234
  path: formFieldNode.name || editorWidget.name || `field-${Date.now()}`,
211
235
  title: formFieldNode.options?.['label'],
212
236
  badge: formFieldNode.options?.['badge'],
213
- description: formFieldNode.options?.['description'],
237
+ description: formFieldNode.options?.['hint'],
214
238
  widget: editorWidget,
215
239
  mode: formFieldNode.mode,
216
240
  };
@@ -223,9 +247,16 @@ class AXPLayoutConversionService {
223
247
  const keyParts = [];
224
248
  keyParts.push(`groups:${formDefinition.groups.length}`);
225
249
  formDefinition.groups.forEach((group, groupIndex) => {
226
- keyParts.push(`g${groupIndex}:${group.name}:${group.parameters.length}`);
250
+ // Include group.mode so view vs edit (or mixed) layouts do not share a cached widget tree.
251
+ const groupModePart = group.mode ?? '_';
252
+ keyParts.push(`g${groupIndex}:${group.name}:${group.parameters.length}:${groupModePart}`);
253
+ keyParts.push(`gL${groupIndex}:${JSON.stringify(group.title ?? null)}:${JSON.stringify(group.description ?? null)}`);
227
254
  group.parameters.forEach((param, paramIndex) => {
228
- keyParts.push(`p${groupIndex}.${paramIndex}:${param.path}:${param.widget.type}`);
255
+ // Field mode must be part of the key; otherwise metadata forms that only differ by
256
+ // view/edit (same paths and widget types) incorrectly reuse the first cached tree.
257
+ const fieldModePart = param.mode ?? '_';
258
+ keyParts.push(`p${groupIndex}.${paramIndex}:${param.path}:${param.widget.type}:${fieldModePart}`);
259
+ keyParts.push(`pL${groupIndex}.${paramIndex}:${JSON.stringify(param.title ?? null)}:${JSON.stringify(param.description ?? null)}:${JSON.stringify(param.badge ?? null)}`);
229
260
  });
230
261
  });
231
262
  if (formDefinition.mode) {
@@ -286,10 +317,10 @@ class AXPLayoutConversionService {
286
317
  }
287
318
  return Math.abs(hash).toString(36); // Convert to base36 for shorter string
288
319
  }
289
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AXPLayoutConversionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
290
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AXPLayoutConversionService, providedIn: 'root' }); }
320
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutConversionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
321
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutConversionService, providedIn: 'root' }); }
291
322
  }
292
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AXPLayoutConversionService, decorators: [{
323
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutConversionService, decorators: [{
293
324
  type: Injectable,
294
325
  args: [{
295
326
  providedIn: 'root',
@@ -399,17 +430,18 @@ function getNestedValue(obj, path) {
399
430
  class AXPLayoutBuilderService {
400
431
  constructor() {
401
432
  this.popupService = inject(AXPopupService);
433
+ this.hookService = inject(AXPHookService, { optional: true }) ?? undefined;
402
434
  }
403
435
  /**
404
436
  * Create a new layout builder
405
437
  */
406
438
  create() {
407
- return new LayoutBuilder(this.popupService);
439
+ return new LayoutBuilder(this.popupService, this.hookService);
408
440
  }
409
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AXPLayoutBuilderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
410
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AXPLayoutBuilderService, providedIn: 'root' }); }
441
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutBuilderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
442
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutBuilderService, providedIn: 'root' }); }
411
443
  }
412
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AXPLayoutBuilderService, decorators: [{
444
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutBuilderService, decorators: [{
413
445
  type: Injectable,
414
446
  args: [{
415
447
  providedIn: 'root',
@@ -422,8 +454,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
422
454
  * Open/Closed: Extensible through container delegates
423
455
  */
424
456
  class LayoutBuilder {
425
- constructor(popupService) {
457
+ constructor(popupService, hookService) {
426
458
  this.popupService = popupService;
459
+ this.hookService = hookService;
427
460
  this.root = {
428
461
  children: [],
429
462
  mode: 'edit',
@@ -493,7 +526,7 @@ class LayoutBuilder {
493
526
  if (!this.popupService) {
494
527
  throw new Error('LayoutBuilder requires AXPopupService to create dialogs. Please inject it in the service constructor.');
495
528
  }
496
- const container = new DialogContainerBuilder(this.popupService);
529
+ const container = new DialogContainerBuilder(this.popupService, this.hookService);
497
530
  if (delegate) {
498
531
  delegate(container);
499
532
  }
@@ -518,11 +551,21 @@ class LayoutBuilder {
518
551
  return this;
519
552
  }
520
553
  build() {
521
- return {
522
- type: this.root.type,
523
- children: this.root.children,
524
- mode: this.root.mode,
554
+ const r = this.root;
555
+ const node = {
556
+ type: r.type,
557
+ ...(r.mode !== undefined ? { mode: r.mode } : {}),
558
+ ...(r.children !== undefined ? { children: r.children } : {}),
559
+ ...(r.options !== undefined ? { options: r.options } : {}),
560
+ ...(r.name !== undefined ? { name: r.name } : {}),
561
+ ...(r.path !== undefined ? { path: r.path } : {}),
562
+ ...(r.visible !== undefined ? { visible: r.visible } : {}),
563
+ ...(r.defaultValue !== undefined ? { defaultValue: r.defaultValue } : {}),
564
+ ...(r.triggers !== undefined ? { triggers: r.triggers } : {}),
565
+ ...(r.meta !== undefined ? { meta: r.meta } : {}),
566
+ ...(r.valueTransforms !== undefined ? { valueTransforms: r.valueTransforms } : {}),
525
567
  };
568
+ return node;
526
569
  }
527
570
  /**
528
571
  * Converts the built widget node to JSON string
@@ -591,6 +634,7 @@ class BaseContainerBuilder {
591
634
  'number-editor',
592
635
  'select-editor',
593
636
  'lookup-editor',
637
+ 'entity-definition-provider-editor',
594
638
  'selection-list-editor',
595
639
  'date-time-editor',
596
640
  'toggle-editor',
@@ -693,32 +737,30 @@ class BaseContainerMixin extends BaseContainerBuilder {
693
737
  class LayoutContainerMixin extends BaseContainerMixin {
694
738
  layout(value) {
695
739
  // Map layout intent to grid item sizing so containers like `form-field`
696
- // can span multiple columns inside grid/fieldset layouts.
740
+ // can span multiple columns/rows inside grid/fieldset layouts.
697
741
  if (!this.containerState.options)
698
742
  this.containerState.options = {};
699
743
  if (typeof value === 'number') {
700
- // Direct numeric shorthand → colSpan
701
744
  this.containerState.options.colSpan = value;
702
745
  }
703
746
  else if (value) {
704
- // Try to extract grid positioning from breakpoint positions
705
747
  const positions = value.positions;
706
748
  if (positions) {
707
- // Resolve the best breakpoint (prefer lg, then fallback to others)
708
- const bp = positions?.lg ?? positions?.xl ?? positions?.xxl ?? positions?.md ?? positions?.sm;
709
- if (bp) {
710
- if (bp.colSpan != null)
711
- this.containerState.options.colSpan = bp.colSpan;
712
- if (bp.colStart != null)
713
- this.containerState.options.colStart = bp.colStart;
714
- if (bp.colEnd != null)
715
- this.containerState.options.colEnd = bp.colEnd;
716
- if (bp.rowSpan != null)
717
- this.containerState.options.rowSpan = bp.rowSpan;
718
- if (bp.rowStart != null)
719
- this.containerState.options.rowStart = bp.rowStart;
720
- if (bp.rowEnd != null)
721
- this.containerState.options.rowEnd = bp.rowEnd;
749
+ const placement = positions?.lg ?? positions?.xl ?? positions?.xxl ?? positions?.md ?? positions?.sm;
750
+ if (placement) {
751
+ const opts = this.containerState.options;
752
+ if (placement.colSpan != null)
753
+ opts.colSpan = placement.colSpan;
754
+ if (placement.colStart != null)
755
+ opts.colStart = placement.colStart;
756
+ if (placement.colEnd != null)
757
+ opts.colEnd = placement.colEnd;
758
+ if (placement.rowSpan != null)
759
+ opts.rowSpan = placement.rowSpan;
760
+ if (placement.rowStart != null)
761
+ opts.rowStart = placement.rowStart;
762
+ if (placement.rowEnd != null)
763
+ opts.rowEnd = placement.rowEnd;
722
764
  }
723
765
  }
724
766
  }
@@ -937,6 +979,7 @@ class WidgetContainerMixin extends ChildContainerMixin {
937
979
  'number-editor',
938
980
  'select-editor',
939
981
  'lookup-editor',
982
+ 'entity-definition-provider-editor',
940
983
  'selection-list-editor',
941
984
  'date-time-editor',
942
985
  'toggle-editor',
@@ -992,12 +1035,73 @@ class FlexContainerBuilder extends WidgetContainerMixin {
992
1035
  * Grid Container Builder - Liskov Substitution Principle
993
1036
  * Extends WidgetContainerMixin to inherit all common functionality
994
1037
  */
1038
+ /**
1039
+ * Extracts flat grid-item options from AXPGridLayoutOptions for grid-item-layout widget.
1040
+ * Uses first available breakpoint (lg, xl, md, sm).
1041
+ */
1042
+ /**
1043
+ * Deep-merges grid breakpoint buckets so sequential fluent calls (e.g. setColumns then setGap)
1044
+ * do not wipe sibling keys under options.grid.default.
1045
+ */
1046
+ function mergeAXPGridContainerOptions(prev, patch) {
1047
+ const next = { ...(prev ?? {}), ...patch };
1048
+ if (prev?.grid?.default || patch.grid?.default) {
1049
+ next.grid = {
1050
+ ...(prev?.grid ?? {}),
1051
+ ...(patch.grid ?? {}),
1052
+ default: {
1053
+ ...(prev?.grid?.default ?? {}),
1054
+ ...(patch.grid?.default ?? {}),
1055
+ },
1056
+ };
1057
+ }
1058
+ return next;
1059
+ }
1060
+ function toGridItemOptions(layoutOptions) {
1061
+ if (!layoutOptions?.positions)
1062
+ return { colSpan: 12 };
1063
+ const positions = layoutOptions.positions;
1064
+ const placement = positions['lg'] ?? positions['xl'] ?? positions['xxl'] ?? positions['md'] ?? positions['sm'];
1065
+ if (!placement)
1066
+ return { colSpan: 12 };
1067
+ const opts = {};
1068
+ if (placement['colSpan'] != null)
1069
+ opts['colSpan'] = placement['colSpan'];
1070
+ if (placement['colStart'] != null)
1071
+ opts['colStart'] = placement['colStart'];
1072
+ if (placement['colEnd'] != null)
1073
+ opts['colEnd'] = placement['colEnd'];
1074
+ if (placement['rowSpan'] != null)
1075
+ opts['rowSpan'] = placement['rowSpan'];
1076
+ if (placement['rowStart'] != null)
1077
+ opts['rowStart'] = placement['rowStart'];
1078
+ if (placement['rowEnd'] != null)
1079
+ opts['rowEnd'] = placement['rowEnd'];
1080
+ if (Object.keys(opts).length === 0)
1081
+ opts['colSpan'] = 12;
1082
+ return opts;
1083
+ }
995
1084
  class GridContainerBuilder extends WidgetContainerMixin {
996
1085
  constructor() {
997
1086
  super('grid-layout');
998
1087
  }
999
1088
  setOptions(options) {
1000
- this.containerState.options = { ...this.containerState.options, ...options };
1089
+ this.containerState.options = mergeAXPGridContainerOptions(this.containerState.options, options);
1090
+ return this;
1091
+ }
1092
+ item(layoutOptions, delegate) {
1093
+ const fieldset = new FieldsetContainerBuilder();
1094
+ fieldset.withInheritanceContext(this.inheritanceContext);
1095
+ delegate(fieldset);
1096
+ const fieldsetNode = fieldset.build();
1097
+ const gridItemOptions = toGridItemOptions(layoutOptions);
1098
+ const gridItemNode = {
1099
+ type: 'grid-item-layout',
1100
+ options: gridItemOptions,
1101
+ children: [fieldsetNode],
1102
+ };
1103
+ this.ensureChildren();
1104
+ this.containerState.children.push(gridItemNode);
1001
1105
  return this;
1002
1106
  }
1003
1107
  // Individual fluent methods for Grid
@@ -1428,7 +1532,7 @@ class ListWidgetBuilder extends WidgetContainerMixin {
1428
1532
  * Uses composition instead of inheritance for cleaner separation
1429
1533
  */
1430
1534
  class DialogContainerBuilder {
1431
- constructor(popupService) {
1535
+ constructor(popupService, hookService) {
1432
1536
  this.dialogState = {
1433
1537
  type: 'flex-layout', // This will be overridden when content layout exists
1434
1538
  children: [],
@@ -1451,6 +1555,7 @@ class DialogContainerBuilder {
1451
1555
  else {
1452
1556
  this.popupService = inject(AXPopupService);
1453
1557
  }
1558
+ this.hookService = hookService ?? inject(AXPHookService, { optional: true }) ?? undefined;
1454
1559
  }
1455
1560
  setOptions(options) {
1456
1561
  this.dialogState.dialogOptions = { ...this.dialogState.dialogOptions, ...options };
@@ -1491,7 +1596,12 @@ class DialogContainerBuilder {
1491
1596
  return this;
1492
1597
  }
1493
1598
  onAction(handler) {
1494
- (this.dialogState.dialogOptions ??= {}).onAction = handler;
1599
+ this.dialogState.dialogOptions ??= {
1600
+ title: '',
1601
+ size: 'md',
1602
+ closeButton: false,
1603
+ };
1604
+ this.dialogState.dialogOptions.onAction = handler;
1495
1605
  return this;
1496
1606
  }
1497
1607
  addCustomAction(action) {
@@ -1540,21 +1650,40 @@ class DialogContainerBuilder {
1540
1650
  const dialogNode = this.build();
1541
1651
  // Import the dialog renderer component dynamically
1542
1652
  const { AXPDialogRendererComponent } = await Promise.resolve().then(function () { return dialogRenderer_component; });
1543
- // Collect default values from widget tree and merge into initial context
1544
- const initialContext = this.dialogState.dialogOptions?.context || {};
1545
- //TODO remove using collectDefaultValues and use initialContext directly for now:
1546
- const contextWithDefaults = collectDefaultValues(dialogNode, initialContext);
1653
+ this.dialogState.dialogOptions ??= {};
1654
+ if (this.dialogState.dialogOptions.context == null || typeof this.dialogState.dialogOptions.context !== 'object') {
1655
+ this.dialogState.dialogOptions.context = {};
1656
+ }
1657
+ const initialContext = this.dialogState.dialogOptions.context;
1658
+ this.dialogState.actions ??= { footer: { prefix: [], suffix: [] } };
1659
+ this.dialogState.actions.footer ??= { prefix: [], suffix: [] };
1660
+ this.dialogState.actions.footer.prefix ??= [];
1661
+ this.dialogState.actions.footer.suffix ??= [];
1662
+ const hookService = this.hookService;
1663
+ if (hookService) {
1664
+ const beforePayload = {
1665
+ context: initialContext,
1666
+ dialogOptions: this.dialogState.dialogOptions,
1667
+ };
1668
+ await hookService.runAsync(AXP_LAYOUT_BUILDER_DIALOG_BEFORE_OPEN_HOOK_KEY, beforePayload);
1669
+ }
1547
1670
  // Create dialog configuration
1548
1671
  const dialogConfig = {
1549
1672
  title: this.dialogState.dialogOptions?.title || '',
1550
1673
  message: this.dialogState.dialogOptions?.message,
1551
1674
  context: initialContext,
1552
1675
  definition: dialogNode,
1676
+ metadata: this.dialogState.dialogOptions.metadata,
1553
1677
  actions: this.dialogState.actions,
1554
1678
  onAction: this.dialogState.dialogOptions?.onAction,
1555
1679
  };
1680
+ //
1681
+ if (hookService) {
1682
+ await hookService.runAsync(AXP_LAYOUT_BUILDER_DIALOG_CONFIG_HOOK_KEY, dialogConfig);
1683
+ }
1556
1684
  // The Promise resolves when user clicks an action button
1557
1685
  return new Promise(async (resolve) => {
1686
+ let flag = false;
1558
1687
  this.popupService.open(AXPDialogRendererComponent, {
1559
1688
  title: dialogConfig.title,
1560
1689
  size: this.dialogState.dialogOptions?.size || 'md',
@@ -1564,10 +1693,14 @@ class DialogContainerBuilder {
1564
1693
  data: {
1565
1694
  config: dialogConfig,
1566
1695
  callBack: (result) => {
1696
+ flag = true;
1567
1697
  resolve(result);
1568
1698
  },
1569
1699
  },
1570
1700
  });
1701
+ if (!flag) {
1702
+ resolve({ success: false });
1703
+ }
1571
1704
  });
1572
1705
  }
1573
1706
  }
@@ -1769,10 +1902,19 @@ class ActionBuilder {
1769
1902
  return this;
1770
1903
  }
1771
1904
  custom(action) {
1772
- if (!this.dialogBuilder['dialogState'].actions.footer.suffix) {
1773
- this.dialogBuilder['dialogState'].actions.footer.suffix = [];
1905
+ const position = action.position ?? 'suffix';
1906
+ if (position === 'prefix') {
1907
+ if (!this.dialogBuilder['dialogState'].actions.footer.prefix) {
1908
+ this.dialogBuilder['dialogState'].actions.footer.prefix = [];
1909
+ }
1910
+ this.dialogBuilder['dialogState'].actions.footer.prefix.push(action);
1911
+ }
1912
+ else {
1913
+ if (!this.dialogBuilder['dialogState'].actions.footer.suffix) {
1914
+ this.dialogBuilder['dialogState'].actions.footer.suffix = [];
1915
+ }
1916
+ this.dialogBuilder['dialogState'].actions.footer.suffix.push(action);
1774
1917
  }
1775
- this.dialogBuilder['dialogState'].actions.footer.suffix.push(action);
1776
1918
  return this;
1777
1919
  }
1778
1920
  }
@@ -1921,22 +2063,27 @@ class AXPLayoutRendererComponent {
1921
2063
  /**
1922
2064
  * Form definition containing groups and fields OR widget tree
1923
2065
  */
1924
- this.layout = input.required(...(ngDevMode ? [{ debugName: "layout" }] : []));
2066
+ this.layout = input.required(...(ngDevMode ? [{ debugName: "layout" }] : /* istanbul ignore next */ []));
1925
2067
  /**
1926
2068
  * Form context/model data
1927
2069
  */
1928
- this.context = model({}, ...(ngDevMode ? [{ debugName: "context" }] : []));
2070
+ this.context = model({}, ...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
1929
2071
  /**
1930
2072
  * Form appearance and density styling (normal, compact, spacious)
1931
2073
  */
1932
- this.look = input('fieldset', ...(ngDevMode ? [{ debugName: "look" }] : []));
2074
+ this.look = input('fieldset', ...(ngDevMode ? [{ debugName: "look" }] : /* istanbul ignore next */ []));
1933
2075
  /**
1934
2076
  * Default form mode. Can be overridden by section/group and field.
1935
2077
  */
1936
- this.mode = input('edit', ...(ngDevMode ? [{ debugName: "mode" }] : []));
2078
+ this.mode = input('edit', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
1937
2079
  //#endregion
1938
2080
  //#region ---- Widget Tree Conversion ----
1939
- this.widgetTree = signal(null, ...(ngDevMode ? [{ debugName: "widgetTree" }] : []));
2081
+ this.widgetTree = signal(null, ...(ngDevMode ? [{ debugName: "widgetTree" }] : /* istanbul ignore next */ []));
2082
+ /**
2083
+ * Prefer explicit {@link AXPWidgetNode.mode} on the root node (e.g. dialog flex `mode('view')`)
2084
+ * so nested widgets resolve view vs edit correctly; fall back to the layout `mode` input.
2085
+ */
2086
+ this.effectiveRenderMode = computed(() => this.widgetTree()?.mode ?? this.mode(), ...(ngDevMode ? [{ debugName: "effectiveRenderMode" }] : /* istanbul ignore next */ []));
1940
2087
  /**
1941
2088
  * Convert layout data to widget tree when inputs change
1942
2089
  */
@@ -1961,7 +2108,7 @@ class AXPLayoutRendererComponent {
1961
2108
  if (!isEqual(prev, tree)) {
1962
2109
  this.widgetTree.set(tree);
1963
2110
  }
1964
- }, ...(ngDevMode ? [{ debugName: "conversionEffect" }] : []));
2111
+ }, ...(ngDevMode ? [{ debugName: "conversionEffect" }] : /* istanbul ignore next */ []));
1965
2112
  //#endregion
1966
2113
  //#region ---- Outputs ----
1967
2114
  /**
@@ -1974,12 +2121,12 @@ class AXPLayoutRendererComponent {
1974
2121
  this.validityChange = output();
1975
2122
  //#endregion
1976
2123
  //#region ---- Properties ----
1977
- this.form = viewChild(AXFormComponent, ...(ngDevMode ? [{ debugName: "form" }] : []));
1978
- this.container = viewChild(AXPWidgetContainerComponent, ...(ngDevMode ? [{ debugName: "container" }] : []));
2124
+ this.form = viewChild(AXFormComponent, ...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
2125
+ this.container = viewChild(AXPWidgetContainerComponent, ...(ngDevMode ? [{ debugName: "container" }] : /* istanbul ignore next */ []));
1979
2126
  /**
1980
2127
  * Internal context signal for reactivity
1981
2128
  */
1982
- this.internalContext = signal({}, ...(ngDevMode ? [{ debugName: "internalContext" }] : []));
2129
+ this.internalContext = signal({}, ...(ngDevMode ? [{ debugName: "internalContext" }] : /* istanbul ignore next */ []));
1983
2130
  /**
1984
2131
  * Initial context for reset functionality
1985
2132
  */
@@ -1992,7 +2139,7 @@ class AXPLayoutRendererComponent {
1992
2139
  this.#contextSyncEffect = effect(() => {
1993
2140
  const ctx = this.context() ?? {};
1994
2141
  this.contextUpdateSubject.next(ctx);
1995
- }, ...(ngDevMode ? [{ debugName: "#contextSyncEffect" }] : []));
2142
+ }, ...(ngDevMode ? [{ debugName: "#contextSyncEffect" }] : /* istanbul ignore next */ []));
1996
2143
  /**
1997
2144
  * Effect to handle widget tree status changes
1998
2145
  */
@@ -2001,7 +2148,7 @@ class AXPLayoutRendererComponent {
2001
2148
  if (widgetTree) {
2002
2149
  this.container()?.builderService.setStatus(AXPPageStatus.Rendered);
2003
2150
  }
2004
- }, ...(ngDevMode ? [{ debugName: "#widgetStatusEffect" }] : []));
2151
+ }, ...(ngDevMode ? [{ debugName: "#widgetStatusEffect" }] : /* istanbul ignore next */ []));
2005
2152
  }
2006
2153
  //#endregion
2007
2154
  //#region ---- Lifecycle Methods ----
@@ -2141,40 +2288,67 @@ class AXPLayoutRendererComponent {
2141
2288
  isWidgetNode(data) {
2142
2289
  return data && typeof data === 'object' && 'type' in data && typeof data.type === 'string';
2143
2290
  }
2144
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AXPLayoutRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2145
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", 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: `
2291
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2292
+ 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: `
2146
2293
  <ax-form>
2147
2294
  <axp-widgets-container [context]="internalContext()" (onContextChanged)="handleContextChanged($event)">
2148
2295
  @if (widgetTree()) {
2149
- <ng-container axp-widget-renderer [node]="widgetTree()!" [mode]="mode()"></ng-container>
2296
+ <ng-container
2297
+ axp-widget-renderer
2298
+ [node]="widgetTree()!"
2299
+ [mode]="effectiveRenderMode()"
2300
+ ></ng-container>
2150
2301
  }
2151
2302
  </axp-widgets-container>
2152
2303
  </ax-form>
2153
- `, 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", "inUserInteractionActive"], outputs: ["onValidate", "updateOnChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2304
+ `, 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 }); }
2154
2305
  }
2155
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AXPLayoutRendererComponent, decorators: [{
2306
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutRendererComponent, decorators: [{
2156
2307
  type: Component,
2157
- args: [{ selector: 'axp-layout-renderer', standalone: true, imports: [CommonModule, AXPWidgetCoreModule, AXFormModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
2308
+ args: [{ selector: 'axp-layout-renderer', standalone: true, imports: [AXPWidgetCoreModule, AXFormModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
2158
2309
  <ax-form>
2159
2310
  <axp-widgets-container [context]="internalContext()" (onContextChanged)="handleContextChanged($event)">
2160
2311
  @if (widgetTree()) {
2161
- <ng-container axp-widget-renderer [node]="widgetTree()!" [mode]="mode()"></ng-container>
2312
+ <ng-container
2313
+ axp-widget-renderer
2314
+ [node]="widgetTree()!"
2315
+ [mode]="effectiveRenderMode()"
2316
+ ></ng-container>
2162
2317
  }
2163
2318
  </axp-widgets-container>
2164
2319
  </ax-form>
2165
2320
  `, styles: [":host{display:block;width:100%}\n"] }]
2166
2321
  }], 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 }] }] } });
2167
2322
 
2323
+ /** Registration key for {@link AXPPreviewWidgetFieldCommand}; lives alone so `LayoutBuilderModule` can reference it without static-importing the command implementation. */
2324
+ const AXP_PREVIEW_WIDGET_FIELD_COMMAND_KEY = 'Widget:Preview';
2325
+
2168
2326
  class LayoutBuilderModule {
2169
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LayoutBuilderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
2170
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.19", ngImport: i0, type: LayoutBuilderModule, imports: [CommonModule, AXPLayoutRendererComponent], exports: [AXPLayoutRendererComponent] }); }
2171
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LayoutBuilderModule, providers: [AXPLayoutBuilderService], imports: [CommonModule, AXPLayoutRendererComponent] }); }
2327
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LayoutBuilderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
2328
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: LayoutBuilderModule, imports: [CommonModule, AXPLayoutRendererComponent], exports: [AXPLayoutRendererComponent] }); }
2329
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LayoutBuilderModule, providers: [
2330
+ AXPLayoutBuilderService,
2331
+ provideCommandSetups([
2332
+ {
2333
+ key: AXP_PREVIEW_WIDGET_FIELD_COMMAND_KEY,
2334
+ command: () => Promise.resolve().then(function () { return previewWidgetField_command; }).then((c) => c.AXPPreviewWidgetFieldCommand),
2335
+ },
2336
+ ]),
2337
+ ], imports: [CommonModule, AXPLayoutRendererComponent] }); }
2172
2338
  }
2173
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LayoutBuilderModule, decorators: [{
2339
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LayoutBuilderModule, decorators: [{
2174
2340
  type: NgModule,
2175
2341
  args: [{
2176
2342
  imports: [CommonModule, AXPLayoutRendererComponent],
2177
- providers: [AXPLayoutBuilderService],
2343
+ providers: [
2344
+ AXPLayoutBuilderService,
2345
+ provideCommandSetups([
2346
+ {
2347
+ key: AXP_PREVIEW_WIDGET_FIELD_COMMAND_KEY,
2348
+ command: () => Promise.resolve().then(function () { return previewWidgetField_command; }).then((c) => c.AXPPreviewWidgetFieldCommand),
2349
+ },
2350
+ ]),
2351
+ ],
2178
2352
  exports: [AXPLayoutRendererComponent],
2179
2353
  }]
2180
2354
  }] });
@@ -2186,17 +2360,17 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2186
2360
  super(...arguments);
2187
2361
  this.result = new EventEmitter();
2188
2362
  this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
2189
- this.context = signal({}, ...(ngDevMode ? [{ debugName: "context" }] : []));
2363
+ this.context = signal({}, ...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
2190
2364
  // This will be set by the popup service automatically - same as dynamic-dialog
2191
2365
  this.callBack = () => { };
2192
- this.isDialogLoading = signal(false, ...(ngDevMode ? [{ debugName: "isDialogLoading" }] : []));
2366
+ this.isDialogLoading = signal(false, ...(ngDevMode ? [{ debugName: "isDialogLoading" }] : /* istanbul ignore next */ []));
2193
2367
  // Aggregated actions for footer rendering
2194
- this.footerPrefix = signal([], ...(ngDevMode ? [{ debugName: "footerPrefix" }] : []));
2195
- this.footerSuffix = signal([], ...(ngDevMode ? [{ debugName: "footerSuffix" }] : []));
2368
+ this.footerPrefix = signal([], ...(ngDevMode ? [{ debugName: "footerPrefix" }] : /* istanbul ignore next */ []));
2369
+ this.footerSuffix = signal([], ...(ngDevMode ? [{ debugName: "footerSuffix" }] : /* istanbul ignore next */ []));
2196
2370
  //#endregion
2197
2371
  //#region ---- View Accessors ----
2198
2372
  // Access the internal layout renderer to reach the widgets container injector
2199
- this.layoutRenderer = viewChild(AXPLayoutRendererComponent, ...(ngDevMode ? [{ debugName: "layoutRenderer" }] : []));
2373
+ this.layoutRenderer = viewChild(AXPLayoutRendererComponent, ...(ngDevMode ? [{ debugName: "layoutRenderer" }] : /* istanbul ignore next */ []));
2200
2374
  this.#eff = effect(() => {
2201
2375
  let count = 0;
2202
2376
  this.aggregateAndEvaluateActions();
@@ -2217,7 +2391,7 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2217
2391
  this.aggregateAndEvaluateActions();
2218
2392
  }, 200);
2219
2393
  }
2220
- }, ...(ngDevMode ? [{ debugName: "#eff" }] : []));
2394
+ }, ...(ngDevMode ? [{ debugName: "#eff" }] : /* istanbul ignore next */ []));
2221
2395
  }
2222
2396
  //#endregion
2223
2397
  //#region ---- Lifecycle ----
@@ -2247,17 +2421,17 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2247
2421
  return this.isDialogLoading();
2248
2422
  }
2249
2423
  async executeAction(action) {
2250
- const cmd = action.command;
2251
- if (cmd !== 'cancel') {
2424
+ const cmd = this.resolveActionCommandName(action.command);
2425
+ if (this.shouldValidateBeforeAction(cmd)) {
2252
2426
  const isValid = await this.layoutRenderer()?.validate();
2253
2427
  if (!isValid?.result) {
2254
2428
  return;
2255
2429
  }
2256
2430
  }
2257
- if (typeof cmd === 'string' && cmd.startsWith('widget:')) {
2431
+ if (cmd?.startsWith('widget:')) {
2258
2432
  const parsed = this.parseWidgetCommand(cmd);
2259
2433
  if (parsed.widgetName && parsed.action) {
2260
- await this.executeWidgetApi(parsed.widgetName, parsed.action);
2434
+ await this.invokeWidget(parsed.widgetName, parsed.action, {});
2261
2435
  await this.aggregateAndEvaluateActions();
2262
2436
  return;
2263
2437
  }
@@ -2265,17 +2439,15 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2265
2439
  const context = this.context();
2266
2440
  const onAction = this.config?.onAction;
2267
2441
  if (onAction) {
2268
- const dialogRef = {
2269
- close: (res) => this.close(res),
2270
- context: () => this.context(),
2271
- action: () => cmd ?? undefined,
2272
- setLoading: (loading) => this.isDialogLoading.set(loading),
2273
- };
2442
+ const dialogRef = this.createDialogRef(cmd);
2274
2443
  try {
2275
2444
  this.isDialogLoading.set(true);
2276
2445
  const result = await Promise.resolve(onAction(dialogRef));
2446
+ if (result && typeof result === 'object' && result.keepDialogOpen) {
2447
+ return;
2448
+ }
2277
2449
  this.callBack(result);
2278
- this.close(result);
2450
+ await this.closeWithOptionalSkipValidate(result);
2279
2451
  }
2280
2452
  catch {
2281
2453
  // Handler threw: stay open for retry, actions remain clickable
@@ -2293,15 +2465,57 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2293
2465
  this.data.action = result.action;
2294
2466
  }
2295
2467
  this.callBack({
2468
+ ...this.createDialogRef(cmd),
2469
+ action: () => result.action,
2470
+ });
2471
+ // Without `onAction`, only the configured cancel action dismisses the dialog (not submit/custom).
2472
+ if (cmd === 'cancel') {
2473
+ await this.close(result);
2474
+ }
2475
+ }
2476
+ /** Whether the layout form should be validated before running this footer command. */
2477
+ shouldValidateBeforeAction(cmd) {
2478
+ if (!cmd || cmd === 'cancel' || cmd === 'entity-form-done') {
2479
+ return false;
2480
+ }
2481
+ if (cmd.startsWith('widget:')) {
2482
+ return false;
2483
+ }
2484
+ return true;
2485
+ }
2486
+ createDialogRef(actionCmd) {
2487
+ return {
2296
2488
  close: (res) => {
2297
- this.close(res);
2489
+ void this.closeWithOptionalSkipValidate(res);
2298
2490
  },
2299
2491
  context: () => this.context(),
2300
- action: () => result.action,
2301
- setLoading: (loading) => {
2302
- this.isDialogLoading.set(loading);
2492
+ action: () => actionCmd,
2493
+ setLoading: (loading) => this.isDialogLoading.set(loading),
2494
+ patchContext: (partial) => {
2495
+ const merged = merge({}, this.context(), partial);
2496
+ this.context.set(merged);
2497
+ this.layoutRenderer()?.updateContext(merged);
2303
2498
  },
2304
- });
2499
+ invokeWidget: (widgetName, method, opts) => this.invokeWidget(widgetName, method, opts ?? {}),
2500
+ };
2501
+ }
2502
+ async closeWithOptionalSkipValidate(result) {
2503
+ if (result && typeof result === 'object' && result.skipValidate) {
2504
+ this.result.emit(result);
2505
+ await super.close(result);
2506
+ return;
2507
+ }
2508
+ await this.close(result);
2509
+ }
2510
+ /** Resolves footer/widget action command to a string (e.g. `cancel`, `submit`, `widget:...`). */
2511
+ resolveActionCommandName(command) {
2512
+ if (typeof command === 'string') {
2513
+ return command;
2514
+ }
2515
+ if (command && typeof command === 'object' && 'name' in command) {
2516
+ return command.name;
2517
+ }
2518
+ return undefined;
2305
2519
  }
2306
2520
  parseWidgetCommand(cmd) {
2307
2521
  // Expected 'widget:<widgetName>.<action>'
@@ -2313,7 +2527,7 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2313
2527
  return {};
2314
2528
  return { widgetName: rest.slice(0, dot), action: rest.slice(dot + 1) };
2315
2529
  }
2316
- async executeWidgetApi(widgetName, apiMethod) {
2530
+ async invokeWidget(widgetName, apiMethod, opts) {
2317
2531
  if (!this.widgetCoreService)
2318
2532
  return;
2319
2533
  try {
@@ -2323,16 +2537,20 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2323
2537
  if (typeof fn === 'function') {
2324
2538
  await Promise.resolve(fn({
2325
2539
  close: (result) => {
2326
- this.close(result);
2540
+ void this.closeWithOptionalSkipValidate(result);
2327
2541
  },
2328
2542
  context: () => this.context(),
2329
2543
  setLoading: (loading) => {
2330
- this.isDialogLoading.set(loading);
2544
+ (opts.setLoading ?? ((v) => this.isDialogLoading.set(v)))(loading);
2331
2545
  },
2332
2546
  }));
2547
+ // Footer predicates (e.g. wizard step) must refresh when the widget advances outside executeAction (e.g. dialogRef.invokeWidget after entity-form continue).
2548
+ await this.aggregateAndEvaluateActions();
2333
2549
  }
2334
2550
  }
2335
- catch { }
2551
+ catch {
2552
+ //
2553
+ }
2336
2554
  }
2337
2555
  async close(result) {
2338
2556
  if (result) {
@@ -2382,6 +2600,7 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2382
2600
  zone: 'footer',
2383
2601
  placement,
2384
2602
  scope: a.scope,
2603
+ predicateApiWidgetName: a.predicateApiWidgetName,
2385
2604
  });
2386
2605
  const prefix = (footer?.prefix || []).map((a) => mapOne(a, 'prefix'));
2387
2606
  const suffix = (footer?.suffix || []).map((a) => mapOne(a, 'suffix'));
@@ -2391,16 +2610,18 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2391
2610
  const out = [];
2392
2611
  for (const a of actions) {
2393
2612
  const parsed = typeof a.command === 'string' ? this.parseWidgetCommand(a.command) : {};
2394
- const api = parsed.widgetName ? await this.resolveApi(parsed.widgetName) : undefined;
2613
+ const widgetNameForApi = parsed.widgetName ?? a.predicateApiWidgetName;
2614
+ const api = widgetNameForApi ? await this.resolveApi(widgetNameForApi) : undefined;
2395
2615
  const scope = {
2396
2616
  api,
2397
- widget: { name: parsed.widgetName },
2617
+ widget: { name: widgetNameForApi },
2398
2618
  dialog: { context: this.context() },
2399
2619
  context: this.context(),
2400
2620
  };
2401
2621
  const disabled = await this.evalBool(a.disabled, scope);
2402
2622
  const hidden = await this.evalBool(a.hidden, scope);
2403
- out.push({ ...a, disabled, hidden });
2623
+ const resolvedTitle = (await this.evalActionTitle(a.title, scope)) ?? a.title;
2624
+ out.push({ ...a, disabled, hidden, title: resolvedTitle });
2404
2625
  }
2405
2626
  return out;
2406
2627
  }
@@ -2418,6 +2639,25 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2418
2639
  }
2419
2640
  return value;
2420
2641
  }
2642
+ /** Resolves footer action title when it contains {{ ... }} (e.g. wizard labels from dialog context). */
2643
+ async evalActionTitle(value, scope) {
2644
+ if (value == null || typeof value !== 'string' || !value.includes('{{')) {
2645
+ return value;
2646
+ }
2647
+ try {
2648
+ const result = await this.expressionEvaluator.evaluate(value, scope);
2649
+ if (typeof result === 'string' && result.length > 0) {
2650
+ return result;
2651
+ }
2652
+ if (result != null && result !== false) {
2653
+ return String(result);
2654
+ }
2655
+ }
2656
+ catch {
2657
+ //
2658
+ }
2659
+ return value;
2660
+ }
2421
2661
  async resolveApi(widgetName) {
2422
2662
  try {
2423
2663
  await this.widgetCoreService?.waitForWidget(widgetName, 2000);
@@ -2428,10 +2668,13 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2428
2668
  return undefined;
2429
2669
  }
2430
2670
  }
2431
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AXPDialogRendererComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
2432
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.19", type: AXPDialogRendererComponent, isStandalone: true, selector: "axp-dialog-renderer", outputs: { result: "result" }, viewQueries: [{ propertyName: "layoutRenderer", first: true, predicate: AXPLayoutRendererComponent, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
2671
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDialogRendererComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
2672
+ 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: `
2433
2673
  <axp-component-slot name="dialog-header" [context]="context()"></axp-component-slot>
2434
- <div class="ax-p-4">
2674
+ <div class="p-4">
2675
+ @if (config.message) {
2676
+ <p class="mb-4 leading-relaxed">{{ config.message | translate | async }}</p>
2677
+ }
2435
2678
  <axp-layout-renderer
2436
2679
  [layout]="config.definition"
2437
2680
  [context]="context()"
@@ -2484,9 +2727,9 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2484
2727
  </ax-suffix>
2485
2728
  </ax-footer>
2486
2729
  }
2487
- `, 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" }] }); }
2730
+ `, 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 }); }
2488
2731
  }
2489
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AXPDialogRendererComponent, decorators: [{
2732
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDialogRendererComponent, decorators: [{
2490
2733
  type: Component,
2491
2734
  args: [{
2492
2735
  selector: 'axp-dialog-renderer',
@@ -2500,9 +2743,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
2500
2743
  AXTranslationModule,
2501
2744
  AXPComponentSlotModule,
2502
2745
  ],
2746
+ providers: [AXPContextStore],
2747
+ changeDetection: ChangeDetectionStrategy.OnPush,
2503
2748
  template: `
2504
2749
  <axp-component-slot name="dialog-header" [context]="context()"></axp-component-slot>
2505
- <div class="ax-p-4">
2750
+ <div class="p-4">
2751
+ @if (config.message) {
2752
+ <p class="mb-4 leading-relaxed">{{ config.message | translate | async }}</p>
2753
+ }
2506
2754
  <axp-layout-renderer
2507
2755
  [layout]="config.definition"
2508
2756
  [context]="context()"
@@ -2566,9 +2814,190 @@ var dialogRenderer_component = /*#__PURE__*/Object.freeze({
2566
2814
  AXPDialogRendererComponent: AXPDialogRendererComponent
2567
2815
  });
2568
2816
 
2817
+ //#region ---- Imports ----
2818
+ /**
2819
+ * `customWidget` only forwards keys from its options bag into the built node via `addSingleWidget`.
2820
+ * Designer / configurator persist `defaultValue` (and other extended fields) on the widget node root;
2821
+ * spreading `options` alone drops them, so preview never applied defaults.
2822
+ */
2823
+ /**
2824
+ * Widget options are sometimes persisted with an extra nesting (`options.options`) when context
2825
+ * was merged incorrectly. Flatten so list/data-source resolution sees `dataSource` at the top level.
2826
+ */
2827
+ function optionsBagForPreview(node) {
2828
+ const raw = (node.options ?? {});
2829
+ const inner = raw['options'];
2830
+ if (inner !== undefined && typeof inner === 'object' && !Array.isArray(inner)) {
2831
+ const { options: _nested, ...rest } = raw;
2832
+ return { ...rest, ...inner };
2833
+ }
2834
+ return { ...raw };
2835
+ }
2836
+ function extendedNodePropsForPreview(node) {
2837
+ const out = {};
2838
+ if (node.defaultValue !== undefined) {
2839
+ out['defaultValue'] = node.defaultValue;
2840
+ }
2841
+ if (node.triggers !== undefined) {
2842
+ out['triggers'] = node.triggers;
2843
+ }
2844
+ if (node.meta !== undefined) {
2845
+ out['meta'] = node.meta;
2846
+ }
2847
+ if (node.valueTransforms !== undefined) {
2848
+ out['valueTransforms'] = node.valueTransforms;
2849
+ }
2850
+ if (node.visible !== undefined) {
2851
+ out['visible'] = node.visible;
2852
+ }
2853
+ if (node.mode !== undefined) {
2854
+ out['mode'] = node.mode;
2855
+ }
2856
+ if (node.children !== undefined) {
2857
+ out['children'] = node.children;
2858
+ }
2859
+ return out;
2860
+ }
2861
+ //#endregion
2862
+ //#region ---- Command ----
2863
+ /**
2864
+ * Opens a dialog that previews a widget configuration (same behavior as the preview button on
2865
+ * `axp-widget-field-configurator`). Invoked from that component and from entity list actions.
2866
+ */
2867
+ class AXPPreviewWidgetFieldCommand {
2868
+ constructor() {
2869
+ this.formBuilderService = inject(AXPLayoutBuilderService);
2870
+ this.widgetRegistry = inject(AXPWidgetRegistryService);
2871
+ this.translationService = inject(AXTranslationService);
2872
+ this.crudService = inject(AXP_ENTITY_DEFINITION_CRUD_SERVICE, { optional: true });
2873
+ }
2874
+ async execute(input) {
2875
+ try {
2876
+ const merged = this.mergeInvocation(input);
2877
+ const currentWidget = this.normalizeWidget(merged['widget'] ?? merged['interface']);
2878
+ if (!currentWidget?.type) {
2879
+ return {
2880
+ success: false,
2881
+ message: {
2882
+ text: (await this.translationService.translateAsync('@general:messages.invalid-data')) || 'Invalid data',
2883
+ },
2884
+ };
2885
+ }
2886
+ const fieldName = String(merged['fieldName'] ?? merged['name'] ?? 'Field');
2887
+ const rawTitle = (merged['fieldTitle'] ?? merged['title']);
2888
+ const fieldTitleLabel = this.resolveFieldTitleLabel(rawTitle, fieldName);
2889
+ const dialogTitle = (await this.resolveWidgetDisplayTitle(currentWidget.type)) || currentWidget.type || fieldTitleLabel;
2890
+ const previewWidgetOptions = {
2891
+ ...optionsBagForPreview(currentWidget),
2892
+ name: fieldName,
2893
+ ...extendedNodePropsForPreview(currentWidget),
2894
+ };
2895
+ const dialogOutcome = await this.formBuilderService
2896
+ .create()
2897
+ .dialog((dialog) => {
2898
+ dialog
2899
+ .setTitle(dialogTitle)
2900
+ .setSize('md')
2901
+ .setCloseButton(true)
2902
+ .setContext({})
2903
+ .content((layoutBuilder) => {
2904
+ layoutBuilder.formField(fieldTitleLabel, (formField) => {
2905
+ formField.customWidget(currentWidget.type, previewWidgetOptions);
2906
+ });
2907
+ })
2908
+ .setActions((actions) => actions.cancel('@general:actions.close.title'));
2909
+ })
2910
+ .show();
2911
+ const cancelled = this.isCancelDialogOutcome(dialogOutcome);
2912
+ return {
2913
+ success: !cancelled,
2914
+ message: { text: '' },
2915
+ };
2916
+ }
2917
+ catch (error) {
2918
+ const message = error instanceof Error ? error.message : 'Unknown error';
2919
+ return {
2920
+ success: false,
2921
+ message: { text: message },
2922
+ };
2923
+ }
2924
+ }
2925
+ mergeInvocation(input) {
2926
+ const contextOptions = input.__context__?.options;
2927
+ const ctxData = input.__context__?.data;
2928
+ const { __context__: _ctx, ...rest } = input;
2929
+ return {
2930
+ ...(ctxData ?? {}),
2931
+ ...(contextOptions ?? {}),
2932
+ ...rest,
2933
+ };
2934
+ }
2935
+ normalizeWidget(raw) {
2936
+ if (raw == null)
2937
+ return null;
2938
+ if (typeof raw === 'string') {
2939
+ const t = raw.trim();
2940
+ return t ? { type: t, options: {} } : null;
2941
+ }
2942
+ if (typeof raw === 'object' && !Array.isArray(raw) && 'type' in raw) {
2943
+ const w = raw;
2944
+ return w.type ? cloneDeep(w) : null;
2945
+ }
2946
+ return null;
2947
+ }
2948
+ resolveFieldTitleLabel(raw, fallback) {
2949
+ let source = fallback;
2950
+ if (raw !== undefined && raw !== null) {
2951
+ if (typeof raw === 'string') {
2952
+ if (raw.trim() !== '') {
2953
+ source = raw;
2954
+ }
2955
+ }
2956
+ else if (typeof raw === 'object' && !Array.isArray(raw) && Object.keys(raw).length > 0) {
2957
+ source = raw;
2958
+ }
2959
+ }
2960
+ return this.translationService.resolve(source);
2961
+ }
2962
+ isCancelDialogOutcome(outcome) {
2963
+ if (outcome == null) {
2964
+ return false;
2965
+ }
2966
+ const ref = outcome;
2967
+ if (typeof ref.action !== 'function') {
2968
+ return false;
2969
+ }
2970
+ return ref.action() === 'cancel';
2971
+ }
2972
+ async resolveWidgetDisplayTitle(widgetType) {
2973
+ const crud = this.crudService;
2974
+ if (crud) {
2975
+ const interfaces = await crud.listInterfaces();
2976
+ const iface = interfaces.find((d) => d.name === widgetType);
2977
+ return iface?.title ?? iface?.name;
2978
+ }
2979
+ const config = this.widgetRegistry.getOptional(widgetType);
2980
+ if (!config) {
2981
+ return undefined;
2982
+ }
2983
+ const resolved = this.translationService.resolve(config.title);
2984
+ return resolved || undefined;
2985
+ }
2986
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPreviewWidgetFieldCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2987
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPreviewWidgetFieldCommand }); }
2988
+ }
2989
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPPreviewWidgetFieldCommand, decorators: [{
2990
+ type: Injectable
2991
+ }] });
2992
+
2993
+ var previewWidgetField_command = /*#__PURE__*/Object.freeze({
2994
+ __proto__: null,
2995
+ AXPPreviewWidgetFieldCommand: AXPPreviewWidgetFieldCommand
2996
+ });
2997
+
2569
2998
  /**
2570
2999
  * Generated bundle index. Do not edit.
2571
3000
  */
2572
3001
 
2573
- export { AXPDialogRendererComponent, AXPLayoutBuilderService, AXPLayoutConversionService, AXPLayoutRendererComponent, LayoutBuilderModule };
3002
+ export { AXPDialogRendererComponent, AXPLayoutBuilderService, AXPLayoutConversionService, AXPLayoutRendererComponent, AXPPreviewWidgetFieldCommand, AXP_LAYOUT_BUILDER_DIALOG_BEFORE_OPEN_HOOK_KEY, AXP_LAYOUT_BUILDER_DIALOG_CONFIG_HOOK_KEY, AXP_PREVIEW_WIDGET_FIELD_COMMAND_KEY, LayoutBuilderModule };
2574
3003
  //# sourceMappingURL=acorex-platform-layout-builder.mjs.map