@acorex/platform 20.3.0-next.14 → 20.3.0-next.15

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 (84) hide show
  1. package/fesm2022/acorex-platform-auth.mjs +19 -19
  2. package/fesm2022/acorex-platform-auth.mjs.map +1 -1
  3. package/fesm2022/acorex-platform-common.mjs +99 -99
  4. package/fesm2022/acorex-platform-common.mjs.map +1 -1
  5. package/fesm2022/acorex-platform-core.mjs +42 -42
  6. package/fesm2022/acorex-platform-core.mjs.map +1 -1
  7. package/fesm2022/acorex-platform-domain.mjs +16 -16
  8. package/fesm2022/acorex-platform-domain.mjs.map +1 -1
  9. package/fesm2022/acorex-platform-layout-builder.mjs +1535 -969
  10. package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
  11. package/fesm2022/acorex-platform-layout-components.mjs +2189 -1567
  12. package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
  13. package/fesm2022/acorex-platform-layout-designer.mjs +73 -73
  14. package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
  15. package/fesm2022/{acorex-platform-layout-entity-create-entity.command-CuueLekJ.mjs → acorex-platform-layout-entity-create-entity.command-CFBqiwfy.mjs} +4 -4
  16. package/fesm2022/{acorex-platform-layout-entity-create-entity.command-CuueLekJ.mjs.map → acorex-platform-layout-entity-create-entity.command-CFBqiwfy.mjs.map} +1 -1
  17. package/fesm2022/acorex-platform-layout-entity.mjs +160 -161
  18. package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
  19. package/fesm2022/acorex-platform-layout-views.mjs +15 -15
  20. package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
  21. package/fesm2022/acorex-platform-layout-widget-core.mjs +163 -74
  22. package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
  23. package/fesm2022/{acorex-platform-layout-widgets-button-widget-designer.component-C82aG5Rf.mjs → acorex-platform-layout-widgets-button-widget-designer.component-BzsfTNs2.mjs} +4 -4
  24. package/fesm2022/{acorex-platform-layout-widgets-button-widget-designer.component-C82aG5Rf.mjs.map → acorex-platform-layout-widgets-button-widget-designer.component-BzsfTNs2.mjs.map} +1 -1
  25. package/fesm2022/{acorex-platform-layout-widgets-extra-properties-schema-widget-edit.component-CMmz70I8.mjs → acorex-platform-layout-widgets-extra-properties-schema-widget-edit.component-Dvk76-2W.mjs} +4 -4
  26. package/fesm2022/{acorex-platform-layout-widgets-extra-properties-schema-widget-edit.component-CMmz70I8.mjs.map → acorex-platform-layout-widgets-extra-properties-schema-widget-edit.component-Dvk76-2W.mjs.map} +1 -1
  27. package/fesm2022/{acorex-platform-layout-widgets-extra-properties-schema-widget-view.component-DDOyf7NG.mjs → acorex-platform-layout-widgets-extra-properties-schema-widget-view.component-BYLaipWi.mjs} +4 -4
  28. package/fesm2022/{acorex-platform-layout-widgets-extra-properties-schema-widget-view.component-DDOyf7NG.mjs.map → acorex-platform-layout-widgets-extra-properties-schema-widget-view.component-BYLaipWi.mjs.map} +1 -1
  29. package/fesm2022/{acorex-platform-layout-widgets-extra-properties-values-widget-edit.component-flsveRJc.mjs → acorex-platform-layout-widgets-extra-properties-values-widget-edit.component-DcSllNik.mjs} +4 -4
  30. package/fesm2022/{acorex-platform-layout-widgets-extra-properties-values-widget-edit.component-flsveRJc.mjs.map → acorex-platform-layout-widgets-extra-properties-values-widget-edit.component-DcSllNik.mjs.map} +1 -1
  31. package/fesm2022/{acorex-platform-layout-widgets-extra-properties-values-widget-view.component-CFXLM9Ls.mjs → acorex-platform-layout-widgets-extra-properties-values-widget-view.component-BT-U4BiA.mjs} +4 -4
  32. package/fesm2022/{acorex-platform-layout-widgets-extra-properties-values-widget-view.component-CFXLM9Ls.mjs.map → acorex-platform-layout-widgets-extra-properties-values-widget-view.component-BT-U4BiA.mjs.map} +1 -1
  33. package/fesm2022/{acorex-platform-layout-widgets-extra-properties-widget-edit.component-1Wd5IUpo.mjs → acorex-platform-layout-widgets-extra-properties-widget-edit.component-Il7jnRBg.mjs} +4 -4
  34. package/fesm2022/{acorex-platform-layout-widgets-extra-properties-widget-edit.component-1Wd5IUpo.mjs.map → acorex-platform-layout-widgets-extra-properties-widget-edit.component-Il7jnRBg.mjs.map} +1 -1
  35. package/fesm2022/{acorex-platform-layout-widgets-extra-properties-widget-view.component-WLyXXg19.mjs → acorex-platform-layout-widgets-extra-properties-widget-view.component-CBEPu7Fl.mjs} +4 -4
  36. package/fesm2022/{acorex-platform-layout-widgets-extra-properties-widget-view.component-WLyXXg19.mjs.map → acorex-platform-layout-widgets-extra-properties-widget-view.component-CBEPu7Fl.mjs.map} +1 -1
  37. package/fesm2022/{acorex-platform-layout-widgets-file-list-popup.component-DxTXIO_k.mjs → acorex-platform-layout-widgets-file-list-popup.component-BPzn8lr3.mjs} +5 -5
  38. package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-BPzn8lr3.mjs.map +1 -0
  39. package/fesm2022/{acorex-platform-layout-widgets-page-widget-designer.component-44YSAqDc.mjs → acorex-platform-layout-widgets-page-widget-designer.component-C_JrGoXy.mjs} +4 -4
  40. package/fesm2022/{acorex-platform-layout-widgets-page-widget-designer.component-44YSAqDc.mjs.map → acorex-platform-layout-widgets-page-widget-designer.component-C_JrGoXy.mjs.map} +1 -1
  41. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-edit-popup.component-Dat0cqWe.mjs → acorex-platform-layout-widgets-tabular-data-edit-popup.component-C6DaBt_N.mjs} +4 -4
  42. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-edit-popup.component-Dat0cqWe.mjs.map → acorex-platform-layout-widgets-tabular-data-edit-popup.component-C6DaBt_N.mjs.map} +1 -1
  43. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-view-popup.component-CtJBzqq_.mjs → acorex-platform-layout-widgets-tabular-data-view-popup.component-Bth3jI9T.mjs} +4 -4
  44. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-view-popup.component-CtJBzqq_.mjs.map → acorex-platform-layout-widgets-tabular-data-view-popup.component-Bth3jI9T.mjs.map} +1 -1
  45. package/fesm2022/{acorex-platform-layout-widgets-text-block-widget-designer.component-Djpj1fNO.mjs → acorex-platform-layout-widgets-text-block-widget-designer.component-CUHptbP4.mjs} +4 -4
  46. package/fesm2022/{acorex-platform-layout-widgets-text-block-widget-designer.component-Djpj1fNO.mjs.map → acorex-platform-layout-widgets-text-block-widget-designer.component-CUHptbP4.mjs.map} +1 -1
  47. package/fesm2022/acorex-platform-layout-widgets.mjs +697 -664
  48. package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
  49. package/fesm2022/acorex-platform-native.mjs +7 -7
  50. package/fesm2022/acorex-platform-native.mjs.map +1 -1
  51. package/fesm2022/acorex-platform-runtime.mjs +40 -40
  52. package/fesm2022/acorex-platform-runtime.mjs.map +1 -1
  53. package/fesm2022/{acorex-platform-themes-default-entity-master-create-view.component-oOA674Jd.mjs → acorex-platform-themes-default-entity-master-create-view.component-eGzN6g2Y.mjs} +4 -4
  54. package/fesm2022/{acorex-platform-themes-default-entity-master-create-view.component-oOA674Jd.mjs.map → acorex-platform-themes-default-entity-master-create-view.component-eGzN6g2Y.mjs.map} +1 -1
  55. package/fesm2022/{acorex-platform-themes-default-entity-master-list-view.component-Bk4p9oHD.mjs → acorex-platform-themes-default-entity-master-list-view.component-DjNvH3OA.mjs} +10 -10
  56. package/fesm2022/{acorex-platform-themes-default-entity-master-list-view.component-Bk4p9oHD.mjs.map → acorex-platform-themes-default-entity-master-list-view.component-DjNvH3OA.mjs.map} +1 -1
  57. package/fesm2022/{acorex-platform-themes-default-entity-master-modify-view.component-CrDQOCpB.mjs → acorex-platform-themes-default-entity-master-modify-view.component-HJyalvcu.mjs} +4 -4
  58. package/fesm2022/{acorex-platform-themes-default-entity-master-modify-view.component-CrDQOCpB.mjs.map → acorex-platform-themes-default-entity-master-modify-view.component-HJyalvcu.mjs.map} +1 -1
  59. package/fesm2022/{acorex-platform-themes-default-entity-master-single-view.component-CT-1gX4H.mjs → acorex-platform-themes-default-entity-master-single-view.component-e7m70Wls.mjs} +7 -7
  60. package/fesm2022/{acorex-platform-themes-default-entity-master-single-view.component-CT-1gX4H.mjs.map → acorex-platform-themes-default-entity-master-single-view.component-e7m70Wls.mjs.map} +1 -1
  61. package/fesm2022/{acorex-platform-themes-default-error-401.component-CHJFmJ2W.mjs → acorex-platform-themes-default-error-401.component-CoBaQFTn.mjs} +4 -4
  62. package/fesm2022/{acorex-platform-themes-default-error-401.component-CHJFmJ2W.mjs.map → acorex-platform-themes-default-error-401.component-CoBaQFTn.mjs.map} +1 -1
  63. package/fesm2022/{acorex-platform-themes-default-error-404.component-Db8KkVIF.mjs → acorex-platform-themes-default-error-404.component-BLlVOsS2.mjs} +4 -4
  64. package/fesm2022/{acorex-platform-themes-default-error-404.component-Db8KkVIF.mjs.map → acorex-platform-themes-default-error-404.component-BLlVOsS2.mjs.map} +1 -1
  65. package/fesm2022/{acorex-platform-themes-default-error-offline.component-DH39Viy-.mjs → acorex-platform-themes-default-error-offline.component-CybYQI9F.mjs} +4 -4
  66. package/fesm2022/{acorex-platform-themes-default-error-offline.component-DH39Viy-.mjs.map → acorex-platform-themes-default-error-offline.component-CybYQI9F.mjs.map} +1 -1
  67. package/fesm2022/acorex-platform-themes-default.mjs +39 -39
  68. package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
  69. package/fesm2022/{acorex-platform-themes-shared-icon-chooser-view.component-mFBYGE0_.mjs → acorex-platform-themes-shared-icon-chooser-view.component-ReKSoVeN.mjs} +4 -4
  70. package/fesm2022/{acorex-platform-themes-shared-icon-chooser-view.component-mFBYGE0_.mjs.map → acorex-platform-themes-shared-icon-chooser-view.component-ReKSoVeN.mjs.map} +1 -1
  71. package/fesm2022/{acorex-platform-themes-shared-theme-color-chooser-column.component-viyyova6.mjs → acorex-platform-themes-shared-theme-color-chooser-column.component-B2HDyY2z.mjs} +4 -4
  72. package/fesm2022/{acorex-platform-themes-shared-theme-color-chooser-column.component-viyyova6.mjs.map → acorex-platform-themes-shared-theme-color-chooser-column.component-B2HDyY2z.mjs.map} +1 -1
  73. package/fesm2022/{acorex-platform-themes-shared-theme-color-chooser-view.component-BrUtKTXd.mjs → acorex-platform-themes-shared-theme-color-chooser-view.component-CeZxa49U.mjs} +4 -4
  74. package/fesm2022/{acorex-platform-themes-shared-theme-color-chooser-view.component-BrUtKTXd.mjs.map → acorex-platform-themes-shared-theme-color-chooser-view.component-CeZxa49U.mjs.map} +1 -1
  75. package/fesm2022/acorex-platform-themes-shared.mjs +42 -42
  76. package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
  77. package/fesm2022/acorex-platform-workflow.mjs +25 -25
  78. package/fesm2022/acorex-platform-workflow.mjs.map +1 -1
  79. package/layout/builder/index.d.ts +389 -237
  80. package/layout/components/index.d.ts +130 -42
  81. package/layout/widget-core/index.d.ts +8 -1
  82. package/layout/widgets/index.d.ts +80 -2
  83. package/package.json +10 -10
  84. package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-DxTXIO_k.mjs.map +0 -1
@@ -1,660 +1,503 @@
1
+ import * as i1$1 from '@angular/common';
1
2
  import { CommonModule } from '@angular/common';
2
3
  import * as i0 from '@angular/core';
3
- import { Injectable, Input, Component, NgModule } 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';
4
6
  import * as i1 from '@acorex/platform/layout/widget-core';
