@acorex/platform 21.0.0-next.1 → 21.0.0-next.2

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 (69) hide show
  1. package/auth/index.d.ts +238 -92
  2. package/common/index.d.ts +42 -606
  3. package/core/index.d.ts +432 -574
  4. package/fesm2022/acorex-platform-auth.mjs +200 -153
  5. package/fesm2022/acorex-platform-auth.mjs.map +1 -1
  6. package/fesm2022/acorex-platform-common.mjs +112 -1009
  7. package/fesm2022/acorex-platform-common.mjs.map +1 -1
  8. package/fesm2022/acorex-platform-core.mjs +416 -694
  9. package/fesm2022/acorex-platform-core.mjs.map +1 -1
  10. package/fesm2022/acorex-platform-domain.mjs +11 -54
  11. package/fesm2022/acorex-platform-domain.mjs.map +1 -1
  12. package/fesm2022/acorex-platform-layout-builder.mjs +272 -414
  13. package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
  14. package/fesm2022/acorex-platform-layout-components.mjs +3105 -2064
  15. package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
  16. package/fesm2022/acorex-platform-layout-designer.mjs +7 -7
  17. package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
  18. package/fesm2022/acorex-platform-layout-entity.mjs +648 -756
  19. package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
  20. package/fesm2022/acorex-platform-layout-views.mjs +4 -4
  21. package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
  22. package/fesm2022/acorex-platform-layout-widget-core.mjs +174 -248
  23. package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
  24. package/fesm2022/acorex-platform-layout-widgets-extra-properties-schema-widget-edit.component-fhhZOWul.mjs +50 -0
  25. package/fesm2022/acorex-platform-layout-widgets-extra-properties-schema-widget-edit.component-fhhZOWul.mjs.map +1 -0
  26. package/fesm2022/acorex-platform-layout-widgets-extra-properties-schema-widget-view.component-C3Qbs0fz.mjs +42 -0
  27. package/fesm2022/acorex-platform-layout-widgets-extra-properties-schema-widget-view.component-C3Qbs0fz.mjs.map +1 -0
  28. package/fesm2022/acorex-platform-layout-widgets-extra-properties-values-widget-edit.component-CngQBUlN.mjs +55 -0
  29. package/fesm2022/acorex-platform-layout-widgets-extra-properties-values-widget-edit.component-CngQBUlN.mjs.map +1 -0
  30. package/fesm2022/acorex-platform-layout-widgets-extra-properties-values-widget-view.component-DSNo9e4W.mjs +50 -0
  31. package/fesm2022/acorex-platform-layout-widgets-extra-properties-values-widget-view.component-DSNo9e4W.mjs.map +1 -0
  32. package/fesm2022/acorex-platform-layout-widgets-extra-properties-widget-edit.component-CL0CwEHX.mjs +48 -0
  33. package/fesm2022/acorex-platform-layout-widgets-extra-properties-widget-edit.component-CL0CwEHX.mjs.map +1 -0
  34. package/fesm2022/acorex-platform-layout-widgets-extra-properties-widget-view.component-B6Fi0xTw.mjs +42 -0
  35. package/fesm2022/acorex-platform-layout-widgets-extra-properties-widget-view.component-B6Fi0xTw.mjs.map +1 -0
  36. package/fesm2022/{acorex-platform-layout-widgets-file-list-popup.component-CxrsI6Hn.mjs → acorex-platform-layout-widgets-file-list-popup.component-D0y-9nE5.mjs} +2 -2
  37. package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-D0y-9nE5.mjs.map +1 -0
  38. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-edit-popup.component-Ck7-wpT2.mjs → acorex-platform-layout-widgets-tabular-data-edit-popup.component-m8rHZP8L.mjs} +2 -2
  39. package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-m8rHZP8L.mjs.map +1 -0
  40. package/fesm2022/acorex-platform-layout-widgets.mjs +974 -2994
  41. package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
  42. package/fesm2022/{acorex-platform-themes-default-entity-master-create-view.component-VIGuU5M4.mjs → acorex-platform-themes-default-entity-master-create-view.component-mARj77Mr.mjs} +5 -26
  43. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-mARj77Mr.mjs.map +1 -0
  44. package/fesm2022/{acorex-platform-themes-default-entity-master-list-view.component-DyDa_hyd.mjs → acorex-platform-themes-default-entity-master-list-view.component-Cym8pq0v.mjs} +5 -4
  45. package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-Cym8pq0v.mjs.map +1 -0
  46. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-BTA6h7Xd.mjs +101 -0
  47. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-BTA6h7Xd.mjs.map +1 -0
  48. package/fesm2022/{acorex-platform-themes-default-entity-master-single-view.component-eMBby9k4.mjs → acorex-platform-themes-default-entity-master-single-view.component-B_P0a5KW.mjs} +3 -3
  49. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-B_P0a5KW.mjs.map +1 -0
  50. package/fesm2022/acorex-platform-themes-default.mjs +30 -166
  51. package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
  52. package/fesm2022/acorex-platform-themes-shared.mjs +27 -27
  53. package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
  54. package/layout/builder/index.d.ts +1 -4
  55. package/layout/components/index.d.ts +327 -405
  56. package/layout/designer/index.d.ts +3 -3
  57. package/layout/entity/index.d.ts +108 -163
  58. package/layout/widget-core/index.d.ts +49 -39
  59. package/layout/widgets/index.d.ts +54 -368
  60. package/package.json +5 -5
  61. package/themes/default/index.d.ts +2 -15
  62. package/themes/shared/index.d.ts +10 -10
  63. package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-CxrsI6Hn.mjs.map +0 -1
  64. package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-Ck7-wpT2.mjs.map +0 -1
  65. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-VIGuU5M4.mjs.map +0 -1
  66. package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-DyDa_hyd.mjs.map +0 -1
  67. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-Ua3ZA5hk.mjs +0 -101
  68. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-Ua3ZA5hk.mjs.map +0 -1
  69. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-eMBby9k4.mjs.map +0 -1
