@acorex/platform 21.0.0-next.70 → 21.0.0-next.72

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 (55) hide show
  1. package/fesm2022/acorex-platform-auth.mjs +10 -2
  2. package/fesm2022/acorex-platform-auth.mjs.map +1 -1
  3. package/fesm2022/{acorex-platform-common-common-settings.provider-Bi1RYif5.mjs → acorex-platform-common-common-settings.provider-Ytey9uhY.mjs} +15 -1
  4. package/fesm2022/acorex-platform-common-common-settings.provider-Ytey9uhY.mjs.map +1 -0
  5. package/fesm2022/acorex-platform-common.mjs +3798 -1674
  6. package/fesm2022/acorex-platform-common.mjs.map +1 -1
  7. package/fesm2022/acorex-platform-core.mjs +1362 -97
  8. package/fesm2022/acorex-platform-core.mjs.map +1 -1
  9. package/fesm2022/acorex-platform-layout-builder.mjs +446 -44
  10. package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
  11. package/fesm2022/acorex-platform-layout-components.mjs +149 -109
  12. package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
  13. package/fesm2022/acorex-platform-layout-designer.mjs +199 -126
  14. package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
  15. package/fesm2022/{acorex-platform-layout-entity-attachments-page.component-D8iQnT-R.mjs → acorex-platform-layout-entity-attachments-page.component-B0EkdqvH.mjs} +6 -1
  16. package/fesm2022/acorex-platform-layout-entity-attachments-page.component-B0EkdqvH.mjs.map +1 -0
  17. package/fesm2022/acorex-platform-layout-entity.mjs +823 -594
  18. package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
  19. package/fesm2022/acorex-platform-layout-views.mjs +845 -218
  20. package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
  21. package/fesm2022/acorex-platform-layout-widget-core.mjs +122 -33
  22. package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
  23. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-edit-popup.component-BcpRkpJp.mjs → acorex-platform-layout-widgets-tabular-data-edit-popup.component-DjpZU6gz.mjs} +2 -2
  24. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-edit-popup.component-BcpRkpJp.mjs.map → acorex-platform-layout-widgets-tabular-data-edit-popup.component-DjpZU6gz.mjs.map} +1 -1
  25. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-view-popup.component-DQtK4lxl.mjs → acorex-platform-layout-widgets-tabular-data-view-popup.component-gX-3Kx9I.mjs} +2 -2
  26. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-view-popup.component-DQtK4lxl.mjs.map → acorex-platform-layout-widgets-tabular-data-view-popup.component-gX-3Kx9I.mjs.map} +1 -1
  27. package/fesm2022/acorex-platform-layout-widgets.mjs +312 -676
  28. package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
  29. package/fesm2022/acorex-platform-themes-default-error-401.component-B1nsdpTY.mjs +48 -0
  30. package/fesm2022/acorex-platform-themes-default-error-401.component-B1nsdpTY.mjs.map +1 -0
  31. package/fesm2022/acorex-platform-themes-default-error-404.component-D4UvRe8u.mjs +42 -0
  32. package/fesm2022/acorex-platform-themes-default-error-404.component-D4UvRe8u.mjs.map +1 -0
  33. package/fesm2022/acorex-platform-themes-default.mjs +89 -46
  34. package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
  35. package/fesm2022/acorex-platform-themes-shared.mjs +50 -30
  36. package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
  37. package/package.json +1 -1
  38. package/types/acorex-platform-auth.d.ts +2 -0
  39. package/types/acorex-platform-common.d.ts +899 -256
  40. package/types/acorex-platform-core.d.ts +394 -60
  41. package/types/acorex-platform-layout-builder.d.ts +78 -13
  42. package/types/acorex-platform-layout-components.d.ts +30 -24
  43. package/types/acorex-platform-layout-entity.d.ts +93 -44
  44. package/types/acorex-platform-layout-views.d.ts +162 -42
  45. package/types/acorex-platform-layout-widget-core.d.ts +60 -33
  46. package/types/acorex-platform-layout-widgets.d.ts +48 -20
  47. package/types/acorex-platform-themes-default.d.ts +38 -8
  48. package/types/acorex-platform-themes-shared.d.ts +6 -0
  49. package/types/acorex-platform-workflow.d.ts +1 -1
  50. package/fesm2022/acorex-platform-common-common-settings.provider-Bi1RYif5.mjs.map +0 -1
  51. package/fesm2022/acorex-platform-layout-entity-attachments-page.component-D8iQnT-R.mjs.map +0 -1
  52. package/fesm2022/acorex-platform-themes-default-error-401.component-C7EYJzSr.mjs +0 -31
  53. package/fesm2022/acorex-platform-themes-default-error-401.component-C7EYJzSr.mjs.map +0 -1
  54. package/fesm2022/acorex-platform-themes-default-error-404.component-7MVLMwIa.mjs +0 -25
  55. package/fesm2022/acorex-platform-themes-default-error-404.component-7MVLMwIa.mjs.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
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';
3
3
  import { convertArrayToDataSource, AXDataSource, AX_STYLE_COLOR_TYPES, AX_STYLE_LOOK_TYPES } from '@acorex/cdk/common';
