@acorex/platform 20.3.0-next.2 → 20.3.0-next.21

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 (132) hide show
  1. package/common/index.d.ts +403 -374
  2. package/core/index.d.ts +576 -46
  3. package/fesm2022/acorex-platform-auth.mjs +19 -19
  4. package/fesm2022/acorex-platform-auth.mjs.map +1 -1
  5. package/fesm2022/acorex-platform-common.mjs +152 -231
  6. package/fesm2022/acorex-platform-common.mjs.map +1 -1
  7. package/fesm2022/acorex-platform-core.mjs +657 -110
  8. package/fesm2022/acorex-platform-core.mjs.map +1 -1
  9. package/fesm2022/acorex-platform-domain.mjs +16 -16
  10. package/fesm2022/acorex-platform-domain.mjs.map +1 -1
  11. package/fesm2022/acorex-platform-layout-builder.mjs +1946 -1947
  12. package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
  13. package/fesm2022/acorex-platform-layout-components.mjs +2094 -477
  14. package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
  15. package/fesm2022/acorex-platform-layout-designer.mjs +96 -89
  16. package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
  17. package/fesm2022/acorex-platform-layout-entity-create-entity.command-DyXF9zAh.mjs +52 -0
  18. package/fesm2022/acorex-platform-layout-entity-create-entity.command-DyXF9zAh.mjs.map +1 -0
  19. package/fesm2022/acorex-platform-layout-entity.mjs +1763 -1233
  20. package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
  21. package/fesm2022/acorex-platform-layout-views.mjs +43 -33
  22. package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
  23. package/fesm2022/acorex-platform-layout-widget-core.mjs +2756 -0
  24. package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -0
  25. package/fesm2022/{acorex-platform-widgets-button-widget-designer.component-C2Qn1YAW.mjs → acorex-platform-layout-widgets-button-widget-designer.component-C_3IWNkj.mjs} +6 -6
  26. package/fesm2022/acorex-platform-layout-widgets-button-widget-designer.component-C_3IWNkj.mjs.map +1 -0
  27. package/fesm2022/{acorex-platform-widgets-extra-properties-schema-widget-edit.component-D9mf08rU.mjs → acorex-platform-layout-widgets-extra-properties-schema-widget-edit.component-CJltEgut.mjs} +5 -5
  28. package/fesm2022/acorex-platform-layout-widgets-extra-properties-schema-widget-edit.component-CJltEgut.mjs.map +1 -0
  29. package/fesm2022/{acorex-platform-widgets-extra-properties-schema-widget-view.component-D6GQ-eyr.mjs → acorex-platform-layout-widgets-extra-properties-schema-widget-view.component-pM-TIuk0.mjs} +5 -5
  30. package/fesm2022/acorex-platform-layout-widgets-extra-properties-schema-widget-view.component-pM-TIuk0.mjs.map +1 -0
  31. package/fesm2022/{acorex-platform-widgets-extra-properties-values-widget-edit.component-DVbIdVZ6.mjs → acorex-platform-layout-widgets-extra-properties-values-widget-edit.component-BqI96-fU.mjs} +5 -5
  32. package/fesm2022/acorex-platform-layout-widgets-extra-properties-values-widget-edit.component-BqI96-fU.mjs.map +1 -0
  33. package/fesm2022/{acorex-platform-widgets-extra-properties-values-widget-view.component-D-aM64Hu.mjs → acorex-platform-layout-widgets-extra-properties-values-widget-view.component-C-AhenaM.mjs} +5 -5
  34. package/fesm2022/acorex-platform-layout-widgets-extra-properties-values-widget-view.component-C-AhenaM.mjs.map +1 -0
  35. package/fesm2022/{acorex-platform-widgets-extra-properties-widget-edit.component-em2-aU8E.mjs → acorex-platform-layout-widgets-extra-properties-widget-edit.component-DCAya5ne.mjs} +5 -5
  36. package/fesm2022/acorex-platform-layout-widgets-extra-properties-widget-edit.component-DCAya5ne.mjs.map +1 -0
  37. package/fesm2022/{acorex-platform-widgets-extra-properties-widget-view.component-BeuIofdr.mjs → acorex-platform-layout-widgets-extra-properties-widget-view.component-D-PnBqLb.mjs} +5 -5
  38. package/fesm2022/acorex-platform-layout-widgets-extra-properties-widget-view.component-D-PnBqLb.mjs.map +1 -0
  39. package/fesm2022/{acorex-platform-widgets-file-list-popup.component-rW2RD35f.mjs → acorex-platform-layout-widgets-file-list-popup.component-DuuFHWvB.mjs} +10 -10
  40. package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-DuuFHWvB.mjs.map +1 -0
  41. package/fesm2022/{acorex-platform-widgets-page-widget-designer.component-DNvnQ4Mc.mjs → acorex-platform-layout-widgets-page-widget-designer.component-Bss0xUcu.mjs} +8 -8
  42. package/fesm2022/acorex-platform-layout-widgets-page-widget-designer.component-Bss0xUcu.mjs.map +1 -0
  43. package/fesm2022/{acorex-platform-widgets-tabular-data-edit-popup.component-CPVRbE8B.mjs → acorex-platform-layout-widgets-tabular-data-edit-popup.component-Cy9mHnNP.mjs} +14 -14
  44. package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-Cy9mHnNP.mjs.map +1 -0
  45. package/fesm2022/{acorex-platform-widgets-tabular-data-view-popup.component-Dmg5DdX8.mjs → acorex-platform-layout-widgets-tabular-data-view-popup.component-DznLtuer.mjs} +6 -5
  46. package/fesm2022/acorex-platform-layout-widgets-tabular-data-view-popup.component-DznLtuer.mjs.map +1 -0
  47. package/fesm2022/{acorex-platform-widgets-text-block-widget-designer.component-yADN3Xji.mjs → acorex-platform-layout-widgets-text-block-widget-designer.component-ndOUSFi9.mjs} +6 -7
  48. package/fesm2022/acorex-platform-layout-widgets-text-block-widget-designer.component-ndOUSFi9.mjs.map +1 -0
  49. package/fesm2022/{acorex-platform-widgets.mjs → acorex-platform-layout-widgets.mjs} +8836 -8103
  50. package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -0
  51. package/fesm2022/acorex-platform-native.mjs +7 -7
  52. package/fesm2022/acorex-platform-native.mjs.map +1 -1
  53. package/fesm2022/acorex-platform-runtime.mjs +40 -40
  54. package/fesm2022/acorex-platform-runtime.mjs.map +1 -1
  55. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-Fnj54AxV.mjs +115 -0
  56. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-Fnj54AxV.mjs.map +1 -0
  57. package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-C60W6UnN.mjs +753 -0
  58. package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-C60W6UnN.mjs.map +1 -0
  59. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-B3NyKGIG.mjs +101 -0
  60. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-B3NyKGIG.mjs.map +1 -0
  61. package/fesm2022/{acorex-platform-themes-default-entity-master-single-view.component-BExtm1JE.mjs → acorex-platform-themes-default-entity-master-single-view.component-B8gx5cG7.mjs} +17 -17
  62. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-B8gx5cG7.mjs.map +1 -0
  63. package/fesm2022/{acorex-platform-themes-default-error-401.component-DrO1PEOH.mjs → acorex-platform-themes-default-error-401.component-CcvGfdhu.mjs} +4 -4
  64. package/fesm2022/{acorex-platform-themes-default-error-401.component-DrO1PEOH.mjs.map → acorex-platform-themes-default-error-401.component-CcvGfdhu.mjs.map} +1 -1
  65. package/fesm2022/{acorex-platform-themes-default-error-404.component-DqVq0oHX.mjs → acorex-platform-themes-default-error-404.component-4-CaEsnV.mjs} +4 -4
  66. package/fesm2022/{acorex-platform-themes-default-error-404.component-DqVq0oHX.mjs.map → acorex-platform-themes-default-error-404.component-4-CaEsnV.mjs.map} +1 -1
  67. package/fesm2022/{acorex-platform-themes-default-error-offline.component-Bt2PTL7_.mjs → acorex-platform-themes-default-error-offline.component-BNecbFEj.mjs} +4 -4
  68. package/fesm2022/{acorex-platform-themes-default-error-offline.component-Bt2PTL7_.mjs.map → acorex-platform-themes-default-error-offline.component-BNecbFEj.mjs.map} +1 -1
  69. package/fesm2022/acorex-platform-themes-default.mjs +45 -45
  70. package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
  71. package/fesm2022/{acorex-platform-themes-shared-icon-chooser-view.component-KpZWpnOJ.mjs → acorex-platform-themes-shared-icon-chooser-view.component-Dc_Txe32.mjs} +25 -15
  72. package/fesm2022/acorex-platform-themes-shared-icon-chooser-view.component-Dc_Txe32.mjs.map +1 -0
  73. package/fesm2022/{acorex-platform-themes-shared-settings.provider-CXiRmniv.mjs → acorex-platform-themes-shared-settings.provider-DY2xFnrv.mjs} +9 -9
  74. package/fesm2022/acorex-platform-themes-shared-settings.provider-DY2xFnrv.mjs.map +1 -0
  75. package/fesm2022/{acorex-platform-themes-shared-theme-color-chooser-column.component-BvOiVCgt.mjs → acorex-platform-themes-shared-theme-color-chooser-column.component-hgWLhhle.mjs} +24 -9
  76. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-hgWLhhle.mjs.map +1 -0
  77. package/fesm2022/{acorex-platform-themes-shared-theme-color-chooser-view.component-BW0rfkjk.mjs → acorex-platform-themes-shared-theme-color-chooser-view.component-CY3JZK_W.mjs} +24 -9
  78. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-CY3JZK_W.mjs.map +1 -0
  79. package/fesm2022/acorex-platform-themes-shared.mjs +264 -86
  80. package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
  81. package/fesm2022/acorex-platform-workflow.mjs +27 -39
  82. package/fesm2022/acorex-platform-workflow.mjs.map +1 -1
  83. package/layout/builder/README.md +1577 -3
  84. package/layout/builder/index.d.ts +735 -814
  85. package/layout/components/index.d.ts +443 -214
  86. package/layout/designer/index.d.ts +8 -6
  87. package/layout/entity/index.d.ts +979 -282
  88. package/layout/views/index.d.ts +13 -13
  89. package/layout/widget-core/README.md +4 -0
  90. package/layout/widget-core/index.d.ts +957 -0
  91. package/layout/widgets/README.md +4 -0
  92. package/{widgets → layout/widgets}/index.d.ts +1908 -770
  93. package/package.json +22 -18
  94. package/themes/shared/index.d.ts +2 -2
  95. package/workflow/index.d.ts +3 -173
  96. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-BXbkGGei.mjs +0 -115
  97. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-BXbkGGei.mjs.map +0 -1
  98. package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-X0hLRZhX.mjs +0 -708
  99. package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-X0hLRZhX.mjs.map +0 -1
  100. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-Bp1JLsj1.mjs +0 -101
  101. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-Bp1JLsj1.mjs.map +0 -1
  102. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-BExtm1JE.mjs.map +0 -1
  103. package/fesm2022/acorex-platform-themes-shared-icon-chooser-view.component-KpZWpnOJ.mjs.map +0 -1
  104. package/fesm2022/acorex-platform-themes-shared-settings.provider-CXiRmniv.mjs.map +0 -1
  105. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-BvOiVCgt.mjs.map +0 -1
  106. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-BW0rfkjk.mjs.map +0 -1
  107. package/fesm2022/acorex-platform-widgets-button-widget-designer.component-C2Qn1YAW.mjs.map +0 -1
  108. package/fesm2022/acorex-platform-widgets-checkbox-widget-column.component-CzEFmKWG.mjs +0 -84
  109. package/fesm2022/acorex-platform-widgets-checkbox-widget-column.component-CzEFmKWG.mjs.map +0 -1
  110. package/fesm2022/acorex-platform-widgets-checkbox-widget-designer.component-JC_nYunG.mjs +0 -55
  111. package/fesm2022/acorex-platform-widgets-checkbox-widget-designer.component-JC_nYunG.mjs.map +0 -1
  112. package/fesm2022/acorex-platform-widgets-checkbox-widget-view.component-C-4bWr9G.mjs +0 -76
  113. package/fesm2022/acorex-platform-widgets-checkbox-widget-view.component-C-4bWr9G.mjs.map +0 -1
  114. package/fesm2022/acorex-platform-widgets-color-box-widget-designer.component-CxgKO2VI.mjs +0 -55
  115. package/fesm2022/acorex-platform-widgets-color-box-widget-designer.component-CxgKO2VI.mjs.map +0 -1
  116. package/fesm2022/acorex-platform-widgets-extra-properties-schema-widget-edit.component-D9mf08rU.mjs.map +0 -1
  117. package/fesm2022/acorex-platform-widgets-extra-properties-schema-widget-view.component-D6GQ-eyr.mjs.map +0 -1
  118. package/fesm2022/acorex-platform-widgets-extra-properties-values-widget-edit.component-DVbIdVZ6.mjs.map +0 -1
  119. package/fesm2022/acorex-platform-widgets-extra-properties-values-widget-view.component-D-aM64Hu.mjs.map +0 -1
  120. package/fesm2022/acorex-platform-widgets-extra-properties-widget-edit.component-em2-aU8E.mjs.map +0 -1
  121. package/fesm2022/acorex-platform-widgets-extra-properties-widget-view.component-BeuIofdr.mjs.map +0 -1
  122. package/fesm2022/acorex-platform-widgets-file-list-popup.component-rW2RD35f.mjs.map +0 -1
  123. package/fesm2022/acorex-platform-widgets-file-rename-popup.component-DHFMnkls.mjs +0 -211
  124. package/fesm2022/acorex-platform-widgets-file-rename-popup.component-DHFMnkls.mjs.map +0 -1
  125. package/fesm2022/acorex-platform-widgets-page-widget-designer.component-DNvnQ4Mc.mjs.map +0 -1
  126. package/fesm2022/acorex-platform-widgets-rich-text-popup.component-Cydlpsat.mjs +0 -40
  127. package/fesm2022/acorex-platform-widgets-rich-text-popup.component-Cydlpsat.mjs.map +0 -1
  128. package/fesm2022/acorex-platform-widgets-tabular-data-edit-popup.component-CPVRbE8B.mjs.map +0 -1
  129. package/fesm2022/acorex-platform-widgets-tabular-data-view-popup.component-Dmg5DdX8.mjs.map +0 -1
  130. package/fesm2022/acorex-platform-widgets-text-block-widget-designer.component-yADN3Xji.mjs.map +0 -1
  131. package/fesm2022/acorex-platform-widgets.mjs.map +0 -1
  132. package/widgets/README.md +0 -4
@@ -1,2104 +1,2103 @@
1
- import * as i0 from '@angular/core';
2
- import { signal, computed, Injectable, InjectionToken, inject, ElementRef, effect, Injector, ChangeDetectorRef, ViewChild, Input, ChangeDetectionStrategy, Component, EventEmitter, Output, input, output, ViewContainerRef, Directive, Optional, Inject, NgModule } from '@angular/core';
3
- import { convertArrayToDataSource, AXDataSource } from '@acorex/cdk/common';
4
- import { setSmart, AXPDataSourceDefinitionProviderService, extractValue, getSmart, AXPExpressionEvaluatorService } from '@acorex/platform/core';
5
- import { set, cloneDeep, isEqual, get, merge, isNil, isUndefined, isObjectLike, sum, isEmpty, isString } from 'lodash-es';
6
- import { Subject, BehaviorSubject, filter } from 'rxjs';
7
- import { signalStore, withState, withComputed, withMethods, patchState } from '@ngrx/signals';
8
- import * as i1$1 from '@acorex/components/skeleton';
9
- import { AXSkeletonModule } from '@acorex/components/skeleton';
10
- import * as i2 from '@acorex/core/translation';
11
- import { AXTranslationService, AXTranslationModule } from '@acorex/core/translation';
12
- import { PortalModule } from '@angular/cdk/portal';
13
- import * as i1 from '@angular/common';
1
+ import * as i1$1 from '@angular/common';
14
2
  import { CommonModule } from '@angular/common';
15
- import { AXDataTableColumnComponent, AXBaseDataTable } from '@acorex/components/data-table';
16
- import { AXUnsubscriber } from '@acorex/core/utils';
17
-
18
- var AXPPageStatus;
19
- (function (AXPPageStatus) {
20
- // Idle statuses
21
- AXPPageStatus["Idle"] = "idle";
22
- // Rendering statuses
23
- AXPPageStatus["Rendering"] = "rendering";
24
- AXPPageStatus["Rendered"] = "rendered";
25
- // Processing statuses
26
- AXPPageStatus["Processing"] = "processing";
27
- // Submission statuses
28
- AXPPageStatus["Submitting"] = "submitting";
29
- AXPPageStatus["Submitted"] = "submitted";
30
- // Validation statuses
31
- AXPPageStatus["Validating"] = "validating";
32
- AXPPageStatus["Validated"] = "validated";
33
- // Error handling
34
- AXPPageStatus["Error"] = "error";
35
- })(AXPPageStatus || (AXPPageStatus = {}));
36
- var AXPWidgetStatus;
37
- (function (AXPWidgetStatus) {
38
- // Rendering statuses
39
- AXPWidgetStatus["Rendering"] = "rendering";
40
- AXPWidgetStatus["Rendered"] = "rendered";
41
- // Processing statuses
42
- AXPWidgetStatus["Processing"] = "processing";
43
- // Error handling
44
- AXPWidgetStatus["Error"] = "error";
45
- })(AXPWidgetStatus || (AXPWidgetStatus = {}));
3
+ import * as i0 from '@angular/core';
4
+ import { inject, Injectable, input, model, signal, effect, output, viewChild, ChangeDetectionStrategy, Component, NgModule, EventEmitter, Output, Input } from '@angular/core';
5
+ import { AXPopupService } from '@acorex/components/popup';
6
+ import * as i2 from '@acorex/components/form';
7
+ import { AXFormComponent, AXFormModule } from '@acorex/components/form';
8
+ import { AXPExpressionEvaluatorService } from '@acorex/platform/core';
9
+ import * as i1 from '@acorex/platform/layout/widget-core';
10
+ import { AXPWidgetContainerComponent, AXPPageStatus, AXPWidgetCoreModule } from '@acorex/platform/layout/widget-core';
11
+ import { isEqual, get, cloneDeep } from 'lodash-es';
12
+ import { Subject, debounceTime, distinctUntilChanged, startWith } from 'rxjs';
13
+ import * as i2$1 from '@acorex/components/button';
14
+ import { AXButtonModule } from '@acorex/components/button';
15
+ import * as i3 from '@acorex/components/decorators';
16
+ import { AXDecoratorModule } from '@acorex/components/decorators';
17
+ import * as i4 from '@acorex/components/loading';
18
+ import { AXLoadingModule } from '@acorex/components/loading';
19
+ import { AXBasePageComponent } from '@acorex/components/page';
20
+ import * as i5 from '@acorex/core/translation';
21
+ import { AXTranslationModule } from '@acorex/core/translation';
46
22
 
47
- class AXPLayoutElement {
48
- api() {
49
- return {};
23
+ //#region ---- Inheritance Utilities ----
24
+ /**
25
+ * Resolves inherited properties from context and local values
26
+ */
27
+ function resolveInheritedProperties(context, localValues = {}) {
28
+ return {
29
+ mode: localValues.mode ?? context.mode ?? 'edit',
30
+ disabled: localValues.disabled ?? context.disabled,
31
+ readonly: localValues.readonly ?? context.readonly,
32
+ direction: localValues.direction ?? context.direction,
33
+ visible: localValues.visible ?? context.visible,
34
+ };
35
+ }
36
+ /**
37
+ * Merges inheritance context with local overrides
38
+ */
39
+ function mergeInheritanceContext(parentContext, localOverrides = {}) {
40
+ return {
41
+ ...parentContext,
42
+ ...localOverrides,
43
+ };
44
+ }
45
+ /**
46
+ * Generates a random string for path/name generation
47
+ */
48
+ function generateRandomId() {
49
+ return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
50
+ }
51
+ /**
52
+ * Converts label to a valid path/name
53
+ */
54
+ function labelToPath(label) {
55
+ return label
56
+ .toLowerCase()
57
+ .replace(/[^a-z0-9\s]/g, '') // Remove special characters
58
+ .replace(/\s+/g, '_') // Replace spaces with underscores
59
+ .substring(0, 50); // Limit length
60
+ }
61
+ /**
62
+ * Generates path for value widgets based on hierarchy
63
+ */
64
+ function generateValueWidgetPath(widgetName, formFieldName, formFieldLabel) {
65
+ // Priority: widget name -> form field name -> generated from label -> random
66
+ if (widgetName) {
67
+ return widgetName;
68
+ }
69
+ if (formFieldName) {
70
+ return formFieldName;
50
71
  }
72
+ if (formFieldLabel) {
73
+ return labelToPath(formFieldLabel);
74
+ }
75
+ return generateRandomId();
51
76
  }
77
+ //#endregion
78
+ //#region ---- Service Implementation ----
52
79
  class AXPLayoutBuilderService {
53
80
  constructor() {
54
- this.variables$ = signal({}, ...(ngDevMode ? [{ debugName: "variables$" }] : []));
55
- this.functions$ = signal({}, ...(ngDevMode ? [{ debugName: "functions$" }] : []));
56
- this.onRefresh = new Subject();
57
- this.widgets = new Map();
58
- this.onWidgetRegistered = new Subject();
59
- this.status$ = signal(AXPPageStatus.Rendering, ...(ngDevMode ? [{ debugName: "status$" }] : []));
60
- this.status = this.status$.asReadonly();
61
- this.isBusy = computed(() => {
62
- return [AXPPageStatus.Processing, AXPPageStatus.Submitting, AXPPageStatus.Rendering].includes(this.status());
63
- }, ...(ngDevMode ? [{ debugName: "isBusy" }] : []));
64
- }
65
- get variables() {
66
- return this.variables$();
67
- }
68
- get functions() {
69
- return this.functions$();
70
- }
71
- updateStatus() {
72
- this.status$.update(() => this.detectStatus());
73
- }
74
- detectStatus() {
75
- const statuses = Array.from(this.widgets.values()).map((c) => c.status());
76
- // Rendering statuses
77
- if (statuses.some((status) => status === AXPWidgetStatus.Rendering)) {
78
- return AXPPageStatus.Rendering;
79
- }
80
- if (statuses.every((status) => status === AXPWidgetStatus.Rendered)) {
81
- return AXPPageStatus.Rendered;
82
- }
83
- // Processing statuses
84
- if (statuses.some((status) => status === AXPWidgetStatus.Processing)) {
85
- return AXPPageStatus.Processing;
86
- }
87
- // Error handling
88
- if (statuses.some((status) => status === AXPWidgetStatus.Error)) {
89
- return AXPPageStatus.Error;
90
- }
91
- return AXPPageStatus.Rendered; // Default to Loaded when all widgets are in a completed state
92
- }
93
- refresh() {
94
- setTimeout(() => {
95
- this.onRefresh.next();
96
- }, 0);
97
- }
98
- setStatus(status) {
99
- this.status$.set(status);
100
- }
101
- setVariables(...args) {
102
- if (args.length == 0)
103
- return;
104
- else if (args.length == 1)
105
- this.variables$.set(args[0]);
106
- else if (args.length == 2) {
107
- this.variables$.update((v) => set(v, args[0], args[1]));
108
- }
109
- }
110
- setFunctions(...args) {
111
- if (args.length == 0)
112
- return;
113
- else if (args.length == 1)
114
- this.functions$.set(args[0]);
115
- else if (args.length == 2) {
116
- this.functions$.update((v) => set(v, args[0], args[1]));
117
- }
118
- }
119
- registerWidget(id, widget) {
120
- this.widgets.set(id, widget);
121
- this.onWidgetRegistered.next({ id, widget });
122
- }
123
- getWidget(id) {
124
- return this.widgets.get(id);
81
+ this.popupService = inject(AXPopupService);
125
82
  }
126
83
  /**
127
- * Waits until a widget with the given id is registered, then resolves with it.
128
- * If the widget is already registered, resolves immediately.
129
- * Optionally accepts a timeout (in ms) after which it resolves with undefined.
84
+ * Create a new layout builder
130
85
  */
131
- async waitForWidget(id, timeoutMs) {
132
- const existing = this.widgets.get(id);
133
- if (existing) {
134
- return existing;
135
- }
136
- return new Promise((resolve) => {
137
- let resolved = false;
138
- let timer = null;
139
- const sub = this.onWidgetRegistered.subscribe(({ id: registeredId, widget }) => {
140
- if (registeredId === id && !resolved) {
141
- resolved = true;
142
- sub.unsubscribe();
143
- if (timer) {
144
- clearTimeout(timer);
145
- }
146
- resolve(widget);
147
- }
148
- });
149
- if (timeoutMs != null && timeoutMs > 0) {
150
- timer = setTimeout(() => {
151
- if (!resolved) {
152
- resolved = true;
153
- sub.unsubscribe();
154
- resolve(undefined);
155
- }
156
- }, timeoutMs);
157
- }
158
- });
86
+ create() {
87
+ return new LayoutBuilder(this.popupService);
159
88
  }
160
- ngOnDestroy() { }
161
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPLayoutBuilderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
162
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPLayoutBuilderService }); }
89
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AXPLayoutBuilderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
90
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AXPLayoutBuilderService, providedIn: 'root' }); }
163
91
  }
