@acorex/platform 0.0.0-ACOREX

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 (116) hide show
  1. package/README.md +7 -0
  2. package/auth/README.md +3 -0
  3. package/common/README.md +3 -0
  4. package/core/README.md +4 -0
  5. package/fesm2022/acorex-platform-auth.mjs +1362 -0
  6. package/fesm2022/acorex-platform-auth.mjs.map +1 -0
  7. package/fesm2022/acorex-platform-common-common-settings.provider-G9XcXXOG.mjs +127 -0
  8. package/fesm2022/acorex-platform-common-common-settings.provider-G9XcXXOG.mjs.map +1 -0
  9. package/fesm2022/acorex-platform-common.mjs +4601 -0
  10. package/fesm2022/acorex-platform-common.mjs.map +1 -0
  11. package/fesm2022/acorex-platform-core.mjs +4374 -0
  12. package/fesm2022/acorex-platform-core.mjs.map +1 -0
  13. package/fesm2022/acorex-platform-domain.mjs +3234 -0
  14. package/fesm2022/acorex-platform-domain.mjs.map +1 -0
  15. package/fesm2022/acorex-platform-layout-builder.mjs +2847 -0
  16. package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -0
  17. package/fesm2022/acorex-platform-layout-components-binding-expression-editor-popup.component-CXEdvDTf.mjs +121 -0
  18. package/fesm2022/acorex-platform-layout-components-binding-expression-editor-popup.component-CXEdvDTf.mjs.map +1 -0
  19. package/fesm2022/acorex-platform-layout-components.mjs +8583 -0
  20. package/fesm2022/acorex-platform-layout-components.mjs.map +1 -0
  21. package/fesm2022/acorex-platform-layout-designer.mjs +2474 -0
  22. package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -0
  23. package/fesm2022/acorex-platform-layout-entity.mjs +19150 -0
  24. package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -0
  25. package/fesm2022/acorex-platform-layout-views.mjs +1468 -0
  26. package/fesm2022/acorex-platform-layout-views.mjs.map +1 -0
  27. package/fesm2022/acorex-platform-layout-widget-core.mjs +2950 -0
  28. package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -0
  29. package/fesm2022/acorex-platform-layout-widgets-button-widget-designer.component-Dy7jF-oD.mjs +72 -0
  30. package/fesm2022/acorex-platform-layout-widgets-button-widget-designer.component-Dy7jF-oD.mjs.map +1 -0
  31. package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-9uCkMxcc.mjs +158 -0
  32. package/fesm2022/acorex-platform-layout-widgets-file-list-popup.component-9uCkMxcc.mjs.map +1 -0
  33. package/fesm2022/acorex-platform-layout-widgets-image-preview.popup-C_EPAvCU.mjs +29 -0
  34. package/fesm2022/acorex-platform-layout-widgets-image-preview.popup-C_EPAvCU.mjs.map +1 -0
  35. package/fesm2022/acorex-platform-layout-widgets-page-widget-designer.component-D10yO28c.mjs +172 -0
  36. package/fesm2022/acorex-platform-layout-widgets-page-widget-designer.component-D10yO28c.mjs.map +1 -0
  37. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-BGQqY5Mw.mjs +111 -0
  38. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-BGQqY5Mw.mjs.map +1 -0
  39. package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-DmzNTYiS.mjs +274 -0
  40. package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-DmzNTYiS.mjs.map +1 -0
  41. package/fesm2022/acorex-platform-layout-widgets-tabular-data-view-popup.component-BNG_588B.mjs +64 -0
  42. package/fesm2022/acorex-platform-layout-widgets-tabular-data-view-popup.component-BNG_588B.mjs.map +1 -0
  43. package/fesm2022/acorex-platform-layout-widgets-text-block-widget-designer.component-Vo4fWHtX.mjs +34 -0
  44. package/fesm2022/acorex-platform-layout-widgets-text-block-widget-designer.component-Vo4fWHtX.mjs.map +1 -0
  45. package/fesm2022/acorex-platform-layout-widgets.mjs +29791 -0
  46. package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -0
  47. package/fesm2022/acorex-platform-native.mjs +155 -0
  48. package/fesm2022/acorex-platform-native.mjs.map +1 -0
  49. package/fesm2022/acorex-platform-runtime-catalog-command-definition.mjs +20 -0
  50. package/fesm2022/acorex-platform-runtime-catalog-command-definition.mjs.map +1 -0
  51. package/fesm2022/acorex-platform-runtime-catalog-query-definition.mjs +20 -0
  52. package/fesm2022/acorex-platform-runtime-catalog-query-definition.mjs.map +1 -0
  53. package/fesm2022/acorex-platform-runtime.mjs +899 -0
  54. package/fesm2022/acorex-platform-runtime.mjs.map +1 -0
  55. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-Cvvr4HnL.mjs +160 -0
  56. package/fesm2022/acorex-platform-themes-default-entity-master-create-view.component-Cvvr4HnL.mjs.map +1 -0
  57. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-TYoLN1Jq.mjs +120 -0
  58. package/fesm2022/acorex-platform-themes-default-entity-master-modify-view.component-TYoLN1Jq.mjs.map +1 -0
  59. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-C2z5Lq9y.mjs +237 -0
  60. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-C2z5Lq9y.mjs.map +1 -0
  61. package/fesm2022/acorex-platform-themes-default-error-401.component-C7EYJzSr.mjs +31 -0
  62. package/fesm2022/acorex-platform-themes-default-error-401.component-C7EYJzSr.mjs.map +1 -0
  63. package/fesm2022/acorex-platform-themes-default-error-404.component-7MVLMwIa.mjs +25 -0
  64. package/fesm2022/acorex-platform-themes-default-error-404.component-7MVLMwIa.mjs.map +1 -0
  65. package/fesm2022/acorex-platform-themes-default-error-offline.component-DR6G8gPC.mjs +19 -0
  66. package/fesm2022/acorex-platform-themes-default-error-offline.component-DR6G8gPC.mjs.map +1 -0
  67. package/fesm2022/acorex-platform-themes-default.mjs +2589 -0
  68. package/fesm2022/acorex-platform-themes-default.mjs.map +1 -0
  69. package/fesm2022/acorex-platform-themes-shared-icon-chooser-column.component-CqkWJYdv.mjs +55 -0
  70. package/fesm2022/acorex-platform-themes-shared-icon-chooser-column.component-CqkWJYdv.mjs.map +1 -0
  71. package/fesm2022/acorex-platform-themes-shared-icon-chooser-view.component-BOTuLdWN.mjs +57 -0
  72. package/fesm2022/acorex-platform-themes-shared-icon-chooser-view.component-BOTuLdWN.mjs.map +1 -0
  73. package/fesm2022/acorex-platform-themes-shared-settings.provider-DSs1o1M6.mjs +168 -0
  74. package/fesm2022/acorex-platform-themes-shared-settings.provider-DSs1o1M6.mjs.map +1 -0
  75. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-CHfrTtol.mjs +65 -0
  76. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-column.component-CHfrTtol.mjs.map +1 -0
  77. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-BSmvnUVq.mjs +64 -0
  78. package/fesm2022/acorex-platform-themes-shared-theme-color-chooser-view.component-BSmvnUVq.mjs.map +1 -0
  79. package/fesm2022/acorex-platform-themes-shared.mjs +2125 -0
  80. package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -0
  81. package/fesm2022/acorex-platform-workflow.mjs +2501 -0
  82. package/fesm2022/acorex-platform-workflow.mjs.map +1 -0
  83. package/fesm2022/acorex-platform.mjs +6 -0
  84. package/fesm2022/acorex-platform.mjs.map +1 -0
  85. package/layout/builder/README.md +1578 -0
  86. package/layout/components/README.md +3 -0
  87. package/layout/designer/README.md +4 -0
  88. package/layout/entity/README.md +4 -0
  89. package/layout/views/README.md +3 -0
  90. package/layout/widget-core/README.md +4 -0
  91. package/layout/widgets/README.md +3 -0
  92. package/native/README.md +4 -0
  93. package/package.json +103 -0
  94. package/runtime/README.md +3 -0
  95. package/themes/default/README.md +3 -0
  96. package/themes/shared/README.md +3 -0
  97. package/types/acorex-platform-auth.d.ts +680 -0
  98. package/types/acorex-platform-common.d.ts +2926 -0
  99. package/types/acorex-platform-core.d.ts +2896 -0
  100. package/types/acorex-platform-domain.d.ts +2353 -0
  101. package/types/acorex-platform-layout-builder.d.ts +926 -0
  102. package/types/acorex-platform-layout-components.d.ts +2903 -0
  103. package/types/acorex-platform-layout-designer.d.ts +422 -0
  104. package/types/acorex-platform-layout-entity.d.ts +3189 -0
  105. package/types/acorex-platform-layout-views.d.ts +667 -0
  106. package/types/acorex-platform-layout-widget-core.d.ts +1086 -0
  107. package/types/acorex-platform-layout-widgets.d.ts +5478 -0
  108. package/types/acorex-platform-native.d.ts +28 -0
  109. package/types/acorex-platform-runtime-catalog-command-definition.d.ts +137 -0
  110. package/types/acorex-platform-runtime-catalog-query-definition.d.ts +125 -0
  111. package/types/acorex-platform-runtime.d.ts +470 -0
  112. package/types/acorex-platform-themes-default.d.ts +573 -0
  113. package/types/acorex-platform-themes-shared.d.ts +170 -0
  114. package/types/acorex-platform-workflow.d.ts +1806 -0
  115. package/types/acorex-platform.d.ts +2 -0
  116. package/workflow/README.md +4 -0
