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

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 (32) hide show
  1. package/fesm2022/acorex-platform-common.mjs +11 -0
  2. package/fesm2022/acorex-platform-common.mjs.map +1 -1
  3. package/fesm2022/acorex-platform-core.mjs +332 -76
  4. package/fesm2022/acorex-platform-core.mjs.map +1 -1
  5. package/fesm2022/acorex-platform-layout-builder.mjs +563 -44
  6. package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
  7. package/fesm2022/acorex-platform-layout-components.mjs +83 -67
  8. package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
  9. package/fesm2022/acorex-platform-layout-entity.mjs +543 -237
  10. package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
  11. package/fesm2022/acorex-platform-layout-views.mjs +319 -66
  12. package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
  13. package/fesm2022/acorex-platform-layout-widget-core.mjs +54 -6
  14. package/fesm2022/acorex-platform-layout-widget-core.mjs.map +1 -1
  15. package/fesm2022/acorex-platform-layout-widgets.mjs +198 -91
  16. package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
  17. package/fesm2022/acorex-platform-themes-default.mjs +13 -14
  18. package/fesm2022/acorex-platform-themes-default.mjs.map +1 -1
  19. package/fesm2022/acorex-platform-themes-shared.mjs +50 -30
  20. package/fesm2022/acorex-platform-themes-shared.mjs.map +1 -1
  21. package/package.json +1 -1
  22. package/types/acorex-platform-common.d.ts +11 -0
  23. package/types/acorex-platform-core.d.ts +137 -47
  24. package/types/acorex-platform-layout-builder.d.ts +90 -13
  25. package/types/acorex-platform-layout-components.d.ts +21 -17
  26. package/types/acorex-platform-layout-entity.d.ts +63 -10
  27. package/types/acorex-platform-layout-views.d.ts +68 -6
  28. package/types/acorex-platform-layout-widget-core.d.ts +43 -8
  29. package/types/acorex-platform-layout-widgets.d.ts +21 -6
  30. package/types/acorex-platform-themes-default.d.ts +24 -4
  31. package/types/acorex-platform-themes-shared.d.ts +6 -0
  32. package/types/acorex-platform-workflow.d.ts +1 -1
@@ -9,7 +9,7 @@ import { Subject, takeUntil } from 'rxjs';
9
9
  import { AXPLayoutBuilderService, LayoutBuilderModule } from '@acorex/platform/layout/builder';
10
10
  import * as i3 from '@acorex/platform/layout/widget-core';
11
11
  import { AXPWidgetsCatalog, AXPWidgetCoreModule, AXPPageStatus, AXPWidgetRegistryService, AXPColumnWidgetComponent, AXPValueWidgetComponent, AXPWidgetGroupEnum, createBooleanProperty, AXPWidgetRendererDirective, AXPBaseWidgetComponent, AXPWidgetStatus, createStringProperty, createNumberProperty, AXP_WIDGETS_ADVANCE_SUB_MEDIA, AXP_WIDGETS_ADVANCE_CATEGORY, createSelectProperty, AXP_WIDGETS_EDITOR_CATEGORY, AXP_WIDGET_DEFINITION_PROVIDER } from '@acorex/platform/layout/widget-core';
12
- import { AXPSystemActionType, AXPDeviceService, AXPExpressionEvaluatorService, AXPBroadcastEventService, applyFilterArray, applySortArray, resolveActionLook, AXPDistributedEventListenerService, AXPPlatformScope, AXHighlightService, extractValue, setSmart, getChangedPaths, objectKeyValueTransforms, AXPHookService, AXPDataGenerator, AXPComponentSlotModule, AXPColumnWidthService, AXPModuleManifestRegistry, defaultColumnWidthProvider, AXP_COLUMN_WIDTH_PROVIDER, AXP_DATASOURCE_DEFINITION_PROVIDER, AXPModuleManifestsDataSourceDefinition } from '@acorex/platform/core';
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
15
  import { AXPSessionService, AXPAuthGuard, AXPPermissionDefinitionsDataSourceDefinition } from '@acorex/platform/auth';
@@ -23,7 +23,7 @@ import * as i2 from '@acorex/components/popover';
23
23
  import { AXPopoverModule } from '@acorex/components/popover';
24
24
  import { AXFormatService } from '@acorex/core/format';
25
25
  import * as i5 from '@angular/common';
26
- import { CommonModule, AsyncPipe } from '@angular/common';
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
28
  import { AXPPageLayoutBaseComponent, AXPPageLayoutComponent, AXPPageComponentRendererDirective, AXPPageComponentInstanceRegistryService } from '@acorex/platform/layout/views';
29
29
  import { AXDataSource } from '@acorex/cdk/common';
@@ -1686,13 +1686,14 @@ class PropertyFilter {
1686
1686
  d.setTitle(title);
1687
1687
  d.setSize(this.externalSize ?? (useFormWizard ? 'lg' : calculatedSize));
1688
1688
  d.setCloseButton(true);
1689
+ d.confirmCloseWhenDirty();
1689
1690
  d.content((layout) => {
1690
1691
  const defaultMode = this.kind === 'single' ? 'view' : 'edit';
1691
1692
  layout.mode(this.externalMode ?? defaultMode);
1692
1693
  if (useFormWizard) {
1693
1694
  layout.stepWizard((w) => {
1694
1695
  w.name(ENTITY_FORM_STEP_WIZARD_NAME);
1695
- w.setLook('arrow-minimal');
1696
+ w.setLook('rectangular-arrow-minimal');
1696
1697
  w.setShowActions(false);
1697
1698
  const mainStepTitle = (entity.formats?.individual ||
1698
1699
  entity.title ||
@@ -2554,7 +2555,9 @@ class AXPCreateEntityCommand {
2554
2555
  const entityRef = await this.entityService.resolve(moduleName, entityName);
2555
2556
  let chain = this.entityForm.entity(`${moduleName}.${entityName}`).create(data);
2556
2557
  chain.actions((actions) => {
2557
- actions.cancel('@general:actions.cancel.title').submit('@general:actions.create.title');
2558
+ actions
2559
+ .cancel('@general:actions.cancel.title')
2560
+ .submit('@general:actions.create.title', { shortcuts: ['Enter', 'ctrl+s'] });
2558
2561
  });
2559
2562
  if (excludeProperties && excludeProperties.length > 0) {
2560
2563
  chain = chain.exclude(...excludeProperties);
@@ -3382,11 +3385,11 @@ class AXPEntityDetailPopoverComponent {
3382
3385
  return prop.name;
3383
3386
  }
3384
3387
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPEntityDetailPopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3385
- 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 }); }
3388
+ 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 dark:ax-dark-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-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 }); }
3386
3389
  }
3387
3390
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPEntityDetailPopoverComponent, decorators: [{
3388
3391
  type: Component,
3389
- 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>" }]
3392
+ 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 dark:ax-dark-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-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>" }]
3390
3393
  }], 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 }] }] } });
3391
3394
 
3392
3395
  class AXPEntityDetailPopoverService {
@@ -3624,6 +3627,58 @@ class AXMEntityCrudServiceImpl {
3624
3627
  async custom(request) { }
3625
3628
  }
3626
3629
 
3630
+ /**
3631
+ * Error type that can be thrown by middlewares to abort the chain with a structured payload.
3632
+ */
3633
+ class AXPMiddlewareAbortError extends Error {
3634
+ constructor(message, options = {}) {
3635
+ super(message);
3636
+ this.message = message;
3637
+ this.options = options;
3638
+ this.name = 'AXPMiddlewareAbortError';
3639
+ }
3640
+ toResponse() {
3641
+ return {
3642
+ success: false,
3643
+ error: {
3644
+ code: this.options.code,
3645
+ message: this.message,
3646
+ status: this.options.status,
3647
+ details: this.options.details,
3648
+ },
3649
+ };
3650
+ }
3651
+ }
3652
+ /** Type guard for AXPMiddlewareAbortError */
3653
+ function isAXPMiddlewareAbortError(error) {
3654
+ return error instanceof AXPMiddlewareAbortError;
3655
+ }
3656
+ //#endregion
3657
+
3658
+ /**
3659
+ * Maps storage middleware failures to a command result the details view can surface.
3660
+ */
3661
+ function mapEntityStorageErrorToCommandResult(error, fallback = 'Failed to update entity') {
3662
+ if (isAXPMiddlewareAbortError(error)) {
3663
+ return {
3664
+ success: false,
3665
+ message: {
3666
+ type: 'error',
3667
+ code: error.options.code,
3668
+ text: error.message,
3669
+ details: error.options.details,
3670
+ },
3671
+ };
3672
+ }
3673
+ const text = error instanceof Error ? error.message : typeof error === 'string' ? error : fallback;
3674
+ return {
3675
+ success: false,
3676
+ message: {
3677
+ type: 'error',
3678
+ text,
3679
+ },
3680
+ };
3681
+ }
3627
3682
  /**
3628
3683
  * Best-effort plain string for thrown {@link Error} messages from command results (no i18n lookup).
3629
3684
  */
@@ -11426,12 +11481,11 @@ class AXPEntityCategoryWidgetEditComponent extends AXPValueWidgetComponent {
11426
11481
  setSmart(itemToExpose, i.target, this.singleOrMultiple(values));
11427
11482
  }
11428
11483
  });
11429
- this.contextService.patch(itemToExpose, true);
11430
- // Fire triggers
11431
- this.setValue(this.singleOrMultiple(keys));
11484
+ this.contextService.applyObjectPaths(itemToExpose, { origin: 'user' });
11485
+ this.setUserValue(this.singleOrMultiple(keys));
11432
11486
  }
11433
11487
  else {
11434
- this.setValue(this.singleOrMultiple(keys));
11488
+ this.setUserValue(this.singleOrMultiple(keys));
11435
11489
  }
11436
11490
  // Trigger change detection
11437
11491
  this.cdr.markForCheck();
@@ -13072,6 +13126,14 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
13072
13126
  hasAnyChange: changes.length > 0,
13073
13127
  };
13074
13128
  }
13129
+ /**
13130
+ * Maps toolbar sort queries ({ name, dir }) to AXDataSource sort options ({ field, dir }).
13131
+ */
13132
+ toDataSourceSortOptions(sorts) {
13133
+ return (sorts ?? [])
13134
+ .filter((s) => s.dir)
13135
+ .map((s) => ({ field: s.name, dir: s.dir }));
13136
+ }
13075
13137
  /**
13076
13138
  * Applies filter and sort changes to the data source
13077
13139
  */
@@ -13082,7 +13144,8 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
13082
13144
  });
13083
13145
  }
13084
13146
  if (changeTracker.isSortChanged) {
13085
- dataSource.sort(...(queries.sorts || []));
13147
+ const sortOptions = this.toDataSourceSortOptions(queries.sorts);
13148
+ dataSource.sort(...sortOptions);
13086
13149
  }
13087
13150
  }
13088
13151
  /**
@@ -15534,7 +15597,8 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
15534
15597
  this.setLoading = (loading) => {
15535
15598
  this.isLoading.set(loading);
15536
15599
  };
15537
- this.setItems = (items) => {
15600
+ this.setItems = (items, options) => {
15601
+ const origin = options?.origin ?? 'system';
15538
15602
  // Ensure items is always an array
15539
15603
  items = castArray(items);
15540
15604
  // Filter out null/undefined items
@@ -15556,7 +15620,7 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
15556
15620
  //
15557
15621
  // extract data from valueField and set context by expose path
15558
15622
  if (this.expose()) {
15559
- this.expoesItems();
15623
+ this.expoesItems(origin);
15560
15624
  }
15561
15625
  const newValue = this.singleOrMultiple(keys);
15562
15626
  if (this.filterMode()) {
@@ -15566,10 +15630,10 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
15566
15630
  operation: {
15567
15631
  type: this.multiple() ? 'in' : 'equal',
15568
15632
  },
15569
- });
15633
+ }, { origin });
15570
15634
  }
15571
15635
  else {
15572
- this.setValue(newValue);
15636
+ this.setValue(newValue, { origin });
15573
15637
  }
15574
15638
  };
15575
15639
  }
@@ -15621,7 +15685,7 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
15621
15685
  }
15622
15686
  //#region ---- Event Handlers from Dumb Components ----
15623
15687
  handleComponentValueChanged(items) {
15624
- this.setItems(items ?? []);
15688
+ this.setItems(items ?? [], { origin: 'user' });
15625
15689
  }
15626
15690
  async handleCreateClick(_e) {
15627
15691
  const entity = this.entityDef();
@@ -15646,10 +15710,10 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
15646
15710
  if (this.multiple()) {
15647
15711
  const exists = currentItems.some((item) => get(item, valueField) === newId);
15648
15712
  const newItems = exists ? currentItems : [...currentItems, createdItem];
15649
- this.setItems(newItems);
15713
+ this.setItems(newItems, { origin: 'user' });
15650
15714
  }
15651
15715
  else {
15652
- this.setItems([createdItem]);
15716
+ this.setItems([createdItem], { origin: 'user' });
15653
15717
  }
15654
15718
  }
15655
15719
  }
@@ -15666,7 +15730,7 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
15666
15730
  * so e.g. `{ person: { educationLevel: { id, title } } }` would replace the entire `person`
15667
15731
  * object and drop sibling fields like `person.educationLevelId`, causing a value/effect loop.
15668
15732
  */
