@acorex/platform 21.0.0-next.59 → 21.0.0-next.64

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.
@@ -21,12 +21,12 @@ import * as i4 from '@acorex/components/skeleton';
21
21
  import { AXSkeletonModule } from '@acorex/components/skeleton';
22
22
  import * as i2 from '@acorex/components/popover';
23
23
  import { AXPopoverModule } from '@acorex/components/popover';
24
+ import { AXFormatService } from '@acorex/core/format';
24
25
  import * as i5 from '@angular/common';
25
26
  import { CommonModule, AsyncPipe } from '@angular/common';
26
27
  import { AXPThemeLayoutBlockComponent, AXPPreloadFiltersComponent, AXPStateMessageComponent, AXPColumnItemListComponent, AXPDataSelectorService, AXPPageComponentRegistryService } from '@acorex/platform/layout/components';
27
28
  import { AXPPageLayoutBaseComponent, AXPPageLayoutComponent, AXPPageComponentInstanceRegistryService } from '@acorex/platform/layout/views';
28
29
  import { AXDataSource } from '@acorex/cdk/common';
29
- import { AXFormatService } from '@acorex/core/format';
30
30
  import * as i1$3 from '@acorex/platform/workflow';
31
31
  import { AXPWorkflowService, ofType, createWorkFlowEvent, AXPWorkflowAction, AXPWorkflowEventService, AXPWorkflowModule } from '@acorex/platform/workflow';
32
32
  import { AXPLayoutThemeService } from '@acorex/platform/themes/shared';
@@ -2845,17 +2845,119 @@ var viewEntityDetails_command = /*#__PURE__*/Object.freeze({
2845
2845
  AXPViewEntityDetailsCommand: AXPViewEntityDetailsCommand
2846
2846
  });
2847
2847
 
