@acorex/platform 21.0.0-next.85 → 21.0.0-next.89

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 (81) hide show
  1. package/fesm2022/acorex-platform-auth.mjs +9 -3
  2. package/fesm2022/acorex-platform-auth.mjs.map +1 -1
  3. package/fesm2022/{acorex-platform-common-common-settings.provider-Ytey9uhY.mjs → acorex-platform-common-common-settings.provider-DilV4dql.mjs} +16 -3
  4. package/fesm2022/acorex-platform-common-common-settings.provider-DilV4dql.mjs.map +1 -0
  5. package/fesm2022/acorex-platform-common.mjs +3426 -2488
  6. package/fesm2022/acorex-platform-common.mjs.map +1 -1
  7. package/fesm2022/acorex-platform-contracts.mjs +1160 -9
  8. package/fesm2022/acorex-platform-contracts.mjs.map +1 -1
  9. package/fesm2022/acorex-platform-core.mjs +826 -152
  10. package/fesm2022/acorex-platform-core.mjs.map +1 -1
  11. package/fesm2022/acorex-platform-domain-contracts.mjs +58 -0
  12. package/fesm2022/acorex-platform-domain-contracts.mjs.map +1 -0
  13. package/fesm2022/acorex-platform-domain.mjs +2 -50
  14. package/fesm2022/acorex-platform-domain.mjs.map +1 -1
  15. package/fesm2022/acorex-platform-layout-builder.mjs +98 -189
  16. package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
  17. package/fesm2022/{acorex-platform-layout-components-binding-expression-editor-popup.component-CXEdvDTf.mjs → acorex-platform-layout-components-binding-expression-editor-popup.component-RttFwaw9.mjs} +17 -4
  18. package/fesm2022/acorex-platform-layout-components-binding-expression-editor-popup.component-RttFwaw9.mjs.map +1 -0
  19. package/fesm2022/acorex-platform-layout-components.mjs +181 -46
  20. package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
  21. package/fesm2022/acorex-platform-layout-designer.mjs +7 -6
  22. package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
  23. package/fesm2022/acorex-platform-layout-entity-attachments-page.component-CwG6n_Yb.mjs.map +1 -1
  24. package/fesm2022/acorex-platform-layout-entity-contracts.mjs +1208 -0
  25. package/fesm2022/acorex-platform-layout-entity-contracts.mjs.map +1 -0
  26. package/fesm2022/acorex-platform-layout-entity-file-list-popup.component--IeMZLIr.mjs.map +1 -1
  27. package/fesm2022/acorex-platform-layout-entity.mjs +1080 -889
  28. package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
  29. package/fesm2022/acorex-platform-layout-views.mjs +1150 -247
  30. package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
  31. package/fesm2022/acorex-platform-layout-widget-core.mjs +388 -207
  32. package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
  33. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-CFyfix31.mjs +513 -0
  34. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-CFyfix31.mjs.map +1 -0
  35. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-edit-popup.component-DjpZU6gz.mjs → acorex-platform-layout-widgets-tabular-data-edit-popup.component-CZc2zFlg.mjs} +4 -4
  36. package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-CZc2zFlg.mjs.map +1 -0
  37. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-view-popup.component-gX-3Kx9I.mjs → acorex-platform-layout-widgets-tabular-data-view-popup.component-XWRUVoL4.mjs} +2 -2
  38. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-view-popup.component-gX-3Kx9I.mjs.map → acorex-platform-layout-widgets-tabular-data-view-popup.component-XWRUVoL4.mjs.map} +1 -1
  39. package/fesm2022/acorex-platform-layout-widgets.mjs +1541 -394
  40. package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
  41. package/fesm2022/acorex-platform-runtime.mjs.map +1 -1
  42. package/fesm2022/{acorex-platform-themes-default-entity-master-single-view.component-Br9p5aXT.mjs → acorex-platform-themes-default-entity-master-single-view.component-DrUwqKKA.mjs} +2 -3
  43. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-DrUwqKKA.mjs.map +1 -0
  44. package/fesm2022/acorex-platform-themes-default.mjs +956 -115
  45. package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
  46. package/fesm2022/{acorex-platform-themes-shared-settings.provider-BjuzSe0T.mjs → acorex-platform-themes-shared-settings.provider-UyKdkeyk.mjs} +50 -3
  47. package/fesm2022/acorex-platform-themes-shared-settings.provider-UyKdkeyk.mjs.map +1 -0
  48. package/fesm2022/acorex-platform-themes-shared.mjs +104 -24
  49. package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
  50. package/fesm2022/acorex-platform-workflow.mjs +6 -8
  51. package/fesm2022/acorex-platform-workflow.mjs.map +1 -1
  52. package/package.json +9 -5
  53. package/types/acorex-platform-auth.d.ts +3 -1
  54. package/types/acorex-platform-common.d.ts +655 -1268
  55. package/types/acorex-platform-contracts.d.ts +1791 -260
  56. package/types/acorex-platform-core.d.ts +2302 -2800
  57. package/types/acorex-platform-domain-contracts.d.ts +416 -0
  58. package/types/acorex-platform-domain.d.ts +4 -415
  59. package/types/acorex-platform-layout-builder.d.ts +11 -20
  60. package/types/acorex-platform-layout-components.d.ts +29 -5
  61. package/types/acorex-platform-layout-designer.d.ts +2 -1
  62. package/types/acorex-platform-layout-entity-contracts.d.ts +889 -0
  63. package/types/acorex-platform-layout-entity.d.ts +143 -275
  64. package/types/acorex-platform-layout-views.d.ts +147 -31
  65. package/types/acorex-platform-layout-widget-core.d.ts +74 -14
  66. package/types/acorex-platform-layout-widgets.d.ts +136 -38
  67. package/types/acorex-platform-runtime.d.ts +1 -1
  68. package/types/acorex-platform-themes-default.d.ts +227 -73
  69. package/types/acorex-platform-themes-shared.d.ts +4 -0
  70. package/types/acorex-platform-workflow.d.ts +5 -18
  71. package/fesm2022/acorex-platform-common-common-settings.provider-Ytey9uhY.mjs.map +0 -1
  72. package/fesm2022/acorex-platform-layout-components-binding-expression-editor-popup.component-CXEdvDTf.mjs.map +0 -1
  73. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-J0zcGKBX.mjs +0 -116
  74. package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-J0zcGKBX.mjs.map +0 -1
  75. package/fesm2022/acorex-platform-layout-widgets-tabular-data-edit-popup.component-DjpZU6gz.mjs.map +0 -1
  76. package/fesm2022/acorex-platform-native.mjs +0 -155
  77. package/fesm2022/acorex-platform-native.mjs.map +0 -1
  78. package/fesm2022/acorex-platform-themes-default-entity-master-single-view.component-Br9p5aXT.mjs.map +0 -1
  79. package/fesm2022/acorex-platform-themes-shared-settings.provider-BjuzSe0T.mjs.map +0 -1
  80. package/native/README.md +0 -4
  81. package/types/acorex-platform-native.d.ts +0 -28