@@ -1,13 +1,13 @@
1
1
  import * as i4 from '@angular/common';
2
2
  import { CommonModule } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
- import { Injectable, inject, input, model, signal, effect, output, viewChild, ChangeDetectionStrategy, Component, NgModule, EventEmitter, Output, Input } from '@angular/core';
4
+ import { inject, Injectable, input, model, signal, effect, output, viewChild, ChangeDetectionStrategy, Component, NgModule, EventEmitter, Output, Input } from '@angular/core';
5
5
  import { AXPopupService } from '@acorex/components/popup';
6
- import { cloneDeep, isNil, set, isEqual } from 'lodash-es';
7
6
  import * as i2 from '@acorex/components/form';
8
7
  import { AXFormComponent, AXFormModule } from '@acorex/components/form';
9
8
  import * as i1 from '@acorex/platform/layout/widget-core';
10
9
  import { AXPWidgetContainerComponent, AXPPageStatus, AXPWidgetCoreModule } from '@acorex/platform/layout/widget-core';
10
+ import { isEqual, cloneDeep } from 'lodash-es';
11
11
  import { Subject, debounceTime, distinctUntilChanged, startWith } from 'rxjs';
12
12
  import * as i1$1 from '@acorex/components/button';
13
13
  import { AXButtonModule } from '@acorex/components/button';
@@ -20,281 +20,6 @@ import * as i5 from '@acorex/core/translation';
20
20
  import { AXTranslationModule } from '@acorex/core/translation';
21
21
  import { AXPExpressionEvaluatorService } from '@acorex/platform/core';
22
22
 
