@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,29 +1,21 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, Injectable, Directive, computed, Injector, ChangeDetectionStrategy, Component, input, ElementRef, ViewContainerRef, signal, effect, runInInjectionContext, Optional, Inject, NgModule, EventEmitter, HostListener, Output, provideAppInitializer, Pipe } from '@angular/core';
2
+ import { InjectionToken, inject, Injectable, Directive, computed, Injector, ChangeDetectionStrategy, Component, input, ElementRef, ViewContainerRef, signal, effect, runInInjectionContext, Optional, Inject, NgModule, EventEmitter, HostListener, Output, DestroyRef, untracked, provideAppInitializer, Pipe } from '@angular/core';
3
3
  import { AXTranslationService, resolveMultiLanguageString } from '@acorex/core/translation';
4
4
  import { AXDataSource } from '@acorex/cdk/common';
5
- import { isPlainObject, get, set, isNil, isEmpty, isArray, merge, isObjectLike, transform, isEqual, differenceWith, union, cloneDeep, has, sortBy, isUndefined, endsWith, startsWith, includes, lte, gte, lt, gt, orderBy } from 'lodash-es';
5
+ import { AXPPlatformCoreDataSourceKeys, AXPSystemActionType } from '@acorex/platform/contracts';
6
+ import { isPlainObject, get, set, isNil, isEmpty, isArray, merge, isObjectLike, transform, isEqual, differenceWith, union, cloneDeep, has, unset, sortBy, isUndefined, endsWith, startsWith, includes, lte, gte, lt, gt, orderBy } from 'lodash-es';
6
7
  import { signalStore, withState, withComputed, withMethods, patchState } from '@ngrx/signals';
8
+ import { AXFormatService } from '@acorex/core/format';
7
9
  import * as i1 from '@acorex/components/skeleton';
8
10
  import { AXSkeletonModule } from '@acorex/components/skeleton';
9
11
  import { Subject, interval, fromEvent } from 'rxjs';
10
12
  import { toSignal } from '@angular/core/rxjs-interop';
11
13
  import { DOCUMENT } from '@angular/common';
12
14
  import { AXLocaleService } from '@acorex/core/locale';
15
+ import { delay } from '@acorex/core/utils';
13
16
  import { AXCalendarService } from '@acorex/core/date-time';
14
17
  import { startWith, map, debounceTime } from 'rxjs/operators';
15
18
 
16
- //#region ---- Datasource keys ----
17
- /** Platform core datasource registry names. */
18
- const AXPPlatformCoreDataSourceKeys = {
19
- ModuleManifests: 'platform-module-manifests',
20
- CatalogScopes: 'platform-catalog-scopes',
21
- };
22
- //#endregion
23
-
24
- //#region ---- Contracts barrel ----
25
- //#endregion
26
-
27
19
  const AXP_ACTIVITY_LOG_PROVIDER = new InjectionToken('AXP_ACTIVITY_LOGS_PROVIDER');
28
20
  class AXPActivityLogService {
29
21
  constructor() {
@@ -715,6 +707,104 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
715
707
  type: Directive
716
708
  }] });
717
709
 
710
+ //#region ---- Imports ----
711
+ //#endregion
712
+ //#region ---- Debug Service ----
713
+ /**
714
+ * Platform debug and trace logging.
715
+ * Call `trace` / `traceGroup` for diagnostics (gated by tenant Debug Mode).
716
+ * Call `warn` / `error` for always-on console output.
717
+ *
718
+ * Debug Mode is applied via `setDebugMode()` (wired from settings in platform common).
719
+ */
720
+ class AXPDebugService {
721
+ //#endregion
722
+ //#region ---- Lifecycle ----
723
+ constructor() {
724
+ //#endregion
725
+ //#region ---- State ----
726
+ this.debugModeEnabled = false;
727
+ AXPDebugService.instance = this;
728
+ }
729
+ //#endregion
730
+ //#region ---- Debug Mode ----
731
+ isDebugMode() {
732
+ return this.debugModeEnabled;
733
+ }
734
+ setDebugMode(enabled) {
735
+ this.debugModeEnabled = enabled;
736
+ }
737
+ //#endregion
738
+ //#region ---- Trace API ----
739
+ /**
740
+ * @param namespace Module/feature namespace (kebab-case). Filter console with `[AXP:<namespace>]`.
741
+ * @param title Short area name (component, service, middleware).
742
+ * @param message What happened.
743
+ * @param data Optional structured payload, or a factory evaluated only when Debug Mode is on.
744
+ */
745
+ trace(namespace, title, message, data) {
746
+ if (!this.debugModeEnabled) {
747
+ return;
748
+ }
749
+ this.log('log', namespace, title, message, data);
750
+ }
751
+ /**
752
+ * Same as {@link trace} but emits a collapsible console group (useful for multi-field payloads).
753
+ */
754
+ traceGroup(namespace, title, message, data) {
755
+ if (!this.debugModeEnabled) {
756
+ return;
757
+ }
758
+ const resolved = this.resolveData(data);
759
+ console.groupCollapsed(this.formatPrefix(namespace, title, message));
760
+ if (resolved != null) {
761
+ for (const [key, value] of Object.entries(resolved)) {
762
+ if (value !== undefined) {
763
+ console.log(key, value);
764
+ }
765
+ }
766
+ }
767
+ console.groupEnd();
768
+ }
769
+ warn(namespace, title, message, data) {
770
+ this.log('warn', namespace, title, message, data);
771
+ }
772
+ error(namespace, title, message, data) {
773
+ this.log('error', namespace, title, message, data);
774
+ }
775
+ //#endregion
776
+ //#region ---- Utility Methods ----
777
+ formatPrefix(namespace, title, message) {
778
+ return `[AXP:${namespace}] ${title} — ${message}`;
779
+ }
780
+ resolveData(data) {
781
+ if (data == null) {
782
+ return undefined;
783
+ }
784
+ return typeof data === 'function' ? data() : data;
785
+ }
786
+ log(level, namespace, title, message, data) {
787
+ const prefix = this.formatPrefix(namespace, title, message);
788
+ const resolved = this.resolveData(data);
789
+ if (resolved != null) {
790
+ console[level](prefix, resolved);
791
+ return;
792
+ }
793
+ console[level](prefix);
794
+ }
795
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDebugService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
796
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDebugService, providedIn: 'root' }); }
797
+ }
798
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPDebugService, decorators: [{
799
+ type: Injectable,
800
+ args: [{
801
+ providedIn: 'root',
802
+ }]
803
+ }], ctorParameters: () => [] });
804
+
805
+ /** Trace namespace for form dirty-state diagnostics (lookup/select revert, discard, baseline). */
806
+ const FORM_DIRTY_TRACE_NS = 'FORM STATE';
807
+
718
808
  //#region ---- Locale map shape ----
719
809
  /** Per-locale string map (`{ 'en-US': '...', 'fa-IR': '...' }`). */
720
810
  function isLocaleStringMap(value) {
@@ -1037,6 +1127,13 @@ function normalizeLocaleStringMap(value) {
1037
1127
  }
1038
1128
  return Object.keys(normalized).length === 0 ? null : normalized;
1039
1129
  }