@@ -1,10 +1,10 @@
1
1
  import * as i0 from '@angular/core';
2
- import { signal, computed, Injectable, InjectionToken, inject, ElementRef, effect, untracked, EventEmitter, Injector, ChangeDetectorRef, ViewChild, Input, Output, ChangeDetectionStrategy, Component, input, output, ViewContainerRef, Directive, NgModule } from '@angular/core';
2
+ import { signal, computed, effect, untracked, Injectable, InjectionToken, inject, ElementRef, DestroyRef, EventEmitter, Injector, ChangeDetectorRef, ViewChild, Input, Output, ChangeDetectionStrategy, Component, input, output, ViewContainerRef, Directive, NgModule } from '@angular/core';
3
3
  import { convertArrayToDataSource, AXDataSource, AX_STYLE_COLOR_TYPES, AX_STYLE_LOOK_TYPES } from '@acorex/cdk/common';
4
- import { AXPContextStore, isSelectionValueEqual, isFormValueEqual, AXPDataSourceDefinitionProviderService, extractValue, AXPExpressionEvaluatorService, getSmart } from '@acorex/platform/core';
5
- export { normalizeDefinitionCategories } from '@acorex/platform/core';
4
+ import { AXPContextStore, isSelectionValueEqual, isFormValueEqual, AXPDebugService, FORM_DIRTY_TRACE_NS, AXPDataSourceDefinitionProviderService, extractValue, AXPExpressionEvaluatorService, getSmart, summarizeFormContextDirtyDiff, OVERLAY_WIDGET_SETTLE_TRACE_NS } from '@acorex/platform/core';
6
5
  import { set, merge, cloneDeep, isNil, get, isEqual, isUndefined, isObjectLike, sum, isEmpty, isString } from 'lodash-es';
7
6
  import { Subject, BehaviorSubject, filter } from 'rxjs';
7
+ import { AXPWidgetsCatalog } from '@acorex/platform/contracts';
8
8
  import * as i1$1 from '@acorex/components/skeleton';
9
9
  import { AXSkeletonModule } from '@acorex/components/skeleton';
10
10
  import * as i2 from '@acorex/core/translation';
@@ -38,8 +38,6 @@ var AXPWidgetStatus;
38
38
  // Rendering statuses
39
39
  AXPWidgetStatus["Rendering"] = "rendering";
40
40
  AXPWidgetStatus["Rendered"] = "rendered";
41
- // Processing statuses
42
- AXPWidgetStatus["Processing"] = "processing";
43
41
  // Error handling
44
42
  AXPWidgetStatus["Error"] = "error";
45
43
  })(AXPWidgetStatus || (AXPWidgetStatus = {}));
@@ -59,14 +57,37 @@ class AXPWidgetCoreService {
59
57
  this.onRefresh = new Subject();
60
58
  this.widgets = new Map();
61
59
  this.onWidgetRegistered = new Subject();
62
- this.status$ = signal(AXPPageStatus.Rendering, ...(ngDevMode ? [{ debugName: "status$" }] : /* istanbul ignore next */ []));
60
+ /** Bumps when widget render/hydration state changes so aggregate computeds re-evaluate. */
61
+ this.widgetsRevision = signal(0, ...(ngDevMode ? [{ debugName: "widgetsRevision" }] : /* istanbul ignore next */ []));
62
+ this.status$ = signal(AXPPageStatus.Rendered, ...(ngDevMode ? [{ debugName: "status$" }] : /* istanbul ignore next */ []));
63
63
  this.status = this.status$.asReadonly();
64
- this.isBusy = computed(() => {
65
- return [AXPPageStatus.Processing, AXPPageStatus.Submitting, AXPPageStatus.Rendering].includes(this.status());
66
- }, ...(ngDevMode ? [{ debugName: "isBusy" }] : /* istanbul ignore next */ []));
67
64
  this.registeredWidgetsCount = signal(0, ...(ngDevMode ? [{ debugName: "registeredWidgetsCount" }] : /* istanbul ignore next */ []));
65
+ this.isRendering = computed(() => {
66
+ this.widgetsRevision();
67
+ return Array.from(this.widgets.values()).some((widget) => widget.status() === AXPWidgetStatus.Rendering);
68
+ }, ...(ngDevMode ? [{ debugName: "isRendering" }] : /* istanbul ignore next */ []));
69
+ this.isHydrating = computed(() => {
70
+ this.widgetsRevision();
71
+ return Array.from(this.widgets.values()).some((widget) => widget.isHydrating());
72
+ }, ...(ngDevMode ? [{ debugName: "isHydrating" }] : /* istanbul ignore next */ []));
73
+ this.isBusy = computed(() => this.isRendering() || this.isHydrating(), ...(ngDevMode ? [{ debugName: "isBusy" }] : /* istanbul ignore next */ []));
74
+ this.isStructureReady = computed(() => {
75
+ this.widgetsRevision();
76
+ const widgets = Array.from(this.widgets.values());
77
+ if (!widgets.length) {
78
+ return false;
79
+ }
80
+ return widgets.every((widget) => widget.status() === AXPWidgetStatus.Rendered);
81
+ }, ...(ngDevMode ? [{ debugName: "isStructureReady" }] : /* istanbul ignore next */ []));
68
82
  this.dirtyWidgetsRevision = signal(0, ...(ngDevMode ? [{ debugName: "dirtyWidgetsRevision" }] : /* istanbul ignore next */ []));
69
83
  this.dirtyWidgetsRevisionSignal = this.dirtyWidgetsRevision.asReadonly();
84
+ effect(() => {
85
+ this.widgetsRevision();
86
+ this.registeredWidgetsCount();
87
+ untracked(() => {
88
+ this.status$.set(this.detectAggregateStatus());
89
+ });
90
+ });
70
91
  }