15669
- expoesItems() {
15733
+ expoesItems(origin = 'system') {
15670
15734
  const exposeValue = castArray(this.expose());
15671
15735
  const items = this.selectedItems();
15672
15736
  const isEmpty = !items || items.length === 0;
@@ -15679,7 +15743,7 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
15679
15743
  parentPaths.add(pathParts.slice(0, -1).join('.'));
15680
15744
  }
15681
15745
  else {
15682
- this.contextService.update(i, null);
15746
+ this.contextService.update(i, null, { origin });
15683
15747
  }
15684
15748
  }
15685
15749
  else {
@@ -15688,25 +15752,25 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
15688
15752
  parentPaths.add(pathParts.slice(0, -1).join('.'));
15689
15753
  }
15690
15754
  else {
15691
- this.contextService.update(i.target, null);
15755
+ this.contextService.update(i.target, null, { origin });
15692
15756
  }
15693
15757
  }
15694
15758
  });
15695
15759
  parentPaths.forEach((parentPath) => {
15696
- this.contextService.update(parentPath, null);
15760
+ this.contextService.update(parentPath, null, { origin });
15697
15761
  });
15698
15762
  return;
15699
15763
  }
15700
15764
  exposeValue.forEach((i) => {
15701
15765
  if (typeof i === 'string') {
15702
15766
  const values = items.map((item) => set({}, i, get(item, i)));
15703
- this.contextService.update(i, this.singleOrMultiple(values));
15767
+ this.contextService.update(i, this.singleOrMultiple(values), { origin });
15704
15768
  }
15705
15769
  else {
15706
15770
  const values = this.multiple()
15707
15771
  ? items.map((item) => set({}, i.source, get(item, i.source)))
15708
15772
  : items.map((item) => get(item, i.source));
15709
- this.contextService.update(i.target, this.singleOrMultiple(values));
15773
+ this.contextService.update(i.target, this.singleOrMultiple(values), { origin });
15710
15774
  }
15711
15775
  });
15712
15776
  }
@@ -16516,11 +16580,11 @@ class AXPLookupWidgetColumnComponent extends AXPColumnWidgetComponent {
16516
16580
  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);
16517
16581
  }
16518
16582
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLookupWidgetColumnComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
16519
- 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]=\"detailPopoverEnabled()\"\n [class.hover:ax-text-primary]=\"detailPopoverEnabled()\"\n [class.hover:ax-underline]=\"detailPopoverEnabled()\"\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]=\"detailPopoverEnabled()\"\n [class.hover:ax-text-primary]=\"detailPopoverEnabled()\"\n [class.hover:ax-underline]=\"detailPopoverEnabled()\"\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 }); }
16583
+ 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 [class.ax-cursor-pointer]=\"detailPopoverEnabled()\" [class.hover:ax-text-primary]=\"detailPopoverEnabled()\"\n [class.hover:ax-underline]=\"detailPopoverEnabled()\" (click)=\"handleItemClick($index)\">\n {{ label }}\n </span>\n @if ($index < visibleItems().length - 1) { <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()\" #moreButton>\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 class=\"ax-cursor-pointer ax-text-primary hover:ax-underline\"\n (click)=\"openLazyPopover(); $event.stopPropagation()\" #lazyTrigger>\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 dark:ax-dark-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 [class.ax-cursor-pointer]=\"detailPopoverEnabled()\" [class.hover:ax-text-primary]=\"detailPopoverEnabled()\"\n [class.hover:ax-underline]=\"detailPopoverEnabled()\" (click)=\"handlePopoverItemClick($index)\">\n {{ label }}\n </span>\n } @empty {\n <span class=\"ax-text-muted\">---</span>\n }\n </div>\n }\n </div>\n</ax-popover>", 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 }); }
16520
16584
  }
16521
16585
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPLookupWidgetColumnComponent, decorators: [{
16522
16586
  type: Component,
16523
- args: [{ changeDetection: ChangeDetectionStrategy.OnPush, imports: [AXLoadingModule, AXPopoverModule], inputs: ['rawValue', 'rowData'], 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]=\"detailPopoverEnabled()\"\n [class.hover:ax-text-primary]=\"detailPopoverEnabled()\"\n [class.hover:ax-underline]=\"detailPopoverEnabled()\"\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]=\"detailPopoverEnabled()\"\n [class.hover:ax-text-primary]=\"detailPopoverEnabled()\"\n [class.hover:ax-underline]=\"detailPopoverEnabled()\"\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" }]
16587
+ args: [{ changeDetection: ChangeDetectionStrategy.OnPush, imports: [AXLoadingModule, AXPopoverModule], inputs: ['rawValue', 'rowData'], 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 [class.ax-cursor-pointer]=\"detailPopoverEnabled()\" [class.hover:ax-text-primary]=\"detailPopoverEnabled()\"\n [class.hover:ax-underline]=\"detailPopoverEnabled()\" (click)=\"handleItemClick($index)\">\n {{ label }}\n </span>\n @if ($index < visibleItems().length - 1) { <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()\" #moreButton>\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 class=\"ax-cursor-pointer ax-text-primary hover:ax-underline\"\n (click)=\"openLazyPopover(); $event.stopPropagation()\" #lazyTrigger>\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 dark:ax-dark-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 [class.ax-cursor-pointer]=\"detailPopoverEnabled()\" [class.hover:ax-text-primary]=\"detailPopoverEnabled()\"\n [class.hover:ax-underline]=\"detailPopoverEnabled()\" (click)=\"handlePopoverItemClick($index)\">\n {{ label }}\n </span>\n } @empty {\n <span class=\"ax-text-muted\">---</span>\n }\n </div>\n }\n </div>\n</ax-popover>" }]
16524
16588
  }], ctorParameters: () => [], propDecorators: { moreButton: [{ type: i0.ViewChild, args: ['moreButton', { isSignal: true }] }], lazyTrigger: [{ type: i0.ViewChild, args: ['lazyTrigger', { isSignal: true }] }], morePopover: [{ type: i0.ViewChild, args: ['morePopover', { isSignal: true }] }] } });
16525
16589
 