2848
+ //#region ---- Template resolution ----
2849
+ /**
2850
+ * Normalizes lookup/search display templates for row-based formatting.
2851
+ * Converts `context.eval('path')` expressions to `{{ path }}` and single braces to mustache form.
2852
+ */
2853
+ function normalizeLookupDisplayTemplate(template) {
2854
+ const withContextEval = template.replace(/\{\{\s*context\.eval\(['"]([^'"]+)['"]\)\s*\}\}/g, '{{ $1 }}');
2855
+ if (withContextEval.includes('{{')) {
2856
+ return withContextEval;
2857
+ }
2858
+ return withContextEval.replace(/\{/g, '{{').replace(/\}/g, '}}');
2859
+ }
2860
+ /**
2861
+ * Resolves the display template for a lookup item.
2862
+ * Priority: explicit `displayFormat` → entity `formats.lookup` (template) → entity `formats.searchResult.title`.
2863
+ * Returns undefined when `textField` is set or no template applies (use {@link resolveLookupDisplayField} instead).
2864
+ */
2865
+ function resolveLookupDisplayTemplate(entity, options) {
2866
+ const explicit = options.displayFormat?.trim();
2867
+ if (explicit) {
2868
+ return normalizeLookupDisplayTemplate(explicit);
2869
+ }
2870
+ if (options.textField?.trim()) {
2871
+ return undefined;
2872
+ }
2873
+ const lookupFormat = entity?.formats?.lookup?.trim();
2874
+ if (lookupFormat && (lookupFormat.includes('{{') || lookupFormat.includes('{'))) {
2875
+ return normalizeLookupDisplayTemplate(lookupFormat);
2876
+ }
2877
+ const searchTitle = entity?.formats?.searchResult?.title?.trim();
2878
+ if (searchTitle) {
2879
+ return normalizeLookupDisplayTemplate(searchTitle);
2880
+ }
2881
+ return undefined;
2882
+ }
2883
+ /**
2884
+ * Resolves the property path used for lookup display when no template applies.
2885
+ * Priority: explicit `textField` → entity `formats.lookup` (simple path) → common property names.
2886
+ */
2887
+ function resolveLookupDisplayField(entity, options) {
2888
+ if (options.textField?.trim()) {
2889
+ return options.textField.trim();
2890
+ }
2891
+ const lookupFormat = entity?.formats?.lookup?.trim();
2892
+ if (lookupFormat && !lookupFormat.includes('{') && !lookupFormat.includes('{{')) {
2893
+ return lookupFormat;
2894
+ }
2895
+ const defaults = ['title', 'name', 'code', 'description'];
2896
+ const found = defaults.find((name) => entity?.properties?.some((p) => p.name === name));
2897
+ return found ?? 'title';
2898
+ }
2899
+ /**
2900
+ * Formats a lookup row for display using template and/or field resolution.
2901
+ */
2902
+ function formatLookupItemDisplay(item, entity, options, formatService, resolveMultiLanguage) {
2903
+ if (item == null) {
2904
+ return '';
2905
+ }
2906
+ if (typeof item === 'string' || typeof item === 'number' || typeof item === 'boolean') {
2907
+ return String(item);
2908
+ }
2909
+ if (typeof item !== 'object') {
2910
+ return '';
2911
+ }
2912
+ if (options.isMultiLanguage?.(item) && resolveMultiLanguage) {
2913
+ return resolveMultiLanguage(item);
2914
+ }
2915
+ const template = resolveLookupDisplayTemplate(entity, options);
2916
+ if (template) {
2917
+ const formatted = formatService.format(template, 'string', item);
2918
+ if (formatted) {
2919
+ const text = String(formatted);
2920
+ if (!isUnresolvedLookupDisplayTemplate(text)) {
2921
+ return text;
2922
+ }
2923
+ }
2924
+ }
2925
+ const field = resolveLookupDisplayField(entity, options);
2926
+ const raw = get(item, field);
2927
+ if (raw == null) {
2928
+ return '';
2929
+ }
2930
+ if (typeof raw === 'string' || typeof raw === 'number' || typeof raw === 'boolean') {
2931
+ return String(raw);
2932
+ }
2933
+ if (typeof raw === 'object' && !Array.isArray(raw)) {
2934
+ if (resolveMultiLanguage) {
2935
+ return resolveMultiLanguage(raw);
2936
+ }
2937
+ return raw;
2938
+ }
2939
+ return String(raw);
2940
+ }
2941
+ /** True when a formatted label still contains unresolved template markers. */
2942
+ function isUnresolvedLookupDisplayTemplate(value) {
2943
+ return /\{\{/.test(value) || /context\.eval\s*\(/.test(value);
2944
+ }
2945
+ //#endregion
2946
+
2848
2947
  class AXPEntityDetailPopoverComponent {
2849
2948
  constructor() {
2850
2949
  //#region ---- Dependencies ----
2851
2950
  this.commandService = inject(AXPCommandService);
2852
2951
  this.queryService = inject(AXPQueryService);
2952
+ this.formatService = inject(AXFormatService);
2953
+ this.translation = inject(AXTranslationService);
2853
2954
  //#endregion
2854
2955
  //#region ---- Inputs ----
2855
2956
  this.entity = input.required(...(ngDevMode ? [{ debugName: "entity" }] : /* istanbul ignore next */ []));
2856
2957
  this.entityId = input.required(...(ngDevMode ? [{ debugName: "entityId" }] : /* istanbul ignore next */ []));
2857
2958
  this.textField = input('title', ...(ngDevMode ? [{ debugName: "textField" }] : /* istanbul ignore next */ []));
2858
2959
  this.valueField = input('id', ...(ngDevMode ? [{ debugName: "valueField" }] : /* istanbul ignore next */ []));
2960
+ this.displayTitle = input('', ...(ngDevMode ? [{ debugName: "displayTitle" }] : /* istanbul ignore next */ []));
2859
2961
  this.item = input(...(ngDevMode ? [undefined, { debugName: "item" }] : /* istanbul ignore next */ []));
2860
2962
  this.breadcrumb = input(null, ...(ngDevMode ? [{ debugName: "breadcrumb" }] : /* istanbul ignore next */ []));
2861
2963
  //#endregion
@@ -2868,6 +2970,29 @@ class AXPEntityDetailPopoverComponent {
2868
2970
  this.isDetailPopoverOpen = signal(false, ...(ngDevMode ? [{ debugName: "isDetailPopoverOpen" }] : /* istanbul ignore next */ []));
2869
2971
  /** Placeholder rows for the loading skeleton (matches typical property count). */
2870
2972
  this.loadingSkeletonRows = [1, 2, 3, 4, 5];
2973
+ this.headerTitle = computed(() => {
2974
+ const details = this.entityDetails();
2975
+ const entityDefinition = details?.entityDefinition;
2976
+ const data = details?.entityData;
2977
+ if (data && entityDefinition) {
2978
+ const fromDetails = this.resolveLookupDisplayLabel(data, entityDefinition);
2979
+ if (fromDetails) {
2980
+ return fromDetails;
2981
+ }
2982
+ }
2983
+ const preset = this.displayTitle()?.trim();
2984
+ if (preset && !isUnresolvedLookupDisplayTemplate(preset)) {
2985
+ return preset;
2986
+ }
2987
+ const fallbackItem = this.item();
2988
+ if (fallbackItem) {
2989
+ const fromItem = this.resolveLookupDisplayLabel(fallbackItem, entityDefinition);
2990
+ if (fromItem) {
2991
+ return fromItem;
2992
+ }
2993
+ }
2994
+ return preset ?? '';
2995
+ }, ...(ngDevMode ? [{ debugName: "headerTitle" }] : /* istanbul ignore next */ []));
2871
2996
  /**
2872
2997
  * Stable list of property widgets for the template. Must be a signal (computed), not a method:
2873
2998
  * calling a method from the template rebuilds nodes every CD cycle and can make the widget renderer loop.
@@ -2952,6 +3077,11 @@ class AXPEntityDetailPopoverComponent {
2952
3077
  }
2953
3078
  //#endregion
2954
3079
  //#region ---- Helper Methods ----
3080
+ resolveLookupDisplayLabel(item, entityDefinition) {
3081
+ const label = formatLookupItemDisplay(item, entityDefinition, { textField: this.textField() }, this.formatService, (value) => this.translation.resolve(value));
3082
+ const resolved = typeof label === 'string' ? label : this.translation.resolve(label);
3083
+ return resolved && !isUnresolvedLookupDisplayTemplate(resolved) ? resolved : '';
3084
+ }
2955
3085
  /**
2956
3086
  * Returns true if a value is meaningful for display (non-empty/non-null).
2957
3087
  */
@@ -3129,12 +3259,12 @@ class AXPEntityDetailPopoverComponent {
3129
3259
  return prop.name;
3130
3260
  }
3131
3261
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPEntityDetailPopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3132
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPEntityDetailPopoverComponent, isStandalone: true, selector: "axp-entity-detail-popover", inputs: { entity: { classPropertyName: "entity", publicName: "entity", isSignal: true, isRequired: true, transformFunction: null }, entityId: { classPropertyName: "entityId", publicName: "entityId", isSignal: true, isRequired: true, transformFunction: null }, textField: { classPropertyName: "textField", publicName: "textField", isSignal: true, isRequired: false, transformFunction: null }, valueField: { classPropertyName: "valueField", publicName: "valueField", isSignal: true, isRequired: false, transformFunction: null }, item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: false, transformFunction: null }, breadcrumb: { classPropertyName: "breadcrumb", publicName: "breadcrumb", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "detailPopover", first: true, predicate: ["detailPopover"], descendants: true, isSignal: true }], ngImport: i0, template: "<ax-popover [openOn]=\"'manual'\" #detailPopover (openChange)=\"onDetailPopoverOpenChange($event)\">\n <div class=\"ax-lightest-surface ax-border ax-rounded-lg ax-shadow-lg ax-p-4 ax-w-[min(600px,calc(100vw-2rem))]\">\n <div class=\"ax-mb-4 ax-border-b ax-pb-2\">\n <h3 class=\"ax-text-base ax-font-semibold ax-text-on-lightest-surface\">\n {{ entityDetails()?.entityData?.[textField()] ?? item()?.[textField()] | translate | async }}\n </h3>\n @if (breadcrumb()) {\n <div class=\"ax-text-xs ax-text-neutral-500 ax-mt-1\">{{ breadcrumb() }}</div>\n }\n </div>\n @if (isLoadingDetails()) {\n <div class=\"ax-space-y-2 ax-mb-4\">\n @for (row of loadingSkeletonRows; track row) {\n <div class=\"ax-grid ax-grid-cols-[minmax(7rem,35%)_1fr] ax-gap-x-4 ax-items-start\">\n <ax-skeleton [animated]=\"true\" class=\"ax-h-4 ax-w-2/3 ax-rounded\"></ax-skeleton>\n @if (row === 2) {\n <ax-skeleton [animated]=\"true\" class=\"ax-h-16 ax-w-full ax-rounded\"></ax-skeleton>\n } @else {\n <ax-skeleton [animated]=\"true\" class=\"ax-h-4 ax-w-full ax-rounded\"></ax-skeleton>\n }\n </div>\n }\n </div>\n <div class=\"ax-flex ax-justify-end\">\n <ax-skeleton [animated]=\"true\" class=\"ax-h-9 ax-w-28 ax-rounded\"></ax-skeleton>\n </div>\n } @else if (entityDetails()) {\n <div class=\"ax-space-y-3 ax-mb-4\">\n <!-- Important Entity Data -->\n @if (entityDetails()?.entityData) {\n <axp-widgets-container [context]=\"entityDetails()?.entityData\">\n <div class=\"ax-space-y-2\">\n @for (item of entityPropertiesWithWidgets(); track item.name) {\n <div class=\"ax-grid ax-grid-cols-[minmax(7rem,35%)_1fr] ax-gap-x-4 ax-gap-y-1 ax-items-start\">\n <span class=\"ax-text-sm ax-font-medium ax-text-neutral-600 ax-shrink-0\">{{ item.title | translate | async }}:</span>\n <div class=\"ax-min-w-0 ax-text-sm\">\n <ng-container axp-widget-renderer [node]=\"item.node\" [mode]=\"'view'\"></ng-container>\n </div>\n </div>\n }\n </div>\n </axp-widgets-container>\n }\n </div>\n <div class=\"ax-flex ax-gap-2 ax-justify-end ax-sm\">\n <ax-button [color]=\"'primary'\" [look]=\"'solid'\" [text]=\"'@general:actions.open-details.title' | translate | async\"\n (click)=\"navigateToDetails()\">\n </ax-button>\n </div>\n }\n </div>\n</ax-popover>", 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: AXPopoverModule }, { kind: "component", type: i2.AXPopoverComponent, selector: "ax-popover", inputs: ["width", "disablePanelClass", "disabled", "offsetX", "offsetY", "target", "placement", "content", "openOn", "closeOn", "hasBackdrop", "openAfter", "closeAfter", "closeOnScroll", "backdropClass", "panelClass", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i3.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: i3.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged", "onLoad"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i4.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3262
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPEntityDetailPopoverComponent, isStandalone: true, selector: "axp-entity-detail-popover", inputs: { entity: { classPropertyName: "entity", publicName: "entity", isSignal: true, isRequired: true, transformFunction: null }, entityId: { classPropertyName: "entityId", publicName: "entityId", isSignal: true, isRequired: true, transformFunction: null }, textField: { classPropertyName: "textField", publicName: "textField", isSignal: true, isRequired: false, transformFunction: null }, valueField: { classPropertyName: "valueField", publicName: "valueField", isSignal: true, isRequired: false, transformFunction: null }, displayTitle: { classPropertyName: "displayTitle", publicName: "displayTitle", isSignal: true, isRequired: false, transformFunction: null }, item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: false, transformFunction: null }, breadcrumb: { classPropertyName: "breadcrumb", publicName: "breadcrumb", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "detailPopover", first: true, predicate: ["detailPopover"], descendants: true, isSignal: true }], ngImport: i0, template: "<ax-popover [openOn]=\"'manual'\" #detailPopover (openChange)=\"onDetailPopoverOpenChange($event)\">\n <div class=\"ax-lightest-surface ax-p-4 ax-w-[min(600px,calc(100vw-2rem))]\">\n <div class=\"ax-mb-4 ax-border-b ax-pb-2\">\n <h3 class=\"ax-text-base ax-font-semibold ax-text-on-lightest-surface\">\n {{ headerTitle() }}\n </h3>\n @if (breadcrumb()) {\n <div class=\"ax-text-xs ax-text-neutral-500 ax-mt-1\">{{ breadcrumb() }}</div>\n }\n </div>\n @if (isLoadingDetails()) {\n <div class=\"ax-space-y-2 ax-mb-4\">\n @for (row of loadingSkeletonRows; track row) {\n <div class=\"ax-grid ax-grid-cols-[minmax(7rem,35%)_1fr] ax-gap-x-4 ax-items-start\">\n <ax-skeleton [animated]=\"true\" class=\"ax-h-4 ax-w-2/3 ax-rounded\"></ax-skeleton>\n @if (row === 2) {\n <ax-skeleton [animated]=\"true\" class=\"ax-h-16 ax-w-full ax-rounded\"></ax-skeleton>\n } @else {\n <ax-skeleton [animated]=\"true\" class=\"ax-h-4 ax-w-full ax-rounded\"></ax-skeleton>\n }\n </div>\n }\n </div>\n <div class=\"ax-flex ax-justify-end\">\n <ax-skeleton [animated]=\"true\" class=\"ax-h-9 ax-w-28 ax-rounded\"></ax-skeleton>\n </div>\n } @else if (entityDetails()) {\n <div class=\"ax-space-y-3 ax-mb-4\">\n <!-- Important Entity Data -->\n @if (entityDetails()?.entityData) {\n <axp-widgets-container [context]=\"entityDetails()?.entityData\">\n <div class=\"ax-space-y-2\">\n @for (item of entityPropertiesWithWidgets(); track item.name) {\n <div class=\"ax-grid ax-grid-cols-[minmax(7rem,35%)_1fr] ax-gap-x-4 ax-gap-y-1 ax-items-start\">\n <span class=\"ax-text-sm ax-font-medium ax-text-neutral-600 ax-shrink-0\">{{ item.title | translate | async\n }}:</span>\n <div class=\"ax-min-w-0 ax-text-sm\">\n <ng-container axp-widget-renderer [node]=\"item.node\" [mode]=\"'view'\"></ng-container>\n </div>\n </div>\n }\n </div>\n </axp-widgets-container>\n }\n </div>\n <div class=\"ax-flex ax-gap-2 ax-justify-end ax-sm\">\n <ax-button [color]=\"'primary'\" [look]=\"'solid'\" [text]=\"'@general:actions.open-details.title' | translate | async\"\n (click)=\"navigateToDetails()\">\n </ax-button>\n </div>\n }\n </div>\n</ax-popover>", 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: AXPopoverModule }, { kind: "component", type: i2.AXPopoverComponent, selector: "ax-popover", inputs: ["width", "disablePanelClass", "disabled", "offsetX", "offsetY", "target", "placement", "content", "openOn", "closeOn", "hasBackdrop", "openAfter", "closeAfter", "closeOnScroll", "backdropClass", "panelClass", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i3.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: i3.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged", "onLoad"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i4.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3133
3263
  }
3134
3264
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPEntityDetailPopoverComponent, decorators: [{
3135
3265
  type: Component,
3136
- args: [{ selector: 'axp-entity-detail-popover', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, AXButtonModule, AXPopoverModule, AXPWidgetCoreModule, AXTranslationModule, AXSkeletonModule], template: "<ax-popover [openOn]=\"'manual'\" #detailPopover (openChange)=\"onDetailPopoverOpenChange($event)\">\n <div class=\"ax-lightest-surface ax-border ax-rounded-lg ax-shadow-lg ax-p-4 ax-w-[min(600px,calc(100vw-2rem))]\">\n <div class=\"ax-mb-4 ax-border-b ax-pb-2\">\n <h3 class=\"ax-text-base ax-font-semibold ax-text-on-lightest-surface\">\n {{ entityDetails()?.entityData?.[textField()] ?? item()?.[textField()] | translate | async }}\n </h3>\n @if (breadcrumb()) {\n <div class=\"ax-text-xs ax-text-neutral-500 ax-mt-1\">{{ breadcrumb() }}</div>\n }\n </div>\n @if (isLoadingDetails()) {\n <div class=\"ax-space-y-2 ax-mb-4\">\n @for (row of loadingSkeletonRows; track row) {\n <div class=\"ax-grid ax-grid-cols-[minmax(7rem,35%)_1fr] ax-gap-x-4 ax-items-start\">\n <ax-skeleton [animated]=\"true\" class=\"ax-h-4 ax-w-2/3 ax-rounded\"></ax-skeleton>\n @if (row === 2) {\n <ax-skeleton [animated]=\"true\" class=\"ax-h-16 ax-w-full ax-rounded\"></ax-skeleton>\n } @else {\n <ax-skeleton [animated]=\"true\" class=\"ax-h-4 ax-w-full ax-rounded\"></ax-skeleton>\n }\n </div>\n }\n </div>\n <div class=\"ax-flex ax-justify-end\">\n <ax-skeleton [animated]=\"true\" class=\"ax-h-9 ax-w-28 ax-rounded\"></ax-skeleton>\n </div>\n } @else if (entityDetails()) {\n <div class=\"ax-space-y-3 ax-mb-4\">\n <!-- Important Entity Data -->\n @if (entityDetails()?.entityData) {\n <axp-widgets-container [context]=\"entityDetails()?.entityData\">\n <div class=\"ax-space-y-2\">\n @for (item of entityPropertiesWithWidgets(); track item.name) {\n <div class=\"ax-grid ax-grid-cols-[minmax(7rem,35%)_1fr] ax-gap-x-4 ax-gap-y-1 ax-items-start\">\n <span class=\"ax-text-sm ax-font-medium ax-text-neutral-600 ax-shrink-0\">{{ item.title | translate | async }}:</span>\n <div class=\"ax-min-w-0 ax-text-sm\">\n <ng-container axp-widget-renderer [node]=\"item.node\" [mode]=\"'view'\"></ng-container>\n </div>\n </div>\n }\n </div>\n </axp-widgets-container>\n }\n </div>\n <div class=\"ax-flex ax-gap-2 ax-justify-end ax-sm\">\n <ax-button [color]=\"'primary'\" [look]=\"'solid'\" [text]=\"'@general:actions.open-details.title' | translate | async\"\n (click)=\"navigateToDetails()\">\n </ax-button>\n </div>\n }\n </div>\n</ax-popover>" }]
3137
- }], propDecorators: { entity: [{ type: i0.Input, args: [{ isSignal: true, alias: "entity", required: true }] }], entityId: [{ type: i0.Input, args: [{ isSignal: true, alias: "entityId", required: true }] }], textField: [{ type: i0.Input, args: [{ isSignal: true, alias: "textField", required: false }] }], valueField: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueField", required: false }] }], item: [{ type: i0.Input, args: [{ isSignal: true, alias: "item", required: false }] }], breadcrumb: [{ type: i0.Input, args: [{ isSignal: true, alias: "breadcrumb", required: false }] }], detailPopover: [{ type: i0.ViewChild, args: ['detailPopover', { isSignal: true }] }] } });
3266
+ args: [{ selector: 'axp-entity-detail-popover', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, AXButtonModule, AXPopoverModule, AXPWidgetCoreModule, AXTranslationModule, AXSkeletonModule], template: "<ax-popover [openOn]=\"'manual'\" #detailPopover (openChange)=\"onDetailPopoverOpenChange($event)\">\n <div class=\"ax-lightest-surface ax-p-4 ax-w-[min(600px,calc(100vw-2rem))]\">\n <div class=\"ax-mb-4 ax-border-b ax-pb-2\">\n <h3 class=\"ax-text-base ax-font-semibold ax-text-on-lightest-surface\">\n {{ headerTitle() }}\n </h3>\n @if (breadcrumb()) {\n <div class=\"ax-text-xs ax-text-neutral-500 ax-mt-1\">{{ breadcrumb() }}</div>\n }\n </div>\n @if (isLoadingDetails()) {\n <div class=\"ax-space-y-2 ax-mb-4\">\n @for (row of loadingSkeletonRows; track row) {\n <div class=\"ax-grid ax-grid-cols-[minmax(7rem,35%)_1fr] ax-gap-x-4 ax-items-start\">\n <ax-skeleton [animated]=\"true\" class=\"ax-h-4 ax-w-2/3 ax-rounded\"></ax-skeleton>\n @if (row === 2) {\n <ax-skeleton [animated]=\"true\" class=\"ax-h-16 ax-w-full ax-rounded\"></ax-skeleton>\n } @else {\n <ax-skeleton [animated]=\"true\" class=\"ax-h-4 ax-w-full ax-rounded\"></ax-skeleton>\n }\n </div>\n }\n </div>\n <div class=\"ax-flex ax-justify-end\">\n <ax-skeleton [animated]=\"true\" class=\"ax-h-9 ax-w-28 ax-rounded\"></ax-skeleton>\n </div>\n } @else if (entityDetails()) {\n <div class=\"ax-space-y-3 ax-mb-4\">\n <!-- Important Entity Data -->\n @if (entityDetails()?.entityData) {\n <axp-widgets-container [context]=\"entityDetails()?.entityData\">\n <div class=\"ax-space-y-2\">\n @for (item of entityPropertiesWithWidgets(); track item.name) {\n <div class=\"ax-grid ax-grid-cols-[minmax(7rem,35%)_1fr] ax-gap-x-4 ax-gap-y-1 ax-items-start\">\n <span class=\"ax-text-sm ax-font-medium ax-text-neutral-600 ax-shrink-0\">{{ item.title | translate | async\n }}:</span>\n <div class=\"ax-min-w-0 ax-text-sm\">\n <ng-container axp-widget-renderer [node]=\"item.node\" [mode]=\"'view'\"></ng-container>\n </div>\n </div>\n }\n </div>\n </axp-widgets-container>\n }\n </div>\n <div class=\"ax-flex ax-gap-2 ax-justify-end ax-sm\">\n <ax-button [color]=\"'primary'\" [look]=\"'solid'\" [text]=\"'@general:actions.open-details.title' | translate | async\"\n (click)=\"navigateToDetails()\">\n </ax-button>\n </div>\n }\n </div>\n</ax-popover>" }]
3267
+ }], propDecorators: { entity: [{ type: i0.Input, args: [{ isSignal: true, alias: "entity", required: true }] }], entityId: [{ type: i0.Input, args: [{ isSignal: true, alias: "entityId", required: true }] }], textField: [{ type: i0.Input, args: [{ isSignal: true, alias: "textField", required: false }] }], valueField: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueField", required: false }] }], displayTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayTitle", required: false }] }], item: [{ type: i0.Input, args: [{ isSignal: true, alias: "item", required: false }] }], breadcrumb: [{ type: i0.Input, args: [{ isSignal: true, alias: "breadcrumb", required: false }] }], detailPopover: [{ type: i0.ViewChild, args: ['detailPopover', { isSignal: true }] }] } });
3138
3268
 