5
- import { AXPWidgetCoreModule } from '@acorex/platform/layout/widget-core';
7
+ import { AXPWidgetContainerComponent, AXPPageStatus, AXPWidgetCoreModule } from '@acorex/platform/layout/widget-core';
8
+ import * as i2 from '@acorex/components/form';
9
+ import { AXFormComponent, AXFormModule } from '@acorex/components/form';
10
+ import { isEqual, get } from 'lodash-es';
11
+ import { AXPExpressionEvaluatorService } from '@acorex/platform/core';
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';
6
22
 
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;
71
+ }
72
+ if (formFieldLabel) {
73
+ return labelToPath(formFieldLabel);
74
+ }
75
+ return generateRandomId();
76
+ }
77
+ //#endregion
78
+ //#region ---- Service Implementation ----
7
79
  class AXPLayoutBuilderService {
80
+ constructor() {
81
+ this.popupService = inject(AXPopupService);
82
+ }
8
83
  /**
9
84
  * Create a new layout builder
10
85
  */
11
86
  create() {
12
- return new LayoutBuilder();
87
+ return new LayoutBuilder(this.popupService);
13
88
  }
14
- /**
15
- * Convert Layout Builder definition to Layout Renderer format
16
- * با processing و cleaning options
17
- */
18
- convertToLayoutRenderer(definition) {
19
- const layoutDefinition = {
20
- zones: {},
21
- styles: definition.layoutConfig ? this.convertGridLayoutToStyles(definition.layoutConfig) : {}
22
- };
23
- const layoutNodes = [];
24
- let nodeCounter = 0;
25
- // Helper: count all nodes recursively
26
- const countNodes = (nodes) => {
27
- if (!nodes || nodes.length === 0)
28
- return 0;
29
- return nodes.reduce((acc, n) => acc + 1 + countNodes(n.children), 0);
30
- };
31
- const totalNodeCount = countNodes(definition.nodes);
32
- const hasMultipleNodes = totalNodeCount > 1;
33
- // Process nodes recursively - flatten everything into the same zone
34
- const processNodes = (nodes, parentZone = 'main') => {
35
- nodes.forEach((node) => {
36
- if (!node.widget)
37
- return;
38
- // Create layout node for widget
39
- const nodeId = node.id || `widget-${nodeCounter++}`;
40
- const layoutNode = {
41
- id: nodeId,
42
- type: node.widget.type,
43
- zone: parentZone,
44
- mode: node.mode || 'edit',
45
- options: node.widget.options || {}
46
- };
47
- // Convert layout config to node layout
48
- if (node.layout) {
49
- layoutNode.layout = this.convertToNodeLayout(node.layout);
50
- }
51
- else if (hasMultipleNodes && definition.layoutConfig) {
52
- // Apply default layout for multi-node structures when node has no explicit layout
53
- layoutNode.layout = this.convertToNodeLayout(definition.layoutConfig);
54
- }
55
- // Add order if specified
56
- if (node.order !== undefined) {
57
- if (!layoutNode.layout)
58
- layoutNode.layout = {};
59
- layoutNode.layout.order = typeof node.order === 'number' ? node.order : parseInt(node.order.toString());
60
- }
61
- // Process widget options and content
62
- this.processWidgetOptions(layoutNode, node);
63
- layoutNodes.push(layoutNode);
64
- // Recursively add children into the same zone (flat)
65
- if (node.children && node.children.length > 0) {
66
- processNodes(node.children, parentZone);
67
- }
68
- });
89
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXPLayoutBuilderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
90
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXPLayoutBuilderService, providedIn: 'root' }); }
91
+ }
92
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXPLayoutBuilderService, decorators: [{
93
+ type: Injectable,
94
+ args: [{
95
+ providedIn: 'root'
96
+ }]
97
+ }] });
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',
69
111
  };
70
- // Create zones in order and assign auto-order to nodes (recursively)
71
- let nodeOrder = 1;
72
- const assignOrder = (nodes) => {
73
- if (!nodes)
74
- return;
75
- nodes.forEach(n => {
76
- if (n.order === undefined || n.order === null) {
77
- n.order = nodeOrder++;
78
- }
79
- if (n.children && n.children.length > 0)
80
- assignOrder(n.children);
81
- });
112
+ this.inheritanceContext = {
113
+ mode: 'edit',
82
114
  };
83
- assignOrder(definition.nodes);
84
- // Always create a default main zone when there are nodes
85
- if (totalNodeCount > 0) {
86
- layoutDefinition.zones['main'] = {
87
- layout: {
88
- type: 'flex',
89
- flexDirection: definition.direction === 'horizontal' ? 'row' : 'column',
90
- gap: '16px'
91
- },
92
- order: 0 // Main zone gets order 0 (first)
93
- };
115
+ }
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);
94
122
  }
95
- processNodes(definition.nodes);
96
- return {
97
- layoutDefinition,
98
- layoutNodes
99
- };
123
+ //this.state.children!.push(container.build());
124
+ this.root = container.build();
125
+ return this;
100
126
  }