16526
16590
  var lookupWidgetColumn_component = /*#__PURE__*/Object.freeze({
@@ -18320,6 +18384,14 @@ class AXPFileListComponent {
18320
18384
  /** When true, edit dialog shows name, title and description fields. Default false. */
18321
18385
  this.enableTitleDescription = input(false, ...(ngDevMode ? [{ debugName: "enableTitleDescription" }] : /* istanbul ignore next */ []));
18322
18386
  this.multiple = input(true, ...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
18387
+ /** `rows`: file-type icons and action buttons; `links`: compact paperclip links (download on click). */
18388
+ this.look = input('rows', ...(ngDevMode ? [{ debugName: "look" }] : /* istanbul ignore next */ []));
18389
+ /** i18n key for links look label (template adds trailing colon). */
18390
+ this.titleKey = input('@general:widgets.file-uploader.attachments.title', ...(ngDevMode ? [{ debugName: "titleKey" }] : /* istanbul ignore next */ []));
18391
+ /** When false, links look omits the label (e.g. popover body). */
18392
+ this.showLabel = input(true, ...(ngDevMode ? [{ debugName: "showLabel" }] : /* istanbul ignore next */ []));
18393
+ /** `inline`: label + divider; `menu`: compact list for popovers/panels. */
18394
+ this.linksLayout = input('inline', ...(ngDevMode ? [{ debugName: "linksLayout" }] : /* istanbul ignore next */ []));
18323
18395
  this.files = input([], ...(ngDevMode ? [{ debugName: "files" }] : /* istanbul ignore next */ []));
18324
18396
  // Plugin context (passed from parent widget)
18325
18397
  this.plugins = input(undefined, ...(ngDevMode ? [{ debugName: "plugins" }] : /* istanbul ignore next */ []));
@@ -18330,11 +18402,19 @@ class AXPFileListComponent {
18330
18402
  * All files should be displayed, even those with `deleted` status.
18331
18403
  * The template will handle the visual differences based on the status.
18332
18404
  */
18333
- this.displayFiles = computed(() => this.files(), ...(ngDevMode ? [{ debugName: "displayFiles" }] : /* istanbul ignore next */ []));
18405
+ this.displayFiles = computed(() => {
18406
+ const list = this.files();
18407
+ if (this.look() === 'links') {
18408
+ return list.filter((file) => file.status !== 'deleted');
18409
+ }
18410
+ return list;
18411
+ }, ...(ngDevMode ? [{ debugName: "displayFiles" }] : /* istanbul ignore next */ []));
18334
18412
  // Cache for per-file actions provided by hooks
18335
18413
  this.fileIdToActions = signal({}, ...(ngDevMode ? [{ debugName: "fileIdToActions" }] : /* istanbul ignore next */ []));
18336
18414
  // Global actions from FileUploaderWebhookKeys.Actions hook
18337
18415
  this.globalActions = signal([], ...(ngDevMode ? [{ debugName: "globalActions" }] : /* istanbul ignore next */ []));
18416
+ /** Suppress duplicate downloads from double-click (two click events). */
18417
+ this.recentDownloadAtByKey = new Map();
18338
18418
  // Clear cache when files change to reload actions
18339
18419
  this.filesChangeEffect = effect(() => {
18340
18420
  // Track files changes to clear cache
@@ -18342,6 +18422,7 @@ class AXPFileListComponent {
18342
18422
  this.fileIdToActions.set({});
18343
18423
  }, ...(ngDevMode ? [{ debugName: "filesChangeEffect" }] : /* istanbul ignore next */ []));
18344
18424
  }
18425
+ static { this.DOWNLOAD_DEBOUNCE_MS = 500; }
18345
18426
  // Default actions that can be removed via hooks
18346
18427
  getDefaultActions(file) {
18347
18428
  const defaultActions = [];
@@ -18438,6 +18519,10 @@ class AXPFileListComponent {
18438
18519
  this.fileIdToActions.set(next);
18439
18520
  }
18440
18521
  async ngOnInit() {
18522
+ if (this.look() === 'links') {
18523
+ this.isLoading.set(false);
18524
+ return;
18525
+ }
18441
18526
  this.fileTypes.set(await this.fileTypeService.items());
18442
18527
  await this.loadGlobalActions();
18443
18528
  this.isLoading.set(false);
@@ -18457,6 +18542,36 @@ class AXPFileListComponent {
18457
18542
  this.globalActions.set([]);
18458
18543
  }
18459
18544
  }
18545
+ shouldSkipDuplicateDownload(file) {
18546
+ const key = this.downloadDedupeKey(file);
18547
+ const now = Date.now();
18548
+ const last = this.recentDownloadAtByKey.get(key);
18549
+ if (last != null && now - last < AXPFileListComponent.DOWNLOAD_DEBOUNCE_MS) {
18550
+ return true;
18551
+ }
18552
+ this.recentDownloadAtByKey.set(key, now);
18553
+ return false;
18554
+ }
18555
+ downloadDedupeKey(file) {
18556
+ if (typeof file.id === 'string' && file.id.trim()) {
18557
+ return file.id;
18558
+ }
18559
+ const src = file.source;
18560
+ if (src?.kind === 'fileId' && typeof src.value === 'string' && src.value.trim()) {
18561
+ return src.value;
18562
+ }
18563
+ return file.name;
18564
+ }
18565
+ /** Ensures `source` is set for links look and legacy rows that only carry storage id on `id`. */
18566
+ resolveFileForDownload(file) {
18567
+ if (file.source) {
18568
+ return file;
18569
+ }
18570
+ if (typeof file.id === 'string' && file.id.trim()) {
18571
+ return { ...file, source: { kind: 'fileId', value: file.id } };
18572
+ }
18573
+ return file;
18574
+ }
18460
18575
  getFileInfo(fileName) {
18461
18576
  const extension = fileName.split('.').pop()?.toLowerCase() || '';
18462
18577
  const extensions = this.fileTypes().flatMap((t) => t.extensions);
@@ -18466,9 +18581,22 @@ class AXPFileListComponent {
18466
18581
  type: fileType?.name || 'Unknown',
18467
18582
  };
18468
18583
  }
18584
+ /**
18585
+ * Double-click on a file row (rows look) or link row triggers download.
18586
+ */
18587
+ handleFileRowActivate(event, file) {
18588
+ if (file.status === 'deleted') {
18589
+ return;
18590
+ }
18591
+ void this.handleFileDownload(event, file);
18592
+ }
18469
18593
  async handleFileDownload(event, file) {
18470
- event.nativeEvent.preventDefault();
18471
- event.nativeEvent.stopPropagation();
18594
+ const nativeEvent = event instanceof Event ? event : event.nativeEvent;
18595
+ nativeEvent.preventDefault();
18596
+ nativeEvent.stopPropagation();
18597
+ if (this.shouldSkipDuplicateDownload(file)) {
18598
+ return;
18599
+ }
18472
18600
  try {
18473
18601
  await this.hooks.runAsync(FileUploaderWebhookKeys.FileItem.BeforeDownload, {
18474
18602
  file,
@@ -18478,7 +18606,9 @@ class AXPFileListComponent {
18478
18606
  });
18479
18607
  }
18480
18608
  catch { }
18481
- if (!file.source) {
18609
+ const resolved = this.resolveFileForDownload(file);
18610
+ const source = resolved.source;
18611
+ if (!source) {
18482
18612
  console.error('File source is undefined, cannot download.', file);
18483
18613
  return;
18484
18614
  }
@@ -18492,37 +18622,37 @@ class AXPFileListComponent {
18492
18622
  document.body.removeChild(link);
18493
18623
  URL.revokeObjectURL(url);
18494
18624
  };
18495
- switch (file.source.kind) {
18625
+ switch (source.kind) {
18496
18626
  case 'blob':
18497
- if (file.source.value instanceof Blob) {
18498
- triggerDownload(file.source.value, file.name ?? 'download');
18627
+ if (source.value instanceof Blob) {
18628
+ triggerDownload(source.value, resolved.name ?? 'download');
18499
18629
  }
18500
18630
  else {
18501
18631
  console.error('Source kind is blob, but value is not a Blob.', file);
18502
18632
  }
18503
18633
  break;
18504
18634
  case 'fileId':
18505
- if (typeof file.source.value === 'string') {
18635
+ if (typeof source.value === 'string') {
18506
18636
  try {
18507
- const fileInfo = await this.fileStorageService.getInfo(file.source.value);
18637
+ const fileInfo = await this.fileStorageService.getInfo(source.value);
18508
18638
  if (fileInfo && fileInfo.url) {
18509
18639
  const link = document.createElement('a');
18510
18640
  link.href = fileInfo.url;
18511
- link.download = file.name ?? fileInfo.name ?? 'download';
18641
+ link.download = resolved.name ?? fileInfo.name ?? 'download';
18512
18642
  link.target = '_blank';
18513
18643
  document.body.appendChild(link);
18514
18644
  link.click();
18515
18645
  document.body.removeChild(link);
18516
18646
  }
18517
18647
  else if (fileInfo && fileInfo.binary instanceof Blob) {
18518
- triggerDownload(fileInfo.binary, file.name ?? fileInfo.name ?? 'download');
18648
+ triggerDownload(fileInfo.binary, resolved.name ?? fileInfo.name ?? 'download');
18519
18649
  }
18520
18650
  else {
18521
18651
  console.error('Could not retrieve file for download from fileId:', fileInfo);
18522
18652
  }
18523
18653
  }
18524
18654
  catch (error) {
18525
- console.error('Error downloading file by fileId:', file.source.value, error);
18655
+ console.error('Error downloading file by fileId:', source.value, error);
18526
18656
  }
18527
18657
  }
18528
18658
  else {
@@ -18530,10 +18660,10 @@ class AXPFileListComponent {
18530
18660
  }
18531
18661
  break;
18532
18662
  case 'url':
18533
- if (typeof file.source.value === 'string') {
18663
+ if (typeof source.value === 'string') {
18534
18664
  const link = document.createElement('a');
18535
- link.href = file.source.value;
18536
- link.download = file.name ?? 'download';
18665
+ link.href = source.value;
18666
+ link.download = resolved.name ?? 'download';
18537
18667
  link.target = '_blank';
18538
18668
  document.body.appendChild(link);
18539
18669
  link.click();
@@ -18544,14 +18674,14 @@ class AXPFileListComponent {
18544
18674
  }
18545
18675
  break;
18546
18676
  case 'reference':
18547
- if (file.source.value &&
18548
- typeof file.source.value === 'object' &&
18549
- 'id' in file.source.value &&
18550
- 'type' in file.source.value) {
18677
+ if (source.value &&
18678
+ typeof source.value === 'object' &&
18679
+ 'id' in source.value &&
18680
+ 'type' in source.value) {
18551
18681
  try {
18552
18682
  const result = await this.hooks.runAsync(FileUploaderWebhookKeys.FileItem.DownloadReference, {
18553
- file,
18554
- reference: file.source.value,
18683
+ file: resolved,
18684
+ reference: source.value,
18555
18685
  plugins: this.plugins() ?? [],
18556
18686
  excludePlugins: this.excludePlugins() ?? [],
18557
18687
  capabilities: this.capabilities(),
@@ -18561,14 +18691,14 @@ class AXPFileListComponent {
18561
18691
  if (result.fileInfo.url) {
18562
18692
  const link = document.createElement('a');
18563
18693
  link.href = result.fileInfo.url;
18564
- link.download = file.name ?? result.fileInfo.name ?? 'download';
18694
+ link.download = resolved.name ?? result.fileInfo.name ?? 'download';
18565
18695
  link.target = '_blank';
18566
18696
  document.body.appendChild(link);
18567
18697
  link.click();
18568
18698
  document.body.removeChild(link);
18569
18699
  }
18570
18700
  else if (result.fileInfo.binary instanceof Blob) {
18571
- triggerDownload(result.fileInfo.binary, file.name ?? result.fileInfo.name ?? 'download');
18701
+ triggerDownload(result.fileInfo.binary, resolved.name ?? result.fileInfo.name ?? 'download');
18572
18702
  }
18573
18703
  else {
18574
18704
  console.error('Could not retrieve file for download from reference:', result.fileInfo);
@@ -18579,7 +18709,7 @@ class AXPFileListComponent {
18579
18709
  }
18580
18710
  }
18581
18711
  catch (error) {
18582
- console.error('Error downloading file by reference:', file.source.value, error);
18712
+ console.error('Error downloading file by reference:', source.value, error);
18583
18713
  }
18584
18714
  }
18585
18715
  else {
@@ -18589,12 +18719,12 @@ class AXPFileListComponent {
18589
18719
  case 'preview':
18590
18720
  case 'none':
18591
18721
  default:
18592
- console.error(`Download not supported for source kind: ${file.source.kind}`, file);
18722
+ console.error(`Download not supported for source kind: ${source.kind}`, file);
18593
18723
  break;
18594
18724
  }
18595
18725
  try {
18596
18726
  await this.hooks.runAsync(FileUploaderWebhookKeys.FileItem.AfterDownload, {
18597
- file,
18727
+ file: resolved,
18598
18728
  plugins: this.plugins() ?? [],
18599
18729
  excludePlugins: this.excludePlugins() ?? [],
18600
18730
  capabilities: this.capabilities(),
@@ -18663,7 +18793,9 @@ class AXPFileListComponent {
18663
18793
  }
18664
18794
  catch { }
18665
18795
  }
18666
- ngOnDestroy() { }
18796
+ ngOnDestroy() {
18797
+ this.recentDownloadAtByKey.clear();
18798
+ }
18667
18799
  async runAction(action) {
18668
18800
  try {
18669
18801
  await action.run(this.capabilities());
@@ -18687,19 +18819,24 @@ class AXPFileListComponent {
18687
18819
  return action.color ?? 'primary';
18688
18820
  }
18689
18821
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
18690
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPFileListComponent, isStandalone: true, selector: "axp-file-list", inputs: { readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, fileEditable: { classPropertyName: "fileEditable", publicName: "fileEditable", isSignal: true, isRequired: false, transformFunction: null }, enableTitleDescription: { classPropertyName: "enableTitleDescription", publicName: "enableTitleDescription", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, files: { classPropertyName: "files", publicName: "files", isSignal: true, isRequired: false, transformFunction: null }, plugins: { classPropertyName: "plugins", publicName: "plugins", isSignal: true, isRequired: false, transformFunction: null }, excludePlugins: { classPropertyName: "excludePlugins", publicName: "excludePlugins", isSignal: true, isRequired: false, transformFunction: null }, capabilities: { classPropertyName: "capabilities", publicName: "capabilities", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onRemove: "onRemove", onRevert: "onRevert", onRename: "onRename" }, providers: [], ngImport: i0, template: "@for (file of displayFiles(); track $index) {\n <div\n class=\"__item\"\n [ngClass]=\"{\n '--removed': file.status === 'deleted',\n '--attached': file.status === 'attached',\n '--with-meta': enableTitleDescription(),\n '--item-readonly': file.readOnly === true,\n }\"\n >\n <div class=\"__icon\">\n <i class=\"fa-fw {{ getFileInfo(file.name).icon }} fa-solid\"></i>\n </div>\n @if (enableTitleDescription()) {\n <div class=\"__content\">\n <div class=\"__primary\">{{ file.name }}{{ file.title?.trim() ? ' - ' + file.title : '' }}</div>\n @if (file.description?.trim()) {\n <div class=\"__secondary\">{{ file.description }}</div>\n }\n </div>\n } @else {\n <div class=\"__name\">{{ file.name }}</div>\n }\n <div class=\"__actions\">\n @if (file.status === 'deleted' && multiple() && !isItemInteractionLocked(file)) {\n <!-- Revert button - only show when multiple is true -->\n <ax-button [look]=\"'blank'\" class=\"ax-sm\" color=\"warning\" (onClick)=\"handleFileRevert($event,file)\">\n <ax-icon class=\"fa-light fa-rotate-left\"></ax-icon>\n </ax-button>\n } @else if (file.status !== 'deleted') {\n <!-- Default + hook actions; readonly still shows download (edit/remove omitted in component logic). -->\n @for (action of actionsFor(file, $index); track action.id ?? action.textKey ?? action.text ?? $index) {\n <ax-button\n [look]=\"'blank'\"\n class=\"ax-sm\"\n [color]=\"getActionColor(action)\"\n (onClick)=\"runAction(action)\"\n >\n @if (action.icon) {\n <ax-icon class=\"{{ action.icon }}\"></ax-icon>\n }\n </ax-button>\n }\n }\n </div>\n </div>\n} @empty {\n <div class=\"__empty-state\">\n <axp-state-message\n icon=\"fa-light fa-folder-open\"\n [title]=\"'@general:widgets.file-uploader.empty-state.title'\"\n [description]=\"'@general:widgets.file-uploader.empty-state.description'\"\n >\n </axp-state-message>\n </div>\n}\n", styles: [":host{display:flex;width:100%;flex-direction:column;gap:.125rem;padding-top:.5rem;padding-bottom:.5rem}:host .__item{display:flex;cursor:pointer;align-items:center;gap:.75rem;border-left-width:4px;border-color:transparent;padding:.5rem}:host .__item:hover{background-color:rgb(var(--ax-sys-color-lighter-surface));color:rgb(var(--ax-sys-color-on-lighter-surface));border-color:rgb(var(--ax-sys-color-border-lighter-surface))}:host .__item.--removed{--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-danger-500),var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-danger-50),var(--tw-bg-opacity, 1))}:host .__item.--attached{--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-success-500),var(--tw-border-opacity, 1));animation:attached-flash 1s ease-out forwards}:host .__item.--item-readonly:not(.--removed){opacity:.85}:host .__item .__icon{width:1.5rem;flex-shrink:0}:host .__item .__name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:start;font-size:.875rem;line-height:1.25rem;font-weight:500}:host .__item .__content{display:flex;min-width:0px;flex:1 1 0%;flex-direction:column;gap:.125rem}:host .__item .__primary{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:start;font-size:.875rem;line-height:1.25rem;font-weight:500}:host .__item .__secondary{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;text-align:start;font-size:.75rem;line-height:1rem;color:var(--ax-sys-color-text-secondary)}:host .__item.--with-meta .__content{justify-content:center}:host .__item .__actions{margin-left:auto;display:flex;flex-shrink:0}@keyframes attached-flash{0%{background-color:rgb(var(--ax-sys-color-success-50))}to{background-color:transparent}}.__empty-state{cursor:pointer;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s;animation-duration:.2s}.__empty-state:hover{opacity:.8}\n"], dependencies: [{ kind: "ngmodule", type:
18691
- // Angular
18692
- CommonModule }, { kind: "directive", type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type:
18822
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPFileListComponent, isStandalone: true, selector: "axp-file-list", inputs: { readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, fileEditable: { classPropertyName: "fileEditable", publicName: "fileEditable", isSignal: true, isRequired: false, transformFunction: null }, enableTitleDescription: { classPropertyName: "enableTitleDescription", publicName: "enableTitleDescription", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, look: { classPropertyName: "look", publicName: "look", isSignal: true, isRequired: false, transformFunction: null }, titleKey: { classPropertyName: "titleKey", publicName: "titleKey", isSignal: true, isRequired: false, transformFunction: null }, showLabel: { classPropertyName: "showLabel", publicName: "showLabel", isSignal: true, isRequired: false, transformFunction: null }, linksLayout: { classPropertyName: "linksLayout", publicName: "linksLayout", isSignal: true, isRequired: false, transformFunction: null }, files: { classPropertyName: "files", publicName: "files", isSignal: true, isRequired: false, transformFunction: null }, plugins: { classPropertyName: "plugins", publicName: "plugins", isSignal: true, isRequired: false, transformFunction: null }, excludePlugins: { classPropertyName: "excludePlugins", publicName: "excludePlugins", isSignal: true, isRequired: false, transformFunction: null }, capabilities: { classPropertyName: "capabilities", publicName: "capabilities", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onRemove: "onRemove", onRevert: "onRevert", onRename: "onRename" }, host: { properties: { "class.--look-rows": "look() === \"rows\"", "class.--look-links": "look() === \"links\"", "class.--links-inline": "look() === \"links\" && linksLayout() === \"inline\"" } }, providers: [], ngImport: i0, template: "@if (look() === 'links') {\n @if (displayFiles().length > 0) {\n <div class=\"__links\">\n @if (showLabel()) {\n <span class=\"__links-label\">{{ titleKey() | translate | async }}:</span>\n }\n <ul class=\"__links-list\">\n @for (file of displayFiles(); track file.id ?? $index) {\n <li>\n <button\n type=\"button\"\n class=\"__link-item\"\n (click)=\"handleFileDownload($event, file)\"\n (dblclick)=\"handleFileRowActivate($event, file)\"\n >\n <i class=\"fa-light fa-paperclip ax-flex-shrink-0\"></i>\n <span class=\"ax-min-w-0 ax-break-all\">{{ file.name }}</span>\n </button>\n </li>\n }\n </ul>\n </div>\n }\n} @else {\n @for (file of displayFiles(); track $index) {\n <div\n class=\"__item\"\n [ngClass]=\"{\n '--removed': file.status === 'deleted',\n '--attached': file.status === 'attached',\n '--with-meta': enableTitleDescription(),\n '--item-readonly': file.readOnly === true,\n '--downloadable': file.status !== 'deleted',\n }\"\n (dblclick)=\"handleFileRowActivate($event, file)\"\n >\n <div class=\"__icon\">\n <i class=\"fa-fw {{ getFileInfo(file.name).icon }} fa-solid\"></i>\n </div>\n @if (enableTitleDescription()) {\n <div class=\"__content\">\n <div class=\"__primary\">{{ file.name }}{{ file.title?.trim() ? ' - ' + file.title : '' }}</div>\n @if (file.description?.trim()) {\n <div class=\"__secondary\">{{ file.description }}</div>\n }\n </div>\n } @else {\n <div class=\"__name\">{{ file.name }}</div>\n }\n <div class=\"__actions\" (dblclick)=\"$event.stopPropagation()\">\n @if (file.status === 'deleted' && multiple() && !isItemInteractionLocked(file)) {\n <ax-button [look]=\"'blank'\" class=\"ax-sm\" color=\"warning\" (onClick)=\"handleFileRevert($event, file)\">\n <ax-icon class=\"fa-light fa-rotate-left\"></ax-icon>\n </ax-button>\n } @else if (file.status !== 'deleted') {\n @for (action of actionsFor(file, $index); track action.id ?? action.textKey ?? action.text ?? $index) {\n <ax-button\n [look]=\"'blank'\"\n class=\"ax-sm\"\n [color]=\"getActionColor(action)\"\n (onClick)=\"runAction(action)\"\n >\n @if (action.icon) {\n <ax-icon class=\"{{ action.icon }}\"></ax-icon>\n }\n </ax-button>\n }\n }\n </div>\n </div>\n } @empty {\n <div class=\"__empty-state\">\n <axp-state-message\n icon=\"fa-light fa-folder-open\"\n [title]=\"'@general:widgets.file-uploader.empty-state.title'\"\n [description]=\"'@general:widgets.file-uploader.empty-state.description'\"\n >\n </axp-state-message>\n </div>\n }\n}\n", styles: [":host{display:flex;width:100%;flex-direction:column;gap:.125rem}:host.--look-rows{padding-top:.5rem;padding-bottom:.5rem}:host.--look-links{padding-top:0;padding-bottom:0}:host.--look-links .__links .__links-label{margin-inline-end:.5rem;font-size:.875rem;line-height:1.25rem;font-weight:500;color:rgb(var(--ax-sys-color-on-surface-variant))}:host.--look-links .__links .__links-list{margin:0;margin-top:.25rem;list-style-type:none;padding:0}:host.--look-links .__links .__links-list li{margin:0;padding:0}:host.--look-links .__links .__links-list .__link-item{display:inline-flex;width:100%;cursor:pointer;align-items:center;gap:.75rem;border-radius:.375rem;border-width:0px;background-color:transparent;padding:.625rem .75rem;text-align:start;font-size:.875rem;line-height:1.25rem;text-decoration-line:none;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;color:rgb(var(--ax-sys-color-primary))}:host.--look-links .__links .__links-list .__link-item:hover{background:rgb(var(--ax-sys-color-lighter-surface))}:host.--look-links.--links-inline .__links{margin-bottom:1rem;border-bottom-width:1px;--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-border-surface),var(--tw-border-opacity, 1));padding-bottom:1rem}:host.--look-links:not(.--links-inline) .__links-list{margin-top:0;display:flex;flex-direction:column;gap:.5rem}:host .__item{display:flex;align-items:center;gap:.75rem;border-left-width:4px;border-color:transparent;padding:.5rem}:host .__item.--downloadable{cursor:pointer}:host .__item.--downloadable:hover{background-color:rgb(var(--ax-sys-color-lighter-surface));color:rgb(var(--ax-sys-color-on-lighter-surface));border-color:rgb(var(--ax-sys-color-border-lighter-surface))}:host .__item:not(.--downloadable){cursor:default}:host .__item.--removed{--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-danger-500),var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-danger-50),var(--tw-bg-opacity, 1))}:host .__item.--attached{--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-success-500),var(--tw-border-opacity, 1));animation:attached-flash 1s ease-out forwards}:host .__item.--item-readonly:not(.--removed){opacity:.85}:host .__item .__icon{width:1.5rem;flex-shrink:0}:host .__item .__name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:start;font-size:.875rem;line-height:1.25rem;font-weight:500}:host .__item .__content{display:flex;min-width:0px;flex:1 1 0%;flex-direction:column;gap:.125rem}:host .__item .__primary{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:start;font-size:.875rem;line-height:1.25rem;font-weight:500}:host .__item .__secondary{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;text-align:start;font-size:.75rem;line-height:1rem;color:var(--ax-sys-color-text-secondary)}:host .__item.--with-meta .__content{justify-content:center}:host .__item .__actions{margin-left:auto;display:flex;flex-shrink:0}@keyframes attached-flash{0%{background-color:rgb(var(--ax-sys-color-success-50))}to{background-color:transparent}}.__empty-state{cursor:pointer;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s;animation-duration:.2s}.__empty-state:hover{opacity:.8}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type:
18693
18823
  // ACoreX
18694
18824
  AXFormModule }, { kind: "ngmodule", type: AXTextBoxModule }, { 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: AXLabelModule }, { kind: "ngmodule", type: AXCheckBoxModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i3$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type:
18695
18825
  // Platform
18696
- AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "look"] }] }); }
18826
+ AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "look"] }, { kind: "pipe", type:
18827
+ // Angular
18828
+ AsyncPipe, name: "async" }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }] }); }
18697
18829
  }
18698
18830
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileListComponent, decorators: [{
18699
18831
  type: Component,
18700
- args: [{ selector: 'axp-file-list', imports: [
18832
+ args: [{ selector: 'axp-file-list', host: {
18833
+ '[class.--look-rows]': 'look() === "rows"',
18834
+ '[class.--look-links]': 'look() === "links"',
18835
+ '[class.--links-inline]': 'look() === "links" && linksLayout() === "inline"',
18836
+ }, imports: [
18701
18837
  // Angular
18702
- CommonModule,
18838
+ AsyncPipe,
18839
+ NgClass,
18703
18840
  // ACoreX
18704
18841
  AXFormModule,
18705
18842
  AXTextBoxModule,
@@ -18710,8 +18847,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
18710
18847
  AXTranslationModule,
18711
18848
  // Platform
18712
18849
  AXPStateMessageComponent,
18713
- ], providers: [], template: "@for (file of displayFiles(); track $index) {\n <div\n class=\"__item\"\n [ngClass]=\"{\n '--removed': file.status === 'deleted',\n '--attached': file.status === 'attached',\n '--with-meta': enableTitleDescription(),\n '--item-readonly': file.readOnly === true,\n }\"\n >\n <div class=\"__icon\">\n <i class=\"fa-fw {{ getFileInfo(file.name).icon }} fa-solid\"></i>\n </div>\n @if (enableTitleDescription()) {\n <div class=\"__content\">\n <div class=\"__primary\">{{ file.name }}{{ file.title?.trim() ? ' - ' + file.title : '' }}</div>\n @if (file.description?.trim()) {\n <div class=\"__secondary\">{{ file.description }}</div>\n }\n </div>\n } @else {\n <div class=\"__name\">{{ file.name }}</div>\n }\n <div class=\"__actions\">\n @if (file.status === 'deleted' && multiple() && !isItemInteractionLocked(file)) {\n <!-- Revert button - only show when multiple is true -->\n <ax-button [look]=\"'blank'\" class=\"ax-sm\" color=\"warning\" (onClick)=\"handleFileRevert($event,file)\">\n <ax-icon class=\"fa-light fa-rotate-left\"></ax-icon>\n </ax-button>\n } @else if (file.status !== 'deleted') {\n <!-- Default + hook actions; readonly still shows download (edit/remove omitted in component logic). -->\n @for (action of actionsFor(file, $index); track action.id ?? action.textKey ?? action.text ?? $index) {\n <ax-button\n [look]=\"'blank'\"\n class=\"ax-sm\"\n [color]=\"getActionColor(action)\"\n (onClick)=\"runAction(action)\"\n >\n @if (action.icon) {\n <ax-icon class=\"{{ action.icon }}\"></ax-icon>\n }\n </ax-button>\n }\n }\n </div>\n </div>\n} @empty {\n <div class=\"__empty-state\">\n <axp-state-message\n icon=\"fa-light fa-folder-open\"\n [title]=\"'@general:widgets.file-uploader.empty-state.title'\"\n [description]=\"'@general:widgets.file-uploader.empty-state.description'\"\n >\n </axp-state-message>\n </div>\n}\n", styles: [":host{display:flex;width:100%;flex-direction:column;gap:.125rem;padding-top:.5rem;padding-bottom:.5rem}:host .__item{display:flex;cursor:pointer;align-items:center;gap:.75rem;border-left-width:4px;border-color:transparent;padding:.5rem}:host .__item:hover{background-color:rgb(var(--ax-sys-color-lighter-surface));color:rgb(var(--ax-sys-color-on-lighter-surface));border-color:rgb(var(--ax-sys-color-border-lighter-surface))}:host .__item.--removed{--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-danger-500),var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-danger-50),var(--tw-bg-opacity, 1))}:host .__item.--attached{--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-success-500),var(--tw-border-opacity, 1));animation:attached-flash 1s ease-out forwards}:host .__item.--item-readonly:not(.--removed){opacity:.85}:host .__item .__icon{width:1.5rem;flex-shrink:0}:host .__item .__name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:start;font-size:.875rem;line-height:1.25rem;font-weight:500}:host .__item .__content{display:flex;min-width:0px;flex:1 1 0%;flex-direction:column;gap:.125rem}:host .__item .__primary{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:start;font-size:.875rem;line-height:1.25rem;font-weight:500}:host .__item .__secondary{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;text-align:start;font-size:.75rem;line-height:1rem;color:var(--ax-sys-color-text-secondary)}:host .__item.--with-meta .__content{justify-content:center}:host .__item .__actions{margin-left:auto;display:flex;flex-shrink:0}@keyframes attached-flash{0%{background-color:rgb(var(--ax-sys-color-success-50))}to{background-color:transparent}}.__empty-state{cursor:pointer;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s;animation-duration:.2s}.__empty-state:hover{opacity:.8}\n"] }]
18714
- }], propDecorators: { onRemove: [{ type: i0.Output, args: ["onRemove"] }], onRevert: [{ type: i0.Output, args: ["onRevert"] }], onRename: [{ type: i0.Output, args: ["onRename"] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], fileEditable: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileEditable", required: false }] }], enableTitleDescription: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableTitleDescription", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], files: [{ type: i0.Input, args: [{ isSignal: true, alias: "files", required: false }] }], plugins: [{ type: i0.Input, args: [{ isSignal: true, alias: "plugins", required: false }] }], excludePlugins: [{ type: i0.Input, args: [{ isSignal: true, alias: "excludePlugins", required: false }] }], capabilities: [{ type: i0.Input, args: [{ isSignal: true, alias: "capabilities", required: false }] }] } });
18850
+ ], providers: [], template: "@if (look() === 'links') {\n @if (displayFiles().length > 0) {\n <div class=\"__links\">\n @if (showLabel()) {\n <span class=\"__links-label\">{{ titleKey() | translate | async }}:</span>\n }\n <ul class=\"__links-list\">\n @for (file of displayFiles(); track file.id ?? $index) {\n <li>\n <button\n type=\"button\"\n class=\"__link-item\"\n (click)=\"handleFileDownload($event, file)\"\n (dblclick)=\"handleFileRowActivate($event, file)\"\n >\n <i class=\"fa-light fa-paperclip ax-flex-shrink-0\"></i>\n <span class=\"ax-min-w-0 ax-break-all\">{{ file.name }}</span>\n </button>\n </li>\n }\n </ul>\n </div>\n }\n} @else {\n @for (file of displayFiles(); track $index) {\n <div\n class=\"__item\"\n [ngClass]=\"{\n '--removed': file.status === 'deleted',\n '--attached': file.status === 'attached',\n '--with-meta': enableTitleDescription(),\n '--item-readonly': file.readOnly === true,\n '--downloadable': file.status !== 'deleted',\n }\"\n (dblclick)=\"handleFileRowActivate($event, file)\"\n >\n <div class=\"__icon\">\n <i class=\"fa-fw {{ getFileInfo(file.name).icon }} fa-solid\"></i>\n </div>\n @if (enableTitleDescription()) {\n <div class=\"__content\">\n <div class=\"__primary\">{{ file.name }}{{ file.title?.trim() ? ' - ' + file.title : '' }}</div>\n @if (file.description?.trim()) {\n <div class=\"__secondary\">{{ file.description }}</div>\n }\n </div>\n } @else {\n <div class=\"__name\">{{ file.name }}</div>\n }\n <div class=\"__actions\" (dblclick)=\"$event.stopPropagation()\">\n @if (file.status === 'deleted' && multiple() && !isItemInteractionLocked(file)) {\n <ax-button [look]=\"'blank'\" class=\"ax-sm\" color=\"warning\" (onClick)=\"handleFileRevert($event, file)\">\n <ax-icon class=\"fa-light fa-rotate-left\"></ax-icon>\n </ax-button>\n } @else if (file.status !== 'deleted') {\n @for (action of actionsFor(file, $index); track action.id ?? action.textKey ?? action.text ?? $index) {\n <ax-button\n [look]=\"'blank'\"\n class=\"ax-sm\"\n [color]=\"getActionColor(action)\"\n (onClick)=\"runAction(action)\"\n >\n @if (action.icon) {\n <ax-icon class=\"{{ action.icon }}\"></ax-icon>\n }\n </ax-button>\n }\n }\n </div>\n </div>\n } @empty {\n <div class=\"__empty-state\">\n <axp-state-message\n icon=\"fa-light fa-folder-open\"\n [title]=\"'@general:widgets.file-uploader.empty-state.title'\"\n [description]=\"'@general:widgets.file-uploader.empty-state.description'\"\n >\n </axp-state-message>\n </div>\n }\n}\n", styles: [":host{display:flex;width:100%;flex-direction:column;gap:.125rem}:host.--look-rows{padding-top:.5rem;padding-bottom:.5rem}:host.--look-links{padding-top:0;padding-bottom:0}:host.--look-links .__links .__links-label{margin-inline-end:.5rem;font-size:.875rem;line-height:1.25rem;font-weight:500;color:rgb(var(--ax-sys-color-on-surface-variant))}:host.--look-links .__links .__links-list{margin:0;margin-top:.25rem;list-style-type:none;padding:0}:host.--look-links .__links .__links-list li{margin:0;padding:0}:host.--look-links .__links .__links-list .__link-item{display:inline-flex;width:100%;cursor:pointer;align-items:center;gap:.75rem;border-radius:.375rem;border-width:0px;background-color:transparent;padding:.625rem .75rem;text-align:start;font-size:.875rem;line-height:1.25rem;text-decoration-line:none;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;color:rgb(var(--ax-sys-color-primary))}:host.--look-links .__links .__links-list .__link-item:hover{background:rgb(var(--ax-sys-color-lighter-surface))}:host.--look-links.--links-inline .__links{margin-bottom:1rem;border-bottom-width:1px;--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-border-surface),var(--tw-border-opacity, 1));padding-bottom:1rem}:host.--look-links:not(.--links-inline) .__links-list{margin-top:0;display:flex;flex-direction:column;gap:.5rem}:host .__item{display:flex;align-items:center;gap:.75rem;border-left-width:4px;border-color:transparent;padding:.5rem}:host .__item.--downloadable{cursor:pointer}:host .__item.--downloadable:hover{background-color:rgb(var(--ax-sys-color-lighter-surface));color:rgb(var(--ax-sys-color-on-lighter-surface));border-color:rgb(var(--ax-sys-color-border-lighter-surface))}:host .__item:not(.--downloadable){cursor:default}:host .__item.--removed{--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-danger-500),var(--tw-border-opacity, 1));--tw-bg-opacity: 1;background-color:rgba(var(--ax-sys-color-danger-50),var(--tw-bg-opacity, 1))}:host .__item.--attached{--tw-border-opacity: 1;border-color:rgba(var(--ax-sys-color-success-500),var(--tw-border-opacity, 1));animation:attached-flash 1s ease-out forwards}:host .__item.--item-readonly:not(.--removed){opacity:.85}:host .__item .__icon{width:1.5rem;flex-shrink:0}:host .__item .__name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:start;font-size:.875rem;line-height:1.25rem;font-weight:500}:host .__item .__content{display:flex;min-width:0px;flex:1 1 0%;flex-direction:column;gap:.125rem}:host .__item .__primary{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-align:start;font-size:.875rem;line-height:1.25rem;font-weight:500}:host .__item .__secondary{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;text-align:start;font-size:.75rem;line-height:1rem;color:var(--ax-sys-color-text-secondary)}:host .__item.--with-meta .__content{justify-content:center}:host .__item .__actions{margin-left:auto;display:flex;flex-shrink:0}@keyframes attached-flash{0%{background-color:rgb(var(--ax-sys-color-success-50))}to{background-color:transparent}}.__empty-state{cursor:pointer;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s;animation-duration:.2s}.__empty-state:hover{opacity:.8}\n"] }]
18851
+ }], propDecorators: { onRemove: [{ type: i0.Output, args: ["onRemove"] }], onRevert: [{ type: i0.Output, args: ["onRevert"] }], onRename: [{ type: i0.Output, args: ["onRename"] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], fileEditable: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileEditable", required: false }] }], enableTitleDescription: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableTitleDescription", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], look: [{ type: i0.Input, args: [{ isSignal: true, alias: "look", required: false }] }], titleKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleKey", required: false }] }], showLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLabel", required: false }] }], linksLayout: [{ type: i0.Input, args: [{ isSignal: true, alias: "linksLayout", required: false }] }], files: [{ type: i0.Input, args: [{ isSignal: true, alias: "files", required: false }] }], plugins: [{ type: i0.Input, args: [{ isSignal: true, alias: "plugins", required: false }] }], excludePlugins: [{ type: i0.Input, args: [{ isSignal: true, alias: "excludePlugins", required: false }] }], capabilities: [{ type: i0.Input, args: [{ isSignal: true, alias: "capabilities", required: false }] }] } });
18715
18852
 
18716
18853
  //#region ---- Attachment fingerprint (no blob comparison) ----
18717
18854
  /** Stable fingerprint for one file row; ignores Blob binary content. */
@@ -18968,95 +19105,146 @@ function hasFileUploaderTitleOrDescriptionFields(editDialog) {
18968
19105
  }
18969
19106
  //#endregion
18970
19107
 
18971
- class AXPFileUploaderWidgetService {
18972
- constructor() {
18973
- //#region ---- Services & Dependencies ----
18974
- this.popupService = inject(AXPopupService);
18975
- this.translate = inject(AXTranslationService);
18976
- this.commandExecutor = inject(AXPCommandExecutor);
18977
- this.queryExecutor = inject(AXPQueryExecutor);
19108
+ //#region ---- Imports ----
19109
+ //#endregion
19110
+ //#region ---- Title template resolution ----
19111
+ 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;
18978
19119
  }
18979
- //#endregion
18980
- //#region ---- Public API ----
18981
- async showFileList(options) {
18982
- const entity = options?.entity;
18983
- let files = options?.files;
18984
- if (entity) {
18985
- const filesResult = await this.queryExecutor.fetch('FileUploader:LoadFiles', entity);
18986
- if (!filesResult) {
18987
- return undefined;
18988
- }
18989
- files = filesResult.files ?? [];
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
+ /** Resolves a row field or format result to a plain display string for the active locale. */
19135
+ function resolveEntityRowDisplayText(value, activeLang) {
19136
+ if (value == null || value === '') {
19137
+ return '';
19138
+ }
19139
+ if (typeof value === 'string') {
19140
+ const text = value.trim();
19141
+ if (!text || text === INVALID_DISPLAY_TEXT || isUnresolvedLookupDisplayTemplate(text)) {
19142
+ return '';
18990
19143
  }
18991
- const resolved = {
18992
- files: files ?? [],
18993
- readonly: options?.readonly ?? false,
18994
- multiple: options?.multiple ?? false,
18995
- accept: options?.accept ?? '*',
18996
- fileEditable: options?.fileEditable ?? true,
18997
- maxFileSize: options?.maxFileSize ?? 1024 * 1024 * 10,
18998
- showAddItemButton: options?.showAddItemButton ?? true,
18999
- plugins: options?.plugins ?? [],
19000
- editDialog: options?.editDialog,
19001
- };
19002
- const component = await import('./acorex-platform-layout-entity-file-list-popup.component-_yrP5SQe.mjs').then((m) => m.AXPFileListPopupComponent);
19003
- const result = await this.popupService.open(component, {
19004
- title: await this.translate.translateAsync('@document-management:terms.common.file'),
19005
- data: {
19006
- files: signal(resolved.files),
19007
- isReadonly: signal(resolved.readonly),
19008
- multiple: signal(resolved.multiple),
19009
- accept: signal(resolved.accept),
19010
- maxFileSize: signal(resolved.maxFileSize),
19011
- fileEditable: signal(resolved.fileEditable),
19012
- showAddItemButton: signal(resolved.showAddItemButton),
19013
- plugins: signal(resolved.plugins),
19014
- editDialog: signal(resolved.editDialog),
19015
- },
19016
- });
19017
- const updatedFiles = result?.data?.data?.files;
19018
- if (!updatedFiles) {
19019
- return undefined;
19144
+ return text;
19145
+ }
19146
+ if (typeof value === 'number' || typeof value === 'boolean') {
19147
+ return String(value);
19148
+ }
19149
+ if (typeof value === 'object' && !Array.isArray(value)) {
19150
+ const text = resolveMultiLanguageString(value, activeLang).trim();
19151
+ if (!text || text === INVALID_DISPLAY_TEXT) {
19152
+ return '';
19020
19153
  }
19021
- if (!entity) {
19022
- return updatedFiles;
19154
+ return text;
19155
+ }
19156
+ return '';
19157
+ }
19158
+ /**
19159
+ * Formats the entity row title for display using row fields and single-view template.
19160
+ */
19161
+ function formatEntityRowDisplayTitle(entity, rowData, formatService, activeLang) {
19162
+ if (!rowData) {
19163
+ return '';
19164
+ }
19165
+ for (const field of ['title', 'name', 'code']) {
19166
+ const fromField = resolveEntityRowDisplayText(get(rowData, field), activeLang);
19167
+ if (fromField) {
19168
+ return fromField;
19023
19169
  }
19024
- const saveResult = await this.commandExecutor.execute('FileUploader:SaveFiles', {
19025
- ...entity,
19026
- files: updatedFiles,
19027
- });
19028
- if (!saveResult?.success) {
19029
- return undefined;
19170
+ }
19171
+ const template = resolveEntityRowTitleTemplate(entity);
19172
+ if (template) {
19173
+ const formatted = formatService.format(template, 'string', rowData);
19174
+ const fromTemplate = resolveEntityRowDisplayText(formatted, activeLang);
19175
+ if (fromTemplate) {
19176
+ return fromTemplate;
19030
19177
  }
19031
- return updatedFiles;
19032
19178
  }
19033
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderWidgetService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
19034
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderWidgetService, providedIn: 'root' }); }
19179
+ return '';
19035
19180
  }
19036
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderWidgetService, decorators: [{
19037
- type: Injectable,
19038
- args: [{
19039
- providedIn: 'root',
19040
- }]
19041
- }] });
19181
+ //#endregion
19042
19182
 
19183
+ //#region ---- Imports ----
19184
+ //#endregion
19185
+ //#region ---- Component ----
19043
19186
  class AXPFileUploaderWidgetColumnComponent extends AXPColumnWidgetComponent {
19044
19187
  //#endregion
19045
19188
  //#region ---- Lifecycle ----
19046
19189
  constructor() {
19047
19190
  super();
19191
+ //#region ---- Services & Dependencies ----
19192
+ this.queryExecutor = inject(AXPQueryExecutor);
19193
+ this.entityResolver = inject(AXPEntityDefinitionRegistryService);
19194
+ this.formatService = inject(AXFormatService);
19195
+ this.translation = inject(AXTranslationService);
19196
+ //#endregion
19197
+ //#region ---- View Children ----
19198
+ this.fileListTrigger = viewChild('fileListTrigger', ...(ngDevMode ? [{ debugName: "fileListTrigger" }] : /* istanbul ignore next */ []));
19199
+ this.fileListPopover = viewChild('fileListPopover', ...(ngDevMode ? [{ debugName: "fileListPopover" }] : /* istanbul ignore next */ []));
19200
+ //#endregion
19048
19201
  //#region ---- State ----
19049
19202
  this.fileCount = signal(0, ...(ngDevMode ? [{ debugName: "fileCount" }] : /* istanbul ignore next */ []));
19203
+ this.popoverFiles = signal([], ...(ngDevMode ? [{ debugName: "popoverFiles" }] : /* istanbul ignore next */ []));
19204
+ this.loadStatus = signal('idle', ...(ngDevMode ? [{ debugName: "loadStatus" }] : /* istanbul ignore next */ []));
19205
+ this.loadError = signal(null, ...(ngDevMode ? [{ debugName: "loadError" }] : /* istanbul ignore next */ []));
19206
+ this.isPopoverOpen = signal(false, ...(ngDevMode ? [{ debugName: "isPopoverOpen" }] : /* istanbul ignore next */ []));
19207
+ this.entityDef = signal(null, ...(ngDevMode ? [{ debugName: "entityDef" }] : /* istanbul ignore next */ []));
19050
19208
  this.loadRequestId = 0;
19209
+ this.popoverLoadRequestId = 0;
19051
19210
  //#endregion
19052
- //#region ---- Services & Dependencies ----
19053
- this.fileService = inject(AXPFileUploaderWidgetService);
19054
- this.queryExecutor = inject(AXPQueryExecutor);
19211
+ //#region ---- Computed ----
19212
+ this.entityScope = computed(() => resolveFileUploaderEntityScope(this.options ?? {}, this.rowData, this.path), ...(ngDevMode ? [{ debugName: "entityScope" }] : /* istanbul ignore next */ []));
19213
+ this.popoverTitle = computed(() => {
19214
+ const row = this.rowData;
19215
+ const lang = this.translation.getActiveLang();
19216
+ const titleRaw = get(row, 'title');
19217
+ if (titleRaw != null &&
19218
+ typeof titleRaw === 'object' &&
19219
+ !Array.isArray(titleRaw) &&
19220
+ this.translation.isValidMultiLanguageObject(titleRaw)) {
19221
+ return titleRaw;
19222
+ }
19223
+ const resolved = formatEntityRowDisplayTitle(this.entityDef(), row, this.formatService, lang);
19224
+ if (resolved) {
19225
+ return resolved;
19226
+ }
19227
+ const fallback = this.entityDef()?.formats?.individual;
19228
+ if (fallback != null && fallback !== '') {
19229
+ return resolveEntityRowDisplayText(fallback, lang) || fallback;
19230
+ }
19231
+ return '@document-management:terms.common.file';
19232
+ }, ...(ngDevMode ? [{ debugName: "popoverTitle" }] : /* istanbul ignore next */ []));
19055
19233
  effect(() => {
19056
19234
  const raw = this.rawValue;
19057
19235
  const row = this.rowData;
19058
19236
  const opts = this.options ?? {};
19059
19237
  const path = this.path;
19238
+ const rowId = row?.['id'] != null ? String(row['id']) : undefined;
19239
+ if (rowId !== this.previousRowId) {
19240
+ this.previousRowId = rowId;
19241
+ untracked(() => {
19242
+ this.popoverFiles.set([]);
19243
+ this.loadStatus.set('idle');
19244
+ this.loadError.set(null);
19245
+ this.popoverLoadRequestId++;
19246
+ });
19247
+ }
19060
19248
  const inlineCount = attachmentFieldCount(raw);
19061
19249
  if (inlineCount > 0) {
19062
19250
  untracked(() => this.fileCount.set(inlineCount));
@@ -19069,7 +19257,6 @@ class AXPFileUploaderWidgetColumnComponent extends AXPColumnWidgetComponent {
19069
19257
  }
19070
19258
  const requestId = ++this.loadRequestId;
19071
19259
  untracked(() => {
19072
- console.log('loadFiles for count', entity);
19073
19260
  void this.queryExecutor
19074
19261
  .fetch('FileUploader:LoadFiles', entity)
19075
19262
  .then((result) => {
@@ -19086,51 +19273,102 @@ class AXPFileUploaderWidgetColumnComponent extends AXPColumnWidgetComponent {
19086
19273
  });
19087
19274
  });
19088
19275
  });
19276
+ effect(() => {
19277
+ const scope = this.entityScope();
19278
+ if (!scope?.name?.includes('.')) {
19279
+ this.entityDef.set(null);
19280
+ return;
19281
+ }
19282
+ const [module, name] = scope.name.split('.');
19283
+ void this.entityResolver.resolve(module, name).then((def) => this.entityDef.set(def));
19284
+ });
19089
19285
  }
19090
19286
  //#endregion
19091
19287
  //#region ---- UI Handlers ----
19092
- async openFileList() {
19093
- const entity = resolveFileUploaderEntityScope(this.options ?? {}, this.rowData, this.path);
19094
- await this.fileService.showFileList({
19095
- entity,
19096
- readonly: true,
19097
- });
19288
+ openFileListPopover() {
19289
+ this.openPopoverFromRef(this.fileListTrigger());
19290
+ void this.ensurePopoverFilesLoaded();
19098
19291
  }
19099
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderWidgetColumnComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
19100
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPFileUploaderWidgetColumnComponent, isStandalone: true, selector: "axp-file-uploader-widget-column", inputs: { rawValue: "rawValue", rowData: "rowData" }, usesInheritance: true, ngImport: i0, template: `
19101
- @if (fileCount() > 0) {
19102
- <span
19103
- class="ax-cursor-pointer ax-text-primary ax-underline"
19104
- (click)="openFileList()"
19105
- >
19106
- {{ fileCount() }} {{ '@document-management:file' | translate | async }}
19107
- </span>
19108
- } @else {
19109
- <span class="ax-text-muted">---</span>
19292
+ onPopoverOpenChange(event) {
19293
+ const open = this.coercePopoverOpenEvent(event);
19294
+ this.isPopoverOpen.set(open);
19295
+ if (open) {
19296
+ void this.ensurePopoverFilesLoaded();
19297
+ }
19298
+ }
19299
+ //#endregion
19300
+ //#region ---- Private Methods ----
19301
+ openPopoverFromRef(ref) {
19302
+ const popover = this.fileListPopover();
19303
+ if (popover && ref) {
19304
+ popover.target = ref.nativeElement;
19305
+ popover.open();
19306
+ this.isPopoverOpen.set(true);
19307
+ }
19308
+ }
19309
+ async ensurePopoverFilesLoaded() {
19310
+ if (this.loadStatus() === 'loading') {
19311
+ return;
19312
+ }
19313
+ const inline = normalizeEntityFieldToFileList(this.rawValue);
19314
+ if (inline.length > 0) {
19315
+ this.popoverFiles.set(inline);
19316
+ this.loadStatus.set('ready');
19317
+ this.loadError.set(null);
19318
+ return;
19319
+ }
19320
+ const entity = this.entityScope();
19321
+ if (!entity) {
19322
+ this.popoverFiles.set([]);
19323
+ this.loadStatus.set('ready');
19324
+ return;
19325
+ }
19326
+ if (this.loadStatus() === 'ready' && this.popoverFiles().length > 0) {
19327
+ return;
19328
+ }
19329
+ const requestId = ++this.popoverLoadRequestId;
19330
+ this.loadStatus.set('loading');
19331
+ this.loadError.set(null);
19332
+ try {
19333
+ const result = await this.queryExecutor.fetch('FileUploader:LoadFiles', entity);
19334
+ if (requestId !== this.popoverLoadRequestId) {
19335
+ return;
19336
+ }
19337
+ this.popoverFiles.set(result?.files ?? []);
19338
+ this.loadStatus.set('ready');
19339
+ }
19340
+ catch {
19341
+ if (requestId !== this.popoverLoadRequestId) {
19342
+ return;
19343
+ }
19344
+ const msg = await this.translation.translateAsync('@general:widgets.lookup.column.load-failed');
19345
+ this.loadError.set(msg);
19346
+ this.loadStatus.set('error');
19347
+ this.popoverFiles.set([]);
19348
+ }
19110
19349
  }
19111
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
19350
+ coercePopoverOpenEvent(event) {
19351
+ if (typeof event === 'boolean') {
19352
+ return event;
19353
+ }
19354
+ if (event && typeof event === 'object') {
19355
+ const o = event;
19356
+ if ('detail' in o) {
19357
+ return Boolean(o['detail']);
19358
+ }
19359
+ if ('open' in o) {
19360
+ return Boolean(o['open']);
19361
+ }
19362
+ }
19363
+ return false;
19364
+ }
19365
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderWidgetColumnComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
19366
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: AXPFileUploaderWidgetColumnComponent, isStandalone: true, selector: "axp-file-uploader-widget-column", inputs: { rawValue: "rawValue", rowData: "rowData" }, viewQueries: [{ propertyName: "fileListTrigger", first: true, predicate: ["fileListTrigger"], descendants: true, isSignal: true }, { propertyName: "fileListPopover", first: true, predicate: ["fileListPopover"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "@if (fileCount() > 0) {\n<span class=\"ax-cursor-pointer ax-text-primary ax-underline\" (click)=\"openFileListPopover(); $event.stopPropagation()\"\n #fileListTrigger>\n {{\n '@general:widgets.file-uploader.column.files-count'\n | translate: { params: { count: fileCount() } }\n | async\n }}\n</span>\n} @else {\n<span class=\"ax-text-muted\">---</span>\n}\n\n<ax-popover [openOn]=\"'manual'\" #fileListPopover (openChange)=\"onPopoverOpenChange($event)\">\n <div class=\"ax-lightest-surface dark:ax-dark-surface ax-min-w-[17.5rem] ax-max-w-[26rem] ax-overflow-hidden\">\n <div class=\"ax-border-b ax-border-surface ax-px-4 ax-py-3\">\n <h3 class=\"ax-m-0 ax-text-sm ax-font-semibold ax-leading-normal ax-text-on-lightest-surface\">\n {{ popoverTitle() | translate | async }}\n </h3>\n </div>\n <div class=\"ax-max-h-64 ax-overflow-y-auto ax-px-3 ax-py-3\" style=\"max-height: max(30dvh, 12rem)\">\n @if (loadStatus() === '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 (loadStatus() === 'error') {\n <div class=\"ax-text-danger ax-text-sm\">{{ loadError() }}</div>\n } @else {\n <axp-file-list [files]=\"popoverFiles()\" look=\"links\" linksLayout=\"menu\" [showLabel]=\"false\" [readonly]=\"true\" />\n }\n </div>\n </div>\n</ax-popover>", dependencies: [{ kind: "ngmodule", type: AXTranslationModule }, { 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: AXLoadingModule }, { kind: "component", type: i1$2.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "component", type: AXPFileListComponent, selector: "axp-file-list", inputs: ["readonly", "fileEditable", "enableTitleDescription", "multiple", "look", "titleKey", "showLabel", "linksLayout", "files", "plugins", "excludePlugins", "capabilities"], outputs: ["onRemove", "onRevert", "onRename"] }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
19112
19367
  }
19113
19368
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderWidgetColumnComponent, decorators: [{
19114
19369
  type: Component,
19115
- args: [{
19116
- selector: 'axp-file-uploader-widget-column',
19117
- template: `
19118
- @if (fileCount() > 0) {
19119
- <span
19120
- class="ax-cursor-pointer ax-text-primary ax-underline"
19121
- (click)="openFileList()"
19122
- >
19123
- {{ fileCount() }} {{ '@document-management:file' | translate | async }}
19124
- </span>
19125
- } @else {
19126
- <span class="ax-text-muted">---</span>
19127
- }
19128
- `,
19129
- changeDetection: ChangeDetectionStrategy.OnPush,
19130
- imports: [AXTranslationModule, AsyncPipe],
19131
- inputs: ['rawValue', 'rowData'],
19132
- }]
19133
- }], ctorParameters: () => [] });
19370
+ args: [{ selector: 'axp-file-uploader-widget-column', changeDetection: ChangeDetectionStrategy.OnPush, imports: [AXTranslationModule, AsyncPipe, AXPopoverModule, AXLoadingModule, AXPFileListComponent], inputs: ['rawValue', 'rowData'], template: "@if (fileCount() > 0) {\n<span class=\"ax-cursor-pointer ax-text-primary ax-underline\" (click)=\"openFileListPopover(); $event.stopPropagation()\"\n #fileListTrigger>\n {{\n '@general:widgets.file-uploader.column.files-count'\n | translate: { params: { count: fileCount() } }\n | async\n }}\n</span>\n} @else {\n<span class=\"ax-text-muted\">---</span>\n}\n\n<ax-popover [openOn]=\"'manual'\" #fileListPopover (openChange)=\"onPopoverOpenChange($event)\">\n <div class=\"ax-lightest-surface dark:ax-dark-surface ax-min-w-[17.5rem] ax-max-w-[26rem] ax-overflow-hidden\">\n <div class=\"ax-border-b ax-border-surface ax-px-4 ax-py-3\">\n <h3 class=\"ax-m-0 ax-text-sm ax-font-semibold ax-leading-normal ax-text-on-lightest-surface\">\n {{ popoverTitle() | translate | async }}\n </h3>\n </div>\n <div class=\"ax-max-h-64 ax-overflow-y-auto ax-px-3 ax-py-3\" style=\"max-height: max(30dvh, 12rem)\">\n @if (loadStatus() === '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 (loadStatus() === 'error') {\n <div class=\"ax-text-danger ax-text-sm\">{{ loadError() }}</div>\n } @else {\n <axp-file-list [files]=\"popoverFiles()\" look=\"links\" linksLayout=\"menu\" [showLabel]=\"false\" [readonly]=\"true\" />\n }\n </div>\n </div>\n</ax-popover>" }]
19371
+ }], ctorParameters: () => [], propDecorators: { fileListTrigger: [{ type: i0.ViewChild, args: ['fileListTrigger', { isSignal: true }] }], fileListPopover: [{ type: i0.ViewChild, args: ['fileListPopover', { isSignal: true }] }] } });
19134
19372
 
19135
19373
  var fileUploaderWidgetColumn_component = /*#__PURE__*/Object.freeze({
19136
19374
  __proto__: null,
@@ -19341,6 +19579,7 @@ class AXPFileUploaderWidgetEditComponent extends AXPValueWidgetComponent {
19341
19579
  }
19342
19580
  this.isDirtySignal.set(dirty);
19343
19581
  this.dirtyChangeSubject.next(dirty);
19582
+ this.layoutService.notifyWidgetDirtyChanged();
19344
19583
  }
19345
19584
  //#endregion
19346
19585
  async loadActions() {
@@ -19574,7 +19813,7 @@ class AXPFileUploaderWidgetEditComponent extends AXPValueWidgetComponent {
19574
19813
  (onRename)="handleFileRename($event)"
19575
19814
  ></axp-file-list>
19576
19815
  </div>
19577
- `, isInline: true, styles: [":host{border-color:rgba(var(--ax-comp-editor-border-color))}:host.axp-file-uploader-widget-edit--borderless{border:none!important}.__drag-over{background-color:rgba(var(--ax-sys-color-primary-50),.1);border:2px dashed rgb(var(--ax-sys-color-primary-500));border-radius:.375rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { 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: "component", type: i1.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i1.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items", "closeParentOnClick", "lockOnLoading"], outputs: ["onItemClick"] }, { kind: "directive", type: AXUploaderZoneDirective, selector: "[axUploaderZone]", inputs: ["multiple", "accept", "overlayTemplate", "disableBrowse", "disableDragDrop"], outputs: ["fileChange", "onChanged", "dragEnter", "dragLeave", "dragOver", "onFileUploadComplete", "onFilesUploadComplete"] }, { 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: AXLoadingModule }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i3$3.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXPComponentSlotModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "directive", type: i6.AXTranslatorDirective, selector: "[translate]" }, { kind: "component", type: AXPFileListComponent, selector: "axp-file-list", inputs: ["readonly", "fileEditable", "enableTitleDescription", "multiple", "files", "plugins", "excludePlugins", "capabilities"], outputs: ["onRemove", "onRevert", "onRename"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
19816
+ `, isInline: true, styles: [":host{border-color:rgba(var(--ax-comp-editor-border-color))}:host.axp-file-uploader-widget-edit--borderless{border:none!important}.__drag-over{background-color:rgba(var(--ax-sys-color-primary-50),.1);border:2px dashed rgb(var(--ax-sys-color-primary-500));border-radius:.375rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { 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: "component", type: i1.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i1.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items", "closeParentOnClick", "lockOnLoading"], outputs: ["onItemClick"] }, { kind: "directive", type: AXUploaderZoneDirective, selector: "[axUploaderZone]", inputs: ["multiple", "accept", "fileType", "overlayTemplate", "disableBrowse", "disableDragDrop"], outputs: ["fileChange", "onChanged", "dragEnter", "dragLeave", "dragOver", "onFileUploadComplete", "onFilesUploadComplete"] }, { 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: AXLoadingModule }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i3$3.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXPComponentSlotModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "directive", type: i6.AXTranslatorDirective, selector: "[translate]" }, { kind: "component", type: AXPFileListComponent, selector: "axp-file-list", inputs: ["readonly", "fileEditable", "enableTitleDescription", "multiple", "look", "titleKey", "showLabel", "linksLayout", "files", "plugins", "excludePlugins", "capabilities"], outputs: ["onRemove", "onRevert", "onRename"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
19578
19817
  }
19579
19818
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderWidgetEditComponent, decorators: [{
19580
19819
  type: Component,
@@ -19666,7 +19905,7 @@ class AXPFileUploaderWidgetViewComponent extends AXPValueWidgetComponent {
19666
19905
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderWidgetViewComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
19667
19906
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.9", type: AXPFileUploaderWidgetViewComponent, isStandalone: true, selector: "axp-file-uploader-widget-view", host: { properties: { "class": "this.__class" } }, usesInheritance: true, ngImport: i0, template: `
19668
19907
  <axp-file-list [files]="files()" [readonly]="true"></axp-file-list>
19669
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "ngmodule", type: AXUploaderModule }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPFileListComponent, selector: "axp-file-list", inputs: ["readonly", "fileEditable", "enableTitleDescription", "multiple", "files", "plugins", "excludePlugins", "capabilities"], outputs: ["onRemove", "onRevert", "onRename"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
19908
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "ngmodule", type: AXUploaderModule }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPFileListComponent, selector: "axp-file-list", inputs: ["readonly", "fileEditable", "enableTitleDescription", "multiple", "look", "titleKey", "showLabel", "linksLayout", "files", "plugins", "excludePlugins", "capabilities"], outputs: ["onRemove", "onRevert", "onRename"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
19670
19909
  }
19671
19910
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderWidgetViewComponent, decorators: [{
19672
19911
  type: Component,
@@ -19773,6 +20012,78 @@ const AXPFileUploaderWidget = {
19773
20012
  },
19774
20013
  };
19775
20014
 
20015
+ class AXPFileUploaderWidgetService {
20016
+ constructor() {
20017
+ //#region ---- Services & Dependencies ----
20018
+ this.popupService = inject(AXPopupService);
20019
+ this.translate = inject(AXTranslationService);
20020
+ this.commandExecutor = inject(AXPCommandExecutor);
20021
+ this.queryExecutor = inject(AXPQueryExecutor);
20022
+ }
20023
+ //#endregion
20024
+ //#region ---- Public API ----
20025
+ async showFileList(options) {
20026
+ const entity = options?.entity;
20027
+ let files = options?.files;
20028
+ if (entity) {
20029
+ const filesResult = await this.queryExecutor.fetch('FileUploader:LoadFiles', entity);
20030
+ if (!filesResult) {
20031
+ return undefined;
20032
+ }
20033
+ files = filesResult.files ?? [];
20034
+ }
20035
+ const resolved = {
20036
+ files: files ?? [],
20037
+ readonly: options?.readonly ?? false,
20038
+ multiple: options?.multiple ?? false,
20039
+ accept: options?.accept ?? '*',
20040
+ fileEditable: options?.fileEditable ?? true,
20041
+ maxFileSize: options?.maxFileSize ?? 1024 * 1024 * 10,
20042
+ showAddItemButton: options?.showAddItemButton ?? true,
20043
+ plugins: options?.plugins ?? [],
20044
+ editDialog: options?.editDialog,
20045
+ };
20046
+ const component = await import('./acorex-platform-layout-entity-file-list-popup.component-_yrP5SQe.mjs').then((m) => m.AXPFileListPopupComponent);
20047
+ const result = await this.popupService.open(component, {
20048
+ title: await this.translate.translateAsync('@document-management:terms.common.file'),
20049
+ data: {
20050
+ files: signal(resolved.files),
20051
+ isReadonly: signal(resolved.readonly),
20052
+ multiple: signal(resolved.multiple),
20053
+ accept: signal(resolved.accept),
20054
+ maxFileSize: signal(resolved.maxFileSize),
20055
+ fileEditable: signal(resolved.fileEditable),
20056
+ showAddItemButton: signal(resolved.showAddItemButton),
20057
+ plugins: signal(resolved.plugins),
20058
+ editDialog: signal(resolved.editDialog),
20059
+ },
20060
+ });
20061
+ const updatedFiles = result?.data?.data?.files;
20062
+ if (!updatedFiles) {
20063
+ return undefined;
20064
+ }
20065
+ if (!entity) {
20066
+ return updatedFiles;
20067
+ }
20068
+ const saveResult = await this.commandExecutor.execute('FileUploader:SaveFiles', {
20069
+ ...entity,
20070
+ files: updatedFiles,
20071
+ });
20072
+ if (!saveResult?.success) {
20073
+ return undefined;
20074
+ }
20075
+ return updatedFiles;
20076
+ }
20077
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderWidgetService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
20078
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderWidgetService, providedIn: 'root' }); }
20079
+ }
20080
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPFileUploaderWidgetService, decorators: [{
20081
+ type: Injectable,
20082
+ args: [{
20083
+ providedIn: 'root',
20084
+ }]
20085
+ }] });
20086
+
19776
20087
  var index = /*#__PURE__*/Object.freeze({
19777
20088
  __proto__: null,
19778
20089
  AXPEditFileUploaderCommand: AXPEditFileUploaderCommand,
@@ -21556,7 +21867,7 @@ class AXPPageComponentConverter {
21556
21867
  return empty;
21557
21868
  }
21558
21869
  const tempInjector = Injector.create({
21559
- providers: [{ provide: ComponentType, useClass: ComponentType }],
21870
+ providers: [AXPContextStore, { provide: ComponentType, useClass: ComponentType }],
21560
21871
  parent: this.injector,
21561
21872
  });
21562
21873
  const componentInstance = tempInjector.get(ComponentType);
@@ -21754,6 +22065,20 @@ class AXPPageDetailsConverter extends AXPBaseRelatedEntityConverter {
21754
22065
  if (e.name === 'update-entity') {
21755
22066
  const fn = entityDef?.commands?.update?.execute;
21756
22067
  const result = await fn(context);
22068
+ if (result && typeof result === 'object' && 'success' in result) {
22069
+ const cmd = result;
22070
+ if (!cmd.success) {
22071
+ const errorText = commandMessageTextForError(cmd.message?.text) || 'Failed to update entity';
22072
+ return {
22073
+ success: false,
22074
+ message: cmd.message ?? { text: errorText },
22075
+ };
22076
+ }
22077
+ return {
22078
+ success: true,
22079
+ data: cmd.data ?? result,
22080
+ };
22081
+ }
21757
22082
  return {
21758
22083
  success: true,
21759
22084
  data: result,
@@ -21807,12 +22132,7 @@ class AXPPageDetailsConverter extends AXPBaseRelatedEntityConverter {
21807
22132
  }
21808
22133
  }
21809
22134
  catch (error) {
21810
- return {
21811
- success: false,
21812
- message: {
21813
- text: error,
21814
- },
21815
- };
22135
+ return mapEntityStorageErrorToCommandResult(error);
21816
22136
  }
21817
22137
  };
21818
22138
  }
@@ -22604,6 +22924,20 @@ class AXPMainEntityContentBuilder {
22604
22924
  if (e.name == 'update-entity') {
22605
22925
  const fn = entity?.commands?.update?.execute;
22606
22926
  const result = await fn(context);
22927
+ if (result && typeof result === 'object' && 'success' in result) {
22928
+ const cmd = result;
22929
+ if (!cmd.success) {
22930
+ const errorText = commandMessageTextForError(cmd.message?.text) || 'Failed to update entity';
22931
+ return {
22932
+ success: false,
22933
+ message: cmd.message ?? { text: errorText },
22934
+ };
22935
+ }
22936
+ return {
22937
+ success: true,
22938
+ data: cmd.data ?? result,
22939
+ };
22940
+ }
22607
22941
  return {
22608
22942
  success: true,
22609
22943
  data: result,
@@ -22657,12 +22991,7 @@ class AXPMainEntityContentBuilder {
22657
22991
  }
22658
22992
  }
22659
22993
  catch (error) {
22660
- return {
22661
- success: false,
22662
- message: {
22663
- text: error,
22664
- },
22665
- };
22994
+ return mapEntityStorageErrorToCommandResult(error);
22666
22995
  }
22667
22996
  },
22668
22997
  tabs: [...tabDetailTabs, ...tabListTabs, ...nestedTabListTabs],
@@ -23218,32 +23547,33 @@ const eventDispatchMiddleware = {
23218
23547
  const dispatcher = inject(AXPEntityEventDispatcherService);
23219
23548
  const workflowEventService = inject(AXPWorkflowEventService);
23220
23549
  await next();
23221
- if (ctx.op === 'create') {
23222
- const createdData = ctx.result ? { ...ctx.data, id: ctx.result } : ctx.data;
23223
- await dispatcher.dispatchInserted(ctx.entityName, { refType: ctx.entityName, data: createdData });
23224
- // Dispatch workflow refresh event to trigger entity list refresh
23225
- workflowEventService.dispatch(AXPRefreshEvent({
23226
- entity: ctx.entityName,
23227
- }));
23228
- }
23229
- else if (ctx.op === 'update') {
23230
- await dispatcher.dispatchUpdated(ctx.entityName, {
23231
- refType: ctx.entityName,
23232
- data: ctx.result,
23233
- changes: ctx.locals.get('changes'),
23234
- });
23235
- // Dispatch workflow refresh event to trigger entity list refresh
23236
- workflowEventService.dispatch(AXPRefreshEvent({
23237
- entity: ctx.entityName,
23238
- }));
23550
+ try {
23551
+ if (ctx.op === 'create') {
23552
+ const createdData = ctx.result ? { ...ctx.data, id: ctx.result } : ctx.data;
23553
+ await dispatcher.dispatchInserted(ctx.entityName, { refType: ctx.entityName, data: createdData });
23554
+ workflowEventService.dispatch(AXPRefreshEvent({
23555
+ entity: ctx.entityName,
23556
+ }));
23557
+ }
23558
+ else if (ctx.op === 'update') {
23559
+ await dispatcher.dispatchUpdated(ctx.entityName, {
23560
+ refType: ctx.entityName,
23561
+ data: ctx.result,
23562
+ changes: ctx.locals.get('changes'),
23563
+ });
23564
+ workflowEventService.dispatch(AXPRefreshEvent({
23565
+ entity: ctx.entityName,
23566
+ }));
23567
+ }
23568
+ else if (ctx.op === 'delete') {
23569
+ await dispatcher.dispatchDeleted(ctx.entityName, { refType: ctx.entityName, data: ctx.result ?? ctx.previous });
23570
+ workflowEventService.dispatch(AXPRefreshEvent({
23571
+ entity: ctx.entityName,
23572
+ }));
23573
+ }
23239
23574
  }
23240
- else if (ctx.op === 'delete') {
23241
- // For delete, prefer previous entity if available
23242
- await dispatcher.dispatchDeleted(ctx.entityName, { refType: ctx.entityName, data: ctx.result ?? ctx.previous });
23243
- // Dispatch workflow refresh event to trigger entity list refresh
23244
- workflowEventService.dispatch(AXPRefreshEvent({
23245
- entity: ctx.entityName,
23246
- }));
23575
+ catch {
23576
+ // Persist already succeeded; event dispatch must not block the write result.
23247
23577
  }
23248
23578
  },
23249
23579
  };
@@ -23904,8 +24234,12 @@ const AXPCrudModifier = {
23904
24234
  if (!command?.update) {
23905
24235
  command.update = {
23906
24236
  execute: async (data) => {
23907
- console.log('update', ctx.module.get() + '.' + ctx.name.get(), data);
23908
- return await dataService.updateOne(data.id, data);
24237
+ const entityKey = ctx.module.get() + '.' + ctx.name.get();
24238
+ const { id, ...patch } = data ?? {};
24239
+ if (id == null || id === '') {
24240
+ throw new Error(`Update requires entity id (${entityKey})`);
24241
+ }
24242
+ return await dataService.updateOne(id, patch);
23909
24243
  },
23910
24244
  };
23911
24245
  }
@@ -23989,34 +24323,6 @@ class AXPEntitySearchDefinitionProvider {
23989
24323
  }
23990
24324
  }
23991
24325
 
23992
- /**
23993
- * Error type that can be thrown by middlewares to abort the chain with a structured payload.
23994
- */
23995
- class AXPMiddlewareAbortError extends Error {
23996
- constructor(message, options = {}) {
23997
- super(message);
23998
- this.message = message;
23999
- this.options = options;
24000
- this.name = 'AXPMiddlewareAbortError';
24001
- }
24002
- toResponse() {
24003
- return {
24004
- success: false,
24005
- error: {
24006
- code: this.options.code,
24007
- message: this.message,
24008
- status: this.options.status,
24009
- details: this.options.details,
24010
- },
24011
- };
24012
- }
24013
- }
24014
- /** Type guard for AXPMiddlewareAbortError */
24015
- function isAXPMiddlewareAbortError(error) {
24016
- return error instanceof AXPMiddlewareAbortError;
24017
- }
24018
- //#endregion
24019
-
24020
24326
  const AXP_ENTITY_STORAGE_BACKEND = new InjectionToken('AXP_ENTITY_STORAGE_BACKEND');
24021
24327
  const AXP_ENTITY_STORAGE_MIDDLEWARE = new InjectionToken('AXP_ENTITY_STORAGE_MIDDLEWARE');
24022
24328
 
@@ -25305,5 +25611,5 @@ var getEntityDetails_query = /*#__PURE__*/Object.freeze({
25305
25611
  * Generated bundle index. Do not edit.
25306
25612
  */
25307
25613
 
25308
- 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, 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, mapLegacyEntityDataSelectorOptions, mergeForeignKeyFieldIntoCreateActions, normalizeEntityDataSelectorOptions, normalizeEntityFieldToFileList, normalizeEntityListPersistenceMode, normalizeListPaging, normalizeLookupDisplayTemplate, persistedAttachments, provideEntity, resolveEntityPluginDetailPageOrder, resolveFileUploaderEditDialog, resolveFileUploaderEntityScope, resolveLookupDisplayField, resolveLookupDisplayTemplate, restoreEntityListExpandedRows, runEntityQuery, searchResultDescriptionMiddleware, searchResultDescriptionMiddlewareProvider, shouldLoadEntityListStateFromStorage, shouldResetEntityListStateOnRouteEntry };
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 };
25309
25615
  //# sourceMappingURL=acorex-platform-layout-entity.mjs.map