4
- import { AXPContextStore, AXPDataSourceDefinitionProviderService, extractValue, AXPExpressionEvaluatorService, getSmart } from '@acorex/platform/core';
4
+ import { AXPContextStore, isSelectionValueEqual, isFormValueEqual, AXPDataSourceDefinitionProviderService, extractValue, AXPExpressionEvaluatorService, getSmart } from '@acorex/platform/core';
5
5
  export { normalizeDefinitionCategories } from '@acorex/platform/core';
6
6
  import { set, merge, cloneDeep, isNil, get, isEqual, isUndefined, isObjectLike, sum, isEmpty, isString } from 'lodash-es';
7
7
  import { Subject, BehaviorSubject, filter } from 'rxjs';
@@ -65,6 +65,8 @@ class AXPWidgetCoreService {
65
65
  return [AXPPageStatus.Processing, AXPPageStatus.Submitting, AXPPageStatus.Rendering].includes(this.status());
66
66
  }, ...(ngDevMode ? [{ debugName: "isBusy" }] : /* istanbul ignore next */ []));
67
67
  this.registeredWidgetsCount = signal(0, ...(ngDevMode ? [{ debugName: "registeredWidgetsCount" }] : /* istanbul ignore next */ []));
68
+ this.dirtyWidgetsRevision = signal(0, ...(ngDevMode ? [{ debugName: "dirtyWidgetsRevision" }] : /* istanbul ignore next */ []));
69
+ this.dirtyWidgetsRevisionSignal = this.dirtyWidgetsRevision.asReadonly();
68
70
  }
69
71
  get variables() {
70
72
  return this.variables$();
@@ -78,22 +80,19 @@ class AXPWidgetCoreService {
78
80
  }
79
81
  detectStatus() {
80
82
  const statuses = Array.from(this.widgets.values()).map((c) => c.status());
81
- // Rendering statuses
82
83
  if (statuses.some((status) => status === AXPWidgetStatus.Rendering)) {
83
84
  return AXPPageStatus.Rendering;
84
85
  }
85
86
  if (statuses.every((status) => status === AXPWidgetStatus.Rendered)) {
86
87
  return AXPPageStatus.Rendered;
87
88
  }
88
- // Processing statuses
89
89
  if (statuses.some((status) => status === AXPWidgetStatus.Processing)) {
90
90
  return AXPPageStatus.Processing;
91
91
  }
92
- // Error handling
93
92
  if (statuses.some((status) => status === AXPWidgetStatus.Error)) {
94
93
  return AXPPageStatus.Error;
95
94
  }
96
- return AXPPageStatus.Rendered; // Default to Loaded when all widgets are in a completed state
95
+ return AXPPageStatus.Rendered;
97
96
  }
98
97
  refresh() {
99
98
  setTimeout(() => {
@@ -128,11 +127,6 @@ class AXPWidgetCoreService {
128
127
  getWidget(id) {
129
128
  return this.widgets.get(id);
130
129
  }
131
- /**
132
- * Waits until a widget with the given id is registered, then resolves with it.
133
- * If the widget is already registered, resolves immediately.
134
- * Optionally accepts a timeout (in ms) after which it resolves with undefined.
135
- */
136
130
  async waitForWidget(id, timeoutMs) {
137
131
  const existing = this.widgets.get(id);
138
132
  if (existing) {
@@ -162,12 +156,12 @@ class AXPWidgetCoreService {
162
156
  }
163
157
  });
164
158
  }
165
- /**
166
- * Returns a list of registered widget ids (names).
167
- */
168
159
  listRegisteredWidgetNames() {
169
160
  return Array.from(this.widgets.keys());
170
161
  }
162
+ notifyWidgetDirtyChanged() {
163
+ this.dirtyWidgetsRevision.update((value) => value + 1);
164
+ }
171
165
  ngOnDestroy() { }
172
166
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetCoreService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
173
167
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetCoreService }); }
@@ -501,23 +495,34 @@ class AXPValueWidgetComponent extends AXPLayoutBaseWidgetComponent {
501
495
  if (this.node.valueTransforms?.setter) {
502
496
  value = this.node.valueTransforms?.setter(value);
503
497
  }
498
+ const path = this.fullPath();
499
+ if (!path) {
500
+ return;
501
+ }
504
502
  const oldValue = this.getValue();
505
503
  value = isUndefined(value) ? null : value;
506
504
  if (isNil(value) && isNil(oldValue)) {
507
505
  return;
508
506
  }
507
+ const savedAtPath = this.contextService.getSavedValue(path);
508
+ if (this.contextService.isSavedCommitted() &&
509
+ isSelectionValueEqual(value, savedAtPath) &&
510
+ !isEqual(oldValue, savedAtPath)) {
511
+ this.contextService.update(path, cloneDeep(savedAtPath));
512
+ this.onValueChanged.next({ sender: this });
513
+ return;
514
+ }
509
515
  // Reordered arrays must persist even when items are deep-equal (e.g. empty row objects).
510
516
  const isArrayReorder = Array.isArray(oldValue) &&
511
517
  Array.isArray(value) &&
512
518
  oldValue.length === value.length &&
513
519
  oldValue.some((v, i) => v !== value[i]);
514
- if (!isArrayReorder && isEqual(oldValue, value)) {
520
+ const isArrayLengthChange = Array.isArray(value) && (!Array.isArray(oldValue) || value.length !== oldValue.length);
521
+ if (!isArrayReorder && !isArrayLengthChange && isFormValueEqual(oldValue, value)) {
515
522
  return;
516
523
  }
517
- if (this.fullPath()) {
518
- this.contextService.update(this.fullPath(), value);
519
- this.onValueChanged.next({ sender: this });
520
- }
524
+ this.contextService.update(path, value);
525
+ this.onValueChanged.next({ sender: this });
521
526
  }
522
527
  detectFullPath() {
523
528
  const sections = [];
@@ -1649,9 +1654,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
1649
1654
  args: ['header']
1650
1655
  }] } });
