@acorex/platform 20.6.0-next.21 → 20.6.0-next.23

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.
@@ -1,20 +1,20 @@
1
1
  import { AXToastService } from '@acorex/components/toast';
2
2
  import * as i6 from '@acorex/core/translation';
3
3
  import { AXTranslationService, AXTranslationModule } from '@acorex/core/translation';
4
- import * as i4 from '@acorex/platform/common';
4
+ import * as i4$1 from '@acorex/platform/common';
5
5
  import { AXPSettingService, AXPFilterOperatorMiddlewareService, AXPEntityCommandScope, getEntityInfo, AXPRefreshEvent, AXPReloadEvent, AXPCommonSettings, AXPCleanNestedFilters, AXPWorkflowNavigateAction, AXPToastAction, AXP_SEARCH_DEFINITION_PROVIDER } from '@acorex/platform/common';
6
- import * as i1$2 from '@acorex/platform/core';
7
- import { AXPDeviceService, AXPBroadcastEventService, resolveActionLook, AXPExpressionEvaluatorService, AXPDistributedEventListenerService, AXPPlatformScope, extractValue, setSmart, getChangedPaths, AXPSystemActionType } from '@acorex/platform/core';
6
+ import * as i1$1 from '@acorex/platform/core';
7
+ import { AXPDeviceService, AXPBroadcastEventService, resolveActionLook, AXPExpressionEvaluatorService, AXPDistributedEventListenerService, AXPPlatformScope, AXHighlightService, extractValue, setSmart, getChangedPaths, AXPSystemActionType } from '@acorex/platform/core';
8
8
  import * as i0 from '@angular/core';
9
- import { InjectionToken, inject, Injector, runInInjectionContext, Injectable, input, viewChild, signal, ElementRef, ChangeDetectionStrategy, Component, ApplicationRef, EnvironmentInjector, createComponent, computed, afterNextRender, ViewEncapsulation, ChangeDetectorRef, effect, viewChildren, linkedSignal, untracked, HostBinding, ViewChild, NgModule } from '@angular/core';
9
+ import { InjectionToken, inject, Injector, runInInjectionContext, Injectable, input, viewChild, signal, ElementRef, ChangeDetectionStrategy, Component, ApplicationRef, EnvironmentInjector, createComponent, computed, effect, Input, afterNextRender, ViewEncapsulation, ChangeDetectorRef, viewChildren, linkedSignal, untracked, HostBinding, ViewChild, NgModule } from '@angular/core';
10
10
  import { Subject, takeUntil } from 'rxjs';
11
11
  import { AXPLayoutBuilderService } from '@acorex/platform/layout/builder';
12
- import { merge, castArray, get, cloneDeep, set, orderBy, isNil, isEqual, isEmpty, sortBy } from 'lodash-es';
12
+ import { merge, castArray, get, cloneDeep, set, orderBy, isNil, isEmpty, isEqual, sortBy } from 'lodash-es';
13
13
  import { AXPSessionService, AXPAuthGuard } from '@acorex/platform/auth';
14
14
  import { Router, RouterModule, ROUTES } from '@angular/router';
15
15
  import * as i3 from '@acorex/components/button';
16
16
  import { AXButtonModule } from '@acorex/components/button';
17
- import * as i1 from '@acorex/components/loading';
17
+ import * as i4 from '@acorex/components/loading';
18
18
  import { AXLoadingModule } from '@acorex/components/loading';
19
19
  import * as i2 from '@acorex/components/popover';
20
20
  import { AXPopoverModule } from '@acorex/components/popover';
@@ -33,31 +33,33 @@ import { AXDialogService } from '@acorex/components/dialog';
33
33
  import { AXLoadingDialogService } from '@acorex/components/loading-dialog';
34
34
  import { AXPopupService } from '@acorex/components/popup';
35
35
  import { AXPlatform } from '@acorex/core/platform';
36
- import * as i2$1 from '@acorex/components/decorators';
36
+ import * as i2$1 from '@acorex/components/check-box';
37
+ import { AXCheckBoxModule } from '@acorex/components/check-box';
38
+ import * as i3$2 from '@acorex/components/decorators';
37
39
  import { AXDecoratorModule } from '@acorex/components/decorators';
38
40
  import { AXBasePageComponent } from '@acorex/components/page';
39
- import * as i3$2 from '@acorex/components/search-box';
41
+ import * as i4$2 from '@acorex/components/search-box';
40
42
  import { AXSearchBoxModule, AXSearchBoxComponent } from '@acorex/components/search-box';
41
- import * as i4$1 from '@acorex/components/skeleton';
43
+ import * as i5$1 from '@acorex/components/skeleton';
42
44
  import { AXSkeletonModule } from '@acorex/components/skeleton';
43
45
  import { AXTreeViewComponent } from '@acorex/components/tree-view';
46
+ import { AXPStateMessageComponent, AXPDataSelectorService, AXPWidgetPropertyViewerComponent } from '@acorex/platform/layout/components';
47
+ import * as i1 from '@angular/forms';
48
+ import { FormsModule } from '@angular/forms';
44
49
  import * as i2$2 from '@acorex/components/badge';
45
50
  import { AXBadgeModule } from '@acorex/components/badge';
46
- import * as i5$1 from '@acorex/components/form';
51
+ import * as i5$2 from '@acorex/components/form';
47
52
  import { AXFormModule } from '@acorex/components/form';
53
+ import * as i6$1 from '@acorex/components/tag-box';
54
+ import { AXTagBoxModule, AXTagBoxComponent } from '@acorex/components/tag-box';
48
55
  import { AXValidationModule } from '@acorex/core/validation';
49
56
  import { AXP_DISABLED_PROPERTY, AXP_ALLOW_CLEAR_PROPERTY, AXP_DATA_PATH_PROPERTY, AXP_DATA_PROPERTY_GROUP, AXP_ALLOW_MULTIPLE_PROPERTY, AXP_NAME_PROPERTY, AXPFileUploaderWidgetService } from '@acorex/platform/layout/widgets';
50
- import * as i4$2 from '@acorex/components/dropdown';
57
+ import * as i4$3 from '@acorex/components/dropdown';
51
58
  import { AXDropdownModule } from '@acorex/components/dropdown';
52
- import * as i1$1 from '@angular/forms';
53
- import { FormsModule } from '@angular/forms';
54
59
  import * as i7 from '@acorex/components/select-box';
55
60
  import { AXSelectBoxModule } from '@acorex/components/select-box';
56
61
  import * as i2$3 from '@acorex/components/text-box';
57
62
  import { AXTextBoxModule, AXTextBoxComponent } from '@acorex/components/text-box';
58
- import * as i6$1 from '@acorex/components/tag-box';
59
- import { AXTagBoxComponent, AXTagBoxModule } from '@acorex/components/tag-box';
60
- import { AXPDataSelectorService, AXPWidgetPropertyViewerComponent } from '@acorex/platform/layout/components';
61
63
  import { transform, isEqual as isEqual$1 } from 'lodash';
62
64
 
63
65
  function ensureListActions(ctx) {
@@ -1034,7 +1036,7 @@ class AXPCreateEntityCommand {
1034
1036
  const entityRef = await this.entityService.resolve(moduleName, entityName);
1035
1037
  let dialogRef;
1036
1038
  try {
1037
- let chain = this.entityForm.entity(`${moduleName}.${entityName}`).create(input.__metadata__);
1039
+ let chain = this.entityForm.entity(`${moduleName}.${entityName}`).create(data);
1038
1040
  chain.actions((actions) => {
1039
1041
  actions.cancel('@general:actions.cancel.title');
1040
1042
  actions.submit('@general:actions.create.title');
@@ -1679,7 +1681,7 @@ class AXPEntityDetailPopoverComponent {
1679
1681
  return importantProperties;
1680
1682
  }
1681
1683
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPEntityDetailPopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1682
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", type: AXPEntityDetailPopoverComponent, isStandalone: true, selector: "axp-entity-detail-popover", inputs: { entity: { classPropertyName: "entity", publicName: "entity", isSignal: true, isRequired: true, transformFunction: null }, entityId: { classPropertyName: "entityId", publicName: "entityId", isSignal: true, isRequired: true, transformFunction: null }, textField: { classPropertyName: "textField", publicName: "textField", isSignal: true, isRequired: false, transformFunction: null }, valueField: { classPropertyName: "valueField", publicName: "valueField", isSignal: true, isRequired: false, transformFunction: null }, item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "detailPopover", first: true, predicate: ["detailPopover"], descendants: true, isSignal: true }], ngImport: i0, template: "<ax-popover [openOn]=\"'manual'\" #detailPopover (openChange)=\"onDetailPopoverOpenChange($event)\">\n <div class=\"ax-lightest-surface ax-border ax-rounded-lg ax-shadow-lg ax-p-4 ax-min-w-[400px]\">\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 @if (entityDetails()?.entityData?.[textField()]) {\n {{ entityDetails()?.entityData[textField()] }}\n } @else {\n {{ item()?.[textField()] }}\n }\n </h3>\n </div>\n @if (isLoadingDetails()) {\n <div class=\"ax-flex ax-items-center ax-justify-center ax-py-8\">\n <ax-loading>Loading details...</ax-loading>\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 getEntityPropertiesWithWidgets(); track item.name) {\n <div class=\"ax-flex ax-justify-between ax-items-center\">\n <span class=\"ax-text-sm ax-font-medium\">{{ item.title | translate | async }}:</span>\n <div class=\"ax-flex-1 ax-ml-2 ax-max-w-48\">\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=\"Open Details\" (click)=\"navigateToDetails()\"> </ax-button>\n </div>\n }\n </div>\n</ax-popover>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i3.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", "disabled", "offsetX", "offsetY", "target", "placement", "content", "openOn", "closeOn", "hasBackdrop", "openAfter", "closeAfter", "backdropClass", "panelClass", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i3$1.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: i3$1.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i1.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1684
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", type: AXPEntityDetailPopoverComponent, isStandalone: true, selector: "axp-entity-detail-popover", inputs: { entity: { classPropertyName: "entity", publicName: "entity", isSignal: true, isRequired: true, transformFunction: null }, entityId: { classPropertyName: "entityId", publicName: "entityId", isSignal: true, isRequired: true, transformFunction: null }, textField: { classPropertyName: "textField", publicName: "textField", isSignal: true, isRequired: false, transformFunction: null }, valueField: { classPropertyName: "valueField", publicName: "valueField", isSignal: true, isRequired: false, transformFunction: null }, item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "detailPopover", first: true, predicate: ["detailPopover"], descendants: true, isSignal: true }], ngImport: i0, template: "<ax-popover [openOn]=\"'manual'\" #detailPopover (openChange)=\"onDetailPopoverOpenChange($event)\">\n <div class=\"ax-lightest-surface ax-border ax-rounded-lg ax-shadow-lg ax-p-4 ax-min-w-[400px]\">\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 @if (entityDetails()?.entityData?.[textField()]) {\n {{ entityDetails()?.entityData[textField()] }}\n } @else {\n {{ item()?.[textField()] }}\n }\n </h3>\n </div>\n @if (isLoadingDetails()) {\n <div class=\"ax-flex ax-items-center ax-justify-center ax-py-8\">\n <ax-loading>Loading details...</ax-loading>\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 getEntityPropertiesWithWidgets(); track item.name) {\n <div class=\"ax-flex ax-justify-between ax-items-center\">\n <span class=\"ax-text-sm ax-font-medium\">{{ item.title | translate | async }}:</span>\n <div class=\"ax-flex-1 ax-ml-2 ax-max-w-48\">\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=\"Open Details\" (click)=\"navigateToDetails()\"> </ax-button>\n </div>\n }\n </div>\n</ax-popover>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i3.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", "disabled", "offsetX", "offsetY", "target", "placement", "content", "openOn", "closeOn", "hasBackdrop", "openAfter", "closeAfter", "backdropClass", "panelClass", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXPWidgetCoreModule }, { kind: "component", type: i3$1.AXPWidgetContainerComponent, selector: "axp-widgets-container", inputs: ["context", "functions"], outputs: ["onContextChanged"] }, { kind: "directive", type: i3$1.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i4.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1683
1685
  }
1684
1686
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPEntityDetailPopoverComponent, decorators: [{
1685
1687
  type: Component,
@@ -4323,24 +4325,48 @@ class AXPPageListConverter extends AXPBaseRelatedEntityConverter {
4323
4325
  message: {
4324
4326
  code: 'ACTION_NOT_FOUND',
4325
4327
  text: `Action ${commandName} not found`,
4326
- }
4328
+ },
4327
4329
  };
4328
4330
  }
4329
- await context.workflowService.execute(commandName, {
4330
- entity: getEntityInfo(entityDef).source,
4331
- entityInfo: {
4332
- name: entityDef.name,
4333
- module: entityDef.module,
4334
- title: entityDef.title,
4335
- parentKey: entityDef.parentKey,
4336
- source: `${entityDef.module}.${entityDef.name}`,
4337
- },
4338
- data: action.scope == AXPEntityCommandScope.Selected
4339
- ? executeContext
4340
- : action.options?.['process']?.data || null,
4341
- options: action.options,
4342
- metadata: action.metadata,
4343
- });
4331
+ if (context.commandService.exists(commandName)) {
4332
+ // check options for evaluation
4333
+ await context.commandService.execute(commandName, {
4334
+ __context__: {
4335
+ entity: getEntityInfo(entityDef).source,
4336
+ entityInfo: {
4337
+ name: entityDef.name,
4338
+ module: entityDef.module,
4339
+ title: entityDef.title,
4340
+ parentKey: entityDef.parentKey,
4341
+ source: `${entityDef.module}.${entityDef.name}`,
4342
+ },
4343
+ data: action.scope == AXPEntityCommandScope.Selected
4344
+ ? executeContext
4345
+ : action.options?.['process']?.data || null,
4346
+ options: action.options,
4347
+ metadata: action.metadata,
4348
+ },
4349
+ options: action.options,
4350
+ metadata: action.metadata,
4351
+ });
4352
+ }
4353
+ else {
4354
+ await context.workflowService.execute(commandName, {
4355
+ entity: getEntityInfo(entityDef).source,
4356
+ entityInfo: {
4357
+ name: entityDef.name,
4358
+ module: entityDef.module,
4359
+ title: entityDef.title,
4360
+ parentKey: entityDef.parentKey,
4361
+ source: `${entityDef.module}.${entityDef.name}`,
4362
+ },
4363
+ data: action.scope == AXPEntityCommandScope.Selected
4364
+ ? executeContext
4365
+ : action.options?.['process']?.data || null,
4366
+ options: action.options,
4367
+ metadata: action.metadata,
4368
+ });
4369
+ }
4344
4370
  return { success: true };
4345
4371
  }
4346
4372
  catch (error) {
@@ -4367,6 +4393,7 @@ class AXPPageListConverter extends AXPBaseRelatedEntityConverter {
4367
4393
  entity: relatedEntity.entity,
4368
4394
  showEntityActions: false,
4369
4395
  actions: evaluatedActions,
4396
+ includeColumns: relatedEntity.columns,
4370
4397
  },
4371
4398
  },
4372
4399
  ],
@@ -5274,7 +5301,7 @@ class AXPLayoutAdapterFactory {
5274
5301
  const title = await dependencies.expressionEvaluator.evaluate(entity.interfaces?.master?.single?.title, rootContext);
5275
5302
  return title;
5276
5303
  }
5277
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLayoutAdapterFactory, deps: [{ token: AXPRelatedEntityConverterFactory }, { token: AXPMainEntityContentBuilder }, { token: AXPLayoutAdapterBuilder }, { token: i4.AXPFilterOperatorMiddlewareService }], target: i0.ɵɵFactoryTarget.Injectable }); }
5304
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLayoutAdapterFactory, deps: [{ token: AXPRelatedEntityConverterFactory }, { token: AXPMainEntityContentBuilder }, { token: AXPLayoutAdapterBuilder }, { token: i4$1.AXPFilterOperatorMiddlewareService }], target: i0.ɵɵFactoryTarget.Injectable }); }
5278
5305
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLayoutAdapterFactory, providedIn: 'root' }); }
5279
5306
  }
5280
5307
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLayoutAdapterFactory, decorators: [{
@@ -5282,7 +5309,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
5282
5309
  args: [{
5283
5310
  providedIn: 'root',
5284
5311
  }]
5285
- }], ctorParameters: () => [{ type: AXPRelatedEntityConverterFactory }, { type: AXPMainEntityContentBuilder }, { type: AXPLayoutAdapterBuilder }, { type: i4.AXPFilterOperatorMiddlewareService }] });
5312
+ }], ctorParameters: () => [{ type: AXPRelatedEntityConverterFactory }, { type: AXPMainEntityContentBuilder }, { type: AXPLayoutAdapterBuilder }, { type: i4$1.AXPFilterOperatorMiddlewareService }] });
5286
5313
 