164
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPLayoutBuilderService, decorators: [{
165
- type: Injectable
92
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AXPLayoutBuilderService, decorators: [{
93
+ type: Injectable,
94
+ args: [{
95
+ providedIn: 'root',
96
+ }]
166
97
  }] });
167
-
168
- class AXPLayoutContextChangeEvent {
169
- }
170
- const AXPLayoutBuilderContextStore = signalStore(
171
- // Initial State
172
- withState(() => ({
173
- data: {}, // Shared context data
174
- state: 'initiated', // Current state
175
- initialSnapshot: {}, // Snapshot of the first initialized state
176
- previousSnapshot: {}, // Snapshot of the previous state
177
- lastChange: {
178
- state: 'initiated',
179
- }, // Last change event
180
- })),
181
- // Computed Signals
182
- withComputed(({ data, state, lastChange, initialSnapshot, previousSnapshot }) => ({
183
- isChanged: computed(() => state() === 'changed'),
184
- isReset: computed(() => state() === 'restored'),
185
- isInitiated: computed(() => state() === 'initiated'),
186
- isEmpty: computed(() => Object.keys(data()).length === 0),
187
- isDirty: computed(() => !isEqual(data(), previousSnapshot())),
188
- snapshot: computed(() => cloneDeep(data())), // Current data snapshot
189
- initial: computed(() => cloneDeep(initialSnapshot())), // Initial snapshot
190
- previous: computed(() => cloneDeep(previousSnapshot())), // Previous snapshot
191
- changeEvent: computed(() => lastChange()), // Reactive last change event
192
- })),
193
- // Methods for State Management
194
- withMethods((store) => ({
195
- // Update a specific value
196
- update(path, value) {
197
- const currentData = cloneDeep(store.data());
198
- const oldValue = get(currentData, path);
199
- // Skip if the value hasn't changed
200
- if (isEqual(oldValue, value)) {
201
- return;
202
- }
203
- // Update the value and prepare the change event
204
- const updatedData = setSmart(currentData, path, value);
205
- const changeEvent = {
206
- oldValue,
207
- newValue: value,
208
- path,
209
- state: 'changed',
210
- data: updatedData,
211
- };
212
- // Patch the state
213
- patchState(store, {
214
- previousSnapshot: store.snapshot(), // Save the previous state
215
- data: updatedData,
216
- state: 'changed',
217
- lastChange: changeEvent,
218
- });
219
- },
220
- patch(context) {
221
- const currentData = cloneDeep(store.data());
222
- // Update the value and prepare the change event
223
- const updatedData = { ...currentData, ...context };
224
- const changeEvent = {
225
- state: 'patch',
226
- data: updatedData,
227
- };
228
- // Patch the state
229
- patchState(store, {
230
- previousSnapshot: store.snapshot(), // Save the previous state
231
- data: updatedData,
232
- state: 'changed',
233
- lastChange: changeEvent,
234
- });
235
- },
236
- // Reset to the initial state
237
- reset() {
238
- const initialData = store.initial();
239
- const changeEvent = {
240
- oldValue: cloneDeep(store.data()), // Current data becomes old value
241
- newValue: cloneDeep(initialData), // Reset to the initial state
242
- path: '',
243
- state: 'restored',
244
- data: initialData,
98
+ //#endregion
99
+ //#region ---- Layout Builder Implementation ----
100
+ /**
101
+ * Main layout builder - Single Responsibility: Layout orchestration
102
+ * Open/Closed: Extensible through container delegates
103
+ */
104
+ class LayoutBuilder {
105
+ constructor(popupService) {
106
+ this.popupService = popupService;
107
+ this.root = {
108
+ children: [],
109
+ mode: 'edit',
110
+ type: 'flex-layout',
245
111
  };
246
- patchState(store, {
247
- previousSnapshot: store.snapshot(), // Save the previous state
248
- data: initialData,
249
- state: 'restored',
250
- lastChange: changeEvent,
251
- });
252
- },
253
- // Initialize the state
254
- set(initialData) {
255
- const currentData = store.data();
256
- if (isEqual(currentData, initialData)) {
257
- return; // Skip if the current state matches the initial state
258
- }
259
- const changeEvent = {
260
- oldValue: null,
261
- newValue: cloneDeep(initialData),
262
- path: '',
263
- state: 'initiated',
264
- data: initialData,
112
+ this.inheritanceContext = {
113
+ mode: 'edit',
265
114
  };
266
- patchState(store, {
267
- initialSnapshot: cloneDeep(initialData), // Save the initial state
268
- previousSnapshot: store.snapshot(), // Save the current state as the previous
269
- data: initialData,
270
- state: 'initiated',
271
- lastChange: changeEvent,
272
- });
273
- },
274
- // Get a specific value
275
- getValue(path) {
276
- return get(store.data(), path);
277
- },
278
- })));
279
-
280
- const AXPWidgetsCatalog = {
281
- timeDuration: 'time-duration',
282
- tagable: 'tagable-editor',
283
- checkbox: 'checkbox-editor',
284
- color: 'color-editor',
285
- contact: 'contact-editor',
286
- dateTime: 'date-time-editor',
287
- email: 'email-editor',
288
- largeText: 'large-text-editor',
289
- link: 'link-editor',
290
- number: 'number-editor',
291
- numberUnit: 'number-unit-editor',
292
- password: 'password-editor',
293
- phone: 'phone-editor',
294
- richText: 'rich-text-editor',
295
- select: 'select-editor',
296
- selectionList: 'selection-list-editor',
297
- text: 'text-editor',
298
- table: 'table-editor',
299
- toggle: 'toggle-editor',
300
- blockLayout: 'block-layout',
301
- pageLayout: 'page-layout',
302
- repeaterLayout: 'repeater-layout',
303
- textBlockLayout: 'text-block-layout',
304
- fileUploader: 'file-uploader',
305
- fileTypeExtension: 'file-type-extension',
306
- map: 'map',
307
- imageMarker: 'image-marker',
308
- image: 'image',
309
- gallery: 'gallery',
310
- signature: 'signature',
311
- buttonAction: 'button-action',
312
- document: 'document-layout',
313
- lookup: 'lookup-editor',
314
- formField: 'form-field',
315
- qrcode: 'qrcode',
316
- advancedGrid: 'advanced-grid-layout',
317
- advancedGridItem: 'advanced-grid-item-layout',
318
- grid: 'grid-layout',
319
- gridItem: 'grid-item-layout',
320
- gridRow: 'grid-row-layout',
321
- widgetSelector: 'widget-selector',
322
- template: 'template',
323
- templateDesigner: 'template-designer',
324
- cronJob: 'cron-job',
325
- spacing: 'spacing',
326
- direction: 'direction',
327
- border: 'border',
328
- flexLayout: 'flex-layout',
329
- flexItem: 'flex-item-layout',
330
- avatar: 'avatar',
331
- themePaletteChooser: 'theme-palette-chooser',
332
- themeModeChooser: 'theme-mode-chooser',
333
- menuOrientationChooser: 'menu-orientation-chooser',
334
- fontStyleChooser: 'font-style-chooser',
335
- fontSizeChooser: 'font-size-chooser',
336
- iconChooser: 'icon-chooser',
337
- themeColorChooser: 'theme-color-chooser',
338
- gridOptions: 'grid-options',
339
- gridItemOptions: 'grid-item-options',
340
- advancedGridOptions: 'advanced-grid-options',
341
- stringFilter: 'string-filter',
342
- numberFilter: 'number-filter',
343
- dateTimeFilter: 'datetime-filter',
344
- booleanFilter: 'boolean-filter',
345
- lookupFilter: 'lookup-filter',
346
- flexOptions: 'flex-options',
347
- flexItemOptions: 'flex-item-options',
348
- selectFilter: 'select-filter',
349
- requiredValidation: 'required-validation',
350
- regularExpressionValidation: 'regular-expression-validation',
351
- minLengthValidation: 'min-length-validation',
352
- maxLengthValidation: 'max-length-validation',
353
- lessThanValidation: 'less-than-validation',
354
- greaterThanValidation: 'greater-than-validation',
355
- betweenValidation: 'between-validation',
356
- equalValidation: 'equal-validation',
357
- callbackValidation: 'callback-validation',
358
- donutChart: 'donut-chart',
359
- lineChart: 'line-chart',
360
- barChart: 'bar-chart',
361
- gaugeChart: 'gauge-chart',
362
- stickyNote: 'sticky-note',
363
- clockCalendar: 'clock-calendar',
364
- analogClock: 'analog-clock',
365
- weather: 'weather',
366
- minimalWeather: 'minimal-weather',
367
- advancedWeather: 'advanced-weather',
368
- metaData: 'meta-data-editor',
369
- templateEditor: 'template-box-editor',
370
- panel: 'panel',
371
- notification: 'notification',
372
- taskBoard: 'task-board',
373
- comment: 'comment',
374
- list: 'list',
375
- listToolbar: 'list-toolbar',
376
- entityList: 'entity-list',
377
- documentUploader: 'document-uploader',
378
- };
379
-
380
- function cloneProperty(property, values) {
381
- return merge(cloneDeep(property), values);
382
- }
383
- function createStringProperty(ctor) {
384
- return {
385
- name: ctor.name,
386
- title: ctor.title,
387
- group: ctor.group,
388
- schema: {
389
- dataType: 'string',
390
- defaultValue: ctor.defaultValue,
391
- interface: {
392
- name: ctor.name,
393
- path: ctor.path ?? ctor.name,
394
- type: AXPWidgetsCatalog.text,
395
- },
396
- },
397
- visible: !isNil(ctor.visible) ? ctor.visible : true,
398
- };
399
- }
400
- function createNumberProperty(ctor) {
401
- return {
402
- name: ctor.name,
403
- title: ctor.title,
404
- group: ctor.group,
405
- schema: {
406
- dataType: 'number',
407
- defaultValue: ctor.defaultValue,
408
- interface: {
409
- name: ctor.name,
410
- path: ctor.path ?? ctor.name,
411
- type: AXPWidgetsCatalog.number,
412
- options: ctor.options,
413
- },
414
- },
415
- visible: !isNil(ctor.visible) ? ctor.visible : true,
416
- };
417
- }
418
- function createBooleanProperty(ctor) {
419
- return {
420
- name: ctor.name,
421
- title: ctor.title,
422
- group: ctor.group,
423
- schema: {
424
- dataType: 'boolean',
425
- defaultValue: ctor.defaultValue ?? false,
426
- interface: {
427
- name: ctor.name,
428
- path: ctor.path ?? ctor.name,
429
- type: AXPWidgetsCatalog.toggle,
430
- },
431
- },
432
- visible: ctor.visible ?? true,
433
- };
434
- }
435
- function createSelectProperty(ctor) {
436
- return {
437
- name: ctor.name,
438
- title: ctor.title,
439
- group: ctor.group,
440
- schema: {
441
- dataType: 'string',
442
- defaultValue: Array.isArray(ctor.defaultValue)
443
- ? ctor.defaultValue.map((item) => (typeof item === 'string' ? { id: item } : item))
444
- : typeof ctor.defaultValue === 'string'
445
- ? { id: ctor.defaultValue, title: ctor.defaultValue }
446
- : ctor.defaultValue,
447
- interface: {
448
- name: ctor.name,
449
- path: ctor.path ?? ctor.name,
450
- type: AXPWidgetsCatalog.select,
451
- options: {
452
- dataSource: ctor.dataSource.map((item) => (typeof item === 'string' ? { id: item, title: item } : item)),
453
- },
454
- },
455
- },
456
- visible: ctor.visible ?? true,
457
- };
458
- }
459
- const AXP_WIDGET_TOKEN = new InjectionToken('AXP_WIDGET_TOKEN');
460
- const AXP_WIDGET_COLUMN_TOKEN = new InjectionToken('AXP_WIDGET_COLUMN_TOKEN');
461
-
462
- class AXPBaseWidgetComponent extends AXPLayoutElement {
463
- constructor() {
464
- super(...arguments);
465
- this.token = inject(AXP_WIDGET_TOKEN);
466
- this.host = inject(ElementRef).nativeElement;
467
- this.layoutService = inject(AXPLayoutBuilderService);
468
- this.contextService = inject(AXPLayoutBuilderContextStore);
469
- this.config = this.token.config;
470
- this.node = this.token.node;
471
- this.name = this.token.node.name;
472
- this._options = signal(this.token.options ?? {}, ...(ngDevMode ? [{ debugName: "_options" }] : []));
473
- this.options = this._options.asReadonly();
474
- this.onOptionsChanged = new Subject();
475
- this._status = signal(AXPWidgetStatus.Rendering, ...(ngDevMode ? [{ debugName: "_status" }] : []));
476
- this.status = this._status.asReadonly();
477
- this.onStatusChanged = new BehaviorSubject(this._status());
478
- this.#statusEffect = effect(() => {
479
- this.onStatusChanged.next(this.status());
480
- }, ...(ngDevMode ? [{ debugName: "#statusEffect" }] : []));
481
- this.isBusy = computed(() => [AXPWidgetStatus.Rendering, AXPWidgetStatus.Processing].includes(this.status()), ...(ngDevMode ? [{ debugName: "isBusy" }] : []));
482
- this._children = signal(this.token.node.children ?? [], ...(ngDevMode ? [{ debugName: "_children" }] : []));
483
- this.children = this._children.asReadonly();
484
- }
485
- get id() {
486
- return this._id;
487
- }
488
- #statusEffect;
489
- outputs() {
490
- return [];
491
115
  }
492
- ngOnInit() {
493
- if (get(this.node, '__meta__.added')) {
494
- this.onAdded();
495
- }
496
- this.setStatus(AXPWidgetStatus.Rendered);
497
- }
498
- setStatus(status) {
499
- this._status.set(status);
500
- this.layoutService.updateStatus();
501
- }
502
- setOptions(values) {
503
- const oldValue = this.options();
504
- const value = cloneDeep(values);
505
- this._options.set({ ...oldValue, ...value });
506
- this.onOptionsChanged.next({ sender: this });
507
- }
508
- output(name) {
509
- const outputs = this.outputs().map((c) => (typeof c == 'string' ? { name: c, value: c } : c));
510
- if (outputs.some((c) => c.name == name)) {
511
- const opt = get(this, name);
512
- if (typeof opt == 'function') {
513
- return opt();
514
- }
515
- return opt;
116
+ // Predefined layout containers at root level - delegate pattern required
117
+ grid(delegate) {
118
+ const container = new GridContainerBuilder();
119
+ container.withInheritanceContext(this.inheritanceContext);
120
+ if (delegate) {
121
+ delegate(container);
122
+ }
123
+ //this.state.children!.push(container.build());
124
+ this.root = container.build();
125
+ return this;
126
+ }
127
+ flex(delegate) {
128
+ const container = new FlexContainerBuilder();
129
+ container.withInheritanceContext(this.inheritanceContext);
130
+ if (delegate) {
131
+ delegate(container);
132
+ }
133
+ this.root.children.push(container.build());
134
+ return this;
135
+ }
136
+ panel(delegate) {
137
+ const container = new PanelContainerBuilder();
138
+ container.withInheritanceContext(this.inheritanceContext);
139
+ if (delegate) {
140
+ delegate(container);
141
+ }
142
+ this.root.children.push(container.build());
143
+ return this;
144
+ }
145
+ page(delegate) {
146
+ const container = new PageContainerBuilder();
147
+ container.withInheritanceContext(this.inheritanceContext);
148
+ if (delegate) {
149
+ delegate(container);
150
+ }
151
+ this.root.children.push(container.build());
152
+ return this;
153
+ }
154
+ tabset(delegate) {
155
+ const container = new TabsetContainerBuilder();
156
+ container.withInheritanceContext(this.inheritanceContext);
157
+ if (delegate) {
158
+ delegate(container);
159
+ }
160
+ this.root.children.push(container.build());
161
+ return this;
162
+ }
163
+ fieldset(delegate) {
164
+ const container = new FieldsetContainerBuilder();
165
+ container.withInheritanceContext(this.inheritanceContext);
166
+ if (delegate) {
167
+ delegate(container);
516
168
  }
517
- return null;
169
+ this.root.children.push(container.build());
170
+ return this;
518
171
  }
519
- call(name, ...args) {
520
- const fn = get(this, name);
521
- if (fn && typeof fn == 'function') {
522
- fn.bind(this)(...args);
172
+ dialog(delegate) {
173
+ const container = new DialogContainerBuilder(this.popupService);
174
+ if (delegate) {
175
+ delegate(container);
523
176
  }
177
+ return container;
524
178
  }
525
- setChildren(children) {
526
- this._children.set([...children]);
179
+ formField(label, delegate) {
180
+ const field = new FormFieldBuilder(label);
181
+ field.withInheritanceContext(this.inheritanceContext);
182
+ if (delegate) {
183
+ delegate(field);
184
+ }
185
+ this.root.children.push(field.build());
186
+ return this;
187
+ }
188
+ build() {
189
+ return {
190
+ type: this.root.type,
191
+ children: this.root.children,
192
+ mode: this.root.mode,
193
+ };
527
194
  }
528
- onAdded() { }
529
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPBaseWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
530
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPBaseWidgetComponent }); }
531
- }
532
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPBaseWidgetComponent, decorators: [{
533
- type: Injectable
534
- }] });
535
- class AXPLayoutBaseWidgetComponent extends AXPBaseWidgetComponent {
536
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPLayoutBaseWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
537
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPLayoutBaseWidgetComponent }); }
538
195
  }
