@acorex-platform/framework-client 0.0.10

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 (112) 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-framework-client-auth.mjs +1383 -0
  6. package/fesm2022/acorex-platform-framework-client-auth.mjs.map +1 -0
  7. package/fesm2022/acorex-platform-framework-client-common-common-settings.provider-CbzOYCnI.mjs +190 -0
  8. package/fesm2022/acorex-platform-framework-client-common-common-settings.provider-CbzOYCnI.mjs.map +1 -0
  9. package/fesm2022/acorex-platform-framework-client-common.mjs +9467 -0
  10. package/fesm2022/acorex-platform-framework-client-common.mjs.map +1 -0
  11. package/fesm2022/acorex-platform-framework-client-core.mjs +4655 -0
  12. package/fesm2022/acorex-platform-framework-client-core.mjs.map +1 -0
  13. package/fesm2022/acorex-platform-framework-client-domain.mjs +3189 -0
  14. package/fesm2022/acorex-platform-framework-client-domain.mjs.map +1 -0
  15. package/fesm2022/acorex-platform-framework-client-layout-builder.mjs +3439 -0
  16. package/fesm2022/acorex-platform-framework-client-layout-builder.mjs.map +1 -0
  17. package/fesm2022/acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-CWVU3wXL.mjs +134 -0
  18. package/fesm2022/acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-CWVU3wXL.mjs.map +1 -0
  19. package/fesm2022/acorex-platform-framework-client-layout-components.mjs +9777 -0
  20. package/fesm2022/acorex-platform-framework-client-layout-components.mjs.map +1 -0
  21. package/fesm2022/acorex-platform-framework-client-layout-designer.mjs +2799 -0
  22. package/fesm2022/acorex-platform-framework-client-layout-designer.mjs.map +1 -0
  23. package/fesm2022/acorex-platform-framework-client-layout-entity-attachments-page.component-CxBxxpmA.mjs +376 -0
  24. package/fesm2022/acorex-platform-framework-client-layout-entity-attachments-page.component-CxBxxpmA.mjs.map +1 -0
  25. package/fesm2022/acorex-platform-framework-client-layout-entity-file-list-popup.component-l0JAHKIH.mjs +100 -0
  26. package/fesm2022/acorex-platform-framework-client-layout-entity-file-list-popup.component-l0JAHKIH.mjs.map +1 -0
  27. package/fesm2022/acorex-platform-framework-client-layout-entity.mjs +26299 -0
  28. package/fesm2022/acorex-platform-framework-client-layout-entity.mjs.map +1 -0
  29. package/fesm2022/acorex-platform-framework-client-layout-views.mjs +2960 -0
  30. package/fesm2022/acorex-platform-framework-client-layout-views.mjs.map +1 -0
  31. package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs +4711 -0
  32. package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs.map +1 -0
  33. package/fesm2022/acorex-platform-framework-client-layout-widgets-button-widget-designer.component-CpkWwygb.mjs +72 -0
  34. package/fesm2022/acorex-platform-framework-client-layout-widgets-button-widget-designer.component-CpkWwygb.mjs.map +1 -0
  35. package/fesm2022/acorex-platform-framework-client-layout-widgets-image-preview.popup-C_EPAvCU.mjs +29 -0
  36. package/fesm2022/acorex-platform-framework-client-layout-widgets-image-preview.popup-C_EPAvCU.mjs.map +1 -0
  37. package/fesm2022/acorex-platform-framework-client-layout-widgets-page-widget-designer.component-CmtTq2Mt.mjs +172 -0
  38. package/fesm2022/acorex-platform-framework-client-layout-widgets-page-widget-designer.component-CmtTq2Mt.mjs.map +1 -0
  39. package/fesm2022/acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-DSx0E3NV.mjs +513 -0
  40. package/fesm2022/acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-DSx0E3NV.mjs.map +1 -0
  41. package/fesm2022/acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-DdfcGV1C.mjs +274 -0
  42. package/fesm2022/acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-DdfcGV1C.mjs.map +1 -0
  43. package/fesm2022/acorex-platform-framework-client-layout-widgets-tabular-data-view-popup.component-CntopmDS.mjs +64 -0
  44. package/fesm2022/acorex-platform-framework-client-layout-widgets-tabular-data-view-popup.component-CntopmDS.mjs.map +1 -0
  45. package/fesm2022/acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-B5kSWxGk.mjs +34 -0
  46. package/fesm2022/acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-B5kSWxGk.mjs.map +1 -0
  47. package/fesm2022/acorex-platform-framework-client-layout-widgets.mjs +28553 -0
  48. package/fesm2022/acorex-platform-framework-client-layout-widgets.mjs.map +1 -0
  49. package/fesm2022/acorex-platform-framework-client-native.mjs +155 -0
  50. package/fesm2022/acorex-platform-framework-client-native.mjs.map +1 -0
  51. package/fesm2022/acorex-platform-framework-client-runtime.mjs +911 -0
  52. package/fesm2022/acorex-platform-framework-client-runtime.mjs.map +1 -0
  53. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-create-view.component-DnTHwsDJ.mjs +160 -0
  54. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-create-view.component-DnTHwsDJ.mjs.map +1 -0
  55. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-modify-view.component-BpR9g6QS.mjs +121 -0
  56. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-modify-view.component-BpR9g6QS.mjs.map +1 -0
  57. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-single-view.component-D1a7UI71.mjs +236 -0
  58. package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-single-view.component-D1a7UI71.mjs.map +1 -0
  59. package/fesm2022/acorex-platform-framework-client-themes-default-error-401.component-D--_MIar.mjs +48 -0
  60. package/fesm2022/acorex-platform-framework-client-themes-default-error-401.component-D--_MIar.mjs.map +1 -0
  61. package/fesm2022/acorex-platform-framework-client-themes-default-error-404.component-Byyqk8pH.mjs +42 -0
  62. package/fesm2022/acorex-platform-framework-client-themes-default-error-404.component-Byyqk8pH.mjs.map +1 -0
  63. package/fesm2022/acorex-platform-framework-client-themes-default-error-offline.component-DR6G8gPC.mjs +19 -0
  64. package/fesm2022/acorex-platform-framework-client-themes-default-error-offline.component-DR6G8gPC.mjs.map +1 -0
  65. package/fesm2022/acorex-platform-framework-client-themes-default.mjs +4160 -0
  66. package/fesm2022/acorex-platform-framework-client-themes-default.mjs.map +1 -0
  67. package/fesm2022/acorex-platform-framework-client-themes-shared-icon-chooser-column.component-CXSGiTCK.mjs +55 -0
  68. package/fesm2022/acorex-platform-framework-client-themes-shared-icon-chooser-column.component-CXSGiTCK.mjs.map +1 -0
  69. package/fesm2022/acorex-platform-framework-client-themes-shared-icon-chooser-view.component-BY5clQ1J.mjs +57 -0
  70. package/fesm2022/acorex-platform-framework-client-themes-shared-icon-chooser-view.component-BY5clQ1J.mjs.map +1 -0
  71. package/fesm2022/acorex-platform-framework-client-themes-shared-settings.provider-DEL2YOTP.mjs +234 -0
  72. package/fesm2022/acorex-platform-framework-client-themes-shared-settings.provider-DEL2YOTP.mjs.map +1 -0
  73. package/fesm2022/acorex-platform-framework-client-themes-shared-theme-color-chooser-column.component-BXl8u4Yp.mjs +94 -0
  74. package/fesm2022/acorex-platform-framework-client-themes-shared-theme-color-chooser-column.component-BXl8u4Yp.mjs.map +1 -0
  75. package/fesm2022/acorex-platform-framework-client-themes-shared-theme-color-chooser-view.component-i2sOOkVj.mjs +86 -0
  76. package/fesm2022/acorex-platform-framework-client-themes-shared-theme-color-chooser-view.component-i2sOOkVj.mjs.map +1 -0
  77. package/fesm2022/acorex-platform-framework-client-themes-shared.mjs +2375 -0
  78. package/fesm2022/acorex-platform-framework-client-themes-shared.mjs.map +1 -0
  79. package/fesm2022/acorex-platform-framework-client-workflow.mjs +2836 -0
  80. package/fesm2022/acorex-platform-framework-client-workflow.mjs.map +1 -0
  81. package/fesm2022/acorex-platform-framework-client.mjs +6 -0
  82. package/fesm2022/acorex-platform-framework-client.mjs.map +1 -0
  83. package/layout/builder/README.md +1578 -0
  84. package/layout/components/README.md +3 -0
  85. package/layout/designer/README.md +4 -0
  86. package/layout/entity/README.md +4 -0
  87. package/layout/views/README.md +3 -0
  88. package/layout/widget-core/README.md +4 -0
  89. package/layout/widgets/README.md +3 -0
  90. package/native/README.md +4 -0
  91. package/package.json +98 -0
  92. package/runtime/README.md +3 -0
  93. package/themes/default/README.md +3 -0
  94. package/themes/shared/README.md +3 -0
  95. package/types/acorex-platform-framework-client-auth.d.ts +690 -0
  96. package/types/acorex-platform-framework-client-common.d.ts +3287 -0
  97. package/types/acorex-platform-framework-client-core.d.ts +1849 -0
  98. package/types/acorex-platform-framework-client-domain.d.ts +1968 -0
  99. package/types/acorex-platform-framework-client-layout-builder.d.ts +1058 -0
  100. package/types/acorex-platform-framework-client-layout-components.d.ts +3362 -0
  101. package/types/acorex-platform-framework-client-layout-designer.d.ts +501 -0
  102. package/types/acorex-platform-framework-client-layout-entity.d.ts +4374 -0
  103. package/types/acorex-platform-framework-client-layout-views.d.ts +975 -0
  104. package/types/acorex-platform-framework-client-layout-widget-core.d.ts +1080 -0
  105. package/types/acorex-platform-framework-client-layout-widgets.d.ts +5263 -0
  106. package/types/acorex-platform-framework-client-native.d.ts +28 -0
  107. package/types/acorex-platform-framework-client-runtime.d.ts +301 -0
  108. package/types/acorex-platform-framework-client-themes-default.d.ts +889 -0
  109. package/types/acorex-platform-framework-client-themes-shared.d.ts +202 -0
  110. package/types/acorex-platform-framework-client-workflow.d.ts +1345 -0
  111. package/types/acorex-platform-framework-client.d.ts +2 -0
  112. package/workflow/README.md +4 -0
