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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/fesm2022/acorex-platform-auth.mjs +10 -2
  2. package/fesm2022/acorex-platform-auth.mjs.map +1 -1
  3. package/fesm2022/{acorex-platform-common-common-settings.provider-Bi1RYif5.mjs → acorex-platform-common-common-settings.provider-Ytey9uhY.mjs} +15 -1
  4. package/fesm2022/acorex-platform-common-common-settings.provider-Ytey9uhY.mjs.map +1 -0
  5. package/fesm2022/acorex-platform-common.mjs +3792 -1679
  6. package/fesm2022/acorex-platform-common.mjs.map +1 -1
  7. package/fesm2022/acorex-platform-core.mjs +1112 -103
  8. package/fesm2022/acorex-platform-core.mjs.map +1 -1
  9. package/fesm2022/acorex-platform-layout-builder.mjs +53 -170
  10. package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
  11. package/fesm2022/acorex-platform-layout-components.mjs +70 -46
  12. package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
  13. package/fesm2022/acorex-platform-layout-designer.mjs +199 -126
  14. package/fesm2022/acorex-platform-layout-designer.mjs.map +1 -1
  15. package/fesm2022/{acorex-platform-layout-entity-attachments-page.component-D8iQnT-R.mjs → acorex-platform-layout-entity-attachments-page.component-B0EkdqvH.mjs} +6 -1
  16. package/fesm2022/acorex-platform-layout-entity-attachments-page.component-B0EkdqvH.mjs.map +1 -0
  17. package/fesm2022/acorex-platform-layout-entity.mjs +341 -418
  18. package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
  19. package/fesm2022/acorex-platform-layout-views.mjs +675 -301
  20. package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
  21. package/fesm2022/acorex-platform-layout-widget-core.mjs +115 -74
  22. package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
  23. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-edit-popup.component-BcpRkpJp.mjs → acorex-platform-layout-widgets-tabular-data-edit-popup.component-DjpZU6gz.mjs} +2 -2
  24. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-edit-popup.component-BcpRkpJp.mjs.map → acorex-platform-layout-widgets-tabular-data-edit-popup.component-DjpZU6gz.mjs.map} +1 -1
  25. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-view-popup.component-DQtK4lxl.mjs → acorex-platform-layout-widgets-tabular-data-view-popup.component-gX-3Kx9I.mjs} +2 -2
  26. package/fesm2022/{acorex-platform-layout-widgets-tabular-data-view-popup.component-DQtK4lxl.mjs.map → acorex-platform-layout-widgets-tabular-data-view-popup.component-gX-3Kx9I.mjs.map} +1 -1
  27. package/fesm2022/acorex-platform-layout-widgets.mjs +184 -655
  28. package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
  29. package/fesm2022/acorex-platform-themes-default-error-401.component-B1nsdpTY.mjs +48 -0
  30. package/fesm2022/acorex-platform-themes-default-error-401.component-B1nsdpTY.mjs.map +1 -0
  31. package/fesm2022/acorex-platform-themes-default-error-404.component-D4UvRe8u.mjs +42 -0
  32. package/fesm2022/acorex-platform-themes-default-error-404.component-D4UvRe8u.mjs.map +1 -0
  33. package/fesm2022/acorex-platform-themes-default.mjs +76 -32
  34. package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
  35. package/package.json +1 -1
  36. package/types/acorex-platform-auth.d.ts +2 -0
  37. package/types/acorex-platform-common.d.ts +891 -259
  38. package/types/acorex-platform-core.d.ts +284 -40
  39. package/types/acorex-platform-layout-builder.d.ts +10 -22
  40. package/types/acorex-platform-layout-components.d.ts +9 -7
  41. package/types/acorex-platform-layout-entity.d.ts +37 -41
  42. package/types/acorex-platform-layout-views.d.ts +125 -67
  43. package/types/acorex-platform-layout-widget-core.d.ts +53 -61
  44. package/types/acorex-platform-layout-widgets.d.ts +33 -20
  45. package/types/acorex-platform-themes-default.d.ts +14 -4
  46. package/fesm2022/acorex-platform-common-common-settings.provider-Bi1RYif5.mjs.map +0 -1
  47. package/fesm2022/acorex-platform-layout-entity-attachments-page.component-D8iQnT-R.mjs.map +0 -1
  48. package/fesm2022/acorex-platform-themes-default-error-401.component-C7EYJzSr.mjs +0 -31
  49. package/fesm2022/acorex-platform-themes-default-error-401.component-C7EYJzSr.mjs.map +0 -1
  50. package/fesm2022/acorex-platform-themes-default-error-404.component-7MVLMwIa.mjs +0 -25
  51. package/fesm2022/acorex-platform-themes-default-error-404.component-7MVLMwIa.mjs.map +0 -1
@@ -2,7 +2,7 @@ import { AXToastService } from '@acorex/components/toast';
2
2
  import * as i6 from '@acorex/core/translation';
3
3
  import { AXTranslationService, AXTranslationModule, resolveMultiLanguageString, translateSync } from '@acorex/core/translation';
4
4
  import * as i4$3 from '@acorex/platform/common';
5
- import { AXPEntityCommandScope, AXPSettingsService, AXPCommonSettings, AXPFilterOperatorMiddlewareService, isCardFieldBadgeDisplay, resolveCardFieldBadgeColor, getEntityInfo, resolveEnabledMasterListLayouts, resolveDefaultMasterListLayout, AXPRefreshEvent, AXPReloadEvent, AXPCleanNestedFilters, AXPFileTypeProviderService, AXPFileStorageService, AXPFileActionsService, AXPDefaultMultiLanguageConfigService, withDefaultMultiLanguageOnWidgetNodeTree, AXPEntityQueryType, AXPWorkflowNavigateAction, AXPToastAction, AXP_SEARCH_DEFINITION_PROVIDER, AXPMenuItemsDataSourceDefinition } from '@acorex/platform/common';
5
+ import { AXPNotFoundError, AXPEntityCommandScope, axpNavigateAppPath, AXPSettingsService, AXPCommonSettings, normalizeEntityDisplayTemplate, isUnresolvedEntityDisplayTemplate, AXPFilterOperatorMiddlewareService, isCardFieldBadgeDisplay, resolveCardFieldBadgeColor, getEntityInfo, resolveEnabledMasterListLayouts, resolveDefaultMasterListLayout, AXPRefreshEvent, AXPReloadEvent, axpRedirectToNotFound, AXPCleanNestedFilters, AXPFileTypeProviderService, AXPFileStorageService, buildEntitySearchTitleContext, resolveEntityRowTitleTemplate, AXPFileActionsService, axpIsEntityRecordNotFound, AXPDefaultMultiLanguageConfigService, withDefaultMultiLanguageOnWidgetNodeTree, AXPEntityQueryType, AXPWorkflowNavigateAction, AXP_NOT_FOUND_ROUTE, AXP_PROTECTED_ROUTE_GUARDS, AXPToastAction, AXP_SEARCH_DEFINITION_PROVIDER, AXPMenuItemsDataSourceDefinition } from '@acorex/platform/common';
6
6
  import * as i0 from '@angular/core';
7
7
  import { InjectionToken, inject, Injector, runInInjectionContext, Injectable, input, viewChild, signal, computed, ElementRef, ChangeDetectionStrategy, Component, ApplicationRef, EnvironmentInjector, createComponent, NgModule, ChangeDetectorRef, effect, Input, afterNextRender, untracked, ViewEncapsulation, viewChildren, linkedSignal, HostBinding, output, makeEnvironmentProviders } from '@angular/core';
8
8
  import { Subject, takeUntil } from 'rxjs';
@@ -12,7 +12,7 @@ import { AXPWidgetsCatalog, AXPWidgetCoreModule, AXPPageStatus, AXPWidgetRegistr
12
12
  import { AXPSystemActionType, AXPDeviceService, AXPExpressionEvaluatorService, AXPBroadcastEventService, applyFilterArray, applySortArray, resolveActionLook, AXPDistributedEventListenerService, AXPPlatformScope, AXHighlightService, extractValue, setSmart, getChangedPaths, objectKeyValueTransforms, AXPHookService, AXPDataGenerator, AXPComponentSlotModule, AXPContextStore, AXPColumnWidthService, AXPModuleManifestRegistry, defaultColumnWidthProvider, AXP_COLUMN_WIDTH_PROVIDER, AXP_DATASOURCE_DEFINITION_PROVIDER, AXPModuleManifestsDataSourceDefinition } from '@acorex/platform/core';
13
13
  import { cloneDeep, merge, get, castArray, set, orderBy, omit, isNil, isEmpty, isEqual as isEqual$1, isArray, isString } from 'lodash-es';
14
14
  import { transform, isEqual } from 'lodash';
15
- import { AXPSessionService, AXPAuthGuard, AXPPermissionDefinitionsDataSourceDefinition } from '@acorex/platform/auth';
15
+ import { AXPSessionService, AXPPermissionDefinitionsDataSourceDefinition } from '@acorex/platform/auth';
16
16
  import { Router, ActivatedRoute, RouterModule, ROUTES } from '@angular/router';
17
17
  import { defineCommand, AXP_COMMAND_DEFINITION_CATEGORY_ENTITY, AXPCommandService, AXPQueryService, AXPQueryExecutor, AXPCommandExecutor, provideCommandSetups, provideQuerySetups, AXPCommandRegistry, AXPQueryRegistry } from '@acorex/platform/runtime';
18
18
  import * as i1 from '@acorex/components/button';
@@ -25,7 +25,7 @@ import { AXFormatService } from '@acorex/core/format';
25
25
  import * as i5 from '@angular/common';
26
26
  import { CommonModule, AsyncPipe, NgClass } from '@angular/common';
27
27
  import { AXPThemeLayoutBlockComponent, AXPPreloadFiltersComponent, AXP_PAGE_COMPONENT_PROVIDER, AXPStateMessageComponent, AXPColumnItemListComponent, AXPDataSelectorService, AXPPageComponentRegistryService } from '@acorex/platform/layout/components';
28
- import { AXPPageLayoutBaseComponent, AXPPageLayoutComponent, AXPPageComponentRendererDirective, AXPPageComponentInstanceRegistryService } from '@acorex/platform/layout/views';
28
+ import { AXPPageLayoutBaseComponent, AXPPageLayoutComponent, AXPPageComponentRendererDirective, AXPPageComponentInstanceRegistryService, axpDetailsViewCanDeactivateGuard } from '@acorex/platform/layout/views';
29
29
  import { AXDataSource } from '@acorex/cdk/common';
30
30
  import { AXP_ENTITY_CRUD_SETUP } from '@acorex/platform/domain';
31
31
  export { AXP_ENTITY_DEFINITION_CRUD_SERVICE } from '@acorex/platform/domain';
@@ -823,7 +823,7 @@ class AXPEntityDefinitionRegistryService {
823
823
  throw error; // Rethrow to allow error handling by caller
824
824
  }
825
825
  if (!config) {
826
- throw new Error(`Invalid entity name: ${key}`);
826
+ throw new AXPNotFoundError(`Invalid entity name: ${key}`);
827
827
  }
828
828
  }
829
829
  return config;
@@ -902,6 +902,7 @@ function entityMasterDeleteAction() {
902
902
  type: AXPSystemActionType.Delete,
903
903
  scope: AXPEntityCommandScope.Individual,
904
904
  order: 100,
905
+ shortcuts: ['ctrl+shift+delete'],
905
906
  };
906
907
  }
907
908
  function entityMasterCrudActions(options) {
@@ -2498,8 +2499,7 @@ class AXPOpenEntityDetailsCommand {
2498
2499
  };
2499
2500
  }
2500
2501
  const url = `/${this.sessionService.application?.name}/m/${module}/e/${entityName}/${data.id}/view`;
2501
- // Navigate to the entity details page
2502
- this.router.navigate([url]);
2502
+ await axpNavigateAppPath(this.router, url);
2503
2503
  return {
2504
2504
  success: true,
2505
2505
  };
@@ -2972,17 +2972,6 @@ var viewEntityDetails_command = /*#__PURE__*/Object.freeze({
2972
2972
  });
2973
2973
 
2974
2974
  //#region ---- Template resolution ----
