@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.
- package/fesm2022/acorex-platform-layout-components.mjs +2 -2
- package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-entity.mjs +1617 -514
- package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-views.mjs +4 -2
- package/fesm2022/acorex-platform-layout-views.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-widgets.mjs +14 -13
- package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
- package/fesm2022/{acorex-platform-themes-default-entity-master-list-view.component-D5K0XUqM.mjs → acorex-platform-themes-default-entity-master-list-view.component-Cym8pq0v.mjs} +27 -6
- package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-Cym8pq0v.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-default.mjs +2 -2
- package/layout/entity/index.d.ts +107 -29
- package/layout/widgets/index.d.ts +2 -1
- package/package.json +5 -5
- package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-D5K0XUqM.mjs.map +0 -1
|
@@ -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$
|
|
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,
|
|
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,
|
|
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
|
|
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/
|
|
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
|
|
41
|
+
import * as i4$2 from '@acorex/components/search-box';
|
|
40
42
|
import { AXSearchBoxModule, AXSearchBoxComponent } from '@acorex/components/search-box';
|
|
41
|
-
import * as
|
|
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$
|
|
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$
|
|
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(
|
|
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:
|
|
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
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
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
|
-
|
|
5998
|
-
|
|
5999
|
-
const
|
|
6000
|
-
|
|
6001
|
-
|
|
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
|
-
|
|
6074
|
-
|
|
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
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
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
|
|
6544
|
-
this.selectedNodeIds.
|
|
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
|
-
*
|
|
6554
|
-
*
|
|
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
|
|
6557
|
-
if (!
|
|
6650
|
+
async handleCheckboxChange(nodeId, checked) {
|
|
6651
|
+
if (!nodeId || nodeId === 'all') {
|
|
6558
6652
|
return;
|
|
6559
6653
|
}
|
|
6560
|
-
|
|
6561
|
-
const
|
|
6562
|
-
if (
|
|
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
|
-
|
|
6567
|
-
const
|
|
6568
|
-
|
|
6569
|
-
|
|
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
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
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
|
-
|
|
6595
|
-
|
|
6708
|
+
finally {
|
|
6709
|
+
this.isUpdatingSelection = false;
|
|
6596
6710
|
}
|
|
6597
6711
|
}
|
|
6598
6712
|
/**
|
|
6599
|
-
*
|
|
6600
|
-
* This ensures pre-selected nodes appear selected when the tree is rendered.
|
|
6713
|
+
* Collects all descendant node IDs recursively
|
|
6601
6714
|
*/
|
|
6602
|
-
|
|
6603
|
-
|
|
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
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
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
|
-
*
|
|
6753
|
+
* Deselects a node and recursively deselects all its children
|
|
6629
6754
|
*/
|
|
6630
|
-
|
|
6631
|
-
const
|
|
6632
|
-
if (
|
|
6633
|
-
|
|
6755
|
+
async deselectNodeAndChildren(nodeId) {
|
|
6756
|
+
const treeComponent = this.tree();
|
|
6757
|
+
if (!treeComponent) {
|
|
6758
|
+
return;
|
|
6634
6759
|
}
|
|
6635
|
-
//
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
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
|
-
*
|
|
6790
|
+
* Recursively deselects all children of a parent node
|
|
6645
6791
|
*/
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6667
|
-
|
|
6668
|
-
|
|
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
|
-
|
|
6677
|
-
|
|
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
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
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
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
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
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
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
|
|
6916
|
-
<
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
|
|
6921
|
-
|
|
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]="
|
|
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
|
-
|
|
6948
|
-
|
|
6949
|
-
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
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"
|
|
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-
|
|
6967
|
-
<
|
|
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:
|
|
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
|
-
|
|
6997
|
-
|
|
6998
|
-
|
|
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
|
|
7025
|
-
<
|
|
7026
|
-
|
|
7027
|
-
|
|
7028
|
-
|
|
7029
|
-
|
|
7030
|
-
|
|
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]="
|
|
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
|
-
|
|
7057
|
-
|
|
7058
|
-
|
|
7059
|
-
|
|
7060
|
-
|
|
7061
|
-
|
|
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"
|
|
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-
|
|
7076
|
-
<
|
|
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 ----
|
|
7117
|
-
this.
|
|
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.
|
|
7122
|
-
this.valueField = this.options['valueField'] ?? 'id';
|
|
7123
|
-
this.textField = this.options['textField'] ?? 'title';
|
|
7124
|
-
this.
|
|
7125
|
-
this.
|
|
7126
|
-
|
|
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 ----
|
|
7129
|
-
this.
|
|
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 ----
|
|
7133
|
-
this.
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
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.
|
|
7168
|
-
if (
|
|
8104
|
+
const entityKey = this.entity();
|
|
8105
|
+
if (entityKey && id) {
|
|
7169
8106
|
await this.entityDetailPopoverService.show(this.host, {
|
|
7170
|
-
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.
|
|
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
|
-
|
|
7186
|
-
|
|
7187
|
-
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
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
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
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
|
-
|
|
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
|
-
|
|
8160
|
+
itemObj = item;
|
|
7206
8161
|
}
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
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
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
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
|
-
|
|
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"
|
|
7220
|
-
<div class="ax-flex ax-
|
|
7221
|
-
@for (item of visibleItems(); track
|
|
7222
|
-
<ax-
|
|
7223
|
-
|
|
7224
|
-
|
|
7225
|
-
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
|
|
7229
|
-
|
|
7230
|
-
|
|
7231
|
-
|
|
7232
|
-
|
|
7233
|
-
|
|
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:
|
|
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-
|
|
7253
|
-
@for (item of visibleItems(); track
|
|
7254
|
-
<ax-
|
|
7255
|
-
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
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,
|
|
8294
|
+
imports: [CommonModule, AXBadgeModule, AXDecoratorModule],
|
|
7280
8295
|
inputs: ['rawValue', 'rowData'],
|
|
7281
8296
|
}]
|
|
7282
|
-
}], propDecorators: {
|
|
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:
|
|
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
|
-
|
|
7761
|
-
|
|
7762
|
-
|
|
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
|
-
<
|
|
8079
|
-
|
|
8080
|
-
|
|
8081
|
-
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
|
|
8086
|
-
|
|
8087
|
-
|
|
8088
|
-
|
|
8089
|
-
|
|
8090
|
-
|
|
8091
|
-
|
|
8092
|
-
|
|
8093
|
-
|
|
8094
|
-
|
|
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
|
-
<
|
|
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-
|
|
9167
|
+
<ax-icon icon="far fa-search"></ax-icon>
|
|
8108
9168
|
}
|
|
8109
9169
|
</ax-button>
|
|
8110
|
-
|
|
8111
|
-
|
|
8112
|
-
|
|
8113
|
-
|
|
8114
|
-
|
|
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
|
-
|
|
8126
|
-
|
|
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
|
-
<
|
|
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-
|
|
9223
|
+
<ax-icon icon="far fa-search"></ax-icon>
|
|
8163
9224
|
}
|
|
8164
9225
|
</ax-button>
|
|
8165
|
-
|
|
8166
|
-
|
|
8167
|
-
|
|
8168
|
-
|
|
8169
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
8916
|
-
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
|
|
8922
|
-
|
|
8923
|
-
|
|
8924
|
-
|
|
8925
|
-
|
|
8926
|
-
|
|
8927
|
-
|
|
8928
|
-
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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$
|
|
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$
|
|
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: '
|
|
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: '
|
|
12899
|
+
name: 'Entity:Create',
|
|
11797
12900
|
options: {
|
|
11798
12901
|
process: {
|
|
11799
12902
|
redirect: false,
|