@acorex/platform 20.8.9 → 20.8.11

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 (123) hide show
  1. package/fesm2022/acorex-platform-auth.mjs +125 -27
  2. package/fesm2022/acorex-platform-auth.mjs.map +1 -1
  3. package/fesm2022/{acorex-platform-common-common-settings.provider-DVvuLUfF.mjs → acorex-platform-common-common-settings.provider-Bi1RYif5.mjs} +30 -32
  4. package/fesm2022/acorex-platform-common-common-settings.provider-Bi1RYif5.mjs.map +1 -0
  5. package/fesm2022/acorex-platform-common.mjs +711 -205
  6. package/fesm2022/acorex-platform-common.mjs.map +1 -1
  7. package/fesm2022/acorex-platform-core.mjs +420 -127
  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 +634 -114
  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-CXEdvDTf.mjs} +9 -9
  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 +3365 -880
  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 +18667 -14048
  20. package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
  21. package/fesm2022/acorex-platform-layout-views.mjs +413 -171
  22. package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
  23. package/fesm2022/acorex-platform-layout-widget-core.mjs +507 -441
  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-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-Dtv6U3df.mjs → acorex-platform-layout-widgets-file-list-popup.component-CDYAGBku.mjs} +21 -76
  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-QxJfswhK.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-CVdssZBD.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-DVaZN9QN.mjs → acorex-platform-layout-widgets-tabular-data-edit-popup.component-DmzNTYiS.mjs} +5 -5
  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-DPGHgXa6.mjs → acorex-platform-layout-widgets-tabular-data-view-popup.component-BNG_588B.mjs} +4 -4
  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-CdiNW691.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 +6578 -4314
  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-rGsMVAZj.mjs → acorex-platform-themes-default-entity-master-single-view.component-BfCeUU5F.mjs} +16 -23
  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-53VB-PS_.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-DVF9soT5.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 +1784 -61
  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-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-BHcKkIx0.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 +408 -305
  72. package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
  73. package/fesm2022/acorex-platform-workflow.mjs +359 -100
  74. package/fesm2022/acorex-platform-workflow.mjs.map +1 -1
  75. package/fesm2022/acorex-platform.mjs.map +1 -1
  76. package/package.json +31 -31
  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} +302 -30
  79. package/{core/index.d.ts → types/acorex-platform-core.d.ts} +188 -44
  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} +160 -38
  82. package/{layout/components/index.d.ts → types/acorex-platform-layout-components.d.ts} +854 -125
  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} +848 -63
  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} +608 -122
  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} +113 -4
  91. package/{themes/shared/index.d.ts → types/acorex-platform-themes-shared.d.ts} +5 -4
  92. package/{workflow/index.d.ts → types/acorex-platform-workflow.d.ts} +162 -81
  93. package/fesm2022/acorex-platform-common-common-settings.provider-DVvuLUfF.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-D2CtNrSn.mjs +0 -1716
  107. package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-D2CtNrSn.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.map +0 -1
  112. package/fesm2022/acorex-platform-themes-default-error-404.component-DVF9soT5.mjs.map +0 -1
  113. package/fesm2022/acorex-platform-themes-default-error-offline.component-CwNNHzZn.mjs +0 -19
  114. package/fesm2022/acorex-platform-themes-default-error-offline.component-CwNNHzZn.mjs.map +0 -1
  115. package/fesm2022/acorex-platform-themes-shared-icon-chooser-column.component-TJ9PWHMY.mjs.map +0 -1
  116. package/fesm2022/acorex-platform-themes-shared-icon-chooser-view.component-BHcKkIx0.mjs.map +0 -1
  117. package/fesm2022/acorex-platform-themes-shared-settings.provider-DSs1o1M6.mjs.map +0 -1
  118. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-BUPs84MI.mjs +0 -65
  119. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-BUPs84MI.mjs.map +0 -1
  120. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-BotknoHn.mjs +0 -64
  121. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-BotknoHn.mjs.map +0 -1
  122. package/runtime/index.d.ts +0 -307
  123. /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, AXPCommandService } from '@acorex/platform/runtime';
5
6
  import { AXPopupService } from '@acorex/components/popup';