1651
1656
 
1657
+ /** Idle period after widgets register / hydrate before committing the saved baseline. */
1658
+ const FORM_SAVED_BASELINE_IDLE_MS = 500;
1652
1659
  class AXPWidgetContainerComponent {
1653
1660
  set context(value) {
1654
- this.contextService.set(value);
1661
+ this.syncExternalContext(value);
1655
1662
  }
1656
1663
  set functions(v) {
1657
1664
  this.builderService.setFunctions(v);
@@ -1660,16 +1667,38 @@ class AXPWidgetContainerComponent {
1660
1667
  this.contextService = inject(AXPContextStore);
1661
1668
  this.builderService = inject(AXPWidgetCoreService);
1662
1669
  this.onContextChanged = new EventEmitter();
1663
- this.status = computed(() => {
1664
- return this.builderService.status();
1665
- }, ...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
1666
- this.isBusy = computed(() => {
1667
- return this.builderService.isBusy();
1668
- }, ...(ngDevMode ? [{ debugName: "isBusy" }] : /* istanbul ignore next */ []));
1670
+ this.isSavedCommitted = computed(() => this.contextService.isSavedCommitted(), ...(ngDevMode ? [{ debugName: "isSavedCommitted" }] : /* istanbul ignore next */ []));
1671
+ this.isFormDirty = computed(() => {
1672
+ if (!this.contextService.isSavedCommitted()) {
1673
+ return false;
1674
+ }
1675
+ this.contextService.data();
1676
+ return this.contextService.isDirty();
1677
+ }, ...(ngDevMode ? [{ debugName: "isFormDirty" }] : /* istanbul ignore next */ []));
1678
+ this.status = computed(() => this.builderService.status(), ...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
1679
+ this.isBusy = computed(() => this.builderService.isBusy(), ...(ngDevMode ? [{ debugName: "isBusy" }] : /* istanbul ignore next */ []));
1669
1680
  effect(() => {
1670
- if (this.contextService.isChanged()) {
1671
- this.onContextChanged.emit(this.contextService.changeEvent());
1681
+ const isDirty = this.isFormDirty();
1682
+ const data = this.contextService.data();
1683
+ const changeEvent = this.contextService.changeEvent();
1684
+ if (!this.contextService.isChanged() && this.lastEmittedFormDirty === isDirty) {
1685
+ return;
1672
1686
  }
1687
+ this.lastEmittedFormDirty = isDirty;
1688
+ this.onContextChanged.emit({
1689
+ ...changeEvent,
1690
+ data: cloneDeep(data),
1691
+ isFormDirty: isDirty,
1692
+ });
1693
+ });
1694
+ effect(() => {
1695
+ if (this.contextService.isSavedCommitted()) {
1696
+ return;
1697
+ }
1698
+ if (this.builderService.registeredWidgetsCount() === 0) {
1699
+ return;
1700
+ }
1701
+ untracked(() => void this.settleSavedBaseline());
1673
1702
  });
1674
1703
  }
1675
1704
  refresh() {
@@ -1678,6 +1707,62 @@ class AXPWidgetContainerComponent {
1678
1707
  find(name) {
1679
1708
  return this.builderService.waitForWidget(name);
1680
1709
  }
1710
+ syncExternalContext(value) {
1711
+ const next = cloneDeep(value ?? {});
1712
+ const current = this.contextService.data();
1713
+ if (isEqual(current, next)) {
1714
+ return;
1715
+ }
1716
+ if (this.contextService.isSavedCommitted() && this.isFormDirty()) {
1717
+ return;
1718
+ }
1719
+ if (this.contextService.isSavedCommitted()) {
1720
+ this.contextService.set(next);
1721
+ void this.settleSavedBaseline();
1722
+ return;
1723
+ }
1724
+ if (this.contextService.isChanged()) {
1725
+ return;
1726
+ }
1727
+ this.contextService.set(next);
1728
+ void this.settleSavedBaseline();
1729
+ }
1730
+ /** Commits the current data as saved after widget hydration settles. */
1731
+ settleSavedBaseline() {
1732
+ this.clearSavedBaselineTimer();
1733
+ return new Promise((resolve) => {
1734
+ const commit = () => {
1735
+ if (this.builderService.registeredWidgetsCount() === 0) {
1736
+ this.savedBaselineTimer = setTimeout(commit, FORM_SAVED_BASELINE_IDLE_MS);
1737
+ return;
1738
+ }
1739
+ this.contextService.commitSaved();
1740
+ this.lastEmittedFormDirty = undefined;
1741
+ this.savedBaselineTimer = undefined;
1742
+ resolve();
1743
+ };
1744
+ this.savedBaselineTimer = setTimeout(commit, FORM_SAVED_BASELINE_IDLE_MS);
1745
+ });
1746
+ }
1747
+ async replaceContext(value) {
1748
+ this.clearSavedBaselineTimer();
1749
+ this.contextService.set(cloneDeep(value));
1750
+ // Commit immediately so save/replace is clean even when `set()` no-ops (data already matches).
1751
+ this.contextService.commitSaved();
1752
+ this.lastEmittedFormDirty = undefined;
1753
+ await this.settleSavedBaseline();
1754
+ }
1755
+ revertAndSettle() {
1756
+ this.clearSavedBaselineTimer();
1757
+ this.contextService.revertToSaved();
1758
+ this.lastEmittedFormDirty = undefined;
1759
+ }
1760
+ clearSavedBaselineTimer() {
1761
+ if (this.savedBaselineTimer) {
1762
+ clearTimeout(this.savedBaselineTimer);
1763
+ this.savedBaselineTimer = undefined;
1764
+ }
1765
+ }
1681
1766
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1682
1767
  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 }); }
1683
1768
  }
@@ -1753,6 +1838,7 @@ class AXPWidgetRendererDirective {
1753
1838
  this.isVisible = signal(true, ...(ngDevMode ? [{ debugName: "isVisible" }] : /* istanbul ignore next */ []));
1754
1839
  this.expressionEvaluators = new Map();
1755
1840
  this.renderTimeoutId = null;
1841
+ this.renderGeneration = 0;
1756
1842
  this.hasInitialRender = false;
1757
1843
  this.onContextChanged = new Subject();
1758
1844
  this.onLoadEvent = new Subject();
@@ -2111,18 +2197,18 @@ class AXPWidgetRendererDirective {
2111
2197
  }
2112
2198
  }
2113
2199
  rerenderComponent() {
2114
- // Clear any pending render operation to prevent double rendering
2115
2200
  if (this.renderTimeoutId) {
2116
2201
  clearTimeout(this.renderTimeoutId);
2117
2202
  this.renderTimeoutId = null;
2118
2203
  }
2119
- // Reset loading state to allow re-rendering
2120
- this.isLoading.set(false);
2121
- // Schedule the component loading
2204
+ const generation = ++this.renderGeneration;
2122
2205
  this.renderTimeoutId = setTimeout(async () => {
2123
- await this.loadComponent();
2124
2206
  this.renderTimeoutId = null;
2125
- });
2207
+ if (generation !== this.renderGeneration) {
2208
+ return;
2209
+ }
2210
+ await this.loadComponent();
2211
+ }, 0);
2126
2212
  }
2127
2213
  ngOnDestroy() {
2128
2214
  if (this.renderTimeoutId) {
@@ -2511,6 +2597,9 @@ class AXPWidgetRendererDirective {
2511
2597
  return this.contextService.data();
2512
2598
  },
2513
2599
  isDirty: () => {
2600
+ if (!this.contextService.isSavedCommitted()) {
2601
+ return false;
2602
+ }
2514
2603
  return this.contextService.isDirty();
2515
2604
  },
2516
2605
  /**