5287
5314
  const AXPLayoutDetailsViewRouteResolver = async (route, state, entityResolver = inject(AXPEntityDefinitionRegistryService), expressionEvaluator = inject(AXPExpressionEvaluatorService), session = inject(AXPSessionService), formatService = inject(AXFormatService), workflowService = inject(AXPWorkflowService), commandService = inject(AXPCommandService), layoutAdapterFactory = inject(AXPLayoutAdapterFactory)) => {
5288
5315
  const moduleName = route.parent?.paramMap.get('module');
@@ -5897,13 +5924,13 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
5897
5924
  //#region ---- Services & Dependencies ----
5898
5925
  this.categoryTreeService = inject(AXPCategoryTreeService);
5899
5926
  this.translationService = inject(AXTranslationService);
5927
+ this.highlightService = inject(AXHighlightService);
5900
5928
  //#endregion
5901
5929
  //#region ---- Properties (Set by popup service) ----
5902
5930
  this.entityKey = signal('', ...(ngDevMode ? [{ debugName: "entityKey" }] : []));
5903
5931
  this.textField = signal('title', ...(ngDevMode ? [{ debugName: "textField" }] : []));
5904
5932
  this.valueField = signal('id', ...(ngDevMode ? [{ debugName: "valueField" }] : []));
5905
5933
  this.allowMultiple = signal(false, ...(ngDevMode ? [{ debugName: "allowMultiple" }] : []));
5906
- this.selectionBehavior = signal('leaf', ...(ngDevMode ? [{ debugName: "selectionBehavior" }] : []));
5907
5934
  this.selectedValues = signal([], ...(ngDevMode ? [{ debugName: "selectedValues" }] : []));
5908
5935
  this.searchPlaceholder = signal('@general:terms.interface.category.search.placeholder', ...(ngDevMode ? [{ debugName: "searchPlaceholder" }] : []));
5909
5936
  this.excludedNodeId = signal(undefined, ...(ngDevMode ? [{ debugName: "excludedNodeId" }] : [])); // Node ID to disable
@@ -5922,6 +5949,7 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
5922
5949
  this.noRecordsTitle = signal('', ...(ngDevMode ? [{ debugName: "noRecordsTitle" }] : []));
5923
5950
  this.currentSearchTerm = null;
5924
5951
  this.isRefreshing = false;
5952
+ this.isUpdatingSelection = false; // Flag to prevent recursive updates during batch operations
5925
5953
  /**
5926
5954
  * Computed property to check if we should show the "no search results" empty state.
5927
5955
  * Returns true when search is active, not searching, and no results found.
@@ -5941,6 +5969,7 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
5941
5969
  this.relevantNodeIds = new Set(); // For search filtering
5942
5970
  this.expandedNodesBeforeSearch = []; // Store expanded nodes before search to restore after
5943
5971
  this.nodesExpandedDuringSearch = []; // Track nodes we expanded during search
5972
+ this.isInitializing = false; // Flag to track if we're in initialization phase
5944
5973
  /** Datasource callback for tree-view component. */
5945
5974
  this.datasource = async (id) => {
5946
5975
  if (!this.treeData || !this.treeConfig) {
@@ -5992,15 +6021,42 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
5992
6021
  this.markNodeAsSelectedIfNeeded(node);
5993
6022
  });
5994
6023
  // After children load, programmatically select pre-selected nodes
6024
+ // Use a small delay to ensure nodes are fully added to tree structure
5995
6025
  const treeComponent = this.tree();
5996
6026
  if (treeComponent) {
5997
- const selectedIds = this.selectedNodeIds();
5998
- childNodes.forEach((node) => {
5999
- const nodeId = String(node['id'] ?? '');
6000
- if (nodeId && selectedIds.includes(nodeId) && nodeId !== 'all') {
6001
- treeComponent.selectNode(nodeId);
6002
- }
6003
- });
6027
+ // Use setTimeout to ensure nodes are in tree structure before selecting
6028
+ setTimeout(() => {
6029
+ const selectedIds = this.selectedNodeIds();
6030
+ childNodes.forEach((node) => {
6031
+ const nodeId = String(node['id'] ?? '');
6032
+ if (nodeId && selectedIds.includes(nodeId) && nodeId !== 'all') {
6033
+ try {
6034
+ // Try to find and select the node
6035
+ const treeNode = treeComponent.findNode(nodeId);
6036
+ if (treeNode) {
6037
+ treeComponent.selectNode(nodeId);
6038
+ }
6039
+ else {
6040
+ // If node not found, try again after a short delay (might still be loading)
6041
+ setTimeout(() => {
6042
+ try {
6043
+ const retryNode = treeComponent.findNode(nodeId);
6044
+ if (retryNode) {
6045
+ treeComponent.selectNode(nodeId);
6046
+ }
6047
+ }
6048
+ catch {
6049
+ // Node still not found, will be selected when it loads
6050
+ }
6051
+ }, 50);
6052
+ }
6053
+ }
6054
+ catch (error) {
6055
+ // Node might not be in tree yet, will be selected when it loads
6056
+ }
6057
+ }
6058
+ });
6059
+ }, 10);
6004
6060
  }
6005
6061
  return childNodes;
6006
6062
  };
@@ -6019,6 +6075,7 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6019
6075
  return;
6020
6076
  }
6021
6077
  this.loading.set(true);
6078
+ this.isInitializing = true; // Mark that we're in initialization phase
6022
6079
  try {
6023
6080
  this.treeConfig = {
6024
6081
  entityKey: this.entityKey(),
@@ -6028,6 +6085,7 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6028
6085
  this.treeData = await this.categoryTreeService.initializeCategoryTree(this.treeConfig);
6029
6086
  if (!this.treeData) {
6030
6087
  this.loading.set(false);
6088
+ this.isInitializing = false;
6031
6089
  return;
6032
6090
  }
6033
6091
  // Get parentKey from entity definition
@@ -6035,13 +6093,22 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6035
6093
  this.treeConfig.parentKey = this.treeData.categoryEntityDef.parentKey;
6036
6094
  }
6037
6095
  // Initialize selected nodes and load their data into cache
6096
+ // Wait for tree to be ready before syncing selection
6097
+ await new Promise((resolve) => setTimeout(resolve, 100));
6038
6098
  await this.updateSelectedNodes(this.selectedValues());
6099
+ // After initial sync, wait a bit more and sync again to catch any nodes that loaded late
6100
+ await new Promise((resolve) => setTimeout(resolve, 200));
6101
+ const finalSelectedIds = this.selectedNodeIds();
6102
+ if (finalSelectedIds.length > 0) {
6103
+ await this.syncSelectionWithTree(finalSelectedIds);
6104
+ }
6039
6105
  }
6040
6106
  catch (error) {
6041
6107
  console.error('Error loading entity definition:', error);
6042
6108
  }
6043
6109
  finally {
6044
6110
  this.loading.set(false);
6111
+ this.isInitializing = false; // Mark initialization as complete
6045
6112
  }
6046
6113
  }
6047
6114
  //#endregion
@@ -6069,11 +6136,11 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6069
6136
  return;
6070
6137
  }
6071
6138
  // Prevent concurrent searches
6072
- if (this.isSearching() && this.currentSearchTerm !== null) {
6073
- if (this.currentSearchTerm === searchTerm) {
6074
- return;
6075
- }
6076
- }
6139
+ // if (this.isSearching() && this.currentSearchTerm !== null) {
6140
+ // if (this.currentSearchTerm === searchTerm) {
6141
+ // return;
6142
+ // }
6143
+ // }
6077
6144
  this.searchValue.set(event.value ?? '');
6078
6145
  this.currentSearchTerm = searchTerm;
6079
6146
  const treeComponent = this.tree();
@@ -6113,8 +6180,12 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6113
6180
  }
6114
6181
  if (!searchResults || searchResults.length === 0) {
6115
6182
  this.matchingNodeIds.clear();
6183
+ this.relevantNodeIds.clear();
6116
6184
  this.searchResultCount.set(0);
6117
6185
  await this.updateTranslatedMessages(0, searchTerm);
6186
+ // Clear highlighting and reload tree to show all nodes
6187
+ this.highlightService.clear();
6188
+ await treeComponent.reloadData();
6118
6189
  return;
6119
6190
  }
6120
6191
  // Store matching node IDs from search results
@@ -6167,11 +6238,25 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6167
6238
  }
6168
6239
  }
6169
6240
  }
6241
+ // Step 5: Apply highlighting after tree is rendered
6242
+ // Use setTimeout to ensure DOM is updated after tree reload
6243
+ setTimeout(() => {
6244
+ if (this.searchValue().trim()) {
6245
+ this.highlightService.highlight('ax-tree-view .ax-truncate', this.searchValue().trim());
6246
+ }
6247
+ }, 100);
6170
6248
  }
6171
6249
  catch (error) {
6172
6250
  console.error('Error searching categories:', error);
6173
6251
  this.matchingNodeIds.clear();
6252
+ this.relevantNodeIds.clear();
6174
6253
  this.searchResultCount.set(0);
6254
+ this.highlightService.clear();
6255
+ // Reload tree to clear any filters
6256
+ const treeComponent = this.tree();
6257
+ if (treeComponent) {
6258
+ await treeComponent.reloadData();
6259
+ }
6175
6260
  }