7
+ import * as i4 from '@acorex/platform/core';
8
+ import { AXPHookService, AXPExpressionEvaluatorService, AXPComponentSlotModule, AXPContextStore } from '@acorex/platform/core';
6
9
  import * as i1 from '@acorex/platform/layout/widget-core';
7
- import { AXPWidgetSerializationHelper, AXPWidgetContainerComponent, AXPPageStatus, AXPWidgetCoreModule } from '@acorex/platform/layout/widget-core';
8
- import { cloneDeep, isNil, set, isEqual } from 'lodash-es';
10
+ import { AXPWidgetSerializationHelper, AXPWidgetContainerComponent, AXPPageStatus, AXPWidgetCoreModule, AXPWidgetRegistryService } from '@acorex/platform/layout/widget-core';
11
+ import { cloneDeep, isNil, set, isEqual, merge } from 'lodash-es';
9
12
  import * as i2 from '@acorex/components/form';
10
13
  import { AXFormComponent, AXFormModule } from '@acorex/components/form';
11
14
  import { Subject, debounceTime, distinctUntilChanged, startWith } from 'rxjs';
@@ -17,9 +20,35 @@ 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
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
23
52
 
24
53
  class AXPLayoutConversionService {
25
54
  constructor() {
@@ -159,6 +188,10 @@ class AXPLayoutConversionService {
159
188
  if (!editorWidget.mode) {
160
189
  editorWidget.mode = fieldMode;
161
190
  }
191
+ const hintOpts = field.description != null &&
192
+ (typeof field.description !== 'string' || field.description.trim().length > 0)
193
+ ? { hint: field.description, hintDisplayMode: 'note' }
194
+ : {};
162
195
  return {
163
196
  type: 'form-field',
164
197
  name: field.path,
@@ -166,8 +199,8 @@ class AXPLayoutConversionService {
166
199
  options: {
167
200
  label: field.title,
168
201
  badge: field.badge,
169
- description: field.description,
170
202
  showLabel: true,
203
+ ...hintOpts,
171
204
  },
172
205
  children: [editorWidget], // The editor widget becomes a child of form-field
173
206
  };
@@ -210,7 +243,7 @@ class AXPLayoutConversionService {
210
243
  path: formFieldNode.name || editorWidget.name || `field-${Date.now()}`,
211
244
  title: formFieldNode.options?.['label'],
212
245
  badge: formFieldNode.options?.['badge'],
213
- description: formFieldNode.options?.['description'],
246
+ description: formFieldNode.options?.['hint'],
214
247
  widget: editorWidget,
215
248
  mode: formFieldNode.mode,
216
249
  };
@@ -223,9 +256,16 @@ class AXPLayoutConversionService {
223
256
  const keyParts = [];
224
257
  keyParts.push(`groups:${formDefinition.groups.length}`);
225
258
  formDefinition.groups.forEach((group, groupIndex) => {
226
- 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)}`);
227
263
  group.parameters.forEach((param, paramIndex) => {
228
- 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)}`);
229
269
  });
230
270
  });
231
271
  if (formDefinition.mode) {
@@ -286,10 +326,10 @@ class AXPLayoutConversionService {
286
326
  }
287
327
  return Math.abs(hash).toString(36); // Convert to base36 for shorter string
288
328
  }
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' }); }
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' }); }
291
331
  }
