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

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 (32) hide show
  1. package/fesm2022/acorex-platform-common.mjs +11 -0
  2. package/fesm2022/acorex-platform-common.mjs.map +1 -1
  3. package/fesm2022/acorex-platform-core.mjs +332 -76
  4. package/fesm2022/acorex-platform-core.mjs.map +1 -1
  5. package/fesm2022/acorex-platform-layout-builder.mjs +563 -44
  6. package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
  7. package/fesm2022/acorex-platform-layout-components.mjs +83 -67
  8. package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
  9. package/fesm2022/acorex-platform-layout-entity.mjs +543 -237
  10. package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
  11. package/fesm2022/acorex-platform-layout-views.mjs +319 -66
  12. package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
  13. package/fesm2022/acorex-platform-layout-widget-core.mjs +54 -6
  14. package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
  15. package/fesm2022/acorex-platform-layout-widgets.mjs +198 -91
  16. package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
  17. package/fesm2022/acorex-platform-themes-default.mjs +13 -14
  18. package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
  19. package/fesm2022/acorex-platform-themes-shared.mjs +50 -30
  20. package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
  21. package/package.json +1 -1
  22. package/types/acorex-platform-common.d.ts +11 -0
  23. package/types/acorex-platform-core.d.ts +137 -47
  24. package/types/acorex-platform-layout-builder.d.ts +90 -13
  25. package/types/acorex-platform-layout-components.d.ts +21 -17
  26. package/types/acorex-platform-layout-entity.d.ts +63 -10
  27. package/types/acorex-platform-layout-views.d.ts +68 -6
  28. package/types/acorex-platform-layout-widget-core.d.ts +43 -8
  29. package/types/acorex-platform-layout-widgets.d.ts +21 -6
  30. package/types/acorex-platform-themes-default.d.ts +24 -4
  31. package/types/acorex-platform-themes-shared.d.ts +6 -0
  32. package/types/acorex-platform-workflow.d.ts +1 -1
@@ -65,6 +65,9 @@ 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
+ /** Bumped when a widget reports dirty-state changes via `api().isDirty()`. */
69
+ this.dirtyWidgetsRevision = signal(0, ...(ngDevMode ? [{ debugName: "dirtyWidgetsRevision" }] : /* istanbul ignore next */ []));
70
+ this.dirtyWidgetsRevisionSignal = this.dirtyWidgetsRevision.asReadonly();
68
71
  }
69
72
  get variables() {
70
73
  return this.variables$();
@@ -168,6 +171,22 @@ class AXPWidgetCoreService {
168
171
  listRegisteredWidgetNames() {
169
172
  return Array.from(this.widgets.keys());
170
173
  }
174
+ /** Notifies listeners that a widget's composite dirty state may have changed. */
175
+ notifyWidgetDirtyChanged() {
176
+ this.dirtyWidgetsRevision.update((value) => value + 1);
177
+ }
178
+ /**
179
+ * Returns whether any registered widget reports dirty state via `api().isDirty()`.
180
+ */
181
+ hasDirtyWidgets() {
182
+ for (const widget of this.widgets.values()) {
183
+ const isDirty = widget.api()?.['isDirty'];
184
+ if (typeof isDirty === 'function' && isDirty()) {
185
+ return true;
186
+ }
187
+ }
188
+ return false;
189
+ }
171
190
  ngOnDestroy() { }
172
191
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetCoreService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
173
192
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetCoreService }); }
@@ -484,7 +503,7 @@ class AXPValueWidgetComponent extends AXPLayoutBaseWidgetComponent {
484
503
  this.detectFullPath();
485
504
  // Set defaultValue if it exists and the path doesn't exist in context
486
505
  if (!isNil(this.defaultValue) && this.fullPath() && !this.contextService.hasValue(this.fullPath())) {
487
- this.setValue(this.defaultValue);
506
+ this.setValue(this.defaultValue, { origin: 'system' });
488
507
  }
489
508
  }
490
509
  //
@@ -497,7 +516,7 @@ class AXPValueWidgetComponent extends AXPLayoutBaseWidgetComponent {
497
516
  }
498
517
  return rawValue;
499
518
  }