@@ -0,0 +1,4711 @@
1
+ import * as i0 from '@angular/core';
2
+ import { signal, computed, effect, untracked, Injectable, inject, ElementRef, DestroyRef, InjectionToken, EventEmitter, Injector, ChangeDetectorRef, ViewChild, Input, Output, ChangeDetectionStrategy, Component, input, output, ViewContainerRef, Directive, NgModule } from '@angular/core';
3
+ import { isSelectionValueEqual, isFormValueEqual, extractValue, getSmart, AXPWidgetsCatalog } from '@acorex-platform/framework-shared/core';
4
+ import { AXPPageStatus, AXPWidgetStatus, AXP_WIDGET_TOKEN, AXP_WIDGET_COLUMN_TOKEN, createStringProperty, createBooleanProperty, createSelectProperty, createNumberProperty, cloneProperty } from '@acorex-platform/framework-shared/widget-core';
5
+ import { convertArrayToDataSource, AXDataSource, AX_STYLE_COLOR_TYPES, AX_STYLE_LOOK_TYPES } from '@acorex/cdk/common';
6
+ import { AXPContextStore, AXPDataSourceDefinitionProviderService, AXPExpressionEvaluatorService } from '@acorex-platform/framework-client/core';
7
+ import { set, get, cloneDeep, isEqual, isNil, isUndefined, isObjectLike, merge, sum, isEmpty, isString } from 'lodash-es';
8
+ import { Subject, BehaviorSubject, filter } from 'rxjs';
9
+ import * as i1$1 from '@acorex/components/skeleton';
10
+ import { AXSkeletonModule } from '@acorex/components/skeleton';
11
+ import * as i2 from '@acorex/core/translation';
12
+ import { AXTranslationService, AXTranslationModule } from '@acorex/core/translation';
13
+ import { PortalModule } from '@angular/cdk/portal';
14
+ import * as i1 from '@angular/common';
15
+ import { CommonModule, NgComponentOutlet } from '@angular/common';
16
+ import { AXDataTableColumnComponent, AXBaseDataTable } from '@acorex/components/data-table';
17
+ import { AXUnsubscriber } from '@acorex/core/utils';
18
+
19
+ class AXPWidgetCoreElement {
20
+ api() {
21
+ return {};
22
+ }
23
+ actions() {
24
+ return [];
25
+ }
26
+ }
27
+ class AXPWidgetCoreService {
28
+ constructor() {
29
+ this.variables$ = signal({}, ...(ngDevMode ? [{ debugName: "variables$" }] : /* istanbul ignore next */ []));
30
+ this.functions$ = signal({}, ...(ngDevMode ? [{ debugName: "functions$" }] : /* istanbul ignore next */ []));
31
+ this.onRefresh = new Subject();
32
+ this.widgets = new Map();
33
+ this.onWidgetRegistered = new Subject();
34
+ /** Fires whenever a widget registers, including re-registration after remount (same id, new instance). */
35
+ this.widgetRegistered$ = this.onWidgetRegistered.asObservable();
36
+ /** Bumps when widget render/hydration state changes so aggregate computeds re-evaluate. */
37
+ this.widgetsRevision = signal(0, ...(ngDevMode ? [{ debugName: "widgetsRevision" }] : /* istanbul ignore next */ []));
38
+ this.status$ = signal(AXPPageStatus.Rendered, ...(ngDevMode ? [{ debugName: "status$" }] : /* istanbul ignore next */ []));
39
+ this.status = this.status$.asReadonly();
40
+ this.registeredWidgetsCount = signal(0, ...(ngDevMode ? [{ debugName: "registeredWidgetsCount" }] : /* istanbul ignore next */ []));
41
+ this.isRendering = computed(() => {
42
+ this.widgetsRevision();
43
+ return Array.from(this.widgets.values()).some((widget) => widget.status() === AXPWidgetStatus.Rendering);
44
+ }, ...(ngDevMode ? [{ debugName: "isRendering" }] : /* istanbul ignore next */ []));
45
+ this.isHydrating = computed(() => {
46
+ this.widgetsRevision();
47
+ return Array.from(this.widgets.values()).some((widget) => widget.isHydrating());
48
+ }, ...(ngDevMode ? [{ debugName: "isHydrating" }] : /* istanbul ignore next */ []));
49
+ this.isBusy = computed(() => this.isRendering() || this.isHydrating(), ...(ngDevMode ? [{ debugName: "isBusy" }] : /* istanbul ignore next */ []));
50
+ this.isStructureReady = computed(() => {
51
+ this.widgetsRevision();
52
+ const widgets = Array.from(this.widgets.values());
53
+ if (!widgets.length) {
54
+ return false;
55
+ }
56
+ return widgets.every((widget) => widget.status() === AXPWidgetStatus.Rendered);
57
+ }, ...(ngDevMode ? [{ debugName: "isStructureReady" }] : /* istanbul ignore next */ []));
58
+ this.dirtyWidgetsRevision = signal(0, ...(ngDevMode ? [{ debugName: "dirtyWidgetsRevision" }] : /* istanbul ignore next */ []));
59
+ this.dirtyWidgetsRevisionSignal = this.dirtyWidgetsRevision.asReadonly();
60
+ effect(() => {
61
+ this.widgetsRevision();
62
+ this.registeredWidgetsCount();
63
+ untracked(() => {
64
+ this.status$.set(this.detectAggregateStatus());
65
+ });
66
+ });
67
+ }
68
+ get variables() {
69
+ return this.variables$();
70
+ }
71
+ get functions() {
72
+ return this.functions$();
73
+ }
74
+ /** Recomputes aggregate container state after a widget registers or changes status/hydration. */
75
+ notifyWidgetStateChanged() {
76
+ this.registeredWidgetsCount.set(this.widgets.size);
77
+ this.widgetsRevision.update((value) => value + 1);
78
+ }
79
+ /** @deprecated Prefer {@link notifyWidgetStateChanged}. */
80
+ updateStatus() {
81
+ this.notifyWidgetStateChanged();
82
+ }
83
+ /** Debug snapshot of registered widget render/hydration state. */
84
+ getWidgetStateSnapshot() {
85
+ const widgets = Array.from(this.widgets.entries()).map(([id, widget]) => ({
86
+ id,
87
+ status: widget.status(),
88
+ isHydrating: widget.isHydrating(),
89
+ }));
90
+ return {
91
+ count: widgets.length,
92
+ isRendering: this.isRendering(),
93
+ isHydrating: this.isHydrating(),
94
+ isStructureReady: this.isStructureReady(),
95
+ isBusy: this.isBusy(),
96
+ widgets,
97
+ };
98
+ }
99
+ detectAggregateStatus() {
100
+ const widgets = Array.from(this.widgets.values());
101
+ if (!widgets.length) {
102
+ return AXPPageStatus.Rendered;
103
+ }
104
+ const statuses = widgets.map((widget) => widget.status());
105
+ if (statuses.some((status) => status === AXPWidgetStatus.Rendering)) {
106
+ return AXPPageStatus.Rendering;
107
+ }
108
+ if (statuses.some((status) => status === AXPWidgetStatus.Error)) {
109
+ return AXPPageStatus.Error;
110
+ }
111
+ if (widgets.some((widget) => widget.isHydrating())) {
112
+ return AXPPageStatus.Rendering;
113
+ }
114
+ if (statuses.every((status) => status === AXPWidgetStatus.Rendered)) {
115
+ return AXPPageStatus.Rendered;
116
+ }
117
+ return AXPPageStatus.Rendered;
118
+ }
119
+ refresh() {
120
+ setTimeout(() => {
121
+ this.onRefresh.next();
122
+ }, 0);
123
+ }
124
+ setStatus(status) {
125
+ this.status$.set(status);
126
+ }
127
+ setVariables(...args) {
128
+ if (args.length == 0)
129
+ return;
130
+ else if (args.length == 1)
131
+ this.variables$.set(args[0]);
132
+ else if (args.length == 2) {
133
+ this.variables$.update((v) => set(v, args[0], args[1]));
134
+ }
135
+ }
136
+ setFunctions(...args) {
137
+ if (args.length == 0)
138
+ return;
139
+ else if (args.length == 1)
140
+ this.functions$.set(args[0]);
141
+ else if (args.length == 2) {
142
+ this.functions$.update((v) => set(v, args[0], args[1]));
143
+ }
144
+ }
145
+ registerWidget(id, widget) {
146
+ this.widgets.set(id, widget);
147
+ this.onWidgetRegistered.next({ id, widget });
148
+ this.notifyWidgetStateChanged();
149
+ }
150
+ unregisterWidget(id) {
151
+ if (this.widgets.delete(id)) {
152
+ this.notifyWidgetStateChanged();
153
+ }
154
+ }
155
+ clearWidgets() {
156
+ if (this.widgets.size === 0) {
157
+ return;
158
+ }
159
+ this.widgets.clear();
160
+ this.notifyWidgetStateChanged();
161
+ }
162
+ getWidget(id) {
163
+ return this.widgets.get(id);
164
+ }
165
+ async waitForWidget(id, timeoutMs) {
166
+ const existing = this.widgets.get(id);
167
+ if (existing) {
168
+ return existing;
169
+ }
170
+ return new Promise((resolve) => {
171
+ let resolved = false;
172
+ let timer = null;
173
+ const sub = this.onWidgetRegistered.subscribe(({ id: registeredId, widget }) => {
174
+ if (registeredId === id && !resolved) {
175
+ resolved = true;
176
+ sub.unsubscribe();
177
+ if (timer) {
178
+ clearTimeout(timer);
179
+ }
180
+ resolve(widget);
181
+ }
182
+ });
183
+ if (timeoutMs != null && timeoutMs > 0) {
184
+ timer = setTimeout(() => {
185
+ if (!resolved) {
186
+ resolved = true;
187
+ sub.unsubscribe();
188
+ resolve(undefined);
189
+ }
190
+ }, timeoutMs);
191
+ }
192
+ });
193
+ }
194
+ listRegisteredWidgetNames() {
195
+ return Array.from(this.widgets.keys());
196
+ }
197
+ notifyWidgetDirtyChanged() {
198
+ this.dirtyWidgetsRevision.update((value) => value + 1);
199
+ }
200
+ ngOnDestroy() { }
201
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetCoreService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
202
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetCoreService }); }
203
+ }
204
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetCoreService, decorators: [{
205
+ type: Injectable
206
+ }], ctorParameters: () => [] });
207
+
208
+ class AXPBaseWidgetComponent extends AXPWidgetCoreElement {
209
+ constructor() {
210
+ super(...arguments);
211
+ this.token = inject(AXP_WIDGET_TOKEN);
212
+ this.host = inject(ElementRef).nativeElement;
213
+ this.layoutService = inject(AXPWidgetCoreService);
214
+ this.contextService = inject(AXPContextStore);
215
+ /** Unregisters this widget from the container registry when the instance is destroyed. */
216
+ this.#unregisterFromLayoutOnDestroy = inject(DestroyRef).onDestroy(() => {
217
+ const registrationId = this._id ?? this.name;
218
+ if (registrationId) {
219
+ this.layoutService.unregisterWidget(registrationId);
220
+ }
221
+ if (this.hydrationDepth > 0) {
222
+ this.hydrationDepth = 0;
223
+ this._isHydrating.set(false);
224
+ this.layoutService.notifyWidgetStateChanged();
225
+ }
226
+ });
227
+ this.config = this.token.config;
228
+ this.node = this.token.node;
229
+ this.name = this.token.node.name;
230
+ this.component = this;
231
+ this._options = signal(this.token.options ?? {}, ...(ngDevMode ? [{ debugName: "_options" }] : /* istanbul ignore next */ []));
232
+ this.options = this._options.asReadonly();
233
+ this.onOptionsChanged = new Subject();
234
+ this._status = signal(AXPWidgetStatus.Rendering, ...(ngDevMode ? [{ debugName: "_status" }] : /* istanbul ignore next */ []));
235
+ this.status = this._status.asReadonly();
236
+ this.onStatusChanged = new BehaviorSubject(this._status());
237
+ /** Depth counter so nested async hydration calls share one busy state. */
238
+ this.hydrationDepth = 0;
239
+ this._isHydrating = signal(false, ...(ngDevMode ? [{ debugName: "_isHydrating" }] : /* istanbul ignore next */ []));
240
+ this.isHydrating = this._isHydrating.asReadonly();
241
+ this.#statusEffect = effect(() => {
242
+ this.onStatusChanged.next(this.status());
243
+ }, ...(ngDevMode ? [{ debugName: "#statusEffect" }] : /* istanbul ignore next */ []));
244
+ this.isBusy = computed(() => this.status() === AXPWidgetStatus.Rendering || this.isHydrating(), ...(ngDevMode ? [{ debugName: "isBusy" }] : /* istanbul ignore next */ []));
245
+ this._children = signal(this.token.node.children ?? [], ...(ngDevMode ? [{ debugName: "_children" }] : /* istanbul ignore next */ []));
246
+ this.children = this._children.asReadonly();
247
+ }
248
+ /** Unregisters this widget from the container registry when the instance is destroyed. */
249
+ #unregisterFromLayoutOnDestroy;
250
+ get id() {
251
+ return this._id;
252
+ }
253
+ #statusEffect;
254
+ outputs() {
255
+ return [];
256
+ }
257
+ ngOnInit() {
258
+ if (get(this.node, '__meta__.added')) {
259
+ this.onAdded();
260
+ }
261
+ this.setStatus(AXPWidgetStatus.Rendered);
262
+ }
263
+ setStatus(status) {
264
+ this._status.set(status);
265
+ this.layoutService.notifyWidgetStateChanged();
266
+ }
267
+ /** Marks async data loading (lookup, select, file fetch, etc.). */
268
+ beginHydration() {
269
+ this.hydrationDepth += 1;
270
+ if (this.hydrationDepth === 1) {
271
+ this._isHydrating.set(true);
272
+ this.layoutService.notifyWidgetStateChanged();
273
+ }
274
+ }
275
+ /** Clears hydration started by {@link beginHydration}. */
276
+ endHydration() {
277
+ if (this.hydrationDepth <= 0) {
278
+ return;
279
+ }
280
+ this.hydrationDepth -= 1;
281
+ if (this.hydrationDepth === 0) {
282
+ this._isHydrating.set(false);
283
+ this.layoutService.notifyWidgetStateChanged();
284
+ }
285
+ }
286
+ setOptions(values) {
287
+ const oldValue = this.options();
288
+ const value = cloneDeep(values);
289
+ this._options.set({ ...oldValue, ...value });
290
+ this.onOptionsChanged.next({ sender: this });
291
+ }
292
+ output(name) {
293
+ const outputs = this.outputs().map((c) => (typeof c == 'string' ? { name: c, value: c } : c));
294
+ if (outputs.some((c) => c.name == name)) {
295
+ const opt = outputs.find((c) => c.name == name)?.value;
296
+ if (typeof opt == 'function') {
297
+ return opt();
298
+ }
299
+ return opt;
300
+ }
301
+ return null;
302
+ }
303
+ call(name, ...args) {
304
+ const fn = get(this, name);
305
+ if (fn && typeof fn == 'function') {
306
+ fn.bind(this)(...args);
307
+ }
308
+ }
309
+ setChildren(children) {
310
+ this._children.set([...children]);
311
+ }
312
+ onAdded() { }
313
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBaseWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
314
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBaseWidgetComponent }); }
315
+ }
316
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBaseWidgetComponent, decorators: [{
317
+ type: Injectable
318
+ }] });
319
+ class AXPLayoutBaseWidgetComponent extends AXPBaseWidgetComponent {
320
+ //TODO change this approach
321
+ ngOnInit() {
322
+ if (this.name && !(this instanceof AXPValueWidgetComponent)) {
323
+ this.layoutService.registerWidget(this.name, this);
324
+ }
325
+ super.ngOnInit();
326
+ }
327
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutBaseWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
328
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutBaseWidgetComponent }); }
329
+ }
330
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLayoutBaseWidgetComponent, decorators: [{
331
+ type: Injectable
332
+ }] });
333
+ class AXPValueWidgetComponent extends AXPLayoutBaseWidgetComponent {
334
+ constructor() {
335
+ super(...arguments);
336
+ this.path = this.token.node.path;
337
+ this.defaultValue = this.token.defaultValue ?? this.token.node.defaultValue;
338
+ this._isValueWidget = false;
339
+ this.isValueWidget = () => this._isValueWidget;
340
+ this.onValueChanged = new Subject();
341
+ this.fullPath = signal(null, ...(ngDevMode ? [{ debugName: "fullPath" }] : /* istanbul ignore next */ []));
342
+ this.parentPath = signal(null, ...(ngDevMode ? [{ debugName: "parentPath" }] : /* istanbul ignore next */ []));
343
+ this.getValue = computed(() => {
344
+ return this.fullPath() ? this.extractValue(this.fullPath()) : null;
345
+ }, { ...(ngDevMode ? { debugName: "getValue" } : /* istanbul ignore next */ {}), equal: isEqual });
346
+ this.validationRules = computed(() => {
347
+ const validationsRaw = this.options()['validations'];
348
+ if (validationsRaw == null) {
349
+ return [];
350
+ }
351
+ return Object.values(this.options()['validations'])
352
+ .filter((c) => c != null)
353
+ .map((c) => ({
354
+ rule: c.rule,
355
+ message: c.message,
356
+ options: c.options,
357
+ }));
358
+ }, ...(ngDevMode ? [{ debugName: "validationRules" }] : /* istanbul ignore next */ []));
359
+ this.hasRequiredValidation = computed(() => this.validationRules().some((rule) => rule.rule === 'required'), ...(ngDevMode ? [{ debugName: "hasRequiredValidation" }] : /* istanbul ignore next */ []));
360
+ this.hasClearButton = computed(() => this.resolveClearButtonOption('hasClearButton'), ...(ngDevMode ? [{ debugName: "hasClearButton" }] : /* istanbul ignore next */ []));
361
+ this.clearButton = computed(() => this.resolveClearButtonOption('clearButton'), ...(ngDevMode ? [{ debugName: "clearButton" }] : /* istanbul ignore next */ []));
362
+ }
363
+ ngOnInit() {
364
+ this._isValueWidget = this.config.properties?.some((c) => c.name == 'path') ?? false;
365
+ if (this.isValueWidget()) {
366
+ this.detectFullPath();
367
+ // Set defaultValue if it exists and the path doesn't exist in context
368
+ if (!isNil(this.defaultValue) && this.fullPath() && !this.contextService.hasValue(this.fullPath())) {
369
+ this.applyDefaultValue(this.defaultValue);
370
+ }
371
+ }
372
+ //
373
+ super.ngOnInit();
374
+ }
375
+ /**
376
+ * When `hasClearButton` / `clearButton` is not set on the widget, show clear for optional fields
377
+ * (no `required` validation) and hide it for required fields.
378
+ */
379
+ resolveClearButtonOption(optionKey) {
380
+ const explicit = this.options()[optionKey];
381
+ if (explicit != null) {
382
+ return explicit;
383
+ }
384
+ return !this.hasRequiredValidation();
385
+ }
386
+ extractValue(path) {
387
+ const rawValue = this.contextService.getValue(path);
388
+ if (this.node.valueTransforms?.getter) {
389
+ return this.node.valueTransforms?.getter(rawValue);
390
+ }
391
+ return rawValue;
392
+ }
393
+ /** Applies schema default without marking the form as user-edited. */
394
+ applyDefaultValue(value) {
395
+ if (this.node.valueTransforms?.setter) {
396
+ value = this.node.valueTransforms?.setter(value);
397
+ }
398
+ const path = this.fullPath();
399
+ if (!path) {
400
+ return;
401
+ }
402
+ value = isUndefined(value) ? null : value;
403
+ if (isNil(value)) {
404
+ return;
405
+ }
406
+ this.contextService.update(path, value, { origin: 'system' });
407
+ this.onValueChanged.next({ sender: this });
408
+ }
409
+ setValue(value) {
410
+ if (this.node.valueTransforms?.setter) {
411
+ value = this.node.valueTransforms?.setter(value);
412
+ }
413
+ const path = this.fullPath();
414
+ if (!path) {
415
+ return;
416
+ }
417
+ const oldValue = this.getValue();
418
+ value = isUndefined(value) ? null : value;
419
+ if (isNil(value) && isNil(oldValue)) {
420
+ return;
421
+ }
422
+ const savedAtPath = this.contextService.getSavedValue(path);
423
+ if (this.contextService.isSavedCommitted() &&
424
+ isSelectionValueEqual(value, savedAtPath) &&
425
+ !isFormValueEqual(value, savedAtPath)) {
426
+ this.contextService.update(path, cloneDeep(savedAtPath), { origin: 'system' });
427
+ this.onValueChanged.next({ sender: this });
428
+ return;
429
+ }
430
+ // Reordered arrays must persist even when items are deep-equal (e.g. empty row objects).
431
+ const isArrayReorder = Array.isArray(oldValue) &&
432
+ Array.isArray(value) &&
433
+ oldValue.length === value.length &&
434
+ oldValue.some((v, i) => v !== value[i]);
435
+ const isArrayLengthChange = Array.isArray(value) && (!Array.isArray(oldValue) || value.length !== oldValue.length);
436
+ if (!isArrayReorder && !isArrayLengthChange && isFormValueEqual(oldValue, value)) {
437
+ return;
438
+ }
439
+ this.contextService.update(path, value, { origin: 'user' });
440
+ this.onValueChanged.next({ sender: this });
441
+ }
442
+ detectFullPath() {
443
+ const sections = [];
444
+ const ids = [];
445
+ //
446
+ let parent = this;
447
+ //
448
+ while (parent) {
449
+ const isValueWidget = parent instanceof AXPValueWidgetComponent && parent.isValueWidget();
450
+ const valueParent = parent;
451
+ const path = valueParent.path ?? (isValueWidget ? valueParent.name : null);
452
+ const id = valueParent.name;
453
+ //
454
+ if (path) {
455
+ sections.push(path);
456
+ }
457
+ if (parent.index != null && isValueWidget) {
458
+ sections.push(`[${parent.index}]`);
459
+ }
460
+ if (id) {
461
+ ids.push(id);
462
+ if (parent.index != null) {
463
+ ids.push(`${parent.index}`);
464
+ }
465
+ }
466
+ parent = parent.parent;
467
+ }
468
+ //
469
+ this.fullPath.set(sections.reverse().join('.'));
470
+ this.parentPath.set(sections.slice(0, sections.length - 1).join('.'));
471
+ this._id = this.name || this.parent ? ids.reverse().join('_') : null;
472
+ if (this._id) {
473
+ this.layoutService.registerWidget(this._id, this);
474
+ }
475
+ }
476
+ handleValueChanged(e) {
477
+ if (e.isUserInteraction) {
478
+ this.setValue(e.value);
479
+ }
480
+ }
481
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPValueWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
482
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPValueWidgetComponent }); }
483
+ }
484
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPValueWidgetComponent, decorators: [{
485
+ type: Injectable
486
+ }] });
487
+ class AXPDataListWidgetComponent extends AXPValueWidgetComponent {
488
+ constructor() {
489
+ super(...arguments);
490
+ this.dataService = inject(AXPDataSourceDefinitionProviderService);
491
+ this.textField = computed(() => this.options()['textField'] ?? 'title', ...(ngDevMode ? [{ debugName: "textField" }] : /* istanbul ignore next */ []));
492
+ this.valueField = computed(() => this.options()['valueField'] ?? 'id', ...(ngDevMode ? [{ debugName: "valueField" }] : /* istanbul ignore next */ []));
493
+ this.textTemplate = computed(() => isNil(this.options()['textTemplate'])
494
+ ? undefined
495
+ : this.options()['textTemplate'].replace(/{/g, '{{').replace(/}/g, '}}'), ...(ngDevMode ? [{ debugName: "textTemplate" }] : /* istanbul ignore next */ []));
496
+ this.dataSource = signal(convertArrayToDataSource([]), ...(ngDevMode ? [{ debugName: "dataSource" }] : /* istanbul ignore next */ []));
497
+ this.selectedItems = signal([], ...(ngDevMode ? [{ debugName: "selectedItems" }] : /* istanbul ignore next */ []));
498
+ //#region ---- DataSource Loading Effect ----
499
+ /**
500
+ * Track the last loaded string dataSource reference to prevent infinite loops
501
+ * Only used for string-based datasources (not arrays or AXDataSource instances)
502
+ */
503
+ this.lastStringDataSourceRef = null;
504
+ this.rf = effect(async () => {
505
+ const rawValue = this.options()['dataSource'];
506
+ // For string datasources: prevent infinite loop by checking reference
507
+ if (typeof rawValue === 'string') {
508
+ if (rawValue === this.lastStringDataSourceRef) {
509
+ return;
510
+ }
511
+ this.lastStringDataSourceRef = rawValue;
512
+ }
513
+ else {
514
+ // Reset string reference when dataSource changes to non-string
515
+ this.lastStringDataSourceRef = null;
516
+ }
517
+ this.beginHydration();
518
+ try {
519
+ // Process dataSource creation in untracked context
520
+ await untracked(async () => {
521
+ if (rawValue instanceof AXDataSource) {
522
+ // For AXDataSource instances, check if config changed
523
+ if (!isEqual(rawValue.config, this.dataSource().config)) {
524
+ this.dataSource.set(rawValue);
525
+ }
526
+ }
527
+ // static array datasource - always recreate when options change
528
+ else if (Array.isArray(rawValue)) {
529
+ const ds = new AXDataSource({
530
+ key: this.valueField(),
531
+ pageSize: 10,
532
+ load: async (e) => {
533
+ const raw = this.options()['dataSource'];
534
+ const skip = e.skip ?? 0;
535
+ const take = e.take ?? raw.length;
536
+ return {
537
+ items: raw.slice(skip, skip + take),
538
+ total: raw.length,
539
+ };
540
+ },
541
+ byKey: (key) => {
542
+ const raw = this.options()['dataSource'];
543
+ const item = raw.filter((c) => c[this.valueField()] == key);
544
+ return Promise.resolve(item[0]);
545
+ },
546
+ });
547
+ this.dataSource.set(ds);
548
+ }
549
+ // resolve data source by name (string or object with id)
550
+ else if (rawValue && (typeof rawValue == 'string' || typeof rawValue == 'object')) {
551
+ const id = typeof rawValue == 'object' ? rawValue['id'] : rawValue;
552
+ const c = await this.dataService.get(id);
553
+ if (this.mode == 'designer' && c?.samples?.length) {
554
+ this.dataSource.set(convertArrayToDataSource(c.samples, {
555
+ key: this.valueField(),
556
+ pageSize: 500,
557
+ }));
558
+ }
559
+ else {
560
+ const ds = c?.source();
561
+ if (ds && ds instanceof Promise) {
562
+ const d = await ds;
563
+ this.dataSource.set(d);
564
+ }
565
+ else if (ds) {
566
+ this.dataSource.set(ds);
567
+ }
568
+ // empty datasource
569
+ else {
570
+ this.dataSource.set(convertArrayToDataSource([]));
571
+ }
572
+ }
573
+ }
574
+ // empty datasource
575
+ else {
576
+ this.dataSource.set(convertArrayToDataSource([]));
577
+ }
578
+ });
579
+ }
580
+ finally {
581
+ this.endHydration();
582
+ }
583
+ }, ...(ngDevMode ? [{ debugName: "rf" }] : /* istanbul ignore next */ []));
584
+ this.effect2 = effect(async () => {
585
+ this.beginHydration();
586
+ try {
587
+ const value = this.getValue();
588
+ const items = [];
589
+ if (Array.isArray(value)) {
590
+ items.push(...(await Promise.all(value.map((item) => this.extractItem(item)))));
591
+ }
592
+ else {
593
+ items.push(await this.extractItem(value));
594
+ }
595
+ this.selectedItems.set(items.filter((c) => c != null));
596
+ }
597
+ finally {
598
+ this.endHydration();
599
+ }
600
+ }, ...(ngDevMode ? [{ debugName: "effect2" }] : /* istanbul ignore next */ []));
601
+ }
602
+ //#endregion
603
+ async extractItem(item) {
604
+ if (isNil(item)) {
605
+ return null;
606
+ }
607
+ if (isObjectLike(item) && get(item, this.textField()) != null) {
608
+ return item;
609
+ }
610
+ const key = extractValue(item, this.valueField());
611
+ const ds = this.dataSource();
612
+ if (ds.config?.byKey) {
613
+ const found = await ds.config?.byKey(key);
614
+ if (found) {
615
+ return found;
616
+ }
617
+ }
618
+ return isObjectLike(item)
619
+ ? item
620
+ : {
621
+ [this.valueField()]: item,
622
+ [this.textField()]: item,
623
+ };
624
+ }
625
+ /**
626
+ * Value used to resolve `selectedItems`. Defaults to the stored value, but
627
+ * widgets that wrap their value (e.g. filter mode) can override this to expose
628
+ * the unwrapped scalar/array so item resolution receives a real key.
629
+ */
630
+ resolveSelectedItemsValue() {
631
+ return this.getValue();
632
+ }
633
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDataListWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
634
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDataListWidgetComponent }); }
635
+ }
636
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDataListWidgetComponent, decorators: [{
637
+ type: Injectable
638
+ }] });
639
+ class AXPColumnWidgetComponent {
640
+ constructor() {
641
+ this.token = inject(AXP_WIDGET_COLUMN_TOKEN);
642
+ this.path = this.token.path;
643
+ this.options = this.token.options ?? {};
644
+ /** Column header caption when rendered inside `axp-widget-column-renderer`. */
645
+ this.columnCaption = this.token.caption;
646
+ this.rawValue = null;
647
+ this.nullText = this.options['nullText'];
648
+ this.nullValue = this.options['nullValue'];
649
+ this.value = () => {
650
+ if (isNil(this.rawValue) && !isNil(this.nullValue)) {
651
+ return this.nullValue;
652
+ }
653
+ return this.rawValue;
654
+ };
655
+ }
656
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPColumnWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
657
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPColumnWidgetComponent }); }
658
+ }
659
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPColumnWidgetComponent, decorators: [{
660
+ type: Injectable
661
+ }] });
662
+
663
+ class AXPBoxModelLayoutWidgetComponent extends AXPLayoutBaseWidgetComponent {
664
+ constructor() {
665
+ super(...arguments);
666
+ this.hostBoxStyle = computed(() => {
667
+ const options = this.options();
668
+ const style = {};
669
+ const spacing = options?.['spacing'];
670
+ const border = options?.['border'];
671
+ const backgroundColor = options?.['backgroundColor'];
672
+ const direction = options?.['direction'];
673
+ const overflow = options?.['overflow'];
674
+ const overflowX = options?.['overflowX'];
675
+ const overflowY = options?.['overflowY'];
676
+ style['background-color'] = backgroundColor ?? '';
677
+ style['padding'] = spacing?.padding ?? '';
678
+ style['margin'] = spacing?.margin ?? '';
679
+ style['border-radius'] = border?.radius ?? '';
680
+ style['border-width'] = border?.width ?? '';
681
+ style['border-color'] = border?.color ?? '';
682
+ style['border-style'] = border?.style ?? '';
683
+ style['overflow'] = overflow ?? '';
684
+ style['overflow-x'] = overflowX ?? '';
685
+ style['overflow-y'] = overflowY ?? '';
686
+ style['direction'] = direction ?? '';
687
+ return style;
688
+ }, ...(ngDevMode ? [{ debugName: "hostBoxStyle" }] : /* istanbul ignore next */ []));
689
+ this.blockStyle = computed(() => {
690
+ const options = this.options();
691
+ const style = { ...this.hostBoxStyle() };
692
+ const width = options?.['width'];
693
+ const minWidth = options?.['minWidth'];
694
+ const maxWidth = options?.['maxWidth'];
695
+ const height = options?.['height'];
696
+ const minHeight = options?.['minHeight'];
697
+ const maxHeight = options?.['maxHeight'];
698
+ style['min-width'] = minWidth ?? '';
699
+ style['width'] = width ?? '';
700
+ style['max-width'] = maxWidth ?? '';
701
+ style['min-height'] = minHeight ?? '';
702
+ style['height'] = height ?? '';
703
+ style['max-height'] = maxHeight ?? '';
704
+ return style;
705
+ }, ...(ngDevMode ? [{ debugName: "blockStyle" }] : /* istanbul ignore next */ []));
706
+ this.inlineStyle = computed(() => {
707
+ return { ...this.hostBoxStyle() };
708
+ }, ...(ngDevMode ? [{ debugName: "inlineStyle" }] : /* istanbul ignore next */ []));
709
+ this.blockClass = computed(() => {
710
+ return {
711
+ 'ax-block': true,
712
+ 'ax-w-full': true,
713
+ // 'ax-widget-outline': true,
714
+ };
715
+ }, ...(ngDevMode ? [{ debugName: "blockClass" }] : /* istanbul ignore next */ []));
716
+ this.inlineClass = computed(() => {
717
+ return {
718
+ 'ax-inline-block': true,
719
+ };
720
+ }, ...(ngDevMode ? [{ debugName: "inlineClass" }] : /* istanbul ignore next */ []));
721
+ }
722
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBoxModelLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
723
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBoxModelLayoutWidgetComponent }); }
724
+ }
725
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBoxModelLayoutWidgetComponent, decorators: [{
726
+ type: Injectable
727
+ }] });
728
+
729
+ class AXPBlockBaseLayoutWidgetComponent extends AXPBoxModelLayoutWidgetComponent {
730
+ constructor() {
731
+ super(...arguments);
732
+ this.hostClass = computed(() => this.blockClass(), ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
733
+ this.hostStyle = computed(() => this.blockStyle(), ...(ngDevMode ? [{ debugName: "hostStyle" }] : /* istanbul ignore next */ []));
734
+ }
735
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBlockBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
736
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBlockBaseLayoutWidgetComponent }); }
737
+ }
738
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPBlockBaseLayoutWidgetComponent, decorators: [{
739
+ type: Injectable
740
+ }] });
741
+
742
+ class AXPFlexBaseLayoutWidgetComponent extends AXPBlockBaseLayoutWidgetComponent {
743
+ constructor() {
744
+ super(...arguments);
745
+ this.flex = computed(() => this.options(), ...(ngDevMode ? [{ debugName: "flex" }] : /* istanbul ignore next */ []));
746
+ this.hostFlexStyle = computed(() => {
747
+ const blockStyle = this.blockStyle();
748
+ const style = { ...blockStyle };
749
+ const flex = this.flex();
750
+ if (isNil(flex?.flexDirection)) {
751
+ style['flex-direction'] = '';
752
+ }
753
+ else {
754
+ style['flex-direction'] = flex.flexDirection;
755
+ }
756
+ if (isNil(flex?.flexWrap)) {
757
+ style['flex-wrap'] = '';
758
+ }
759
+ else {
760
+ style['flex-wrap'] = flex.flexWrap;
761
+ }
762
+ //TODO NEED TO FIX LATER
763
+ style['overflow'] = flex?.flexWrap === 'nowrap' ? 'auto' : '';
764
+ //END
765
+ if (isNil(flex?.justifyContent)) {
766
+ style['justify-content'] = '';
767
+ }
768
+ else {
769
+ style['justify-content'] = flex.justifyContent;
770
+ }
771
+ if (isNil(flex?.alignItems)) {
772
+ style['align-items'] = '';
773
+ }
774
+ else {
775
+ style['align-items'] = flex.alignItems;
776
+ }
777
+ if (isNil(flex?.gap)) {
778
+ style['gap'] = '';
779
+ }
780
+ else {
781
+ style['gap'] = flex.gap;
782
+ }
783
+ return style;
784
+ }, ...(ngDevMode ? [{ debugName: "hostFlexStyle" }] : /* istanbul ignore next */ []));
785
+ this.hostFlexClass = computed(() => {
786
+ return {
787
+ ...this.blockClass(),
788
+ 'ax-flex': true,
789
+ 'ax-h-full': true,
790
+ };
791
+ }, ...(ngDevMode ? [{ debugName: "hostFlexClass" }] : /* istanbul ignore next */ []));
792
+ this.hostClass = computed(() => {
793
+ return this.hostFlexClass();
794
+ }, ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
795
+ this.hostStyle = computed(() => {
796
+ return this.hostFlexStyle();
797
+ }, ...(ngDevMode ? [{ debugName: "hostStyle" }] : /* istanbul ignore next */ []));
798
+ }
799
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFlexBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
800
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFlexBaseLayoutWidgetComponent }); }
801
+ }
802
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFlexBaseLayoutWidgetComponent, decorators: [{
803
+ type: Injectable
804
+ }] });
805
+
806
+ class AXPInlineBaseLayoutWidgetComponent extends AXPBoxModelLayoutWidgetComponent {
807
+ constructor() {
808
+ super(...arguments);
809
+ this.hostClass = computed(() => this.inlineClass(), ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
810
+ this.hostStyle = computed(() => this.inlineStyle(), ...(ngDevMode ? [{ debugName: "hostStyle" }] : /* istanbul ignore next */ []));
811
+ }
812
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPInlineBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
813
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPInlineBaseLayoutWidgetComponent }); }
814
+ }
815
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPInlineBaseLayoutWidgetComponent, decorators: [{
816
+ type: Injectable
817
+ }] });
818
+
819
+ class AXPFlexItemBaseLayoutWidgetComponent extends AXPInlineBaseLayoutWidgetComponent {
820
+ constructor() {
821
+ super(...arguments);
822
+ this.flexItem = computed(() => this.options(), ...(ngDevMode ? [{ debugName: "flexItem" }] : /* istanbul ignore next */ []));
823
+ this.hostFlexItemStyle = computed(() => {
824
+ const inlineStyle = this.blockStyle();
825
+ const style = { ...inlineStyle };
826
+ const fi = this.flexItem();
827
+ if (isNil(fi?.order)) {
828
+ style['order'] = '';
829
+ }
830
+ else {
831
+ style['order'] = fi.order;
832
+ }
833
+ if (isNil(fi?.grow)) {
834
+ style['flex-grow'] = '';
835
+ }
836
+ else {
837
+ style['flex-grow'] = fi.grow;
838
+ }
839
+ if (isNil(fi?.shrink)) {
840
+ style['flex-shrink'] = '';
841
+ }
842
+ else {
843
+ style['flex-shrink'] = fi.shrink;
844
+ }
845
+ if (isNil(fi?.basis)) {
846
+ style['flex-basis'] = '';
847
+ }
848
+ else {
849
+ style['flex-basis'] = fi.basis;
850
+ }
851
+ if (isNil(fi?.alignSelf)) {
852
+ style['align-self'] = '';
853
+ }
854
+ else {
855
+ style['align-self'] = fi.alignSelf;
856
+ }
857
+ return style;
858
+ }, ...(ngDevMode ? [{ debugName: "hostFlexItemStyle" }] : /* istanbul ignore next */ []));
859
+ this.hostFlexItemClass = computed(() => {
860
+ return {
861
+ ...this.blockClass(),
862
+ };
863
+ }, ...(ngDevMode ? [{ debugName: "hostFlexItemClass" }] : /* istanbul ignore next */ []));
864
+ this.hostClass = computed(() => {
865
+ return this.hostFlexItemClass();
866
+ }, ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
867
+ this.hostStyle = computed(() => {
868
+ return this.hostFlexItemStyle();
869
+ }, ...(ngDevMode ? [{ debugName: "hostStyle" }] : /* istanbul ignore next */ []));
870
+ }
871
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFlexItemBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
872
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFlexItemBaseLayoutWidgetComponent }); }
873
+ }
874
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFlexItemBaseLayoutWidgetComponent, decorators: [{
875
+ type: Injectable
876
+ }] });
877
+
878
+ class AXPGridBaseLayoutWidgetComponent extends AXPBlockBaseLayoutWidgetComponent {
879
+ constructor() {
880
+ super(...arguments);
881
+ this.grid = computed(() => this.options()?.['grid'], ...(ngDevMode ? [{ debugName: "grid" }] : /* istanbul ignore next */ []));
882
+ this.hostGridStyle = computed(() => {
883
+ const style = { ...this.inlineStyle() };
884
+ const g = this.grid()?.default;
885
+ if (g?.gap)
886
+ style['gap'] = g.gap;
887
+ return style;
888
+ }, ...(ngDevMode ? [{ debugName: "hostGridStyle" }] : /* istanbul ignore next */ []));
889
+ this.hostGridClass = computed(() => {
890
+ const cls = {
891
+ ...this.inlineClass(),
892
+ 'ax-grid': true,
893
+ };
894
+ const g = this.grid()?.default;
895
+ if (g?.columns)
896
+ cls[`lg:ax-grid-cols-${g.columns}`] = true;
897
+ // if (g?.rows) cls[`lg:ax-grid-rows-${g.rows}`] = true;
898
+ if (g?.justifyItems)
899
+ cls[`lg:ax-justify-items-${g.justifyItems}`] = true;
900
+ if (g?.alignItems)
901
+ cls[`lg:ax-align-items-${g.alignItems}`] = true;
902
+ if (g?.autoFlow)
903
+ cls[`lg:ax-grid-flow-${g.autoFlow}`] = true;
904
+ return cls;
905
+ }, ...(ngDevMode ? [{ debugName: "hostGridClass" }] : /* istanbul ignore next */ []));
906
+ this.hostClass = computed(() => this.hostGridClass(), ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
907
+ this.hostStyle = computed(() => this.hostGridStyle(), ...(ngDevMode ? [{ debugName: "hostStyle" }] : /* istanbul ignore next */ []));
908
+ }
909
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPGridBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
910
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPGridBaseLayoutWidgetComponent }); }
911
+ }
912
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPGridBaseLayoutWidgetComponent, decorators: [{
913
+ type: Injectable
914
+ }] });
915
+
916
+ class AXPGridItemBaseLayoutWidgetComponent extends AXPFlexBaseLayoutWidgetComponent {
917
+ constructor() {
918
+ super(...arguments);
919
+ this.gridItem = computed(() => this.options(), ...(ngDevMode ? [{ debugName: "gridItem" }] : /* istanbul ignore next */ []));
920
+ this.hostGridItemStyle = computed(() => {
921
+ const style = { ...this.hostFlexStyle() };
922
+ const g = this.gridItem();
923
+ if (g?.alignSelf)
924
+ style['align-self'] = g.alignSelf;
925
+ if (g?.justifySelf)
926
+ style['justify-self'] = g.justifySelf;
927
+ return style;
928
+ }, ...(ngDevMode ? [{ debugName: "hostGridItemStyle" }] : /* istanbul ignore next */ []));
929
+ this.hostGridItemClass = computed(() => {
930
+ const cls = { ...this.hostFlexClass() };
931
+ const g = this.gridItem();
932
+ // Mobile-first: full width on small screens so items stack vertically and don't overflow
933
+ cls['ax-col-span-12'] = true;
934
+ // Large screens: apply defined placement so multi-column layout works on desktop
935
+ if (g?.colSpan)
936
+ cls[`lg:ax-col-span-${g.colSpan}`] = true;
937
+ if (g?.colStart)
938
+ cls[`lg:ax-col-start-${g.colStart}`] = true;
939
+ if (g?.colEnd)
940
+ cls[`lg:ax-col-end-${g.colEnd}`] = true;
941
+ if (g?.rowSpan)
942
+ cls[`lg:ax-row-span-${g.rowSpan}`] = true;
943
+ if (g?.rowStart)
944
+ cls[`lg:ax-row-start-${g.rowStart}`] = true;
945
+ if (g?.rowEnd)
946
+ cls[`lg:ax-row-end-${g.rowEnd}`] = true;
947
+ return cls;
948
+ }, ...(ngDevMode ? [{ debugName: "hostGridItemClass" }] : /* istanbul ignore next */ []));
949
+ this.hostClass = computed(() => this.hostGridItemClass(), ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
950
+ this.hostStyle = computed(() => this.hostGridItemStyle(), ...(ngDevMode ? [{ debugName: "hostStyle" }] : /* istanbul ignore next */ []));
951
+ }
952
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPGridItemBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
953
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPGridItemBaseLayoutWidgetComponent }); }
954
+ }
955
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPGridItemBaseLayoutWidgetComponent, decorators: [{
956
+ type: Injectable
957
+ }] });
958
+
959
+ class AXPTableBaseLayoutWidgetComponent extends AXPBlockBaseLayoutWidgetComponent {
960
+ constructor() {
961
+ super(...arguments);
962
+ this.hostTableClass = computed(() => ({
963
+ ...this.blockClass(),
964
+ }), ...(ngDevMode ? [{ debugName: "hostTableClass" }] : /* istanbul ignore next */ []));
965
+ this.hostTableStyle = computed(() => {
966
+ const style = { ...this.blockStyle() };
967
+ style['overflow-x'] = 'auto';
968
+ return style;
969
+ }, ...(ngDevMode ? [{ debugName: "hostTableStyle" }] : /* istanbul ignore next */ []));
970
+ this.hostClass = computed(() => this.hostTableClass(), ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
971
+ this.hostStyle = computed(() => this.hostTableStyle(), ...(ngDevMode ? [{ debugName: "hostStyle" }] : /* istanbul ignore next */ []));
972
+ }
973
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTableBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
974
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTableBaseLayoutWidgetComponent }); }
975
+ }
976
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTableBaseLayoutWidgetComponent, decorators: [{
977
+ type: Injectable
978
+ }] });
979
+
980
+ class AXPTableItemOpsBaseLayoutWidgetComponent extends AXPBlockBaseLayoutWidgetComponent {
981
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTableItemOpsBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
982
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTableItemOpsBaseLayoutWidgetComponent }); }
983
+ }
984
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTableItemOpsBaseLayoutWidgetComponent, decorators: [{
985
+ type: Injectable
986
+ }] });
987
+
988
+ class AXPTableItemBaseLayoutWidgetComponent extends AXPTableItemOpsBaseLayoutWidgetComponent {
989
+ constructor() {
990
+ super(...arguments);
991
+ this.colSpan = computed(() => {
992
+ const v = this.options()?.colSpan;
993
+ return v ? Math.max(1, v) : 1;
994
+ }, ...(ngDevMode ? [{ debugName: "colSpan" }] : /* istanbul ignore next */ []));
995
+ this.rowSpan = computed(() => {
996
+ const v = this.options()?.rowSpan;
997
+ return v ? Math.max(1, v) : 1;
998
+ }, ...(ngDevMode ? [{ debugName: "rowSpan" }] : /* istanbul ignore next */ []));
999
+ this.hostClass = computed(() => this.blockClass(), ...(ngDevMode ? [{ debugName: "hostClass" }] : /* istanbul ignore next */ []));
1000
+ this.hostStyle = computed(() => this.blockStyle(), ...(ngDevMode ? [{ debugName: "hostStyle" }] : /* istanbul ignore next */ []));
1001
+ }
1002
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTableItemBaseLayoutWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
1003
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTableItemBaseLayoutWidgetComponent }); }
1004
+ }
1005
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPTableItemBaseLayoutWidgetComponent, decorators: [{
1006
+ type: Injectable
1007
+ }] });
1008
+
1009
+ //#region ---- Imports ----
1010
+ /**
1011
+ * Injection token for widget definition providers.
1012
+ * Modules register their widget providers using this token (multi: true).
1013
+ */
1014
+ const AXP_WIDGET_DEFINITION_PROVIDER = new InjectionToken('AXP_WIDGET_DEFINITION_PROVIDER', {
1015
+ factory: () => [],
1016
+ });
1017
+ //#endregion
1018
+
1019
+ //#region ---- Build widget map (shared with tooling / snapshot scripts) ----
1020
+ //TODO RECHECK THIS FUNCTION s.hosseini
1021
+ /**
1022
+ * Merges core + extended widgets the same way as AXPWidgetRegistryService.ensureBuilt().
1023
+ * Use with concrete providers (e.g. AXPCoreWidgetsProvider, AXPEntityWidgetsProvider) in Node scripts without DI.
1024
+ */
1025
+ function buildWidgetRegistryMapFromProviders(providers) {
1026
+ const types = new Map();
1027
+ const AXPExtendedWidgets = [];
1028
+ for (const provider of providers) {
1029
+ const widgets = provider.getWidgets?.() ?? [];
1030
+ for (const w of widgets) {
1031
+ types.set(w.name, w);
1032
+ }
1033
+ const extended = provider.getExtendedWidgets?.() ?? [];
1034
+ AXPExtendedWidgets.push(...extended);
1035
+ }
1036
+ for (const { parentName, widget } of AXPExtendedWidgets) {
1037
+ const parent = types.get(parentName);
1038
+ if (parent) {
1039
+ const merged = merge({}, parent, widget);
1040
+ merged.name = widget.name;
1041
+ types.set(merged.name, merged);
1042
+ }
1043
+ }
1044
+ return types;
1045
+ }
1046
+ //#endregion
1047
+ //#region ---- Widget Registry Service (Token-Based) ----
1048
+ /**
1049
+ * Aggregates widgets from all AXP_WIDGET_DEFINITION_PROVIDER instances.
1050
+ * Token-based like AXP_MENU_PROVIDER / AXP_PERMISSION_DEFINITION_PROVIDER / AXP_SETTING_DEFINITION_PROVIDER.
1051
+ * No separate registry - widgets come solely from providers.
1052
+ */
1053
+ class AXPWidgetRegistryService {
1054
+ constructor() {
1055
+ this._providers = inject(AXP_WIDGET_DEFINITION_PROVIDER, { optional: true }) ?? [];
1056
+ this._types = null;
1057
+ }
1058
+ /**
1059
+ * Lazy-build widget map from all providers.
1060
+ */
1061
+ ensureBuilt() {
1062
+ if (this._types)
1063
+ return this._types;
1064
+ this._types = buildWidgetRegistryMapFromProviders(this._providers);
1065
+ return this._types;
1066
+ }
1067
+ resolve(name) {
1068
+ const widget = this.ensureBuilt().get(name);
1069
+ if (!widget) {
1070
+ throw new Error(`Widget with name "${name}" does not exist.`);
1071
+ }
1072
+ return widget;
1073
+ }
1074
+ /**
1075
+ * Registered widget config when present; otherwise `undefined`. Does not throw — use when
1076
+ * {@link resolve}'s value may be a non-widget id (e.g. data-source row id in dynamic field configurator).
1077
+ */
1078
+ getOptional(name) {
1079
+ return this.ensureBuilt().get(name);
1080
+ }
1081
+ all() {
1082
+ return Array.from(this.ensureBuilt().values());
1083
+ }
1084
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1085
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetRegistryService, providedIn: 'root' }); }
1086
+ }
1087
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetRegistryService, decorators: [{
1088
+ type: Injectable,
1089
+ args: [{
1090
+ providedIn: 'root',
1091
+ }]
1092
+ }] });
1093
+
1094
+ class AXPWidgetColumnRendererComponent extends AXDataTableColumnComponent {
1095
+ constructor() {
1096
+ super(...arguments);
1097
+ this.widgetRegistery = inject(AXPWidgetRegistryService);
1098
+ this.grid = inject(AXBaseDataTable);
1099
+ this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
1100
+ this.mergedOptions = signal({}, ...(ngDevMode ? [{ debugName: "mergedOptions" }] : /* istanbul ignore next */ []));
1101
+ this.loadingRow = signal(null, ...(ngDevMode ? [{ debugName: "loadingRow" }] : /* istanbul ignore next */ []));
1102
+ this.rowInjectorsCache = new Map();
1103
+ this.hasExpressions = false;
1104
+ this.pendingEvaluations = new Set();
1105
+ this.changeDetectionScheduled = false;
1106
+ this.MAX_CACHE_SIZE = 1000; // Limit cache size to prevent memory leaks
1107
+ /** When true, header click cycles sort (handled by parent; synced with list sort state). */
1108
+ this.sortEnabled = false;
1109
+ this.sortToggle = new EventEmitter();
1110
+ this.injector = inject(Injector);
1111
+ this.cdr = inject(ChangeDetectorRef);
1112
+ }
1113
+ get node() {
1114
+ return this._node;
1115
+ }
1116
+ set node(v) {
1117
+ this._node = v;
1118
+ }
1119
+ get renderFooterTemplate() {
1120
+ return this.footerTemplate ?? this._contentFooterTemplate;
1121
+ }
1122
+ get renderCellTemplate() {
1123
+ return this.cellTemplate ?? this._contentCellTemplate;
1124
+ }
1125
+ /**
1126
+ * True when the row should show the expand icon: either hasChild is true (from server/initial load)
1127
+ * or the row has loaded children (e.g. after refreshItemChildren when children go from 0 to 1).
1128
+ */
1129
+ hasExpandableRow(row) {
1130
+ const data = row?.data;
1131
+ if (!data)
1132
+ return false;
1133
+ if (data['hasChild'] === true)
1134
+ return true;
1135
+ const children = data['__meta__']?.['children'];
1136
+ return Array.isArray(children) && children.length > 0;
1137
+ }
1138
+ async handleExpandRow(row) {
1139
+ this.loadingRow.set(row);
1140
+ await this.grid.expandRow(row);
1141
+ this.loadingRow.set(null);
1142
+ // if (row.data?.__meta__?.expanded === undefined) {
1143
+ // this.width = `${parseInt(this.width as string) + 24}px`;
1144
+ // }
1145
+ }
1146
+ get renderHeaderTemplate() {
1147
+ return this.headerTemplate ?? this._contentHeaderTemplate;
1148
+ }
1149
+ //#region ---- Column alignment (header / cell) ----
1150
+ /**
1151
+ * Resolves `options.align`: string applies to header and cell; object uses optional `header` / `cell` (default start).
1152
+ * Supports designer select values `{ id: 'center' }`.
1153
+ */
1154
+ getHeaderTextAlign() {
1155
+ return this.resolveAlignForSide('header');
1156
+ }
1157
+ getHeaderJustifyContent() {
1158
+ const align = this.getHeaderTextAlign();
1159
+ if (align === 'center') {
1160
+ return 'center';
1161
+ }
1162
+ if (align === 'end') {
1163
+ return 'flex-end';
1164
+ }
1165
+ return 'flex-start';
1166
+ }
1167
+ onHeaderClick(event) {
1168
+ if (!this.sortEnabled) {
1169
+ return;
1170
+ }
1171
+ event.stopPropagation();
1172
+ this.sortToggle.emit(event);
1173
+ }
1174
+ /**
1175
+ * When true, the cell body uses flex so `justify-content` can place content on the main axis.
1176
+ */
1177
+ getCellUseFlexLayout() {
1178
+ return !!this.expandHandler || this.resolveAlignForSide('cell') !== 'start';
1179
+ }
1180
+ /**
1181
+ * Flex `justify-content` for the cell wrapper; omit when not using flex layout.
1182
+ */
1183
+ getCellJustifyContent() {
1184
+ if (!this.getCellUseFlexLayout()) {
1185
+ return null;
1186
+ }
1187
+ return this.resolveAlignForSide('cell');
1188
+ }
1189
+ resolveAlignForSide(side) {
1190
+ const align = this.mergedOptions().align;
1191
+ if (align == null || align === '') {
1192
+ return 'start';
1193
+ }
1194
+ if (typeof align === 'string') {
1195
+ return this.normalizeColumnAlignment(align);
1196
+ }
1197
+ if (typeof align === 'object' && !Array.isArray(align)) {
1198
+ const raw = side === 'header' ? align.header : align.cell;
1199
+ return this.normalizeColumnAlignment(raw);
1200
+ }
1201
+ return 'start';
1202
+ }
1203
+ normalizeColumnAlignment(value) {
1204
+ if (value === 'center' || value === 'end' || value === 'start') {
1205
+ return value;
1206
+ }
1207
+ if (value && typeof value === 'object' && 'id' in value) {
1208
+ const id = value.id;
1209
+ if (id === 'center' || id === 'end' || id === 'start') {
1210
+ return id;
1211
+ }
1212
+ }
1213
+ return 'start';
1214
+ }
1215
+ //#endregion
1216
+ get loadingEnabled() {
1217
+ return true;
1218
+ }
1219
+ get name() {
1220
+ return `col-${this.node.path}`;
1221
+ }
1222
+ async ngOnInit() {
1223
+ const widget = this.widgetRegistery.resolve(this.node.type);
1224
+ const mode = 'column';
1225
+ this.component = await widget?.components[mode]?.component();
1226
+ //
1227
+ const props = widget?.components[mode]?.properties
1228
+ ?.filter((c) => c.schema.defaultValue)
1229
+ .map((c) => ({ [c.name]: c.schema.defaultValue }))
1230
+ .reduce((acc, curr) => {
1231
+ return { ...acc, ...curr };
1232
+ }, {});
1233
+ //
1234
+ this.mergedOptions.set(merge(props, this.node.options) || {});
1235
+ // Check if options contain expressions (performance optimization)
1236
+ this.hasExpressions = this.checkForExpressions(this.mergedOptions());
1237
+ const tokenValue = {
1238
+ path: this.node.path,
1239
+ options: this.mergedOptions(),
1240
+ caption: this.caption,
1241
+ };
1242
+ this.widgetInjector = Injector.create({
1243
+ parent: this.injector,
1244
+ providers: [
1245
+ {
1246
+ provide: AXP_WIDGET_COLUMN_TOKEN,
1247
+ useValue: tokenValue,
1248
+ },
1249
+ ],
1250
+ });
1251
+ this.width = this.mergedOptions().width === 'auto' ? 'auto' : this.customWidth ? this.customWidth : (this.mergedOptions().width ?? '200px');
1252
+ this.allowResizing = this.mergedOptions().allowResizing || true;
1253
+ this.allowSorting = this.sortEnabled;
1254
+ this.cdr.detectChanges();
1255
+ this.fixed = this.columnFixed;
1256
+ }
1257
+ ngOnChanges() {
1258
+ this.allowSorting = this.sortEnabled;
1259
+ this.sortOrder = this.headerSortDirection;
1260
+ this.sortIndex = this.headerSortPriority;
1261
+ this.fixed = this.columnFixed;
1262
+ if (this.customWidth) {
1263
+ this.width = this.customWidth;
1264
+ }
1265
+ }
1266
+ //#region ---- Performance Optimization Methods ----
1267
+ /**
1268
+ * Check if options contain any expressions that need evaluation
1269
+ */
1270
+ checkForExpressions(obj) {
1271
+ if (typeof obj === 'string') {
1272
+ return this.expressionEvaluator.isExpression(obj);
1273
+ }
1274
+ if (Array.isArray(obj)) {
1275
+ return obj.some((item) => this.checkForExpressions(item));
1276
+ }
1277
+ if (obj && typeof obj === 'object') {
1278
+ return Object.values(obj).some((value) => this.checkForExpressions(value));
1279
+ }
1280
+ return false;
1281
+ }
1282
+ /**
1283
+ * Get cache key for row data - optimized to avoid JSON.stringify when possible
1284
+ */
1285
+ getCacheKey(data) {
1286
+ if (data?.id) {
1287
+ return `row-${data.id}`;
1288
+ }
1289
+ // Use a hash of key properties instead of full JSON.stringify
1290
+ // This is much faster for large objects
1291
+ const keyProps = ['id', 'code', 'name', 'title'];
1292
+ const keyValues = keyProps.map((prop) => data?.[prop]).filter((v) => v != null);
1293
+ if (keyValues.length > 0) {
1294
+ return `row-${keyValues.join('-')}`;
1295
+ }
1296
+ // Fallback to JSON.stringify only if no key properties exist
1297
+ // But limit the stringified size to prevent performance issues
1298
+ try {
1299
+ const str = JSON.stringify(data);
1300
+ return str.length > 1000 ? `row-${str.substring(0, 1000)}` : `row-${str}`;
1301
+ }
1302
+ catch {
1303
+ // If JSON.stringify fails, use a simple hash
1304
+ return `row-${Object.keys(data || {}).length}`;
1305
+ }
1306
+ }
1307
+ /**
1308
+ * Schedule change detection (debounced to batch multiple updates)
1309
+ */
1310
+ scheduleChangeDetection() {
1311
+ if (this.changeDetectionScheduled) {
1312
+ return;
1313
+ }
1314
+ this.changeDetectionScheduled = true;
1315
+ // Use requestAnimationFrame for better performance
1316
+ requestAnimationFrame(() => {
1317
+ this.cdr.detectChanges();
1318
+ this.changeDetectionScheduled = false;
1319
+ });
1320
+ }
1321
+ /**
1322
+ * Limit cache size using simple FIFO eviction
1323
+ */
1324
+ evictCacheIfNeeded() {
1325
+ if (this.rowInjectorsCache.size >= this.MAX_CACHE_SIZE) {
1326
+ // Remove oldest entries (first 25% of cache)
1327
+ const entriesToRemove = Math.floor(this.MAX_CACHE_SIZE * 0.25);
1328
+ const keysToRemove = Array.from(this.rowInjectorsCache.keys()).slice(0, entriesToRemove);
1329
+ keysToRemove.forEach((key) => this.rowInjectorsCache.delete(key));
1330
+ }
1331
+ }
1332
+ //#endregion
1333
+ getInputs(data) {
1334
+ return {
1335
+ rawValue: get(data, this.node.path),
1336
+ rowData: data,
1337
+ };
1338
+ }
1339
+ getRowInjector(data) {
1340
+ // Optimized cache key generation
1341
+ const cacheKey = this.getCacheKey(data);
1342
+ // Check if we have a cached injector for this row
1343
+ let rowInjector = this.rowInjectorsCache.get(cacheKey);
1344
+ if (!rowInjector) {
1345
+ // If no expressions in options, use original options directly (performance optimization)
1346
+ if (!this.hasExpressions) {
1347
+ const tokenValue = {
1348
+ path: this.node.path,
1349
+ options: this.mergedOptions(),
1350
+ caption: this.caption,
1351
+ };
1352
+ rowInjector = Injector.create({
1353
+ parent: this.injector,
1354
+ providers: [
1355
+ {
1356
+ provide: AXP_WIDGET_COLUMN_TOKEN,
1357
+ useValue: tokenValue,
1358
+ },
1359
+ ],
1360
+ });
1361
+ this.evictCacheIfNeeded();
1362
+ this.rowInjectorsCache.set(cacheKey, rowInjector);
1363
+ return rowInjector;
1364
+ }
1365
+ // Check if evaluation is already pending for this row
1366
+ if (!this.pendingEvaluations.has(cacheKey)) {
1367
+ this.pendingEvaluations.add(cacheKey);
1368
+ // Evaluate expressions in options based on row data
1369
+ const scope = {
1370
+ ...(data ?? {}),
1371
+ context: {
1372
+ eval: (path) => {
1373
+ return getSmart(data, path);
1374
+ },
1375
+ },
1376
+ };
1377
+ // Start async evaluation - will update injector when complete
1378
+ this.expressionEvaluator.evaluate(this.mergedOptions(), scope).then((evaluatedOptions) => {
1379
+ // Create injector with evaluated options
1380
+ const tokenValue = {
1381
+ path: this.node.path,
1382
+ options: evaluatedOptions,
1383
+ caption: this.caption,
1384
+ };
1385
+ const newInjector = Injector.create({
1386
+ parent: this.injector,
1387
+ providers: [
1388
+ {
1389
+ provide: AXP_WIDGET_COLUMN_TOKEN,
1390
+ useValue: tokenValue,
1391
+ },
1392
+ ],
1393
+ });
1394
+ // Replace cached injector to force component recreation on next render
1395
+ this.rowInjectorsCache.delete(cacheKey);
1396
+ this.evictCacheIfNeeded();
1397
+ this.rowInjectorsCache.set(cacheKey, newInjector);
1398
+ this.pendingEvaluations.delete(cacheKey);
1399
+ // Schedule batched change detection (performance optimization)
1400
+ this.scheduleChangeDetection();
1401
+ }).catch(() => {
1402
+ // If evaluation fails, keep using the fallback injector
1403
+ this.pendingEvaluations.delete(cacheKey);
1404
+ });
1405
+ }
1406
+ // Create injector with original options as fallback (will be updated when evaluation completes)
1407
+ const tokenValue = {
1408
+ path: this.node.path,
1409
+ options: this.mergedOptions(),
1410
+ caption: this.caption,
1411
+ };
1412
+ rowInjector = Injector.create({
1413
+ parent: this.injector,
1414
+ providers: [
1415
+ {
1416
+ provide: AXP_WIDGET_COLUMN_TOKEN,
1417
+ useValue: tokenValue,
1418
+ },
1419
+ ],
1420
+ });
1421
+ // Cache the initial injector
1422
+ this.evictCacheIfNeeded();
1423
+ this.rowInjectorsCache.set(cacheKey, rowInjector);
1424
+ }
1425
+ return rowInjector;
1426
+ }
1427
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetColumnRendererComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
1428
+ 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", sortEnabled: "sortEnabled", headerSortDirection: "headerSortDirection", headerSortPriority: "headerSortPriority", columnFixed: "columnFixed", cellTemplate: "cellTemplate", headerTemplate: "headerTemplate" }, outputs: { sortToggle: "sortToggle" }, providers: [
1429
+ AXPWidgetCoreService,
1430
+ { provide: AXDataTableColumnComponent, useExisting: AXPWidgetColumnRendererComponent },
1431
+ ], 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, usesOnChanges: true, ngImport: i0, template: `
1432
+ <ng-template #header>
1433
+ <div
1434
+ class="axp-widget-column-header ax-w-full ax-flex ax-items-center ax-select-none"
1435
+ [class.axp-widget-column-header--sortable]="sortEnabled"
1436
+ [class.axp-widget-column-header--sort-active]="headerSortDirection === 'asc' || headerSortDirection === 'desc'"
1437
+ [class.ax-gap-1]="!sortEnabled"
1438
+ [style.justify-content]="sortEnabled ? 'flex-start' : getHeaderJustifyContent()"
1439
+ (click)="onHeaderClick($event)"
1440
+ >
1441
+ <span
1442
+ class="axp-widget-column-header__caption ax-truncate"
1443
+ [class.axp-widget-column-header__caption--with-sort]="sortEnabled"
1444
+ [style.text-align]="sortEnabled ? getHeaderTextAlign() : null"
1445
+ >{{ caption | translate | async }}</span>
1446
+ @if (sortEnabled) {
1447
+ <span class="axp-widget-column-header__sort ax-flex ax-items-center ax-gap-0.5 ax-shrink-0">
1448
+ @if (headerSortPriority !== undefined && headerSortPriority > 1) {
1449
+ <span class="axp-widget-column-header__sort-priority ax-text-xs ax-opacity-70">{{ headerSortPriority }}</span>
1450
+ }
1451
+ <i
1452
+ class="fa-solid fa-arrow-up-long ax-text-neutral-400"
1453
+ [class.ax-text-primary]="headerSortDirection === 'asc'"
1454
+ ></i>
1455
+ <i
1456
+ class="fa-solid fa-arrow-down-long ax-text-neutral-400"
1457
+ [class.ax-text-primary]="headerSortDirection === 'desc'"
1458
+ ></i>
1459
+ </span>
1460
+ }
1461
+ </div>
1462
+ </ng-template>
1463
+ <ng-template #cell let-row>
1464
+ <div
1465
+ class="ax-w-full"
1466
+ [class.ax-flex]="getCellUseFlexLayout()"
1467
+ [class.ax-gap-2]="expandHandler"
1468
+ [class.ax-items-center]="getCellUseFlexLayout()"
1469
+ [style.justify-content]="getCellJustifyContent()"
1470
+ >
1471
+ @if (expandHandler) {
1472
+ <div
1473
+ (click)="handleExpandRow(row)"
1474
+ class="ax-expand-handler"
1475
+ [class.ax-invisible]="!hasExpandableRow(row)"
1476
+ id="ax-expand-handler-container"
1477
+ [style.padding-inline-start.rem]="row.data?.__meta__?.level * 2"
1478
+ >
1479
+ @if (loadingRow() === row) {
1480
+ <i class="fas fa-spinner-third ax-animate-twSpin ax-animate-infinite"></i>
1481
+ } @else {
1482
+ @if (row.data?.__meta__?.expanded) {
1483
+ <i [class]="customCollapseIcon || 'far fa-minus-square ax-text-md ax-opacity-75'"></i>
1484
+ } @else {
1485
+ <i [class]="customExpandIcon || 'far fa-plus-square ax-text-md ax-opacity-75'"></i>
1486
+ }
1487
+ }
1488
+ </div>
1489
+ }
1490
+ @if (component && row?.data) {
1491
+ <ng-container
1492
+ *ngComponentOutlet="component; injector: getRowInjector(row.data); inputs: getInputs(row.data)"
1493
+ ></ng-container>
1494
+ }
1495
+ </div>
1496
+ </ng-template>
1497
+ <ng-template #footer></ng-template>
1498
+ `, isInline: true, styles: [".axp-widget-column-header--sortable{cursor:pointer}.axp-widget-column-header__caption{min-width:0}.axp-widget-column-header__caption--with-sort{flex:1 1 0}.axp-widget-column-header__sort{opacity:0;transition:opacity .15s ease}.axp-widget-column-header--sortable:hover .axp-widget-column-header__sort,.axp-widget-column-header--sortable:focus-within .axp-widget-column-header__sort,.axp-widget-column-header--sort-active .axp-widget-column-header__sort{opacity:1}.axp-widget-column-header__sort-priority{line-height:1}:host-context(.ax-data-table-head-cell.ax-interactive:hover) .axp-widget-column-header__sort,:host-context(.ax-data-table-head-cell.ax-interactive:focus-within) .axp-widget-column-header__sort,:host-context(.ax-data-table-head-cell) .axp-widget-column-header--sort-active .axp-widget-column-header__sort{opacity:1}\n"], 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 }); }
1499
+ }
1500
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetColumnRendererComponent, decorators: [{
1501
+ type: Component,
1502
+ args: [{ selector: 'axp-widget-column-renderer', template: `
1503
+ <ng-template #header>
1504
+ <div
1505
+ class="axp-widget-column-header ax-w-full ax-flex ax-items-center ax-select-none"
1506
+ [class.axp-widget-column-header--sortable]="sortEnabled"
1507
+ [class.axp-widget-column-header--sort-active]="headerSortDirection === 'asc' || headerSortDirection === 'desc'"
1508
+ [class.ax-gap-1]="!sortEnabled"
1509
+ [style.justify-content]="sortEnabled ? 'flex-start' : getHeaderJustifyContent()"
1510
+ (click)="onHeaderClick($event)"
1511
+ >
1512
+ <span
1513
+ class="axp-widget-column-header__caption ax-truncate"
1514
+ [class.axp-widget-column-header__caption--with-sort]="sortEnabled"
1515
+ [style.text-align]="sortEnabled ? getHeaderTextAlign() : null"
1516
+ >{{ caption | translate | async }}</span>
1517
+ @if (sortEnabled) {
1518
+ <span class="axp-widget-column-header__sort ax-flex ax-items-center ax-gap-0.5 ax-shrink-0">
1519
+ @if (headerSortPriority !== undefined && headerSortPriority > 1) {
1520
+ <span class="axp-widget-column-header__sort-priority ax-text-xs ax-opacity-70">{{ headerSortPriority }}</span>
1521
+ }
1522
+ <i
1523
+ class="fa-solid fa-arrow-up-long ax-text-neutral-400"
1524
+ [class.ax-text-primary]="headerSortDirection === 'asc'"
1525
+ ></i>
1526
+ <i
1527
+ class="fa-solid fa-arrow-down-long ax-text-neutral-400"
1528
+ [class.ax-text-primary]="headerSortDirection === 'desc'"
1529
+ ></i>
1530
+ </span>
1531
+ }
1532
+ </div>
1533
+ </ng-template>
1534
+ <ng-template #cell let-row>
1535
+ <div
1536
+ class="ax-w-full"
1537
+ [class.ax-flex]="getCellUseFlexLayout()"
1538
+ [class.ax-gap-2]="expandHandler"
1539
+ [class.ax-items-center]="getCellUseFlexLayout()"
1540
+ [style.justify-content]="getCellJustifyContent()"
1541
+ >
1542
+ @if (expandHandler) {
1543
+ <div
1544
+ (click)="handleExpandRow(row)"
1545
+ class="ax-expand-handler"
1546
+ [class.ax-invisible]="!hasExpandableRow(row)"
1547
+ id="ax-expand-handler-container"
1548
+ [style.padding-inline-start.rem]="row.data?.__meta__?.level * 2"
1549
+ >
1550
+ @if (loadingRow() === row) {
1551
+ <i class="fas fa-spinner-third ax-animate-twSpin ax-animate-infinite"></i>
1552
+ } @else {
1553
+ @if (row.data?.__meta__?.expanded) {
1554
+ <i [class]="customCollapseIcon || 'far fa-minus-square ax-text-md ax-opacity-75'"></i>
1555
+ } @else {
1556
+ <i [class]="customExpandIcon || 'far fa-plus-square ax-text-md ax-opacity-75'"></i>
1557
+ }
1558
+ }
1559
+ </div>
1560
+ }
1561
+ @if (component && row?.data) {
1562
+ <ng-container
1563
+ *ngComponentOutlet="component; injector: getRowInjector(row.data); inputs: getInputs(row.data)"
1564
+ ></ng-container>
1565
+ }
1566
+ </div>
1567
+ </ng-template>
1568
+ <ng-template #footer></ng-template>
1569
+ `, providers: [
1570
+ AXPWidgetCoreService,
1571
+ { provide: AXDataTableColumnComponent, useExisting: AXPWidgetColumnRendererComponent },
1572
+ ], changeDetection: ChangeDetectionStrategy.OnPush, inputs: ['caption'], standalone: false, styles: [".axp-widget-column-header--sortable{cursor:pointer}.axp-widget-column-header__caption{min-width:0}.axp-widget-column-header__caption--with-sort{flex:1 1 0}.axp-widget-column-header__sort{opacity:0;transition:opacity .15s ease}.axp-widget-column-header--sortable:hover .axp-widget-column-header__sort,.axp-widget-column-header--sortable:focus-within .axp-widget-column-header__sort,.axp-widget-column-header--sort-active .axp-widget-column-header__sort{opacity:1}.axp-widget-column-header__sort-priority{line-height:1}:host-context(.ax-data-table-head-cell.ax-interactive:hover) .axp-widget-column-header__sort,:host-context(.ax-data-table-head-cell.ax-interactive:focus-within) .axp-widget-column-header__sort,:host-context(.ax-data-table-head-cell) .axp-widget-column-header--sort-active .axp-widget-column-header__sort{opacity:1}\n"] }]
1573
+ }], propDecorators: { customExpandIcon: [{
1574
+ type: Input
1575
+ }], customCollapseIcon: [{
1576
+ type: Input
1577
+ }], customWidth: [{
1578
+ type: Input
1579
+ }], node: [{
1580
+ type: Input,
1581
+ args: [{ required: true }]
1582
+ }], footerTemplate: [{
1583
+ type: Input
1584
+ }], _contentFooterTemplate: [{
1585
+ type: ViewChild,
1586
+ args: ['footer']
1587
+ }], expandHandler: [{
1588
+ type: Input
1589
+ }], sortEnabled: [{
1590
+ type: Input
1591
+ }], headerSortDirection: [{
1592
+ type: Input
1593
+ }], headerSortPriority: [{
1594
+ type: Input
1595
+ }], columnFixed: [{
1596
+ type: Input
1597
+ }], sortToggle: [{
1598
+ type: Output
1599
+ }], cellTemplate: [{
1600
+ type: Input
1601
+ }], _contentCellTemplate: [{
1602
+ type: ViewChild,
1603
+ args: ['cell']
1604
+ }], headerTemplate: [{
1605
+ type: Input
1606
+ }], _contentHeaderTemplate: [{
1607
+ type: ViewChild,
1608
+ args: ['header']
1609
+ }] } });
1610
+
1611
+ /** Continuous idle period required after widgets settle before committing baseline. */
1612
+ const FORM_SAVED_BASELINE_IDLE_MS = 500;
1613
+ /** Poll interval while waiting for widgets to settle (~20s max). */
1614
+ const FORM_SAVED_BASELINE_POLL_MS = 250;
1615
+ /** Max poll attempts for settle / ready (~20s); applies even when widgets are registered. */
1616
+ const FORM_SAVED_BASELINE_MAX_SETTLE_POLLS = 80;
1617
+ class AXPWidgetContainerComponent {
1618
+ set context(value) {
1619
+ this.syncExternalContext(value);
1620
+ }
1621
+ set functions(v) {
1622
+ this.builderService.setFunctions(v);
1623
+ }
1624
+ constructor() {
1625
+ this.contextService = inject(AXPContextStore);
1626
+ this.builderService = inject(AXPWidgetCoreService);
1627
+ this.onContextChanged = new EventEmitter();
1628
+ this.wasHydrating = false;
1629
+ this.isSavedCommitted = computed(() => this.contextService.isSavedCommitted(), ...(ngDevMode ? [{ debugName: "isSavedCommitted" }] : /* istanbul ignore next */ []));
1630
+ this.isFormDirty = computed(() => {
1631
+ if (!this.contextService.isSavedCommitted()) {
1632
+ return false;
1633
+ }
1634
+ this.contextService.data();
1635
+ return this.contextService.isDirty();
1636
+ }, ...(ngDevMode ? [{ debugName: "isFormDirty" }] : /* istanbul ignore next */ []));
1637
+ this.status = computed(() => this.builderService.status(), ...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
1638
+ this.isBusy = computed(() => this.builderService.isBusy(), ...(ngDevMode ? [{ debugName: "isBusy" }] : /* istanbul ignore next */ []));
1639
+ this.isHydrating = computed(() => this.builderService.isHydrating(), ...(ngDevMode ? [{ debugName: "isHydrating" }] : /* istanbul ignore next */ []));
1640
+ /** True when registered widgets exist and none are rendering or hydrating. */
1641
+ this.isWidgetsReady = computed(() => {
1642
+ const count = this.builderService.registeredWidgetsCount();
1643
+ if (count === 0) {
1644
+ return false;
1645
+ }
1646
+ return this.builderService.isStructureReady() && !this.builderService.isBusy();
1647
+ }, ...(ngDevMode ? [{ debugName: "isWidgetsReady" }] : /* istanbul ignore next */ []));
1648
+ effect(() => {
1649
+ const isDirty = this.isFormDirty();
1650
+ const data = this.contextService.data();
1651
+ const changeEvent = this.contextService.changeEvent();
1652
+ if (!this.contextService.isChanged() && this.lastEmittedFormDirty === isDirty) {
1653
+ return;
1654
+ }
1655
+ this.lastEmittedFormDirty = isDirty;
1656
+ this.onContextChanged.emit({
1657
+ ...changeEvent,
1658
+ data: cloneDeep(data),
1659
+ isFormDirty: isDirty,
1660
+ });
1661
+ });
1662
+ effect(() => {
1663
+ if (this.contextService.isSavedCommitted()) {
1664
+ return;
1665
+ }
1666
+ this.builderService.registeredWidgetsCount();
1667
+ this.builderService.isHydrating();
1668
+ this.builderService.isStructureReady();
1669
+ if (this.builderService.registeredWidgetsCount() === 0) {
1670
+ return;
1671
+ }
1672
+ untracked(() => void this.settleSavedBaseline());
1673
+ });
1674
+ effect(() => {
1675
+ const hydrating = this.builderService.isHydrating();
1676
+ const savedCommitted = this.contextService.isSavedCommitted();
1677
+ if (!savedCommitted) {
1678
+ this.wasHydrating = hydrating;
1679
+ return;
1680
+ }
1681
+ if (this.wasHydrating && !hydrating) {
1682
+ untracked(() => this.scheduleRebaselineAfterHydration());
1683
+ }
1684
+ this.wasHydrating = hydrating;
1685
+ });
1686
+ }
1687
+ refresh() {
1688
+ this.builderService.refresh();
1689
+ }
1690
+ find(name) {
1691
+ return this.builderService.waitForWidget(name);
1692
+ }
1693
+ syncExternalContext(value) {
1694
+ const next = cloneDeep(value ?? {});
1695
+ const current = this.contextService.data();
1696
+ if (isEqual(current, next)) {
1697
+ return;
1698
+ }
1699
+ if (this.contextService.isSavedCommitted() && this.isFormDirty()) {
1700
+ return;
1701
+ }
1702
+ if (this.contextService.isSavedCommitted()) {
1703
+ this.builderService.clearWidgets();
1704
+ this.contextService.set(next);
1705
+ void this.settleSavedBaseline();
1706
+ return;
1707
+ }
1708
+ if (this.contextService.isChanged()) {
1709
+ return;
1710
+ }
1711
+ this.builderService.clearWidgets();
1712
+ this.contextService.set(next);
1713
+ void this.settleSavedBaseline();
1714
+ }
1715
+ /** Commits the current data as saved after widget hydration settles. */
1716
+ settleSavedBaseline() {
1717
+ if (this.settlePromise) {
1718
+ return this.settlePromise;
1719
+ }
1720
+ this.clearSavedBaselineTimer();
1721
+ this.settlePromise = this.waitUntilSettled()
1722
+ .then(() => {
1723
+ this.contextService.commitSaved();
1724
+ this.lastEmittedFormDirty = undefined;
1725
+ })
1726
+ .finally(() => {
1727
+ this.settlePromise = undefined;
1728
+ });
1729
+ return this.settlePromise;
1730
+ }
1731
+ /** Resolves when widgets stay registered, structure-ready, and idle for {@link FORM_SAVED_BASELINE_IDLE_MS}. */
1732
+ waitUntilSettled() {
1733
+ return new Promise((resolve) => {
1734
+ let idleRetries = 0;
1735
+ let stableTimer;
1736
+ let pollTimer;
1737
+ const finish = (reason) => {
1738
+ if (pollTimer) {
1739
+ clearTimeout(pollTimer);
1740
+ }
1741
+ if (stableTimer) {
1742
+ clearTimeout(stableTimer);
1743
+ }
1744
+ resolve();
1745
+ };
1746
+ const poll = () => {
1747
+ const snapshot = this.builderService.getWidgetStateSnapshot();
1748
+ const isSettled = snapshot.count > 0 &&
1749
+ snapshot.isStructureReady &&
1750
+ !snapshot.isHydrating &&
1751
+ !snapshot.isRendering;
1752
+ if (isSettled) {
1753
+ if (!stableTimer) {
1754
+ stableTimer = setTimeout(() => finish('stable-idle'), FORM_SAVED_BASELINE_IDLE_MS);
1755
+ }
1756
+ return;
1757
+ }
1758
+ if (stableTimer) {
1759
+ clearTimeout(stableTimer);
1760
+ stableTimer = undefined;
1761
+ }
1762
+ idleRetries += 1;
1763
+ if (idleRetries >= FORM_SAVED_BASELINE_MAX_SETTLE_POLLS) {
1764
+ finish('max-polls');
1765
+ return;
1766
+ }
1767
+ pollTimer = setTimeout(poll, FORM_SAVED_BASELINE_POLL_MS);
1768
+ };
1769
+ pollTimer = setTimeout(poll, FORM_SAVED_BASELINE_POLL_MS);
1770
+ });
1771
+ }
1772
+ async replaceContext(value) {
1773
+ this.clearSavedBaselineTimer();
1774
+ this.settlePromise = undefined;
1775
+ this.contextService.set(cloneDeep(value));
1776
+ // Commit immediately so save/replace is clean even when `set()` no-ops (data already matches).
1777
+ this.contextService.commitSaved();
1778
+ this.lastEmittedFormDirty = undefined;
1779
+ await this.settleSavedBaseline();
1780
+ }
1781
+ revertAndSettle() {
1782
+ this.clearSavedBaselineTimer();
1783
+ this.settlePromise = undefined;
1784
+ this.contextService.revertToSaved();
1785
+ this.lastEmittedFormDirty = undefined;
1786
+ }
1787
+ /** Re-commits baseline after post-load hydration when the user has not edited the form. */
1788
+ scheduleRebaselineAfterHydration() {
1789
+ this.clearSavedBaselineTimer();
1790
+ this.savedBaselineTimer = setTimeout(() => {
1791
+ this.savedBaselineTimer = undefined;
1792
+ if (this.contextService.hasUserEditedSinceCommit()) {
1793
+ return;
1794
+ }
1795
+ void this.waitUntilSettled().then(() => {
1796
+ if (this.contextService.hasUserEditedSinceCommit()) {
1797
+ return;
1798
+ }
1799
+ this.contextService.commitSaved();
1800
+ this.lastEmittedFormDirty = undefined;
1801
+ });
1802
+ }, FORM_SAVED_BASELINE_IDLE_MS);
1803
+ }
1804
+ clearSavedBaselineTimer() {
1805
+ if (this.savedBaselineTimer) {
1806
+ clearTimeout(this.savedBaselineTimer);
1807
+ this.savedBaselineTimer = undefined;
1808
+ }
1809
+ }
1810
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1811
+ 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 }); }
1812
+ }
1813
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetContainerComponent, decorators: [{
1814
+ type: Component,
1815
+ args: [{
1816
+ selector: 'axp-widgets-container',
1817
+ template: `<ng-content></ng-content>`,
1818
+ changeDetection: ChangeDetectionStrategy.OnPush,
1819
+ host: { style: 'display: contents;' },
1820
+ providers: [AXPWidgetCoreService, AXPContextStore],
1821
+ standalone: false,
1822
+ }]
1823
+ }], ctorParameters: () => [], propDecorators: { onContextChanged: [{
1824
+ type: Output
1825
+ }], context: [{
1826
+ type: Input
1827
+ }], functions: [{
1828
+ type: Input
1829
+ }] } });
1830
+
1831
+ class AXPWidgetPlaceholderComponent {
1832
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetPlaceholderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1833
+ 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>
1834
+ <ax-skeleton class="ax-w-full ax-h-10 ax-rounded-md"></ax-skeleton>
1835
+ </div>`, isInline: true, dependencies: [{ kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i1$1.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1836
+ }
1837
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetPlaceholderComponent, decorators: [{
1838
+ type: Component,
1839
+ args: [{
1840
+ selector: 'axp-widget-placeholder',
1841
+ template: `<div>
1842
+ <ax-skeleton class="ax-w-full ax-h-10 ax-rounded-md"></ax-skeleton>
1843
+ </div>`,
1844
+ changeDetection: ChangeDetectionStrategy.OnPush,
1845
+ imports: [AXSkeletonModule],
1846
+ standalone: true,
1847
+ }]
1848
+ }] });
1849
+
1850
+ class AXPWidgetRendererDirective {
1851
+ //#endregion
1852
+ //#endregion
1853
+ constructor() {
1854
+ this.parentNode = input(...(ngDevMode ? [undefined, { debugName: "parentNode" }] : /* istanbul ignore next */ []));
1855
+ this.index = input(...(ngDevMode ? [undefined, { debugName: "index" }] : /* istanbul ignore next */ []));
1856
+ this.mode = input.required(...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
1857
+ this.node = input.required(...(ngDevMode ? [{ debugName: "node" }] : /* istanbul ignore next */ []));
1858
+ this._options = signal({}, ...(ngDevMode ? [{ debugName: "_options" }] : /* istanbul ignore next */ []));
1859
+ this.options = this._options.asReadonly();
1860
+ this.onOptionsChanged = output();
1861
+ this.onValueChanged = output();
1862
+ this.onLoad = output();
1863
+ //#region ---- Public API ----
1864
+ /**
1865
+ * Signal that emits the component reference when it's ready
1866
+ */
1867
+ this._componentRefSignal = signal(null, ...(ngDevMode ? [{ debugName: "_componentRefSignal" }] : /* istanbul ignore next */ []));
1868
+ this.componentRefSignal = this._componentRefSignal.asReadonly();
1869
+ //#endregion
1870
+ //#region ---- Properties ----
1871
+ this.mergedOptions = signal({}, ...(ngDevMode ? [{ debugName: "mergedOptions" }] : /* istanbul ignore next */ []));
1872
+ this.injector = inject(Injector);
1873
+ this.builderService = inject(AXPWidgetCoreService);
1874
+ this.contextService = inject(AXPContextStore);
1875
+ this.widgetRegistery = inject(AXPWidgetRegistryService);
1876
+ this.unsubscriber = inject(AXUnsubscriber);
1877
+ this.translateService = inject(AXTranslationService);
1878
+ this.widgetService = inject(AXPWidgetRegistryService);
1879
+ this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
1880
+ this.viewContainerRef = inject(ViewContainerRef);
1881
+ this.isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
1882
+ this.isVisible = signal(true, ...(ngDevMode ? [{ debugName: "isVisible" }] : /* istanbul ignore next */ []));
1883
+ this.expressionEvaluators = new Map();
1884
+ this.renderTimeoutId = null;
1885
+ this.renderGeneration = 0;
1886
+ this.hasInitialRender = false;
1887
+ this.onContextChanged = new Subject();
1888
+ this.onLoadEvent = new Subject();
1889
+ //#region ---- Performance Optimization Properties ----
1890
+ this.contextUpdateQueue = new Set();
1891
+ this.contextUpdateTimeout = null;
1892
+ this.CONTEXT_UPDATE_DEBOUNCE_MS = 4; // ~250fps for large forms
1893
+ // Removed visibility detection - not needed for current implementation
1894
+ // Expression result caching
1895
+ this.expressionCache = new Map();
1896
+ this.EXPRESSION_CACHE_TTL = 500; // Cache for 500ms (increased for better hit rate)
1897
+ // Options change tracking
1898
+ this.lastAppliedOptions = null;
1899
+ // Expression result tracking
1900
+ this.lastExpressionResults = new Map();
1901
+ // Buffer for context changes that happen before initial render completes
1902
+ this.preRenderContextQueue = new Set();
1903
+ /**
1904
+ * Last context {@link AXPContextChangeEvent} handled by this renderer.
1905
+ * Prevents re-queueing when the effect re-runs while `lastChange` in the store is unchanged
1906
+ * (e.g. cascade refresh clears an already-empty dependent lookup).
1907
+ */
1908
+ this.lastProcessedContextChangeRef = null;
1909
+ effect(async () => {
1910
+ const changed = this.contextService.changeEvent();
1911
+ // Don't trigger re-render during initial setup
1912
+ if (!this.hasInitialRender) {
1913
+ // Handle initial context set (state: 'initiated' with empty path means full context update)
1914
+ if (changed.state === 'initiated' && (!changed.path || changed.path === '')) {
1915
+ // Mark that we need to re-evaluate all expressions when initial render completes
1916
+ // This handles the case where context is set before/during widget initialization
1917
+ this.preRenderContextQueue.add('*'); // Use '*' as a marker for full context initialization
1918
+ // console.log(`📝 [${this.node().type}] Buffered initial context set`);
1919
+ }
1920
+ else if (changed.path) {
1921
+ this.preRenderContextQueue.add(changed.path);
1922
+ // console.log(`📝 [${this.node().type}] Buffered pre-render context change: ${changed.path}`);
1923
+ }
1924
+ return;
1925
+ }
1926
+ // CRITICAL PERFORMANCE FIX: Only respond to relevant context changes
1927
+ // Handle initial context set (full context update)
1928
+ if (changed.state === 'initiated' && (!changed.path || changed.path === '')) {
1929
+ // Full context initialization - trigger re-evaluation of all expressions
1930
+ if (this.markContextChangeHandled(changed)) {
1931
+ this.clearExpressionCache();
1932
+ if (this.expressionEvaluators.size > 0) {
1933
+ await this.updateOptionsBasedOnContext();
1934
+ this.applyOptions();
1935
+ }
1936
+ }
1937
+ }
1938
+ else if (changed.path && this.isRelevantContextChange(changed.path)) {
1939
+ const changedPath = changed.path;
1940
+ if (this.markContextChangeHandled(changed)) {
1941
+ this.queueContextUpdate(changedPath);
1942
+ }
1943
+ }
1944
+ await this.updateVisibility();
1945
+ });
1946
+ this.builderService.onRefresh.pipe(this.unsubscriber.takeUntilDestroy).subscribe(async () => {
1947
+ await this.processBatchedUpdates();
1948
+ });
1949
+ }
1950
+ //#region ---- Expression Caching Methods ----
1951
+ getCachedExpressionResult(expressionKey) {
1952
+ const cached = this.expressionCache.get(expressionKey);
1953
+ if (cached && performance.now() - cached.timestamp < this.EXPRESSION_CACHE_TTL) {
1954
+ // console.log(`💾 [${this.node().type}] Using cached expression result for '${expressionKey}'`);
1955
+ return cached.value;
1956
+ }
1957
+ return null;
1958
+ }
1959
+ setCachedExpressionResult(expressionKey, value) {
1960
+ this.expressionCache.set(expressionKey, {
1961
+ value,
1962
+ timestamp: performance.now(),
1963
+ });
1964
+ }
1965
+ clearExpressionCache() {
1966
+ this.expressionCache.clear();
1967
+ }
1968
+ isPathAffectingExpressions(changedPath) {
1969
+ // console.log(
1970
+ // `🔍 [${this.node().type}] Checking if path '${changedPath}' affects expressions. Expression count: ${this.expressionEvaluators.size}`,
1971
+ // );
1972
+ // If widget has no expressions, no need to clear cache
1973
+ if (this.expressionEvaluators.size === 0) {
1974
+ // console.log(`🔍 [${this.node().type}] Path '${changedPath}' - no expressions, keeping cache`);
1975
+ return false;
1976
+ }
1977
+ // Use the same logic as hasExpressionDependency to check for actual dependencies
1978
+ if (this.hasExpressionDependency(changedPath)) {
1979
+ // console.log(`🔍 [${this.node().type}] Path '${changedPath}' affects expressions - clearing cache`);
1980
+ return true;
1981
+ }
1982
+ // console.log(`🔍 [${this.node().type}] Path '${changedPath}' does not affect expressions - keeping cache`);
1983
+ return false;
1984
+ }
1985
+ //#endregion
1986
+ //#region ---- Context Batching Methods ----
1987
+ /**
1988
+ * Returns false when this renderer already handled the same store change event.
1989
+ * Angular effects can re-run after async work while `changeEvent()` still references
1990
+ * the previous `lastChange` object.
1991
+ */
1992
+ markContextChangeHandled(changed) {
1993
+ if (this.lastProcessedContextChangeRef === changed) {
1994
+ return false;
1995
+ }
1996
+ this.lastProcessedContextChangeRef = changed;
1997
+ return true;
1998
+ }
1999
+ queueContextUpdate(path) {
2000
+ if (path) {
2001
+ // console.log(`🔄 [${this.node().type}] Queueing context update for path: ${path}`);
2002
+ this.contextUpdateQueue.add(path);
2003
+ // Clear existing timeout
2004
+ if (this.contextUpdateTimeout) {
2005
+ clearTimeout(this.contextUpdateTimeout);
2006
+ }
2007
+ // Debounce updates
2008
+ this.contextUpdateTimeout = setTimeout(() => {
2009
+ // console.log(`⚡ [${this.node().type}] Processing batched updates for ${this.contextUpdateQueue.size} paths`);
2010
+ this.processBatchedUpdates();
2011
+ }, this.CONTEXT_UPDATE_DEBOUNCE_MS);
2012
+ }
2013
+ }
2014
+ async processBatchedUpdates() {
2015
+ if (this.contextUpdateQueue.size === 0)
2016
+ return;
2017
+ const startTime = performance.now();
2018
+ const paths = Array.from(this.contextUpdateQueue);
2019
+ this.contextUpdateQueue.clear();
2020
+ // console.log(`📊 [${this.node().type}] Processing ${paths.length} paths:`, paths);
2021
+ // Clear expression cache only if changed paths affect this widget's expressions
2022
+ const shouldClearCache = paths.some((path) => this.isPathAffectingExpressions(path));
2023
+ if (shouldClearCache) {
2024
+ // console.log(`🗑️ [${this.node().type}] Clearing expression cache due to expression-affecting path change`);
2025
+ this.clearExpressionCache();
2026
+ }
2027
+ // Process updates in batches
2028
+ const optionsStartTime = performance.now();
2029
+ const hasOptionsUpdate = await this.updateOptionsBasedOnContext();
2030
+ const optionsTime = performance.now() - optionsStartTime;
2031
+ if (typeof hasOptionsUpdate === 'number' && hasOptionsUpdate > 0) {
2032
+ // console.log(`🔧 [${this.node().type}] Options updated (${optionsTime.toFixed(2)}ms)`);
2033
+ this.applyOptions();
2034
+ }
2035
+ // Check visibility for any of the changed paths
2036
+ const visibilityStartTime = performance.now();
2037
+ const visibilityNeedsUpdate = paths.some((path) => this.hasVisibilityDependency(path));
2038
+ if (visibilityNeedsUpdate) {
2039
+ // console.log(`👁️ [${this.node().type}] Visibility needs update`);
2040
+ await this.updateVisibility();
2041
+ }
2042
+ const visibilityTime = performance.now() - visibilityStartTime;
2043
+ // Emit context changes
2044
+ paths.forEach((path) => {
2045
+ this.onContextChanged.next({ path });
2046
+ });
2047
+ const totalTime = performance.now() - startTime;
2048
+ // console.log(
2049
+ // `✅ [${this.node().type}] Batch processing completed in ${totalTime.toFixed(2)}ms (options: ${optionsTime.toFixed(2)}ms, visibility: ${visibilityTime.toFixed(2)}ms)`,
2050
+ // );
2051
+ }
2052
+ //#endregion
2053
+ //#region ---- Context Relevance Filtering ----
2054
+ isRelevantContextChange(changedPath) {
2055
+ const node = this.node();
2056
+ // 1. Direct path match - widget's own field
2057
+ if (node.path === changedPath) {
2058
+ return true;
2059
+ }
2060
+ // 2. Parent path match - widget is inside the changed container
2061
+ if (node.path && changedPath.startsWith(node.path + '.')) {
2062
+ return true;
2063
+ }
2064
+ // 3. Child path match - changed field is inside this widget's container
2065
+ if (node.path && node.path.startsWith(changedPath + '.')) {
2066
+ return true;
2067
+ }
2068
+ // 4. Expression dependency check - if widget has expressions that depend on this path
2069
+ if (this.hasExpressionDependency(changedPath)) {
2070
+ return true;
2071
+ }
2072
+ // 5. Trigger dependency check - if widget has triggers that depend on this path
2073
+ if (this.hasTriggerDependency(changedPath)) {
2074
+ return true;
2075
+ }
2076
+ // 7. Visibility dependency check - if widget's visibility depends on this path
2077
+ if (this.hasVisibilityDependency(changedPath)) {
2078
+ return true;
2079
+ }
2080
+ return false;
2081
+ }
2082
+ /**
2083
+ * True when a context store path used in an expression (`context.eval`, `context.options`) is
2084
+ * affected by `changedPath`.
2085
+ */
2086
+ contextStorePathDependsOnChangedPath(evalPath, changedPath) {
2087
+ const normalizePath = (p) => {
2088
+ if (!p)
2089
+ return p;
2090
+ const parts = p.split('.');
2091
+ const last = parts[parts.length - 1];
2092
+ if (last && last.endsWith('Id')) {
2093
+ parts.splice(parts.length - 1, 1, last.slice(0, -2), 'id');
2094
+ }
2095
+ return parts.join('.');
2096
+ };
2097
+ const isSegmentSuffix = (a, b) => {
2098
+ if (!a || !b)
2099
+ return false;
2100
+ const pa = a.split('.');
2101
+ const pb = b.split('.');
2102
+ if (pb.length > pa.length)
2103
+ return false;
2104
+ for (let i = 1; i <= pb.length; i++) {
2105
+ if (pa[pa.length - i] !== pb[pb.length - i])
2106
+ return false;
2107
+ }
2108
+ return true;
2109
+ };
2110
+ const evalNorm = normalizePath(evalPath);
2111
+ const changedNorm = normalizePath(changedPath);
2112
+ const rawMatch = evalPath === changedPath ||
2113
+ evalPath.startsWith(changedPath + '.') ||
2114
+ changedPath.startsWith(evalPath + '.') ||
2115
+ isSegmentSuffix(evalPath, changedPath) ||
2116
+ isSegmentSuffix(changedPath, evalPath);
2117
+ const normMatch = evalNorm === changedNorm ||
2118
+ evalNorm.startsWith(changedNorm + '.') ||
2119
+ changedNorm.startsWith(evalNorm + '.') ||
2120
+ isSegmentSuffix(evalNorm, changedNorm) ||
2121
+ isSegmentSuffix(changedNorm, evalNorm);
2122
+ if (rawMatch || normMatch) {
2123
+ return true;
2124
+ }
2125
+ return this.isPathAlias(evalPath, changedPath);
2126
+ }
2127
+ /**
2128
+ * Whether an expression template string reads context store paths affected by `changedPath`.
2129
+ */
2130
+ expressionTextDependsOnContextPath(expressionValue, changedPath) {
2131
+ const contextEvalRegex = /context\.eval\(['"]([^'\"]+)['"]\)/g;
2132
+ let match;
2133
+ while ((match = contextEvalRegex.exec(expressionValue)) !== null) {
2134
+ if (this.contextStorePathDependsOnChangedPath(match[1], changedPath)) {
2135
+ return true;
2136
+ }
2137
+ }
2138
+ const contextOptionsQuoted = /context\.options\(\s*['"]([^'"]*)['"]\s*\)/g;
2139
+ while ((match = contextOptionsQuoted.exec(expressionValue)) !== null) {
2140
+ const sub = match[1];
2141
+ const evalPath = sub === '' ? 'options' : `options.${sub}`;
2142
+ if (this.contextStorePathDependsOnChangedPath(evalPath, changedPath)) {
2143
+ return true;
2144
+ }
2145
+ }
2146
+ const contextOptionsEmpty = /context\.options\(\s*\)/g;
2147
+ while ((match = contextOptionsEmpty.exec(expressionValue)) !== null) {
2148
+ if (this.contextStorePathDependsOnChangedPath('options', changedPath)) {
2149
+ return true;
2150
+ }
2151
+ }
2152
+ return false;
2153
+ }
2154
+ hasExpressionDependency(changedPath) {
2155
+ for (const [path, evaluator] of this.expressionEvaluators) {
2156
+ if (path.includes(changedPath)) {
2157
+ return true;
2158
+ }
2159
+ const node = this.node();
2160
+ const expressionValue = this.getExpressionValueFromNode(node, path);
2161
+ if (expressionValue && typeof expressionValue === 'string') {
2162
+ if (this.expressionTextDependsOnContextPath(expressionValue, changedPath)) {
2163
+ return true;
2164
+ }
2165
+ }
2166
+ }
2167
+ return false;
2168
+ }
2169
+ isPathAlias(evalPath, changedPath) {
2170
+ // Dynamic path alias detection based on 'Id' suffix pattern
2171
+ // Examples: typeId.id <-> type.id, categoryId.name <-> category.name
2172
+ // Check if evalPath ends with 'Id' and has a property
2173
+ if (evalPath.endsWith('Id') && evalPath.includes('.')) {
2174
+ const basePath = evalPath.substring(0, evalPath.lastIndexOf('Id'));
2175
+ const property = evalPath.substring(evalPath.lastIndexOf('.') + 1);
2176
+ const mappedPath = `${basePath}.${property}`;
2177
+ if (changedPath === mappedPath) {
2178
+ // console.log(`🔍 [${this.node().type}] Path alias detected: '${evalPath}' <-> '${changedPath}'`);
2179
+ return true;
2180
+ }
2181
+ }
2182
+ // Check if changedPath ends with 'Id' and has a property
2183
+ if (changedPath.endsWith('Id') && changedPath.includes('.')) {
2184
+ const basePath = changedPath.substring(0, changedPath.lastIndexOf('Id'));
2185
+ const property = changedPath.substring(changedPath.lastIndexOf('.') + 1);
2186
+ const mappedPath = `${basePath}.${property}`;
2187
+ if (evalPath === mappedPath) {
2188
+ // console.log(`🔍 [${this.node().type}] Path alias detected: '${evalPath}' <-> '${changedPath}'`);
2189
+ return true;
2190
+ }
2191
+ }
2192
+ // Check for direct 'Id' suffix mapping (e.g., typeId <-> type.id)
2193
+ if (evalPath.endsWith('Id') && !evalPath.includes('.')) {
2194
+ const basePath = evalPath.substring(0, evalPath.lastIndexOf('Id'));
2195
+ const mappedPath = `${basePath}.id`;
2196
+ if (changedPath === mappedPath) {
2197
+ // console.log(`🔍 [${this.node().type}] Path alias detected: '${evalPath}' <-> '${changedPath}'`);
2198
+ return true;
2199
+ }
2200
+ }
2201
+ if (changedPath.endsWith('Id') && !changedPath.includes('.')) {
2202
+ const basePath = changedPath.substring(0, changedPath.lastIndexOf('Id'));
2203
+ const mappedPath = `${basePath}.id`;
2204
+ if (evalPath === mappedPath) {
2205
+ // console.log(`🔍 [${this.node().type}] Path alias detected: '${evalPath}' <-> '${changedPath}'`);
2206
+ return true;
2207
+ }
2208
+ }
2209
+ return false;
2210
+ }
2211
+ getExpressionValueFromNode(node, path) {
2212
+ try {
2213
+ // Navigate through the node structure to find the expression value
2214
+ const pathParts = path.split('.');
2215
+ let current = node.options || {};
2216
+ // Navigate through the path parts
2217
+ for (const part of pathParts) {
2218
+ if (current && typeof current === 'object' && part in current) {
2219
+ current = current[part];
2220
+ }
2221
+ else {
2222
+ return null;
2223
+ }
2224
+ }
2225
+ return typeof current === 'string' ? current : null;
2226
+ }
2227
+ catch (error) {
2228
+ // console.error('Error extracting expression value from node:', error);
2229
+ return null;
2230
+ }
2231
+ }
2232
+ hasTriggerDependency(changedPath) {
2233
+ const node = this.node();
2234
+ const triggers = node.triggers || node.options?.['triggers'] || this.mergedOptions()?.triggers;
2235
+ if (!triggers)
2236
+ return false;
2237
+ // Check if any trigger event depends on the changed path
2238
+ for (const trigger of triggers) {
2239
+ if (trigger.event && trigger.event.includes(changedPath)) {
2240
+ return true;
2241
+ }
2242
+ }
2243
+ return false;
2244
+ }
2245
+ hasVisibilityDependency(changedPath) {
2246
+ const node = this.node();
2247
+ const visibility = node.options?.['visible'] || this.mergedOptions()?.visible;
2248
+ if (!visibility || typeof visibility !== 'string')
2249
+ return false;
2250
+ if (this.expressionEvaluator.isExpression(visibility)) {
2251
+ return this.expressionTextDependsOnContextPath(visibility, changedPath);
2252
+ }
2253
+ return false;
2254
+ }
2255
+ //#endregion
2256
+ // Removed visibility detection methods - not needed for current implementation
2257
+ // Detect input changes
2258
+ ngOnChanges(changes) {
2259
+ if (changes['mode'] || changes['node']) {
2260
+ // Check if either 'mode' or 'node' has changed
2261
+ this.rerenderComponent();
2262
+ }
2263
+ }
2264
+ rerenderComponent() {
2265
+ if (this.renderTimeoutId) {
2266
+ clearTimeout(this.renderTimeoutId);
2267
+ this.renderTimeoutId = null;
2268
+ }
2269
+ const generation = ++this.renderGeneration;
2270
+ this.renderTimeoutId = setTimeout(async () => {
2271
+ this.renderTimeoutId = null;
2272
+ if (generation !== this.renderGeneration) {
2273
+ return;
2274
+ }
2275
+ await this.loadComponent();
2276
+ }, 0);
2277
+ }
2278
+ ngOnDestroy() {
2279
+ if (this.renderTimeoutId) {
2280
+ clearTimeout(this.renderTimeoutId);
2281
+ }
2282
+ if (this.contextUpdateTimeout) {
2283
+ clearTimeout(this.contextUpdateTimeout);
2284
+ }
2285
+ if (this.componentRef) {
2286
+ this.componentRef.destroy();
2287
+ }
2288
+ this.onLoadEvent.complete();
2289
+ }
2290
+ async loadComponent() {
2291
+ if (this.isLoading()) {
2292
+ return;
2293
+ }
2294
+ this.isLoading.set(true);
2295
+ this.lastProcessedContextChangeRef = null;
2296
+ try {
2297
+ // Destroy the existing component if it exists
2298
+ if (this.componentRef) {
2299
+ this.componentRef.destroy();
2300
+ }
2301
+ this.viewContainerRef.clear();
2302
+ // Reset the component ref signal
2303
+ this._componentRefSignal.set(null);
2304
+ //
2305
+ const widget = this.widgetRegistery.resolve(this.node().type);
2306
+ //
2307
+ const propertiesToProcess = [
2308
+ ...(widget?.properties ?? []),
2309
+ ...(widget?.components[this.mode()]?.properties ?? []),
2310
+ ]?.filter((c) => c.schema.defaultValue != null);
2311
+ // Process default values (evaluate expressions if needed)
2312
+ const props = {};
2313
+ for (const property of propertiesToProcess) {
2314
+ const defaultValue = property.schema.defaultValue;
2315
+ if (typeof defaultValue === 'string' && this.expressionEvaluator.isExpression(defaultValue)) {
2316
+ // Evaluate expression for default value
2317
+ try {
2318
+ const evaluatedValue = await this.evaluateExpression(defaultValue);
2319
+ props[property.name] = evaluatedValue;
2320
+ }
2321
+ catch (error) {
2322
+ // console.error(`Error evaluating default value expression for property ${property.name}:`, error);
2323
+ props[property.name] = cloneDeep(defaultValue); // Fallback to original value
2324
+ }
2325
+ }
2326
+ else {
2327
+ // Use static default value
2328
+ props[property.name] = cloneDeep(defaultValue);
2329
+ }
2330
+ }
2331
+ //
2332
+ this.mergedOptions.set(merge(props, widget?.options, this.node().options) || {});
2333
+ this.expressionEvaluators.clear();
2334
+ // Register expressions from widget defaults and node options to cover related-entity cases
2335
+ this.preprocessAndInitialOptions(cloneDeep(widget?.options));
2336
+ this.preprocessAndInitialOptions(cloneDeep(this.node().options));
2337
+ // Check if context is available before evaluating expressions
2338
+ // If context is empty, expressions will be re-evaluated after initial render
2339
+ const contextData = this.contextService.data();
2340
+ const hasContextData = contextData && Object.keys(contextData).length > 0;
2341
+ if (hasContextData) {
2342
+ await this.updateOptionsBasedOnContext();
2343
+ }
2344
+ else {
2345
+ // Context not yet available - expressions will be evaluated after initial render
2346
+ // when context is set (handled by the effect and preRenderContextQueue)
2347
+ }
2348
+ //
2349
+ this._options.update((val) => ({ ...val, ...this.mergedOptions() }));
2350
+ // Evaluate default value
2351
+ let defaultValue = this.node().defaultValue;
2352
+ if (typeof defaultValue === 'string' && this.expressionEvaluator.isExpression(defaultValue)) {
2353
+ defaultValue = await this.evaluateExpression(defaultValue);
2354
+ }
2355
+ //
2356
+ const tokenValue = {
2357
+ node: this.node(),
2358
+ defaultValue: defaultValue,
2359
+ options: this.mergedOptions(),
2360
+ config: widget,
2361
+ };
2362
+ const token = Injector.create({
2363
+ parent: this.injector,
2364
+ providers: [
2365
+ {
2366
+ provide: AXP_WIDGET_TOKEN,
2367
+ useValue: tokenValue,
2368
+ },
2369
+ ],
2370
+ });
2371
+ //
2372
+ const loadingRef = this.viewContainerRef.createComponent(AXPWidgetPlaceholderComponent);
2373
+ //
2374
+ const com = await widget?.components[this.mode()]?.component();
2375
+ if (!com) {
2376
+ // console.error(`${this.node().type} widget component not found with mode: ${this.mode()}`);
2377
+ return;
2378
+ }
2379
+ this.componentRef = this.viewContainerRef.createComponent(com, { injector: token });
2380
+ this.instance = this.componentRef.instance;
2381
+ this.instance.setStatus(AXPWidgetStatus.Rendering);
2382
+ this.instance.parent = this.parentNode();
2383
+ this.instance.index = this.index();
2384
+ this.instance.mode = this.mode();
2385
+ // Update the component ref signal for reactive access
2386
+ this._componentRefSignal.set(this.componentRef);
2387
+ this.instance?.onOptionsChanged?.pipe(this.unsubscriber.takeUntilDestroy).subscribe((c) => {
2388
+ this.onOptionsChanged.emit({ sender: this, widget: c.sender });
2389
+ });
2390
+ this.instance?.onValueChanged?.pipe(this.unsubscriber.takeUntilDestroy).subscribe((c) => {
2391
+ this.onValueChanged.emit({ sender: this, widget: c.sender });
2392
+ });
2393
+ await this.updateVisibility();
2394
+ await this.assignTriggers();
2395
+ //
2396
+ loadingRef.destroy();
2397
+ // Mark that initial render is complete
2398
+ this.hasInitialRender = true;
2399
+ // Re-evaluate expressions after initial render to catch late-arriving related context
2400
+ if (this.expressionEvaluators.size > 0) {
2401
+ // console.log(`🔄 [${this.node().type}] Re-evaluating expressions after initial render`);
2402
+ await this.updateOptionsBasedOnContext();
2403
+ this.applyOptions();
2404
+ }
2405
+ // Re-evaluate visibility after initial render
2406
+ await this.updateVisibility();
2407
+ // Process any buffered pre-render context changes now that the component is ready
2408
+ if (this.preRenderContextQueue.size > 0) {
2409
+ // Check if we have a full context initialization marker
2410
+ const hasFullContextInit = this.preRenderContextQueue.has('*');
2411
+ if (hasFullContextInit) {
2412
+ // Full context was initialized - clear cache and re-evaluate all expressions
2413
+ // console.log(`🚀 [${this.node().type}] Processing initial context set - full re-evaluation`);
2414
+ this.clearExpressionCache();
2415
+ this.preRenderContextQueue.clear();
2416
+ // Re-evaluate all expressions since context is now available
2417
+ if (this.expressionEvaluators.size > 0) {
2418
+ await this.updateOptionsBasedOnContext();
2419
+ this.applyOptions();
2420
+ }
2421
+ await this.updateVisibility();
2422
+ }
2423
+ else {
2424
+ // console.log(
2425
+ // `🚀 [${this.node().type}] Processing ${this.preRenderContextQueue.size} buffered pre-render changes`,
2426
+ // );
2427
+ this.preRenderContextQueue.forEach((p) => this.contextUpdateQueue.add(p));
2428
+ this.preRenderContextQueue.clear();
2429
+ await this.processBatchedUpdates();
2430
+ }
2431
+ }
2432
+ // Emit load event after widget is fully loaded
2433
+ const loadEvent = { sender: this, widget: this.instance };
2434
+ this.onLoadEvent.next(loadEvent);
2435
+ this.onLoad.emit(loadEvent);
2436
+ }
2437
+ catch (error) {
2438
+ // console.error('Error loading component:', error);
2439
+ }
2440
+ finally {
2441
+ this.isLoading.set(false);
2442
+ }
2443
+ }
2444
+ applyOptions() {
2445
+ if (!this.instance)
2446
+ return;
2447
+ const currentOptions = this.mergedOptions();
2448
+ // Check if options have actually changed
2449
+ if (this.hasOptionsChanged(currentOptions)) {
2450
+ // console.log('applyOptions', this.node().path, '- options changed');
2451
+ this._options.update((val) => ({ ...val, ...currentOptions }));
2452
+ this.instance.setOptions(currentOptions);
2453
+ this.lastAppliedOptions = cloneDeep(currentOptions); // Deep clone using Lodash
2454
+ }
2455
+ else {
2456
+ // console.log('applyOptions', this.node().path, '- no changes, skipping');
2457
+ }
2458
+ }
2459
+ hasOptionsChanged(newOptions) {
2460
+ if (!this.lastAppliedOptions) {
2461
+ return true; // First time, always apply
2462
+ }
2463
+ // Deep comparison of options using Lodash isEqual
2464
+ return !isEqual(newOptions, this.lastAppliedOptions);
2465
+ }
2466
+ // Removed deepCloneValue method - now using Lodash cloneDeep
2467
+ preprocessAndInitialOptions(obj, pathPrefix = '') {
2468
+ if (!obj) {
2469
+ return;
2470
+ }
2471
+ Object.entries(obj).forEach(([key, value]) => {
2472
+ const currentPath = pathPrefix ? `${pathPrefix}.${key}` : key;
2473
+ // CRITICAL FIX: Skip trigger actions during options processing
2474
+ //
2475
+ // PROBLEM: Trigger actions were being evaluated immediately during widget setup/options processing,
2476
+ // causing them to execute before the actual trigger event occurred. This meant triggers would fire
2477
+ // during initialization instead of when the specified context path actually changed.
2478
+ //
2479
+ // ROOT CAUSE: The expression evaluator was processing trigger action expressions (like console.log
2480
+ // or widget.setValue calls) as part of the normal options preprocessing, treating them as dynamic
2481
+ // expressions that needed immediate evaluation.
2482
+ //
2483
+ // SOLUTION: Detect when we're processing trigger actions and store them as static values without
2484
+ // expression evaluation. This ensures trigger actions are only evaluated when the trigger's
2485
+ // subscription callback actually fires (when the event condition is met).
2486
+ //
2487
+ // RESULT: Triggers now only execute when their event filters pass (e.g., when the specified
2488
+ // context path changes), not during widget initialization.
2489
+ if (currentPath.includes('triggers') && (key === 'action' || currentPath.endsWith('.action'))) {
2490
+ // Apply static values directly without expression evaluation
2491
+ this.mergedOptions.update((currentOptions) => {
2492
+ return set(currentOptions, currentPath, value);
2493
+ });
2494
+ return;
2495
+ }
2496
+ if (typeof value === 'string' && this.expressionEvaluator.isExpression(value)) {
2497
+ // Cache dynamic expression for later evaluation
2498
+ this.expressionEvaluators.set(currentPath, () => this.evaluateExpression(value));
2499
+ }
2500
+ else if (typeof value === 'object' &&
2501
+ value !== null &&
2502
+ (value.constructor === Object || Array.isArray(value))) {
2503
+ // Recursively handle nested objects
2504
+ this.preprocessAndInitialOptions(value, currentPath);
2505
+ }
2506
+ else {
2507
+ // Apply static values directly
2508
+ this.mergedOptions.update((currentOptions) => {
2509
+ return set(currentOptions, currentPath, value);
2510
+ });
2511
+ }
2512
+ });
2513
+ }
2514
+ async updateOptionsBasedOnContext() {
2515
+ // Early return if no expressions to evaluate
2516
+ if (this.expressionEvaluators.size === 0) {
2517
+ return 0;
2518
+ }
2519
+ // console.log(`🔍 [${this.node().type}] Evaluating ${this.expressionEvaluators.size} expressions`);
2520
+ const updatePromises = Array.from(this.expressionEvaluators).map(async ([path, evaluator]) => {
2521
+ // Check cache first
2522
+ const cachedValue = this.getCachedExpressionResult(path);
2523
+ if (cachedValue !== null) {
2524
+ // console.log(`💾 [${this.node().type}] Using cached expression result for '${path}'`);
2525
+ return { path, newValue: cachedValue, fromCache: true };
2526
+ }
2527
+ // Evaluate expression if not cached
2528
+ const evalStartTime = performance.now();
2529
+ const newValue = await evaluator();
2530
+ const evalTime = performance.now() - evalStartTime;
2531
+ // Check if result has actually changed using Lodash isEqual
2532
+ // Important: We must check if the key exists in the map, not just compare values
2533
+ // This handles the case where the expression evaluates to `undefined` for the first time
2534
+ // Without this check, `undefined` would be incorrectly treated as "no change"
2535
+ // because Map.get() returns `undefined` for non-existent keys
2536
+ const hasLastValue = this.lastExpressionResults.has(path);
2537
+ const lastValue = this.lastExpressionResults.get(path);
2538
+ const hasChanged = !hasLastValue || !isEqual(newValue, lastValue);
2539
+ if (hasChanged) {
2540
+ // console.log(
2541
+ // `📝 [${this.node().type}] Expression '${path}' evaluated in ${evalTime.toFixed(2)}ms - value changed`,
2542
+ // );
2543
+ // Cache the result and track it
2544
+ this.setCachedExpressionResult(path, newValue);
2545
+ this.lastExpressionResults.set(path, cloneDeep(newValue));
2546
+ return { path, newValue, fromCache: false, hasChanged: true };
2547
+ }
2548
+ else {
2549
+ // console.log(
2550
+ // `📝 [${this.node().type}] Expression '${path}' evaluated in ${evalTime.toFixed(2)}ms - value unchanged, skipping update`,
2551
+ // );
2552
+ // Cache the result but don't update
2553
+ this.setCachedExpressionResult(path, newValue);
2554
+ this.lastExpressionResults.set(path, cloneDeep(newValue));
2555
+ return { path, newValue, fromCache: false, hasChanged: false };
2556
+ }
2557
+ });
2558
+ // Wait for all evaluators to complete
2559
+ const updates = await Promise.all(updatePromises);
2560
+ // Filter updates to only include those that have actually changed
2561
+ const changedUpdates = updates.filter((update) => update.hasChanged !== false);
2562
+ // Apply updates to mergedOptions only for changed values
2563
+ if (changedUpdates.length > 0) {
2564
+ this.mergedOptions.update((o) => {
2565
+ const updatedOptions = { ...o };
2566
+ changedUpdates.forEach(({ path, newValue }) => {
2567
+ set(updatedOptions, path, newValue);
2568
+ });
2569
+ return updatedOptions;
2570
+ });
2571
+ }
2572
+ const cacheHits = updates.filter((update) => update.fromCache).length;
2573
+ const skippedUpdates = updates.length - changedUpdates.length;
2574
+ // console.log(
2575
+ // `📋 [${this.node().type}] Applied ${changedUpdates.length} expression updates (${cacheHits} cache hits, ${skippedUpdates} skipped)`,
2576
+ // );
2577
+ return changedUpdates.length;
2578
+ }
2579
+ async updateVisibility() {
2580
+ const node = this.node();
2581
+ const visibility = node.options?.['visible'] || this.mergedOptions()?.visible;
2582
+ if (visibility === undefined || visibility === null) {
2583
+ // Default to visible if no visibility option is set
2584
+ this.show();
2585
+ return;
2586
+ }
2587
+ let shouldBeVisible;
2588
+ if (typeof visibility === 'boolean') {
2589
+ shouldBeVisible = visibility;
2590
+ }
2591
+ else if (typeof visibility === 'string' && this.expressionEvaluator.isExpression(visibility)) {
2592
+ // Evaluate expression for visibility
2593
+ try {
2594
+ const result = await this.evaluateExpression(visibility);
2595
+ shouldBeVisible = Boolean(result);
2596
+ }
2597
+ catch (error) {
2598
+ // console.error('Error evaluating visibility expression:', error);
2599
+ shouldBeVisible = true; // Default to visible on error
2600
+ }
2601
+ }
2602
+ else {
2603
+ // Treat other values as truthy/falsy
2604
+ shouldBeVisible = Boolean(visibility);
2605
+ }
2606
+ if (shouldBeVisible) {
2607
+ this.show();
2608
+ }
2609
+ else {
2610
+ this.hide();
2611
+ }
2612
+ }
2613
+ show() {
2614
+ if (!this.isVisible()) {
2615
+ this.isVisible.set(true);
2616
+ if (this.componentRef && this.componentRef.location) {
2617
+ this.loadComponent();
2618
+ // this.componentRef.location.nativeElement.style.display = '';
2619
+ }
2620
+ }
2621
+ }
2622
+ hide() {
2623
+ if (this.isVisible()) {
2624
+ this.isVisible.set(false);
2625
+ if (this.componentRef && this.componentRef.location) {
2626
+ this.componentRef.destroy();
2627
+ // this.componentRef.location.nativeElement.style.display = 'none';
2628
+ }
2629
+ }
2630
+ }
2631
+ async evaluateExpression(templateExpression) {
2632
+ try {
2633
+ const scope = this.buildExpressionScope();
2634
+ return await this.expressionEvaluator.evaluate(templateExpression, scope);
2635
+ }
2636
+ catch (error) {
2637
+ // console.error('Error evaluating expression:', error);
2638
+ return false;
2639
+ }
2640
+ }
2641
+ buildExpressionScope() {
2642
+ return {
2643
+ context: this.getContextScope(),
2644
+ events: this.getEventScope(),
2645
+ widget: this.getWidgetScope(),
2646
+ methods: this.getFunctionScope(),
2647
+ vars: this.getVariablesScope(),
2648
+ };
2649
+ }
2650
+ getContextScope() {
2651
+ return {
2652
+ eval: (path) => {
2653
+ //TODO: Handle array index
2654
+ const fullPath = path.startsWith('>') ? `${this.instance?.parentPath()}.${path.substring(1)}` : path;
2655
+ const value = this.contextService.getValue(fullPath);
2656
+ return value;
2657
+ },
2658
+ set: (path, value) => {
2659
+ this.contextService.update(path, value, { origin: 'user' });
2660
+ },
2661
+ data: () => {
2662
+ return this.contextService.data();
2663
+ },
2664
+ isDirty: () => {
2665
+ if (!this.contextService.isSavedCommitted()) {
2666
+ return false;
2667
+ }
2668
+ return this.contextService.isDirty();
2669
+ },
2670
+ /**
2671
+ * Host/widget-under-edit `options` from the shared context store (same values as
2672
+ * `context.eval('options' + (path ? '.' + path : ''))`), not `widget.options` on the nested
2673
+ * property field instance. Uses one `getValue` so smart-path behavior matches `context.eval`.
2674
+ */
2675
+ options: (path) => {
2676
+ if (path == null || path === '') {
2677
+ return this.contextService.getValue('options');
2678
+ }
2679
+ return this.contextService.getValue(`options.${path}`);
2680
+ },
2681
+ };
2682
+ }
2683
+ getEventScope() {
2684
+ return {
2685
+ context: (...paths) => {
2686
+ return this.onContextChanged.pipe(filter((c) => {
2687
+ const activePaths = paths.filter((p) => !!p && p.trim().length > 0);
2688
+ // If no path filters specified, pass all events
2689
+ if (activePaths.length === 0) {
2690
+ return true;
2691
+ }
2692
+ // Ensure c.path exists
2693
+ if (!c.path) {
2694
+ return false;
2695
+ }
2696
+ return activePaths.some((path) => {
2697
+ // Pattern: "prefix*" - matches paths that start with prefix
2698
+ if (path.endsWith('*')) {
2699
+ const prefix = path.substring(0, path.length - 1);
2700
+ return c.path.startsWith(prefix);
2701
+ }
2702
+ // Pattern: "*suffix" - matches paths that end with suffix
2703
+ if (path.startsWith('*')) {
2704
+ const suffix = path.substring(1);
2705
+ return c.path.endsWith(suffix);
2706
+ }
2707
+ // Exact match
2708
+ return c.path === path;
2709
+ });
2710
+ }));
2711
+ },
2712
+ from: (event) => get(this.instance.api(), event),
2713
+ load: () => {
2714
+ return this.onLoadEvent.asObservable();
2715
+ },
2716
+ };
2717
+ }
2718
+ getWidgetScope() {
2719
+ return {
2720
+ /** Resolved options for this renderer's widget instance (not the host / property-viewer target). */
2721
+ options: (path) => {
2722
+ const opts = this.instance && typeof this.instance.options === 'function'
2723
+ ? this.instance.options()
2724
+ : this.mergedOptions();
2725
+ if (path == null || path === '') {
2726
+ return opts;
2727
+ }
2728
+ return getSmart(opts, path);
2729
+ },
2730
+ call: (name, ...args) => {
2731
+ this.instance.call(name, ...args);
2732
+ },
2733
+ setValue: (value) => {
2734
+ this.instance.setValue(value);
2735
+ },
2736
+ clear: () => {
2737
+ this.instance.setValue(undefined);
2738
+ },
2739
+ refresh: async () => {
2740
+ await this.updateOptionsBasedOnContext();
2741
+ this.applyOptions();
2742
+ const refresh = this.instance?.['refresh'];
2743
+ if (refresh && typeof refresh === 'function') {
2744
+ refresh.bind(this.instance)();
2745
+ }
2746
+ },
2747
+ output: (name) => {
2748
+ return this.instance.output(name);
2749
+ },
2750
+ find: (id) => {
2751
+ return this.builderService.getWidget(id);
2752
+ },
2753
+ };
2754
+ }
2755
+ getFunctionScope() {
2756
+ const scope = {
2757
+ sum: (values) => {
2758
+ return sum(values);
2759
+ },
2760
+ translate: (text, options) => {
2761
+ return this.translateService.translateAsync(text, options);
2762
+ },
2763
+ widgetInfo: (name) => {
2764
+ return this.widgetService.resolve(name);
2765
+ },
2766
+ };
2767
+ // Add custom functions from builder service
2768
+ Object.entries(this.builderService.functions).forEach(([key, fn]) => {
2769
+ scope[key] = (...args) => {
2770
+ return fn(...args);
2771
+ };
2772
+ });
2773
+ return scope;
2774
+ }
2775
+ getVariablesScope() {
2776
+ return {
2777
+ eval: (path) => get(this.builderService.variables, path),
2778
+ };
2779
+ }
2780
+ async assignTriggers() {
2781
+ const node = this.node();
2782
+ // Check multiple possible locations for triggers
2783
+ const triggers = node.triggers || node.options?.['triggers'] || this.mergedOptions()?.triggers;
2784
+ if (!triggers) {
2785
+ return;
2786
+ }
2787
+ for (const trigger of triggers) {
2788
+ try {
2789
+ const event = await this.evaluateTrigger(trigger.event);
2790
+ if (event) {
2791
+ event.pipe(this.unsubscriber.takeUntilDestroy).subscribe(() => {
2792
+ const exec = async (action) => {
2793
+ if (!isEmpty(action) && isString(action)) {
2794
+ await this.evaluateAction(action);
2795
+ }
2796
+ };
2797
+ const actions = Array.isArray(trigger.action) ? trigger.action : [trigger.action];
2798
+ actions.forEach(async (a) => await exec(a));
2799
+ });
2800
+ }
2801
+ }
2802
+ catch (error) {
2803
+ // console.error('Error assigning trigger:', error);
2804
+ }
2805
+ }
2806
+ }
2807
+ async evaluateTrigger(templateExpression) {
2808
+ try {
2809
+ const scope = this.buildExpressionScope();
2810
+ const result = await this.expressionEvaluator.evaluate(templateExpression, scope);
2811
+ // Check if result is an Observable
2812
+ if (result && typeof result.subscribe === 'function') {
2813
+ return result;
2814
+ }
2815
+ else {
2816
+ return null;
2817
+ }
2818
+ }
2819
+ catch (error) {
2820
+ // console.error('Error evaluating trigger expression:', error);
2821
+ return null;
2822
+ }
2823
+ }
2824
+ async evaluateAction(templateExpression) {
2825
+ try {
2826
+ const scope = this.buildExpressionScope();
2827
+ await this.expressionEvaluator.evaluate(templateExpression, scope);
2828
+ }
2829
+ catch (error) {
2830
+ // console.error('Error evaluating action expression:', templateExpression, error);
2831
+ }
2832
+ }
2833
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetRendererDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2834
+ 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: [
2835
+ {
2836
+ provide: AXUnsubscriber,
2837
+ },
2838
+ ], exportAs: ["widgetRenderer"], usesOnChanges: true, ngImport: i0 }); }
2839
+ }
2840
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetRendererDirective, decorators: [{
2841
+ type: Directive,
2842
+ args: [{
2843
+ selector: '[axp-widget-renderer]',
2844
+ exportAs: 'widgetRenderer',
2845
+ providers: [
2846
+ {
2847
+ provide: AXUnsubscriber,
2848
+ },
2849
+ ],
2850
+ standalone: false,
2851
+ }]
2852
+ }], 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"] }] } });
2853
+
2854
+ const COMPONENTS = [AXPWidgetContainerComponent, AXPWidgetColumnRendererComponent, AXPWidgetRendererDirective];
2855
+ class AXPWidgetCoreModule {
2856
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetCoreModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
2857
+ 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] }); }
2858
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetCoreModule, imports: [CommonModule, PortalModule, AXSkeletonModule, AXTranslationModule] }); }
2859
+ }
2860
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetCoreModule, decorators: [{
2861
+ type: NgModule,
2862
+ args: [{
2863
+ imports: [CommonModule, PortalModule, AXSkeletonModule, AXTranslationModule],
2864
+ exports: [...COMPONENTS],
2865
+ declarations: [...COMPONENTS],
2866
+ providers: [],
2867
+ }]
2868
+ }] });
2869
+
2870
+ //#endregion
2871
+ //#region ---- Mapping ----
2872
+ /**
2873
+ * Converts a domain {@link AXPProperty} into a {@link AXPWidgetProperty} row for
2874
+ * the layout property viewer (`AXPPropertyViewerService` and `buildPropertyViewerTabsFromProperties`).
2875
+ */
2876
+ function mapAXPPropertyToWidgetProperty(property, options) {
2877
+ const { group, order, valuePathPrefix, resolveTitle, resolveDescription } = options;
2878
+ const path = valuePathPrefix ? `${valuePathPrefix}.${property.name}` : property.name;
2879
+ const iface = property.interface;
2880
+ const widgetType = iface?.type ?? 'text-editor';
2881
+ const description = resolveDescription?.(property);
2882
+ return {
2883
+ name: property.name,
2884
+ title: resolveTitle(property),
2885
+ ...(description !== undefined && description !== '' ? { description } : {}),
2886
+ group,
2887
+ order,
2888
+ schema: {
2889
+ dataType: property.dataType,
2890
+ interface: {
2891
+ path,
2892
+ name: property.name,
2893
+ type: widgetType,
2894
+ options: iface?.options ?? {},
2895
+ ...(iface?.triggers ? { triggers: iface.triggers } : {}),
2896
+ },
2897
+ },
2898
+ validations: property.validations,
2899
+ visible: true,
2900
+ };
2901
+ }
2902
+ //#endregion
2903
+
2904
+ class AXPPropertyEditorHelper {
2905
+ static expandShorthand(values) {
2906
+ switch (values.length) {
2907
+ case 1:
2908
+ return [values[0], values[0], values[0], values[0]];
2909
+ case 2:
2910
+ return [values[0], values[1], values[0], values[1]];
2911
+ case 3:
2912
+ return [values[0], values[1], values[2], values[1]];
2913
+ case 4:
2914
+ return values;
2915
+ default:
2916
+ throw new Error(`Invalid shorthand value count. Input: ${values}`);
2917
+ }
2918
+ }
2919
+ static condenseShorthand(values) {
2920
+ if (values.length !== 4) {
2921
+ throw new Error('Expected 4 values for condensation.');
2922
+ }
2923
+ if (values[0] === values[1] && values[1] === values[2] && values[2] === values[3]) {
2924
+ return `${values[0]}`;
2925
+ }
2926
+ else if (values[0] === values[2] && values[1] === values[3]) {
2927
+ return `${values[0]} ${values[1]}`;
2928
+ }
2929
+ else if (values[1] === values[3]) {
2930
+ return `${values[0]} ${values[1]} ${values[2]}`;
2931
+ }
2932
+ else {
2933
+ return `${values[0]} ${values[1]} ${values[2]} ${values[3]}`;
2934
+ }
2935
+ }
2936
+ static parseSides(input) {
2937
+ const values = this.expandShorthand(input.match(/(?:rgb\([^)]+\)|[^ ]+)/g)?.map((value) => value.trim()) || []);
2938
+ return { top: values[0], right: values[1], bottom: values[2], left: values[3] };
2939
+ }
2940
+ static parseSidesWithUnits(input) {
2941
+ const values = this.expandShorthand(input.match(/(?:rgb\([^)]+\)|[^ ]+)/g)?.map((value) => value.trim()) || []);
2942
+ return {
2943
+ top: AXPPropertyEditorHelper.getValueWithUnit(values[0]).value,
2944
+ right: AXPPropertyEditorHelper.getValueWithUnit(values[1]).value,
2945
+ bottom: AXPPropertyEditorHelper.getValueWithUnit(values[2]).value,
2946
+ left: AXPPropertyEditorHelper.getValueWithUnit(values[3]).value,
2947
+ };
2948
+ }
2949
+ static parseCorners(input) {
2950
+ const values = this.expandShorthand(input.split(' ').map((value) => value.trim()));
2951
+ return {
2952
+ 'top-left': AXPPropertyEditorHelper.getValueWithUnit(values[0]).value,
2953
+ 'top-right': AXPPropertyEditorHelper.getValueWithUnit(values[1]).value,
2954
+ 'bottom-left': AXPPropertyEditorHelper.getValueWithUnit(values[2]).value,
2955
+ 'bottom-right': AXPPropertyEditorHelper.getValueWithUnit(values[3]).value,
2956
+ };
2957
+ }
2958
+ static parseSpacingBox(input) {
2959
+ return {
2960
+ margin: this.parseSidesWithUnits(input.margin),
2961
+ padding: this.parseSidesWithUnits(input.padding),
2962
+ };
2963
+ }
2964
+ static parseBorderBox(input) {
2965
+ return {
2966
+ width: this.parseSidesWithUnits(input.width),
2967
+ radius: this.parseCorners(input.radius),
2968
+ color: this.parseSides(input.color),
2969
+ style: this.parseSides(input.style),
2970
+ };
2971
+ }
2972
+ static parseSpacingBoxReverse(input, units) {
2973
+ const format = (value, unit) => `${value}${unit}`;
2974
+ return {
2975
+ margin: AXPPropertyEditorHelper.condenseShorthand([
2976
+ format(input.margin.top, units.margin.top),
2977
+ format(input.margin.right, units.margin.right),
2978
+ format(input.margin.bottom, units.margin.bottom),
2979
+ format(input.margin.left, units.margin.left),
2980
+ ]),
2981
+ padding: AXPPropertyEditorHelper.condenseShorthand([
2982
+ format(input.padding.top, units.padding.top),
2983
+ format(input.padding.right, units.padding.right),
2984
+ format(input.padding.bottom, units.padding.bottom),
2985
+ format(input.padding.left, units.padding.left),
2986
+ ]),
2987
+ };
2988
+ }
2989
+ static parseBorderBoxReverse(input, units) {
2990
+ const format = (value, unit) => `${value}${unit}`;
2991
+ return {
2992
+ width: AXPPropertyEditorHelper.condenseShorthand([
2993
+ format(input.width.top, units.width.top),
2994
+ format(input.width.right, units.width.right),
2995
+ format(input.width.bottom, units.width.bottom),
2996
+ format(input.width.left, units.width.left),
2997
+ ]),
2998
+ radius: AXPPropertyEditorHelper.condenseShorthand([
2999
+ format(input.radius['top-left'], units.radius['top-left']),
3000
+ format(input.radius['top-right'], units.radius['top-right']),
3001
+ format(input.radius['bottom-right'], units.radius['bottom-right']),
3002
+ format(input.radius['bottom-left'], units.radius['bottom-left']),
3003
+ ]),
3004
+ color: AXPPropertyEditorHelper.condenseShorthand([
3005
+ `${input.color.top}${units.color.top}`,
3006
+ `${input.color.right}${units.color.right}`,
3007
+ `${input.color.bottom}${units.color.bottom}`,
3008
+ `${input.color.left}${units.color.left}`,
3009
+ ]),
3010
+ style: AXPPropertyEditorHelper.condenseShorthand([
3011
+ `${input.style.top}${units.style.top}`,
3012
+ `${input.style.right}${units.style.right}`,
3013
+ `${input.style.bottom}${units.style.bottom}`,
3014
+ `${input.style.left}${units.style.left}`,
3015
+ ]),
3016
+ };
3017
+ }
3018
+ static getValueWithUnit(input) {
3019
+ if (typeof input === 'number')
3020
+ return { value: input, unit: 'px' };
3021
+ if (input === 'auto')
3022
+ return { value: 0, unit: 'px' };
3023
+ const match = input.match(/^([0-9.]+)([a-z%]*)$/i);
3024
+ if (!match)
3025
+ throw new Error(`Invalid unit format: ${input}`);
3026
+ return { value: parseFloat(match[1]), unit: match[2] || '' };
3027
+ }
3028
+ static getValueFromUnit(value, unit) {
3029
+ return unit ? `${value}${unit}` : `${value}`;
3030
+ }
3031
+ static parseGap(gap) {
3032
+ const parts = gap.split(/\s+/);
3033
+ const match = parts[0].match(/^(\d+\.?\d*)([a-z%]+)$/);
3034
+ if (!match) {
3035
+ throw new Error('Invalid gap format');
3036
+ }
3037
+ const [, xValue, unit] = match;
3038
+ let yValue = parseFloat(xValue);
3039
+ if (parts.length === 2) {
3040
+ const secondMatch = parts[1].match(/^(\d+\.?\d*)[a-z%]+$/);
3041
+ if (!secondMatch) {
3042
+ throw new Error('Invalid gap format');
3043
+ }
3044
+ yValue = parseFloat(secondMatch[1]);
3045
+ }
3046
+ return {
3047
+ values: {
3048
+ x: parseFloat(xValue),
3049
+ y: yValue,
3050
+ },
3051
+ unit,
3052
+ };
3053
+ }
3054
+ static parseGridTemplate(gridTemplate) {
3055
+ const match = gridTemplate.match(/^repeat\((\d+),\s*(?:1fr|auto|minmax\([^)]*\))\)$/);
3056
+ if (!match) {
3057
+ throw new Error("Invalid grid template format. Expected 'repeat(N, 1fr|auto|minmax(...))'.");
3058
+ }
3059
+ return parseInt(match[1], 10);
3060
+ }
3061
+ static createGridTemplate(repetitionCount) {
3062
+ if (repetitionCount <= 0) {
3063
+ throw new Error('Repetition count must be a positive integer.');
3064
+ }
3065
+ return `repeat(${repetitionCount}, 1fr)`;
3066
+ }
3067
+ }
3068
+ function findNonEmptyBreakpoints(values) {
3069
+ const breakpoints = ['default', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'];
3070
+ const nonEmptyBreakpoints = [];
3071
+ for (const breakpoint of breakpoints) {
3072
+ if (values[breakpoint] !== undefined) {
3073
+ nonEmptyBreakpoints.push(breakpoint);
3074
+ }
3075
+ }
3076
+ return nonEmptyBreakpoints;
3077
+ }
3078
+
3079
+ //#region ---- Imports ----
3080
+ //#endregion
3081
+ //#region ---- Helper Class ----
3082
+ /**
3083
+ * Helper class for serializing and deserializing AXPWidgetNode to/from JSON
3084
+ */
3085
+ class AXPWidgetSerializationHelper {
3086
+ /**
3087
+ * Removes non-serializable properties from widget node
3088
+ * @param node - The widget node to clean
3089
+ * @param options - Serialization options
3090
+ * @returns Cleaned widget node ready for JSON serialization
3091
+ */
3092
+ static cleanNode(node, options = {}) {
3093
+ const { removeMeta = true, removeFunctions = true } = options;
3094
+ // Deep clone to avoid mutating original
3095
+ const cleaned = cloneDeep(node);
3096
+ // Remove meta property if requested
3097
+ if (removeMeta && 'meta' in cleaned) {
3098
+ delete cleaned.meta;
3099
+ }
3100
+ // Remove functions from options, triggers, and valueTransforms
3101
+ if (removeFunctions) {
3102
+ this.removeFunctions(cleaned);
3103
+ }
3104
+ // Recursively clean children
3105
+ if (cleaned.children && Array.isArray(cleaned.children)) {
3106
+ cleaned.children = cleaned.children.map((child) => this.cleanNode(child, options));
3107
+ }
3108
+ return cleaned;
3109
+ }
3110
+ /**
3111
+ * Recursively removes functions from object properties
3112
+ */
3113
+ static removeFunctions(obj) {
3114
+ if (obj === null || obj === undefined) {
3115
+ return;
3116
+ }
3117
+ if (Array.isArray(obj)) {
3118
+ obj.forEach((item) => this.removeFunctions(item));
3119
+ return;
3120
+ }
3121
+ if (typeof obj === 'object') {
3122
+ for (const key in obj) {
3123
+ if (typeof obj[key] === 'function') {
3124
+ delete obj[key];
3125
+ }
3126
+ else if (typeof obj[key] === 'object') {
3127
+ this.removeFunctions(obj[key]);
3128
+ }
3129
+ }
3130
+ }
3131
+ }
3132
+ /**
3133
+ * Converts AXPWidgetNode to JSON string
3134
+ * @param node - The widget node to serialize
3135
+ * @param options - Serialization options
3136
+ * @returns JSON string representation of the widget node
3137
+ */
3138
+ static toJson(node, options = {}) {
3139
+ const { pretty = false, ...cleanOptions } = options;
3140
+ const cleaned = this.cleanNode(node, cleanOptions);
3141
+ if (pretty) {
3142
+ return JSON.stringify(cleaned, null, 2);
3143
+ }
3144
+ return JSON.stringify(cleaned);
3145
+ }
3146
+ /**
3147
+ * Converts JSON string to AXPWidgetNode
3148
+ * @param json - JSON string to deserialize
3149
+ * @returns Parsed AXPWidgetNode object
3150
+ * @throws Error if JSON is invalid or doesn't match AXPWidgetNode structure
3151
+ */
3152
+ static fromJson(json) {
3153
+ try {
3154
+ const parsed = JSON.parse(json);
3155
+ // Basic validation - ensure it has at least a type property
3156
+ if (!parsed || typeof parsed !== 'object') {
3157
+ throw new Error('Invalid JSON: Expected an object');
3158
+ }
3159
+ if (!parsed.type) {
3160
+ throw new Error('Invalid AXPWidgetNode: Missing required property "type"');
3161
+ }
3162
+ // Recursively validate and parse children if they exist
3163
+ if (parsed.children && Array.isArray(parsed.children)) {
3164
+ parsed.children = parsed.children.map((child) => this.fromJson(JSON.stringify(child)));
3165
+ }
3166
+ return parsed;
3167
+ }
3168
+ catch (error) {
3169
+ if (error instanceof SyntaxError) {
3170
+ throw new Error(`Invalid JSON string: ${error.message}`);
3171
+ }
3172
+ throw error;
3173
+ }
3174
+ }
3175
+ /**
3176
+ * Removes meta property from widget node (similar to designer service)
3177
+ * @param node - The widget node to process
3178
+ * @returns Widget node without meta property
3179
+ */
3180
+ static removeMeta(node) {
3181
+ return this.cleanNode(node, { removeMeta: true, removeFunctions: false });
3182
+ }
3183
+ }
3184
+ //#endregion
3185
+
3186
+ //#region ---- Editor sub-categories (widget picker) ----
3187
+ const AXP_WIDGETS_EDITOR_SUB_TEXT_INPUTS = {
3188
+ name: 'editor-text-inputs',
3189
+ order: 1,
3190
+ title: '@platform-layout-widgets:subcategories.editor-text-inputs.title',
3191
+ };
3192
+ const AXP_WIDGETS_EDITOR_SUB_NUMERIC = {
3193
+ name: 'editor-numeric',
3194
+ order: 2,
3195
+ title: '@platform-layout-widgets:subcategories.editor-numeric.title',
3196
+ };
3197
+ const AXP_WIDGETS_EDITOR_SUB_DATETIME = {
3198
+ name: 'editor-datetime',
3199
+ order: 3,
3200
+ title: '@platform-layout-widgets:subcategories.editor-datetime.title',
3201
+ };
3202
+ const AXP_WIDGETS_EDITOR_SUB_CHOICE = {
3203
+ name: 'editor-choice',
3204
+ order: 4,
3205
+ title: '@platform-layout-widgets:subcategories.editor-choice.title',
3206
+ };
3207
+ const AXP_WIDGETS_EDITOR_SUB_RICH = {
3208
+ name: 'editor-rich',
3209
+ order: 5,
3210
+ title: '@platform-layout-widgets:subcategories.editor-rich.title',
3211
+ };
3212
+ const AXP_WIDGETS_EDITOR_SUB_VISUAL = {
3213
+ name: 'editor-visual',
3214
+ order: 6,
3215
+ title: '@platform-layout-widgets:subcategories.editor-visual.title',
3216
+ };
3217
+ const AXP_WIDGETS_EDITOR_SUB_COMPOSITE = {
3218
+ name: 'editor-composite',
3219
+ order: 7,
3220
+ title: '@platform-layout-widgets:subcategories.editor-composite.title',
3221
+ };
3222
+ const AXP_WIDGETS_EDITOR_SUB_CONFIG = {
3223
+ name: 'editor-config',
3224
+ order: 8,
3225
+ title: '@platform-layout-widgets:subcategories.editor-config.title',
3226
+ };
3227
+ //#endregion
3228
+ //#region ---- Layout sub-categories (widget picker) ----
3229
+ const AXP_WIDGETS_LAYOUT_SUB_SHELL = {
3230
+ name: 'layout-shell',
3231
+ order: 1,
3232
+ title: '@platform-layout-widgets:subcategories.layout-shell.title',
3233
+ };
3234
+ const AXP_WIDGETS_LAYOUT_SUB_GRID = {
3235
+ name: 'layout-grid',
3236
+ order: 2,
3237
+ title: '@platform-layout-widgets:subcategories.layout-grid.title',
3238
+ };
3239
+ const AXP_WIDGETS_LAYOUT_SUB_SURFACES = {
3240
+ name: 'layout-surfaces',
3241
+ order: 3,
3242
+ title: '@platform-layout-widgets:subcategories.layout-surfaces.title',
3243
+ };
3244
+ const AXP_WIDGETS_LAYOUT_SUB_TABS = {
3245
+ name: 'layout-tabs',
3246
+ order: 4,
3247
+ title: '@platform-layout-widgets:subcategories.layout-tabs.title',
3248
+ };
3249
+ const AXP_WIDGETS_LAYOUT_SUB_LISTS = {
3250
+ name: 'layout-lists',
3251
+ order: 5,
3252
+ title: '@platform-layout-widgets:subcategories.layout-lists.title',
3253
+ };
3254
+ const AXP_WIDGETS_LAYOUT_SUB_UTILITY = {
3255
+ name: 'layout-utility',
3256
+ order: 6,
3257
+ title: '@platform-layout-widgets:subcategories.layout-utility.title',
3258
+ };
3259
+ //#endregion
3260
+ //#region ---- Action sub-categories (widget picker) ----
3261
+ const AXP_WIDGETS_ACTION_SUB_CONTROLS = {
3262
+ name: 'action-controls',
3263
+ order: 1,
3264
+ title: '@platform-layout-widgets:subcategories.action-controls.title',
3265
+ };
3266
+ //#endregion
3267
+ //#region ---- Advance sub-categories (widget picker) ----
3268
+ const AXP_WIDGETS_ADVANCE_SUB_MEDIA = {
3269
+ name: 'advance-media',
3270
+ order: 1,
3271
+ title: '@platform-layout-widgets:subcategories.advance-media.title',
3272
+ };
3273
+ const AXP_WIDGETS_ADVANCE_SUB_INPUT = {
3274
+ name: 'advance-input',
3275
+ order: 2,
3276
+ title: '@platform-layout-widgets:subcategories.advance-input.title',
3277
+ };
3278
+ const AXP_WIDGETS_ADVANCE_SUB_DATA = {
3279
+ name: 'advance-data',
3280
+ order: 3,
3281
+ title: '@platform-layout-widgets:subcategories.advance-data.title',
3282
+ };
3283
+ const AXP_WIDGETS_ADVANCE_SUB_TOOLS = {
3284
+ name: 'advance-tools',
3285
+ order: 4,
3286
+ title: '@platform-layout-widgets:subcategories.advance-tools.title',
3287
+ };
3288
+ //#endregion
3289
+ /** Normalizes widget categories to an array for iteration and serialization. */
3290
+ function normalizeWidgetCategories(categories) {
3291
+ if (categories == null) {
3292
+ return [];
3293
+ }
3294
+ return Array.isArray(categories) ? categories : [categories];
3295
+ }
3296
+ const AXP_WIDGETS_LAYOUT_CATEGORY = {
3297
+ name: 'layout',
3298
+ order: 1,
3299
+ title: '@platform-layout-widgets:categories.layout.title',
3300
+ };
3301
+ const AXP_WIDGETS_EDITOR_CATEGORY = {
3302
+ name: 'editor',
3303
+ order: 2,
3304
+ title: '@platform-layout-widgets:categories.editor.title',
3305
+ };
3306
+ const AXP_WIDGETS_ACTION_CATEGORY = {
3307
+ name: 'action',
3308
+ order: 3,
3309
+ title: '@platform-layout-widgets:categories.action.title',
3310
+ };
3311
+ const AXP_WIDGETS_ADVANCE_CATEGORY = {
3312
+ name: 'advance',
3313
+ order: 4,
3314
+ title: '@platform-layout-widgets:categories.advance.title',
3315
+ };
3316
+ /** AI category tab in the widget picker (metadata); unrelated to Widgets:GetForAI listing scope. */
3317
+ const AXP_WIDGETS_AI_CATEGORY = {
3318
+ name: 'ai',
3319
+ order: 5,
3320
+ title: '@platform-layout-widgets:categories.ai.title',
3321
+ };
3322
+ const AXP_WIDGETS_CATEGORIES = [
3323
+ AXP_WIDGETS_LAYOUT_CATEGORY,
3324
+ AXP_WIDGETS_EDITOR_CATEGORY,
3325
+ AXP_WIDGETS_ACTION_CATEGORY,
3326
+ AXP_WIDGETS_ADVANCE_CATEGORY,
3327
+ AXP_WIDGETS_AI_CATEGORY,
3328
+ ];
3329
+
3330
+ /**
3331
+ * Standalone cell renderer using the same column-mode widget pipeline as {@link AXPWidgetColumnRendererComponent}.
3332
+ */
3333
+ class AXPWidgetColumnCellComponent {
3334
+ constructor() {
3335
+ this.caption = '';
3336
+ this.mergedOptions = signal({}, ...(ngDevMode ? [{ debugName: "mergedOptions" }] : /* istanbul ignore next */ []));
3337
+ this.rowInjectorsCache = new Map();
3338
+ this.hasExpressions = false;
3339
+ this.pendingEvaluations = new Set();
3340
+ this.changeDetectionScheduled = false;
3341
+ this.injector = inject(Injector);
3342
+ this.cdr = inject(ChangeDetectorRef);
3343
+ this.widgetRegistry = inject(AXPWidgetRegistryService);
3344
+ this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
3345
+ }
3346
+ async ngOnInit() {
3347
+ await this.resolveComponent();
3348
+ }
3349
+ ngOnChanges(changes) {
3350
+ if (changes['node'] && !changes['node'].firstChange) {
3351
+ void this.resolveComponent();
3352
+ }
3353
+ if (changes['rowData'] && !changes['rowData'].firstChange) {
3354
+ this.rowInjectorsCache.clear();
3355
+ this.cdr.markForCheck();
3356
+ }
3357
+ }
3358
+ getInputs(data) {
3359
+ return {
3360
+ rawValue: get(data, this.node.path),
3361
+ rowData: data,
3362
+ };
3363
+ }
3364
+ getRowInjector(data) {
3365
+ const cacheKey = this.getCacheKey(data);
3366
+ let rowInjector = this.rowInjectorsCache.get(cacheKey);
3367
+ if (!rowInjector) {
3368
+ if (!this.hasExpressions) {
3369
+ rowInjector = this.createRowInjector(this.mergedOptions());
3370
+ this.rowInjectorsCache.set(cacheKey, rowInjector);
3371
+ return rowInjector;
3372
+ }
3373
+ if (!this.pendingEvaluations.has(cacheKey)) {
3374
+ this.pendingEvaluations.add(cacheKey);
3375
+ const scope = {
3376
+ ...(data ?? {}),
3377
+ context: {
3378
+ eval: (path) => getSmart(data, path),
3379
+ },
3380
+ };
3381
+ void this.expressionEvaluator.evaluate(this.mergedOptions(), scope).then((evaluatedOptions) => {
3382
+ const newInjector = this.createRowInjector(evaluatedOptions);
3383
+ this.rowInjectorsCache.delete(cacheKey);
3384
+ this.rowInjectorsCache.set(cacheKey, newInjector);
3385
+ this.pendingEvaluations.delete(cacheKey);
3386
+ this.scheduleChangeDetection();
3387
+ }).catch(() => {
3388
+ this.pendingEvaluations.delete(cacheKey);
3389
+ });
3390
+ }
3391
+ rowInjector = this.createRowInjector(this.mergedOptions());
3392
+ this.rowInjectorsCache.set(cacheKey, rowInjector);
3393
+ }
3394
+ return rowInjector;
3395
+ }
3396
+ async resolveComponent() {
3397
+ const widget = this.widgetRegistry.resolve(this.node.type);
3398
+ const mode = 'column';
3399
+ this.component = await widget?.components[mode]?.component();
3400
+ const props = widget?.components[mode]?.properties
3401
+ ?.filter((c) => c.schema.defaultValue)
3402
+ .map((c) => ({ [c.name]: c.schema.defaultValue }))
3403
+ .reduce((acc, curr) => ({ ...acc, ...curr }), {});
3404
+ this.mergedOptions.set(merge(props, this.node.options) || {});
3405
+ this.hasExpressions = this.checkForExpressions(this.mergedOptions());
3406
+ this.rowInjectorsCache.clear();
3407
+ this.cdr.markForCheck();
3408
+ }
3409
+ createRowInjector(options) {
3410
+ return Injector.create({
3411
+ parent: this.injector,
3412
+ providers: [
3413
+ {
3414
+ provide: AXP_WIDGET_COLUMN_TOKEN,
3415
+ useValue: {
3416
+ path: this.node.path,
3417
+ options,
3418
+ caption: this.caption,
3419
+ },
3420
+ },
3421
+ ],
3422
+ });
3423
+ }
3424
+ getCacheKey(data) {
3425
+ const id = data['id'];
3426
+ if (id != null) {
3427
+ return `row-${String(id)}`;
3428
+ }
3429
+ return `row-${Object.keys(data).length}`;
3430
+ }
3431
+ checkForExpressions(obj) {
3432
+ if (typeof obj === 'string') {
3433
+ return this.expressionEvaluator.isExpression(obj);
3434
+ }
3435
+ if (Array.isArray(obj)) {
3436
+ return obj.some((item) => this.checkForExpressions(item));
3437
+ }
3438
+ if (obj && typeof obj === 'object') {
3439
+ return Object.values(obj).some((value) => this.checkForExpressions(value));
3440
+ }
3441
+ return false;
3442
+ }
3443
+ scheduleChangeDetection() {
3444
+ if (this.changeDetectionScheduled) {
3445
+ return;
3446
+ }
3447
+ this.changeDetectionScheduled = true;
3448
+ requestAnimationFrame(() => {
3449
+ this.cdr.markForCheck();
3450
+ this.changeDetectionScheduled = false;
3451
+ });
3452
+ }
3453
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetColumnCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3454
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPWidgetColumnCellComponent, isStandalone: true, selector: "axp-widget-column-cell", inputs: { node: "node", rowData: "rowData", caption: "caption" }, usesOnChanges: true, ngImport: i0, template: `
3455
+ @if (component && rowData) {
3456
+ <ng-container
3457
+ *ngComponentOutlet="component; injector: getRowInjector(rowData); inputs: getInputs(rowData)"
3458
+ ></ng-container>
3459
+ }
3460
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3461
+ }
3462
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetColumnCellComponent, decorators: [{
3463
+ type: Component,
3464
+ args: [{
3465
+ selector: 'axp-widget-column-cell',
3466
+ changeDetection: ChangeDetectionStrategy.OnPush,
3467
+ imports: [NgComponentOutlet],
3468
+ template: `
3469
+ @if (component && rowData) {
3470
+ <ng-container
3471
+ *ngComponentOutlet="component; injector: getRowInjector(rowData); inputs: getInputs(rowData)"
3472
+ ></ng-container>
3473
+ }
3474
+ `,
3475
+ }]
3476
+ }], propDecorators: { node: [{
3477
+ type: Input,
3478
+ args: [{ required: true }]
3479
+ }], rowData: [{
3480
+ type: Input,
3481
+ args: [{ required: true }]
3482
+ }], caption: [{
3483
+ type: Input
3484
+ }] } });
3485
+
3486
+ /** Trace namespace for lookup cascade / widget trigger diagnostics (Debug Mode only). */
3487
+ const WIDGET_CASCADE_TRACE_NS = 'widget-cascade';
3488
+
3489
+ const AXP_STYLING_PROPERTY_GROUP = {
3490
+ name: 'styling',
3491
+ order: 1,
3492
+ title: 'Styling',
3493
+ };
3494
+ const AXP_DATA_PROPERTY_GROUP = {
3495
+ name: 'data',
3496
+ order: 7,
3497
+ title: 'Data',
3498
+ };
3499
+ const AXP_BEHAVIOR_PROPERTY_GROUP = {
3500
+ name: 'behavior',
3501
+ order: 2,
3502
+ title: 'Behavior',
3503
+ };
3504
+ const AXP_WIDGET_PROPERTY_GROUP = {
3505
+ name: 'widget',
3506
+ order: 0,
3507
+ title: 'Widget',
3508
+ };
3509
+ const AXP_VALIDATION_PROPERTY_GROUP = {
3510
+ name: 'validation',
3511
+ order: 4,
3512
+ title: 'Validation',
3513
+ };
3514
+ const AXP_APPEARANCE_PROPERTY_GROUP = {
3515
+ name: 'appearance',
3516
+ order: 2,
3517
+ title: 'Appearance',
3518
+ };
3519
+ const AXP_ANIMATION_PROPERTY_GROUP = {
3520
+ name: 'animation',
3521
+ order: 5,
3522
+ title: 'Animation',
3523
+ };
3524
+ const AXP_TRIGGERS_PROPERTY_GROUP = {
3525
+ name: 'triggers',
3526
+ order: 3,
3527
+ title: 'Triggers',
3528
+ };
3529
+
3530
+ /**
3531
+ * Single property that edits all data-source-related options (dataSource, valueField, textField, filters)
3532
+ * in one dedicated widget. Uses path: 'options' and setValue with merge (same pattern as flex-options).
3533
+ */
3534
+ const AXP_DATA_SOURCE_OPTIONS_PROPERTY = {
3535
+ name: 'dataSourceOptions',
3536
+ title: 'Data Source',
3537
+ group: AXP_DATA_PROPERTY_GROUP,
3538
+ showLabel: false,
3539
+ schema: {
3540
+ dataType: 'object',
3541
+ interface: {
3542
+ name: 'dataSourceOptions',
3543
+ path: 'options',
3544
+ type: 'data-source-options',
3545
+ options: {},
3546
+ },
3547
+ },
3548
+ visible: true,
3549
+ };
3550
+ // export const AXP_DATA_SOURCE_PROPERTY: AXPWidgetProperty = {
3551
+ // name: 'dataSource',
3552
+ // title: 'Data Source',
3553
+ // group: AXP_DATA_PROPERTY_GROUP,
3554
+ // schema: {
3555
+ // dataType: 'object',
3556
+ // interface: {
3557
+ // name: 'dataSource',
3558
+ // path: 'options.dataSource',
3559
+ // type: AXPWidgetsCatalog.select,
3560
+ // valueTransforms: objectKeyValueTransforms('name'),
3561
+ // options: {
3562
+ // valueField: 'name',
3563
+ // textField: 'title',
3564
+ // dataSource: 'dataSources',
3565
+ // hasClearButton: true,
3566
+ // allowSearch: true,
3567
+ // },
3568
+ // },
3569
+ // },
3570
+ // visible: true,
3571
+ // };
3572
+ // export const AXP_DATA_SOURCE_VALUE_FIELD: AXPWidgetProperty = {
3573
+ // name: 'valueField',
3574
+ // title: 'Value Field',
3575
+ // group: AXP_DATA_PROPERTY_GROUP,
3576
+ // schema: {
3577
+ // dataType: 'string',
3578
+ // interface: {
3579
+ // name: 'valueField',
3580
+ // path: 'options.valueField',
3581
+ // type: AXPWidgetsCatalog.text,
3582
+ // options: {
3583
+ // hasClearButton: true,
3584
+ // },
3585
+ // triggers: [
3586
+ // {
3587
+ // event: `{{ events.context('options.dataSource') }}`,
3588
+ // action: `{{ context.set('options.valueField',widget.find('dataSource').output('item').valueField?.name); }}`,
3589
+ // },
3590
+ // ],
3591
+ // },
3592
+ // },
3593
+ // visible: true,
3594
+ // };
3595
+ // export const AXP_DATA_SOURCE_TEXT_FIELD: AXPWidgetProperty = {
3596
+ // name: 'textField',
3597
+ // title: 'Text Field',
3598
+ // group: AXP_DATA_PROPERTY_GROUP,
3599
+ // schema: {
3600
+ // dataType: 'string',
3601
+ // interface: {
3602
+ // name: 'textField',
3603
+ // path: 'options.textField',
3604
+ // type: AXPWidgetsCatalog.text,
3605
+ // options: {
3606
+ // hasClearButton: true,
3607
+ // },
3608
+ // triggers: [
3609
+ // {
3610
+ // event: `{{ events.context('options.dataSource') }}`,
3611
+ // action: `{{ context.set('options.textField',widget.find('dataSource').output('item').textField?.name); }}`,
3612
+ // },
3613
+ // ],
3614
+ // },
3615
+ // },
3616
+ // visible: true,
3617
+ // };
3618
+ // /** Use AXP_DATA_SOURCE_OPTIONS_PROPERTY for a single unified data-source editor, or the three below for separate fields. */
3619
+ // export const AXP_DATA_SOURCE_PROPERTIES = [
3620
+ // AXP_DATA_SOURCE_PROPERTY,
3621
+ // AXP_DATA_SOURCE_VALUE_FIELD,
3622
+ // AXP_DATA_SOURCE_TEXT_FIELD,
3623
+ // ];
3624
+
3625
+ const AXP_NAME_PROPERTY = createStringProperty({
3626
+ name: 'name',
3627
+ title: 'Name',
3628
+ group: AXP_WIDGET_PROPERTY_GROUP,
3629
+ });
3630
+ const AXP_THEME_PROPERTY = {
3631
+ name: 'theme',
3632
+ title: 'Theme',
3633
+ group: AXP_STYLING_PROPERTY_GROUP,
3634
+ schema: {
3635
+ dataType: 'string',
3636
+ defaultValue: { id: 'default', title: 'Default' },
3637
+ interface: {
3638
+ name: 'theme',
3639
+ path: 'options.theme',
3640
+ type: AXPWidgetsCatalog.select,
3641
+ options: {
3642
+ dataSource: [
3643
+ {
3644
+ id: 'default',
3645
+ title: 'Default',
3646
+ },
3647
+ {
3648
+ id: 'light',
3649
+ title: 'Light',
3650
+ },
3651
+ {
3652
+ id: 'dark',
3653
+ title: 'Dark',
3654
+ },
3655
+ ],
3656
+ },
3657
+ },
3658
+ },
3659
+ visible: true,
3660
+ };
3661
+ const AXP_DIRECTION_PROPERTY = {
3662
+ name: 'direction',
3663
+ title: 'Direction',
3664
+ group: AXP_STYLING_PROPERTY_GROUP,
3665
+ schema: {
3666
+ dataType: 'string',
3667
+ defaultValue: { id: 'horizontal' },
3668
+ interface: {
3669
+ name: 'direction',
3670
+ path: 'options.direction',
3671
+ type: AXPWidgetsCatalog.select,
3672
+ options: {
3673
+ dataSource: [
3674
+ {
3675
+ id: 'horizontal',
3676
+ title: 'Horizontal',
3677
+ },
3678
+ {
3679
+ id: 'vertical',
3680
+ title: 'Vertical',
3681
+ },
3682
+ ],
3683
+ },
3684
+ },
3685
+ },
3686
+ visible: true,
3687
+ };
3688
+ const AXP_COLOR_PROPERTY = {
3689
+ name: 'color',
3690
+ title: 'Color',
3691
+ group: AXP_STYLING_PROPERTY_GROUP,
3692
+ schema: {
3693
+ dataType: 'string',
3694
+ interface: {
3695
+ path: 'options.color',
3696
+ options: {
3697
+ hasClearButton: true,
3698
+ },
3699
+ type: AXPWidgetsCatalog.color,
3700
+ },
3701
+ },
3702
+ visible: true,
3703
+ };
3704
+ const AXP_FONT_SIZE_PROPERTY = {
3705
+ name: 'fontSize',
3706
+ title: 'Font Size',
3707
+ group: AXP_STYLING_PROPERTY_GROUP,
3708
+ schema: {
3709
+ dataType: 'string',
3710
+ defaultValue: '12px',
3711
+ interface: {
3712
+ name: 'fontSize',
3713
+ path: 'options.fontSize',
3714
+ type: AXPWidgetsCatalog.text,
3715
+ },
3716
+ },
3717
+ visible: true,
3718
+ };
3719
+ const AXP_BG_COLOR_PROPERTY = {
3720
+ name: 'backgroundColor',
3721
+ title: 'Background Color',
3722
+ group: AXP_STYLING_PROPERTY_GROUP,
3723
+ schema: {
3724
+ dataType: 'string',
3725
+ interface: {
3726
+ path: 'options.backgroundColor',
3727
+ options: {
3728
+ hasClearButton: true,
3729
+ },
3730
+ type: AXPWidgetsCatalog.color,
3731
+ },
3732
+ },
3733
+ visible: true,
3734
+ };
3735
+
3736
+ const AXP_CONTENT_PROPERTY = createStringProperty({
3737
+ name: 'content',
3738
+ title: 'Content',
3739
+ path: 'options.content',
3740
+ group: AXP_DATA_PROPERTY_GROUP,
3741
+ });
3742
+ const AXP_PLACEHOLDER_PROPERTY = createStringProperty({
3743
+ name: 'placeholder',
3744
+ title: 'Placeholder',
3745
+ path: 'options.placeholder',
3746
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
3747
+ });
3748
+ const AXP_DATA_PATH_PROPERTY = createStringProperty({
3749
+ name: 'path',
3750
+ title: 'Data Path',
3751
+ group: AXP_DATA_PROPERTY_GROUP,
3752
+ });
3753
+ const AXP_DISABLED_PROPERTY = createBooleanProperty({
3754
+ name: 'disabled',
3755
+ title: 'Disabled',
3756
+ path: 'options.disabled',
3757
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
3758
+ });
3759
+ const AXP_READONLY_PROPERTY = createBooleanProperty({
3760
+ name: 'readonly',
3761
+ title: 'Readonly',
3762
+ path: 'options.readonly',
3763
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
3764
+ });
3765
+ const AXP_ALLOW_MULTIPLE_PROPERTY = createBooleanProperty({
3766
+ name: 'multiple',
3767
+ title: 'Multiple',
3768
+ path: 'options.multiple',
3769
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
3770
+ });
3771
+ const AXP_ALLOW_CLEAR_PROPERTY = createBooleanProperty({
3772
+ name: 'allowClear',
3773
+ title: 'Allow Clear',
3774
+ path: 'options.allowClear',
3775
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
3776
+ });
3777
+ const AXP_HAS_LABEL_PROPERTY = createBooleanProperty({
3778
+ name: 'hasLabel',
3779
+ title: 'Label',
3780
+ path: 'options.hasLabel',
3781
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
3782
+ defaultValue: true,
3783
+ });
3784
+ const AXP_HAS_ICON_PROPERTY = createBooleanProperty({
3785
+ name: 'hasIcon',
3786
+ title: 'Icon',
3787
+ path: 'options.hasIcon',
3788
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
3789
+ defaultValue: true,
3790
+ });
3791
+ const AXP_ICON_PROPERTY = createStringProperty({
3792
+ name: 'icon',
3793
+ title: 'Icon Name',
3794
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
3795
+ path: 'options.icon',
3796
+ });
3797
+ const AXP_IS_LOADING_PROPERTY = createBooleanProperty({
3798
+ name: 'loading',
3799
+ title: 'Is Loading',
3800
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
3801
+ path: 'options.loading',
3802
+ });
3803
+ const AXP_HAS_CLEAR_BUTTON_PROPERTY = createBooleanProperty({
3804
+ name: 'hasClearButton',
3805
+ title: 'Clear Button',
3806
+ path: 'options.hasClearButton',
3807
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
3808
+ defaultValue: false,
3809
+ });
3810
+ const AXP_ALLOW_SEARCH_PROPERTY = createBooleanProperty({
3811
+ name: 'allowSearch',
3812
+ title: 'Allow Search',
3813
+ path: 'options.allowSearch',
3814
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
3815
+ defaultValue: false,
3816
+ });
3817
+ const AXP_HAS_COPY_ICON_PROPERTY = createBooleanProperty({
3818
+ name: 'hasCopyIcon',
3819
+ title: 'Copy Icon',
3820
+ path: 'options.hasCopyIcon',
3821
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
3822
+ defaultValue: true,
3823
+ });
3824
+ const AXP_HAS_EYE_ICON_PROPERTY = createBooleanProperty({
3825
+ name: 'hasEyeIcon',
3826
+ title: 'Eye Icon',
3827
+ path: 'options.hasEyeIcon',
3828
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
3829
+ defaultValue: true,
3830
+ });
3831
+ const AXP_DOWNLOADABLE_PROPERTY = createBooleanProperty({
3832
+ name: 'downloadable',
3833
+ title: 'Downloadable',
3834
+ path: 'options.downloadable',
3835
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
3836
+ defaultValue: true,
3837
+ });
3838
+ // Converted with createStringProperty
3839
+ const AXP_TRULY_TEXT_PROPERTY = createStringProperty({
3840
+ name: 'trulyText',
3841
+ title: 'Truly Text',
3842
+ path: 'options.trulyText',
3843
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
3844
+ });
3845
+ const AXP_FALSY_TEXT_PROPERTY = createStringProperty({
3846
+ name: 'falsyText',
3847
+ title: 'Falsy Text',
3848
+ path: 'options.falsyText',
3849
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
3850
+ });
3851
+ const AXP_TEXT_PROPERTY = createStringProperty({
3852
+ name: 'text',
3853
+ title: 'Text',
3854
+ path: 'options.text',
3855
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
3856
+ });
3857
+ const AXP_TITLE_PROPERTY = createStringProperty({
3858
+ name: 'title',
3859
+ title: 'Title',
3860
+ path: 'options.title',
3861
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
3862
+ });
3863
+ const AXP_LABEL_PROPERTY = createStringProperty({
3864
+ name: 'label',
3865
+ title: 'Label',
3866
+ path: 'options.label',
3867
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
3868
+ defaultValue: '',
3869
+ });
3870
+ const AXP_VALUE_FIELD_PROPERTY = createStringProperty({
3871
+ name: 'valueField',
3872
+ title: 'Value Field',
3873
+ path: 'options.valueField',
3874
+ group: AXP_DATA_PROPERTY_GROUP,
3875
+ defaultValue: 'id',
3876
+ });
3877
+ const AXP_TEXT_FIELD_PROPERTY = createStringProperty({
3878
+ name: 'textField',
3879
+ title: 'Text Field',
3880
+ path: 'options.textField',
3881
+ group: AXP_DATA_PROPERTY_GROUP,
3882
+ defaultValue: 'title',
3883
+ });
3884
+ const AXP_DESCRIPTION_PROPERTY = createStringProperty({
3885
+ name: 'description',
3886
+ title: 'Description',
3887
+ path: 'options.description',
3888
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
3889
+ });
3890
+ const AXP_STYLE_COLOR_PROPERTY = createSelectProperty({
3891
+ name: 'color',
3892
+ title: 'Color',
3893
+ dataSource: AX_STYLE_COLOR_TYPES,
3894
+ defaultValue: 'primary',
3895
+ path: 'options.color',
3896
+ group: AXP_STYLING_PROPERTY_GROUP,
3897
+ });
3898
+ const AXP_STYLE_LOOK_PROPERTY = createSelectProperty({
3899
+ name: 'look',
3900
+ title: 'Look',
3901
+ dataSource: AX_STYLE_LOOK_TYPES,
3902
+ defaultValue: 'solid',
3903
+ path: 'options.look',
3904
+ group: AXP_STYLING_PROPERTY_GROUP,
3905
+ });
3906
+ const AXP_TEXT_ALIGN_PROPERTY = createSelectProperty({
3907
+ name: 'textAlign',
3908
+ title: 'Text Align',
3909
+ path: 'options.textAlign',
3910
+ group: AXP_STYLING_PROPERTY_GROUP,
3911
+ defaultValue: { id: 'left', title: 'Left' },
3912
+ dataSource: [
3913
+ { id: 'left', title: 'Left' },
3914
+ { id: 'center', title: 'Center' },
3915
+ { id: 'right', title: 'Right' },
3916
+ { id: 'justify', title: 'Justify' },
3917
+ ],
3918
+ });
3919
+ const AXP_VERTICAL_ALIGN_PROPERTY = createSelectProperty({
3920
+ name: 'verticalAlign',
3921
+ title: 'Vertical Align',
3922
+ path: 'options.verticalAlign',
3923
+ group: AXP_STYLING_PROPERTY_GROUP,
3924
+ defaultValue: { id: 'top', title: 'Top' },
3925
+ dataSource: [
3926
+ { id: 'top', title: 'Top' },
3927
+ { id: 'middle', title: 'Middle' },
3928
+ { id: 'bottom', title: 'Bottom' },
3929
+ { id: 'baseline', title: 'Baseline' },
3930
+ ],
3931
+ });
3932
+ const AXP_DATE_FORMAT_PROPERTY = createSelectProperty({
3933
+ name: 'format',
3934
+ title: 'Format',
3935
+ dataSource: [
3936
+ { id: 'date', title: 'Date' },
3937
+ { id: 'datetime', title: 'DateTime' },
3938
+ { id: 'time', title: 'Time' },
3939
+ ],
3940
+ defaultValue: { id: 'date', title: 'Date' },
3941
+ path: 'options.format',
3942
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
3943
+ });
3944
+ const AXP_DATE_DEPTH_PROPERTY = createSelectProperty({
3945
+ name: 'depth',
3946
+ title: 'Input Depth',
3947
+ dataSource: [
3948
+ { id: 'year', title: 'Year' },
3949
+ { id: 'month', title: 'Month' },
3950
+ { id: 'day', title: 'Day' },
3951
+ ],
3952
+ path: 'options.depth',
3953
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
3954
+ defaultValue: { id: 'day', title: 'Day' },
3955
+ });
3956
+ /** Max span in days between from/to when filter operation is `between`. */
3957
+ const AXP_DATE_RANGE_LIMIT_PROPERTY = createNumberProperty({
3958
+ name: 'limit',
3959
+ title: 'Range Limit (Days)',
3960
+ path: 'options.limit',
3961
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
3962
+ });
3963
+ function booleanDefaultProperty(defaultValue = false) {
3964
+ return {
3965
+ name: 'defaultValue',
3966
+ title: 'Default Value',
3967
+ group: AXP_DATA_PROPERTY_GROUP,
3968
+ schema: {
3969
+ dataType: 'boolean',
3970
+ defaultValue: defaultValue,
3971
+ interface: {
3972
+ name: 'defaultValue',
3973
+ path: 'defaultValue',
3974
+ type: AXPWidgetsCatalog.toggle,
3975
+ },
3976
+ },
3977
+ visible: true,
3978
+ };
3979
+ }
3980
+ function plainTextDefaultProperty() {
3981
+ return {
3982
+ name: 'defaultValue',
3983
+ title: 'Default Value',
3984
+ group: AXP_DATA_PROPERTY_GROUP,
3985
+ schema: {
3986
+ dataType: 'string',
3987
+ interface: {
3988
+ name: 'defaultValue',
3989
+ path: 'defaultValue',
3990
+ type: AXPWidgetsCatalog.text,
3991
+ options: {
3992
+ placeholder: '@layout-designer:property-editor.placeholders.default-value-plain-text',
3993
+ },
3994
+ },
3995
+ },
3996
+ visible: true,
3997
+ };
3998
+ }
3999
+ function largeTextDefaultProperty() {
4000
+ return {
4001
+ name: 'defaultValue',
4002
+ title: 'Default Value',
4003
+ group: AXP_DATA_PROPERTY_GROUP,
4004
+ schema: {
4005
+ dataType: 'string',
4006
+ interface: {
4007
+ name: 'defaultValue',
4008
+ path: 'defaultValue',
4009
+ type: AXPWidgetsCatalog.largeText,
4010
+ options: {
4011
+ placeholder: '@layout-designer:property-editor.placeholders.default-value-large-text',
4012
+ },
4013
+ },
4014
+ },
4015
+ visible: true,
4016
+ };
4017
+ }
4018
+ function richTextDefaultProperty() {
4019
+ return {
4020
+ name: 'defaultValue',
4021
+ title: 'Default Value',
4022
+ group: AXP_DATA_PROPERTY_GROUP,
4023
+ schema: {
4024
+ dataType: 'string',
4025
+ interface: {
4026
+ name: 'defaultValue',
4027
+ path: 'defaultValue',
4028
+ type: AXPWidgetsCatalog.richText,
4029
+ options: {
4030
+ placeholder: '@layout-designer:property-editor.placeholders.default-value-rich-text',
4031
+ },
4032
+ },
4033
+ },
4034
+ visible: true,
4035
+ };
4036
+ }
4037
+ /**
4038
+ * Default value editor for select-editor: uses `context.options(key)` so the default picker reads
4039
+ * the **host** widget's options from the shared context (not the nested property field's `widget`).
4040
+ */
4041
+ function selectEditorDefaultValueProperty() {
4042
+ return {
4043
+ name: 'defaultValue',
4044
+ title: 'Default Value',
4045
+ group: AXP_DATA_PROPERTY_GROUP,
4046
+ schema: {
4047
+ dataType: 'object',
4048
+ interface: {
4049
+ name: 'defaultValue',
4050
+ path: 'defaultValue',
4051
+ type: AXPWidgetsCatalog.select,
4052
+ options: {
4053
+ placeholder: '@layout-designer:property-editor.placeholders.default-value-select',
4054
+ dataSource: '{{ context.options("dataSource") }}',
4055
+ valueField: '{{ context.options("valueField") }}',
4056
+ textField: '{{ context.options("textField") }}',
4057
+ multiple: '{{ context.options("multiple") }}',
4058
+ hasClearButton: true,
4059
+ },
4060
+ },
4061
+ },
4062
+ visible: true,
4063
+ };
4064
+ }
4065
+ /**
4066
+ * Default value editor for selection-list-editor: uses the select widget in the designer so picking
4067
+ * a default matches select UX while still binding to the host's dataSource / fields via `context.options`.
4068
+ */
4069
+ function selectionListEditorDefaultValueProperty() {
4070
+ return {
4071
+ name: 'defaultValue',
4072
+ title: 'Default Value',
4073
+ group: AXP_DATA_PROPERTY_GROUP,
4074
+ schema: {
4075
+ dataType: 'object',
4076
+ interface: {
4077
+ name: 'defaultValue',
4078
+ path: 'defaultValue',
4079
+ type: AXPWidgetsCatalog.select,
4080
+ options: {
4081
+ placeholder: '@layout-designer:property-editor.placeholders.default-value-select',
4082
+ dataSource: '{{ context.options("dataSource") }}',
4083
+ valueField: '{{ context.options("valueField") }}',
4084
+ textField: '{{ context.options("textField") }}',
4085
+ multiple: '{{ context.options("multiple") }}',
4086
+ hasClearButton: true,
4087
+ },
4088
+ },
4089
+ },
4090
+ visible: true,
4091
+ };
4092
+ }
4093
+ function numberDefaultProperty() {
4094
+ return {
4095
+ name: 'defaultValue',
4096
+ title: 'Default Value',
4097
+ group: AXP_DATA_PROPERTY_GROUP,
4098
+ schema: {
4099
+ dataType: 'number',
4100
+ interface: {
4101
+ name: 'defaultValue',
4102
+ path: 'defaultValue',
4103
+ type: AXPWidgetsCatalog.number,
4104
+ options: {
4105
+ placeholder: '@layout-designer:property-editor.placeholders.default-value-number',
4106
+ },
4107
+ },
4108
+ },
4109
+ visible: true,
4110
+ };
4111
+ }
4112
+ function numberMinValueProperty(value) {
4113
+ return {
4114
+ name: 'minValue',
4115
+ title: 'Min Value',
4116
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
4117
+ order: 9,
4118
+ schema: {
4119
+ dataType: 'number',
4120
+ defaultValue: value,
4121
+ interface: {
4122
+ name: 'minValue',
4123
+ path: 'options.minValue',
4124
+ type: AXPWidgetsCatalog.number,
4125
+ options: {
4126
+ placeholder: '@layout-designer:property-editor.placeholders.number-min-value',
4127
+ },
4128
+ },
4129
+ },
4130
+ visible: true,
4131
+ };
4132
+ }
4133
+ function numberMaxValueProperty(value) {
4134
+ return {
4135
+ name: 'maxValue',
4136
+ title: 'Max Value',
4137
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
4138
+ order: 10,
4139
+ schema: {
4140
+ dataType: 'number',
4141
+ defaultValue: value,
4142
+ interface: {
4143
+ name: 'maxValue',
4144
+ path: 'options.maxValue',
4145
+ type: AXPWidgetsCatalog.number,
4146
+ options: {
4147
+ placeholder: '@layout-designer:property-editor.placeholders.number-max-value',
4148
+ },
4149
+ },
4150
+ },
4151
+ visible: true,
4152
+ };
4153
+ }
4154
+ const AXP_MULTI_LANGUAGE_PROPERTY = createBooleanProperty({
4155
+ name: 'multiLanguage',
4156
+ title: 'Multi Language',
4157
+ path: 'options.multiLanguage',
4158
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
4159
+ defaultValue: false,
4160
+ });
4161
+ const AXP_SPIN_BUTTON_PROPERTY = createBooleanProperty({
4162
+ name: 'spinButton',
4163
+ title: 'Spin Button',
4164
+ path: 'options.spinButton',
4165
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
4166
+ });
4167
+ const AXP_NUMBER_SEPARATOR_PROPERTY = createStringProperty({
4168
+ name: 'seprator',
4169
+ title: 'Thousands Separator',
4170
+ path: 'options.seprator',
4171
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
4172
+ });
4173
+ const AXP_DEFAULT_ROW_COUNT_PROPERTY = createNumberProperty({
4174
+ name: 'defaultRowCount',
4175
+ title: 'Default Row Count',
4176
+ path: 'options.defaultRowCount',
4177
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
4178
+ });
4179
+ const AXP_MIN_LINE_COUNT_PROPERTY = createNumberProperty({
4180
+ name: 'minLineCount',
4181
+ title: 'Min Line Count',
4182
+ path: 'options.minLineCount',
4183
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
4184
+ });
4185
+ const AXP_MAX_LINE_COUNT_PROPERTY = createNumberProperty({
4186
+ name: 'maxLineCount',
4187
+ title: 'Max Line Count',
4188
+ path: 'options.maxLineCount',
4189
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
4190
+ });
4191
+ const AXP_FIT_LINE_COUNT_PROPERTY = createBooleanProperty({
4192
+ name: 'fitLineCount',
4193
+ title: 'Fit Line Count',
4194
+ path: 'options.fitLineCount',
4195
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
4196
+ });
4197
+ const AXP_SHOW_PASSWORD_PROPERTY = createBooleanProperty({
4198
+ name: 'showPassword',
4199
+ title: 'Show Password',
4200
+ path: 'options.showPassword',
4201
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
4202
+ });
4203
+ const AXP_TRIGGERS_PROPERTY = {
4204
+ name: 'triggers',
4205
+ title: 'Triggers',
4206
+ group: AXP_TRIGGERS_PROPERTY_GROUP,
4207
+ showLabel: false,
4208
+ schema: {
4209
+ dataType: 'object',
4210
+ interface: {
4211
+ name: 'triggers',
4212
+ path: 'triggers',
4213
+ type: 'triggers-editor',
4214
+ },
4215
+ },
4216
+ visible: true,
4217
+ };
4218
+
4219
+ const AXP_LAYOUT_GRID_PROPERTY_GROUP = {
4220
+ name: 'grid',
4221
+ order: 1,
4222
+ title: 'Grid Layout',
4223
+ };
4224
+ const AXP_LAYOUT_FLEX_PROPERTY_GROUP = {
4225
+ name: 'flex',
4226
+ order: 1,
4227
+ title: 'Flex Layout',
4228
+ };
4229
+ const AXP_BOX_MODEL_PROPERTY_GROUP = {
4230
+ name: 'box',
4231
+ order: 1,
4232
+ title: 'Box Model',
4233
+ };
4234
+ const AXP_LAYOUT_TABLE_PROPERTY_GROUP = {
4235
+ name: 'table',
4236
+ order: 1,
4237
+ title: 'Table Layout',
4238
+ };
4239
+ const AXP_LAYOUT_GAP_PROPERTY = {
4240
+ name: 'gap',
4241
+ title: 'Gap',
4242
+ group: AXP_LAYOUT_GRID_PROPERTY_GROUP,
4243
+ schema: {
4244
+ dataType: 'number',
4245
+ defaultValue: 2,
4246
+ interface: {
4247
+ name: 'gap',
4248
+ path: 'options.gap',
4249
+ type: AXPWidgetsCatalog.number,
4250
+ options: {
4251
+ minValue: 0,
4252
+ maxValue: 12,
4253
+ },
4254
+ },
4255
+ },
4256
+ visible: true,
4257
+ };
4258
+ const AXP_LAYOUT_COLUMNS_PROPERTY = {
4259
+ name: 'cols',
4260
+ title: 'Columns',
4261
+ group: AXP_LAYOUT_GRID_PROPERTY_GROUP,
4262
+ schema: {
4263
+ dataType: 'number',
4264
+ defaultValue: 1,
4265
+ interface: {
4266
+ name: 'cols',
4267
+ path: 'options.cols',
4268
+ type: AXPWidgetsCatalog.number,
4269
+ options: {
4270
+ minValue: 1,
4271
+ maxValue: 12,
4272
+ },
4273
+ },
4274
+ },
4275
+ visible: true,
4276
+ };
4277
+ const AXP_LAYOUT_ROWS_PROPERTY = {
4278
+ name: 'rows',
4279
+ title: 'Rows',
4280
+ group: AXP_LAYOUT_GRID_PROPERTY_GROUP,
4281
+ schema: {
4282
+ dataType: 'number',
4283
+ defaultValue: 1,
4284
+ interface: {
4285
+ name: 'rows',
4286
+ path: 'options.rows',
4287
+ type: AXPWidgetsCatalog.number,
4288
+ options: {
4289
+ minValue: 1,
4290
+ maxValue: 100,
4291
+ },
4292
+ },
4293
+ },
4294
+ visible: true,
4295
+ };
4296
+ const AXP_LAYOUT_GRID_PROPERTIES = [
4297
+ AXP_LAYOUT_GAP_PROPERTY,
4298
+ AXP_LAYOUT_COLUMNS_PROPERTY,
4299
+ AXP_LAYOUT_ROWS_PROPERTY,
4300
+ ];
4301
+ const AXP_LAYOUT_GRID_ROW_PROPERTIES = [
4302
+ AXP_LAYOUT_GAP_PROPERTY,
4303
+ cloneProperty(AXP_LAYOUT_COLUMNS_PROPERTY, { schema: { defaultValue: 12 } }),
4304
+ ];
4305
+ const AXP_LAYOUT_GRID_ITEM_PROPERTY = {
4306
+ name: 'gridItem',
4307
+ title: 'Grid Item',
4308
+ group: AXP_LAYOUT_GRID_PROPERTY_GROUP,
4309
+ schema: {
4310
+ dataType: 'object',
4311
+ interface: {
4312
+ name: 'gridItem',
4313
+ path: 'options',
4314
+ type: AXPWidgetsCatalog.gridItemOptions,
4315
+ options: {},
4316
+ },
4317
+ },
4318
+ visible: true,
4319
+ };
4320
+ const AXP_LAYOUT_SPACING_PROPERTY = {
4321
+ name: 'spacing',
4322
+ title: 'Spacing',
4323
+ group: AXP_BOX_MODEL_PROPERTY_GROUP,
4324
+ schema: {
4325
+ dataType: 'object',
4326
+ interface: {
4327
+ name: 'spacing',
4328
+ path: 'options.spacing',
4329
+ type: AXPWidgetsCatalog.spacing,
4330
+ options: {},
4331
+ },
4332
+ },
4333
+ visible: true,
4334
+ };
4335
+ const AXP_LAYOUT_BORDER_PROPERTY = {
4336
+ name: 'border',
4337
+ title: 'Border',
4338
+ group: AXP_BOX_MODEL_PROPERTY_GROUP,
4339
+ schema: {
4340
+ dataType: 'object',
4341
+ interface: {
4342
+ name: 'border',
4343
+ path: 'options.border',
4344
+ type: AXPWidgetsCatalog.border,
4345
+ options: {},
4346
+ },
4347
+ },
4348
+ visible: true,
4349
+ };
4350
+ const AXP_LAYOUT_FLEX_PROPERTY = {
4351
+ name: 'flex',
4352
+ title: 'Flex',
4353
+ group: AXP_LAYOUT_FLEX_PROPERTY_GROUP,
4354
+ schema: {
4355
+ dataType: 'object',
4356
+ interface: {
4357
+ name: 'flex',
4358
+ path: 'options',
4359
+ type: AXPWidgetsCatalog.flexOptions,
4360
+ options: {},
4361
+ },
4362
+ },
4363
+ visible: true,
4364
+ };
4365
+ const AXP_LAYOUT_FLEX_ITEM_PROPERTY = {
4366
+ name: 'flexItem',
4367
+ title: 'Flex Item',
4368
+ group: AXP_LAYOUT_FLEX_PROPERTY_GROUP,
4369
+ schema: {
4370
+ dataType: 'object',
4371
+ interface: {
4372
+ name: 'flexItem',
4373
+ path: 'options',
4374
+ type: AXPWidgetsCatalog.flexItemOptions,
4375
+ options: {},
4376
+ },
4377
+ },
4378
+ visible: true,
4379
+ };
4380
+ const AXP_LAYOUT_GRID_PROPERTY = {
4381
+ name: 'grid',
4382
+ title: 'Grid',
4383
+ group: AXP_LAYOUT_GRID_PROPERTY_GROUP,
4384
+ schema: {
4385
+ dataType: 'object',
4386
+ interface: {
4387
+ name: 'grid',
4388
+ path: 'options.grid',
4389
+ type: AXPWidgetsCatalog.gridOptions,
4390
+ options: {},
4391
+ },
4392
+ },
4393
+ visible: true,
4394
+ };
4395
+ const AXP_TABLE_COLUMNS_PROPERTY = {
4396
+ name: 'columns',
4397
+ title: 'Columns',
4398
+ group: AXP_LAYOUT_TABLE_PROPERTY_GROUP,
4399
+ schema: {
4400
+ dataType: 'number',
4401
+ defaultValue: 3,
4402
+ interface: {
4403
+ name: 'columns',
4404
+ path: 'options.columns',
4405
+ type: AXPWidgetsCatalog.number,
4406
+ options: {
4407
+ minValue: 1,
4408
+ maxValue: 12,
4409
+ },
4410
+ },
4411
+ },
4412
+ visible: true,
4413
+ };
4414
+ // Removed rowHeight/rows from table properties
4415
+ const AXP_TABLE_ITEM_COLSPAN_PROPERTY = {
4416
+ name: 'colSpan',
4417
+ title: 'Column Span',
4418
+ group: AXP_LAYOUT_TABLE_PROPERTY_GROUP,
4419
+ schema: {
4420
+ dataType: 'number',
4421
+ defaultValue: 1,
4422
+ interface: {
4423
+ name: 'colSpan',
4424
+ path: 'options.colSpan',
4425
+ type: AXPWidgetsCatalog.number,
4426
+ options: {
4427
+ minValue: 1,
4428
+ maxValue: 24,
4429
+ },
4430
+ },
4431
+ },
4432
+ visible: true,
4433
+ };
4434
+ const AXP_TABLE_ITEM_ROWSPAN_PROPERTY = {
4435
+ name: 'rowSpan',
4436
+ title: 'Row Span',
4437
+ group: AXP_LAYOUT_TABLE_PROPERTY_GROUP,
4438
+ schema: {
4439
+ dataType: 'number',
4440
+ defaultValue: 1,
4441
+ interface: {
4442
+ name: 'rowSpan',
4443
+ path: 'options.rowSpan',
4444
+ type: AXPWidgetsCatalog.number,
4445
+ options: {
4446
+ minValue: 1,
4447
+ maxValue: 100,
4448
+ },
4449
+ },
4450
+ },
4451
+ visible: true,
4452
+ };
4453
+ // Removed header/footer/sticky related table properties
4454
+ const AXP_LAYOUT_ADVANCED_GRID_PROPERTY = {
4455
+ name: 'advancedGrid',
4456
+ title: 'Advanced Grid',
4457
+ group: AXP_LAYOUT_GRID_PROPERTY_GROUP,
4458
+ schema: {
4459
+ dataType: 'object',
4460
+ interface: {
4461
+ name: 'advancedGrid',
4462
+ path: 'options.advancedGrid',
4463
+ type: AXPWidgetsCatalog.advancedGridOptions,
4464
+ options: {},
4465
+ },
4466
+ },
4467
+ visible: true,
4468
+ };
4469
+ const AXP_LAYOUT_SHOW_HEADER_PROPERTY = createBooleanProperty({
4470
+ name: 'showHeader',
4471
+ title: 'Show Header',
4472
+ group: AXP_BEHAVIOR_PROPERTY_GROUP,
4473
+ path: 'options.showHeader',
4474
+ defaultValue: true,
4475
+ });
4476
+ const AXP_LAYOUT_DIRECTION_PROPERTY = {
4477
+ name: 'direction',
4478
+ title: 'Direction',
4479
+ group: AXP_BOX_MODEL_PROPERTY_GROUP,
4480
+ order: 99,
4481
+ schema: {
4482
+ dataType: 'string',
4483
+ interface: {
4484
+ name: 'direction',
4485
+ path: 'options.direction',
4486
+ type: AXPWidgetsCatalog.direction,
4487
+ options: {},
4488
+ },
4489
+ },
4490
+ visible: true,
4491
+ };
4492
+
4493
+ const TABLE_COLUMN_ALIGN_DATA_SOURCE = [
4494
+ { id: 'start', title: 'Start' },
4495
+ { id: 'center', title: 'Center' },
4496
+ { id: 'end', title: 'End' },
4497
+ ];
4498
+ const AXP_TABLE_COLUMN_HEADER_ALIGN_PROPERTY = {
4499
+ name: 'alignHeader',
4500
+ title: 'Header alignment',
4501
+ group: AXP_STYLING_PROPERTY_GROUP,
4502
+ schema: {
4503
+ dataType: 'string',
4504
+ defaultValue: 'start',
4505
+ interface: {
4506
+ name: 'alignHeader',
4507
+ path: 'options.align.header',
4508
+ type: AXPWidgetsCatalog.select,
4509
+ options: {
4510
+ dataSource: [...TABLE_COLUMN_ALIGN_DATA_SOURCE],
4511
+ },
4512
+ },
4513
+ },
4514
+ visible: true,
4515
+ };
4516
+ const AXP_TABLE_COLUMN_CELL_ALIGN_PROPERTY = {
4517
+ name: 'alignCell',
4518
+ title: 'Cell alignment',
4519
+ group: AXP_STYLING_PROPERTY_GROUP,
4520
+ schema: {
4521
+ dataType: 'string',
4522
+ defaultValue: 'start',
4523
+ interface: {
4524
+ name: 'alignCell',
4525
+ path: 'options.align.cell',
4526
+ type: AXPWidgetsCatalog.select,
4527
+ options: {
4528
+ dataSource: [...TABLE_COLUMN_ALIGN_DATA_SOURCE],
4529
+ },
4530
+ },
4531
+ },
4532
+ visible: true,
4533
+ };
4534
+ /** Optional column designer fields; spread next to `AXP_TABLE_COLUMN_WIDTH_PROPERTY` where needed. */
4535
+ const AXP_TABLE_COLUMN_ALIGNMENT_PROPERTIES = [
4536
+ AXP_TABLE_COLUMN_HEADER_ALIGN_PROPERTY,
4537
+ AXP_TABLE_COLUMN_CELL_ALIGN_PROPERTY,
4538
+ ];
4539
+ const AXP_TABLE_COLUMN_WIDTH_PROPERTY = {
4540
+ name: 'width',
4541
+ title: 'Width',
4542
+ group: AXP_STYLING_PROPERTY_GROUP,
4543
+ schema: {
4544
+ dataType: 'string',
4545
+ defaultValue: '300px',
4546
+ interface: {
4547
+ name: 'width',
4548
+ path: 'options.width',
4549
+ type: AXPWidgetsCatalog.text,
4550
+ },
4551
+ },
4552
+ visible: true,
4553
+ };
4554
+ const AXP_TABLE_COLUMN_HEIGHT_PROPERTY = {
4555
+ name: 'height',
4556
+ title: 'Height',
4557
+ group: AXP_STYLING_PROPERTY_GROUP,
4558
+ schema: {
4559
+ dataType: 'string',
4560
+ defaultValue: '300px',
4561
+ interface: {
4562
+ name: 'height',
4563
+ path: 'options.height',
4564
+ type: AXPWidgetsCatalog.text,
4565
+ },
4566
+ },
4567
+ visible: true,
4568
+ };
4569
+
4570
+ const AXP_REQUIRED_VALIDATION_PROPERTY = {
4571
+ name: 'requiredValidation',
4572
+ title: 'Required',
4573
+ group: AXP_VALIDATION_PROPERTY_GROUP,
4574
+ schema: {
4575
+ dataType: 'object',
4576
+ interface: {
4577
+ name: 'requiredValidation',
4578
+ path: 'options.validations.required',
4579
+ type: AXPWidgetsCatalog.requiredValidation,
4580
+ defaultValue: false,
4581
+ },
4582
+ },
4583
+ visible: true,
4584
+ };
4585
+ const AXP_MIN_LENGTH_VALIDATION_PROPERTY = {
4586
+ name: 'minLengthValidation',
4587
+ title: 'Minimum Length',
4588
+ group: AXP_VALIDATION_PROPERTY_GROUP,
4589
+ schema: {
4590
+ dataType: 'object',
4591
+ interface: {
4592
+ name: 'minLengthValidation',
4593
+ path: 'options.validations.minLength',
4594
+ type: AXPWidgetsCatalog.minLengthValidation,
4595
+ defaultValue: null,
4596
+ },
4597
+ },
4598
+ visible: true,
4599
+ };
4600
+ const AXP_MAX_LENGTH_VALIDATION_PROPERTY = {
4601
+ name: 'maxLengthValidation',
4602
+ title: 'Maximum Length',
4603
+ group: AXP_VALIDATION_PROPERTY_GROUP,
4604
+ schema: {
4605
+ dataType: 'object',
4606
+ interface: {
4607
+ name: 'maxLengthValidation',
4608
+ path: 'options.validations.maxLength',
4609
+ type: AXPWidgetsCatalog.maxLengthValidation,
4610
+ defaultValue: null,
4611
+ },
4612
+ },
4613
+ visible: true,
4614
+ };
4615
+ const AXP_REGULAR_EXPRESSION_VALIDATION_PROPERTY = {
4616
+ name: 'regularExpressionValidation',
4617
+ title: 'Regular Expression',
4618
+ group: AXP_VALIDATION_PROPERTY_GROUP,
4619
+ schema: {
4620
+ dataType: 'object',
4621
+ interface: {
4622
+ name: 'regularExpressionValidation',
4623
+ path: 'options.validations.regex',
4624
+ type: AXPWidgetsCatalog.regularExpressionValidation,
4625
+ defaultValue: false,
4626
+ },
4627
+ },
4628
+ visible: true,
4629
+ };
4630
+ const AXP_LESS_THAN_VALIDATION_PROPERTY = {
4631
+ name: 'lessThanValidation',
4632
+ title: 'Less Than',
4633
+ group: AXP_VALIDATION_PROPERTY_GROUP,
4634
+ schema: {
4635
+ dataType: 'object',
4636
+ interface: {
4637
+ name: 'lessThanValidation',
4638
+ path: 'options.validations.lessThan',
4639
+ type: AXPWidgetsCatalog.lessThanValidation,
4640
+ defaultValue: false,
4641
+ },
4642
+ },
4643
+ visible: true,
4644
+ };
4645
+ const AXP_GREATER_THAN_VALIDATION_PROPERTY = {
4646
+ name: 'greaterThanValidation',
4647
+ title: 'Greater Than',
4648
+ group: AXP_VALIDATION_PROPERTY_GROUP,
4649
+ schema: {
4650
+ dataType: 'object',
4651
+ interface: {
4652
+ name: 'greaterThanValidation',
4653
+ path: 'options.validations.greaterThan',
4654
+ type: AXPWidgetsCatalog.greaterThanValidation,
4655
+ defaultValue: false,
4656
+ },
4657
+ },
4658
+ visible: true,
4659
+ };
4660
+ const AXP_BETWEEN_VALIDATION_PROPERTY = {
4661
+ name: 'betweenValidation',
4662
+ title: 'Between',
4663
+ group: AXP_VALIDATION_PROPERTY_GROUP,
4664
+ schema: {
4665
+ dataType: 'object',
4666
+ interface: {
4667
+ name: 'betweenValidation',
4668
+ path: 'options.validations.between',
4669
+ type: AXPWidgetsCatalog.betweenValidation,
4670
+ defaultValue: false,
4671
+ },
4672
+ },
4673
+ visible: true,
4674
+ };
4675
+ const AXP_EQUAL_VALIDATION_PROPERTY = {
4676
+ name: 'equalValidation',
4677
+ title: 'Equal',
4678
+ group: AXP_VALIDATION_PROPERTY_GROUP,
4679
+ schema: {
4680
+ dataType: 'object',
4681
+ interface: {
4682
+ name: 'equalValidation',
4683
+ path: 'options.validations.equal',
4684
+ type: AXPWidgetsCatalog.equalValidation,
4685
+ defaultValue: false,
4686
+ },
4687
+ },
4688
+ visible: true,
4689
+ };
4690
+ const AXP_CALLBACK_VALIDATION_PROPERTY = {
4691
+ name: 'callbackValidation',
4692
+ title: 'Callback',
4693
+ group: AXP_VALIDATION_PROPERTY_GROUP,
4694
+ schema: {
4695
+ dataType: 'object',
4696
+ interface: {
4697
+ name: 'callbackValidation',
4698
+ path: 'options.validations.callback',
4699
+ type: AXPWidgetsCatalog.callbackValidation,
4700
+ defaultValue: false,
4701
+ },
4702
+ },
4703
+ visible: true,
4704
+ };
4705
+
4706
+ /**
4707
+ * Generated bundle index. Do not edit.
4708
+ */
4709
+
4710
+ export { AXPBaseWidgetComponent, AXPBlockBaseLayoutWidgetComponent, AXPBoxModelLayoutWidgetComponent, AXPColumnWidgetComponent, AXPDataListWidgetComponent, AXPFlexBaseLayoutWidgetComponent, AXPFlexItemBaseLayoutWidgetComponent, AXPGridBaseLayoutWidgetComponent, AXPGridItemBaseLayoutWidgetComponent, AXPInlineBaseLayoutWidgetComponent, AXPLayoutBaseWidgetComponent, AXPPropertyEditorHelper, AXPTableBaseLayoutWidgetComponent, AXPTableItemBaseLayoutWidgetComponent, AXPTableItemOpsBaseLayoutWidgetComponent, AXPValueWidgetComponent, AXPWidgetColumnCellComponent, AXPWidgetColumnRendererComponent, AXPWidgetContainerComponent, AXPWidgetCoreElement, AXPWidgetCoreModule, AXPWidgetCoreService, AXPWidgetRegistryService, AXPWidgetRendererDirective, AXPWidgetSerializationHelper, AXP_ALLOW_CLEAR_PROPERTY, AXP_ALLOW_MULTIPLE_PROPERTY, AXP_ALLOW_SEARCH_PROPERTY, AXP_ANIMATION_PROPERTY_GROUP, AXP_APPEARANCE_PROPERTY_GROUP, AXP_BEHAVIOR_PROPERTY_GROUP, AXP_BETWEEN_VALIDATION_PROPERTY, AXP_BG_COLOR_PROPERTY, AXP_BOX_MODEL_PROPERTY_GROUP, AXP_CALLBACK_VALIDATION_PROPERTY, AXP_COLOR_PROPERTY, AXP_CONTENT_PROPERTY, AXP_DATA_PATH_PROPERTY, AXP_DATA_PROPERTY_GROUP, AXP_DATA_SOURCE_OPTIONS_PROPERTY, AXP_DATE_DEPTH_PROPERTY, AXP_DATE_FORMAT_PROPERTY, AXP_DATE_RANGE_LIMIT_PROPERTY, AXP_DEFAULT_ROW_COUNT_PROPERTY, AXP_DESCRIPTION_PROPERTY, AXP_DIRECTION_PROPERTY, AXP_DISABLED_PROPERTY, AXP_DOWNLOADABLE_PROPERTY, AXP_EQUAL_VALIDATION_PROPERTY, AXP_FALSY_TEXT_PROPERTY, AXP_FIT_LINE_COUNT_PROPERTY, AXP_FONT_SIZE_PROPERTY, AXP_GREATER_THAN_VALIDATION_PROPERTY, AXP_HAS_CLEAR_BUTTON_PROPERTY, AXP_HAS_COPY_ICON_PROPERTY, AXP_HAS_EYE_ICON_PROPERTY, AXP_HAS_ICON_PROPERTY, AXP_HAS_LABEL_PROPERTY, AXP_ICON_PROPERTY, AXP_IS_LOADING_PROPERTY, AXP_LABEL_PROPERTY, AXP_LAYOUT_ADVANCED_GRID_PROPERTY, AXP_LAYOUT_BORDER_PROPERTY, AXP_LAYOUT_COLUMNS_PROPERTY, AXP_LAYOUT_DIRECTION_PROPERTY, AXP_LAYOUT_FLEX_ITEM_PROPERTY, AXP_LAYOUT_FLEX_PROPERTY, AXP_LAYOUT_FLEX_PROPERTY_GROUP, AXP_LAYOUT_GAP_PROPERTY, AXP_LAYOUT_GRID_ITEM_PROPERTY, AXP_LAYOUT_GRID_PROPERTIES, AXP_LAYOUT_GRID_PROPERTY, AXP_LAYOUT_GRID_PROPERTY_GROUP, AXP_LAYOUT_GRID_ROW_PROPERTIES, AXP_LAYOUT_ROWS_PROPERTY, AXP_LAYOUT_SHOW_HEADER_PROPERTY, AXP_LAYOUT_SPACING_PROPERTY, AXP_LAYOUT_TABLE_PROPERTY_GROUP, AXP_LESS_THAN_VALIDATION_PROPERTY, AXP_MAX_LENGTH_VALIDATION_PROPERTY, AXP_MAX_LINE_COUNT_PROPERTY, AXP_MIN_LENGTH_VALIDATION_PROPERTY, AXP_MIN_LINE_COUNT_PROPERTY, AXP_MULTI_LANGUAGE_PROPERTY, AXP_NAME_PROPERTY, AXP_NUMBER_SEPARATOR_PROPERTY, AXP_PLACEHOLDER_PROPERTY, AXP_READONLY_PROPERTY, AXP_REGULAR_EXPRESSION_VALIDATION_PROPERTY, AXP_REQUIRED_VALIDATION_PROPERTY, AXP_SHOW_PASSWORD_PROPERTY, AXP_SPIN_BUTTON_PROPERTY, AXP_STYLE_COLOR_PROPERTY, AXP_STYLE_LOOK_PROPERTY, AXP_STYLING_PROPERTY_GROUP, AXP_TABLE_COLUMNS_PROPERTY, AXP_TABLE_COLUMN_ALIGNMENT_PROPERTIES, AXP_TABLE_COLUMN_CELL_ALIGN_PROPERTY, AXP_TABLE_COLUMN_HEADER_ALIGN_PROPERTY, AXP_TABLE_COLUMN_HEIGHT_PROPERTY, AXP_TABLE_COLUMN_WIDTH_PROPERTY, AXP_TABLE_ITEM_COLSPAN_PROPERTY, AXP_TABLE_ITEM_ROWSPAN_PROPERTY, AXP_TEXT_ALIGN_PROPERTY, AXP_TEXT_FIELD_PROPERTY, AXP_TEXT_PROPERTY, AXP_THEME_PROPERTY, AXP_TITLE_PROPERTY, AXP_TRIGGERS_PROPERTY, AXP_TRIGGERS_PROPERTY_GROUP, AXP_TRULY_TEXT_PROPERTY, AXP_VALIDATION_PROPERTY_GROUP, AXP_VALUE_FIELD_PROPERTY, AXP_VERTICAL_ALIGN_PROPERTY, AXP_WIDGETS_ACTION_CATEGORY, AXP_WIDGETS_ACTION_SUB_CONTROLS, AXP_WIDGETS_ADVANCE_CATEGORY, AXP_WIDGETS_ADVANCE_SUB_DATA, AXP_WIDGETS_ADVANCE_SUB_INPUT, AXP_WIDGETS_ADVANCE_SUB_MEDIA, AXP_WIDGETS_ADVANCE_SUB_TOOLS, AXP_WIDGETS_AI_CATEGORY, AXP_WIDGETS_CATEGORIES, AXP_WIDGETS_EDITOR_CATEGORY, AXP_WIDGETS_EDITOR_SUB_CHOICE, AXP_WIDGETS_EDITOR_SUB_COMPOSITE, AXP_WIDGETS_EDITOR_SUB_CONFIG, AXP_WIDGETS_EDITOR_SUB_DATETIME, AXP_WIDGETS_EDITOR_SUB_NUMERIC, AXP_WIDGETS_EDITOR_SUB_RICH, AXP_WIDGETS_EDITOR_SUB_TEXT_INPUTS, AXP_WIDGETS_EDITOR_SUB_VISUAL, AXP_WIDGETS_LAYOUT_CATEGORY, AXP_WIDGETS_LAYOUT_SUB_GRID, AXP_WIDGETS_LAYOUT_SUB_LISTS, AXP_WIDGETS_LAYOUT_SUB_SHELL, AXP_WIDGETS_LAYOUT_SUB_SURFACES, AXP_WIDGETS_LAYOUT_SUB_TABS, AXP_WIDGETS_LAYOUT_SUB_UTILITY, AXP_WIDGET_DEFINITION_PROVIDER, AXP_WIDGET_PROPERTY_GROUP, WIDGET_CASCADE_TRACE_NS, booleanDefaultProperty, buildWidgetRegistryMapFromProviders, findNonEmptyBreakpoints, largeTextDefaultProperty, mapAXPPropertyToWidgetProperty, normalizeWidgetCategories, numberDefaultProperty, numberMaxValueProperty, numberMinValueProperty, plainTextDefaultProperty, richTextDefaultProperty, selectEditorDefaultValueProperty, selectionListEditorDefaultValueProperty };
4711
+ //# sourceMappingURL=acorex-platform-framework-client-layout-widget-core.mjs.map