71
92
  get variables() {
72
93
  return this.variables$();
@@ -74,24 +95,49 @@ class AXPWidgetCoreService {
74
95
  get functions() {
75
96
  return this.functions$();
76
97
  }
77
- updateStatus() {
78
- this.status$.update(() => this.detectStatus());
98
+ /** Recomputes aggregate container state after a widget registers or changes status/hydration. */
99
+ notifyWidgetStateChanged() {
79
100
  this.registeredWidgetsCount.set(this.widgets.size);
101
+ this.widgetsRevision.update((value) => value + 1);
80
102
  }
81
- detectStatus() {
82
- const statuses = Array.from(this.widgets.values()).map((c) => c.status());
83
- if (statuses.some((status) => status === AXPWidgetStatus.Rendering)) {
84
- return AXPPageStatus.Rendering;
85
- }
86
- if (statuses.every((status) => status === AXPWidgetStatus.Rendered)) {
103
+ /** @deprecated Prefer {@link notifyWidgetStateChanged}. */
104
+ updateStatus() {
105
+ this.notifyWidgetStateChanged();
106
+ }
107
+ /** Debug snapshot of registered widget render/hydration state. */
108
+ getWidgetStateSnapshot() {
109
+ const widgets = Array.from(this.widgets.entries()).map(([id, widget]) => ({
110
+ id,
111
+ status: widget.status(),
112
+ isHydrating: widget.isHydrating(),
113
+ }));
114
+ return {
115
+ count: widgets.length,
116
+ isRendering: this.isRendering(),
117
+ isHydrating: this.isHydrating(),
118
+ isStructureReady: this.isStructureReady(),
119
+ isBusy: this.isBusy(),
120
+ widgets,
121
+ };
122
+ }
123
+ detectAggregateStatus() {
124
+ const widgets = Array.from(this.widgets.values());
125
+ if (!widgets.length) {
87
126
  return AXPPageStatus.Rendered;
88
127
  }
89
- if (statuses.some((status) => status === AXPWidgetStatus.Processing)) {
90
- return AXPPageStatus.Processing;
128
+ const statuses = widgets.map((widget) => widget.status());
129
+ if (statuses.some((status) => status === AXPWidgetStatus.Rendering)) {
130
+ return AXPPageStatus.Rendering;
91
131
  }
92
132
  if (statuses.some((status) => status === AXPWidgetStatus.Error)) {
93
133
  return AXPPageStatus.Error;
94
134
  }
135
+ if (widgets.some((widget) => widget.isHydrating())) {
136
+ return AXPPageStatus.Rendering;
137
+ }
138
+ if (statuses.every((status) => status === AXPWidgetStatus.Rendered)) {
139
+ return AXPPageStatus.Rendered;
140
+ }
95
141
  return AXPPageStatus.Rendered;
96
142
  }
97
143
  refresh() {
@@ -123,6 +169,19 @@ class AXPWidgetCoreService {
123
169
  registerWidget(id, widget) {
124
170
  this.widgets.set(id, widget);
125
171
  this.onWidgetRegistered.next({ id, widget });
172
+ this.notifyWidgetStateChanged();
173
+ }
174
+ unregisterWidget(id) {
175
+ if (this.widgets.delete(id)) {
176
+ this.notifyWidgetStateChanged();
177
+ }
178
+ }
179
+ clearWidgets() {
180
+ if (this.widgets.size === 0) {
181
+ return;
182
+ }
183
+ this.widgets.clear();
184
+ this.notifyWidgetStateChanged();
126
185
  }
127
186
  getWidget(id) {
128
187
  return this.widgets.get(id);
@@ -168,109 +227,7 @@ class AXPWidgetCoreService {
168
227
  }
169
228
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetCoreService, decorators: [{
170
229
  type: Injectable
171
- }] });
172
-
173
- //
174
- // @deprecated
175
- // Prefer AXPWidgetsList from @acorex/modules/common; dashboard widget type ids live in dashboard-management RootConfig.widgets.
176
- const AXPWidgetsCatalog = {
177
- timeDuration: 'time-duration',
178
- timeDurationFilter: 'time-duration-filter',
179
- checkbox: 'checkbox-editor',
180
- color: 'color-editor',
181
- connectedLists: 'connected-lists-editor',
182
- list: 'list-editor',
183
- contact: 'contact-editor',
184
- dateTime: 'date-time-editor',
185
- largeText: 'large-text-editor',
186
- number: 'number-editor',
187
- numberUnit: 'number-unit-editor',
188
- password: 'password-editor',
189
- richText: 'rich-text-editor',
190
- select: 'select-editor',
191
- selectionList: 'selection-list-editor',
192
- text: 'text-editor',
193
- table: 'table-editor',
194
- toggle: 'toggle-editor',
195
- blockLayout: 'block-layout',
196
- pageLayout: 'page-layout',
197
- repeaterLayout: 'repeater-layout',
198
- textBlockLayout: 'text-block-layout',
199
- alertBoxLayout: 'alert-box-layout',
200
- badgeLayout: 'badge-layout',
201
- attachments: 'attachments',
202
- fileTypeExtension: 'file-type-extension',
203
- map: 'map',
204
- imageMarker: 'image-marker',
205
- image: 'image',
206
- gallery: 'gallery',
207
- signature: 'signature',
208
- buttonAction: 'button-action',
209
- document: 'document-layout',
210
- lookup: 'lookup-editor',
211
- formField: 'form-field',
212
- qrcode: 'qrcode',
213
- schedulerPicker: 'scheduler-picker',
214
- advancedGrid: 'advanced-grid-layout',
215
- advancedGridItem: 'advanced-grid-item-layout',
216
- grid: 'grid-layout',
217
- gridItem: 'grid-item-layout',
218
- template: 'template',
219
- templateDesigner: 'template-designer',
220
- cronJob: 'cron-job',
221
- spacing: 'spacing',
222
- direction: 'direction',
223
- border: 'border',
224
- flexLayout: 'flex-layout',
225
- flexItem: 'flex-item-layout',
226
- tableLayout: 'table-layout',
227
- tableItem: 'table-item-layout',
228
- avatar: 'avatar',
229
- themeModeChooser: 'theme-mode-chooser',
230
- menuOrientationChooser: 'menu-orientation-chooser',
231
- fontStyleChooser: 'font-style-chooser',
232
- fontSizeChooser: 'font-size-chooser',
233
- iconChooser: 'icon-chooser',
234
- icon: 'icon',
235
- themeColorChooser: 'theme-color-chooser',
236
- gridOptions: 'grid-options',
237
- gridItemOptions: 'grid-item-options',
238
- advancedGridOptions: 'advanced-grid-options',
239
- stringFilter: 'string-filter',
240
- numberFilter: 'number-filter',
241
- dateTimeFilter: 'datetime-filter',
242
- booleanFilter: 'boolean-filter',
243
- lookupFilter: 'lookup-filter',
244
- flexOptions: 'flex-options',
245
- flexItemOptions: 'flex-item-options',
246
- selectFilter: 'select-filter',
247
- requiredValidation: 'required-validation',
248
- regularExpressionValidation: 'regular-expression-validation',
249
- minLengthValidation: 'min-length-validation',
250
- maxLengthValidation: 'max-length-validation',
251
- lessThanValidation: 'less-than-validation',
252
- greaterThanValidation: 'greater-than-validation',
253
- betweenValidation: 'between-validation',
254
- equalValidation: 'equal-validation',
255
- callbackValidation: 'callback-validation',
256
- metaData: 'meta-data-editor',
257
- templateEditor: 'template-box-editor',
258
- templateContentEditor: 'template-content-editor',
259
- panel: 'panel',
260
- notification: 'notification',
261
- comment: 'comment',
262
- dataList: 'data-list',
263
- listToolbar: 'list-toolbar',
264
- entityList: 'entity-list',
265
- pageComponent: 'page-component',
266
- editorJs: 'editor-js-editor',
267
- documentUploader: 'document-uploader',
268
- stepWizard: 'step-wizard',
269
- progressBar: 'progress-bar-editor',
270
- rate: 'rate-picker-editor',
271
- documentFileTypeFilter: 'document-file-type-filter',
272
- entityDefinitionProvider: 'entity-definition-provider-editor',
273
- };
230
+ }], ctorParameters: () => [] });
274
231
 
275
232
  function cloneProperty(property, values) {
276
233
  return merge(cloneDeep(property), values);
@@ -364,6 +321,18 @@ class AXPBaseWidgetComponent extends AXPWidgetCoreElement {
364
321
  this.host = inject(ElementRef).nativeElement;
365
322
  this.layoutService = inject(AXPWidgetCoreService);
366
323
  this.contextService = inject(AXPContextStore);
324
+ /** Unregisters this widget from the container registry when the instance is destroyed. */
325
+ this.#unregisterFromLayoutOnDestroy = inject(DestroyRef).onDestroy(() => {
326
+ const registrationId = this._id ?? this.name;
327
+ if (registrationId) {
328
+ this.layoutService.unregisterWidget(registrationId);
329
+ }
330
+ if (this.hydrationDepth > 0) {
331
+ this.hydrationDepth = 0;
332
+ this._isHydrating.set(false);
333
+ this.layoutService.notifyWidgetStateChanged();
334
+ }
335
+ });
367
336
  this.config = this.token.config;
368
337
  this.node = this.token.node;
369
338
  this.name = this.token.node.name;
@@ -374,13 +343,19 @@ class AXPBaseWidgetComponent extends AXPWidgetCoreElement {
374
343
  this._status = signal(AXPWidgetStatus.Rendering, ...(ngDevMode ? [{ debugName: "_status" }] : /* istanbul ignore next */ []));
375
344
  this.status = this._status.asReadonly();
376
345
  this.onStatusChanged = new BehaviorSubject(this._status());
346
+ /** Depth counter so nested async hydration calls share one busy state. */
347
+ this.hydrationDepth = 0;
348
+ this._isHydrating = signal(false, ...(ngDevMode ? [{ debugName: "_isHydrating" }] : /* istanbul ignore next */ []));
349
+ this.isHydrating = this._isHydrating.asReadonly();
377
350
  this.#statusEffect = effect(() => {
378
351
  this.onStatusChanged.next(this.status());
379
352
  }, ...(ngDevMode ? [{ debugName: "#statusEffect" }] : /* istanbul ignore next */ []));
380
- this.isBusy = computed(() => [AXPWidgetStatus.Rendering, AXPWidgetStatus.Processing].includes(this.status()), ...(ngDevMode ? [{ debugName: "isBusy" }] : /* istanbul ignore next */ []));
353
+ this.isBusy = computed(() => this.status() === AXPWidgetStatus.Rendering || this.isHydrating(), ...(ngDevMode ? [{ debugName: "isBusy" }] : /* istanbul ignore next */ []));
381
354
  this._children = signal(this.token.node.children ?? [], ...(ngDevMode ? [{ debugName: "_children" }] : /* istanbul ignore next */ []));
382
355
  this.children = this._children.asReadonly();
383
356
  }
357
+ /** Unregisters this widget from the container registry when the instance is destroyed. */
358
+ #unregisterFromLayoutOnDestroy;
384
359
  get id() {
385
360
  return this._id;
386
361
  }
@@ -396,7 +371,26 @@ class AXPBaseWidgetComponent extends AXPWidgetCoreElement {
396
371
  }
397
372
  setStatus(status) {
398
373
  this._status.set(status);
399
- this.layoutService.updateStatus();
374
+ this.layoutService.notifyWidgetStateChanged();
375
+ }
376
+ /** Marks async data loading (lookup, select, file fetch, etc.). */
377
+ beginHydration() {
378
+ this.hydrationDepth += 1;
379
+ if (this.hydrationDepth === 1) {
380
+ this._isHydrating.set(true);
381
+ this.layoutService.notifyWidgetStateChanged();
382
+ }
383
+ }
384
+ /** Clears hydration started by {@link beginHydration}. */
385
+ endHydration() {
386
+ if (this.hydrationDepth <= 0) {
387
+ return;
388
+ }
389
+ this.hydrationDepth -= 1;
390
+ if (this.hydrationDepth === 0) {
391
+ this._isHydrating.set(false);
392
+ this.layoutService.notifyWidgetStateChanged();
393
+ }
400
394
  }
401
395
  setOptions(values) {
402
396
  const oldValue = this.options();
@@ -478,7 +472,7 @@ class AXPValueWidgetComponent extends AXPLayoutBaseWidgetComponent {
478
472
  this.detectFullPath();
479
473
  // Set defaultValue if it exists and the path doesn't exist in context
480
474
  if (!isNil(this.defaultValue) && this.fullPath() && !this.contextService.hasValue(this.fullPath())) {
481
- this.setValue(this.defaultValue);
475
+ this.applyDefaultValue(this.defaultValue);
482
476
  }
483
477
  }
484
478
  //
@@ -491,6 +485,22 @@ class AXPValueWidgetComponent extends AXPLayoutBaseWidgetComponent {
491
485
  }
492
486
  return rawValue;
493
487
  }
488
+ /** Applies schema default without marking the form as user-edited. */
489
+ applyDefaultValue(value) {
490
+ if (this.node.valueTransforms?.setter) {
491
+ value = this.node.valueTransforms?.setter(value);
492
+ }
493
+ const path = this.fullPath();
494
+ if (!path) {
495
+ return;
496
+ }
497
+ value = isUndefined(value) ? null : value;
498
+ if (isNil(value)) {
499
+ return;
500
+ }
501
+ this.contextService.update(path, value, { origin: 'system' });
502
+ this.onValueChanged.next({ sender: this });
503
+ }
494
504
  setValue(value) {
495
505
  if (this.node.valueTransforms?.setter) {
496
506
  value = this.node.valueTransforms?.setter(value);
@@ -505,10 +515,18 @@ class AXPValueWidgetComponent extends AXPLayoutBaseWidgetComponent {
505
515
  return;
506
516
  }
507
517
  const savedAtPath = this.contextService.getSavedValue(path);
518
+ const widgetName = this.name ?? this._id ?? 'value-widget';
508
519
  if (this.contextService.isSavedCommitted() &&
509
520
  isSelectionValueEqual(value, savedAtPath) &&
510
- !isEqual(oldValue, savedAtPath)) {
511
- this.contextService.update(path, cloneDeep(savedAtPath));
521
+ !isFormValueEqual(value, savedAtPath)) {
522
+ AXPDebugService.instance?.trace(FORM_DIRTY_TRACE_NS, widgetName, 'setValue restore saved shape', () => ({
523
+ path,
524
+ widget: widgetName,
525
+ incomingValue: value,
526
+ savedAtPath,
527
+ oldValue,
528
+ }));
529
+ this.contextService.update(path, cloneDeep(savedAtPath), { origin: 'system' });
512
530
  this.onValueChanged.next({ sender: this });
513
531
  return;
514
532
  }
@@ -519,9 +537,32 @@ class AXPValueWidgetComponent extends AXPLayoutBaseWidgetComponent {
519
537
  oldValue.some((v, i) => v !== value[i]);
520
538
  const isArrayLengthChange = Array.isArray(value) && (!Array.isArray(oldValue) || value.length !== oldValue.length);
521
539
  if (!isArrayReorder && !isArrayLengthChange && isFormValueEqual(oldValue, value)) {
540
+ AXPDebugService.instance?.trace(FORM_DIRTY_TRACE_NS, widgetName, 'setValue skipped (isFormValueEqual)', () => ({
541
+ path,
542
+ widget: widgetName,
543
+ value,
544
+ oldValue,
545
+ savedAtPath,
546
+ selectionEqualToSaved: this.contextService.isSavedCommitted()
547
+ ? isSelectionValueEqual(value, savedAtPath)
548
+ : null,
549
+ }));
522
550
  return;
523
551
  }
524
- this.contextService.update(path, value);
552
+ AXPDebugService.instance?.trace(FORM_DIRTY_TRACE_NS, widgetName, 'setValue user update', () => ({
553
+ path,
554
+ widget: widgetName,
555
+ value,
556
+ oldValue,
557
+ savedAtPath,
558
+ selectionEqualToSaved: this.contextService.isSavedCommitted()
559
+ ? isSelectionValueEqual(value, savedAtPath)
560
+ : null,
561
+ formValueEqualToSaved: this.contextService.isSavedCommitted()
562
+ ? isFormValueEqual(value, savedAtPath)
563
+ : null,
564
+ }));
565
+ this.contextService.update(path, value, { origin: 'user' });
525
566
  this.onValueChanged.next({ sender: this });
526
567
  }
527
568
  detectFullPath() {
@@ -599,77 +640,89 @@ class AXPDataListWidgetComponent extends AXPValueWidgetComponent {
599
640
  // Reset string reference when dataSource changes to non-string
600
641
  this.lastStringDataSourceRef = null;
601
642
  }
602
- // Process dataSource creation in untracked context
603
- await untracked(async () => {
604
- if (rawValue instanceof AXDataSource) {
605
- // For AXDataSource instances, check if config changed
606
- if (!isEqual(rawValue.config, this.dataSource().config)) {
607
- this.dataSource.set(rawValue);
643
+ this.beginHydration();
644
+ try {
645
+ // Process dataSource creation in untracked context
646
+ await untracked(async () => {
647
+ if (rawValue instanceof AXDataSource) {
648
+ // For AXDataSource instances, check if config changed
649
+ if (!isEqual(rawValue.config, this.dataSource().config)) {
650
+ this.dataSource.set(rawValue);
651
+ }
608
652
  }
609
- }
610
- // static array datasource - always recreate when options change
611
- else if (Array.isArray(rawValue)) {
612
- const ds = new AXDataSource({
613
- key: this.valueField(),
614
- pageSize: 10,
615
- load: async (e) => {
616
- const raw = this.options()['dataSource'];
617
- const skip = e.skip ?? 0;
618
- const take = e.take ?? raw.length;
619
- return {
620
- items: raw.slice(skip, skip + take),
621
- total: raw.length,
622
- };
623
- },
624
- byKey: (key) => {
625
- const raw = this.options()['dataSource'];
626
- const item = raw.filter((c) => c[this.valueField()] == key);
627
- return Promise.resolve(item[0]);
628
- },
629
- });
630
- this.dataSource.set(ds);
631
- }
632
- // resolve data source by name (string or object with id)
633
- else if (rawValue && (typeof rawValue == 'string' || typeof rawValue == 'object')) {
634
- const id = typeof rawValue == 'object' ? rawValue['id'] : rawValue;
635
- const c = await this.dataService.get(id);
636
- if (this.mode == 'designer' && c?.samples?.length) {
637
- this.dataSource.set(convertArrayToDataSource(c.samples, {
653
+ // static array datasource - always recreate when options change
654
+ else if (Array.isArray(rawValue)) {
655
+ const ds = new AXDataSource({
638
656
  key: this.valueField(),
639
- pageSize: 500,
640
- }));
657
+ pageSize: 10,
658
+ load: async (e) => {
659
+ const raw = this.options()['dataSource'];
660
+ const skip = e.skip ?? 0;
661
+ const take = e.take ?? raw.length;
662
+ return {
663
+ items: raw.slice(skip, skip + take),
664
+ total: raw.length,
665
+ };
666
+ },
667
+ byKey: (key) => {
668
+ const raw = this.options()['dataSource'];
669
+ const item = raw.filter((c) => c[this.valueField()] == key);
670
+ return Promise.resolve(item[0]);
671
+ },
672
+ });
673
+ this.dataSource.set(ds);
641
674
  }
642
- else {
643
- const ds = c?.source();
644
- if (ds && ds instanceof Promise) {
645
- const d = await ds;
646
- this.dataSource.set(d);
675
+ // resolve data source by name (string or object with id)
676
+ else if (rawValue && (typeof rawValue == 'string' || typeof rawValue == 'object')) {
677
+ const id = typeof rawValue == 'object' ? rawValue['id'] : rawValue;
678
+ const c = await this.dataService.get(id);
679
+ if (this.mode == 'designer' && c?.samples?.length) {
680
+ this.dataSource.set(convertArrayToDataSource(c.samples, {
681
+ key: this.valueField(),
682
+ pageSize: 500,
683
+ }));
647
684
  }
648
- else if (ds) {
649
- this.dataSource.set(ds);
650
- }
651
- // empty datasource
652
685
  else {
653
- this.dataSource.set(convertArrayToDataSource([]));
686
+ const ds = c?.source();
687
+ if (ds && ds instanceof Promise) {
688
+ const d = await ds;
689
+ this.dataSource.set(d);
690
+ }
691
+ else if (ds) {
692
+ this.dataSource.set(ds);
693
+ }
694
+ // empty datasource
695
+ else {
696
+ this.dataSource.set(convertArrayToDataSource([]));
697
+ }
654
698
  }
655
699
  }
700
+ // empty datasource
701
+ else {
702
+ this.dataSource.set(convertArrayToDataSource([]));
703
+ }
704
+ });
705
+ }
706
+ finally {
707
+ this.endHydration();
708
+ }
709
+ }, ...(ngDevMode ? [{ debugName: "rf" }] : /* istanbul ignore next */ []));
710
+ this.effect2 = effect(async () => {
711
+ this.beginHydration();
712
+ try {
713
+ const value = this.getValue();
714
+ const items = [];
715
+ if (Array.isArray(value)) {
716
+ items.push(...(await Promise.all(value.map((item) => this.extractItem(item)))));
656
717
  }
657
- // empty datasource
658
718
  else {
659
- this.dataSource.set(convertArrayToDataSource([]));
719
+ items.push(await this.extractItem(value));
660
720
  }
661
- });
662
- }, ...(ngDevMode ? [{ debugName: "rf" }] : /* istanbul ignore next */ []));
663
- this.effect2 = effect(async () => {
664
- const value = this.getValue();
665
- const items = [];
666
- if (Array.isArray(value)) {
667
- items.push(...(await Promise.all(value.map((item) => this.extractItem(item)))));
721
+ this.selectedItems.set(items.filter((c) => c != null));
668
722
  }
669
- else {
670
- items.push(await this.extractItem(value));
723
+ finally {
724
+ this.endHydration();
671
725
  }
672
- this.selectedItems.set(items.filter((c) => c != null));
673
726
  }, ...(ngDevMode ? [{ debugName: "effect2" }] : /* istanbul ignore next */ []));
674
727
  }
675
728
  //#endregion
@@ -695,6 +748,14 @@ class AXPDataListWidgetComponent extends AXPValueWidgetComponent {
695
748
  [this.textField()]: item,
696
749
  };
697
750
  }
751
+ /**
752
+ * Value used to resolve `selectedItems`. Defaults to the stored value, but
753
+ * widgets that wrap their value (e.g. filter mode) can override this to expose
754
+ * the unwrapped scalar/array so item resolution receives a real key.
755
+ */
756
+ resolveSelectedItemsValue() {
757
+ return this.getValue();
758
+ }
698
759
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDataListWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
699
760
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDataListWidgetComponent }); }
700
761
  }
@@ -1496,12 +1557,18 @@ class AXPWidgetColumnRendererComponent extends AXDataTableColumnComponent {
1496
1557
  ], 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: `
1497
1558
  <ng-template #header>
1498
1559
  <div
1499
- class="axp-widget-column-header ax-w-full ax-flex ax-items-center ax-gap-1 ax-select-none"
1560
+ class="axp-widget-column-header ax-w-full ax-flex ax-items-center ax-select-none"
1500
1561
  [class.axp-widget-column-header--sortable]="sortEnabled"
1501
- [style.justify-content]="getHeaderJustifyContent()"
1562
+ [class.axp-widget-column-header--sort-active]="headerSortDirection === 'asc' || headerSortDirection === 'desc'"
1563
+ [class.ax-gap-1]="!sortEnabled"
1564
+ [style.justify-content]="sortEnabled ? 'flex-start' : getHeaderJustifyContent()"
1502
1565
  (click)="onHeaderClick($event)"
1503
1566
  >
1504
- <span class="axp-widget-column-header__caption ax-truncate">{{ caption | translate | async }}</span>
1567
+ <span
1568
+ class="axp-widget-column-header__caption ax-truncate"
1569
+ [class.axp-widget-column-header__caption--with-sort]="sortEnabled"
1570
+ [style.text-align]="sortEnabled ? getHeaderTextAlign() : null"
1571
+ >{{ caption | translate | async }}</span>
1505
1572
  @if (sortEnabled) {
1506
1573
  <span class="axp-widget-column-header__sort ax-flex ax-items-center ax-gap-0.5 ax-shrink-0">
1507
1574
  @if (headerSortPriority !== undefined && headerSortPriority > 1) {
@@ -1554,19 +1621,25 @@ class AXPWidgetColumnRendererComponent extends AXDataTableColumnComponent {
1554
1621
  </div>
1555
1622
  </ng-template>
1556
1623
  <ng-template #footer></ng-template>
1557
- `, isInline: true, styles: [".axp-widget-column-header--sortable{cursor:pointer}.axp-widget-column-header__caption{min-width: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{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{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 }); }
1624
+ `, 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 }); }
1558
1625
  }
1559
1626
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetColumnRendererComponent, decorators: [{
1560
1627
  type: Component,
1561
1628
  args: [{ selector: 'axp-widget-column-renderer', template: `
1562
1629
  <ng-template #header>
1563
1630
  <div
1564
- class="axp-widget-column-header ax-w-full ax-flex ax-items-center ax-gap-1 ax-select-none"
1631
+ class="axp-widget-column-header ax-w-full ax-flex ax-items-center ax-select-none"
1565
1632
  [class.axp-widget-column-header--sortable]="sortEnabled"
1566
- [style.justify-content]="getHeaderJustifyContent()"
1633
+ [class.axp-widget-column-header--sort-active]="headerSortDirection === 'asc' || headerSortDirection === 'desc'"
1634
+ [class.ax-gap-1]="!sortEnabled"
1635
+ [style.justify-content]="sortEnabled ? 'flex-start' : getHeaderJustifyContent()"
1567
1636
  (click)="onHeaderClick($event)"
1568
1637
  >
1569
- <span class="axp-widget-column-header__caption ax-truncate">{{ caption | translate | async }}</span>
1638
+ <span
1639
+ class="axp-widget-column-header__caption ax-truncate"
1640
+ [class.axp-widget-column-header__caption--with-sort]="sortEnabled"
1641
+ [style.text-align]="sortEnabled ? getHeaderTextAlign() : null"
1642
+ >{{ caption | translate | async }}</span>
1570
1643
  @if (sortEnabled) {
1571
1644
  <span class="axp-widget-column-header__sort ax-flex ax-items-center ax-gap-0.5 ax-shrink-0">
1572
1645
  @if (headerSortPriority !== undefined && headerSortPriority > 1) {
@@ -1622,7 +1695,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
1622
1695
  `, providers: [
1623
1696
  AXPWidgetCoreService,
1624
1697
  { provide: AXDataTableColumnComponent, useExisting: AXPWidgetColumnRendererComponent },
1625
- ], 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__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{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{opacity:1}\n"] }]
1698
+ ], 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"] }]
1626
1699
  }], propDecorators: { customExpandIcon: [{
1627
1700
  type: Input
1628
1701
  }], customCollapseIcon: [{
@@ -1661,8 +1734,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
1661
1734
  args: ['header']
1662
1735
  }] } });
1663
1736
 
1664
- /** Idle period after widgets register / hydrate before committing the saved baseline. */
1737
+ /** Continuous idle period required after widgets settle before committing baseline. */
1665
1738
  const FORM_SAVED_BASELINE_IDLE_MS = 500;
1739
+ /** Poll interval while waiting for widgets to settle (~20s max). */
1740
+ const FORM_SAVED_BASELINE_POLL_MS = 250;
1741
+ /** Max poll attempts for settle / ready (~20s); applies even when widgets are registered. */
1742
+ const FORM_SAVED_BASELINE_MAX_SETTLE_POLLS = 80;
1666
1743
  class AXPWidgetContainerComponent {
1667
1744
  set context(value) {
1668
1745
  this.syncExternalContext(value);
@@ -1673,7 +1750,9 @@ class AXPWidgetContainerComponent {
1673
1750
  constructor() {
1674
1751
  this.contextService = inject(AXPContextStore);
1675
1752
  this.builderService = inject(AXPWidgetCoreService);
1753
+ this.debug = inject(AXPDebugService);
1676
1754
  this.onContextChanged = new EventEmitter();
1755
+ this.wasHydrating = false;
1677
1756
  this.isSavedCommitted = computed(() => this.contextService.isSavedCommitted(), ...(ngDevMode ? [{ debugName: "isSavedCommitted" }] : /* istanbul ignore next */ []));
1678
1757
  this.isFormDirty = computed(() => {
1679
1758
  if (!this.contextService.isSavedCommitted()) {
@@ -1684,6 +1763,15 @@ class AXPWidgetContainerComponent {
1684
1763
  }, ...(ngDevMode ? [{ debugName: "isFormDirty" }] : /* istanbul ignore next */ []));
1685
1764
  this.status = computed(() => this.builderService.status(), ...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
1686
1765
  this.isBusy = computed(() => this.builderService.isBusy(), ...(ngDevMode ? [{ debugName: "isBusy" }] : /* istanbul ignore next */ []));
1766
+ this.isHydrating = computed(() => this.builderService.isHydrating(), ...(ngDevMode ? [{ debugName: "isHydrating" }] : /* istanbul ignore next */ []));
1767
+ /** True when registered widgets exist and none are rendering or hydrating. */
1768
+ this.isWidgetsReady = computed(() => {
1769
+ const count = this.builderService.registeredWidgetsCount();
1770
+ if (count === 0) {
1771
+ return false;
1772
+ }
1773
+ return this.builderService.isStructureReady() && !this.builderService.isBusy();
1774
+ }, ...(ngDevMode ? [{ debugName: "isWidgetsReady" }] : /* istanbul ignore next */ []));
1687
1775
  effect(() => {
1688
1776
  const isDirty = this.isFormDirty();
1689
1777
  const data = this.contextService.data();
@@ -1692,6 +1780,16 @@ class AXPWidgetContainerComponent {
1692
1780
  return;
1693
1781
  }
1694
1782
  this.lastEmittedFormDirty = isDirty;
1783
+ this.debug.trace(FORM_DIRTY_TRACE_NS, 'widget-container', 'isFormDirty changed', () => ({
1784
+ isFormDirty: isDirty,
1785
+ userEditedSinceCommit: this.contextService.hasUserEditedSinceCommit(),
1786
+ isSavedCommitted: this.contextService.isSavedCommitted(),
1787
+ changePath: changeEvent.path,
1788
+ changeOrigin: changeEvent.origin,
1789
+ dirtyPaths: this.contextService.isSavedCommitted()
1790
+ ? summarizeFormContextDirtyDiff(this.contextService.data(), this.contextService.saved())
1791
+ : [],
1792
+ }));
1695
1793
  this.onContextChanged.emit({
1696
1794
  ...changeEvent,
1697
1795
  data: cloneDeep(data),
@@ -1702,11 +1800,26 @@ class AXPWidgetContainerComponent {
1702
1800
  if (this.contextService.isSavedCommitted()) {
1703
1801
  return;
1704
1802
  }
1803
+ this.builderService.registeredWidgetsCount();
1804
+ this.builderService.isHydrating();
1805
+ this.builderService.isStructureReady();
1705
1806
  if (this.builderService.registeredWidgetsCount() === 0) {
1706
1807
  return;
1707
1808
  }
1708
1809
  untracked(() => void this.settleSavedBaseline());
1709
1810
  });
1811
+ effect(() => {
1812
+ const hydrating = this.builderService.isHydrating();
1813
+ const savedCommitted = this.contextService.isSavedCommitted();
1814
+ if (!savedCommitted) {
1815
+ this.wasHydrating = hydrating;
1816
+ return;
1817
+ }
1818
+ if (this.wasHydrating && !hydrating) {
1819
+ untracked(() => this.scheduleRebaselineAfterHydration());
1820
+ }
1821
+ this.wasHydrating = hydrating;
1822
+ });
1710
1823
  }
1711
1824
  refresh() {
1712
1825
  this.builderService.refresh();
@@ -1724,6 +1837,7 @@ class AXPWidgetContainerComponent {
1724
1837
  return;
1725
1838
  }
1726
1839
  if (this.contextService.isSavedCommitted()) {
1840
+ this.builderService.clearWidgets();
1727
1841
  this.contextService.set(next);
1728
1842
  void this.settleSavedBaseline();
1729
1843
  return;
@@ -1731,28 +1845,77 @@ class AXPWidgetContainerComponent {
1731
1845
  if (this.contextService.isChanged()) {
1732
1846
  return;
1733
1847
  }
1848
+ this.builderService.clearWidgets();
1734
1849
  this.contextService.set(next);
1735
1850
  void this.settleSavedBaseline();
1736
1851
  }
1737
1852
  /** Commits the current data as saved after widget hydration settles. */
1738
1853
  settleSavedBaseline() {
1854
+ if (this.settlePromise) {
1855
+ return this.settlePromise;
1856
+ }
1739
1857
  this.clearSavedBaselineTimer();
1858
+ this.settlePromise = this.waitUntilSettled()
1859
+ .then(() => {
1860
+ this.contextService.commitSaved();
1861
+ this.lastEmittedFormDirty = undefined;
1862
+ })
1863
+ .finally(() => {
1864
+ this.settlePromise = undefined;
1865
+ });
1866
+ return this.settlePromise;
1867
+ }
1868
+ /** Resolves when widgets stay registered, structure-ready, and idle for {@link FORM_SAVED_BASELINE_IDLE_MS}. */
1869
+ waitUntilSettled() {
1740
1870
  return new Promise((resolve) => {
1741
- const commit = () => {
1742
- if (this.builderService.registeredWidgetsCount() === 0) {
1743
- this.savedBaselineTimer = setTimeout(commit, FORM_SAVED_BASELINE_IDLE_MS);
1744
- return;
1871
+ let idleRetries = 0;
1872
+ let stableTimer;
1873
+ let pollTimer;
1874
+ const finish = (reason) => {
1875
+ const snapshot = this.builderService.getWidgetStateSnapshot();
1876
+ this.debug.trace(OVERLAY_WIDGET_SETTLE_TRACE_NS, 'settle', 'Completed', () => ({
1877
+ reason,
1878
+ idleRetries,
1879
+ ...snapshot,
1880
+ }));
1881
+ if (pollTimer) {
1882
+ clearTimeout(pollTimer);
1883
+ }
1884
+ if (stableTimer) {
1885
+ clearTimeout(stableTimer);
1745
1886
  }
1746
- this.contextService.commitSaved();
1747
- this.lastEmittedFormDirty = undefined;
1748
- this.savedBaselineTimer = undefined;
1749
1887
  resolve();
1750
1888
  };
1751
- this.savedBaselineTimer = setTimeout(commit, FORM_SAVED_BASELINE_IDLE_MS);
1889
+ this.debug.trace(OVERLAY_WIDGET_SETTLE_TRACE_NS, 'settle', 'Started');
1890
+ const poll = () => {
1891
+ const snapshot = this.builderService.getWidgetStateSnapshot();
1892
+ const isSettled = snapshot.count > 0 &&
1893
+ snapshot.isStructureReady &&
1894
+ !snapshot.isHydrating &&
1895
+ !snapshot.isRendering;
1896
+ if (isSettled) {
1897
+ if (!stableTimer) {
1898
+ stableTimer = setTimeout(() => finish('stable-idle'), FORM_SAVED_BASELINE_IDLE_MS);
1899
+ }
1900
+ return;
1901
+ }
1902
+ if (stableTimer) {
1903
+ clearTimeout(stableTimer);
1904
+ stableTimer = undefined;
1905
+ }
1906
+ idleRetries += 1;
1907
+ if (idleRetries >= FORM_SAVED_BASELINE_MAX_SETTLE_POLLS) {
1908
+ finish('max-polls');
1909
+ return;
1910
+ }
1911
+ pollTimer = setTimeout(poll, FORM_SAVED_BASELINE_POLL_MS);
1912
+ };
1913
+ pollTimer = setTimeout(poll, FORM_SAVED_BASELINE_POLL_MS);
1752
1914
  });
1753
1915
  }
1754
1916
  async replaceContext(value) {
1755
1917
  this.clearSavedBaselineTimer();
1918
+ this.settlePromise = undefined;
1756
1919
  this.contextService.set(cloneDeep(value));
1757
1920
  // Commit immediately so save/replace is clean even when `set()` no-ops (data already matches).
1758
1921
  this.contextService.commitSaved();
@@ -1761,9 +1924,28 @@ class AXPWidgetContainerComponent {
1761
1924
  }
1762
1925
  revertAndSettle() {
1763
1926
  this.clearSavedBaselineTimer();
1927
+ this.settlePromise = undefined;
1928
+ this.debug.trace(FORM_DIRTY_TRACE_NS, 'widget-container', 'revertAndSettle');
1764
1929
  this.contextService.revertToSaved();
1765
1930
  this.lastEmittedFormDirty = undefined;
1766
1931
  }
1932
+ /** Re-commits baseline after post-load hydration when the user has not edited the form. */
1933
+ scheduleRebaselineAfterHydration() {
1934
+ this.clearSavedBaselineTimer();
1935
+ this.savedBaselineTimer = setTimeout(() => {
1936
+ this.savedBaselineTimer = undefined;
1937
+ if (this.contextService.hasUserEditedSinceCommit()) {
1938
+ return;
1939
+ }
1940
+ void this.waitUntilSettled().then(() => {
1941
+ if (this.contextService.hasUserEditedSinceCommit()) {
1942
+ return;
1943
+ }
1944
+ this.contextService.commitSaved();
1945
+ this.lastEmittedFormDirty = undefined;
1946
+ });
1947
+ }, FORM_SAVED_BASELINE_IDLE_MS);
1948
+ }
1767
1949
  clearSavedBaselineTimer() {
1768
1950
  if (this.savedBaselineTimer) {
1769
1951
  clearTimeout(this.savedBaselineTimer);
@@ -2323,7 +2505,6 @@ class AXPWidgetRendererDirective {
2323
2505
  this.instance.parent = this.parentNode();
2324
2506
  this.instance.index = this.index();
2325
2507
  this.instance.mode = this.mode();
2326
- this.instance.setStatus(AXPWidgetStatus.Rendered);
2327
2508
  // Update the component ref signal for reactive access
2328
2509
  this._componentRefSignal.set(this.componentRef);
2329
2510
  this.instance?.onOptionsChanged?.pipe(this.unsubscriber.takeUntilDestroy).subscribe((c) => {
@@ -2598,7 +2779,7 @@ class AXPWidgetRendererDirective {
2598
2779
  return value;
2599
2780
  },
2600
2781
  set: (path, value) => {
2601
- this.contextService.update(path, value);
2782
+ this.contextService.update(path, value, { origin: 'user' });
2602
2783
  },
2603
2784
  data: () => {
2604
2785
  return this.contextService.data();
@@ -4664,5 +4845,5 @@ const AXP_CALLBACK_VALIDATION_PROPERTY = {
4664
4845
  * Generated bundle index. Do not edit.
4665
4846
  */
4666
4847
 
4667
- export { AXPBaseWidgetComponent, AXPBlockBaseLayoutWidgetComponent, AXPBoxModelLayoutWidgetComponent, AXPColumnWidgetComponent, AXPDataListWidgetComponent, AXPFlexBaseLayoutWidgetComponent, AXPFlexItemBaseLayoutWidgetComponent, AXPGridBaseLayoutWidgetComponent, AXPGridItemBaseLayoutWidgetComponent, AXPInlineBaseLayoutWidgetComponent, AXPLayoutBaseWidgetComponent, AXPPageStatus, AXPPropertyEditorHelper, AXPTableBaseLayoutWidgetComponent, AXPTableItemBaseLayoutWidgetComponent, AXPTableItemOpsBaseLayoutWidgetComponent, AXPValueWidgetComponent, AXPWidgetColumnCellComponent, AXPWidgetColumnRendererComponent, AXPWidgetContainerComponent, AXPWidgetCoreElement, AXPWidgetCoreModule, AXPWidgetCoreService, AXPWidgetGroupEnum, AXPWidgetRegistryService, AXPWidgetRendererDirective, AXPWidgetSerializationHelper, AXPWidgetStatus, AXPWidgetsCatalog, 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_COLUMN_TOKEN, AXP_WIDGET_DEFINITION_PROVIDER, AXP_WIDGET_PROPERTY_GROUP, AXP_WIDGET_TOKEN, booleanDefaultProperty, buildWidgetRegistryMapFromProviders, cloneProperty, createBooleanProperty, createNumberProperty, createSelectProperty, createStringProperty, findNonEmptyBreakpoints, largeTextDefaultProperty, mapAXPPropertyToWidgetProperty, normalizeWidgetCategories, numberDefaultProperty, numberMaxValueProperty, numberMinValueProperty, plainTextDefaultProperty, richTextDefaultProperty, selectEditorDefaultValueProperty, selectionListEditorDefaultValueProperty };
4848
+ export { AXPBaseWidgetComponent, AXPBlockBaseLayoutWidgetComponent, AXPBoxModelLayoutWidgetComponent, AXPColumnWidgetComponent, AXPDataListWidgetComponent, AXPFlexBaseLayoutWidgetComponent, AXPFlexItemBaseLayoutWidgetComponent, AXPGridBaseLayoutWidgetComponent, AXPGridItemBaseLayoutWidgetComponent, AXPInlineBaseLayoutWidgetComponent, AXPLayoutBaseWidgetComponent, AXPPageStatus, AXPPropertyEditorHelper, AXPTableBaseLayoutWidgetComponent, AXPTableItemBaseLayoutWidgetComponent, AXPTableItemOpsBaseLayoutWidgetComponent, AXPValueWidgetComponent, AXPWidgetColumnCellComponent, AXPWidgetColumnRendererComponent, AXPWidgetContainerComponent, AXPWidgetCoreElement, AXPWidgetCoreModule, AXPWidgetCoreService, AXPWidgetGroupEnum, AXPWidgetRegistryService, AXPWidgetRendererDirective, AXPWidgetSerializationHelper, AXPWidgetStatus, 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_COLUMN_TOKEN, AXP_WIDGET_DEFINITION_PROVIDER, AXP_WIDGET_PROPERTY_GROUP, AXP_WIDGET_TOKEN, booleanDefaultProperty, buildWidgetRegistryMapFromProviders, cloneProperty, createBooleanProperty, createNumberProperty, createSelectProperty, createStringProperty, findNonEmptyBreakpoints, largeTextDefaultProperty, mapAXPPropertyToWidgetProperty, normalizeWidgetCategories, numberDefaultProperty, numberMaxValueProperty, numberMinValueProperty, plainTextDefaultProperty, richTextDefaultProperty, selectEditorDefaultValueProperty, selectionListEditorDefaultValueProperty };
4668
4849
  //# sourceMappingURL=acorex-platform-layout-widget-core.mjs.map