500
- setValue(value) {
519
+ setValue(value, options) {
501
520
  if (this.node.valueTransforms?.setter) {
502
521
  value = this.node.valueTransforms?.setter(value);
503
522
  }
@@ -515,10 +534,14 @@ class AXPValueWidgetComponent extends AXPLayoutBaseWidgetComponent {
515
534
  return;
516
535
  }
517
536
  if (this.fullPath()) {
518
- this.contextService.update(this.fullPath(), value);
537
+ this.contextService.update(this.fullPath(), value, { origin: options?.origin ?? 'user' });
519
538
  this.onValueChanged.next({ sender: this });
520
539
  }
521
540
  }
541
+ /** Persists a user-initiated value change into the shared context store. */
542
+ setUserValue(value) {
543
+ this.setValue(value, { origin: 'user' });
544
+ }
522
545
  detectFullPath() {
523
546
  const sections = [];
524
547
  const ids = [];
@@ -555,7 +578,7 @@ class AXPValueWidgetComponent extends AXPLayoutBaseWidgetComponent {
555
578
  }
556
579
  handleValueChanged(e) {
557
580
  if (e.isUserInteraction) {
558
- this.setValue(e.value);
581
+ this.setUserValue(e.value);
559
582
  }
560
583
  }
561
584
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPValueWidgetComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
@@ -1651,6 +1674,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
1651
1674
 
1652
1675
  class AXPWidgetContainerComponent {
1653
1676
  set context(value) {
1677
+ // Parent re-binds the same context after widget-driven updates; avoid reset() clearing dirty paths.
1678
+ if (isEqual(this.contextService.data(), value)) {
1679
+ return;
1680
+ }
1654
1681
  this.contextService.set(value);
1655
1682
  }
1656
1683
  set functions(v) {
@@ -1660,6 +1687,11 @@ class AXPWidgetContainerComponent {
1660
1687
  this.contextService = inject(AXPContextStore);
1661
1688
  this.builderService = inject(AXPWidgetCoreService);
1662
1689
  this.onContextChanged = new EventEmitter();
1690
+ /** True when user-edited context paths or composite widgets report dirty state. */
1691
+ this.isFormDirty = computed(() => {
1692
+ this.builderService.dirtyWidgetsRevisionSignal();
1693
+ return this.contextService.isUserDirty() || this.builderService.hasDirtyWidgets();
1694
+ }, ...(ngDevMode ? [{ debugName: "isFormDirty" }] : /* istanbul ignore next */ []));
1663
1695
  this.status = computed(() => {
1664
1696
  return this.builderService.status();
1665
1697
  }, ...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
@@ -1668,7 +1700,11 @@ class AXPWidgetContainerComponent {
1668
1700
  }, ...(ngDevMode ? [{ debugName: "isBusy" }] : /* istanbul ignore next */ []));
1669
1701
  effect(() => {
1670
1702
  if (this.contextService.isChanged()) {
1671
- this.onContextChanged.emit(this.contextService.changeEvent());
1703
+ const changeEvent = this.contextService.changeEvent();
1704
+ this.onContextChanged.emit({
1705
+ ...changeEvent,
1706
+ isFormDirty: this.isFormDirty(),
1707
+ });
1672
1708
  }
1673
1709
  });
1674
1710
  }
@@ -1678,6 +1714,18 @@ class AXPWidgetContainerComponent {
1678
1714
  find(name) {
1679
1715
  return this.builderService.waitForWidget(name);
1680
1716
  }
1717
+ /** Replaces context and clears dirty tracking (discard, save reload, external reset). */
1718
+ replaceContext(value) {
1719
+ const next = cloneDeep(value);
1720
+ this.contextService.clearUserDirtyPaths();
1721
+ // After save the parent often re-binds deeply-equal data; still reset the clean baseline.
1722
+ if (isEqual(this.contextService.data(), next)) {
1723
+ this.contextService.commitBaseline();
1724
+ this.builderService.notifyWidgetDirtyChanged();
1725
+ return;
1726
+ }
1727
+ this.contextService.set(next);
1728
+ }
1681
1729
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPWidgetContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1682
1730
  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
1731
  }
@@ -2511,7 +2559,7 @@ class AXPWidgetRendererDirective {
2511
2559
  return this.contextService.data();
2512
2560
  },
2513
2561
  isDirty: () => {
2514
- return this.contextService.isDirty();
2562
+ return this.contextService.isUserDirty() || this.builderService.hasDirtyWidgets();
2515
2563
  },
2516
2564
  /**
2517
2565
  * Host/widget-under-edit `options` from the shared context store (same values as