2975
- /**
2976
- * Normalizes lookup/search display templates for row-based formatting.
2977
- * Converts `context.eval('path')` expressions to `{{ path }}` and single braces to mustache form.
2978
- */
2979
- function normalizeLookupDisplayTemplate(template) {
2980
- const withContextEval = template.replace(/\{\{\s*context\.eval\(['"]([^'"]+)['"]\)\s*\}\}/g, '{{ $1 }}');
2981
- if (withContextEval.includes('{{')) {
2982
- return withContextEval;
2983
- }
2984
- return withContextEval.replace(/\{/g, '{{').replace(/\}/g, '}}');
2985
- }
2986
2975
  /**
2987
2976
  * Resolves the display template for a lookup item.
2988
2977
  * Priority: explicit `displayFormat` → entity `formats.lookup` (template) → entity `formats.searchResult.title`.
@@ -2991,18 +2980,18 @@ function normalizeLookupDisplayTemplate(template) {
2991
2980
  function resolveLookupDisplayTemplate(entity, options) {
2992
2981
  const explicit = options.displayFormat?.trim();
2993
2982
  if (explicit) {
2994
- return normalizeLookupDisplayTemplate(explicit);
2983
+ return normalizeEntityDisplayTemplate(explicit);
2995
2984
  }
2996
2985
  if (options.textField?.trim()) {
2997
2986
  return undefined;
2998
2987
  }
2999
2988
  const lookupFormat = entity?.formats?.lookup?.trim();
3000
2989
  if (lookupFormat && (lookupFormat.includes('{{') || lookupFormat.includes('{'))) {
3001
- return normalizeLookupDisplayTemplate(lookupFormat);
2990
+ return normalizeEntityDisplayTemplate(lookupFormat);
3002
2991
  }
3003
2992
  const searchTitle = entity?.formats?.searchResult?.title?.trim();
3004
2993
  if (searchTitle) {
3005
- return normalizeLookupDisplayTemplate(searchTitle);
2994
+ return normalizeEntityDisplayTemplate(searchTitle);
3006
2995
  }
3007
2996
  return undefined;
3008
2997
  }
@@ -3043,7 +3032,7 @@ function formatLookupItemDisplay(item, entity, options, formatService, resolveMu
3043
3032
  const formatted = formatService.format(template, 'string', item);
3044
3033
  if (formatted) {
3045
3034
  const text = String(formatted);
3046
- if (!isUnresolvedLookupDisplayTemplate(text)) {
3035
+ if (!isUnresolvedEntityDisplayTemplate(text)) {
3047
3036
  return text;
3048
3037
  }
3049
3038
  }
@@ -3064,10 +3053,6 @@ function formatLookupItemDisplay(item, entity, options, formatService, resolveMu
3064
3053
  }
3065
3054
  return String(raw);
3066
3055
  }
3067
- /** True when a formatted label still contains unresolved template markers. */
3068
- function isUnresolvedLookupDisplayTemplate(value) {
3069
- return /\{\{/.test(value) || /context\.eval\s*\(/.test(value);
3070
- }
3071
3056
  //#endregion
3072
3057
 
3073
3058
  class AXPEntityDetailPopoverComponent {
@@ -3107,7 +3092,7 @@ class AXPEntityDetailPopoverComponent {
3107
3092
  }
3108
3093
  }
3109
3094
  const preset = this.displayTitle()?.trim();
3110
- if (preset && !isUnresolvedLookupDisplayTemplate(preset)) {
3095
+ if (preset && !isUnresolvedEntityDisplayTemplate(preset)) {
3111
3096
  return preset;
3112
3097
  }
3113
3098
  const fallbackItem = this.item();
@@ -3206,7 +3191,7 @@ class AXPEntityDetailPopoverComponent {
3206
3191
  resolveLookupDisplayLabel(item, entityDefinition) {
3207
3192
  const label = formatLookupItemDisplay(item, entityDefinition, { textField: this.textField() }, this.formatService, (value) => this.translation.resolve(value));
3208
3193
  const resolved = typeof label === 'string' ? label : this.translation.resolve(label);
3209
- return resolved && !isUnresolvedLookupDisplayTemplate(resolved) ? resolved : '';
3194
+ return resolved && !isUnresolvedEntityDisplayTemplate(resolved) ? resolved : '';
3210
3195
  }
3211
3196
  /**
3212
3197
  * Returns true if a value is meaningful for display (non-empty/non-null).
@@ -4356,6 +4341,7 @@ class AXPEntityCommandTriggerViewModel {
4356
4341
  this.hidden = action.hidden ?? false;
4357
4342
  this.disabled = action.disabled ?? false;
4358
4343
  this.default = action.default ?? false;
4344
+ this.shortcuts = action.shortcuts;
4359
4345
  this.scope = action.scope;
4360
4346
  this.order = action.order ?? 0;
4361
4347
  this.isChild = isChild;
@@ -6642,20 +6628,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
6642
6628
  type: Injectable,
6643
6629
  args: [{ providedIn: 'root' }]
6644
6630
  }] });
6645
- const AXPEntityListViewModelResolver = async (route, state, service = inject(AXPEntityListViewModelFactory)) => {
6631
+ const AXPEntityListViewModelResolver = async (route, state, service = inject(AXPEntityListViewModelFactory), router = inject(Router)) => {
6646
6632
  const moduleName = route.parent?.paramMap.get('module');
6647
6633
  const entityName = route.paramMap.get('entity');
6648
- const vm = await service.create(moduleName, entityName);
6649
- // Check if filters are provided in query params
6650
- const filtersParam = route.queryParamMap.get('filters');
6651
- if (filtersParam) {
6652
- const applied = vm.applyFiltersFromQueryParams(filtersParam);
6653
- if (applied) {
6654
- // Trigger filter and sort application
6655
- await vm.applyFilterAndSort();
6634
+ try {
6635
+ const vm = await service.create(moduleName, entityName);
6636
+ // Check if filters are provided in query params
6637
+ const filtersParam = route.queryParamMap.get('filters');
6638
+ if (filtersParam) {
6639
+ const applied = vm.applyFiltersFromQueryParams(filtersParam);
6640
+ if (applied) {
6641
+ // Trigger filter and sort application
6642
+ await vm.applyFilterAndSort();
6643
+ }
6644
+ }
6645
+ return vm;
6646
+ }
6647
+ catch (error) {
6648
+ if (error instanceof AXPNotFoundError) {
6649
+ return axpRedirectToNotFound(router);
6656
6650
  }
6651
+ throw error;
6657
6652
  }
6658
- return vm;
6659
6653
  };
6660
6654
 
6661
6655
  const AXPEntityDeletedEvent = createWorkFlowEvent('[Entity] Deleted');
@@ -6706,7 +6700,7 @@ class AXPEntityPerformDeleteAction extends AXPWorkflowAction {
6706
6700
  dialog = this.loadingDialog.show({
6707
6701
  title: await this.translationService.translateAsync('@general:workflow.deleting'),
6708
6702
  mode: 'determinate',
6709
- status: 'Deleting...',
6703
+ status: await this.translationService.translateAsync('@general:workflow.deleting'),
6710
6704
  progressValue: 0,
6711
6705
  text: `0/${idLength}`,
6712
6706
  buttons: [
@@ -7467,10 +7461,19 @@ class AXPEntityPreloadFiltersViewModel {
7467
7461
  const AXPEntityPreloadFiltersViewModelResolver = async (route, state) => {
7468
7462
  const injector = inject(Injector);
7469
7463
  const entityRegistry = inject(AXPEntityDefinitionRegistryService);
7464
+ const router = inject(Router);
7470
7465
  const moduleName = route.parent?.paramMap.get('module');
7471
7466
  const entityName = route.paramMap.get('entity');
7472
- const entity = await entityRegistry.resolve(moduleName, entityName);
7473
- return new AXPEntityPreloadFiltersViewModel(injector, entity);
7467
+ try {
7468
+ const entity = await entityRegistry.resolve(moduleName, entityName);
7469
+ return new AXPEntityPreloadFiltersViewModel(injector, entity);
7470
+ }
7471
+ catch (error) {
7472
+ if (error instanceof AXPNotFoundError) {
7473
+ return axpRedirectToNotFound(router);
7474
+ }
7475
+ throw error;
7476
+ }
7474
7477
  };
7475
7478
  //#endregion
7476
7479
 
@@ -8035,7 +8038,7 @@ class AXMAttachmentsPageComponentProvider {
8035
8038
  return [
8036
8039
  {
8037
8040
  key: ATTACHMENTS_PAGE_COMPONENT_KEY,
8038
- loader: () => import('./acorex-platform-layout-entity-attachments-page.component-D8iQnT-R.mjs').then((m) => m.AXMAttachmentsPageComponent),
8041
+ loader: () => import('./acorex-platform-layout-entity-attachments-page.component-B0EkdqvH.mjs').then((m) => m.AXMAttachmentsPageComponent),
8039
8042
  },
8040
8043
  ];
8041
8044
  }
@@ -8354,7 +8357,7 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
8354
8357
  this.noRecordsTitle = signal('', ...(ngDevMode ? [{ debugName: "noRecordsTitle" }] : /* istanbul ignore next */ []));
8355
8358
  this.currentSearchTerm = null;
8356
8359
  this.isRefreshing = false;
8357
- this.isUpdatingSelection = false; // Flag to prevent recursive updates during batch operations
8360
+ this.isUpdatingSelection = false; // Flag to prevent recursive updates during batch operations (multiple mode)
8358
8361
  /**
8359
8362
  * Computed property to check if we should show the "no search results" empty state.
8360
8363
  * Returns true when search is active, not searching, and no results found.
@@ -8421,9 +8424,8 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
8421
8424
  }
8422
8425
  });
8423
8426
  }
8424
- // Mark pre-selected nodes as selected in the child nodes (for visual display)
8425
- // ONLY do this during initial load, NOT during user selection changes
8426
- if (!this.isUpdatingSelection) {
8427
+ // Mark pre-selected leaf nodes on loaded children (multiple mode only; single uses controlledSelection)
8428
+ if (!this.isUpdatingSelection && this.allowMultiple()) {
8427
8429
  const selectedIds = this.selectedNodeIds();
8428
8430
  childNodes.forEach((node) => {
8429
8431
  const nodeId = String(node['id'] ?? '');
@@ -8431,38 +8433,6 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
8431
8433
  node['selected'] = true;
8432
8434
  }
8433
8435
  });
8434
- // After children load, programmatically select pre-selected nodes in tree component
8435
- // This ensures the tree's internal state matches our selectedNodeIds
8436
- // Skip this if we're in the middle of a selection update (user is selecting/deselecting)
8437
- const treeComponent = this.tree();
8438
- if (treeComponent && selectedIds.length > 0) {
8439
- // Use setTimeout to ensure nodes are in tree structure before selecting
8440
- setTimeout(() => {
8441
- // Double-check we're not in a selection update when timeout fires
8442
- if (this.isUpdatingSelection) {
8443
- return;
8444
- }
8445
- this.isUpdatingSelection = true;
8446
- try {
8447
- // Re-check selectedNodeIds at this point (might have changed)
8448
- const currentSelectedIds = this.selectedNodeIds();
8449
- childNodes.forEach((node) => {
8450
- const nodeId = String(node['id'] ?? '');
8451
- if (nodeId && currentSelectedIds.includes(nodeId) && nodeId !== 'all') {
8452
- try {
8453
- treeComponent.selectNode(nodeId);
8454
- }
8455
- catch {
8456
- // Node might not be in tree yet
8457
- }
8458
- }
8459
- });
8460
- }
8461
- finally {
8462
- this.isUpdatingSelection = false;
8463
- }
8464
- }, 10);
8465
- }
8466
8436
  }
8467
8437
  return childNodes;
8468
8438
  };
@@ -8488,6 +8458,60 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
8488
8458
  }
8489
8459
  //#endregion
8490
8460
  //#region ---- Private Methods ----
8461
+ /** Keeps at most one id when {@link allowMultiple} is false. */
8462
+ normalizeSelectedIds(ids) {
8463
+ const filtered = ids.filter((id) => id && id !== 'all');
8464
+ if (this.allowMultiple()) {
8465
+ return [...filtered];
8466
+ }
8467
+ return filtered.length > 0 ? [filtered[filtered.length - 1]] : [];
8468
+ }
8469
+ setSelectedNodeIds(ids) {
8470
+ this.selectedNodeIds.set(this.normalizeSelectedIds(ids));
8471
+ }
8472
+ /** Keeps widget selectedNodeIds in sync when the tree uses controlled single selection. */
8473
+ onControlledSelectedIdsChange(ids) {
8474
+ if (this.allowMultiple()) {
8475
+ return;
8476
+ }
8477
+ this.setSelectedNodeIds(ids);
8478
+ }
8479
+ async waitForTreeComponent(maxWaitMs = 3500) {
8480
+ const deadline = Date.now() + maxWaitMs;
8481
+ while (Date.now() < deadline) {
8482
+ const treeComponent = this.tree();
8483
+ if (treeComponent) {
8484
+ return treeComponent;
8485
+ }
8486
+ await new Promise((resolve) => setTimeout(resolve, 80));
8487
+ }
8488
+ return undefined;
8489
+ }
8490
+ /** Expands ancestor paths and reveals the initial selection via the tree API. */
8491
+ async revealInitialSelection(ids) {
8492
+ const normalizedIds = this.normalizeSelectedIds(ids);
8493
+ if (normalizedIds.length === 0) {
8494
+ return;
8495
+ }
8496
+ const treeComponent = await this.waitForTreeComponent();
8497
+ if (!treeComponent) {
8498
+ return;
8499
+ }
8500
+ try {
8501
+ if (this.allowMultiple()) {
8502
+ await this.restoreMultipleSelectionAfterReload(normalizedIds);
8503
+ }
8504
+ else {
8505
+ const chains = await this.buildAncestorChains(normalizedIds);
8506
+ const ancestorPaths = normalizedIds.map((id) => chains.get(id) ?? []);
8507
+ await treeComponent.selectAndReveal(normalizedIds, { ancestorPaths });
8508
+ }
8509
+ }
8510
+ catch (error) {
8511
+ console.error('Error revealing initial category selection:', error);
8512
+ }
8513
+ this.changeDetectorRef.markForCheck();
8514
+ }
8491
8515
  async initializeTree() {
8492
8516
  if (!this.entityKey()) {
8493
8517
  this.loading.set(false);
@@ -8512,12 +8536,12 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
8512
8536
  // Resolve initial selected IDs from data (popup) or input binding
8513
8537
  const fromData = this.selectedValues().filter((id) => id && id !== 'all');
8514
8538
  const fromInput = (this.selectedValuesInput() ?? []).filter((id) => id && id !== 'all');
8515
- const initialSelectedIds = fromData.length > 0 ? fromData : fromInput;
8539
+ const initialSelectedIds = this.normalizeSelectedIds(fromData.length > 0 ? fromData : fromInput);
8516
8540
  if (initialSelectedIds.length > 0) {
8517
8541
  await this.loadMissingNodeData(initialSelectedIds);
8518
8542
  // Re-fetch any node that has no parent info (batch query may return minimal fields)
8519
8543
  await this.ensureParentDataInCache(initialSelectedIds);
8520
- this.selectedNodeIds.set(initialSelectedIds);
8544
+ this.setSelectedNodeIds(initialSelectedIds);
8521
8545
  }
8522
8546
  }
8523
8547
  catch (error) {
@@ -8531,52 +8555,20 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
8531
8555
  const selectedIds = this.selectedNodeIds();
8532
8556
  if (selectedIds.length > 0) {
8533
8557
  this.initialExpandSyncDone = true;
8534
- this.runExpandAndSyncWhenTreeReady(selectedIds);
8558
+ void this.revealInitialSelection(selectedIds);
8535
8559
  }
8536
8560
  }
8537
8561
  /**
8538
8562
  * Called when popup data arrives after ngOnInit: load data, set selection, expand and sync.
8539
8563
  */
8540
8564
  runInitialExpandAndSync(ids) {
8541
- this.selectedNodeIds.set(ids);
8565
+ this.setSelectedNodeIds(ids);
8542
8566
  Promise.resolve()
8543
8567
  .then(() => this.loadMissingNodeData(ids))
8544
8568
  .then(() => this.ensureParentDataInCache(ids))
8545
- .then(() => {
8546
- this.runExpandAndSyncWhenTreeReady(ids);
8547
- })
8569
+ .then(() => this.revealInitialSelection(ids))
8548
8570
  .catch((err) => console.error('Error in runInitialExpandAndSync:', err));
8549
8571
  }
8550
- /**
8551
- * Runs expand path + sync selection once the tree viewChild is available.
8552
- * Uses retry loop so we don't run before the view has rendered the tree.
8553
- */
8554
- runExpandAndSyncWhenTreeReady(selectedIds) {
8555
- const maxWaitMs = 3500;
8556
- const pollMs = 80;
8557
- const start = Date.now();
8558
- const run = async () => {
8559
- while (Date.now() - start < maxWaitMs) {
8560
- const treeComponent = this.tree();
8561
- if (treeComponent) {
8562
- try {
8563
- await new Promise((resolve) => setTimeout(resolve, 320));
8564
- const ancestorChains = await this.buildAncestorChains(selectedIds);
8565
- await this.expandAncestorNodesInOrder(ancestorChains);
8566
- await new Promise((resolve) => setTimeout(resolve, 120));
8567
- await this.syncSelectionWithTree(selectedIds);
8568
- }
8569
- catch (error) {
8570
- console.error('Error syncing selection after tree render:', error);
8571
- }
8572
- this.changeDetectorRef.markForCheck();
8573
- return;
8574
- }
8575
- await new Promise((resolve) => setTimeout(resolve, pollMs));
8576
- }
8577
- };
8578
- run();
8579
- }
8580
8572
  //#endregion
8581
8573
  //#region ---- Public Methods ----
8582
8574
  /**
@@ -8981,8 +8973,7 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
8981
8973
  this.nodesExpandedDuringSearch = [];
8982
8974
  return;
8983
8975
  }
8984
- // Store current selected IDs before reload
8985
- const selectedIds = this.selectedNodeIds();
8976
+ const expandTargetIds = [...this.selectedNodeIds()];
8986
8977
  // Reload tree to show all nodes (no filtering)
8987
8978
  await treeComponent.reloadData();
8988
8979
  // Collapse nodes that were expanded during search (in reverse order - leaves first)
@@ -9002,39 +8993,38 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
9002
8993
  // Clear the stored expanded nodes
9003
8994
  this.expandedNodesBeforeSearch = [];
9004
8995
  // Restore selection state after tree reload
9005
- if (selectedIds.length > 0) {
9006
- // Wait for tree to stabilize after reload
9007
- await new Promise((resolve) => setTimeout(resolve, 100));
9008
- // Re-sync selection with tree
9009
- await this.restoreSelectionAfterReload(selectedIds);
8996
+ if (expandTargetIds.length > 0) {
8997
+ if (!this.allowMultiple()) {
8998
+ await this.revealInitialSelection(expandTargetIds);
8999
+ }
9000
+ else {
9001
+ await this.restoreMultipleSelectionAfterReload(expandTargetIds);
9002
+ }
9010
9003
  }
9011
9004
  }
9012
9005
  /**
9013
- * Restores selection state after tree reload.
9014
- * Expands ancestor nodes and selects the previously selected leaf nodes.
9006
+ * Restores multiple-mode leaf selection after search reset.
9015
9007
  */
9016
- async restoreSelectionAfterReload(selectedIds) {
9008
+ async restoreMultipleSelectionAfterReload(selectedLeafIds) {
9017
9009
  const treeComponent = this.tree();
9018
- if (!treeComponent || selectedIds.length === 0) {
9010
+ const normalizedIds = this.normalizeSelectedIds(selectedLeafIds);
9011
+ if (!treeComponent || normalizedIds.length === 0) {
9019
9012
  return;
9020
9013
  }
9021
9014
  this.isUpdatingSelection = true;
9022
9015
  try {
9023
- // Build ancestor chains for selected nodes
9024
- const ancestorChains = await this.buildAncestorChains(selectedIds);
9025
- // Expand ancestor nodes to make selected nodes visible
9016
+ const ancestorChains = await this.buildAncestorChains(normalizedIds);
9026
9017
  await this.expandAncestorNodesInOrder(ancestorChains);
9027
- // Wait for tree to render expanded nodes
9028
- await new Promise((resolve) => setTimeout(resolve, 50));
9029
- // Select the nodes visually in the tree
9030
- for (const id of selectedIds) {
9018
+ for (const id of normalizedIds) {
9031
9019
  try {
9032
9020
  treeComponent.selectNode(id);
9033
9021
  }
9034
9022
  catch {
9035
- // Node might not be in tree yet
9023
+ // Node might not be loaded yet
9036
9024
  }
9037
9025
  }
9026
+ await this.updateParentStatesAfterSelection(normalizedIds);
9027
+ treeComponent.refresh();
9038
9028
  }
9039
9029
  finally {
9040
9030
  this.isUpdatingSelection = false;
@@ -9051,10 +9041,6 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
9051
9041
  if (!event.isUserInteraction) {
9052
9042
  return;
9053
9043
  }
9054
- // Don't process if we're already updating selection
9055
- if (this.isUpdatingSelection) {
9056
- return;
9057
- }
9058
9044
  // Cache node data for getSelectedItems (except for 'all' node)
9059
9045
  if (nodeId !== 'all') {
9060
9046
  const nodeData = node['data'];
@@ -9062,72 +9048,64 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
9062
9048
  this.nodeDataCache.set(nodeId, nodeData);
9063
9049
  }
9064
9050
  }
9065
- // Check if this node is being selected or deselected
9051
+ // Single selection is handled by the tree controlledSelection + selectedIds binding.
9052
+ if (!this.allowMultiple()) {
9053
+ return;
9054
+ }
9066
9055
  const isSelected = node['selected'] === true;
9067
- if (this.allowMultiple()) {
9068
- const children = node['children'];
9069
- const childrenCount = node['childrenCount'];
9070
- // Determine if this node has children (is a parent node)
9071
- // A node has children if: childrenCount > 0, or has non-empty children array
9072
- // Note: childrenCount === undefined means "unknown" - we need to check via datasource
9073
- const hasLoadedChildren = children && children.length > 0;
9074
- const hasChildrenCount = childrenCount !== undefined && childrenCount > 0;
9075
- const isDefinitelyLeaf = childrenCount === 0 && (!children || children.length === 0);
9076
- if (isSelected) {
9077
- // SELECTION: Only add LEAF nodes to selectedNodeIds
9078
- this.isUpdatingSelection = true;
9079
- try {
9080
- if (nodeId === 'all') {
9081
- // "All Items" selected - recursively select all leaf descendants
9082
- await this.selectAllLeafDescendants(nodeId);
9083
- }
9084
- else if (isDefinitelyLeaf) {
9085
- // This is definitely a leaf node - add it directly
9056
+ const children = node['children'];
9057
+ const childrenCount = node['childrenCount'];
9058
+ // Determine if this node has children (is a parent node)
9059
+ // A node has children if: childrenCount > 0, or has non-empty children array
9060
+ // Note: childrenCount === undefined means "unknown" - we need to check via datasource
9061
+ const hasLoadedChildren = children && children.length > 0;
9062
+ const hasChildrenCount = childrenCount !== undefined && childrenCount > 0;
9063
+ const isDefinitelyLeaf = childrenCount === 0 && (!children || children.length === 0);
9064
+ if (isSelected) {
9065
+ // SELECTION: Only add LEAF nodes to selectedNodeIds
9066
+ this.isUpdatingSelection = true;
9067
+ try {
9068
+ if (nodeId === 'all') {
9069
+ // "All Items" selected - recursively select all leaf descendants
9070
+ await this.selectAllLeafDescendants(nodeId);
9071
+ }
9072
+ else if (isDefinitelyLeaf) {
9073
+ // This is definitely a leaf node - add it directly
9074
+ const currentSelected = new Set(this.selectedNodeIds());
9075
+ currentSelected.add(nodeId);
9076
+ this.setSelectedNodeIds(Array.from(currentSelected));
9077
+ }
9078
+ else if (hasLoadedChildren || hasChildrenCount) {
9079
+ // This node has children - only add its leaf descendants
9080
+ await this.selectAllLeafDescendants(nodeId);
9081
+ }
9082
+ else {
9083
+ // childrenCount is undefined - fetch once and reuse to avoid double datasource call
9084
+ const childNodes = await this.datasource(nodeId);
9085
+ if (!childNodes || childNodes.length === 0) {
9086
+ // No children - this is a leaf node, add it
9086
9087
  const currentSelected = new Set(this.selectedNodeIds());
9087
9088
  currentSelected.add(nodeId);
9088
- this.selectedNodeIds.set(Array.from(currentSelected));
9089
- }
9090
- else if (hasLoadedChildren || hasChildrenCount) {
9091
- // This node has children - only add its leaf descendants
9092
- await this.selectAllLeafDescendants(nodeId);
9089
+ this.setSelectedNodeIds(Array.from(currentSelected));
9093
9090
  }
9094
9091
  else {
9095
- // childrenCount is undefined - fetch once and reuse to avoid double datasource call
9096
- const childNodes = await this.datasource(nodeId);
9097
- if (!childNodes || childNodes.length === 0) {
9098
- // No children - this is a leaf node, add it
9099
- const currentSelected = new Set(this.selectedNodeIds());
9100
- currentSelected.add(nodeId);
9101
- this.selectedNodeIds.set(Array.from(currentSelected));
9102
- }
9103
- else {
9104
- // Has children - pass prefetched childNodes to avoid datasource(nodeId) again
9105
- await this.selectAllLeafDescendants(nodeId, childNodes);
9106
- }
9092
+ // Has children - pass prefetched childNodes to avoid datasource(nodeId) again
9093
+ await this.selectAllLeafDescendants(nodeId, childNodes);
9107
9094
  }
9108
9095
  }
9109
- finally {
9110
- this.isUpdatingSelection = false;
9111
- }
9112
9096
  }
9113
- else {
9114
- // DESELECTION: Remove node (if leaf) and all leaf descendants from selectedNodeIds
9115
- this.isUpdatingSelection = true;
9116
- try {
9117
- await this.deselectAllLeafDescendants(nodeId);
9118
- }
9119
- finally {
9120
- this.isUpdatingSelection = false;
9121
- }
9097
+ finally {
9098
+ this.isUpdatingSelection = false;
9122
9099
  }
9123
9100
  }
9124
9101
  else {
9125
- // Single selection mode: just update selectedNodeIds
9126
- if (isSelected) {
9127
- this.selectedNodeIds.set([nodeId]);
9102
+ // DESELECTION: Remove node (if leaf) and all leaf descendants from selectedNodeIds
9103
+ this.isUpdatingSelection = true;
9104
+ try {
9105
+ await this.deselectAllLeafDescendants(nodeId);
9128
9106
  }
9129
- else {
9130
- this.selectedNodeIds.set([]);
9107
+ finally {
9108
+ this.isUpdatingSelection = false;
9131
9109
  }
9132
9110
  }
9133
9111
  }
@@ -9143,7 +9121,7 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
9143
9121
  * Clears all selected items
9144
9122
  */
9145
9123
  onClearAll() {
9146
- this.selectedNodeIds.set([]);
9124
+ this.setSelectedNodeIds([]);
9147
9125
  const treeComponent = this.tree();
9148
9126
  if (treeComponent) {
9149
9127
  treeComponent.deselectAll();
@@ -9194,7 +9172,7 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
9194
9172
  for (const leafId of leafIds) {
9195
9173
  currentSelected.add(leafId);
9196
9174
  }
9197
- this.selectedNodeIds.set(Array.from(currentSelected));
9175
+ this.setSelectedNodeIds(Array.from(currentSelected));
9198
9176
  // Select all leaf nodes visually in the tree
9199
9177
  if (treeComponent) {
9200
9178
  for (const leafId of leafIds) {
@@ -9223,7 +9201,7 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
9223
9201
  try {
9224
9202
  // Special case: deselecting 'all' clears everything
9225
9203
  if (parentId === 'all') {
9226
- this.selectedNodeIds.set([]);
9204
+ this.setSelectedNodeIds([]);
9227
9205
  // Use tree's deselectAll() to clear all visual selections
9228
9206
  const treeComponent = this.tree();
9229
9207
  if (treeComponent) {
@@ -9244,7 +9222,7 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
9244
9222
  // Only update our internal state - tree handles visual state
9245
9223
  const currentSelected = this.selectedNodeIds();
9246
9224
  const newSelected = currentSelected.filter((id) => !leafIds.has(id));
9247
- this.selectedNodeIds.set(newSelected);
9225
+ this.setSelectedNodeIds(newSelected);
9248
9226
  }
9249
9227
  catch (error) {
9250
9228
  console.error(`Error deselecting leaf descendants for node ${parentId}:`, error);
@@ -9494,60 +9472,6 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
9494
9472
  await new Promise((r) => setTimeout(r, pollMs));
9495
9473
  }
9496
9474
  }
9497
- /**
9498
- * Syncs selection state with the tree component.
9499
- * Selects leaf nodes and manually updates parent states (indeterminate/selected).
9500
- */
9501
- async syncSelectionWithTree(selectedIds) {
9502
- const treeComponent = this.tree();
9503
- if (!treeComponent || selectedIds.length === 0) {
9504
- return;
9505
- }
9506
- // Wait for tree to be fully initialized after ancestor expansion
9507
- await new Promise((resolve) => setTimeout(resolve, 200));
9508
- // Keep track of successfully synced nodes
9509
- const syncedNodes = new Set();
9510
- // Try multiple times to sync selection (nodes might load progressively)
9511
- for (let attempt = 0; attempt < 5; attempt++) {
9512
- for (const id of selectedIds) {
9513
- if (id && id !== 'all' && !syncedNodes.has(id)) {
9514
- try {
9515
- const node = treeComponent.findNode(id);
9516
- if (node) {
9517
- // Node exists in tree - mark it as selected using both methods
9518
- // 1. Direct property assignment for immediate visual feedback
9519
- node['selected'] = true;
9520
- node['indeterminate'] = false;
9521
- // 2. Use selectNode API for tree's internal state tracking
9522
- try {
9523
- treeComponent.selectNode(id);
9524
- }
9525
- catch {
9526
- // selectNode might fail but direct assignment should work
9527
- }
9528
- syncedNodes.add(id);
9529
- }
9530
- }
9531
- catch {
9532
- // Node not in tree yet
9533
- }
9534
- }
9535
- }
9536
- // If all nodes are synced, we're done
9537
- if (syncedNodes.size === selectedIds.length) {
9538
- break;
9539
- }
9540
- // Wait before next attempt
9541
- await new Promise((resolve) => setTimeout(resolve, 100));
9542
- }
9543
- // Now update parent states (indeterminate/selected) based on children
9544
- // This uses bottom-up traversal with datasource fallback for loading children
9545
- await this.updateParentStatesAfterSelection(selectedIds);
9546
- // Refresh tree to apply changes
9547
- treeComponent.refresh();
9548
- // Trigger change detection to update UI
9549
- this.changeDetectorRef.markForCheck();
9550
- }
9551
9475
  /**
9552
9476
  * Updates parent node states (selected/indeterminate) based on children selection.
9553
9477
  * Called after leaf nodes are selected to properly show parent states.
@@ -9741,35 +9665,6 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
9741
9665
  const id = String(parentIdValue);
9742
9666
  return id && id !== 'all' ? id : null;
9743
9667
  }
9744
- /**
9745
- * Marks nodes as selected in the tree structure based on selectedNodeIds.
9746
- * This ensures pre-selected nodes appear selected when the tree is rendered.
9747
- */
9748
- markNodesAsSelected(node) {
9749
- const selectedIds = this.selectedNodeIds();
9750
- if (selectedIds.length === 0) {
9751
- return;
9752
- }
9753
- // Mark the node itself if it's selected
9754
- this.markNodeAsSelectedIfNeeded(node);
9755
- // Recursively mark children
9756
- const nodeChildren = node['children'];
9757
- if (nodeChildren) {
9758
- nodeChildren.forEach((child) => {
9759
- this.markNodesAsSelected(child);
9760
- });
9761
- }
9762
- }
9763
- /**
9764
- * Marks a single node as selected if it's in the selectedNodeIds list.
9765
- */
9766
- markNodeAsSelectedIfNeeded(node) {
9767
- const selectedIds = this.selectedNodeIds();
9768
- const nodeId = String(node['id'] ?? '');
9769
- if (nodeId && selectedIds.includes(nodeId)) {
9770
- node['selected'] = true;
9771
- }
9772
- }
9773
9668
  /**
9774
9669
  * Marks a node and its children as disabled if the node ID matches the excluded ID.
9775
9670
  */
@@ -9778,7 +9673,6 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
9778
9673
  if (nodeId === excludedId) {
9779
9674
  node['disabled'] = true;
9780
9675
  }
9781
- // Recursively mark children
9782
9676
  const nodeChildren = node['children'];
9783
9677
  if (nodeChildren) {
9784
9678
  nodeChildren.forEach((child) => {
@@ -9787,41 +9681,16 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
9787
9681
  }
9788
9682
  }
9789
9683
  /**
9790
- * Processes root node: marks excluded as disabled, marks selected, and syncs selection with tree component
9684
+ * Processes root node: marks excluded nodes as disabled.
9791
9685
  */
9792
9686
  processRootNode(rootNode) {
9793
9687
  const excludedId = this.excludedNodeId();
9794
9688
  if (excludedId) {
9795
9689
  this.markNodeAsDisabled(rootNode, excludedId);
9796
9690
  }
9797
- this.markNodesAsSelected(rootNode);
9798
- // Sync selection with tree component after a short delay to ensure tree is rendered
9799
- const treeComponent = this.tree();
9800
- if (treeComponent) {
9801
- setTimeout(() => {
9802
- this.isUpdatingSelection = true;
9803
- try {
9804
- const selectedIds = this.selectedNodeIds();
9805
- selectedIds.forEach((id) => {
9806
- if (id && id !== 'all') {
9807
- try {
9808
- treeComponent.selectNode(id);
9809
- }
9810
- catch {
9811
- // Node might not be in tree yet
9812
- }
9813
- }
9814
- });
9815
- }
9816
- finally {
9817
- this.isUpdatingSelection = false;
9818
- }
9819
- }, 0);
9820
- }
9821
9691
  }
9822
9692
  /**
9823
9693
  * Processes child nodes: marks excluded as disabled
9824
- * Selection marking is handled in datasource callback ONLY during initial load
9825
9694
  */
9826
9695
  processChildNodes(childNodes) {
9827
9696
  const excludedId = this.excludedNodeId();
@@ -9967,8 +9836,7 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
9967
9836
  return this.translationService.resolve(raw);
9968
9837
  }
9969
9838
  async getSelectedItems() {
9970
- // selectedNodeIds now only contains LEAF nodes (already filtered)
9971
- const selectedIds = this.selectedNodeIds();
9839
+ const selectedIds = this.normalizeSelectedIds(this.selectedNodeIds());
9972
9840
  if (selectedIds.length === 0) {
9973
9841
  return [];
9974
9842
  }
@@ -10061,6 +9929,9 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
10061
9929
  [dragBehavior]="'none'"
10062
9930
  [selectMode]="allowMultiple() ? 'multiple' : 'single'"
10063
9931
  [selectionBehavior]="allowMultiple() ? 'intermediate-nested' : 'all'"
9932
+ [controlledSelection]="!allowMultiple()"
9933
+ [selectedIds]="selectedNodeIds()"
9934
+ (selectedIdsChange)="onControlledSelectedIdsChange($event)"
10064
9935
  [showIcons]="true"
10065
9936
  [titleField]="textField()"
10066
9937
  [idField]="valueField()"
@@ -10147,7 +10018,7 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
10147
10018
  ></ax-button>
10148
10019
  </ax-suffix>
10149
10020
  </ax-footer>
10150
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i2$1.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "ngmodule", type: AXCheckBoxModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i3$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i3$1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXSearchBoxModule }, { kind: "component", type: i4$1.AXSearchBoxComponent, selector: "ax-search-box", inputs: ["disabled", "readonly", "tabIndex", "placeholder", "value", "state", "name", "id", "look", "class", "delayTime", "type", "autoSearch"], outputs: ["valueChange", "stateChange", "onValueChanged", "onBlur", "onFocus", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i4.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "component", type: AXTreeViewComponent, selector: "ax-tree-view", inputs: ["datasource", "selectMode", "selectionBehavior", "dragArea", "dragBehavior", "showIcons", "showChildrenBadge", "expandedIcon", "collapsedIcon", "indentSize", "look", "nodeTemplate", "idField", "titleField", "tooltipField", "iconField", "expandedField", "selectedField", "indeterminateField", "disabledField", "hiddenField", "childrenField", "childrenCountField", "dataField", "inheritDisabled", "expandOnDoubleClick", "doubleClickDuration", "tooltipDelay"], outputs: ["datasourceChange", "onBeforeDrop", "onNodeToggle", "onNodeSelect", "onNodeDoubleClick", "onNodeClick", "onSelectionChange", "onOrderChange", "onMoveChange", "onItemsChange"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "look"] }, { kind: "ngmodule", type: FormsModule }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
10021
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i1.AXButtonComponent, selector: "ax-button", inputs: ["disabled", "size", "tabIndex", "color", "look", "text", "toggleable", "selected", "iconOnly", "type", "loadingText"], outputs: ["onBlur", "onFocus", "onClick", "selectedChange", "toggleableChange", "lookChange", "colorChange", "disabledChange", "loadingTextChange"] }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i2$1.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "ngmodule", type: AXCheckBoxModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i3$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i3$1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXSearchBoxModule }, { kind: "component", type: i4$1.AXSearchBoxComponent, selector: "ax-search-box", inputs: ["disabled", "readonly", "tabIndex", "placeholder", "value", "state", "name", "id", "look", "class", "delayTime", "type", "autoSearch"], outputs: ["valueChange", "stateChange", "onValueChanged", "onBlur", "onFocus", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i4.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "component", type: AXTreeViewComponent, selector: "ax-tree-view", inputs: ["datasource", "selectMode", "selectionBehavior", "dragArea", "dragBehavior", "showIcons", "showChildrenBadge", "expandedIcon", "collapsedIcon", "indentSize", "look", "nodeTemplate", "idField", "titleField", "tooltipField", "iconField", "expandedField", "selectedField", "indeterminateField", "disabledField", "hiddenField", "childrenField", "childrenCountField", "dataField", "inheritDisabled", "expandOnDoubleClick", "doubleClickDuration", "tooltipDelay", "controlledSelection", "selectedIds"], outputs: ["datasourceChange", "selectedIdsChange", "onBeforeDrop", "onNodeToggle", "onNodeSelect", "onNodeDoubleClick", "onNodeClick", "onSelectionChange", "onOrderChange", "onMoveChange", "onItemsChange"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "look"] }, { kind: "ngmodule", type: FormsModule }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
10151
10022
  }
10152
10023
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPEntityCategoryTreeSelectorComponent, decorators: [{
10153
10024
  type: Component,
@@ -10198,6 +10069,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
10198
10069
  [dragBehavior]="'none'"
10199
10070
  [selectMode]="allowMultiple() ? 'multiple' : 'single'"
10200
10071
  [selectionBehavior]="allowMultiple() ? 'intermediate-nested' : 'all'"
10072
+ [controlledSelection]="!allowMultiple()"
10073
+ [selectedIds]="selectedNodeIds()"
10074
+ (selectedIdsChange)="onControlledSelectedIdsChange($event)"
10201
10075
  [showIcons]="true"
10202
10076
  [titleField]="textField()"
10203
10077
  [idField]="valueField()"
@@ -11307,9 +11181,7 @@ class AXPEntityCategoryWidgetEditComponent extends AXPValueWidgetComponent {
11307
11181
  this.isOpen.set(true);
11308
11182
  const currentValue = this.getValue();
11309
11183
  const selectedIds = currentValue
11310
- ? (this.multiple() ? castArray(currentValue) : [currentValue])
11311
- .map((v) => String(extractValue(v, this.valueField())))
11312
- .filter((id) => id && id !== 'all')
11184
+ ? this.normalizePopupSelectedIds(currentValue)
11313
11185
  : [];
11314
11186
  // Get current entity ID from context (if editing an entity)
11315
11187
  const currentEntityId = this.contextService.getValue('id');
@@ -11330,9 +11202,7 @@ class AXPEntityCategoryWidgetEditComponent extends AXPValueWidgetComponent {
11330
11202
  },
11331
11203
  });
11332
11204
  if (result?.data?.selected && Array.isArray(result.data.selected)) {
11333
- // Use the popup result directly as the source of truth
11334
- // This ensures that deselected items are properly removed
11335
- const newItems = result.data.selected;
11205
+ const newItems = this.multiple() ? result.data.selected : result.data.selected.slice(0, 1);
11336
11206
  if (newItems.length === 0) {
11337
11207
  // All items were deselected
11338
11208
  this.clear();
@@ -11482,10 +11352,10 @@ class AXPEntityCategoryWidgetEditComponent extends AXPValueWidgetComponent {
11482
11352
  }
11483
11353
  });
11484
11354
  this.contextService.applyObjectPaths(itemToExpose, { origin: 'user' });
11485
- this.setUserValue(this.singleOrMultiple(keys));
11355
+ this.setValue(this.singleOrMultiple(keys));
11486
11356
  }
11487
11357
  else {
11488
- this.setUserValue(this.singleOrMultiple(keys));
11358
+ this.setValue(this.singleOrMultiple(keys));
11489
11359
  }
11490
11360
  // Trigger change detection
11491
11361
  this.cdr.markForCheck();
@@ -11500,6 +11370,12 @@ class AXPEntityCategoryWidgetEditComponent extends AXPValueWidgetComponent {
11500
11370
  singleOrMultiple(values) {
11501
11371
  return this.multiple() ? values : values[0];
11502
11372
  }
11373
+ normalizePopupSelectedIds(currentValue) {
11374
+ const ids = (this.multiple() ? castArray(currentValue) : castArray(currentValue).slice(0, 1))
11375
+ .map((v) => String(extractValue(v, this.valueField())))
11376
+ .filter((id) => id && id !== 'all');
11377
+ return this.multiple() ? ids : ids.slice(0, 1);
11378
+ }
11503
11379
  getItemLabel(item) {
11504
11380
  if (!item) {
11505
11381
  return '';
@@ -12777,11 +12653,10 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
12777
12653
  }
12778
12654
  untracked(() => void this.applyRelatedFiltersFromContextAndDatasource(specs));
12779
12655
  }, ...(ngDevMode ? [{ debugName: "#relatedFilterSyncEffect" }] : /* istanbul ignore next */ []));
12780
- /** Patches data-list `refresh` so the grid footer / toolbar refresh keeps parent-scoped filters on the data source. */
12656
+ /** Patches data-list `refresh` so reload keeps parent-scoped toolbar filters on the data source. */
12781
12657
  this.#patchDataListRefreshEffect = effect(() => {
12782
12658
  const inst = this.listWidget()?.instance;
12783
- const opts = this.options();
12784
- if (!inst?.refresh || inst.__axpEntityListRefreshPatched || !opts['syncRelatedListFiltersFromDialogContext']) {
12659
+ if (!inst?.refresh || inst.__axpEntityListRefreshPatched) {
12785
12660
  return;
12786
12661
  }
12787
12662
  inst.__axpEntityListRefreshPatched = true;
@@ -12793,6 +12668,9 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
12793
12668
  if (o['syncRelatedListFiltersFromDialogContext'] && specs?.length) {
12794
12669
  await this.applyRelatedFiltersFromContextAndDatasource(specs);
12795
12670
  }
12671
+ else {
12672
+ await this.reapplyScopedFiltersToDataSource();
12673
+ }
12796
12674
  originalRefresh();
12797
12675
  })();
12798
12676
  };
@@ -13013,6 +12891,10 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
13013
12891
  * Applies merged route + parent-scope filters to the widget value and data source.
13014
12892
  */
13015
12893
  applyMergedRouteFiltersToList() {
12894
+ void this.reapplyScopedFiltersToDataSource();
12895
+ }
12896
+ /** Syncs toolbar filters to context and AXDataSource (retries until data-list is mounted). */
12897
+ async reapplyScopedFiltersToDataSource() {
13016
12898
  const merged = this.getMergedToolbarFilters();
13017
12899
  const current = this.getValue();
13018
12900
  if (!isEqual$1(current?.toolbar?.filters, merged)) {
@@ -13021,7 +12903,26 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
13021
12903
  toolbar: { ...(current?.toolbar ?? {}), filters: merged },
13022
12904
  });
13023
12905
  }
12906
+ const pushed = await this.ensureToolbarFiltersOnDataSource();
12907
+ if (pushed) {
12908
+ this.triggerInitialLoadIfNeeded();
12909
+ }
12910
+ }
12911
+ /**
12912
+ * Entity lists use `fetchDataMode: 'manual'`; after scoped filters reach the data source,
12913
+ * the grid must be refreshed once (initial #effect may run before the data-list is ready).
12914
+ */
12915
+ triggerInitialLoadIfNeeded() {
12916
+ if (this.isMounted()) {
12917
+ return;
12918
+ }
12919
+ const listInstance = this.listWidget()?.instance;
12920
+ if (!listInstance?.call) {
12921
+ return;
12922
+ }
13024
12923
  this.pushToolbarFiltersToDataSource();
12924
+ listInstance.call('refresh');
12925
+ this.isMounted.set(true);
13025
12926
  }
13026
12927
  /**
13027
12928
  * Re-evaluates related-entity list filters from the live dialog form context (e.g. after create saves the main row id).
@@ -13068,26 +12969,29 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
13068
12969
  return true;
13069
12970
  }
13070
12971
  /**
13071
- * Writes toolbar filters from specs and pushes them onto the data source so refresh/reload keeps the parent scope.
12972
+ * Pushes toolbar filters once the embedded data-list exposes its data source
12973
+ * (listNode is created via deferred setTimeout in ngOnInit).
13072
12974
  */
13073
- async applyRelatedFiltersFromContextAndDatasource(specs) {
13074
- await this.applyRelatedFiltersFromContext(specs);
12975
+ async ensureToolbarFiltersOnDataSource() {
13075
12976
  if (this.pushToolbarFiltersToDataSource()) {
13076
- return;
13077
- }
13078
- const opts = this.options();
13079
- if (!opts['syncRelatedListFiltersFromDialogContext']) {
13080
- return;
12977
+ return true;
13081
12978
  }
13082
- /** Data-list is created in ngOnInit via deferred listNode.set; retry briefly until instance exposes dataSource. */
13083
12979
  const maxAttempts = 40;
13084
12980
  const delayMs = 50;
13085
12981
  for (let i = 0; i < maxAttempts; i++) {
13086
12982
  await new Promise((resolve) => setTimeout(resolve, delayMs));
13087
12983
  if (this.pushToolbarFiltersToDataSource()) {
13088
- return;
12984
+ return true;
13089
12985
  }
13090
12986
  }
12987
+ return false;
12988
+ }
12989
+ /**
12990
+ * Writes toolbar filters from specs and pushes them onto the data source so refresh/reload keeps the parent scope.
12991
+ */
12992
+ async applyRelatedFiltersFromContextAndDatasource(specs) {
12993
+ await this.applyRelatedFiltersFromContext(specs);
12994
+ await this.ensureToolbarFiltersOnDataSource();
13091
12995
  }
13092
12996
  /**
13093
12997
  * Refreshes the embedded data list (toolbar / workflow). In wizard mode, `refresh` is patched to re-apply scoped filters first.
@@ -13095,7 +12999,7 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
13095
12999
  refreshGridWithParentScopedFilters() {
13096
13000
  this.listWidget()?.instance?.call('refresh');
13097
13001
  }
13098
- /** Patches data-list `refresh` so the grid footer / toolbar refresh keeps parent-scoped filters on the data source. */
13002
+ /** Patches data-list `refresh` so reload keeps parent-scoped toolbar filters on the data source. */
13099
13003
  #patchDataListRefreshEffect;
13100
13004
  #effect;
13101
13005
  /**
@@ -13110,7 +13014,7 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
13110
13014
  handleQueryChanges(queries, dataSource, listInstance, isMounted) {
13111
13015
  const changeTracker = this.analyzeChanges(queries);
13112
13016
  this.previousQueries = queries;
13113
- this.applyDataSourceChanges(dataSource, queries, changeTracker);
13017
+ this.applyDataSourceChanges(dataSource, queries, changeTracker, isMounted);
13114
13018
  this.handleListRefresh(listInstance, changeTracker, isMounted);
13115
13019
  this.handleColumnChanges(changeTracker);
13116
13020
  }
@@ -13137,8 +13041,9 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
13137
13041
  /**
13138
13042
  * Applies filter and sort changes to the data source
13139
13043
  */
13140
- applyDataSourceChanges(dataSource, queries, changeTracker) {
13141
- if (changeTracker.isFilterChanged) {
13044
+ applyDataSourceChanges(dataSource, queries, changeTracker, isMounted) {
13045
+ const shouldApplyFilters = changeTracker.isFilterChanged || (!isMounted && (queries?.filters?.length ?? 0) > 0);
13046
+ if (shouldApplyFilters) {
13142
13047
  dataSource.filter({
13143
13048
  filters: queries?.filters,
13144
13049
  });
@@ -13149,16 +13054,16 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
13149
13054
  }
13150
13055
  }
13151
13056
  /**
13152
- * Handles list refresh logic based on changes and mount status
13057
+ * Handles list refresh logic based on changes and mount status.
13058
+ * Initial fetch is owned by {@link triggerInitialLoadIfNeeded} (manual fetchDataMode).
13153
13059
  */
13154
13060
  handleListRefresh(listInstance, changeTracker, isMounted) {
13155
- const shouldRefresh = changeTracker.isFilterChanged || changeTracker.isSortChanged || !isMounted;
13156
- if (shouldRefresh) {
13061
+ if (!isMounted) {
13062
+ return;
13063
+ }
13064
+ if (changeTracker.isFilterChanged || changeTracker.isSortChanged) {
13065
+ this.pushToolbarFiltersToDataSource();
13157
13066
  listInstance.call('refresh');
13158
- // Set mounted flag only on initial load (not when filters/sorts change)
13159
- if (!changeTracker.isFilterChanged && !changeTracker.isSortChanged) {
13160
- this.isMounted.set(true);
13161
- }
13162
13067
  }
13163
13068
  }
13164
13069
  /**
@@ -15597,8 +15502,8 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
15597
15502
  this.setLoading = (loading) => {
15598
15503
  this.isLoading.set(loading);
15599
15504
  };
15600
- this.setItems = (items, options) => {
15601
- const origin = options?.origin ?? 'system';
15505
+ /** @param syncContext When false, only updates UI (hydration/revert); skips context writes. */
15506
+ this.setItems = (items, syncContext = true) => {
15602
15507
  // Ensure items is always an array
15603
15508
  items = castArray(items);
15604
15509
  // Filter out null/undefined items
@@ -15614,13 +15519,13 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
15614
15519
  return item;
15615
15520
  });
15616
15521
  this.selectedItems.set(items);
15617
- //
15522
+ if (!syncContext) {
15523
+ return;
15524
+ }
15618
15525
  const keys = items.map((item) => get(item, this.valueField()));
15619
15526
  const text = items.map((item) => this.mlsResolver.resolve(get(item, displayField)));
15620
- //
15621
- // extract data from valueField and set context by expose path
15622
15527
  if (this.expose()) {
15623
- this.expoesItems(origin);
15528
+ this.expoesItems();
15624
15529
  }
15625
15530
  const newValue = this.singleOrMultiple(keys);
15626
15531
  if (this.filterMode()) {
@@ -15630,10 +15535,10 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
15630
15535
  operation: {
15631
15536
  type: this.multiple() ? 'in' : 'equal',
15632
15537
  },
15633
- }, { origin });
15538
+ });
15634
15539
  }
15635
15540
  else {
15636
- this.setValue(newValue, { origin });
15541
+ this.setValue(newValue);
15637
15542
  }
15638
15543
  };
15639
15544
  }
@@ -15647,7 +15552,7 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
15647
15552
  ? castArray(this.filterMode() ? rawValue.value : rawValue)
15648
15553
  : [rawValue].filter((v) => v != null);
15649
15554
  if (!values.length) {
15650
- this.setItems([]);
15555
+ this.setItems([], false);
15651
15556
  this.isLoading.set(false);
15652
15557
  return;
15653
15558
  }
@@ -15662,22 +15567,22 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
15662
15567
  .map((id) => entityDataAccessor.byKey(id)));
15663
15568
  // Filter out null/undefined results
15664
15569
  const validItems = items.filter((item) => item != null);
15665
- this.setItems(validItems);
15570
+ this.setItems(validItems, false);
15666
15571
  }
15667
15572
  else {
15668
15573
  const id = extractValue(this.filterMode() ? values[0].value : values[0], this.valueField());
15669
15574
  if (id != null) {
15670
15575
  const item = await entityDataAccessor.byKey(id);
15671
- this.setItems(item != null ? [item] : []);
15576
+ this.setItems(item != null ? [item] : [], false);
15672
15577
  }
15673
15578
  else {
15674
- this.setItems([]);
15579
+ this.setItems([], false);
15675
15580
  }
15676
15581
  }
15677
15582
  }
15678
15583
  catch (error) {
15679
15584
  console.error('[LookupWidget] findByValue() error:', error);
15680
- this.setItems([]);
15585
+ this.setItems([], false);
15681
15586
  }
15682
15587
  finally {
15683
15588
  this.isLoading.set(false);
@@ -15685,7 +15590,7 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
15685
15590
  }
15686
15591
  //#region ---- Event Handlers from Dumb Components ----
15687
15592
  handleComponentValueChanged(items) {
15688
- this.setItems(items ?? [], { origin: 'user' });
15593
+ this.setItems(items ?? []);
15689
15594
  }
15690
15595
  async handleCreateClick(_e) {
15691
15596
  const entity = this.entityDef();
@@ -15710,10 +15615,10 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
15710
15615
  if (this.multiple()) {
15711
15616
  const exists = currentItems.some((item) => get(item, valueField) === newId);
15712
15617
  const newItems = exists ? currentItems : [...currentItems, createdItem];
15713
- this.setItems(newItems, { origin: 'user' });
15618
+ this.setItems(newItems);
15714
15619
  }
15715
15620
  else {
15716
- this.setItems([createdItem], { origin: 'user' });
15621
+ this.setItems([createdItem]);
15717
15622
  }
15718
15623
  }
15719
15624
  }
@@ -15730,7 +15635,7 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
15730
15635
  * so e.g. `{ person: { educationLevel: { id, title } } }` would replace the entire `person`
15731
15636
  * object and drop sibling fields like `person.educationLevelId`, causing a value/effect loop.
15732
15637
  */
15733
- expoesItems(origin = 'system') {
15638
+ expoesItems() {
15734
15639
  const exposeValue = castArray(this.expose());
15735
15640
  const items = this.selectedItems();
15736
15641
  const isEmpty = !items || items.length === 0;
@@ -15743,7 +15648,7 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
15743
15648
  parentPaths.add(pathParts.slice(0, -1).join('.'));
15744
15649
  }
15745
15650
  else {
15746
- this.contextService.update(i, null, { origin });
15651
+ this.contextService.update(i, null);
15747
15652
  }
15748
15653
  }
15749
15654
  else {
@@ -15752,25 +15657,25 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
15752
15657
  parentPaths.add(pathParts.slice(0, -1).join('.'));
15753
15658
  }
15754
15659
  else {
15755
- this.contextService.update(i.target, null, { origin });
15660
+ this.contextService.update(i.target, null);
15756
15661
  }
15757
15662
  }
15758
15663
  });
15759
15664
  parentPaths.forEach((parentPath) => {
15760
- this.contextService.update(parentPath, null, { origin });
15665
+ this.contextService.update(parentPath, null);
15761
15666
  });
15762
15667
  return;
15763
15668
  }
15764
15669
  exposeValue.forEach((i) => {
15765
15670
  if (typeof i === 'string') {
15766
15671
  const values = items.map((item) => set({}, i, get(item, i)));
15767
- this.contextService.update(i, this.singleOrMultiple(values), { origin });
15672
+ this.contextService.update(i, this.singleOrMultiple(values));
15768
15673
  }
15769
15674
  else {
15770
15675
  const values = this.multiple()
15771
15676
  ? items.map((item) => set({}, i.source, get(item, i.source)))
15772
15677
  : items.map((item) => get(item, i.source));
15773
- this.contextService.update(i.target, this.singleOrMultiple(values), { origin });
15678
+ this.contextService.update(i.target, this.singleOrMultiple(values));
15774
15679
  }
15775
15680
  });
15776
15681
  }
@@ -16185,7 +16090,7 @@ class AXPLookupWidgetColumnComponent extends AXPColumnWidgetComponent {
16185
16090
  if (!resolvedTitle) {
16186
16091
  const displayTitle = formatLookupItemDisplay(headerItem, this.entityDef(), this.lookupDisplayOptions(), this.formatService, (value) => this.translation.resolve(value));
16187
16092
  resolvedTitle = typeof displayTitle === 'string' ? displayTitle : this.translation.resolve(displayTitle);
16188
- if (isUnresolvedLookupDisplayTemplate(resolvedTitle)) {
16093
+ if (isUnresolvedEntityDisplayTemplate(resolvedTitle)) {
16189
16094
  resolvedTitle = '';
16190
16095
  }
16191
16096
  }
@@ -19105,32 +19010,10 @@ function hasFileUploaderTitleOrDescriptionFields(editDialog) {
19105
19010
  }
19106
19011
  //#endregion
19107
19012
 
19108
- //#region ---- Imports ----
19013
+ //#region ---- Imports ----
19109
19014
  //#endregion
19110
- //#region ---- Title template resolution ----
19015
+ //#region ---- Title template resolution ----
19111
19016
  const INVALID_DISPLAY_TEXT = '[object Object]';
19112
- /**
19113
- * Row title template for entity-scoped popovers (e.g. attachment file list).
19114
- * Priority: `interfaces.master.single.title` → `formats.searchResult.title` → `formats.individual`.
19115
- */
19116
- function resolveEntityRowTitleTemplate(entity) {
19117
- if (!entity) {
19118
- return undefined;
19119
- }
19120
- const singleTitle = entity.interfaces?.master?.single?.title?.trim();
19121
- if (singleTitle) {
19122
- return normalizeLookupDisplayTemplate(singleTitle);
19123
- }
19124
- const searchTitle = entity.formats?.searchResult?.title?.trim();
19125
- if (searchTitle) {
19126
- return normalizeLookupDisplayTemplate(searchTitle);
19127
- }
19128
- const individual = entity.formats?.individual?.trim();
19129
- if (individual) {
19130
- return normalizeLookupDisplayTemplate(individual);
19131
- }
19132
- return undefined;
19133
- }
19134
19017
  /** Resolves a row field or format result to a plain display string for the active locale. */
19135
19018
  function resolveEntityRowDisplayText(value, activeLang) {
19136
19019
  if (value == null || value === '') {
@@ -19138,7 +19021,7 @@ function resolveEntityRowDisplayText(value, activeLang) {
19138
19021
  }
19139
19022
  if (typeof value === 'string') {
19140
19023
  const text = value.trim();
19141
- if (!text || text === INVALID_DISPLAY_TEXT || isUnresolvedLookupDisplayTemplate(text)) {
19024
+ if (!text || text === INVALID_DISPLAY_TEXT || isUnresolvedEntityDisplayTemplate(text)) {
19142
19025
  return '';
19143
19026
  }
19144
19027
  return text;
@@ -19156,21 +19039,25 @@ function resolveEntityRowDisplayText(value, activeLang) {
19156
19039
  return '';
19157
19040
  }
19158
19041
  /**
19159
- * Formats the entity row title for display using row fields and single-view template.
19042
+ * Formats the entity row title for display using entity-driven templates and property paths.
19160
19043
  */
19161
19044
  function formatEntityRowDisplayTitle(entity, rowData, formatService, activeLang) {
19162
19045
  if (!rowData) {
19163
19046
  return '';
19164
19047
  }
19165
- for (const field of ['title', 'name', 'code']) {
19166
- const fromField = resolveEntityRowDisplayText(get(rowData, field), activeLang);
19048
+ const titleContext = buildEntitySearchTitleContext(entity ?? {});
19049
+ for (const path of titleContext.fallbackFields) {
19050
+ const fromField = resolveEntityRowDisplayText(get(rowData, path), activeLang);
19167
19051
  if (fromField) {
19168
19052
  return fromField;
19169
19053
  }
19170
19054
  }
19171
- const template = resolveEntityRowTitleTemplate(entity);
19172
- if (template) {
19173
- const formatted = formatService.format(template, 'string', rowData);
19055
+ const templates = [
19056
+ resolveEntityRowTitleTemplate(entity),
19057
+ ...titleContext.fallbackTemplates,
19058
+ ].filter((template) => !!template?.trim());
19059
+ for (const template of templates) {
19060
+ const formatted = formatService.format(normalizeEntityDisplayTemplate(template), 'string', rowData);
19174
19061
  const fromTemplate = resolveEntityRowDisplayText(formatted, activeLang);
19175
19062
  if (fromTemplate) {
19176
19063
  return fromTemplate;
@@ -21522,7 +21409,7 @@ class AXPLayoutAdapterBuilder {
21522
21409
  load: this.createLoadFunction(),
21523
21410
  pages: this.adapter.pages || [],
21524
21411
  exitUrl: this.adapter.exitUrl,
21525
- //getPageBadge: (context, isDirty) => this.badgeStatusService.getPageBadge(name, context, isDirty),
21412
+ getPageBadge: (context, isDirty) => this.badgeStatusService.getPageBadge(name, context, isDirty),
21526
21413
  getPageStatus: (context, currentPage) => this.badgeStatusService.getPageStatus(name, context, currentPage),
21527
21414
  };
21528
21415
  }
@@ -22210,6 +22097,8 @@ class AXPPageListConverter extends AXPBaseRelatedEntityConverter {
22210
22097
  title: context.rootTitle ?? '',
22211
22098
  label: relatedEntity.title,
22212
22099
  icon: relatedEntity.icon || entityDef.icon,
22100
+ /** List-only page: scoped filters are system state, not user edits. */
22101
+ isReadonly: true,
22213
22102
  actions: this.mergeActions(entityDef, actions)
22214
22103
  ?.filter((a) => a.priority === 'primary' && !a.isChild)
22215
22104
  ?.map((a) => {
@@ -22892,6 +22781,7 @@ class AXPMainEntityContentBuilder {
22892
22781
  icon: 'fa-light fa-rotate-left',
22893
22782
  color: 'default',
22894
22783
  visible: '{{context.isDirty()}}',
22784
+ shortcuts: ['escape'],
22895
22785
  command: {
22896
22786
  name: 'discard',
22897
22787
  },
@@ -22901,6 +22791,7 @@ class AXPMainEntityContentBuilder {
22901
22791
  icon: 'fa-light fa-floppy-disk',
22902
22792
  color: 'primary',
22903
22793
  visible: '{{context.isDirty()}}',
22794
+ shortcuts: ['ctrl+s'],
22904
22795
  command: {
22905
22796
  name: 'update-entity',
22906
22797
  },
@@ -23194,6 +23085,7 @@ class AXPMainEntityContentBuilder {
23194
23085
  icon: action.icon,
23195
23086
  color: action.color,
23196
23087
  disabled: disabled || false,
23088
+ shortcuts: action.shortcuts,
23197
23089
  zone: 'header',
23198
23090
  priority: action.priority,
23199
23091
  scope: action.scope === AXPEntityCommandScope.Individual ? AXPEntityCommandScope.TypeLevel : action.scope,
@@ -23256,17 +23148,25 @@ class AXPLayoutAdapterFactory {
23256
23148
  async createDetailsViewAdapter(entityResolver, moduleName, entityName, id, dependencies) {
23257
23149
  const entity = await entityResolver.resolve(moduleName, entityName);
23258
23150
  if (!entity) {
23259
- throw new Error(`Entity ${moduleName}.${entityName} not found`);
23151
+ throw new AXPNotFoundError(`Entity ${moduleName}.${entityName} not found`);
23260
23152
  }
23261
23153
  const rootContext = await this.loadRootContext(entity, id);
23262
- // Evaluate hidden expressions for related entities once, reuse across building and composing
23263
- const evaluatedRelatedEntities = await this.evaluateRelatedEntitiesHidden(entity?.relatedEntities ?? [], rootContext, dependencies);
23264
- // Build main and related pages using evaluated related entities
23265
- const entityWithEvaluatedRelated = { ...entity, relatedEntities: evaluatedRelatedEntities };
23266
- const mainPage = await this.buildMainPage(entityWithEvaluatedRelated, rootContext, id, dependencies);
23267
- const relatedPages = await this.buildRelatedPages(entityWithEvaluatedRelated, rootContext, dependencies);
23154
+ if (axpIsEntityRecordNotFound(rootContext)) {
23155
+ throw new AXPNotFoundError(`Entity record ${moduleName}.${entityName}#${id} not found`);
23156
+ }
23157
+ // Evaluate hidden expressions for related entities and component pages once, reuse across building and composing
23158
+ const evaluatedRelatedEntities = await this.evaluateHiddenFlags(entity?.relatedEntities ?? [], rootContext, dependencies);
23159
+ const evaluatedPages = await this.evaluateHiddenFlags(entity?.pages ?? [], rootContext, dependencies);
23160
+ // Build main and related pages using evaluated related entities and pages
23161
+ const entityWithEvaluatedLayout = {
23162
+ ...entity,
23163
+ relatedEntities: evaluatedRelatedEntities,
23164
+ pages: evaluatedPages,
23165
+ };
23166
+ const mainPage = await this.buildMainPage(entityWithEvaluatedLayout, rootContext, id, dependencies);
23167
+ const relatedPages = await this.buildRelatedPages(entityWithEvaluatedLayout, rootContext, dependencies);
23268
23168
  // Compose ordered pages around the primary page
23269
- const orderedPages = this.composePagesWithPositions(mainPage, relatedPages, entityWithEvaluatedRelated);
23169
+ const orderedPages = this.composePagesWithPositions(mainPage, relatedPages, entityWithEvaluatedLayout);
23270
23170
  const applicationName = dependencies.session.application?.name ?? 'default';
23271
23171
  const rootTitle = await this.getRootTitle(entity, rootContext, dependencies);
23272
23172
  return this.layoutAdapterBuilder
@@ -23285,15 +23185,15 @@ class AXPLayoutAdapterFactory {
23285
23185
  return this.mainEntityContentBuilder.build(entity, rootContext, { ...dependencies, reloadRootContext: () => this.loadRootContext(entity, id) }, await this.getRootTitle(entity, rootContext, dependencies));
23286
23186
  }
23287
23187
  /**
23288
- * Evaluates the 'hidden' expression for each related entity using the expression evaluator.
23289
- * Returns a new array of related entities with the evaluated hidden values.
23188
+ * Evaluates the 'hidden' expression for layout items using the expression evaluator.
23189
+ * Returns a new array with evaluated hidden values.
23290
23190
  */
23291
- async evaluateRelatedEntitiesHidden(relatedEntities, rootContext, dependencies) {
23292
- if (!relatedEntities?.length || !dependencies?.expressionEvaluator) {
23293
- return relatedEntities ?? [];
23191
+ async evaluateHiddenFlags(items, rootContext, dependencies) {
23192
+ if (!items?.length || !dependencies?.expressionEvaluator) {
23193
+ return items ?? [];
23294
23194
  }
23295
- return Promise.all(relatedEntities.map(async (re) => {
23296
- let hidden = re.hidden;
23195
+ return Promise.all(items.map(async (item) => {
23196
+ let hidden = item.hidden;
23297
23197
  if (hidden && typeof hidden === 'string') {
23298
23198
  try {
23299
23199
  const scope = {
@@ -23308,9 +23208,12 @@ class AXPLayoutAdapterFactory {
23308
23208
  // Keep original hidden value if evaluation fails
23309
23209
  }
23310
23210
  }
23311
- return { ...re, hidden };
23211
+ return { ...item, hidden };
23312
23212
  }));
23313
23213
  }
23214
+ async evaluateRelatedEntitiesHidden(relatedEntities, rootContext, dependencies) {
23215
+ return this.evaluateHiddenFlags(relatedEntities, rootContext, dependencies);
23216
+ }
23314
23217
  async buildRelatedPages(entity, rootContext, dependencies) {
23315
23218
  const pages = [];
23316
23219
  const rootTitle = await this.getRootTitle(entity, rootContext, dependencies);
@@ -23475,7 +23378,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
23475
23378
  }]
23476
23379
  }], ctorParameters: () => [{ type: AXPRelatedEntityConverterFactory }, { type: AXPMainEntityContentBuilder }, { type: AXPLayoutAdapterBuilder }, { type: i4$3.AXPFilterOperatorMiddlewareService }] });
23477
23380
 
23478
- const AXPLayoutDetailsViewRouteResolver = async (route, state, entityResolver = inject(AXPEntityDefinitionRegistryService), expressionEvaluator = inject(AXPExpressionEvaluatorService), session = inject(AXPSessionService), formatService = inject(AXFormatService), workflowService = inject(AXPWorkflowService), commandService = inject(AXPCommandService), layoutAdapterFactory = inject(AXPLayoutAdapterFactory)) => {
23381
+ const AXPLayoutDetailsViewRouteResolver = async (route, state, entityResolver = inject(AXPEntityDefinitionRegistryService), expressionEvaluator = inject(AXPExpressionEvaluatorService), session = inject(AXPSessionService), formatService = inject(AXFormatService), workflowService = inject(AXPWorkflowService), commandService = inject(AXPCommandService), layoutAdapterFactory = inject(AXPLayoutAdapterFactory), router = inject(Router)) => {
23479
23382
  const moduleName = route.parent?.paramMap.get('module');
23480
23383
  const entityName = route.paramMap.get('entity');
23481
23384
  const id = route.paramMap.get('id');
@@ -23487,8 +23390,15 @@ const AXPLayoutDetailsViewRouteResolver = async (route, state, entityResolver =
23487
23390
  workflowService,
23488
23391
  commandService,
23489
23392
  };
23490
- const adapter = await layoutAdapterFactory.createDetailsViewAdapter(entityResolver, moduleName, entityName, id, dependencies);
23491
- return adapter;
23393
+ try {
23394
+ return await layoutAdapterFactory.createDetailsViewAdapter(entityResolver, moduleName, entityName, id, dependencies);
23395
+ }
23396
+ catch (error) {
23397
+ if (error instanceof AXPNotFoundError) {
23398
+ return axpRedirectToNotFound(router);
23399
+ }
23400
+ throw error;
23401
+ }
23492
23402
  };
23493
23403
 
23494
23404
  class AXPEntityPreloadFilterGuard {
@@ -24262,7 +24172,7 @@ const AXPCrudModifier = {
24262
24172
  queries.list = {
24263
24173
  execute: async (e) => {
24264
24174
  const res = await dataService.query(e);
24265
- console.log('query', res, ctx.module.get() + '.' + ctx.name.get(), e);
24175
+ // console.log('query', res, ctx.module.get() + '.' + ctx.name.get(), e);
24266
24176
  return res;
24267
24177
  },
24268
24178
  type: AXPEntityQueryType.List,
@@ -24305,6 +24215,8 @@ class AXPEntitySearchDefinitionProvider {
24305
24215
  this.entityRegister.getAll().forEach((entity) => {
24306
24216
  const manifest = this.manifestRegistry.get(entity.module);
24307
24217
  const i18nScope = manifest?.i18n ?? moduleNameToI18nScope(entity.module);
24218
+ const searchTitleTemplate = entity.formats.searchResult?.title;
24219
+ const titleContext = buildEntitySearchTitleContext(entity, searchTitleTemplate);
24308
24220
  context.addDefinition(`Module.${entity.module}.${entity.name}`, `@${i18nScope}:module.title`, `Module.${entity.module}`, entity.icon ?? 'fa-solid fa-objects-column', 4, {
24309
24221
  actions: [
24310
24222
  {
@@ -24314,9 +24226,11 @@ class AXPEntitySearchDefinitionProvider {
24314
24226
  },
24315
24227
  ],
24316
24228
  format: {
24317
- title: entity.formats.searchResult?.title,
24229
+ title: searchTitleTemplate,
24318
24230
  description: (entity.formats.searchResult?.description) ?? `${entity.module} / ${entity.title}`,
24319
24231
  id: '{{data.id}}',
24232
+ titleFallbackTemplates: titleContext.fallbackTemplates,
24233
+ titleFallbackFields: titleContext.fallbackFields,
24320
24234
  },
24321
24235
  });
24322
24236
  });
@@ -25000,7 +24914,7 @@ function routesFacory() {
25000
24914
  loadComponent: () => {
25001
24915
  return config.viewers.root();
25002
24916
  },
25003
- canActivate: [AXPAuthGuard],
24917
+ canActivate: [...AXP_PROTECTED_ROUTE_GUARDS],
25004
24918
  children: [
25005
24919
  {
25006
24920
  path: ':module',
@@ -25038,6 +24952,7 @@ function routesFacory() {
25038
24952
  loadComponent: () => {
25039
24953
  return config.viewers.master.details();
25040
24954
  },
24955
+ canDeactivate: [axpDetailsViewCanDeactivateGuard],
25041
24956
  runGuardsAndResolvers: (from, to) => {
25042
24957
  const entityChanged = from.params['module'] !== to.params['module'] || from.params['entity'] !== to.params['entity'];
25043
24958
  const idChanged = from.params['id'] !== to.params['id'];
@@ -25056,8 +24971,16 @@ function routesFacory() {
25056
24971
  redirectTo: ':entity/list',
25057
24972
  pathMatch: 'full',
25058
24973
  },
24974
+ {
24975
+ path: '**',
24976
+ redirectTo: AXP_NOT_FOUND_ROUTE,
24977
+ },
25059
24978
  ],
25060
24979
  },
24980
+ {
24981
+ path: '**',
24982
+ redirectTo: AXP_NOT_FOUND_ROUTE,
24983
+ },
25061
24984
  ],
25062
24985
  },
25063
24986
  ];
@@ -25611,5 +25534,5 @@ var getEntityDetails_query = /*#__PURE__*/Object.freeze({
25611
25534
  * Generated bundle index. Do not edit.
25612
25535
  */
25613
25536
 
25614
- export { ATTACHMENTS_PAGE_COMPONENT_KEY, AXMEntityCrudService, AXMEntityCrudServiceImpl, AXPCategoryTreeService, AXPCreateEntityCommand, AXPCreateEntityWorkflow, AXPDataSeederService, AXPDeleteEntityWorkflow, AXPEditFileUploaderCommand, AXPEntitiesListDataSourceDefinition, AXPEntityApplyUpdatesAction, AXPEntityCategoryTreeSelectorComponent, AXPEntityCategoryWidget, AXPEntityCategoryWidgetColumnComponent, AXPEntityCategoryWidgetEditComponent, AXPEntityCategoryWidgetViewComponent, AXPEntityCommandTriggerViewModel, AXPEntityCreateEvent, AXPEntityCreatePopupAction, AXPEntityCreateSubmittedAction, AXPEntityCreateViewElementViewModel, AXPEntityCreateViewModelFactory, AXPEntityCreateViewSectionViewModel, AXPEntityDataProvider, AXPEntityDataProviderImpl, AXPEntityDataSelectorRowActionsService, AXPEntityDataSelectorService, AXPEntityDefinitionProviderWidget, AXPEntityDefinitionProviderWidgetEditComponent, AXPEntityDefinitionRegistryService, AXPEntityDeletedEvent, AXPEntityDetailListViewModel, AXPEntityDetailPopoverComponent, AXPEntityDetailPopoverService, AXPEntityDetailViewModelFactory, AXPEntityDetailViewModelResolver, AXPEntityEventDispatcherService, AXPEntityEventsKeys, AXPEntityFormBuilderService, AXPEntityListPersistenceModeDefault, AXPEntityListTableService, AXPEntityListToolbarService, AXPEntityListViewCardFieldViewModel, AXPEntityListViewColumnViewModel, AXPEntityListViewModelFactory, AXPEntityListViewModelResolver, AXPEntityListWidget, AXPEntityListWidgetViewComponent, AXPEntityMasterCreateViewModel, AXPEntityMasterListCardSelectActionName, AXPEntityMasterListViewModel, AXPEntityMasterListViewQueryViewModel, AXPEntityMasterSingleElementViewModel, AXPEntityMasterSingleViewGroupViewModel, AXPEntityMasterSingleViewModel, AXPEntityMasterUpdateElementViewModel, AXPEntityMasterUpdateViewModel, AXPEntityMasterUpdateViewModelFactory, AXPEntityMiddleware, AXPEntityModifyConfirmedAction, AXPEntityModifyEvent, AXPEntityModifySectionPopupAction, AXPEntityModule, AXPEntityPerformDeleteAction, AXPEntityPreloadFiltersContainerComponent, AXPEntityPreloadFiltersViewModel, AXPEntityPreloadFiltersViewModelResolver, AXPEntityResolver, AXPEntityService, AXPEntityStorageService, AXPEntityUpdateViewSectionViewModel, AXPFileListComponent, AXPFileUploaderLoadFilesQuery, AXPFileUploaderSaveFilesCommand, AXPFileUploaderWidget, AXPFileUploaderWidgetColumnComponent, AXPFileUploaderWidgetEditComponent, AXPFileUploaderWidgetService, AXPFileUploaderWidgetViewComponent, AXPGetEntityDetailsQuery, AXPLayoutOrderingConfigService, AXPLookupWidget, AXPLookupWidgetColumnComponent, AXPLookupWidgetEditComponent, AXPLookupWidgetViewComponent, AXPMiddlewareAbortError, AXPMiddlewareEntityStorageService, AXPModifyEntitySectionWorkflow, AXPMultiSourceDefinitionProviderContext, AXPMultiSourceDefinitionProviderService, AXPMultiSourceFederatedSearchService, AXPMultiSourceSelectorComponent, AXPMultiSourceSelectorService, AXPMultiSourceSelectorWidget, AXPMultiSourceSelectorWidgetColumnComponent, AXPMultiSourceSelectorWidgetEditComponent, AXPMultiSourceSelectorWidgetViewComponent, AXPMultiSourceType, AXPOpenEntityDetailsCommand, AXPQuickEntityModifyPopupAction, AXPQuickModifyEntityWorkflow, AXPRelatedColumnEnrichmentService, AXPRelatedColumnMetadataResolver, AXPSelectorStructureWidget, AXPSelectorStructureWidgetColumnComponent, AXPSelectorStructureWidgetEditComponent, AXPSelectorStructureWidgetViewComponent, AXPShowDetailViewAction, AXPShowDetailsViewWorkflow, AXPShowListViewAction, AXPShowListViewWorkflow, AXPTruncatedBreadcrumbComponent, AXPUpdateEntityCommand, AXPViewEntityDetailsCommand, AXP_CATEGORY_TREE_ROOT_TITLE_I18N_KEY, AXP_DATA_SEEDER_TOKEN, AXP_ENTITY_ACTION_PLUGIN, AXP_ENTITY_CONFIG_TOKEN, AXP_ENTITY_DEFINITION_LOADER, AXP_ENTITY_MODIFIER, AXP_ENTITY_STORAGE_BACKEND, AXP_ENTITY_STORAGE_MIDDLEWARE, AXP_MULTI_SOURCE_DEFINITION_PROVIDER, AXP_RECORD_WORKFLOW_INFO_CORRELATION_ID_FIELD, AXP_RECORD_WORKFLOW_INFO_DEFINITION_ID_FIELD, AXP_RECORD_WORKFLOW_INFO_INSTANCE_ID_FIELD, DEFAULT_COLUMN_ORDER, DEFAULT_PAIR_SPAN_RULES, DEFAULT_PROPERTY_ORDER, DEFAULT_SECTION_ORDER, ENTITY_LIST_ROUTE_CONTEXT_SESSION_KEY, EntityBuilder, EntityDataAccessor, actionExists, applyDataSourcePagingWithoutLoad, attachmentFieldCount, attachmentsPlugin, attachmentsSemanticallyEqual, axpCreateEntityAiToolInputDefaults, axpCreateEntityCommandDefinition, buildAXPRecordWorkflowInfo, canPersistEntityListState, cloneLayoutArrays, collectEntityQuickSearchFieldPaths, collectNestedCreateHiddenProperties, collectNestedFieldPathsFromEntityColumns, collectQuickSearchPathsFromSingleEntityDefinition, columnOrderingMiddleware, columnOrderingMiddlewareProvider, columnWidthMiddleware, columnWidthMiddlewareProvider, commandMessageTextForError, committedAttachments, computeEntityAggregates, createColumnOrderingMiddlewareProvider, createLayoutOrderingMiddlewareProvider, createModifierContext, defaultCardLayoutMiddleware, defaultCardLayoutMiddlewareProvider, defaultMultiLanguageMiddleware, defaultMultiLanguageMiddlewareProvider, detectEntityChanges, ensureLayoutPropertyView, ensureLayoutSection, ensureListActions, entityDetailsCreateActions, entityDetailsCreateActionsDeferredParent, entityDetailsCrudActions, entityDetailsEditAction, entityDetailsNewEditAction, entityDetailsReferenceCondition, entityDetailsReferenceCreateActions, entityDetailsSimpleCondition, entityMasterBulkDeleteAction, entityMasterCreateAction, entityMasterCrudActions, entityMasterDeleteAction, entityMasterEditAction, entityMasterRecordActions, entityMasterViewAction, entityOverrideDetailsViewAction, eventDispatchMiddleware, filterSortEntityRows, findEntityListRowDataInTree, fingerprintAttachmentItem, fingerprintAttachments, formatLookupItemDisplay, getDataSourcePageIndex, getEntityListRowId, getMasterInterfacePropertySortKey, getRecordWorkflowCorrelationId, getRecordWorkflowInstanceId, hasFileUploaderTitleOrDescriptionFields, isAXPMiddlewareAbortError, isAttachmentListEntry, isCategoryEntity, isCategoryFilter, isFileListItem, isFileUploaderEditDialogAuto, isLegacyEntityDataSelectorOptions, isUnresolvedLookupDisplayTemplate, layoutOrderingMiddlewareFactory, layoutOrderingMiddlewareProvider, mapEntityStorageErrorToCommandResult, mapLegacyEntityDataSelectorOptions, mergeForeignKeyFieldIntoCreateActions, normalizeEntityDataSelectorOptions, normalizeEntityFieldToFileList, normalizeEntityListPersistenceMode, normalizeListPaging, normalizeLookupDisplayTemplate, persistedAttachments, provideEntity, resolveEntityPluginDetailPageOrder, resolveFileUploaderEditDialog, resolveFileUploaderEntityScope, resolveLookupDisplayField, resolveLookupDisplayTemplate, restoreEntityListExpandedRows, runEntityQuery, searchResultDescriptionMiddleware, searchResultDescriptionMiddlewareProvider, shouldLoadEntityListStateFromStorage, shouldResetEntityListStateOnRouteEntry };
25537
+ export { ATTACHMENTS_PAGE_COMPONENT_KEY, AXMEntityCrudService, AXMEntityCrudServiceImpl, AXPCategoryTreeService, AXPCreateEntityCommand, AXPCreateEntityWorkflow, AXPDataSeederService, AXPDeleteEntityWorkflow, AXPEditFileUploaderCommand, AXPEntitiesListDataSourceDefinition, AXPEntityApplyUpdatesAction, AXPEntityCategoryTreeSelectorComponent, AXPEntityCategoryWidget, AXPEntityCategoryWidgetColumnComponent, AXPEntityCategoryWidgetEditComponent, AXPEntityCategoryWidgetViewComponent, AXPEntityCommandTriggerViewModel, AXPEntityCreateEvent, AXPEntityCreatePopupAction, AXPEntityCreateSubmittedAction, AXPEntityCreateViewElementViewModel, AXPEntityCreateViewModelFactory, AXPEntityCreateViewSectionViewModel, AXPEntityDataProvider, AXPEntityDataProviderImpl, AXPEntityDataSelectorRowActionsService, AXPEntityDataSelectorService, AXPEntityDefinitionProviderWidget, AXPEntityDefinitionProviderWidgetEditComponent, AXPEntityDefinitionRegistryService, AXPEntityDeletedEvent, AXPEntityDetailListViewModel, AXPEntityDetailPopoverComponent, AXPEntityDetailPopoverService, AXPEntityDetailViewModelFactory, AXPEntityDetailViewModelResolver, AXPEntityEventDispatcherService, AXPEntityEventsKeys, AXPEntityFormBuilderService, AXPEntityListPersistenceModeDefault, AXPEntityListTableService, AXPEntityListToolbarService, AXPEntityListViewCardFieldViewModel, AXPEntityListViewColumnViewModel, AXPEntityListViewModelFactory, AXPEntityListViewModelResolver, AXPEntityListWidget, AXPEntityListWidgetViewComponent, AXPEntityMasterCreateViewModel, AXPEntityMasterListCardSelectActionName, AXPEntityMasterListViewModel, AXPEntityMasterListViewQueryViewModel, AXPEntityMasterSingleElementViewModel, AXPEntityMasterSingleViewGroupViewModel, AXPEntityMasterSingleViewModel, AXPEntityMasterUpdateElementViewModel, AXPEntityMasterUpdateViewModel, AXPEntityMasterUpdateViewModelFactory, AXPEntityMiddleware, AXPEntityModifyConfirmedAction, AXPEntityModifyEvent, AXPEntityModifySectionPopupAction, AXPEntityModule, AXPEntityPerformDeleteAction, AXPEntityPreloadFiltersContainerComponent, AXPEntityPreloadFiltersViewModel, AXPEntityPreloadFiltersViewModelResolver, AXPEntityResolver, AXPEntityService, AXPEntityStorageService, AXPEntityUpdateViewSectionViewModel, AXPFileListComponent, AXPFileUploaderLoadFilesQuery, AXPFileUploaderSaveFilesCommand, AXPFileUploaderWidget, AXPFileUploaderWidgetColumnComponent, AXPFileUploaderWidgetEditComponent, AXPFileUploaderWidgetService, AXPFileUploaderWidgetViewComponent, AXPGetEntityDetailsQuery, AXPLayoutOrderingConfigService, AXPLookupWidget, AXPLookupWidgetColumnComponent, AXPLookupWidgetEditComponent, AXPLookupWidgetViewComponent, AXPMiddlewareAbortError, AXPMiddlewareEntityStorageService, AXPModifyEntitySectionWorkflow, AXPMultiSourceDefinitionProviderContext, AXPMultiSourceDefinitionProviderService, AXPMultiSourceFederatedSearchService, AXPMultiSourceSelectorComponent, AXPMultiSourceSelectorService, AXPMultiSourceSelectorWidget, AXPMultiSourceSelectorWidgetColumnComponent, AXPMultiSourceSelectorWidgetEditComponent, AXPMultiSourceSelectorWidgetViewComponent, AXPMultiSourceType, AXPOpenEntityDetailsCommand, AXPQuickEntityModifyPopupAction, AXPQuickModifyEntityWorkflow, AXPRelatedColumnEnrichmentService, AXPRelatedColumnMetadataResolver, AXPSelectorStructureWidget, AXPSelectorStructureWidgetColumnComponent, AXPSelectorStructureWidgetEditComponent, AXPSelectorStructureWidgetViewComponent, AXPShowDetailViewAction, AXPShowDetailsViewWorkflow, AXPShowListViewAction, AXPShowListViewWorkflow, AXPTruncatedBreadcrumbComponent, AXPUpdateEntityCommand, AXPViewEntityDetailsCommand, AXP_CATEGORY_TREE_ROOT_TITLE_I18N_KEY, AXP_DATA_SEEDER_TOKEN, AXP_ENTITY_ACTION_PLUGIN, AXP_ENTITY_CONFIG_TOKEN, AXP_ENTITY_DEFINITION_LOADER, AXP_ENTITY_MODIFIER, AXP_ENTITY_STORAGE_BACKEND, AXP_ENTITY_STORAGE_MIDDLEWARE, AXP_MULTI_SOURCE_DEFINITION_PROVIDER, AXP_RECORD_WORKFLOW_INFO_CORRELATION_ID_FIELD, AXP_RECORD_WORKFLOW_INFO_DEFINITION_ID_FIELD, AXP_RECORD_WORKFLOW_INFO_INSTANCE_ID_FIELD, DEFAULT_COLUMN_ORDER, DEFAULT_PAIR_SPAN_RULES, DEFAULT_PROPERTY_ORDER, DEFAULT_SECTION_ORDER, ENTITY_LIST_ROUTE_CONTEXT_SESSION_KEY, EntityBuilder, EntityDataAccessor, actionExists, applyDataSourcePagingWithoutLoad, attachmentFieldCount, attachmentsPlugin, attachmentsSemanticallyEqual, axpCreateEntityAiToolInputDefaults, axpCreateEntityCommandDefinition, buildAXPRecordWorkflowInfo, canPersistEntityListState, cloneLayoutArrays, collectEntityQuickSearchFieldPaths, collectNestedCreateHiddenProperties, collectNestedFieldPathsFromEntityColumns, collectQuickSearchPathsFromSingleEntityDefinition, columnOrderingMiddleware, columnOrderingMiddlewareProvider, columnWidthMiddleware, columnWidthMiddlewareProvider, commandMessageTextForError, committedAttachments, computeEntityAggregates, createColumnOrderingMiddlewareProvider, createLayoutOrderingMiddlewareProvider, createModifierContext, defaultCardLayoutMiddleware, defaultCardLayoutMiddlewareProvider, defaultMultiLanguageMiddleware, defaultMultiLanguageMiddlewareProvider, detectEntityChanges, ensureLayoutPropertyView, ensureLayoutSection, ensureListActions, entityDetailsCreateActions, entityDetailsCreateActionsDeferredParent, entityDetailsCrudActions, entityDetailsEditAction, entityDetailsNewEditAction, entityDetailsReferenceCondition, entityDetailsReferenceCreateActions, entityDetailsSimpleCondition, entityMasterBulkDeleteAction, entityMasterCreateAction, entityMasterCrudActions, entityMasterDeleteAction, entityMasterEditAction, entityMasterRecordActions, entityMasterViewAction, entityOverrideDetailsViewAction, eventDispatchMiddleware, filterSortEntityRows, findEntityListRowDataInTree, fingerprintAttachmentItem, fingerprintAttachments, formatLookupItemDisplay, getDataSourcePageIndex, getEntityListRowId, getMasterInterfacePropertySortKey, getRecordWorkflowCorrelationId, getRecordWorkflowInstanceId, hasFileUploaderTitleOrDescriptionFields, isAXPMiddlewareAbortError, isAttachmentListEntry, isCategoryEntity, isCategoryFilter, isFileListItem, isFileUploaderEditDialogAuto, isLegacyEntityDataSelectorOptions, layoutOrderingMiddlewareFactory, layoutOrderingMiddlewareProvider, mapEntityStorageErrorToCommandResult, mapLegacyEntityDataSelectorOptions, mergeForeignKeyFieldIntoCreateActions, normalizeEntityDataSelectorOptions, normalizeEntityFieldToFileList, normalizeEntityListPersistenceMode, normalizeListPaging, persistedAttachments, provideEntity, resolveEntityPluginDetailPageOrder, resolveFileUploaderEditDialog, resolveFileUploaderEntityScope, resolveLookupDisplayField, resolveLookupDisplayTemplate, restoreEntityListExpandedRows, runEntityQuery, searchResultDescriptionMiddleware, searchResultDescriptionMiddlewareProvider, shouldLoadEntityListStateFromStorage, shouldResetEntityListStateOnRouteEntry };
25615
25538
  //# sourceMappingURL=acorex-platform-layout-entity.mjs.map