292
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AXPLayoutConversionService, decorators: [{
332
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutConversionService, decorators: [{
293
333
  type: Injectable,
294
334
  args: [{
295
335
  providedIn: 'root',
@@ -399,17 +439,18 @@ function getNestedValue(obj, path) {
399
439
  class AXPLayoutBuilderService {
400
440
  constructor() {
401
441
  this.popupService = inject(AXPopupService);
442
+ this.hookService = inject(AXPHookService, { optional: true }) ?? undefined;
402
443
  }
403
444
  /**
404
445
  * Create a new layout builder
405
446
  */
406
447
  create() {
407
- return new LayoutBuilder(this.popupService);
448
+ return new LayoutBuilder(this.popupService, this.hookService);
408
449
  }
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' }); }
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' }); }
411
452
  }
412
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AXPLayoutBuilderService, decorators: [{
453
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutBuilderService, decorators: [{
413
454
  type: Injectable,
414
455
  args: [{
415
456
  providedIn: 'root',
@@ -422,8 +463,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
422
463
  * Open/Closed: Extensible through container delegates
423
464
  */
424
465
  class LayoutBuilder {
425
- constructor(popupService) {
466
+ constructor(popupService, hookService) {
426
467
  this.popupService = popupService;
468
+ this.hookService = hookService;
427
469
  this.root = {
428
470
  children: [],
429
471
  mode: 'edit',
@@ -493,7 +535,7 @@ class LayoutBuilder {
493
535
  if (!this.popupService) {
494
536
  throw new Error('LayoutBuilder requires AXPopupService to create dialogs. Please inject it in the service constructor.');
495
537
  }
496
- const container = new DialogContainerBuilder(this.popupService);
538
+ const container = new DialogContainerBuilder(this.popupService, this.hookService);
497
539
  if (delegate) {
498
540
  delegate(container);
499
541
  }
@@ -518,11 +560,21 @@ class LayoutBuilder {
518
560
  return this;
519
561
  }
520
562
  build() {
521
- return {
522
- type: this.root.type,
523
- children: this.root.children,
524
- 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 } : {}),
525
576
  };
577
+ return node;
526
578
  }
527
579
  /**
528
580
  * Converts the built widget node to JSON string
@@ -591,6 +643,7 @@ class BaseContainerBuilder {
591
643
  'number-editor',
592
644
  'select-editor',
593
645
  'lookup-editor',
646
+ 'entity-definition-provider-editor',
594
647
  'selection-list-editor',
595
648
  'date-time-editor',
596
649
  'toggle-editor',
@@ -693,32 +746,30 @@ class BaseContainerMixin extends BaseContainerBuilder {
693
746
  class LayoutContainerMixin extends BaseContainerMixin {
694
747
  layout(value) {
695
748
  // Map layout intent to grid item sizing so containers like `form-field`
696
- // can span multiple columns inside grid/fieldset layouts.
749
+ // can span multiple columns/rows inside grid/fieldset layouts.
697
750
  if (!this.containerState.options)
698
751
  this.containerState.options = {};
699
752
  if (typeof value === 'number') {
700
- // Direct numeric shorthand → colSpan
701
753
  this.containerState.options.colSpan = value;
702
754
  }
703
755
  else if (value) {
704
- // Try to extract grid positioning from breakpoint positions
705
756
  const positions = value.positions;
706
757
  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;
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;
722
773
  }
723
774
  }
724
775
  }
@@ -937,6 +988,7 @@ class WidgetContainerMixin extends ChildContainerMixin {
937
988
  'number-editor',
938
989
  'select-editor',
939
990
  'lookup-editor',
991
+ 'entity-definition-provider-editor',
940
992
  'selection-list-editor',
941
993
  'date-time-editor',
942
994
  'toggle-editor',
@@ -992,12 +1044,73 @@ class FlexContainerBuilder extends WidgetContainerMixin {
992
1044
  * Grid Container Builder - Liskov Substitution Principle
993
1045
  * Extends WidgetContainerMixin to inherit all common functionality
994
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
+ }
995
1093
  class GridContainerBuilder extends WidgetContainerMixin {
996
1094
  constructor() {
997
1095
  super('grid-layout');
998
1096
  }
999
1097
  setOptions(options) {
1000
- 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);
1001
1114
  return this;
1002
1115
  }
1003
1116
  // Individual fluent methods for Grid
@@ -1428,7 +1541,7 @@ class ListWidgetBuilder extends WidgetContainerMixin {
1428
1541
  * Uses composition instead of inheritance for cleaner separation
1429
1542
  */
1430
1543
  class DialogContainerBuilder {
1431
- constructor(popupService) {
1544
+ constructor(popupService, hookService) {
1432
1545
  this.dialogState = {
1433
1546
  type: 'flex-layout', // This will be overridden when content layout exists
1434
1547
  children: [],
@@ -1451,6 +1564,7 @@ class DialogContainerBuilder {
1451
1564
  else {
1452
1565
  this.popupService = inject(AXPopupService);
1453
1566
  }
1567
+ this.hookService = hookService ?? inject(AXPHookService, { optional: true }) ?? undefined;
1454
1568
  }
1455
1569
  setOptions(options) {
1456
1570
  this.dialogState.dialogOptions = { ...this.dialogState.dialogOptions, ...options };
@@ -1491,7 +1605,12 @@ class DialogContainerBuilder {
1491
1605
  return this;
1492
1606
  }
1493
1607
  onAction(handler) {
1494
- (this.dialogState.dialogOptions ??= {}).onAction = handler;
1608
+ this.dialogState.dialogOptions ??= {
1609
+ title: '',
1610
+ size: 'md',
1611
+ closeButton: false,
1612
+ };
1613
+ this.dialogState.dialogOptions.onAction = handler;
1495
1614
  return this;
1496
1615
  }
1497
1616
  addCustomAction(action) {
@@ -1540,22 +1659,42 @@ class DialogContainerBuilder {
1540
1659
  const dialogNode = this.build();
1541
1660
  // Import the dialog renderer component dynamically
1542
1661
  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);
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
+ }
1547
1679
  // Create dialog configuration
1548
1680
  const dialogConfig = {
1549
1681
  title: this.dialogState.dialogOptions?.title || '',
1550
- message: this.dialogState.dialogOptions?.message,
1682
+ //TODO: why we need message?
1683
+ //message: this.dialogState.dialogOptions?.message,
1551
1684
  context: initialContext,
1552
1685
  definition: dialogNode,
1686
+ metadata: this.dialogState.dialogOptions.metadata,
1553
1687
  actions: this.dialogState.actions,
1554
1688
  onAction: this.dialogState.dialogOptions?.onAction,
1555
1689
  };
1690
+ //
1691
+ if (hookService) {
1692
+ await hookService.runAsync(AXP_LAYOUT_BUILDER_DIALOG_CONFIG_HOOK_KEY, dialogConfig);
1693
+ }
1556
1694
  // The Promise resolves when user clicks an action button
1557
1695
  return new Promise(async (resolve) => {
1558
- this.popupService.open(AXPDialogRendererComponent, {
1696
+ let flag = false;
1697
+ await this.popupService.open(AXPDialogRendererComponent, {
1559
1698
  title: dialogConfig.title,
1560
1699
  size: this.dialogState.dialogOptions?.size || 'md',
1561
1700
  closeButton: this.dialogState.dialogOptions?.closeButton || false,
@@ -1564,10 +1703,14 @@ class DialogContainerBuilder {
1564
1703
  data: {
1565
1704
  config: dialogConfig,
1566
1705
  callBack: (result) => {
1706
+ flag = true;
1567
1707
  resolve(result);
1568
1708
  },
1569
1709
  },
1570
1710
  });
1711
+ if (!flag) {
1712
+ resolve({ success: false });
1713
+ }
1571
1714
  });
1572
1715
  }
1573
1716
  }
@@ -1769,10 +1912,19 @@ class ActionBuilder {
1769
1912
  return this;
1770
1913
  }
1771
1914
  custom(action) {
1772
- if (!this.dialogBuilder['dialogState'].actions.footer.suffix) {
1773
- 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);
1774
1927
  }
1775
- this.dialogBuilder['dialogState'].actions.footer.suffix.push(action);
1776
1928
  return this;
1777
1929
  }
1778
1930
  }
@@ -1921,22 +2073,27 @@ class AXPLayoutRendererComponent {
1921
2073
  /**
1922
2074
  * Form definition containing groups and fields OR widget tree
1923
2075
  */
1924
- this.layout = input.required(...(ngDevMode ? [{ debugName: "layout" }] : []));
2076
+ this.layout = input.required(...(ngDevMode ? [{ debugName: "layout" }] : /* istanbul ignore next */ []));
1925
2077
  /**
1926
2078
  * Form context/model data
1927
2079
  */
1928
- this.context = model({}, ...(ngDevMode ? [{ debugName: "context" }] : []));
2080
+ this.context = model({}, ...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
1929
2081
  /**
1930
2082
  * Form appearance and density styling (normal, compact, spacious)
1931
2083
  */
1932
- this.look = input('fieldset', ...(ngDevMode ? [{ debugName: "look" }] : []));
2084
+ this.look = input('fieldset', ...(ngDevMode ? [{ debugName: "look" }] : /* istanbul ignore next */ []));
1933
2085
  /**
1934
2086
  * Default form mode. Can be overridden by section/group and field.
1935
2087
  */
1936
- this.mode = input('edit', ...(ngDevMode ? [{ debugName: "mode" }] : []));
2088
+ this.mode = input('edit', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
1937
2089
  //#endregion
1938
2090
  //#region ---- Widget Tree Conversion ----
1939
- 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 */ []));
1940
2097
  /**
1941
2098
  * Convert layout data to widget tree when inputs change
1942
2099
  */
@@ -1961,7 +2118,7 @@ class AXPLayoutRendererComponent {
1961
2118
  if (!isEqual(prev, tree)) {
1962
2119
  this.widgetTree.set(tree);
1963
2120
  }
1964
- }, ...(ngDevMode ? [{ debugName: "conversionEffect" }] : []));
2121
+ }, ...(ngDevMode ? [{ debugName: "conversionEffect" }] : /* istanbul ignore next */ []));
1965
2122
  //#endregion
1966
2123
  //#region ---- Outputs ----
1967
2124
  /**
@@ -1974,12 +2131,12 @@ class AXPLayoutRendererComponent {
1974
2131
  this.validityChange = output();
1975
2132
  //#endregion
1976
2133
  //#region ---- Properties ----
1977
- this.form = viewChild(AXFormComponent, ...(ngDevMode ? [{ debugName: "form" }] : []));
1978
- 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 */ []));
1979
2136
  /**
1980
2137
  * Internal context signal for reactivity
1981
2138
  */
1982
- this.internalContext = signal({}, ...(ngDevMode ? [{ debugName: "internalContext" }] : []));
2139
+ this.internalContext = signal({}, ...(ngDevMode ? [{ debugName: "internalContext" }] : /* istanbul ignore next */ []));
1983
2140
  /**
1984
2141
  * Initial context for reset functionality
1985
2142
  */
@@ -1992,7 +2149,7 @@ class AXPLayoutRendererComponent {
1992
2149
  this.#contextSyncEffect = effect(() => {
1993
2150
  const ctx = this.context() ?? {};
1994
2151
  this.contextUpdateSubject.next(ctx);
1995
- }, ...(ngDevMode ? [{ debugName: "#contextSyncEffect" }] : []));
2152
+ }, ...(ngDevMode ? [{ debugName: "#contextSyncEffect" }] : /* istanbul ignore next */ []));
1996
2153
  /**
1997
2154
  * Effect to handle widget tree status changes
1998
2155
  */
@@ -2001,7 +2158,7 @@ class AXPLayoutRendererComponent {
2001
2158
  if (widgetTree) {
2002
2159
  this.container()?.builderService.setStatus(AXPPageStatus.Rendered);
2003
2160
  }
2004
- }, ...(ngDevMode ? [{ debugName: "#widgetStatusEffect" }] : []));
2161
+ }, ...(ngDevMode ? [{ debugName: "#widgetStatusEffect" }] : /* istanbul ignore next */ []));
2005
2162
  }
2006
2163
  //#endregion
2007
2164
  //#region ---- Lifecycle Methods ----
@@ -2141,40 +2298,67 @@ class AXPLayoutRendererComponent {
2141
2298
  isWidgetNode(data) {
2142
2299
  return data && typeof data === 'object' && 'type' in data && typeof data.type === 'string';
2143
2300
  }
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: `
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: `
2146
2303
  <ax-form>
2147
2304
  <axp-widgets-container [context]="internalContext()" (onContextChanged)="handleContextChanged($event)">
2148
2305
  @if (widgetTree()) {
2149
- <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>
2150
2311
  }
2151
2312
  </axp-widgets-container>
2152
2313
  </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 }); }
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 }); }
2154
2315
  }
2155
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AXPLayoutRendererComponent, decorators: [{
2316
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutRendererComponent, decorators: [{
2156
2317
  type: Component,
2157
- 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: `
2158
2319
  <ax-form>
2159
2320
  <axp-widgets-container [context]="internalContext()" (onContextChanged)="handleContextChanged($event)">
2160
2321
  @if (widgetTree()) {
2161
- <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>
2162
2327
  }
2163
2328
  </axp-widgets-container>
2164
2329
  </ax-form>
2165
2330
  `, styles: [":host{display:block;width:100%}\n"] }]
2166
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 }] }] } });
2167
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
+
2168
2336
  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] }); }
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] }); }
2172
2348
  }
