@acorex/platform 21.0.0-next.71 → 21.0.0-next.73
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.
- package/contracts/README.md +3 -0
- package/fesm2022/acorex-platform-auth.mjs +82 -60
- package/fesm2022/acorex-platform-auth.mjs.map +1 -1
- package/fesm2022/{acorex-platform-common-common-settings.provider-Bi1RYif5.mjs → acorex-platform-common-common-settings.provider-Ytey9uhY.mjs} +15 -1
- package/fesm2022/acorex-platform-common-common-settings.provider-Ytey9uhY.mjs.map +1 -0
- package/fesm2022/acorex-platform-common.mjs +3796 -1677
- package/fesm2022/acorex-platform-common.mjs.map +1 -1
- package/fesm2022/acorex-platform-contracts.mjs +10 -0
- package/fesm2022/acorex-platform-contracts.mjs.map +1 -0
- package/fesm2022/acorex-platform-core.mjs +1126 -114
- package/fesm2022/acorex-platform-core.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-builder.mjs +56 -170
- package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-components.mjs +254 -53
- package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-designer.mjs +199 -126
- package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
- package/fesm2022/{acorex-platform-layout-entity-attachments-page.component-D8iQnT-R.mjs → acorex-platform-layout-entity-attachments-page.component-B0EkdqvH.mjs} +6 -1
- package/fesm2022/acorex-platform-layout-entity-attachments-page.component-B0EkdqvH.mjs.map +1 -0
- package/fesm2022/acorex-platform-layout-entity.mjs +403 -460
- package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-views.mjs +675 -301
- package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-widget-core.mjs +126 -75
- package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
- 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
- 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
- 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
- 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
- package/fesm2022/acorex-platform-layout-widgets.mjs +450 -750
- package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
- package/fesm2022/acorex-platform-themes-default-error-401.component-B1nsdpTY.mjs +48 -0
- package/fesm2022/acorex-platform-themes-default-error-401.component-B1nsdpTY.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-default-error-404.component-D4UvRe8u.mjs +42 -0
- package/fesm2022/acorex-platform-themes-default-error-404.component-D4UvRe8u.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-default.mjs +79 -35
- package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
- package/fesm2022/acorex-platform-themes-shared-settings.provider-BjuzSe0T.mjs.map +1 -1
- package/fesm2022/acorex-platform-themes-shared.mjs +3 -0
- package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
- package/fesm2022/acorex-platform-workflow.mjs +57 -54
- package/fesm2022/acorex-platform-workflow.mjs.map +1 -1
- package/package.json +5 -1
- package/types/acorex-platform-auth.d.ts +31 -22
- package/types/acorex-platform-common.d.ts +892 -259
- package/types/acorex-platform-contracts.d.ts +39 -0
- package/types/acorex-platform-core.d.ts +291 -47
- package/types/acorex-platform-layout-builder.d.ts +10 -22
- package/types/acorex-platform-layout-components.d.ts +86 -14
- package/types/acorex-platform-layout-entity.d.ts +218 -212
- package/types/acorex-platform-layout-views.d.ts +125 -67
- package/types/acorex-platform-layout-widget-core.d.ts +56 -62
- package/types/acorex-platform-layout-widgets.d.ts +78 -65
- package/types/acorex-platform-themes-default.d.ts +14 -4
- package/fesm2022/acorex-platform-common-common-settings.provider-Bi1RYif5.mjs.map +0 -1
- package/fesm2022/acorex-platform-layout-entity-attachments-page.component-D8iQnT-R.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-error-401.component-C7EYJzSr.mjs +0 -31
- package/fesm2022/acorex-platform-themes-default-error-401.component-C7EYJzSr.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-error-404.component-7MVLMwIa.mjs +0 -25
- package/fesm2022/acorex-platform-themes-default-error-404.component-7MVLMwIa.mjs.map +0 -1
|
@@ -5,7 +5,7 @@ import { Injectable, inject, input, model, signal, computed, effect, output, vie
|
|
|
5
5
|
import { provideCommandSetups, AXPCommandService } from '@acorex/platform/runtime';
|
|
6
6
|
import { AXPopupService } from '@acorex/components/popup';
|
|
7
7
|
import * as i4 from '@acorex/platform/core';
|
|
8
|
-
import { AXPHookService,
|
|
8
|
+
import { normalizeKeyboardShortcuts, AXPHookService, findOverlayContainerAncestor, getNestedVisibleOverlayPanes, getTopVisibleOverlayContainer, AXPExpressionEvaluatorService, AXPComponentSlotModule, AXPContextStore } from '@acorex/platform/core';
|
|
9
9
|
import * as i1 from '@acorex/platform/layout/widget-core';
|
|
10
10
|
import { AXPWidgetSerializationHelper, AXPWidgetContainerComponent, AXPPageStatus, AXPWidgetCoreModule, AXPWidgetRegistryService } from '@acorex/platform/layout/widget-core';
|
|
11
11
|
import { cloneDeep, isNil, set, isEqual, merge } from 'lodash-es';
|
|
@@ -15,7 +15,6 @@ import { Subject, debounceTime, distinctUntilChanged, startWith } from 'rxjs';
|
|
|
15
15
|
import { AXOverlayService } from '@acorex/cdk/overlay';
|
|
16
16
|
import * as i1$1 from '@acorex/components/button';
|
|
17
17
|
import { AXButtonModule } from '@acorex/components/button';
|
|
18
|
-
import { AXDialogService } from '@acorex/components/dialog';
|
|
19
18
|
import * as i2$1 from '@acorex/components/decorators';
|
|
20
19
|
import { AXDecoratorModule } from '@acorex/components/decorators';
|
|
21
20
|
import * as i3 from '@acorex/components/loading';
|
|
@@ -23,6 +22,7 @@ import { AXLoadingModule } from '@acorex/components/loading';
|
|
|
23
22
|
import { AXBasePageComponent } from '@acorex/components/page';
|
|
24
23
|
import * as i6 from '@acorex/core/translation';
|
|
25
24
|
import { AXTranslationService, AXTranslationModule } from '@acorex/core/translation';
|
|
25
|
+
import { AXPUnsavedChangesConfirmService } from '@acorex/platform/common';
|
|
26
26
|
import { AXP_ENTITY_DEFINITION_CRUD_SERVICE } from '@acorex/platform/domain';
|
|
27
27
|
|
|
28
28
|
//#region ---- Dialog Action Shortcut Utilities ----
|
|
@@ -75,9 +75,9 @@ function resolveDialogActionShortcuts(defaults, overrides) {
|
|
|
75
75
|
if (overrides.length === 0) {
|
|
76
76
|
return undefined;
|
|
77
77
|
}
|
|
78
|
-
return
|
|
78
|
+
return flattenDialogActionShortcutChords([...defaults, ...overrides]);
|
|
79
79
|
}
|
|
80
|
-
return defaults
|
|
80
|
+
return flattenDialogActionShortcutChords(defaults);
|
|
81
81
|
}
|
|
82
82
|
/**
|
|
83
83
|
* Applies built-in footer shortcuts when actions are declared without `shortcuts`
|
|
@@ -95,6 +95,11 @@ function resolveConfiguredFooterActionShortcuts(command, explicit) {
|
|
|
95
95
|
}
|
|
96
96
|
return undefined;
|
|
97
97
|
}
|
|
98
|
+
function flattenDialogActionShortcutChords(shortcuts) {
|
|
99
|
+
const chords = normalizeKeyboardShortcuts(shortcuts).flatMap((shortcut) => shortcut.keys);
|
|
100
|
+
const deduped = dedupeDialogActionShortcuts(chords);
|
|
101
|
+
return deduped.length ? deduped : undefined;
|
|
102
|
+
}
|
|
98
103
|
function dedupeDialogActionShortcuts(shortcuts) {
|
|
99
104
|
const seen = new Set();
|
|
100
105
|
const result = [];
|
|
@@ -2555,8 +2560,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
2555
2560
|
}]
|
|
2556
2561
|
}] });
|
|
2557
2562
|
|
|
2558
|
-
|
|
2559
|
-
|
|
2563
|
+
//#region ---- Contracts barrel ----
|
|
2564
|
+
//#endregion
|
|
2565
|
+
|
|
2560
2566
|
/**
|
|
2561
2567
|
* Returns true when a nested overlay is open and Esc should close it first.
|
|
2562
2568
|
* Returns false when only the dialog shell is on top.
|
|
@@ -2578,52 +2584,6 @@ function hasOpenNestedOverlayFromAcorexService(overlayService, dialogOverlay) {
|
|
|
2578
2584
|
}
|
|
2579
2585
|
return null;
|
|
2580
2586
|
}
|
|
2581
|
-
/**
|
|
2582
|
-
* Returns true when the overlay element is rendered and visible in the viewport.
|
|
2583
|
-
*/
|
|
2584
|
-
function isVisibleOverlayElement(element) {
|
|
2585
|
-
if (!(element instanceof HTMLElement)) {
|
|
2586
|
-
return false;
|
|
2587
|
-
}
|
|
2588
|
-
const style = getComputedStyle(element);
|
|
2589
|
-
if (style.display === 'none' || style.visibility === 'hidden' || Number(style.opacity) === 0) {
|
|
2590
|
-
return false;
|
|
2591
|
-
}
|
|
2592
|
-
const rect = element.getBoundingClientRect();
|
|
2593
|
-
return rect.width > 0 && rect.height > 0;
|
|
2594
|
-
}
|
|
2595
|
-
/**
|
|
2596
|
-
* Collects visible overlay containers in stacking order (later / higher z-index wins).
|
|
2597
|
-
*/
|
|
2598
|
-
function getVisibleOverlayContainers(document) {
|
|
2599
|
-
return Array.from(document.querySelectorAll(OVERLAY_CONTAINER_SELECTOR))
|
|
2600
|
-
.filter(isVisibleOverlayElement)
|
|
2601
|
-
.sort(compareOverlayStackOrder);
|
|
2602
|
-
}
|
|
2603
|
-
/**
|
|
2604
|
-
* Compares two overlay containers by z-index, then DOM order.
|
|
2605
|
-
*/
|
|
2606
|
-
function compareOverlayStackOrder(a, b) {
|
|
2607
|
-
const za = Number(getComputedStyle(a).zIndex) || 0;
|
|
2608
|
-
const zb = Number(getComputedStyle(b).zIndex) || 0;
|
|
2609
|
-
if (za !== zb) {
|
|
2610
|
-
return za - zb;
|
|
2611
|
-
}
|
|
2612
|
-
const position = a.compareDocumentPosition(b);
|
|
2613
|
-
if (position & Node.DOCUMENT_POSITION_FOLLOWING) {
|
|
2614
|
-
return -1;
|
|
2615
|
-
}
|
|
2616
|
-
if (position & Node.DOCUMENT_POSITION_PRECEDING) {
|
|
2617
|
-
return 1;
|
|
2618
|
-
}
|
|
2619
|
-
return 0;
|
|
2620
|
-
}
|
|
2621
|
-
/**
|
|
2622
|
-
* Returns visible anchored overlay panes that belong to widget popovers, not the dialog shell.
|
|
2623
|
-
*/
|
|
2624
|
-
function getNestedOverlayPanes(document, dialogOverlay) {
|
|
2625
|
-
return Array.from(document.querySelectorAll(OVERLAY_PANE_SELECTOR)).filter((pane) => isVisibleOverlayElement(pane) && !dialogOverlay?.contains(pane));
|
|
2626
|
-
}
|
|
2627
2587
|
/**
|
|
2628
2588
|
* DOM fallback until {@link AXOverlayService} exposes open-overlay queries.
|
|
2629
2589
|
*
|
|
@@ -2631,15 +2591,14 @@ function getNestedOverlayPanes(document, dialogOverlay) {
|
|
|
2631
2591
|
* Widget popovers (select, datetime, dropdown) use anchored containers with `.ax-overlay-pane`.
|
|
2632
2592
|
*/
|
|
2633
2593
|
function shouldDeferEscapeToNestedOverlayFromDom(document, dialogHost) {
|
|
2634
|
-
const dialogOverlay = dialogHost
|
|
2635
|
-
if (
|
|
2594
|
+
const dialogOverlay = findOverlayContainerAncestor(dialogHost);
|
|
2595
|
+
if (getNestedVisibleOverlayPanes(document, dialogOverlay).length > 0) {
|
|
2636
2596
|
return true;
|
|
2637
2597
|
}
|
|
2638
|
-
const
|
|
2639
|
-
if (!
|
|
2598
|
+
const topContainer = getTopVisibleOverlayContainer(document);
|
|
2599
|
+
if (!topContainer) {
|
|
2640
2600
|
return false;
|
|
2641
2601
|
}
|
|
2642
|
-
const topContainer = visibleContainers[visibleContainers.length - 1];
|
|
2643
2602
|
if (!dialogOverlay) {
|
|
2644
2603
|
return topContainer.contains(dialogHost) === false;
|
|
2645
2604
|
}
|
|
@@ -2650,7 +2609,7 @@ function shouldDeferEscapeToNestedOverlayFromDom(document, dialogHost) {
|
|
|
2650
2609
|
* should not run on the parent dialog.
|
|
2651
2610
|
*/
|
|
2652
2611
|
function shouldDeferDialogShortcutsToNestedOverlay(document, dialogHost, overlayService) {
|
|
2653
|
-
const dialogOverlay = dialogHost
|
|
2612
|
+
const dialogOverlay = findOverlayContainerAncestor(dialogHost);
|
|
2654
2613
|
if (overlayService) {
|
|
2655
2614
|
const fromService = hasOpenNestedOverlayFromAcorexService(overlayService, dialogOverlay);
|
|
2656
2615
|
if (fromService !== null) {
|
|
@@ -2687,10 +2646,6 @@ function shouldRouteOnActionCancelThroughDismissGate(confirmCloseWhenDirtyEnable
|
|
|
2687
2646
|
return confirmCloseWhenDirtyEnabled === true;
|
|
2688
2647
|
}
|
|
2689
2648
|
|
|
2690
|
-
/** Idle period after the last context change before capturing the clean baseline. */
|
|
2691
|
-
const DIALOG_DIRTY_BASELINE_IDLE_MS = 500;
|
|
2692
|
-
/** Hard fallback when idle detection never settles. */
|
|
2693
|
-
const DIALOG_DIRTY_BASELINE_FALLBACK_DELAY_MS = 2000;
|
|
2694
2649
|
/** Debounce after widget count stabilizes before re-evaluating footer actions. */
|
|
2695
2650
|
const DIALOG_WIDGET_COUNT_STABLE_DELAY_MS = 350;
|
|
2696
2651
|
class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
@@ -2700,17 +2655,17 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2700
2655
|
this.expressionEvaluator = inject(AXPExpressionEvaluatorService);
|
|
2701
2656
|
this.commandService = inject(AXPCommandService);
|
|
2702
2657
|
this.hookService = inject(AXPHookService, { optional: true });
|
|
2703
|
-
this.
|
|
2658
|
+
this.unsavedChangesConfirm = inject(AXPUnsavedChangesConfirmService);
|
|
2704
2659
|
this.overlayService = inject(AXOverlayService);
|
|
2705
2660
|
this.translationService = inject(AXTranslationService);
|
|
2706
2661
|
this.document = inject(DOCUMENT);
|
|
2707
2662
|
this.host = inject((ElementRef));
|
|
2708
2663
|
/** Ensures `show()` resolves once when the dialog closes (footer action or header close). */
|
|
2709
2664
|
this.callbackInvoked = false;
|
|
2710
|
-
/** True after the post-init baseline snapshot has been captured. */
|
|
2711
|
-
this.dirtyBaselineCaptured = false;
|
|
2712
2665
|
/** Skips dirty confirmation on the next {@link onClosing} (successful submit / programmatic close). */
|
|
2713
2666
|
this.skipNextOnClosingDirtyCheck = false;
|
|
2667
|
+
/** Blocks re-entrant {@link onClosing} while the first close gate is still resolving. */
|
|
2668
|
+
this.closeGateInProgress = false;
|
|
2714
2669
|
this.destroyRef = inject(DestroyRef);
|
|
2715
2670
|
this.context = signal({}, ...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
|
|
2716
2671
|
// This will be set by the popup service automatically - same as dynamic-dialog
|
|
@@ -2733,7 +2688,7 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2733
2688
|
return;
|
|
2734
2689
|
}
|
|
2735
2690
|
// Unsaved-changes confirm is modal — parent footer shortcuts must not run underneath it.
|
|
2736
|
-
if (this.
|
|
2691
|
+
if (this.unsavedChangesConfirm.isConfirmPending()) {
|
|
2737
2692
|
return;
|
|
2738
2693
|
}
|
|
2739
2694
|
if (shouldDeferDialogShortcutsToNestedOverlay(this.document, this.host.nativeElement, this.overlayService)) {
|
|
@@ -2784,36 +2739,15 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2784
2739
|
this.aggregateAndEvaluateActions();
|
|
2785
2740
|
}, DIALOG_WIDGET_COUNT_STABLE_DELAY_MS);
|
|
2786
2741
|
}, ...(ngDevMode ? [{ debugName: "#widgetActionsEffect" }] : /* istanbul ignore next */ []));
|
|
2787
|
-
this.#dirtyBaselineEffect = effect(() => {
|
|
2788
|
-
if (this.dirtyBaselineCaptured) {
|
|
2789
|
-
return;
|
|
2790
|
-
}
|
|
2791
|
-
const store = this.getWidgetContextStore();
|
|
2792
|
-
if (!store) {
|
|
2793
|
-
return;
|
|
2794
|
-
}
|
|
2795
|
-
store.data();
|
|
2796
|
-
this.widgetCoreService?.registeredWidgetsCount();
|
|
2797
|
-
this.scheduleDirtyBaselineCapture();
|
|
2798
|
-
}, ...(ngDevMode ? [{ debugName: "#dirtyBaselineEffect" }] : /* istanbul ignore next */ []));
|
|
2799
2742
|
}
|
|
2800
2743
|
//#endregion
|
|
2801
2744
|
//#region ---- Lifecycle ----
|
|
2802
2745
|
ngOnInit() {
|
|
2803
2746
|
const initialContext = this.config?.context || {};
|
|
2804
2747
|
this.context.set(initialContext);
|
|
2805
|
-
this.dirtyBaselineFallbackTimer = setTimeout(() => {
|
|
2806
|
-
this.captureDirtyBaselineIfNeeded();
|
|
2807
|
-
}, DIALOG_DIRTY_BASELINE_FALLBACK_DELAY_MS);
|
|
2808
2748
|
this.document.addEventListener('keydown', this.onDialogShortcutCapture, true);
|
|
2809
2749
|
this.destroyRef.onDestroy(() => {
|
|
2810
2750
|
this.document.removeEventListener('keydown', this.onDialogShortcutCapture, true);
|
|
2811
|
-
if (this.dirtyBaselineIdleTimer) {
|
|
2812
|
-
clearTimeout(this.dirtyBaselineIdleTimer);
|
|
2813
|
-
}
|
|
2814
|
-
if (this.dirtyBaselineFallbackTimer) {
|
|
2815
|
-
clearTimeout(this.dirtyBaselineFallbackTimer);
|
|
2816
|
-
}
|
|
2817
2751
|
});
|
|
2818
2752
|
void this.invokeLayoutContextChangedHooks();
|
|
2819
2753
|
}
|
|
@@ -2824,25 +2758,31 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2824
2758
|
* This is the only place that prompts for unsaved changes.
|
|
2825
2759
|
*/
|
|
2826
2760
|
async onClosing(e) {
|
|
2827
|
-
if (this.
|
|
2761
|
+
if (this.unsavedChangesConfirm.isConfirmPending() || this.closeGateInProgress) {
|
|
2828
2762
|
e.cancel = true;
|
|
2829
2763
|
return;
|
|
2830
2764
|
}
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
this.
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2765
|
+
this.closeGateInProgress = true;
|
|
2766
|
+
try {
|
|
2767
|
+
if (this.skipNextOnClosingDirtyCheck) {
|
|
2768
|
+
this.skipNextOnClosingDirtyCheck = false;
|
|
2769
|
+
this.completeDismissResolve();
|
|
2770
|
+
return;
|
|
2771
|
+
}
|
|
2772
|
+
if (!this.config?.confirmCloseWhenDirty?.enabled) {
|
|
2773
|
+
this.completeDismissResolve();
|
|
2774
|
+
return;
|
|
2775
|
+
}
|
|
2776
|
+
if (!(await this.confirmDismissIfDirty())) {
|
|
2777
|
+
e.cancel = true;
|
|
2778
|
+
this.pendingDismissResolvePayload = undefined;
|
|
2779
|
+
return;
|
|
2780
|
+
}
|
|
2837
2781
|
this.completeDismissResolve();
|
|
2838
|
-
return;
|
|
2839
2782
|
}
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
this.pendingDismissResolvePayload = undefined;
|
|
2843
|
-
return;
|
|
2783
|
+
finally {
|
|
2784
|
+
this.closeGateInProgress = false;
|
|
2844
2785
|
}
|
|
2845
|
-
this.completeDismissResolve();
|
|
2846
2786
|
}
|
|
2847
2787
|
/**
|
|
2848
2788
|
* Footer cancel and other in-app dismiss actions. Routes through `super.close()` so the
|
|
@@ -2866,50 +2806,10 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2866
2806
|
}
|
|
2867
2807
|
//#endregion
|
|
2868
2808
|
//#region ---- Dirty State ----
|
|
2869
|
-
|
|
2870
|
-
return this.layoutRenderer()?.getContainer()
|
|
2871
|
-
}
|
|
2872
|
-
scheduleDirtyBaselineCapture() {
|
|
2873
|
-
if (this.dirtyBaselineCaptured) {
|
|
2874
|
-
return;
|
|
2875
|
-
}
|
|
2876
|
-
if (this.dirtyBaselineIdleTimer) {
|
|
2877
|
-
clearTimeout(this.dirtyBaselineIdleTimer);
|
|
2878
|
-
}
|
|
2879
|
-
this.dirtyBaselineIdleTimer = setTimeout(() => {
|
|
2880
|
-
this.captureDirtyBaselineIfNeeded();
|
|
2881
|
-
}, DIALOG_DIRTY_BASELINE_IDLE_MS);
|
|
2882
|
-
}
|
|
2883
|
-
/**
|
|
2884
|
-
* Captures a dialog-local baseline once after widget init/normalization goes idle.
|
|
2885
|
-
*/
|
|
2886
|
-
captureDirtyBaselineIfNeeded() {
|
|
2887
|
-
if (this.dirtyBaselineCaptured) {
|
|
2888
|
-
return;
|
|
2889
|
-
}
|
|
2890
|
-
const store = this.getWidgetContextStore();
|
|
2891
|
-
if (!store) {
|
|
2892
|
-
return;
|
|
2893
|
-
}
|
|
2894
|
-
const widgetCount = this.widgetCoreService?.registeredWidgetsCount() ?? 0;
|
|
2895
|
-
if (widgetCount === 0) {
|
|
2896
|
-
return;
|
|
2897
|
-
}
|
|
2898
|
-
const snapshot = store.data();
|
|
2899
|
-
this.dirtyBaseline = captureFormContextBaseline(snapshot);
|
|
2900
|
-
store.commitBaseline();
|
|
2901
|
-
this.dirtyBaselineCaptured = true;
|
|
2902
|
-
if (this.dirtyBaselineIdleTimer) {
|
|
2903
|
-
clearTimeout(this.dirtyBaselineIdleTimer);
|
|
2904
|
-
this.dirtyBaselineIdleTimer = undefined;
|
|
2905
|
-
}
|
|
2906
|
-
if (this.dirtyBaselineFallbackTimer) {
|
|
2907
|
-
clearTimeout(this.dirtyBaselineFallbackTimer);
|
|
2908
|
-
this.dirtyBaselineFallbackTimer = undefined;
|
|
2909
|
-
}
|
|
2809
|
+
getWidgetContainer() {
|
|
2810
|
+
return this.layoutRenderer()?.getContainer();
|
|
2910
2811
|
}
|
|
2911
2812
|
#widgetActionsEffect;
|
|
2912
|
-
#dirtyBaselineEffect;
|
|
2913
2813
|
//#endregion
|
|
2914
2814
|
handleContextChanged(event) {
|
|
2915
2815
|
this.context.set(event);
|
|
@@ -2929,7 +2829,7 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2929
2829
|
const payload = {
|
|
2930
2830
|
sessionKey: this.contextChangedHooksSessionKey,
|
|
2931
2831
|
getContext: () => {
|
|
2932
|
-
const store = this.
|
|
2832
|
+
const store = this.getWidgetContainer()?.contextService;
|
|
2933
2833
|
return (store?.data() ?? this.context() ?? {});
|
|
2934
2834
|
},
|
|
2935
2835
|
metadata: meta,
|
|
@@ -3037,43 +2937,29 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
3037
2937
|
}
|
|
3038
2938
|
isDialogDirty() {
|
|
3039
2939
|
const confirmOptions = this.config?.confirmCloseWhenDirty;
|
|
3040
|
-
if (!confirmOptions?.enabled
|
|
2940
|
+
if (!confirmOptions?.enabled) {
|
|
3041
2941
|
return false;
|
|
3042
2942
|
}
|
|
3043
|
-
const
|
|
3044
|
-
if (!
|
|
2943
|
+
const container = this.getWidgetContainer();
|
|
2944
|
+
if (!container?.isSavedCommitted()) {
|
|
3045
2945
|
return false;
|
|
3046
2946
|
}
|
|
3047
|
-
const
|
|
3048
|
-
const baseline = this.dirtyBaseline ?? store.initial();
|
|
2947
|
+
const store = container.contextService;
|
|
3049
2948
|
if (typeof confirmOptions.isDirty === 'function') {
|
|
3050
|
-
return confirmOptions.isDirty(
|
|
2949
|
+
return confirmOptions.isDirty(store.data(), store.saved());
|
|
3051
2950
|
}
|
|
3052
|
-
return
|
|
3053
|
-
(this.widgetCoreService?.hasDirtyWidgets() ?? false) ||
|
|
3054
|
-
isFormContextDirty(current, baseline));
|
|
2951
|
+
return container.isFormDirty();
|
|
3055
2952
|
}
|
|
3056
|
-
|
|
3057
|
-
if (this.dismissConfirmPromise) {
|
|
3058
|
-
return this.dismissConfirmPromise;
|
|
3059
|
-
}
|
|
2953
|
+
confirmDismissIfDirty() {
|
|
3060
2954
|
const confirmOptions = this.config?.confirmCloseWhenDirty;
|
|
3061
2955
|
if (!confirmOptions?.enabled || !this.isDialogDirty()) {
|
|
3062
|
-
return true;
|
|
3063
|
-
}
|
|
3064
|
-
this.dismissConfirmPromise = this.promptUnsavedChangesConfirm(confirmOptions);
|
|
3065
|
-
try {
|
|
3066
|
-
return await this.dismissConfirmPromise;
|
|
2956
|
+
return Promise.resolve(true);
|
|
3067
2957
|
}
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
const title = await this.translationService.translateAsync(confirmOptions.title ?? '@general:messages.unsaved-changes.title');
|
|
3074
|
-
const message = await this.translationService.translateAsync(confirmOptions.message ?? '@general:messages.unsaved-changes.message');
|
|
3075
|
-
const dialogResult = await this.dialogService.confirm(title, message, 'warning', 'horizontal', false, 'cancel');
|
|
3076
|
-
return dialogResult.result === true;
|
|
2958
|
+
return this.unsavedChangesConfirm.confirmIfDirty(true, {
|
|
2959
|
+
enabled: confirmOptions.enabled,
|
|
2960
|
+
title: confirmOptions.title,
|
|
2961
|
+
message: confirmOptions.message,
|
|
2962
|
+
});
|
|
3077
2963
|
}
|
|
3078
2964
|
/** Whether the layout form should be validated before running this footer command. */
|
|
3079
2965
|
shouldValidateBeforeAction(cmd) {
|