@@ -0,0 +1,2950 @@
1
+ import * as i0 from '@angular/core';
2
+ import { signal, computed, Injectable, InjectionToken, inject, ElementRef, effect, untracked, Injector, ChangeDetectorRef, ViewChild, Input, ChangeDetectionStrategy, Component, EventEmitter, Output, input, output, ViewContainerRef, Directive, NgModule } from '@angular/core';
3
+ import { convertArrayToDataSource, AXDataSource } from '@acorex/cdk/common';
4
+ import { AXPContextStore, AXPDataSourceDefinitionProviderService, extractValue, AXPExpressionEvaluatorService, getSmart } from '@acorex/platform/core';
5
+ import { set, merge, cloneDeep, isNil, get, isEqual, isUndefined, isObjectLike, sum, isEmpty, isString } from 'lodash-es';
6
+ import { Subject, BehaviorSubject, filter } from 'rxjs';
7
+ import * as i1$1 from '@acorex/components/skeleton';
8
+ import { AXSkeletonModule } from '@acorex/components/skeleton';
9
+ import * as i2 from '@acorex/core/translation';
10
+ import { AXTranslationService, AXTranslationModule } from '@acorex/core/translation';
11
+ import { PortalModule } from '@angular/cdk/portal';
12
+ import * as i1 from '@angular/common';
13
+ import { CommonModule } from '@angular/common';
14
+ import { AXDataTableColumnComponent, AXBaseDataTable } from '@acorex/components/data-table';
15
+ import { AXUnsubscriber } from '@acorex/core/utils';
16
+
17
+ var AXPPageStatus;
18
+ (function (AXPPageStatus) {
19
+ // Idle statuses
20
+ AXPPageStatus["Idle"] = "idle";
21
+ // Rendering statuses
22
+ AXPPageStatus["Rendering"] = "rendering";
23
+ AXPPageStatus["Rendered"] = "rendered";
24
+ // Processing statuses
25
+ AXPPageStatus["Processing"] = "processing";
26
+ // Submission statuses
27
+ AXPPageStatus["Submitting"] = "submitting";
28
+ AXPPageStatus["Submitted"] = "submitted";
29
+ // Validation statuses
30
+ AXPPageStatus["Validating"] = "validating";
31
+ AXPPageStatus["Validated"] = "validated";
32
+ // Error handling
33
+ AXPPageStatus["Error"] = "error";
34
+ })(AXPPageStatus || (AXPPageStatus = {}));
35
+ var AXPWidgetStatus;
36
+ (function (AXPWidgetStatus) {
37
+ // Rendering statuses
38
+ AXPWidgetStatus["Rendering"] = "rendering";
39
+ AXPWidgetStatus["Rendered"] = "rendered";
40
+ // Processing statuses
41
+ AXPWidgetStatus["Processing"] = "processing";
42
+ // Error handling
43
+ AXPWidgetStatus["Error"] = "error";
44
+ })(AXPWidgetStatus || (AXPWidgetStatus = {}));
45
+
46
+ class AXPWidgetCoreElement {
47
+ api() {
48
+ return {};
49
+ }
50
+ actions() {
51
+ return [];
52
+ }
53
+ }
54
+ class AXPWidgetCoreService {
55
+ constructor() {
56
+ this.variables$ = signal({}, ...(ngDevMode ? [{ debugName: "variables$" }] : /* istanbul ignore next */ []));
57
+ this.functions$ = signal({}, ...(ngDevMode ? [{ debugName: "functions$" }] : /* istanbul ignore next */ []));
58
+ this.onRefresh = new Subject();
59
+ this.widgets = new Map();
60
+ this.onWidgetRegistered = new Subject();
61
+ this.status$ = signal(AXPPageStatus.Rendering, ...(ngDevMode ? [{ debugName: "status$" }] : /* istanbul ignore next */ []));
62
+ this.status = this.status$.asReadonly();
63
+ this.isBusy = computed(() => {
64
+ return [AXPPageStatus.Processing, AXPPageStatus.Submitting, AXPPageStatus.Rendering].includes(this.status());
65
+ }, ...(ngDevMode ? [{ debugName: "isBusy" }] : /* istanbul ignore next */ []));
66
+ this.registeredWidgetsCount = signal(0, ...(ngDevMode ? [{ debugName: "registeredWidgetsCount" }] : /* istanbul ignore next */ []));
67
+ }
68
+ get variables() {
69
+ return this.variables$();
70
+ }
71
+ get functions() {
72
+ return this.functions$();
73
+ }
74
+ updateStatus() {
75
+ this.status$.update(() => this.detectStatus());
76
+ this.registeredWidgetsCount.set(this.widgets.size);
77
+ }
78
+ detectStatus() {
79
+ const statuses = Array.from(this.widgets.values()).map((c) => c.status());
80
+ // Rendering statuses
81
+ if (statuses.some((status) => status === AXPWidgetStatus.Rendering)) {
82
+ return AXPPageStatus.Rendering;
83
+ }
84
+ if (statuses.every((status) => status === AXPWidgetStatus.Rendered)) {
85
+ return AXPPageStatus.Rendered;
86
+ }
87
+ // Processing statuses
88
+ if (statuses.some((status) => status === AXPWidgetStatus.Processing)) {
89
+ return AXPPageStatus.Processing;
90
+ }
91
+ // Error handling
92
+ if (statuses.some((status) => status === AXPWidgetStatus.Error)) {
93
+ return AXPPageStatus.Error;
94
+ }
95
+ return AXPPageStatus.Rendered; // Default to Loaded when all widgets are in a completed state
96
+ }
97
+ refresh() {
98
+ setTimeout(() => {
99
+ this.onRefresh.next();
100
+ }, 0);
101
+ }
102
+ setStatus(status) {
103
+ this.status$.set(status);
104
+ }
105
+ setVariables(...args) {
106
+ if (args.length == 0)
107
+ return;
108
+ else if (args.length == 1)
109
+ this.variables$.set(args[0]);
110
+ else if (args.length == 2) {
111
+ this.variables$.update((v) => set(v, args[0], args[1]));
112
+ }
113
+ }
114
+ setFunctions(...args) {
115
+ if (args.length == 0)
116
+ return;
117
+ else if (args.length == 1)
118
+ this.functions$.set(args[0]);
119
+ else if (args.length == 2) {
120
+ this.functions$.update((v) => set(v, args[0], args[1]));
121
+ }
122
+ }
123
+ registerWidget(id, widget) {
124
+ this.widgets.set(id, widget);
125
+ this.onWidgetRegistered.next({ id, widget });
126
+ }
127
+ getWidget(id) {
128
+ return this.widgets.get(id);
129
+ }
130
+ /**
131
+ * Waits until a widget with the given id is registered, then resolves with it.
132
+ * If the widget is already registered, resolves immediately.
133
+ * Optionally accepts a timeout (in ms) after which it resolves with undefined.
134
+ */
135
+ async waitForWidget(id, timeoutMs) {
136
+ const existing = this.widgets.get(id);
137
+ if (existing) {
138
+ return existing;
139
+ }
140
+ return new Promise((resolve) => {
141
+ let resolved = false;
142
+ let timer = null;
143
+ const sub = this.onWidgetRegistered.subscribe(({ id: registeredId, widget }) => {
144
+ if (registeredId === id && !resolved) {
145
+ resolved = true;
146
+ sub.unsubscribe();
147
+ if (timer) {
148
+ clearTimeout(timer);
149
+ }
150
+ resolve(widget);
151
+ }
152
+ });
153
+ if (timeoutMs != null && timeoutMs > 0) {
154
+ timer = setTimeout(() => {
155
+ if (!resolved) {
156
+ resolved = true;
157
+ sub.unsubscribe();
158
+ resolve(undefined);
159
+ }
160
+ }, timeoutMs);
161
+ }
162
+ });
163
+ }
164
+ /**
165
+ * Returns a list of registered widget ids (names).
166
+ */
167
+ listRegisteredWidgetNames() {
168
+ return Array.from(this.widgets.keys());
169
+ }
170
+ ngOnDestroy() { }
171
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetCoreService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
172
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetCoreService }); }
173
+ }
174
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetCoreService, decorators: [{
175
+ type: Injectable
176
+ }] });
177
+
178
+ //
179
+ // @deprecated
180
+ // use AXPWidgetsList instead
181
+ const AXPWidgetsCatalog = {
182
+ timeDuration: 'time-duration',
183
+ timeDurationFilter: 'time-duration-filter',
184
+ checkbox: 'checkbox-editor',
185
+ color: 'color-editor',
186
+ connectedLists: 'connected-lists-editor',
187
+ list: 'list-editor',
188
+ contact: 'contact-editor',
189
+ dateTime: 'date-time-editor',
190
+ largeText: 'large-text-editor',
191
+ number: 'number-editor',
192
+ numberUnit: 'number-unit-editor',
193
+ password: 'password-editor',
194
+ richText: 'rich-text-editor',
195
+ select: 'select-editor',
196
+ selectionList: 'selection-list-editor',
197
+ text: 'text-editor',
198
+ table: 'table-editor',
199
+ toggle: 'toggle-editor',
200
+ blockLayout: 'block-layout',
201
+ pageLayout: 'page-layout',
202
+ repeaterLayout: 'repeater-layout',
203
+ textBlockLayout: 'text-block-layout',
204
+ alertBoxLayout: 'alert-box-layout',
205
+ fileUploader: 'file-uploader',
206
+ fileTypeExtension: 'file-type-extension',
207
+ map: 'map',
208
+ imageMarker: 'image-marker',
209
+ image: 'image',
210
+ gallery: 'gallery',
211
+ signature: 'signature',
212
+ buttonAction: 'button-action',
213
+ document: 'document-layout',
214
+ lookup: 'lookup-editor',
215
+ formField: 'form-field',
216
+ qrcode: 'qrcode',
217
+ schedulerPicker: 'scheduler-picker',
218
+ advancedGrid: 'advanced-grid-layout',
219
+ advancedGridItem: 'advanced-grid-item-layout',
220
+ grid: 'grid-layout',
221
+ gridItem: 'grid-item-layout',
222
+ template: 'template',
223
+ templateDesigner: 'template-designer',
224
+ cronJob: 'cron-job',
225
+ spacing: 'spacing',
226
+ direction: 'direction',
227
+ border: 'border',
228
+ flexLayout: 'flex-layout',
229
+ flexItem: 'flex-item-layout',
230
+ tableLayout: 'table-layout',
231
+ tableItem: 'table-item-layout',
232
+ avatar: 'avatar',
233
+ themePaletteChooser: 'theme-palette-chooser',
234
+ themeModeChooser: 'theme-mode-chooser',
235
+ menuOrientationChooser: 'menu-orientation-chooser',
236
+ fontStyleChooser: 'font-style-chooser',
237
+ fontSizeChooser: 'font-size-chooser',
238
+ iconChooser: 'icon-chooser',
239
+ icon: 'icon',
240
+ themeColorChooser: 'theme-color-chooser',
241
+ gridOptions: 'grid-options',
242
+ gridItemOptions: 'grid-item-options',
243
+ advancedGridOptions: 'advanced-grid-options',
244
+ stringFilter: 'string-filter',
245
+ numberFilter: 'number-filter',
246
+ dateTimeFilter: 'datetime-filter',
247
+ booleanFilter: 'boolean-filter',
248
+ lookupFilter: 'lookup-filter',
249
+ flexOptions: 'flex-options',
250
+ flexItemOptions: 'flex-item-options',
251
+ selectFilter: 'select-filter',
252
+ requiredValidation: 'required-validation',
253
+ regularExpressionValidation: 'regular-expression-validation',
254
+ minLengthValidation: 'min-length-validation',
255
+ maxLengthValidation: 'max-length-validation',
256
+ lessThanValidation: 'less-than-validation',
257
+ greaterThanValidation: 'greater-than-validation',
258
+ betweenValidation: 'between-validation',
259
+ equalValidation: 'equal-validation',
260
+ callbackValidation: 'callback-validation',
261
+ donutChart: 'donut-chart',
262
+ lineChart: 'line-chart',
263
+ barChart: 'bar-chart',
264
+ gaugeChart: 'gauge-chart',
265
+ stickyNote: 'sticky-note',
266
+ clockCalendar: 'clock-calendar',
267
+ analogClock: 'analog-clock',
268
+ weather: 'weather',
269
+ minimalWeather: 'minimal-weather',
270
+ advancedWeather: 'advanced-weather',
271
+ metaData: 'meta-data-editor',
272
+ templateEditor: 'template-box-editor',
273
+ templateContentEditor: 'template-content-editor',
274
+ panel: 'panel',
275
+ notification: 'notification',
276
+ taskBoard: 'task-board',
277
+ comment: 'comment',
278
+ dataList: 'data-list',
279
+ listToolbar: 'list-toolbar',
280
+ entityList: 'entity-list',
281
+ editorJs: 'editor-js-editor',
282
+ documentUploader: 'document-uploader',
283
+ stepWizard: 'step-wizard',
284
+ progressBar: 'progress-bar-editor',
285
+ rate: 'rate-picker-editor',
286
+ documentFileTypeFilter: 'document-file-type-filter',
287
+ entityDefinitionProvider: 'entity-definition-provider-editor',
288
+ };
289
+
290
+ function cloneProperty(property, values) {
291
+ return merge(cloneDeep(property), values);
292
+ }
293
+ function createStringProperty(ctor) {
294
+ return {
295
+ name: ctor.name,
296
+ title: ctor.title,
297
+ group: ctor.group,
298
+ schema: {
299
+ dataType: 'string',
300
+ defaultValue: ctor.defaultValue,
301
+ interface: {
302
+ name: ctor.name,
303
+ path: ctor.path ?? ctor.name,
304
+ type: AXPWidgetsCatalog.text,
305
+ },
306
+ },
307
+ visible: !isNil(ctor.visible) ? ctor.visible : true,
308
+ };
309
+ }
310
+ function createNumberProperty(ctor) {
311
+ return {
312
+ name: ctor.name,
313
+ title: ctor.title,
314
+ group: ctor.group,
315
+ schema: {
316
+ dataType: 'number',
317
+ defaultValue: ctor.defaultValue,
318
+ interface: {
319
+ name: ctor.name,
320
+ path: ctor.path ?? ctor.name,
321
+ type: AXPWidgetsCatalog.number,
322
+ options: ctor.options,
323
+ },
324
+ },
325
+ visible: !isNil(ctor.visible) ? ctor.visible : true,
326
+ };
327
+ }
328
+ function createBooleanProperty(ctor) {
329
+ return {
330
+ name: ctor.name,
331
+ title: ctor.title,
332
+ group: ctor.group,
333
+ schema: {
334
+ dataType: 'boolean',
335
+ defaultValue: ctor.defaultValue ?? false,
336
+ interface: {
337
+ name: ctor.name,
338
+ path: ctor.path ?? ctor.name,
339
+ type: AXPWidgetsCatalog.toggle,
340
+ },
341
+ },
342
+ visible: !isNil(ctor.visible) ? ctor.visible : true,
343
+ binding: {
344
+ enabled: true,
345
+ },
346
+ };
347
+ }
348
+ function createSelectProperty(ctor) {
349
+ return {
350
+ name: ctor.name,
351
+ title: ctor.title,
352
+ group: ctor.group,
353
+ schema: {
354
+ dataType: 'string',
355
+ defaultValue: Array.isArray(ctor.defaultValue)
356
+ ? ctor.defaultValue.map((item) => (typeof item === 'string' ? { id: item } : item))
357
+ : typeof ctor.defaultValue === 'string'
358
+ ? { id: ctor.defaultValue, title: ctor.defaultValue }
359
+ : ctor.defaultValue,
360
+ interface: {
361
+ name: ctor.name,
362
+ path: ctor.path ?? ctor.name,
363
+ type: AXPWidgetsCatalog.select,
364
+ options: {
365
+ dataSource: ctor.dataSource.map((item) => (typeof item === 'string' ? { id: item, title: item } : item)),
366
+ },
367
+ },
368
+ },
369
+ visible: !isNil(ctor.visible) ? ctor.visible : true,
370
+ };
371
+ }
372
+ const AXP_WIDGET_TOKEN = new InjectionToken('AXP_WIDGET_TOKEN');
373
+ const AXP_WIDGET_COLUMN_TOKEN = new InjectionToken('AXP_WIDGET_COLUMN_TOKEN');
374
+
375
+ class AXPBaseWidgetComponent extends AXPWidgetCoreElement {
376
+ constructor() {
377
+ super(...arguments);
378
+ this.token = inject(AXP_WIDGET_TOKEN);
379
+ this.host = inject(ElementRef).nativeElement;
380
+ this.layoutService = inject(AXPWidgetCoreService);
381
+ this.contextService = inject(AXPContextStore);
382
+ this.config = this.token.config;
383
+ this.node = this.token.node;
384
+ this.name = this.token.node.name;
385
+ this.component = this;
386
+ this._options = signal(this.token.options ?? {}, ...(ngDevMode ? [{ debugName: "_options" }] : /* istanbul ignore next */ []));
387
+ this.options = this._options.asReadonly();
388
+ this.onOptionsChanged = new Subject();
389
+ this._status = signal(AXPWidgetStatus.Rendering, ...(ngDevMode ? [{ debugName: "_status" }] : /* istanbul ignore next */ []));
390
+ this.status = this._status.asReadonly();
391
+ this.onStatusChanged = new BehaviorSubject(this._status());
392
+ this.#statusEffect = effect(() => {
393
+ this.onStatusChanged.next(this.status());
394
+ }, ...(ngDevMode ? [{ debugName: "#statusEffect" }] : /* istanbul ignore next */ []));
395
+ this.isBusy = computed(() => [AXPWidgetStatus.Rendering, AXPWidgetStatus.Processing].includes(this.status()), ...(ngDevMode ? [{ debugName: "isBusy" }] : /* istanbul ignore next */ []));
396
+ this._children = signal(this.token.node.children ?? [], ...(ngDevMode ? [{ debugName: "_children" }] : /* istanbul ignore next */ []));
397
+ this.children = this._children.asReadonly();
398
+ }
399
+ get id() {
400
+ return this._id;
401
+ }
402
+ #statusEffect;
403
+ outputs() {
404
+ return [];
405
+ }
406
+ ngOnInit() {
407
+ if (get(this.node, '__meta__.added')) {
408
+ this.onAdded();
409
+ }
410
+ this.setStatus(AXPWidgetStatus.Rendered);
411
+ }
412
+ setStatus(status) {
413
+ this._status.set(status);
414
+ this.layoutService.updateStatus();
415
+ }
416
+ setOptions(values) {
417
+ const oldValue = this.options();
418
+ const value = cloneDeep(values);
419
+ this._options.set({ ...oldValue, ...value });
420
+ this.onOptionsChanged.next({ sender: this });
421
+ }
422
+ output(name) {
423
+ const outputs = this.outputs().map((c) => (typeof c == 'string' ? { name: c, value: c } : c));
424
+ if (outputs.some((c) => c.name == name)) {
425
+ const opt = get(this, name);
426
+ if (typeof opt == 'function') {
427
+ return opt();
428
+ }
429
+ return opt;
430
+ }
431
+ return null;
432
+ }
433
+ call(name, ...args) {
434
+ const fn = get(this, name);
435
+ if (fn && typeof fn == 'function') {
436
+ fn.bind(this)(...args);
437
+ }
438
+ }
439
+ setChildren(children) {
440
+ this._children.set([...children]);
441
+ }
442
+ onAdded() { }
443
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBaseWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
444
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBaseWidgetComponent }); }
445
+ }
446
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBaseWidgetComponent, decorators: [{
447
+ type: Injectable
448
+ }] });
449
+ class AXPLayoutBaseWidgetComponent extends AXPBaseWidgetComponent {
450
+ //TODO change this approach
451
+ ngOnInit() {
452
+ if (this.name && !(this instanceof AXPValueWidgetComponent)) {
453
+ this.layoutService.registerWidget(this.name, this);
454
+ }
455
+ super.ngOnInit();
456
+ }
457
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutBaseWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
458
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutBaseWidgetComponent }); }
459
+ }
460
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutBaseWidgetComponent, decorators: [{
461
+ type: Injectable
462
+ }] });
463
+ class AXPValueWidgetComponent extends AXPLayoutBaseWidgetComponent {
464
+ constructor() {
465
+ super(...arguments);
466
+ this.path = this.token.node.path;
467
+ this.defaultValue = this.token.defaultValue ?? this.token.node.defaultValue;
468
+ this._isValueWidget = false;
469
+ this.isValueWidget = () => this._isValueWidget;
470
+ this.onValueChanged = new Subject();
471
+ this.fullPath = signal(null, ...(ngDevMode ? [{ debugName: "fullPath" }] : /* istanbul ignore next */ []));
472
+ this.parentPath = signal(null, ...(ngDevMode ? [{ debugName: "parentPath" }] : /* istanbul ignore next */ []));
473
+ this.getValue = computed(() => {
474
+ return this.fullPath() ? this.extractValue(this.fullPath()) : null;
475
+ }, { ...(ngDevMode ? { debugName: "getValue" } : /* istanbul ignore next */ {}), equal: isEqual });
476
+ this.validationRules = computed(() => {
477
+ const validationsRaw = this.options()['validations'];
478
+ if (validationsRaw == null) {
479
+ return [];
480
+ }
481
+ return Object.values(this.options()['validations'])
482
+ .filter((c) => c != null)
483
+ .map((c) => ({
484
+ rule: c.rule,
485
+ message: c.message,
486
+ options: c.options,
487
+ }));
488
+ }, ...(ngDevMode ? [{ debugName: "validationRules" }] : /* istanbul ignore next */ []));
489
+ }
490
+ ngOnInit() {
491
+ this._isValueWidget = this.config.properties?.some((c) => c.name == 'path') ?? false;
492
+ if (this.isValueWidget()) {
493
+ this.detectFullPath();
494
+ // Set defaultValue if it exists and the path doesn't exist in context
495
+ if (!isNil(this.defaultValue) && this.fullPath() && !this.contextService.hasValue(this.fullPath())) {
496
+ this.setValue(this.defaultValue);
497
+ }
498
+ }
499
+ //
500
+ super.ngOnInit();
501
+ }
502
+ extractValue(path) {
503
+ const rawValue = this.contextService.getValue(path);
504
+ if (this.node.valueTransforms?.getter) {
505
+ return this.node.valueTransforms?.getter(rawValue);
506
+ }
507
+ return rawValue;
508
+ }
509
+ setValue(value) {
510
+ if (this.node.valueTransforms?.setter) {
511
+ value = this.node.valueTransforms?.setter(value);
512
+ }
513
+ const oldValue = this.getValue();
514
+ value = isUndefined(value) ? null : value;
515
+ if (isNil(value) && isNil(oldValue)) {
516
+ return;
517
+ }
518
+ // Reordered arrays must persist even when items are deep-equal (e.g. empty row objects).
519
+ const isArrayReorder = Array.isArray(oldValue) &&
520
+ Array.isArray(value) &&
521
+ oldValue.length === value.length &&
522
+ oldValue.some((v, i) => v !== value[i]);
523
+ if (!isArrayReorder && isEqual(oldValue, value)) {
524
+ return;
525
+ }
526
+ if (this.fullPath()) {
527
+ this.contextService.update(this.fullPath(), value);
528
+ this.onValueChanged.next({ sender: this });
529
+ }
530
+ }
531
+ detectFullPath() {
532
+ const sections = [];
533
+ const ids = [];
534
+ //
535
+ let parent = this;
536
+ //
537
+ while (parent) {
538
+ const isValueWidget = parent instanceof AXPValueWidgetComponent && parent.isValueWidget();
539
+ const valueParent = parent;
540
+ const path = valueParent.path ?? (isValueWidget ? valueParent.name : null);
541
+ const id = valueParent.name;
542
+ //
543
+ if (path) {
544
+ sections.push(path);
545
+ }
546
+ if (parent.index != null && isValueWidget) {
547
+ sections.push(`[${parent.index}]`);
548
+ }
549
+ if (id) {
550
+ ids.push(id);
551
+ if (parent.index != null) {
552
+ ids.push(`${parent.index}`);
553
+ }
554
+ }
555
+ parent = parent.parent;
556
+ }
557
+ //
558
+ this.fullPath.set(sections.reverse().join('.'));
559
+ this.parentPath.set(sections.slice(0, sections.length - 1).join('.'));
560
+ this._id = this.name || this.parent ? ids.reverse().join('_') : null;
561
+ if (this._id) {
562
+ this.layoutService.registerWidget(this._id, this);
563
+ }
564
+ }
565
+ handleValueChanged(e) {
566
+ if (e.isUserInteraction) {
567
+ this.setValue(e.value);
568
+ }
569
+ }
570
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPValueWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
571
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPValueWidgetComponent }); }
572
+ }
573
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPValueWidgetComponent, decorators: [{
574
+ type: Injectable
575
+ }] });
576
+ class AXPDataListWidgetComponent extends AXPValueWidgetComponent {
577
+ constructor() {
578
+ super(...arguments);
579
+ this.dataService = inject(AXPDataSourceDefinitionProviderService);
580
+ this.textField = computed(() => this.options()['textField'] ?? 'title', ...(ngDevMode ? [{ debugName: "textField" }] : /* istanbul ignore next */ []));
581
+ this.valueField = computed(() => this.options()['valueField'] ?? 'id', ...(ngDevMode ? [{ debugName: "valueField" }] : /* istanbul ignore next */ []));
582
+ this.textTemplate = computed(() => isNil(this.options()['textTemplate'])
583
+ ? undefined
584
+ : this.options()['textTemplate'].replace(/{/g, '{{').replace(/}/g, '}}'), ...(ngDevMode ? [{ debugName: "textTemplate" }] : /* istanbul ignore next */ []));
585
+ this.dataSource = signal(convertArrayToDataSource([]), ...(ngDevMode ? [{ debugName: "dataSource" }] : /* istanbul ignore next */ []));
586
+ this.selectedItems = signal([], ...(ngDevMode ? [{ debugName: "selectedItems" }] : /* istanbul ignore next */ []));
587
+ //#region ---- DataSource Loading Effect ----
588
+ /**
589
+ * Track the last loaded string dataSource reference to prevent infinite loops
590
+ * Only used for string-based datasources (not arrays or AXDataSource instances)
591
+ */
592
+ this.lastStringDataSourceRef = null;
593
+ this.rf = effect(async () => {
594
+ const rawValue = this.options()['dataSource'];
595
+ // For string datasources: prevent infinite loop by checking reference
596
+ if (typeof rawValue === 'string') {
597
+ if (rawValue === this.lastStringDataSourceRef) {
598
+ return;
599
+ }
600
+ this.lastStringDataSourceRef = rawValue;
601
+ }
602
+ else {
603
+ // Reset string reference when dataSource changes to non-string
604
+ this.lastStringDataSourceRef = null;
605
+ }
606
+ // Process dataSource creation in untracked context
607
+ await untracked(async () => {
608
+ if (rawValue instanceof AXDataSource) {
609
+ // For AXDataSource instances, check if config changed
610
+ if (!isEqual(rawValue.config, this.dataSource().config)) {
611
+ this.dataSource.set(rawValue);
612
+ }
613
+ }
614
+ // static array datasource - always recreate when options change
615
+ else if (Array.isArray(rawValue)) {
616
+ const ds = new AXDataSource({
617
+ key: this.valueField(),
618
+ pageSize: 10,
619
+ load: async (e) => {
620
+ const raw = this.options()['dataSource'];
621
+ const skip = e.skip ?? 0;
622
+ const take = e.take ?? raw.length;
623
+ return {
624
+ items: raw.slice(skip, skip + take),
625
+ total: raw.length,
626
+ };
627
+ },
628
+ byKey: (key) => {
629
+ const raw = this.options()['dataSource'];
630
+ const item = raw.filter((c) => c[this.valueField()] == key);
631
+ return Promise.resolve(item[0]);
632
+ },
633
+ });
634
+ this.dataSource.set(ds);
635
+ }
636
+ // resolve data source by name (string or object with id)
637
+ else if (rawValue && (typeof rawValue == 'string' || typeof rawValue == 'object')) {
638
+ const id = typeof rawValue == 'object' ? rawValue['id'] : rawValue;
639
+ const c = await this.dataService.get(id);
640
+ if (this.mode == 'designer' && c?.samples?.length) {
641
+ this.dataSource.set(convertArrayToDataSource(c.samples, {
642
+ key: this.valueField(),
643
+ pageSize: 500,
644
+ }));
645
+ }
646
+ else {
647
+ const ds = c?.source();
648
+ if (ds && ds instanceof Promise) {
649
+ const d = await ds;
650
+ this.dataSource.set(d);
651
+ }
652
+ else if (ds) {
653
+ this.dataSource.set(ds);
654
+ }
655
+ // empty datasource
656
+ else {
657
+ this.dataSource.set(convertArrayToDataSource([]));
658
+ }
659
+ }
660
+ }
661
+ // empty datasource
662
+ else {
663
+ this.dataSource.set(convertArrayToDataSource([]));
664
+ }
665
+ });
666
+ }, ...(ngDevMode ? [{ debugName: "rf" }] : /* istanbul ignore next */ []));
667
+ this.effect2 = effect(async () => {
668
+ const value = this.getValue();
669
+ const items = [];
670
+ if (Array.isArray(value)) {
671
+ items.push(...(await Promise.all(value.map((item) => this.extractItem(item)))));
672
+ }
673
+ else {
674
+ items.push(await this.extractItem(value));
675
+ }
676
+ this.selectedItems.set(items.filter((c) => c != null));
677
+ }, ...(ngDevMode ? [{ debugName: "effect2" }] : /* istanbul ignore next */ []));
678
+ }
679
+ //#endregion
680
+ async extractItem(item) {
681
+ if (isNil(item)) {
682
+ return null;
683
+ }
684
+ if (isObjectLike(item) && get(item, this.textField()) != null) {
685
+ return item;
686
+ }
687
+ const key = extractValue(item, this.valueField());
688
+ const ds = this.dataSource();
689
+ if (ds.config?.byKey) {
690
+ const found = await ds.config?.byKey(key);
691
+ if (found) {
692
+ return found;
693
+ }
694
+ }
695
+ return isObjectLike(item)
696
+ ? item
697
+ : {
698
+ [this.valueField()]: item,
699
+ [this.textField()]: item,
700
+ };
701
+ }
702
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDataListWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
703
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDataListWidgetComponent }); }
704
+ }
705
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDataListWidgetComponent, decorators: [{
706
+ type: Injectable
707
+ }] });
708
+ class AXPColumnWidgetComponent {
709
+ constructor() {
710
+ this.token = inject(AXP_WIDGET_COLUMN_TOKEN);
711
+ this.path = this.token.path;
712
+ this.options = this.token.options ?? {};
713
+ this.rawValue = null;
714
+ this.nullText = this.options['nullText'];
715
+ this.nullValue = this.options['nullValue'];
716
+ this.value = () => {
717
+ if (isNil(this.rawValue) && !isNil(this.nullValue)) {
718
+ return this.nullValue;
719
+ }
720
+ return this.rawValue;
721
+ };
722
+ }
723
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPColumnWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
724
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPColumnWidgetComponent }); }
725
+ }
726
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPColumnWidgetComponent, decorators: [{
727
+ type: Injectable
728
+ }] });
729
+
730
+ class AXPBoxModelLayoutWidgetComponent extends AXPLayoutBaseWidgetComponent {
731
+ constructor() {
732
+ super(...arguments);
733
+ this.hostBoxStyle = computed(() => {
734
+ const options = this.options();
735
+ const style = {};
736
+ const spacing = options?.['spacing'];
737
+ const border = options?.['border'];
738
+ const backgroundColor = options?.['backgroundColor'];
739
+ const direction = options?.['direction'];
740
+ const overflow = options?.['overflow'];
741
+ const overflowX = options?.['overflowX'];
742
+ const overflowY = options?.['overflowY'];
743
+ style['background-color'] = backgroundColor ?? '';
744
+ style['padding'] = spacing?.padding ?? '';
745
+ style['margin'] = spacing?.margin ?? '';
746
+ style['border-radius'] = border?.radius ?? '';
747
+ style['border-width'] = border?.width ?? '';
748
+ style['border-color'] = border?.color ?? '';
749
+ style['border-style'] = border?.style ?? '';
750
+ style['overflow'] = overflow ?? '';
751
+ style['overflow-x'] = overflowX ?? '';
752
+ style['overflow-y'] = overflowY ?? '';
753
+ style['direction'] = direction ?? '';
754
+ return style;
755
+ }, ...(ngDevMode ? [{ debugName: "hostBoxStyle" }] : /* istanbul ignore next */ []));
756
+ this.blockStyle = computed(() => {
757
+ const options = this.options();
758
+ const style = { ...this.hostBoxStyle() };
759
+ const width = options?.['width'];
760
+ const minWidth = options?.['minWidth'];
761
+ const maxWidth = options?.['maxWidth'];
762
+ const height = options?.['height'];
763
+ const minHeight = options?.['minHeight'];
764
+ const maxHeight = options?.['maxHeight'];
765
+ style['min-width'] = minWidth ?? '';
766
+ style['width'] = width ?? '';
767
+ style['max-width'] = maxWidth ?? '';
768
+ style['min-height'] = minHeight ?? '';
769
+ style['height'] = height ?? '';
770
+ style['max-height'] = maxHeight ?? '';
771
+ return style;
772
+ }, ...(ngDevMode ? [{ debugName: "blockStyle" }] : /* istanbul ignore next */ []));
773
+ this.inlineStyle = computed(() => {
774
+ return { ...this.hostBoxStyle() };
775
+ }, ...(ngDevMode ? [{ debugName: "inlineStyle" }] : /* istanbul ignore next */ []));
776
+ this.blockClass = computed(() => {
777
+ return {
778
+ 'ax-block': true,
779
+ 'ax-w-full': true,
780
+ // 'ax-widget-outline': true,
781
+ };
782
+ }, ...(ngDevMode ? [{ debugName: "blockClass" }] : /* istanbul ignore next */ []));
783
+ this.inlineClass = computed(() => {
784
+ return {
785
+ 'ax-inline-block': true,
786
+ };
787
+ }, ...(ngDevMode ? [{ debugName: "inlineClass" }] : /* istanbul ignore next */ []));
788
+ }
789
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBoxModelLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
790
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBoxModelLayoutWidgetComponent }); }
791
+ }
792
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBoxModelLayoutWidgetComponent, decorators: [{
793
+ type: Injectable
794
+ }] });
795
+
796
+ class AXPBlockBaseLayoutWidgetComponent extends AXPBoxModelLayoutWidgetComponent {
797
+ constructor() {
798
+ super(...arguments);
799
+ this.hostClass = computed(() => this.blockClass(), ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
800
+ this.hostStyle = computed(() => this.blockStyle(), ...(ngDevMode ? [{ debugName: "hostStyle" }] : /* istanbul ignore next */ []));
801
+ }
802
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBlockBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
803
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBlockBaseLayoutWidgetComponent }); }
804
+ }
805
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBlockBaseLayoutWidgetComponent, decorators: [{
806
+ type: Injectable
807
+ }] });
808
+
809
+ class AXPFlexBaseLayoutWidgetComponent extends AXPBlockBaseLayoutWidgetComponent {
810
+ constructor() {
811
+ super(...arguments);
812
+ this.flex = computed(() => this.options(), ...(ngDevMode ? [{ debugName: "flex" }] : /* istanbul ignore next */ []));
813
+ this.hostFlexStyle = computed(() => {
814
+ const blockStyle = this.blockStyle();
815
+ const style = { ...blockStyle };
816
+ const flex = this.flex();
817
+ if (isNil(flex?.flexDirection)) {
818
+ style['flex-direction'] = '';
819
+ }
820
+ else {
821
+ style['flex-direction'] = flex.flexDirection;
822
+ }
823
+ if (isNil(flex?.flexWrap)) {
824
+ style['flex-wrap'] = '';
825
+ }
826
+ else {
827
+ style['flex-wrap'] = flex.flexWrap;
828
+ }
829
+ //TODO NEED TO FIX LATER
830
+ style['overflow'] = flex?.flexWrap === 'nowrap' ? 'auto' : '';
831
+ //END
832
+ if (isNil(flex?.justifyContent)) {
833
+ style['justify-content'] = '';
834
+ }
835
+ else {
836
+ style['justify-content'] = flex.justifyContent;
837
+ }
838
+ if (isNil(flex?.alignItems)) {
839
+ style['align-items'] = '';
840
+ }
841
+ else {
842
+ style['align-items'] = flex.alignItems;
843
+ }
844
+ if (isNil(flex?.gap)) {
845
+ style['gap'] = '';
846
+ }
847
+ else {
848
+ style['gap'] = flex.gap;
849
+ }
850
+ return style;
851
+ }, ...(ngDevMode ? [{ debugName: "hostFlexStyle" }] : /* istanbul ignore next */ []));
852
+ this.hostFlexClass = computed(() => {
853
+ return {
854
+ ...this.blockClass(),
855
+ 'ax-flex': true,
856
+ 'ax-h-full': true,
857
+ };
858
+ }, ...(ngDevMode ? [{ debugName: "hostFlexClass" }] : /* istanbul ignore next */ []));
859
+ this.hostClass = computed(() => {
860
+ return this.hostFlexClass();
861
+ }, ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
862
+ this.hostStyle = computed(() => {
863
+ return this.hostFlexStyle();
864
+ }, ...(ngDevMode ? [{ debugName: "hostStyle" }] : /* istanbul ignore next */ []));
865
+ }
866
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFlexBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
867
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFlexBaseLayoutWidgetComponent }); }
868
+ }
869
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFlexBaseLayoutWidgetComponent, decorators: [{
870
+ type: Injectable
871
+ }] });
872
+
873
+ class AXPInlineBaseLayoutWidgetComponent extends AXPBoxModelLayoutWidgetComponent {
874
+ constructor() {
875
+ super(...arguments);
876
+ this.hostClass = computed(() => this.inlineClass(), ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
877
+ this.hostStyle = computed(() => this.inlineStyle(), ...(ngDevMode ? [{ debugName: "hostStyle" }] : /* istanbul ignore next */ []));
878
+ }
879
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPInlineBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
880
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPInlineBaseLayoutWidgetComponent }); }
881
+ }
882
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPInlineBaseLayoutWidgetComponent, decorators: [{
883
+ type: Injectable
884
+ }] });
885
+
886
+ class AXPFlexItemBaseLayoutWidgetComponent extends AXPInlineBaseLayoutWidgetComponent {
887
+ constructor() {
888
+ super(...arguments);
889
+ this.flexItem = computed(() => this.options(), ...(ngDevMode ? [{ debugName: "flexItem" }] : /* istanbul ignore next */ []));
890
+ this.hostFlexItemStyle = computed(() => {
891
+ const inlineStyle = this.blockStyle();
892
+ const style = { ...inlineStyle };
893
+ const fi = this.flexItem();
894
+ if (isNil(fi?.order)) {
895
+ style['order'] = '';
896
+ }
897
+ else {
898
+ style['order'] = fi.order;
899
+ }
900
+ if (isNil(fi?.grow)) {
901
+ style['flex-grow'] = '';
902
+ }
903
+ else {
904
+ style['flex-grow'] = fi.grow;
905
+ }
906
+ if (isNil(fi?.shrink)) {
907
+ style['flex-shrink'] = '';
908
+ }
909
+ else {
910
+ style['flex-shrink'] = fi.shrink;
911
+ }
912
+ if (isNil(fi?.basis)) {
913
+ style['flex-basis'] = '';
914
+ }
915
+ else {
916
+ style['flex-basis'] = fi.basis;
917
+ }
918
+ if (isNil(fi?.alignSelf)) {
919
+ style['align-self'] = '';
920
+ }
921
+ else {
922
+ style['align-self'] = fi.alignSelf;
923
+ }
924
+ return style;
925
+ }, ...(ngDevMode ? [{ debugName: "hostFlexItemStyle" }] : /* istanbul ignore next */ []));
926
+ this.hostFlexItemClass = computed(() => {
927
+ return {
928
+ ...this.blockClass(),
929
+ };
930
+ }, ...(ngDevMode ? [{ debugName: "hostFlexItemClass" }] : /* istanbul ignore next */ []));
931
+ this.hostClass = computed(() => {
932
+ return this.hostFlexItemClass();
933
+ }, ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
934
+ this.hostStyle = computed(() => {
935
+ return this.hostFlexItemStyle();
936
+ }, ...(ngDevMode ? [{ debugName: "hostStyle" }] : /* istanbul ignore next */ []));
937
+ }
938
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFlexItemBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
939
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFlexItemBaseLayoutWidgetComponent }); }
940
+ }
941
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFlexItemBaseLayoutWidgetComponent, decorators: [{
942
+ type: Injectable
943
+ }] });
944
+
945
+ class AXPGridBaseLayoutWidgetComponent extends AXPBlockBaseLayoutWidgetComponent {
946
+ constructor() {
947
+ super(...arguments);
948
+ this.grid = computed(() => this.options()?.['grid'], ...(ngDevMode ? [{ debugName: "grid" }] : /* istanbul ignore next */ []));
949
+ this.hostGridStyle = computed(() => {
950
+ const style = { ...this.inlineStyle() };
951
+ const g = this.grid()?.default;
952
+ if (g?.gap)
953
+ style['gap'] = g.gap;
954
+ return style;
955
+ }, ...(ngDevMode ? [{ debugName: "hostGridStyle" }] : /* istanbul ignore next */ []));
956
+ this.hostGridClass = computed(() => {
957
+ const cls = {
958
+ ...this.inlineClass(),
959
+ 'ax-grid': true,
960
+ };
961
+ const g = this.grid()?.default;
962
+ if (g?.columns)
963
+ cls[`lg:ax-grid-cols-${g.columns}`] = true;
964
+ // if (g?.rows) cls[`lg:ax-grid-rows-${g.rows}`] = true;
965
+ if (g?.justifyItems)
966
+ cls[`lg:ax-justify-items-${g.justifyItems}`] = true;
967
+ if (g?.alignItems)
968
+ cls[`lg:ax-align-items-${g.alignItems}`] = true;
969
+ if (g?.autoFlow)
970
+ cls[`lg:ax-grid-flow-${g.autoFlow}`] = true;
971
+ return cls;
972
+ }, ...(ngDevMode ? [{ debugName: "hostGridClass" }] : /* istanbul ignore next */ []));
973
+ this.hostClass = computed(() => this.hostGridClass(), ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
974
+ this.hostStyle = computed(() => this.hostGridStyle(), ...(ngDevMode ? [{ debugName: "hostStyle" }] : /* istanbul ignore next */ []));
975
+ }
976
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPGridBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
977
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPGridBaseLayoutWidgetComponent }); }
978
+ }
979
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPGridBaseLayoutWidgetComponent, decorators: [{
980
+ type: Injectable
981
+ }] });
982
+
983
+ class AXPGridItemBaseLayoutWidgetComponent extends AXPFlexBaseLayoutWidgetComponent {
984
+ constructor() {
985
+ super(...arguments);
986
+ this.gridItem = computed(() => this.options(), ...(ngDevMode ? [{ debugName: "gridItem" }] : /* istanbul ignore next */ []));
987
+ this.hostGridItemStyle = computed(() => {
988
+ const style = { ...this.hostFlexStyle() };
989
+ const g = this.gridItem();
990
+ if (g?.alignSelf)
991
+ style['align-self'] = g.alignSelf;
992
+ if (g?.justifySelf)
993
+ style['justify-self'] = g.justifySelf;
994
+ return style;
995
+ }, ...(ngDevMode ? [{ debugName: "hostGridItemStyle" }] : /* istanbul ignore next */ []));
996
+ this.hostGridItemClass = computed(() => {
997
+ const cls = { ...this.hostFlexClass() };
998
+ const g = this.gridItem();
999
+ // Mobile-first: full width on small screens so items stack vertically and don't overflow
1000
+ cls['ax-col-span-12'] = true;
1001
+ // Large screens: apply defined placement so multi-column layout works on desktop
1002
+ if (g?.colSpan)
1003
+ cls[`lg:ax-col-span-${g.colSpan}`] = true;
1004
+ if (g?.colStart)
1005
+ cls[`lg:ax-col-start-${g.colStart}`] = true;
1006
+ if (g?.colEnd)
1007
+ cls[`lg:ax-col-end-${g.colEnd}`] = true;
1008
+ if (g?.rowSpan)
1009
+ cls[`lg:ax-row-span-${g.rowSpan}`] = true;
1010
+ if (g?.rowStart)
1011
+ cls[`lg:ax-row-start-${g.rowStart}`] = true;
1012
+ if (g?.rowEnd)
1013
+ cls[`lg:ax-row-end-${g.rowEnd}`] = true;
1014
+ return cls;
1015
+ }, ...(ngDevMode ? [{ debugName: "hostGridItemClass" }] : /* istanbul ignore next */ []));
1016
+ this.hostClass = computed(() => this.hostGridItemClass(), ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
1017
+ this.hostStyle = computed(() => this.hostGridItemStyle(), ...(ngDevMode ? [{ debugName: "hostStyle" }] : /* istanbul ignore next */ []));
1018
+ }
1019
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPGridItemBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
1020
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPGridItemBaseLayoutWidgetComponent }); }
1021
+ }
1022
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPGridItemBaseLayoutWidgetComponent, decorators: [{
1023
+ type: Injectable
1024
+ }] });
1025
+
1026
+ class AXPTableBaseLayoutWidgetComponent extends AXPBlockBaseLayoutWidgetComponent {
1027
+ constructor() {
1028
+ super(...arguments);
1029
+ this.hostTableClass = computed(() => ({
1030
+ ...this.blockClass(),
1031
+ }), ...(ngDevMode ? [{ debugName: "hostTableClass" }] : /* istanbul ignore next */ []));
1032
+ this.hostTableStyle = computed(() => {
1033
+ const style = { ...this.blockStyle() };
1034
+ style['overflow-x'] = 'auto';
1035
+ return style;
1036
+ }, ...(ngDevMode ? [{ debugName: "hostTableStyle" }] : /* istanbul ignore next */ []));
1037
+ this.hostClass = computed(() => this.hostTableClass(), ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
1038
+ this.hostStyle = computed(() => this.hostTableStyle(), ...(ngDevMode ? [{ debugName: "hostStyle" }] : /* istanbul ignore next */ []));
1039
+ }
1040
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTableBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
1041
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTableBaseLayoutWidgetComponent }); }
1042
+ }
1043
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTableBaseLayoutWidgetComponent, decorators: [{
1044
+ type: Injectable
1045
+ }] });
1046
+
1047
+ class AXPTableItemOpsBaseLayoutWidgetComponent extends AXPBlockBaseLayoutWidgetComponent {
1048
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTableItemOpsBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
1049
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTableItemOpsBaseLayoutWidgetComponent }); }
1050
+ }
1051
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTableItemOpsBaseLayoutWidgetComponent, decorators: [{
1052
+ type: Injectable
1053
+ }] });
1054
+
1055
+ class AXPTableItemBaseLayoutWidgetComponent extends AXPTableItemOpsBaseLayoutWidgetComponent {
1056
+ constructor() {
1057
+ super(...arguments);
1058
+ this.colSpan = computed(() => {
1059
+ const v = this.options()?.colSpan;
1060
+ return v ? Math.max(1, v) : 1;
1061
+ }, ...(ngDevMode ? [{ debugName: "colSpan" }] : /* istanbul ignore next */ []));
1062
+ this.rowSpan = computed(() => {
1063
+ const v = this.options()?.rowSpan;
1064
+ return v ? Math.max(1, v) : 1;
1065
+ }, ...(ngDevMode ? [{ debugName: "rowSpan" }] : /* istanbul ignore next */ []));
1066
+ this.hostClass = computed(() => this.blockClass(), ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
1067
+ this.hostStyle = computed(() => this.blockStyle(), ...(ngDevMode ? [{ debugName: "hostStyle" }] : /* istanbul ignore next */ []));
1068
+ }
1069
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTableItemBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
1070
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTableItemBaseLayoutWidgetComponent }); }
1071
+ }
1072
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTableItemBaseLayoutWidgetComponent, decorators: [{
1073
+ type: Injectable
1074
+ }] });
1075
+
1076
+ //#region ---- Imports ----
1077
+ /**
1078
+ * Injection token for widget definition providers.
1079
+ * Modules register their widget providers using this token (multi: true).
1080
+ */
1081
+ const AXP_WIDGET_DEFINITION_PROVIDER = new InjectionToken('AXP_WIDGET_DEFINITION_PROVIDER', {
1082
+ factory: () => [],
1083
+ });
1084
+ //#endregion
1085
+
1086
+ //#region ---- Build widget map (shared with tooling / snapshot scripts) ----
1087
+ //TODO RECHECK THIS FUNCTION s.hosseini
1088
+ /**
1089
+ * Merges core + extended widgets the same way as AXPWidgetRegistryService.ensureBuilt().
1090
+ * Use with concrete providers (e.g. AXPCoreWidgetsProvider, AXPEntityWidgetsProvider) in Node scripts without DI.
1091
+ */
1092
+ function buildWidgetRegistryMapFromProviders(providers) {
1093
+ const types = new Map();
1094
+ const extendedWidgets = [];
1095
+ for (const provider of providers) {
1096
+ const widgets = provider.getWidgets?.() ?? [];
1097
+ for (const w of widgets) {
1098
+ types.set(w.name, w);
1099
+ }
1100
+ const extended = provider.getExtendedWidgets?.() ?? [];
1101
+ extendedWidgets.push(...extended);
1102
+ }
1103
+ for (const { parentName, widget } of extendedWidgets) {
1104
+ const parent = types.get(parentName);
1105
+ if (parent) {
1106
+ const merged = merge({}, parent, widget);
1107
+ merged.name = widget.name;
1108
+ types.set(merged.name, merged);
1109
+ }
1110
+ }
1111
+ return types;
1112
+ }
1113
+ //#endregion
1114
+ //#region ---- Widget Registry Service (Token-Based) ----
1115
+ /**
1116
+ * Aggregates widgets from all AXP_WIDGET_DEFINITION_PROVIDER instances.
1117
+ * Token-based like AXP_MENU_PROVIDER / AXP_PERMISSION_DEFINITION_PROVIDER / AXP_SETTING_DEFINITION_PROVIDER.
1118
+ * No separate registry - widgets come solely from providers.
1119
+ */
1120
+ class AXPWidgetRegistryService {
1121
+ constructor() {
1122
+ this._providers = inject(AXP_WIDGET_DEFINITION_PROVIDER, { optional: true }) ?? [];
1123
+ this._types = null;
1124
+ }
1125
+ /**
1126
+ * Lazy-build widget map from all providers.
1127
+ */
1128
+ ensureBuilt() {
1129
+ if (this._types)
1130
+ return this._types;
1131
+ this._types = buildWidgetRegistryMapFromProviders(this._providers);
1132
+ return this._types;
1133
+ }
1134
+ resolve(name) {
1135
+ const widget = this.ensureBuilt().get(name);
1136
+ if (!widget) {
1137
+ throw new Error(`Widget with name "${name}" does not exist.`);
1138
+ }
1139
+ return widget;
1140
+ }
1141
+ /**
1142
+ * Registered widget config when present; otherwise `undefined`. Does not throw — use when
1143
+ * {@link resolve}'s value may be a non-widget id (e.g. data-source row id in dynamic field configurator).
1144
+ */
1145
+ getOptional(name) {
1146
+ return this.ensureBuilt().get(name);
1147
+ }
1148
+ all() {
1149
+ return Array.from(this.ensureBuilt().values());
1150
+ }
1151
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1152
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetRegistryService, providedIn: 'root' }); }
1153
+ }
1154
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetRegistryService, decorators: [{
1155
+ type: Injectable,
1156
+ args: [{
1157
+ providedIn: 'root',
1158
+ }]
1159
+ }] });
1160
+
1161
+ class AXPWidgetColumnRendererComponent extends AXDataTableColumnComponent {
1162
+ constructor() {
1163
+ super(...arguments);
1164
+ this.widgetRegistery = inject(AXPWidgetRegistryService);
1165
+ this.grid = inject(AXBaseDataTable);
1166
+ this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
1167
+ this.mergedOptions = signal({}, ...(ngDevMode ? [{ debugName: "mergedOptions" }] : /* istanbul ignore next */ []));
1168
+ this.loadingRow = signal(null, ...(ngDevMode ? [{ debugName: "loadingRow" }] : /* istanbul ignore next */ []));
1169
+ this.rowInjectorsCache = new Map();
1170
+ this.hasExpressions = false;
1171
+ this.pendingEvaluations = new Set();
1172
+ this.changeDetectionScheduled = false;
1173
+ this.MAX_CACHE_SIZE = 1000; // Limit cache size to prevent memory leaks
1174
+ this.injector = inject(Injector);
1175
+ this.cdr = inject(ChangeDetectorRef);
1176
+ }
1177
+ get node() {
1178
+ return this._node;
1179
+ }
1180
+ set node(v) {
1181
+ this._node = v;
1182
+ }
1183
+ get renderFooterTemplate() {
1184
+ return this.footerTemplate ?? this._contentFooterTemplate;
1185
+ }
1186
+ get renderCellTemplate() {
1187
+ return this.cellTemplate ?? this._contentCellTemplate;
1188
+ }
1189
+ /**
1190
+ * True when the row should show the expand icon: either hasChild is true (from server/initial load)
1191
+ * or the row has loaded children (e.g. after refreshItemChildren when children go from 0 to 1).
1192
+ */
1193
+ hasExpandableRow(row) {
1194
+ const data = row?.data;
1195
+ if (!data)
1196
+ return false;
1197
+ if (data['hasChild'] === true)
1198
+ return true;
1199
+ const children = data['__meta__']?.['children'];
1200
+ return Array.isArray(children) && children.length > 0;
1201
+ }
1202
+ async handleExpandRow(row) {
1203
+ this.loadingRow.set(row);
1204
+ await this.grid.expandRow(row);
1205
+ this.loadingRow.set(null);
1206
+ // if (row.data?.__meta__?.expanded === undefined) {
1207
+ // this.width = `${parseInt(this.width as string) + 24}px`;
1208
+ // }
1209
+ }
1210
+ get renderHeaderTemplate() {
1211
+ return this.headerTemplate ?? this._contentHeaderTemplate;
1212
+ }
1213
+ get loadingEnabled() {
1214
+ return true;
1215
+ }
1216
+ get name() {
1217
+ return `col-${this.node.path}`;
1218
+ }
1219
+ async ngOnInit() {
1220
+ const widget = this.widgetRegistery.resolve(this.node.type);
1221
+ const mode = 'column';
1222
+ this.component = await widget?.components[mode]?.component();
1223
+ //
1224
+ const props = widget?.components[mode]?.properties
1225
+ ?.filter((c) => c.schema.defaultValue)
1226
+ .map((c) => ({ [c.name]: c.schema.defaultValue }))
1227
+ .reduce((acc, curr) => {
1228
+ return { ...acc, ...curr };
1229
+ }, {});
1230
+ //
1231
+ this.mergedOptions.set(merge(props, this.node.options) || {});
1232
+ // Check if options contain expressions (performance optimization)
1233
+ this.hasExpressions = this.checkForExpressions(this.mergedOptions());
1234
+ const tokenValue = {
1235
+ path: this.node.path,
1236
+ options: this.mergedOptions(),
1237
+ };
1238
+ this.widgetInjector = Injector.create({
1239
+ parent: this.injector,
1240
+ providers: [
1241
+ {
1242
+ provide: AXP_WIDGET_COLUMN_TOKEN,
1243
+ useValue: tokenValue,
1244
+ },
1245
+ ],
1246
+ });
1247
+ this.width = this.mergedOptions().width === 'auto' ? 'auto' : this.customWidth ? this.customWidth : (this.mergedOptions().width ?? '200px');
1248
+ this.allowResizing = this.mergedOptions().allowResizing || true;
1249
+ this.cdr.detectChanges();
1250
+ }
1251
+ //#region ---- Performance Optimization Methods ----
1252
+ /**
1253
+ * Check if options contain any expressions that need evaluation
1254
+ */
1255
+ checkForExpressions(obj) {
1256
+ if (typeof obj === 'string') {
1257
+ return this.expressionEvaluator.isExpression(obj);
1258
+ }
1259
+ if (Array.isArray(obj)) {
1260
+ return obj.some((item) => this.checkForExpressions(item));
1261
+ }
1262
+ if (obj && typeof obj === 'object') {
1263
+ return Object.values(obj).some((value) => this.checkForExpressions(value));
1264
+ }
1265
+ return false;
1266
+ }
1267
+ /**
1268
+ * Get cache key for row data - optimized to avoid JSON.stringify when possible
1269
+ */
1270
+ getCacheKey(data) {
1271
+ if (data?.id) {
1272
+ return `row-${data.id}`;
1273
+ }
1274
+ // Use a hash of key properties instead of full JSON.stringify
1275
+ // This is much faster for large objects
1276
+ const keyProps = ['id', 'code', 'name', 'title'];
1277
+ const keyValues = keyProps.map((prop) => data?.[prop]).filter((v) => v != null);
1278
+ if (keyValues.length > 0) {
1279
+ return `row-${keyValues.join('-')}`;
1280
+ }
1281
+ // Fallback to JSON.stringify only if no key properties exist
1282
+ // But limit the stringified size to prevent performance issues
1283
+ try {
1284
+ const str = JSON.stringify(data);
1285
+ return str.length > 1000 ? `row-${str.substring(0, 1000)}` : `row-${str}`;
1286
+ }
1287
+ catch {
1288
+ // If JSON.stringify fails, use a simple hash
1289
+ return `row-${Object.keys(data || {}).length}`;
1290
+ }
1291
+ }
1292
+ /**
1293
+ * Schedule change detection (debounced to batch multiple updates)
1294
+ */
1295
+ scheduleChangeDetection() {
1296
+ if (this.changeDetectionScheduled) {
1297
+ return;
1298
+ }
1299
+ this.changeDetectionScheduled = true;
1300
+ // Use requestAnimationFrame for better performance
1301
+ requestAnimationFrame(() => {
1302
+ this.cdr.detectChanges();
1303
+ this.changeDetectionScheduled = false;
1304
+ });
1305
+ }
1306
+ /**
1307
+ * Limit cache size using simple FIFO eviction
1308
+ */
1309
+ evictCacheIfNeeded() {
1310
+ if (this.rowInjectorsCache.size >= this.MAX_CACHE_SIZE) {
1311
+ // Remove oldest entries (first 25% of cache)
1312
+ const entriesToRemove = Math.floor(this.MAX_CACHE_SIZE * 0.25);
1313
+ const keysToRemove = Array.from(this.rowInjectorsCache.keys()).slice(0, entriesToRemove);
1314
+ keysToRemove.forEach((key) => this.rowInjectorsCache.delete(key));
1315
+ }
1316
+ }
1317
+ //#endregion
1318
+ getInputs(data) {
1319
+ return {
1320
+ rawValue: get(data, this.node.path),
1321
+ rowData: data,
1322
+ };
1323
+ }
1324
+ getRowInjector(data) {
1325
+ // Optimized cache key generation
1326
+ const cacheKey = this.getCacheKey(data);
1327
+ // Check if we have a cached injector for this row
1328
+ let rowInjector = this.rowInjectorsCache.get(cacheKey);
1329
+ if (!rowInjector) {
1330
+ // If no expressions in options, use original options directly (performance optimization)
1331
+ if (!this.hasExpressions) {
1332
+ const tokenValue = {
1333
+ path: this.node.path,
1334
+ options: this.mergedOptions(),
1335
+ };
1336
+ rowInjector = Injector.create({
1337
+ parent: this.injector,
1338
+ providers: [
1339
+ {
1340
+ provide: AXP_WIDGET_COLUMN_TOKEN,
1341
+ useValue: tokenValue,
1342
+ },
1343
+ ],
1344
+ });
1345
+ this.evictCacheIfNeeded();
1346
+ this.rowInjectorsCache.set(cacheKey, rowInjector);
1347
+ return rowInjector;
1348
+ }
1349
+ // Check if evaluation is already pending for this row
1350
+ if (!this.pendingEvaluations.has(cacheKey)) {
1351
+ this.pendingEvaluations.add(cacheKey);
1352
+ // Evaluate expressions in options based on row data
1353
+ const scope = {
1354
+ ...(data ?? {}),
1355
+ context: {
1356
+ eval: (path) => {
1357
+ return getSmart(data, path);
1358
+ },
1359
+ },
1360
+ };
1361
+ // Start async evaluation - will update injector when complete
1362
+ this.expressionEvaluator.evaluate(this.mergedOptions(), scope).then((evaluatedOptions) => {
1363
+ // Create injector with evaluated options
1364
+ const tokenValue = {
1365
+ path: this.node.path,
1366
+ options: evaluatedOptions,
1367
+ };
1368
+ const newInjector = Injector.create({
1369
+ parent: this.injector,
1370
+ providers: [
1371
+ {
1372
+ provide: AXP_WIDGET_COLUMN_TOKEN,
1373
+ useValue: tokenValue,
1374
+ },
1375
+ ],
1376
+ });
1377
+ // Replace cached injector to force component recreation on next render
1378
+ this.rowInjectorsCache.delete(cacheKey);
1379
+ this.evictCacheIfNeeded();
1380
+ this.rowInjectorsCache.set(cacheKey, newInjector);
1381
+ this.pendingEvaluations.delete(cacheKey);
1382
+ // Schedule batched change detection (performance optimization)
1383
+ this.scheduleChangeDetection();
1384
+ }).catch(() => {
1385
+ // If evaluation fails, keep using the fallback injector
1386
+ this.pendingEvaluations.delete(cacheKey);
1387
+ });
1388
+ }
1389
+ // Create injector with original options as fallback (will be updated when evaluation completes)
1390
+ const tokenValue = {
1391
+ path: this.node.path,
1392
+ options: this.mergedOptions(),
1393
+ };
1394
+ rowInjector = Injector.create({
1395
+ parent: this.injector,
1396
+ providers: [
1397
+ {
1398
+ provide: AXP_WIDGET_COLUMN_TOKEN,
1399
+ useValue: tokenValue,
1400
+ },
1401
+ ],
1402
+ });
1403
+ // Cache the initial injector
1404
+ this.evictCacheIfNeeded();
1405
+ this.rowInjectorsCache.set(cacheKey, rowInjector);
1406
+ }
1407
+ return rowInjector;
1408
+ }
1409
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetColumnRendererComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
1410
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPWidgetColumnRendererComponent, isStandalone: false, selector: "axp-widget-column-renderer", inputs: { caption: "caption", customExpandIcon: "customExpandIcon", customCollapseIcon: "customCollapseIcon", customWidth: "customWidth", node: "node", footerTemplate: "footerTemplate", expandHandler: "expandHandler", cellTemplate: "cellTemplate", headerTemplate: "headerTemplate" }, providers: [
1411
+ AXPWidgetCoreService,
1412
+ { provide: AXDataTableColumnComponent, useExisting: AXPWidgetColumnRendererComponent },
1413
+ ], viewQueries: [{ propertyName: "_contentFooterTemplate", first: true, predicate: ["footer"], descendants: true }, { propertyName: "_contentCellTemplate", first: true, predicate: ["cell"], descendants: true }, { propertyName: "_contentHeaderTemplate", first: true, predicate: ["header"], descendants: true }], usesInheritance: true, ngImport: i0, template: `
1414
+ <ng-template #header>{{ caption | translate | async }}</ng-template>
1415
+ <ng-template #cell let-row>
1416
+ <div [class]="expandHandler ? 'ax-flex ax-gap-2 ax-items-center' : ''">
1417
+ @if (expandHandler) {
1418
+ <div
1419
+ (click)="handleExpandRow(row)"
1420
+ class="ax-expand-handler"
1421
+ [class.ax-invisible]="!hasExpandableRow(row)"
1422
+ id="ax-expand-handler-container"
1423
+ [style.padding-inline-start.rem]="row.data?.__meta__?.level * 2"
1424
+ >
1425
+ @if (loadingRow() === row) {
1426
+ <i class="fas fa-spinner-third ax-animate-twSpin ax-animate-infinite"></i>
1427
+ } @else {
1428
+ @if (row.data?.__meta__?.expanded) {
1429
+ <i [class]="customCollapseIcon || 'far fa-minus-square ax-text-md ax-opacity-75'"></i>
1430
+ } @else {
1431
+ <i [class]="customExpandIcon || 'far fa-plus-square ax-text-md ax-opacity-75'"></i>
1432
+ }
1433
+ }
1434
+ </div>
1435
+ }
1436
+ @if (component && row?.data) {
1437
+ <ng-container
1438
+ *ngComponentOutlet="component; injector: getRowInjector(row.data); inputs: getInputs(row.data)"
1439
+ ></ng-container>
1440
+ }
1441
+ </div>
1442
+ </ng-template>
1443
+ <ng-template #footer></ng-template>
1444
+ `, isInline: true, dependencies: [{ kind: "directive", type: i1.NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "pipe", type: i1.AsyncPipe, name: "async" }, { kind: "pipe", type: i2.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1445
+ }
1446
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetColumnRendererComponent, decorators: [{
1447
+ type: Component,
1448
+ args: [{
1449
+ selector: 'axp-widget-column-renderer',
1450
+ template: `
1451
+ <ng-template #header>{{ caption | translate | async }}</ng-template>
1452
+ <ng-template #cell let-row>
1453
+ <div [class]="expandHandler ? 'ax-flex ax-gap-2 ax-items-center' : ''">
1454
+ @if (expandHandler) {
1455
+ <div
1456
+ (click)="handleExpandRow(row)"
1457
+ class="ax-expand-handler"
1458
+ [class.ax-invisible]="!hasExpandableRow(row)"
1459
+ id="ax-expand-handler-container"
1460
+ [style.padding-inline-start.rem]="row.data?.__meta__?.level * 2"
1461
+ >
1462
+ @if (loadingRow() === row) {
1463
+ <i class="fas fa-spinner-third ax-animate-twSpin ax-animate-infinite"></i>
1464
+ } @else {
1465
+ @if (row.data?.__meta__?.expanded) {
1466
+ <i [class]="customCollapseIcon || 'far fa-minus-square ax-text-md ax-opacity-75'"></i>
1467
+ } @else {
1468
+ <i [class]="customExpandIcon || 'far fa-plus-square ax-text-md ax-opacity-75'"></i>
1469
+ }
1470
+ }
1471
+ </div>
1472
+ }
1473
+ @if (component && row?.data) {
1474
+ <ng-container
1475
+ *ngComponentOutlet="component; injector: getRowInjector(row.data); inputs: getInputs(row.data)"
1476
+ ></ng-container>
1477
+ }
1478
+ </div>
1479
+ </ng-template>
1480
+ <ng-template #footer></ng-template>
1481
+ `,
1482
+ providers: [
1483
+ AXPWidgetCoreService,
1484
+ { provide: AXDataTableColumnComponent, useExisting: AXPWidgetColumnRendererComponent },
1485
+ ],
1486
+ changeDetection: ChangeDetectionStrategy.OnPush,
1487
+ inputs: ['caption'],
1488
+ standalone: false,
1489
+ }]
1490
+ }], propDecorators: { customExpandIcon: [{
1491
+ type: Input
1492
+ }], customCollapseIcon: [{
1493
+ type: Input
1494
+ }], customWidth: [{
1495
+ type: Input
1496
+ }], node: [{
1497
+ type: Input,
1498
+ args: [{ required: true }]
1499
+ }], footerTemplate: [{
1500
+ type: Input
1501
+ }], _contentFooterTemplate: [{
1502
+ type: ViewChild,
1503
+ args: ['footer']
1504
+ }], expandHandler: [{
1505
+ type: Input
1506
+ }], cellTemplate: [{
1507
+ type: Input
1508
+ }], _contentCellTemplate: [{
1509
+ type: ViewChild,
1510
+ args: ['cell']
1511
+ }], headerTemplate: [{
1512
+ type: Input
1513
+ }], _contentHeaderTemplate: [{
1514
+ type: ViewChild,
1515
+ args: ['header']
1516
+ }] } });
1517
+
1518
+ class AXPWidgetContainerComponent {
1519
+ set context(value) {
1520
+ this.contextService.set(value);
1521
+ }
1522
+ set functions(v) {
1523
+ this.builderService.setFunctions(v);
1524
+ }
1525
+ constructor() {
1526
+ this.contextService = inject(AXPContextStore);
1527
+ this.builderService = inject(AXPWidgetCoreService);
1528
+ this.onContextChanged = new EventEmitter();
1529
+ this.status = computed(() => {
1530
+ return this.builderService.status();
1531
+ }, ...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
1532
+ this.isBusy = computed(() => {
1533
+ return this.builderService.isBusy();
1534
+ }, ...(ngDevMode ? [{ debugName: "isBusy" }] : /* istanbul ignore next */ []));
1535
+ effect(() => {
1536
+ if (this.contextService.isChanged()) {
1537
+ this.onContextChanged.emit(this.contextService.changeEvent());
1538
+ }
1539
+ });
1540
+ }
1541
+ refresh() {
1542
+ this.builderService.refresh();
1543
+ }
1544
+ find(name) {
1545
+ return this.builderService.waitForWidget(name);
1546
+ }
1547
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1548
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.9", type: AXPWidgetContainerComponent, isStandalone: false, selector: "axp-widgets-container", inputs: { context: "context", functions: "functions" }, outputs: { onContextChanged: "onContextChanged" }, host: { styleAttribute: "display: contents;" }, providers: [AXPWidgetCoreService, AXPContextStore], ngImport: i0, template: `<ng-content></ng-content>`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1549
+ }
1550
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetContainerComponent, decorators: [{
1551
+ type: Component,
1552
+ args: [{
1553
+ selector: 'axp-widgets-container',
1554
+ template: `<ng-content></ng-content>`,
1555
+ changeDetection: ChangeDetectionStrategy.OnPush,
1556
+ host: { style: 'display: contents;' },
1557
+ providers: [AXPWidgetCoreService, AXPContextStore],
1558
+ standalone: false,
1559
+ }]
1560
+ }], ctorParameters: () => [], propDecorators: { onContextChanged: [{
1561
+ type: Output
1562
+ }], context: [{
1563
+ type: Input
1564
+ }], functions: [{
1565
+ type: Input
1566
+ }] } });
1567
+
1568
+ class AXPWidgetPlaceholderComponent {
1569
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetPlaceholderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1570
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.9", type: AXPWidgetPlaceholderComponent, isStandalone: true, selector: "axp-widget-placeholder", ngImport: i0, template: `<div>
1571
+ <ax-skeleton class="ax-w-full ax-h-10 ax-rounded-md"></ax-skeleton>
1572
+ </div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i1$1.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1573
+ }
1574
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetPlaceholderComponent, decorators: [{
1575
+ type: Component,
1576
+ args: [{
1577
+ selector: 'axp-widget-placeholder',
1578
+ template: `<div>
1579
+ <ax-skeleton class="ax-w-full ax-h-10 ax-rounded-md"></ax-skeleton>
1580
+ </div>`,
1581
+ changeDetection: ChangeDetectionStrategy.OnPush,
1582
+ imports: [AXSkeletonModule],
1583
+ standalone: true,
1584
+ }]
1585
+ }] });
1586
+
1587
+ class AXPWidgetRendererDirective {
1588
+ //#endregion
1589
+ //#endregion
1590
+ constructor() {
1591
+ this.parentNode = input(...(ngDevMode ? [undefined, { debugName: "parentNode" }] : /* istanbul ignore next */ []));
1592
+ this.index = input(...(ngDevMode ? [undefined, { debugName: "index" }] : /* istanbul ignore next */ []));
1593
+ this.mode = input.required(...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
1594
+ this.node = input.required(...(ngDevMode ? [{ debugName: "node" }] : /* istanbul ignore next */ []));
1595
+ this._options = signal({}, ...(ngDevMode ? [{ debugName: "_options" }] : /* istanbul ignore next */ []));
1596
+ this.options = this._options.asReadonly();
1597
+ this.onOptionsChanged = output();
1598
+ this.onValueChanged = output();
1599
+ this.onLoad = output();
1600
+ //#region ---- Public API ----
1601
+ /**
1602
+ * Signal that emits the component reference when it's ready
1603
+ */
1604
+ this._componentRefSignal = signal(null, ...(ngDevMode ? [{ debugName: "_componentRefSignal" }] : /* istanbul ignore next */ []));
1605
+ this.componentRefSignal = this._componentRefSignal.asReadonly();
1606
+ //#endregion
1607
+ //#region ---- Properties ----
1608
+ this.mergedOptions = signal({}, ...(ngDevMode ? [{ debugName: "mergedOptions" }] : /* istanbul ignore next */ []));
1609
+ this.injector = inject(Injector);
1610
+ this.builderService = inject(AXPWidgetCoreService);
1611
+ this.contextService = inject(AXPContextStore);
1612
+ this.widgetRegistery = inject(AXPWidgetRegistryService);
1613
+ this.unsubscriber = inject(AXUnsubscriber);
1614
+ this.translateService = inject(AXTranslationService);
1615
+ this.widgetService = inject(AXPWidgetRegistryService);
1616
+ this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
1617
+ this.viewContainerRef = inject(ViewContainerRef);
1618
+ this.isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
1619
+ this.isVisible = signal(true, ...(ngDevMode ? [{ debugName: "isVisible" }] : /* istanbul ignore next */ []));
1620
+ this.expressionEvaluators = new Map();
1621
+ this.renderTimeoutId = null;
1622
+ this.hasInitialRender = false;
1623
+ this.onContextChanged = new Subject();
1624
+ this.onLoadEvent = new Subject();
1625
+ //#region ---- Performance Optimization Properties ----
1626
+ this.contextUpdateQueue = new Set();
1627
+ this.contextUpdateTimeout = null;
1628
+ this.CONTEXT_UPDATE_DEBOUNCE_MS = 4; // ~250fps for large forms
1629
+ // Removed visibility detection - not needed for current implementation
1630
+ // Expression result caching
1631
+ this.expressionCache = new Map();
1632
+ this.EXPRESSION_CACHE_TTL = 500; // Cache for 500ms (increased for better hit rate)
1633
+ // Options change tracking
1634
+ this.lastAppliedOptions = null;
1635
+ // Expression result tracking
1636
+ this.lastExpressionResults = new Map();
1637
+ // Buffer for context changes that happen before initial render completes
1638
+ this.preRenderContextQueue = new Set();
1639
+ effect(async () => {
1640
+ const changed = this.contextService.changeEvent();
1641
+ // Don't trigger re-render during initial setup
1642
+ if (!this.hasInitialRender) {
1643
+ // Handle initial context set (state: 'initiated' with empty path means full context update)
1644
+ if (changed.state === 'initiated' && (!changed.path || changed.path === '')) {
1645
+ // Mark that we need to re-evaluate all expressions when initial render completes
1646
+ // This handles the case where context is set before/during widget initialization
1647
+ this.preRenderContextQueue.add('*'); // Use '*' as a marker for full context initialization
1648
+ // console.log(`📝 [${this.node().type}] Buffered initial context set`);
1649
+ }
1650
+ else if (changed.path) {
1651
+ this.preRenderContextQueue.add(changed.path);
1652
+ // console.log(`📝 [${this.node().type}] Buffered pre-render context change: ${changed.path}`);
1653
+ }
1654
+ return;
1655
+ }
1656
+ // CRITICAL PERFORMANCE FIX: Only respond to relevant context changes
1657
+ // Handle initial context set (full context update)
1658
+ if (changed.state === 'initiated' && (!changed.path || changed.path === '')) {
1659
+ // Full context initialization - trigger re-evaluation of all expressions
1660
+ // console.log(`🎯 [${this.node().type}] Initial context set detected - triggering full re-evaluation`);
1661
+ this.clearExpressionCache();
1662
+ if (this.expressionEvaluators.size > 0) {
1663
+ await this.updateOptionsBasedOnContext();
1664
+ this.applyOptions();
1665
+ }
1666
+ }
1667
+ else if (changed.path && this.isRelevantContextChange(changed.path)) {
1668
+ // console.log(`🎯 [${this.node().type}] Context change detected: ${changed.path}`);
1669
+ this.queueContextUpdate(changed.path);
1670
+ }
1671
+ await this.updateVisibility();
1672
+ });
1673
+ this.builderService.onRefresh.pipe(this.unsubscriber.takeUntilDestroy).subscribe(async () => {
1674
+ await this.processBatchedUpdates();
1675
+ });
1676
+ }
1677
+ //#region ---- Expression Caching Methods ----
1678
+ getCachedExpressionResult(expressionKey) {
1679
+ const cached = this.expressionCache.get(expressionKey);
1680
+ if (cached && performance.now() - cached.timestamp < this.EXPRESSION_CACHE_TTL) {
1681
+ // console.log(`💾 [${this.node().type}] Using cached expression result for '${expressionKey}'`);
1682
+ return cached.value;
1683
+ }
1684
+ return null;
1685
+ }
1686
+ setCachedExpressionResult(expressionKey, value) {
1687
+ this.expressionCache.set(expressionKey, {
1688
+ value,
1689
+ timestamp: performance.now(),
1690
+ });
1691
+ }
1692
+ clearExpressionCache() {
1693
+ this.expressionCache.clear();
1694
+ }
1695
+ isPathAffectingExpressions(changedPath) {
1696
+ // console.log(
1697
+ // `🔍 [${this.node().type}] Checking if path '${changedPath}' affects expressions. Expression count: ${this.expressionEvaluators.size}`,
1698
+ // );
1699
+ // If widget has no expressions, no need to clear cache
1700
+ if (this.expressionEvaluators.size === 0) {
1701
+ // console.log(`🔍 [${this.node().type}] Path '${changedPath}' - no expressions, keeping cache`);
1702
+ return false;
1703
+ }
1704
+ // Use the same logic as hasExpressionDependency to check for actual dependencies
1705
+ if (this.hasExpressionDependency(changedPath)) {
1706
+ // console.log(`🔍 [${this.node().type}] Path '${changedPath}' affects expressions - clearing cache`);
1707
+ return true;
1708
+ }
1709
+ // console.log(`🔍 [${this.node().type}] Path '${changedPath}' does not affect expressions - keeping cache`);
1710
+ return false;
1711
+ }
1712
+ //#endregion
1713
+ //#region ---- Context Batching Methods ----
1714
+ queueContextUpdate(path) {
1715
+ if (path) {
1716
+ // console.log(`🔄 [${this.node().type}] Queueing context update for path: ${path}`);
1717
+ this.contextUpdateQueue.add(path);
1718
+ // Clear existing timeout
1719
+ if (this.contextUpdateTimeout) {
1720
+ clearTimeout(this.contextUpdateTimeout);
1721
+ }
1722
+ // Debounce updates
1723
+ this.contextUpdateTimeout = setTimeout(() => {
1724
+ // console.log(`⚡ [${this.node().type}] Processing batched updates for ${this.contextUpdateQueue.size} paths`);
1725
+ this.processBatchedUpdates();
1726
+ }, this.CONTEXT_UPDATE_DEBOUNCE_MS);
1727
+ }
1728
+ }
1729
+ async processBatchedUpdates() {
1730
+ if (this.contextUpdateQueue.size === 0)
1731
+ return;
1732
+ const startTime = performance.now();
1733
+ const paths = Array.from(this.contextUpdateQueue);
1734
+ this.contextUpdateQueue.clear();
1735
+ // console.log(`📊 [${this.node().type}] Processing ${paths.length} paths:`, paths);
1736
+ // Clear expression cache only if changed paths affect this widget's expressions
1737
+ const shouldClearCache = paths.some((path) => this.isPathAffectingExpressions(path));
1738
+ if (shouldClearCache) {
1739
+ // console.log(`🗑️ [${this.node().type}] Clearing expression cache due to expression-affecting path change`);
1740
+ this.clearExpressionCache();
1741
+ }
1742
+ // Process updates in batches
1743
+ const optionsStartTime = performance.now();
1744
+ const hasOptionsUpdate = await this.updateOptionsBasedOnContext();
1745
+ const optionsTime = performance.now() - optionsStartTime;
1746
+ if (typeof hasOptionsUpdate === 'number' && hasOptionsUpdate > 0) {
1747
+ // console.log(`🔧 [${this.node().type}] Options updated (${optionsTime.toFixed(2)}ms)`);
1748
+ this.applyOptions();
1749
+ }
1750
+ // Check visibility for any of the changed paths
1751
+ const visibilityStartTime = performance.now();
1752
+ const visibilityNeedsUpdate = paths.some((path) => this.hasVisibilityDependency(path));
1753
+ if (visibilityNeedsUpdate) {
1754
+ // console.log(`👁️ [${this.node().type}] Visibility needs update`);
1755
+ await this.updateVisibility();
1756
+ }
1757
+ const visibilityTime = performance.now() - visibilityStartTime;
1758
+ // Emit context changes
1759
+ paths.forEach((path) => {
1760
+ this.onContextChanged.next({ path });
1761
+ });
1762
+ const totalTime = performance.now() - startTime;
1763
+ // console.log(
1764
+ // `✅ [${this.node().type}] Batch processing completed in ${totalTime.toFixed(2)}ms (options: ${optionsTime.toFixed(2)}ms, visibility: ${visibilityTime.toFixed(2)}ms)`,
1765
+ // );
1766
+ }
1767
+ //#endregion
1768
+ //#region ---- Context Relevance Filtering ----
1769
+ isRelevantContextChange(changedPath) {
1770
+ const node = this.node();
1771
+ // 1. Direct path match - widget's own field
1772
+ if (node.path === changedPath) {
1773
+ return true;
1774
+ }
1775
+ // 2. Parent path match - widget is inside the changed container
1776
+ if (node.path && changedPath.startsWith(node.path + '.')) {
1777
+ return true;
1778
+ }
1779
+ // 3. Child path match - changed field is inside this widget's container
1780
+ if (node.path && node.path.startsWith(changedPath + '.')) {
1781
+ return true;
1782
+ }
1783
+ // 4. Expression dependency check - if widget has expressions that depend on this path
1784
+ if (this.hasExpressionDependency(changedPath)) {
1785
+ return true;
1786
+ }
1787
+ // 5. Trigger dependency check - if widget has triggers that depend on this path
1788
+ if (this.hasTriggerDependency(changedPath)) {
1789
+ return true;
1790
+ }
1791
+ // 7. Visibility dependency check - if widget's visibility depends on this path
1792
+ if (this.hasVisibilityDependency(changedPath)) {
1793
+ return true;
1794
+ }
1795
+ return false;
1796
+ }
1797
+ /**
1798
+ * True when a context store path used in an expression (`context.eval`, `context.options`) is
1799
+ * affected by `changedPath`.
1800
+ */
1801
+ contextStorePathDependsOnChangedPath(evalPath, changedPath) {
1802
+ const normalizePath = (p) => {
1803
+ if (!p)
1804
+ return p;
1805
+ const parts = p.split('.');
1806
+ const last = parts[parts.length - 1];
1807
+ if (last && last.endsWith('Id')) {
1808
+ parts.splice(parts.length - 1, 1, last.slice(0, -2), 'id');
1809
+ }
1810
+ return parts.join('.');
1811
+ };
1812
+ const isSegmentSuffix = (a, b) => {
1813
+ if (!a || !b)
1814
+ return false;
1815
+ const pa = a.split('.');
1816
+ const pb = b.split('.');
1817
+ if (pb.length > pa.length)
1818
+ return false;
1819
+ for (let i = 1; i <= pb.length; i++) {
1820
+ if (pa[pa.length - i] !== pb[pb.length - i])
1821
+ return false;
1822
+ }
1823
+ return true;
1824
+ };
1825
+ const evalNorm = normalizePath(evalPath);
1826
+ const changedNorm = normalizePath(changedPath);
1827
+ const rawMatch = evalPath === changedPath ||
1828
+ evalPath.startsWith(changedPath + '.') ||
1829
+ changedPath.startsWith(evalPath + '.') ||
1830
+ isSegmentSuffix(evalPath, changedPath) ||
1831
+ isSegmentSuffix(changedPath, evalPath);
1832
+ const normMatch = evalNorm === changedNorm ||
1833
+ evalNorm.startsWith(changedNorm + '.') ||
1834
+ changedNorm.startsWith(evalNorm + '.') ||
1835
+ isSegmentSuffix(evalNorm, changedNorm) ||
1836
+ isSegmentSuffix(changedNorm, evalNorm);
1837
+ if (rawMatch || normMatch) {
1838
+ return true;
1839
+ }
1840
+ return this.isPathAlias(evalPath, changedPath);
1841
+ }
1842
+ /**
1843
+ * Whether an expression template string reads context store paths affected by `changedPath`.
1844
+ */
1845
+ expressionTextDependsOnContextPath(expressionValue, changedPath) {
1846
+ const contextEvalRegex = /context\.eval\(['"]([^'\"]+)['"]\)/g;
1847
+ let match;
1848
+ while ((match = contextEvalRegex.exec(expressionValue)) !== null) {
1849
+ if (this.contextStorePathDependsOnChangedPath(match[1], changedPath)) {
1850
+ return true;
1851
+ }
1852
+ }
1853
+ const contextOptionsQuoted = /context\.options\(\s*['"]([^'"]*)['"]\s*\)/g;
1854
+ while ((match = contextOptionsQuoted.exec(expressionValue)) !== null) {
1855
+ const sub = match[1];
1856
+ const evalPath = sub === '' ? 'options' : `options.${sub}`;
1857
+ if (this.contextStorePathDependsOnChangedPath(evalPath, changedPath)) {
1858
+ return true;
1859
+ }
1860
+ }
1861
+ const contextOptionsEmpty = /context\.options\(\s*\)/g;
1862
+ while ((match = contextOptionsEmpty.exec(expressionValue)) !== null) {
1863
+ if (this.contextStorePathDependsOnChangedPath('options', changedPath)) {
1864
+ return true;
1865
+ }
1866
+ }
1867
+ return false;
1868
+ }
1869
+ hasExpressionDependency(changedPath) {
1870
+ for (const [path, evaluator] of this.expressionEvaluators) {
1871
+ if (path.includes(changedPath)) {
1872
+ return true;
1873
+ }
1874
+ const node = this.node();
1875
+ const expressionValue = this.getExpressionValueFromNode(node, path);
1876
+ if (expressionValue && typeof expressionValue === 'string') {
1877
+ if (this.expressionTextDependsOnContextPath(expressionValue, changedPath)) {
1878
+ return true;
1879
+ }
1880
+ }
1881
+ }
1882
+ return false;
1883
+ }
1884
+ isPathAlias(evalPath, changedPath) {
1885
+ // Dynamic path alias detection based on 'Id' suffix pattern
1886
+ // Examples: typeId.id <-> type.id, categoryId.name <-> category.name
1887
+ // Check if evalPath ends with 'Id' and has a property
1888
+ if (evalPath.endsWith('Id') && evalPath.includes('.')) {
1889
+ const basePath = evalPath.substring(0, evalPath.lastIndexOf('Id'));
1890
+ const property = evalPath.substring(evalPath.lastIndexOf('.') + 1);
1891
+ const mappedPath = `${basePath}.${property}`;
1892
+ if (changedPath === mappedPath) {
1893
+ // console.log(`🔍 [${this.node().type}] Path alias detected: '${evalPath}' <-> '${changedPath}'`);
1894
+ return true;
1895
+ }
1896
+ }
1897
+ // Check if changedPath ends with 'Id' and has a property
1898
+ if (changedPath.endsWith('Id') && changedPath.includes('.')) {
1899
+ const basePath = changedPath.substring(0, changedPath.lastIndexOf('Id'));
1900
+ const property = changedPath.substring(changedPath.lastIndexOf('.') + 1);
1901
+ const mappedPath = `${basePath}.${property}`;
1902
+ if (evalPath === mappedPath) {
1903
+ // console.log(`🔍 [${this.node().type}] Path alias detected: '${evalPath}' <-> '${changedPath}'`);
1904
+ return true;
1905
+ }
1906
+ }
1907
+ // Check for direct 'Id' suffix mapping (e.g., typeId <-> type.id)
1908
+ if (evalPath.endsWith('Id') && !evalPath.includes('.')) {
1909
+ const basePath = evalPath.substring(0, evalPath.lastIndexOf('Id'));
1910
+ const mappedPath = `${basePath}.id`;
1911
+ if (changedPath === mappedPath) {
1912
+ // console.log(`🔍 [${this.node().type}] Path alias detected: '${evalPath}' <-> '${changedPath}'`);
1913
+ return true;
1914
+ }
1915
+ }
1916
+ if (changedPath.endsWith('Id') && !changedPath.includes('.')) {
1917
+ const basePath = changedPath.substring(0, changedPath.lastIndexOf('Id'));
1918
+ const mappedPath = `${basePath}.id`;
1919
+ if (evalPath === mappedPath) {
1920
+ // console.log(`🔍 [${this.node().type}] Path alias detected: '${evalPath}' <-> '${changedPath}'`);
1921
+ return true;
1922
+ }
1923
+ }
1924
+ return false;
1925
+ }
1926
+ getExpressionValueFromNode(node, path) {
1927
+ try {
1928
+ // Navigate through the node structure to find the expression value
1929
+ const pathParts = path.split('.');
1930
+ let current = node.options || {};
1931
+ // Navigate through the path parts
1932
+ for (const part of pathParts) {
1933
+ if (current && typeof current === 'object' && part in current) {
1934
+ current = current[part];
1935
+ }
1936
+ else {
1937
+ return null;
1938
+ }
1939
+ }
1940
+ return typeof current === 'string' ? current : null;
1941
+ }
1942
+ catch (error) {
1943
+ // console.error('Error extracting expression value from node:', error);
1944
+ return null;
1945
+ }
1946
+ }
1947
+ hasTriggerDependency(changedPath) {
1948
+ const node = this.node();
1949
+ const triggers = node.triggers || node.options?.['triggers'] || this.mergedOptions()?.triggers;
1950
+ if (!triggers)
1951
+ return false;
1952
+ // Check if any trigger event depends on the changed path
1953
+ for (const trigger of triggers) {
1954
+ if (trigger.event && trigger.event.includes(changedPath)) {
1955
+ return true;
1956
+ }
1957
+ }
1958
+ return false;
1959
+ }
1960
+ hasVisibilityDependency(changedPath) {
1961
+ const node = this.node();
1962
+ const visibility = node.options?.['visible'] || this.mergedOptions()?.visible;
1963
+ if (!visibility || typeof visibility !== 'string')
1964
+ return false;
1965
+ if (this.expressionEvaluator.isExpression(visibility)) {
1966
+ return this.expressionTextDependsOnContextPath(visibility, changedPath);
1967
+ }
1968
+ return false;
1969
+ }
1970
+ //#endregion
1971
+ // Removed visibility detection methods - not needed for current implementation
1972
+ // Detect input changes
1973
+ ngOnChanges(changes) {
1974
+ if (changes['mode'] || changes['node']) {
1975
+ // Check if either 'mode' or 'node' has changed
1976
+ this.rerenderComponent();
1977
+ }
1978
+ }
1979
+ rerenderComponent() {
1980
+ // Clear any pending render operation to prevent double rendering
1981
+ if (this.renderTimeoutId) {
1982
+ clearTimeout(this.renderTimeoutId);
1983
+ this.renderTimeoutId = null;
1984
+ }
1985
+ // Reset loading state to allow re-rendering
1986
+ this.isLoading.set(false);
1987
+ // Schedule the component loading
1988
+ this.renderTimeoutId = setTimeout(async () => {
1989
+ await this.loadComponent();
1990
+ this.renderTimeoutId = null;
1991
+ });
1992
+ }
1993
+ ngOnDestroy() {
1994
+ if (this.renderTimeoutId) {
1995
+ clearTimeout(this.renderTimeoutId);
1996
+ }
1997
+ if (this.contextUpdateTimeout) {
1998
+ clearTimeout(this.contextUpdateTimeout);
1999
+ }
2000
+ if (this.componentRef) {
2001
+ this.componentRef.destroy();
2002
+ }
2003
+ this.onLoadEvent.complete();
2004
+ }
2005
+ async loadComponent() {
2006
+ if (this.isLoading()) {
2007
+ return;
2008
+ }
2009
+ this.isLoading.set(true);
2010
+ try {
2011
+ // Destroy the existing component if it exists
2012
+ if (this.componentRef) {
2013
+ this.componentRef.destroy();
2014
+ }
2015
+ this.viewContainerRef.clear();
2016
+ // Reset the component ref signal
2017
+ this._componentRefSignal.set(null);
2018
+ //
2019
+ const widget = this.widgetRegistery.resolve(this.node().type);
2020
+ //
2021
+ const propertiesToProcess = [
2022
+ ...(widget?.properties ?? []),
2023
+ ...(widget?.components[this.mode()]?.properties ?? []),
2024
+ ]?.filter((c) => c.schema.defaultValue != null);
2025
+ // Process default values (evaluate expressions if needed)
2026
+ const props = {};
2027
+ for (const property of propertiesToProcess) {
2028
+ const defaultValue = property.schema.defaultValue;
2029
+ if (typeof defaultValue === 'string' && this.expressionEvaluator.isExpression(defaultValue)) {
2030
+ // Evaluate expression for default value
2031
+ try {
2032
+ const evaluatedValue = await this.evaluateExpression(defaultValue);
2033
+ props[property.name] = evaluatedValue;
2034
+ }
2035
+ catch (error) {
2036
+ // console.error(`Error evaluating default value expression for property ${property.name}:`, error);
2037
+ props[property.name] = cloneDeep(defaultValue); // Fallback to original value
2038
+ }
2039
+ }
2040
+ else {
2041
+ // Use static default value
2042
+ props[property.name] = cloneDeep(defaultValue);
2043
+ }
2044
+ }
2045
+ //
2046
+ this.mergedOptions.set(merge(props, widget?.options, this.node().options) || {});
2047
+ this.expressionEvaluators.clear();
2048
+ // Register expressions from widget defaults and node options to cover related-entity cases
2049
+ this.preprocessAndInitialOptions(cloneDeep(widget?.options));
2050
+ this.preprocessAndInitialOptions(cloneDeep(this.node().options));
2051
+ // Check if context is available before evaluating expressions
2052
+ // If context is empty, expressions will be re-evaluated after initial render
2053
+ const contextData = this.contextService.data();
2054
+ const hasContextData = contextData && Object.keys(contextData).length > 0;
2055
+ if (hasContextData) {
2056
+ await this.updateOptionsBasedOnContext();
2057
+ }
2058
+ else {
2059
+ // Context not yet available - expressions will be evaluated after initial render
2060
+ // when context is set (handled by the effect and preRenderContextQueue)
2061
+ }
2062
+ //
2063
+ this._options.update((val) => ({ ...val, ...this.mergedOptions() }));
2064
+ // Evaluate default value
2065
+ let defaultValue = this.node().defaultValue;
2066
+ if (defaultValue && this.expressionEvaluator.isExpression(defaultValue)) {
2067
+ defaultValue = await this.evaluateExpression(defaultValue);
2068
+ }
2069
+ //
2070
+ const tokenValue = {
2071
+ node: this.node(),
2072
+ defaultValue: defaultValue,
2073
+ options: this.mergedOptions(),
2074
+ config: widget,
2075
+ };
2076
+ const token = Injector.create({
2077
+ parent: this.injector,
2078
+ providers: [
2079
+ {
2080
+ provide: AXP_WIDGET_TOKEN,
2081
+ useValue: tokenValue,
2082
+ },
2083
+ ],
2084
+ });
2085
+ //
2086
+ const loadingRef = this.viewContainerRef.createComponent(AXPWidgetPlaceholderComponent);
2087
+ //
2088
+ const com = await widget?.components[this.mode()]?.component();
2089
+ if (!com) {
2090
+ // console.error(`${this.node().type} widget component not found with mode: ${this.mode()}`);
2091
+ return;
2092
+ }
2093
+ this.componentRef = this.viewContainerRef.createComponent(com, { injector: token });
2094
+ this.instance = this.componentRef.instance;
2095
+ this.instance.setStatus(AXPWidgetStatus.Rendering);
2096
+ this.instance.parent = this.parentNode();
2097
+ this.instance.index = this.index();
2098
+ this.instance.mode = this.mode();
2099
+ this.instance.setStatus(AXPWidgetStatus.Rendered);
2100
+ // Update the component ref signal for reactive access
2101
+ this._componentRefSignal.set(this.componentRef);
2102
+ this.instance?.onOptionsChanged?.pipe(this.unsubscriber.takeUntilDestroy).subscribe((c) => {
2103
+ this.onOptionsChanged.emit({ sender: this, widget: c.sender });
2104
+ });
2105
+ this.instance?.onValueChanged?.pipe(this.unsubscriber.takeUntilDestroy).subscribe((c) => {
2106
+ this.onValueChanged.emit({ sender: this, widget: c.sender });
2107
+ });
2108
+ await this.updateVisibility();
2109
+ await this.assignTriggers();
2110
+ //
2111
+ loadingRef.destroy();
2112
+ // Mark that initial render is complete
2113
+ this.hasInitialRender = true;
2114
+ // Re-evaluate expressions after initial render to catch late-arriving related context
2115
+ if (this.expressionEvaluators.size > 0) {
2116
+ // console.log(`🔄 [${this.node().type}] Re-evaluating expressions after initial render`);
2117
+ await this.updateOptionsBasedOnContext();
2118
+ this.applyOptions();
2119
+ }
2120
+ // Re-evaluate visibility after initial render
2121
+ await this.updateVisibility();
2122
+ // Process any buffered pre-render context changes now that the component is ready
2123
+ if (this.preRenderContextQueue.size > 0) {
2124
+ // Check if we have a full context initialization marker
2125
+ const hasFullContextInit = this.preRenderContextQueue.has('*');
2126
+ if (hasFullContextInit) {
2127
+ // Full context was initialized - clear cache and re-evaluate all expressions
2128
+ // console.log(`🚀 [${this.node().type}] Processing initial context set - full re-evaluation`);
2129
+ this.clearExpressionCache();
2130
+ this.preRenderContextQueue.clear();
2131
+ // Re-evaluate all expressions since context is now available
2132
+ if (this.expressionEvaluators.size > 0) {
2133
+ await this.updateOptionsBasedOnContext();
2134
+ this.applyOptions();
2135
+ }
2136
+ await this.updateVisibility();
2137
+ }
2138
+ else {
2139
+ // console.log(
2140
+ // `🚀 [${this.node().type}] Processing ${this.preRenderContextQueue.size} buffered pre-render changes`,
2141
+ // );
2142
+ this.preRenderContextQueue.forEach((p) => this.contextUpdateQueue.add(p));
2143
+ this.preRenderContextQueue.clear();
2144
+ await this.processBatchedUpdates();
2145
+ }
2146
+ }
2147
+ // Emit load event after widget is fully loaded
2148
+ const loadEvent = { sender: this, widget: this.instance };
2149
+ this.onLoadEvent.next(loadEvent);
2150
+ this.onLoad.emit(loadEvent);
2151
+ }
2152
+ catch (error) {
2153
+ // console.error('Error loading component:', error);
2154
+ }
2155
+ finally {
2156
+ this.isLoading.set(false);
2157
+ }
2158
+ }
2159
+ applyOptions() {
2160
+ if (!this.instance)
2161
+ return;
2162
+ const currentOptions = this.mergedOptions();
2163
+ // Check if options have actually changed
2164
+ if (this.hasOptionsChanged(currentOptions)) {
2165
+ // console.log('applyOptions', this.node().path, '- options changed');
2166
+ this._options.update((val) => ({ ...val, ...currentOptions }));
2167
+ this.instance.setOptions(currentOptions);
2168
+ this.lastAppliedOptions = cloneDeep(currentOptions); // Deep clone using Lodash
2169
+ }
2170
+ else {
2171
+ // console.log('applyOptions', this.node().path, '- no changes, skipping');
2172
+ }
2173
+ }
2174
+ hasOptionsChanged(newOptions) {
2175
+ if (!this.lastAppliedOptions) {
2176
+ return true; // First time, always apply
2177
+ }
2178
+ // Deep comparison of options using Lodash isEqual
2179
+ return !isEqual(newOptions, this.lastAppliedOptions);
2180
+ }
2181
+ // Removed deepCloneValue method - now using Lodash cloneDeep
2182
+ preprocessAndInitialOptions(obj, pathPrefix = '') {
2183
+ if (!obj) {
2184
+ return;
2185
+ }
2186
+ Object.entries(obj).forEach(([key, value]) => {
2187
+ const currentPath = pathPrefix ? `${pathPrefix}.${key}` : key;
2188
+ // CRITICAL FIX: Skip trigger actions during options processing
2189
+ //
2190
+ // PROBLEM: Trigger actions were being evaluated immediately during widget setup/options processing,
2191
+ // causing them to execute before the actual trigger event occurred. This meant triggers would fire
2192
+ // during initialization instead of when the specified context path actually changed.
2193
+ //
2194
+ // ROOT CAUSE: The expression evaluator was processing trigger action expressions (like console.log
2195
+ // or widget.setValue calls) as part of the normal options preprocessing, treating them as dynamic
2196
+ // expressions that needed immediate evaluation.
2197
+ //
2198
+ // SOLUTION: Detect when we're processing trigger actions and store them as static values without
2199
+ // expression evaluation. This ensures trigger actions are only evaluated when the trigger's
2200
+ // subscription callback actually fires (when the event condition is met).
2201
+ //
2202
+ // RESULT: Triggers now only execute when their event filters pass (e.g., when the specified
2203
+ // context path changes), not during widget initialization.
2204
+ if (currentPath.includes('triggers') && (key === 'action' || currentPath.endsWith('.action'))) {
2205
+ // Apply static values directly without expression evaluation
2206
+ this.mergedOptions.update((currentOptions) => {
2207
+ return set(currentOptions, currentPath, value);
2208
+ });
2209
+ return;
2210
+ }
2211
+ if (typeof value === 'string' && this.expressionEvaluator.isExpression(value)) {
2212
+ // Cache dynamic expression for later evaluation
2213
+ this.expressionEvaluators.set(currentPath, () => this.evaluateExpression(value));
2214
+ }
2215
+ else if (typeof value === 'object' &&
2216
+ value !== null &&
2217
+ (value.constructor === Object || Array.isArray(value))) {
2218
+ // Recursively handle nested objects
2219
+ this.preprocessAndInitialOptions(value, currentPath);
2220
+ }
2221
+ else {
2222
+ // Apply static values directly
2223
+ this.mergedOptions.update((currentOptions) => {
2224
+ return set(currentOptions, currentPath, value);
2225
+ });
2226
+ }
2227
+ });
2228
+ }
2229
+ async updateOptionsBasedOnContext() {
2230
+ // Early return if no expressions to evaluate
2231
+ if (this.expressionEvaluators.size === 0) {
2232
+ return 0;
2233
+ }
2234
+ // console.log(`🔍 [${this.node().type}] Evaluating ${this.expressionEvaluators.size} expressions`);
2235
+ const updatePromises = Array.from(this.expressionEvaluators).map(async ([path, evaluator]) => {
2236
+ // Check cache first
2237
+ const cachedValue = this.getCachedExpressionResult(path);
2238
+ if (cachedValue !== null) {
2239
+ // console.log(`💾 [${this.node().type}] Using cached expression result for '${path}'`);
2240
+ return { path, newValue: cachedValue, fromCache: true };
2241
+ }
2242
+ // Evaluate expression if not cached
2243
+ const evalStartTime = performance.now();
2244
+ const newValue = await evaluator();
2245
+ const evalTime = performance.now() - evalStartTime;
2246
+ // Check if result has actually changed using Lodash isEqual
2247
+ // Important: We must check if the key exists in the map, not just compare values
2248
+ // This handles the case where the expression evaluates to `undefined` for the first time
2249
+ // Without this check, `undefined` would be incorrectly treated as "no change"
2250
+ // because Map.get() returns `undefined` for non-existent keys
2251
+ const hasLastValue = this.lastExpressionResults.has(path);
2252
+ const lastValue = this.lastExpressionResults.get(path);
2253
+ const hasChanged = !hasLastValue || !isEqual(newValue, lastValue);
2254
+ if (hasChanged) {
2255
+ // console.log(
2256
+ // `📝 [${this.node().type}] Expression '${path}' evaluated in ${evalTime.toFixed(2)}ms - value changed`,
2257
+ // );
2258
+ // Cache the result and track it
2259
+ this.setCachedExpressionResult(path, newValue);
2260
+ this.lastExpressionResults.set(path, cloneDeep(newValue));
2261
+ return { path, newValue, fromCache: false, hasChanged: true };
2262
+ }
2263
+ else {
2264
+ // console.log(
2265
+ // `📝 [${this.node().type}] Expression '${path}' evaluated in ${evalTime.toFixed(2)}ms - value unchanged, skipping update`,
2266
+ // );
2267
+ // Cache the result but don't update
2268
+ this.setCachedExpressionResult(path, newValue);
2269
+ this.lastExpressionResults.set(path, cloneDeep(newValue));
2270
+ return { path, newValue, fromCache: false, hasChanged: false };
2271
+ }
2272
+ });
2273
+ // Wait for all evaluators to complete
2274
+ const updates = await Promise.all(updatePromises);
2275
+ // Filter updates to only include those that have actually changed
2276
+ const changedUpdates = updates.filter((update) => update.hasChanged !== false);
2277
+ // Apply updates to mergedOptions only for changed values
2278
+ if (changedUpdates.length > 0) {
2279
+ this.mergedOptions.update((o) => {
2280
+ const updatedOptions = { ...o };
2281
+ changedUpdates.forEach(({ path, newValue }) => {
2282
+ set(updatedOptions, path, newValue);
2283
+ });
2284
+ return updatedOptions;
2285
+ });
2286
+ }
2287
+ const cacheHits = updates.filter((update) => update.fromCache).length;
2288
+ const skippedUpdates = updates.length - changedUpdates.length;
2289
+ // console.log(
2290
+ // `📋 [${this.node().type}] Applied ${changedUpdates.length} expression updates (${cacheHits} cache hits, ${skippedUpdates} skipped)`,
2291
+ // );
2292
+ return changedUpdates.length;
2293
+ }
2294
+ async updateVisibility() {
2295
+ const node = this.node();
2296
+ const visibility = node.options?.['visible'] || this.mergedOptions()?.visible;
2297
+ if (visibility === undefined || visibility === null) {
2298
+ // Default to visible if no visibility option is set
2299
+ this.show();
2300
+ return;
2301
+ }
2302
+ let shouldBeVisible;
2303
+ if (typeof visibility === 'boolean') {
2304
+ shouldBeVisible = visibility;
2305
+ }
2306
+ else if (typeof visibility === 'string' && this.expressionEvaluator.isExpression(visibility)) {
2307
+ // Evaluate expression for visibility
2308
+ try {
2309
+ const result = await this.evaluateExpression(visibility);
2310
+ shouldBeVisible = Boolean(result);
2311
+ }
2312
+ catch (error) {
2313
+ // console.error('Error evaluating visibility expression:', error);
2314
+ shouldBeVisible = true; // Default to visible on error
2315
+ }
2316
+ }
2317
+ else {
2318
+ // Treat other values as truthy/falsy
2319
+ shouldBeVisible = Boolean(visibility);
2320
+ }
2321
+ if (shouldBeVisible) {
2322
+ this.show();
2323
+ }
2324
+ else {
2325
+ this.hide();
2326
+ }
2327
+ }
2328
+ show() {
2329
+ if (!this.isVisible()) {
2330
+ this.isVisible.set(true);
2331
+ if (this.componentRef && this.componentRef.location) {
2332
+ this.loadComponent();
2333
+ // this.componentRef.location.nativeElement.style.display = '';
2334
+ }
2335
+ }
2336
+ }
2337
+ hide() {
2338
+ if (this.isVisible()) {
2339
+ this.isVisible.set(false);
2340
+ if (this.componentRef && this.componentRef.location) {
2341
+ this.componentRef.destroy();
2342
+ // this.componentRef.location.nativeElement.style.display = 'none';
2343
+ }
2344
+ }
2345
+ }
2346
+ async evaluateExpression(templateExpression) {
2347
+ try {
2348
+ const scope = this.buildExpressionScope();
2349
+ return await this.expressionEvaluator.evaluate(templateExpression, scope);
2350
+ }
2351
+ catch (error) {
2352
+ // console.error('Error evaluating expression:', error);
2353
+ return false;
2354
+ }
2355
+ }
2356
+ buildExpressionScope() {
2357
+ return {
2358
+ context: this.getContextScope(),
2359
+ events: this.getEventScope(),
2360
+ widget: this.getWidgetScope(),
2361
+ methods: this.getFunctionScope(),
2362
+ vars: this.getVariablesScope(),
2363
+ };
2364
+ }
2365
+ getContextScope() {
2366
+ return {
2367
+ eval: (path) => {
2368
+ //TODO: Handle array index
2369
+ const fullPath = path.startsWith('>') ? `${this.instance?.parentPath()}.${path.substring(1)}` : path;
2370
+ const value = this.contextService.getValue(fullPath);
2371
+ return value;
2372
+ },
2373
+ set: (path, value) => {
2374
+ this.contextService.update(path, value);
2375
+ },
2376
+ data: () => {
2377
+ return this.contextService.data();
2378
+ },
2379
+ isDirty: () => {
2380
+ return this.contextService.isDirty();
2381
+ },
2382
+ /**
2383
+ * Host/widget-under-edit `options` from the shared context store (same values as
2384
+ * `context.eval('options' + (path ? '.' + path : ''))`), not `widget.options` on the nested
2385
+ * property field instance. Uses one `getValue` so smart-path behavior matches `context.eval`.
2386
+ */
2387
+ options: (path) => {
2388
+ if (path == null || path === '') {
2389
+ return this.contextService.getValue('options');
2390
+ }
2391
+ return this.contextService.getValue(`options.${path}`);
2392
+ },
2393
+ };
2394
+ }
2395
+ getEventScope() {
2396
+ return {
2397
+ context: (path) => {
2398
+ return this.onContextChanged.pipe(filter((c) => {
2399
+ // If no path filter specified, pass all events
2400
+ if (path == null || path === '') {
2401
+ return true;
2402
+ }
2403
+ // Ensure c.path exists
2404
+ if (!c.path) {
2405
+ return false;
2406
+ }
2407
+ // Pattern: "prefix*" - matches paths that start with prefix
2408
+ if (path.endsWith('*')) {
2409
+ const prefix = path.substring(0, path.length - 1);
2410
+ return c.path.startsWith(prefix);
2411
+ }
2412
+ // Pattern: "*suffix" - matches paths that end with suffix
2413
+ else if (path.startsWith('*')) {
2414
+ const suffix = path.substring(1);
2415
+ return c.path.endsWith(suffix);
2416
+ }
2417
+ // Exact match
2418
+ else {
2419
+ return c.path === path;
2420
+ }
2421
+ }));
2422
+ },
2423
+ from: (event) => get(this.instance.api(), event),
2424
+ load: () => {
2425
+ return this.onLoadEvent.asObservable();
2426
+ },
2427
+ };
2428
+ }
2429
+ getWidgetScope() {
2430
+ return {
2431
+ /** Resolved options for this renderer's widget instance (not the host / property-viewer target). */
2432
+ options: (path) => {
2433
+ const opts = this.instance && typeof this.instance.options === 'function'
2434
+ ? this.instance.options()
2435
+ : this.mergedOptions();
2436
+ if (path == null || path === '') {
2437
+ return opts;
2438
+ }
2439
+ return getSmart(opts, path);
2440
+ },
2441
+ call: (name, ...args) => {
2442
+ this.instance.call(name, ...args);
2443
+ },
2444
+ setValue: (value) => {
2445
+ this.instance.setValue(value);
2446
+ },
2447
+ clear: () => {
2448
+ this.instance.setValue(undefined);
2449
+ },
2450
+ refresh: () => {
2451
+ const refresh = this.instance?.['refresh'];
2452
+ if (refresh && typeof refresh === 'function') {
2453
+ refresh.bind(this.instance)();
2454
+ }
2455
+ else {
2456
+ this.updateOptionsBasedOnContext();
2457
+ this.applyOptions();
2458
+ }
2459
+ },
2460
+ output: (name) => {
2461
+ this.instance.output(name);
2462
+ },
2463
+ find: (id) => {
2464
+ return this.builderService.getWidget(id);
2465
+ },
2466
+ };
2467
+ }
2468
+ getFunctionScope() {
2469
+ const scope = {
2470
+ sum: (values) => {
2471
+ return sum(values);
2472
+ },
2473
+ translate: (text, options) => {
2474
+ return this.translateService.translateAsync(text, options);
2475
+ },
2476
+ widgetInfo: (name) => {
2477
+ return this.widgetService.resolve(name);
2478
+ },
2479
+ };
2480
+ // Add custom functions from builder service
2481
+ Object.entries(this.builderService.functions).forEach(([key, fn]) => {
2482
+ scope[key] = (...args) => {
2483
+ return fn(...args);
2484
+ };
2485
+ });
2486
+ return scope;
2487
+ }
2488
+ getVariablesScope() {
2489
+ return {
2490
+ eval: (path) => get(this.builderService.variables, path),
2491
+ };
2492
+ }
2493
+ async assignTriggers() {
2494
+ const node = this.node();
2495
+ // Check multiple possible locations for triggers
2496
+ const triggers = node.triggers || node.options?.['triggers'] || this.mergedOptions()?.triggers;
2497
+ if (!triggers) {
2498
+ return;
2499
+ }
2500
+ for (const trigger of triggers) {
2501
+ try {
2502
+ const event = await this.evaluateTrigger(trigger.event);
2503
+ if (event) {
2504
+ event.pipe(this.unsubscriber.takeUntilDestroy).subscribe(() => {
2505
+ const exec = async (action) => {
2506
+ if (!isEmpty(action) && isString(action)) {
2507
+ await this.evaluateAction(action);
2508
+ }
2509
+ };
2510
+ const actions = Array.isArray(trigger.action) ? trigger.action : [trigger.action];
2511
+ actions.forEach(async (a) => await exec(a));
2512
+ });
2513
+ }
2514
+ }
2515
+ catch (error) {
2516
+ // console.error('Error assigning trigger:', error);
2517
+ }
2518
+ }
2519
+ }
2520
+ async evaluateTrigger(templateExpression) {
2521
+ try {
2522
+ const scope = this.buildExpressionScope();
2523
+ const result = await this.expressionEvaluator.evaluate(templateExpression, scope);
2524
+ // Check if result is an Observable
2525
+ if (result && typeof result.subscribe === 'function') {
2526
+ return result;
2527
+ }
2528
+ else {
2529
+ return null;
2530
+ }
2531
+ }
2532
+ catch (error) {
2533
+ // console.error('Error evaluating trigger expression:', error);
2534
+ return null;
2535
+ }
2536
+ }
2537
+ async evaluateAction(templateExpression) {
2538
+ try {
2539
+ const scope = this.buildExpressionScope();
2540
+ await this.expressionEvaluator.evaluate(templateExpression, scope);
2541
+ }
2542
+ catch (error) {
2543
+ // console.error('Error evaluating action expression:', templateExpression, error);
2544
+ }
2545
+ }
2546
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetRendererDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2547
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.9", type: AXPWidgetRendererDirective, isStandalone: false, selector: "[axp-widget-renderer]", inputs: { parentNode: { classPropertyName: "parentNode", publicName: "parentNode", isSignal: true, isRequired: false, transformFunction: null }, index: { classPropertyName: "index", publicName: "index", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: true, transformFunction: null }, node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { onOptionsChanged: "onOptionsChanged", onValueChanged: "onValueChanged", onLoad: "onLoad" }, providers: [
2548
+ {
2549
+ provide: AXUnsubscriber,
2550
+ },
2551
+ ], exportAs: ["widgetRenderer"], usesOnChanges: true, ngImport: i0 }); }
2552
+ }
2553
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetRendererDirective, decorators: [{
2554
+ type: Directive,
2555
+ args: [{
2556
+ selector: '[axp-widget-renderer]',
2557
+ exportAs: 'widgetRenderer',
2558
+ providers: [
2559
+ {
2560
+ provide: AXUnsubscriber,
2561
+ },
2562
+ ],
2563
+ standalone: false,
2564
+ }]
2565
+ }], ctorParameters: () => [], propDecorators: { parentNode: [{ type: i0.Input, args: [{ isSignal: true, alias: "parentNode", required: false }] }], index: [{ type: i0.Input, args: [{ isSignal: true, alias: "index", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: true }] }], node: [{ type: i0.Input, args: [{ isSignal: true, alias: "node", required: true }] }], onOptionsChanged: [{ type: i0.Output, args: ["onOptionsChanged"] }], onValueChanged: [{ type: i0.Output, args: ["onValueChanged"] }], onLoad: [{ type: i0.Output, args: ["onLoad"] }] } });
2566
+
2567
+ const COMPONENTS = [AXPWidgetContainerComponent, AXPWidgetColumnRendererComponent, AXPWidgetRendererDirective];
2568
+ class AXPWidgetCoreModule {
2569
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetCoreModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
2570
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetCoreModule, declarations: [AXPWidgetContainerComponent, AXPWidgetColumnRendererComponent, AXPWidgetRendererDirective], imports: [CommonModule, PortalModule, AXSkeletonModule, AXTranslationModule], exports: [AXPWidgetContainerComponent, AXPWidgetColumnRendererComponent, AXPWidgetRendererDirective] }); }
2571
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetCoreModule, imports: [CommonModule, PortalModule, AXSkeletonModule, AXTranslationModule] }); }
2572
+ }
2573
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetCoreModule, decorators: [{
2574
+ type: NgModule,
2575
+ args: [{
2576
+ imports: [CommonModule, PortalModule, AXSkeletonModule, AXTranslationModule],
2577
+ exports: [...COMPONENTS],
2578
+ declarations: [...COMPONENTS],
2579
+ providers: [],
2580
+ }]
2581
+ }] });
2582
+
2583
+ class AXPPropertyEditorHelper {
2584
+ static expandShorthand(values) {
2585
+ switch (values.length) {
2586
+ case 1:
2587
+ return [values[0], values[0], values[0], values[0]];
2588
+ case 2:
2589
+ return [values[0], values[1], values[0], values[1]];
2590
+ case 3:
2591
+ return [values[0], values[1], values[2], values[1]];
2592
+ case 4:
2593
+ return values;
2594
+ default:
2595
+ throw new Error(`Invalid shorthand value count. Input: ${values}`);
2596
+ }
2597
+ }
2598
+ static condenseShorthand(values) {
2599
+ if (values.length !== 4) {
2600
+ throw new Error('Expected 4 values for condensation.');
2601
+ }
2602
+ if (values[0] === values[1] && values[1] === values[2] && values[2] === values[3]) {
2603
+ return `${values[0]}`;
2604
+ }
2605
+ else if (values[0] === values[2] && values[1] === values[3]) {
2606
+ return `${values[0]} ${values[1]}`;
2607
+ }
2608
+ else if (values[1] === values[3]) {
2609
+ return `${values[0]} ${values[1]} ${values[2]}`;
2610
+ }
2611
+ else {
2612
+ return `${values[0]} ${values[1]} ${values[2]} ${values[3]}`;
2613
+ }
2614
+ }
2615
+ static parseSides(input) {
2616
+ const values = this.expandShorthand(input.match(/(?:rgb\([^)]+\)|[^ ]+)/g)?.map((value) => value.trim()) || []);
2617
+ return { top: values[0], right: values[1], bottom: values[2], left: values[3] };
2618
+ }
2619
+ static parseSidesWithUnits(input) {
2620
+ const values = this.expandShorthand(input.match(/(?:rgb\([^)]+\)|[^ ]+)/g)?.map((value) => value.trim()) || []);
2621
+ return {
2622
+ top: AXPPropertyEditorHelper.getValueWithUnit(values[0]).value,
2623
+ right: AXPPropertyEditorHelper.getValueWithUnit(values[1]).value,
2624
+ bottom: AXPPropertyEditorHelper.getValueWithUnit(values[2]).value,
2625
+ left: AXPPropertyEditorHelper.getValueWithUnit(values[3]).value,
2626
+ };
2627
+ }
2628
+ static parseCorners(input) {
2629
+ const values = this.expandShorthand(input.split(' ').map((value) => value.trim()));
2630
+ return {
2631
+ 'top-left': AXPPropertyEditorHelper.getValueWithUnit(values[0]).value,
2632
+ 'top-right': AXPPropertyEditorHelper.getValueWithUnit(values[1]).value,
2633
+ 'bottom-left': AXPPropertyEditorHelper.getValueWithUnit(values[2]).value,
2634
+ 'bottom-right': AXPPropertyEditorHelper.getValueWithUnit(values[3]).value,
2635
+ };
2636
+ }
2637
+ static parseSpacingBox(input) {
2638
+ return {
2639
+ margin: this.parseSidesWithUnits(input.margin),
2640
+ padding: this.parseSidesWithUnits(input.padding),
2641
+ };
2642
+ }
2643
+ static parseBorderBox(input) {
2644
+ return {
2645
+ width: this.parseSidesWithUnits(input.width),
2646
+ radius: this.parseCorners(input.radius),
2647
+ color: this.parseSides(input.color),
2648
+ style: this.parseSides(input.style),
2649
+ };
2650
+ }
2651
+ static parseSpacingBoxReverse(input, units) {
2652
+ const format = (value, unit) => `${value}${unit}`;
2653
+ return {
2654
+ margin: AXPPropertyEditorHelper.condenseShorthand([
2655
+ format(input.margin.top, units.margin.top),
2656
+ format(input.margin.right, units.margin.right),
2657
+ format(input.margin.bottom, units.margin.bottom),
2658
+ format(input.margin.left, units.margin.left),
2659
+ ]),
2660
+ padding: AXPPropertyEditorHelper.condenseShorthand([
2661
+ format(input.padding.top, units.padding.top),
2662
+ format(input.padding.right, units.padding.right),
2663
+ format(input.padding.bottom, units.padding.bottom),
2664
+ format(input.padding.left, units.padding.left),
2665
+ ]),
2666
+ };
2667
+ }
2668
+ static parseBorderBoxReverse(input, units) {
2669
+ const format = (value, unit) => `${value}${unit}`;
2670
+ return {
2671
+ width: AXPPropertyEditorHelper.condenseShorthand([
2672
+ format(input.width.top, units.width.top),
2673
+ format(input.width.right, units.width.right),
2674
+ format(input.width.bottom, units.width.bottom),
2675
+ format(input.width.left, units.width.left),
2676
+ ]),
2677
+ radius: AXPPropertyEditorHelper.condenseShorthand([
2678
+ format(input.radius['top-left'], units.radius['top-left']),
2679
+ format(input.radius['top-right'], units.radius['top-right']),
2680
+ format(input.radius['bottom-right'], units.radius['bottom-right']),
2681
+ format(input.radius['bottom-left'], units.radius['bottom-left']),
2682
+ ]),
2683
+ color: AXPPropertyEditorHelper.condenseShorthand([
2684
+ `${input.color.top}${units.color.top}`,
2685
+ `${input.color.right}${units.color.right}`,
2686
+ `${input.color.bottom}${units.color.bottom}`,
2687
+ `${input.color.left}${units.color.left}`,
2688
+ ]),
2689
+ style: AXPPropertyEditorHelper.condenseShorthand([
2690
+ `${input.style.top}${units.style.top}`,
2691
+ `${input.style.right}${units.style.right}`,
2692
+ `${input.style.bottom}${units.style.bottom}`,
2693
+ `${input.style.left}${units.style.left}`,
2694
+ ]),
2695
+ };
2696
+ }
2697
+ static getValueWithUnit(input) {
2698
+ if (typeof input === 'number')
2699
+ return { value: input, unit: 'px' };
2700
+ if (input === 'auto')
2701
+ return { value: 0, unit: 'px' };
2702
+ const match = input.match(/^([0-9.]+)([a-z%]*)$/i);
2703
+ if (!match)
2704
+ throw new Error(`Invalid unit format: ${input}`);
2705
+ return { value: parseFloat(match[1]), unit: match[2] || '' };
2706
+ }
2707
+ static getValueFromUnit(value, unit) {
2708
+ return unit ? `${value}${unit}` : `${value}`;
2709
+ }
2710
+ static parseGap(gap) {
2711
+ const parts = gap.split(/\s+/);
2712
+ const match = parts[0].match(/^(\d+\.?\d*)([a-z%]+)$/);
2713
+ if (!match) {
2714
+ throw new Error('Invalid gap format');
2715
+ }
2716
+ const [, xValue, unit] = match;
2717
+ let yValue = parseFloat(xValue);
2718
+ if (parts.length === 2) {
2719
+ const secondMatch = parts[1].match(/^(\d+\.?\d*)[a-z%]+$/);
2720
+ if (!secondMatch) {
2721
+ throw new Error('Invalid gap format');
2722
+ }
2723
+ yValue = parseFloat(secondMatch[1]);
2724
+ }
2725
+ return {
2726
+ values: {
2727
+ x: parseFloat(xValue),
2728
+ y: yValue,
2729
+ },
2730
+ unit,
2731
+ };
2732
+ }
2733
+ static parseGridTemplate(gridTemplate) {
2734
+ const match = gridTemplate.match(/^repeat\((\d+),\s*(?:1fr|auto|minmax\([^)]*\))\)$/);
2735
+ if (!match) {
2736
+ throw new Error("Invalid grid template format. Expected 'repeat(N, 1fr|auto|minmax(...))'.");
2737
+ }
2738
+ return parseInt(match[1], 10);
2739
+ }
2740
+ static createGridTemplate(repetitionCount) {
2741
+ if (repetitionCount <= 0) {
2742
+ throw new Error('Repetition count must be a positive integer.');
2743
+ }
2744
+ return `repeat(${repetitionCount}, 1fr)`;
2745
+ }
2746
+ }
2747
+ function findNonEmptyBreakpoints(values) {
2748
+ const breakpoints = ['default', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'];
2749
+ const nonEmptyBreakpoints = [];
2750
+ for (const breakpoint of breakpoints) {
2751
+ if (values[breakpoint] !== undefined) {
2752
+ nonEmptyBreakpoints.push(breakpoint);
2753
+ }
2754
+ }
2755
+ return nonEmptyBreakpoints;
2756
+ }
2757
+
2758
+ //#region ---- Imports ----
2759
+ //#endregion
2760
+ //#region ---- Helper Class ----
2761
+ /**
2762
+ * Helper class for serializing and deserializing AXPWidgetNode to/from JSON
2763
+ */
2764
+ class AXPWidgetSerializationHelper {
2765
+ /**
2766
+ * Removes non-serializable properties from widget node
2767
+ * @param node - The widget node to clean
2768
+ * @param options - Serialization options
2769
+ * @returns Cleaned widget node ready for JSON serialization
2770
+ */
2771
+ static cleanNode(node, options = {}) {
2772
+ const { removeMeta = true, removeFunctions = true } = options;
2773
+ // Deep clone to avoid mutating original
2774
+ const cleaned = cloneDeep(node);
2775
+ // Remove meta property if requested
2776
+ if (removeMeta && 'meta' in cleaned) {
2777
+ delete cleaned.meta;
2778
+ }
2779
+ // Remove functions from options, triggers, and valueTransforms
2780
+ if (removeFunctions) {
2781
+ this.removeFunctions(cleaned);
2782
+ }
2783
+ // Recursively clean children
2784
+ if (cleaned.children && Array.isArray(cleaned.children)) {
2785
+ cleaned.children = cleaned.children.map((child) => this.cleanNode(child, options));
2786
+ }
2787
+ return cleaned;
2788
+ }
2789
+ /**
2790
+ * Recursively removes functions from object properties
2791
+ */
2792
+ static removeFunctions(obj) {
2793
+ if (obj === null || obj === undefined) {
2794
+ return;
2795
+ }
2796
+ if (Array.isArray(obj)) {
2797
+ obj.forEach((item) => this.removeFunctions(item));
2798
+ return;
2799
+ }
2800
+ if (typeof obj === 'object') {
2801
+ for (const key in obj) {
2802
+ if (typeof obj[key] === 'function') {
2803
+ delete obj[key];
2804
+ }
2805
+ else if (typeof obj[key] === 'object') {
2806
+ this.removeFunctions(obj[key]);
2807
+ }
2808
+ }
2809
+ }
2810
+ }
2811
+ /**
2812
+ * Converts AXPWidgetNode to JSON string
2813
+ * @param node - The widget node to serialize
2814
+ * @param options - Serialization options
2815
+ * @returns JSON string representation of the widget node
2816
+ */
2817
+ static toJson(node, options = {}) {
2818
+ const { pretty = false, ...cleanOptions } = options;
2819
+ const cleaned = this.cleanNode(node, cleanOptions);
2820
+ if (pretty) {
2821
+ return JSON.stringify(cleaned, null, 2);
2822
+ }
2823
+ return JSON.stringify(cleaned);
2824
+ }
2825
+ /**
2826
+ * Converts JSON string to AXPWidgetNode
2827
+ * @param json - JSON string to deserialize
2828
+ * @returns Parsed AXPWidgetNode object
2829
+ * @throws Error if JSON is invalid or doesn't match AXPWidgetNode structure
2830
+ */
2831
+ static fromJson(json) {
2832
+ try {
2833
+ const parsed = JSON.parse(json);
2834
+ // Basic validation - ensure it has at least a type property
2835
+ if (!parsed || typeof parsed !== 'object') {
2836
+ throw new Error('Invalid JSON: Expected an object');
2837
+ }
2838
+ if (!parsed.type) {
2839
+ throw new Error('Invalid AXPWidgetNode: Missing required property "type"');
2840
+ }
2841
+ // Recursively validate and parse children if they exist
2842
+ if (parsed.children && Array.isArray(parsed.children)) {
2843
+ parsed.children = parsed.children.map((child) => this.fromJson(JSON.stringify(child)));
2844
+ }
2845
+ return parsed;
2846
+ }
2847
+ catch (error) {
2848
+ if (error instanceof SyntaxError) {
2849
+ throw new Error(`Invalid JSON string: ${error.message}`);
2850
+ }
2851
+ throw error;
2852
+ }
2853
+ }
2854
+ /**
2855
+ * Removes meta property from widget node (similar to designer service)
2856
+ * @param node - The widget node to process
2857
+ * @returns Widget node without meta property
2858
+ */
2859
+ static removeMeta(node) {
2860
+ return this.cleanNode(node, { removeMeta: true, removeFunctions: false });
2861
+ }
2862
+ }
2863
+ //#endregion
2864
+
2865
+ /** Normalizes widget categories to an array for iteration and serialization. */
2866
+ function normalizeWidgetCategories(categories) {
2867
+ if (categories == null) {
2868
+ return [];
2869
+ }
2870
+ return Array.isArray(categories) ? categories : [categories];
2871
+ }
2872
+ const AXP_WIDGETS_LAYOUT_CATEGORY = {
2873
+ name: 'layout',
2874
+ order: 1,
2875
+ title: 'Layout',
2876
+ };
2877
+ const AXP_WIDGETS_EDITOR_CATEGORY = {
2878
+ name: 'editor',
2879
+ order: 2,
2880
+ title: 'Editors',
2881
+ };
2882
+ const AXP_WIDGETS_ACTION_CATEGORY = {
2883
+ name: 'action',
2884
+ order: 3,
2885
+ title: 'Action',
2886
+ };
2887
+ const AXP_WIDGETS_ADVANCE_CATEGORY = {
2888
+ name: 'advance',
2889
+ order: 4,
2890
+ title: 'Advance',
2891
+ };
2892
+ /** Widgets exposed to AI assistants for structured rendering (see Widgets:GetForAI). */
2893
+ const AXP_WIDGETS_AI_CATEGORY = {
2894
+ name: 'ai',
2895
+ order: 5,
2896
+ title: 'AI',
2897
+ };
2898
+ const AXP_WIDGETS_CATEGORIES = [
2899
+ AXP_WIDGETS_LAYOUT_CATEGORY,
2900
+ AXP_WIDGETS_EDITOR_CATEGORY,
2901
+ AXP_WIDGETS_ACTION_CATEGORY,
2902
+ AXP_WIDGETS_ADVANCE_CATEGORY,
2903
+ AXP_WIDGETS_AI_CATEGORY,
2904
+ ];
2905
+
2906
+ var AXPWidgetGroupEnum;
2907
+ (function (AXPWidgetGroupEnum) {
2908
+ AXPWidgetGroupEnum["FormElement"] = "form-element";
2909
+ AXPWidgetGroupEnum["DashboardWidget"] = "dashboard-widget";
2910
+ AXPWidgetGroupEnum["FormTemplate"] = "form-template";
2911
+ AXPWidgetGroupEnum["PropertyEditor"] = "property-editor";
2912
+ AXPWidgetGroupEnum["MetaData"] = "meta-data";
2913
+ AXPWidgetGroupEnum["SettingWidget"] = "setting-widget";
2914
+ AXPWidgetGroupEnum["EntityWidget"] = "entity-widget";
2915
+ AXPWidgetGroupEnum["UtilityWidget"] = "utility-widget";
2916
+ })(AXPWidgetGroupEnum || (AXPWidgetGroupEnum = {}));
2917
+
2918
+ //#region ---- Helpers ----
2919
+ /**
2920
+ * True when the widget declares the AI category (see {@link AXP_WIDGETS_AI_CATEGORY}).
2921
+ */
2922
+ function hasAiWidgetCategory(config) {
2923
+ return normalizeWidgetCategories(config.categories).some((c) => c.name === AXP_WIDGETS_AI_CATEGORY.name);
2924
+ }
2925
+ //#endregion
2926
+ //#region ---- Public API ----
2927
+ /**
2928
+ * Resolves AI catalog visibility using {@link AXPWidgetConfig.aiCatalog} and the AI widget category.
2929
+ * - `exclude`: never listed.
2930
+ * - `include`: always listed (override when the widget cannot declare the AI category yet).
2931
+ * - `inherit` / omitted: listed only if {@link hasAiWidgetCategory} is true.
2932
+ */
2933
+ function isWidgetAiCatalogIncluded(config) {
2934
+ const mode = config.aiCatalog ?? 'inherit';
2935
+ if (mode === 'exclude') {
2936
+ return false;
2937
+ }
2938
+ if (mode === 'include') {
2939
+ return true;
2940
+ }
2941
+ return hasAiWidgetCategory(config);
2942
+ }
2943
+ //#endregion
2944
+
2945
+ /**
2946
+ * Generated bundle index. Do not edit.
2947
+ */
2948
+
2949
+ export { AXPBaseWidgetComponent, AXPBlockBaseLayoutWidgetComponent, AXPBoxModelLayoutWidgetComponent, AXPColumnWidgetComponent, AXPDataListWidgetComponent, AXPFlexBaseLayoutWidgetComponent, AXPFlexItemBaseLayoutWidgetComponent, AXPGridBaseLayoutWidgetComponent, AXPGridItemBaseLayoutWidgetComponent, AXPInlineBaseLayoutWidgetComponent, AXPLayoutBaseWidgetComponent, AXPPageStatus, AXPPropertyEditorHelper, AXPTableBaseLayoutWidgetComponent, AXPTableItemBaseLayoutWidgetComponent, AXPTableItemOpsBaseLayoutWidgetComponent, AXPValueWidgetComponent, AXPWidgetColumnRendererComponent, AXPWidgetContainerComponent, AXPWidgetCoreElement, AXPWidgetCoreModule, AXPWidgetCoreService, AXPWidgetGroupEnum, AXPWidgetRegistryService, AXPWidgetRendererDirective, AXPWidgetSerializationHelper, AXPWidgetStatus, AXPWidgetsCatalog, AXP_WIDGETS_ACTION_CATEGORY, AXP_WIDGETS_ADVANCE_CATEGORY, AXP_WIDGETS_AI_CATEGORY, AXP_WIDGETS_CATEGORIES, AXP_WIDGETS_EDITOR_CATEGORY, AXP_WIDGETS_LAYOUT_CATEGORY, AXP_WIDGET_COLUMN_TOKEN, AXP_WIDGET_DEFINITION_PROVIDER, AXP_WIDGET_TOKEN, buildWidgetRegistryMapFromProviders, cloneProperty, createBooleanProperty, createNumberProperty, createSelectProperty, createStringProperty, findNonEmptyBreakpoints, hasAiWidgetCategory, isWidgetAiCatalogIncluded, normalizeWidgetCategories };
2950
+ //# sourceMappingURL=acorex-platform-layout-widget-core.mjs.map