23
- class AXPLayoutConversionService {
24
- constructor() {
25
- //#region ---- Caching ----
26
- this.widgetTreeCache = new Map();
27
- this.formDefinitionCache = new Map();
28
- }
29
- //#endregion
30
- //#region ---- Public Methods ----
31
- /**
32
- * Convert AXPDynamicFormDefinition to AXPWidgetNode tree structure
33
- * Groups become Fieldset Layouts with Form Field widgets as children
34
- * Fields become Form Field widgets with Editor widgets as children
35
- */
36
- convertFormDefinition(formDefinition) {
37
- // Create cache key based on form definition content
38
- const cacheKey = this.createFormDefinitionCacheKey(formDefinition);
39
- // Check cache first
40
- if (this.widgetTreeCache.has(cacheKey)) {
41
- return this.widgetTreeCache.get(cacheKey);
42
- }
43
- // Generate widget tree
44
- const widgetTree = {
45
- type: 'grid-layout',
46
- name: 'dynamic-form-container',
47
- mode: formDefinition.mode, // Preserve form-level mode
48
- options: {
49
- title: 'Dynamic Form',
50
- grid: {
51
- default: {
52
- columns: 1,
53
- gap: '1rem',
54
- },
55
- },
56
- },
57
- children: formDefinition.groups.map((group) => this.createGroupAsFieldsetWidget(group, formDefinition.mode)),
58
- };
59
- // Cache the result
60
- this.widgetTreeCache.set(cacheKey, widgetTree);
61
- return widgetTree;
62
- }
63
- /**
64
- * Convert AXPWidgetNode tree back to AXPDynamicFormDefinition
65
- * Parses Fieldset Layouts back to Groups
66
- * Parses Form Field widgets back to Fields
67
- */
68
- convertWidgetTreeToFormDefinition(widgetTree) {
69
- // Create cache key based on widget tree content
70
- const cacheKey = this.createWidgetTreeCacheKey(widgetTree);
71
- // Check cache first
72
- if (this.formDefinitionCache.has(cacheKey)) {
73
- return this.formDefinitionCache.get(cacheKey);
74
- }
75
- // Parse widget tree
76
- const groups = [];
77
- if (widgetTree.children) {
78
- widgetTree.children.forEach((child) => {
79
- if (child.type === 'fieldset-layout') {
80
- const group = this.extractGroupFromFieldset(child);
81
- groups.push(group);
82
- }
83
- });
84
- }
85
- const formDefinition = { groups };
86
- // Cache the result
87
- this.formDefinitionCache.set(cacheKey, formDefinition);
88
- return formDefinition;
89
- }
90
- /**
91
- * Validate that a widget tree represents a valid dynamic form structure
92
- */
93
- validateFormWidgetTree(widgetTree) {
94
- if (!widgetTree || widgetTree.type !== 'grid-layout') {
95
- return false;
96
- }
97
- if (!widgetTree.children || widgetTree.children.length === 0) {
98
- return true; // Empty form is valid
99
- }
100
- // Check that all children are fieldset-layout widgets
101
- return widgetTree.children.every((child) => child.type === 'fieldset-layout' &&
102
- child.children &&
103
- child.children.every((formField) => formField.type === 'form-field' && formField.children && formField.children.length > 0));
104
- }
105
- /**
106
- * Clear all caches
107
- */
108
- clearCaches() {
109
- this.widgetTreeCache.clear();
110
- this.formDefinitionCache.clear();
111
- }
112
- /**
113
- * Get cache statistics
114
- */
115
- getCacheStats() {
116
- return {
117
- widgetTreeCacheSize: this.widgetTreeCache.size,
118
- formDefinitionCacheSize: this.formDefinitionCache.size,
119
- };
120
- }
121
- //#endregion
122
- //#region ---- Private Methods ----
123
- /**
124
- * Convert a single group to Fieldset widget structure
125
- */
126
- createGroupAsFieldsetWidget(group, formMode) {
127
- // Determine columns count from layout or default to 1
128
- const columnsCount = 1;
129
- // Use group mode if set, otherwise inherit from form mode
130
- const groupMode = group.mode || formMode;
131
- return {
132
- type: 'fieldset-layout',
133
- name: group.name,
134
- mode: groupMode,
135
- options: {
136
- title: group.title,
137
- description: group.description,
138
- cols: columnsCount,
139
- look: group.look || 'container', // Default to 'container' if not specified
140
- },
141
- children: this.createFieldWidgets(group.parameters, columnsCount, groupMode),
142
- };
143
- }
144
- /**
145
- * Convert fields to Form Field widgets
146
- */
147
- createFieldWidgets(fields, columnsCount, groupMode) {
148
- return fields.map((field) => this.createFormFieldWidget(field, groupMode));
149
- }
150
- /**
151
- * Convert a single field to Form Field widget with editor as child
152
- */
153
- createFormFieldWidget(field, groupMode) {
154
- // Use field mode if set, otherwise inherit from group mode
155
- const fieldMode = field.mode || groupMode;
156
- // Ensure the editor widget also has the mode set
157
- const editorWidget = { ...field.widget };
158
- if (!editorWidget.mode) {
159
- editorWidget.mode = fieldMode;
160
- }
161
- return {
162
- type: 'form-field',
163
- name: field.path,
164
- mode: fieldMode,
165
- options: {
166
- label: field.title,
167
- badge: field.badge,
168
- description: field.description,
169
- showLabel: true,
170
- },
171
- children: [editorWidget], // The editor widget becomes a child of form-field
172
- };
173
- }
174
- /**
175
- * Extract group information from Fieldset Layout widget
176
- */
177
- extractGroupFromFieldset(fieldsetNode) {
178
- const columnsCount = fieldsetNode.options?.['cols'] || 1;
179
- // Extract fields directly from fieldset children
180
- const fields = [];
181
- if (fieldsetNode.children) {
182
- fieldsetNode.children.forEach((formField) => {
183
- if (formField.type === 'form-field' && formField.children && formField.children.length > 0) {
184
- const field = this.extractFieldFromFormWidget(formField);
185
- if (field) {
186
- fields.push(field);
187
- }
188
- }
189
- });
190
- }
191
- return {
192
- name: fieldsetNode.name || `group-${Date.now()}`,
193
- title: fieldsetNode.options?.['title'],
194
- description: fieldsetNode.options?.['description'],
195
- parameters: fields,
196
- mode: fieldsetNode.mode,
197
- look: fieldsetNode.options?.['look'],
198
- };
199
- }
200
- /**
201
- * Extract field information from Form Field widget
202
- */
203
- extractFieldFromFormWidget(formFieldNode) {
204
- if (!formFieldNode.children || formFieldNode.children.length === 0) {
205
- return null;
206
- }
207
- const editorWidget = formFieldNode.children[0];
208
- return {
209
- path: formFieldNode.name || editorWidget.name || `field-${Date.now()}`,
210
- title: formFieldNode.options?.['label'],
211
- badge: formFieldNode.options?.['badge'],
212
- description: formFieldNode.options?.['description'],
213
- widget: editorWidget,
214
- mode: formFieldNode.mode,
215
- };
216
- }
217
- /**
218
- * Create cache key for form definition
219
- */
220
- createFormDefinitionCacheKey(formDefinition) {
221
- // Create a hash-like key instead of full JSON string
222
- const keyParts = [];
223
- keyParts.push(`groups:${formDefinition.groups.length}`);
224
- formDefinition.groups.forEach((group, groupIndex) => {
225
- keyParts.push(`g${groupIndex}:${group.name}:${group.parameters.length}`);
226
- group.parameters.forEach((param, paramIndex) => {
227
- keyParts.push(`p${groupIndex}.${paramIndex}:${param.path}:${param.widget.type}`);
228
- });
229
- });
230
- if (formDefinition.mode) {
231
- keyParts.push(`mode:${formDefinition.mode}`);
232
- }
233
- // Join with delimiter and create a shorter hash
234
- const keyString = keyParts.join('|');
235
- // If still too long, create a simple hash
236
- if (keyString.length > 100) {
237
- return this.createSimpleHash(keyString);
238
- }
239
- return keyString;
240
- }
241
- /**
242
- * Create cache key for widget tree
243
- */
244
- createWidgetTreeCacheKey(widgetTree) {
245
- // Create a hash-like key instead of full JSON string
246
- const keyParts = [];
247
- keyParts.push(`type:${widgetTree.type}`);
248
- if (widgetTree.name) {
249
- keyParts.push(`name:${widgetTree.name}`);
250
- }
251
- if (widgetTree.children) {
252
- keyParts.push(`children:${widgetTree.children.length}`);
253
- widgetTree.children.forEach((child, index) => {
254
- keyParts.push(`c${index}:${child.type}`);
255
- if (child.children) {
256
- keyParts.push(`cc${index}:${child.children.length}`);
257
- child.children.forEach((grandChild, gIndex) => {
258
- keyParts.push(`gc${index}.${gIndex}:${grandChild.type}`);
259
- if (grandChild.children) {
260
- keyParts.push(`gcc${index}.${gIndex}:${grandChild.children.length}`);
261
- }
262
- });
263
- }
264
- });
265
- }
266
- // Join with delimiter and create a shorter hash
267
- const keyString = keyParts.join('|');
268
- // If still too long, create a simple hash
269
- if (keyString.length > 100) {
270
- return this.createSimpleHash(keyString);
271
- }
272
- return keyString;
273
- }
274
- /**
275
- * Create a simple hash from a string
276
- */
277
- createSimpleHash(str) {
278
- let hash = 0;
279
- if (str.length === 0)
280
- return hash.toString();
281
- for (let i = 0; i < str.length; i++) {
282
- const char = str.charCodeAt(i);
283
- hash = (hash << 5) - hash + char;
284
- hash = hash & hash; // Convert to 32-bit integer
285
- }
286
- return Math.abs(hash).toString(36); // Convert to base36 for shorter string
287
- }
288
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLayoutConversionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
289
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLayoutConversionService, providedIn: 'root' }); }
290
- }
291
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLayoutConversionService, decorators: [{
292
- type: Injectable,
293
- args: [{
294
- providedIn: 'root',
295
- }]
296
- }] });
297
-
298
23
  //#region ---- Inheritance Utilities ----