539
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPLayoutBaseWidgetComponent, decorators: [{
540
- type: Injectable
541
- }] });
542
- class AXPValueWidgetComponent extends AXPLayoutBaseWidgetComponent {
543
- constructor() {
544
- super(...arguments);
545
- this.path = this.token.node.path;
546
- this.defaultValue = this.token.defaultValue ?? this.token.node.defaultValue;
547
- this._isValueWidget = false;
548
- this.isValueWidget = () => this._isValueWidget;
549
- this.onValueChanged = new Subject();
550
- this.fullPath = signal(null, ...(ngDevMode ? [{ debugName: "fullPath" }] : []));
551
- this.parentPath = signal(null, ...(ngDevMode ? [{ debugName: "parentPath" }] : []));
552
- this.getValue = computed(() => {
553
- return this.fullPath() ? this.extractValue(this.fullPath()) : null;
554
- }, ...(ngDevMode ? [{ debugName: "getValue", equal: isEqual }] : [{ equal: isEqual }]));
555
- this.validationRules = computed(() => {
556
- const validationsRaw = this.options()['validations'];
557
- if (validationsRaw == null) {
558
- return [];
196
+ //#endregion
197
+ //#region ---- Container Builder Implementation ----
198
+ /**
199
+ * Abstract base container builder - Open/Closed Principle
200
+ * Provides common functionality for all container types
201
+ */
202
+ class BaseContainerBuilder {
203
+ constructor(containerType) {
204
+ this.containerState = {
205
+ mode: 'edit',
206
+ type: 'flex-layout',
207
+ children: [],
208
+ };
209
+ this.inheritanceContext = {};
210
+ this.containerState.type = containerType;
211
+ }
212
+ // Base methods shared by all containers
213
+ ensureChildren() {
214
+ this.containerState.children = this.containerState.children || [];
215
+ }
216
+ addWidget(type, options) {
217
+ const child = new WidgetBuilder();
218
+ child.type(type);
219
+ // For value widgets, ensure path is provided
220
+ const widgetOptions = options ?? {};
221
+ const path = widgetOptions.path;
222
+ const name = widgetOptions.name;
223
+ if (this.isValueWidget(type) && !path) {
224
+ throw new Error(`Value widget '${type}' requires a 'path' property`);
225
+ }
226
+ // Set name and path in widget state, not options
227
+ if (name) {
228
+ child.name(name);
229
+ }
230
+ if (path) {
231
+ child.path(path);
232
+ }
233
+ // Remove name and path from options
234
+ const { name: _, path: __, ...cleanOptions } = widgetOptions;
235
+ // IMPORTANT: Apply inheritance context BEFORE setting options
236
+ // This ensures that inherited properties (readonly, disabled, etc.) are applied first
237
+ // Then user-provided options will override them if explicitly set
238
+ child.withInheritanceContext(this.inheritanceContext);
239
+ child.options(cleanOptions);
240
+ this.ensureChildren();
241
+ this.containerState.children.push(child.build());
242
+ return this;
243
+ }
244
+ isValueWidget(type) {
245
+ const valueWidgetTypes = [
246
+ 'text-editor',
247
+ 'large-text-editor',
248
+ 'rich-text-editor',
249
+ 'password-editor',
250
+ 'number-editor',
251
+ 'select-editor',
252
+ 'lookup-editor',
253
+ 'selection-list-editor',
254
+ 'date-time-editor',
255
+ 'toggle-editor',
256
+ 'color-editor',
257
+ ];
258
+ return valueWidgetTypes.includes(type);
259
+ }
260
+ build() {
261
+ const result = {
262
+ type: this.containerState.type,
263
+ children: this.containerState.children,
264
+ options: this.containerState.options,
265
+ mode: this.containerState.mode,
266
+ visible: this.containerState.visible,
267
+ };
268
+ // Add name with _form_field suffix for form fields
269
+ if (this.containerState.type === 'form-field') {
270
+ if (this.containerState.name) {
271
+ result.name = this.containerState.name;
559
272
  }
560
- return Object.values(this.options()['validations'])
561
- .filter((c) => c != null)
562
- .map((c) => ({
563
- rule: c.rule,
564
- message: c.message,
565
- options: c.options,
566
- }));
567
- }, ...(ngDevMode ? [{ debugName: "validationRules" }] : []));
568
- }
569
- ngOnInit() {
570
- this._isValueWidget = this.config.properties?.some((c) => c.name == 'path') ?? false;
571
- if (this.isValueWidget()) {
572
- this.detectFullPath();
573
- if (!isNil(this.defaultValue) && isNil(this.getValue())) {
574
- this.setValue(this.defaultValue);
273
+ else if (this.containerState.options?.['label']) {
274
+ result.name = labelToPath(this.containerState.options['label']) + '_form_field';
575
275
  }
276
+ // Form fields don't have path
576
277
  }
577
- //
578
- super.ngOnInit();
579
- }
580
- extractValue(path) {
581
- const rawValue = this.contextService.getValue(path);
582
- if (this.node.valueTransforms?.getter) {
583
- return this.node.valueTransforms?.getter(rawValue);
584
- }
585
- return rawValue;
586
- }
587
- setValue(value) {
588
- if (this.node.valueTransforms?.setter) {
589
- value = this.node.valueTransforms?.setter(value);
590
- }
591
- const oldValue = this.getValue();
592
- value = isUndefined(value) ? null : value;
593
- if (isNil(value) && isNil(oldValue)) {
594
- return;
595
- }
596
- if (isEqual(oldValue, value)) {
597
- return;
598
- }
599
- if (this.fullPath()) {
600
- this.contextService.update(this.fullPath(), value);
601
- this.onValueChanged.next({ sender: this });
602
- }
603
- }
604
- detectFullPath() {
605
- const sections = [];
606
- const ids = [];
607
- //
608
- let parent = this;
609
- //
610
- while (parent) {
611
- const isValueWidget = parent instanceof AXPValueWidgetComponent && parent.isValueWidget();
612
- const valueParent = parent;
613
- const path = valueParent.path ?? (isValueWidget ? valueParent.name : null);
614
- const id = valueParent.name;
615
- //
616
- if (path) {
617
- sections.push(path);
278
+ else {
279
+ // Other containers can have name and path
280
+ if (this.containerState.name) {
281
+ result.name = this.containerState.name;
618
282
  }
619
- if (parent.index != null && isValueWidget) {
620
- sections.push(`[${parent.index}]`);
283
+ if (this.containerState.path) {
284
+ result.path = this.containerState.path;
621
285
  }
622
- if (id) {
623
- ids.push(id);
624
- if (parent.index != null) {
625
- ids.push(`${parent.index}`);
626
- }
286
+ if (this.containerState.defaultValue !== undefined) {
287
+ result.defaultValue = this.containerState.defaultValue;
627
288
  }
628
- parent = parent.parent;
629
- }
630
- //
631
- this.fullPath.set(sections.reverse().join('.'));
632
- this.parentPath.set(sections.slice(0, sections.length - 1).join('.'));
633
- this._id = this.name || this.parent ? ids.reverse().join('_') : null;
634
- if (this._id) {
635
- this.layoutService.registerWidget(this._id, this);
636
289
  }
290
+ return result;
637
291
  }
638
- handleValueChanged(e) {
639
- if (e.isUserInteraction) {
640
- this.setValue(e.value);
641
- }
292
+ }
293
+ /**
294
+ * Base container builder mixin - Interface Segregation Principle
295
+ * Provides common container operations
296
+ */
297
+ class BaseContainerMixin extends BaseContainerBuilder {
298
+ name(name) {
299
+ this.containerState.name = name;
300
+ return this;
301
+ }
302
+ path(path) {
303
+ this.containerState.path = path;
304
+ return this;
305
+ }
306
+ mode(mode) {
307
+ this.containerState.mode = mode;
308
+ this.inheritanceContext.mode = mode;
309
+ return this;
310
+ }
311
+ visible(condition) {
312
+ if (!this.containerState.options)
313
+ this.containerState.options = {};
314
+ this.containerState.options['visible'] = condition;
315
+ this.inheritanceContext.visible = condition;
316
+ return this;
317
+ }
318
+ disabled(condition) {
319
+ if (!this.containerState.options)
320
+ this.containerState.options = {};
321
+ this.containerState.options['disabled'] = condition;
322
+ this.inheritanceContext.disabled = condition;
323
+ return this;
324
+ }
325
+ readonly(condition) {
326
+ if (!this.containerState.options)
327
+ this.containerState.options = {};
328
+ this.containerState.options['readonly'] = condition;
329
+ this.inheritanceContext.readonly = condition;
330
+ return this;
331
+ }
332
+ direction(direction) {
333
+ if (!this.containerState.options)
334
+ this.containerState.options = {};
335
+ this.containerState.options['direction'] = direction;
336
+ this.inheritanceContext.direction = direction;
337
+ return this;
338
+ }
339
+ // Inheritance context methods
340
+ withInheritanceContext(context) {
341
+ this.inheritanceContext = mergeInheritanceContext(context);
342
+ return this;
343
+ }
344
+ getInheritanceContext() {
345
+ return { ...this.inheritanceContext };
642
346
  }
643
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPValueWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
644
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPValueWidgetComponent }); }
645
347
  }
646
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPValueWidgetComponent, decorators: [{
647
- type: Injectable
648
- }] });
649
- class AXPDataListWidgetComponent extends AXPValueWidgetComponent {
650
- constructor() {
651
- super(...arguments);
652
- this.dataService = inject(AXPDataSourceDefinitionProviderService);
653
- this.textField = computed(() => this.options()['textField'] ?? 'title', ...(ngDevMode ? [{ debugName: "textField" }] : []));
654
- this.valueField = computed(() => this.options()['valueField'] ?? 'id', ...(ngDevMode ? [{ debugName: "valueField" }] : []));
655
- this.dataSource = signal(convertArrayToDataSource([]), ...(ngDevMode ? [{ debugName: "dataSource" }] : []));
656
- this.isReady = computed(() => {
657
- const key = this.dataSource().config?.key;
658
- const valueField = this.valueField();
659
- const result = key == valueField;
660
- return result;
661
- }, ...(ngDevMode ? [{ debugName: "isReady" }] : []));
662
- this.selectedItems = signal([], ...(ngDevMode ? [{ debugName: "selectedItems" }] : []));
663
- this.rf = effect(async () => {
664
- const rawValue = this.options()['dataSource'];
665
- // static datasource class
666
- if (rawValue instanceof AXDataSource) {
667
- this.dataSource.set(rawValue);
668
- }
669
- // static array datasource
670
- else if (Array.isArray(rawValue)) {
671
- const ds = new AXDataSource({
672
- key: this.valueField(),
673
- pageSize: 10,
674
- load: async (e) => {
675
- const raw = this.options()['dataSource'];
676
- return {
677
- items: raw,
678
- total: raw.length,
679
- };
680
- },
681
- byKey: (key) => {
682
- const raw = this.options()['dataSource'];
683
- const item = raw.filter((c) => c[this.valueField()] == key);
684
- return Promise.resolve(item[0]);
685
- },
686
- });
687
- this.dataSource.set(ds);
688
- }
689
- // resolve data source by name
690
- else if (rawValue && (typeof rawValue == 'string' || typeof rawValue == 'object')) {
691
- const id = typeof rawValue == 'object' ? rawValue['id'] : rawValue;
692
- const c = await this.dataService.get(id);
693
- if (this.mode == 'designer' && c?.samples?.length) {
694
- this.dataSource.set(convertArrayToDataSource(c.samples, {
695
- key: this.valueField(),
696
- pageSize: 500,
697
- }));
698
- }
699
- else {
700
- const ds = c?.source();
701
- if (ds && ds instanceof Promise) {
702
- const d = await ds;
703
- this.dataSource.set(d);
704
- }
705
- else if (ds) {
706
- this.dataSource.set(ds);
707
- }
708
- // empty datasource
709
- else {
710
- this.dataSource.set(convertArrayToDataSource([]));
711
- }
348
+ /**
349
+ * Layout container mixin - Interface Segregation Principle
350
+ * Provides layout-specific operations
351
+ */
352
+ class LayoutContainerMixin extends BaseContainerMixin {
353
+ layout(value) {
354
+ // Map layout intent to grid item sizing so containers like `form-field`
355
+ // can span multiple columns inside grid/fieldset layouts.
356
+ if (!this.containerState.options)
357
+ this.containerState.options = {};
358
+ if (typeof value === 'number') {
359
+ // Direct numeric shorthand → colSpan
360
+ this.containerState.options.colSpan = value;
361
+ }
362
+ else if (value) {
363
+ // Try to extract a reasonable colSpan from breakpoint positions
364
+ const positions = value.positions;
365
+ if (positions) {
366
+ const colSpan = positions?.lg?.colSpan ??
367
+ positions?.xl?.colSpan ??
368
+ positions?.xxl?.colSpan ??
369
+ positions?.md?.colSpan ??
370
+ positions?.sm?.colSpan;
371
+ if (colSpan != null) {
372
+ this.containerState.options.colSpan = colSpan;
712
373
  }
713
374
  }
714
- // empty datasource
715
- else {
716
- this.dataSource.set(convertArrayToDataSource([]));
717
- }
718
- }, ...(ngDevMode ? [{ debugName: "rf" }] : []));
719
- this.effect2 = effect(async () => {
720
- const value = this.getValue();
721
- const items = [];
722
- if (Array.isArray(value)) {
723
- items.push(...(await Promise.all(value.map((item) => this.extractItem(item)))));
724
- }
725
- else {
726
- items.push(await this.extractItem(value));
727
- }
728
- this.selectedItems.set(items.filter((c) => c != null));
729
- }, ...(ngDevMode ? [{ debugName: "effect2" }] : []));
375
+ }
376
+ return this;
730
377
  }
731
- async extractItem(item) {
732
- if (isNil(item)) {
733
- return null;
378
+ }
379
+ /**
380
+ * Child container mixin - Interface Segregation Principle
381
+ * Provides child container management
382
+ */
383
+ class ChildContainerMixin extends LayoutContainerMixin {
384
+ grid(delegate) {
385
+ const container = new GridContainerBuilder();
386
+ container.withInheritanceContext(this.inheritanceContext);
387
+ if (delegate) {
388
+ delegate(container);
734
389
  }
735
- if (isObjectLike(item) && get(item, this.textField()) != null) {
736
- return item;
390
+ this.ensureChildren();
391
+ this.containerState.children.push(container.build());
392
+ return this;
393
+ }
394
+ flex(delegate) {
395
+ const container = new FlexContainerBuilder();
396
+ container.withInheritanceContext(this.inheritanceContext);
397
+ if (delegate) {
398
+ delegate(container);
737
399
  }
738
- const key = extractValue(item, this.valueField());
739
- const ds = this.dataSource();
740
- if (ds.config?.byKey) {
741
- const found = await ds.config?.byKey(key);
742
- if (found) {
743
- return found;
744
- }
400
+ this.ensureChildren();
401
+ this.containerState.children.push(container.build());
402
+ return this;
403
+ }
404
+ panel(delegate) {
405
+ const container = new PanelContainerBuilder();
406
+ container.withInheritanceContext(this.inheritanceContext);
407
+ if (delegate) {
408
+ delegate(container);
745
409
  }
746
- return isObjectLike(item)
747
- ? item
748
- : {
749
- [this.valueField()]: item,
750
- [this.textField()]: item,
751
- };
410
+ this.ensureChildren();
411
+ this.containerState.children.push(container.build());
412
+ return this;
413
+ }
414
+ page(delegate) {
415
+ const container = new PageContainerBuilder();
416
+ container.withInheritanceContext(this.inheritanceContext);
417
+ if (delegate) {
418
+ delegate(container);
419
+ }
420
+ this.ensureChildren();
421
+ this.containerState.children.push(container.build());
422
+ return this;
423
+ }
424
+ tabset(delegate) {
425
+ const container = new TabsetContainerBuilder();
426
+ container.withInheritanceContext(this.inheritanceContext);
427
+ if (delegate) {
428
+ delegate(container);
429
+ }
430
+ this.ensureChildren();
431
+ this.containerState.children.push(container.build());
432
+ return this;
433
+ }
434
+ fieldset(delegate) {
435
+ const container = new FieldsetContainerBuilder();
436
+ container.withInheritanceContext(this.inheritanceContext);
437
+ if (delegate) {
438
+ delegate(container);
439
+ }
440
+ this.ensureChildren();
441
+ this.containerState.children.push(container.build());
442
+ return this;
443
+ }
444
+ dialog(delegate) {
445
+ const container = new DialogContainerBuilder(); // Will use inject() fallback
446
+ if (delegate) {
447
+ delegate(container);
448
+ }
449
+ return container;
450
+ }
451
+ formField(label, delegate) {
452
+ const field = new FormFieldBuilder(label);
453
+ field.withInheritanceContext(this.inheritanceContext);
454
+ if (delegate) {
455
+ delegate(field);
456
+ }
457
+ this.ensureChildren();
458
+ this.containerState.children.push(field.build());
459
+ return this;
752
460
  }
753
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPDataListWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
754
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPDataListWidgetComponent }); }
755
461
  }
756
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPDataListWidgetComponent, decorators: [{
757
- type: Injectable
758
- }] });
759
- class AXPColumnWidgetComponent {
760
- constructor() {
761
- this.token = inject(AXP_WIDGET_COLUMN_TOKEN);
762
- this.path = this.token.path;
763
- this.options = this.token.options ?? {};
764
- this.rawValue = null;
765
- this.nullText = this.options['nullText'];
766
- this.nullValue = this.options['nullValue'];
767
- this.value = computed(() => {
768
- if (isNil(this.rawValue) && !isNil(this.nullValue)) {
769
- return this.nullValue;
770
- }
771
- return this.rawValue;
772
- }, ...(ngDevMode ? [{ debugName: "value" }] : []));
462
+ /**
463
+ * Widget container mixin - Interface Segregation Principle
464
+ * Provides widget creation operations
465
+ */
466
+ class WidgetContainerMixin extends ChildContainerMixin {
467
+ textBox(options) {
468
+ this.addWidget('text-editor', options);
469
+ return this;
470
+ }
471
+ largeTextBox(options) {
472
+ this.addWidget('large-text-editor', options);
473
+ return this;
474
+ }
475
+ richText(options) {
476
+ this.addWidget('rich-text-editor', options);
477
+ return this;
478
+ }
479
+ passwordBox(options) {
480
+ this.addWidget('password-editor', options);
481
+ return this;
482
+ }
483
+ numberBox(options) {
484
+ this.addWidget('number-editor', options);
485
+ return this;
486
+ }
487
+ selectBox(options) {
488
+ this.addWidget('select-editor', options);
489
+ return this;
490
+ }
491
+ lookupBox(options) {
492
+ this.addWidget('lookup-editor', options);
493
+ return this;
494
+ }
495
+ selectionList(options) {
496
+ this.addWidget('selection-list-editor', options);
497
+ return this;
498
+ }
499
+ dateTimeBox(options) {
500
+ this.addWidget('date-time-editor', options);
501
+ return this;
502
+ }
503
+ toggleSwitch(options) {
504
+ this.addWidget('toggle-editor', options);
505
+ return this;
506
+ }
507
+ colorBox(options) {
508
+ this.addWidget('color-editor', options);
509
+ return this;
510
+ }
511
+ list(delegate) {
512
+ const container = new ListWidgetBuilder();
513
+ container.withInheritanceContext(this.inheritanceContext);
514
+ if (delegate) {
515
+ delegate(container);
516
+ }
517
+ this.ensureChildren();
518
+ this.containerState.children.push(container.build());
519
+ return this;
520
+ }
521
+ customWidget(type, options) {
522
+ this.addWidget(type, options);
523
+ return this;
773
524
  }
774
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPColumnWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
775
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPColumnWidgetComponent }); }
776
525
  }
777
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPColumnWidgetComponent, decorators: [{
778
- type: Injectable
779
- }] });
780
-
781
- class AXPBoxModelLayoutWidgetComponent extends AXPLayoutBaseWidgetComponent {
526
+ /**
527
+ * Flex Container Builder - Liskov Substitution Principle
528
+ * Extends WidgetContainerMixin to inherit all common functionality
529
+ */
530
+ class FlexContainerBuilder extends WidgetContainerMixin {
782
531
  constructor() {
783
- super(...arguments);
784
- this.hostBoxStyle = computed(() => {
785
- const options = this.options();
786
- const style = {};
787
- const spacing = options?.['spacing'];
788
- const border = options?.['border'];
789
- const backgroundColor = options?.['backgroundColor'];
790
- const direction = options?.['direction'];
791
- const overflow = options?.['overflow'];
792
- const overflowX = options?.['overflowX'];
793
- const overflowY = options?.['overflowY'];
794
- style['background-color'] = backgroundColor ?? '';
795
- style['padding'] = spacing?.padding ?? '';
796
- style['margin'] = spacing?.margin ?? '';
797
- style['border-radius'] = border?.radius ?? '';
798
- style['border-width'] = border?.width ?? '';
799
- style['border-color'] = border?.color ?? '';
800
- style['border-style'] = border?.style ?? '';
801
- style['overflow'] = overflow ?? '';
802
- style['overflow-x'] = overflowX ?? '';
803
- style['overflow-y'] = overflowY ?? '';
804
- style['direction'] = direction ?? '';
805
- return style;
806
- }, ...(ngDevMode ? [{ debugName: "hostBoxStyle" }] : []));
807
- this.blockStyle = computed(() => {
808
- const options = this.options();
809
- const style = { ...this.hostBoxStyle() };
810
- const width = options?.['width'];
811
- const minWidth = options?.['minWidth'];
812
- const maxWidth = options?.['maxWidth'];
813
- const height = options?.['height'];
814
- const minHeight = options?.['minHeight'];
815
- const maxHeight = options?.['maxHeight'];
816
- style['min-width'] = minWidth ?? '';
817
- style['width'] = width ?? '';
818
- style['max-width'] = maxWidth ?? '';
819
- style['min-height'] = minHeight ?? '';
820
- style['height'] = height ?? '';
821
- style['max-height'] = maxHeight ?? '';
822
- return style;
823
- }, ...(ngDevMode ? [{ debugName: "blockStyle" }] : []));
824
- this.inlineStyle = computed(() => {
825
- return { ...this.hostBoxStyle() };
826
- }, ...(ngDevMode ? [{ debugName: "inlineStyle" }] : []));
827
- this.blockClass = computed(() => {
828
- return {
829
- 'ax-block': true,
830
- 'ax-w-full': true,
831
- // 'ax-widget-outline': true,
832
- };
833
- }, ...(ngDevMode ? [{ debugName: "blockClass" }] : []));
834
- this.inlineClass = computed(() => {
835
- return {
836
- 'ax-inline-block': true,
837
- };
838
- }, ...(ngDevMode ? [{ debugName: "inlineClass" }] : []));
532
+ super('flex-layout');
533
+ }
534
+ setOptions(options) {
535
+ this.containerState.options = { ...this.containerState.options, ...options };
536
+ return this;
537
+ }
538
+ // Individual fluent methods for Flex
539
+ setDirection(direction) {
540
+ return this.setOptions({ flexDirection: direction });
541
+ }
542
+ setWrap(wrap) {
543
+ return this.setOptions({ flexWrap: wrap });
544
+ }
545
+ setJustifyContent(justify) {
546
+ return this.setOptions({ justifyContent: justify });
547
+ }
548
+ setAlignItems(align) {
549
+ return this.setOptions({ alignItems: align });
550
+ }
551
+ setGap(gap) {
552
+ return this.setOptions({ gap });
553
+ }
554
+ setBackgroundColor(color) {
555
+ return this.setOptions({ backgroundColor: color });
556
+ }
557
+ setPadding(padding) {
558
+ return this.setOptions({ spacing: { padding } });
559
+ }
560
+ setMargin(margin) {
561
+ return this.setOptions({ spacing: { margin } });
839
562
  }
840
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPBoxModelLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
841
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPBoxModelLayoutWidgetComponent }); }
842
563
  }
843
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPBoxModelLayoutWidgetComponent, decorators: [{
844
- type: Injectable
845
- }] });
846
-
847
- class AXPBlockBaseLayoutWidgetComponent extends AXPBoxModelLayoutWidgetComponent {
564
+ /**
565
+ * Grid Container Builder - Liskov Substitution Principle
566
+ * Extends WidgetContainerMixin to inherit all common functionality
567
+ */
568
+ class GridContainerBuilder extends WidgetContainerMixin {
848
569
  constructor() {
849
- super(...arguments);
850
- this.hostClass = computed(() => this.blockClass(), ...(ngDevMode ? [{ debugName: "hostClass" }] : []));
851
- this.hostStyle = computed(() => this.blockStyle(), ...(ngDevMode ? [{ debugName: "hostStyle" }] : []));
570
+ super('grid-layout');
571
+ }
572
+ setOptions(options) {
573
+ this.containerState.options = { ...this.containerState.options, ...options };
574
+ return this;
575
+ }
576
+ // Individual fluent methods for Grid
577
+ setColumns(columns) {
578
+ return this.setOptions({ grid: { default: { columns } } });
579
+ }
580
+ setRows(rows) {
581
+ return this.setOptions({ grid: { default: { rows } } });
582
+ }
583
+ setGap(gap) {
584
+ return this.setOptions({ grid: { default: { gap } } });
585
+ }
586
+ setJustifyItems(justify) {
587
+ return this.setOptions({ grid: { default: { justifyItems: justify } } });
588
+ }
589
+ setAlignItems(align) {
590
+ return this.setOptions({ grid: { default: { alignItems: align } } });
591
+ }
592
+ setAutoFlow(flow) {
593
+ return this.setOptions({ grid: { default: { autoFlow: flow } } });
594
+ }
595
+ setBackgroundColor(color) {
596
+ return this.setOptions({ backgroundColor: color });
597
+ }
598
+ setPadding(padding) {
599
+ return this.setOptions({ spacing: { padding } });
600
+ }
601
+ setMargin(margin) {
602
+ return this.setOptions({ spacing: { margin } });
852
603
  }
853
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPBlockBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
854
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPBlockBaseLayoutWidgetComponent }); }
855
604
  }