101
- // Removed container mapping helpers - all nodes are treated uniformly
102
- /**
103
- * Convert grid layout options to CSS styles
104
- */
105
- convertGridLayoutToStyles(layout) {
106
- const styles = {};
107
- // Handle responsive positions
108
- if (layout.positions) {
109
- // Use largest breakpoint for base styles
110
- const position = layout.positions.xxl || layout.positions.xl || layout.positions.lg ||
111
- layout.positions.md || layout.positions.sm;
112
- if (position) {
113
- if (position.colSpan && position.colSpan < 12) {
114
- styles['width'] = `${(position.colSpan / 12) * 100}%`;
115
- }
116
- }
127
+ flex(delegate) {
128
+ const container = new FlexContainerBuilder();
129
+ container.withInheritanceContext(this.inheritanceContext);
130
+ if (delegate) {
131
+ delegate(container);
117
132
  }
118
- return styles;
133
+ this.root.children.push(container.build());
134
+ return this;
119
135
  }
120
- /**
121
- * Convert grid layout to node layout configuration
122
- */
123
- convertToNodeLayout(layout) {
124
- const nodeLayout = {};
125
- if (layout.positions) {
126
- const position = layout.positions.xxl || layout.positions.xl || layout.positions.lg ||
127
- layout.positions.md || layout.positions.sm;
128
- if (position) {
129
- if (position.colSpan && position.colSpan < 12) {
130
- nodeLayout.flex = `0 0 ${(position.colSpan / 12) * 100}%`;
131
- }
132
- if (position.rowSpan && position.rowSpan > 1) {
133
- nodeLayout.gridArea = `span ${position.rowSpan}`;
134
- }
135
- }
136
+ panel(delegate) {
137
+ const container = new PanelContainerBuilder();
138
+ container.withInheritanceContext(this.inheritanceContext);
139
+ if (delegate) {
140
+ delegate(container);
136
141
  }
137
- return nodeLayout;
142
+ this.root.children.push(container.build());
143
+ return this;
138
144
  }
139
- /**
140
- * Process widget options based on actual widget configurations
141
- */
142
- processWidgetOptions(layoutNode, sourceNode) {
143
- if (!sourceNode.widget?.options)
144
- return;
145
- const options = sourceNode.widget.options;
146
- const processedOptions = { ...options };
147
- // Process based on actual widget type
148
- switch (sourceNode.widget.type) {
149
- case 'text-block-layout':
150
- // Text Block Widget - فقط content
151
- processedOptions.content = this.processTextBlockContent(options);
152
- break;
153
- case 'grid-layout':
154
- // Grid Layout Widget - complete grid system
155
- processedOptions.grid = this.processGridOptions(options);
156
- processedOptions.spacing = this.processSpacingOptions(options);
157
- processedOptions.border = this.processBorderOptions(options);
158
- if (options.backgroundColor)
159
- processedOptions.backgroundColor = options.backgroundColor;
160
- break;
161
- case 'flex-layout':
162
- // Flex Layout Widget
163
- processedOptions.flex = this.processFlexOptions(options);
164
- break;
165
- case 'panel-layout':
166
- // Panel Layout Widget
167
- processedOptions.panel = this.processPanelOptions(options);
168
- break;
169
- case 'tag':
170
- // Tag Widget (custom)
171
- processedOptions.label = options.label || options.content || 'Tag';
172
- processedOptions.tone = options.tone || 'primary';
173
- break;
174
- default:
175
- // Generic processing برای custom widgets
176
- if (options.content) {
177
- processedOptions.content = this.processGenericContent(options);
178
- }
179
- break;
145
+ page(delegate) {
146
+ const container = new PageContainerBuilder();
147
+ container.withInheritanceContext(this.inheritanceContext);
148
+ if (delegate) {
149
+ delegate(container);
180
150
  }
181
- layoutNode.options = processedOptions;
151
+ this.root.children.push(container.build());
152
+ return this;
182
153
  }
183
- /**
184
- * Process text-block-layout content
185
- */
186
- processTextBlockContent(options) {
187
- if (!options.content)
188
- return '';
189
- let content = options.content;
190
- // اگر قبلاً HTML tag داره، استفاده کن
191
- if (content.includes('<')) {
192
- return content;
154
+ tabset(delegate) {
155
+ const container = new TabsetContainerBuilder();
156
+ container.withInheritanceContext(this.inheritanceContext);
157
+ if (delegate) {
158
+ delegate(container);
193
159
  }
194
- // تبدیل plain text به HTML با styling مناسب
195
- if (options.type === 'heading' || options.level === 'h1' || options.level === 'h2') {
196
- const level = options.level || 'h2';
197
- return `<${level} style="margin: 0 0 16px 0; color: #333; font-weight: 600;">${content}</${level}>`;
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);
198
168
  }
199
- if (options.weight === 'bold' || options.bold) {
200
- return `<strong style="color: #333;">${content}</strong>`;
169
+ this.root.children.push(container.build());
170
+ return this;
171
+ }
172
+ dialog(delegate) {
173
+ const container = new DialogContainerBuilder(this.popupService);
174
+ if (delegate) {
175
+ delegate(container);
201
176
  }
202
- // Default text styling
203
- return `<span style="color: #333; line-height: 1.5;">${content}</span>`;
177
+ return container;
204
178
  }
205
- /**
206
- * Process grid-layout options
207
- */
208
- processGridOptions(options) {
209
- return {
210
- default: {
211
- columns: options.columns || 12,
212
- rows: options.rows || 'auto',
213
- gap: options.gap || '16px',
214
- justifyItems: options.justifyItems || 'stretch',
215
- alignItems: options.alignItems || 'stretch',
216
- autoFlow: options.autoFlow || 'row'
217
- }
218
- };
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;
219
187
  }
220
- /**
221
- * Process spacing options
222
- */
223
- processSpacingOptions(options) {
224
- if (!options.spacing && !options.padding && !options.margin)
225
- return undefined;
188
+ build() {
226
189
  return {
227
- padding: options.spacing?.padding || options.padding || '0px',
228
- margin: options.spacing?.margin || options.margin || '0px'
190
+ type: this.root.type,
191
+ children: this.root.children,
192
+ mode: this.root.mode,
229
193
  };
230
194
  }
231
- /**
232
- * Process border options
233
- */
234
- processBorderOptions(options) {
235
- if (!options.border && !options.borderRadius && !options.borderWidth)
236
- return undefined;
237
- return {
238
- radius: options.border?.radius || options.borderRadius || '0px',
239
- width: options.border?.width || options.borderWidth || '0px',
240
- color: options.border?.color || options.borderColor || '#ddd',
241
- style: options.border?.style || options.borderStyle || 'solid'
195
+ }
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: [],
242
208
  };
209
+ this.inheritanceContext = {};
210
+ this.containerState.type = containerType;
243
211
  }
244
- /**
245
- * Process flex-layout options
246
- */
247
- processFlexOptions(options) {
248
- return {
249
- flexDirection: options.flexDirection || 'row',
250
- justifyContent: options.justifyContent || 'flex-start',
251
- alignItems: options.alignItems || 'stretch',
252
- gap: options.gap || '16px',
253
- flexWrap: options.flexWrap || 'nowrap'
254
- };
212
+ // Base methods shared by all containers
213
+ ensureChildren() {
214
+ this.containerState.children = this.containerState.children || [];
255
215
  }
256
- /**
257
- * Process panel-layout options
258
- */
259
- processPanelOptions(options) {
260
- return {
261
- title: options.title || '',
262
- collapsible: options.collapsible || false,
263
- collapsed: options.collapsed || false,
264
- padding: options.padding || '16px'
265
- };
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
+ child.options(cleanOptions);
236
+ child.withInheritanceContext(this.inheritanceContext);
237
+ this.ensureChildren();
238
+ this.containerState.children.push(child.build());
239
+ return this;
266
240
  }
267
- /**
268
- * Process tag content
269
- */
270
- processTagContent(options) {
271
- return options.label || options.content || 'Tag';
241
+ isValueWidget(type) {
242
+ const valueWidgetTypes = [
243
+ 'text-editor', 'large-text-editor', 'rich-text-editor', 'password-editor',
244
+ 'number-editor', 'select-editor', 'lookup-editor', 'selection-list-editor',
245
+ 'date-time-editor', 'toggle-editor', 'color-editor'
246
+ ];
247
+ return valueWidgetTypes.includes(type);
272
248
  }
273
- /**
274
- * Process generic content
275
- */
276
- processGenericContent(options) {
277
- if (!options.content)
278
- return '';
279
- // اگر HTML tags داره، استفاده کن
280
- if (options.content.includes('<')) {
281
- return options.content;
249
+ build() {
250
+ const result = {
251
+ type: this.containerState.type,
252
+ children: this.containerState.children,
253
+ options: this.containerState.options,
254
+ mode: this.containerState.mode,
255
+ visible: this.containerState.visible,
256
+ };
257
+ // Add name with _form_field suffix for form fields
258
+ if (this.containerState.type === 'form-field') {
259
+ if (this.containerState.name) {
260
+ result.name = this.containerState.name;
261
+ }
262
+ else if (this.containerState.options?.['label']) {
263
+ result.name = labelToPath(this.containerState.options['label']) + '_form_field';
264
+ }
265
+ // Form fields don't have path
266
+ }
267
+ else {
268
+ // Other containers can have name and path
269
+ if (this.containerState.name) {
270
+ result.name = this.containerState.name;
271
+ }
272
+ if (this.containerState.path) {
273
+ result.path = this.containerState.path;
274
+ }
275
+ if (this.containerState.defaultValue !== undefined) {
276
+ result.defaultValue = this.containerState.defaultValue;
277
+ }
282
278
  }
283
- // Plain text با basic styling
284
- return `<span style="color: #333;">${options.content}</span>`;
279
+ return result;
285
280
  }
286
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: AXPLayoutBuilderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
287
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: AXPLayoutBuilderService, providedIn: 'root' }); }
288
281
  }
289
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: AXPLayoutBuilderService, decorators: [{
290
- type: Injectable,
291
- args: [{
292
- providedIn: 'root'
293
- }]
294
- }] });
295
- //#region ---- Layout Builder Implementation ----
296
- class LayoutBuilder {
297
- constructor() {
298
- this.state = {
299
- nodes: [],
300
- mode: 'edit',
301
- direction: 'vertical',
302
- look: 'normal',
303
- layoutConfig: {
304
- positions: {
305
- sm: { colSpan: 12 },
306
- md: { colSpan: 8 },
307
- lg: { colSpan: 6 },
308
- xl: { colSpan: 5 },
309
- xxl: { colSpan: 4 },
310
- },
311
- },
312
- };
313
- }
314
- widget(name, delegate) {
315
- const widgetBuilder = new WidgetBuilder(name);
316
- if (delegate) {
317
- delegate(widgetBuilder);
318
- }
319
- this.state.nodes.push(widgetBuilder.build());
282
+ /**
283
+ * Base container builder mixin - Interface Segregation Principle
284
+ * Provides common container operations
285
+ */
286
+ class BaseContainerMixin extends BaseContainerBuilder {
287
+ name(name) {
288
+ this.containerState.name = name;
320
289
  return this;
321
290
  }
322
- layout(config) {
323
- this.state.layoutConfig = { ...this.state.layoutConfig, ...config };
291
+ path(path) {
292
+ this.containerState.path = path;
324
293
  return this;
325
294
  }
326
295
  mode(mode) {
327
- this.state.mode = mode;
296
+ this.containerState.mode = mode;
297
+ this.inheritanceContext.mode = mode;
298
+ return this;
299
+ }
300
+ visible(condition) {
301
+ if (!this.containerState.options)
302
+ this.containerState.options = {};
303
+ this.containerState.options['visible'] = condition;
304
+ this.inheritanceContext.visible = condition;
305
+ return this;
306
+ }
307
+ disabled(condition) {
308
+ if (!this.containerState.options)
309
+ this.containerState.options = {};
310
+ this.containerState.options['disabled'] = condition;
311
+ this.inheritanceContext.disabled = condition;
312
+ return this;
313
+ }
314
+ readonly(condition) {
315
+ if (!this.containerState.options)
316
+ this.containerState.options = {};
317
+ this.containerState.options['readonly'] = condition;
318
+ this.inheritanceContext.readonly = condition;
328
319
  return this;
329
320
  }
330
321
  direction(direction) {
331
- this.state.direction = direction;
322
+ if (!this.containerState.options)
323
+ this.containerState.options = {};
324
+ this.containerState.options['direction'] = direction;
325
+ this.inheritanceContext.direction = direction;
332
326
  return this;
333
327
  }
334
- look(look) {
335
- this.state.look = look;
328
+ // Inheritance context methods
329
+ withInheritanceContext(context) {
330
+ this.inheritanceContext = mergeInheritanceContext(context);
336
331
  return this;
337
332
  }
338
- // Predefined layout containers at root level - delegate pattern required
333
+ getInheritanceContext() {
334
+ return { ...this.inheritanceContext };
335
+ }
336
+ }
337
+ /**
338
+ * Layout container mixin - Interface Segregation Principle
339
+ * Provides layout-specific operations
340
+ */
341
+ class LayoutContainerMixin extends BaseContainerMixin {
342
+ layout(value) {
343
+ // Layout handling can be added here if needed
344
+ return this;
345
+ }
346
+ }
347
+ /**
348
+ * Child container mixin - Interface Segregation Principle
349
+ * Provides child container management
350
+ */
351
+ class ChildContainerMixin extends LayoutContainerMixin {
339
352
  grid(delegate) {
340
353
  const container = new GridContainerBuilder();
354
+ container.withInheritanceContext(this.inheritanceContext);
341
355
  if (delegate) {
342
356
  delegate(container);
343
357
  }
344
- this.state.nodes.push(container.build());
358
+ this.ensureChildren();
359
+ this.containerState.children.push(container.build());
345
360
  return this;
346
361
  }
347
362
  flex(delegate) {
348
363
  const container = new FlexContainerBuilder();
364
+ container.withInheritanceContext(this.inheritanceContext);
349
365
  if (delegate) {
350
366
  delegate(container);
351
367
  }
352
- this.state.nodes.push(container.build());
368
+ this.ensureChildren();
369
+ this.containerState.children.push(container.build());
353
370
  return this;
354
371
  }
355
372
  panel(delegate) {
356
373
  const container = new PanelContainerBuilder();
374
+ container.withInheritanceContext(this.inheritanceContext);
357
375
  if (delegate) {
358
376
  delegate(container);
359
377
  }
360
- this.state.nodes.push(container.build());
378
+ this.ensureChildren();
379
+ this.containerState.children.push(container.build());
361
380
  return this;
362
381
  }
363
382
  page(delegate) {
364
383
  const container = new PageContainerBuilder();
384
+ container.withInheritanceContext(this.inheritanceContext);
365
385
  if (delegate) {
366
386
  delegate(container);
367
387
  }
368
- this.state.nodes.push(container.build());
388
+ this.ensureChildren();
389
+ this.containerState.children.push(container.build());
369
390
  return this;
370
391
  }
371
392
  tabset(delegate) {
372
393
  const container = new TabsetContainerBuilder();
394
+ container.withInheritanceContext(this.inheritanceContext);
395
+ if (delegate) {
396
+ delegate(container);
397
+ }
398
+ this.ensureChildren();
399
+ this.containerState.children.push(container.build());
400
+ return this;
401
+ }
402
+ fieldset(delegate) {
403
+ const container = new FieldsetContainerBuilder();
404
+ container.withInheritanceContext(this.inheritanceContext);
405
+ if (delegate) {
406
+ delegate(container);
407
+ }
408
+ this.ensureChildren();
409
+ this.containerState.children.push(container.build());
410
+ return this;
411
+ }
412
+ dialog(delegate) {
413
+ const container = new DialogContainerBuilder(); // Will use inject() fallback
373
414
  if (delegate) {
374
415
  delegate(container);
375
416
  }
376
- this.state.nodes.push(container.build());
417
+ return container;
418
+ }
419
+ formField(label, delegate) {
420
+ const field = new FormFieldBuilder(label);
421
+ field.withInheritanceContext(this.inheritanceContext);
422
+ if (delegate) {
423
+ delegate(field);
424
+ }
425
+ this.ensureChildren();
426
+ this.containerState.children.push(field.build());
377
427
  return this;
378
428
  }
379
- // Direct widget methods at root level
429
+ }
430
+ /**
431
+ * Widget container mixin - Interface Segregation Principle
432
+ * Provides widget creation operations
433
+ */
434
+ class WidgetContainerMixin extends ChildContainerMixin {
380
435
  textBox(options) {
381
- const widgetBuilder = new WidgetBuilder().textBox(options);
382
- this.state.nodes.push(widgetBuilder.build());
436
+ this.addWidget('text-editor', options);
383
437
  return this;
384
438
  }
385
439
  largeTextBox(options) {
386
- const widgetBuilder = new WidgetBuilder().largeTextBox(options);
387
- this.state.nodes.push(widgetBuilder.build());
440
+ this.addWidget('large-text-editor', options);
388
441
  return this;
389
442
  }
390
443
  richText(options) {
391
- const widgetBuilder = new WidgetBuilder().richText(options);
392
- this.state.nodes.push(widgetBuilder.build());
444
+ this.addWidget('rich-text-editor', options);
393
445
  return this;
394
446
  }
395
447
  passwordBox(options) {
396
- const widgetBuilder = new WidgetBuilder().passwordBox(options);
397
- this.state.nodes.push(widgetBuilder.build());
448
+ this.addWidget('password-editor', options);
398
449
  return this;
399
450
  }
400
451
  numberBox(options) {
401
- const widgetBuilder = new WidgetBuilder().numberBox(options);
402
- this.state.nodes.push(widgetBuilder.build());
452
+ this.addWidget('number-editor', options);
403
453
  return this;
404
454
  }
405
455
  selectBox(options) {
406
- const widgetBuilder = new WidgetBuilder().selectBox(options);
407
- this.state.nodes.push(widgetBuilder.build());
456
+ this.addWidget('select-editor', options);
408
457
  return this;
409
458
  }
410
459
  lookupBox(options) {
411
- const widgetBuilder = new WidgetBuilder().lookupBox(options);
412
- this.state.nodes.push(widgetBuilder.build());
460
+ this.addWidget('lookup-editor', options);
413
461
  return this;
414
462
  }
415
463
  selectionList(options) {
416
- const widgetBuilder = new WidgetBuilder().selectionList(options);
417
- this.state.nodes.push(widgetBuilder.build());
464
+ this.addWidget('selection-list-editor', options);
418
465
  return this;
419
466
  }
420
467
  dateTimeBox(options) {
421
- const widgetBuilder = new WidgetBuilder().dateTimeBox(options);
422
- this.state.nodes.push(widgetBuilder.build());
468
+ this.addWidget('date-time-editor', options);
423
469
  return this;
424
470
  }
425
471
  toggleSwitch(options) {
426
- const widgetBuilder = new WidgetBuilder().toggleSwitch(options);
427
- this.state.nodes.push(widgetBuilder.build());
472
+ this.addWidget('toggle-editor', options);
428
473
  return this;
429
474
  }
430
475
  colorBox(options) {
431
- const widgetBuilder = new WidgetBuilder().colorBox(options);
432
- this.state.nodes.push(widgetBuilder.build());
476
+ this.addWidget('color-editor', options);
433
477
  return this;
434
478
  }
435
479
  customWidget(type, options) {
436
- const widgetBuilder = new WidgetBuilder().customWidget(type, options);
437
- this.state.nodes.push(widgetBuilder.build());
480
+ this.addWidget(type, options);
438
481
  return this;
439
482
  }
440
- build() {
441
- return {
442
- nodes: this.state.nodes,
443
- mode: this.state.mode,
444
- direction: this.state.direction,
445
- look: this.state.look,
446
- layoutConfig: this.state.layoutConfig,
447
- };
448
- }
449
- render() {
450
- console.log('render() called, state:', JSON.parse(JSON.stringify(this.state)));
451
- if (!this.state.nodes || !Array.isArray(this.state.nodes)) {
452
- console.error('Invalid nodes in state:', this.state.nodes);
453
- return {};
454
- }
455
- try {
456
- const nodes = this.transformToWidgetNodes(this.state.nodes);
457
- console.log('render() transformed nodes:', JSON.parse(JSON.stringify(nodes)));
458
- // No nodes
459
- if (!nodes || nodes.length === 0) {
460
- return {};
461
- }
462
- // Single node → return directly
463
- if (nodes.length === 1) {
464
- return nodes[0];
465
- }
466
- // Multiple nodes → wrap in a container node with layout config
467
- const flexDirection = this.state.direction === 'horizontal' ? 'row' : 'column';
468
- const container = {
469
- type: 'flex-layout',
470
- name: 'root',
471
- options: {
472
- flexDirection,
473
- gap: '16px',
474
- },
475
- children: nodes,
476
- };
477
- return container;
478
- }
479
- catch (error) {
480
- console.error('Error in render():', error);
481
- return {};
482
- }
483
- }
484
- transformToWidgetNodes(nodes) {
485
- console.log('transformToWidgetNodes called with nodes:', nodes);
486
- const sorted = this.sortNodes(nodes);
487
- // Helper to compute flex-item options from grid layout positions
488
- const toFlexItemOptions = (layout, order) => {
489
- let colSpan;
490
- const positions = layout?.positions ?? this.state.layoutConfig?.positions;
491
- if (positions) {
492
- colSpan = positions.xxl?.colSpan || positions.xl?.colSpan || positions.lg?.colSpan || positions.md?.colSpan || positions.sm?.colSpan;
493
- }
494
- const basis = colSpan && colSpan > 0 && colSpan <= 12 ? `${(colSpan / 12) * 100}%` : undefined;
495
- const opt = {};
496
- if (basis)
497
- opt.basis = basis;
498
- if (order !== undefined)
499
- opt.order = typeof order === 'number' ? order : parseInt(order.toString());
500
- return opt;
501
- };
502
- const multipleSiblings = sorted.length > 1;
503
- return sorted.map((node) => {
504
- console.log('Processing node:', node);
505
- // Build the actual widget node
506
- const widget = {
507
- type: node.widget?.type || 'unknown',
508
- name: node.id,
509
- options: {
510
- ...node.widget?.options,
511
- mode: node.mode,
512
- visible: node.visible,
513
- disabled: node.disabled,
514
- readonly: node.readonly,
515
- },
516
- };
517
- if (node.children && node.children.length > 0) {
518
- widget.children = this.transformToWidgetNodes(this.sortNodes(node.children));
519
- }
520
- // If we have multiple siblings at this level, wrap each item in a flex-item container
521
- if (multipleSiblings) {
522
- const flexItemOptions = toFlexItemOptions(node.layout, node.order);
523
- const wrapped = {
524
- type: 'flex-item-layout',
525
- name: `${node.id || 'item'}-container`,
526
- options: {
527
- ...flexItemOptions,
528
- },
529
- children: [widget],
530
- };
531
- return wrapped;
532
- }
533
- return widget;
534
- });
535
- }
536
- sortNodes(nodes) {
537
- if (!Array.isArray(nodes))
538
- return [];
539
- return nodes
540
- .map((n, idx) => ({ n, idx }))
541
- .sort((a, b) => {
542
- const ao = a.n.order;
543
- const bo = b.n.order;
544
- const aHas = ao !== undefined && ao !== null;
545
- const bHas = bo !== undefined && bo !== null;
546
- if (aHas && bHas) {
547
- if (typeof ao === 'number' && typeof bo === 'number')
548
- return ao - bo;
549
- return String(ao).localeCompare(String(bo));
550
- }
551
- if (aHas && !bHas)
552
- return -1;
553
- if (!aHas && bHas)
554
- return 1;
555
- return a.idx - b.idx;
556
- })
557
- .map(x => x.n);
558
- }
559
483
  }
560
- //#endregion
561
- //#region ---- Container Builder Implementation ----
562
- // Base Container Builder
563
- class BaseContainerBuilder {
564
- constructor(containerType) {
565
- this.containerState = {
566
- type: 'widget',
567
- children: [],
568
- };
569
- this.containerState.widget = { type: containerType, options: {} };
484
+ /**
485
+ * Flex Container Builder - Liskov Substitution Principle
486
+ * Extends WidgetContainerMixin to inherit all common functionality
487
+ */
488
+ class FlexContainerBuilder extends WidgetContainerMixin {
489
+ constructor() {
490
+ super('flex-layout');
570
491
  }
571
- baseId(id) {
572
- this.containerState.id = id;
573
- return this;
574
- }
575
- baseLayout(value) {
576
- if (typeof value === 'number') {
577
- this.containerState.layout = {
578
- positions: {
579
- sm: { colSpan: 12 },
580
- md: { colSpan: 12 },
581
- lg: { colSpan: value },
582
- xl: { colSpan: value },
583
- xxl: { colSpan: value },
584
- },
585
- };
586
- }
587
- else {
588
- this.containerState.layout = value;
589
- }
590
- return this;
591
- }
592
- // Base methods shared by all containers
593
- ensureChildren() {
594
- this.containerState.children = this.containerState.children || [];
595
- }
596
- addDirectWidget(type, options) {
597
- const child = new WidgetBuilder();
598
- child.customWidget(type, options);
599
- this.ensureChildren();
600
- this.containerState.children.push(child.build());
601
- return this;
602
- }
603
- build() {
604
- return {
605
- id: this.containerState.id,
606
- name: this.containerState.name,
607
- widget: this.containerState.widget,
608
- children: this.containerState.children,
609
- layout: this.containerState.layout,
610
- mode: this.containerState.mode,
611
- order: this.containerState.order,
612
- visible: this.containerState.visible,
613
- disabled: this.containerState.disabled,
614
- readonly: this.containerState.readonly,
615
- };
616
- }
617
- }
618
- // Flex Container Builder
619
- class FlexContainerBuilder extends BaseContainerBuilder {
620
- constructor() {
621
- super('flex-layout');
622
- }
623
- id(id) {
624
- return this.baseId(id);
625
- }
626
- layout(value) {
627
- return this.baseLayout(value);
628
- }
629
- mode(mode) {
630
- this.containerState.mode = mode;
631
- return this;
632
- }
633
- order(value) {
634
- this.containerState.order = value;
635
- return this;
636
- }
637
- visible(condition) {
638
- this.containerState.visible = condition;
639
- return this;
640
- }
641
- disabled(condition) {
642
- this.containerState.disabled = condition;
643
- return this;
644
- }
645
- readonly(condition) {
646
- this.containerState.readonly = condition;
647
- return this;
648
- }
649
- setOptions(options) {
650
- this.containerState.widget.options = { ...this.containerState.widget.options, ...options };
492
+ setOptions(options) {
493
+ this.containerState.options = { ...this.containerState.options, ...options };
651
494
  return this;
652
495
  }
653
496
  // Individual fluent methods for Flex
654
- setFlexDirection(direction) {
497
+ setDirection(direction) {
655
498
  return this.setOptions({ flexDirection: direction });
656
499
  }
657
- setFlexWrap(wrap) {
500
+ setWrap(wrap) {
658
501
  return this.setOptions({ flexWrap: wrap });
659
502
  }
660
503
  setJustifyContent(justify) {
@@ -675,70 +518,17 @@ class FlexContainerBuilder extends BaseContainerBuilder {
675
518
  setMargin(margin) {
676
519
  return this.setOptions({ spacing: { margin } });
677
520
  }
678
- addContainer(name, delegate) {
679
- // Implementation will be simplified for now
680
- return this;
681
- }
682
- addWidget(name, delegate) {
683
- const childWidget = new WidgetBuilder(name);
684
- if (delegate) {
685
- delegate(childWidget);
686
- }
687
- this.ensureChildren();
688
- this.containerState.children.push(childWidget.build());
689
- return this;
690
- }
691
- textBox(options) {
692
- return this.addDirectWidget('text-editor', options);
693
- }
694
- largeTextBox(options) {
695
- return this.addDirectWidget('large-text-editor', options);
696
- }
697
- richText(options) {
698
- return this.addDirectWidget('rich-text-editor', options);
699
- }
700
- passwordBox(options) {
701
- return this.addDirectWidget('password-editor', options);
702
- }
703
- numberBox(options) {
704
- return this.addDirectWidget('number-editor', options);
705
- }
706
- selectBox(options) {
707
- return this.addDirectWidget('select-editor', options);
708
- }
709
- lookupBox(options) {
710
- return this.addDirectWidget('lookup-editor', options);
711
- }
712
- selectionList(options) {
713
- return this.addDirectWidget('selection-list', options);
714
- }
715
- dateTimeBox(options) {
716
- return this.addDirectWidget('date-time-editor', options);
717
- }
718
- toggleSwitch(options) {
719
- return this.addDirectWidget('toggle-editor', options);
720
- }
721
- colorBox(options) {
722
- return this.addDirectWidget('color-editor', options);
723
- }
724
- customWidget(type, options) {
725
- return this.addDirectWidget(type, options);
726
- }
727
521
  }
728
- // Grid Container Builder
729
- class GridContainerBuilder extends BaseContainerBuilder {
522
+ /**
523
+ * Grid Container Builder - Liskov Substitution Principle
524
+ * Extends WidgetContainerMixin to inherit all common functionality
525
+ */
526
+ class GridContainerBuilder extends WidgetContainerMixin {
730
527
  constructor() {
731
528
  super('grid-layout');
732
529
  }
733
- id(id) { return this.baseId(id); }
734
- layout(value) { return this.baseLayout(value); }
735
- mode(mode) { this.containerState.mode = mode; return this; }
736
- order(value) { this.containerState.order = value; return this; }
737
- visible(condition) { this.containerState.visible = condition; return this; }
738
- disabled(condition) { this.containerState.disabled = condition; return this; }
739
- readonly(condition) { this.containerState.readonly = condition; return this; }
740
530
  setOptions(options) {
741
- this.containerState.widget.options = { ...this.containerState.widget.options, ...options };
531
+ this.containerState.options = { ...this.containerState.options, ...options };
742
532
  return this;
743
533
  }
744
534
  // Individual fluent methods for Grid
@@ -769,42 +559,17 @@ class GridContainerBuilder extends BaseContainerBuilder {
769
559
  setMargin(margin) {
770
560
  return this.setOptions({ spacing: { margin } });
771
561
  }
772
- addContainer(name, delegate) { return this; }
773
- addWidget(name, delegate) {
774
- const childWidget = new WidgetBuilder(name);
775
- if (delegate)
776
- delegate(childWidget);
777
- this.ensureChildren();
778
- this.containerState.children.push(childWidget.build());
779
- return this;
780
- }
781
- textBox(options) { return this.addDirectWidget('text-editor', options); }
782
- largeTextBox(options) { return this.addDirectWidget('large-text-editor', options); }
783
- richText(options) { return this.addDirectWidget('rich-text-editor', options); }
784
- passwordBox(options) { return this.addDirectWidget('password-editor', options); }
785
- numberBox(options) { return this.addDirectWidget('number-editor', options); }
786
- selectBox(options) { return this.addDirectWidget('select-editor', options); }
787
- lookupBox(options) { return this.addDirectWidget('lookup-editor', options); }
788
- selectionList(options) { return this.addDirectWidget('selection-list', options); }
789
- dateTimeBox(options) { return this.addDirectWidget('date-time-editor', options); }
790
- toggleSwitch(options) { return this.addDirectWidget('toggle-editor', options); }
791
- colorBox(options) { return this.addDirectWidget('color-editor', options); }
792
- customWidget(type, options) { return this.addDirectWidget(type, options); }
793
562
  }
794
- // Panel Container Builder
795
- class PanelContainerBuilder extends BaseContainerBuilder {
563
+ /**
564
+ * Panel Container Builder - Liskov Substitution Principle
565
+ * Extends WidgetContainerMixin to inherit all common functionality
566
+ */
567
+ class PanelContainerBuilder extends WidgetContainerMixin {
796
568
  constructor() {
797
569
  super('panel-layout');
798
570
  }
799
- id(id) { return this.baseId(id); }
800
- layout(value) { return this.baseLayout(value); }
801
- mode(mode) { this.containerState.mode = mode; return this; }
802
- order(value) { this.containerState.order = value; return this; }
803
- visible(condition) { this.containerState.visible = condition; return this; }
804
- disabled(condition) { this.containerState.disabled = condition; return this; }
805
- readonly(condition) { this.containerState.readonly = condition; return this; }
806
571
  setOptions(options) {
807
- this.containerState.widget.options = { ...this.containerState.widget.options, ...options };
572
+ this.containerState.options = { ...this.containerState.options, ...options };
808
573
  return this;
809
574
  }
810
575
  // Individual fluent methods for Panel
@@ -823,42 +588,17 @@ class PanelContainerBuilder extends BaseContainerBuilder {
823
588
  setCollapsed(collapsed) {
824
589
  return this.setOptions({ collapsed });
825
590
  }
826
- addContainer(name, delegate) { return this; }
827
- addWidget(name, delegate) {
828
- const childWidget = new WidgetBuilder(name);
829
- if (delegate)
830
- delegate(childWidget);
831
- this.ensureChildren();
832
- this.containerState.children.push(childWidget.build());
833
- return this;
834
- }
835
- textBox(options) { return this.addDirectWidget('text-editor', options); }
836
- largeTextBox(options) { return this.addDirectWidget('large-text-editor', options); }
837
- richText(options) { return this.addDirectWidget('rich-text-editor', options); }
838
- passwordBox(options) { return this.addDirectWidget('password-editor', options); }
839
- numberBox(options) { return this.addDirectWidget('number-editor', options); }
840
- selectBox(options) { return this.addDirectWidget('select-editor', options); }
841
- lookupBox(options) { return this.addDirectWidget('lookup-editor', options); }
842
- selectionList(options) { return this.addDirectWidget('selection-list', options); }
843
- dateTimeBox(options) { return this.addDirectWidget('date-time-editor', options); }
844
- toggleSwitch(options) { return this.addDirectWidget('toggle-editor', options); }
845
- colorBox(options) { return this.addDirectWidget('color-editor', options); }
846
- customWidget(type, options) { return this.addDirectWidget(type, options); }
847
591
  }
848
- // Page Container Builder
849
- class PageContainerBuilder extends BaseContainerBuilder {
592
+ /**
593
+ * Page Container Builder - Liskov Substitution Principle
594
+ * Extends WidgetContainerMixin to inherit all common functionality
595
+ */
596
+ class PageContainerBuilder extends WidgetContainerMixin {
850
597
  constructor() {
851
598
  super('page-layout');
852
599
  }
853
- id(id) { return this.baseId(id); }
854
- layout(value) { return this.baseLayout(value); }
855
- mode(mode) { this.containerState.mode = mode; return this; }
856
- order(value) { this.containerState.order = value; return this; }
857
- visible(condition) { this.containerState.visible = condition; return this; }
858
- disabled(condition) { this.containerState.disabled = condition; return this; }
859
- readonly(condition) { this.containerState.readonly = condition; return this; }
860
600
  setOptions(options) {
861
- this.containerState.widget.options = { ...this.containerState.widget.options, ...options };
601
+ this.containerState.options = { ...this.containerState.options, ...options };
862
602
  return this;
863
603
  }
864
604
  // Individual fluent methods for Page
@@ -877,42 +617,17 @@ class PageContainerBuilder extends BaseContainerBuilder {
877
617
  setDirection(direction) {
878
618
  return this.setOptions({ direction });
879
619
  }
880
- addContainer(name, delegate) { return this; }
881
- addWidget(name, delegate) {
882
- const childWidget = new WidgetBuilder(name);
883
- if (delegate)
884
- delegate(childWidget);
885
- this.ensureChildren();
886
- this.containerState.children.push(childWidget.build());
887
- return this;
888
- }
889
- textBox(options) { return this.addDirectWidget('text-editor', options); }
890
- largeTextBox(options) { return this.addDirectWidget('large-text-editor', options); }
891
- richText(options) { return this.addDirectWidget('rich-text-editor', options); }
892
- passwordBox(options) { return this.addDirectWidget('password-editor', options); }
893
- numberBox(options) { return this.addDirectWidget('number-editor', options); }
894
- selectBox(options) { return this.addDirectWidget('select-editor', options); }
895
- lookupBox(options) { return this.addDirectWidget('lookup-editor', options); }
896
- selectionList(options) { return this.addDirectWidget('selection-list', options); }
897
- dateTimeBox(options) { return this.addDirectWidget('date-time-editor', options); }
898
- toggleSwitch(options) { return this.addDirectWidget('toggle-editor', options); }
899
- colorBox(options) { return this.addDirectWidget('color-editor', options); }
900
- customWidget(type, options) { return this.addDirectWidget(type, options); }
901
620
  }
902
- // Tabset Container Builder
903
- class TabsetContainerBuilder extends BaseContainerBuilder {
621
+ /**
622
+ * Tabset Container Builder - Liskov Substitution Principle
623
+ * Extends WidgetContainerMixin to inherit all common functionality
624
+ */
625
+ class TabsetContainerBuilder extends WidgetContainerMixin {
904
626
  constructor() {
905
627
  super('tabset-layout');
906
628
  }
907
- id(id) { return this.baseId(id); }
908
- layout(value) { return this.baseLayout(value); }
909
- mode(mode) { this.containerState.mode = mode; return this; }
910
- order(value) { this.containerState.order = value; return this; }
911
- visible(condition) { this.containerState.visible = condition; return this; }
912
- disabled(condition) { this.containerState.disabled = condition; return this; }
913
- readonly(condition) { this.containerState.readonly = condition; return this; }
914
629
  setOptions(options) {
915
- this.containerState.widget.options = { ...this.containerState.widget.options, ...options };
630
+ this.containerState.options = { ...this.containerState.options, ...options };
916
631
  return this;
917
632
  }
918
633
  // Individual fluent methods for Tabset
@@ -925,48 +640,312 @@ class TabsetContainerBuilder extends BaseContainerBuilder {
925
640
  setActiveIndex(index) {
926
641
  return this.setOptions({ activeIndex: index });
927
642
  }
928
- addContainer(name, delegate) { return this; }
929
- addWidget(name, delegate) {
930
- const childWidget = new WidgetBuilder(name);
931
- if (delegate)
932
- delegate(childWidget);
643
+ }
644
+ /**
645
+ * Form Field Builder - Liskov Substitution Principle
646
+ * Can only contain ONE widget with automatic path generation
647
+ */
648
+ class FormFieldBuilder extends LayoutContainerMixin {
649
+ constructor(label) {
650
+ super('form-field');
651
+ this.hasWidget = false;
652
+ this.containerState.options = { label, showLabel: true };
653
+ }
654
+ setOptions(options) {
655
+ this.containerState.options = { ...this.containerState.options, ...options };
656
+ return this;
657
+ }
658
+ setLabel(label) {
659
+ return this.setOptions({ label });
660
+ }
661
+ setShowLabel(showLabel) {
662
+ return this.setOptions({ showLabel });
663
+ }
664
+ // Single widget methods with automatic path generation
665
+ addSingleWidget(type, options) {
666
+ if (this.hasWidget) {
667
+ throw new Error('Form field can only contain one widget');
668
+ }
669
+ const formFieldName = this.containerState.name;
670
+ const formFieldPath = this.containerState.path; // Get explicit path from form field
671
+ const formFieldLabel = this.containerState.options?.['label'];
672
+ const widgetName = options?.name;
673
+ // Generate widget path: explicit path -> widget name -> form field name -> label -> random
674
+ let widgetPath;
675
+ if (formFieldPath) {
676
+ widgetPath = formFieldPath; // Use explicit form field path first
677
+ }
678
+ else if (widgetName) {
679
+ widgetPath = widgetName;
680
+ }
681
+ else if (formFieldName) {
682
+ widgetPath = formFieldName; // Use form field name as default path
683
+ }
684
+ else if (formFieldLabel) {
685
+ widgetPath = labelToPath(formFieldLabel);
686
+ }
687
+ else {
688
+ widgetPath = generateRandomId();
689
+ }
690
+ const finalName = widgetName || formFieldName || widgetPath;
691
+ const child = new WidgetBuilder();
692
+ child.type(type);
693
+ child.name(finalName);
694
+ child.path(widgetPath);
695
+ // Remove name from options since it's now in state
696
+ const { name: _, ...cleanOptions } = (options || {});
697
+ child.options(cleanOptions);
698
+ child.withInheritanceContext(this.inheritanceContext);
933
699
  this.ensureChildren();
934
- this.containerState.children.push(childWidget.build());
700
+ this.containerState.children.push(child.build());
701
+ this.hasWidget = true;
935
702
  return this;
936
703
  }
937
- textBox(options) { return this.addDirectWidget('text-editor', options); }
938
- largeTextBox(options) { return this.addDirectWidget('large-text-editor', options); }
939
- richText(options) { return this.addDirectWidget('rich-text-editor', options); }
940
- passwordBox(options) { return this.addDirectWidget('password-editor', options); }
941
- numberBox(options) { return this.addDirectWidget('number-editor', options); }
942
- selectBox(options) { return this.addDirectWidget('select-editor', options); }
943
- lookupBox(options) { return this.addDirectWidget('lookup-editor', options); }
944
- selectionList(options) { return this.addDirectWidget('selection-list', options); }
945
- dateTimeBox(options) { return this.addDirectWidget('date-time-editor', options); }
946
- toggleSwitch(options) { return this.addDirectWidget('toggle-editor', options); }
947
- colorBox(options) { return this.addDirectWidget('color-editor', options); }
948
- customWidget(type, options) { return this.addDirectWidget(type, options); }
704
+ textBox(options) {
705
+ return this.addSingleWidget('text-editor', options);
706
+ }
707
+ largeTextBox(options) {
708
+ return this.addSingleWidget('large-text-editor', options);
709
+ }
710
+ richText(options) {
711
+ return this.addSingleWidget('rich-text-editor', options);
712
+ }
713
+ passwordBox(options) {
714
+ return this.addSingleWidget('password-editor', options);
715
+ }
716
+ numberBox(options) {
717
+ return this.addSingleWidget('number-editor', options);
718
+ }
719
+ selectBox(options) {
720
+ return this.addSingleWidget('select-editor', options);
721
+ }
722
+ lookupBox(options) {
723
+ return this.addSingleWidget('lookup-editor', options);
724
+ }
725
+ selectionList(options) {
726
+ return this.addSingleWidget('selection-list-editor', options);
727
+ }
728
+ dateTimeBox(options) {
729
+ return this.addSingleWidget('date-time-editor', options);
730
+ }
731
+ toggleSwitch(options) {
732
+ return this.addSingleWidget('toggle-editor', options);
733
+ }
734
+ colorBox(options) {
735
+ return this.addSingleWidget('color-editor', options);
736
+ }
737
+ customWidget(type, options) {
738
+ return this.addSingleWidget(type, options);
739
+ }
740
+ }
741
+ /**
742
+ * Fieldset Container Builder - Liskov Substitution Principle
743
+ * Extends LayoutContainerMixin to inherit layout functionality
744
+ * Specialized for form fields only
745
+ */
746
+ class FieldsetContainerBuilder extends LayoutContainerMixin {
747
+ constructor() {
748
+ super('fieldset-layout');
749
+ this.containerState.options = {};
750
+ }
751
+ setOptions(options) {
752
+ this.containerState.options = { ...this.containerState.options, ...options };
753
+ return this;
754
+ }
755
+ // Individual fluent methods for Fieldset
756
+ setTitle(title) {
757
+ return this.setOptions({ title });
758
+ }
759
+ setDescription(description) {
760
+ return this.setOptions({ description });
761
+ }
762
+ setIcon(icon) {
763
+ return this.setOptions({ icon });
764
+ }
765
+ setCollapsible(collapsible) {
766
+ return this.setOptions({ collapsible });
767
+ }
768
+ setIsOpen(isOpen) {
769
+ return this.setOptions({ isOpen });
770
+ }
771
+ setLook(look) {
772
+ return this.setOptions({ look });
773
+ }
774
+ setShowHeader(showHeader) {
775
+ return this.setOptions({ showHeader });
776
+ }
777
+ setCols(cols) {
778
+ return this.setOptions({ cols });
779
+ }
780
+ // Only form fields are allowed in fieldset
781
+ formField(label, delegate) {
782
+ const field = new FormFieldBuilder(label);
783
+ field.withInheritanceContext(this.inheritanceContext);
784
+ if (delegate) {
785
+ delegate(field);
786
+ }
787
+ this.ensureChildren();
788
+ this.containerState.children.push(field.build());
789
+ return this;
790
+ }
791
+ }
792
+ /**
793
+ * Dialog Container Builder - Specialized for dialog functionality
794
+ * Uses composition instead of inheritance for cleaner separation
795
+ */
796
+ class DialogContainerBuilder {
797
+ constructor(popupService) {
798
+ this.dialogState = {
799
+ type: 'flex-layout', // This will be overridden when content layout exists
800
+ children: [],
801
+ mode: 'edit',
802
+ dialogOptions: {
803
+ title: '',
804
+ size: 'md',
805
+ closeButton: false,
806
+ },
807
+ actions: {
808
+ footer: {
809
+ prefix: [],
810
+ suffix: []
811
+ }
812
+ }
813
+ };
814
+ if (popupService) {
815
+ this.popupService = popupService;
816
+ }
817
+ else {
818
+ this.popupService = inject(AXPopupService);
819
+ }
820
+ }
821
+ setOptions(options) {
822
+ this.dialogState.dialogOptions = { ...this.dialogState.dialogOptions, ...options };
823
+ return this;
824
+ }
825
+ // Individual fluent methods for Dialog
826
+ setTitle(title) {
827
+ return this.setOptions({ title });
828
+ }
829
+ setMessage(message) {
830
+ return this.setOptions({ message });
831
+ }
832
+ setSize(size) {
833
+ return this.setOptions({ size });
834
+ }
835
+ setCloseButton(closeButton) {
836
+ return this.setOptions({ closeButton });
837
+ }
838
+ setContext(context) {
839
+ return this.setOptions({ context });
840
+ }
841
+ content(delegate) {
842
+ if (delegate) {
843
+ // Create a flex container directly instead of through LayoutBuilder
844
+ const flexContainer = new FlexContainerBuilder();
845
+ flexContainer.setDirection('column');
846
+ flexContainer.setGap('10px');
847
+ delegate(flexContainer);
848
+ this.contentLayout = flexContainer.build();
849
+ }
850
+ return this;
851
+ }
852
+ setActions(delegate) {
853
+ if (delegate) {
854
+ const actionBuilder = new ActionBuilder(this);
855
+ delegate(actionBuilder);
856
+ }
857
+ return this;
858
+ }
859
+ // Build method to create dialog node
860
+ build() {
861
+ // If we have content layout, use it directly to avoid extra wrapper
862
+ if (this.contentLayout) {
863
+ return {
864
+ ...this.contentLayout,
865
+ // Add dialog-specific properties
866
+ options: {
867
+ ...this.contentLayout.options,
868
+ ...this.dialogState.dialogOptions
869
+ }
870
+ };
871
+ }
872
+ // Fallback to dialog state structure if no content
873
+ const result = {
874
+ ...this.dialogState,
875
+ children: []
876
+ };
877
+ // Add dialog-specific properties
878
+ if (this.dialogState.dialogOptions) {
879
+ result.options = { ...result.options, ...this.dialogState.dialogOptions };
880
+ }
881
+ return result;
882
+ }
883
+ // Dialog-specific methods
884
+ async show() {
885
+ const dialogNode = this.build();
886
+ // Import the dialog renderer component dynamically
887
+ const { AXPDialogRendererComponent } = await Promise.resolve().then(function () { return dialogRenderer_component; });
888
+ // Create dialog configuration
889
+ const dialogConfig = {
890
+ title: this.dialogState.dialogOptions?.title || '',
891
+ message: this.dialogState.dialogOptions?.message,
892
+ context: this.dialogState.dialogOptions?.context || {},
893
+ definition: dialogNode,
894
+ actions: this.dialogState.actions,
895
+ };
896
+ // The Promise resolves when user clicks an action button
897
+ return new Promise(async (resolve) => {
898
+ this.popupService.open(AXPDialogRendererComponent, {
899
+ title: dialogConfig.title,
900
+ size: this.dialogState.dialogOptions?.size || 'md',
901
+ closeButton: this.dialogState.dialogOptions?.closeButton || false,
902
+ closeOnBackdropClick: false,
903
+ draggable: false,
904
+ data: {
905
+ config: dialogConfig,
906
+ callBack: (result) => {
907
+ // Resolve with the dialog reference when user clicks an action
908
+ resolve(result);
909
+ }
910
+ }
911
+ });
912
+ });
913
+ }
949
914
  }
950
915
  //#endregion
951
916
  //#region ---- Widget Builder Implementation ----
917
+ /**
918
+ * Widget Builder - Single Responsibility Principle
919
+ * Handles individual widget configuration and building
920
+ */
952
921
  class WidgetBuilder {
953
922
  constructor(name) {
954
923
  this.widgetState = {
955
924
  type: 'widget',
956
- children: [],
925
+ options: {},
957
926
  };
927
+ this.inheritanceContext = {};
958
928
  if (name) {
959
929
  this.widgetState.name = name;
960
930
  }
961
931
  }
962
- // No utility methods needed - widget options are pure now
963
- isContainerWidgetType(type) {
964
- if (!type)
965
- return false;
966
- return ['grid-layout', 'flex-layout', 'panel-layout', 'page-layout', 'tabset-layout'].includes(type);
932
+ type(type) {
933
+ this.widgetState.type = type;
934
+ return this;
935
+ }
936
+ name(name) {
937
+ this.widgetState.name = name;
938
+ if (!this.widgetState.path) {
939
+ this.widgetState.path = name;
940
+ }
941
+ return this;
967
942
  }
968
- id(id) {
969
- this.widgetState.id = id;
943
+ path(path) {
944
+ this.widgetState.path = path;
945
+ return this;
946
+ }
947
+ options(options) {
948
+ this.widgetState.options = options;
970
949
  return this;
971
950
  }
972
951
  layout(value) {
@@ -988,333 +967,710 @@ class WidgetBuilder {
988
967
  }
989
968
  mode(mode) {
990
969
  this.widgetState.mode = mode;
970
+ this.inheritanceContext.mode = mode;
991
971
  return this;
992
972
  }
993
973
  visible(condition) {
994
- this.widgetState.visible = condition;
974
+ if (!this.widgetState.options) {
975
+ this.widgetState.options = {};
976
+ }
977
+ this.widgetState.options['visible'] = condition;
978
+ this.inheritanceContext.visible = condition;
995
979
  return this;
996
980
  }
997
981
  disabled(condition) {
998
- this.widgetState.disabled = condition;
982
+ if (!this.widgetState.options) {
983
+ this.widgetState.options = {};
984
+ }
985
+ this.widgetState.options['disabled'] = condition;
986
+ this.inheritanceContext.disabled = condition;
999
987
  return this;
1000
988
  }
1001
989
  readonly(condition) {
1002
- this.widgetState.readonly = condition;
1003
- return this;
1004
- }
1005
- order(value) {
1006
- this.widgetState.order = value;
1007
- return this;
1008
- }
1009
- // Container helpers - delegate pattern required
1010
- grid(delegate) {
1011
- const container = new GridContainerBuilder();
1012
- if (delegate) {
1013
- delegate(container);
990
+ if (!this.widgetState.options) {
991
+ this.widgetState.options = {};
1014
992
  }
1015
- const built = container.build();
1016
- this.widgetState.widget = built.widget;
1017
- this.widgetState.children = built.children;
993
+ this.widgetState.options['readonly'] = condition;
994
+ this.inheritanceContext.readonly = condition;
1018
995
  return this;
1019
996
  }
1020
- flex(delegate) {
1021
- const container = new FlexContainerBuilder();
1022
- if (delegate) {
1023
- delegate(container);
997
+ direction(direction) {
998
+ if (!this.widgetState.options) {
999
+ this.widgetState.options = {};
1024
1000
  }
1025
- const built = container.build();
1026
- this.widgetState.widget = built.widget;
1027
- this.widgetState.children = built.children;
1001
+ this.widgetState.options['direction'] = direction;
1002
+ this.inheritanceContext.direction = direction;
1028
1003
  return this;
1029
1004
  }
1030
- panel(delegate) {
1031
- const container = new PanelContainerBuilder();
1032
- if (delegate) {
1033
- delegate(container);
1005
+ // Inheritance context methods
1006
+ withInheritanceContext(context) {
1007
+ this.inheritanceContext = mergeInheritanceContext(context);
1008
+ // Apply inherited properties to widget state
1009
+ const resolved = resolveInheritedProperties(context, this.inheritanceContext);
1010
+ if (resolved.mode && !this.widgetState.mode) {
1011
+ this.widgetState.mode = resolved.mode;
1012
+ }
1013
+ if (!this.widgetState.options)
1014
+ this.widgetState.options = {};
1015
+ if (resolved.disabled !== undefined && !this.widgetState.options['disabled']) {
1016
+ this.widgetState.options['disabled'] = resolved.disabled;
1017
+ }
1018
+ if (resolved.readonly !== undefined && !this.widgetState.options['readonly']) {
1019
+ this.widgetState.options['readonly'] = resolved.readonly;
1020
+ }
1021
+ if (resolved.direction !== undefined && !this.widgetState.options['direction']) {
1022
+ this.widgetState.options['direction'] = resolved.direction;
1023
+ }
1024
+ if (resolved.visible !== undefined && !this.widgetState.options['visible']) {
1025
+ this.widgetState.options['visible'] = resolved.visible;
1034
1026
  }
1035
- const built = container.build();
1036
- this.widgetState.widget = built.widget;
1037
- this.widgetState.children = built.children;
1038
1027
  return this;
1039
1028
  }
1040
- page(delegate) {
1041
- const container = new PageContainerBuilder();
1042
- if (delegate) {
1043
- delegate(container);
1029
+ getInheritanceContext() {
1030
+ return { ...this.inheritanceContext };
1031
+ }
1032
+ build() {
1033
+ return {
1034
+ name: this.widgetState.name,
1035
+ type: this.widgetState.type,
1036
+ options: this.widgetState.options,
1037
+ mode: this.widgetState.mode,
1038
+ path: this.widgetState.path,
1039
+ defaultValue: this.widgetState.defaultValue,
1040
+ };
1041
+ }
1042
+ }
1043
+ //#region ---- Action Builder Implementation ----
1044
+ class ActionBuilder {
1045
+ constructor(dialogBuilder) {
1046
+ this.dialogBuilder = dialogBuilder;
1047
+ }
1048
+ cancel(text) {
1049
+ if (!this.dialogBuilder['dialogState'].actions.footer.suffix) {
1050
+ this.dialogBuilder['dialogState'].actions.footer.suffix = [];
1044
1051
  }
1045
- const built = container.build();
1046
- this.widgetState.widget = built.widget;
1047
- this.widgetState.children = built.children;
1052
+ this.dialogBuilder['dialogState'].actions.footer.suffix.push({
1053
+ title: text || '@general:actions.cancel.title',
1054
+ icon: 'fa-times',
1055
+ color: 'default',
1056
+ command: { name: 'cancel' },
1057
+ });
1048
1058
  return this;
1049
1059
  }
1050
- tabset(delegate) {
1051
- const container = new TabsetContainerBuilder();
1052
- if (delegate) {
1053
- delegate(container);
1060
+ submit(text) {
1061
+ if (!this.dialogBuilder['dialogState'].actions.footer.suffix) {
1062
+ this.dialogBuilder['dialogState'].actions.footer.suffix = [];
1054
1063
  }
1055
- const built = container.build();
1056
- this.widgetState.widget = built.widget;
1057
- this.widgetState.children = built.children;
1064
+ this.dialogBuilder['dialogState'].actions.footer.suffix.push({
1065
+ title: text || '@general:actions.submit.title',
1066
+ icon: 'fa-check',
1067
+ color: 'primary',
1068
+ command: { name: 'submit', options: { validate: true } },
1069
+ });
1058
1070
  return this;
1059
1071
  }
1060
- widget(name, delegate) {
1061
- const child = new WidgetBuilder(name);
1062
- if (delegate) {
1063
- delegate(child);
1072
+ custom(action) {
1073
+ if (!this.dialogBuilder['dialogState'].actions.footer.suffix) {
1074
+ this.dialogBuilder['dialogState'].actions.footer.suffix = [];
1064
1075
  }
1065
- this.widgetState.children = this.widgetState.children || [];
1066
- this.widgetState.children.push(child.build());
1076
+ this.dialogBuilder['dialogState'].actions.footer.suffix.push(action);
1067
1077
  return this;
1068
1078
  }
1069
- // Widget type methods
1070
- textBox(options) {
1071
- if (this.isContainerWidgetType(this.widgetState.widget?.type)) {
1072
- const child = new WidgetBuilder();
1073
- child.textBox(options);
1074
- this.widgetState.children.push(child.build());
1075
- return this;
1076
- }
1077
- else {
1078
- this.widgetState.widget = {
1079
- type: 'text-editor',
1080
- options,
1081
- };
1082
- return this;
1083
- }
1079
+ }
1080
+
1081
+ class AXPLayoutConversionService {
1082
+ constructor() {
1083
+ //#region ---- Caching ----
1084
+ this.widgetTreeCache = new Map();
1085
+ this.formDefinitionCache = new Map();
1084
1086
  }
1085
- largeTextBox(options) {
1086
- if (this.isContainerWidgetType(this.widgetState.widget?.type)) {
1087
- const child = new WidgetBuilder();
1088
- child.largeTextBox(options);
1089
- this.widgetState.children.push(child.build());
1090
- return this;
1091
- }
1092
- else {
1093
- this.widgetState.widget = {
1094
- type: 'large-text-editor',
1095
- options,
1096
- };
1097
- return this;
1087
+ //#endregion
1088
+ //#region ---- Public Methods ----
1089
+ /**
1090
+ * Convert AXPDynamicFormDefinition to AXPWidgetNode tree structure
1091
+ * Groups become Fieldset Layouts with Form Field widgets as children
1092
+ * Fields become Form Field widgets with Editor widgets as children
1093
+ */
1094
+ convertFormDefinition(formDefinition) {
1095
+ // Create cache key based on form definition content
1096
+ const cacheKey = this.createFormDefinitionCacheKey(formDefinition);
1097
+ // Check cache first
1098
+ if (this.widgetTreeCache.has(cacheKey)) {
1099
+ return this.widgetTreeCache.get(cacheKey);
1098
1100
  }
1101
+ // Generate widget tree
1102
+ const widgetTree = {
1103
+ type: 'grid-layout',
1104
+ name: 'dynamic-form-container',
1105
+ options: {
1106
+ title: 'Dynamic Form',
1107
+ grid: {
1108
+ default: {
1109
+ columns: 1,
1110
+ gap: '1rem'
1111
+ }
1112
+ }
1113
+ },
1114
+ children: formDefinition.groups.map(group => this.createGroupAsFieldsetWidget(group))
1115
+ };
1116
+ // Cache the result
1117
+ this.widgetTreeCache.set(cacheKey, widgetTree);
1118
+ return widgetTree;
1099
1119
  }
1100
- richText(options) {
1101
- if (this.isContainerWidgetType(this.widgetState.widget?.type)) {
1102
- const child = new WidgetBuilder();
1103
- child.richText(options);
1104
- this.widgetState.children.push(child.build());
1105
- return this;
1120
+ /**
1121
+ * Convert AXPWidgetNode tree back to AXPDynamicFormDefinition
1122
+ * Parses Fieldset Layouts back to Groups
1123
+ * Parses Form Field widgets back to Fields
1124
+ */
1125
+ convertWidgetTreeToFormDefinition(widgetTree) {
1126
+ // Create cache key based on widget tree content
1127
+ const cacheKey = this.createWidgetTreeCacheKey(widgetTree);
1128
+ // Check cache first
1129
+ if (this.formDefinitionCache.has(cacheKey)) {
1130
+ return this.formDefinitionCache.get(cacheKey);
1106
1131
  }
1107
- else {
1108
- this.widgetState.widget = {
1109
- type: 'rich-text-editor',
1110
- options,
1111
- };
1112
- return this;
1132
+ // Parse widget tree
1133
+ const groups = [];
1134
+ if (widgetTree.children) {
1135
+ widgetTree.children.forEach(child => {
1136
+ if (child.type === 'fieldset-layout') {
1137
+ const group = this.extractGroupFromFieldset(child);
1138
+ groups.push(group);
1139
+ }
1140
+ });
1113
1141
  }
1142
+ const formDefinition = { groups };
1143
+ // Cache the result
1144
+ this.formDefinitionCache.set(cacheKey, formDefinition);
1145
+ return formDefinition;
1114
1146
  }
1115
- passwordBox(options) {
1116
- if (this.isContainerWidgetType(this.widgetState.widget?.type)) {
1117
- const child = new WidgetBuilder();
1118
- child.passwordBox(options);
1119
- this.widgetState.children.push(child.build());
1120
- return this;
1147
+ /**
1148
+ * Validate that a widget tree represents a valid dynamic form structure
1149
+ */
1150
+ validateFormWidgetTree(widgetTree) {
1151
+ if (!widgetTree || widgetTree.type !== 'grid-layout') {
1152
+ return false;
1121
1153
  }
1122
- else {
1123
- this.widgetState.widget = {
1124
- type: 'password-editor',
1125
- options,
1126
- };
1127
- return this;
1154
+ if (!widgetTree.children || widgetTree.children.length === 0) {
1155
+ return true; // Empty form is valid
1128
1156
  }
1157
+ // Check that all children are fieldset-layout widgets
1158
+ return widgetTree.children.every(child => child.type === 'fieldset-layout' &&
1159
+ child.children &&
1160
+ child.children.every(formField => formField.type === 'form-field' &&
1161
+ formField.children &&
1162
+ formField.children.length > 0));
1129
1163
  }
1130
- numberBox(options) {
1131
- if (this.isContainerWidgetType(this.widgetState.widget?.type)) {
1132
- const child = new WidgetBuilder();
1133
- child.numberBox(options);
1134
- this.widgetState.children.push(child.build());
1135
- return this;
1136
- }
1137
- else {
1138
- this.widgetState.widget = {
1139
- type: 'number-editor',
1140
- options,
1141
- };
1142
- return this;
1143
- }
1164
+ /**
1165
+ * Clear all caches
1166
+ */
1167
+ clearCaches() {
1168
+ this.widgetTreeCache.clear();
1169
+ this.formDefinitionCache.clear();
1144
1170
  }
1145
- selectBox(options) {
1146
- if (this.isContainerWidgetType(this.widgetState.widget?.type)) {
1147
- const child = new WidgetBuilder();
1148
- child.selectBox(options);
1149
- this.widgetState.children.push(child.build());
1150
- return this;
1151
- }
1152
- else {
1153
- this.widgetState.widget = {
1154
- type: 'select-editor',
1155
- options,
1156
- };
1157
- return this;
1158
- }
1171
+ /**
1172
+ * Get cache statistics
1173
+ */
1174
+ getCacheStats() {
1175
+ return {
1176
+ widgetTreeCacheSize: this.widgetTreeCache.size,
1177
+ formDefinitionCacheSize: this.formDefinitionCache.size
1178
+ };
1159
1179
  }
1160
- lookupBox(options) {
1161
- if (this.isContainerWidgetType(this.widgetState.widget?.type)) {
1162
- const child = new WidgetBuilder();
1163
- child.lookupBox(options);
1164
- this.widgetState.children.push(child.build());
1165
- return this;
1180
+ //#endregion
1181
+ //#region ---- Private Methods ----
1182
+ /**
1183
+ * Convert a single group to Fieldset widget structure
1184
+ */
1185
+ createGroupAsFieldsetWidget(group) {
1186
+ // Determine columns count from layout or default to 1
1187
+ const columnsCount = 1;
1188
+ return {
1189
+ type: 'fieldset-layout',
1190
+ name: group.name,
1191
+ options: {
1192
+ title: group.title,
1193
+ description: group.description,
1194
+ cols: columnsCount
1195
+ },
1196
+ children: this.createFieldWidgets(group.parameters, columnsCount)
1197
+ };
1198
+ }
1199
+ /**
1200
+ * Convert fields to Form Field widgets
1201
+ */
1202
+ createFieldWidgets(fields, columnsCount) {
1203
+ return fields.map(field => this.createFormFieldWidget(field));
1204
+ }
1205
+ /**
1206
+ * Convert a single field to Form Field widget with editor as child
1207
+ */
1208
+ createFormFieldWidget(field) {
1209
+ return {
1210
+ type: 'form-field',
1211
+ name: field.path,
1212
+ options: {
1213
+ label: field.title,
1214
+ description: field.description,
1215
+ showLabel: true
1216
+ },
1217
+ children: [field.widget] // The editor widget becomes a child of form-field
1218
+ };
1219
+ }
1220
+ /**
1221
+ * Extract group information from Fieldset Layout widget
1222
+ */
1223
+ extractGroupFromFieldset(fieldsetNode) {
1224
+ const columnsCount = fieldsetNode.options?.['cols'] || 1;
1225
+ // Extract fields directly from fieldset children
1226
+ const fields = [];
1227
+ if (fieldsetNode.children) {
1228
+ fieldsetNode.children.forEach(formField => {
1229
+ if (formField.type === 'form-field' && formField.children && formField.children.length > 0) {
1230
+ const field = this.extractFieldFromFormWidget(formField);
1231
+ if (field) {
1232
+ fields.push(field);
1233
+ }
1234
+ }
1235
+ });
1166
1236
  }
1167
- else {
1168
- this.widgetState.widget = {
1169
- type: 'lookup-editor',
1170
- options,
1171
- };
1172
- return this;
1237
+ return {
1238
+ name: fieldsetNode.name || `group-${Date.now()}`,
1239
+ title: fieldsetNode.options?.['title'],
1240
+ description: fieldsetNode.options?.['description'],
1241
+ parameters: fields,
1242
+ };
1243
+ }
1244
+ /**
1245
+ * Extract field information from Form Field widget
1246
+ */
1247
+ extractFieldFromFormWidget(formFieldNode) {
1248
+ if (!formFieldNode.children || formFieldNode.children.length === 0) {
1249
+ return null;
1173
1250
  }
1251
+ const editorWidget = formFieldNode.children[0];
1252
+ return {
1253
+ path: formFieldNode.name || editorWidget.name || `field-${Date.now()}`,
1254
+ title: formFieldNode.options?.['label'],
1255
+ description: formFieldNode.options?.['description'],
1256
+ widget: editorWidget,
1257
+ mode: formFieldNode.mode
1258
+ };
1174
1259
  }
1175
- selectionList(options) {
1176
- if (this.isContainerWidgetType(this.widgetState.widget?.type)) {
1177
- const child = new WidgetBuilder();
1178
- child.selectionList(options);
1179
- this.widgetState.children.push(child.build());
1180
- return this;
1260
+ /**
1261
+ * Create cache key for form definition
1262
+ */
1263
+ createFormDefinitionCacheKey(formDefinition) {
1264
+ // Create a hash-like key instead of full JSON string
1265
+ const keyParts = [];
1266
+ keyParts.push(`groups:${formDefinition.groups.length}`);
1267
+ formDefinition.groups.forEach((group, groupIndex) => {
1268
+ keyParts.push(`g${groupIndex}:${group.name}:${group.parameters.length}`);
1269
+ group.parameters.forEach((param, paramIndex) => {
1270
+ keyParts.push(`p${groupIndex}.${paramIndex}:${param.path}:${param.widget.type}`);
1271
+ });
1272
+ });
1273
+ if (formDefinition.mode) {
1274
+ keyParts.push(`mode:${formDefinition.mode}`);
1181
1275
  }
1182
- else {
1183
- this.widgetState.widget = {
1184
- type: 'selection-list',
1185
- options,
1186
- };
1187
- return this;
1276
+ // Join with delimiter and create a shorter hash
1277
+ const keyString = keyParts.join('|');
1278
+ // If still too long, create a simple hash
1279
+ if (keyString.length > 100) {
1280
+ return this.createSimpleHash(keyString);
1188
1281
  }
1282
+ return keyString;
1189
1283
  }
1190
- dateTimeBox(options) {
1191
- if (this.isContainerWidgetType(this.widgetState.widget?.type)) {
1192
- const child = new WidgetBuilder();
1193
- child.dateTimeBox(options);
1194
- this.widgetState.children.push(child.build());
1195
- return this;
1284
+ /**
1285
+ * Create cache key for widget tree
1286
+ */
1287
+ createWidgetTreeCacheKey(widgetTree) {
1288
+ // Create a hash-like key instead of full JSON string
1289
+ const keyParts = [];
1290
+ keyParts.push(`type:${widgetTree.type}`);
1291
+ if (widgetTree.name) {
1292
+ keyParts.push(`name:${widgetTree.name}`);
1196
1293
  }
1197
- else {
1198
- this.widgetState.widget = {
1199
- type: 'date-time-editor',
1200
- options,
1201
- };
1202
- return this;
1294
+ if (widgetTree.children) {
1295
+ keyParts.push(`children:${widgetTree.children.length}`);
1296
+ widgetTree.children.forEach((child, index) => {
1297
+ keyParts.push(`c${index}:${child.type}`);
1298
+ if (child.children) {
1299
+ keyParts.push(`cc${index}:${child.children.length}`);
1300
+ child.children.forEach((grandChild, gIndex) => {
1301
+ keyParts.push(`gc${index}.${gIndex}:${grandChild.type}`);
1302
+ if (grandChild.children) {
1303
+ keyParts.push(`gcc${index}.${gIndex}:${grandChild.children.length}`);
1304
+ }
1305
+ });
1306
+ }
1307
+ });
1203
1308
  }
1204
- }
1205
- toggleSwitch(options) {
1206
- if (this.isContainerWidgetType(this.widgetState.widget?.type)) {
1207
- const child = new WidgetBuilder();
1208
- child.toggleSwitch(options);
1209
- this.widgetState.children.push(child.build());
1210
- return this;
1309
+ // Join with delimiter and create a shorter hash
1310
+ const keyString = keyParts.join('|');
1311
+ // If still too long, create a simple hash
1312
+ if (keyString.length > 100) {
1313
+ return this.createSimpleHash(keyString);
1211
1314
  }
1212
- else {
1213
- this.widgetState.widget = {
1214
- type: 'toggle-editor',
1215
- options,
1216
- };
1217
- return this;
1315
+ return keyString;
1316
+ }
1317
+ /**
1318
+ * Create a simple hash from a string
1319
+ */
1320
+ createSimpleHash(str) {
1321
+ let hash = 0;
1322
+ if (str.length === 0)
1323
+ return hash.toString();
1324
+ for (let i = 0; i < str.length; i++) {
1325
+ const char = str.charCodeAt(i);
1326
+ hash = ((hash << 5) - hash) + char;
1327
+ hash = hash & hash; // Convert to 32-bit integer
1218
1328
  }
1329
+ return Math.abs(hash).toString(36); // Convert to base36 for shorter string
1219
1330
  }
1220
- colorBox(options) {
1221
- if (this.isContainerWidgetType(this.widgetState.widget?.type)) {
1222
- const child = new WidgetBuilder();
1223
- child.colorBox(options);
1224
- this.widgetState.children.push(child.build());
1225
- return this;
1331
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXPLayoutConversionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1332
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXPLayoutConversionService, providedIn: 'root' }); }
1333
+ }
1334
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXPLayoutConversionService, decorators: [{
1335
+ type: Injectable,
1336
+ args: [{
1337
+ providedIn: 'root'
1338
+ }]
1339
+ }] });
1340
+
1341
+ class AXPLayoutRendererComponent {
1342
+ constructor() {
1343
+ this.evaluatorService = inject(AXPExpressionEvaluatorService);
1344
+ this.conversionService = inject(AXPLayoutConversionService);
1345
+ /**
1346
+ * Tracks the latest scheduled evaluation to ensure last-write-wins for async evaluate
1347
+ */
1348
+ this.evaluationRunId = 0;
1349
+ /**
1350
+ * RxJS subjects for context management
1351
+ */
1352
+ this.contextUpdateSubject = new Subject();
1353
+ this.contextChangeSubject = new Subject();
1354
+ /**
1355
+ * Cache for expression evaluation results
1356
+ */
1357
+ this.expressionCache = new Map();
1358
+ /**
1359
+ * Cache for widget tree comparisons
1360
+ */
1361
+ this.widgetTreeCache = new Map();
1362
+ /**
1363
+ * Last layout hash for change detection
1364
+ */
1365
+ this.lastLayoutHash = '';
1366
+ //#region ---- Inputs ----
1367
+ /**
1368
+ * Form definition containing groups and fields OR widget tree
1369
+ */
1370
+ this.layout = input.required(...(ngDevMode ? [{ debugName: "layout" }] : []));
1371
+ /**
1372
+ * Form context/model data
1373
+ */
1374
+ this.context = model({}, ...(ngDevMode ? [{ debugName: "context" }] : []));
1375
+ /**
1376
+ * Form appearance and density styling (normal, compact, spacious)
1377
+ */
1378
+ this.look = input('fieldset', ...(ngDevMode ? [{ debugName: "look" }] : []));
1379
+ /**
1380
+ * Default form mode. Can be overridden by section/group and field.
1381
+ */
1382
+ this.mode = input('edit', ...(ngDevMode ? [{ debugName: "mode" }] : []));
1383
+ //#endregion
1384
+ //#region ---- Widget Tree Conversion ----
1385
+ this.widgetTree = signal(null, ...(ngDevMode ? [{ debugName: "widgetTree" }] : []));
1386
+ /**
1387
+ * Convert and evaluate data when inputs change (optimized with RxJS)
1388
+ */
1389
+ this.conversionEffect = effect(() => {
1390
+ const inputData = this.layout();
1391
+ const ctx = this.internalContext();
1392
+ const look = this.look();
1393
+ const runId = ++this.evaluationRunId;
1394
+ // Generate layout hash for change detection
1395
+ const layoutHash = this.generateLayoutHash(inputData, look);
1396
+ // Skip if layout hasn't changed
1397
+ if (layoutHash === this.lastLayoutHash && this.widgetTree()) {
1398
+ return;
1399
+ }
1400
+ this.lastLayoutHash = layoutHash;
1401
+ (async () => {
1402
+ // First evaluate expressions if needed
1403
+ const evaluated = await this.expressionEvaluator(inputData, ctx);
1404
+ // Ignore stale results
1405
+ if (runId !== this.evaluationRunId) {
1406
+ return;
1407
+ }
1408
+ // Convert to widget tree (this will also apply the layout look)
1409
+ const tree = evaluated;
1410
+ // Update widget tree
1411
+ const prev = this.widgetTree();
1412
+ if (!isEqual(prev, tree)) {
1413
+ tree.mode = this.mode();
1414
+ this.widgetTree.set(tree);
1415
+ }
1416
+ })();
1417
+ }, ...(ngDevMode ? [{ debugName: "conversionEffect" }] : []));
1418
+ //#endregion
1419
+ //#region ---- Outputs ----
1420
+ /**
1421
+ * Emitted when context change is initiated
1422
+ */
1423
+ this.contextInitiated = output();
1424
+ /**
1425
+ * Emitted when form becomes valid/invalid
1426
+ */
1427
+ this.validityChange = output();
1428
+ //#endregion
1429
+ //#region ---- Properties ----
1430
+ this.form = viewChild(AXFormComponent, ...(ngDevMode ? [{ debugName: "form" }] : []));
1431
+ this.container = viewChild(AXPWidgetContainerComponent, ...(ngDevMode ? [{ debugName: "container" }] : []));
1432
+ /**
1433
+ * Internal context signal for reactivity
1434
+ */
1435
+ this.internalContext = signal({}, ...(ngDevMode ? [{ debugName: "internalContext" }] : []));
1436
+ //#endregion
1437
+ //#region ---- Effects ----
1438
+ /**
1439
+ * Effect to sync context changes from external to internal (optimized with RxJS)
1440
+ */
1441
+ this.#contextSyncEffect = effect(() => {
1442
+ const ctx = this.context() ?? {};
1443
+ this.contextUpdateSubject.next(ctx);
1444
+ }, ...(ngDevMode ? [{ debugName: "#contextSyncEffect" }] : []));
1445
+ /**
1446
+ * Effect to handle widget tree status changes
1447
+ */
1448
+ this.#widgetStatusEffect = effect(() => {
1449
+ const widgetTree = this.widgetTree();
1450
+ if (widgetTree) {
1451
+ this.container()?.builderService.setStatus(AXPPageStatus.Rendered);
1452
+ }
1453
+ }, ...(ngDevMode ? [{ debugName: "#widgetStatusEffect" }] : []));
1454
+ }
1455
+ async expressionEvaluator(expression, context) {
1456
+ // Check if it's a form definition that needs conversion
1457
+ if (this.isFormDefinition(expression)) {
1458
+ return this.conversionService.convertFormDefinition(expression);
1226
1459
  }
1227
- else {
1228
- this.widgetState.widget = {
1229
- type: 'color-editor',
1230
- options,
1231
- };
1232
- return this;
1460
+ // Generate cache key using a more efficient method
1461
+ const cacheKey = this.generateCacheKey(expression, context);
1462
+ // Check cache first
1463
+ if (this.expressionCache.has(cacheKey)) {
1464
+ return this.expressionCache.get(cacheKey);
1465
+ }
1466
+ const scope = {
1467
+ context: {
1468
+ eval: (path) => get(context, path),
1469
+ },
1470
+ };
1471
+ const result = await this.evaluatorService.evaluate(expression, scope);
1472
+ // Cache result with LRU-like behavior
1473
+ if (this.expressionCache.size > 50) {
1474
+ // Clear half the cache when it gets too large
1475
+ const keysToDelete = Array.from(this.expressionCache.keys()).slice(0, 25);
1476
+ keysToDelete.forEach(key => this.expressionCache.delete(key));
1233
1477
  }
1478
+ this.expressionCache.set(cacheKey, result);
1479
+ return result;
1234
1480
  }
1235
- customWidget(type, options) {
1236
- if (this.isContainerWidgetType(this.widgetState.widget?.type)) {
1237
- const child = new WidgetBuilder();
1238
- child.customWidget(type, options);
1239
- this.widgetState.children.push(child.build());
1240
- return this;
1481
+ //#endregion
1482
+ //#region ---- Lifecycle Methods ----
1483
+ ngOnInit() {
1484
+ // Initialize internal context with input context
1485
+ this.internalContext.set(this.context() ?? {});
1486
+ // Setup RxJS streams for context management
1487
+ this.setupContextStreams();
1488
+ }
1489
+ //#endregion
1490
+ //#region ---- Effects ----
1491
+ /**
1492
+ * Effect to sync context changes from external to internal (optimized with RxJS)
1493
+ */
1494
+ #contextSyncEffect;
1495
+ /**
1496
+ * Effect to handle widget tree status changes
1497
+ */
1498
+ #widgetStatusEffect;
1499
+ //#endregion
1500
+ //#region ---- Event Handlers ----
1501
+ /**
1502
+ * Handle context change events from widget container (optimized with RxJS)
1503
+ */
1504
+ handleContextChanged(event) {
1505
+ if (event.state === 'initiated') {
1506
+ this.contextInitiated.emit(event.data);
1241
1507
  }
1242
1508
  else {
1243
- this.widgetState.widget = {
1244
- type,
1245
- options: options,
1246
- };
1247
- return this;
1509
+ this.contextChangeSubject.next(event.data ?? {});
1248
1510
  }
1249
1511
  }
1250
- build() {
1512
+ //#endregion
1513
+ //#region ---- Public Methods ----
1514
+ /**
1515
+ * Get the form component instance
1516
+ */
1517
+ getForm() {
1518
+ return this.form();
1519
+ }
1520
+ /**
1521
+ * Get the widget container component instance
1522
+ */
1523
+ getContainer() {
1524
+ return this.container();
1525
+ }
1526
+ /**
1527
+ * Get current form context
1528
+ */
1529
+ getContext() {
1530
+ return this.internalContext();
1531
+ }
1532
+ /**
1533
+ * Update form context programmatically
1534
+ */
1535
+ updateContext(context) {
1536
+ this.internalContext.set(context);
1537
+ }
1538
+ /**
1539
+ * Get the current widget tree
1540
+ */
1541
+ getWidgetTree() {
1542
+ return this.widgetTree();
1543
+ }
1544
+ /**
1545
+ * Validate the form
1546
+ */
1547
+ async validate() {
1548
+ const form = this.form();
1549
+ if (form) {
1550
+ const isValid = await form.validate();
1551
+ this.validityChange.emit(isValid.result);
1552
+ return isValid;
1553
+ }
1251
1554
  return {
1252
- id: this.widgetState.id,
1253
- name: this.widgetState.name,
1254
- widget: this.widgetState.widget,
1255
- children: this.widgetState.children,
1256
- layout: this.widgetState.layout,
1257
- mode: this.widgetState.mode,
1258
- order: this.widgetState.order,
1259
- visible: this.widgetState.visible,
1260
- disabled: this.widgetState.disabled,
1261
- readonly: this.widgetState.readonly,
1555
+ result: false,
1556
+ messages: [],
1557
+ rules: [],
1262
1558
  };
1263
1559
  }
1264
- }
1265
-
1266
- class AXPLayoutRendererComponent {
1267
- constructor() {
1268
- this.renderedNode = {};
1560
+ //#endregion
1561
+ //#region ---- RxJS Stream Setup ----
1562
+ /**
1563
+ * Setup RxJS streams for context management
1564
+ */
1565
+ setupContextStreams() {
1566
+ // Debounced context updates from external source
1567
+ this.contextUpdateSubject
1568
+ .pipe(debounceTime(16), // ~60fps
1569
+ distinctUntilChanged((prev, curr) => isEqual(prev, curr)), startWith(this.context() ?? {}))
1570
+ .subscribe(ctx => {
1571
+ this.internalContext.set(ctx);
1572
+ });
1573
+ // Debounced context changes from widgets
1574
+ this.contextChangeSubject
1575
+ .pipe(debounceTime(16), // ~60fps
1576
+ distinctUntilChanged((prev, curr) => isEqual(prev, curr)))
1577
+ .subscribe(ctx => {
1578
+ this.internalContext.set(ctx);
1579
+ // Update the model signal directly - it will emit change events automatically
1580
+ this.context.set(this.internalContext());
1581
+ });
1269
1582
  }
1270
- ngOnInit() {
1271
- if (this.builder) {
1272
- this.renderedNode = {
1273
- type: 'block-layout',
1274
- mode: this.builder.mode ?? 'view',
1275
- children: (this.builder.nodes ?? []).map((node) => ({
1276
- type: node.widget?.type ?? 'text-block-layout',
1277
- mode: node.mode ?? 'view',
1278
- children: (node.children ?? []).map((child) => ({
1279
- type: child.widget?.type ?? 'text-block-layout',
1280
- mode: child.mode ?? 'view',
1281
- })),
1282
- })),
1283
- };
1583
+ //#endregion
1584
+ //#region ---- Type Guards ----
1585
+ /**
1586
+ * Type guard to check if the input is a form definition
1587
+ */
1588
+ isFormDefinition(data) {
1589
+ return data &&
1590
+ typeof data === 'object' &&
1591
+ 'groups' in data &&
1592
+ Array.isArray(data.groups);
1593
+ }
1594
+ //#endregion
1595
+ //#region ---- Utility Methods ----
1596
+ /**
1597
+ * Generate layout hash for change detection (short hash)
1598
+ */
1599
+ generateLayoutHash(layout, look) {
1600
+ if (!layout)
1601
+ return '';
1602
+ // Generate short hash for large layout strings
1603
+ const layoutStr = typeof layout === 'string' ? layout : JSON.stringify(layout);
1604
+ const layoutHash = this.simpleHash(layoutStr);
1605
+ return `${layoutHash}|${look}`;
1606
+ }
1607
+ /**
1608
+ * Generate cache key for expression evaluation (short hash)
1609
+ */
1610
+ generateCacheKey(expression, context) {
1611
+ // Use short hash for better performance
1612
+ const exprStr = typeof expression === 'string' ? expression : JSON.stringify(expression);
1613
+ const exprHash = this.simpleHash(exprStr);
1614
+ const ctxHash = this.generateContextHash(context);
1615
+ return `${exprHash}|${ctxHash}`;
1616
+ }
1617
+ /**
1618
+ * Generate a simple hash for context change detection
1619
+ */
1620
+ generateContextHash(context) {
1621
+ if (!context || typeof context !== 'object') {
1622
+ return String(context);
1284
1623
  }
1624
+ // Generate short hash for context
1625
+ const keys = Object.keys(context).sort();
1626
+ const contextStr = keys.map(key => `${key}:${context[key]}`).join('|');
1627
+ return this.simpleHash(contextStr);
1285
1628
  }
1286
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: AXPLayoutRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1287
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.2.4", type: AXPLayoutRendererComponent, isStandalone: true, selector: "axp-layout-renderer", inputs: { builder: "builder" }, ngImport: i0, template: `
1288
- <axp-widgets-container>
1289
- <ng-container
1290
- axp-widget-renderer
1291
- [node]="renderedNode"
1292
- [mode]="builder?.mode ?? 'view'">
1293
- </ng-container>
1294
- </axp-widgets-container>
1295
- `, 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"] }] }); }
1629
+ /**
1630
+ * Simple hash function for generating short keys
1631
+ */
1632
+ simpleHash(str) {
1633
+ let hash = 0;
1634
+ if (str.length === 0)
1635
+ return hash.toString();
1636
+ for (let i = 0; i < str.length; i++) {
1637
+ const char = str.charCodeAt(i);
1638
+ hash = ((hash << 5) - hash) + char;
1639
+ hash = hash & hash; // Convert to 32-bit integer
1640
+ }
1641
+ // Convert to positive hex string
1642
+ return Math.abs(hash).toString(16);
1643
+ }
1644
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXPLayoutRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1645
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", 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: `
1646
+ <ax-form>
1647
+ <axp-widgets-container [context]="internalContext()" (onContextChanged)="handleContextChanged($event)">
1648
+ @if (widgetTree()) {
1649
+ <ng-container axp-widget-renderer [node]="widgetTree()!" [mode]="mode()"></ng-container>
1650
+ }
1651
+ </axp-widgets-container>
1652
+ </ax-form>
1653
+ `, 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 }); }
1296
1654
  }
1297
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: AXPLayoutRendererComponent, decorators: [{
1655
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXPLayoutRendererComponent, decorators: [{
1298
1656
  type: Component,
1299
- args: [{ selector: 'axp-layout-renderer', standalone: true, imports: [CommonModule, AXPWidgetCoreModule], template: `
1300
- <axp-widgets-container>
1301
- <ng-container
1302
- axp-widget-renderer
1303
- [node]="renderedNode"
1304
- [mode]="builder?.mode ?? 'view'">
1305
- </ng-container>
1306
- </axp-widgets-container>
1657
+ args: [{ selector: 'axp-layout-renderer', standalone: true, imports: [CommonModule, AXPWidgetCoreModule, AXFormModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
1658
+ <ax-form>
1659
+ <axp-widgets-container [context]="internalContext()" (onContextChanged)="handleContextChanged($event)">
1660
+ @if (widgetTree()) {
1661
+ <ng-container axp-widget-renderer [node]="widgetTree()!" [mode]="mode()"></ng-container>
1662
+ }
1663
+ </axp-widgets-container>
1664
+ </ax-form>
1307
1665
  `, styles: [":host{display:block;width:100%}\n"] }]
1308
- }], propDecorators: { builder: [{
1309
- type: Input
1310
- }] } });
1666
+ }] });
1311
1667
 
1312
1668
  class LayoutBuilderModule {
1313
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: LayoutBuilderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1314
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.2.4", ngImport: i0, type: LayoutBuilderModule, imports: [CommonModule, AXPLayoutRendererComponent], exports: [AXPLayoutRendererComponent] }); }
1315
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: LayoutBuilderModule, providers: [AXPLayoutBuilderService], imports: [CommonModule, AXPLayoutRendererComponent] }); }
1669
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: LayoutBuilderModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1670
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.3", ngImport: i0, type: LayoutBuilderModule, imports: [CommonModule, AXPLayoutRendererComponent], exports: [AXPLayoutRendererComponent] }); }
1671
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: LayoutBuilderModule, providers: [AXPLayoutBuilderService], imports: [CommonModule, AXPLayoutRendererComponent] }); }
1316
1672
  }
1317
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: LayoutBuilderModule, decorators: [{
1673
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: LayoutBuilderModule, decorators: [{
1318
1674
  type: NgModule,
1319
1675
  args: [{
1320
1676
  imports: [CommonModule, AXPLayoutRendererComponent],
@@ -1323,9 +1679,219 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImpor
1323
1679
  }]
1324
1680
  }] });
1325
1681
 
1682
+ //#endregion
1683
+
1684
+ class AXPDialogRendererComponent extends AXBasePageComponent {
1685
+ constructor() {
1686
+ super(...arguments);
1687
+ this.result = new EventEmitter();
1688
+ this.context = signal({}, ...(ngDevMode ? [{ debugName: "context" }] : []));
1689
+ // This will be set by the popup service automatically - same as dynamic-dialog
1690
+ this.callBack = () => { };
1691
+ this.isDialogLoading = signal(false, ...(ngDevMode ? [{ debugName: "isDialogLoading" }] : []));
1692
+ }
1693
+ ngOnInit() {
1694
+ // Initialize context with provided context
1695
+ this.context.set(this.config?.context || {});
1696
+ }
1697
+ handleContextChanged(event) {
1698
+ this.context.set(event);
1699
+ }
1700
+ handleContextInitiated(event) {
1701
+ this.context.set(event);
1702
+ }
1703
+ visibleFooterPrefixActions() {
1704
+ return this.config?.actions?.footer?.prefix || [];
1705
+ }
1706
+ visibleFooterSuffixActions() {
1707
+ return this.config?.actions?.footer?.suffix || [
1708
+ { title: 'Cancel', color: 'secondary', command: { name: 'cancel' } },
1709
+ { title: 'Confirm', color: 'primary', command: { name: 'confirm' } }
1710
+ ];
1711
+ }
1712
+ isFormLoading() {
1713
+ return this.isDialogLoading();
1714
+ }
1715
+ isSubmitting() {
1716
+ return this.isDialogLoading();
1717
+ }
1718
+ async executeAction(action) {
1719
+ const actionName = action.command?.name || action.title?.toLowerCase();
1720
+ // Store the action and context - same pattern as dynamic-dialog
1721
+ const result = {
1722
+ context: this.context(),
1723
+ action: actionName
1724
+ };
1725
+ // Store result in component property
1726
+ this.dialogResult = result;
1727
+ // Store in popup data for DialogRef access
1728
+ if (this.data) {
1729
+ this.data.context = result.context;
1730
+ this.data.action = result.action;
1731
+ }
1732
+ // Call the callback with DialogRef - same pattern as dynamic-dialog
1733
+ this.callBack({
1734
+ close: (result) => {
1735
+ this.close(result);
1736
+ },
1737
+ context: () => {
1738
+ return this.context();
1739
+ },
1740
+ action: () => {
1741
+ return result.action;
1742
+ },
1743
+ setLoading: (loading) => {
1744
+ this.isDialogLoading.set(loading);
1745
+ }
1746
+ });
1747
+ }
1748
+ close(result) {
1749
+ if (result) {
1750
+ this.result.emit(result);
1751
+ }
1752
+ super.close(result);
1753
+ }
1754
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXPDialogRendererComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
1755
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: AXPDialogRendererComponent, isStandalone: true, selector: "axp-dialog-renderer", inputs: { config: "config" }, outputs: { result: "result" }, usesInheritance: true, ngImport: i0, template: `
1756
+ <div class="ax-p-4">
1757
+ <axp-layout-renderer
1758
+ [layout]="config.definition"
1759
+ [context]="context()"
1760
+ (contextChange)="handleContextChanged($event)"
1761
+ (contextInitiated)="handleContextInitiated($event)"
1762
+ >
1763
+ </axp-layout-renderer>
1764
+ </div>
1765
+
1766
+ <ax-footer>
1767
+ <ax-prefix>
1768
+ <ng-container *ngTemplateOutlet="footerPrefixActions"></ng-container>
1769
+ </ax-prefix>
1770
+ <ax-suffix>
1771
+ <ng-container *ngTemplateOutlet="footerSuffixActions"></ng-container>
1772
+ </ax-suffix>
1773
+ </ax-footer>
1774
+
1775
+ <!-- Footer Prefix Actions -->
1776
+ <ng-template #footerPrefixActions>
1777
+ @for (action of visibleFooterPrefixActions(); track $index) {
1778
+ <ax-button
1779
+ [disabled]="action.disabled || isFormLoading()"
1780
+ [text]="(action.title | translate | async)!"
1781
+ [look]="'outline'"
1782
+ [color]="action.color"
1783
+ (onClick)="executeAction(action)"
1784
+ >
1785
+ @if (isFormLoading() && action.command?.name != 'cancel') {
1786
+ <ax-loading></ax-loading>
1787
+ }
1788
+ <ax-prefix>
1789
+ <i class="{{ action.icon }}"></i>
1790
+ </ax-prefix>
1791
+ </ax-button>
1792
+ }
1793
+ </ng-template>
1794
+
1795
+ <!-- Footer Suffix Actions -->
1796
+ <ng-template #footerSuffixActions>
1797
+ @for (action of visibleFooterSuffixActions(); track $index) {
1798
+ <ax-button
1799
+ [disabled]="action.disabled || isSubmitting()"
1800
+ [text]="(action.title | translate | async)!"
1801
+ [look]="'solid'"
1802
+ [color]="action.color"
1803
+ (onClick)="executeAction(action)"
1804
+ >
1805
+ @if (action.icon) {
1806
+ <ax-prefix>
1807
+ <ax-icon icon="{{ action.icon }}"></ax-icon>
1808
+ </ax-prefix>
1809
+ }
1810
+ </ax-button>
1811
+ }
1812
+ </ng-template>
1813
+ `, 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" }] }); }
1814
+ }
1815
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXPDialogRendererComponent, decorators: [{
1816
+ type: Component,
1817
+ args: [{
1818
+ selector: 'axp-dialog-renderer',
1819
+ standalone: true,
1820
+ imports: [CommonModule, AXPLayoutRendererComponent, AXButtonModule, AXDecoratorModule, AXLoadingModule, AXTranslationModule],
1821
+ template: `
1822
+ <div class="ax-p-4">
1823
+ <axp-layout-renderer
1824
+ [layout]="config.definition"
1825
+ [context]="context()"
1826
+ (contextChange)="handleContextChanged($event)"
1827
+ (contextInitiated)="handleContextInitiated($event)"
1828
+ >
1829
+ </axp-layout-renderer>
1830
+ </div>
1831
+
1832
+ <ax-footer>
1833
+ <ax-prefix>
1834
+ <ng-container *ngTemplateOutlet="footerPrefixActions"></ng-container>
1835
+ </ax-prefix>
1836
+ <ax-suffix>
1837
+ <ng-container *ngTemplateOutlet="footerSuffixActions"></ng-container>
1838
+ </ax-suffix>
1839
+ </ax-footer>
1840
+
1841
+ <!-- Footer Prefix Actions -->
1842
+ <ng-template #footerPrefixActions>
1843
+ @for (action of visibleFooterPrefixActions(); track $index) {
1844
+ <ax-button
1845
+ [disabled]="action.disabled || isFormLoading()"
1846
+ [text]="(action.title | translate | async)!"
1847
+ [look]="'outline'"
1848
+ [color]="action.color"
1849
+ (onClick)="executeAction(action)"
1850
+ >
1851
+ @if (isFormLoading() && action.command?.name != 'cancel') {
1852
+ <ax-loading></ax-loading>
1853
+ }
1854
+ <ax-prefix>
1855
+ <i class="{{ action.icon }}"></i>
1856
+ </ax-prefix>
1857
+ </ax-button>
1858
+ }
1859
+ </ng-template>
1860
+
1861
+ <!-- Footer Suffix Actions -->
1862
+ <ng-template #footerSuffixActions>
1863
+ @for (action of visibleFooterSuffixActions(); track $index) {
1864
+ <ax-button
1865
+ [disabled]="action.disabled || isSubmitting()"
1866
+ [text]="(action.title | translate | async)!"
1867
+ [look]="'solid'"
1868
+ [color]="action.color"
1869
+ (onClick)="executeAction(action)"
1870
+ >
1871
+ @if (action.icon) {
1872
+ <ax-prefix>
1873
+ <ax-icon icon="{{ action.icon }}"></ax-icon>
1874
+ </ax-prefix>
1875
+ }
1876
+ </ax-button>
1877
+ }
1878
+ </ng-template>
1879
+ `
1880
+ }]
1881
+ }], propDecorators: { config: [{
1882
+ type: Input
1883
+ }], result: [{
1884
+ type: Output
1885
+ }] } });
1886
+
1887
+ var dialogRenderer_component = /*#__PURE__*/Object.freeze({
1888
+ __proto__: null,
1889
+ AXPDialogRendererComponent: AXPDialogRendererComponent
1890
+ });
1891
+
1326
1892
  /**
1327
1893
  * Generated bundle index. Do not edit.
1328
1894
  */
1329
1895
 
1330
- export { AXPLayoutBuilderService, AXPLayoutRendererComponent, LayoutBuilderModule };
1896
+ export { AXPDialogRendererComponent, AXPLayoutBuilderService, AXPLayoutConversionService, AXPLayoutRendererComponent, LayoutBuilderModule };
1331
1897
  //# sourceMappingURL=acorex-platform-layout-builder.mjs.map