3139
3269
  class AXPEntityDetailPopoverService {
3140
3270
  constructor() {
@@ -3164,6 +3294,7 @@ class AXPEntityDetailPopoverService {
3164
3294
  this.activePopoverRef.setInput('entityId', options.id);
3165
3295
  this.activePopoverRef.setInput('textField', options.textField || 'title');
3166
3296
  this.activePopoverRef.setInput('valueField', options.valueField || 'id');
3297
+ this.activePopoverRef.setInput('displayTitle', options.displayTitle ?? '');
3167
3298
  this.activePopoverRef.setInput('breadcrumb', options.breadcrumb || null);
3168
3299
  this.activePopoverRef.setInput('item', options.item);
3169
3300
  // Attach to application
@@ -3964,7 +4095,6 @@ class AXPEntityListViewColumnViewModel {
3964
4095
  this.name = this.property.name;
3965
4096
  this.title = this.column.title ?? this.property.title;
3966
4097
  this.visible = this.column?.options?.visible ?? true;
3967
- this.dir = this.column.sort?.dir;
3968
4098
  this.sortEnabled = this.property.options?.sort?.enabled;
3969
4099
  this.node = computed(() => {
3970
4100
  const widget = this.property.schema.interface;
@@ -4977,7 +5107,7 @@ class AXPEntityMasterListViewModel {
4977
5107
  this.events$.next({ action: 'refresh' });
4978
5108
  }
4979
5109
  });
4980
- this.sortedFields.set(this.sortableFields());
5110
+ this.sortedFields.set([]);
4981
5111
  void this.syncShowRowIndexColumnSetting();
4982
5112
  this.settings.onLoaded.pipe(takeUntil(this.destroyed)).subscribe(() => {
4983
5113
  void this.syncShowRowIndexColumnSetting();
@@ -5080,10 +5210,8 @@ class AXPEntityMasterListViewModel {
5080
5210
  .sort((a, b) => columns.findIndex((col) => col.name === (a.column?.options?.dataPath ?? a.name)) -
5081
5211
  columns.findIndex((col) => col.name === (b.column?.options?.dataPath ?? b.name))));
5082
5212
  }
5083
- if (Array.isArray(sorts)) {
5084
- // sorts are AXPSortQuery[]; ensure we map by name
5085
- const sortsMap = new Map(sorts.map((s) => [s.name, s.dir]));
5086
- this.sortedFields.update((prev) => prev.map((sf) => ({ ...sf, dir: sortsMap.get(sf.name) || sf.dir })));
5213
+ if (Array.isArray(sorts) && sorts.length) {
5214
+ this.setActiveSortsFromQueries(sorts);
5087
5215
  }
5088
5216
  // Don't override filters if they came from queryParams
5089
5217
  if (Array.isArray(filters) && !this.hasQueryParamsFilters) {
@@ -5600,18 +5728,80 @@ class AXPEntityMasterListViewModel {
5600
5728
  resetSorts() {
5601
5729
  this.applyViewSorts();
5602
5730
  }
5731
+ /**
5732
+ * Applies active sorts in the given order (click order, saved settings, or view defaults).
5733
+ */
5734
+ setActiveSortsFromQueries(sorts) {
5735
+ const sortableByName = new Map(this.sortableFields().map((f) => [f.name, f]));
5736
+ this.sortedFields.set(sorts
5737
+ .filter((s) => s.dir && sortableByName.has(s.name))
5738
+ .map((s) => ({
5739
+ name: s.name,
5740
+ title: sortableByName.get(s.name).title,
5741
+ dir: s.dir,
5742
+ })));
5743
+ }
5603
5744
  applyViewSorts() {
5604
- const viewSorts = this.view().sorts;
5605
- this.sortedFields.update((prev) => {
5606
- const viewSortsMap = new Map(viewSorts.map((vs) => [vs.name, vs]));
5607
- return prev.map((sf) => {
5608
- const updatedSort = viewSortsMap.get(sf.name);
5609
- if (updatedSort) {
5610
- return { ...updatedSort, title: sf.title };
5611
- }
5612
- return sf;
5613
- });
5614
- });
5745
+ const viewSorts = this.view().sorts ?? [];
5746
+ this.setActiveSortsFromQueries(viewSorts);
5747
+ }
5748
+ /**
5749
+ * Active sort direction for a column (synced with toolbar sort UI and dataSource).
5750
+ */
5751
+ getColumnSortDirection(columnName) {
5752
+ const dir = this.sortedFields().find((sf) => sf.name === columnName)?.dir;
5753
+ return dir === 'asc' || dir === 'desc' ? dir : undefined;
5754
+ }
5755
+ /**
5756
+ * 1-based priority index when multiple columns are sorted (Ctrl+click order).
5757
+ */
5758
+ getColumnSortIndex(columnName) {
5759
+ const index = this.sortedFields().findIndex((sf) => sf.name === columnName);
5760
+ return index >= 0 ? index + 1 : undefined;
5761
+ }
5762
+ /** True when active sorts still match the current view defaults (not yet customized). */
5763
+ isViewDefaultSorts(activeSorts) {
5764
+ const viewSorts = this.view().sorts ?? [];
5765
+ if (activeSorts.length !== viewSorts.length) {
5766
+ return false;
5767
+ }
5768
+ return activeSorts.every((s, i) => s.name === viewSorts[i]?.name && s.dir === viewSorts[i]?.dir);
5769
+ }
5770
+ /**
5771
+ * Toggles column sort (asc → desc → none). Without Ctrl, only one column is active.
5772
+ * With Ctrl/Cmd, multiple columns are sorted in click order (first clicked = primary).
5773
+ */
5774
+ async toggleColumnSort(columnName, multiSort) {
5775
+ const fieldMeta = this.sortableFields().find((f) => f.name === columnName);
5776
+ if (!fieldMeta) {
5777
+ return;
5778
+ }
5779
+ let activeSorts = [...this.sortedFields()];
5780
+ const index = activeSorts.findIndex((sf) => sf.name === columnName);
5781
+ const currentDir = index >= 0 ? activeSorts[index].dir : undefined;
5782
+ const newDir = currentDir === 'asc' ? 'desc' : currentDir === 'desc' ? undefined : 'asc';
5783
+ if (!multiSort) {
5784
+ activeSorts = newDir ? [{ name: columnName, title: fieldMeta.title, dir: newDir }] : [];
5785
+ }
5786
+ else if (index >= 0) {
5787
+ const [item] = activeSorts.splice(index, 1);
5788
+ if (newDir) {
5789
+ // Re-click moves column to end so click order can override view/default order.
5790
+ activeSorts.push({ ...item, title: fieldMeta.title, dir: newDir });
5791
+ }
5792
+ }
5793
+ else if (newDir) {
5794
+ if (this.isViewDefaultSorts(activeSorts)) {
5795
+ activeSorts = [{ name: columnName, title: fieldMeta.title, dir: newDir }];
5796
+ }
5797
+ else {
5798
+ activeSorts.push({ name: columnName, title: fieldMeta.title, dir: newDir });
5799
+ }
5800
+ }
5801
+ this.sortedFields.set(activeSorts);
5802
+ this.lastAppliedSortKey = null;
5803
+ await this.saveSettings('sorts', activeSorts.map((s) => ({ name: s.name, dir: s.dir })));
5804
+ await this.applyFilterAndSort();
5615
5805
  }
5616
5806
  //****************** Commands ******************//
5617
5807
  async executeCommand(commandName, data = null) {
@@ -12547,22 +12737,18 @@ class AXPLookupWidgetViewComponent extends AXPValueWidgetComponent {
12547
12737
  constructor() {
12548
12738
  super(...arguments);
12549
12739
  this.formatService = inject(AXFormatService);
12740
+ this.translation = inject(AXTranslationService);
12550
12741
  this.entityResolver = inject(AXPEntityDefinitionRegistryService);
12551
12742
  this.entity = computed(() => this.options()['entity'], ...(ngDevMode ? [{ debugName: "entity" }] : /* istanbul ignore next */ []));
12552
12743
  this.multiple = computed(() => this.options()['multiple'], ...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
12553
12744
  this.valueField = computed(() => this.options()['valueField'] ?? 'id', ...(ngDevMode ? [{ debugName: "valueField" }] : /* istanbul ignore next */ []));
12554
12745
  this.textField = computed(() => this.options()['textField'] ?? 'title', ...(ngDevMode ? [{ debugName: "textField" }] : /* istanbul ignore next */ []));
12555
12746
  this.badgeClass = computed(() => this.options()['badgeClass'] ?? 'ax-accent1', ...(ngDevMode ? [{ debugName: "badgeClass" }] : /* istanbul ignore next */ []));
12556
- this.displayFormat = computed(() => {
12557
- const template = this.options()['displayFormat'];
12558
- return template ? template.replace(/\{/g, '{{').replace(/\}/g, '}}') : undefined;
12559
- }, ...(ngDevMode ? [{ debugName: "displayFormat" }] : /* istanbul ignore next */ []));
12560
- this.displayField = computed(() => {
12561
- return (this.textField() ??
12562
- this.entityDef()?.formats.lookup ??
12563
- this.entityDef()?.properties.find((c) => c.name != 'id')?.name ??
12564
- 'title');
12565
- }, ...(ngDevMode ? [{ debugName: "displayField" }] : /* istanbul ignore next */ []));
12747
+ this.displayFormat = computed(() => resolveLookupDisplayTemplate(this.entityDef(), {
12748
+ displayFormat: this.options()['displayFormat'],
12749
+ textField: this.textField(),
12750
+ }), ...(ngDevMode ? [{ debugName: "displayFormat" }] : /* istanbul ignore next */ []));
12751
+ this.displayField = computed(() => resolveLookupDisplayField(this.entityDef(), { textField: this.textField() }), ...(ngDevMode ? [{ debugName: "displayField" }] : /* istanbul ignore next */ []));
12566
12752
  this.loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
12567
12753
  this.entityDef = signal(null, ...(ngDevMode ? [{ debugName: "entityDef" }] : /* istanbul ignore next */ []));
12568
12754
  this.displayItems = signal([], ...(ngDevMode ? [{ debugName: "displayItems" }] : /* istanbul ignore next */ []));
@@ -12605,27 +12791,11 @@ class AXPLookupWidgetViewComponent extends AXPValueWidgetComponent {
12605
12791
  };
12606
12792
  }
12607
12793
  getDisplayRaw(item) {
12608
- if (!item) {
12609
- return '';
12610
- }
12611
- const template = this.displayFormat();
12612
- if (template) {
12613
- const formatted = this.formatService.format(template, 'string', item);
12614
- if (formatted) {
12615
- return formatted;
12616
- }
12617
- }
12618
- const raw = get(item, this.displayField());
12619
- if (raw == null) {
12620
- return '';
12621
- }
12622
- if (typeof raw === 'string' || typeof raw === 'number' || typeof raw === 'boolean') {
12623
- return String(raw);
12624
- }
12625
- if (typeof raw === 'object' && !Array.isArray(raw)) {
12626
- return raw;
12627
- }
12628
- return String(raw);
12794
+ const text = formatLookupItemDisplay(item, this.entityDef(), {
12795
+ displayFormat: this.options()['displayFormat'],
12796
+ textField: this.textField(),
12797
+ }, this.formatService, (value) => this.translation.resolve(value));
12798
+ return text;
12629
12799
  }
12630
12800
  get __class() {
12631
12801
  const cls = {};
@@ -13520,27 +13690,10 @@ class AXPLookupWidgetTagboxComponent extends LookupWidgetLookBase {
13520
13690
  //#endregion
13521
13691
  //#region ---- Utility Methods ----
13522
13692
  getDisplayRaw(item) {
13523
- if (!item) {
13524
- return '';
13525
- }
13526
- const template = this.displayFormatValue();
13527
- if (template) {
13528
- const formatted = this.formatService.format(template, 'string', item);
13529
- if (formatted) {
13530
- return formatted;
13531
- }
13532
- }
13533
- const raw = get(item, this.displayFieldValue());
13534
- if (raw == null) {
13535
- return '';
13536
- }
13537
- if (typeof raw === 'string' || typeof raw === 'number' || typeof raw === 'boolean') {
13538
- return String(raw);
13539
- }
13540
- if (typeof raw === 'object' && !Array.isArray(raw)) {
13541
- return raw;
13542
- }
13543
- return String(raw);
13693
+ return formatLookupItemDisplay(item, this.entityDef()(), {
13694
+ displayFormat: this.displayFormatValue(),
13695
+ textField: this.displayFieldValue(),
13696
+ }, this.formatService, (value) => this.translateService.resolve(value));
13544
13697
  }
13545
13698
  /**
13546
13699
  * Get current input value from tag box
@@ -13792,6 +13945,18 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
13792
13945
  this.disabled = computed(() => this.filterMode() ? false : this.options()['disabled'], ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
13793
13946
  this.columns = computed(() => this.options()['columns'] ?? [], ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
13794
13947
  this.textField = computed(() => this.options()['textField'] ?? '', ...(ngDevMode ? [{ debugName: "textField" }] : /* istanbul ignore next */ []));
13948
+ /**
13949
+ * Template for select look (`ax-select-box`). Uses `textTemplate` when set, otherwise
13950
+ * falls back to `displayFormat` (same as tagbox). Single braces are expanded to `{{ }}`
13951
+ * for the select box template engine (see `AXPDataListWidgetComponent.textTemplate`).
13952
+ */
13953
+ this.textTemplate = computed(() => {
13954
+ const explicit = this.options()['textTemplate'];
13955
+ if (explicit) {
13956
+ return explicit.replace(/\{/g, '{{').replace(/\}/g, '}}');
13957
+ }
13958
+ return this.displayFormat() ?? null;
13959
+ }, ...(ngDevMode ? [{ debugName: "textTemplate" }] : /* istanbul ignore next */ []));
13795
13960
  this.hasClearButton = computed(() => (!this.filterMode() && this.options()['hasClearButton']) ?? false, ...(ngDevMode ? [{ debugName: "hasClearButton" }] : /* istanbul ignore next */ []));
13796
13961
  this.showItemTooltip = computed(() => this.options()['showItemTooltip'] ?? false, ...(ngDevMode ? [{ debugName: "showItemTooltip" }] : /* istanbul ignore next */ []));
13797
13962
  this.isItemTruncated = computed(() => this.options()['isItemTruncated'] ?? true, ...(ngDevMode ? [{ debugName: "isItemTruncated" }] : /* istanbul ignore next */ []));
@@ -13800,23 +13965,13 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
13800
13965
  this.filterMode = computed(() => this.options()['filterMode'], ...(ngDevMode ? [{ debugName: "filterMode" }] : /* istanbul ignore next */ []));
13801
13966
  this.multiple = computed(() => (this.options()['multiple'] ?? false), ...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
13802
13967
  this.look = computed(() => this.options()['look'] ?? 'lookup', ...(ngDevMode ? [{ debugName: "look" }] : /* istanbul ignore next */ []));
13803
- this.defaultTextField = computed(() => {
13804
- const list = ['title', 'name', 'code', 'description'];
13805
- const textField = list.find((c) => this.entityDef()?.properties.find((p) => p.name == c)) ?? 'title';
13806
- return textField;
13807
- }, ...(ngDevMode ? [{ debugName: "defaultTextField" }] : /* istanbul ignore next */ []));
13808
- this.displayField = computed(() => {
13809
- if (this.textField()) {
13810
- return this.textField();
13811
- }
13812
- return this.defaultTextField();
13813
- }, ...(ngDevMode ? [{ debugName: "displayField" }] : /* istanbul ignore next */ []));
13968
+ this.displayField = computed(() => resolveLookupDisplayField(this.entityDef(), { textField: this.textField() }), ...(ngDevMode ? [{ debugName: "displayField" }] : /* istanbul ignore next */ []));
13814
13969
  this.allowCreate = computed(() => this.options()['allowCreate'] ?? 'none', ...(ngDevMode ? [{ debugName: "allowCreate" }] : /* istanbul ignore next */ []));
13815
13970
  this.valueField = computed(() => this.entityDef()?.properties.find((c) => c.name == 'id')?.name ?? 'id', ...(ngDevMode ? [{ debugName: "valueField" }] : /* istanbul ignore next */ []));
13816
- this.displayFormat = computed(() => {
13817
- const template = this.options()['displayFormat'];
13818
- return template ? template.replace(/\{/g, '{{').replace(/\}/g, '}}') : undefined;
13819
- }, ...(ngDevMode ? [{ debugName: "displayFormat" }] : /* istanbul ignore next */ []));
13971
+ this.displayFormat = computed(() => resolveLookupDisplayTemplate(this.entityDef(), {
13972
+ displayFormat: this.options()['displayFormat'],
13973
+ textField: this.textField(),
13974
+ }), ...(ngDevMode ? [{ debugName: "displayFormat" }] : /* istanbul ignore next */ []));
13820
13975
  this.entityDef = signal(null, ...(ngDevMode ? [{ debugName: "entityDef" }] : /* istanbul ignore next */ []));
13821
13976
  this.selectedItems = signal([], ...(ngDevMode ? [{ debugName: "selectedItems" }] : /* istanbul ignore next */ []));
13822
13977
  this.initialItems = signal(false, ...(ngDevMode ? [{ debugName: "initialItems" }] : /* istanbul ignore next */ []));
@@ -14212,6 +14367,7 @@ class AXPLookupWidgetColumnComponent extends AXPColumnWidgetComponent {
14212
14367
  this.translation = inject(AXTranslationService);
14213
14368
  this.formatService = inject(AXFormatService);
14214
14369
  this.entityService = inject(AXPEntityService);
14370
+ this.entityResolver = inject(AXPEntityDefinitionRegistryService);
14215
14371
  this.queryExecutor = inject(AXPQueryExecutor);
14216
14372
  //#endregion
14217
14373
  //#region ---- View Children ----
@@ -14222,15 +14378,16 @@ class AXPLookupWidgetColumnComponent extends AXPColumnWidgetComponent {
14222
14378
  //#region ---- Properties ----
14223
14379
  this.host = inject(ElementRef);
14224
14380
  this.valueField = this.options['valueField'] ?? 'id';
14225
- this.textField = this.options['textField'] ?? 'title';
14226
14381
  this.entity = this.options['entity'] ?? '';
14227
14382
  this.columnName = this.options['columnName'] ?? 'title';
14228
14383
  this.maxVisible = this.options['maxVisible'] ?? 2;
14229
- this.displayFormat = computed(() => {
14230
- const template = this.options['displayFormat'];
14231
- return template ? template.replace(/\{/g, '{{').replace(/\}/g, '}}') : undefined;
14232
- }, ...(ngDevMode ? [{ debugName: "displayFormat" }] : /* istanbul ignore next */ []));
14233
- this.displayField = computed(() => this.textField ?? 'title', ...(ngDevMode ? [{ debugName: "displayField" }] : /* istanbul ignore next */ []));
14384
+ this.lookupDisplayOptions = computed(() => ({
14385
+ displayFormat: this.options['displayFormat'],
14386
+ textField: this.options['textField'],
14387
+ isMultiLanguage: (value) => this.translation.isValidMultiLanguageObject(value),
14388
+ }), ...(ngDevMode ? [{ debugName: "lookupDisplayOptions" }] : /* istanbul ignore next */ []));
14389
+ this.displayFormat = computed(() => resolveLookupDisplayTemplate(this.entityDef(), this.lookupDisplayOptions()), ...(ngDevMode ? [{ debugName: "displayFormat" }] : /* istanbul ignore next */ []));
14390
+ this.displayField = computed(() => resolveLookupDisplayField(this.entityDef(), this.lookupDisplayOptions()), ...(ngDevMode ? [{ debugName: "displayField" }] : /* istanbul ignore next */ []));
14234
14391
  this.columnResolve = computed(() => this.options['columnResolve'], ...(ngDevMode ? [{ debugName: "columnResolve" }] : /* istanbul ignore next */ []));
14235
14392
  this.resolveStrategy = computed(() => {
14236
14393
  return this.columnResolve()?.strategy ?? 'hydrated';
@@ -14245,6 +14402,9 @@ class AXPLookupWidgetColumnComponent extends AXPColumnWidgetComponent {
14245
14402
  this.resolveError = signal(null, ...(ngDevMode ? [{ debugName: "resolveError" }] : /* istanbul ignore next */ []));
14246
14403
  this.summaryLabel = signal('', ...(ngDevMode ? [{ debugName: "summaryLabel" }] : /* istanbul ignore next */ []));
14247
14404
  this.popoverHeader = signal('', ...(ngDevMode ? [{ debugName: "popoverHeader" }] : /* istanbul ignore next */ []));
14405
+ this.entityDef = signal(null, ...(ngDevMode ? [{ debugName: "entityDef" }] : /* istanbul ignore next */ []));
14406
+ this.hydratedDisplayItems = signal(null, ...(ngDevMode ? [{ debugName: "hydratedDisplayItems" }] : /* istanbul ignore next */ []));
14407
+ this.hydrateRequestId = 0;
14248
14408
  //#endregion
14249
14409
  //#region ---- Computed ----
14250
14410
  this.displayItems = computed(() => isNil(this.rawValue)
@@ -14252,7 +14412,13 @@ class AXPLookupWidgetColumnComponent extends AXPColumnWidgetComponent {
14252
14412
  : castArray(this.rawValue)
14253
14413
  .map((item) => this.extractItem(item))
14254
14414
  .filter((c) => c != null), ...(ngDevMode ? [{ debugName: "displayItems" }] : /* istanbul ignore next */ []));
14255
- this.allItems = computed(() => this.displayItems(), ...(ngDevMode ? [{ debugName: "allItems" }] : /* istanbul ignore next */ []));
14415
+ this.allItems = computed(() => {
14416
+ const hydrated = this.hydratedDisplayItems();
14417
+ if (hydrated != null && hydrated.length > 0) {
14418
+ return hydrated;
14419
+ }
14420
+ return this.displayItems();
14421
+ }, ...(ngDevMode ? [{ debugName: "allItems" }] : /* istanbul ignore next */ []));
14256
14422
  this.visibleItems = computed(() => {
14257
14423
  const items = this.allItems();
14258
14424
  return items.slice(0, this.maxVisible);
@@ -14309,6 +14475,8 @@ class AXPLookupWidgetColumnComponent extends AXPColumnWidgetComponent {
14309
14475
  }
14310
14476
  this.previousLazyRowId = key;
14311
14477
  this.resetLazyState();
14478
+ this.hydratedDisplayItems.set(null);
14479
+ this.hydrateRequestId++;
14312
14480
  });
14313
14481
  effect(() => {
14314
14482
  if (!this.isHydratedStrategy()) {
@@ -14325,6 +14493,40 @@ class AXPLookupWidgetColumnComponent extends AXPColumnWidgetComponent {
14325
14493
  : this.displayCount();
14326
14494
  void this.refreshPopoverHeader(count);
14327
14495
  });
14496
+ effect(() => {
14497
+ const key = this.entity;
14498
+ if (!key?.includes('.')) {
14499
+ this.entityDef.set(null);
14500
+ return;
14501
+ }
14502
+ const [module, name] = key.split('.');
14503
+ void this.entityResolver.resolve(module, name).then((def) => this.entityDef.set(def));
14504
+ });
14505
+ effect(() => {
14506
+ if (!this.isHydratedStrategy()) {
14507
+ this.hydratedDisplayItems.set(null);
14508
+ return;
14509
+ }
14510
+ const entityKey = this.entity;
14511
+ const raw = this.rawValue;
14512
+ if (!entityKey?.includes('.') || isNil(raw)) {
14513
+ this.hydratedDisplayItems.set(null);
14514
+ return;
14515
+ }
14516
+ // Display text is already on the row (dataPath, denormalized title, nested object, etc.) — no byKey fetch.
14517
+ if (this.hasPreresolvedDisplay()) {
14518
+ this.hydratedDisplayItems.set(null);
14519
+ return;
14520
+ }
14521
+ const values = castArray(raw).filter((value) => value != null && value !== '');
14522
+ const needsHydrate = values.length > 0 &&
14523
+ values.every((value) => typeof value !== 'object' && this.isLikelyEntityId(value));
14524
+ if (!needsHydrate) {
14525
+ this.hydratedDisplayItems.set(null);
14526
+ return;
14527
+ }
14528
+ void this.hydrateScalarIds(entityKey, values);
14529
+ });
14328
14530
  }
14329
14531
  //#endregion
14330
14532
  //#region ---- Public methods ----
@@ -14354,7 +14556,7 @@ class AXPLookupWidgetColumnComponent extends AXPColumnWidgetComponent {
14354
14556
  this.closeMorePopover();
14355
14557
  // Prefer the row's FK / stored value (columnName) first. List columns often use options.dataPath so
14356
14558
  // rawValue (and thus `item`) is only display text (e.g. manager.person.fullName) while managerId holds the real id.
14357
- const columnData = this.rowData?.[this.columnName];
14559
+ const columnData = get(this.rowData, this.columnName);
14358
14560
  let id;
14359
14561
  if (Array.isArray(columnData)) {
14360
14562
  const cell = columnData[index];
@@ -14375,11 +14577,33 @@ class AXPLookupWidgetColumnComponent extends AXPColumnWidgetComponent {
14375
14577
  if (isNil(id) || id === '') {
14376
14578
  return;
14377
14579
  }
14580
+ const headerItem = this.allItems()[index] ?? item;
14581
+ let resolvedTitle = '';
14582
+ if (headerItem != null && this.hasDisplayTextOnLookupValue(headerItem)) {
14583
+ resolvedTitle =
14584
+ typeof headerItem === 'object'
14585
+ ? this.renderLookupLabel(headerItem)
14586
+ : this.formatDisplayValue(headerItem);
14587
+ }
14588
+ else if (!this.isMultiValueLookup()) {
14589
+ const rowText = this.getRowDisplayTextFromTextField();
14590
+ if (!isNil(rowText) && rowText !== '') {
14591
+ resolvedTitle = this.formatDisplayValue(rowText);
14592
+ }
14593
+ }
14594
+ if (!resolvedTitle) {
14595
+ const displayTitle = formatLookupItemDisplay(headerItem, this.entityDef(), this.lookupDisplayOptions(), this.formatService, (value) => this.translation.resolve(value));
14596
+ resolvedTitle = typeof displayTitle === 'string' ? displayTitle : this.translation.resolve(displayTitle);
14597
+ if (isUnresolvedLookupDisplayTemplate(resolvedTitle)) {
14598
+ resolvedTitle = '';
14599
+ }
14600
+ }
14378
14601
  await this.entityDetailPopoverService.show(this.host, {
14379
14602
  entity: this.entity,
14380
14603
  id,
14381
- textField: this.textField,
14604
+ textField: this.options['textField'] ?? this.displayField(),
14382
14605
  valueField: this.valueField,
14606
+ displayTitle: resolvedTitle,
14383
14607
  item,
14384
14608
  });
14385
14609
  }
@@ -14409,21 +14633,45 @@ class AXPLookupWidgetColumnComponent extends AXPColumnWidgetComponent {
14409
14633
  this.handleResolvedItemClick(index);
14410
14634
  }
14411
14635
  }
14636
+ /**
14637
+ * True when the cell can render without fetching row data via byKey.
14638
+ * Uses textField / expose targets on the row — not merely whether column dataPath differs from columnName.
14639
+ */
14640
+ hasPreresolvedDisplay() {
14641
+ if (this.isMultiValueLookup()) {
14642
+ const values = castArray(this.rawValue).filter((value) => value != null && value !== '');
14643
+ return values.length > 0 && values.every((value) => this.hasDisplayTextOnLookupValue(value));
14644
+ }
14645
+ const rowText = this.getRowDisplayTextFromTextField();
14646
+ if (!isNil(rowText) &&
14647
+ rowText !== '' &&
14648
+ (typeof rowText !== 'string' || !this.isLikelyEntityId(rowText))) {
14649
+ return true;
14650
+ }
14651
+ const raw = this.rawValue;
14652
+ if (isNil(raw)) {
14653
+ return false;
14654
+ }
14655
+ const values = castArray(raw).filter((value) => value != null && value !== '');
14656
+ return values.length > 0 && values.every((value) => this.hasDisplayTextOnLookupValue(value));
14657
+ }
14412
14658
  getDisplayRaw(item) {
14413
- if (isNil(item)) {
14414
- return '';
14659
+ if (item != null && typeof item === 'object' && this.translation.isValidMultiLanguageObject(item)) {
14660
+ return this.translation.resolve(item);
14415
14661
  }
14416
- const resolved = this.resolveDisplayValue(item);
14417
- const template = this.displayFormat();
14418
- if (template) {
14419
- const displayField = this.displayField();
14420
- const templateData = typeof item === 'object' && !Array.isArray(item)
14421
- ? { ...item, [displayField]: resolved }
14422
- : { [displayField]: resolved, value: resolved };
14423
- const formatted = this.formatService.format(template, 'string', templateData);
14424
- return formatted ?? resolved ?? '';
14662
+ if (!this.isMultiValueLookup()) {
14663
+ const rowText = this.getRowDisplayTextFromTextField();
14664
+ if (!isNil(rowText) && rowText !== '') {
14665
+ return this.formatDisplayValue(rowText);
14666
+ }
14667
+ }
14668
+ if (item != null && this.hasDisplayTextOnLookupValue(item)) {
14669
+ if (typeof item === 'object') {
14670
+ return this.renderLookupLabel(item);
14671
+ }
14672
+ return this.formatDisplayValue(item);
14425
14673
  }
14426
- return resolved ?? '';
14674
+ return this.renderLookupLabel(item);
14427
14675
  }
14428
14676
  //#endregion
14429
14677
  //#region ---- Private methods ----
@@ -14594,28 +14842,154 @@ class AXPLookupWidgetColumnComponent extends AXPColumnWidgetComponent {
14594
14842
  }
14595
14843
  return {
14596
14844
  [this.valueField]: item,
14597
- [this.textField]: item,
14845
+ [this.displayField()]: item,
14598
14846
  };
14599
14847
  }
14600
- resolveDisplayValue(item) {
14601
- if (isNil(item)) {
14602
- return '';
14848
+ async hydrateScalarIds(entityKey, ids) {
14849
+ const requestId = ++this.hydrateRequestId;
14850
+ try {
14851
+ const accessor = this.entityService.withEntity(entityKey).data();
14852
+ const items = await Promise.all(ids.map((id) => accessor.byKey(id)));
14853
+ if (requestId !== this.hydrateRequestId) {
14854
+ return;
14855
+ }
14856
+ const resolved = items.filter((item) => item != null);
14857
+ this.hydratedDisplayItems.set(resolved.length > 0 ? resolved : null);
14603
14858
  }
14604
- if (typeof item === 'string' || typeof item === 'number' || typeof item === 'boolean') {
14605
- return String(item);
14859
+ catch {
14860
+ if (requestId === this.hydrateRequestId) {
14861
+ this.hydratedDisplayItems.set(null);
14862
+ }
14606
14863
  }
14607
- if (typeof item !== 'object') {
14608
- return '';
14864
+ }
14865
+ isMultiValueLookup() {
14866
+ return Array.isArray(this.rawValue) && this.rawValue.length > 0;
14867
+ }
14868
+ /** Row display text resolved via widget textField and expose targets (not column dataPath alone). */
14869
+ getRowDisplayTextFromTextField() {
14870
+ const textField = resolveLookupDisplayField(this.entityDef(), this.lookupDisplayOptions());
14871
+ const exposeTargets = this.getExposeTargets();
14872
+ for (const entry of exposeTargets) {
14873
+ if (entry.source === this.valueField || entry.source === 'id') {
14874
+ continue;
14875
+ }
14876
+ if (entry.source === textField || entry.target.endsWith(`.${textField}`)) {
14877
+ const value = get(this.rowData, entry.target);
14878
+ if (!isNil(value) && value !== '') {
14879
+ return value;
14880
+ }
14881
+ }
14609
14882
  }
14610
- const rawDisplayValue = get(item, this.displayField());
14611
- if (!isNil(rawDisplayValue)) {
14612
- return this.resolveDisplayValue(rawDisplayValue);
14883
+ for (const entry of exposeTargets) {
14884
+ if (entry.source === 'title') {
14885
+ const value = get(this.rowData, entry.target);
14886
+ if (!isNil(value) && value !== '') {
14887
+ return value;
14888
+ }
14889
+ }
14613
14890
  }
14614
- if (this.translation.isValidMultiLanguageObject(item)) {
14615
- return this.translation.resolve(item);
14891
+ if (this.path && this.path !== this.columnName) {
14892
+ const atPath = get(this.rowData, this.path);
14893
+ if (typeof atPath === 'string' && atPath !== '' && !this.isLikelyEntityId(atPath)) {
14894
+ return atPath;
14895
+ }
14896
+ if (atPath != null && typeof atPath === 'object') {
14897
+ const nested = get(atPath, textField);
14898
+ if (!isNil(nested) && nested !== '') {
14899
+ return nested;
14900
+ }
14901
+ }
14902
+ const combined = get(this.rowData, `${this.path}.${textField}`);
14903
+ if (!isNil(combined) && combined !== '') {
14904
+ return combined;
14905
+ }
14906
+ }
14907
+ if (this.columnName.endsWith('Id')) {
14908
+ const parentKey = this.columnName.slice(0, -2);
14909
+ const nestedObject = get(this.rowData, parentKey);
14910
+ if (nestedObject != null && typeof nestedObject === 'object') {
14911
+ const nested = get(nestedObject, textField);
14912
+ if (!isNil(nested) && nested !== '') {
14913
+ return nested;
14914
+ }
14915
+ }
14916
+ const direct = get(this.rowData, `${parentKey}.${textField}`);
14917
+ if (!isNil(direct) && direct !== '') {
14918
+ return direct;
14919
+ }
14920
+ }
14921
+ if (this.columnName.includes('.')) {
14922
+ const parts = this.columnName.split('.');
14923
+ const leaf = parts[parts.length - 1];
14924
+ if (leaf === 'id' || leaf.endsWith('Id')) {
14925
+ const parent = parts.slice(0, -1).join('.');
14926
+ const direct = get(this.rowData, `${parent}.${textField}`);
14927
+ if (!isNil(direct) && direct !== '') {
14928
+ return direct;
14929
+ }
14930
+ }
14931
+ }
14932
+ return undefined;
14933
+ }
14934
+ getExposeTargets() {
14935
+ const expose = this.options['expose'];
14936
+ if (!Array.isArray(expose)) {
14937
+ return [];
14938
+ }
14939
+ return expose.filter((entry) => entry != null &&
14940
+ typeof entry === 'object' &&
14941
+ typeof entry.source === 'string' &&
14942
+ typeof entry.target === 'string');
14943
+ }
14944
+ hasDisplayTextOnLookupValue(value) {
14945
+ if (isNil(value) || value === '') {
14946
+ return false;
14947
+ }
14948
+ if (typeof value === 'string') {
14949
+ return !this.isLikelyEntityId(value);
14950
+ }
14951
+ if (typeof value === 'number' || typeof value === 'boolean') {
14952
+ return true;
14953
+ }
14954
+ if (typeof value !== 'object') {
14955
+ return false;
14956
+ }
14957
+ if (this.translation.isValidMultiLanguageObject(value)) {
14958
+ return true;
14959
+ }
14960
+ const textField = resolveLookupDisplayField(this.entityDef(), this.lookupDisplayOptions());
14961
+ const text = get(value, textField);
14962
+ if (!isNil(text) && text !== '' && !this.isLikelyEntityId(text)) {
14963
+ return true;
14964
+ }
14965
+ return ['title', 'name', 'code', 'description'].some((field) => {
14966
+ const candidate = get(value, field);
14967
+ return !isNil(candidate) && candidate !== '' && !this.isLikelyEntityId(candidate);
14968
+ });
14969
+ }
14970
+ renderLookupLabel(value) {
14971
+ const label = formatLookupItemDisplay(value, this.entityDef(), this.lookupDisplayOptions(), this.formatService, (item) => this.translation.resolve(item));
14972
+ return typeof label === 'string' ? label : this.translation.resolve(label);
14973
+ }
14974
+ formatDisplayValue(value) {
14975
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
14976
+ return String(value);
14977
+ }
14978
+ if (value != null && typeof value === 'object' && this.translation.isValidMultiLanguageObject(value)) {
14979
+ return this.translation.resolve(value);
14616
14980
  }
14617
14981
  return '';
14618
14982
  }
14983
+ /** True when a scalar cell value is a stored entity key (uuid/numeric), not pre-resolved display text. */
14984
+ isLikelyEntityId(value) {
14985
+ if (typeof value === 'number') {
14986
+ return true;
14987
+ }
14988
+ if (typeof value !== 'string') {
14989
+ return false;
14990
+ }
14991
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value) || /^\d+$/.test(value);
14992
+ }
14619
14993
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLookupWidgetColumnComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
14620
14994
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPLookupWidgetColumnComponent, isStandalone: true, selector: "ng-component", inputs: { rawValue: "rawValue", rowData: "rowData" }, viewQueries: [{ propertyName: "moreButton", first: true, predicate: ["moreButton"], descendants: true, isSignal: true }, { propertyName: "lazyTrigger", first: true, predicate: ["lazyTrigger"], descendants: true, isSignal: true }, { propertyName: "morePopover", first: true, predicate: ["morePopover"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "@if (isHydratedStrategy()) {\n<div class=\"ax-relative ax-flex ax-min-w-0 ax-items-center ax-gap-1\">\n @for (item of visibleItems(); track $index) {\n @let label = getDisplayRaw(item);\n <span\n [class.ax-cursor-pointer]=\"!options['disableDetailPopover']\"\n [class.hover:ax-text-primary]=\"!options['disableDetailPopover']\"\n [class.hover:ax-underline]=\"!options['disableDetailPopover']\"\n (click)=\"handleItemClick($index)\"\n >\n {{ label }}\n </span>\n @if ($index < visibleItems().length - 1) {\n <span class=\"ax-text-muted\">\u2022</span>\n }\n } @empty {\n <span class=\"ax-text-muted\">---</span>\n }\n @if (hasMoreItems()) {\n <span\n class=\"ax-absolute ax-end-0 ax-flex ax-h-full ax-cursor-pointer ax-items-center ax-px-1 hover:ax-primary-lighter\"\n (click)=\"showMoreItems(); $event.stopPropagation()\"\n #moreButton\n >\n <i class=\"fa-light fa-ellipsis-vertical\"></i>\n </span>\n }\n</div>\n} @else {\n<div class=\"ax-flex ax-min-w-0 ax-items-center ax-gap-1\">\n @if (displayCount() > 0) {\n <span\n class=\"ax-cursor-pointer ax-text-primary hover:ax-underline\"\n (click)=\"openLazyPopover(); $event.stopPropagation()\"\n #lazyTrigger\n >\n {{ summaryLabel() }}\n </span>\n } @else {\n <span class=\"ax-text-muted\">---</span>\n }\n</div>\n}\n\n<ax-popover [openOn]=\"'manual'\" #morePopover (openChange)=\"onPopoverOpenChange($event)\">\n <div class=\"ax-lightest-surface ax-min-w-[280px] ax-rounded-lg ax-border ax-p-4 ax-shadow-lg\">\n <div class=\"ax-mb-4 ax-border-b ax-pb-2\">\n <h3 class=\"ax-text-base ax-font-semibold\">{{ popoverHeader() }}</h3>\n </div>\n\n @if (!isHydratedStrategy() && resolveStatus() === 'loading') {\n <div class=\"ax-flex ax-min-h-[120px] ax-items-center ax-justify-center\">\n <ax-loading></ax-loading>\n </div>\n } @else if (!isHydratedStrategy() && resolveStatus() === 'error') {\n <div class=\"ax-text-danger ax-text-sm\">{{ resolveError() }}</div>\n } @else {\n <div class=\"ax-flex ax-max-h-64 ax-flex-col ax-gap-3\">\n @for (item of popoverListItems(); track $index) {\n @let label = getDisplayRaw(item);\n <span\n [class.ax-cursor-pointer]=\"!options['disableDetailPopover']\"\n [class.hover:ax-text-primary]=\"!options['disableDetailPopover']\"\n [class.hover:ax-underline]=\"!options['disableDetailPopover']\"\n (click)=\"handlePopoverItemClick($index)\"\n >\n {{ label }}\n </span>\n } @empty {\n <span class=\"ax-text-muted\">---</span>\n }\n </div>\n }\n </div>\n</ax-popover>\n", dependencies: [{ kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i1$2.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXPopoverModule }, { kind: "component", type: i2.AXPopoverComponent, selector: "ax-popover", inputs: ["width", "disablePanelClass", "disabled", "offsetX", "offsetY", "target", "placement", "content", "openOn", "closeOn", "hasBackdrop", "openAfter", "closeAfter", "closeOnScroll", "backdropClass", "panelClass", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
14621
14995
  }
@@ -21426,5 +21800,5 @@ var getEntityDetails_query = /*#__PURE__*/Object.freeze({
21426
21800
  * Generated bundle index. Do not edit.
21427
21801
  */
21428
21802
 
21429
- export { AXMEntityCrudService, AXMEntityCrudServiceImpl, AXPCategoryTreeService, AXPCreateEntityCommand, AXPCreateEntityWorkflow, AXPDataSeederService, AXPDeleteEntityWorkflow, AXPEntitiesListDataSourceDefinition, AXPEntityApplyUpdatesAction, AXPEntityCategoryTreeSelectorComponent, AXPEntityCategoryWidget, AXPEntityCategoryWidgetColumnComponent, AXPEntityCategoryWidgetEditComponent, AXPEntityCategoryWidgetViewComponent, AXPEntityCommandTriggerViewModel, AXPEntityCreateEvent, AXPEntityCreatePopupAction, AXPEntityCreateSubmittedAction, AXPEntityCreateViewElementViewModel, AXPEntityCreateViewModelFactory, AXPEntityCreateViewSectionViewModel, AXPEntityDataProvider, AXPEntityDataProviderImpl, AXPEntityDataSelectorService, AXPEntityDefinitionProviderWidget, AXPEntityDefinitionProviderWidgetEditComponent, AXPEntityDefinitionRegistryService, AXPEntityDeletedEvent, AXPEntityDetailListViewModel, AXPEntityDetailPopoverComponent, AXPEntityDetailPopoverService, AXPEntityDetailViewModelFactory, AXPEntityDetailViewModelResolver, AXPEntityEventDispatcherService, AXPEntityEventsKeys, AXPEntityFormBuilderService, AXPEntityListPersistenceModeDefault, AXPEntityListTableService, AXPEntityListToolbarService, AXPEntityListViewColumnViewModel, AXPEntityListViewModelFactory, AXPEntityListViewModelResolver, AXPEntityListWidget, AXPEntityListWidgetViewComponent, AXPEntityMasterCreateViewModel, AXPEntityMasterListViewModel, AXPEntityMasterListViewQueryViewModel, AXPEntityMasterSingleElementViewModel, AXPEntityMasterSingleViewGroupViewModel, AXPEntityMasterSingleViewModel, AXPEntityMasterUpdateElementViewModel, AXPEntityMasterUpdateViewModel, AXPEntityMasterUpdateViewModelFactory, AXPEntityMiddleware, AXPEntityModifyConfirmedAction, AXPEntityModifyEvent, AXPEntityModifySectionPopupAction, AXPEntityModule, AXPEntityPerformDeleteAction, AXPEntityPreloadFiltersContainerComponent, AXPEntityPreloadFiltersViewModel, AXPEntityPreloadFiltersViewModelResolver, AXPEntityResolver, AXPEntityService, AXPEntityStorageService, AXPEntityUpdateViewSectionViewModel, 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, axpCreateEntityAiToolInputDefaults, axpCreateEntityCommandDefinition, buildAXPRecordWorkflowInfo, canPersistEntityListState, cloneLayoutArrays, collectEntityQuickSearchFieldPaths, collectNestedCreateHiddenProperties, collectNestedFieldPathsFromEntityColumns, collectQuickSearchPathsFromSingleEntityDefinition, columnOrderingMiddleware, columnOrderingMiddlewareProvider, columnWidthMiddleware, columnWidthMiddlewareProvider, computeEntityAggregates, createColumnOrderingMiddlewareProvider, createLayoutOrderingMiddlewareProvider, createModifierContext, 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, getDataSourcePageIndex, getEntityListRowId, getMasterInterfacePropertySortKey, getRecordWorkflowCorrelationId, getRecordWorkflowInstanceId, isAXPMiddlewareAbortError, isCategoryEntity, isCategoryFilter, layoutOrderingMiddlewareFactory, layoutOrderingMiddlewareProvider, mergeForeignKeyFieldIntoCreateActions, normalizeEntityListPersistenceMode, normalizeListPaging, provideEntity, resolveEntityPluginDetailPageOrder, restoreEntityListExpandedRows, runEntityQuery, searchResultDescriptionMiddleware, searchResultDescriptionMiddlewareProvider, shouldLoadEntityListStateFromStorage, shouldResetEntityListStateOnRouteEntry };
21803
+ export { AXMEntityCrudService, AXMEntityCrudServiceImpl, AXPCategoryTreeService, AXPCreateEntityCommand, AXPCreateEntityWorkflow, AXPDataSeederService, AXPDeleteEntityWorkflow, AXPEntitiesListDataSourceDefinition, AXPEntityApplyUpdatesAction, AXPEntityCategoryTreeSelectorComponent, AXPEntityCategoryWidget, AXPEntityCategoryWidgetColumnComponent, AXPEntityCategoryWidgetEditComponent, AXPEntityCategoryWidgetViewComponent, AXPEntityCommandTriggerViewModel, AXPEntityCreateEvent, AXPEntityCreatePopupAction, AXPEntityCreateSubmittedAction, AXPEntityCreateViewElementViewModel, AXPEntityCreateViewModelFactory, AXPEntityCreateViewSectionViewModel, AXPEntityDataProvider, AXPEntityDataProviderImpl, AXPEntityDataSelectorService, AXPEntityDefinitionProviderWidget, AXPEntityDefinitionProviderWidgetEditComponent, AXPEntityDefinitionRegistryService, AXPEntityDeletedEvent, AXPEntityDetailListViewModel, AXPEntityDetailPopoverComponent, AXPEntityDetailPopoverService, AXPEntityDetailViewModelFactory, AXPEntityDetailViewModelResolver, AXPEntityEventDispatcherService, AXPEntityEventsKeys, AXPEntityFormBuilderService, AXPEntityListPersistenceModeDefault, AXPEntityListTableService, AXPEntityListToolbarService, AXPEntityListViewColumnViewModel, AXPEntityListViewModelFactory, AXPEntityListViewModelResolver, AXPEntityListWidget, AXPEntityListWidgetViewComponent, AXPEntityMasterCreateViewModel, AXPEntityMasterListViewModel, AXPEntityMasterListViewQueryViewModel, AXPEntityMasterSingleElementViewModel, AXPEntityMasterSingleViewGroupViewModel, AXPEntityMasterSingleViewModel, AXPEntityMasterUpdateElementViewModel, AXPEntityMasterUpdateViewModel, AXPEntityMasterUpdateViewModelFactory, AXPEntityMiddleware, AXPEntityModifyConfirmedAction, AXPEntityModifyEvent, AXPEntityModifySectionPopupAction, AXPEntityModule, AXPEntityPerformDeleteAction, AXPEntityPreloadFiltersContainerComponent, AXPEntityPreloadFiltersViewModel, AXPEntityPreloadFiltersViewModelResolver, AXPEntityResolver, AXPEntityService, AXPEntityStorageService, AXPEntityUpdateViewSectionViewModel, 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, axpCreateEntityAiToolInputDefaults, axpCreateEntityCommandDefinition, buildAXPRecordWorkflowInfo, canPersistEntityListState, cloneLayoutArrays, collectEntityQuickSearchFieldPaths, collectNestedCreateHiddenProperties, collectNestedFieldPathsFromEntityColumns, collectQuickSearchPathsFromSingleEntityDefinition, columnOrderingMiddleware, columnOrderingMiddlewareProvider, columnWidthMiddleware, columnWidthMiddlewareProvider, computeEntityAggregates, createColumnOrderingMiddlewareProvider, createLayoutOrderingMiddlewareProvider, createModifierContext, 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, formatLookupItemDisplay, getDataSourcePageIndex, getEntityListRowId, getMasterInterfacePropertySortKey, getRecordWorkflowCorrelationId, getRecordWorkflowInstanceId, isAXPMiddlewareAbortError, isCategoryEntity, isCategoryFilter, isUnresolvedLookupDisplayTemplate, layoutOrderingMiddlewareFactory, layoutOrderingMiddlewareProvider, mergeForeignKeyFieldIntoCreateActions, normalizeEntityListPersistenceMode, normalizeListPaging, normalizeLookupDisplayTemplate, provideEntity, resolveEntityPluginDetailPageOrder, resolveLookupDisplayField, resolveLookupDisplayTemplate, restoreEntityListExpandedRows, runEntityQuery, searchResultDescriptionMiddleware, searchResultDescriptionMiddlewareProvider, shouldLoadEntityListStateFromStorage, shouldResetEntityListStateOnRouteEntry };
21430
21804
  //# sourceMappingURL=acorex-platform-layout-entity.mjs.map