856
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPBlockBaseLayoutWidgetComponent, decorators: [{
857
- type: Injectable
858
- }] });
859
-
860
- class AXPFlexBaseLayoutWidgetComponent extends AXPBlockBaseLayoutWidgetComponent {
605
+ /**
606
+ * Panel Container Builder - Liskov Substitution Principle
607
+ * Extends WidgetContainerMixin to inherit all common functionality
608
+ */
609
+ class PanelContainerBuilder extends WidgetContainerMixin {
861
610
  constructor() {
862
- super(...arguments);
863
- this.flex = computed(() => this.options(), ...(ngDevMode ? [{ debugName: "flex" }] : []));
864
- this.hostFlexStyle = computed(() => {
865
- const blockStyle = this.blockStyle();
866
- const style = { ...blockStyle };
867
- const flex = this.flex();
868
- if (isNil(flex?.flexDirection)) {
869
- style['flex-direction'] = '';
870
- }
871
- else {
872
- style['flex-direction'] = flex.flexDirection;
873
- }
874
- if (isNil(flex?.flexWrap)) {
875
- style['flex-wrap'] = '';
876
- }
877
- else {
878
- style['flex-wrap'] = flex.flexWrap;
879
- }
880
- //TODO NEED TO FIX LATER
881
- style['overflow'] = flex?.flexWrap === 'nowrap' ? 'auto' : '';
882
- //END
883
- if (isNil(flex?.justifyContent)) {
884
- style['justify-content'] = '';
885
- }
886
- else {
887
- style['justify-content'] = flex.justifyContent;
888
- }
889
- if (isNil(flex?.alignItems)) {
890
- style['align-items'] = '';
891
- }
892
- else {
893
- style['align-items'] = flex.alignItems;
894
- }
895
- if (isNil(flex?.gap)) {
896
- style['gap'] = '';
897
- }
898
- else {
899
- style['gap'] = flex.gap;
900
- }
901
- return style;
902
- }, ...(ngDevMode ? [{ debugName: "hostFlexStyle" }] : []));
903
- this.hostFlexClass = computed(() => {
904
- return {
905
- ...this.blockClass(),
906
- 'ax-flex': true,
907
- 'ax-h-full': true,
908
- };
909
- }, ...(ngDevMode ? [{ debugName: "hostFlexClass" }] : []));
910
- this.hostClass = computed(() => {
911
- return this.hostFlexClass();
912
- }, ...(ngDevMode ? [{ debugName: "hostClass" }] : []));
913
- this.hostStyle = computed(() => {
914
- return this.hostFlexStyle();
915
- }, ...(ngDevMode ? [{ debugName: "hostStyle" }] : []));
916
- }
917
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPFlexBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
918
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPFlexBaseLayoutWidgetComponent }); }
611
+ super('panel-layout');
612
+ }
613
+ setOptions(options) {
614
+ this.containerState.options = { ...this.containerState.options, ...options };
615
+ return this;
616
+ }
617
+ // Individual fluent methods for Panel
618
+ setCaption(caption) {
619
+ return this.setOptions({ caption });
620
+ }
621
+ setIcon(icon) {
622
+ return this.setOptions({ icon });
623
+ }
624
+ setLook(look) {
625
+ return this.setOptions({ look });
626
+ }
627
+ setShowHeader(show) {
628
+ return this.setOptions({ showHeader: show });
629
+ }
630
+ setCollapsed(collapsed) {
631
+ return this.setOptions({ collapsed });
632
+ }
919
633
  }
920
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPFlexBaseLayoutWidgetComponent, decorators: [{
921
- type: Injectable
922
- }] });
923
-
924
- class AXPInlineBaseLayoutWidgetComponent extends AXPBoxModelLayoutWidgetComponent {
634
+ /**
635
+ * Page Container Builder - Liskov Substitution Principle
636
+ * Extends WidgetContainerMixin to inherit all common functionality
637
+ */
638
+ class PageContainerBuilder extends WidgetContainerMixin {
925
639
  constructor() {
926
- super(...arguments);
927
- this.hostClass = computed(() => this.inlineClass(), ...(ngDevMode ? [{ debugName: "hostClass" }] : []));
928
- this.hostStyle = computed(() => this.inlineStyle(), ...(ngDevMode ? [{ debugName: "hostStyle" }] : []));
640
+ super('page-layout');
641
+ }
642
+ setOptions(options) {
643
+ this.containerState.options = { ...this.containerState.options, ...options };
644
+ return this;
645
+ }
646
+ // Individual fluent methods for Page
647
+ setBackgroundColor(color) {
648
+ return this.setOptions({ backgroundColor: color });
649
+ }
650
+ setTheme(theme) {
651
+ return this.setOptions({ theme });
652
+ }
653
+ setHasHeader(hasHeader) {
654
+ return this.setOptions({ hasHeader });
655
+ }
656
+ setHasFooter(hasFooter) {
657
+ return this.setOptions({ hasFooter });
658
+ }
659
+ setDirection(direction) {
660
+ return this.setOptions({ direction });
929
661
  }
930
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPInlineBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
931
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPInlineBaseLayoutWidgetComponent }); }
932
662
  }
933
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPInlineBaseLayoutWidgetComponent, decorators: [{
934
- type: Injectable
935
- }] });
936
-
937
- class AXPFlexItemBaseLayoutWidgetComponent extends AXPInlineBaseLayoutWidgetComponent {
663
+ /**
664
+ * Tabset Container Builder - Liskov Substitution Principle
665
+ * Extends WidgetContainerMixin to inherit all common functionality
666
+ */
667
+ class TabsetContainerBuilder extends WidgetContainerMixin {
938
668
  constructor() {
939
- super(...arguments);
940
- this.flexItem = computed(() => this.options(), ...(ngDevMode ? [{ debugName: "flexItem" }] : []));
941
- this.hostFlexItemStyle = computed(() => {
942
- const inlineStyle = this.blockStyle();
943
- const style = { ...inlineStyle };
944
- const fi = this.flexItem();
945
- if (isNil(fi?.order)) {
946
- style['order'] = '';
947
- }
948
- else {
949
- style['order'] = fi.order;
950
- }
951
- if (isNil(fi?.grow)) {
952
- style['flex-grow'] = '';
953
- }
954
- else {
955
- style['flex-grow'] = fi.grow;
956
- }
957
- if (isNil(fi?.shrink)) {
958
- style['flex-shrink'] = '';
959
- }
960
- else {
961
- style['flex-shrink'] = fi.shrink;
962
- }
963
- if (isNil(fi?.basis)) {
964
- style['flex-basis'] = '';
965
- }
966
- else {
967
- style['flex-basis'] = fi.basis;
968
- }
969
- if (isNil(fi?.alignSelf)) {
970
- style['align-self'] = '';
971
- }
972
- else {
973
- style['align-self'] = fi.alignSelf;
974
- }
975
- return style;
976
- }, ...(ngDevMode ? [{ debugName: "hostFlexItemStyle" }] : []));
977
- this.hostFlexItemClass = computed(() => {
978
- return {
979
- ...this.blockClass(),
980
- };
981
- }, ...(ngDevMode ? [{ debugName: "hostFlexItemClass" }] : []));
982
- this.hostClass = computed(() => {
983
- return this.hostFlexItemClass();
984
- }, ...(ngDevMode ? [{ debugName: "hostClass" }] : []));
985
- this.hostStyle = computed(() => {
986
- return this.hostFlexItemStyle();
987
- }, ...(ngDevMode ? [{ debugName: "hostStyle" }] : []));
988
- }
989
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPFlexItemBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
990
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPFlexItemBaseLayoutWidgetComponent }); }
669
+ super('tabset-layout');
670
+ }
671
+ setOptions(options) {
672
+ this.containerState.options = { ...this.containerState.options, ...options };
673
+ return this;
674
+ }
675
+ // Individual fluent methods for Tabset
676
+ setLook(look) {
677
+ return this.setOptions({ look });
678
+ }
679
+ setOrientation(orientation) {
680
+ return this.setOptions({ orientation });
681
+ }
682
+ setActiveIndex(index) {
683
+ return this.setOptions({ activeIndex: index });
684
+ }
991
685
  }
992
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPFlexItemBaseLayoutWidgetComponent, decorators: [{
993
- type: Injectable
994
- }] });
995
-
996
- class AXPGridBaseLayoutWidgetComponent extends AXPBlockBaseLayoutWidgetComponent {
997
- constructor() {
998
- super(...arguments);
999
- this.grid = computed(() => this.options()?.grid, ...(ngDevMode ? [{ debugName: "grid" }] : []));
1000
- this.hostGridStyle = computed(() => {
1001
- const style = { ...this.inlineStyle() };
1002
- const g = this.grid()?.default;
1003
- if (g?.gap)
1004
- style['gap'] = g.gap;
1005
- return style;
1006
- }, ...(ngDevMode ? [{ debugName: "hostGridStyle" }] : []));
1007
- this.hostGridClass = computed(() => {
1008
- const cls = {
1009
- ...this.inlineClass(),
1010
- 'ax-grid': true,
1011
- };
1012
- const g = this.grid()?.default;
1013
- if (g?.columns)
1014
- cls[`lg:ax-grid-cols-${g.columns}`] = true;
1015
- if (g?.rows)
1016
- cls[`lg:ax-grid-rows-${g.rows}`] = true;
1017
- if (g?.justifyItems)
1018
- cls[`lg:ax-justify-items-${g.justifyItems}`] = true;
1019
- if (g?.alignItems)
1020
- cls[`lg:ax-align-items-${g.alignItems}`] = true;
1021
- if (g?.autoFlow)
1022
- cls[`lg:ax-grid-flow-${g.autoFlow}`] = true;
1023
- return cls;
1024
- }, ...(ngDevMode ? [{ debugName: "hostGridClass" }] : []));
1025
- this.hostClass = computed(() => this.hostGridClass(), ...(ngDevMode ? [{ debugName: "hostClass" }] : []));
1026
- this.hostStyle = computed(() => this.hostGridStyle(), ...(ngDevMode ? [{ debugName: "hostStyle" }] : []));
1027
- }
1028
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPGridBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
1029
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPGridBaseLayoutWidgetComponent }); }
686
+ /**
687
+ * Form Field Builder - Liskov Substitution Principle
688
+ * Can only contain ONE widget with automatic path generation
689
+ */
690
+ class FormFieldBuilder extends LayoutContainerMixin {
691
+ constructor(label) {
692
+ super('form-field');
693
+ this.hasWidget = false;
694
+ this.containerState.options = { label, showLabel: true };
695
+ }
696
+ setOptions(options) {
697
+ this.containerState.options = { ...this.containerState.options, ...options };
698
+ return this;
699
+ }
700
+ setLabel(label) {
701
+ return this.setOptions({ label });
702
+ }
703
+ setShowLabel(showLabel) {
704
+ return this.setOptions({ showLabel });
705
+ }
706
+ // Single widget methods with automatic path generation
707
+ addSingleWidget(type, options) {
708
+ if (this.hasWidget) {
709
+ throw new Error('Form field can only contain one widget');
710
+ }
711
+ const formFieldName = this.containerState.name;
712
+ const formFieldPath = this.containerState.path; // Get explicit path from form field
713
+ const formFieldLabel = this.containerState.options?.['label'];
714
+ const widgetName = options?.name;
715
+ // Generate widget path: explicit path -> widget name -> form field name -> label -> random
716
+ let widgetPath;
717
+ if (formFieldPath) {
718
+ widgetPath = formFieldPath; // Use explicit form field path first
719
+ }
720
+ else if (widgetName) {
721
+ widgetPath = widgetName;
722
+ }
723
+ else if (formFieldName) {
724
+ widgetPath = formFieldName; // Use form field name as default path
725
+ }
726
+ else if (formFieldLabel) {
727
+ widgetPath = labelToPath(formFieldLabel);
728
+ }
729
+ else {
730
+ widgetPath = generateRandomId();
731
+ }
732
+ const finalName = widgetName || formFieldName || widgetPath;
733
+ const child = new WidgetBuilder();
734
+ child.type(type);
735
+ child.name(finalName);
736
+ child.path(widgetPath);
737
+ // Remove name from options since it's now in state
738
+ const { name: _, ...cleanOptions } = (options || {});
739
+ child.withInheritanceContext(this.inheritanceContext);
740
+ child.options(cleanOptions);
741
+ // IMPORTANT: Store the widget builder, don't build it yet!
742
+ // This allows properties set after this method (like disabled, readonly) to be applied
743
+ this.childWidget = child;
744
+ this.hasWidget = true;
745
+ return this;
746
+ }
747
+ textBox(options) {
748
+ return this.addSingleWidget('text-editor', options);
749
+ }
750
+ largeTextBox(options) {
751
+ return this.addSingleWidget('large-text-editor', options);
752
+ }
753
+ richText(options) {
754
+ return this.addSingleWidget('rich-text-editor', options);
755
+ }
756
+ passwordBox(options) {
757
+ return this.addSingleWidget('password-editor', options);
758
+ }
759
+ numberBox(options) {
760
+ return this.addSingleWidget('number-editor', options);
761
+ }
762
+ selectBox(options) {
763
+ return this.addSingleWidget('select-editor', options);
764
+ }
765
+ lookupBox(options) {
766
+ return this.addSingleWidget('lookup-editor', options);
767
+ }
768
+ selectionList(options) {
769
+ return this.addSingleWidget('selection-list-editor', options);
770
+ }
771
+ dateTimeBox(options) {
772
+ return this.addSingleWidget('date-time-editor', options);
773
+ }
774
+ toggleSwitch(options) {
775
+ return this.addSingleWidget('toggle-editor', options);
776
+ }
777
+ colorBox(options) {
778
+ return this.addSingleWidget('color-editor', options);
779
+ }
780
+ customWidget(type, options) {
781
+ return this.addSingleWidget(type, options);
782
+ }
783
+ // Override property setters to propagate changes to child widget
784
+ disabled(condition) {
785
+ super.disabled(condition);
786
+ if (this.childWidget) {
787
+ this.childWidget.withInheritanceContext(this.inheritanceContext);
788
+ }
789
+ return this;
790
+ }
791
+ readonly(condition) {
792
+ super.readonly(condition);
793
+ if (this.childWidget) {
794
+ this.childWidget.withInheritanceContext(this.inheritanceContext);
795
+ }
796
+ return this;
797
+ }
798
+ visible(condition) {
799
+ super.visible(condition);
800
+ if (this.childWidget) {
801
+ this.childWidget.withInheritanceContext(this.inheritanceContext);
802
+ }
803
+ return this;
804
+ }
805
+ direction(direction) {
806
+ super.direction(direction);
807
+ if (this.childWidget) {
808
+ this.childWidget.withInheritanceContext(this.inheritanceContext);
809
+ }
810
+ return this;
811
+ }
812
+ mode(mode) {
813
+ super.mode(mode);
814
+ if (this.childWidget) {
815
+ this.childWidget.withInheritanceContext(this.inheritanceContext);
816
+ }
817
+ return this;
818
+ }
819
+ // Override withInheritanceContext to pass it to the child widget if it exists
820
+ withInheritanceContext(context) {
821
+ // Call parent implementation first
822
+ super.withInheritanceContext(context);
823
+ // If we have a child widget, update its inheritance context too
824
+ if (this.childWidget) {
825
+ this.childWidget.withInheritanceContext(this.inheritanceContext);
826
+ }
827
+ return this;
828
+ }
829
+ // Override build() to build the child widget at the last moment
830
+ build() {
831
+ // Build the child widget and add it to children before building the form field
832
+ if (this.childWidget) {
833
+ this.ensureChildren();
834
+ this.containerState.children.push(this.childWidget.build());
835
+ }
836
+ // Call parent build
837
+ return super.build();
838
+ }
1030
839
  }
1031
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPGridBaseLayoutWidgetComponent, decorators: [{
1032
- type: Injectable
1033
- }] });
1034
-
1035
- class AXPGridItemBaseLayoutWidgetComponent extends AXPFlexBaseLayoutWidgetComponent {
840
+ /**
841
+ * Fieldset Container Builder - Liskov Substitution Principle
842
+ * Extends LayoutContainerMixin to inherit layout functionality
843
+ * Specialized for form fields only
844
+ */
845
+ class FieldsetContainerBuilder extends LayoutContainerMixin {
1036
846
  constructor() {
1037
- super(...arguments);
1038
- this.gridItem = computed(() => this.options(), ...(ngDevMode ? [{ debugName: "gridItem" }] : []));
1039
- this.hostGridItemStyle = computed(() => {
1040
- const style = { ...this.hostFlexStyle() };
1041
- const g = this.gridItem();
1042
- if (g?.alignSelf)
1043
- style['align-self'] = g.alignSelf;
1044
- if (g?.justifySelf)
1045
- style['justify-self'] = g.justifySelf;
1046
- return style;
1047
- }, ...(ngDevMode ? [{ debugName: "hostGridItemStyle" }] : []));
1048
- this.hostGridItemClass = computed(() => {
1049
- const cls = { ...this.hostFlexClass() };
1050
- const g = this.gridItem();
1051
- if (g?.colSpan)
1052
- cls[`lg:ax-col-span-${g.colSpan}`] = true;
1053
- if (g?.colStart)
1054
- cls[`lg:ax-col-start-${g.colStart}`] = true;
1055
- if (g?.colEnd)
1056
- cls[`lg:ax-col-end-${g.colEnd}`] = true;
1057
- if (g?.rowSpan)
1058
- cls[`lg:ax-row-span-${g.rowSpan}`] = true;
1059
- if (g?.rowStart)
1060
- cls[`lg:ax-row-start-${g.rowStart}`] = true;
1061
- if (g?.rowEnd)
1062
- cls[`lg:ax-row-end-${g.rowEnd}`] = true;
1063
- return cls;
1064
- }, ...(ngDevMode ? [{ debugName: "hostGridItemClass" }] : []));
1065
- this.hostClass = computed(() => this.hostGridItemClass(), ...(ngDevMode ? [{ debugName: "hostClass" }] : []));
1066
- this.hostStyle = computed(() => this.hostGridItemStyle(), ...(ngDevMode ? [{ debugName: "hostStyle" }] : []));
1067
- }
1068
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPGridItemBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
1069
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPGridItemBaseLayoutWidgetComponent }); }
847
+ super('fieldset-layout');
848
+ this.containerState.options = {};
849
+ }
850
+ setOptions(options) {
851
+ this.containerState.options = { ...this.containerState.options, ...options };
852
+ return this;
853
+ }
854
+ // Individual fluent methods for Fieldset
855
+ setTitle(title) {
856
+ return this.setOptions({ title });
857
+ }
858
+ setDescription(description) {
859
+ return this.setOptions({ description });
860
+ }
861
+ setIcon(icon) {
862
+ return this.setOptions({ icon });
863
+ }
864
+ setCollapsible(collapsible) {
865
+ return this.setOptions({ collapsible });
866
+ }
867
+ setIsOpen(isOpen) {
868
+ return this.setOptions({ isOpen });
869
+ }
870
+ setLook(look) {
871
+ return this.setOptions({ look });
872
+ }
873
+ setShowHeader(showHeader) {
874
+ return this.setOptions({ showHeader });
875
+ }
876
+ setCols(cols) {
877
+ return this.setOptions({ cols });
878
+ }
879
+ // Only form fields are allowed in fieldset
880
+ formField(label, delegate) {
881
+ const field = new FormFieldBuilder(label);
882
+ field.withInheritanceContext(this.inheritanceContext);
883
+ if (delegate) {
884
+ delegate(field);
885
+ }
886
+ this.ensureChildren();
887
+ this.containerState.children.push(field.build());
888
+ return this;
889
+ }
1070
890
  }