299
24
  /**
300
25
  * Resolves inherited properties from context and local values
@@ -349,54 +74,6 @@ function generateValueWidgetPath(widgetName, formFieldName, formFieldLabel) {
349
74
  }
350
75
  return generateRandomId();
351
76
  }
352
- /**
353
- * Collects default values from widget node tree and merges them into context
354
- * Only sets values for paths that don't already exist in the context
355
- * @param node - The widget node to process
356
- * @param context - The context object to modify in place (cloned at top level only)
357
- * @param isTopLevel - Internal flag to track if this is the top-level call
358
- */
359
- function collectDefaultValues(node, context = {}, isTopLevel = true) {
360
- // Clone context only at the top level, then modify in place for recursive calls
361
- const result = isTopLevel ? cloneDeep(context) : context;
362
- // Check if this node has a defaultValue and a path
363
- // Note: We check for both node.defaultValue and also look in node.options.defaultValue as fallback
364
- const defaultValue = node.defaultValue !== undefined
365
- ? node.defaultValue
366
- : node.options?.defaultValue;
367
- if (defaultValue !== undefined && !isNil(defaultValue) && node.path) {
368
- // Check if path exists in context using lodash get equivalent check
369
- const currentValue = getNestedValue(result, node.path);
370
- if (currentValue === undefined) {
371
- // Clone the defaultValue to avoid reference issues (especially for Date objects)
372
- const clonedValue = defaultValue instanceof Date
373
- ? new Date(defaultValue.getTime())
374
- : cloneDeep(defaultValue);
375
- set(result, node.path, clonedValue);
376
- }
377
- }
378
- // Recursively process children - pass result object to accumulate values in place
379
- if (node.children && Array.isArray(node.children)) {
380
- for (const child of node.children) {
381
- collectDefaultValues(child, result, false);
382
- }
383
- }
384
- return result;
385
- }
386
- /**
387
- * Gets a nested value from an object using dot notation path
388
- */
389
- function getNestedValue(obj, path) {
390
- const keys = path.split('.');
391
- let current = obj;
392
- for (const key of keys) {
393
- if (current === undefined || current === null || typeof current !== 'object') {
394
- return undefined;
395
- }
396
- current = current[key];
397
- }
398
- return current;
399
- }
400
77
  //#endregion