2173
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: LayoutBuilderModule, decorators: [{
2349
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: LayoutBuilderModule, decorators: [{
2174
2350
  type: NgModule,
2175
2351
  args: [{
2176
2352
  imports: [CommonModule, AXPLayoutRendererComponent],
2177
- 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
+ ],
2178
2362
  exports: [AXPLayoutRendererComponent],
2179
2363
  }]
2180
2364
  }] });
@@ -2186,17 +2370,25 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2186
2370
  super(...arguments);
2187
2371
  this.result = new EventEmitter();
2188
2372
  this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
2189
- 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 */ []));
2190
2376
  // This will be set by the popup service automatically - same as dynamic-dialog
2191
2377
  this.callBack = () => { };
2192
- this.isDialogLoading = signal(false, ...(ngDevMode ? [{ debugName: "isDialogLoading" }] : []));
2378
+ this.isDialogLoading = signal(false, ...(ngDevMode ? [{ debugName: "isDialogLoading" }] : /* istanbul ignore next */ []));
2193
2379
  // Aggregated actions for footer rendering
2194
- this.footerPrefix = signal([], ...(ngDevMode ? [{ debugName: "footerPrefix" }] : []));
2195
- 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)}`;
2196
2388
  //#endregion
2197
2389
  //#region ---- View Accessors ----
2198
2390
  // Access the internal layout renderer to reach the widgets container injector
2199
- this.layoutRenderer = viewChild(AXPLayoutRendererComponent, ...(ngDevMode ? [{ debugName: "layoutRenderer" }] : []));
2391
+ this.layoutRenderer = viewChild(AXPLayoutRendererComponent, ...(ngDevMode ? [{ debugName: "layoutRenderer" }] : /* istanbul ignore next */ []));
2200
2392
  this.#eff = effect(() => {
2201
2393
  let count = 0;
2202
2394
  this.aggregateAndEvaluateActions();
@@ -2217,22 +2409,48 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2217
2409
  this.aggregateAndEvaluateActions();
2218
2410
  }, 200);
2219
2411
  }
2220
- }, ...(ngDevMode ? [{ debugName: "#eff" }] : []));
2412
+ }, ...(ngDevMode ? [{ debugName: "#eff" }] : /* istanbul ignore next */ []));
2221
2413
  }
2222
2414
  //#endregion
2223
2415
  //#region ---- Lifecycle ----
2224
2416
  ngOnInit() {
2225
2417
  this.context.set(this.config?.context || {});
2418
+ void this.invokeLayoutContextChangedHooks();
2226
2419
  }
2227
2420
  #eff;
2228
2421
  //#endregion
2229
2422
  handleContextChanged(event) {
2230
2423
  this.context.set(event);
2231
2424
  this.aggregateAndEvaluateActions();
2425
+ void this.invokeLayoutContextChangedHooks();
2232
2426
  }
2233
2427
  handleContextInitiated(event) {
2234
2428
  this.context.set(event);
2235
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
+ }
2236
2454
  }
2237
2455
  footerPrefixActions() {
2238
2456
  return this.footerPrefix();
@@ -2247,35 +2465,53 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2247
2465
  return this.isDialogLoading();
2248
2466
  }
2249
2467
  async executeAction(action) {
2250
- const cmd = action.command;
2251
- if (cmd !== 'cancel') {
2468
+ const cmd = this.resolveActionCommandName(action.command);
2469
+ if (this.shouldValidateBeforeAction(cmd)) {
2252
2470
  const isValid = await this.layoutRenderer()?.validate();
2253
2471
  if (!isValid?.result) {
2254
2472
  return;
2255
2473
  }
2256
2474
  }
2257
- if (typeof cmd === 'string' && cmd.startsWith('widget:')) {
2475
+ //TODO: matin, why we need this? maybe we can remove it?
2476
+ if (cmd?.startsWith('widget:')) {
2258
2477
  const parsed = this.parseWidgetCommand(cmd);
2259
2478
  if (parsed.widgetName && parsed.action) {
2260
- await this.executeWidgetApi(parsed.widgetName, parsed.action);
2479
+ await this.invokeWidget(parsed.widgetName, parsed.action, {});
2261
2480
  await this.aggregateAndEvaluateActions();
2262
2481
  return;
2263
2482
  }
2264
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
+ }
2265
2503
  const context = this.context();
2266
2504
  const onAction = this.config?.onAction;
2267
2505
  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
- };
2506
+ const dialogRef = this.createDialogRef(cmd);
2274
2507
  try {
2275
2508
  this.isDialogLoading.set(true);
2276
2509
  const result = await Promise.resolve(onAction(dialogRef));
2510
+ if (this.shouldKeepDialogOpenAfterCommandResult(result)) {
2511
+ return;
2512
+ }
2277
2513
  this.callBack(result);
2278
- this.close(result);
2514
+ await this.closeWithOptionalSkipValidate(result);
2279
2515
  }
2280
2516
  catch {
2281
2517
  // Handler threw: stay open for retry, actions remain clickable
@@ -2293,15 +2529,74 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2293
2529
  this.data.action = result.action;
2294
2530
  }
2295
2531
  this.callBack({
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 {
2296
2569
  close: (res) => {
2297
- this.close(res);
2570
+ void this.closeWithOptionalSkipValidate(res);
2298
2571
  },
2299
2572
  context: () => this.context(),
2300
- action: () => result.action,
2301
- setLoading: (loading) => {
2302
- 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);
2303
2579
  },
2304
- });
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;
2305
2600
  }
2306
2601
  parseWidgetCommand(cmd) {
2307
2602
  // Expected 'widget:<widgetName>.<action>'
@@ -2313,7 +2608,7 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2313
2608
  return {};
2314
2609
  return { widgetName: rest.slice(0, dot), action: rest.slice(dot + 1) };
2315
2610
  }
2316
- async executeWidgetApi(widgetName, apiMethod) {
2611
+ async invokeWidget(widgetName, apiMethod, opts) {
2317
2612
  if (!this.widgetCoreService)
2318
2613
  return;
2319
2614
  try {
@@ -2323,16 +2618,20 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2323
2618
  if (typeof fn === 'function') {
2324
2619
  await Promise.resolve(fn({
2325
2620
  close: (result) => {
2326
- this.close(result);
2621
+ void this.closeWithOptionalSkipValidate(result);
2327
2622
  },
2328
2623
  context: () => this.context(),
2329
2624
  setLoading: (loading) => {
2330
- this.isDialogLoading.set(loading);
2625
+ (opts.setLoading ?? ((v) => this.isDialogLoading.set(v)))(loading);
2331
2626
  },
2332
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();
2333
2630
  }
2334
2631
  }
2335
- catch { }
2632
+ catch {
2633
+ //
2634
+ }
2336
2635
  }
2337
2636
  async close(result) {
2338
2637
  if (result) {
@@ -2382,6 +2681,7 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2382
2681
  zone: 'footer',
2383
2682
  placement,
2384
2683
  scope: a.scope,
2684
+ predicateApiWidgetName: a.predicateApiWidgetName,
2385
2685
  });
2386
2686
  const prefix = (footer?.prefix || []).map((a) => mapOne(a, 'prefix'));
2387
2687
  const suffix = (footer?.suffix || []).map((a) => mapOne(a, 'suffix'));
@@ -2391,16 +2691,18 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2391
2691
  const out = [];
2392
2692
  for (const a of actions) {
2393
2693
  const parsed = typeof a.command === 'string' ? this.parseWidgetCommand(a.command) : {};
2394
- 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;
2395
2696
  const scope = {
2396
2697
  api,
2397
- widget: { name: parsed.widgetName },
2698
+ widget: { name: widgetNameForApi },
2398
2699
  dialog: { context: this.context() },
2399
2700
  context: this.context(),
2400
2701
  };
2401
2702
  const disabled = await this.evalBool(a.disabled, scope);
2402
2703
  const hidden = await this.evalBool(a.hidden, scope);
2403
- out.push({ ...a, disabled, hidden });
2704
+ const resolvedTitle = (await this.evalActionTitle(a.title, scope)) ?? a.title;
2705
+ out.push({ ...a, disabled, hidden, title: resolvedTitle });
2404
2706
  }
2405
2707
  return out;
2406
2708
  }
@@ -2418,6 +2720,25 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2418
2720
  }
2419
2721
  return value;
2420
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
+ }
2421
2742
  async resolveApi(widgetName) {
2422
2743
  try {
2423
2744
  await this.widgetCoreService?.waitForWidget(widgetName, 2000);
@@ -2428,10 +2749,13 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2428
2749
  return undefined;
2429
2750
  }
2430
2751
  }
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: `
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: `
2433
2754
  <axp-component-slot name="dialog-header" [context]="context()"></axp-component-slot>
2434
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
+ } -->
2435
2759
  <axp-layout-renderer
2436
2760
  [layout]="config.definition"
2437
2761
  [context]="context()"
@@ -2455,8 +2779,13 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2455
2779
  [color]="action.color"
2456
2780
  (onClick)="executeAction(action)"
2457
2781
  >
2782
+ @if (isFormLoading()) {
2783
+ <ax-loading></ax-loading>
2784
+ }
2458
2785
  <ax-prefix>
2459
- <i class="{{ action.icon }}"></i>
2786
+ @if (action.icon) {
2787
+ <ax-icon [icon]="action.icon"></ax-icon>
2788
+ }
2460
2789
  </ax-prefix>
2461
2790
  </ax-button>
2462
2791
  }
@@ -2475,7 +2804,7 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2475
2804
  }
2476
2805
  @if (action.icon) {
2477
2806
  <ax-prefix>
2478
- <ax-icon icon="{{ action.icon }}"></ax-icon>
2807
+ <ax-icon [icon]="action.icon"></ax-icon>
2479
2808
  </ax-prefix>
2480
2809
  }
2481
2810
  </ax-button>
@@ -2484,9 +2813,9 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
2484
2813
  </ax-suffix>
2485
2814
  </ax-footer>
2486
2815
  }
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" }] }); }
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 }); }
2488
2817
  }
2489
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: AXPDialogRendererComponent, decorators: [{
2818
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDialogRendererComponent, decorators: [{
2490
2819
  type: Component,
2491
2820
  args: [{
2492
2821
  selector: 'axp-dialog-renderer',
@@ -2500,9 +2829,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
2500
2829
  AXTranslationModule,
2501
2830
  AXPComponentSlotModule,
2502
2831
  ],
2832
+ providers: [AXPContextStore],
2833
+ changeDetection: ChangeDetectionStrategy.OnPush,
2503
2834
  template: `