6176
6261
  finally {
6177
6262
  if (this.currentSearchTerm === searchTerm) {
@@ -6438,6 +6523,8 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6438
6523
  this.isSearching.set(false);
6439
6524
  this.matchingNodeIds.clear();
6440
6525
  this.relevantNodeIds.clear();
6526
+ // Clear highlighting
6527
+ this.highlightService.clear();
6441
6528
  const treeComponent = this.tree();
6442
6529
  if (!treeComponent) {
6443
6530
  this.expandedNodesBeforeSearch = [];
@@ -6477,11 +6564,22 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6477
6564
  if (nodeData && typeof nodeData === 'object' && nodeData !== null && !Array.isArray(nodeData)) {
6478
6565
  this.nodeDataCache.set(nodeId, nodeData);
6479
6566
  }
6567
+ // NOTE: We do NOT call loadAndSelectChildrenRecursively here
6568
+ // The recursive selection should only happen when user explicitly selects a node via checkbox
6569
+ // (handled in handleCheckboxChange). This prevents intermediate parent states from
6570
+ // triggering unwanted sibling selections.
6480
6571
  }
6481
6572
  async onSelectionChange(event) {
6573
+ // Don't process during initialization or batch updates - let those handle it
6574
+ if (this.isInitializing || this.isUpdatingSelection) {
6575
+ return;
6576
+ }
6482
6577
  // Update selected node IDs from the tree component's selection state
6578
+ // The tree component with intermediate-nested behavior automatically manages parent states
6483
6579
  const selectedNodes = event.selectedNodes || [];
6484
6580
  const selectedIds = selectedNodes.map((node) => String(node['id'] ?? '')).filter((id) => id && id !== 'all');
6581
+ // Sync with tree component's selection state
6582
+ // This includes parents that are automatically selected when all children are selected
6485
6583
  this.selectedNodeIds.set(selectedIds);
6486
6584
  // Cache node data for all selected nodes
6487
6585
  selectedNodes.forEach((node) => {
@@ -6534,182 +6632,925 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6534
6632
  isMatchingNode(nodeId) {
6535
6633
  return this.matchingNodeIds.has(nodeId);
6536
6634
  }
6537
- //#endregion
6538
- async updateSelectedNodes(selectedIds) {
6539
- if (!selectedIds || selectedIds.length === 0) {
6540
- this.selectedNodeIds.set([]);
6541
- return;
6635
+ /**
6636
+ * Checks if a node is currently selected
6637
+ */
6638
+ isNodeSelected(nodeId) {
6639
+ if (!nodeId || nodeId === 'all') {
6640
+ return false;
6542
6641
  }
6543
- const ids = selectedIds.filter((id) => id && id !== 'all');
6544
- this.selectedNodeIds.set(ids);
6545
- // Fetch node data for pre-selected items that aren't in the cache
6546
- // This ensures getSelectedItems() can retrieve them even if their branches aren't expanded
6547
- await this.loadMissingNodeData(ids);
6548
- // Note: Selection state is maintained in selectedNodeIds signal
6549
- // The tree component will sync selection state when nodes are loaded via datasource
6550
- // We mark nodes as selected in the datasource callback by setting node.selected = true
6642
+ const id = String(nodeId);
6643
+ return this.selectedNodeIds().includes(id);
6551
6644
  }
6552
6645
  /**
6553
- * Loads node data for IDs that are selected but not yet in the cache.
6554
- * This is critical for pre-selected values in collapsed branches.
6646
+ * Handles checkbox change event to toggle node selection
6647
+ * In multiple mode: recursively selects/deselects children
6648
+ * Parent states are handled automatically by tree component's intermediate-nested behavior
6555
6649
  */
6556
- async loadMissingNodeData(selectedIds) {
6557
- if (!this.treeData || !this.treeConfig || selectedIds.length === 0) {
6650
+ async handleCheckboxChange(nodeId, checked) {
6651
+ if (!nodeId || nodeId === 'all') {
6558
6652
  return;
6559
6653
  }
6560
- // Find IDs that are selected but not in cache
6561
- const missingIds = selectedIds.filter((id) => !this.nodeDataCache.has(id));
6562
- if (missingIds.length === 0) {
6654
+ const id = String(nodeId);
6655
+ const treeComponent = this.tree();
6656
+ if (!treeComponent) {
6657
+ return;
6658
+ }
6659
+ if (checked) {
6660
+ // Select the node and recursively select all its children
6661
+ await this.selectNodeAndChildren(id);
6662
+ }
6663
+ else {
6664
+ // Deselect the node and recursively deselect all its children
6665
+ await this.deselectNodeAndChildren(id);
6666
+ }
6667
+ // Note: Parent states (select/intermediate) are handled automatically by the tree component
6668
+ // with selectionBehavior: 'intermediate-nested'. We don't need to manually update them.
6669
+ }
6670
+ /**
6671
+ * Selects a node and recursively selects all its children
6672
+ */
6673
+ async selectNodeAndChildren(nodeId) {
6674
+ const treeComponent = this.tree();
6675
+ if (!treeComponent) {
6563
6676
  return;
6564
6677
  }
6678
+ // Set flag to prevent recursive updates during batch operation
6679
+ this.isUpdatingSelection = true;
6565
6680
  try {
6566
- const valueField = this.treeConfig.valueField || 'id';
6567
- const categoryEntityQueryFunc = this.treeData.categoryEntityQueryFunc;
6568
- if (!categoryEntityQueryFunc) {
6569
- return;
6681
+ // Collect all nodes to select (node + all descendants)
6682
+ const nodesToSelect = new Set([nodeId]);
6683
+ await this.collectAllDescendants(nodeId, nodesToSelect);
6684
+ // Batch update selectedNodeIds
6685
+ const currentSelected = this.selectedNodeIds();
6686
+ const newSelected = [...currentSelected];
6687
+ for (const id of nodesToSelect) {
6688
+ if (!newSelected.includes(id)) {
6689
+ newSelected.push(id);
6690
+ }
6570
6691
  }
6571
- // Query for missing nodes by their IDs
6572
- const event = {
6573
- ...this.treeData.basicQueryEvent,
6574
- filter: {
6575
- filters: missingIds.map((id) => ({
6576
- field: valueField,
6577
- value: id,
6578
- operator: { type: 'equal' },
6579
- })),
6580
- logic: 'or',
6581
- },
6582
- };
6583
- const res = await categoryEntityQueryFunc(event);
6584
- if (res?.items) {
6585
- // Cache the fetched node data
6586
- res.items.forEach((item) => {
6587
- const itemId = String(item[valueField] ?? '');
6588
- if (itemId) {
6589
- this.nodeDataCache.set(itemId, item);
6692
+ this.selectedNodeIds.set(newSelected);
6693
+ // Batch select in tree component (with small delays to prevent glitches)
6694
+ for (const id of nodesToSelect) {
6695
+ try {
6696
+ const node = treeComponent.findNode(id);
6697
+ if (node) {
6698
+ treeComponent.selectNode(id);
6699
+ // Small delay to prevent overwhelming the tree component
6700
+ await new Promise((resolve) => setTimeout(resolve, 5));
6590
6701
  }
6591
- });
6702
+ }
6703
+ catch {
6704
+ // Node might not be in tree yet
6705
+ }
6592
6706
  }
6593
6707
  }
6594
- catch (error) {
6595
- console.error('Error loading missing node data:', error);
6708
+ finally {
6709
+ this.isUpdatingSelection = false;
6596
6710
  }
6597
6711
  }
6598
6712
  /**
6599
- * Marks nodes as selected in the tree structure based on selectedNodeIds.
6600
- * This ensures pre-selected nodes appear selected when the tree is rendered.
6713
+ * Collects all descendant node IDs recursively
6601
6714
  */
6602
- markNodesAsSelected(node) {
6603
- const selectedIds = this.selectedNodeIds();
6604
- if (selectedIds.length === 0) {
6715
+ async collectAllDescendants(parentId, collection) {
6716
+ if (!this.treeData || !this.treeConfig || !parentId || parentId === 'all') {
6605
6717
  return;
6606
6718
  }
6607
- // Mark the node itself if it's selected
6608
- this.markNodeAsSelectedIfNeeded(node);
6609
- // Recursively mark children
6610
- const nodeChildren = node['children'];
6611
- if (nodeChildren) {
6612
- nodeChildren.forEach((child) => {
6613
- this.markNodesAsSelected(child);
6614
- });
6719
+ try {
6720
+ const childNodes = await this.datasource(parentId);
6721
+ if (!childNodes || childNodes.length === 0) {
6722
+ return;
6723
+ }
6724
+ for (const childNode of childNodes) {
6725
+ const childId = String(childNode['id'] ?? '');
6726
+ if (childId && childId !== 'all' && !collection.has(childId)) {
6727
+ collection.add(childId);
6728
+ // Cache node data
6729
+ const nodeData = childNode['data'];
6730
+ if (nodeData && typeof nodeData === 'object') {
6731
+ this.nodeDataCache.set(childId, nodeData);
6732
+ }
6733
+ else if (childNode && typeof childNode === 'object') {
6734
+ const valueField = this.treeConfig.valueField || 'id';
6735
+ const textField = this.treeConfig.textField || 'title';
6736
+ const dataObj = {
6737
+ [valueField]: childId,
6738
+ [textField]: childNode['title'] ?? '',
6739
+ ...childNode,
6740
+ };
6741
+ this.nodeDataCache.set(childId, dataObj);
6742
+ }
6743
+ // Recursively collect descendants
6744
+ await this.collectAllDescendants(childId, collection);
6745
+ }
6746
+ }
6615
6747
  }
6616
- }
6617
- /**
6618
- * Marks a single node as selected if it's in the selectedNodeIds list.
6619
- */
6620
- markNodeAsSelectedIfNeeded(node) {
6621
- const selectedIds = this.selectedNodeIds();
6622
- const nodeId = String(node['id'] ?? '');
6623
- if (nodeId && selectedIds.includes(nodeId)) {
6624
- node['selected'] = true;
6748
+ catch (error) {
6749
+ console.error(`Error collecting descendants for node ${parentId}:`, error);
6625
6750
  }
6626
6751
  }
6627
6752
  /**
6628
- * Marks a node and its children as disabled if the node ID matches the excluded ID.
6753
+ * Deselects a node and recursively deselects all its children
6629
6754
  */
6630
- markNodeAsDisabled(node, excludedId) {
6631
- const nodeId = String(node['id'] ?? '');
6632
- if (nodeId === excludedId) {
6633
- node['disabled'] = true;
6755
+ async deselectNodeAndChildren(nodeId) {
6756
+ const treeComponent = this.tree();
6757
+ if (!treeComponent) {
6758
+ return;
6634
6759
  }
6635
- // Recursively mark children
6636
- const nodeChildren = node['children'];
6637
- if (nodeChildren) {
6638
- nodeChildren.forEach((child) => {
6639
- this.markNodeAsDisabled(child, excludedId);
6640
- });
6760
+ // Set flag to prevent recursive updates during batch operation
6761
+ this.isUpdatingSelection = true;
6762
+ try {
6763
+ // Collect all nodes to deselect (node + all descendants)
6764
+ const nodesToDeselect = new Set([nodeId]);
6765
+ await this.collectAllDescendants(nodeId, nodesToDeselect);
6766
+ // Batch update selectedNodeIds
6767
+ const currentSelected = this.selectedNodeIds();
6768
+ const newSelected = currentSelected.filter((id) => !nodesToDeselect.has(id));
6769
+ this.selectedNodeIds.set(newSelected);
6770
+ // Batch deselect in tree component (with small delays to prevent glitches)
6771
+ for (const id of nodesToDeselect) {
6772
+ try {
6773
+ const node = treeComponent.findNode(id);
6774
+ if (node) {
6775
+ treeComponent.deselectNode(id);
6776
+ // Small delay to prevent overwhelming the tree component
6777
+ await new Promise((resolve) => setTimeout(resolve, 5));
6778
+ }
6779
+ }
6780
+ catch {
6781
+ // Node might not be in tree
6782
+ }
6783
+ }
6784
+ }
6785
+ finally {
6786
+ this.isUpdatingSelection = false;
6641
6787
  }
6642
6788
  }
6643
6789
  /**
6644
- * Processes root node: marks excluded as disabled, marks selected, and syncs selection with tree component
6790
+ * Recursively deselects all children of a parent node
6645
6791
  */
6646
- processRootNode(rootNode) {
6647
- const excludedId = this.excludedNodeId();
6648
- if (excludedId) {
6649
- this.markNodeAsDisabled(rootNode, excludedId);
6792
+ async loadAndDeselectChildrenRecursively(parentId) {
6793
+ if (!this.treeData || !this.treeConfig || !parentId || parentId === 'all') {
6794
+ return;
6650
6795
  }
6651
- this.markNodesAsSelected(rootNode);
6652
- // Sync selection with tree component after a short delay to ensure tree is rendered
6653
6796
  const treeComponent = this.tree();
6654
- if (treeComponent) {
6655
- setTimeout(() => {
6656
- const selectedIds = this.selectedNodeIds();
6657
- selectedIds.forEach((id) => {
6658
- if (id && id !== 'all') {
6659
- treeComponent.selectNode(id);
6660
- }
6661
- });
6662
- }, 0);
6797
+ if (!treeComponent) {
6798
+ return;
6663
6799
  }
6664
- }
6665
- /**
6666
- * Processes child nodes: marks excluded as disabled, marks selected, and syncs selection
6667
- */
6668
- processChildNodes(childNodes) {
6669
- const excludedId = this.excludedNodeId();
6670
- const selectedIds = this.selectedNodeIds();
6671
- childNodes.forEach((node) => {
6672
- const nodeId = String(node['id'] ?? '');
6673
- if (excludedId && nodeId === excludedId) {
6674
- node['disabled'] = true;
6800
+ try {
6801
+ // Load children using datasource
6802
+ const childNodes = await this.datasource(parentId);
6803
+ if (!childNodes || childNodes.length === 0) {
6804
+ return;
6675
6805
  }
6676
- if (nodeId && selectedIds.includes(nodeId)) {
6677
- node['selected'] = true;
6806
+ const childIdsToDeselect = [];
6807
+ // Collect all child IDs
6808
+ for (const childNode of childNodes) {
6809
+ const childId = String(childNode['id'] ?? '');
6810
+ if (childId && childId !== 'all') {
6811
+ childIdsToDeselect.push(childId);
6812
+ }
6678
6813
  }
6679
- });
6680
- // Sync selection with tree component
6681
- const treeComponent = this.tree();
6682
- if (treeComponent) {
6683
- setTimeout(() => {
6684
- childNodes.forEach((node) => {
6685
- const nodeId = String(node['id'] ?? '');
6686
- if (nodeId && selectedIds.includes(nodeId) && nodeId !== 'all') {
6687
- treeComponent.selectNode(nodeId);
6688
- }
6689
- });
6690
- }, 0);
6814
+ // Remove children from selectedNodeIds
6815
+ const currentSelected = this.selectedNodeIds();
6816
+ const updatedSelected = currentSelected.filter((id) => !childIdsToDeselect.includes(id));
6817
+ this.selectedNodeIds.set(updatedSelected);
6818
+ // Deselect in tree component
6819
+ for (const childId of childIdsToDeselect) {
6820
+ try {
6821
+ treeComponent.deselectNode(childId);
6822
+ }
6823
+ catch {
6824
+ // Node might not be in tree
6825
+ }
6826
+ }
6827
+ // Recursively deselect children of each child
6828
+ await Promise.all(childIdsToDeselect.map((childId) => this.loadAndDeselectChildrenRecursively(childId)));
6829
+ }
6830
+ catch (error) {
6831
+ console.error(`Error deselecting children for node ${parentId}:`, error);
6691
6832
  }
6692
6833
  }
6693
6834
  /**
6694
- * Caches node data from items array
6835
+ * Updates parent states based on children selection (select/intermediate)
6836
+ * Called after a node is selected/deselected to update parent checkbox states
6695
6837
  */
6696
- cacheNodeData(items) {
6697
- items.forEach((item) => {
6698
- const itemId = String(item[this.treeConfig.valueField || 'id'] ?? '');
6699
- if (itemId && typeof item === 'object' && item !== null) {
6700
- this.nodeDataCache.set(itemId, item);
6838
+ async updateParentStates(changedNodeId) {
6839
+ if (!this.treeData || !this.treeConfig || !this.allowMultiple()) {
6840
+ return;
6841
+ }
6842
+ const parentKey = this.treeData.categoryEntityDef?.parentKey;
6843
+ if (!parentKey) {
6844
+ return; // No parent key means flat structure
6845
+ }
6846
+ const treeComponent = this.tree();
6847
+ if (!treeComponent) {
6848
+ return;
6849
+ }
6850
+ // Start from the changed node's parent and work up the tree
6851
+ const processedParents = new Set();
6852
+ let currentId = changedNodeId;
6853
+ // Process all parents up to root
6854
+ while (currentId && currentId !== 'all') {
6855
+ const nodeData = this.nodeDataCache.get(currentId);
6856
+ if (!nodeData) {
6857
+ break;
6701
6858
  }
6702
- });
6703
- }
6704
- /**
6705
- * Caches node data from tree nodes
6706
- */
6707
- cacheNodeDataFromNodes(nodes) {
6708
- nodes.forEach((node) => {
6709
- const nodeId = String(node['id'] ?? '');
6710
- const nodeData = node['data'];
6711
- if (nodeData && nodeId && typeof nodeData === 'object') {
6712
- this.nodeDataCache.set(nodeId, nodeData);
6859
+ const parentId = nodeData[parentKey];
6860
+ const parentIdStr = String(parentId);
6861
+ if (!parentId || parentId === 'all' || parentId === currentId || processedParents.has(parentIdStr)) {
6862
+ break;
6863
+ }
6864
+ processedParents.add(parentIdStr);
6865
+ // Load all children of this parent
6866
+ try {
6867
+ const childNodes = await this.datasource(parentIdStr);
6868
+ if (!childNodes || childNodes.length === 0) {
6869
+ currentId = parentIdStr;
6870
+ continue;
6871
+ }
6872
+ // Get current selection state (updated after each parent change)
6873
+ const currentSelected = this.selectedNodeIds();
6874
+ const selectedSet = new Set(currentSelected);
6875
+ let selectedCount = 0;
6876
+ let totalCount = 0;
6877
+ // Count selected children
6878
+ for (const childNode of childNodes) {
6879
+ const childId = String(childNode['id'] ?? '');
6880
+ if (childId && childId !== 'all') {
6881
+ totalCount++;
6882
+ if (selectedSet.has(childId)) {
6883
+ selectedCount++;
6884
+ }
6885
+ }
6886
+ }
6887
+ // Update parent selection state
6888
+ const isParentSelected = currentSelected.includes(parentIdStr);
6889
+ if (totalCount > 0) {
6890
+ if (selectedCount === totalCount) {
6891
+ // All children selected - select parent
6892
+ if (!isParentSelected) {
6893
+ this.selectedNodeIds.set([...currentSelected, parentIdStr]);
6894
+ try {
6895
+ treeComponent.selectNode(parentIdStr);
6896
+ }
6897
+ catch {
6898
+ // Parent might not be in tree
6899
+ }
6900
+ }
6901
+ }
6902
+ else if (selectedCount > 0) {
6903
+ // Some children selected - parent should be in intermediate state
6904
+ // The tree component handles intermediate state automatically via selectionBehavior
6905
+ // We just need to ensure parent is not fully selected
6906
+ if (isParentSelected) {
6907
+ // Deselect parent to show intermediate state
6908
+ this.selectedNodeIds.set(currentSelected.filter((id) => id !== parentIdStr));
6909
+ try {
6910
+ treeComponent.deselectNode(parentIdStr);
6911
+ }
6912
+ catch {
6913
+ // Parent might not be in tree
6914
+ }
6915
+ }
6916
+ }
6917
+ else {
6918
+ // No children selected - deselect parent
6919
+ if (isParentSelected) {
6920
+ this.selectedNodeIds.set(currentSelected.filter((id) => id !== parentIdStr));
6921
+ try {
6922
+ treeComponent.deselectNode(parentIdStr);
6923
+ }
6924
+ catch {
6925
+ // Parent might not be in tree
6926
+ }
6927
+ }
6928
+ }
6929
+ }
6930
+ // Cache parent data if not already cached
6931
+ if (!this.nodeDataCache.has(parentIdStr)) {
6932
+ const parentData = await this.fetchItemById(parentIdStr);
6933
+ if (parentData) {
6934
+ this.nodeDataCache.set(parentIdStr, parentData);
6935
+ }
6936
+ }
6937
+ currentId = parentIdStr;
6938
+ }
6939
+ catch (error) {
6940
+ console.error(`Error updating parent state for ${parentIdStr}:`, error);
6941
+ break;
6942
+ }
6943
+ }
6944
+ }
6945
+ /**
6946
+ * Recursively loads and selects all children of a parent node using the datasource
6947
+ * This method directly calls datasource without expanding/collapsing nodes to avoid UI glitches
6948
+ */
6949
+ async loadAndSelectChildrenRecursively(parentId) {
6950
+ if (!this.treeData || !this.treeConfig || !parentId || parentId === 'all') {
6951
+ return;
6952
+ }
6953
+ const treeComponent = this.tree();
6954
+ if (!treeComponent) {
6955
+ return;
6956
+ }
6957
+ try {
6958
+ // Directly call datasource to get children data without expanding the node
6959
+ // This avoids UI glitches from expand/collapse operations
6960
+ // If node has no children, datasource will return empty array
6961
+ const childNodes = await this.datasource(parentId);
6962
+ if (!childNodes || childNodes.length === 0) {
6963
+ return; // No children to process
6964
+ }
6965
+ // Collect all child IDs to add to selectedNodeIds
6966
+ const childIdsToSelect = [];
6967
+ // Process all children and cache their data
6968
+ for (const childNode of childNodes) {
6969
+ const childId = String(childNode['id'] ?? '');
6970
+ if (childId && childId !== 'all') {
6971
+ childIdsToSelect.push(childId);
6972
+ // Cache node data for getSelectedItems
6973
+ // Try to get data from 'data' property first, then fallback to node itself
6974
+ let nodeData = childNode['data'];
6975
+ // If no data property, try to extract data from the node
6976
+ if (!nodeData || typeof nodeData !== 'object') {
6977
+ // Create a data object from the node properties
6978
+ const valueField = this.treeConfig.valueField || 'id';
6979
+ const textField = this.treeConfig.textField || 'title';
6980
+ nodeData = {
6981
+ [valueField]: childId,
6982
+ [textField]: childNode['title'] ?? '',
6983
+ ...childNode,
6984
+ };
6985
+ }
6986
+ // Cache the node data
6987
+ if (nodeData && typeof nodeData === 'object') {
6988
+ this.nodeDataCache.set(childId, nodeData);
6989
+ }
6990
+ }
6991
+ }
6992
+ if (childIdsToSelect.length === 0) {
6993
+ return; // No valid children to select
6994
+ }
6995
+ // Update selectedNodeIds to include all children
6996
+ const currentSelected = this.selectedNodeIds();
6997
+ const newSelected = [...currentSelected];
6998
+ let hasNewSelections = false;
6999
+ for (const childId of childIdsToSelect) {
7000
+ if (!newSelected.includes(childId)) {
7001
+ newSelected.push(childId);
7002
+ hasNewSelections = true;
7003
+ }
7004
+ }
7005
+ if (hasNewSelections) {
7006
+ this.selectedNodeIds.set(newSelected);
7007
+ }
7008
+ // Try to select children in tree component if they're already loaded
7009
+ // If not loaded yet, they'll be selected when the tree loads them (via markNodeAsSelectedIfNeeded)
7010
+ for (const childId of childIdsToSelect) {
7011
+ try {
7012
+ // Only try to select if node exists in tree (might not be loaded if parent isn't expanded)
7013
+ const node = treeComponent.findNode(childId);
7014
+ if (node) {
7015
+ treeComponent.selectNode(childId);
7016
+ }
7017
+ }
7018
+ catch {
7019
+ // If selection fails, it's okay - we've already added to selectedNodeIds
7020
+ // The tree will sync selection when the node is loaded via datasource callback
7021
+ }
7022
+ }
7023
+ // Recursively load and select children of each child
7024
+ // Use Promise.all for parallel processing to improve performance
7025
+ await Promise.all(childIdsToSelect.map((childId) => this.loadAndSelectChildrenRecursively(childId)));
7026
+ }
7027
+ catch (error) {
7028
+ console.error(`Error loading children for node ${parentId}:`, error);
7029
+ }
7030
+ }
7031
+ //#endregion
7032
+ async updateSelectedNodes(selectedIds) {
7033
+ if (!selectedIds || selectedIds.length === 0) {
7034
+ this.selectedNodeIds.set([]);
7035
+ return;
7036
+ }
7037
+ const ids = selectedIds.filter((id) => id && id !== 'all');
7038
+ // Set flag to prevent recursive updates during initialization
7039
+ this.isUpdatingSelection = true;
7040
+ try {
7041
+ // Fetch node data for pre-selected items that aren't in the cache
7042
+ await this.loadMissingNodeData(ids);
7043
+ // Collect all nodes to select (pre-selected + all their descendants)
7044
+ const allNodesToSelect = new Set(ids);
7045
+ for (const nodeId of ids) {
7046
+ await this.collectAllDescendants(nodeId, allNodesToSelect);
7047
+ }
7048
+ // Set all selected nodes at once
7049
+ this.selectedNodeIds.set(Array.from(allNodesToSelect));
7050
+ // Expand parents of pre-selected nodes so they're visible in the tree
7051
+ await this.expandParentsOfSelectedNodes(Array.from(allNodesToSelect));
7052
+ // Sync selection with tree component - wait for tree to be ready
7053
+ await this.syncSelectionWithTree(Array.from(allNodesToSelect));
7054
+ }
7055
+ finally {
7056
+ this.isUpdatingSelection = false;
7057
+ }
7058
+ }
7059
+ /**
7060
+ * Updates all parent states based on their children's selection
7061
+ * Used during initialization to ensure proper parent states (select/intermediate)
7062
+ */
7063
+ async updateAllParentStates(selectedIds) {
7064
+ if (!this.treeData || !this.treeConfig || !this.allowMultiple() || selectedIds.length === 0) {
7065
+ return;
7066
+ }
7067
+ const parentKey = this.treeData.categoryEntityDef?.parentKey;
7068
+ if (!parentKey) {
7069
+ return;
7070
+ }
7071
+ const treeComponent = this.tree();
7072
+ if (!treeComponent) {
7073
+ return;
7074
+ }
7075
+ const selectedSet = new Set(selectedIds);
7076
+ const processedParents = new Set();
7077
+ // Collect all unique parent IDs
7078
+ for (const selectedId of selectedIds) {
7079
+ if (selectedId === 'all') {
7080
+ continue;
7081
+ }
7082
+ const nodeData = this.nodeDataCache.get(selectedId);
7083
+ if (!nodeData) {
7084
+ continue;
7085
+ }
7086
+ let currentId = selectedId;
7087
+ while (currentId && currentId !== 'all') {
7088
+ const currentNodeData = this.nodeDataCache.get(currentId);
7089
+ if (!currentNodeData) {
7090
+ break;
7091
+ }
7092
+ const parentId = currentNodeData[parentKey];
7093
+ const parentIdStr = String(parentId);
7094
+ if (!parentId || parentId === 'all' || parentId === currentId || processedParents.has(parentIdStr)) {
7095
+ break;
7096
+ }
7097
+ processedParents.add(parentIdStr);
7098
+ currentId = parentIdStr;
7099
+ }
7100
+ }
7101
+ // Process parents multiple times to handle cascading parent selections
7102
+ // (when a parent is selected, its parent might also need to be selected)
7103
+ let hasChanges = true;
7104
+ let iterations = 0;
7105
+ const maxIterations = 10; // Prevent infinite loops
7106
+ while (hasChanges && iterations < maxIterations) {
7107
+ iterations++;
7108
+ hasChanges = false;
7109
+ // Update selectedSet with current selectedNodeIds
7110
+ const currentSelected = this.selectedNodeIds();
7111
+ currentSelected.forEach((id) => selectedSet.add(id));
7112
+ // Update each parent's state
7113
+ for (const parentId of processedParents) {
7114
+ try {
7115
+ const childNodes = await this.datasource(parentId);
7116
+ if (!childNodes || childNodes.length === 0) {
7117
+ continue;
7118
+ }
7119
+ let selectedCount = 0;
7120
+ let totalCount = 0;
7121
+ for (const childNode of childNodes) {
7122
+ const childId = String(childNode['id'] ?? '');
7123
+ if (childId && childId !== 'all') {
7124
+ totalCount++;
7125
+ if (selectedSet.has(childId)) {
7126
+ selectedCount++;
7127
+ }
7128
+ }
7129
+ }
7130
+ const isParentSelected = currentSelected.includes(parentId);
7131
+ if (totalCount > 0) {
7132
+ if (selectedCount === totalCount) {
7133
+ // All children selected - select parent
7134
+ if (!isParentSelected) {
7135
+ this.selectedNodeIds.set([...currentSelected, parentId]);
7136
+ selectedSet.add(parentId);
7137
+ hasChanges = true;
7138
+ try {
7139
+ treeComponent.selectNode(parentId);
7140
+ }
7141
+ catch {
7142
+ // Parent might not be in tree
7143
+ }
7144
+ }
7145
+ }
7146
+ else if (selectedCount > 0) {
7147
+ // Some children selected - parent should be in intermediate state
7148
+ if (isParentSelected) {
7149
+ // Deselect parent to show intermediate state
7150
+ this.selectedNodeIds.set(currentSelected.filter((id) => id !== parentId));
7151
+ selectedSet.delete(parentId);
7152
+ hasChanges = true;
7153
+ try {
7154
+ treeComponent.deselectNode(parentId);
7155
+ }
7156
+ catch {
7157
+ // Parent might not be in tree
7158
+ }
7159
+ }
7160
+ }
7161
+ else {
7162
+ // No children selected - deselect parent
7163
+ if (isParentSelected) {
7164
+ this.selectedNodeIds.set(currentSelected.filter((id) => id !== parentId));
7165
+ selectedSet.delete(parentId);
7166
+ hasChanges = true;
7167
+ try {
7168
+ treeComponent.deselectNode(parentId);
7169
+ }
7170
+ catch {
7171
+ // Parent might not be in tree
7172
+ }
7173
+ }
7174
+ }
7175
+ }
7176
+ }
7177
+ catch (error) {
7178
+ console.error(`Error updating parent state for ${parentId}:`, error);
7179
+ }
7180
+ }
7181
+ }
7182
+ }
7183
+ /**
7184
+ * Expands parents of selected nodes so they're visible when popup opens
7185
+ */
7186
+ async expandParentsOfSelectedNodes(selectedIds) {
7187
+ if (!this.treeData || !this.treeConfig || selectedIds.length === 0) {
7188
+ return;
7189
+ }
7190
+ const parentKey = this.treeData.categoryEntityDef?.parentKey;
7191
+ if (!parentKey) {
7192
+ return; // No parent key means flat structure
7193
+ }
7194
+ const treeComponent = this.tree();
7195
+ if (!treeComponent) {
7196
+ return;
7197
+ }
7198
+ // Collect all unique parent IDs that need to be expanded
7199
+ const parentsToExpand = new Set();
7200
+ for (const selectedId of selectedIds) {
7201
+ if (selectedId === 'all') {
7202
+ continue;
7203
+ }
7204
+ const nodeData = this.nodeDataCache.get(selectedId);
7205
+ if (!nodeData) {
7206
+ continue;
7207
+ }
7208
+ const parentId = nodeData[parentKey];
7209
+ if (parentId && parentId !== 'all') {
7210
+ const parentIdStr = String(parentId);
7211
+ parentsToExpand.add(parentIdStr);
7212
+ // Also expand grandparents, etc.
7213
+ let currentParentId = parentIdStr;
7214
+ while (currentParentId) {
7215
+ const parentData = this.nodeDataCache.get(currentParentId);
7216
+ if (!parentData) {
7217
+ break;
7218
+ }
7219
+ const grandParentId = parentData[parentKey];
7220
+ if (grandParentId && grandParentId !== 'all' && grandParentId !== currentParentId) {
7221
+ parentsToExpand.add(String(grandParentId));
7222
+ currentParentId = String(grandParentId);
7223
+ }
7224
+ else {
7225
+ break;
7226
+ }
7227
+ }
7228
+ }
7229
+ }
7230
+ // Expand all parents (starting from root to leaves)
7231
+ const sortedParents = Array.from(parentsToExpand).sort((a, b) => {
7232
+ // Simple sort - expand root nodes first
7233
+ return a.localeCompare(b);
7234
+ });
7235
+ for (const parentId of sortedParents) {
7236
+ try {
7237
+ if (!treeComponent.isNodeExpanded(parentId)) {
7238
+ await treeComponent.expandNode(parentId);
7239
+ // Small delay to ensure children are loaded
7240
+ await new Promise((resolve) => setTimeout(resolve, 50));
7241
+ }
7242
+ }
7243
+ catch {
7244
+ // Parent might not be in tree yet, ignore
7245
+ }
7246
+ }
7247
+ }
7248
+ /**
7249
+ * Selects parents whose all children are selected
7250
+ * This ensures that when all children of a parent are selected, the parent also appears selected
7251
+ */
7252
+ async selectParentsWithAllChildrenSelected(selectedIds) {
7253
+ if (!this.treeData || !this.treeConfig || selectedIds.length === 0) {
7254
+ return;
7255
+ }
7256
+ const parentKey = this.treeData.categoryEntityDef?.parentKey;
7257
+ if (!parentKey) {
7258
+ return; // No parent key means flat structure
7259
+ }
7260
+ // Use current selectedNodeIds signal value (may have been updated)
7261
+ const currentSelectedIds = this.selectedNodeIds();
7262
+ const selectedSet = new Set(currentSelectedIds);
7263
+ const processedParents = new Set();
7264
+ const parentsToSelect = new Set();
7265
+ // For each selected node, check its parent
7266
+ for (const selectedId of currentSelectedIds) {
7267
+ if (selectedId === 'all') {
7268
+ continue;
7269
+ }
7270
+ // Get parent ID from cached data
7271
+ const nodeData = this.nodeDataCache.get(selectedId);
7272
+ if (!nodeData) {
7273
+ continue;
7274
+ }
7275
+ const parentId = nodeData[parentKey];
7276
+ if (!parentId || parentId === 'all' || processedParents.has(String(parentId))) {
7277
+ continue;
7278
+ }
7279
+ const parentIdStr = String(parentId);
7280
+ processedParents.add(parentIdStr);
7281
+ // Load all children of this parent
7282
+ try {
7283
+ const childNodes = await this.datasource(parentIdStr);
7284
+ if (!childNodes || childNodes.length === 0) {
7285
+ continue;
7286
+ }
7287
+ // Check if all children are selected
7288
+ let allChildrenSelected = true;
7289
+ let hasChildren = false;
7290
+ for (const childNode of childNodes) {
7291
+ const childId = String(childNode['id'] ?? '');
7292
+ if (childId && childId !== 'all') {
7293
+ hasChildren = true;
7294
+ if (!selectedSet.has(childId)) {
7295
+ allChildrenSelected = false;
7296
+ break;
7297
+ }
7298
+ }
7299
+ }
7300
+ // If parent has children and all are selected, mark parent for selection
7301
+ if (hasChildren && allChildrenSelected) {
7302
+ parentsToSelect.add(parentIdStr);
7303
+ // Cache parent data if not already cached
7304
+ if (!this.nodeDataCache.has(parentIdStr)) {
7305
+ const parentData = await this.fetchItemById(parentIdStr);
7306
+ if (parentData) {
7307
+ this.nodeDataCache.set(parentIdStr, parentData);
7308
+ }
7309
+ }
7310
+ }
7311
+ }
7312
+ catch (error) {
7313
+ console.error(`Error checking parent ${parentIdStr}:`, error);
7314
+ }
7315
+ }
7316
+ // Add parents to selectedNodeIds
7317
+ if (parentsToSelect.size > 0) {
7318
+ const currentSelected = this.selectedNodeIds();
7319
+ const newSelected = [...currentSelected];
7320
+ let hasNewSelections = false;
7321
+ for (const parentId of parentsToSelect) {
7322
+ if (!newSelected.includes(parentId)) {
7323
+ newSelected.push(parentId);
7324
+ hasNewSelections = true;
7325
+ }
7326
+ }
7327
+ if (hasNewSelections) {
7328
+ this.selectedNodeIds.set(newSelected);
7329
+ // Recursively check parents of these parents
7330
+ const updatedSelected = this.selectedNodeIds();
7331
+ await this.selectParentsWithAllChildrenSelected(updatedSelected);
7332
+ }
7333
+ }
7334
+ }
7335
+ /**
7336
+ * Syncs selection state with the tree component
7337
+ * Handles cases where nodes might not be in the tree structure yet
7338
+ */
7339
+ async syncSelectionWithTree(selectedIds) {
7340
+ const treeComponent = this.tree();
7341
+ if (!treeComponent || selectedIds.length === 0) {
7342
+ return;
7343
+ }
7344
+ // Wait for tree to be fully initialized and rendered
7345
+ await new Promise((resolve) => setTimeout(resolve, 300));
7346
+ // Try multiple times to sync selection (nodes might load progressively)
7347
+ for (let attempt = 0; attempt < 3; attempt++) {
7348
+ let syncedCount = 0;
7349
+ selectedIds.forEach((id) => {
7350
+ if (id && id !== 'all') {
7351
+ try {
7352
+ const node = treeComponent.findNode(id);
7353
+ if (node) {
7354
+ // Node exists in tree, select it
7355
+ treeComponent.selectNode(id);
7356
+ syncedCount++;
7357
+ }
7358
+ }
7359
+ catch {
7360
+ // Node not in tree yet - will be selected when loaded via datasource callback
7361
+ }
7362
+ }
7363
+ });
7364
+ // If all nodes are synced, we're done
7365
+ if (syncedCount === selectedIds.length) {
7366
+ break;
7367
+ }
7368
+ // Wait a bit before next attempt
7369
+ if (attempt < 2) {
7370
+ await new Promise((resolve) => setTimeout(resolve, 100));
7371
+ }
7372
+ }
7373
+ // After syncing, read back from tree component to get any parents that were automatically selected
7374
+ // (due to intermediate-nested behavior when all children are selected)
7375
+ if (this.allowMultiple()) {
7376
+ try {
7377
+ const treeSelectedNodes = treeComponent.getSelectedNodes();
7378
+ const treeSelectedIds = treeSelectedNodes
7379
+ .map((node) => String(node['id'] ?? ''))
7380
+ .filter((id) => id && id !== 'all');
7381
+ // Update selectedNodeIds to match tree component's state (includes auto-selected parents)
7382
+ this.selectedNodeIds.set(treeSelectedIds);
7383
+ }
7384
+ catch {
7385
+ // Tree component might not be ready yet
7386
+ }
7387
+ }
7388
+ // Note: Selection state is maintained in selectedNodeIds signal
7389
+ // The tree component will sync selection state when nodes are loaded via datasource
7390
+ // We mark nodes as selected in the datasource callback by setting node.selected = true
7391
+ // Nodes that aren't in the tree yet will be selected when their parent is expanded
7392
+ }
7393
+ /**
7394
+ * Loads node data for IDs that are selected but not yet in the cache.
7395
+ * This is critical for pre-selected values in collapsed branches.
7396
+ */
7397
+ async loadMissingNodeData(selectedIds) {
7398
+ if (!this.treeData || !this.treeConfig || selectedIds.length === 0) {
7399
+ return;
7400
+ }
7401
+ // Find IDs that are selected but not in cache
7402
+ const missingIds = selectedIds.filter((id) => !this.nodeDataCache.has(id));
7403
+ if (missingIds.length === 0) {
7404
+ return;
7405
+ }
7406
+ try {
7407
+ const valueField = this.treeConfig.valueField || 'id';
7408
+ const categoryEntityQueryFunc = this.treeData.categoryEntityQueryFunc;
7409
+ if (!categoryEntityQueryFunc) {
7410
+ return;
7411
+ }
7412
+ // Query for missing nodes by their IDs
7413
+ const event = {
7414
+ ...this.treeData.basicQueryEvent,
7415
+ filter: {
7416
+ filters: missingIds.map((id) => ({
7417
+ field: valueField,
7418
+ value: id,
7419
+ operator: { type: 'equal' },
7420
+ })),
7421
+ logic: 'or',
7422
+ },
7423
+ };
7424
+ const res = await categoryEntityQueryFunc(event);
7425
+ if (res?.items) {
7426
+ // Cache the fetched node data
7427
+ res.items.forEach((item) => {
7428
+ const itemId = String(item[valueField] ?? '');
7429
+ if (itemId) {
7430
+ this.nodeDataCache.set(itemId, item);
7431
+ }
7432
+ });
7433
+ }
7434
+ }
7435
+ catch (error) {
7436
+ console.error('Error loading missing node data:', error);
7437
+ }
7438
+ }
7439
+ /**
7440
+ * Marks nodes as selected in the tree structure based on selectedNodeIds.
7441
+ * This ensures pre-selected nodes appear selected when the tree is rendered.
7442
+ */
7443
+ markNodesAsSelected(node) {
7444
+ const selectedIds = this.selectedNodeIds();
7445
+ if (selectedIds.length === 0) {
7446
+ return;
7447
+ }
7448
+ // Mark the node itself if it's selected
7449
+ this.markNodeAsSelectedIfNeeded(node);
7450
+ // Recursively mark children
7451
+ const nodeChildren = node['children'];
7452
+ if (nodeChildren) {
7453
+ nodeChildren.forEach((child) => {
7454
+ this.markNodesAsSelected(child);
7455
+ });
7456
+ }
7457
+ }
7458
+ /**
7459
+ * Marks a single node as selected if it's in the selectedNodeIds list.
7460
+ */
7461
+ markNodeAsSelectedIfNeeded(node) {
7462
+ const selectedIds = this.selectedNodeIds();
7463
+ const nodeId = String(node['id'] ?? '');
7464
+ if (nodeId && selectedIds.includes(nodeId)) {
7465
+ node['selected'] = true;
7466
+ }
7467
+ }
7468
+ /**
7469
+ * Marks a node and its children as disabled if the node ID matches the excluded ID.
7470
+ */
7471
+ markNodeAsDisabled(node, excludedId) {
7472
+ const nodeId = String(node['id'] ?? '');
7473
+ if (nodeId === excludedId) {
7474
+ node['disabled'] = true;
7475
+ }
7476
+ // Recursively mark children
7477
+ const nodeChildren = node['children'];
7478
+ if (nodeChildren) {
7479
+ nodeChildren.forEach((child) => {
7480
+ this.markNodeAsDisabled(child, excludedId);
7481
+ });
7482
+ }
7483
+ }
7484
+ /**
7485
+ * Processes root node: marks excluded as disabled, marks selected, and syncs selection with tree component
7486
+ */
7487
+ processRootNode(rootNode) {
7488
+ const excludedId = this.excludedNodeId();
7489
+ if (excludedId) {
7490
+ this.markNodeAsDisabled(rootNode, excludedId);
7491
+ }
7492
+ this.markNodesAsSelected(rootNode);
7493
+ // Sync selection with tree component after a short delay to ensure tree is rendered
7494
+ const treeComponent = this.tree();
7495
+ if (treeComponent) {
7496
+ setTimeout(() => {
7497
+ const selectedIds = this.selectedNodeIds();
7498
+ selectedIds.forEach((id) => {
7499
+ if (id && id !== 'all') {
7500
+ treeComponent.selectNode(id);
7501
+ }
7502
+ });
7503
+ }, 0);
7504
+ }
7505
+ }
7506
+ /**
7507
+ * Processes child nodes: marks excluded as disabled, marks selected, and syncs selection
7508
+ */
7509
+ processChildNodes(childNodes) {
7510
+ const excludedId = this.excludedNodeId();
7511
+ const selectedIds = this.selectedNodeIds();
7512
+ childNodes.forEach((node) => {
7513
+ const nodeId = String(node['id'] ?? '');
7514
+ if (excludedId && nodeId === excludedId) {
7515
+ node['disabled'] = true;
7516
+ }
7517
+ if (nodeId && selectedIds.includes(nodeId)) {
7518
+ node['selected'] = true;
7519
+ }
7520
+ });
7521
+ // Sync selection with tree component
7522
+ const treeComponent = this.tree();
7523
+ if (treeComponent) {
7524
+ setTimeout(() => {
7525
+ childNodes.forEach((node) => {
7526
+ const nodeId = String(node['id'] ?? '');
7527
+ if (nodeId && selectedIds.includes(nodeId) && nodeId !== 'all') {
7528
+ treeComponent.selectNode(nodeId);
7529
+ }
7530
+ });
7531
+ }, 0);
7532
+ }
7533
+ }
7534
+ /**
7535
+ * Caches node data from items array
7536
+ */
7537
+ cacheNodeData(items) {
7538
+ items.forEach((item) => {
7539
+ const itemId = String(item[this.treeConfig.valueField || 'id'] ?? '');
7540
+ if (itemId && typeof item === 'object' && item !== null) {
7541
+ this.nodeDataCache.set(itemId, item);
7542
+ }
7543
+ });
7544
+ }
7545
+ /**
7546
+ * Caches node data from tree nodes
7547
+ */
7548
+ cacheNodeDataFromNodes(nodes) {
7549
+ nodes.forEach((node) => {
7550
+ const nodeId = String(node['id'] ?? '');
7551
+ const nodeData = node['data'];
7552
+ if (nodeData && nodeId && typeof nodeData === 'object') {
7553
+ this.nodeDataCache.set(nodeId, nodeData);
6713
7554
  }
6714
7555
  });
6715
7556
  }
@@ -6835,41 +7676,107 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6835
7676
  const textField = this.treeConfig.textField || 'title';
6836
7677
  return String(nodeData[textField] ?? '');
6837
7678
  }
6838
- async getSelectedItems() {
6839
- // Use tree component's getSelectedNodes if available for more accurate selection
6840
- const treeComponent = this.tree();
6841
- if (treeComponent) {
6842
- const selectedNodes = treeComponent.getSelectedNodes();
6843
- const items = await Promise.all(selectedNodes.map(async (node) => {
6844
- const nodeId = String(node['id'] ?? '');
6845
- if (nodeId === 'all') {
6846
- return null;
6847
- }
6848
- const nodeData = node['data'] || node;
6849
- if (!nodeData) {
6850
- return null;
6851
- }
6852
- // Calculate path for this node
6853
- const path = await this.calculateNodePath(nodeId, nodeData);
6854
- return {
6855
- ...nodeData,
6856
- id: nodeId,
6857
- path: path,
6858
- };
6859
- }));
6860
- return items.filter((item) => item != null);
7679
+ /**
7680
+ * Checks if a node is a leaf node (has no children)
7681
+ */
7682
+ async isLeafNode(nodeId, treeComponent) {
7683
+ if (!treeComponent) {
7684
+ // If no tree component, check if node has children by querying
7685
+ return await this.checkIfNodeHasChildren(nodeId);
7686
+ }
7687
+ try {
7688
+ const node = treeComponent.findNode(nodeId);
7689
+ if (!node) {
7690
+ // Node not found in tree, check via query
7691
+ return await this.checkIfNodeHasChildren(nodeId);
7692
+ }
7693
+ // Check if node has children
7694
+ const children = node['children'];
7695
+ const childrenCount = node['childrenCount'];
7696
+ // If children are loaded, check the array
7697
+ if (children !== undefined) {
7698
+ return !children || children.length === 0;
7699
+ }
7700
+ // If childrenCount is available, use it
7701
+ if (childrenCount !== undefined) {
7702
+ return childrenCount === 0;
7703
+ }
7704
+ // If neither is available, try to check via query
7705
+ return await this.checkIfNodeHasChildren(nodeId);
7706
+ }
7707
+ catch {
7708
+ // If findNode fails, check via query
7709
+ return await this.checkIfNodeHasChildren(nodeId);
7710
+ }
7711
+ }
7712
+ /**
7713
+ * Checks if a node has children by querying the data source
7714
+ */
7715
+ async checkIfNodeHasChildren(nodeId) {
7716
+ if (!this.treeData?.categoryEntityQueryFunc || !this.treeConfig) {
7717
+ return true; // Assume leaf if we can't check
7718
+ }
7719
+ try {
7720
+ const parentKey = this.treeData.categoryEntityDef?.parentKey;
7721
+ if (!parentKey) {
7722
+ return true; // No parent key means flat structure, all nodes are leaves
7723
+ }
7724
+ const event = {
7725
+ ...this.treeData.basicQueryEvent,
7726
+ filter: {
7727
+ field: parentKey,
7728
+ value: nodeId,
7729
+ operator: { type: 'equal' },
7730
+ },
7731
+ take: 1, // Only need to check if any children exist
7732
+ };
7733
+ const res = await this.treeData.categoryEntityQueryFunc(event);
7734
+ return !res?.items || res.items.length === 0;
7735
+ }
7736
+ catch {
7737
+ return true; // Assume leaf on error
6861
7738
  }
6862
- // Fallback to cached data
7739
+ }
7740
+ async getSelectedItems() {
7741
+ // Always use selectedNodeIds as the source of truth
7742
+ // This ensures we get all selected nodes, even if they're not in the tree structure yet
6863
7743
  const selectedIds = this.selectedNodeIds();
6864
7744
  if (selectedIds.length === 0) {
6865
7745
  return [];
6866
7746
  }
7747
+ const treeComponent = this.tree();
6867
7748
  // Get node data from cache and calculate paths
7749
+ // For nodes not in cache, try to get from tree component
6868
7750
  const items = await Promise.all(selectedIds.map(async (id) => {
6869
- const nodeData = this.nodeDataCache.get(id);
7751
+ // First try to get from cache
7752
+ let nodeData = this.nodeDataCache.get(id);
7753
+ // If not in cache, try to get from tree component
7754
+ if (!nodeData && treeComponent) {
7755
+ try {
7756
+ const node = treeComponent.findNode(id);
7757
+ if (node) {
7758
+ nodeData = node['data'] || node;
7759
+ // Cache it for future use
7760
+ if (nodeData && typeof nodeData === 'object') {
7761
+ this.nodeDataCache.set(id, nodeData);
7762
+ }
7763
+ }
7764
+ }
7765
+ catch {
7766
+ // Node not found in tree, continue with cache lookup
7767
+ }
7768
+ }
7769
+ // If still no data, skip this node
6870
7770
  if (!nodeData) {
6871
7771
  return null;
6872
7772
  }
7773
+ // When selectionBehavior is intermediate-nested, only return leaf nodes
7774
+ if (this.allowMultiple()) {
7775
+ const isLeaf = await this.isLeafNode(id, treeComponent ?? null);
7776
+ if (!isLeaf) {
7777
+ return null; // Skip non-leaf nodes
7778
+ }
7779
+ }
6873
7780
  const path = await this.calculateNodePath(id, nodeData);
6874
7781
  return {
6875
7782
  ...nodeData,
@@ -6884,9 +7791,9 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6884
7791
  <div class="ax-flex ax-flex-col ax-h-full">
6885
7792
  @if (loading()) {
6886
7793
  <div class="ax-p-4 ax-flex ax-flex-col ax-gap-3">
6887
- <ax-skeleton class="ax-w-full ax-h-6 ax-rounded-md"></ax-skeleton>
6888
- <ax-skeleton class="ax-w-full ax-h-6 ax-rounded-md"></ax-skeleton>
6889
- <ax-skeleton class="ax-w-full ax-h-6 ax-rounded-md"></ax-skeleton>
7794
+ @for (i of [1, 2, 3, 4]; track i) {
7795
+ <ax-skeleton class="ax-w-full ax-h-6 ax-rounded-md"></ax-skeleton>
7796
+ }
6890
7797
  </div>
6891
7798
  } @else if (treeData) {
6892
7799
  <div class="ax-flex ax-flex-col ax-flex-1 ax-min-h-0">
@@ -6905,23 +7812,18 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6905
7812
  <div class="ax-text-xs ax-text-muted ax-flex ax-items-center ax-gap-1">
6906
7813
  @if (searchResultCount() > 0) {
6907
7814
  <span>{{ resultsFoundText() }}</span>
6908
- } @else {
6909
- <span>{{ '@general:terms.interface.category.search.no-results' | translate | async }}</span>
6910
7815
  }
6911
7816
  </div>
6912
7817
  }
6913
7818
  </div>
6914
7819
  @if (showNoSearchResults()) {
6915
- <div class="ax-flex-1 ax-flex ax-items-center ax-justify-center ax-p-4">
6916
- <div class="ax-text-center ax-text-muted">
6917
- <ax-icon class="fa-light fa-search ax-text-4xl ax-mb-2 ax-opacity-50"></ax-icon>
6918
- <p class="ax-text-sm">
6919
- {{ '@general:terms.interface.category.search.no-results-found.title' | translate | async }}
6920
- </p>
6921
- <p class="ax-text-xs ax-opacity-70">
6922
- {{ '@general:terms.interface.category.search.no-results-found.description' | translate | async }}
6923
- </p>
6924
- </div>
7820
+ <div class="ax-flex-1 ax-flex ax-items-center ax-justify-center">
7821
+ <axp-state-message
7822
+ icon="fa-light fa-search"
7823
+ [title]="'@general:terms.interface.category.search.no-results-found.title'"
7824
+ [description]="'@general:terms.interface.category.search.no-results-found.description'"
7825
+ >
7826
+ </axp-state-message>
6925
7827
  </div>
6926
7828
  } @else {
6927
7829
  <div class="ax-flex-1 ax-overflow-auto ax-p-4">
@@ -6929,14 +7831,14 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6929
7831
  [datasource]="datasource"
6930
7832
  [dragBehavior]="'none'"
6931
7833
  [selectMode]="allowMultiple() ? 'multiple' : 'single'"
6932
- [selectionBehavior]="selectionBehavior()"
7834
+ [selectionBehavior]="allowMultiple() ? 'intermediate-nested' : 'all'"
6933
7835
  [showIcons]="true"
6934
7836
  [titleField]="textField()"
6935
7837
  [idField]="valueField()"
6936
- [nodeTemplate]="itemTemplate"
6937
7838
  (onNodeSelect)="onNodeSelect($event)"
6938
7839
  (onSelectionChange)="onSelectionChange($event)"
6939
7840
  (onNodeToggle)="onNodeToggle($event)"
7841
+ [nodeTemplate]="itemTemplate"
6940
7842
  #tree
6941
7843
  >
6942
7844
  </ax-tree-view>
@@ -6944,18 +7846,22 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6944
7846
  <ng-template #itemTemplate let-node="node" let-level="level">
6945
7847
  @if (node) {
6946
7848
  @let item = node.data || node;
6947
- @let isMatch = isMatchingNode(node.id);
6948
- <div
6949
- class="ax-flex ax-items-center ax-gap-2 ax-w-full ax-overflow-hidden ax-p-2 ax-pe-0"
6950
- [class.ax-bg-secondary-100]="isMatch"
6951
- [class.ax-rounded]="isMatch"
6952
- [class.ax-px-1]="isMatch"
6953
- >
7849
+ <div class="ax-flex ax-items-center ax-gap-2 ax-w-full ax-overflow-hidden ax-p-2 ax-pe-0">
7850
+ @if (allowMultiple()) {
7851
+ <ax-check-box
7852
+ [ngModel]="node['indeterminate'] ? null : node['selected'] || false"
7853
+ [indeterminate]="node['indeterminate'] || false"
7854
+ (onValueChanged)="handleCheckboxChange(node.id, $event.value)"
7855
+ (click)="$event.stopPropagation()"
7856
+ (pointerdown)="$event.stopPropagation()"
7857
+ [disabled]="node.disabled || node.id === 'all'"
7858
+ ></ax-check-box>
7859
+ }
6954
7860
  <ax-icon
6955
7861
  class="fas fa-folder"
6956
7862
  [style.color]="item.color ?? 'rgba(var(--ax-sys-color-warning-500), 1)'"
6957
7863
  ></ax-icon>
6958
- <span class="ax-truncate" [class.ax-font-semibold]="isMatch">{{ node.title }}</span>
7864
+ <span class="ax-truncate">{{ node.title }}</span>
6959
7865
  </div>
6960
7866
  }
6961
7867
  </ng-template>
@@ -6963,8 +7869,13 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6963
7869
  }
6964
7870
  </div>
6965
7871
  } @else {
6966
- <div class="ax-p-4 ax-text-center ax-text-muted">
6967
- <span>---</span>
7872
+ <div class="ax-flex-1 ax-flex ax-items-center ax-justify-center">
7873
+ <axp-state-message
7874
+ icon="fa-light fa-folder-open"
7875
+ [title]="'@general:terms.interface.category.search.no-records.title'"
7876
+ [description]="'@general:terms.interface.category.search.no-records.description'"
7877
+ >
7878
+ </axp-state-message>
6968
7879
  </div>
6969
7880
  }
6970
7881
  </div>
@@ -6979,11 +7890,12 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
6979
7890
  look="solid"
6980
7891
  color="primary"
6981
7892
  [text]="'@general:actions.ok.title' | translate | async"
7893
+ [disabled]="selectedNodeIds().length === 0"
6982
7894
  (onClick)="onConfirm()"
6983
7895
  ></ax-button>
6984
7896
  </ax-suffix>
6985
7897
  </ax-footer>
6986
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i3.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: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2$1.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXSearchBoxModule }, { kind: "component", type: i3$2.AXSearchBoxComponent, selector: "ax-search-box", inputs: ["disabled", "readonly", "tabIndex", "placeholder", "value", "state", "name", "id", "look", "class", "delayTime", "type", "autoSearch"], outputs: ["valueChange", "stateChange", "onValueChanged", "onBlur", "onFocus", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i4$1.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "component", type: AXTreeViewComponent, selector: "ax-tree-view", inputs: ["datasource", "selectMode", "selectionBehavior", "dragArea", "dragBehavior", "showIcons", "showChildrenBadge", "expandedIcon", "collapsedIcon", "indentSize", "look", "nodeTemplate", "idField", "titleField", "tooltipField", "iconField", "expandedField", "selectedField", "indeterminateField", "disabledField", "hiddenField", "childrenField", "childrenCountField", "dataField", "inheritDisabled", "expandOnDoubleClick"], outputs: ["datasourceChange", "onBeforeDrop", "onNodeToggle", "onNodeSelect", "onNodeDoubleClick", "onNodeClick", "onSelectionChange", "onOrderChange", "onMoveChange", "onItemsChange"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
7898
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i3.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: AXCheckBoxModule }, { kind: "component", type: i2$1.AXCheckBoxComponent, selector: "ax-check-box", inputs: ["disabled", "tabIndex", "readonly", "color", "value", "name", "id", "isLoading", "indeterminate"], outputs: ["onBlur", "onFocus", "valueChange", "onValueChanged"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i3$2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i3$2.AXDecoratorGenericComponent, selector: "ax-footer, ax-header, ax-content, ax-divider, ax-form-hint, ax-prefix, ax-suffix, ax-text, ax-title, ax-subtitle, ax-placeholder, ax-overlay" }, { kind: "ngmodule", type: AXSearchBoxModule }, { kind: "component", type: i4$2.AXSearchBoxComponent, selector: "ax-search-box", inputs: ["disabled", "readonly", "tabIndex", "placeholder", "value", "state", "name", "id", "look", "class", "delayTime", "type", "autoSearch"], outputs: ["valueChange", "stateChange", "onValueChanged", "onBlur", "onFocus", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "ngmodule", type: AXSkeletonModule }, { kind: "component", type: i5$1.AXSkeletonComponent, selector: "ax-skeleton", inputs: ["animated"] }, { kind: "component", type: AXTreeViewComponent, selector: "ax-tree-view", inputs: ["datasource", "selectMode", "selectionBehavior", "dragArea", "dragBehavior", "showIcons", "showChildrenBadge", "expandedIcon", "collapsedIcon", "indentSize", "look", "nodeTemplate", "idField", "titleField", "tooltipField", "iconField", "expandedField", "selectedField", "indeterminateField", "disabledField", "hiddenField", "childrenField", "childrenCountField", "dataField", "inheritDisabled", "expandOnDoubleClick"], outputs: ["datasourceChange", "onBeforeDrop", "onNodeToggle", "onNodeSelect", "onNodeDoubleClick", "onNodeClick", "onSelectionChange", "onOrderChange", "onMoveChange", "onItemsChange"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPStateMessageComponent, selector: "axp-state-message", inputs: ["mode", "icon", "title", "description", "variant"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6987
7899
  }
6988
7900
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPEntityCategoryTreeSelectorComponent, decorators: [{
6989
7901
  type: Component,
@@ -6993,9 +7905,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
6993
7905
  <div class="ax-flex ax-flex-col ax-h-full">
6994
7906
  @if (loading()) {
6995
7907
  <div class="ax-p-4 ax-flex ax-flex-col ax-gap-3">
6996
- <ax-skeleton class="ax-w-full ax-h-6 ax-rounded-md"></ax-skeleton>
6997
- <ax-skeleton class="ax-w-full ax-h-6 ax-rounded-md"></ax-skeleton>
6998
- <ax-skeleton class="ax-w-full ax-h-6 ax-rounded-md"></ax-skeleton>
7908
+ @for (i of [1, 2, 3, 4]; track i) {
7909
+ <ax-skeleton class="ax-w-full ax-h-6 ax-rounded-md"></ax-skeleton>
7910
+ }
6999
7911
  </div>
7000
7912
  } @else if (treeData) {
7001
7913
  <div class="ax-flex ax-flex-col ax-flex-1 ax-min-h-0">
@@ -7014,23 +7926,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
7014
7926
  <div class="ax-text-xs ax-text-muted ax-flex ax-items-center ax-gap-1">
7015
7927
  @if (searchResultCount() > 0) {
7016
7928
  <span>{{ resultsFoundText() }}</span>
7017
- } @else {
7018
- <span>{{ '@general:terms.interface.category.search.no-results' | translate | async }}</span>
7019
7929
  }
7020
7930
  </div>
7021
7931
  }
7022
7932
  </div>
7023
7933
  @if (showNoSearchResults()) {
7024
- <div class="ax-flex-1 ax-flex ax-items-center ax-justify-center ax-p-4">
7025
- <div class="ax-text-center ax-text-muted">
7026
- <ax-icon class="fa-light fa-search ax-text-4xl ax-mb-2 ax-opacity-50"></ax-icon>
7027
- <p class="ax-text-sm">
7028
- {{ '@general:terms.interface.category.search.no-results-found.title' | translate | async }}
7029
- </p>
7030
- <p class="ax-text-xs ax-opacity-70">
7031
- {{ '@general:terms.interface.category.search.no-results-found.description' | translate | async }}
7032
- </p>
7033
- </div>
7934
+ <div class="ax-flex-1 ax-flex ax-items-center ax-justify-center">
7935
+ <axp-state-message
7936
+ icon="fa-light fa-search"
7937
+ [title]="'@general:terms.interface.category.search.no-results-found.title'"
7938
+ [description]="'@general:terms.interface.category.search.no-results-found.description'"
7939
+ >
7940
+ </axp-state-message>
7034
7941
  </div>
7035
7942
  } @else {
7036
7943
  <div class="ax-flex-1 ax-overflow-auto ax-p-4">
@@ -7038,14 +7945,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
7038
7945
  [datasource]="datasource"
7039
7946
  [dragBehavior]="'none'"
7040
7947
  [selectMode]="allowMultiple() ? 'multiple' : 'single'"
7041
- [selectionBehavior]="selectionBehavior()"
7948
+ [selectionBehavior]="allowMultiple() ? 'intermediate-nested' : 'all'"
7042
7949
  [showIcons]="true"
7043
7950
  [titleField]="textField()"
7044
7951
  [idField]="valueField()"
7045
- [nodeTemplate]="itemTemplate"
7046
7952
  (onNodeSelect)="onNodeSelect($event)"
7047
7953
  (onSelectionChange)="onSelectionChange($event)"
7048
7954
  (onNodeToggle)="onNodeToggle($event)"
7955
+ [nodeTemplate]="itemTemplate"
7049
7956
  #tree
7050
7957
  >
7051
7958
  </ax-tree-view>
@@ -7053,18 +7960,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
7053
7960
  <ng-template #itemTemplate let-node="node" let-level="level">
7054
7961
  @if (node) {
7055
7962
  @let item = node.data || node;
7056
- @let isMatch = isMatchingNode(node.id);
7057
- <div
7058
- class="ax-flex ax-items-center ax-gap-2 ax-w-full ax-overflow-hidden ax-p-2 ax-pe-0"
7059
- [class.ax-bg-secondary-100]="isMatch"
7060
- [class.ax-rounded]="isMatch"
7061
- [class.ax-px-1]="isMatch"
7062
- >
7963
+ <div class="ax-flex ax-items-center ax-gap-2 ax-w-full ax-overflow-hidden ax-p-2 ax-pe-0">
7964
+ @if (allowMultiple()) {
7965
+ <ax-check-box
7966
+ [ngModel]="node['indeterminate'] ? null : node['selected'] || false"
7967
+ [indeterminate]="node['indeterminate'] || false"
7968
+ (onValueChanged)="handleCheckboxChange(node.id, $event.value)"
7969
+ (click)="$event.stopPropagation()"
7970
+ (pointerdown)="$event.stopPropagation()"
7971
+ [disabled]="node.disabled || node.id === 'all'"
7972
+ ></ax-check-box>
7973
+ }
7063
7974
  <ax-icon
7064
7975
  class="fas fa-folder"
7065
7976
  [style.color]="item.color ?? 'rgba(var(--ax-sys-color-warning-500), 1)'"
7066
7977
  ></ax-icon>
7067
- <span class="ax-truncate" [class.ax-font-semibold]="isMatch">{{ node.title }}</span>
7978
+ <span class="ax-truncate">{{ node.title }}</span>
7068
7979
  </div>
7069
7980
  }
7070
7981
  </ng-template>
@@ -7072,8 +7983,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
7072
7983
  }
7073
7984
  </div>
7074
7985
  } @else {
7075
- <div class="ax-p-4 ax-text-center ax-text-muted">
7076
- <span>---</span>
7986
+ <div class="ax-flex-1 ax-flex ax-items-center ax-justify-center">
7987
+ <axp-state-message
7988
+ icon="fa-light fa-folder-open"
7989
+ [title]="'@general:terms.interface.category.search.no-records.title'"
7990
+ [description]="'@general:terms.interface.category.search.no-records.description'"
7991
+ >
7992
+ </axp-state-message>
7077
7993
  </div>
7078
7994
  }
7079
7995
  </div>
@@ -7088,6 +8004,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
7088
8004
  look="solid"
7089
8005
  color="primary"
7090
8006
  [text]="'@general:actions.ok.title' | translate | async"
8007
+ [disabled]="selectedNodeIds().length === 0"
7091
8008
  (onClick)="onConfirm()"
7092
8009
  ></ax-button>
7093
8010
  </ax-suffix>
@@ -7097,11 +8014,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
7097
8014
  imports: [
7098
8015
  CommonModule,
7099
8016
  AXButtonModule,
8017
+ AXCheckBoxModule,
7100
8018
  AXDecoratorModule,
7101
8019
  AXSearchBoxModule,
7102
8020
  AXSkeletonModule,
7103
8021
  AXTreeViewComponent,
7104
8022
  AXTranslationModule,
8023
+ AXPStateMessageComponent,
8024
+ FormsModule,
7105
8025
  ],
7106
8026
  }]
7107
8027
  }], propDecorators: { tree: [{ type: i0.ViewChild, args: ['tree', { isSignal: true }] }] } });
@@ -7109,177 +8029,275 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
7109
8029
  class AXPEntityCategoryWidgetColumnComponent extends AXPColumnWidgetComponent {
7110
8030
  constructor() {
7111
8031
  super(...arguments);
7112
- //#region ---- Dependencies ----
8032
+ //#region ---- Services & Dependencies ----
7113
8033
  this.entityDetailPopoverService = inject(AXPEntityDetailPopoverService);
7114
8034
  this.formatService = inject(AXFormatService);
8035
+ this.entityResolver = inject(AXPEntityResolver);
7115
8036
  //#endregion
7116
- //#region ---- View Children ----
7117
- this.moreButton = viewChild('moreButton', ...(ngDevMode ? [{ debugName: "moreButton" }] : []));
7118
- this.morePopover = viewChild('morePopover', ...(ngDevMode ? [{ debugName: "morePopover" }] : []));
8037
+ //#region ---- Inputs ----
8038
+ this.rawValueSignal = signal(null, ...(ngDevMode ? [{ debugName: "rawValueSignal" }] : []));
7119
8039
  //#endregion
7120
- //#region ---- Properties ----
7121
- this.host = inject(ElementRef);
7122
- this.valueField = this.options['valueField'] ?? 'id';
7123
- this.textField = this.options['textField'] ?? 'title';
7124
- this.entity = this.options['entity'] ?? '';
7125
- this.columnName = this.options['columnName'] ?? '';
7126
- this.maxVisible = this.options['maxVisible'] ?? 2;
8040
+ //#region ---- Computed Properties ----
8041
+ this.entity = computed(() => this.options['entity'] ?? '', ...(ngDevMode ? [{ debugName: "entity" }] : []));
8042
+ this.valueField = computed(() => this.options['valueField'] ?? 'id', ...(ngDevMode ? [{ debugName: "valueField" }] : []));
8043
+ this.textField = computed(() => this.options['textField'] ?? 'title', ...(ngDevMode ? [{ debugName: "textField" }] : []));
8044
+ this.columnName = computed(() => this.options['columnName'] ?? '', ...(ngDevMode ? [{ debugName: "columnName" }] : []));
8045
+ this.defaultTextField = computed(() => {
8046
+ const textField = this.entityDef()?.formats.lookup ?? this.entityDef()?.properties.find((c) => c.name != 'id')?.name ?? 'title';
8047
+ return textField;
8048
+ }, ...(ngDevMode ? [{ debugName: "defaultTextField" }] : []));
8049
+ this.displayField = computed(() => {
8050
+ if (this.textField()) {
8051
+ return this.textField();
8052
+ }
8053
+ return this.defaultTextField();
8054
+ }, ...(ngDevMode ? [{ debugName: "displayField" }] : []));
7127
8055
  //#endregion
7128
- //#region ---- Signals ----
7129
- this.isMorePopoverOpen = signal(false, ...(ngDevMode ? [{ debugName: "isMorePopoverOpen" }] : []));
8056
+ //#region ---- Component State ----
8057
+ this.host = inject(ElementRef);
8058
+ this.entityDef = signal(null, ...(ngDevMode ? [{ debugName: "entityDef" }] : []));
8059
+ this.displayItems = signal([], ...(ngDevMode ? [{ debugName: "displayItems" }] : []));
7130
8060
  this.selectedItemIndex = signal(-1, ...(ngDevMode ? [{ debugName: "selectedItemIndex" }] : []));
7131
8061
  //#endregion
7132
- //#region ---- Computed Properties ----
7133
- this.displayItems = computed(() => isNil(this.rawValue)
7134
- ? []
7135
- : castArray(this.rawValue)
7136
- .map((item) => this.extractItem(item))
7137
- .filter((c) => c != null), ...(ngDevMode ? [{ debugName: "displayItems" }] : []));
7138
- this.allItems = computed(() => this.displayItems(), ...(ngDevMode ? [{ debugName: "allItems" }] : []));
7139
- this.visibleItems = computed(() => {
7140
- const items = this.allItems();
7141
- return items.slice(0, this.maxVisible);
7142
- }, ...(ngDevMode ? [{ debugName: "visibleItems" }] : []));
7143
- this.remainingItems = computed(() => {
7144
- const items = this.allItems();
7145
- return items.slice(this.maxVisible);
7146
- }, ...(ngDevMode ? [{ debugName: "remainingItems" }] : []));
7147
- this.hasMoreItems = computed(() => {
7148
- return this.allItems().length > this.maxVisible;
7149
- }, ...(ngDevMode ? [{ debugName: "hasMoreItems" }] : []));
7150
- this.remainingItemsCount = computed(() => {
7151
- return this.allItems().length - this.maxVisible;
7152
- }, ...(ngDevMode ? [{ debugName: "remainingItemsCount" }] : []));
8062
+ //#region ---- Effects ----
8063
+ this.efEntity = effect(async () => {
8064
+ const entityKey = this.entity();
8065
+ if (!entityKey) {
8066
+ return;
8067
+ }
8068
+ const [module, entity] = entityKey.split('.');
8069
+ if (!module || !entity) {
8070
+ return;
8071
+ }
8072
+ this.entityDef.set(await this.entityResolver.get(module, entity));
8073
+ }, ...(ngDevMode ? [{ debugName: "efEntity" }] : []));
8074
+ this.efDisplay = effect(async () => {
8075
+ const value = this.rawValueSignal();
8076
+ const entity = this.entityDef();
8077
+ // Wait for entity definition to be loaded before processing value
8078
+ if (!entity) {
8079
+ this.displayItems.set([]);
8080
+ return;
8081
+ }
8082
+ if (isNil(value)) {
8083
+ this.displayItems.set([]);
8084
+ }
8085
+ else {
8086
+ const items = castArray(value);
8087
+ const itemsWithPaths = await Promise.all(items.map((item) => this.extractItemWithPath(item)));
8088
+ this.displayItems.set(itemsWithPaths.filter((c) => c != null));
8089
+ }
8090
+ }, ...(ngDevMode ? [{ debugName: "efDisplay" }] : []));
8091
+ //#endregion
8092
+ //#region ---- Computed Properties (Derived) ----
8093
+ this.visibleItems = computed(() => this.displayItems(), ...(ngDevMode ? [{ debugName: "visibleItems" }] : []));
8094
+ }
8095
+ set rawValueInput(value) {
8096
+ this.rawValueSignal.set(value);
7153
8097
  }
7154
8098
  //#endregion
7155
8099
  //#region ---- Public Methods ----
7156
- showMoreItems() {
7157
- this.entityDetailPopoverService.hide();
7158
- this.openMorePopover();
7159
- }
7160
- onMorePopoverOpenChange(event) {
7161
- this.isMorePopoverOpen.set(event);
7162
- }
7163
8100
  async showItemDetail(item, index) {
7164
- const columnData = this.rowData[this.columnName];
8101
+ const columnData = this.rowData[this.columnName()];
7165
8102
  const id = Array.isArray(columnData) ? columnData[index] : columnData;
7166
8103
  this.selectedItemIndex.set(index);
7167
- this.closeMorePopover();
7168
- if (this.entity && id) {
8104
+ const entityKey = this.entity();
8105
+ if (entityKey && id) {
7169
8106
  await this.entityDetailPopoverService.show(this.host, {
7170
- entity: this.entity,
8107
+ entity: entityKey,
7171
8108
  id: id,
7172
- textField: this.textField,
7173
- valueField: this.valueField,
8109
+ textField: this.textField(),
8110
+ valueField: this.valueField(),
7174
8111
  item,
7175
8112
  });
7176
8113
  }
7177
8114
  }
7178
8115
  handleItemClick(index) {
7179
- const items = this.allItems();
8116
+ const items = this.visibleItems();
7180
8117
  if (index < items.length) {
7181
8118
  const item = items[index];
7182
8119
  this.showItemDetail(item, index);
7183
8120
  }
7184
8121
  }
7185
- //#endregion
7186
- //#region ---- Private Methods ----
7187
- openMorePopover() {
7188
- if (this.morePopover() && this.moreButton()) {
7189
- this.morePopover().target = this.moreButton().nativeElement;
7190
- this.morePopover().open();
7191
- this.isMorePopoverOpen.set(true);
8122
+ getItemPath(item) {
8123
+ if (!item) {
8124
+ return [];
8125
+ }
8126
+ // If path is available as array, return it
8127
+ if (item.path && Array.isArray(item.path) && item.path.length > 0) {
8128
+ return item.path;
7192
8129
  }
8130
+ // Fall back to display field if no path
8131
+ const displayValue = get(item, this.displayField());
8132
+ return displayValue ? [String(displayValue)] : [];
7193
8133
  }
7194
- closeMorePopover() {
7195
- if (this.morePopover()) {
7196
- this.morePopover().close();
7197
- this.isMorePopoverOpen.set(false);
8134
+ hasParent(item) {
8135
+ const path = this.getItemPath(item);
8136
+ return path.length > 1;
8137
+ }
8138
+ getItemText(item) {
8139
+ const path = this.getItemPath(item);
8140
+ if (path.length === 0) {
8141
+ return '';
7198
8142
  }
8143
+ // Return the last item in the path (the actual node)
8144
+ return path[path.length - 1];
7199
8145
  }
7200
- extractItem(item) {
8146
+ getItemId(item) {
8147
+ if (!item) {
8148
+ return '';
8149
+ }
8150
+ return String(get(item, this.valueField()) ?? '');
8151
+ }
8152
+ //#endregion
8153
+ //#region ---- Private Methods ----
8154
+ async extractItemWithPath(item) {
7201
8155
  if (isNil(item)) {
7202
8156
  return null;
7203
8157
  }
8158
+ let itemObj;
7204
8159
  if (typeof item === 'object') {
7205
- return item;
8160
+ itemObj = item;
7206
8161
  }
7207
- return {
7208
- [this.valueField]: item,
7209
- [this.textField]: item,
7210
- };
8162
+ else {
8163
+ // If item is just an ID, fetch the full item
8164
+ const byKey = this.entityDef()?.queries.byKey?.execute;
8165
+ if (byKey) {
8166
+ try {
8167
+ itemObj = await byKey(item);
8168
+ }
8169
+ catch (error) {
8170
+ console.error('Error fetching item:', error);
8171
+ return null;
8172
+ }
8173
+ }
8174
+ else {
8175
+ itemObj = {
8176
+ [this.valueField()]: item,
8177
+ [this.textField()]: item,
8178
+ };
8179
+ }
8180
+ }
8181
+ // If item already has a path array, return it as is
8182
+ if (itemObj.path && Array.isArray(itemObj.path) && itemObj.path.length > 0) {
8183
+ return itemObj;
8184
+ }
8185
+ // Calculate path for the item
8186
+ return await this.calculateItemPath(itemObj);
7211
8187
  }
7212
- getDisplayText(item) {
7213
- if (!item) {
7214
- return '';
8188
+ /**
8189
+ * Calculate the full path from root to the item.
8190
+ * Returns an array of strings like ["C", "B"] for item B under parent C.
8191
+ */
8192
+ async calculateItemPath(item) {
8193
+ if (!item || !this.entityDef()) {
8194
+ return item;
8195
+ }
8196
+ // If item already has a path array, return it as is
8197
+ if (item.path && Array.isArray(item.path) && item.path.length > 0) {
8198
+ return item;
8199
+ }
8200
+ const parentKey = this.entityDef()?.parentKey;
8201
+ if (!parentKey) {
8202
+ // No parent key means flat structure, return item with just its title as path array
8203
+ const title = get(item, this.displayField()) ?? '';
8204
+ return { ...item, path: title ? [title] : [] };
8205
+ }
8206
+ const valueField = this.valueField();
8207
+ const textField = this.displayField();
8208
+ const byKey = this.entityDef()?.queries.byKey?.execute;
8209
+ if (!byKey) {
8210
+ return item;
8211
+ }
8212
+ const path = [];
8213
+ let currentItem = item;
8214
+ const visitedIds = new Set(); // Prevent infinite loops
8215
+ // Build path by traversing up the parent chain
8216
+ while (currentItem) {
8217
+ const currentId = String(get(currentItem, valueField) ?? '');
8218
+ if (!currentId || visitedIds.has(currentId)) {
8219
+ break; // Prevent infinite loops
8220
+ }
8221
+ visitedIds.add(currentId);
8222
+ const title = String(get(currentItem, textField) ?? '');
8223
+ if (title) {
8224
+ path.unshift(title); // Add to beginning to maintain hierarchy order
8225
+ }
8226
+ // Get parent ID from current item
8227
+ const parentId = get(currentItem, parentKey);
8228
+ if (!parentId || parentId === 'all' || parentId === currentId) {
8229
+ break; // Reached root or invalid parent
8230
+ }
8231
+ try {
8232
+ // Fetch parent item
8233
+ const parentItem = await byKey(parentId);
8234
+ if (!parentItem) {
8235
+ break; // Parent not found, stop traversal
8236
+ }
8237
+ currentItem = parentItem;
8238
+ }
8239
+ catch (error) {
8240
+ console.error('Error fetching parent item:', error);
8241
+ break;
8242
+ }
7215
8243
  }
7216
- return item?.[this.textField] ?? String(item);
8244
+ // Return item with path array, or just the item title if no path
8245
+ const pathArray = path.length > 0 ? path : get(item, textField) ? [String(get(item, textField))] : [];
8246
+ return { ...item, path: pathArray };
7217
8247
  }
7218
8248
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPEntityCategoryWidgetColumnComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
7219
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", type: AXPEntityCategoryWidgetColumnComponent, isStandalone: true, selector: "axp-entity-category-widget-column", inputs: { rawValue: "rawValue", rowData: "rowData" }, viewQueries: [{ propertyName: "moreButton", first: true, predicate: ["moreButton"], descendants: true, isSignal: true }, { propertyName: "morePopover", first: true, predicate: ["morePopover"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
7220
- <div class="ax-flex ax-items-center ax-gap-1 ax-flex-wrap">
7221
- @for (item of visibleItems(); track $index) {
7222
- <ax-badge
7223
- class="ax-p-0.5 ax-cursor-pointer"
7224
- [text]="getDisplayText(item)"
7225
- (click)="handleItemClick($index)"
7226
- ></ax-badge>
7227
- }
7228
- @if (hasMoreItems()) {
7229
- <span class="ax-cursor-pointer ax-px-1" (click)="showMoreItems()" #moreButton>
7230
- <i class="fas fa-ellipsis-h"></i>
7231
- </span>
7232
- <ax-popover #morePopover [openOn]="'manual'" (openChange)="onMorePopoverOpenChange($event)">
7233
- <div class="ax-p-2 ax-flex ax-flex-col ax-gap-1">
7234
- @for (item of remainingItems(); track $index) {
7235
- <ax-badge
7236
- class="ax-p-0.5 ax-cursor-pointer"
7237
- [text]="getDisplayText(item)"
7238
- (click)="handleItemClick(visibleItems().length + $index)"
7239
- ></ax-badge>
7240
- }
7241
- </div>
7242
- </ax-popover>
8249
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", type: AXPEntityCategoryWidgetColumnComponent, isStandalone: true, selector: "axp-entity-category-widget-column", inputs: { rawValue: "rawValue", rowData: "rowData", rawValueInput: ["rawValue", "rawValueInput"] }, host: { classAttribute: "ax-w-full" }, usesInheritance: true, ngImport: i0, template: `
8250
+ <div class="ax-flex ax-flex-col ax-gap-1.5">
8251
+ @for (item of visibleItems(); track getItemId(item)) {
8252
+ <div class=" ax-cursor-pointer" (click)="handleItemClick($index)">
8253
+ @if (hasParent(item)) {
8254
+ <ax-badge [color]="'primary'" [look]="'twotone'" class="ax-p-0.5" [text]="getItemText(item)">
8255
+ <ax-prefix>
8256
+ <i class="fas fa-ellipsis-h ax-text-neutral-400 ax-text-xs ax-ps-1"></i>
8257
+ <i class="fas fa-chevron-right ax-text-neutral-400 ax-text-xs ax-px-1"></i>
8258
+ </ax-prefix>
8259
+ </ax-badge>
8260
+ } @else {
8261
+ <ax-badge [color]="'primary'" [look]="'twotone'" class="ax-p-0.5" [text]="getItemText(item)"> </ax-badge>
8262
+ }
8263
+ </div>
7243
8264
  }
7244
8265
  </div>
7245
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i2$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "ngmodule", type: AXPopoverModule }, { kind: "component", type: i2.AXPopoverComponent, selector: "ax-popover", inputs: ["width", "disabled", "offsetX", "offsetY", "target", "placement", "content", "openOn", "closeOn", "hasBackdrop", "openAfter", "closeAfter", "backdropClass", "panelClass", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
8266
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i2$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i3$2.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" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
7246
8267
  }
7247
8268
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPEntityCategoryWidgetColumnComponent, decorators: [{
7248
8269
  type: Component,
7249
8270
  args: [{
7250
8271
  selector: 'axp-entity-category-widget-column',
8272
+ host: {
8273
+ class: 'ax-w-full',
8274
+ },
7251
8275
  template: `
7252
- <div class="ax-flex ax-items-center ax-gap-1 ax-flex-wrap">
7253
- @for (item of visibleItems(); track $index) {
7254
- <ax-badge
7255
- class="ax-p-0.5 ax-cursor-pointer"
7256
- [text]="getDisplayText(item)"
7257
- (click)="handleItemClick($index)"
7258
- ></ax-badge>
7259
- }
7260
- @if (hasMoreItems()) {
7261
- <span class="ax-cursor-pointer ax-px-1" (click)="showMoreItems()" #moreButton>
7262
- <i class="fas fa-ellipsis-h"></i>
7263
- </span>
7264
- <ax-popover #morePopover [openOn]="'manual'" (openChange)="onMorePopoverOpenChange($event)">
7265
- <div class="ax-p-2 ax-flex ax-flex-col ax-gap-1">
7266
- @for (item of remainingItems(); track $index) {
7267
- <ax-badge
7268
- class="ax-p-0.5 ax-cursor-pointer"
7269
- [text]="getDisplayText(item)"
7270
- (click)="handleItemClick(visibleItems().length + $index)"
7271
- ></ax-badge>
7272
- }
7273
- </div>
7274
- </ax-popover>
8276
+ <div class="ax-flex ax-flex-col ax-gap-1.5">
8277
+ @for (item of visibleItems(); track getItemId(item)) {
8278
+ <div class=" ax-cursor-pointer" (click)="handleItemClick($index)">
8279
+ @if (hasParent(item)) {
8280
+ <ax-badge [color]="'primary'" [look]="'twotone'" class="ax-p-0.5" [text]="getItemText(item)">
8281
+ <ax-prefix>
8282
+ <i class="fas fa-ellipsis-h ax-text-neutral-400 ax-text-xs ax-ps-1"></i>
8283
+ <i class="fas fa-chevron-right ax-text-neutral-400 ax-text-xs ax-px-1"></i>
8284
+ </ax-prefix>
8285
+ </ax-badge>
8286
+ } @else {
8287
+ <ax-badge [color]="'primary'" [look]="'twotone'" class="ax-p-0.5" [text]="getItemText(item)"> </ax-badge>
8288
+ }
8289
+ </div>
7275
8290
  }
7276
8291
  </div>
7277
8292
  `,
7278
8293
  changeDetection: ChangeDetectionStrategy.OnPush,
7279
- imports: [CommonModule, AXBadgeModule, AXButtonModule, AXPopoverModule],
8294
+ imports: [CommonModule, AXBadgeModule, AXDecoratorModule],
7280
8295
  inputs: ['rawValue', 'rowData'],
7281
8296
  }]
7282
- }], propDecorators: { moreButton: [{ type: i0.ViewChild, args: ['moreButton', { isSignal: true }] }], morePopover: [{ type: i0.ViewChild, args: ['morePopover', { isSignal: true }] }] } });
8297
+ }], propDecorators: { rawValueInput: [{
8298
+ type: Input,
8299
+ args: ['rawValue']
8300
+ }] } });
7283
8301
 
7284
8302
  var entityCategoryWidgetColumn_component = /*#__PURE__*/Object.freeze({
7285
8303
  __proto__: null,
@@ -7586,7 +8604,7 @@ class AXPTruncatedBreadcrumbComponent {
7586
8604
  }
7587
8605
  }
7588
8606
  </div>
7589
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
8607
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i3$2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
7590
8608
  }
7591
8609
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPTruncatedBreadcrumbComponent, decorators: [{
7592
8610
  type: Component,
@@ -7703,6 +8721,8 @@ class AXPEntityCategoryWidgetEditComponent extends AXPValueWidgetComponent {
7703
8721
  this.selectedItems = signal([], ...(ngDevMode ? [{ debugName: "selectedItems" }] : []));
7704
8722
  this.isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
7705
8723
  this.isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : []));
8724
+ this.tagBox = viewChild('tagBoxComponent', ...(ngDevMode ? [{ debugName: "tagBox" }] : []));
8725
+ this.searchTerm = signal(null, ...(ngDevMode ? [{ debugName: "searchTerm" }] : []));
7706
8726
  //#endregion
7707
8727
  //#region ---- Private Properties ----
7708
8728
  this.entityDef = signal(null, ...(ngDevMode ? [{ debugName: "entityDef" }] : []));
@@ -7757,13 +8777,62 @@ class AXPEntityCategoryWidgetEditComponent extends AXPValueWidgetComponent {
7757
8777
  handleAddClick(e) {
7758
8778
  this.showTreeSelector();
7759
8779
  }
7760
- handleRemoveItemClick(event, item) {
7761
- event.nativeEvent.stopPropagation();
7762
- this.removeItem(item);
8780
+ handleValueChange(e) {
8781
+ if (e.isUserInteraction) {
8782
+ if (isNil(e.value) || isEmpty(e.value)) {
8783
+ this.clear();
8784
+ }
8785
+ else {
8786
+ const items = Array.isArray(e.value) ? e.value : [e.value];
8787
+ this.setItems(items);
8788
+ }
8789
+ }
8790
+ }
8791
+ handleOnBlur(e) {
8792
+ setTimeout(() => {
8793
+ if (!this.isOpen()) {
8794
+ this.clearInput();
8795
+ }
8796
+ }, 100);
8797
+ }
8798
+ async handleKeyUp(e) {
8799
+ const keyEvent = e.nativeEvent;
8800
+ const value = this.tagBox()?.inputValue() ?? '';
8801
+ this.searchTerm.set(value);
8802
+ if ((keyEvent.code == 'Enter' || keyEvent.code == 'NumpadEnter') && value) {
8803
+ this.isLoading.set(true);
8804
+ // For category widget, always open selector on Enter
8805
+ this.showTreeSelector();
8806
+ this.isLoading.set(false);
8807
+ }
8808
+ if (keyEvent.code == 'ArrowDown') {
8809
+ this.showTreeSelector();
8810
+ }
7763
8811
  }
7764
8812
  handleClearClick() {
7765
8813
  this.clear();
7766
8814
  }
8815
+ async handleRemoveItem(event, index) {
8816
+ event.stopPropagation();
8817
+ if (this.disabled()) {
8818
+ return;
8819
+ }
8820
+ const currentItems = this.selectedItems();
8821
+ if (index < 0 || index >= currentItems.length) {
8822
+ return;
8823
+ }
8824
+ const filteredItems = currentItems.filter((_, i) => i !== index);
8825
+ if (filteredItems.length === 0) {
8826
+ this.clear();
8827
+ }
8828
+ else {
8829
+ await this.setItems(filteredItems);
8830
+ }
8831
+ }
8832
+ clearInput() {
8833
+ this.tagBox()?.inputValue.set('');
8834
+ this.searchTerm.set('');
8835
+ }
7767
8836
  async showTreeSelector() {
7768
8837
  this.isOpen.set(true);
7769
8838
  const currentValue = this.getValue();
@@ -7834,24 +8903,12 @@ class AXPEntityCategoryWidgetEditComponent extends AXPValueWidgetComponent {
7834
8903
  }
7835
8904
  finally {
7836
8905
  this.isOpen.set(false);
7837
- }
7838
- }
7839
- async removeItem(item) {
7840
- if (this.disabled()) {
7841
- return;
7842
- }
7843
- const currentItems = this.selectedItems();
7844
- const itemId = get(item, this.valueField());
7845
- const filteredItems = currentItems.filter((i) => get(i, this.valueField()) !== itemId);
7846
- if (filteredItems.length === 0) {
7847
- this.clear();
7848
- }
7849
- else {
7850
- await this.setItems(filteredItems);
8906
+ this.tagBox()?.focus();
7851
8907
  }
7852
8908
  }
7853
8909
  clear() {
7854
8910
  this.setValue(null);
8911
+ this.clearInput();
7855
8912
  this.selectedItems.set([]);
7856
8913
  this.cdr.markForCheck();
7857
8914
  }
@@ -7933,6 +8990,7 @@ class AXPEntityCategoryWidgetEditComponent extends AXPValueWidgetComponent {
7933
8990
  return;
7934
8991
  }
7935
8992
  items = castArray(items);
8993
+ this.clearInput();
7936
8994
  // Ensure all items have paths
7937
8995
  const itemsWithPaths = await Promise.all(items.map(async (item) => {
7938
8996
  // If item already has a path array, return it as is
@@ -8074,47 +9132,78 @@ class AXPEntityCategoryWidgetEditComponent extends AXPValueWidgetComponent {
8074
9132
  return { ...item, path: pathArray };
8075
9133
  }
8076
9134
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPEntityCategoryWidgetEditComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
8077
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", type: AXPEntityCategoryWidgetEditComponent, isStandalone: true, selector: "axp-entity-category-widget-edit", usesInheritance: true, ngImport: i0, template: `
8078
- <div class="ax-flex ax-flex-col ax-gap-3">
8079
- @if (selectedItems().length > 0) {
8080
- <div class="ax-flex ax-flex-col ax-gap-2">
8081
- @for (item of selectedItems(); track getItemId(item)) {
8082
- <div class="ax-flex ax-items-center ax-gap-2 ax-p-2 ax-border ax-primary-lightest ax-rounded-xl">
8083
- <axp-truncated-breadcrumb
8084
- [sections]="getItemPath(item)"
8085
- [characterLimit]="characterLimit()"
8086
- [sectionLimit]="sectionLimit()"
8087
- class="ax-flex-1"
8088
- ></axp-truncated-breadcrumb>
8089
- <ax-button color="ghost" look="blank" class="ax-xs" (onClick)="handleRemoveItemClick($event, item)">
8090
- <ax-icon icon="ax-icon ax-icon-close"></ax-icon>
8091
- </ax-button>
8092
- </div>
8093
- }
8094
- </div>
9135
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.12", type: AXPEntityCategoryWidgetEditComponent, isStandalone: true, selector: "axp-entity-category-widget-edit", viewQueries: [{ propertyName: "tagBox", first: true, predicate: ["tagBoxComponent"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: `
9136
+ <ax-tag-box
9137
+ #tagBoxComponent
9138
+ [tagTemplate]="tagTemplate"
9139
+ [ngModel]="selectedItems()"
9140
+ [textField]="displayField()"
9141
+ [valueField]="valueField()"
9142
+ (onValueChanged)="handleValueChange($event)"
9143
+ [placeholder]="selectedItems().length ? '' : searchPlaceholderText()"
9144
+ [addOnEnter]="false"
9145
+ [addOnComma]="false"
9146
+ [readonly]="true"
9147
+ [disabled]="disabled()"
9148
+ (onKeyUp)="handleKeyUp($event)"
9149
+ (onBlur)="handleOnBlur($event)"
9150
+ >
9151
+ @for (validation of validationRules(); track $index) {
9152
+ <ax-validation-rule
9153
+ [rule]="validation.rule"
9154
+ [message]="validation.options?.message"
9155
+ [options]="validation.options"
9156
+ ></ax-validation-rule>
9157
+ }
9158
+ @if (selectedItems().length > 1 || allowClear()) {
9159
+ <ax-clear-button (click)="handleClearClick()"></ax-clear-button>
8095
9160
  }
8096
9161
 
8097
- <div class="ax-flex ax-items-center ax-gap-2">
8098
- <ax-button
8099
- [text]="'@general:actions.add.title' | translate | async"
8100
- color="primary"
8101
- [disabled]="isLoading() || disabled()"
8102
- (onClick)="handleAddClick($event)"
8103
- >
9162
+ <ax-suffix>
9163
+ <ax-button color="ghost" look="blank" [disabled]="isLoading() || disabled()" (onClick)="handleAddClick($event)">
8104
9164
  @if (isLoading()) {
8105
9165
  <ax-loading></ax-loading>
8106
9166
  } @else {
8107
- <ax-icon icon="far fa-plus"></ax-icon>
9167
+ <ax-icon icon="far fa-search"></ax-icon>
8108
9168
  }
8109
9169
  </ax-button>
8110
-
8111
- @if (allowClear() && selectedItems().length > 0) {
8112
- <ax-button color="ghost" look="outline" [disabled]="disabled()" (onClick)="handleClearClick()">
8113
- Clear All
8114
- </ax-button>
8115
- }
9170
+ </ax-suffix>
9171
+ </ax-tag-box>
9172
+ <ng-template #tagTemplate let-item let-index="index">
9173
+ <div class="ax-inline-flex ax-items-center ax-gap-1.5 ax-rounded-md ax-px-3 ax-py-1 ax-text-sm ax-surface">
9174
+ <axp-truncated-breadcrumb
9175
+ [attr.data-color]="item.color"
9176
+ [sections]="getItemPath(item)"
9177
+ [characterLimit]="characterLimit()"
9178
+ [sectionLimit]="sectionLimit()"
9179
+ ></axp-truncated-breadcrumb>
9180
+ <button type="button" (click)="handleRemoveItem($event, index)">
9181
+ <ax-icon class="ax-icon ax-icon-close"></ax-icon>
9182
+ </button>
8116
9183
  </div>
8117
-
9184
+ </ng-template>
9185
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i3.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: AXDecoratorModule }, { kind: "component", type: i3$2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i3$2.AXDecoratorClearButtonComponent, selector: "ax-clear-button", inputs: ["icon"] }, { kind: "component", type: i3$2.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: "component", type: i4.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXValidationModule }, { kind: "ngmodule", type: AXFormModule }, { kind: "directive", type: i5$2.AXValidationRuleDirective, selector: "ax-validation-rule", inputs: ["rule", "options", "message", "disabled"] }, { kind: "ngmodule", type: AXTagBoxModule }, { kind: "component", type: i6$1.AXTagBoxComponent, selector: "ax-tag-box", inputs: ["disabled", "tabIndex", "readonly", "value", "state", "name", "id", "placeholder", "allowNull", "type", "look", "addOnComma", "addOnEnter", "valueField", "textField", "readonlyField", "allowDuplicateValues", "tagTemplate"], outputs: ["onBlur", "onFocus", "valueChange", "stateChange", "onValueChanged", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress", "onTagClick", "onTagDblClick", "onTagContextMenu"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPTruncatedBreadcrumbComponent, selector: "axp-truncated-breadcrumb", inputs: ["sections", "characterLimit", "sectionLimit", "separatorIcon", "ellipsisIcon", "eyeIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
9186
+ }
9187
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPEntityCategoryWidgetEditComponent, decorators: [{
9188
+ type: Component,
9189
+ args: [{
9190
+ selector: 'axp-entity-category-widget-edit',
9191
+ template: `
9192
+ <ax-tag-box
9193
+ #tagBoxComponent
9194
+ [tagTemplate]="tagTemplate"
9195
+ [ngModel]="selectedItems()"
9196
+ [textField]="displayField()"
9197
+ [valueField]="valueField()"
9198
+ (onValueChanged)="handleValueChange($event)"
9199
+ [placeholder]="selectedItems().length ? '' : searchPlaceholderText()"
9200
+ [addOnEnter]="false"
9201
+ [addOnComma]="false"
9202
+ [readonly]="true"
9203
+ [disabled]="disabled()"
9204
+ (onKeyUp)="handleKeyUp($event)"
9205
+ (onBlur)="handleOnBlur($event)"
9206
+ >
8118
9207
  @for (validation of validationRules(); track $index) {
8119
9208
  <ax-validation-rule
8120
9209
  [rule]="validation.rule"
@@ -8122,76 +9211,49 @@ class AXPEntityCategoryWidgetEditComponent extends AXPValueWidgetComponent {
8122
9211
  [options]="validation.options"
8123
9212
  ></ax-validation-rule>
8124
9213
  }
8125
- </div>
8126
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i3.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: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i1.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXValidationModule }, { kind: "ngmodule", type: AXFormModule }, { kind: "directive", type: i5$1.AXValidationRuleDirective, selector: "ax-validation-rule", inputs: ["rule", "options", "message", "disabled"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "component", type: AXPTruncatedBreadcrumbComponent, selector: "axp-truncated-breadcrumb", inputs: ["sections", "characterLimit", "sectionLimit", "separatorIcon", "ellipsisIcon", "eyeIcon"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
8127
- }
8128
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPEntityCategoryWidgetEditComponent, decorators: [{
8129
- type: Component,
8130
- args: [{
8131
- selector: 'axp-entity-category-widget-edit',
8132
- template: `
8133
- <div class="ax-flex ax-flex-col ax-gap-3">
8134
- @if (selectedItems().length > 0) {
8135
- <div class="ax-flex ax-flex-col ax-gap-2">
8136
- @for (item of selectedItems(); track getItemId(item)) {
8137
- <div class="ax-flex ax-items-center ax-gap-2 ax-p-2 ax-border ax-primary-lightest ax-rounded-xl">
8138
- <axp-truncated-breadcrumb
8139
- [sections]="getItemPath(item)"
8140
- [characterLimit]="characterLimit()"
8141
- [sectionLimit]="sectionLimit()"
8142
- class="ax-flex-1"
8143
- ></axp-truncated-breadcrumb>
8144
- <ax-button color="ghost" look="blank" class="ax-xs" (onClick)="handleRemoveItemClick($event, item)">
8145
- <ax-icon icon="ax-icon ax-icon-close"></ax-icon>
8146
- </ax-button>
8147
- </div>
8148
- }
8149
- </div>
9214
+ @if (selectedItems().length > 1 || allowClear()) {
9215
+ <ax-clear-button (click)="handleClearClick()"></ax-clear-button>
8150
9216
  }
8151
9217
 
8152
- <div class="ax-flex ax-items-center ax-gap-2">
8153
- <ax-button
8154
- [text]="'@general:actions.add.title' | translate | async"
8155
- color="primary"
8156
- [disabled]="isLoading() || disabled()"
8157
- (onClick)="handleAddClick($event)"
8158
- >
9218
+ <ax-suffix>
9219
+ <ax-button color="ghost" look="blank" [disabled]="isLoading() || disabled()" (onClick)="handleAddClick($event)">
8159
9220
  @if (isLoading()) {
8160
9221
  <ax-loading></ax-loading>
8161
9222
  } @else {
8162
- <ax-icon icon="far fa-plus"></ax-icon>
9223
+ <ax-icon icon="far fa-search"></ax-icon>
8163
9224
  }
8164
9225
  </ax-button>
8165
-
8166
- @if (allowClear() && selectedItems().length > 0) {
8167
- <ax-button color="ghost" look="outline" [disabled]="disabled()" (onClick)="handleClearClick()">
8168
- Clear All
8169
- </ax-button>
8170
- }
9226
+ </ax-suffix>
9227
+ </ax-tag-box>
9228
+ <ng-template #tagTemplate let-item let-index="index">
9229
+ <div class="ax-inline-flex ax-items-center ax-gap-1.5 ax-rounded-md ax-px-3 ax-py-1 ax-text-sm ax-surface">
9230
+ <axp-truncated-breadcrumb
9231
+ [attr.data-color]="item.color"
9232
+ [sections]="getItemPath(item)"
9233
+ [characterLimit]="characterLimit()"
9234
+ [sectionLimit]="sectionLimit()"
9235
+ ></axp-truncated-breadcrumb>
9236
+ <button type="button" (click)="handleRemoveItem($event, index)">
9237
+ <ax-icon class="ax-icon ax-icon-close"></ax-icon>
9238
+ </button>
8171
9239
  </div>
8172
-
8173
- @for (validation of validationRules(); track $index) {
8174
- <ax-validation-rule
8175
- [rule]="validation.rule"
8176
- [message]="validation.options?.message"
8177
- [options]="validation.options"
8178
- ></ax-validation-rule>
8179
- }
8180
- </div>
9240
+ </ng-template>
8181
9241
  `,
8182
9242
  changeDetection: ChangeDetectionStrategy.OnPush,
8183
9243
  imports: [
8184
9244
  CommonModule,
9245
+ FormsModule,
8185
9246
  AXButtonModule,
8186
9247
  AXDecoratorModule,
8187
9248
  AXLoadingModule,
8188
9249
  AXValidationModule,
8189
9250
  AXFormModule,
9251
+ AXTagBoxModule,
8190
9252
  AXTranslationModule,
8191
9253
  AXPTruncatedBreadcrumbComponent,
8192
9254
  ],
8193
9255
  }]
8194
- }] });
9256
+ }], propDecorators: { tagBox: [{ type: i0.ViewChild, args: ['tagBoxComponent', { isSignal: true }] }] } });
8195
9257
 
8196
9258
  var entityCategoryWidgetEdit_component = /*#__PURE__*/Object.freeze({
8197
9259
  __proto__: null,
@@ -8293,7 +9355,7 @@ class AXPEntityCategoryWidgetViewComponent extends AXPValueWidgetComponent {
8293
9355
  <span class="ax-text-muted">---</span>
8294
9356
  }
8295
9357
  }
8296
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i1.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i2$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
9358
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i4.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i2$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
8297
9359
  }
8298
9360
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPEntityCategoryWidgetViewComponent, decorators: [{
8299
9361
  type: Component,
@@ -8787,6 +9849,8 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
8787
9849
  this.entityListTableService = inject(AXPEntityListTableService);
8788
9850
  this.entityListToolbarService = inject(AXPEntityListToolbarService);
8789
9851
  this.deviceService = inject(AXPDeviceService);
9852
+ this.commandService = inject(AXPCommandService);
9853
+ this.eventService = inject(AXPBroadcastEventService);
8790
9854
  this.isMounted = signal(false, ...(ngDevMode ? [{ debugName: "isMounted" }] : []));
8791
9855
  this.entity = signal(null, ...(ngDevMode ? [{ debugName: "entity" }] : []));
8792
9856
  this.listNode = signal(null, ...(ngDevMode ? [{ debugName: "listNode" }] : []));
@@ -8869,17 +9933,21 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
8869
9933
  return actions;
8870
9934
  }, ...(ngDevMode ? [{ debugName: "secondaryActions" }] : []));
8871
9935
  //#region ---- Query Change Handler ----
9936
+ this.queries = undefined;
8872
9937
  this.#effect = effect(() => {
8873
- const queries = this.getValue()?.toolbar;
9938
+ //TODO: this is a temporary solution to handle the query changes; this should be removed when the query changes are handled in the widget core;
9939
+ if (this.getValue()?.toolbar) {
9940
+ this.queries = this.getValue()?.toolbar;
9941
+ }
8874
9942
  const listInstance = this.listWidget()?.instance;
8875
9943
  // const dataSource = this.listWidget()?.options()['dataSource'] as AXDataSource;
8876
9944
  const dataSource = listInstance?.options()?.['dataSource'];
8877
9945
  const isMounted = this.isMounted();
8878
- if (!this.hasRequiredDependencies(dataSource, queries, listInstance)) {
9946
+ if (!this.hasRequiredDependencies(dataSource, this.queries, listInstance)) {
8879
9947
  return;
8880
9948
  }
8881
9949
  untracked(() => {
8882
- this.handleQueryChanges(queries, dataSource, listInstance, isMounted);
9950
+ this.handleQueryChanges(this.queries, dataSource, listInstance, isMounted);
8883
9951
  });
8884
9952
  }, ...(ngDevMode ? [{ debugName: "#effect" }] : []));
8885
9953
  //#endregion
@@ -8912,23 +9980,43 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
8912
9980
  });
8913
9981
  const command = commandName.split('&')[0];
8914
9982
  // const options = await this.evaluateExpressions(action?.options, data);
8915
- await this.workflow.execute(command, {
8916
- entity: this.entitySource(),
8917
- entityInfo: {
8918
- name: this.entity()?.name,
8919
- module: this.entity()?.module,
8920
- title: this.entity()?.title,
8921
- parentKey: this.entity()?.parentKey,
8922
- source: `${this.entity()?.module}.${this.entity()?.name}`,
8923
- },
8924
- data: action?.scope == AXPEntityCommandScope.Selected
8925
- ? this.selectedItems()
8926
- : action?.options?.['process']?.data || null,
8927
- options: action?.options,
8928
- metadata: action?.metadata,
8929
- });
9983
+ if (this.commandService.exists(command)) {
9984
+ await this.commandService.execute(command, {
9985
+ __context__: {
9986
+ entity: this.entitySource(),
9987
+ entityInfo: {
9988
+ name: this.entity()?.name,
9989
+ module: this.entity()?.module,
9990
+ title: this.entity()?.title,
9991
+ parentKey: this.entity()?.parentKey,
9992
+ source: `${this.entity()?.module}.${this.entity()?.name}`,
9993
+ },
9994
+ data: action?.scope == AXPEntityCommandScope.Selected
9995
+ ? this.selectedItems()
9996
+ : action?.options?.['process']?.data || null,
9997
+ options: action?.options,
9998
+ metadata: action?.metadata,
9999
+ },
10000
+ });
10001
+ }
10002
+ else {
10003
+ await this.workflow.execute(command, {
10004
+ entity: this.entitySource(),
10005
+ entityInfo: {
10006
+ name: this.entity()?.name,
10007
+ module: this.entity()?.module,
10008
+ title: this.entity()?.title,
10009
+ parentKey: this.entity()?.parentKey,
10010
+ source: `${this.entity()?.module}.${this.entity()?.name}`,
10011
+ },
10012
+ data: action?.scope == AXPEntityCommandScope.Selected
10013
+ ? this.selectedItems()
10014
+ : action?.options?.['process']?.data || null,
10015
+ options: action?.options,
10016
+ metadata: action?.metadata,
10017
+ });
10018
+ }
8930
10019
  }
8931
- //#region ---- Query Change Handler ----
8932
10020
  #effect;
8933
10021
  /**
8934
10022
  * Validates that all required dependencies are available
@@ -9061,6 +10149,14 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
9061
10149
  this.listWidget()?.instance.call('refresh');
9062
10150
  }
9063
10151
  });
10152
+ this.eventService
10153
+ .listen(AXPEntityEventsKeys.REFRESH_LAYOUT)
10154
+ .pipe(takeUntil(this.destroyed))
10155
+ .subscribe((e) => {
10156
+ if (e.data.name == `${this.entity()?.module}.${this.entity()?.name}`) {
10157
+ this.listWidget()?.instance.call('refresh');
10158
+ }
10159
+ });
9064
10160
  const listWidget = (await this.layoutService.waitForWidget(`${this.entitySource()}-tab-list_table`, 500));
9065
10161
  if (listWidget?.api && typeof listWidget.api === 'function') {
9066
10162
  const onSelectionChange = listWidget.api()['onSelectionChange'];
@@ -9189,7 +10285,7 @@ class AXPEntityListWidgetViewComponent extends AXPValueWidgetComponent {
9189
10285
  ></ng-container>
9190
10286
  }
9191
10287
  </div>
9192
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2$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: AXPWidgetCoreModule }, { kind: "directive", type: i3$1.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i3.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: i3.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i3.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items", "closeParentOnClick", "lockOnLoading"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i4$2.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
10288
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXDecoratorModule }, { kind: "component", type: i3$2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i3$2.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: AXPWidgetCoreModule }, { kind: "directive", type: i3$1.AXPWidgetRendererDirective, selector: "[axp-widget-renderer]", inputs: ["parentNode", "index", "mode", "node"], outputs: ["onOptionsChanged", "onValueChanged"], exportAs: ["widgetRenderer"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i3.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: i3.AXButtonItemComponent, selector: "ax-button-item", inputs: ["color", "disabled", "text", "selected", "divided", "data", "name"], outputs: ["onClick", "onFocus", "onBlur", "disabledChange"] }, { kind: "component", type: i3.AXButtonItemListComponent, selector: "ax-button-item-list", inputs: ["items", "closeParentOnClick", "lockOnLoading"], outputs: ["onItemClick"] }, { kind: "ngmodule", type: AXDropdownModule }, { kind: "component", type: i4$3.AXDropdownPanelComponent, selector: "ax-dropdown-panel", inputs: ["isOpen", "fitParent", "dropdownWidth", "position", "placement", "_target", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i6.AXTranslatorPipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
9193
10289
  }
9194
10290
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPEntityListWidgetViewComponent, decorators: [{
9195
10291
  type: Component,
@@ -9760,7 +10856,7 @@ class AXPLookupWidgetViewComponent extends AXPValueWidgetComponent {
9760
10856
  <span class="ax-text-muted">---</span>
9761
10857
  }
9762
10858
  }
9763
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i1.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i2$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
10859
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: AXLoadingModule }, { kind: "component", type: i4.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "component", type: i2$2.AXBadgeComponent, selector: "ax-badge", inputs: ["color", "look", "text"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
9764
10860
  }
9765
10861
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLookupWidgetViewComponent, decorators: [{
9766
10862
  type: Component,
@@ -10012,6 +11108,7 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
10012
11108
  this.disabled = computed(() => this.options()['disabled'], ...(ngDevMode ? [{ debugName: "disabled" }] : []));
10013
11109
  this.columns = computed(() => this.options()['columns'] ?? [], ...(ngDevMode ? [{ debugName: "columns" }] : []));
10014
11110
  this.textField = computed(() => this.options()['textField'] ?? '', ...(ngDevMode ? [{ debugName: "textField" }] : []));
11111
+ this.hasClearButton = computed(() => this.options()['hasClearButton'] ?? false, ...(ngDevMode ? [{ debugName: "hasClearButton" }] : []));
10015
11112
  this.customFilter = computed(() => this.options()['filter'], ...(ngDevMode ? [{ debugName: "customFilter" }] : []));
10016
11113
  this.multiple = computed(() => (this.options()['multiple'] ?? false), ...(ngDevMode ? [{ debugName: "multiple" }] : []));
10017
11114
  this.look = computed(() => this.options()['look'] ?? 'lookup', ...(ngDevMode ? [{ debugName: "look" }] : []));
@@ -10408,6 +11505,9 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
10408
11505
  [multiple]="multiple()"
10409
11506
  (onValueChanged)="selectBoxValueChange($event)"
10410
11507
  >
11508
+ @if (hasClearButton()) {
11509
+ <ax-clear-button></ax-clear-button>
11510
+ }
10411
11511
  <ax-search-box
10412
11512
  [placeholder]="selectedItems().length ? '' : searchPlaceholderText()"
10413
11513
  (onValueChanged)="handleSearchInputChange($event)"
@@ -10476,9 +11576,9 @@ class AXPLookupWidgetEditComponent extends AXPValueWidgetComponent {
10476
11576
  </ng-template>
10477
11577
  }
10478
11578
  }
10479
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type:
11579
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type:
10480
11580
  //
10481
- AXButtonModule }, { kind: "component", type: i3.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: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2$1.AXDecoratorClearButtonComponent, selector: "ax-clear-button", inputs: ["icon"] }, { kind: "component", type: i2$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: "component", type: i1.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXValidationModule }, { kind: "ngmodule", type: AXFormModule }, { kind: "directive", type: i5$1.AXValidationRuleDirective, selector: "ax-validation-rule", inputs: ["rule", "options", "message", "disabled"] }, { kind: "ngmodule", type: AXTagBoxModule }, { kind: "component", type: i6$1.AXTagBoxComponent, selector: "ax-tag-box", inputs: ["disabled", "tabIndex", "readonly", "value", "state", "name", "id", "placeholder", "allowNull", "type", "look", "addOnComma", "addOnEnter", "valueField", "textField", "readonlyField", "allowDuplicateValues", "tagTemplate"], outputs: ["onBlur", "onFocus", "valueChange", "stateChange", "onValueChanged", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress", "onTagClick", "onTagDblClick", "onTagContextMenu"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXSelectBoxModule }, { kind: "component", type: i7.AXSelectBoxComponent, selector: "ax-select-box", inputs: ["disabled", "readonly", "tabIndex", "placeholder", "minValue", "maxValue", "value", "state", "name", "id", "type", "look", "multiple", "valueField", "textField", "disabledField", "textTemplate", "selectedItems", "isItemTruncated", "showItemTooltip", "itemHeight", "maxVisibleItems", "dataSource", "minRecordsForSearch", "caption", "itemTemplate", "selectedTemplate", "emptyTemplate", "loadingTemplate", "dropdownWidth", "searchBoxAutoFocus"], outputs: ["valueChange", "stateChange", "onValueChanged", "onBlur", "onFocus", "readonlyChange", "disabledChange", "onOpened", "onClosed", "onItemSelected", "onItemClick"] }, { kind: "component", type: AXSearchBoxComponent, selector: "ax-search-box", inputs: ["disabled", "readonly", "tabIndex", "placeholder", "value", "state", "name", "id", "look", "class", "delayTime", "type", "autoSearch"], outputs: ["valueChange", "stateChange", "onValueChanged", "onBlur", "onFocus", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11581
+ AXButtonModule }, { kind: "component", type: i3.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: AXDecoratorModule }, { kind: "component", type: i3$2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i3$2.AXDecoratorClearButtonComponent, selector: "ax-clear-button", inputs: ["icon"] }, { kind: "component", type: i3$2.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: "component", type: i4.AXLoadingComponent, selector: "ax-loading", inputs: ["visible", "type", "context"], outputs: ["visibleChange"] }, { kind: "ngmodule", type: AXValidationModule }, { kind: "ngmodule", type: AXFormModule }, { kind: "directive", type: i5$2.AXValidationRuleDirective, selector: "ax-validation-rule", inputs: ["rule", "options", "message", "disabled"] }, { kind: "ngmodule", type: AXTagBoxModule }, { kind: "component", type: i6$1.AXTagBoxComponent, selector: "ax-tag-box", inputs: ["disabled", "tabIndex", "readonly", "value", "state", "name", "id", "placeholder", "allowNull", "type", "look", "addOnComma", "addOnEnter", "valueField", "textField", "readonlyField", "allowDuplicateValues", "tagTemplate"], outputs: ["onBlur", "onFocus", "valueChange", "stateChange", "onValueChanged", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress", "onTagClick", "onTagDblClick", "onTagContextMenu"] }, { kind: "ngmodule", type: AXTranslationModule }, { kind: "ngmodule", type: AXSelectBoxModule }, { kind: "component", type: i7.AXSelectBoxComponent, selector: "ax-select-box", inputs: ["disabled", "readonly", "tabIndex", "placeholder", "minValue", "maxValue", "value", "state", "name", "id", "type", "look", "multiple", "valueField", "textField", "disabledField", "textTemplate", "selectedItems", "isItemTruncated", "showItemTooltip", "itemHeight", "maxVisibleItems", "dataSource", "minRecordsForSearch", "caption", "itemTemplate", "selectedTemplate", "emptyTemplate", "loadingTemplate", "dropdownWidth", "searchBoxAutoFocus"], outputs: ["valueChange", "stateChange", "onValueChanged", "onBlur", "onFocus", "readonlyChange", "disabledChange", "onOpened", "onClosed", "onItemSelected", "onItemClick"] }, { kind: "component", type: AXSearchBoxComponent, selector: "ax-search-box", inputs: ["disabled", "readonly", "tabIndex", "placeholder", "value", "state", "name", "id", "look", "class", "delayTime", "type", "autoSearch"], outputs: ["valueChange", "stateChange", "onValueChanged", "onBlur", "onFocus", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
10482
11582
  }
10483
11583
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPLookupWidgetEditComponent, decorators: [{
10484
11584
  type: Component,
@@ -10497,6 +11597,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
10497
11597
  [multiple]="multiple()"
10498
11598
  (onValueChanged)="selectBoxValueChange($event)"
10499
11599
  >
11600
+ @if (hasClearButton()) {
11601
+ <ax-clear-button></ax-clear-button>
11602
+ }
10500
11603
  <ax-search-box
10501
11604
  [placeholder]="selectedItems().length ? '' : searchPlaceholderText()"
10502
11605
  (onValueChanged)="handleSearchInputChange($event)"
@@ -10851,7 +11954,7 @@ class AXPWidgetSelectorWidgetEditComponent extends AXPValueWidgetComponent {
10851
11954
  <axp-widget-property-viewer [widget]="selectedWidgetNode()!" (onChanged)="handleChangeWidget($event)">
10852
11955
  </axp-widget-property-viewer>
10853
11956
  }
10854
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXSelectBoxModule }, { kind: "ngmodule", type: AXTextBoxModule }, { kind: "component", type: i2$3.AXTextBoxComponent, selector: "ax-text-box", inputs: ["disabled", "tabIndex", "readonly", "value", "state", "name", "id", "placeholder", "maxLength", "allowNull", "type", "autoComplete", "look", "mask-options", "class"], outputs: ["onBlur", "onFocus", "valueChange", "stateChange", "onValueChanged", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i3.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: AXDecoratorModule }, { kind: "component", type: i2$1.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i2$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: AXValidationModule }, { kind: "ngmodule", type: AXFormModule }, { kind: "directive", type: i5$1.AXValidationRuleDirective, selector: "ax-validation-rule", inputs: ["rule", "options", "message", "disabled"] }, { kind: "component", type: AXPWidgetPropertyViewerComponent, selector: "axp-widget-property-viewer", inputs: ["widget", "mode"], outputs: ["onChanged"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11957
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: AXSelectBoxModule }, { kind: "ngmodule", type: AXTextBoxModule }, { kind: "component", type: i2$3.AXTextBoxComponent, selector: "ax-text-box", inputs: ["disabled", "tabIndex", "readonly", "value", "state", "name", "id", "placeholder", "maxLength", "allowNull", "type", "autoComplete", "look", "mask-options", "class"], outputs: ["onBlur", "onFocus", "valueChange", "stateChange", "onValueChanged", "readonlyChange", "disabledChange", "onKeyDown", "onKeyUp", "onKeyPress"] }, { kind: "ngmodule", type: AXButtonModule }, { kind: "component", type: i3.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: AXDecoratorModule }, { kind: "component", type: i3$2.AXDecoratorIconComponent, selector: "ax-icon", inputs: ["icon"] }, { kind: "component", type: i3$2.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: AXValidationModule }, { kind: "ngmodule", type: AXFormModule }, { kind: "directive", type: i5$2.AXValidationRuleDirective, selector: "ax-validation-rule", inputs: ["rule", "options", "message", "disabled"] }, { kind: "component", type: AXPWidgetPropertyViewerComponent, selector: "axp-widget-property-viewer", inputs: ["widget", "mode"], outputs: ["onChanged"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
10855
11958
  }
10856
11959
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPWidgetSelectorWidgetEditComponent, decorators: [{
10857
11960
  type: Component,
@@ -11370,7 +12473,7 @@ class AXPEntityModule {
11370
12473
  },
11371
12474
  });
11372
12475
  }
11373
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPEntityModule, deps: [{ token: i1$2.AXPAppStartUpService }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.NgModule }); }
12476
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPEntityModule, deps: [{ token: i1$1.AXPAppStartUpService }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.NgModule }); }
11374
12477
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.12", ngImport: i0, type: AXPEntityModule, imports: [RouterModule, i2$4.AXPWorkflowModule, i3$1.AXPWidgetCoreModule] }); }
11375
12478
  static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXPEntityModule, providers: [
11376
12479
  {
@@ -11562,7 +12665,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
11562
12665
  ]),
11563
12666
  ],
11564
12667
  }]
11565
- }], ctorParameters: () => [{ type: i1$2.AXPAppStartUpService }, { type: i0.Injector }] });
12668
+ }], ctorParameters: () => [{ type: i1$1.AXPAppStartUpService }, { type: i0.Injector }] });
11566
12669
 
11567
12670
  //#endregion
11568
12671
  //#region ---- Get Entity Details Query ----
@@ -11716,7 +12819,7 @@ function entityDetailsCreateActions(parentId) {
11716
12819
  return {
11717
12820
  title: '@general:actions.create.title',
11718
12821
  command: {
11719
- name: 'create-entity',
12822
+ name: 'Entity:Create',
11720
12823
  options: {
11721
12824
  process: {
11722
12825
  redirect: false,
@@ -11793,7 +12896,7 @@ function entityDetailsReferenceCreateActions(type) {
11793
12896
  {
11794
12897
  title: '@general:actions.create.title',
11795
12898
  command: {
11796
- name: 'create-entity',
12899
+ name: 'Entity:Create',
11797
12900
  options: {
11798
12901
  process: {
11799
12902
  redirect: false,