@acorex-platform/framework-client 0.0.38 → 0.0.48
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/fesm2022/acorex-platform-framework-client-auth.mjs +212 -44
- package/fesm2022/acorex-platform-framework-client-auth.mjs.map +1 -1
- package/fesm2022/{acorex-platform-framework-client-common-common-settings.provider-CzBr0cRi.mjs → acorex-platform-framework-client-common-common-settings.provider-ChU2tvyt.mjs} +31 -1
- package/fesm2022/acorex-platform-framework-client-common-common-settings.provider-ChU2tvyt.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-common.mjs +1172 -193
- package/fesm2022/acorex-platform-framework-client-common.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-core.mjs +233 -132
- package/fesm2022/acorex-platform-framework-client-core.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-layout-builder.mjs +295 -59
- package/fesm2022/acorex-platform-framework-client-layout-builder.mjs.map +1 -1
- package/fesm2022/{acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-Bp2cNCPO.mjs → acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-BqPaHKPM.mjs} +10 -6
- package/fesm2022/acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-BqPaHKPM.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-components-property-viewer-inline-code-editor.component-CcpUTEVQ.mjs +66 -0
- package/fesm2022/acorex-platform-framework-client-layout-components-property-viewer-inline-code-editor.component-CcpUTEVQ.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-components-resource-appointment-board.component-DCkpgsFH.mjs +195 -0
- package/fesm2022/acorex-platform-framework-client-layout-components-resource-appointment-board.component-DCkpgsFH.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-components.mjs +4015 -1014
- package/fesm2022/acorex-platform-framework-client-layout-components.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-layout-designer.mjs +1179 -304
- package/fesm2022/acorex-platform-framework-client-layout-designer.mjs.map +1 -1
- package/fesm2022/{acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-CvaOWbJx.mjs → acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-UkQsf84C.mjs} +1701 -300
- package/fesm2022/acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-UkQsf84C.mjs.map +1 -0
- package/fesm2022/{acorex-platform-framework-client-layout-entity-attachments-page.component-Ds63Rt2n.mjs → acorex-platform-framework-client-layout-entity-attachments-page.component-DmmiDLZw.mjs} +2 -2
- package/fesm2022/{acorex-platform-framework-client-layout-entity-attachments-page.component-Ds63Rt2n.mjs.map → acorex-platform-framework-client-layout-entity-attachments-page.component-DmmiDLZw.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-framework-client-layout-entity-bulk-entity.command-C0pohwdo.mjs → acorex-platform-framework-client-layout-entity-bulk-entity.command-BOFTb66c.mjs} +2 -2
- package/fesm2022/{acorex-platform-framework-client-layout-entity-bulk-entity.command-C0pohwdo.mjs.map → acorex-platform-framework-client-layout-entity-bulk-entity.command-BOFTb66c.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-framework-client-layout-entity-gallery-page.component-o2HuZrDN.mjs → acorex-platform-framework-client-layout-entity-gallery-page.component-Bg-cMOlQ.mjs} +2 -2
- package/fesm2022/{acorex-platform-framework-client-layout-entity-gallery-page.component-o2HuZrDN.mjs.map → acorex-platform-framework-client-layout-entity-gallery-page.component-Bg-cMOlQ.mjs.map} +1 -1
- package/fesm2022/acorex-platform-framework-client-layout-entity.mjs +2 -2
- package/fesm2022/acorex-platform-framework-client-layout-views.mjs +47 -8
- package/fesm2022/acorex-platform-framework-client-layout-views.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs +3854 -3292
- package/fesm2022/acorex-platform-framework-client-layout-widget-core.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-layout-widgets-acorex-platform-framework-client-layout-widgets-DJNSBNVe.mjs +33319 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-acorex-platform-framework-client-layout-widgets-DJNSBNVe.mjs.map +1 -0
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-button-widget-designer.component-BDOdcoyN.mjs → acorex-platform-framework-client-layout-widgets-button-widget-designer.component-BkrPu1wO.mjs} +8 -5
- package/fesm2022/acorex-platform-framework-client-layout-widgets-button-widget-designer.component-BkrPu1wO.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-value.util-Cb7R1qLO.mjs +45 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-value.util-Cb7R1qLO.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-widget-column.component-BL621E1r.mjs +68 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-widget-column.component-BL621E1r.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-widget-edit.component-CVsPKhNl.mjs +88 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-widget-edit.component-CVsPKhNl.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-widget-view.component-BEq4O4al.mjs +71 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-code-editor-widget-view.component-BEq4O4al.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-condition-builder-edit-popup.component-WpiEHusZ.mjs +81 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-condition-builder-edit-popup.component-WpiEHusZ.mjs.map +1 -0
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-page-widget-designer.component-WzIiwnGC.mjs → acorex-platform-framework-client-layout-widgets-page-widget-designer.component-Cadl6guA.mjs} +41 -23
- package/fesm2022/acorex-platform-framework-client-layout-widgets-page-widget-designer.component-Cadl6guA.mjs.map +1 -0
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-4mX8aYzA.mjs → acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-CKtGsWPI.mjs} +3 -3
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-4mX8aYzA.mjs.map → acorex-platform-framework-client-layout-widgets-repeater-widget-column.component-CKtGsWPI.mjs.map} +1 -1
- package/fesm2022/acorex-platform-framework-client-layout-widgets-scheduler-picker-widget-column.component-Clb27Loe.mjs +71 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-scheduler-picker-widget-column.component-Clb27Loe.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-scheduler-picker-widget-edit.component-l7q8A-D3.mjs +111 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-scheduler-picker-widget-edit.component-l7q8A-D3.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-scheduler-picker-widget-view.component-Cc1AH09b.mjs +77 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-scheduler-picker-widget-view.component-Cc1AH09b.mjs.map +1 -0
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-TWxthl21.mjs → acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-BCLxS3rS.mjs} +3 -3
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-TWxthl21.mjs.map → acorex-platform-framework-client-layout-widgets-tabular-data-edit-popup.component-BCLxS3rS.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-D6HFPmGC.mjs → acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-DYxgwR2E.mjs} +21 -8
- package/fesm2022/acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-DYxgwR2E.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-trigger-expression-editor-popup.component-C2PGoC7r.mjs +124 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-trigger-expression-editor-popup.component-C2PGoC7r.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-triggers-widget-editor.component-DDDoPoQa.mjs +345 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets-triggers-widget-editor.component-DDDoPoQa.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-layout-widgets.mjs +1 -31985
- package/fesm2022/acorex-platform-framework-client-layout-widgets.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-realtime.mjs +2 -1
- package/fesm2022/acorex-platform-framework-client-realtime.mjs.map +1 -1
- package/fesm2022/acorex-platform-framework-client-runtime.mjs +2 -2
- package/fesm2022/acorex-platform-framework-client-runtime.mjs.map +1 -1
- package/fesm2022/{acorex-platform-framework-client-themes-default-entity-master-create-view.component-CT7WWWG0.mjs → acorex-platform-framework-client-themes-default-entity-master-create-view.component-DlS4v_zb.mjs} +3 -3
- package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-create-view.component-DlS4v_zb.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-modify-view.component-DBmdBKBL.mjs +122 -0
- package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-modify-view.component-DBmdBKBL.mjs.map +1 -0
- package/fesm2022/{acorex-platform-framework-client-themes-default-entity-master-single-view.component-DBUwSzvg.mjs → acorex-platform-framework-client-themes-default-entity-master-single-view.component-CkpRT1a9.mjs} +3 -3
- package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-single-view.component-CkpRT1a9.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-themes-default.mjs +424 -103
- package/fesm2022/acorex-platform-framework-client-themes-default.mjs.map +1 -1
- package/fesm2022/{acorex-platform-framework-client-themes-shared-settings.provider-CpsAIsxj.mjs → acorex-platform-framework-client-themes-shared-settings.provider-DIkjSTU7.mjs} +102 -1
- package/fesm2022/acorex-platform-framework-client-themes-shared-settings.provider-DIkjSTU7.mjs.map +1 -0
- package/fesm2022/acorex-platform-framework-client-themes-shared.mjs +174 -22
- package/fesm2022/acorex-platform-framework-client-themes-shared.mjs.map +1 -1
- package/package.json +2 -2
- package/realtime/README.md +1 -1
- package/types/acorex-platform-framework-client-auth.d.ts +18 -1
- package/types/acorex-platform-framework-client-common.d.ts +243 -9
- package/types/acorex-platform-framework-client-core.d.ts +152 -119
- package/types/acorex-platform-framework-client-layout-builder.d.ts +29 -0
- package/types/acorex-platform-framework-client-layout-components.d.ts +717 -147
- package/types/acorex-platform-framework-client-layout-designer.d.ts +192 -14
- package/types/acorex-platform-framework-client-layout-entity.d.ts +167 -92
- package/types/acorex-platform-framework-client-layout-views.d.ts +23 -1
- package/types/acorex-platform-framework-client-layout-widget-core.d.ts +148 -40
- package/types/acorex-platform-framework-client-layout-widgets.d.ts +261 -119
- package/types/acorex-platform-framework-client-realtime.d.ts +16 -3
- package/types/acorex-platform-framework-client-themes-default.d.ts +62 -11
- package/types/acorex-platform-framework-client-themes-shared.d.ts +25 -2
- package/fesm2022/acorex-platform-framework-client-common-common-settings.provider-CzBr0cRi.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-layout-components-binding-expression-editor-popup.component-Bp2cNCPO.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-layout-components-file-provider-picker.popup.component-BtLEUWAA.mjs +0 -77
- package/fesm2022/acorex-platform-framework-client-layout-components-file-provider-picker.popup.component-BtLEUWAA.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-layout-entity-acorex-platform-framework-client-layout-entity-CvaOWbJx.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-layout-widgets-button-widget-designer.component-BDOdcoyN.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-layout-widgets-page-widget-designer.component-WzIiwnGC.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-layout-widgets-text-block-widget-designer.component-D6HFPmGC.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-create-view.component-CT7WWWG0.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-modify-view.component-DienTcPE.mjs +0 -122
- package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-modify-view.component-DienTcPE.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-themes-default-entity-master-single-view.component-DBUwSzvg.mjs.map +0 -1
- package/fesm2022/acorex-platform-framework-client-themes-shared-settings.provider-CpsAIsxj.mjs.map +0 -1
|
@@ -3,10 +3,10 @@ import { CommonModule, DOCUMENT } from '@angular/common';
|
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
4
|
import { Injectable, inject, input, model, signal, computed, effect, output, viewChild, ChangeDetectionStrategy, Component, NgModule, EventEmitter, ElementRef, DestroyRef, untracked, Output } from '@angular/core';
|
|
5
5
|
import * as i4 from '@acorex-platform/framework-client/core';
|
|
6
|
-
import { AXPHookService, resolveDialogActionShortcuts, DEFAULT_CANCEL_DIALOG_ACTION_SHORTCUTS, DEFAULT_SUBMIT_DIALOG_ACTION_SHORTCUTS, AXP_SETTINGS_DEFINITION_DIALOG_OPENER, findOverlayContainerAncestor, getNestedVisibleOverlayPanes, getTopVisibleOverlayContainer, AXPExpressionEvaluatorService, AXPKeyboardShortcutRegistry, normalizeDialogActionShortcutChord, isDialogShortcutAllowedInEditableField, resolveConfiguredFooterActionShortcuts, AXPComponentSlotModule, AXPContextStore } from '@acorex-platform/framework-client/core';
|
|
6
|
+
import { AXPHookService, resolveDialogActionShortcuts, DEFAULT_CANCEL_DIALOG_ACTION_SHORTCUTS, DEFAULT_SUBMIT_DIALOG_ACTION_SHORTCUTS, AXP_SETTINGS_DEFINITION_DIALOG_OPENER, findOverlayContainerAncestor, getNestedVisibleOverlayPanes, getTopVisibleOverlayContainer, AXPExpressionEvaluatorService, AXPKeyboardShortcutRegistry, AXPDebugService, normalizeDialogActionShortcutChord, isDialogShortcutAllowedInEditableField, resolveConfiguredFooterActionShortcuts, AXPComponentSlotModule, AXPContextStore } from '@acorex-platform/framework-client/core';
|
|
7
7
|
export { DEFAULT_CANCEL_DIALOG_ACTION_SHORTCUTS, DEFAULT_SUBMIT_DIALOG_ACTION_SHORTCUTS } from '@acorex-platform/framework-client/core';
|
|
8
8
|
import { provideCommandSetups, AXPCommandService } from '@acorex-platform/framework-client/runtime';
|
|
9
|
-
import { AXP_PREVIEW_WIDGET_FIELD_COMMAND_KEY, createRandomUuid, AXPKeyboardShortcutPriority } from '@acorex-platform/framework-shared/core';
|
|
9
|
+
import { toAxpGridItemWidgetOptions, AXP_PREVIEW_WIDGET_FIELD_COMMAND_KEY, AXPWidgetsList, AXPWidgetsCatalog, createRandomUuid, AXPKeyboardShortcutPriority } from '@acorex-platform/framework-shared/core';
|
|
10
10
|
import { AXPopupService } from '@acorex/components/popup';
|
|
11
11
|
import * as i1 from '@acorex-platform/framework-client/layout/widget-core';
|
|
12
12
|
import { AXPWidgetSerializationHelper, AXPWidgetContainerComponent, AXPWidgetCoreModule, AXPWidgetRegistryService } from '@acorex-platform/framework-client/layout/widget-core';
|
|
@@ -22,6 +22,7 @@ import * as i2$1 from '@acorex/components/decorators';
|
|
|
22
22
|
import { AXDecoratorModule } from '@acorex/components/decorators';
|
|
23
23
|
import * as i3 from '@acorex/components/loading';
|
|
24
24
|
import { AXLoadingModule } from '@acorex/components/loading';
|
|
25
|
+
import { AXLoadingDialogService } from '@acorex/components/loading-dialog';
|
|
25
26
|
import { AXBasePageComponent } from '@acorex/components/page';
|
|
26
27
|
import { AXToastService } from '@acorex/components/toast';
|
|
27
28
|
import * as i6 from '@acorex/core/translation';
|
|
@@ -375,6 +376,83 @@ function normalizeDialogCloseConfirmation(value) {
|
|
|
375
376
|
}
|
|
376
377
|
//#endregion
|
|
377
378
|
|
|
379
|
+
/** Panel class on the popup overlay container; the reveal styles live in the host app. */
|
|
380
|
+
const AXP_DIALOG_REVEAL_PANEL_CLASS = 'axp-dialog-reveal';
|
|
381
|
+
/** Entry is owned by the panel class, so the overlay must not add its own fade-in. */
|
|
382
|
+
const AXP_DIALOG_REVEAL_POPUP_ANIMATION = { enter: [] };
|
|
383
|
+
/** Added once layout has settled so the reveal animation can play. */
|
|
384
|
+
const READY_CLASS = 'axp-dialog-reveal-ready';
|
|
385
|
+
/** The popup must keep the same size for this long before it counts as settled. */
|
|
386
|
+
const AXP_DIALOG_REVEAL_RESIZE_STABLE_MS = 320;
|
|
387
|
+
/** Upper bound for the stability wait (heavy entity step wizards). */
|
|
388
|
+
const RESIZE_WAIT_MAX_MS = 2800;
|
|
389
|
+
/** Resolves once the popup shell stops resizing (widgets, footer chrome, step content). */
|
|
390
|
+
function waitForDialogPopupLayoutStable(from) {
|
|
391
|
+
const popup = from.closest('.ax-popup');
|
|
392
|
+
if (!(popup instanceof HTMLElement)) {
|
|
393
|
+
return Promise.resolve();
|
|
394
|
+
}
|
|
395
|
+
return new Promise((resolve) => {
|
|
396
|
+
let quietTimer;
|
|
397
|
+
const finish = () => {
|
|
398
|
+
clearTimeout(quietTimer);
|
|
399
|
+
clearTimeout(maxWaitTimer);
|
|
400
|
+
observer.disconnect();
|
|
401
|
+
resolve();
|
|
402
|
+
};
|
|
403
|
+
const restartQuietWindow = () => {
|
|
404
|
+
clearTimeout(quietTimer);
|
|
405
|
+
quietTimer = setTimeout(finish, AXP_DIALOG_REVEAL_RESIZE_STABLE_MS);
|
|
406
|
+
};
|
|
407
|
+
const maxWaitTimer = setTimeout(finish, RESIZE_WAIT_MAX_MS);
|
|
408
|
+
const observer = new ResizeObserver(restartQuietWindow);
|
|
409
|
+
observer.observe(popup);
|
|
410
|
+
restartQuietWindow();
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
const FIRST_FOCUSABLE_FIELD_SELECTOR = [
|
|
414
|
+
'input:not([type="hidden"]):not([type="button"]):not([type="submit"]):not([type="reset"]):not([disabled]):not([tabindex="-1"])',
|
|
415
|
+
'textarea:not([disabled]):not([tabindex="-1"])',
|
|
416
|
+
'select:not([disabled]):not([tabindex="-1"])',
|
|
417
|
+
'[contenteditable="true"]:not([tabindex="-1"])',
|
|
418
|
+
].join(', ');
|
|
419
|
+
/** Reveals the popup shell that the panel class kept hidden, then focuses the first field. */
|
|
420
|
+
function markDialogRevealReady(from) {
|
|
421
|
+
from.closest(`.${AXP_DIALOG_REVEAL_PANEL_CLASS}`)?.classList.add(READY_CLASS);
|
|
422
|
+
focusFirstPopupField(from);
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Focuses the first visible field in the popup body.
|
|
426
|
+
* Runs after reveal: the shell focus from the popup library happens while the overlay is still hidden.
|
|
427
|
+
*/
|
|
428
|
+
function focusFirstPopupField(from) {
|
|
429
|
+
const popup = from.closest('.ax-popup');
|
|
430
|
+
if (!(popup instanceof HTMLElement)) {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
setTimeout(() => {
|
|
434
|
+
const body = popup.querySelector('.ax-popup-body-container');
|
|
435
|
+
if (!(body instanceof HTMLElement)) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
const field = Array.from(body.querySelectorAll(FIRST_FOCUSABLE_FIELD_SELECTOR)).find(isVisibleFocusTarget);
|
|
439
|
+
(field ?? popup).focus();
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
function isVisibleFocusTarget(element) {
|
|
443
|
+
if (element.closest('[hidden], [aria-hidden="true"]')) {
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
if (element.getAttribute('aria-disabled') === 'true') {
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
const style = getComputedStyle(element);
|
|
450
|
+
if (style.display === 'none' || style.visibility === 'hidden' || Number(style.opacity) === 0) {
|
|
451
|
+
return false;
|
|
452
|
+
}
|
|
453
|
+
return element.getClientRects().length > 0;
|
|
454
|
+
}
|
|
455
|
+
|
|
378
456
|
//#region ---- Inheritance Utilities ----
|
|
379
457
|
/**
|
|
380
458
|
* Resolves inherited properties from context and local values
|
|
@@ -803,29 +881,8 @@ class LayoutContainerMixin extends BaseContainerMixin {
|
|
|
803
881
|
if (typeof value === 'number') {
|
|
804
882
|
this.containerState.options.colSpan = value;
|
|
805
883
|
}
|
|
806
|
-
else if (value) {
|
|
807
|
-
|
|
808
|
-
if (positions) {
|
|
809
|
-
const placement = positions?.lg ?? positions?.xl ?? positions?.xxl ?? positions?.md ?? positions?.sm;
|
|
810
|
-
if (placement) {
|
|
811
|
-
const opts = this.containerState.options;
|
|
812
|
-
if (placement.colSpan != null)
|
|
813
|
-
opts.colSpan = placement.colSpan;
|
|
814
|
-
if (placement.colStart != null)
|
|
815
|
-
opts.colStart = placement.colStart;
|
|
816
|
-
if (placement.colEnd != null)
|
|
817
|
-
opts.colEnd = placement.colEnd;
|
|
818
|
-
if (placement.rowSpan != null)
|
|
819
|
-
opts.rowSpan = placement.rowSpan;
|
|
820
|
-
if (placement.rowStart != null)
|
|
821
|
-
opts.rowStart = placement.rowStart;
|
|
822
|
-
if (placement.rowEnd != null)
|
|
823
|
-
opts.rowEnd = placement.rowEnd;
|
|
824
|
-
const layoutOrder = value.order ?? placement.order;
|
|
825
|
-
if (layoutOrder != null)
|
|
826
|
-
opts.order = layoutOrder;
|
|
827
|
-
}
|
|
828
|
-
}
|
|
884
|
+
else if (value?.positions) {
|
|
885
|
+
Object.assign(this.containerState.options, toAxpGridItemWidgetOptions(value));
|
|
829
886
|
}
|
|
830
887
|
return this;
|
|
831
888
|
}
|
|
@@ -1121,31 +1178,7 @@ function mergeAXPGridContainerOptions(prev, patch) {
|
|
|
1121
1178
|
return next;
|
|
1122
1179
|
}
|
|
1123
1180
|
function toGridItemOptions(layoutOptions) {
|
|
1124
|
-
|
|
1125
|
-
return { colSpan: 12 };
|
|
1126
|
-
const positions = layoutOptions.positions;
|
|
1127
|
-
const placement = positions['lg'] ?? positions['xl'] ?? positions['xxl'] ?? positions['md'] ?? positions['sm'];
|
|
1128
|
-
if (!placement)
|
|
1129
|
-
return { colSpan: 12 };
|
|
1130
|
-
const opts = {};
|
|
1131
|
-
if (placement['colSpan'] != null)
|
|
1132
|
-
opts['colSpan'] = placement['colSpan'];
|
|
1133
|
-
if (placement['colStart'] != null)
|
|
1134
|
-
opts['colStart'] = placement['colStart'];
|
|
1135
|
-
if (placement['colEnd'] != null)
|
|
1136
|
-
opts['colEnd'] = placement['colEnd'];
|
|
1137
|
-
if (placement['rowSpan'] != null)
|
|
1138
|
-
opts['rowSpan'] = placement['rowSpan'];
|
|
1139
|
-
if (placement['rowStart'] != null)
|
|
1140
|
-
opts['rowStart'] = placement['rowStart'];
|
|
1141
|
-
if (placement['rowEnd'] != null)
|
|
1142
|
-
opts['rowEnd'] = placement['rowEnd'];
|
|
1143
|
-
const layoutOrder = layoutOptions.order ?? placement['order'];
|
|
1144
|
-
if (layoutOrder != null)
|
|
1145
|
-
opts['order'] = layoutOrder;
|
|
1146
|
-
if (Object.keys(opts).length === 0)
|
|
1147
|
-
opts['colSpan'] = 12;
|
|
1148
|
-
return opts;
|
|
1181
|
+
return toAxpGridItemWidgetOptions(layoutOptions);
|
|
1149
1182
|
}
|
|
1150
1183
|
class GridContainerBuilder extends WidgetContainerMixin {
|
|
1151
1184
|
constructor() {
|
|
@@ -1640,6 +1673,9 @@ class DialogContainerBuilder {
|
|
|
1640
1673
|
setCloseButton(closeButton) {
|
|
1641
1674
|
return this.setOptions({ closeButton });
|
|
1642
1675
|
}
|
|
1676
|
+
setDraggable(draggable) {
|
|
1677
|
+
return this.setOptions({ draggable });
|
|
1678
|
+
}
|
|
1643
1679
|
setContext(context) {
|
|
1644
1680
|
return this.setOptions({ context });
|
|
1645
1681
|
}
|
|
@@ -1761,7 +1797,9 @@ class DialogContainerBuilder {
|
|
|
1761
1797
|
size: this.dialogState.dialogOptions?.size || 'md',
|
|
1762
1798
|
closeButton: this.dialogState.dialogOptions?.closeButton || false,
|
|
1763
1799
|
closeOnBackdropClick: false,
|
|
1764
|
-
draggable:
|
|
1800
|
+
draggable: this.dialogState.dialogOptions?.draggable ?? true,
|
|
1801
|
+
panelClass: [AXP_DIALOG_REVEAL_PANEL_CLASS],
|
|
1802
|
+
animation: AXP_DIALOG_REVEAL_POPUP_ANIMATION,
|
|
1765
1803
|
data: {
|
|
1766
1804
|
config: dialogConfig,
|
|
1767
1805
|
callBack: (result) => {
|
|
@@ -2634,8 +2672,121 @@ function shouldDeferDialogShortcutsToNestedOverlay(document, dialogHost, overlay
|
|
|
2634
2672
|
return shouldDeferEscapeToNestedOverlayFromDom(document, dialogHost);
|
|
2635
2673
|
}
|
|
2636
2674
|
|
|
2675
|
+
//#endregion
|
|
2676
|
+
//#region ----- Constants -----
|
|
2677
|
+
const SHELL_MS = 90;
|
|
2678
|
+
const MIN_MS = 80;
|
|
2679
|
+
const MAX_MS = 5000;
|
|
2680
|
+
const FIELD_MS = 7;
|
|
2681
|
+
const HEAVY_MS = 70;
|
|
2682
|
+
const CONTAINER_MS = 2;
|
|
2683
|
+
const TAB_MS = 3;
|
|
2684
|
+
const NETWORK_FIRST_MS = 200;
|
|
2685
|
+
const NETWORK_EACH_MS = 20;
|
|
2686
|
+
/** Widgets that fetch on init without a `dataSource` option. */
|
|
2687
|
+
const FETCH_WITHOUT_DATASOURCE = new Set([
|
|
2688
|
+
AXPWidgetsList.Editors.LookupBox,
|
|
2689
|
+
AXPWidgetsList.Editors.TagEditor,
|
|
2690
|
+
AXPWidgetsList.Editors.EntityCategory,
|
|
2691
|
+
AXPWidgetsList.Editors.MultiSourceSelector,
|
|
2692
|
+
AXPWidgetsList.Entity.EntityList,
|
|
2693
|
+
]);
|
|
2694
|
+
/** Widgets that are expensive to instantiate even with no network. */
|
|
2695
|
+
const HEAVY_RENDER = new Set([
|
|
2696
|
+
AXPWidgetsList.Editors.RichText,
|
|
2697
|
+
AXPWidgetsList.Advanced.CodeEditor,
|
|
2698
|
+
AXPWidgetsList.Editors.InlineDataTable,
|
|
2699
|
+
AXPWidgetsList.Editors.SignatureBox,
|
|
2700
|
+
AXPWidgetsList.Editors.MapBox,
|
|
2701
|
+
AXPWidgetsList.Editors.FileViewer,
|
|
2702
|
+
AXPWidgetsList.Editors.QueryBuilder,
|
|
2703
|
+
AXPWidgetsList.Editors.SkillMatrix,
|
|
2704
|
+
AXPWidgetsList.Editors.ResponsibilitiesMatrix,
|
|
2705
|
+
AXPWidgetsList.Editors.RiskMatrix,
|
|
2706
|
+
AXPWidgetsList.Editors.ControlHierarchyDiagram,
|
|
2707
|
+
]);
|
|
2708
|
+
//#endregion
|
|
2709
|
+
//#region ----- Estimate -----
|
|
2710
|
+
/**
|
|
2711
|
+
* Static walk of a dialog widget tree. Predicts how long the first paint will take
|
|
2712
|
+
* so the renderer can decide whether to show the loading dialog immediately.
|
|
2713
|
+
*/
|
|
2714
|
+
function estimateDialogRenderMs(node, kindOf) {
|
|
2715
|
+
const acc = { structural: 0, dataBound: 0 };
|
|
2716
|
+
walk(node, acc, kindOf);
|
|
2717
|
+
const network = acc.dataBound === 0 ? 0 : NETWORK_FIRST_MS + (acc.dataBound - 1) * NETWORK_EACH_MS;
|
|
2718
|
+
const raw = SHELL_MS + acc.structural + network;
|
|
2719
|
+
return Math.min(MAX_MS, Math.max(MIN_MS, raw));
|
|
2720
|
+
}
|
|
2721
|
+
//#endregion
|
|
2722
|
+
//#region ----- Walk -----
|
|
2723
|
+
function walk(node, acc, kindOf) {
|
|
2724
|
+
if (!node || node.visible === false) {
|
|
2725
|
+
return;
|
|
2726
|
+
}
|
|
2727
|
+
if (node.type === AXPWidgetsList.Layouts.TabsetLayout) {
|
|
2728
|
+
chargeNode(node, acc, kindOf);
|
|
2729
|
+
const children = node.children ?? [];
|
|
2730
|
+
if (children.length > 0) {
|
|
2731
|
+
walk(children[0], acc, kindOf);
|
|
2732
|
+
}
|
|
2733
|
+
for (let index = 1; index < children.length; index++) {
|
|
2734
|
+
acc.structural += TAB_MS;
|
|
2735
|
+
}
|
|
2736
|
+
return;
|
|
2737
|
+
}
|
|
2738
|
+
if (node.type === AXPWidgetsCatalog.stepWizard) {
|
|
2739
|
+
chargeNode(node, acc, kindOf);
|
|
2740
|
+
for (const content of wizardStepContents(node)) {
|
|
2741
|
+
walk(content, acc, kindOf);
|
|
2742
|
+
}
|
|
2743
|
+
for (const child of node.children ?? []) {
|
|
2744
|
+
walk(child, acc, kindOf);
|
|
2745
|
+
}
|
|
2746
|
+
return;
|
|
2747
|
+
}
|
|
2748
|
+
chargeNode(node, acc, kindOf);
|
|
2749
|
+
for (const child of node.children ?? []) {
|
|
2750
|
+
walk(child, acc, kindOf);
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2753
|
+
function chargeNode(node, acc, kindOf) {
|
|
2754
|
+
if (HEAVY_RENDER.has(node.type)) {
|
|
2755
|
+
acc.structural += HEAVY_MS;
|
|
2756
|
+
}
|
|
2757
|
+
else if (kindOf(node.type) === 'container') {
|
|
2758
|
+
acc.structural += CONTAINER_MS;
|
|
2759
|
+
}
|
|
2760
|
+
else {
|
|
2761
|
+
acc.structural += FIELD_MS;
|
|
2762
|
+
}
|
|
2763
|
+
if (isNetworkBound(node)) {
|
|
2764
|
+
acc.dataBound += 1;
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
function isNetworkBound(node) {
|
|
2768
|
+
if (FETCH_WITHOUT_DATASOURCE.has(node.type)) {
|
|
2769
|
+
return true;
|
|
2770
|
+
}
|
|
2771
|
+
const raw = node.options?.['dataSource'];
|
|
2772
|
+
return typeof raw === 'string' || (!!raw && typeof raw === 'object' && 'id' in raw);
|
|
2773
|
+
}
|
|
2774
|
+
function wizardStepContents(node) {
|
|
2775
|
+
const steps = node.options
|
|
2776
|
+
?.definition?.steps;
|
|
2777
|
+
if (!Array.isArray(steps)) {
|
|
2778
|
+
return [];
|
|
2779
|
+
}
|
|
2780
|
+
return steps.flatMap((step) => (step.content ? [step.content] : []));
|
|
2781
|
+
}
|
|
2782
|
+
//#endregion
|
|
2783
|
+
|
|
2637
2784
|
/** Debounce after widget count stabilizes before re-evaluating footer actions. */
|
|
2638
2785
|
const DIALOG_WIDGET_COUNT_STABLE_DELAY_MS = 350;
|
|
2786
|
+
/** Safety net so the dialog is never left hidden when the widget debounce does not fire. */
|
|
2787
|
+
const DIALOG_REVEAL_FALLBACK_MS = 3500;
|
|
2788
|
+
/** Fast dialogs skip the loading overlay unless they overrun this wait. */
|
|
2789
|
+
const LOADING_DIALOG_SHOW_THRESHOLD_MS = 400;
|
|
2639
2790
|
class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
2640
2791
|
constructor() {
|
|
2641
2792
|
super(...arguments);
|
|
@@ -2650,12 +2801,21 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2650
2801
|
this.document = inject(DOCUMENT);
|
|
2651
2802
|
this.host = inject((ElementRef));
|
|
2652
2803
|
this.shortcutRegistry = inject(AXPKeyboardShortcutRegistry);
|
|
2804
|
+
this.loadingDialog = inject(AXLoadingDialogService);
|
|
2805
|
+
this.widgetRegistry = inject(AXPWidgetRegistryService);
|
|
2806
|
+
this.debug = inject(AXPDebugService);
|
|
2653
2807
|
/** Ensures `show()` resolves once when the dialog closes (footer action or header close). */
|
|
2654
2808
|
this.callbackInvoked = false;
|
|
2655
2809
|
/** Skips dirty confirmation on the next {@link onClosing} (successful submit / programmatic close). */
|
|
2656
2810
|
this.skipNextOnClosingDirtyCheck = false;
|
|
2657
2811
|
/** Blocks re-entrant {@link onClosing} while the first close gate is still resolving. */
|
|
2658
2812
|
this.closeGateInProgress = false;
|
|
2813
|
+
/** Predicted first-paint cost from the widget tree, used for the loading decision and debug trace. */
|
|
2814
|
+
this.estimatedRenderMs = 0;
|
|
2815
|
+
/** `performance.now()` at `ngOnInit`, for estimated-vs-measured traces. */
|
|
2816
|
+
this.revealStartedAt = 0;
|
|
2817
|
+
/** Keeps the reveal pipeline to a single run (widget debounce and fallback timer both call it). */
|
|
2818
|
+
this.revealRequested = false;
|
|
2659
2819
|
this.destroyRef = inject(DestroyRef);
|
|
2660
2820
|
this.context = signal({}, /* @ts-ignore */
|
|
2661
2821
|
...(ngDevMode ? [{ debugName: "context" }] : /* istanbul ignore next */ []));
|
|
@@ -2668,6 +2828,8 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2668
2828
|
...(ngDevMode ? [{ debugName: "footerPrefix" }] : /* istanbul ignore next */ []));
|
|
2669
2829
|
this.footerSuffix = signal([], /* @ts-ignore */
|
|
2670
2830
|
...(ngDevMode ? [{ debugName: "footerSuffix" }] : /* istanbul ignore next */ []));
|
|
2831
|
+
this.hasDefaultFooterActions = computed(() => this.footerPrefix().length > 0 || this.footerSuffix().length > 0, /* @ts-ignore */
|
|
2832
|
+
...(ngDevMode ? [{ debugName: "hasDefaultFooterActions" }] : /* istanbul ignore next */ []));
|
|
2671
2833
|
/**
|
|
2672
2834
|
* Correlate layout context snapshots for distributed hooks (`layout-builder.dialog.context-changed`).
|
|
2673
2835
|
*/
|
|
@@ -2694,14 +2856,16 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2694
2856
|
const renderer = this.layoutRenderer();
|
|
2695
2857
|
const container = renderer?.getContainer();
|
|
2696
2858
|
this.widgetCoreService = container?.builderService ?? null;
|
|
2697
|
-
return;
|
|
2698
2859
|
}
|
|
2699
|
-
|
|
2860
|
+
else {
|
|
2861
|
+
this.widgetCoreService.registeredWidgetsCount();
|
|
2862
|
+
}
|
|
2700
2863
|
if (this.debounceTimer) {
|
|
2701
2864
|
clearTimeout(this.debounceTimer);
|
|
2702
2865
|
}
|
|
2703
|
-
this.debounceTimer = setTimeout(() => {
|
|
2704
|
-
this.aggregateAndEvaluateActions();
|
|
2866
|
+
this.debounceTimer = setTimeout(async () => {
|
|
2867
|
+
await this.aggregateAndEvaluateActions();
|
|
2868
|
+
void this.revealWhenLayoutStable();
|
|
2705
2869
|
}, DIALOG_WIDGET_COUNT_STABLE_DELAY_MS);
|
|
2706
2870
|
}, /* @ts-ignore */
|
|
2707
2871
|
...(ngDevMode ? [{ debugName: "#widgetActionsEffect" }] : /* istanbul ignore next */ []));
|
|
@@ -2716,9 +2880,24 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
2716
2880
|
//#endregion
|
|
2717
2881
|
//#region ---- Lifecycle ----
|
|
2718
2882
|
ngOnInit() {
|
|
2883
|
+
this.revealStartedAt = performance.now();
|
|
2884
|
+
this.estimatedRenderMs = estimateDialogRenderMs(this.config?.definition, (type) => this.widgetRegistry.getOptional(type)?.type);
|
|
2885
|
+
if (this.estimatedRenderMs >= LOADING_DIALOG_SHOW_THRESHOLD_MS) {
|
|
2886
|
+
this.revealLoading = this.openRevealLoading();
|
|
2887
|
+
}
|
|
2888
|
+
else {
|
|
2889
|
+
this.revealLoadingTimer = setTimeout(() => {
|
|
2890
|
+
this.revealLoading = this.openRevealLoading();
|
|
2891
|
+
}, LOADING_DIALOG_SHOW_THRESHOLD_MS);
|
|
2892
|
+
}
|
|
2719
2893
|
const initialContext = this.config?.context || {};
|
|
2720
2894
|
this.context.set(initialContext);
|
|
2721
2895
|
void this.invokeLayoutContextChangedHooks();
|
|
2896
|
+
const revealFallbackTimer = setTimeout(() => void this.revealWhenLayoutStable(), DIALOG_REVEAL_FALLBACK_MS);
|
|
2897
|
+
this.destroyRef.onDestroy(() => {
|
|
2898
|
+
clearTimeout(revealFallbackTimer);
|
|
2899
|
+
void this.closeRevealLoading();
|
|
2900
|
+
});
|
|
2722
2901
|
}
|
|
2723
2902
|
//#endregion
|
|
2724
2903
|
//#region ---- Keyboard Shortcuts ----
|
|
@@ -3196,6 +3375,61 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
3196
3375
|
const suffix = visible.filter((a) => (a.zone ?? 'footer') === 'footer' && (a.placement ?? 'suffix') === 'suffix');
|
|
3197
3376
|
this.footerPrefix.set(prefix);
|
|
3198
3377
|
this.footerSuffix.set(suffix);
|
|
3378
|
+
if (prefix.length > 0 || suffix.length > 0) {
|
|
3379
|
+
this.scheduleRelocateFooterToPopupChrome();
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
/**
|
|
3383
|
+
* AX popup lifts `:scope > ax-footer` once after first paint. Widget footer
|
|
3384
|
+
* actions can appear later — move the footer into chrome when it is created.
|
|
3385
|
+
*/
|
|
3386
|
+
scheduleRelocateFooterToPopupChrome() {
|
|
3387
|
+
setTimeout(() => {
|
|
3388
|
+
const host = this.host.nativeElement;
|
|
3389
|
+
const footer = host.querySelector(':scope > ax-footer');
|
|
3390
|
+
if (!(footer instanceof HTMLElement)) {
|
|
3391
|
+
return;
|
|
3392
|
+
}
|
|
3393
|
+
const popFooter = host.closest('.ax-popup')?.querySelector('.ax-popup-footer-container');
|
|
3394
|
+
if (!(popFooter instanceof HTMLElement) || popFooter.contains(footer)) {
|
|
3395
|
+
return;
|
|
3396
|
+
}
|
|
3397
|
+
popFooter.append(footer);
|
|
3398
|
+
});
|
|
3399
|
+
}
|
|
3400
|
+
/**
|
|
3401
|
+
* The popup box is on screen before widgets and footer chrome are laid out, so it is held
|
|
3402
|
+
* hidden by the reveal panel class. Wait for the shell to stop resizing, then fade it in.
|
|
3403
|
+
*/
|
|
3404
|
+
async revealWhenLayoutStable() {
|
|
3405
|
+
if (this.revealRequested) {
|
|
3406
|
+
return;
|
|
3407
|
+
}
|
|
3408
|
+
this.revealRequested = true;
|
|
3409
|
+
await waitForDialogPopupLayoutStable(this.host.nativeElement);
|
|
3410
|
+
await this.closeRevealLoading();
|
|
3411
|
+
markDialogRevealReady(this.host.nativeElement);
|
|
3412
|
+
const elapsed = performance.now() - this.revealStartedAt;
|
|
3413
|
+
this.debug.trace('dialog-reveal', 'render-cost', 'estimated vs measured', () => ({
|
|
3414
|
+
estimatedMs: this.estimatedRenderMs,
|
|
3415
|
+
measuredMs: Math.round(elapsed - AXP_DIALOG_REVEAL_RESIZE_STABLE_MS),
|
|
3416
|
+
loadingShown: !!this.revealLoading,
|
|
3417
|
+
}));
|
|
3418
|
+
}
|
|
3419
|
+
async openRevealLoading() {
|
|
3420
|
+
const [title, text] = await Promise.all([
|
|
3421
|
+
this.translationService.translateAsync('@general:state-message.loading.title'),
|
|
3422
|
+
this.translationService.translateAsync('@general:state-message.loading.description'),
|
|
3423
|
+
]);
|
|
3424
|
+
return this.loadingDialog.show({ title, text, mode: 'indeterminate' });
|
|
3425
|
+
}
|
|
3426
|
+
async closeRevealLoading() {
|
|
3427
|
+
if (this.revealLoadingTimer) {
|
|
3428
|
+
clearTimeout(this.revealLoadingTimer);
|
|
3429
|
+
this.revealLoadingTimer = undefined;
|
|
3430
|
+
}
|
|
3431
|
+
const ref = await this.revealLoading?.catch(() => undefined);
|
|
3432
|
+
ref?.close();
|
|
3199
3433
|
}
|
|
3200
3434
|
async collectWidgetActions() {
|
|
3201
3435
|
if (!this.widgetCoreService)
|
|
@@ -3323,9 +3557,10 @@ class AXPDialogRendererComponent extends AXBasePageComponent {
|
|
|
3323
3557
|
</axp-layout-renderer>
|
|
3324
3558
|
</div>
|
|
3325
3559
|
|
|
3326
|
-
<!-- Custom footer slot: if it has content, default footer is
|
|
3560
|
+
<!-- Custom footer slot: if it has content, default footer is not rendered -->
|
|
3327
3561
|
<axp-component-slot name="dialog-footer" #footerSlot="slot" [context]="context()"></axp-component-slot>
|
|
3328
|
-
|
|
3562
|
+
<!-- ----- Default popup footer only when there is at least one action ----- -->
|
|
3563
|
+
@if (footerSlot.isEmpty() && hasDefaultFooterActions()) {
|
|
3329
3564
|
<ax-footer>
|
|
3330
3565
|
<ax-prefix>
|
|
3331
3566
|
<axp-component-slot name="dialog-footer-prefix" [context]="context()"></axp-component-slot>
|
|
@@ -3406,9 +3641,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.8", ngImpor
|
|
|
3406
3641
|
</axp-layout-renderer>
|
|
3407
3642
|
</div>
|
|
3408
3643
|
|
|
3409
|
-
<!-- Custom footer slot: if it has content, default footer is
|
|
3644
|
+
<!-- Custom footer slot: if it has content, default footer is not rendered -->
|
|
3410
3645
|
<axp-component-slot name="dialog-footer" #footerSlot="slot" [context]="context()"></axp-component-slot>
|
|
3411
|
-
|
|
3646
|
+
<!-- ----- Default popup footer only when there is at least one action ----- -->
|
|
3647
|
+
@if (footerSlot.isEmpty() && hasDefaultFooterActions()) {
|
|
3412
3648
|
<ax-footer>
|
|
3413
3649
|
<ax-prefix>
|
|
3414
3650
|
<axp-component-slot name="dialog-footer-prefix" [context]="context()"></axp-component-slot>
|