1130
+ function compareMultiLanguageLocaleStrings(a, b) {
1131
+ const htmlEqual = compareHtmlFormValues(a, b);
1132
+ if (htmlEqual !== null) {
1133
+ return htmlEqual;
1134
+ }
1135
+ return a === b;
1136
+ }
1040
1137
  /**
1041
1138
  * Compares multi-language field values (plain string vs locale map) for dirty/baseline checks.
1042
1139
  * Returns `null` when either side is not a multi-language shape (caller uses generic normalization).
@@ -1067,15 +1164,13 @@ function compareMultiLanguageFormValues(a, b) {
1067
1164
  return false;
1068
1165
  }
1069
1166
  if (typeof left === 'string' && typeof right === 'string') {
1070
- return left === right;
1167
+ return compareMultiLanguageLocaleStrings(left, right);
1071
1168
  }
1072
1169
  if (typeof left === 'string' && typeof right === 'object') {
1073
- const values = Object.values(right);
1074
- return values.length > 0 && values.every((text) => text === left);
1170
+ return Object.values(right).some((text) => typeof text === 'string' && compareMultiLanguageLocaleStrings(left, text));
1075
1171
  }
1076
1172
  if (typeof right === 'string' && typeof left === 'object') {
1077
- const values = Object.values(left);
1078
- return values.length > 0 && values.every((text) => text === right);
1173
+ return Object.values(left).some((text) => typeof text === 'string' && compareMultiLanguageLocaleStrings(right, text));
1079
1174
  }
1080
1175
  if (typeof left !== 'object' || typeof right !== 'object') {
1081
1176
  return false;
@@ -1087,7 +1182,56 @@ function compareMultiLanguageFormValues(a, b) {
1087
1182
  if (leftKeys.length !== rightKeys.length) {
1088
1183
  return false;
1089
1184
  }
1090
- return leftKeys.every((key) => leftMap[key] === rightMap[key]);
1185
+ return leftKeys.every((key) => compareMultiLanguageLocaleStrings(leftMap[key], rightMap[key]));
1186
+ }
1187
+ /**
1188
+ * Lookup/select expose may write a resolved MLS string while the baseline keeps the locale map.
1189
+ * Restore baseline shape when the string matches any locale entry.
1190
+ */
1191
+ function shouldRestoreSavedMultiLanguageShape(value, savedAtPath) {
1192
+ if (typeof value !== 'string' || !isLocaleStringMap(savedAtPath)) {
1193
+ return false;
1194
+ }
1195
+ const trimmed = value.trim();
1196
+ if (trimmed === '') {
1197
+ return false;
1198
+ }
1199
+ return Object.values(savedAtPath).some((entry) => typeof entry === 'string' && entry.trim() === trimmed);
1200
+ }
1201
+ /** Decode common HTML entities for rich-text baseline compare. */
1202
+ function decodeHtmlEntities(value) {
1203
+ return value
1204
+ .replace(/&#x([0-9a-f]+);/gi, (_, hex) => String.fromCharCode(parseInt(hex, 16)))
1205
+ .replace(/&#(\d+);/g, (_, dec) => String.fromCharCode(parseInt(dec, 10)))
1206
+ .replace(/&amp;/g, '&')
1207
+ .replace(/&lt;/g, '<')
1208
+ .replace(/&gt;/g, '>')
1209
+ .replace(/&quot;/g, '"');
1210
+ }
1211
+ function looksLikeHtmlFormValue(value) {
1212
+ return /<[a-z][\s\S]*>/i.test(value.trim());
1213
+ }
1214
+ /** Normalizes rich-text HTML for semantic dirty/baseline compare (e.g. Quill `p`/`br` drift). */
1215
+ function normalizeRichTextHtmlForCompare(value) {
1216
+ const html = decodeHtmlEntities(value.trim());
1217
+ if (typeof document === 'undefined') {
1218
+ return html.replace(/\s+/g, ' ').trim();
1219
+ }
1220
+ const doc = new DOMParser().parseFromString(html, 'text/html');
1221
+ return (doc.body.textContent ?? '').replace(/\s+/g, ' ').trim();
1222
+ }
1223
+ /**
1224
+ * Compares rich-text HTML strings by visible text (ignores `p` vs `br` structure drift).
1225
+ * Returns `null` when either side is not HTML markup.
1226
+ */
1227
+ function compareHtmlFormValues(a, b) {
1228
+ if (typeof a !== 'string' || typeof b !== 'string') {
1229
+ return null;
1230
+ }
1231
+ if (!looksLikeHtmlFormValue(a) || !looksLikeHtmlFormValue(b)) {
1232
+ return null;
1233
+ }
1234
+ return normalizeRichTextHtmlForCompare(a) === normalizeRichTextHtmlForCompare(b);
1091
1235
  }
1092
1236
  /** Lookup/select filter-mode payload: `{ value, displayText?, operation }`. */
1093
1237
  function isFilterFormShape(value) {
@@ -1295,6 +1439,12 @@ function isFormValueEqual(a, b) {
1295
1439
  }
1296
1440
  return true;
1297
1441
  }
1442
+ if (typeof a === 'string' && typeof b === 'string') {
1443
+ const htmlEqual = compareHtmlFormValues(a, b);
1444
+ if (htmlEqual !== null) {
1445
+ return htmlEqual;
1446
+ }
1447
+ }
1298
1448
  return isEqual(normalizeFormValue(a), normalizeFormValue(b));
1299
1449
  }
1300
1450
  /**
@@ -1303,6 +1453,45 @@ function isFormValueEqual(a, b) {
1303
1453
  function isFormContextDirty(current, baseline) {
1304
1454
  return !isFormValueEqual(current, baseline);
1305
1455
  }
1456
+ /**
1457
+ * Lists paths where live context differs from baseline (for dirty-state trace logs).
1458
+ */
1459
+ function summarizeFormContextDirtyDiff(current, baseline, maxEntries = 12) {
1460
+ const diffs = [];
1461
+ const walk = (path, currentValue, baselineValue) => {
1462
+ if (diffs.length >= maxEntries) {
1463
+ return;
1464
+ }
1465
+ if (isFormValueEqual(currentValue, baselineValue)) {
1466
+ return;
1467
+ }
1468
+ const bothPlainObjects = isPlainObject(currentValue) &&
1469
+ isPlainObject(baselineValue) &&
1470
+ !isArray(currentValue) &&
1471
+ !isArray(baselineValue);
1472
+ if (bothPlainObjects) {
1473
+ const left = currentValue;
1474
+ const right = baselineValue;
1475
+ const keys = new Set([...Object.keys(left), ...Object.keys(right)]);
1476
+ for (const key of keys) {
1477
+ if (diffs.length >= maxEntries) {
1478
+ return;
1479
+ }
1480
+ const nextPath = path ? `${path}.${key}` : key;
1481
+ walk(nextPath, left[key], right[key]);
1482
+ }
1483
+ return;
1484
+ }
1485
+ diffs.push({
1486
+ path: path || '(root)',
1487
+ current: currentValue,
1488
+ baseline: baselineValue,
1489
+ selectionEqual: isSelectionValueEqual(currentValue, baselineValue),
1490
+ });
1491
+ };
1492
+ walk('', current, baseline);
1493
+ return diffs;
1494
+ }
1306
1495
  /**
1307
1496
  * Clones a context object for use as a dirty-tracking baseline.
1308
1497
  */
@@ -1321,20 +1510,26 @@ const AXPContextStore = signalStore(withState(() => ({
1321
1510
  /** Last committed / saved baseline — discard reverts to this snapshot. */
1322
1511
  savedSnapshot: {},
1323
1512
  previousSnapshot: {},
1513
+ /** True after a user-origin update since the last {@link commitSaved}. */
1514
+ userEditedSinceCommit: false,
1324
1515
  lastChange: {
1325
1516
  state: 'initiated',
1326
1517
  },
1327
- })), withComputed(({ data, state, lastChange, savedSnapshot, previousSnapshot }) => ({
1518
+ })), withComputed(({ data, state, lastChange, savedSnapshot, previousSnapshot, userEditedSinceCommit }) => ({
1328
1519
  isChanged: computed(() => state() === 'changed'),
1329
1520
  isReset: computed(() => state() === 'restored'),
1330
1521
  isInitiated: computed(() => state() === 'initiated'),
1331
1522
  isEmpty: computed(() => Object.keys(data()).length === 0),
1332
1523
  isSavedCommitted: computed(() => !isEmpty(savedSnapshot())),
1524
+ hasUserEditedSinceCommit: computed(() => userEditedSinceCommit()),
1333
1525
  isDirty: computed(() => {
1334
1526
  const saved = savedSnapshot();
1335
1527
  if (isEmpty(saved)) {
1336
1528
  return false;
1337
1529
  }
1530
+ if (!userEditedSinceCommit()) {
1531
+ return false;
1532
+ }
1338
1533
  return isFormContextDirty(data(), saved);
1339
1534
  }),
1340
1535
  snapshot: computed(() => cloneDeep(data())),
@@ -1344,24 +1539,80 @@ const AXPContextStore = signalStore(withState(() => ({
1344
1539
  previous: computed(() => cloneDeep(previousSnapshot())),
1345
1540
  changeEvent: computed(() => lastChange()),
1346
1541
  })), withMethods((store) => {
1542
+ const traceDirty = (title, message, data) => {
1543
+ AXPDebugService.instance?.trace(FORM_DIRTY_TRACE_NS, title, message, data);
1544
+ };
1545
+ const traceDirtyState = (reason, data, path) => {
1546
+ const saved = store.savedSnapshot();
1547
+ const current = store.data();
1548
+ const hasSaved = !isEmpty(saved);
1549
+ const contextDirty = hasSaved && isFormContextDirty(current, saved);
1550
+ traceDirty('context-store', reason, () => ({
1551
+ path,
1552
+ userEditedSinceCommit: store.userEditedSinceCommit(),
1553
+ isSavedCommitted: hasSaved,
1554
+ isFormContextDirty: contextDirty,
1555
+ isDirty: store.isDirty(),
1556
+ ...(hasSaved
1557
+ ? {
1558
+ dirtyPaths: contextDirty ? summarizeFormContextDirtyDiff(current, saved) : [],
1559
+ }
1560
+ : {}),
1561
+ ...data,
1562
+ }));
1563
+ };
1347
1564
  const updateValue = (path, value, options) => {
1348
1565
  const currentData = cloneDeep(store.data());
1349
1566
  const oldValue = getSmart(currentData, path);
1350
1567
  const hasSaved = !isEmpty(store.savedSnapshot());
1351
- const savedAtPath = hasSaved ? getSmart(store.savedSnapshot(), path) : undefined;
1568
+ const saved = store.savedSnapshot();
1569
+ const savedAtPath = hasSaved ? getSmart(saved, path) : undefined;
1570
+ const origin = options?.origin ?? 'system';
1352
1571
  if (hasSaved && isSelectionValueEqual(value, savedAtPath) && !isEqual(value, savedAtPath)) {
1353
1572
  value = cloneDeep(savedAtPath);
1354
1573
  }
1574
+ if (hasSaved && !isEqual(value, savedAtPath) && isFormValueEqual(value, savedAtPath)) {
1575
+ value = cloneDeep(savedAtPath);
1576
+ }
1355
1577
  const isArrayLengthChange = Array.isArray(value) && (!Array.isArray(oldValue) || value.length !== oldValue.length);
1356
- if (!isArrayLengthChange && isEqual(oldValue, value)) {
1578
+ /** Same selection id as baseline but different object shape (lookup/select item vs scalar). */
1579
+ const needsSavedShapeRestore = hasSaved && isSelectionValueEqual(value, savedAtPath) && !isFormValueEqual(value, savedAtPath);
1580
+ if (!isArrayLengthChange && isEqual(oldValue, value) && !needsSavedShapeRestore) {
1581
+ traceDirty('context-store', 'updateValue skipped (lodash isEqual)', () => ({
1582
+ path,
1583
+ origin,
1584
+ oldValue,
1585
+ value,
1586
+ needsSavedShapeRestore,
1587
+ selectionEqualToSaved: hasSaved ? isSelectionValueEqual(value, savedAtPath) : null,
1588
+ savedAtPath,
1589
+ }));
1357
1590
  return;
1358
1591
  }
1359
1592
  const shouldNormalizeSavedShape = hasSaved && isSelectionValueEqual(value, savedAtPath) && !isEqual(oldValue, value);
1360
- if (!isArrayLengthChange && isFormValueEqual(oldValue, value) && !shouldNormalizeSavedShape) {
1593
+ if (!isArrayLengthChange &&
1594
+ isFormValueEqual(oldValue, value) &&
1595
+ !shouldNormalizeSavedShape &&
1596
+ !needsSavedShapeRestore) {
1597
+ traceDirty('context-store', 'updateValue skipped (isFormValueEqual)', () => ({
1598
+ path,
1599
+ origin,
1600
+ oldValue,
1601
+ value,
1602
+ needsSavedShapeRestore,
1603
+ shouldNormalizeSavedShape,
1604
+ selectionEqualToSaved: hasSaved ? isSelectionValueEqual(value, savedAtPath) : null,
1605
+ savedAtPath,
1606
+ }));
1361
1607
  return;
1362
1608
  }
1363
- const origin = options?.origin ?? 'system';
1364
1609
  const updatedData = setSmart(currentData, path, value);
1610
+ const isStillDirty = hasSaved && isFormContextDirty(updatedData, saved);
1611
+ const nextUserEdited = isStillDirty
1612
+ ? origin === 'user'
1613
+ ? true
1614
+ : store.userEditedSinceCommit()
1615
+ : false;
1365
1616
  const changeEvent = {
1366
1617
  oldValue,
1367
1618
  newValue: value,
@@ -1375,7 +1626,22 @@ const AXPContextStore = signalStore(withState(() => ({
1375
1626
  data: updatedData,
1376
1627
  state: 'changed',
1377
1628
  lastChange: changeEvent,
1629
+ userEditedSinceCommit: nextUserEdited,
1378
1630
  });
1631
+ traceDirty('context-store', 'updateValue applied', () => ({
1632
+ path,
1633
+ origin,
1634
+ oldValue,
1635
+ newValue: value,
1636
+ savedAtPath,
1637
+ selectionEqualToSaved: hasSaved ? isSelectionValueEqual(value, savedAtPath) : null,
1638
+ formValueEqualToSaved: hasSaved ? isFormValueEqual(value, savedAtPath) : null,
1639
+ needsSavedShapeRestore,
1640
+ isStillDirty,
1641
+ nextUserEdited,
1642
+ isDirty: !isEmpty(saved) && nextUserEdited && isStillDirty,
1643
+ dirtyPaths: isStillDirty ? summarizeFormContextDirtyDiff(updatedData, saved) : [],
1644
+ }));
1379
1645
  };
1380
1646
  const applyObjectPaths = (obj, options, prefix = '') => {
1381
1647
  for (const [key, value] of Object.entries(obj ?? {})) {
@@ -1388,6 +1654,57 @@ const AXPContextStore = signalStore(withState(() => ({
1388
1654
  }
1389
1655
  }
1390
1656
  };
1657
+ const removePathFromContext = (path, origin = 'system') => {
1658
+ const currentData = cloneDeep(store.data());
1659
+ if (!has(currentData, path)) {
1660
+ return;
1661
+ }
1662
+ unset(currentData, path);
1663
+ const parts = path.split('.');
1664
+ for (let i = parts.length - 1; i > 0; i--) {
1665
+ const parentPath = parts.slice(0, i).join('.');
1666
+ const parentObj = getSmart(currentData, parentPath);
1667
+ if (isPlainObject(parentObj) && isEmpty(Object.keys(parentObj))) {
1668
+ unset(currentData, parentPath);
1669
+ }
1670
+ }
1671
+ const saved = store.savedSnapshot();
1672
+ const isStillDirty = !isEmpty(saved) && isFormContextDirty(currentData, saved);
1673
+ patchState(store, {
1674
+ previousSnapshot: store.snapshot(),
1675
+ data: currentData,
1676
+ state: 'changed',
1677
+ lastChange: {
1678
+ path,
1679
+ state: 'changed',
1680
+ data: currentData,
1681
+ origin,
1682
+ },
1683
+ userEditedSinceCommit: isStillDirty
1684
+ ? origin === 'user'
1685
+ ? true
1686
+ : store.userEditedSinceCommit()
1687
+ : false,
1688
+ });
1689
+ traceDirty('context-store', 'removePathFromContext', () => ({
1690
+ path,
1691
+ origin,
1692
+ isStillDirty,
1693
+ dirtyPaths: isStillDirty ? summarizeFormContextDirtyDiff(currentData, saved) : [],
1694
+ }));
1695
+ };
1696
+ const syncPathToSavedBaseline = (path, options) => {
1697
+ const saved = store.savedSnapshot();
1698
+ if (isEmpty(saved)) {
1699
+ return;
1700
+ }
1701
+ const origin = options?.origin ?? 'system';
1702
+ if (has(saved, path)) {
1703
+ updateValue(path, cloneDeep(getSmart(saved, path)), { origin });
1704
+ return;
1705
+ }
1706
+ removePathFromContext(path, origin);
1707
+ };
1391
1708
  const revertToSaved = () => {
1392
1709
  const savedData = store.saved();
1393
1710
  const changeEvent = {
@@ -1402,13 +1719,17 @@ const AXPContextStore = signalStore(withState(() => ({
1402
1719
  data: savedData,
1403
1720
  state: 'restored',
1404
1721
  lastChange: changeEvent,
1722
+ userEditedSinceCommit: false,
1405
1723
  });
1724
+ traceDirtyState('revertToSaved');
1406
1725
  };
1407
1726
  return {
1408
1727
  update(path, value, options) {
1409
1728
  updateValue(path, value, options);
1410
1729
  },
1411
1730
  applyObjectPaths,
1731
+ /** Aligns a context path with the saved baseline (restore value or remove if absent). */
1732
+ syncPathToSavedBaseline,
1412
1733
  patch(context, options) {
1413
1734
  const currentData = cloneDeep(store.data());
1414
1735
  const updatedData = { ...currentData, ...context };
@@ -1449,6 +1770,7 @@ const AXPContextStore = signalStore(withState(() => ({
1449
1770
  data: initialData,
1450
1771
  state: 'initiated',
1451
1772
  lastChange: changeEvent,
1773
+ userEditedSinceCommit: false,
1452
1774
  });
1453
1775
  },
1454
1776
  getValue(path) {
@@ -1467,11 +1789,13 @@ const AXPContextStore = signalStore(withState(() => ({
1467
1789
  savedSnapshot: snapshot,
1468
1790
  previousSnapshot: snapshot,
1469
1791
  state: 'initiated',
1792
+ userEditedSinceCommit: false,
1470
1793
  lastChange: {
1471
1794
  state: 'initiated',
1472
1795
  data: snapshot,
1473
1796
  },
1474
1797
  });
1798
+ traceDirtyState('commitSaved');
1475
1799
  },
1476
1800
  /** Merges parent-bound entity fields without resetting the saved baseline. */
1477
1801
  applyParentBind(merged) {
@@ -1486,6 +1810,98 @@ const AXPContextStore = signalStore(withState(() => ({
1486
1810
  }));
1487
1811
  //#endregion
1488
1812
 
1813
+ //#region ---- Imports ----
1814
+ //#endregion
1815
+ //#region ---- Pipe normalization ----
1816
+ /** Normalizes shorthand pipe names to `{ type, options? }` specs. */
1817
+ function normalizeContextEvalPipes(pipes) {
1818
+ return pipes.map((pipe) => (typeof pipe === 'string' ? { type: pipe } : pipe));
1819
+ }
1820
+ //#endregion
1821
+ //#region ---- Pipe application ----
1822
+ /**
1823
+ * Applies a left-to-right formatter pipeline via {@link AXFormatService}.
1824
+ * Unknown formatters fall back to a trimmed string representation.
1825
+ */
1826
+ function applyContextEvalPipes(value, pipes, formatService) {
1827
+ if (!pipes?.length) {
1828
+ return value;
1829
+ }
1830
+ let current = value;
1831
+ for (const pipe of normalizeContextEvalPipes(pipes)) {
1832
+ try {
1833
+ current = formatService.format(current, pipe.type, pipe.options);
1834
+ }
1835
+ catch {
1836
+ current = current == null ? '' : String(current);
1837
+ }
1838
+ }
1839
+ return current;
1840
+ }
1841
+ /** Reads a nested path from data and optionally applies display pipes. */
1842
+ function contextEvalWithPipes(data, path, formatService, pipes) {
1843
+ return applyContextEvalPipes(get(data, path), pipes, formatService);
1844
+ }
1845
+ //#endregion
1846
+
1847
+ //#region ---- Imports ----
1848
+ //#endregion
1849
+ /**
1850
+ * Builds sync `context.eval(path, { pipes })` functions backed by {@link AXFormatService}.
1851
+ * Supports built-in and custom formatters registered on {@link AXFormatModule}.
1852
+ */
1853
+ class AXPContextEvalFactory {
1854
+ constructor() {
1855
+ //#region ---- Services & Dependencies ----
1856
+ this.formatService = inject(AXFormatService);
1857
+ }
1858
+ //#endregion
1859
+ //#region ---- Factory ----
1860
+ /** Creates a sync eval function for the given record/context data object. */
1861
+ create(data) {
1862
+ return (path, options) => {
1863
+ const raw = get(data, path);
1864
+ return applyContextEvalPipes(raw, options?.pipes, this.formatService);
1865
+ };
1866
+ }
1867
+ /** Creates `{ eval }` scope object for expression evaluator scopes. */
1868
+ createScope(data) {
1869
+ return { eval: this.create(data) };
1870
+ }
1871
+ /**
1872
+ * Display-tier scope that re-reads data on each eval (for live stores / changing row context).
1873
+ */
1874
+ createLiveScope(getData) {
1875
+ return {
1876
+ eval: (path, options) => this.create(getData())(path, options),
1877
+ };
1878
+ }
1879
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPContextEvalFactory, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1880
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPContextEvalFactory, providedIn: 'root' }); }
1881
+ }
1882
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPContextEvalFactory, decorators: [{
1883
+ type: Injectable,
1884
+ args: [{ providedIn: 'root' }]
1885
+ }] });
1886
+
1887
+ //#region ---- Types ----
1888
+ //#endregion
1889
+
1890
+ //#region ---- Template normalization ----
1891
+ /**
1892
+ * Normalizes display templates before {@link AXPExpressionEvaluatorService} evaluation.
1893
+ * Converts simple dot paths (e.g. `{{ position.title }}`) to `context.eval("position.title")`
1894
+ * so row data is read from scope.context instead of bare JavaScript identifiers.
1895
+ */
1896
+ function normalizeExpressionTemplate(template) {
1897
+ let normalized = template.replace(/\{\{\s*context\.eval\(['"]([^'"]+)['"]\)\s*\}\}/g, '{{ context.eval("$1") }}');
1898
+ if (!normalized.includes('{{')) {
1899
+ normalized = normalized.replace(/\{/g, '{{').replace(/\}/g, '}}');
1900
+ }
1901
+ return normalized.replace(/\{\{\s*(?!context\.)([\w]+(?:\.[\w]+)*)\s*\}\}/g, '{{ context.eval("$1") }}');
1902
+ }
1903
+ //#endregion
1904
+
1489
1905
  class AXPExpressionEvaluatorScopeProviderContext {
1490
1906
  constructor() {
1491
1907
  this.scopes = {};
@@ -1595,8 +2011,9 @@ class AXPExpressionEvaluatorService {
1595
2011
  }
1596
2012
  }
1597
2013
  async evaluateStringExpression(templateExpression, scope) {
2014
+ const normalizedTemplate = normalizeExpressionTemplate(templateExpression);
1598
2015
  // Check if the input is exactly a single {{ ... }} expression (handle multiline)
1599
- const exactMatch = templateExpression.match(/^\s*\{\{\s*([\s\S]*?)\s*\}\}\s*$/);
2016
+ const exactMatch = normalizedTemplate.match(/^\s*\{\{\s*([\s\S]*?)\s*\}\}\s*$/);
1600
2017
  if (exactMatch) {
1601
2018
  const expression = exactMatch[1];
1602
2019
  const sandbox = this.getOrCompileFunction(expression);
@@ -1608,7 +2025,7 @@ class AXPExpressionEvaluatorService {
1608
2025
  // Collect all matches and their positions
1609
2026
  const matches = [];
1610
2027
  let match;
1611
- while ((match = regex.exec(templateExpression)) !== null) {
2028
+ while ((match = regex.exec(normalizedTemplate)) !== null) {
1612
2029
  matches.push({
1613
2030
  expression: match[1],
1614
2031
  start: match.index,
@@ -1625,12 +2042,12 @@ class AXPExpressionEvaluatorService {
1625
2042
  let result = '';
1626
2043
  let lastIndex = 0;
1627
2044
  matches.forEach((m, i) => {
1628
- result += templateExpression.slice(lastIndex, m.start);
2045
+ result += normalizedTemplate.slice(lastIndex, m.start);
1629
2046
  const value = values[i];
1630
2047
  result += value !== undefined && value !== null ? value : '';
1631
2048
  lastIndex = m.end;
1632
2049
  });
1633
- result += templateExpression.slice(lastIndex);
2050
+ result += normalizedTemplate.slice(lastIndex);
1634
2051
  return result;
1635
2052
  }
1636
2053
  isExpression(expression) {
@@ -2742,12 +3159,12 @@ const HORIZONTAL_DIRECTIONAL_KEY_MIRRORS = {
2742
3159
  };
2743
3160
  /** Maps a horizontal direction key to its RTL semantic counterpart. */
2744
3161
  function mirrorHorizontalDirectionalKey(key) {
2745
- const normalized = normalizeShortcutToken(key);
3162
+ const normalized = normalizeShortcutToken$1(key);
2746
3163
  return HORIZONTAL_DIRECTIONAL_KEY_MIRRORS[normalized] ?? normalized;
2747
3164
  }
2748
3165
  /** True when the chord's primary key is a horizontal direction key (`←`/`→`, `[`/`]`). */
2749
3166
  function isHorizontalDirectionalShortcutKey(key) {
2750
- return normalizeShortcutToken(key) in HORIZONTAL_DIRECTIONAL_KEY_MIRRORS;
3167
+ return normalizeShortcutToken$1(key) in HORIZONTAL_DIRECTIONAL_KEY_MIRRORS;
2751
3168
  }
2752
3169
  /**
2753
3170
  * Resolves direction behavior for a chord.
@@ -2763,7 +3180,7 @@ function resolveEffectiveDirectionBehavior(chord, explicit) {
2763
3180
  }
2764
3181
  /** Resolves the key the user presses for a semantically registered chord in RTL. */
2765
3182
  function resolveSemanticDirectionalKey(storedKey, rtl) {
2766
- const normalized = normalizeShortcutToken(storedKey);
3183
+ const normalized = normalizeShortcutToken$1(storedKey);
2767
3184
  if (!rtl) {
2768
3185
  return normalized;
2769
3186
  }
@@ -2830,7 +3247,7 @@ function parseKeyboardShortcutChord(chord) {
2830
3247
  .split('+')
2831
3248
  .map((part) => part.trim())
2832
3249
  .filter(Boolean);
2833
- const key = normalizeShortcutToken(parts.pop() ?? '');
3250
+ const key = normalizeShortcutToken$1(parts.pop() ?? '');
2834
3251
  const parsed = {
2835
3252
  ctrl: false,
2836
3253
  shift: false,
@@ -2858,7 +3275,7 @@ function matchesKeyboardShortcutChord(event, chord, options) {
2858
3275
  return false;
2859
3276
  }
2860
3277
  const actualKey = normalizeShortcutEventKey(event);
2861
- const expectedKey = normalizeShortcutToken(parsed.key);
3278
+ const expectedKey = normalizeShortcutToken$1(parsed.key);
2862
3279
  const rtlSemantic = directionBehavior === 'semantic' &&
2863
3280
  options?.isRtl === true &&
2864
3281
  isHorizontalDirectionalShortcutKey(expectedKey);
@@ -2922,7 +3339,7 @@ function formatKeyboardShortcutChord(chord, options) {
2922
3339
  if (parts.length === 0) {
2923
3340
  return '';
2924
3341
  }
2925
- const keyPart = normalizeShortcutToken(parts.pop() ?? '');
3342
+ const keyPart = normalizeShortcutToken$1(parts.pop() ?? '');
2926
3343
  const modifierParts = parts.map((part) => formatShortcutPart(part.toLowerCase(), mac));
2927
3344
  const keyDisplay = formatShortcutPart(keyPart, mac);
2928
3345
  if (mac) {
@@ -3016,7 +3433,7 @@ function isMacPlatform() {
3016
3433
  }
3017
3434
  return /Mac|iPhone|iPad|iPod/i.test(navigator.userAgent);
3018
3435
  }
3019
- function normalizeShortcutToken(key) {
3436
+ function normalizeShortcutToken$1(key) {
3020
3437
  const normalized = key.trim().toLowerCase();
3021
3438
  if (normalized === 'esc') {
3022
3439
  return 'escape';
@@ -3078,7 +3495,7 @@ function normalizeShortcutEventKey(event) {
3078
3495
  return event.key.toLowerCase();
3079
3496
  }
3080
3497
  function formatShortcutPart(part, mac) {
3081
- const normalized = normalizeShortcutToken(part);
3498
+ const normalized = normalizeShortcutToken$1(part);
3082
3499
  if (mac && KEY_DISPLAY_MAC[normalized]) {
3083
3500
  return KEY_DISPLAY_MAC[normalized];
3084
3501
  }
@@ -3355,6 +3772,271 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
3355
3772
  }]
3356
3773
  }] });
3357
3774
 
3775
+ //#region ---- Dialog Action Shortcut Utilities ----
3776
+ const DEFAULT_CANCEL_DIALOG_ACTION_SHORTCUTS = ['Esc'];
3777
+ const DEFAULT_SUBMIT_DIALOG_ACTION_SHORTCUTS = ['Enter', 'ctrl+s'];
3778
+ const PRIMARY_DIALOG_ACTION_COMMANDS = new Set([
3779
+ 'submit',
3780
+ 'create',
3781
+ 'apply',
3782
+ 'save',
3783
+ 'select',
3784
+ 'entity-form-done',
3785
+ 'upload-image',
3786
+ ]);
3787
+ /**
3788
+ * Parses a shortcut string such as `Enter`, `Escape`, or `ctrl+shift+s`.
3789
+ */
3790
+ function parseDialogActionShortcut(shortcut) {
3791
+ const parts = shortcut
3792
+ .trim()
3793
+ .toLowerCase()
3794
+ .split('+')
3795
+ .map((part) => part.trim())
3796
+ .filter(Boolean);
3797
+ const key = normalizeShortcutToken(parts.pop() ?? '');
3798
+ return {
3799
+ ctrl: parts.includes('ctrl') || parts.includes('control'),
3800
+ shift: parts.includes('shift'),
3801
+ alt: parts.includes('alt') || parts.includes('option'),
3802
+ meta: parts.includes('meta') || parts.includes('cmd') || parts.includes('command'),
3803
+ key,
3804
+ };
3805
+ }
3806
+ /**
3807
+ * Returns true when the keyboard event matches the given shortcut definition.
3808
+ */
3809
+ function matchesDialogActionShortcut(event, shortcut) {
3810
+ const parsed = parseDialogActionShortcut(shortcut);
3811
+ if (parsed.ctrl !== event.ctrlKey) {
3812
+ return false;
3813
+ }
3814
+ if (parsed.shift !== event.shiftKey) {
3815
+ return false;
3816
+ }
3817
+ if (parsed.alt !== event.altKey) {
3818
+ return false;
3819
+ }
3820
+ if (parsed.meta !== event.metaKey) {
3821
+ return false;
3822
+ }
3823
+ return normalizeShortcutKey(event) === normalizeShortcutToken(parsed.key);
3824
+ }
3825
+ /**
3826
+ * Resolves footer action shortcuts: defaults when omitted, merge when extras are provided, none when `[]`.
3827
+ */
3828
+ function resolveDialogActionShortcuts(defaults, overrides) {
3829
+ if (overrides !== undefined) {
3830
+ if (overrides.length === 0) {
3831
+ return undefined;
3832
+ }
3833
+ return flattenDialogActionShortcutChords([...defaults, ...overrides]);
3834
+ }
3835
+ return flattenDialogActionShortcutChords(defaults);
3836
+ }
3837
+ /**
3838
+ * Applies built-in footer shortcuts when actions are declared without `shortcuts`
3839
+ * (e.g. workflow `show-layout-popup` raw action config).
3840
+ */
3841
+ function resolveConfiguredFooterActionShortcuts(command, explicit) {
3842
+ if (explicit !== undefined) {
3843
+ return resolveDialogActionShortcuts([], explicit);
3844
+ }
3845
+ if (command === 'cancel') {
3846
+ return resolveDialogActionShortcuts(DEFAULT_CANCEL_DIALOG_ACTION_SHORTCUTS, undefined);
3847
+ }
3848
+ if (command && PRIMARY_DIALOG_ACTION_COMMANDS.has(command)) {
3849
+ return resolveDialogActionShortcuts(DEFAULT_SUBMIT_DIALOG_ACTION_SHORTCUTS, undefined);
3850
+ }
3851
+ return undefined;
3852
+ }
3853
+ function flattenDialogActionShortcutChords(shortcuts) {
3854
+ const chords = normalizeKeyboardShortcuts(shortcuts).flatMap((shortcut) => shortcut.keys);
3855
+ const deduped = dedupeDialogActionShortcuts(chords);
3856
+ return deduped.length ? deduped : undefined;
3857
+ }
3858
+ function dedupeDialogActionShortcuts(shortcuts) {
3859
+ const seen = new Set();
3860
+ const result = [];
3861
+ for (const shortcut of shortcuts) {
3862
+ const token = normalizeShortcutToken(shortcut);
3863
+ if (!token || seen.has(token)) {
3864
+ continue;
3865
+ }
3866
+ seen.add(token);
3867
+ result.push(shortcut.trim());
3868
+ }
3869
+ return result;
3870
+ }
3871
+ function normalizeShortcutToken(key) {
3872
+ const normalized = key.trim().toLowerCase();
3873
+ if (normalized === 'esc') {
3874
+ return 'escape';
3875
+ }
3876
+ return normalized;
3877
+ }
3878
+ /**
3879
+ * Whether a dialog footer shortcut may fire while focus is inside an editable field.
3880
+ */
3881
+ function isDialogShortcutAllowedInEditableField(shortcut) {
3882
+ const parsed = parseDialogActionShortcut(shortcut);
3883
+ const isPlainEnter = parsed.key === 'enter' && !parsed.ctrl && !parsed.shift && !parsed.alt && !parsed.meta;
3884
+ return !isPlainEnter;
3885
+ }
3886
+ /**
3887
+ * Normalizes dialog footer chord strings for {@link AXPKeyboardShortcutRegistry} (`Esc` → `escape`).
3888
+ */
3889
+ function normalizeDialogActionShortcutChord(shortcut) {
3890
+ const parts = shortcut
3891
+ .trim()
3892
+ .split('+')
3893
+ .map((part) => part.trim())
3894
+ .filter(Boolean);
3895
+ if (parts.length === 0) {
3896
+ return shortcut.trim();
3897
+ }
3898
+ const key = normalizeShortcutToken(parts.pop() ?? '');
3899
+ const modifiers = parts.map((part) => part.toLowerCase());
3900
+ return modifiers.length ? `${modifiers.join('+')}+${key}` : key;
3901
+ }
3902
+ /**
3903
+ * Whether a shortcut should fire given the current focus target.
3904
+ * Enter without modifiers is suppressed in multiline / rich-text fields.
3905
+ */
3906
+ function shouldTriggerDialogActionShortcut(event, shortcut) {
3907
+ const parsed = parseDialogActionShortcut(shortcut);
3908
+ const isPlainEnter = parsed.key === 'enter' && !parsed.ctrl && !parsed.shift && !parsed.alt && !parsed.meta;
3909
+ if (!isPlainEnter) {
3910
+ return true;
3911
+ }
3912
+ return !isKeyboardTargetMultilineEditable(event.target);
3913
+ }
3914
+ function normalizeShortcutKey(event) {
3915
+ if (event.key === 'Enter') {
3916
+ return 'enter';
3917
+ }
3918
+ if (event.key === 'Escape') {
3919
+ return 'escape';
3920
+ }
3921
+ if (event.key === ' ') {
3922
+ return 'space';
3923
+ }
3924
+ if (event.key.length === 1) {
3925
+ return event.key.toLowerCase();
3926
+ }
3927
+ const codeMatch = event.code.match(/^Key([A-Z])$/);
3928
+ if (codeMatch) {
3929
+ return codeMatch[1].toLowerCase();
3930
+ }
3931
+ const digitMatch = event.code.match(/^Digit([0-9])$/);
3932
+ if (digitMatch) {
3933
+ return digitMatch[1];
3934
+ }
3935
+ return event.key.toLowerCase();
3936
+ }
3937
+ function isKeyboardTargetMultilineEditable(target) {
3938
+ if (!(target instanceof HTMLElement)) {
3939
+ return false;
3940
+ }
3941
+ if (target.isContentEditable) {
3942
+ return true;
3943
+ }
3944
+ const textarea = target.closest('textarea');
3945
+ if (textarea && !textarea.readOnly && !textarea.disabled) {
3946
+ return true;
3947
+ }
3948
+ const richText = target.closest('[contenteditable="true"]');
3949
+ return richText instanceof HTMLElement;
3950
+ }
3951
+ //#endregion
3952
+
3953
+ //#region ---- Popup Footer Keyboard Shortcuts ----
3954
+ /**
3955
+ * Registers popup footer shortcuts at modal priority so they win over page bindings and stop bubbling.
3956
+ */
3957
+ function registerPopupFooterKeyboardShortcuts(options) {
3958
+ const group = '@general:keyboard-shortcuts.groups.dialog';
3959
+ const scope = options.scope ?? group;
3960
+ const shortcuts = [];
3961
+ for (const action of options.actions) {
3962
+ if (action.hidden?.()) {
3963
+ continue;
3964
+ }
3965
+ const chords = action.shortcuts !== undefined
3966
+ ? resolveConfiguredFooterActionShortcuts(action.command, action.shortcuts)
3967
+ : resolveConfiguredFooterActionShortcuts(action.command, undefined);
3968
+ if (!chords?.length) {
3969
+ continue;
3970
+ }
3971
+ for (const shortcut of chords) {
3972
+ shortcuts.push({
3973
+ keys: [normalizeDialogActionShortcutChord(shortcut)],
3974
+ title: action.title,
3975
+ group,
3976
+ allowInEditableFields: isDialogShortcutAllowedInEditableField(shortcut),
3977
+ when: () => {
3978
+ if (options.when && !options.when()) {
3979
+ return false;
3980
+ }
3981
+ if (action.hidden?.()) {
3982
+ return false;
3983
+ }
3984
+ if (action.disabled?.()) {
3985
+ return false;
3986
+ }
3987
+ return true;
3988
+ },
3989
+ handler: () => {
3990
+ void action.handler();
3991
+ },
3992
+ });
3993
+ }
3994
+ }
3995
+ options.registry.register({
3996
+ owner: options.owner,
3997
+ elementRef: options.elementRef,
3998
+ id: options.id,
3999
+ priority: AXPKeyboardShortcutPriority.Modal,
4000
+ scope,
4001
+ when: options.when,
4002
+ shortcuts,
4003
+ });
4004
+ }
4005
+ /**
4006
+ * Binds footer shortcuts for AXPopupService-hosted components. Call from the popup constructor.
4007
+ */
4008
+ function setupPopupFooterKeyboardShortcuts(getActions, config) {
4009
+ const destroyRef = inject(DestroyRef);
4010
+ const elementRef = inject(ElementRef);
4011
+ const registry = inject(AXPKeyboardShortcutRegistry);
4012
+ const id = config?.id ?? 'popup-footer';
4013
+ effect(() => {
4014
+ getActions();
4015
+ config?.when?.();
4016
+ untracked(() => {
4017
+ registerPopupFooterKeyboardShortcuts({
4018
+ registry,
4019
+ owner: destroyRef,
4020
+ elementRef,
4021
+ id,
4022
+ scope: config?.scope,
4023
+ when: config?.when,
4024
+ actions: getActions(),
4025
+ });
4026
+ });
4027
+ });
4028
+ }
4029
+ //#endregion
4030
+
4031
+ /** Global overlay flag + task count ({@link AXPLayoutThemeService}). */
4032
+ const OVERLAY_LAYOUT_TRACE_NS = 'OVERLAY-LAYOUT';
4033
+ /** Root shell loading dialog sync ({@link AXPRootLayoutComponent}). */
4034
+ const OVERLAY_ROOT_LAYOUT_TRACE_NS = 'OVERLAY-ROOT-LAYOUT';
4035
+ /** Details view page load and overlay release orchestration. */
4036
+ const OVERLAY_DETAILS_VIEW_TRACE_NS = 'OVERLAY-DETAILS-VIEW';
4037
+ /** Widget tree mount / settle polling ({@link AXPWidgetContainerComponent}). */
4038
+ const OVERLAY_WIDGET_SETTLE_TRACE_NS = 'OVERLAY-WIDGET-SETTLE';
4039
+
3358
4040
  //#region ---- Imports ----
3359
4041
  //#endregion
3360
4042
 
@@ -3813,6 +4495,112 @@ function searchInMultiLanguageString(value, query, locale) {
3813
4495
  }
3814
4496
  //#endregion
3815
4497
 
4498
+ //#region ---- Imports ----
4499
+ //#endregion
4500
+ //#region ---- Constants ----
4501
+ const DEFAULT_IDLE_TIMEOUT_MS = 2_000;
4502
+ const DEFAULT_MIN_IDLE_MS = 16;
4503
+ //#endregion
4504
+ //#region ---- Service ----
4505
+ /**
4506
+ * Schedules low-priority work during browser idle periods.
4507
+ * Pauses when the tab is hidden or when explicitly paused (e.g. user-initiated seeding).
4508
+ */
4509
+ class AXPIdleSchedulerService {
4510
+ constructor() {
4511
+ //#region ---- State ----
4512
+ this.pausedState = signal(false, ...(ngDevMode ? [{ debugName: "pausedState" }] : /* istanbul ignore next */ []));
4513
+ this.paused = this.pausedState.asReadonly();
4514
+ this.queue = [];
4515
+ this.running = false;
4516
+ }
4517
+ //#endregion
4518
+ //#region ---- Public API ----
4519
+ pause() {
4520
+ this.pausedState.set(true);
4521
+ }
4522
+ resume() {
4523
+ this.pausedState.set(false);
4524
+ void this.drainQueue();
4525
+ }
4526
+ /**
4527
+ * Resolves after the browser reports idle time, or after timeoutMs (whichever comes first).
4528
+ */
4529
+ waitForIdle(options) {
4530
+ const timeoutMs = options?.timeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;
4531
+ const minIdleMs = options?.minIdleMs ?? DEFAULT_MIN_IDLE_MS;
4532
+ if (this.pausedState() || typeof document !== 'undefined' && document.visibilityState === 'hidden') {
4533
+ return delay(timeoutMs);
4534
+ }
4535
+ return new Promise((resolve) => {
4536
+ let settled = false;
4537
+ const finish = () => {
4538
+ if (settled)
4539
+ return;
4540
+ settled = true;
4541
+ resolve();
4542
+ };
4543
+ const timeoutId = setTimeout(finish, timeoutMs);
4544
+ if (typeof requestIdleCallback === 'function') {
4545
+ requestIdleCallback(() => {
4546
+ clearTimeout(timeoutId);
4547
+ finish();
4548
+ }, { timeout: timeoutMs });
4549
+ }
4550
+ else {
4551
+ clearTimeout(timeoutId);
4552
+ setTimeout(finish, minIdleMs);
4553
+ }
4554
+ });
4555
+ }
4556
+ /** Enqueues async work processed sequentially during idle slices. */
4557
+ enqueue(task, options) {
4558
+ if (options?.key) {
4559
+ this.queue = this.queue.filter((item) => item.key !== options.key);
4560
+ }
4561
+ this.queue.push({ key: options?.key, task });
4562
+ void this.drainQueue();
4563
+ }
4564
+ //#endregion
4565
+ //#region ---- Private ----
4566
+ async drainQueue() {
4567
+ if (this.running || this.pausedState()) {
4568
+ return;
4569
+ }
4570
+ this.running = true;
4571
+ try {
4572
+ while (this.queue.length > 0 && !this.pausedState()) {
4573
+ const next = this.queue.shift();
4574
+ if (!next)
4575
+ break;
4576
+ await this.waitForIdle();
4577
+ try {
4578
+ await next.task();
4579
+ }
4580
+ catch (err) {
4581
+ const message = err instanceof Error ? err.message : String(err);
4582
+ console.error('[AXPIdleScheduler] Task failed:', message);
4583
+ }
4584
+ }
4585
+ }
4586
+ finally {
4587
+ this.running = false;
4588
+ if (this.queue.length > 0 && !this.pausedState()) {
4589
+ void this.drainQueue();
4590
+ }
4591
+ }
4592
+ }
4593
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPIdleSchedulerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
4594
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPIdleSchedulerService, providedIn: 'root' }); }
4595
+ }
4596
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPIdleSchedulerService, decorators: [{
4597
+ type: Injectable,
4598
+ args: [{ providedIn: 'root' }]
4599
+ }] });
4600
+
4601
+ //#region ---- Idle scheduler types ----
4602
+ //#endregion
4603
+
3816
4604
  //#region ---- Tag Types ----
3817
4605
  //#endregion
3818
4606
 
@@ -4066,67 +4854,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
4066
4854
  * - Review = 'review' - Review content
4067
4855
  * - Recommend = 'recommend' - Recommend content
4068
4856
  */
4069
- var AXPSystemActionType;
4070
- (function (AXPSystemActionType) {
4071
- AXPSystemActionType["View"] = "view";
4072
- AXPSystemActionType["Create"] = "create";
4073
- AXPSystemActionType["Update"] = "update";
4074
- AXPSystemActionType["Delete"] = "delete";
4075
- AXPSystemActionType["Approve"] = "approve";
4076
- AXPSystemActionType["Reject"] = "reject";
4077
- AXPSystemActionType["Submit"] = "submit";
4078
- AXPSystemActionType["Export"] = "export";
4079
- AXPSystemActionType["Import"] = "import";
4080
- AXPSystemActionType["Print"] = "print";
4081
- AXPSystemActionType["Assign"] = "assign";
4082
- AXPSystemActionType["Lock"] = "lock";
4083
- AXPSystemActionType["Unlock"] = "unlock";
4084
- AXPSystemActionType["Share"] = "share";
4085
- AXPSystemActionType["Configure"] = "configure";
4086
- AXPSystemActionType["Reorder"] = "reorder";
4087
- AXPSystemActionType["Preview"] = "preview";
4088
- AXPSystemActionType["Duplicate"] = "duplicate";
4089
- AXPSystemActionType["Archive"] = "archive";
4090
- AXPSystemActionType["Publish"] = "publish";
4091
- AXPSystemActionType["Unpublish"] = "unpublish";
4092
- AXPSystemActionType["Upload"] = "upload";
4093
- AXPSystemActionType["Download"] = "download";
4094
- AXPSystemActionType["Copy"] = "copy";
4095
- AXPSystemActionType["Move"] = "move";
4096
- AXPSystemActionType["Rename"] = "rename";
4097
- AXPSystemActionType["Restore"] = "restore";
4098
- AXPSystemActionType["Manage"] = "manage";
4099
- AXPSystemActionType["Info"] = "info";
4100
- AXPSystemActionType["Confirm"] = "confirm";
4101
- AXPSystemActionType["Design"] = "design";
4102
- AXPSystemActionType["VersionHistory"] = "version-history";
4103
- AXPSystemActionType["Compare"] = "compare";
4104
- AXPSystemActionType["Comments"] = "comments";
4105
- AXPSystemActionType["Sign"] = "sign";
4106
- AXPSystemActionType["Setup"] = "setup";
4107
- AXPSystemActionType["Send"] = "send";
4108
- AXPSystemActionType["Report"] = "report";
4109
- AXPSystemActionType["Sent"] = "sent";
4110
- AXPSystemActionType["Review"] = "review";
4111
- AXPSystemActionType["Generate"] = "generate";
4112
- AXPSystemActionType["Refresh"] = "refresh";
4113
- AXPSystemActionType["Reload"] = "reload";
4114
- AXPSystemActionType["Search"] = "search";
4115
- AXPSystemActionType["Filter"] = "filter";
4116
- AXPSystemActionType["Sort"] = "sort";
4117
- AXPSystemActionType["Start"] = "start";
4118
- AXPSystemActionType["Stop"] = "stop";
4119
- AXPSystemActionType["Pause"] = "pause";
4120
- AXPSystemActionType["Cancel"] = "cancel";
4121
- AXPSystemActionType["Close"] = "close";
4122
- AXPSystemActionType["Complete"] = "complete";
4123
- AXPSystemActionType["Save"] = "save";
4124
- AXPSystemActionType["SaveAs"] = "save-as";
4125
- AXPSystemActionType["Sync"] = "sync";
4126
- AXPSystemActionType["Reset"] = "reset";
4127
- AXPSystemActionType["Clear"] = "clear";
4128
- AXPSystemActionType["Distribution"] = "distribution";
4129
- })(AXPSystemActionType || (AXPSystemActionType = {}));
4130
4857
  const i18n = (key) => `@general:actions.${key}`;
4131
4858
  const AXPSystemActions = Object.freeze({
4132
4859
  View: {
@@ -4886,27 +5613,7 @@ function getActionButton(actionType) {
4886
5613
  };
4887
5614
  }
4888
5615
 
4889
- function getNestedKeys(obj, prefix = '') {
4890
- let keys = [];
4891
- for (const key in obj) {
4892
- if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
4893
- keys = [...keys, ...getNestedKeys(obj[key], prefix + key + '.')];
4894
- }
4895
- else {
4896
- keys.push(prefix + key);
4897
- }
4898
- }
4899
- return keys;
4900
- }
4901
- /** Normalizes definition categories to an array for iteration and serialization. */
4902
- function normalizeDefinitionCategories(categories) {
4903
- if (categories == null) {
4904
- return [];
4905
- }
4906
- return Array.isArray(categories) ? categories : [categories];
4907
- }
4908
-
4909
- //#region ---- Imports ----
5616
+ //#region ---- Core-only data types (CDK-coupled) ----
4910
5617
  //#endregion
4911
5618
 
4912
5619
  function applySystemActionDefault(action, type) {
@@ -4939,39 +5646,6 @@ class AXPIconLogoConfig {
4939
5646
  }
4940
5647
  }
4941
5648
 
4942
- var AXPPlatformScope;
4943
- (function (AXPPlatformScope) {
4944
- AXPPlatformScope["Platform"] = "P";
4945
- AXPPlatformScope["Tenant"] = "T";
4946
- AXPPlatformScope["User"] = "U";
4947
- })(AXPPlatformScope || (AXPPlatformScope = {}));
4948
- ;
4949
- function resolvePlatformScopeKey(name) {
4950
- const scopeMap = {
4951
- platform: AXPPlatformScope.Platform,
4952
- tenant: AXPPlatformScope.Tenant,
4953
- user: AXPPlatformScope.User,
4954
- };
4955
- return scopeMap[name.toLowerCase()] ?? AXPPlatformScope.User;
4956
- }
4957
- function resolvePlatformScopeName(scope) {
4958
- const scopeMap = {
4959
- P: 'platform',
4960
- T: 'tenant',
4961
- U: 'user',
4962
- };
4963
- return scopeMap[scope] ?? 'user';
4964
- }
4965
-
4966
- var AXPExportTemplateToken;
4967
- (function (AXPExportTemplateToken) {
4968
- AXPExportTemplateToken["Date"] = "{date}";
4969
- AXPExportTemplateToken["Time"] = "{time}";
4970
- AXPExportTemplateToken["User"] = "{user}";
4971
- AXPExportTemplateToken["ReportTitle"] = "{title}";
4972
- AXPExportTemplateToken["UUID"] = "{uuid}";
4973
- })(AXPExportTemplateToken || (AXPExportTemplateToken = {}));
4974
-
4975
5649
  class AXPCountdownPipe {
4976
5650
  constructor() {
4977
5651
  this.calendarService = inject(AXCalendarService);
@@ -5742,5 +6416,5 @@ function generateKebabCase(title) {
5742
6416
  * Generated bundle index. Do not edit.
5743
6417
  */
5744
6418
 
5745
- export { AXHighlightService, AXPActivityLogProvider, AXPActivityLogService, AXPAppStartUpProvider, AXPAppStartUpService, AXPBroadcastEventService, AXPCatalogScopeDefinitionProviderService, AXPCatalogScopeDefinitionsDataSourceDefinition, AXPColorPaletteProvider, AXPColorPaletteService, AXPColumnWidthService, AXPComponentLogoConfig, AXPComponentSlot, AXPComponentSlotDirective, AXPComponentSlotModule, AXPComponentSlotRegistryService, AXPContentCheckerDirective, AXPContextChangeEvent, AXPContextDefinitionProviderService, AXPContextStore, AXPCountdownPipe, AXPDataGenerator, AXPDataSourceDefinitionProviderService, AXPDblClickDirective, AXPDefaultColorPalettesProvider, AXPDeviceService, AXPDeviceType, AXPDistributedEventListenerService, AXPElementDataDirective, AXPExportTemplateToken, AXPExpressionEvaluatorScopeProviderContext, AXPExpressionEvaluatorScopeProviderService, AXPExpressionEvaluatorService, AXPFeatureDefinitionProviderContext, AXPGridLayoutDirective, AXPHookService, AXPIconLogoConfig, AXPImageUrlLogoConfig, AXPKeyboardShortcutPriority, AXPKeyboardShortcutRegistry, AXPModuleManifestModule, AXPModuleManifestRegistry, AXPModuleManifestsDataSourceDefinition, AXPPlatformCoreDataSourceKeys, AXPPlatformScope, AXPScreenSize, AXPSystemActionType, AXPSystemActions, AXPTagProvider, AXPTagService, AXP_ACTIVITY_LOG_PROVIDER, AXP_CATALOG_SCOPE_DEFINITION_PROVIDER, AXP_COLOR_PALETTE_PROVIDER, AXP_COLUMN_WIDTH_PROVIDER, AXP_CONTEXT_DEFINITION_PROVIDER, AXP_DATASOURCE_DEFINITION_PROVIDER, AXP_DISTRIBUTED_EVENT_LISTENER_PROVIDER, AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER, AXP_FEATURE_DEFINITION_PROVIDER, AXP_MODULE_MANIFEST_PROVIDER, AXP_SESSION_SERVICE, AXP_TAG_PROVIDER, AX_OVERLAY_CONTAINER_SELECTOR, AX_OVERLAY_PANE_SELECTOR, applyFilterArray, applyPagination, applyQueryArray, applySortArray, applySystemActionDefault, buildLocaleTextMapValue, captureFormContextBaseline, chordToKbdItemKeys, cleanDeep, coerceUnknownToBoolean, coerceUnknownToDate, coerceUnknownToFiniteNumber, coerceUnknownToTrimmedString, compareMultiLanguageStrings, containsHtmlMarkup, createProviderWithInjectionContext, defaultColumnWidthProvider, extractNestedFieldsWildcard, extractTextFromHtml, extractValue, findOverlayContainerAncestor, formatKeyboardShortcutChord, formatKeyboardShortcutChords, generateKebabCase, getActionButton, getChangedPaths, getDetailedChanges, getEnumValues, getNestedKeys, getNestedVisibleOverlayPanes, getPrimaryKeyboardShortcutChord, getSmart, getSystemActions, getTopVisibleOverlayContainer, getVisibleAnchoredOverlayPanes, getVisibleOverlayContainers, hasForegroundOverlayLayer, isFormContextDirty, isFormValueEqual, isHorizontalDirectionalShortcutKey, isKeyboardTargetInsideEditableField, isLocaleStringMap, isMacPlatform, isSelectionValueEqual, isVisibleOverlayElement, matchesKeyboardShortcutChord, mirrorHorizontalDirectionalKey, normalizeDefinitionCategories, normalizeKeyboardShortcut, normalizeKeyboardShortcuts, objectKeyValueTransforms, parseKeyboardShortcutChord, provideLazyProvider, resolveActionLook, resolveDisplayShortcutChord, resolveEffectiveDirectionBehavior, resolvePlatformScopeKey, resolvePlatformScopeName, resolveSemanticDirectionalKey, searchInMultiLanguageString, setSmart, shouldDeferEscapeToOpenOverlay, shouldUseLocaleMapShape, sortByMultiLanguageString, unwrapValueProperty };
6419
+ export { AXHighlightService, AXPActivityLogProvider, AXPActivityLogService, AXPAppStartUpProvider, AXPAppStartUpService, AXPBroadcastEventService, AXPCatalogScopeDefinitionProviderService, AXPCatalogScopeDefinitionsDataSourceDefinition, AXPColorPaletteProvider, AXPColorPaletteService, AXPColumnWidthService, AXPComponentLogoConfig, AXPComponentSlot, AXPComponentSlotDirective, AXPComponentSlotModule, AXPComponentSlotRegistryService, AXPContentCheckerDirective, AXPContextChangeEvent, AXPContextDefinitionProviderService, AXPContextEvalFactory, AXPContextStore, AXPCountdownPipe, AXPDataGenerator, AXPDataSourceDefinitionProviderService, AXPDblClickDirective, AXPDebugService, AXPDefaultColorPalettesProvider, AXPDeviceService, AXPDeviceType, AXPDistributedEventListenerService, AXPElementDataDirective, AXPExpressionEvaluatorScopeProviderContext, AXPExpressionEvaluatorScopeProviderService, AXPExpressionEvaluatorService, AXPFeatureDefinitionProviderContext, AXPGridLayoutDirective, AXPHookService, AXPIconLogoConfig, AXPIdleSchedulerService, AXPImageUrlLogoConfig, AXPKeyboardShortcutPriority, AXPKeyboardShortcutRegistry, AXPModuleManifestModule, AXPModuleManifestRegistry, AXPModuleManifestsDataSourceDefinition, AXPScreenSize, AXPSystemActions, AXPTagProvider, AXPTagService, AXP_ACTIVITY_LOG_PROVIDER, AXP_CATALOG_SCOPE_DEFINITION_PROVIDER, AXP_COLOR_PALETTE_PROVIDER, AXP_COLUMN_WIDTH_PROVIDER, AXP_CONTEXT_DEFINITION_PROVIDER, AXP_DATASOURCE_DEFINITION_PROVIDER, AXP_DISTRIBUTED_EVENT_LISTENER_PROVIDER, AXP_EXPRESSION_EVALUATOR_SCOPE_PROVIDER, AXP_FEATURE_DEFINITION_PROVIDER, AXP_MODULE_MANIFEST_PROVIDER, AXP_SESSION_SERVICE, AXP_TAG_PROVIDER, AX_OVERLAY_CONTAINER_SELECTOR, AX_OVERLAY_PANE_SELECTOR, DEFAULT_CANCEL_DIALOG_ACTION_SHORTCUTS, DEFAULT_SUBMIT_DIALOG_ACTION_SHORTCUTS, FORM_DIRTY_TRACE_NS, OVERLAY_DETAILS_VIEW_TRACE_NS, OVERLAY_LAYOUT_TRACE_NS, OVERLAY_ROOT_LAYOUT_TRACE_NS, OVERLAY_WIDGET_SETTLE_TRACE_NS, applyContextEvalPipes, applyFilterArray, applyPagination, applyQueryArray, applySortArray, applySystemActionDefault, buildLocaleTextMapValue, captureFormContextBaseline, chordToKbdItemKeys, cleanDeep, coerceUnknownToBoolean, coerceUnknownToDate, coerceUnknownToFiniteNumber, coerceUnknownToTrimmedString, compareMultiLanguageStrings, containsHtmlMarkup, contextEvalWithPipes, createProviderWithInjectionContext, defaultColumnWidthProvider, extractNestedFieldsWildcard, extractTextFromHtml, extractValue, findOverlayContainerAncestor, formatKeyboardShortcutChord, formatKeyboardShortcutChords, generateKebabCase, getActionButton, getChangedPaths, getDetailedChanges, getEnumValues, getNestedVisibleOverlayPanes, getPrimaryKeyboardShortcutChord, getSmart, getSystemActions, getTopVisibleOverlayContainer, getVisibleAnchoredOverlayPanes, getVisibleOverlayContainers, hasForegroundOverlayLayer, isDialogShortcutAllowedInEditableField, isFormContextDirty, isFormValueEqual, isHorizontalDirectionalShortcutKey, isKeyboardTargetInsideEditableField, isLocaleStringMap, isMacPlatform, isSelectionValueEqual, isVisibleOverlayElement, matchesDialogActionShortcut, matchesKeyboardShortcutChord, mirrorHorizontalDirectionalKey, normalizeContextEvalPipes, normalizeDialogActionShortcutChord, normalizeExpressionTemplate, normalizeKeyboardShortcut, normalizeKeyboardShortcuts, objectKeyValueTransforms, parseDialogActionShortcut, parseKeyboardShortcutChord, provideLazyProvider, registerPopupFooterKeyboardShortcuts, resolveActionLook, resolveConfiguredFooterActionShortcuts, resolveDialogActionShortcuts, resolveDisplayShortcutChord, resolveEffectiveDirectionBehavior, resolveSemanticDirectionalKey, searchInMultiLanguageString, setSmart, setupPopupFooterKeyboardShortcuts, shouldDeferEscapeToOpenOverlay, shouldRestoreSavedMultiLanguageShape, shouldTriggerDialogActionShortcut, shouldUseLocaleMapShape, sortByMultiLanguageString, summarizeFormContextDirtyDiff, unwrapValueProperty };
5746
6420
  //# sourceMappingURL=acorex-platform-core.mjs.map