401
78
  //#region ---- Service Implementation ----
402
79
  class AXPLayoutBuilderService {
@@ -803,26 +480,6 @@ class ChildContainerMixin extends LayoutContainerMixin {
803
480
  this.containerState.children.push(field.build());
804
481
  return this;
805
482
  }
806
- dynamicForm(definition) {
807
- // Get conversion service using inject() - works in any context in Angular 14+
808
- let conversionService;
809
- try {
810
- conversionService = inject(AXPLayoutConversionService);
811
- }
812
- catch {
813
- // Fallback: create service instance if inject() fails (shouldn't happen in normal usage)
814
- conversionService = new AXPLayoutConversionService();
815
- }
816
- // Convert form definition to widget node
817
- const widgetNode = conversionService.convertFormDefinition(definition);
818
- // Add the widget node's children to this container
819
- // The converted widget node has a root grid-layout, we want its children (fieldsets)
820
- if (widgetNode.children && widgetNode.children.length > 0) {
821
- this.ensureChildren();
822
- this.containerState.children.push(...widgetNode.children);
823
- }
824
- return this;
825
- }
826
483
  }
827
484
  /**
828
485
  * Widget container mixin - Interface Segregation Principle
@@ -883,56 +540,8 @@ class WidgetContainerMixin extends ChildContainerMixin {
883
540
  this.containerState.children.push(container.build());
884
541
  return this;
885
542
  }
886
- customWidget(type, optionsOrDelegate, delegate) {
887
- const child = new WidgetBuilder();
888
- child.type(type);
889
- // Determine if second parameter is delegate or options
890
- const isDelegate = typeof optionsOrDelegate === 'function';
891
- const options = isDelegate ? undefined : optionsOrDelegate;
892
- const actualDelegate = isDelegate ? optionsOrDelegate : delegate;
893
- // Apply inheritance context BEFORE setting options or calling delegate
894
- child.withInheritanceContext(this.inheritanceContext);
895
- // If options are provided (non-delegate overload), handle them
896
- if (!isDelegate && options !== undefined) {
897
- const widgetOptions = options;
898
- const path = widgetOptions.path;
899
- const name = widgetOptions.name;
900
- // Set name and path in widget state, not options
901
- if (name) {
902
- child.name(name);
903
- }
904
- if (path) {
905
- child.path(path);
906
- }
907
- // Remove name and path from options
908
- const { name: _, path: __, ...cleanOptions } = widgetOptions;
909
- child.options(cleanOptions);
910
- }
911
- // If delegate is provided, call it to allow setting path, options, and other control methods
912
- if (actualDelegate) {
913
- actualDelegate(child);
914
- }
915
- // Build the widget to check if path is set (for value widgets)
916
- const builtWidget = child.build();
917
- // Check if value widget requires path
918
- const valueWidgetTypes = [
919
- 'text-editor',
920
- 'large-text-editor',
921
- 'rich-text-editor',
922
- 'password-editor',
923
- 'number-editor',
924
- 'select-editor',
925
- 'lookup-editor',
926
- 'selection-list-editor',
927
- 'date-time-editor',
928
- 'toggle-editor',
929
- 'color-editor',
930
- ];
931
- if (valueWidgetTypes.includes(type) && !builtWidget.path) {
932
- throw new Error(`Value widget '${type}' requires a 'path' property`);
933
- }
934
- this.ensureChildren();
935
- this.containerState.children.push(builtWidget);
543
+ customWidget(type, options) {
544
+ this.addWidget(type, options);
936
545
  return this;
937
546
  }
938
547
  }
@@ -1498,14 +1107,11 @@ class DialogContainerBuilder {
1498
1107
  const dialogNode = this.build();
1499
1108
  // Import the dialog renderer component dynamically
1500
1109
  const { AXPDialogRendererComponent } = await Promise.resolve().then(function () { return dialogRenderer_component; });
1501
- // Collect default values from widget tree and merge into initial context
1502
- const initialContext = this.dialogState.dialogOptions?.context || {};
1503
- const contextWithDefaults = collectDefaultValues(dialogNode, initialContext);
1504
1110
  // Create dialog configuration
1505
1111
  const dialogConfig = {
1506
1112
  title: this.dialogState.dialogOptions?.title || '',
1507
1113
  message: this.dialogState.dialogOptions?.message,
1508
- context: contextWithDefaults,
1114
+ context: this.dialogState.dialogOptions?.context || {},
1509
1115
  definition: dialogNode,
1510
1116
  actions: this.dialogState.actions,
1511
1117
  };
@@ -1559,20 +1165,10 @@ class WidgetBuilder {
1559
1165
  path(path) {
1560
1166
  this.widgetState.path = path;
1561
1167
  return this;
1562
- }
1563
- options(options) {
1564
- // Extract defaultValue from options - it's an exceptional parameter that updates context, not a widget option
1565
- if (options && options.defaultValue !== undefined) {
1566
- this.widgetState.defaultValue = options.defaultValue;
1567
- // Remove defaultValue from options since it's stored on the node, not in options
1568
- const { defaultValue: _, ...cleanOptions } = options;
1569
- // Merge clean options instead of replacing to preserve inherited properties
1570
- this.widgetState.options = { ...this.widgetState.options, ...cleanOptions };
1571
- }
1572
- else {
1573
- // Merge options instead of replacing to preserve inherited properties
1574
- this.widgetState.options = { ...this.widgetState.options, ...options };
1575
- }
1168
+ }
1169
+ options(options) {
1170
+ // Merge options instead of replacing to preserve inherited properties
1171
+ this.widgetState.options = { ...this.widgetState.options, ...options };
1576
1172
  return this;
1577
1173
  }
1578
1174
  layout(value) {
@@ -1846,6 +1442,268 @@ class StepWizardBuilder extends LayoutContainerMixin {
1846
1442
  }
1847
1443
  }
1848
1444
 
1445
+ class AXPLayoutConversionService {
1446
+ constructor() {
1447
+ //#region ---- Caching ----
1448
+ this.widgetTreeCache = new Map();
1449
+ this.formDefinitionCache = new Map();
1450
+ }
1451
+ //#endregion
1452
+ //#region ---- Public Methods ----
1453
+ /**
1454
+ * Convert AXPDynamicFormDefinition to AXPWidgetNode tree structure
1455
+ * Groups become Fieldset Layouts with Form Field widgets as children
1456
+ * Fields become Form Field widgets with Editor widgets as children
1457
+ */
1458
+ convertFormDefinition(formDefinition) {
1459
+ // Create cache key based on form definition content
1460
+ const cacheKey = this.createFormDefinitionCacheKey(formDefinition);
1461
+ // Check cache first
1462
+ if (this.widgetTreeCache.has(cacheKey)) {
1463
+ return this.widgetTreeCache.get(cacheKey);
1464
+ }
1465
+ // Generate widget tree
1466
+ const widgetTree = {
1467
+ type: 'grid-layout',
1468
+ name: 'dynamic-form-container',
1469
+ options: {
1470
+ title: 'Dynamic Form',
1471
+ grid: {
1472
+ default: {
1473
+ columns: 1,
1474
+ gap: '1rem',
1475
+ },
1476
+ },
1477
+ },
1478
+ children: formDefinition.groups.map((group) => this.createGroupAsFieldsetWidget(group)),
1479
+ };
1480
+ // Cache the result
1481
+ this.widgetTreeCache.set(cacheKey, widgetTree);
1482
+ return widgetTree;
1483
+ }
1484
+ /**
1485
+ * Convert AXPWidgetNode tree back to AXPDynamicFormDefinition
1486
+ * Parses Fieldset Layouts back to Groups
1487
+ * Parses Form Field widgets back to Fields
1488
+ */
1489
+ convertWidgetTreeToFormDefinition(widgetTree) {
1490
+ // Create cache key based on widget tree content
1491
+ const cacheKey = this.createWidgetTreeCacheKey(widgetTree);
1492
+ // Check cache first
1493
+ if (this.formDefinitionCache.has(cacheKey)) {
1494
+ return this.formDefinitionCache.get(cacheKey);
1495
+ }
1496
+ // Parse widget tree
1497
+ const groups = [];
1498
+ if (widgetTree.children) {
1499
+ widgetTree.children.forEach((child) => {
1500
+ if (child.type === 'fieldset-layout') {
1501
+ const group = this.extractGroupFromFieldset(child);
1502
+ groups.push(group);
1503
+ }
1504
+ });
1505
+ }
1506
+ const formDefinition = { groups };
1507
+ // Cache the result
1508
+ this.formDefinitionCache.set(cacheKey, formDefinition);
1509
+ return formDefinition;
1510
+ }
1511
+ /**
1512
+ * Validate that a widget tree represents a valid dynamic form structure
1513
+ */
1514
+ validateFormWidgetTree(widgetTree) {
1515
+ if (!widgetTree || widgetTree.type !== 'grid-layout') {
1516
+ return false;
1517
+ }
1518
+ if (!widgetTree.children || widgetTree.children.length === 0) {
1519
+ return true; // Empty form is valid
1520
+ }
1521
+ // Check that all children are fieldset-layout widgets
1522
+ return widgetTree.children.every((child) => child.type === 'fieldset-layout' &&
1523
+ child.children &&
1524
+ child.children.every((formField) => formField.type === 'form-field' && formField.children && formField.children.length > 0));
1525
+ }
1526
+ /**
1527
+ * Clear all caches
1528
+ */
1529
+ clearCaches() {
1530
+ this.widgetTreeCache.clear();
1531
+ this.formDefinitionCache.clear();
1532
+ }
1533
+ /**
1534
+ * Get cache statistics
1535
+ */
1536
+ getCacheStats() {
1537
+ return {
1538
+ widgetTreeCacheSize: this.widgetTreeCache.size,
1539
+ formDefinitionCacheSize: this.formDefinitionCache.size,
1540
+ };
1541
+ }
1542
+ //#endregion
1543
+ //#region ---- Private Methods ----
1544
+ /**
1545
+ * Convert a single group to Fieldset widget structure
1546
+ */
1547
+ createGroupAsFieldsetWidget(group) {
1548
+ // Determine columns count from layout or default to 1
1549
+ const columnsCount = 1;
1550
+ return {
1551
+ type: 'fieldset-layout',
1552
+ name: group.name,
1553
+ mode: group.mode,
1554
+ options: {
1555
+ title: group.title,
1556
+ description: group.description,
1557
+ cols: columnsCount,
1558
+ look: group.look || 'container', // Default to 'container' if not specified
1559
+ },
1560
+ children: this.createFieldWidgets(group.parameters, columnsCount),
1561
+ };
1562
+ }
1563
+ /**
1564
+ * Convert fields to Form Field widgets
1565
+ */
1566
+ createFieldWidgets(fields, columnsCount) {
1567
+ return fields.map((field) => this.createFormFieldWidget(field));
1568
+ }
1569
+ /**
1570
+ * Convert a single field to Form Field widget with editor as child
1571
+ */
1572
+ createFormFieldWidget(field) {
1573
+ return {
1574
+ type: 'form-field',
1575
+ name: field.path,
1576
+ options: {
1577
+ label: field.title,
1578
+ description: field.description,
1579
+ showLabel: true,
1580
+ },
1581
+ children: [field.widget], // The editor widget becomes a child of form-field
1582
+ };
1583
+ }
1584
+ /**
1585
+ * Extract group information from Fieldset Layout widget
1586
+ */
1587
+ extractGroupFromFieldset(fieldsetNode) {
1588
+ const columnsCount = fieldsetNode.options?.['cols'] || 1;
1589
+ // Extract fields directly from fieldset children
1590
+ const fields = [];
1591
+ if (fieldsetNode.children) {
1592
+ fieldsetNode.children.forEach((formField) => {
1593
+ if (formField.type === 'form-field' && formField.children && formField.children.length > 0) {
1594
+ const field = this.extractFieldFromFormWidget(formField);
1595
+ if (field) {
1596
+ fields.push(field);
1597
+ }
1598
+ }
1599
+ });
1600
+ }
1601
+ return {
1602
+ name: fieldsetNode.name || `group-${Date.now()}`,
1603
+ title: fieldsetNode.options?.['title'],
1604
+ description: fieldsetNode.options?.['description'],
1605
+ parameters: fields,
1606
+ mode: fieldsetNode.mode,
1607
+ look: fieldsetNode.options?.['look'],
1608
+ };
1609
+ }
1610
+ /**
1611
+ * Extract field information from Form Field widget
1612
+ */
1613
+ extractFieldFromFormWidget(formFieldNode) {
1614
+ if (!formFieldNode.children || formFieldNode.children.length === 0) {
1615
+ return null;
1616
+ }
1617
+ const editorWidget = formFieldNode.children[0];
1618
+ return {
1619
+ path: formFieldNode.name || editorWidget.name || `field-${Date.now()}`,
1620
+ title: formFieldNode.options?.['label'],
1621
+ description: formFieldNode.options?.['description'],
1622
+ widget: editorWidget,
1623
+ mode: formFieldNode.mode,
1624
+ };
1625
+ }
1626
+ /**
1627
+ * Create cache key for form definition
1628
+ */
1629
+ createFormDefinitionCacheKey(formDefinition) {
1630
+ // Create a hash-like key instead of full JSON string
1631
+ const keyParts = [];
1632
+ keyParts.push(`groups:${formDefinition.groups.length}`);
1633
+ formDefinition.groups.forEach((group, groupIndex) => {
1634
+ keyParts.push(`g${groupIndex}:${group.name}:${group.parameters.length}`);
1635
+ group.parameters.forEach((param, paramIndex) => {
1636
+ keyParts.push(`p${groupIndex}.${paramIndex}:${param.path}:${param.widget.type}`);
1637
+ });
1638
+ });
1639
+ if (formDefinition.mode) {
1640
+ keyParts.push(`mode:${formDefinition.mode}`);
1641
+ }
1642
+ // Join with delimiter and create a shorter hash
1643
+ const keyString = keyParts.join('|');
1644
+ // If still too long, create a simple hash
1645
+ if (keyString.length > 100) {
1646
+ return this.createSimpleHash(keyString);
1647
+ }
1648
+ return keyString;
1649
+ }
1650
+ /**
1651
+ * Create cache key for widget tree
1652
+ */
1653
+ createWidgetTreeCacheKey(widgetTree) {
1654
+ // Create a hash-like key instead of full JSON string
1655
+ const keyParts = [];
1656
+ keyParts.push(`type:${widgetTree.type}`);
1657
+ if (widgetTree.name) {
1658
+ keyParts.push(`name:${widgetTree.name}`);
1659
+ }
1660
+ if (widgetTree.children) {
1661
+ keyParts.push(`children:${widgetTree.children.length}`);
1662
+ widgetTree.children.forEach((child, index) => {
1663
+ keyParts.push(`c${index}:${child.type}`);
1664
+ if (child.children) {
1665
+ keyParts.push(`cc${index}:${child.children.length}`);
1666
+ child.children.forEach((grandChild, gIndex) => {
1667
+ keyParts.push(`gc${index}.${gIndex}:${grandChild.type}`);
1668
+ if (grandChild.children) {
1669
+ keyParts.push(`gcc${index}.${gIndex}:${grandChild.children.length}`);
1670
+ }
1671
+ });
1672
+ }
1673
+ });
1674
+ }
1675
+ // Join with delimiter and create a shorter hash
1676
+ const keyString = keyParts.join('|');
1677
+ // If still too long, create a simple hash
1678
+ if (keyString.length > 100) {
1679
+ return this.createSimpleHash(keyString);
1680
+ }
1681
+ return keyString;
1682
+ }
1683
+ /**
1684
+ * Create a simple hash from a string
1685
+ */
1686
+ createSimpleHash(str) {
1687
+ let hash = 0;
1688
+ if (str.length === 0)
1689
+ return hash.toString();
1690
+ for (let i = 0; i < str.length; i++) {
1691
+ const char = str.charCodeAt(i);
1692
+ hash = (hash << 5) - hash + char;
1693
+ hash = hash & hash; // Convert to 32-bit integer
1694
+ }
1695
+ return Math.abs(hash).toString(36); // Convert to base36 for shorter string
1696
+ }
1697
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLayoutConversionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1698
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLayoutConversionService, providedIn: 'root' }); }
1699
+ }
1700
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLayoutConversionService, decorators: [{
1701
+ type: Injectable,
1702
+ args: [{
1703
+ providedIn: 'root',
1704
+ }]
1705
+ }] });
1706
+
1849
1707
  class AXPLayoutRendererComponent {
1850
1708
  constructor() {
1851
1709
  this.conversionService = inject(AXPLayoutConversionService);
@@ -2087,7 +1945,7 @@ class AXPLayoutRendererComponent {
2087
1945
  }
2088
1946
  </axp-widgets-container>
2089
1947
  </ax-form>
2090
- `, isInline: true, styles: [":host{display:block;width:100%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i1.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: i1.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged", "onLoad"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXFormModule }, { kind: "component", type: i2.AXFormComponent, selector: "ax-form", inputs: ["disabled", "readonly", "labelMode", "look", "messageStyle", "updateOn"], outputs: ["onValidate", "updateOnChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1948
+ `, 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: ["disabled", "readonly", "labelMode", "look", "messageStyle", "updateOn"], outputs: ["onValidate", "updateOnChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2091
1949
  }
2092
1950
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLayoutRendererComponent, decorators: [{
2093
1951
  type: Component,