2504
2835
  <axp-component-slot name="dialog-header" [context]="context()"></axp-component-slot>
2505
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
+ } -->
2506
2840
  <axp-layout-renderer
2507
2841
  [layout]="config.definition"
2508
2842
  [context]="context()"
@@ -2526,8 +2860,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
2526
2860
  [color]="action.color"
2527
2861
  (onClick)="executeAction(action)"
2528
2862
  >
2863
+ @if (isFormLoading()) {
2864
+ <ax-loading></ax-loading>
2865
+ }
2529
2866
  <ax-prefix>
2530
- <i class="{{ action.icon }}"></i>
2867
+ @if (action.icon) {
2868
+ <ax-icon [icon]="action.icon"></ax-icon>
2869
+ }
2531
2870
  </ax-prefix>
2532
2871
  </ax-button>
2533
2872
  }
@@ -2546,7 +2885,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
2546
2885
  }
2547
2886
  @if (action.icon) {
2548
2887
  <ax-prefix>
2549
- <ax-icon icon="{{ action.icon }}"></ax-icon>
2888
+ <ax-icon [icon]="action.icon"></ax-icon>
2550
2889
  </ax-prefix>
2551
2890
  }
2552
2891
  </ax-button>
@@ -2566,9 +2905,190 @@ var dialogRenderer_component = /*#__PURE__*/Object.freeze({
2566
2905
  AXPDialogRendererComponent: AXPDialogRendererComponent
2567
2906
  });
2568
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
+
2569
3089
  /**
2570
3090
  * Generated bundle index. Do not edit.
2571
3091
  */
2572
3092
 
2573
- 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 };
2574
3094
  //# sourceMappingURL=acorex-platform-layout-builder.mjs.map