1071
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPGridItemBaseLayoutWidgetComponent, decorators: [{
1072
- type: Injectable
1073
- }] });
1074
-
1075
- class AXPWidgetRegistryService {
1076
- /**
1077
- *
1078
- */
891
+ /**
892
+ * List Widget Builder - Liskov Substitution Principle
893
+ * Extends WidgetContainerMixin to inherit all common functionality
894
+ */
895
+ class ListWidgetBuilder extends WidgetContainerMixin {
1079
896
  constructor() {
1080
- this.types = new Map();
1081
- AXPWidgetRegistryService.instance = this;
897
+ super('list');
1082
898
  }
1083
- register(widget) {
1084
- this.types.set(widget.name, widget);
899
+ setOptions(options) {
900
+ this.containerState.options = { ...this.containerState.options, ...options };
901
+ return this;
1085
902
  }
1086
- extend(parentName, widget) {
1087
- const parentWidget = this.resolve(parentName);
1088
- const newWidget = merge({}, parentWidget, widget);
1089
- newWidget.name = widget.name;
1090
- this.register(newWidget);
903
+ // Individual fluent methods for List Widget
904
+ setDataSource(dataSource) {
905
+ return this.setOptions({ dataSource });
1091
906
  }
1092
- resolve(name) {
1093
- const widget = this.types.get(name);
1094
- if (!widget) {
1095
- throw new Error(`Widget with name "${name}" does not exist.`);
1096
- }
1097
- return widget;
907
+ setColumns(columns) {
908
+ return this.setOptions({ columns });
909
+ }
910
+ // Event handlers
911
+ setOnRowClick(handler) {
912
+ return this.setOptions({ onRowClick: handler });
913
+ }
914
+ setOnRowDoubleClick(handler) {
915
+ return this.setOptions({ onRowDoubleClick: handler });
916
+ }
917
+ setOnSelectionChange(handler) {
918
+ return this.setOptions({ onSelectionChange: handler });
919
+ }
920
+ setOnRowCommand(handler) {
921
+ return this.setOptions({ onRowCommand: handler });
922
+ }
923
+ // Table features
924
+ setPaging(paging) {
925
+ return this.setOptions({ paging });
926
+ }
927
+ setShowHeader(show) {
928
+ return this.setOptions({ showHeader: show });
929
+ }
930
+ setShowFooter(show) {
931
+ return this.setOptions({ showFooter: show });
932
+ }
933
+ setFixHeader(fix) {
934
+ return this.setOptions({ fixHeader: fix });
935
+ }
936
+ setFixFooter(fix) {
937
+ return this.setOptions({ fixFooter: fix });
938
+ }
939
+ setFetchDataMode(mode) {
940
+ return this.setOptions({ fetchDataMode: mode });
941
+ }
942
+ setParentField(field) {
943
+ return this.setOptions({ parentField: field });
1098
944
  }
1099
- all() {
1100
- return Array.from(this.types.values());
945
+ setMinHeight(height) {
946
+ return this.setOptions({ minHeight: height });
947
+ }
948
+ // Selection & Index
949
+ setShowIndex(show) {
950
+ return this.setOptions({ showIndex: show });
951
+ }
952
+ setAllowSelection(allow) {
953
+ return this.setOptions({ allowSelection: allow });
954
+ }
955
+ // Commands
956
+ setPrimaryCommands(commands) {
957
+ return this.setOptions({ primaryCommands: commands });
958
+ }
959
+ setSecondaryCommands(commands) {
960
+ return this.setOptions({ secondaryCommands: commands });
961
+ }
962
+ // Loading
963
+ setLoading(loading) {
964
+ return this.setOptions({ loading });
1101
965
  }
1102
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPWidgetRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1103
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPWidgetRegistryService, providedIn: 'root' }); }
1104
966
  }
1105
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPWidgetRegistryService, decorators: [{
1106
- type: Injectable,
1107
- args: [{
1108
- providedIn: 'root',
1109
- }]
1110
- }], ctorParameters: () => [] });
1111
-
1112
- class AXPWidgetColumnRendererComponent extends AXDataTableColumnComponent {
1113
- constructor() {
1114
- super(...arguments);
1115
- this.widgetRegistery = inject(AXPWidgetRegistryService);
1116
- this.grid = inject(AXBaseDataTable);
1117
- this.mergedOptions = signal({}, ...(ngDevMode ? [{ debugName: "mergedOptions" }] : []));
1118
- this.loadingRow = signal(null, ...(ngDevMode ? [{ debugName: "loadingRow" }] : []));
1119
- this.injector = inject(Injector);
1120
- this.cdr = inject(ChangeDetectorRef);
1121
- }
1122
- get node() {
1123
- return this._node;
1124
- }
1125
- set node(v) {
1126
- this._node = v;
1127
- }
1128
- get renderFooterTemplate() {
1129
- return this.footerTemplate ?? this._contentFooterTemplate;
1130
- }
1131
- get renderCellTemplate() {
1132
- return this.cellTemplate ?? this._contentCellTemplate;
1133
- }
1134
- async handleExpandRow(row) {
1135
- this.loadingRow.set(row);
1136
- await this.grid.expandRow(row);
1137
- this.loadingRow.set(null);
1138
- // if (row.data?.__meta__?.expanded === undefined) {
1139
- // this.width = `${parseInt(this.width as string) + 24}px`;
1140
- // }
1141
- }
1142
- get renderHeaderTemplate() {
1143
- return this.headerTemplate ?? this._contentHeaderTemplate;
1144
- }
1145
- get loadingEnabled() {
1146
- return true;
1147
- }
1148
- get name() {
1149
- return `col-${this.node.path}`;
1150
- }
1151
- async ngOnInit() {
1152
- const widget = this.widgetRegistery.resolve(this.node.type);
1153
- const mode = 'column';
1154
- this.component = await widget?.components[mode]?.component();
1155
- //
1156
- const props = widget?.components[mode]?.properties
1157
- ?.filter((c) => c.schema.defaultValue)
1158
- .map((c) => ({ [c.name]: c.schema.defaultValue }))
1159
- .reduce((acc, curr) => {
1160
- return { ...acc, ...curr };
1161
- }, {});
1162
- //
1163
- this.mergedOptions.set(merge(props, this.node.options) || {});
1164
- const tokenValue = {
1165
- path: this.node.path,
1166
- options: this.mergedOptions(),
967
+ /**
968
+ * Dialog Container Builder - Specialized for dialog functionality
969
+ * Uses composition instead of inheritance for cleaner separation
970
+ */
971
+ class DialogContainerBuilder {
972
+ constructor(popupService) {
973
+ this.dialogState = {
974
+ type: 'flex-layout', // This will be overridden when content layout exists
975
+ children: [],
976
+ mode: 'edit',
977
+ dialogOptions: {
978
+ title: '',
979
+ size: 'md',
980
+ closeButton: false,
981
+ },
982
+ actions: {
983
+ footer: {
984
+ prefix: [],
985
+ suffix: [],
986
+ },
987
+ },
988
+ };
989
+ if (popupService) {
990
+ this.popupService = popupService;
991
+ }
992
+ else {
993
+ this.popupService = inject(AXPopupService);
994
+ }
995
+ }
996
+ setOptions(options) {
997
+ this.dialogState.dialogOptions = { ...this.dialogState.dialogOptions, ...options };
998
+ return this;
999
+ }
1000
+ // Individual fluent methods for Dialog
1001
+ setTitle(title) {
1002
+ return this.setOptions({ title });
1003
+ }
1004
+ setMessage(message) {
1005
+ return this.setOptions({ message });
1006
+ }
1007
+ setSize(size) {
1008
+ return this.setOptions({ size });
1009
+ }
1010
+ setCloseButton(closeButton) {
1011
+ return this.setOptions({ closeButton });
1012
+ }
1013
+ setContext(context) {
1014
+ return this.setOptions({ context });
1015
+ }
1016
+ content(delegate) {
1017
+ if (delegate) {
1018
+ // Create a flex container directly instead of through LayoutBuilder
1019
+ const flexContainer = new FlexContainerBuilder();
1020
+ flexContainer.setDirection('column');
1021
+ flexContainer.setGap('10px');
1022
+ delegate(flexContainer);
1023
+ this.contentLayout = flexContainer.build();
1024
+ }
1025
+ return this;
1026
+ }
1027
+ setActions(delegate) {
1028
+ if (delegate) {
1029
+ const actionBuilder = new ActionBuilder(this);
1030
+ delegate(actionBuilder);
1031
+ }
1032
+ return this;
1033
+ }
1034
+ // Build method to create dialog node
1035
+ build() {
1036
+ // If we have content layout, use it directly to avoid extra wrapper
1037
+ if (this.contentLayout) {
1038
+ return {
1039
+ ...this.contentLayout,
1040
+ // Add dialog-specific properties
1041
+ options: {
1042
+ ...this.contentLayout.options,
1043
+ ...this.dialogState.dialogOptions,
1044
+ },
1045
+ };
1046
+ }
1047
+ // Fallback to dialog state structure if no content
1048
+ const result = {
1049
+ ...this.dialogState,
1050
+ children: [],
1167
1051
  };
1168
- this.widgetInjector = Injector.create({
1169
- parent: this.injector,
1170
- providers: [
1171
- {
1172
- provide: AXP_WIDGET_COLUMN_TOKEN,
1173
- useValue: tokenValue,
1052
+ // Add dialog-specific properties
1053
+ if (this.dialogState.dialogOptions) {
1054
+ result.options = { ...result.options, ...this.dialogState.dialogOptions };
1055
+ }
1056
+ return result;
1057
+ }
1058
+ // Dialog-specific methods
1059
+ async show() {
1060
+ const dialogNode = this.build();
1061
+ // Import the dialog renderer component dynamically
1062
+ const { AXPDialogRendererComponent } = await Promise.resolve().then(function () { return dialogRenderer_component; });
1063
+ // Create dialog configuration
1064
+ const dialogConfig = {
1065
+ title: this.dialogState.dialogOptions?.title || '',
1066
+ message: this.dialogState.dialogOptions?.message,
1067
+ context: this.dialogState.dialogOptions?.context || {},
1068
+ definition: dialogNode,
1069
+ actions: this.dialogState.actions,
1070
+ };
1071
+ // The Promise resolves when user clicks an action button
1072
+ return new Promise(async (resolve) => {
1073
+ this.popupService.open(AXPDialogRendererComponent, {
1074
+ title: dialogConfig.title,
1075
+ size: this.dialogState.dialogOptions?.size || 'md',
1076
+ closeButton: this.dialogState.dialogOptions?.closeButton || false,
1077
+ closeOnBackdropClick: false,
1078
+ draggable: false,
1079
+ data: {
1080
+ config: dialogConfig,
1081
+ callBack: (result) => {
1082
+ // Resolve with the dialog reference when user clicks an action
1083
+ resolve(result);
1084
+ },
1174
1085
  },
1175
- ],
1086
+ });
1176
1087
  });
1177
- this.width = this.customWidth ? this.customWidth : (this.mergedOptions().width ?? '200px');
1178
- this.allowResizing = this.mergedOptions().allowResizing || true;
1179
- this.cdr.detectChanges();
1180
1088
  }
1181
- getInputs(data) {
1182
- return {
1183
- rawValue: getSmart(data, this.node.path),
1184
- rowData: data,
1089
+ }
1090
+ //#endregion
1091
+ //#region ---- Widget Builder Implementation ----
1092
+ /**
1093
+ * Widget Builder - Single Responsibility Principle
1094
+ * Handles individual widget configuration and building
1095
+ */
1096
+ class WidgetBuilder {
1097
+ constructor(name) {
1098
+ this.widgetState = {
1099
+ type: 'widget',
1100
+ options: {},
1185
1101
  };
1102
+ this.inheritanceContext = {};
1103
+ if (name) {
1104
+ this.widgetState.name = name;
1105
+ }
1186
1106
  }
1187
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPWidgetColumnRendererComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
1188
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.8", type: AXPWidgetColumnRendererComponent, isStandalone: false, selector: "axp-widget-column-renderer", inputs: { caption: "caption", customExpandIcon: "customExpandIcon", customCollapseIcon: "customCollapseIcon", customWidth: "customWidth", node: "node", footerTemplate: "footerTemplate", expandHandler: "expandHandler", cellTemplate: "cellTemplate", headerTemplate: "headerTemplate" }, providers: [
1189
- AXPLayoutBuilderService,
1190
- { provide: AXDataTableColumnComponent, useExisting: AXPWidgetColumnRendererComponent },
1191
- ], viewQueries: [{ propertyName: "_contentFooterTemplate", first: true, predicate: ["footer"], descendants: true }, { propertyName: "_contentCellTemplate", first: true, predicate: ["cell"], descendants: true }, { propertyName: "_contentHeaderTemplate", first: true, predicate: ["header"], descendants: true }], usesInheritance: true, ngImport: i0, template: `
1192
- <ng-template #header>{{ caption | translate | async }}</ng-template>
1193
- <ng-template #cell let-row>
1194
- <div class="ax-flex ax-gap-2 ax-items-center">
1195
- @if (expandHandler) {
1196
- <div
1197
- (click)="handleExpandRow(row)"
1198
- class="ax-expand-handler"
1199
- [class.ax-invisible]="row.data.hasChild === false"
1200
- id="ax-expand-handler-container"
1201
- [style.padding-inline-start.rem]="row.data?.__meta__?.level * 2"
1202
- >
1203
- @if (loadingRow() === row) {
1204
- <i class="fas fa-spinner-third ax-animate-twSpin ax-animate-infinite"></i>
1205
- } @else {
1206
- @if (row.data?.__meta__?.expanded) {
1207
- <i [class]="customCollapseIcon || 'far fa-minus-square ax-text-md ax-opacity-75'"></i>
1208
- } @else {
1209
- <i [class]="customExpandIcon || 'far fa-plus-square ax-text-md ax-opacity-75'"></i>
1210
- }
1211
- }
1212
- </div>
1107
+ type(type) {
1108
+ this.widgetState.type = type;
1109
+ return this;
1110
+ }
1111
+ name(name) {
1112
+ this.widgetState.name = name;
1113
+ if (!this.widgetState.path) {
1114
+ this.widgetState.path = name;
1213
1115
  }
1214
- @if (component && widgetInjector && row?.data) {
1215
- <ng-container
1216
- *ngComponentOutlet="component; injector: widgetInjector; inputs: getInputs(row.data)"
1217
- ></ng-container>
1116
+ return this;
1117
+ }
1118
+ path(path) {
1119
+ this.widgetState.path = path;
1120
+ return this;
1121
+ }
1122
+ options(options) {
1123
+ // Merge options instead of replacing to preserve inherited properties
1124
+ this.widgetState.options = { ...this.widgetState.options, ...options };
1125
+ return this;
1126
+ }
1127
+ layout(value) {
1128
+ if (typeof value === 'number') {
1129
+ this.widgetState.layout = {
1130
+ positions: {
1131
+ sm: { colSpan: 12 },
1132
+ md: { colSpan: 12 },
1133
+ lg: { colSpan: value },
1134
+ xl: { colSpan: value },
1135
+ xxl: { colSpan: value },
1136
+ },
1137
+ };
1218
1138
  }
1219
- </div>
1220
- </ng-template>
1221
- <ng-template #footer></ng-template>
1222
- `, isInline: true, dependencies: [{ kind: "directive", type: i1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i2.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1223
- }
1224
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPWidgetColumnRendererComponent, decorators: [{
1225
- type: Component,
1226
- args: [{
1227
- selector: 'axp-widget-column-renderer',
1228
- template: `
1229
- <ng-template #header>{{ caption | translate | async }}</ng-template>
1230
- <ng-template #cell let-row>
1231
- <div class="ax-flex ax-gap-2 ax-items-center">
1232
- @if (expandHandler) {
1233
- <div
1234
- (click)="handleExpandRow(row)"
1235
- class="ax-expand-handler"
1236
- [class.ax-invisible]="row.data.hasChild === false"
1237
- id="ax-expand-handler-container"
1238
- [style.padding-inline-start.rem]="row.data?.__meta__?.level * 2"
1239
- >
1240
- @if (loadingRow() === row) {
1241
- <i class="fas fa-spinner-third ax-animate-twSpin ax-animate-infinite"></i>
1242
- } @else {
1243
- @if (row.data?.__meta__?.expanded) {
1244
- <i [class]="customCollapseIcon || 'far fa-minus-square ax-text-md ax-opacity-75'"></i>
1245
- } @else {
1246
- <i [class]="customExpandIcon || 'far fa-plus-square ax-text-md ax-opacity-75'"></i>
1247
- }
1248
- }
1249
- </div>
1139
+ else {
1140
+ this.widgetState.layout = value;
1250
1141
  }
1251
- @if (component && widgetInjector && row?.data) {
1252
- <ng-container
1253
- *ngComponentOutlet="component; injector: widgetInjector; inputs: getInputs(row.data)"
1254
- ></ng-container>
1142
+ return this;
1143
+ }
1144
+ mode(mode) {
1145
+ this.widgetState.mode = mode;
1146
+ this.inheritanceContext.mode = mode;
1147
+ return this;
1148
+ }
1149
+ visible(condition) {
1150
+ if (!this.widgetState.options) {
1151
+ this.widgetState.options = {};
1255
1152
  }
1256
- </div>
1257
- </ng-template>
1258
- <ng-template #footer></ng-template>
1259
- `,
1260
- providers: [
1261
- AXPLayoutBuilderService,
1262
- { provide: AXDataTableColumnComponent, useExisting: AXPWidgetColumnRendererComponent },
1263
- ],
1264
- changeDetection: ChangeDetectionStrategy.OnPush,
1265
- inputs: ['caption'],
1266
- standalone: false,
1267
- }]
1268
- }], propDecorators: { customExpandIcon: [{
1269
- type: Input
1270
- }], customCollapseIcon: [{
1271
- type: Input
1272
- }], customWidth: [{
1273
- type: Input
1274
- }], node: [{
1275
- type: Input,
1276
- args: [{ required: true }]
1277
- }], footerTemplate: [{
1278
- type: Input
1279
- }], _contentFooterTemplate: [{
1280
- type: ViewChild,
1281
- args: ['footer']
1282
- }], expandHandler: [{
1283
- type: Input
1284
- }], cellTemplate: [{
1285
- type: Input
1286
- }], _contentCellTemplate: [{
1287
- type: ViewChild,
1288
- args: ['cell']
1289
- }], headerTemplate: [{
1290
- type: Input
1291
- }], _contentHeaderTemplate: [{
1292
- type: ViewChild,
1293
- args: ['header']
1294
- }] } });
1295
-
1296
- class AXPWidgetContainerComponent {
1297
- set context(value) {
1298
- this.contextService.set(value);
1153
+ this.widgetState.options['visible'] = condition;
1154
+ this.inheritanceContext.visible = condition;
1155
+ return this;
1299
1156
  }
1300
- set functions(v) {
1301
- this.builderService.setFunctions(v);
1157
+ disabled(condition) {
1158
+ if (!this.widgetState.options) {
1159
+ this.widgetState.options = {};
1160
+ }
1161
+ this.widgetState.options['disabled'] = condition;
1162
+ this.inheritanceContext.disabled = condition;
1163
+ return this;
1302
1164
  }
1303
- constructor() {
1304
- this.contextService = inject(AXPLayoutBuilderContextStore);
1305
- this.builderService = inject(AXPLayoutBuilderService);
1306
- this.onContextChanged = new EventEmitter();
1307
- this.status = computed(() => {
1308
- return this.builderService.status();
1309
- }, ...(ngDevMode ? [{ debugName: "status" }] : []));
1310
- this.isBusy = computed(() => {
1311
- return this.builderService.isBusy();
1312
- }, ...(ngDevMode ? [{ debugName: "isBusy" }] : []));
1313
- effect(() => {
1314
- if (this.contextService.isChanged()) {
1315
- this.onContextChanged.emit(this.contextService.changeEvent());
1316
- }
1317
- });
1165
+ readonly(condition) {
1166
+ if (!this.widgetState.options) {
1167
+ this.widgetState.options = {};
1168
+ }
1169
+ this.widgetState.options['readonly'] = condition;
1170
+ this.inheritanceContext.readonly = condition;
1171
+ return this;
1318
1172
  }
1319
- refresh() {
1320
- this.builderService.refresh();
1173
+ direction(direction) {
1174
+ if (!this.widgetState.options) {
1175
+ this.widgetState.options = {};
1176
+ }
1177
+ this.widgetState.options['direction'] = direction;
1178
+ this.inheritanceContext.direction = direction;
1179
+ return this;
1180
+ }
1181
+ // Inheritance context methods
1182
+ withInheritanceContext(context) {
1183
+ this.inheritanceContext = mergeInheritanceContext(context);
1184
+ // Apply inherited properties to widget state
1185
+ const resolved = resolveInheritedProperties(context, this.inheritanceContext);
1186
+ // Always apply inherited properties (remove the conditions that check if already set)
1187
+ // This allows properties to be updated when inheritance context changes
1188
+ if (resolved.mode) {
1189
+ this.widgetState.mode = resolved.mode;
1190
+ }
1191
+ if (!this.widgetState.options)
1192
+ this.widgetState.options = {};
1193
+ if (resolved.disabled !== undefined) {
1194
+ this.widgetState.options['disabled'] = resolved.disabled;
1195
+ }
1196
+ if (resolved.readonly !== undefined) {
1197
+ this.widgetState.options['readonly'] = resolved.readonly;
1198
+ }
1199
+ if (resolved.direction !== undefined) {
1200
+ this.widgetState.options['direction'] = resolved.direction;
1201
+ }
1202
+ if (resolved.visible !== undefined) {
1203
+ this.widgetState.options['visible'] = resolved.visible;
1204
+ }
1205
+ return this;
1321
1206
  }
1322
- find(name) {
1323
- return this.builderService.waitForWidget(name);
1207
+ getInheritanceContext() {
1208
+ return { ...this.inheritanceContext };
1324
1209
  }
1325
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPWidgetContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1326
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.8", type: AXPWidgetContainerComponent, isStandalone: false, selector: "axp-widgets-container", inputs: { context: "context", functions: "functions" }, outputs: { onContextChanged: "onContextChanged" }, host: { styleAttribute: "display: contents;" }, providers: [AXPLayoutBuilderService, AXPLayoutBuilderContextStore], ngImport: i0, template: `<ng-content></ng-content>`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1327
- }
1328
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPWidgetContainerComponent, decorators: [{
1329
- type: Component,
1330
- args: [{
1331
- selector: 'axp-widgets-container',
1332
- template: `<ng-content></ng-content>`,
1333
- changeDetection: ChangeDetectionStrategy.OnPush,
1334
- host: { style: 'display: contents;' },
1335
- providers: [AXPLayoutBuilderService, AXPLayoutBuilderContextStore],
1336
- standalone: false,
1337
- }]
1338
- }], ctorParameters: () => [], propDecorators: { onContextChanged: [{
1339
- type: Output
1340
- }], context: [{
1341
- type: Input
1342
- }], functions: [{
1343
- type: Input
1344
- }] } });
1345
-
1346
- class AXPWidgetPlaceholderComponent {
1347
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPWidgetPlaceholderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1348
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.8", type: AXPWidgetPlaceholderComponent, isStandalone: true, selector: "axp-widget-placeholder", ngImport: i0, template: `<div>
1349
- <ax-skeleton class="ax-w-full ax-h-10 ax-rounded-md"></ax-skeleton>
1350
- </div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i1$1.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1351
- }
1352
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPWidgetPlaceholderComponent, decorators: [{
1353
- type: Component,
1354
- args: [{
1355
- selector: 'axp-widget-placeholder',
1356
- template: `<div>
1357
- <ax-skeleton class="ax-w-full ax-h-10 ax-rounded-md"></ax-skeleton>
1358
- </div>`,
1359
- changeDetection: ChangeDetectionStrategy.OnPush,
1360
- imports: [AXSkeletonModule],
1361
- standalone: true,
1362
- }]
1363
- }] });
1364
-
1365
- class AXPWidgetRendererDirective {
1366
- //#endregion
1367
- constructor() {
1368
- this.parentNode = input(...(ngDevMode ? [undefined, { debugName: "parentNode" }] : []));
1369
- this.index = input(...(ngDevMode ? [undefined, { debugName: "index" }] : []));
1370
- this.mode = input.required(...(ngDevMode ? [{ debugName: "mode" }] : []));
1371
- this.node = input.required(...(ngDevMode ? [{ debugName: "node" }] : []));
1372
- this._options = signal({}, ...(ngDevMode ? [{ debugName: "_options" }] : []));
1373
- this.options = this._options.asReadonly();
1374
- this.onOptionsChanged = output();
1375
- this.onValueChanged = output();
1376
- //#region ---- Properties ----
1377
- this.mergedOptions = signal({}, ...(ngDevMode ? [{ debugName: "mergedOptions" }] : []));
1378
- this.injector = inject(Injector);
1379
- this.builderService = inject(AXPLayoutBuilderService);
1380
- this.contextService = inject(AXPLayoutBuilderContextStore);
1381
- this.widgetRegistery = inject(AXPWidgetRegistryService);
1382
- this.unsubscriber = inject(AXUnsubscriber);
1383
- this.translateService = inject(AXTranslationService);
1384
- this.widgetService = inject(AXPWidgetRegistryService);
1385
- this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
1386
- this.viewContainerRef = inject(ViewContainerRef);
1387
- this.isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
1388
- this.expressionEvaluators = new Map();
1389
- this.renderTimeoutId = null;
1390
- this.hasInitialRender = false;
1391
- this.onContextChanged = new Subject();
1392
- effect(async () => {
1393
- const changed = this.contextService.changeEvent();
1394
- // Don't trigger re-render during initial setup
1395
- if (!this.hasInitialRender) {
1396
- return;
1397
- }
1398
- if ((await this.updateOptionsBasedOnContext()) > 0) {
1399
- this.applyOptions();
1400
- }
1401
- if (this.checkFormulaForUpdate(this.node().formula, changed.path)) {
1402
- await this.updateValueBasedOnFormula();
1403
- }
1404
- //
1405
- if (changed.path) {
1406
- this.onContextChanged.next({ path: changed.path });
1407
- }
1408
- });
1409
- this.builderService.onRefresh.pipe(this.unsubscriber.takeUntilDestroy).subscribe(async () => {
1410
- if ((await this.updateOptionsBasedOnContext()) > 0) {
1411
- this.applyOptions();
1412
- }
1413
- });
1210
+ build() {
1211
+ return {
1212
+ name: this.widgetState.name,
1213
+ type: this.widgetState.type,
1214
+ options: this.widgetState.options,
1215
+ mode: this.widgetState.mode,
1216
+ path: this.widgetState.path,
1217
+ defaultValue: this.widgetState.defaultValue,
1218
+ };
1414
1219
  }
1415
- // Detect input changes
1416
- ngOnChanges(changes) {
1417
- if (changes['mode'] || changes['node']) {
1418
- // Check if either 'mode' or 'node' has changed
1419
- this.rerenderComponent();
1420
- }
1421
- }
1422
- rerenderComponent() {
1423
- // Clear any pending render operation to prevent double rendering
1424
- if (this.renderTimeoutId) {
1425
- clearTimeout(this.renderTimeoutId);
1426
- this.renderTimeoutId = null;
1427
- }
1428
- // Reset loading state to allow re-rendering
1429
- this.isLoading.set(false);
1430
- // Schedule the component loading
1431
- this.renderTimeoutId = setTimeout(async () => {
1432
- await this.loadComponent();
1433
- this.renderTimeoutId = null;
1220
+ }
1221
+ //#region ---- Action Builder Implementation ----
1222
+ class ActionBuilder {
1223
+ constructor(dialogBuilder) {
1224
+ this.dialogBuilder = dialogBuilder;
1225
+ }
1226
+ cancel(text) {
1227
+ if (!this.dialogBuilder['dialogState'].actions.footer.suffix) {
1228
+ this.dialogBuilder['dialogState'].actions.footer.suffix = [];
1229
+ }
1230
+ this.dialogBuilder['dialogState'].actions.footer.suffix.push({
1231
+ title: text || '@general:actions.cancel.title',
1232
+ icon: 'fa-times',
1233
+ color: 'default',
1234
+ command: { name: 'cancel' },
1434
1235
  });
1236
+ return this;
1435
1237
  }
1436
- ngOnDestroy() {
1437
- if (this.renderTimeoutId) {
1438
- clearTimeout(this.renderTimeoutId);
1439
- }
1440
- if (this.componentRef) {
1441
- this.componentRef.destroy();
1238
+ submit(text) {
1239
+ if (!this.dialogBuilder['dialogState'].actions.footer.suffix) {
1240
+ this.dialogBuilder['dialogState'].actions.footer.suffix = [];
1442
1241
  }
1242
+ this.dialogBuilder['dialogState'].actions.footer.suffix.push({
1243
+ title: text || '@general:actions.submit.title',
1244
+ icon: 'fa-check',
1245
+ color: 'primary',
1246
+ command: { name: 'submit', options: { validate: true } },
1247
+ });
1248
+ return this;
1443
1249
  }
1444
- async loadComponent() {
1445
- if (this.isLoading()) {
1446
- return;
1250
+ custom(action) {
1251
+ if (!this.dialogBuilder['dialogState'].actions.footer.suffix) {
1252
+ this.dialogBuilder['dialogState'].actions.footer.suffix = [];
1447
1253
  }
1448
- this.isLoading.set(true);
1449
- try {
1450
- // Destroy the existing component if it exists
1451
- if (this.componentRef) {
1452
- this.componentRef.destroy();
1453
- }
1454
- this.viewContainerRef.clear();
1455
- //
1456
- const widget = this.widgetRegistery.resolve(this.node().type);
1457
- //
1458
- const propertiesToProcess = [
1459
- ...(widget?.properties ?? []),
1460
- ...(widget?.components[this.mode()]?.properties ?? []),
1461
- ]?.filter((c) => c.schema.defaultValue != null);
1462
- // Process default values (evaluate expressions if needed)
1463
- const props = {};
1464
- for (const property of propertiesToProcess) {
1465
- const defaultValue = property.schema.defaultValue;
1466
- if (typeof defaultValue === 'string' && this.expressionEvaluator.isExpression(defaultValue)) {
1467
- // Evaluate expression for default value
1468
- try {
1469
- const evaluatedValue = await this.evaluateExpression(defaultValue);
1470
- props[property.name] = evaluatedValue;
1471
- }
1472
- catch (error) {
1473
- console.error(`Error evaluating default value expression for property ${property.name}:`, error);
1474
- props[property.name] = cloneDeep(defaultValue); // Fallback to original value
1475
- }
1476
- }
1477
- else {
1478
- // Use static default value
1479
- props[property.name] = cloneDeep(defaultValue);
1480
- }
1481
- }
1482
- //
1483
- this.mergedOptions.set(merge(props, widget?.options, this.node().options) || {});
1484
- this.preprocessAndInitialOptions(cloneDeep(this.node().options));
1485
- await this.updateOptionsBasedOnContext();
1486
- //
1487
- this._options.update((val) => ({ ...val, ...this.mergedOptions() }));
1488
- // Evaluate default value
1489
- let defaultValue = this.node().defaultValue;
1490
- if (defaultValue && this.expressionEvaluator.isExpression(defaultValue)) {
1491
- defaultValue = await this.evaluateExpression(defaultValue);
1492
- }
1493
- //
1494
- const tokenValue = {
1495
- node: this.node(),
1496
- defaultValue: defaultValue,
1497
- options: this.mergedOptions(),
1498
- config: widget,
1499
- };
1500
- const token = Injector.create({
1501
- parent: this.injector,
1502
- providers: [
1503
- {
1504
- provide: AXP_WIDGET_TOKEN,
1505
- useValue: tokenValue,
1506
- },
1507
- ],
1508
- });
1509
- //
1510
- const loadingRef = this.viewContainerRef.createComponent(AXPWidgetPlaceholderComponent);
1511
- //
1512
- const com = await widget?.components[this.mode()]?.component();
1513
- if (!com) {
1514
- console.error(`${this.node().type} widget component not found with mode: ${this.mode()}`);
1515
- return;
1516
- }
1517
- this.componentRef = this.viewContainerRef.createComponent(com, { injector: token });
1518
- this.instance = this.componentRef.instance;
1519
- this.instance.setStatus(AXPWidgetStatus.Rendering);
1520
- this.instance.parent = this.parentNode();
1521
- this.instance.index = this.index();
1522
- this.instance.mode = this.mode();
1523
- this.instance.setStatus(AXPWidgetStatus.Rendered);
1524
- this.instance?.onOptionsChanged?.pipe(this.unsubscriber.takeUntilDestroy).subscribe((c) => {
1525
- this.onOptionsChanged.emit({ sender: this, widget: c.sender });
1526
- });
1527
- this.instance?.onValueChanged?.pipe(this.unsubscriber.takeUntilDestroy).subscribe((c) => {
1528
- this.onValueChanged.emit({ sender: this, widget: c.sender });
1529
- });
1530
- await this.updateValueBasedOnFormula();
1531
- await this.assignTriggers();
1532
- //
1533
- loadingRef.destroy();
1534
- // Mark that initial render is complete
1535
- this.hasInitialRender = true;
1536
- }
1537
- catch (error) {
1538
- console.error('Error loading component:', error);
1539
- }
1540
- finally {
1541
- this.isLoading.set(false);
1542
- }
1543
- }
1544
- applyOptions() {
1545
- if (!this.instance)
1546
- return;
1547
- this._options.update((val) => ({ ...val, ...this.mergedOptions() }));
1548
- this.instance.setOptions(this.mergedOptions());
1549
- }
1550
- checkFormulaForUpdate(formula, path) {
1551
- if (formula) {
1552
- const regex = /context\.eval\('([^']+)'\)/g;
1553
- const matches = formula.match(regex);
1554
- const nodes = matches ? matches.map((match) => match.match(/'([^']+)'/)[1]) : [];
1555
- return nodes.includes(path);
1556
- }
1557
- else
1558
- return false;
1254
+ this.dialogBuilder['dialogState'].actions.footer.suffix.push(action);
1255
+ return this;
1559
1256
  }
1560
- preprocessAndInitialOptions(obj, pathPrefix = '') {
1561
- if (!obj) {
1562
- return;
1563
- }
1564
- Object.entries(obj).forEach(([key, value]) => {
1565
- const currentPath = pathPrefix ? `${pathPrefix}.${key}` : key;
1566
- // CRITICAL FIX: Skip trigger actions during options processing
1567
- //
1568
- // PROBLEM: Trigger actions were being evaluated immediately during widget setup/options processing,
1569
- // causing them to execute before the actual trigger event occurred. This meant triggers would fire
1570
- // during initialization instead of when the specified context path actually changed.
1571
- //
1572
- // ROOT CAUSE: The expression evaluator was processing trigger action expressions (like console.log
1573
- // or widget.setValue calls) as part of the normal options preprocessing, treating them as dynamic
1574
- // expressions that needed immediate evaluation.
1575
- //
1576
- // SOLUTION: Detect when we're processing trigger actions and store them as static values without
1577
- // expression evaluation. This ensures trigger actions are only evaluated when the trigger's
1578
- // subscription callback actually fires (when the event condition is met).
1579
- //
1580
- // RESULT: Triggers now only execute when their event filters pass (e.g., when the specified
1581
- // context path changes), not during widget initialization.
1582
- if (currentPath.includes('triggers') && (key === 'action' || currentPath.endsWith('.action'))) {
1583
- // Apply static values directly without expression evaluation
1584
- this.mergedOptions.update((currentOptions) => {
1585
- return set(currentOptions, currentPath, value);
1586
- });
1587
- return;
1588
- }
1589
- if (typeof value === 'string' && this.expressionEvaluator.isExpression(value)) {
1590
- // Cache dynamic expression for later evaluation
1591
- this.expressionEvaluators.set(currentPath, () => this.evaluateExpression(value));
1592
- }
1593
- else if (typeof value === 'object' &&
1594
- value !== null &&
1595
- (value.constructor === Object || Array.isArray(value))) {
1596
- // Recursively handle nested objects
1597
- this.preprocessAndInitialOptions(value, currentPath);
1598
- }
1599
- else {
1600
- // Apply static values directly
1601
- this.mergedOptions.update((currentOptions) => {
1602
- return set(currentOptions, currentPath, value);
1603
- });
1604
- }
1605
- });
1257
+ }
1258
+
1259
+ class AXPLayoutConversionService {
1260
+ constructor() {
1261
+ //#region ---- Caching ----
1262
+ this.widgetTreeCache = new Map();
1263
+ this.formDefinitionCache = new Map();
1606
1264
  }
1607
- async updateOptionsBasedOnContext() {
1608
- const updatePromises = Array.from(this.expressionEvaluators).map(async ([path, evaluator]) => {
1609
- const newValue = await evaluator();
1610
- return { path, newValue };
1611
- });
1612
- // Wait for all evaluators to complete
1613
- const updates = await Promise.all(updatePromises);
1614
- // Apply updates to mergedOptions
1615
- if (updates.length > 0) {
1616
- this.mergedOptions.update((o) => {
1617
- const updatedOptions = { ...o };
1618
- updates.forEach(({ path, newValue }) => {
1619
- // Set the new value in the updatedOptions object by path
1620
- set(updatedOptions, path, newValue); // Assuming 'set' can handle paths like 'property.subproperty'
1621
- });
1622
- return updatedOptions;
1623
- });
1265
+ //#endregion
1266
+ //#region ---- Public Methods ----
1267
+ /**
1268
+ * Convert AXPDynamicFormDefinition to AXPWidgetNode tree structure
1269
+ * Groups become Fieldset Layouts with Form Field widgets as children
1270
+ * Fields become Form Field widgets with Editor widgets as children
1271
+ */
1272
+ convertFormDefinition(formDefinition) {
1273
+ // Create cache key based on form definition content
1274
+ const cacheKey = this.createFormDefinitionCacheKey(formDefinition);
1275
+ // Check cache first
1276
+ if (this.widgetTreeCache.has(cacheKey)) {
1277
+ return this.widgetTreeCache.get(cacheKey);
1624
1278
  }
1625
- return updates.length;
1279
+ // Generate widget tree
1280
+ const widgetTree = {
1281
+ type: 'grid-layout',
1282
+ name: 'dynamic-form-container',
1283
+ options: {
1284
+ title: 'Dynamic Form',
1285
+ grid: {
1286
+ default: {
1287
+ columns: 1,
1288
+ gap: '1rem',
1289
+ },
1290
+ },
1291
+ },
1292
+ children: formDefinition.groups.map((group) => this.createGroupAsFieldsetWidget(group)),
1293
+ };
1294
+ // Cache the result
1295
+ this.widgetTreeCache.set(cacheKey, widgetTree);
1296
+ return widgetTree;
1626
1297
  }
1627
- async updateValueBasedOnFormula() {
1628
- if (this.node().formula) {
1629
- const value = await this.evaluateExpression(this.node().formula);
1630
- this.instance.setValue(value);
1298
+ /**
1299
+ * Convert AXPWidgetNode tree back to AXPDynamicFormDefinition
1300
+ * Parses Fieldset Layouts back to Groups
1301
+ * Parses Form Field widgets back to Fields
1302
+ */
1303
+ convertWidgetTreeToFormDefinition(widgetTree) {
1304
+ // Create cache key based on widget tree content
1305
+ const cacheKey = this.createWidgetTreeCacheKey(widgetTree);
1306
+ // Check cache first
1307
+ if (this.formDefinitionCache.has(cacheKey)) {
1308
+ return this.formDefinitionCache.get(cacheKey);
1631
1309
  }
1632
- }
1633
- async evaluateExpression(templateExpression) {
1634
- try {
1635
- const scope = this.buildExpressionScope();
1636
- return await this.expressionEvaluator.evaluate(templateExpression, scope);
1310
+ // Parse widget tree
1311
+ const groups = [];
1312
+ if (widgetTree.children) {
1313
+ widgetTree.children.forEach((child) => {
1314
+ if (child.type === 'fieldset-layout') {
1315
+ const group = this.extractGroupFromFieldset(child);
1316
+ groups.push(group);
1317
+ }
1318
+ });
1637
1319
  }
1638
- catch (error) {
1639
- console.error('Error evaluating expression:', error);
1320
+ const formDefinition = { groups };
1321
+ // Cache the result
1322
+ this.formDefinitionCache.set(cacheKey, formDefinition);
1323
+ return formDefinition;
1324
+ }
1325
+ /**
1326
+ * Validate that a widget tree represents a valid dynamic form structure
1327
+ */
1328
+ validateFormWidgetTree(widgetTree) {
1329
+ if (!widgetTree || widgetTree.type !== 'grid-layout') {
1640
1330
  return false;
1641
1331
  }
1332
+ if (!widgetTree.children || widgetTree.children.length === 0) {
1333
+ return true; // Empty form is valid
1334
+ }
1335
+ // Check that all children are fieldset-layout widgets
1336
+ return widgetTree.children.every((child) => child.type === 'fieldset-layout' &&
1337
+ child.children &&
1338
+ child.children.every((formField) => formField.type === 'form-field' && formField.children && formField.children.length > 0));
1642
1339
  }
1643
- buildExpressionScope() {
1644
- return {
1645
- context: this.getContextScope(),
1646
- events: this.getEventScope(),
1647
- widget: this.getWidgetScope(),
1648
- methods: this.getFunctionScope(),
1649
- vars: this.getVariablesScope(),
1650
- };
1340
+ /**
1341
+ * Clear all caches
1342
+ */
1343
+ clearCaches() {
1344
+ this.widgetTreeCache.clear();
1345
+ this.formDefinitionCache.clear();
1651
1346
  }
1652
- getContextScope() {
1347
+ /**
1348
+ * Get cache statistics
1349
+ */
1350
+ getCacheStats() {
1653
1351
  return {
1654
- eval: (path) => {
1655
- //TODO: Handle array index
1656
- const fullPath = path.startsWith('>') ? `${this.instance?.parentPath()}.${path.substring(1)}` : path;
1657
- const value = this.contextService.getValue(fullPath);
1658
- return value;
1659
- },
1660
- set: (path, value) => {
1661
- this.contextService.update(path, value);
1662
- },
1663
- data: () => {
1664
- return this.contextService.data();
1665
- },
1666
- isDirty: () => {
1667
- return this.contextService.isDirty();
1668
- },
1352
+ widgetTreeCacheSize: this.widgetTreeCache.size,
1353
+ formDefinitionCacheSize: this.formDefinitionCache.size,
1669
1354
  };
1670
1355
  }
1671
- getEventScope() {
1356
+ //#endregion
1357
+ //#region ---- Private Methods ----
1358
+ /**
1359
+ * Convert a single group to Fieldset widget structure
1360
+ */
1361
+ createGroupAsFieldsetWidget(group) {
1362
+ // Determine columns count from layout or default to 1
1363
+ const columnsCount = 1;
1672
1364
  return {
1673
- context: (path) => {
1674
- return this.onContextChanged.pipe(filter((c) => {
1675
- // If no path filter specified, pass all events
1676
- if (path == null || path === '') {
1677
- return true;
1678
- }
1679
- // Ensure c.path exists
1680
- if (!c.path) {
1681
- return false;
1682
- }
1683
- // Pattern: "prefix*" - matches paths that start with prefix
1684
- if (path.endsWith('*')) {
1685
- const prefix = path.substring(0, path.length - 1);
1686
- return c.path.startsWith(prefix);
1687
- }
1688
- // Pattern: "*suffix" - matches paths that end with suffix
1689
- else if (path.startsWith('*')) {
1690
- const suffix = path.substring(1);
1691
- return c.path.endsWith(suffix);
1692
- }
1693
- // Exact match
1694
- else {
1695
- return c.path === path;
1696
- }
1697
- }));
1365
+ type: 'fieldset-layout',
1366
+ name: group.name,
1367
+ options: {
1368
+ title: group.title,
1369
+ description: group.description,
1370
+ cols: columnsCount,
1698
1371
  },
1699
- from: (event) => get(this.instance.api(), event),
1372
+ children: this.createFieldWidgets(group.parameters, columnsCount),
1700
1373
  };
1701
1374
  }
1702
- getWidgetScope() {
1375
+ /**
1376
+ * Convert fields to Form Field widgets
1377
+ */
1378
+ createFieldWidgets(fields, columnsCount) {
1379
+ return fields.map((field) => this.createFormFieldWidget(field));
1380
+ }
1381
+ /**
1382
+ * Convert a single field to Form Field widget with editor as child
1383
+ */
1384
+ createFormFieldWidget(field) {
1703
1385
  return {
1704
- call: (name, ...args) => {
1705
- this.instance.call(name, ...args);
1706
- },
1707
- setValue: (value) => {
1708
- this.instance.setValue(value);
1709
- },
1710
- clear: () => {
1711
- this.instance.setValue(undefined);
1712
- },
1713
- refresh: () => {
1714
- const refresh = this.instance?.['refresh'];
1715
- if (refresh && typeof refresh === 'function') {
1716
- refresh.bind(this.instance)();
1717
- }
1718
- },
1719
- output: (name) => {
1720
- this.instance.output(name);
1721
- },
1722
- find: (id) => {
1723
- return this.builderService.getWidget(id);
1386
+ type: 'form-field',
1387
+ name: field.path,
1388
+ options: {
1389
+ label: field.title,
1390
+ description: field.description,
1391
+ showLabel: true,
1724
1392
  },
1393
+ children: [field.widget], // The editor widget becomes a child of form-field
1725
1394
  };
1726
1395
  }
1727
- getFunctionScope() {
1728
- const scope = {
1729
- sum: (values) => {
1730
- return sum(values);
1731
- },
1732
- translate: (text, options) => {
1733
- return this.translateService.translateAsync(text, options);
1734
- },
1735
- widgetInfo: (name) => {
1736
- return this.widgetService.resolve(name);
1737
- },
1396
+ /**
1397
+ * Extract group information from Fieldset Layout widget
1398
+ */
1399
+ extractGroupFromFieldset(fieldsetNode) {
1400
+ const columnsCount = fieldsetNode.options?.['cols'] || 1;
1401
+ // Extract fields directly from fieldset children
1402
+ const fields = [];
1403
+ if (fieldsetNode.children) {
1404
+ fieldsetNode.children.forEach((formField) => {
1405
+ if (formField.type === 'form-field' && formField.children && formField.children.length > 0) {
1406
+ const field = this.extractFieldFromFormWidget(formField);
1407
+ if (field) {
1408
+ fields.push(field);
1409
+ }
1410
+ }
1411
+ });
1412
+ }
1413
+ return {
1414
+ name: fieldsetNode.name || `group-${Date.now()}`,
1415
+ title: fieldsetNode.options?.['title'],
1416
+ description: fieldsetNode.options?.['description'],
1417
+ parameters: fields,
1738
1418
  };
1739
- // Add custom functions from builder service
1740
- Object.entries(this.builderService.functions).forEach(([key, fn]) => {
1741
- scope[key] = (...args) => {
1742
- return fn(...args);
1743
- };
1744
- });
1745
- return scope;
1746
1419
  }
1747
- getVariablesScope() {
1420
+ /**
1421
+ * Extract field information from Form Field widget
1422
+ */
1423
+ extractFieldFromFormWidget(formFieldNode) {
1424
+ if (!formFieldNode.children || formFieldNode.children.length === 0) {
1425
+ return null;
1426
+ }
1427
+ const editorWidget = formFieldNode.children[0];
1748
1428
  return {
1749
- eval: (path) => get(this.builderService.variables, path),
1429
+ path: formFieldNode.name || editorWidget.name || `field-${Date.now()}`,
1430
+ title: formFieldNode.options?.['label'],
1431
+ description: formFieldNode.options?.['description'],
1432
+ widget: editorWidget,
1433
+ mode: formFieldNode.mode,
1750
1434
  };
1751
1435
  }
1752
- async assignTriggers() {
1753
- const node = this.node();
1754
- // Check multiple possible locations for triggers
1755
- const triggers = node.triggers || node.options?.['triggers'] || this.mergedOptions()?.triggers;
1756
- if (!triggers) {
1757
- return;
1758
- }
1759
- for (const trigger of triggers) {
1760
- try {
1761
- const event = await this.evaluateTrigger(trigger.event);
1762
- if (event) {
1763
- event.pipe(this.unsubscriber.takeUntilDestroy).subscribe(() => {
1764
- const exec = async (action) => {
1765
- if (!isEmpty(action) && isString(action)) {
1766
- await this.evaluateAction(action);
1767
- }
1768
- };
1769
- const actions = Array.isArray(trigger.action) ? trigger.action : [trigger.action];
1770
- actions.forEach(async (a) => await exec(a));
1771
- });
1772
- }
1773
- }
1774
- catch (error) {
1775
- console.error('Error assigning trigger:', error);
1776
- }
1436
+ /**
1437
+ * Create cache key for form definition
1438
+ */
1439
+ createFormDefinitionCacheKey(formDefinition) {
1440
+ // Create a hash-like key instead of full JSON string
1441
+ const keyParts = [];
1442
+ keyParts.push(`groups:${formDefinition.groups.length}`);
1443
+ formDefinition.groups.forEach((group, groupIndex) => {
1444
+ keyParts.push(`g${groupIndex}:${group.name}:${group.parameters.length}`);
1445
+ group.parameters.forEach((param, paramIndex) => {
1446
+ keyParts.push(`p${groupIndex}.${paramIndex}:${param.path}:${param.widget.type}`);
1447
+ });
1448
+ });
1449
+ if (formDefinition.mode) {
1450
+ keyParts.push(`mode:${formDefinition.mode}`);
1777
1451
  }
1452
+ // Join with delimiter and create a shorter hash
1453
+ const keyString = keyParts.join('|');
1454
+ // If still too long, create a simple hash
1455
+ if (keyString.length > 100) {
1456
+ return this.createSimpleHash(keyString);
1457
+ }
1458
+ return keyString;
1778
1459
  }
1779
- async evaluateTrigger(templateExpression) {
1780
- try {
1781
- const scope = this.buildExpressionScope();
1782
- const result = await this.expressionEvaluator.evaluate(templateExpression, scope);
1783
- // Check if result is an Observable
1784
- if (result && typeof result.subscribe === 'function') {
1785
- return result;
1786
- }
1787
- else {
1788
- return null;
1789
- }
1460
+ /**
1461
+ * Create cache key for widget tree
1462
+ */
1463
+ createWidgetTreeCacheKey(widgetTree) {
1464
+ // Create a hash-like key instead of full JSON string
1465
+ const keyParts = [];
1466
+ keyParts.push(`type:${widgetTree.type}`);
1467
+ if (widgetTree.name) {
1468
+ keyParts.push(`name:${widgetTree.name}`);
1790
1469
  }
1791
- catch (error) {
1792
- console.error('Error evaluating trigger expression:', error);
1793
- return null;
1470
+ if (widgetTree.children) {
1471
+ keyParts.push(`children:${widgetTree.children.length}`);
1472
+ widgetTree.children.forEach((child, index) => {
1473
+ keyParts.push(`c${index}:${child.type}`);
1474
+ if (child.children) {
1475
+ keyParts.push(`cc${index}:${child.children.length}`);
1476
+ child.children.forEach((grandChild, gIndex) => {
1477
+ keyParts.push(`gc${index}.${gIndex}:${grandChild.type}`);
1478
+ if (grandChild.children) {
1479
+ keyParts.push(`gcc${index}.${gIndex}:${grandChild.children.length}`);
1480
+ }
1481
+ });
1482
+ }
1483
+ });
1794
1484
  }
1795
- }
1796
- async evaluateAction(templateExpression) {
1797
- try {
1798
- const scope = this.buildExpressionScope();
1799
- await this.expressionEvaluator.evaluate(templateExpression, scope);
1485
+ // Join with delimiter and create a shorter hash
1486
+ const keyString = keyParts.join('|');
1487
+ // If still too long, create a simple hash
1488
+ if (keyString.length > 100) {
1489
+ return this.createSimpleHash(keyString);
1800
1490
  }
1801
- catch (error) {
1802
- console.error('Error evaluating action expression:', templateExpression, error);
1491
+ return keyString;
1492
+ }
1493
+ /**
1494
+ * Create a simple hash from a string
1495
+ */
1496
+ createSimpleHash(str) {
1497
+ let hash = 0;
1498
+ if (str.length === 0)
1499
+ return hash.toString();
1500
+ for (let i = 0; i < str.length; i++) {
1501
+ const char = str.charCodeAt(i);
1502
+ hash = (hash << 5) - hash + char;
1503
+ hash = hash & hash; // Convert to 32-bit integer
1803
1504
  }
1505
+ return Math.abs(hash).toString(36); // Convert to base36 for shorter string
1804
1506
  }
1805
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPWidgetRendererDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1806
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.1.8", type: AXPWidgetRendererDirective, isStandalone: false, selector: "[axp-widget-renderer]", inputs: { parentNode: { classPropertyName: "parentNode", publicName: "parentNode", isSignal: true, isRequired: false, transformFunction: null }, index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: true, transformFunction: null }, node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { onOptionsChanged: "onOptionsChanged", onValueChanged: "onValueChanged" }, providers: [
1807
- {
1808
- provide: AXUnsubscriber,
1809
- },
1810
- ], exportAs: ["widgetRenderer"], usesOnChanges: true, ngImport: i0 }); }
1507
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AXPLayoutConversionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1508
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AXPLayoutConversionService, providedIn: 'root' }); }
1811
1509
  }
1812
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPWidgetRendererDirective, decorators: [{
1813
- type: Directive,
1510
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AXPLayoutConversionService, decorators: [{
1511
+ type: Injectable,
1814
1512
  args: [{
1815
- selector: '[axp-widget-renderer]',
1816
- exportAs: 'widgetRenderer',
1817
- providers: [
1818
- {
1819
- provide: AXUnsubscriber,
1820
- },
1821
- ],
1822
- standalone: false,
1513
+ providedIn: 'root',
1823
1514
  }]
1824
- }], ctorParameters: () => [] });
1515
+ }] });
1825
1516
 
1826
- const COMPONENTS = [AXPWidgetContainerComponent, AXPWidgetColumnRendererComponent, AXPWidgetRendererDirective];
1827
- class AXPLayoutBuilderModule {
1828
- static forRoot(config) {
1829
- return {
1830
- ngModule: AXPLayoutBuilderModule,
1831
- providers: [
1832
- {
1833
- provide: 'AXPLayoutBuilderModuleFactory',
1834
- useFactory: (registry) => async () => {
1835
- await Promise.all(config?.widgets?.map((w) => Promise.resolve(registry.register(w))) || []);
1836
- await Promise.all(config?.extendedWidgets?.map((ew) => Promise.resolve(registry.extend(ew.parentName, ew.widget))) || []);
1837
- },
1838
- deps: [AXPWidgetRegistryService],
1839
- multi: true,
1840
- },
1841
- ],
1517
+ class AXPLayoutRendererComponent {
1518
+ constructor() {
1519
+ this.evaluatorService = inject(AXPExpressionEvaluatorService);
1520
+ this.conversionService = inject(AXPLayoutConversionService);
1521
+ /**
1522
+ * Tracks the latest scheduled evaluation to ensure last-write-wins for async evaluate
1523
+ */
1524
+ this.evaluationRunId = 0;
1525
+ /**
1526
+ * RxJS subjects for context management
1527
+ */
1528
+ this.contextUpdateSubject = new Subject();
1529
+ this.contextChangeSubject = new Subject();
1530
+ /**
1531
+ * Cache for expression evaluation results
1532
+ */
1533
+ this.expressionCache = new Map();
1534
+ /**
1535
+ * Cache for widget tree comparisons
1536
+ */
1537
+ this.widgetTreeCache = new Map();
1538
+ /**
1539
+ * Last layout hash for change detection
1540
+ */
1541
+ this.lastLayoutHash = '';
1542
+ //#region ---- Inputs ----
1543
+ /**
1544
+ * Form definition containing groups and fields OR widget tree
1545
+ */
1546
+ this.layout = input.required(...(ngDevMode ? [{ debugName: "layout" }] : []));
1547
+ /**
1548
+ * Form context/model data
1549
+ */
1550
+ this.context = model({}, ...(ngDevMode ? [{ debugName: "context" }] : []));
1551
+ /**
1552
+ * Form appearance and density styling (normal, compact, spacious)
1553
+ */
1554
+ this.look = input('fieldset', ...(ngDevMode ? [{ debugName: "look" }] : []));
1555
+ /**
1556
+ * Default form mode. Can be overridden by section/group and field.
1557
+ */
1558
+ this.mode = input('edit', ...(ngDevMode ? [{ debugName: "mode" }] : []));
1559
+ //#endregion
1560
+ //#region ---- Widget Tree Conversion ----
1561
+ this.widgetTree = signal(null, ...(ngDevMode ? [{ debugName: "widgetTree" }] : []));
1562
+ /**
1563
+ * Convert and evaluate data when inputs change (optimized with RxJS)
1564
+ */
1565
+ this.conversionEffect = effect(() => {
1566
+ const inputData = this.layout();
1567
+ const ctx = this.internalContext();
1568
+ const look = this.look();
1569
+ const runId = ++this.evaluationRunId;
1570
+ // Generate layout hash for change detection
1571
+ const layoutHash = this.generateLayoutHash(inputData, look);
1572
+ // Skip if layout hasn't changed
1573
+ if (layoutHash === this.lastLayoutHash && this.widgetTree()) {
1574
+ return;
1575
+ }
1576
+ this.lastLayoutHash = layoutHash;
1577
+ (async () => {
1578
+ // First evaluate expressions if needed
1579
+ const evaluated = await this.expressionEvaluator(inputData, ctx);
1580
+ // Ignore stale results
1581
+ if (runId !== this.evaluationRunId) {
1582
+ return;
1583
+ }
1584
+ // Convert to widget tree (this will also apply the layout look)
1585
+ const tree = evaluated;
1586
+ // Update widget tree
1587
+ const prev = this.widgetTree();
1588
+ if (!isEqual(prev, tree)) {
1589
+ tree.mode = this.mode();
1590
+ this.widgetTree.set(tree);
1591
+ }
1592
+ })();
1593
+ }, ...(ngDevMode ? [{ debugName: "conversionEffect" }] : []));
1594
+ //#endregion
1595
+ //#region ---- Outputs ----
1596
+ /**
1597
+ * Emitted when context change is initiated
1598
+ */
1599
+ this.contextInitiated = output();
1600
+ /**
1601
+ * Emitted when form becomes valid/invalid
1602
+ */
1603
+ this.validityChange = output();
1604
+ //#endregion
1605
+ //#region ---- Properties ----
1606
+ this.form = viewChild(AXFormComponent, ...(ngDevMode ? [{ debugName: "form" }] : []));
1607
+ this.container = viewChild(AXPWidgetContainerComponent, ...(ngDevMode ? [{ debugName: "container" }] : []));
1608
+ /**
1609
+ * Internal context signal for reactivity
1610
+ */
1611
+ this.internalContext = signal({}, ...(ngDevMode ? [{ debugName: "internalContext" }] : []));
1612
+ /**
1613
+ * Initial context for reset functionality
1614
+ */
1615
+ this.initialContext = {};
1616
+ //#endregion
1617
+ //#region ---- Effects ----
1618
+ /**
1619
+ * Effect to sync context changes from external to internal (optimized with RxJS)
1620
+ */
1621
+ this.#contextSyncEffect = effect(() => {
1622
+ const ctx = this.context() ?? {};
1623
+ this.contextUpdateSubject.next(ctx);
1624
+ }, ...(ngDevMode ? [{ debugName: "#contextSyncEffect" }] : []));
1625
+ /**
1626
+ * Effect to handle widget tree status changes
1627
+ */
1628
+ this.#widgetStatusEffect = effect(() => {
1629
+ const widgetTree = this.widgetTree();
1630
+ if (widgetTree) {
1631
+ this.container()?.builderService.setStatus(AXPPageStatus.Rendered);
1632
+ }
1633
+ }, ...(ngDevMode ? [{ debugName: "#widgetStatusEffect" }] : []));
1634
+ }
1635
+ async expressionEvaluator(expression, context) {
1636
+ // Check if it's a form definition that needs conversion
1637
+ if (this.isFormDefinition(expression)) {
1638
+ return this.conversionService.convertFormDefinition(expression);
1639
+ }
1640
+ // Generate cache key using a more efficient method
1641
+ const cacheKey = this.generateCacheKey(expression, context);
1642
+ // Check cache first
1643
+ if (this.expressionCache.has(cacheKey)) {
1644
+ return this.expressionCache.get(cacheKey);
1645
+ }
1646
+ const scope = {
1647
+ context: {
1648
+ eval: (path) => get(context, path),
1649
+ },
1842
1650
  };
1651
+ const result = await this.evaluatorService.evaluate(expression, scope);
1652
+ // Cache result with LRU-like behavior
1653
+ if (this.expressionCache.size > 50) {
1654
+ // Clear half the cache when it gets too large
1655
+ const keysToDelete = Array.from(this.expressionCache.keys()).slice(0, 25);
1656
+ keysToDelete.forEach((key) => this.expressionCache.delete(key));
1657
+ }
1658
+ this.expressionCache.set(cacheKey, result);
1659
+ return result;
1660
+ }
1661
+ //#endregion
1662
+ //#region ---- Lifecycle Methods ----
1663
+ ngOnInit() {
1664
+ // Initialize internal context with input context
1665
+ const ctx = this.context() ?? {};
1666
+ this.internalContext.set(ctx);
1667
+ // Store initial context for reset functionality
1668
+ this.initialContext = cloneDeep(ctx);
1669
+ // Setup RxJS streams for context management
1670
+ this.setupContextStreams();
1671
+ }
1672
+ //#endregion
1673
+ //#region ---- Effects ----
1674
+ /**
1675
+ * Effect to sync context changes from external to internal (optimized with RxJS)
1676
+ */
1677
+ #contextSyncEffect;
1678
+ /**
1679
+ * Effect to handle widget tree status changes
1680
+ */
1681
+ #widgetStatusEffect;
1682
+ //#endregion
1683
+ //#region ---- Event Handlers ----
1684
+ /**
1685
+ * Handle context change events from widget container (optimized with RxJS)
1686
+ */
1687
+ handleContextChanged(event) {
1688
+ if (event.state === 'initiated') {
1689
+ this.contextInitiated.emit(event.data);
1690
+ }
1691
+ else {
1692
+ this.contextChangeSubject.next(event.data ?? {});
1693
+ }
1694
+ }
1695
+ //#endregion
1696
+ //#region ---- Public Methods ----
1697
+ /**
1698
+ * Get the form component instance
1699
+ */
1700
+ getForm() {
1701
+ return this.form();
1702
+ }
1703
+ /**
1704
+ * Get the widget container component instance
1705
+ */
1706
+ getContainer() {
1707
+ return this.container();
1708
+ }
1709
+ /**
1710
+ * Get current form context
1711
+ */
1712
+ getContext() {
1713
+ return this.internalContext();
1714
+ }
1715
+ /**
1716
+ * Update form context programmatically
1717
+ */
1718
+ updateContext(context) {
1719
+ this.internalContext.set(context);
1720
+ }
1721
+ /**
1722
+ * Get the current widget tree
1723
+ */
1724
+ getWidgetTree() {
1725
+ return this.widgetTree();
1843
1726
  }
1844
- static forChild(config) {
1727
+ /**
1728
+ * Validate the form
1729
+ */
1730
+ async validate() {
1731
+ const form = this.form();
1732
+ if (form) {
1733
+ const isValid = await form.validate();
1734
+ this.validityChange.emit(isValid.result);
1735
+ return isValid;
1736
+ }
1845
1737
  return {
1846
- ngModule: AXPLayoutBuilderModule,
1847
- providers: [
1848
- {
1849
- provide: 'AXPLayoutBuilderModuleFactory',
1850
- useFactory: (registry) => async () => {
1851
- await Promise.all(config?.widgets?.map((w) => Promise.resolve(registry.register(w))) || []);
1852
- await Promise.all(config?.extendedWidgets?.map((ew) => Promise.resolve(registry.extend(ew.parentName, ew.widget))) || []);
1853
- },
1854
- deps: [AXPWidgetRegistryService],
1855
- multi: true,
1856
- },
1857
- ],
1738
+ result: false,
1739
+ messages: [],
1740
+ rules: [],
1858
1741
  };
1859
1742
  }
1860
1743
  /**
1861
- * @ignore
1744
+ * Clear the form context
1745
+ */
1746
+ clear() {
1747
+ // Clear internal context
1748
+ this.internalContext.set({});
1749
+ // Update the model signal
1750
+ this.context.set({});
1751
+ }
1752
+ /**
1753
+ * Reset the form to its initial state
1754
+ */
1755
+ reset() {
1756
+ // Reset to initial context
1757
+ const resetContext = cloneDeep(this.initialContext);
1758
+ this.internalContext.set(resetContext);
1759
+ // Update the model signal
1760
+ this.context.set(resetContext);
1761
+ }
1762
+ //#endregion
1763
+ //#region ---- RxJS Stream Setup ----
1764
+ /**
1765
+ * Setup RxJS streams for context management
1862
1766
  */
1863
- constructor(instances) {
1864
- instances?.forEach((f) => {
1865
- f();
1767
+ setupContextStreams() {
1768
+ // Debounced context updates from external source
1769
+ this.contextUpdateSubject
1770
+ .pipe(debounceTime(16), // ~60fps
1771
+ distinctUntilChanged((prev, curr) => isEqual(prev, curr)), startWith(this.context() ?? {}))
1772
+ .subscribe((ctx) => {
1773
+ this.internalContext.set(ctx);
1866
1774
  });
1775
+ // Debounced context changes from widgets
1776
+ this.contextChangeSubject
1777
+ .pipe(debounceTime(16), // ~60fps
1778
+ distinctUntilChanged((prev, curr) => isEqual(prev, curr)))
1779
+ .subscribe((ctx) => {
1780
+ this.internalContext.set(ctx);
1781
+ // Update the model signal directly - it will emit change events automatically
1782
+ this.context.set(this.internalContext());
1783
+ });
1784
+ }
1785
+ //#endregion
1786
+ //#region ---- Type Guards ----
1787
+ /**
1788
+ * Type guard to check if the input is a form definition
1789
+ */
1790
+ isFormDefinition(data) {
1791
+ return data && typeof data === 'object' && 'groups' in data && Array.isArray(data.groups);
1792
+ }
1793
+ //#endregion
1794
+ //#region ---- Utility Methods ----
1795
+ /**
1796
+ * Generate layout hash for change detection (short hash)
1797
+ */
1798
+ generateLayoutHash(layout, look) {
1799
+ if (!layout)
1800
+ return '';
1801
+ // Generate short hash for large layout strings
1802
+ const layoutStr = typeof layout === 'string' ? layout : JSON.stringify(layout);
1803
+ const layoutHash = this.simpleHash(layoutStr);
1804
+ return `${layoutHash}|${look}`;
1805
+ }
1806
+ /**
1807
+ * Generate cache key for expression evaluation (short hash)
1808
+ */
1809
+ generateCacheKey(expression, context) {
1810
+ // Use short hash for better performance
1811
+ const exprStr = typeof expression === 'string' ? expression : JSON.stringify(expression);
1812
+ const exprHash = this.simpleHash(exprStr);
1813
+ const ctxHash = this.generateContextHash(context);
1814
+ return `${exprHash}|${ctxHash}`;
1815
+ }
1816
+ /**
1817
+ * Generate a simple hash for context change detection
1818
+ */
1819
+ generateContextHash(context) {
1820
+ if (!context || typeof context !== 'object') {
1821
+ return String(context);
1822
+ }
1823
+ // Generate short hash for context
1824
+ const keys = Object.keys(context).sort();
1825
+ const contextStr = keys.map((key) => `${key}:${context[key]}`).join('|');
1826
+ return this.simpleHash(contextStr);
1867
1827
  }
1868
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPLayoutBuilderModule, deps: [{ token: 'AXPLayoutBuilderModuleFactory', optional: true }], target: i0.ɵɵFactoryTarget.NgModule }); }
1869
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.1.8", ngImport: i0, type: AXPLayoutBuilderModule, declarations: [AXPWidgetContainerComponent, AXPWidgetColumnRendererComponent, AXPWidgetRendererDirective], imports: [CommonModule, PortalModule, AXSkeletonModule, CommonModule, AXTranslationModule], exports: [AXPWidgetContainerComponent, AXPWidgetColumnRendererComponent, AXPWidgetRendererDirective] }); }
1870
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPLayoutBuilderModule, imports: [CommonModule, PortalModule, AXSkeletonModule, CommonModule, AXTranslationModule] }); }
1828
+ /**
1829
+ * Simple hash function for generating short keys
1830
+ */
1831
+ simpleHash(str) {
1832
+ let hash = 0;
1833
+ if (str.length === 0)
1834
+ return hash.toString();
1835
+ for (let i = 0; i < str.length; i++) {
1836
+ const char = str.charCodeAt(i);
1837
+ hash = (hash << 5) - hash + char;
1838
+ hash = hash & hash; // Convert to 32-bit integer
1839
+ }
1840
+ // Convert to positive hex string
1841
+ return Math.abs(hash).toString(16);
1842
+ }
1843
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AXPLayoutRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1844
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", 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: `
1845
+ <ax-form>
1846
+ <axp-widgets-container [context]="internalContext()" (onContextChanged)="handleContextChanged($event)">
1847
+ @if (widgetTree()) {
1848
+ <ng-container axp-widget-renderer [node]="widgetTree()!" [mode]="mode()"></ng-container>
1849
+ }
1850
+ </axp-widgets-container>
1851
+ </ax-form>
1852
+ `, 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"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXFormModule }, { kind: "component", type: i2.AXFormComponent, selector: "ax-form", inputs: ["labelMode", "look", "messageStyle", "updateOn"], outputs: ["onValidate", "updateOnChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1853
+ }
1854
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AXPLayoutRendererComponent, decorators: [{
1855
+ type: Component,
1856
+ args: [{ selector: 'axp-layout-renderer', standalone: true, imports: [CommonModule, AXPWidgetCoreModule, AXFormModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
1857
+ <ax-form>
1858
+ <axp-widgets-container [context]="internalContext()" (onContextChanged)="handleContextChanged($event)">
1859
+ @if (widgetTree()) {
1860
+ <ng-container axp-widget-renderer [node]="widgetTree()!" [mode]="mode()"></ng-container>
1861
+ }
1862
+ </axp-widgets-container>
1863
+ </ax-form>
1864
+ `, styles: [":host{display:block;width:100%}\n"] }]
1865
+ }] });
1866
+
1867
+ class LayoutBuilderModule {
1868
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: LayoutBuilderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1869
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.4", ngImport: i0, type: LayoutBuilderModule, imports: [CommonModule, AXPLayoutRendererComponent], exports: [AXPLayoutRendererComponent] }); }
1870
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: LayoutBuilderModule, providers: [AXPLayoutBuilderService], imports: [CommonModule, AXPLayoutRendererComponent] }); }
1871
1871
  }
1872
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: AXPLayoutBuilderModule, decorators: [{
1872
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: LayoutBuilderModule, decorators: [{
1873
1873
  type: NgModule,
1874
1874
  args: [{
1875
- imports: [CommonModule, PortalModule, AXSkeletonModule, CommonModule, AXTranslationModule],
1876
- exports: [...COMPONENTS],
1877
- declarations: [...COMPONENTS],
1875
+ imports: [CommonModule, AXPLayoutRendererComponent],
1876
+ providers: [AXPLayoutBuilderService],
1877
+ exports: [AXPLayoutRendererComponent],
1878
1878
  }]
1879
- }], ctorParameters: () => [{ type: undefined, decorators: [{
1880
- type: Optional
1881
- }, {
1882
- type: Inject,
1883
- args: ['AXPLayoutBuilderModuleFactory']
1884
- }] }] });
1879
+ }] });
1885
1880
 
1886
- class AXPPropertyEditorHelper {
1887
- static expandShorthand(values) {
1888
- switch (values.length) {
1889
- case 1:
1890
- return [values[0], values[0], values[0], values[0]];
1891
- case 2:
1892
- return [values[0], values[1], values[0], values[1]];
1893
- case 3:
1894
- return [values[0], values[1], values[2], values[1]];
1895
- case 4:
1896
- return values;
1897
- default:
1898
- throw new Error(`Invalid shorthand value count. Input: ${values}`);
1899
- }
1900
- }
1901
- static condenseShorthand(values) {
1902
- if (values.length !== 4) {
1903
- throw new Error('Expected 4 values for condensation.');
1904
- }
1905
- if (values[0] === values[1] && values[1] === values[2] && values[2] === values[3]) {
1906
- return `${values[0]}`;
1907
- }
1908
- else if (values[0] === values[2] && values[1] === values[3]) {
1909
- return `${values[0]} ${values[1]}`;
1910
- }
1911
- else if (values[1] === values[3]) {
1912
- return `${values[0]} ${values[1]} ${values[2]}`;
1913
- }
1914
- else {
1915
- return `${values[0]} ${values[1]} ${values[2]} ${values[3]}`;
1916
- }
1881
+ //#endregion
1882
+
1883
+ class AXPDialogRendererComponent extends AXBasePageComponent {
1884
+ constructor() {
1885
+ super(...arguments);
1886
+ this.result = new EventEmitter();
1887
+ this.context = signal({}, ...(ngDevMode ? [{ debugName: "context" }] : []));
1888
+ // This will be set by the popup service automatically - same as dynamic-dialog
1889
+ this.callBack = () => { };
1890
+ this.isDialogLoading = signal(false, ...(ngDevMode ? [{ debugName: "isDialogLoading" }] : []));
1917
1891
  }
1918
- static parseSides(input) {
1919
- const values = this.expandShorthand(input.match(/(?:rgb\([^)]+\)|[^ ]+)/g)?.map((value) => value.trim()) || []);
1920
- return { top: values[0], right: values[1], bottom: values[2], left: values[3] };
1892
+ ngOnInit() {
1893
+ // Initialize context with provided context
1894
+ this.context.set(this.config?.context || {});
1921
1895
  }
1922
- static parseSidesWithUnits(input) {
1923
- const values = this.expandShorthand(input.match(/(?:rgb\([^)]+\)|[^ ]+)/g)?.map((value) => value.trim()) || []);
1924
- return {
1925
- top: AXPPropertyEditorHelper.getValueWithUnit(values[0]).value,
1926
- right: AXPPropertyEditorHelper.getValueWithUnit(values[1]).value,
1927
- bottom: AXPPropertyEditorHelper.getValueWithUnit(values[2]).value,
1928
- left: AXPPropertyEditorHelper.getValueWithUnit(values[3]).value,
1929
- };
1896
+ handleContextChanged(event) {
1897
+ this.context.set(event);
1930
1898
  }
1931
- static parseCorners(input) {
1932
- const values = this.expandShorthand(input.split(' ').map((value) => value.trim()));
1933
- return {
1934
- 'top-left': AXPPropertyEditorHelper.getValueWithUnit(values[0]).value,
1935
- 'top-right': AXPPropertyEditorHelper.getValueWithUnit(values[1]).value,
1936
- 'bottom-left': AXPPropertyEditorHelper.getValueWithUnit(values[2]).value,
1937
- 'bottom-right': AXPPropertyEditorHelper.getValueWithUnit(values[3]).value,
1938
- };
1899
+ handleContextInitiated(event) {
1900
+ this.context.set(event);
1939
1901
  }
1940
- static parseSpacingBox(input) {
1941
- return {
1942
- margin: this.parseSidesWithUnits(input.margin),
1943
- padding: this.parseSidesWithUnits(input.padding),
1944
- };
1902
+ visibleFooterPrefixActions() {
1903
+ return this.config?.actions?.footer?.prefix || [];
1945
1904
  }
1946
- static parseBorderBox(input) {
1947
- return {
1948
- width: this.parseSidesWithUnits(input.width),
1949
- radius: this.parseCorners(input.radius),
1950
- color: this.parseSides(input.color),
1951
- style: this.parseSides(input.style),
1952
- };
1905
+ visibleFooterSuffixActions() {
1906
+ return (this.config?.actions?.footer?.suffix || [
1907
+ { title: 'Cancel', color: 'secondary', command: { name: 'cancel' } },
1908
+ { title: 'Confirm', color: 'primary', command: { name: 'confirm' } },
1909
+ ]);
1953
1910
  }
1954
- static parseSpacingBoxReverse(input, units) {
1955
- const format = (value, unit) => `${value}${unit}`;
1956
- return {
1957
- margin: AXPPropertyEditorHelper.condenseShorthand([
1958
- format(input.margin.top, units.margin.top),
1959
- format(input.margin.right, units.margin.right),
1960
- format(input.margin.bottom, units.margin.bottom),
1961
- format(input.margin.left, units.margin.left),
1962
- ]),
1963
- padding: AXPPropertyEditorHelper.condenseShorthand([
1964
- format(input.padding.top, units.padding.top),
1965
- format(input.padding.right, units.padding.right),
1966
- format(input.padding.bottom, units.padding.bottom),
1967
- format(input.padding.left, units.padding.left),
1968
- ]),
1969
- };
1911
+ isFormLoading() {
1912
+ return this.isDialogLoading();
1970
1913
  }
1971
- static parseBorderBoxReverse(input, units) {
1972
- const format = (value, unit) => `${value}${unit}`;
1973
- return {
1974
- width: AXPPropertyEditorHelper.condenseShorthand([
1975
- format(input.width.top, units.width.top),
1976
- format(input.width.right, units.width.right),
1977
- format(input.width.bottom, units.width.bottom),
1978
- format(input.width.left, units.width.left),
1979
- ]),
1980
- radius: AXPPropertyEditorHelper.condenseShorthand([
1981
- format(input.radius['top-left'], units.radius['top-left']),
1982
- format(input.radius['top-right'], units.radius['top-right']),
1983
- format(input.radius['bottom-right'], units.radius['bottom-right']),
1984
- format(input.radius['bottom-left'], units.radius['bottom-left']),
1985
- ]),
1986
- color: AXPPropertyEditorHelper.condenseShorthand([
1987
- `${input.color.top}${units.color.top}`,
1988
- `${input.color.right}${units.color.right}`,
1989
- `${input.color.bottom}${units.color.bottom}`,
1990
- `${input.color.left}${units.color.left}`,
1991
- ]),
1992
- style: AXPPropertyEditorHelper.condenseShorthand([
1993
- `${input.style.top}${units.style.top}`,
1994
- `${input.style.right}${units.style.right}`,
1995
- `${input.style.bottom}${units.style.bottom}`,
1996
- `${input.style.left}${units.style.left}`,
1997
- ]),
1998
- };
1914
+ isSubmitting() {
1915
+ return this.isDialogLoading();
1999
1916
  }
2000
- static getValueWithUnit(input) {
2001
- if (typeof input === 'number')
2002
- return { value: input, unit: 'px' };
2003
- if (input === 'auto')
2004
- return { value: 0, unit: 'px' };
2005
- const match = input.match(/^([0-9.]+)([a-z%]*)$/i);
2006
- if (!match)
2007
- throw new Error(`Invalid unit format: ${input}`);
2008
- return { value: parseFloat(match[1]), unit: match[2] || '' };
2009
- }
2010
- static getValueFromUnit(value, unit) {
2011
- return unit ? `${value}${unit}` : `${value}`;
2012
- }
2013
- static parseGap(gap) {
2014
- const parts = gap.split(/\s+/);
2015
- const match = parts[0].match(/^(\d+\.?\d*)([a-z%]+)$/);
2016
- if (!match) {
2017
- throw new Error('Invalid gap format');
2018
- }
2019
- const [, xValue, unit] = match;
2020
- let yValue = parseFloat(xValue);
2021
- if (parts.length === 2) {
2022
- const secondMatch = parts[1].match(/^(\d+\.?\d*)[a-z%]+$/);
2023
- if (!secondMatch) {
2024
- throw new Error('Invalid gap format');
2025
- }
2026
- yValue = parseFloat(secondMatch[1]);
2027
- }
2028
- return {
2029
- values: {
2030
- x: parseFloat(xValue),
2031
- y: yValue,
2032
- },
2033
- unit,
1917
+ async executeAction(action) {
1918
+ const actionName = action.command?.name || action.title?.toLowerCase();
1919
+ // Store the action and context - same pattern as dynamic-dialog
1920
+ const result = {
1921
+ context: this.context(),
1922
+ action: actionName,
2034
1923
  };
2035
- }
2036
- static parseGridTemplate(gridTemplate) {
2037
- const match = gridTemplate.match(/^repeat\((\d+),\s*(?:1fr|auto|minmax\([^)]*\))\)$/);
2038
- if (!match) {
2039
- throw new Error("Invalid grid template format. Expected 'repeat(N, 1fr|auto|minmax(...))'.");
1924
+ // Store result in component property
1925
+ this.dialogResult = result;
1926
+ // Store in popup data for DialogRef access
1927
+ if (this.data) {
1928
+ this.data.context = result.context;
1929
+ this.data.action = result.action;
2040
1930
  }
2041
- return parseInt(match[1], 10);
2042
- }
2043
- static createGridTemplate(repetitionCount) {
2044
- if (repetitionCount <= 0) {
2045
- throw new Error('Repetition count must be a positive integer.');
2046
- }
2047
- return `repeat(${repetitionCount}, 1fr)`;
1931
+ // Call the callback with DialogRef - same pattern as dynamic-dialog
1932
+ this.callBack({
1933
+ close: (result) => {
1934
+ this.close(result);
1935
+ },
1936
+ context: () => {
1937
+ return this.context();
1938
+ },
1939
+ action: () => {
1940
+ return result.action;
1941
+ },
1942
+ setLoading: (loading) => {
1943
+ this.isDialogLoading.set(loading);
1944
+ },
1945
+ });
2048
1946
  }
2049
- }
2050
- function findNonEmptyBreakpoints(values) {
2051
- const breakpoints = ['default', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'];
2052
- const nonEmptyBreakpoints = [];
2053
- for (const breakpoint of breakpoints) {
2054
- if (values[breakpoint] !== undefined) {
2055
- nonEmptyBreakpoints.push(breakpoint);
1947
+ close(result) {
1948
+ if (result) {
1949
+ this.result.emit(result);
2056
1950
  }
2057
- }
2058
- return nonEmptyBreakpoints;
1951
+ super.close(result);
1952
+ }
1953
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AXPDialogRendererComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
1954
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: AXPDialogRendererComponent, isStandalone: true, selector: "axp-dialog-renderer", inputs: { config: "config" }, outputs: { result: "result" }, usesInheritance: true, ngImport: i0, template: `
1955
+ <div class="ax-p-4">
1956
+ <axp-layout-renderer
1957
+ [layout]="config.definition"
1958
+ [context]="context()"
1959
+ (contextChange)="handleContextChanged($event)"
1960
+ (contextInitiated)="handleContextInitiated($event)"
1961
+ >
1962
+ </axp-layout-renderer>
1963
+ </div>
1964
+
1965
+ <ax-footer>
1966
+ <ax-prefix>
1967
+ <ng-container *ngTemplateOutlet="footerPrefixActions"></ng-container>
1968
+ </ax-prefix>
1969
+ <ax-suffix>
1970
+ <ng-container *ngTemplateOutlet="footerSuffixActions"></ng-container>
1971
+ </ax-suffix>
1972
+ </ax-footer>
1973
+
1974
+ <!-- Footer Prefix Actions -->
1975
+ <ng-template #footerPrefixActions>
1976
+ @for (action of visibleFooterPrefixActions(); track $index) {
1977
+ <ax-button
1978
+ [disabled]="action.disabled || isFormLoading()"
1979
+ [text]="(action.title | translate | async)!"
1980
+ [look]="'outline'"
1981
+ [color]="action.color"
1982
+ (onClick)="executeAction(action)"
1983
+ >
1984
+ @if (isFormLoading() && action.command?.name != 'cancel') {
1985
+ <ax-loading></ax-loading>
1986
+ }
1987
+ <ax-prefix>
1988
+ <i class="{{ action.icon }}"></i>
1989
+ </ax-prefix>
1990
+ </ax-button>
1991
+ }
1992
+ </ng-template>
1993
+
1994
+ <!-- Footer Suffix Actions -->
1995
+ <ng-template #footerSuffixActions>
1996
+ @for (action of visibleFooterSuffixActions(); track $index) {
1997
+ <ax-button
1998
+ [disabled]="action.disabled || isSubmitting()"
1999
+ [text]="(action.title | translate | async)!"
2000
+ [look]="'solid'"
2001
+ [color]="action.color"
2002
+ (onClick)="executeAction(action)"
2003
+ >
2004
+ @if (action.icon) {
2005
+ <ax-prefix>
2006
+ <ax-icon icon="{{ action.icon }}"></ax-icon>
2007
+ </ax-prefix>
2008
+ }
2009
+ </ax-button>
2010
+ }
2011
+ </ng-template>
2012
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: AXPLayoutRendererComponent, selector: "axp-layout-renderer", inputs: ["layout", "context", "look", "mode"], outputs: ["contextChange", "contextInitiated", "validityChange"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i2$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: i3.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i3.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: i4.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i1$1.AsyncPipe, name: "async" }, { kind: "pipe", type: i5.AXTranslatorPipe, name: "translate" }] }); }
2059
2013
  }
2014
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: AXPDialogRendererComponent, decorators: [{
2015
+ type: Component,
2016
+ args: [{
2017
+ selector: 'axp-dialog-renderer',
2018
+ standalone: true,
2019
+ imports: [
2020
+ CommonModule,
2021
+ AXPLayoutRendererComponent,
2022
+ AXButtonModule,
2023
+ AXDecoratorModule,
2024
+ AXLoadingModule,
2025
+ AXTranslationModule,
2026
+ ],
2027
+ template: `
2028
+ <div class="ax-p-4">
2029
+ <axp-layout-renderer
2030
+ [layout]="config.definition"
2031
+ [context]="context()"
2032
+ (contextChange)="handleContextChanged($event)"
2033
+ (contextInitiated)="handleContextInitiated($event)"
2034
+ >
2035
+ </axp-layout-renderer>
2036
+ </div>
2037
+
2038
+ <ax-footer>
2039
+ <ax-prefix>
2040
+ <ng-container *ngTemplateOutlet="footerPrefixActions"></ng-container>
2041
+ </ax-prefix>
2042
+ <ax-suffix>
2043
+ <ng-container *ngTemplateOutlet="footerSuffixActions"></ng-container>
2044
+ </ax-suffix>
2045
+ </ax-footer>
2060
2046
 
2061
- const AXP_WIDGETS_LAYOUT_CATEGORY = {
2062
- name: 'layout',
2063
- order: 1,
2064
- title: 'Layout',
2065
- };
2066
- const AXP_WIDGETS_EDITOR_CATEGORY = {
2067
- name: 'editor',
2068
- order: 2,
2069
- title: 'Editors',
2070
- };
2071
- const AXP_WIDGETS_ACTION_CATEGORY = {
2072
- name: 'action',
2073
- order: 3,
2074
- title: 'Action',
2075
- };
2076
- const AXP_WIDGETS_ADVANCE_CATEGORY = {
2077
- name: 'advance',
2078
- order: 4,
2079
- title: 'Advance',
2080
- };
2081
- const AXP_WIDGETS_CATEGORIES = [
2082
- AXP_WIDGETS_LAYOUT_CATEGORY,
2083
- AXP_WIDGETS_EDITOR_CATEGORY,
2084
- AXP_WIDGETS_ACTION_CATEGORY,
2085
- AXP_WIDGETS_ADVANCE_CATEGORY,
2086
- ];
2047
+ <!-- Footer Prefix Actions -->
2048
+ <ng-template #footerPrefixActions>
2049
+ @for (action of visibleFooterPrefixActions(); track $index) {
2050
+ <ax-button
2051
+ [disabled]="action.disabled || isFormLoading()"
2052
+ [text]="(action.title | translate | async)!"
2053
+ [look]="'outline'"
2054
+ [color]="action.color"
2055
+ (onClick)="executeAction(action)"
2056
+ >
2057
+ @if (isFormLoading() && action.command?.name != 'cancel') {
2058
+ <ax-loading></ax-loading>
2059
+ }
2060
+ <ax-prefix>
2061
+ <i class="{{ action.icon }}"></i>
2062
+ </ax-prefix>
2063
+ </ax-button>
2064
+ }
2065
+ </ng-template>
2066
+
2067
+ <!-- Footer Suffix Actions -->
2068
+ <ng-template #footerSuffixActions>
2069
+ @for (action of visibleFooterSuffixActions(); track $index) {
2070
+ <ax-button
2071
+ [disabled]="action.disabled || isSubmitting()"
2072
+ [text]="(action.title | translate | async)!"
2073
+ [look]="'solid'"
2074
+ [color]="action.color"
2075
+ (onClick)="executeAction(action)"
2076
+ >
2077
+ @if (action.icon) {
2078
+ <ax-prefix>
2079
+ <ax-icon icon="{{ action.icon }}"></ax-icon>
2080
+ </ax-prefix>
2081
+ }
2082
+ </ax-button>
2083
+ }
2084
+ </ng-template>
2085
+ `,
2086
+ }]
2087
+ }], propDecorators: { config: [{
2088
+ type: Input
2089
+ }], result: [{
2090
+ type: Output
2091
+ }] } });
2087
2092
 
2088
- var AXPWidgetGroupEnum;
2089
- (function (AXPWidgetGroupEnum) {
2090
- AXPWidgetGroupEnum["FormElement"] = "form-element";
2091
- AXPWidgetGroupEnum["DashboardWidget"] = "dashboard-widget";
2092
- AXPWidgetGroupEnum["FormTemplate"] = "form-template";
2093
- AXPWidgetGroupEnum["PropertyEditor"] = "property-editor";
2094
- AXPWidgetGroupEnum["MetaData"] = "meta-data";
2095
- AXPWidgetGroupEnum["SettingWidget"] = "setting-widget";
2096
- AXPWidgetGroupEnum["EntityWidget"] = "entity-widget";
2097
- })(AXPWidgetGroupEnum || (AXPWidgetGroupEnum = {}));
2093
+ var dialogRenderer_component = /*#__PURE__*/Object.freeze({
2094
+ __proto__: null,
2095
+ AXPDialogRendererComponent: AXPDialogRendererComponent
2096
+ });
2098
2097
 
2099
2098
  /**
2100
2099
  * Generated bundle index. Do not edit.
2101
2100
  */
2102
2101
 
2103
- export { AXPBaseWidgetComponent, AXPBlockBaseLayoutWidgetComponent, AXPBoxModelLayoutWidgetComponent, AXPColumnWidgetComponent, AXPDataListWidgetComponent, AXPFlexBaseLayoutWidgetComponent, AXPFlexItemBaseLayoutWidgetComponent, AXPGridBaseLayoutWidgetComponent, AXPGridItemBaseLayoutWidgetComponent, AXPInlineBaseLayoutWidgetComponent, AXPLayoutBaseWidgetComponent, AXPLayoutBuilderContextStore, AXPLayoutBuilderModule, AXPLayoutBuilderService, AXPLayoutContextChangeEvent, AXPLayoutElement, AXPPageStatus, AXPPropertyEditorHelper, AXPValueWidgetComponent, AXPWidgetColumnRendererComponent, AXPWidgetContainerComponent, AXPWidgetGroupEnum, AXPWidgetRegistryService, AXPWidgetRendererDirective, AXPWidgetStatus, AXPWidgetsCatalog, AXP_WIDGETS_ACTION_CATEGORY, AXP_WIDGETS_ADVANCE_CATEGORY, AXP_WIDGETS_CATEGORIES, AXP_WIDGETS_EDITOR_CATEGORY, AXP_WIDGETS_LAYOUT_CATEGORY, AXP_WIDGET_COLUMN_TOKEN, AXP_WIDGET_TOKEN, cloneProperty, createBooleanProperty, createNumberProperty, createSelectProperty, createStringProperty, findNonEmptyBreakpoints };
2102
+ export { AXPDialogRendererComponent, AXPLayoutBuilderService, AXPLayoutConversionService, AXPLayoutRendererComponent, LayoutBuilderModule };
2104
2103
  //# sourceMappingURL=acorex-platform-layout-builder.mjs.map