@acorex/platform 20.7.8 → 20.7.10
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/common/index.d.ts +26 -5
- package/core/index.d.ts +13 -7
- package/fesm2022/acorex-platform-common.mjs +5 -1
- package/fesm2022/acorex-platform-common.mjs.map +1 -1
- package/fesm2022/acorex-platform-core.mjs +20 -11
- package/fesm2022/acorex-platform-core.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-builder.mjs +31 -4
- package/fesm2022/acorex-platform-layout-builder.mjs.map +1 -1
- package/fesm2022/{acorex-platform-layout-components-binding-expression-editor-popup.component-ZnTG7wlJ.mjs → acorex-platform-layout-components-binding-expression-editor-popup.component-Cb6Lk4Ch.mjs} +5 -5
- package/fesm2022/{acorex-platform-layout-components-binding-expression-editor-popup.component-ZnTG7wlJ.mjs.map → acorex-platform-layout-components-binding-expression-editor-popup.component-Cb6Lk4Ch.mjs.map} +1 -1
- package/fesm2022/acorex-platform-layout-components.mjs +913 -36
- package/fesm2022/acorex-platform-layout-components.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-entity.mjs +471 -789
- package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
- package/fesm2022/{acorex-platform-layout-widgets-repeater-widget-column.component-fcCirNxz.mjs → acorex-platform-layout-widgets-repeater-widget-column.component-DnhR00cH.mjs} +2 -2
- package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-DnhR00cH.mjs.map +1 -0
- package/fesm2022/acorex-platform-layout-widgets.mjs +219 -327
- package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
- package/fesm2022/{acorex-platform-themes-default-entity-master-list-view.component-DzWjSMSK.mjs → acorex-platform-themes-default-entity-master-list-view.component-HBr-ZTSt.mjs} +20 -5
- package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-HBr-ZTSt.mjs.map +1 -0
- package/fesm2022/acorex-platform-themes-default.mjs +2 -2
- package/layout/builder/index.d.ts +25 -14
- package/layout/components/index.d.ts +295 -2
- package/layout/entity/index.d.ts +59 -58
- package/layout/widgets/index.d.ts +38 -32
- package/package.json +5 -5
- package/fesm2022/acorex-platform-layout-widgets-repeater-widget-column.component-fcCirNxz.mjs.map +0 -1
- package/fesm2022/acorex-platform-themes-default-entity-master-list-view.component-DzWjSMSK.mjs.map +0 -1
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { AXToastService } from '@acorex/components/toast';
|
|
2
|
-
import { AXPlatform } from '@acorex/core/platform';
|
|
3
2
|
import * as i6 from '@acorex/core/translation';
|
|
4
3
|
import { AXTranslationService, AXTranslationModule } from '@acorex/core/translation';
|
|
5
|
-
import * as i4$1 from '@acorex/platform/common';
|
|
6
|
-
import { AXPSettingsService, AXPFilterOperatorMiddlewareService, AXPEntityCommandScope, getEntityInfo, AXPRefreshEvent, AXPReloadEvent, AXPCommonSettings, AXPEntityQueryType, AXPCleanNestedFilters, AXPWorkflowNavigateAction, AXPToastAction, AXP_SEARCH_DEFINITION_PROVIDER } from '@acorex/platform/common';
|
|
7
|
-
import { AXPDeviceService, AXPBroadcastEventService, applyFilterArray, applySortArray, resolveActionLook, AXPExpressionEvaluatorService, AXPDistributedEventListenerService, AXPPlatformScope, AXPColumnWidthService, AXHighlightService, extractValue, setSmart, getChangedPaths, defaultColumnWidthProvider, AXP_COLUMN_WIDTH_PROVIDER, AXP_DATASOURCE_DEFINITION_PROVIDER, AXPSystemActionType } from '@acorex/platform/core';
|
|
8
4
|
import * as i0 from '@angular/core';
|
|
9
5
|
import { InjectionToken, inject, Injector, runInInjectionContext, Injectable, input, viewChild, signal, ElementRef, ChangeDetectionStrategy, Component, ApplicationRef, EnvironmentInjector, createComponent, computed, ChangeDetectorRef, effect, Input, afterNextRender, untracked, ViewEncapsulation, viewChildren, linkedSignal, HostBinding, output, NgModule } from '@angular/core';
|
|
10
6
|
import { Subject, takeUntil } from 'rxjs';
|
|
11
7
|
import { AXPLayoutBuilderService } from '@acorex/platform/layout/builder';
|
|
8
|
+
import { AXPDeviceService, AXPBroadcastEventService, applyFilterArray, applySortArray, resolveActionLook, AXPExpressionEvaluatorService, AXPDistributedEventListenerService, AXPPlatformScope, AXPColumnWidthService, AXHighlightService, extractValue, setSmart, getChangedPaths, defaultColumnWidthProvider, AXP_COLUMN_WIDTH_PROVIDER, AXP_DATASOURCE_DEFINITION_PROVIDER, AXPSystemActionType } from '@acorex/platform/core';
|
|
12
9
|
import { merge, castArray, get, cloneDeep, set, orderBy, isNil, isEmpty, isEqual } from 'lodash-es';
|
|
10
|
+
import * as i4$1 from '@acorex/platform/common';
|
|
11
|
+
import { AXPSettingsService, AXPFilterOperatorMiddlewareService, AXPEntityCommandScope, getEntityInfo, AXPRefreshEvent, AXPReloadEvent, AXPCommonSettings, AXPEntityQueryType, AXPCleanNestedFilters, AXPWorkflowNavigateAction, AXPToastAction, AXP_SEARCH_DEFINITION_PROVIDER } from '@acorex/platform/common';
|
|
13
12
|
import { AXPSessionService, AXPAuthGuard } from '@acorex/platform/auth';
|
|
14
13
|
import { Router, RouterModule, ROUTES } from '@angular/router';
|
|
15
14
|
import * as i3 from '@acorex/components/button';
|
|
@@ -32,6 +31,7 @@ import { moveItemInArray } from '@angular/cdk/drag-drop';
|
|
|
32
31
|
import { AXDialogService } from '@acorex/components/dialog';
|
|
33
32
|
import { AXLoadingDialogService } from '@acorex/components/loading-dialog';
|
|
34
33
|
import { AXPopupService } from '@acorex/components/popup';
|
|
34
|
+
import { AXPlatform } from '@acorex/core/platform';
|
|
35
35
|
import * as i2$1 from '@acorex/components/badge';
|
|
36
36
|
import { AXBadgeModule } from '@acorex/components/badge';
|
|
37
37
|
import { AXCheckBoxModule } from '@acorex/components/check-box';
|
|
@@ -728,6 +728,10 @@ class PropertyFilter {
|
|
|
728
728
|
this.externalActionsDelegate = delegate;
|
|
729
729
|
return this;
|
|
730
730
|
}
|
|
731
|
+
onAction(handler) {
|
|
732
|
+
this.onActionHandler = handler;
|
|
733
|
+
return this;
|
|
734
|
+
}
|
|
731
735
|
field(groupId, path, delegate) {
|
|
732
736
|
const list = this.extraFieldsByGroup.get(groupId) ?? [];
|
|
733
737
|
list.push({ path, delegate });
|
|
@@ -1042,6 +1046,9 @@ class PropertyFilter {
|
|
|
1042
1046
|
// For create/update, show cancel + submit
|
|
1043
1047
|
d.setActions((a) => a.cancel().submit());
|
|
1044
1048
|
}
|
|
1049
|
+
if (this.onActionHandler) {
|
|
1050
|
+
d.onAction(this.onActionHandler);
|
|
1051
|
+
}
|
|
1045
1052
|
});
|
|
1046
1053
|
return dialog;
|
|
1047
1054
|
}
|
|
@@ -1346,42 +1353,24 @@ function sortMergedSections(mainSections, extraSections, mainSectionIds) {
|
|
|
1346
1353
|
return entityOrderDiff;
|
|
1347
1354
|
return (a.order ?? 0) - (b.order ?? 0);
|
|
1348
1355
|
});
|
|
1349
|
-
// Build index map for main sections to preserve original order when no explicit order is specified
|
|
1350
|
-
const mainSectionIndexMap = new Map();
|
|
1351
|
-
mainSections.forEach((s, idx) => mainSectionIndexMap.set(s.id, idx));
|
|
1352
1356
|
// Middle block: main sections + middle-only sections
|
|
1353
|
-
// Sort by
|
|
1354
|
-
|
|
1355
|
-
const
|
|
1356
|
-
|
|
1357
|
-
const
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
return a.order - b.order;
|
|
1361
|
-
}
|
|
1362
|
-
// If neither has order, preserve original array position for main sections
|
|
1363
|
-
if (!aHasOrder && !bHasOrder) {
|
|
1364
|
-
const aIdx = mainSectionIndexMap.get(a.id) ?? Infinity;
|
|
1365
|
-
const bIdx = mainSectionIndexMap.get(b.id) ?? Infinity;
|
|
1366
|
-
return aIdx - bIdx;
|
|
1367
|
-
}
|
|
1368
|
-
// If only one has order, the one with order comes first (explicit ordering takes precedence)
|
|
1369
|
-
if (aHasOrder)
|
|
1370
|
-
return -1;
|
|
1371
|
-
return 1;
|
|
1357
|
+
// Sort by order (undefined order is treated as 0)
|
|
1358
|
+
const middleBlock = [...mainSections, ...middleSections];
|
|
1359
|
+
const sortedMiddleBlock = middleBlock.sort((a, b) => {
|
|
1360
|
+
// Treat undefined order as 0 for sorting
|
|
1361
|
+
const aOrder = a.order ?? 0;
|
|
1362
|
+
const bOrder = b.order ?? 0;
|
|
1363
|
+
return aOrder - bOrder;
|
|
1372
1364
|
});
|
|
1373
|
-
return [...beforeSections, ...
|
|
1365
|
+
return [...beforeSections, ...sortedMiddleBlock, ...afterSections];
|
|
1374
1366
|
}
|
|
1375
1367
|
|
|
1376
1368
|
class AXPCreateEntityCommand {
|
|
1377
1369
|
constructor() {
|
|
1378
1370
|
this.entityForm = inject(AXPEntityFormBuilderService);
|
|
1379
|
-
this.settingsService = inject(AXPSettingsService);
|
|
1380
1371
|
this.entityService = inject(AXPEntityDefinitionRegistryService);
|
|
1381
1372
|
this.toastService = inject(AXToastService);
|
|
1382
1373
|
this.translationService = inject(AXTranslationService);
|
|
1383
|
-
this.eventService = inject(AXPBroadcastEventService);
|
|
1384
|
-
this.platform = inject(AXPlatform);
|
|
1385
1374
|
this.context = {};
|
|
1386
1375
|
}
|
|
1387
1376
|
async execute(input) {
|
|
@@ -1404,100 +1393,83 @@ class AXPCreateEntityCommand {
|
|
|
1404
1393
|
};
|
|
1405
1394
|
}
|
|
1406
1395
|
const entityRef = await this.entityService.resolve(moduleName, entityName);
|
|
1407
|
-
let
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
}
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1396
|
+
let chain = this.entityForm.entity(`${moduleName}.${entityName}`).create(data);
|
|
1397
|
+
chain.actions((actions) => {
|
|
1398
|
+
actions.cancel('@general:actions.cancel.title');
|
|
1399
|
+
actions.submit('@general:actions.create.title');
|
|
1400
|
+
});
|
|
1401
|
+
if (excludeProperties && excludeProperties.length > 0) {
|
|
1402
|
+
chain = chain.exclude(...excludeProperties);
|
|
1403
|
+
}
|
|
1404
|
+
if (includeProperties && includeProperties.length > 0) {
|
|
1405
|
+
chain = chain.include(...includeProperties);
|
|
1406
|
+
}
|
|
1407
|
+
// Set dialog title: use decoration.header.title if available, otherwise use entityInfo.title
|
|
1408
|
+
if (headerTitle) {
|
|
1409
|
+
chain = chain.title(headerTitle);
|
|
1410
|
+
}
|
|
1411
|
+
else if (entityInfo?.title) {
|
|
1412
|
+
const createText = await this.translationService.translateAsync('@general:actions.create.title');
|
|
1413
|
+
const translatedTitle = await this.translationService.translateAsync(entityInfo.title);
|
|
1414
|
+
chain = chain.title(`${createText} ${translatedTitle}`);
|
|
1415
|
+
}
|
|
1416
|
+
// Set dialog size: prioritize layout.size, then dialogSize from input
|
|
1417
|
+
const finalSize = layoutSize || dialogSize;
|
|
1418
|
+
if (finalSize) {
|
|
1419
|
+
chain.size(finalSize);
|
|
1420
|
+
}
|
|
1421
|
+
const result = await chain
|
|
1422
|
+
.onAction(async (dialogRef) => {
|
|
1423
|
+
if (dialogRef.action() === 'cancel') {
|
|
1424
|
+
return { success: false };
|
|
1425
|
+
}
|
|
1426
|
+
const createFn = entityRef.commands?.create?.execute;
|
|
1427
|
+
if (!createFn) {
|
|
1428
|
+
const msg = await this.translationService.translateAsync('@general:messages.entity.create-command-unavailable');
|
|
1429
|
+
this.toastService.show({
|
|
1430
|
+
color: 'danger',
|
|
1431
|
+
title: await this.translationService.translateAsync('@general:messages.generic.error.title'),
|
|
1432
|
+
content: msg,
|
|
1433
|
+
});
|
|
1434
|
+
throw new Error(msg);
|
|
1446
1435
|
}
|
|
1447
|
-
|
|
1448
|
-
|
|
1436
|
+
dialogRef.setLoading(true);
|
|
1437
|
+
try {
|
|
1449
1438
|
const context = dialogRef.context();
|
|
1450
|
-
const createFn = entityRef.commands?.create?.execute;
|
|
1451
|
-
if (!createFn) {
|
|
1452
|
-
return {
|
|
1453
|
-
success: false,
|
|
1454
|
-
message: {
|
|
1455
|
-
text: await this.translationService.translateAsync('@general:messages.entity.create-command-unavailable'),
|
|
1456
|
-
},
|
|
1457
|
-
};
|
|
1458
|
-
}
|
|
1459
1439
|
const result = await createFn(context);
|
|
1460
1440
|
if (result) {
|
|
1461
|
-
dialogRef.close();
|
|
1462
1441
|
return {
|
|
1463
1442
|
success: true,
|
|
1464
|
-
data: result,
|
|
1443
|
+
data: result.data ?? result,
|
|
1465
1444
|
message: {
|
|
1466
1445
|
text: await this.translationService.translateAsync('@general:messages.generic.success.description'),
|
|
1467
1446
|
},
|
|
1468
1447
|
};
|
|
1469
1448
|
}
|
|
1470
1449
|
else {
|
|
1471
|
-
return
|
|
1450
|
+
return {
|
|
1472
1451
|
success: false,
|
|
1473
1452
|
message: {
|
|
1474
|
-
text: await this.translationService.translateAsync('@general:messages.entity.
|
|
1453
|
+
text: await this.translationService.translateAsync('@general:messages.entity.create-failed'),
|
|
1475
1454
|
},
|
|
1476
|
-
}
|
|
1455
|
+
};
|
|
1477
1456
|
}
|
|
1478
1457
|
}
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
message: {
|
|
1490
|
-
text: error instanceof Error
|
|
1491
|
-
? error.message
|
|
1492
|
-
: await this.translationService.translateAsync('@general:messages.entity.create-failed'),
|
|
1493
|
-
},
|
|
1494
|
-
};
|
|
1495
|
-
}
|
|
1496
|
-
finally {
|
|
1497
|
-
if (dialogRef) {
|
|
1458
|
+
catch (e) {
|
|
1459
|
+
const errorMsg = e.message ?? (await this.translationService.translateAsync('@general:messages.entity.create-failed'));
|
|
1460
|
+
this.toastService.show({
|
|
1461
|
+
color: 'danger',
|
|
1462
|
+
title: await this.translationService.translateAsync('@general:messages.generic.error.title'),
|
|
1463
|
+
content: errorMsg,
|
|
1464
|
+
});
|
|
1465
|
+
throw e;
|
|
1466
|
+
}
|
|
1467
|
+
finally {
|
|
1498
1468
|
dialogRef.setLoading(false);
|
|
1499
1469
|
}
|
|
1500
|
-
}
|
|
1470
|
+
})
|
|
1471
|
+
.show();
|
|
1472
|
+
return result;
|
|
1501
1473
|
}
|
|
1502
1474
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPCreateEntityCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1503
1475
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPCreateEntityCommand, providedIn: 'root' }); }
|
|
@@ -2071,7 +2043,7 @@ class AXPEntityDetailPopoverComponent {
|
|
|
2071
2043
|
return importantProperties;
|
|
2072
2044
|
}
|
|
2073
2045
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPEntityDetailPopoverComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2074
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AXPEntityDetailPopoverComponent, isStandalone: true, selector: "axp-entity-detail-popover", inputs: { entity: { classPropertyName: "entity", publicName: "entity", isSignal: true, isRequired: true, transformFunction: null }, entityId: { classPropertyName: "entityId", publicName: "entityId", isSignal: true, isRequired: true, transformFunction: null }, textField: { classPropertyName: "textField", publicName: "textField", isSignal: true, isRequired: false, transformFunction: null }, valueField: { classPropertyName: "valueField", publicName: "valueField", isSignal: true, isRequired: false, transformFunction: null }, item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: false, transformFunction: null }, breadcrumb: { classPropertyName: "breadcrumb", publicName: "breadcrumb", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "detailPopover", first: true, predicate: ["detailPopover"], descendants: true, isSignal: true }], ngImport: i0, template: "<ax-popover [openOn]=\"'manual'\" #detailPopover (openChange)=\"onDetailPopoverOpenChange($event)\">\n <div class=\"ax-lightest-surface ax-border ax-rounded-lg ax-shadow-lg ax-p-4 ax-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 @if (breadcrumb()) {\n <div class=\"ax-text-xs ax-text-neutral-500 ax-mt-1\">{{ breadcrumb() }}</div>\n }\n </div>\n @if (isLoadingDetails()) {\n <div class=\"ax-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", "repositionOnScroll", "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", "onLoad"], 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 }); }
|
|
2046
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AXPEntityDetailPopoverComponent, isStandalone: true, selector: "axp-entity-detail-popover", inputs: { entity: { classPropertyName: "entity", publicName: "entity", isSignal: true, isRequired: true, transformFunction: null }, entityId: { classPropertyName: "entityId", publicName: "entityId", isSignal: true, isRequired: true, transformFunction: null }, textField: { classPropertyName: "textField", publicName: "textField", isSignal: true, isRequired: false, transformFunction: null }, valueField: { classPropertyName: "valueField", publicName: "valueField", isSignal: true, isRequired: false, transformFunction: null }, item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: false, transformFunction: null }, breadcrumb: { classPropertyName: "breadcrumb", publicName: "breadcrumb", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "detailPopover", first: true, predicate: ["detailPopover"], descendants: true, isSignal: true }], ngImport: i0, template: "<ax-popover [openOn]=\"'manual'\" #detailPopover (openChange)=\"onDetailPopoverOpenChange($event)\">\n <div class=\"ax-lightest-surface ax-border ax-rounded-lg ax-shadow-lg ax-p-4 ax-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 @if (breadcrumb()) {\n <div class=\"ax-text-xs ax-text-neutral-500 ax-mt-1\">{{ breadcrumb() }}</div>\n }\n </div>\n @if (isLoadingDetails()) {\n <div class=\"ax-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", "forceDisableActionSheetStyle", "disabled", "offsetX", "offsetY", "target", "placement", "content", "openOn", "closeOn", "hasBackdrop", "openAfter", "closeAfter", "repositionOnScroll", "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", "onLoad"], 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 }); }
|
|
2075
2047
|
}
|
|
2076
2048
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPEntityDetailPopoverComponent, decorators: [{
|
|
2077
2049
|
type: Component,
|
|
@@ -2919,6 +2891,12 @@ class AXPEntityMasterListViewModel {
|
|
|
2919
2891
|
key: 'id',
|
|
2920
2892
|
});
|
|
2921
2893
|
this.selectedItems = signal([], ...(ngDevMode ? [{ debugName: "selectedItems" }] : []));
|
|
2894
|
+
/**
|
|
2895
|
+
* Selected category from the category sidebar (left panel).
|
|
2896
|
+
* Used to set default category when creating new entities via command.
|
|
2897
|
+
* Structure: { id: string; title: string } or null when "All Items" is selected.
|
|
2898
|
+
*/
|
|
2899
|
+
this.selectedCategory = signal(null, ...(ngDevMode ? [{ debugName: "selectedCategory" }] : []));
|
|
2922
2900
|
this.parentKey = computed(() => {
|
|
2923
2901
|
return this.entityDef.parentKey;
|
|
2924
2902
|
}, ...(ngDevMode ? [{ debugName: "parentKey" }] : []));
|
|
@@ -3189,6 +3167,13 @@ class AXPEntityMasterListViewModel {
|
|
|
3189
3167
|
clearSelection() {
|
|
3190
3168
|
this.selectedItems.set([]);
|
|
3191
3169
|
}
|
|
3170
|
+
/**
|
|
3171
|
+
* Sets the selected category from the category sidebar.
|
|
3172
|
+
* Call with null to clear (e.g. when "All Items" is selected).
|
|
3173
|
+
*/
|
|
3174
|
+
setSelectedCategory(category) {
|
|
3175
|
+
this.selectedCategory.set(category);
|
|
3176
|
+
}
|
|
3192
3177
|
async getPrimaryActions() {
|
|
3193
3178
|
const scope = {};
|
|
3194
3179
|
const actions = await Promise.all(this.allActions()
|
|
@@ -3352,6 +3337,8 @@ class AXPEntityMasterListViewModel {
|
|
|
3352
3337
|
});
|
|
3353
3338
|
const command = commandName.split('&')[0];
|
|
3354
3339
|
const options = await this.evaluateExpressions(action?.options, data);
|
|
3340
|
+
const baseData = action?.scope == AXPEntityCommandScope.Selected ? this.selectedItems() : data;
|
|
3341
|
+
const commandData = this.mergeDefaultCategoryForCreate(command, baseData);
|
|
3355
3342
|
if (this.workflow.exists(command)) {
|
|
3356
3343
|
await this.workflow.execute(command, {
|
|
3357
3344
|
entity: getEntityInfo(this.entityDef).source,
|
|
@@ -3362,7 +3349,7 @@ class AXPEntityMasterListViewModel {
|
|
|
3362
3349
|
parentKey: this.entityDef.parentKey,
|
|
3363
3350
|
source: `${this.entityDef.module}.${this.entityDef.name}`,
|
|
3364
3351
|
},
|
|
3365
|
-
data:
|
|
3352
|
+
data: commandData,
|
|
3366
3353
|
options: options,
|
|
3367
3354
|
metadata: action?.metadata,
|
|
3368
3355
|
});
|
|
@@ -3370,7 +3357,7 @@ class AXPEntityMasterListViewModel {
|
|
|
3370
3357
|
else {
|
|
3371
3358
|
const commandOptions = {
|
|
3372
3359
|
__context__: {
|
|
3373
|
-
data:
|
|
3360
|
+
data: commandData,
|
|
3374
3361
|
entity: getEntityInfo(this.entityDef).source,
|
|
3375
3362
|
entityInfo: {
|
|
3376
3363
|
name: this.entityDef.name,
|
|
@@ -3398,6 +3385,41 @@ class AXPEntityMasterListViewModel {
|
|
|
3398
3385
|
// });
|
|
3399
3386
|
}
|
|
3400
3387
|
}
|
|
3388
|
+
/**
|
|
3389
|
+
* Merges default category from selected category in sidebar when executing create command.
|
|
3390
|
+
* For Entity:Create and create-entity, if entity has category plugin and a category is selected,
|
|
3391
|
+
* pre-fills categoryIds and categories for the create form.
|
|
3392
|
+
*/
|
|
3393
|
+
mergeDefaultCategoryForCreate(command, baseData) {
|
|
3394
|
+
const isCreateCommand = command === 'Entity:Create' || command === 'create-entity';
|
|
3395
|
+
if (!isCreateCommand) {
|
|
3396
|
+
return baseData;
|
|
3397
|
+
}
|
|
3398
|
+
const categoryExt = this.entityDef.extensions?.category;
|
|
3399
|
+
if (!categoryExt) {
|
|
3400
|
+
return baseData;
|
|
3401
|
+
}
|
|
3402
|
+
const selectedCat = this.selectedCategory();
|
|
3403
|
+
if (!selectedCat) {
|
|
3404
|
+
return baseData;
|
|
3405
|
+
}
|
|
3406
|
+
const propName = categoryExt.propertyName || 'categoryIds';
|
|
3407
|
+
const valueField = categoryExt.valueField ?? 'id';
|
|
3408
|
+
const textField = categoryExt.textField ?? 'title';
|
|
3409
|
+
const idVal = selectedCat[valueField] ?? selectedCat.id;
|
|
3410
|
+
const titleVal = selectedCat[textField] ?? selectedCat.title;
|
|
3411
|
+
const defaultCategoryData = {
|
|
3412
|
+
[propName]: [idVal],
|
|
3413
|
+
categories: [{ [valueField]: idVal, [textField]: titleVal }],
|
|
3414
|
+
};
|
|
3415
|
+
if (baseData == null || (Array.isArray(baseData) && baseData.length === 0)) {
|
|
3416
|
+
return defaultCategoryData;
|
|
3417
|
+
}
|
|
3418
|
+
if (typeof baseData === 'object' && !Array.isArray(baseData)) {
|
|
3419
|
+
return { ...baseData, ...defaultCategoryData };
|
|
3420
|
+
}
|
|
3421
|
+
return baseData;
|
|
3422
|
+
}
|
|
3401
3423
|
async execute(command) {
|
|
3402
3424
|
switch (command?.name) {
|
|
3403
3425
|
case 'navigate':
|
|
@@ -5014,7 +5036,8 @@ class AXPPageListConverter extends AXPBaseRelatedEntityConverter {
|
|
|
5014
5036
|
};
|
|
5015
5037
|
// Don't evaluate actions here - keep expression strings for lazy evaluation at execution time
|
|
5016
5038
|
// This ensures actions use the latest context data when executed
|
|
5017
|
-
const actions = relatedEntity?.actions ?? [];
|
|
5039
|
+
// const actions = relatedEntity?.actions ?? [];
|
|
5040
|
+
const actions = await evaluateExpressions(relatedEntity?.actions ?? []);
|
|
5018
5041
|
const filters = await Promise.all(relatedEntity.conditions?.map(async (c) => {
|
|
5019
5042
|
const value = await evaluateExpressions(c.value);
|
|
5020
5043
|
return {
|
|
@@ -5238,7 +5261,7 @@ class AXPTabListConverter extends AXPBaseRelatedEntityConverter {
|
|
|
5238
5261
|
}) ?? []);
|
|
5239
5262
|
// Don't evaluate actions here - keep expression strings for lazy evaluation at execution time
|
|
5240
5263
|
// This ensures actions use the latest context data when executed
|
|
5241
|
-
const actions = relatedEntity.actions;
|
|
5264
|
+
const actions = await evaluateExpressions(relatedEntity.actions);
|
|
5242
5265
|
return {
|
|
5243
5266
|
id: entityDef?.name ?? '',
|
|
5244
5267
|
title: relatedEntity.title ?? entityDef?.title ?? '',
|
|
@@ -6171,12 +6194,12 @@ const columnWidthMiddlewareProvider = {
|
|
|
6171
6194
|
* Default order for common sections
|
|
6172
6195
|
*/
|
|
6173
6196
|
const DEFAULT_SECTION_ORDER = {
|
|
6174
|
-
'basic-info':
|
|
6197
|
+
'basic-info': -100,
|
|
6175
6198
|
classification: 20,
|
|
6176
|
-
appearance:
|
|
6177
|
-
settings: 100,
|
|
6199
|
+
appearance: 90,
|
|
6178
6200
|
advanced: 110,
|
|
6179
6201
|
metadata: 120,
|
|
6202
|
+
settings: 200,
|
|
6180
6203
|
};
|
|
6181
6204
|
/**
|
|
6182
6205
|
* Default order for common properties
|
|
@@ -6640,11 +6663,22 @@ class AXPCategoryTreeService {
|
|
|
6640
6663
|
convertToTreeNode(item, config) {
|
|
6641
6664
|
const textField = config.textField ?? 'title';
|
|
6642
6665
|
const valueField = config.valueField ?? 'id';
|
|
6666
|
+
// Determine childrenCount properly:
|
|
6667
|
+
// - Use explicit childrenCount from backend if it's a number (including 0)
|
|
6668
|
+
// - Or use children array length if available
|
|
6669
|
+
// - Default to undefined to indicate "unknown" - this allows lazy loading to work
|
|
6670
|
+
let childrenCount;
|
|
6671
|
+
if (typeof item['childrenCount'] === 'number') {
|
|
6672
|
+
childrenCount = item['childrenCount'];
|
|
6673
|
+
}
|
|
6674
|
+
else if (Array.isArray(item['children'])) {
|
|
6675
|
+
childrenCount = item['children'].length;
|
|
6676
|
+
}
|
|
6643
6677
|
return {
|
|
6644
6678
|
id: String(item[valueField] ?? ''),
|
|
6645
6679
|
title: String(item[textField] ?? ''),
|
|
6646
6680
|
icon: 'fa-solid fa-folder',
|
|
6647
|
-
childrenCount
|
|
6681
|
+
childrenCount,
|
|
6648
6682
|
expanded: false,
|
|
6649
6683
|
data: item,
|
|
6650
6684
|
};
|
|
@@ -6800,7 +6834,6 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
6800
6834
|
this.relevantNodeIds = new Set(); // For search filtering
|
|
6801
6835
|
this.expandedNodesBeforeSearch = []; // Store expanded nodes before search to restore after
|
|
6802
6836
|
this.nodesExpandedDuringSearch = []; // Track nodes we expanded during search
|
|
6803
|
-
this.isInitializing = false; // Flag to track if we're in initialization phase
|
|
6804
6837
|
/** Datasource callback for tree-view component. */
|
|
6805
6838
|
this.datasource = async (id) => {
|
|
6806
6839
|
if (!this.treeData || !this.treeConfig) {
|
|
@@ -6847,31 +6880,48 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
6847
6880
|
}
|
|
6848
6881
|
});
|
|
6849
6882
|
}
|
|
6850
|
-
// Mark pre-selected
|
|
6851
|
-
|
|
6852
|
-
|
|
6853
|
-
const
|
|
6854
|
-
|
|
6855
|
-
node['
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
//
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
// Node might not be in tree yet
|
|
6871
|
-
}
|
|
6883
|
+
// Mark pre-selected nodes as selected in the child nodes (for visual display)
|
|
6884
|
+
// ONLY do this during initial load, NOT during user selection changes
|
|
6885
|
+
if (!this.isUpdatingSelection) {
|
|
6886
|
+
const selectedIds = this.selectedNodeIds();
|
|
6887
|
+
childNodes.forEach((node) => {
|
|
6888
|
+
const nodeId = String(node['id'] ?? '');
|
|
6889
|
+
if (nodeId && selectedIds.includes(nodeId)) {
|
|
6890
|
+
node['selected'] = true;
|
|
6891
|
+
}
|
|
6892
|
+
});
|
|
6893
|
+
// After children load, programmatically select pre-selected nodes in tree component
|
|
6894
|
+
// This ensures the tree's internal state matches our selectedNodeIds
|
|
6895
|
+
// Skip this if we're in the middle of a selection update (user is selecting/deselecting)
|
|
6896
|
+
const treeComponent = this.tree();
|
|
6897
|
+
if (treeComponent && selectedIds.length > 0) {
|
|
6898
|
+
// Use setTimeout to ensure nodes are in tree structure before selecting
|
|
6899
|
+
setTimeout(() => {
|
|
6900
|
+
// Double-check we're not in a selection update when timeout fires
|
|
6901
|
+
if (this.isUpdatingSelection) {
|
|
6902
|
+
return;
|
|
6872
6903
|
}
|
|
6873
|
-
|
|
6874
|
-
|
|
6904
|
+
this.isUpdatingSelection = true;
|
|
6905
|
+
try {
|
|
6906
|
+
// Re-check selectedNodeIds at this point (might have changed)
|
|
6907
|
+
const currentSelectedIds = this.selectedNodeIds();
|
|
6908
|
+
childNodes.forEach((node) => {
|
|
6909
|
+
const nodeId = String(node['id'] ?? '');
|
|
6910
|
+
if (nodeId && currentSelectedIds.includes(nodeId) && nodeId !== 'all') {
|
|
6911
|
+
try {
|
|
6912
|
+
treeComponent.selectNode(nodeId);
|
|
6913
|
+
}
|
|
6914
|
+
catch {
|
|
6915
|
+
// Node might not be in tree yet
|
|
6916
|
+
}
|
|
6917
|
+
}
|
|
6918
|
+
});
|
|
6919
|
+
}
|
|
6920
|
+
finally {
|
|
6921
|
+
this.isUpdatingSelection = false;
|
|
6922
|
+
}
|
|
6923
|
+
}, 10);
|
|
6924
|
+
}
|
|
6875
6925
|
}
|
|
6876
6926
|
return childNodes;
|
|
6877
6927
|
};
|
|
@@ -6890,7 +6940,6 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
6890
6940
|
return;
|
|
6891
6941
|
}
|
|
6892
6942
|
this.loading.set(true);
|
|
6893
|
-
this.isInitializing = true; // Mark that we're in initialization phase
|
|
6894
6943
|
try {
|
|
6895
6944
|
this.treeConfig = {
|
|
6896
6945
|
entityKey: this.entityKey(),
|
|
@@ -6900,7 +6949,6 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
6900
6949
|
this.treeData = await this.categoryTreeService.initializeCategoryTree(this.treeConfig);
|
|
6901
6950
|
if (!this.treeData) {
|
|
6902
6951
|
this.loading.set(false);
|
|
6903
|
-
this.isInitializing = false;
|
|
6904
6952
|
return;
|
|
6905
6953
|
}
|
|
6906
6954
|
// Get parentKey from entity definition
|
|
@@ -6946,7 +6994,6 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
6946
6994
|
console.error('Error syncing selection after tree render:', error);
|
|
6947
6995
|
}
|
|
6948
6996
|
}
|
|
6949
|
-
this.isInitializing = false; // Mark initialization as complete
|
|
6950
6997
|
}
|
|
6951
6998
|
//#endregion
|
|
6952
6999
|
//#region ---- Public Methods ----
|
|
@@ -7368,6 +7415,8 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
7368
7415
|
this.nodesExpandedDuringSearch = [];
|
|
7369
7416
|
return;
|
|
7370
7417
|
}
|
|
7418
|
+
// Store current selected IDs before reload
|
|
7419
|
+
const selectedIds = this.selectedNodeIds();
|
|
7371
7420
|
// Reload tree to show all nodes (no filtering)
|
|
7372
7421
|
await treeComponent.reloadData();
|
|
7373
7422
|
// Collapse nodes that were expanded during search (in reverse order - leaves first)
|
|
@@ -7386,88 +7435,136 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
7386
7435
|
}
|
|
7387
7436
|
// Clear the stored expanded nodes
|
|
7388
7437
|
this.expandedNodesBeforeSearch = [];
|
|
7438
|
+
// Restore selection state after tree reload
|
|
7439
|
+
if (selectedIds.length > 0) {
|
|
7440
|
+
// Wait for tree to stabilize after reload
|
|
7441
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
7442
|
+
// Re-sync selection with tree
|
|
7443
|
+
await this.restoreSelectionAfterReload(selectedIds);
|
|
7444
|
+
}
|
|
7389
7445
|
}
|
|
7390
|
-
|
|
7391
|
-
|
|
7446
|
+
/**
|
|
7447
|
+
* Restores selection state after tree reload.
|
|
7448
|
+
* Expands ancestor nodes and selects the previously selected leaf nodes.
|
|
7449
|
+
*/
|
|
7450
|
+
async restoreSelectionAfterReload(selectedIds) {
|
|
7451
|
+
const treeComponent = this.tree();
|
|
7452
|
+
if (!treeComponent || selectedIds.length === 0) {
|
|
7453
|
+
return;
|
|
7454
|
+
}
|
|
7455
|
+
this.isUpdatingSelection = true;
|
|
7456
|
+
try {
|
|
7457
|
+
// Build ancestor chains for selected nodes
|
|
7458
|
+
const ancestorChains = await this.buildAncestorChains(selectedIds);
|
|
7459
|
+
// Expand ancestor nodes to make selected nodes visible
|
|
7460
|
+
await this.expandAncestorNodesInOrder(ancestorChains);
|
|
7461
|
+
// Wait for tree to render expanded nodes
|
|
7462
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
7463
|
+
// Select the nodes visually in the tree
|
|
7464
|
+
for (const id of selectedIds) {
|
|
7465
|
+
try {
|
|
7466
|
+
treeComponent.selectNode(id);
|
|
7467
|
+
}
|
|
7468
|
+
catch {
|
|
7469
|
+
// Node might not be in tree yet
|
|
7470
|
+
}
|
|
7471
|
+
}
|
|
7472
|
+
}
|
|
7473
|
+
finally {
|
|
7474
|
+
this.isUpdatingSelection = false;
|
|
7475
|
+
}
|
|
7392
7476
|
}
|
|
7393
7477
|
async onNodeSelect(event) {
|
|
7394
7478
|
const node = event.node;
|
|
7395
7479
|
const nodeId = String(node['id'] ?? '');
|
|
7396
|
-
if (!node
|
|
7480
|
+
if (!node) {
|
|
7397
7481
|
return;
|
|
7398
7482
|
}
|
|
7399
|
-
//
|
|
7400
|
-
|
|
7401
|
-
if (
|
|
7402
|
-
|
|
7483
|
+
// Only process USER interactions, not programmatic selections
|
|
7484
|
+
// This prevents infinite loops when datasource callback syncs selection
|
|
7485
|
+
if (!event.isUserInteraction) {
|
|
7486
|
+
return;
|
|
7403
7487
|
}
|
|
7404
|
-
//
|
|
7405
|
-
|
|
7406
|
-
// (handled in handleCheckboxChange). This prevents intermediate parent states from
|
|
7407
|
-
// triggering unwanted sibling selections.
|
|
7408
|
-
}
|
|
7409
|
-
async onSelectionChange(event) {
|
|
7410
|
-
// Don't process during initialization or batch updates - let those handle it
|
|
7411
|
-
if (this.isInitializing || this.isUpdatingSelection) {
|
|
7488
|
+
// Don't process if we're already updating selection
|
|
7489
|
+
if (this.isUpdatingSelection) {
|
|
7412
7490
|
return;
|
|
7413
7491
|
}
|
|
7414
|
-
//
|
|
7415
|
-
|
|
7416
|
-
const selectedNodes = event.selectedNodes || [];
|
|
7417
|
-
// Cache node data for all selected nodes first
|
|
7418
|
-
selectedNodes.forEach((node) => {
|
|
7419
|
-
const nodeId = String(node['id'] ?? '');
|
|
7492
|
+
// Cache node data for getSelectedItems (except for 'all' node)
|
|
7493
|
+
if (nodeId !== 'all') {
|
|
7420
7494
|
const nodeData = node['data'];
|
|
7421
|
-
if (nodeData &&
|
|
7495
|
+
if (nodeData && typeof nodeData === 'object' && nodeData !== null && !Array.isArray(nodeData)) {
|
|
7422
7496
|
this.nodeDataCache.set(nodeId, nodeData);
|
|
7423
7497
|
}
|
|
7424
|
-
}
|
|
7425
|
-
//
|
|
7426
|
-
const
|
|
7427
|
-
|
|
7428
|
-
const nodeId = String(node['id'] ?? '');
|
|
7429
|
-
if (!nodeId || nodeId === 'all') {
|
|
7430
|
-
continue;
|
|
7431
|
-
}
|
|
7432
|
-
// Check if this is a leaf node
|
|
7498
|
+
}
|
|
7499
|
+
// Check if this node is being selected or deselected
|
|
7500
|
+
const isSelected = node['selected'] === true;
|
|
7501
|
+
if (this.allowMultiple()) {
|
|
7433
7502
|
const children = node['children'];
|
|
7434
7503
|
const childrenCount = node['childrenCount'];
|
|
7435
|
-
|
|
7436
|
-
if
|
|
7437
|
-
|
|
7438
|
-
|
|
7439
|
-
|
|
7440
|
-
|
|
7441
|
-
|
|
7442
|
-
|
|
7443
|
-
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
7462
|
-
|
|
7463
|
-
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
|
|
7504
|
+
// Determine if this node has children (is a parent node)
|
|
7505
|
+
// A node has children if: childrenCount > 0, or has non-empty children array
|
|
7506
|
+
// Note: childrenCount === undefined means "unknown" - we need to check via datasource
|
|
7507
|
+
const hasLoadedChildren = children && children.length > 0;
|
|
7508
|
+
const hasChildrenCount = childrenCount !== undefined && childrenCount > 0;
|
|
7509
|
+
const isDefinitelyLeaf = childrenCount === 0 && (!children || children.length === 0);
|
|
7510
|
+
if (isSelected) {
|
|
7511
|
+
// SELECTION: Only add LEAF nodes to selectedNodeIds
|
|
7512
|
+
this.isUpdatingSelection = true;
|
|
7513
|
+
try {
|
|
7514
|
+
if (nodeId === 'all') {
|
|
7515
|
+
// "All Items" selected - recursively select all leaf descendants
|
|
7516
|
+
await this.selectAllLeafDescendants(nodeId);
|
|
7517
|
+
}
|
|
7518
|
+
else if (isDefinitelyLeaf) {
|
|
7519
|
+
// This is definitely a leaf node - add it directly
|
|
7520
|
+
const currentSelected = new Set(this.selectedNodeIds());
|
|
7521
|
+
currentSelected.add(nodeId);
|
|
7522
|
+
this.selectedNodeIds.set(Array.from(currentSelected));
|
|
7523
|
+
}
|
|
7524
|
+
else if (hasLoadedChildren || hasChildrenCount) {
|
|
7525
|
+
// This node has children - only add its leaf descendants
|
|
7526
|
+
await this.selectAllLeafDescendants(nodeId);
|
|
7527
|
+
}
|
|
7528
|
+
else {
|
|
7529
|
+
// childrenCount is undefined - need to check if it has children
|
|
7530
|
+
const childNodes = await this.datasource(nodeId);
|
|
7531
|
+
if (!childNodes || childNodes.length === 0) {
|
|
7532
|
+
// No children - this is a leaf node, add it
|
|
7533
|
+
const currentSelected = new Set(this.selectedNodeIds());
|
|
7534
|
+
currentSelected.add(nodeId);
|
|
7535
|
+
this.selectedNodeIds.set(Array.from(currentSelected));
|
|
7536
|
+
}
|
|
7537
|
+
else {
|
|
7538
|
+
// Has children - only add leaf descendants
|
|
7539
|
+
await this.selectAllLeafDescendants(nodeId);
|
|
7540
|
+
}
|
|
7541
|
+
}
|
|
7542
|
+
}
|
|
7543
|
+
finally {
|
|
7544
|
+
this.isUpdatingSelection = false;
|
|
7545
|
+
}
|
|
7546
|
+
}
|
|
7547
|
+
else {
|
|
7548
|
+
// DESELECTION: Remove node (if leaf) and all leaf descendants from selectedNodeIds
|
|
7549
|
+
this.isUpdatingSelection = true;
|
|
7550
|
+
try {
|
|
7551
|
+
await this.deselectAllLeafDescendants(nodeId);
|
|
7552
|
+
}
|
|
7553
|
+
finally {
|
|
7554
|
+
this.isUpdatingSelection = false;
|
|
7555
|
+
}
|
|
7556
|
+
}
|
|
7557
|
+
}
|
|
7558
|
+
else {
|
|
7559
|
+
// Single selection mode: just update selectedNodeIds
|
|
7560
|
+
if (isSelected) {
|
|
7561
|
+
this.selectedNodeIds.set([nodeId]);
|
|
7562
|
+
}
|
|
7563
|
+
else {
|
|
7564
|
+
this.selectedNodeIds.set([]);
|
|
7565
|
+
}
|
|
7566
|
+
}
|
|
7567
|
+
}
|
|
7471
7568
|
async onConfirm() {
|
|
7472
7569
|
// Use internal selectedNodeIds state which is kept in sync via onNodeSelect events
|
|
7473
7570
|
const selectedItems = await this.getSelectedItems();
|
|
@@ -7480,21 +7577,11 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
7480
7577
|
* Clears all selected items
|
|
7481
7578
|
*/
|
|
7482
7579
|
onClearAll() {
|
|
7580
|
+
this.selectedNodeIds.set([]);
|
|
7483
7581
|
const treeComponent = this.tree();
|
|
7484
7582
|
if (treeComponent) {
|
|
7485
|
-
|
|
7486
|
-
const currentSelected = this.selectedNodeIds();
|
|
7487
|
-
for (const id of currentSelected) {
|
|
7488
|
-
try {
|
|
7489
|
-
treeComponent.deselectNode(id);
|
|
7490
|
-
}
|
|
7491
|
-
catch {
|
|
7492
|
-
// Node might not be in tree
|
|
7493
|
-
}
|
|
7494
|
-
}
|
|
7583
|
+
treeComponent.deselectAll();
|
|
7495
7584
|
}
|
|
7496
|
-
// Clear the selection
|
|
7497
|
-
this.selectedNodeIds.set([]);
|
|
7498
7585
|
}
|
|
7499
7586
|
/**
|
|
7500
7587
|
* Checks if a node matches the current search term
|
|
@@ -7513,543 +7600,207 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
7513
7600
|
return this.selectedNodeIds().includes(id);
|
|
7514
7601
|
}
|
|
7515
7602
|
/**
|
|
7516
|
-
*
|
|
7517
|
-
*
|
|
7518
|
-
* Parent states are calculated based on leaf descendants
|
|
7603
|
+
* Expands parent nodes, collects all LEAF descendants, and selects them visually.
|
|
7604
|
+
* If the parent itself is a leaf (no children), it will be added.
|
|
7519
7605
|
*/
|
|
7520
|
-
async
|
|
7521
|
-
if (!
|
|
7606
|
+
async selectAllLeafDescendants(parentId) {
|
|
7607
|
+
if (!this.treeData || !this.treeConfig) {
|
|
7522
7608
|
return;
|
|
7523
7609
|
}
|
|
7524
|
-
const id = String(nodeId);
|
|
7525
7610
|
const treeComponent = this.tree();
|
|
7526
|
-
if (!treeComponent) {
|
|
7527
|
-
return;
|
|
7528
|
-
}
|
|
7529
|
-
if (checked) {
|
|
7530
|
-
// Select all descendant LEAF nodes
|
|
7531
|
-
await this.selectLeafDescendants(id);
|
|
7532
|
-
}
|
|
7533
|
-
else {
|
|
7534
|
-
// Deselect all descendant LEAF nodes
|
|
7535
|
-
await this.deselectLeafDescendants(id);
|
|
7536
|
-
}
|
|
7537
|
-
// Update parent states after selection change
|
|
7538
|
-
await this.refreshParentStatesInTree();
|
|
7539
|
-
}
|
|
7540
|
-
/**
|
|
7541
|
-
* Selects all leaf descendants of a node (and the node itself if it's a leaf)
|
|
7542
|
-
*/
|
|
7543
|
-
async selectLeafDescendants(nodeId) {
|
|
7544
|
-
this.isUpdatingSelection = true;
|
|
7545
7611
|
try {
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
await this.
|
|
7549
|
-
if (
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
const currentSelected = new Set(this.selectedNodeIds());
|
|
7555
|
-
leafNodes.forEach((id) => currentSelected.add(id));
|
|
7556
|
-
this.selectedNodeIds.set(Array.from(currentSelected));
|
|
7557
|
-
// Sync with tree component
|
|
7558
|
-
const treeComponent = this.tree();
|
|
7559
|
-
if (treeComponent) {
|
|
7560
|
-
for (const leafId of leafNodes) {
|
|
7561
|
-
try {
|
|
7562
|
-
const node = treeComponent.findNode(leafId);
|
|
7563
|
-
if (node) {
|
|
7564
|
-
treeComponent.selectNode(leafId);
|
|
7565
|
-
}
|
|
7566
|
-
}
|
|
7567
|
-
catch {
|
|
7568
|
-
// Node might not be in tree
|
|
7569
|
-
}
|
|
7612
|
+
const leafIds = new Set();
|
|
7613
|
+
// Expand and collect leaf nodes simultaneously
|
|
7614
|
+
await this.collectLeafNodes(parentId, leafIds, treeComponent);
|
|
7615
|
+
// Also check if the parent itself is a leaf (has no children)
|
|
7616
|
+
if (parentId !== 'all') {
|
|
7617
|
+
const isLeaf = await this.isLeafNodeCheck(parentId);
|
|
7618
|
+
if (isLeaf) {
|
|
7619
|
+
leafIds.add(parentId);
|
|
7570
7620
|
}
|
|
7571
7621
|
}
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
|
|
7577
|
-
|
|
7578
|
-
|
|
7579
|
-
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
try {
|
|
7583
|
-
// Collect all leaf descendants
|
|
7584
|
-
const leafNodes = new Set();
|
|
7585
|
-
await this.collectLeafDescendants(nodeId, leafNodes);
|
|
7586
|
-
if (leafNodes.size === 0) {
|
|
7587
|
-
// Node itself is a leaf
|
|
7588
|
-
leafNodes.add(nodeId);
|
|
7589
|
-
}
|
|
7590
|
-
// Remove leaf nodes from selectedNodeIds
|
|
7591
|
-
const currentSelected = this.selectedNodeIds();
|
|
7592
|
-
const newSelected = currentSelected.filter((id) => !leafNodes.has(id));
|
|
7593
|
-
this.selectedNodeIds.set(newSelected);
|
|
7594
|
-
// Sync with tree component
|
|
7595
|
-
const treeComponent = this.tree();
|
|
7622
|
+
if (leafIds.size === 0) {
|
|
7623
|
+
return;
|
|
7624
|
+
}
|
|
7625
|
+
// Update our internal state
|
|
7626
|
+
const currentSelected = new Set(this.selectedNodeIds());
|
|
7627
|
+
for (const leafId of leafIds) {
|
|
7628
|
+
currentSelected.add(leafId);
|
|
7629
|
+
}
|
|
7630
|
+
this.selectedNodeIds.set(Array.from(currentSelected));
|
|
7631
|
+
// Select all leaf nodes visually in the tree
|
|
7596
7632
|
if (treeComponent) {
|
|
7597
|
-
for (const leafId of
|
|
7633
|
+
for (const leafId of leafIds) {
|
|
7598
7634
|
try {
|
|
7599
|
-
|
|
7600
|
-
if (node) {
|
|
7601
|
-
treeComponent.deselectNode(leafId);
|
|
7602
|
-
}
|
|
7635
|
+
treeComponent.selectNode(leafId);
|
|
7603
7636
|
}
|
|
7604
7637
|
catch {
|
|
7605
|
-
// Node might not be in tree
|
|
7638
|
+
// Node might not be in tree yet
|
|
7606
7639
|
}
|
|
7607
7640
|
}
|
|
7608
7641
|
}
|
|
7609
7642
|
}
|
|
7610
|
-
|
|
7611
|
-
|
|
7643
|
+
catch (error) {
|
|
7644
|
+
console.error(`Error selecting leaf descendants for node ${parentId}:`, error);
|
|
7612
7645
|
}
|
|
7613
7646
|
}
|
|
7614
7647
|
/**
|
|
7615
|
-
*
|
|
7616
|
-
*
|
|
7648
|
+
* Removes all LEAF descendants from selectedNodeIds.
|
|
7649
|
+
* For 'all' node: clears everything and uses tree's deselectAll().
|
|
7650
|
+
* For other nodes: tree handles visual state via user click.
|
|
7617
7651
|
*/
|
|
7618
|
-
async
|
|
7619
|
-
if (!this.treeData || !this.treeConfig
|
|
7652
|
+
async deselectAllLeafDescendants(parentId) {
|
|
7653
|
+
if (!this.treeData || !this.treeConfig) {
|
|
7620
7654
|
return;
|
|
7621
7655
|
}
|
|
7622
7656
|
try {
|
|
7623
|
-
|
|
7624
|
-
if (
|
|
7625
|
-
|
|
7626
|
-
//
|
|
7657
|
+
// Special case: deselecting 'all' clears everything
|
|
7658
|
+
if (parentId === 'all') {
|
|
7659
|
+
this.selectedNodeIds.set([]);
|
|
7660
|
+
// Use tree's deselectAll() to clear all visual selections
|
|
7661
|
+
const treeComponent = this.tree();
|
|
7662
|
+
if (treeComponent) {
|
|
7663
|
+
treeComponent.deselectAll();
|
|
7664
|
+
}
|
|
7627
7665
|
return;
|
|
7628
7666
|
}
|
|
7629
|
-
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
const nodeData = childNode['data'];
|
|
7636
|
-
if (nodeData && typeof nodeData === 'object') {
|
|
7637
|
-
this.nodeDataCache.set(childId, nodeData);
|
|
7638
|
-
}
|
|
7639
|
-
// Check if this child is a leaf by trying to load its children
|
|
7640
|
-
const grandchildNodes = await this.datasource(childId);
|
|
7641
|
-
if (!grandchildNodes || grandchildNodes.length === 0) {
|
|
7642
|
-
// This child is a leaf node
|
|
7643
|
-
collection.add(childId);
|
|
7644
|
-
}
|
|
7645
|
-
else {
|
|
7646
|
-
// This child has children, recurse
|
|
7647
|
-
await this.collectLeafDescendants(childId, collection);
|
|
7648
|
-
}
|
|
7667
|
+
const leafIds = new Set();
|
|
7668
|
+
await this.collectLeafNodes(parentId, leafIds);
|
|
7669
|
+
// Also check if the parent itself is a leaf
|
|
7670
|
+
const isLeaf = await this.isLeafNodeCheck(parentId);
|
|
7671
|
+
if (isLeaf) {
|
|
7672
|
+
leafIds.add(parentId);
|
|
7649
7673
|
}
|
|
7650
|
-
|
|
7651
|
-
|
|
7652
|
-
console.error(`Error collecting leaf descendants for node ${parentId}:`, error);
|
|
7653
|
-
}
|
|
7654
|
-
}
|
|
7655
|
-
/**
|
|
7656
|
-
* Refreshes parent states in the tree based on leaf selection
|
|
7657
|
-
*/
|
|
7658
|
-
async refreshParentStatesInTree() {
|
|
7659
|
-
const treeComponent = this.tree();
|
|
7660
|
-
if (!treeComponent) {
|
|
7661
|
-
return;
|
|
7662
|
-
}
|
|
7663
|
-
// The tree component with intermediate-nested behavior should handle this
|
|
7664
|
-
// But we need to force a refresh by reloading data
|
|
7665
|
-
// For now, we rely on the tree component's built-in behavior
|
|
7666
|
-
}
|
|
7667
|
-
/**
|
|
7668
|
-
* Selects a node and recursively selects all its children
|
|
7669
|
-
*/
|
|
7670
|
-
async selectNodeAndChildren(nodeId) {
|
|
7671
|
-
const treeComponent = this.tree();
|
|
7672
|
-
if (!treeComponent) {
|
|
7673
|
-
return;
|
|
7674
|
-
}
|
|
7675
|
-
// Set flag to prevent recursive updates during batch operation
|
|
7676
|
-
this.isUpdatingSelection = true;
|
|
7677
|
-
try {
|
|
7678
|
-
// Collect all nodes to select (node + all descendants)
|
|
7679
|
-
const nodesToSelect = new Set([nodeId]);
|
|
7680
|
-
await this.collectAllDescendants(nodeId, nodesToSelect);
|
|
7681
|
-
// Batch update selectedNodeIds
|
|
7682
|
-
const currentSelected = this.selectedNodeIds();
|
|
7683
|
-
const newSelected = [...currentSelected];
|
|
7684
|
-
for (const id of nodesToSelect) {
|
|
7685
|
-
if (!newSelected.includes(id)) {
|
|
7686
|
-
newSelected.push(id);
|
|
7687
|
-
}
|
|
7674
|
+
if (leafIds.size === 0) {
|
|
7675
|
+
return;
|
|
7688
7676
|
}
|
|
7677
|
+
// Only update our internal state - tree handles visual state
|
|
7678
|
+
const currentSelected = this.selectedNodeIds();
|
|
7679
|
+
const newSelected = currentSelected.filter((id) => !leafIds.has(id));
|
|
7689
7680
|
this.selectedNodeIds.set(newSelected);
|
|
7690
|
-
// Batch select in tree component (with small delays to prevent glitches)
|
|
7691
|
-
for (const id of nodesToSelect) {
|
|
7692
|
-
try {
|
|
7693
|
-
const node = treeComponent.findNode(id);
|
|
7694
|
-
if (node) {
|
|
7695
|
-
treeComponent.selectNode(id);
|
|
7696
|
-
// Small delay to prevent overwhelming the tree component
|
|
7697
|
-
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
7698
|
-
}
|
|
7699
|
-
}
|
|
7700
|
-
catch {
|
|
7701
|
-
// Node might not be in tree yet
|
|
7702
|
-
}
|
|
7703
|
-
}
|
|
7704
7681
|
}
|
|
7705
|
-
|
|
7706
|
-
|
|
7682
|
+
catch (error) {
|
|
7683
|
+
console.error(`Error deselecting leaf descendants for node ${parentId}:`, error);
|
|
7707
7684
|
}
|
|
7708
7685
|
}
|
|
7709
7686
|
/**
|
|
7710
|
-
*
|
|
7687
|
+
* Recursively expands parent nodes and collects LEAF node IDs.
|
|
7688
|
+
* When treeComponent is provided, expands each parent node before loading children.
|
|
7711
7689
|
*/
|
|
7712
|
-
async
|
|
7713
|
-
if (!this.treeData || !this.treeConfig
|
|
7690
|
+
async collectLeafNodes(parentId, collection, treeComponent) {
|
|
7691
|
+
if (!this.treeData || !this.treeConfig) {
|
|
7714
7692
|
return;
|
|
7715
7693
|
}
|
|
7716
7694
|
try {
|
|
7717
|
-
|
|
7718
|
-
if (
|
|
7719
|
-
|
|
7720
|
-
|
|
7721
|
-
|
|
7722
|
-
|
|
7723
|
-
if (childId && childId !== 'all' && !collection.has(childId)) {
|
|
7724
|
-
collection.add(childId);
|
|
7725
|
-
// Cache node data
|
|
7726
|
-
const nodeData = childNode['data'];
|
|
7727
|
-
if (nodeData && typeof nodeData === 'object') {
|
|
7728
|
-
this.nodeDataCache.set(childId, nodeData);
|
|
7695
|
+
let childNodes;
|
|
7696
|
+
if (parentId === 'all') {
|
|
7697
|
+
// Expand 'all' node if tree component provided
|
|
7698
|
+
if (treeComponent) {
|
|
7699
|
+
try {
|
|
7700
|
+
treeComponent.expandNode('all');
|
|
7729
7701
|
}
|
|
7730
|
-
|
|
7731
|
-
|
|
7732
|
-
const textField = this.treeConfig.textField || 'title';
|
|
7733
|
-
const dataObj = {
|
|
7734
|
-
[valueField]: childId,
|
|
7735
|
-
[textField]: childNode['title'] ?? '',
|
|
7736
|
-
...childNode,
|
|
7737
|
-
};
|
|
7738
|
-
this.nodeDataCache.set(childId, dataObj);
|
|
7702
|
+
catch {
|
|
7703
|
+
// Ignore expand errors
|
|
7739
7704
|
}
|
|
7740
|
-
// Recursively collect descendants
|
|
7741
|
-
await this.collectAllDescendants(childId, collection);
|
|
7742
7705
|
}
|
|
7706
|
+
// Get root node's children
|
|
7707
|
+
const rootNodes = await this.datasource();
|
|
7708
|
+
if (!rootNodes || rootNodes.length === 0) {
|
|
7709
|
+
return;
|
|
7710
|
+
}
|
|
7711
|
+
const rootNode = rootNodes[0];
|
|
7712
|
+
childNodes = rootNode['children'] || [];
|
|
7743
7713
|
}
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
|
|
7750
|
-
|
|
7751
|
-
|
|
7752
|
-
async deselectNodeAndChildren(nodeId) {
|
|
7753
|
-
const treeComponent = this.tree();
|
|
7754
|
-
if (!treeComponent) {
|
|
7755
|
-
return;
|
|
7756
|
-
}
|
|
7757
|
-
// Set flag to prevent recursive updates during batch operation
|
|
7758
|
-
this.isUpdatingSelection = true;
|
|
7759
|
-
try {
|
|
7760
|
-
// Collect all nodes to deselect (node + all descendants)
|
|
7761
|
-
const nodesToDeselect = new Set([nodeId]);
|
|
7762
|
-
await this.collectAllDescendants(nodeId, nodesToDeselect);
|
|
7763
|
-
// Batch update selectedNodeIds
|
|
7764
|
-
const currentSelected = this.selectedNodeIds();
|
|
7765
|
-
const newSelected = currentSelected.filter((id) => !nodesToDeselect.has(id));
|
|
7766
|
-
this.selectedNodeIds.set(newSelected);
|
|
7767
|
-
// Batch deselect in tree component (with small delays to prevent glitches)
|
|
7768
|
-
for (const id of nodesToDeselect) {
|
|
7769
|
-
try {
|
|
7770
|
-
const node = treeComponent.findNode(id);
|
|
7771
|
-
if (node) {
|
|
7772
|
-
treeComponent.deselectNode(id);
|
|
7773
|
-
// Small delay to prevent overwhelming the tree component
|
|
7774
|
-
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
7714
|
+
else {
|
|
7715
|
+
// Expand this parent node if tree component provided
|
|
7716
|
+
if (treeComponent) {
|
|
7717
|
+
try {
|
|
7718
|
+
treeComponent.expandNode(parentId);
|
|
7719
|
+
}
|
|
7720
|
+
catch {
|
|
7721
|
+
// Ignore expand errors
|
|
7775
7722
|
}
|
|
7776
7723
|
}
|
|
7777
|
-
|
|
7778
|
-
|
|
7779
|
-
}
|
|
7724
|
+
// Load children via datasource
|
|
7725
|
+
childNodes = await this.datasource(parentId);
|
|
7780
7726
|
}
|
|
7781
|
-
}
|
|
7782
|
-
finally {
|
|
7783
|
-
this.isUpdatingSelection = false;
|
|
7784
|
-
}
|
|
7785
|
-
}
|
|
7786
|
-
/**
|
|
7787
|
-
* Recursively deselects all children of a parent node
|
|
7788
|
-
*/
|
|
7789
|
-
async loadAndDeselectChildrenRecursively(parentId) {
|
|
7790
|
-
if (!this.treeData || !this.treeConfig || !parentId || parentId === 'all') {
|
|
7791
|
-
return;
|
|
7792
|
-
}
|
|
7793
|
-
const treeComponent = this.tree();
|
|
7794
|
-
if (!treeComponent) {
|
|
7795
|
-
return;
|
|
7796
|
-
}
|
|
7797
|
-
try {
|
|
7798
|
-
// Load children using datasource
|
|
7799
|
-
const childNodes = await this.datasource(parentId);
|
|
7800
7727
|
if (!childNodes || childNodes.length === 0) {
|
|
7801
7728
|
return;
|
|
7802
7729
|
}
|
|
7803
|
-
|
|
7804
|
-
// Collect all child IDs
|
|
7730
|
+
// Process each child
|
|
7805
7731
|
for (const childNode of childNodes) {
|
|
7806
7732
|
const childId = String(childNode['id'] ?? '');
|
|
7807
|
-
if (childId
|
|
7808
|
-
|
|
7733
|
+
if (!childId || childId === 'all' || collection.has(childId)) {
|
|
7734
|
+
continue;
|
|
7809
7735
|
}
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
try {
|
|
7818
|
-
treeComponent.deselectNode(childId);
|
|
7736
|
+
// Cache node data
|
|
7737
|
+
this.cacheNodeFromTreeNode(childNode);
|
|
7738
|
+
// Check if this child has children
|
|
7739
|
+
const hasChildren = await this.nodeHasChildren(childId, childNode);
|
|
7740
|
+
if (!hasChildren) {
|
|
7741
|
+
// This is a LEAF node - add it
|
|
7742
|
+
collection.add(childId);
|
|
7819
7743
|
}
|
|
7820
|
-
|
|
7821
|
-
//
|
|
7744
|
+
else {
|
|
7745
|
+
// Has children - expand and recurse (pass tree component to expand children too)
|
|
7746
|
+
await this.collectLeafNodes(childId, collection, treeComponent);
|
|
7822
7747
|
}
|
|
7823
7748
|
}
|
|
7824
|
-
// Recursively deselect children of each child
|
|
7825
|
-
await Promise.all(childIdsToDeselect.map((childId) => this.loadAndDeselectChildrenRecursively(childId)));
|
|
7826
7749
|
}
|
|
7827
7750
|
catch (error) {
|
|
7828
|
-
console.error(`Error
|
|
7751
|
+
console.error(`Error collecting leaf nodes for ${parentId}:`, error);
|
|
7829
7752
|
}
|
|
7830
7753
|
}
|
|
7831
7754
|
/**
|
|
7832
|
-
*
|
|
7833
|
-
* Called after a node is selected/deselected to update parent checkbox states
|
|
7755
|
+
* Checks if a node has children
|
|
7834
7756
|
*/
|
|
7835
|
-
async
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
}
|
|
7843
|
-
const treeComponent = this.tree();
|
|
7844
|
-
if (!treeComponent) {
|
|
7845
|
-
return;
|
|
7846
|
-
}
|
|
7847
|
-
// Start from the changed node's parent and work up the tree
|
|
7848
|
-
const processedParents = new Set();
|
|
7849
|
-
let currentId = changedNodeId;
|
|
7850
|
-
// Process all parents up to root
|
|
7851
|
-
while (currentId && currentId !== 'all') {
|
|
7852
|
-
const nodeData = this.nodeDataCache.get(currentId);
|
|
7853
|
-
if (!nodeData) {
|
|
7854
|
-
break;
|
|
7855
|
-
}
|
|
7856
|
-
const parentId = nodeData[parentKey];
|
|
7857
|
-
const parentIdStr = String(parentId);
|
|
7858
|
-
if (!parentId || parentId === 'all' || parentId === currentId || processedParents.has(parentIdStr)) {
|
|
7859
|
-
break;
|
|
7757
|
+
async nodeHasChildren(nodeId, node) {
|
|
7758
|
+
// First check node properties if available
|
|
7759
|
+
if (node) {
|
|
7760
|
+
const children = node['children'];
|
|
7761
|
+
const childrenCount = node['childrenCount'];
|
|
7762
|
+
if (children && children.length > 0) {
|
|
7763
|
+
return true;
|
|
7860
7764
|
}
|
|
7861
|
-
|
|
7862
|
-
|
|
7863
|
-
try {
|
|
7864
|
-
const childNodes = await this.datasource(parentIdStr);
|
|
7865
|
-
if (!childNodes || childNodes.length === 0) {
|
|
7866
|
-
currentId = parentIdStr;
|
|
7867
|
-
continue;
|
|
7868
|
-
}
|
|
7869
|
-
// Get current selection state (updated after each parent change)
|
|
7870
|
-
const currentSelected = this.selectedNodeIds();
|
|
7871
|
-
const selectedSet = new Set(currentSelected);
|
|
7872
|
-
let selectedCount = 0;
|
|
7873
|
-
let totalCount = 0;
|
|
7874
|
-
// Count selected children
|
|
7875
|
-
for (const childNode of childNodes) {
|
|
7876
|
-
const childId = String(childNode['id'] ?? '');
|
|
7877
|
-
if (childId && childId !== 'all') {
|
|
7878
|
-
totalCount++;
|
|
7879
|
-
if (selectedSet.has(childId)) {
|
|
7880
|
-
selectedCount++;
|
|
7881
|
-
}
|
|
7882
|
-
}
|
|
7883
|
-
}
|
|
7884
|
-
// Update parent selection state
|
|
7885
|
-
const isParentSelected = currentSelected.includes(parentIdStr);
|
|
7886
|
-
if (totalCount > 0) {
|
|
7887
|
-
if (selectedCount === totalCount) {
|
|
7888
|
-
// All children selected - select parent
|
|
7889
|
-
if (!isParentSelected) {
|
|
7890
|
-
this.selectedNodeIds.set([...currentSelected, parentIdStr]);
|
|
7891
|
-
try {
|
|
7892
|
-
treeComponent.selectNode(parentIdStr);
|
|
7893
|
-
}
|
|
7894
|
-
catch {
|
|
7895
|
-
// Parent might not be in tree
|
|
7896
|
-
}
|
|
7897
|
-
}
|
|
7898
|
-
}
|
|
7899
|
-
else if (selectedCount > 0) {
|
|
7900
|
-
// Some children selected - parent should be in intermediate state
|
|
7901
|
-
// The tree component handles intermediate state automatically via selectionBehavior
|
|
7902
|
-
// We just need to ensure parent is not fully selected
|
|
7903
|
-
if (isParentSelected) {
|
|
7904
|
-
// Deselect parent to show intermediate state
|
|
7905
|
-
this.selectedNodeIds.set(currentSelected.filter((id) => id !== parentIdStr));
|
|
7906
|
-
try {
|
|
7907
|
-
treeComponent.deselectNode(parentIdStr);
|
|
7908
|
-
}
|
|
7909
|
-
catch {
|
|
7910
|
-
// Parent might not be in tree
|
|
7911
|
-
}
|
|
7912
|
-
}
|
|
7913
|
-
}
|
|
7914
|
-
else {
|
|
7915
|
-
// No children selected - deselect parent
|
|
7916
|
-
if (isParentSelected) {
|
|
7917
|
-
this.selectedNodeIds.set(currentSelected.filter((id) => id !== parentIdStr));
|
|
7918
|
-
try {
|
|
7919
|
-
treeComponent.deselectNode(parentIdStr);
|
|
7920
|
-
}
|
|
7921
|
-
catch {
|
|
7922
|
-
// Parent might not be in tree
|
|
7923
|
-
}
|
|
7924
|
-
}
|
|
7925
|
-
}
|
|
7926
|
-
}
|
|
7927
|
-
// Cache parent data if not already cached
|
|
7928
|
-
if (!this.nodeDataCache.has(parentIdStr)) {
|
|
7929
|
-
const parentData = await this.fetchItemById(parentIdStr);
|
|
7930
|
-
if (parentData) {
|
|
7931
|
-
this.nodeDataCache.set(parentIdStr, parentData);
|
|
7932
|
-
}
|
|
7933
|
-
}
|
|
7934
|
-
currentId = parentIdStr;
|
|
7765
|
+
if (childrenCount !== undefined && childrenCount > 0) {
|
|
7766
|
+
return true;
|
|
7935
7767
|
}
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
break;
|
|
7768
|
+
if (childrenCount === 0) {
|
|
7769
|
+
return false;
|
|
7939
7770
|
}
|
|
7940
7771
|
}
|
|
7772
|
+
// Query datasource to check
|
|
7773
|
+
const childNodes = await this.datasource(nodeId);
|
|
7774
|
+
return childNodes && childNodes.length > 0;
|
|
7941
7775
|
}
|
|
7942
7776
|
/**
|
|
7943
|
-
*
|
|
7944
|
-
* This method directly calls datasource without expanding/collapsing nodes to avoid UI glitches
|
|
7777
|
+
* Checks if a node is a leaf (has no children)
|
|
7945
7778
|
*/
|
|
7946
|
-
async
|
|
7947
|
-
|
|
7948
|
-
|
|
7949
|
-
}
|
|
7950
|
-
const treeComponent = this.tree();
|
|
7951
|
-
if (!treeComponent) {
|
|
7952
|
-
return;
|
|
7953
|
-
}
|
|
7954
|
-
try {
|
|
7955
|
-
// Directly call datasource to get children data without expanding the node
|
|
7956
|
-
// This avoids UI glitches from expand/collapse operations
|
|
7957
|
-
// If node has no children, datasource will return empty array
|
|
7958
|
-
const childNodes = await this.datasource(parentId);
|
|
7959
|
-
if (!childNodes || childNodes.length === 0) {
|
|
7960
|
-
return; // No children to process
|
|
7961
|
-
}
|
|
7962
|
-
// Collect all child IDs to add to selectedNodeIds
|
|
7963
|
-
const childIdsToSelect = [];
|
|
7964
|
-
// Process all children and cache their data
|
|
7965
|
-
for (const childNode of childNodes) {
|
|
7966
|
-
const childId = String(childNode['id'] ?? '');
|
|
7967
|
-
if (childId && childId !== 'all') {
|
|
7968
|
-
childIdsToSelect.push(childId);
|
|
7969
|
-
// Cache node data for getSelectedItems
|
|
7970
|
-
// Try to get data from 'data' property first, then fallback to node itself
|
|
7971
|
-
let nodeData = childNode['data'];
|
|
7972
|
-
// If no data property, try to extract data from the node
|
|
7973
|
-
if (!nodeData || typeof nodeData !== 'object') {
|
|
7974
|
-
// Create a data object from the node properties
|
|
7975
|
-
const valueField = this.treeConfig.valueField || 'id';
|
|
7976
|
-
const textField = this.treeConfig.textField || 'title';
|
|
7977
|
-
nodeData = {
|
|
7978
|
-
[valueField]: childId,
|
|
7979
|
-
[textField]: childNode['title'] ?? '',
|
|
7980
|
-
...childNode,
|
|
7981
|
-
};
|
|
7982
|
-
}
|
|
7983
|
-
// Cache the node data
|
|
7984
|
-
if (nodeData && typeof nodeData === 'object') {
|
|
7985
|
-
this.nodeDataCache.set(childId, nodeData);
|
|
7986
|
-
}
|
|
7987
|
-
}
|
|
7988
|
-
}
|
|
7989
|
-
if (childIdsToSelect.length === 0) {
|
|
7990
|
-
return; // No valid children to select
|
|
7991
|
-
}
|
|
7992
|
-
// Update selectedNodeIds to include all children
|
|
7993
|
-
const currentSelected = this.selectedNodeIds();
|
|
7994
|
-
const newSelected = [...currentSelected];
|
|
7995
|
-
let hasNewSelections = false;
|
|
7996
|
-
for (const childId of childIdsToSelect) {
|
|
7997
|
-
if (!newSelected.includes(childId)) {
|
|
7998
|
-
newSelected.push(childId);
|
|
7999
|
-
hasNewSelections = true;
|
|
8000
|
-
}
|
|
8001
|
-
}
|
|
8002
|
-
if (hasNewSelections) {
|
|
8003
|
-
this.selectedNodeIds.set(newSelected);
|
|
8004
|
-
}
|
|
8005
|
-
// Try to select children in tree component if they're already loaded
|
|
8006
|
-
// If not loaded yet, they'll be selected when the tree loads them (via markNodeAsSelectedIfNeeded)
|
|
8007
|
-
for (const childId of childIdsToSelect) {
|
|
8008
|
-
try {
|
|
8009
|
-
// Only try to select if node exists in tree (might not be loaded if parent isn't expanded)
|
|
8010
|
-
const node = treeComponent.findNode(childId);
|
|
8011
|
-
if (node) {
|
|
8012
|
-
treeComponent.selectNode(childId);
|
|
8013
|
-
}
|
|
8014
|
-
}
|
|
8015
|
-
catch {
|
|
8016
|
-
// If selection fails, it's okay - we've already added to selectedNodeIds
|
|
8017
|
-
// The tree will sync selection when the node is loaded via datasource callback
|
|
8018
|
-
}
|
|
8019
|
-
}
|
|
8020
|
-
// Recursively load and select children of each child
|
|
8021
|
-
// Use Promise.all for parallel processing to improve performance
|
|
8022
|
-
await Promise.all(childIdsToSelect.map((childId) => this.loadAndSelectChildrenRecursively(childId)));
|
|
8023
|
-
}
|
|
8024
|
-
catch (error) {
|
|
8025
|
-
console.error(`Error loading children for node ${parentId}:`, error);
|
|
8026
|
-
}
|
|
7779
|
+
async isLeafNodeCheck(nodeId) {
|
|
7780
|
+
const hasChildren = await this.nodeHasChildren(nodeId);
|
|
7781
|
+
return !hasChildren;
|
|
8027
7782
|
}
|
|
8028
|
-
|
|
8029
|
-
|
|
8030
|
-
|
|
8031
|
-
|
|
7783
|
+
/**
|
|
7784
|
+
* Caches node data from a tree node
|
|
7785
|
+
*/
|
|
7786
|
+
cacheNodeFromTreeNode(node) {
|
|
7787
|
+
const nodeId = String(node['id'] ?? '');
|
|
7788
|
+
if (!nodeId || !this.treeConfig) {
|
|
8032
7789
|
return;
|
|
8033
7790
|
}
|
|
8034
|
-
|
|
8035
|
-
|
|
8036
|
-
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
|
|
8040
|
-
|
|
8041
|
-
|
|
8042
|
-
|
|
8043
|
-
this.selectedNodeIds.set(ids);
|
|
8044
|
-
// Step 4: Expand ancestor nodes in order (root to leaf) to load them into tree
|
|
8045
|
-
await this.expandAncestorNodesInOrder(ancestorChains);
|
|
8046
|
-
// Step 5: Wait for tree to render and sync selection
|
|
8047
|
-
await this.syncSelectionWithTree(ids);
|
|
8048
|
-
}
|
|
8049
|
-
finally {
|
|
8050
|
-
this.isUpdatingSelection = false;
|
|
7791
|
+
let nodeData = node['data'];
|
|
7792
|
+
if (!nodeData || typeof nodeData !== 'object') {
|
|
7793
|
+
const valueField = this.treeConfig.valueField || 'id';
|
|
7794
|
+
const textField = this.treeConfig.textField || 'title';
|
|
7795
|
+
nodeData = {
|
|
7796
|
+
[valueField]: nodeId,
|
|
7797
|
+
[textField]: node['title'] ?? '',
|
|
7798
|
+
...node,
|
|
7799
|
+
};
|
|
8051
7800
|
}
|
|
7801
|
+
this.nodeDataCache.set(nodeId, nodeData);
|
|
8052
7802
|
}
|
|
7803
|
+
//#endregion
|
|
8053
7804
|
/**
|
|
8054
7805
|
* Builds complete ancestor chains for all selected node IDs.
|
|
8055
7806
|
* Returns a Map where key is the selected node ID and value is array of ancestor IDs from root to parent.
|
|
@@ -8430,42 +8181,38 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
8430
8181
|
const treeComponent = this.tree();
|
|
8431
8182
|
if (treeComponent) {
|
|
8432
8183
|
setTimeout(() => {
|
|
8433
|
-
|
|
8434
|
-
|
|
8435
|
-
|
|
8436
|
-
|
|
8437
|
-
|
|
8438
|
-
|
|
8184
|
+
this.isUpdatingSelection = true;
|
|
8185
|
+
try {
|
|
8186
|
+
const selectedIds = this.selectedNodeIds();
|
|
8187
|
+
selectedIds.forEach((id) => {
|
|
8188
|
+
if (id && id !== 'all') {
|
|
8189
|
+
try {
|
|
8190
|
+
treeComponent.selectNode(id);
|
|
8191
|
+
}
|
|
8192
|
+
catch {
|
|
8193
|
+
// Node might not be in tree yet
|
|
8194
|
+
}
|
|
8195
|
+
}
|
|
8196
|
+
});
|
|
8197
|
+
}
|
|
8198
|
+
finally {
|
|
8199
|
+
this.isUpdatingSelection = false;
|
|
8200
|
+
}
|
|
8439
8201
|
}, 0);
|
|
8440
8202
|
}
|
|
8441
8203
|
}
|
|
8442
8204
|
/**
|
|
8443
|
-
* Processes child nodes: marks excluded as disabled
|
|
8205
|
+
* Processes child nodes: marks excluded as disabled
|
|
8206
|
+
* Selection marking is handled in datasource callback ONLY during initial load
|
|
8444
8207
|
*/
|
|
8445
8208
|
processChildNodes(childNodes) {
|
|
8446
8209
|
const excludedId = this.excludedNodeId();
|
|
8447
|
-
const selectedIds = this.selectedNodeIds();
|
|
8448
8210
|
childNodes.forEach((node) => {
|
|
8449
8211
|
const nodeId = String(node['id'] ?? '');
|
|
8450
8212
|
if (excludedId && nodeId === excludedId) {
|
|
8451
8213
|
node['disabled'] = true;
|
|
8452
8214
|
}
|
|
8453
|
-
if (nodeId && selectedIds.includes(nodeId)) {
|
|
8454
|
-
node['selected'] = true;
|
|
8455
|
-
}
|
|
8456
8215
|
});
|
|
8457
|
-
// Sync selection with tree component
|
|
8458
|
-
const treeComponent = this.tree();
|
|
8459
|
-
if (treeComponent) {
|
|
8460
|
-
setTimeout(() => {
|
|
8461
|
-
childNodes.forEach((node) => {
|
|
8462
|
-
const nodeId = String(node['id'] ?? '');
|
|
8463
|
-
if (nodeId && selectedIds.includes(nodeId) && nodeId !== 'all') {
|
|
8464
|
-
treeComponent.selectNode(nodeId);
|
|
8465
|
-
}
|
|
8466
|
-
});
|
|
8467
|
-
}, 0);
|
|
8468
|
-
}
|
|
8469
8216
|
}
|
|
8470
8217
|
/**
|
|
8471
8218
|
* Caches node data from items array
|
|
@@ -8609,67 +8356,6 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
8609
8356
|
const textField = this.treeConfig.textField || 'title';
|
|
8610
8357
|
return String(nodeData[textField] ?? '');
|
|
8611
8358
|
}
|
|
8612
|
-
/**
|
|
8613
|
-
* Checks if a node is a leaf node (has no children)
|
|
8614
|
-
*/
|
|
8615
|
-
async isLeafNode(nodeId, treeComponent) {
|
|
8616
|
-
if (!treeComponent) {
|
|
8617
|
-
// If no tree component, check if node has children by querying
|
|
8618
|
-
return await this.checkIfNodeHasChildren(nodeId);
|
|
8619
|
-
}
|
|
8620
|
-
try {
|
|
8621
|
-
const node = treeComponent.findNode(nodeId);
|
|
8622
|
-
if (!node) {
|
|
8623
|
-
// Node not found in tree, check via query
|
|
8624
|
-
return await this.checkIfNodeHasChildren(nodeId);
|
|
8625
|
-
}
|
|
8626
|
-
// Check if node has children
|
|
8627
|
-
const children = node['children'];
|
|
8628
|
-
const childrenCount = node['childrenCount'];
|
|
8629
|
-
// If children are loaded, check the array
|
|
8630
|
-
if (children !== undefined) {
|
|
8631
|
-
return !children || children.length === 0;
|
|
8632
|
-
}
|
|
8633
|
-
// If childrenCount is available, use it
|
|
8634
|
-
if (childrenCount !== undefined) {
|
|
8635
|
-
return childrenCount === 0;
|
|
8636
|
-
}
|
|
8637
|
-
// If neither is available, try to check via query
|
|
8638
|
-
return await this.checkIfNodeHasChildren(nodeId);
|
|
8639
|
-
}
|
|
8640
|
-
catch {
|
|
8641
|
-
// If findNode fails, check via query
|
|
8642
|
-
return await this.checkIfNodeHasChildren(nodeId);
|
|
8643
|
-
}
|
|
8644
|
-
}
|
|
8645
|
-
/**
|
|
8646
|
-
* Checks if a node has children by querying the data source
|
|
8647
|
-
*/
|
|
8648
|
-
async checkIfNodeHasChildren(nodeId) {
|
|
8649
|
-
if (!this.treeData?.categoryEntityQueryFunc || !this.treeConfig) {
|
|
8650
|
-
return true; // Assume leaf if we can't check
|
|
8651
|
-
}
|
|
8652
|
-
try {
|
|
8653
|
-
const parentKey = this.treeData.categoryEntityDef?.parentKey;
|
|
8654
|
-
if (!parentKey) {
|
|
8655
|
-
return true; // No parent key means flat structure, all nodes are leaves
|
|
8656
|
-
}
|
|
8657
|
-
const event = {
|
|
8658
|
-
...this.treeData.basicQueryEvent,
|
|
8659
|
-
filter: {
|
|
8660
|
-
field: parentKey,
|
|
8661
|
-
value: nodeId,
|
|
8662
|
-
operator: { type: 'equal' },
|
|
8663
|
-
},
|
|
8664
|
-
take: 1, // Only need to check if any children exist
|
|
8665
|
-
};
|
|
8666
|
-
const res = await this.treeData.categoryEntityQueryFunc(event);
|
|
8667
|
-
return !res?.items || res.items.length === 0;
|
|
8668
|
-
}
|
|
8669
|
-
catch {
|
|
8670
|
-
return true; // Assume leaf on error
|
|
8671
|
-
}
|
|
8672
|
-
}
|
|
8673
8359
|
async getSelectedItems() {
|
|
8674
8360
|
// selectedNodeIds now only contains LEAF nodes (already filtered)
|
|
8675
8361
|
const selectedIds = this.selectedNodeIds();
|
|
@@ -8769,8 +8455,6 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
8769
8455
|
[titleField]="textField()"
|
|
8770
8456
|
[idField]="valueField()"
|
|
8771
8457
|
(onNodeSelect)="onNodeSelect($event)"
|
|
8772
|
-
(onSelectionChange)="onSelectionChange($event)"
|
|
8773
|
-
(onNodeToggle)="onNodeToggle($event)"
|
|
8774
8458
|
[nodeTemplate]="itemTemplate"
|
|
8775
8459
|
#tree
|
|
8776
8460
|
>
|
|
@@ -8900,8 +8584,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
8900
8584
|
[titleField]="textField()"
|
|
8901
8585
|
[idField]="valueField()"
|
|
8902
8586
|
(onNodeSelect)="onNodeSelect($event)"
|
|
8903
|
-
(onSelectionChange)="onSelectionChange($event)"
|
|
8904
|
-
(onNodeToggle)="onNodeToggle($event)"
|
|
8905
8587
|
[nodeTemplate]="itemTemplate"
|
|
8906
8588
|
#tree
|
|
8907
8589
|
>
|
|
@@ -12876,7 +12558,7 @@ class AXPLookupWidgetColumnComponent extends AXPColumnWidgetComponent {
|
|
|
12876
12558
|
return get(item, this.displayField()) ?? '';
|
|
12877
12559
|
}
|
|
12878
12560
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPLookupWidgetColumnComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
|
|
12879
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AXPLookupWidgetColumnComponent, isStandalone: true, selector: "ng-component", inputs: { rawValue: "rawValue", rowData: "rowData" }, viewQueries: [{ propertyName: "moreButton", first: true, predicate: ["moreButton"], descendants: true, isSignal: true }, { propertyName: "morePopover", first: true, predicate: ["morePopover"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-flex ax-gap-1 ax-items-center\">\n @if (visibleItems().length > 0) {\n @for (item of visibleItems(); track $index) {\n <span class=\"ax-cursor-pointer hover:ax-text-primary hover:ax-underline\" (click)=\"handleItemClick($index)\">\n {{ getDisplayText(item) }}\n </span>\n @if ($index < visibleItems().length - 1) { <span class=\"ax-text-muted\">\u2022</span>\n }\n }\n } @else {\n <span class=\"ax-text-muted\">---</span>\n }\n\n @if (hasMoreItems()) {\n <span\n class=\"ax-absolute ax-flex ax-items-center ax-end-0 ax-px-1 ax-cursor-pointer ax-h-full hover:ax-primary-lighter\"\n (click)=\"showMoreItems()\" #moreButton>\n <i class=\"fa-light fa-ellipsis-vertical\"></i>\n </span>\n }\n</div>\n\n<!-- More Items Popover -->\n<ax-popover [openOn]=\"'manual'\" #morePopover (openChange)=\"onMorePopoverOpenChange($event)\">\n <div class=\"ax-lightest-surface ax-border ax-rounded-lg ax-shadow-lg ax-p-4 ax-min-w-[280px]\">\n <div class=\"ax-mb-4 ax-border-b ax-pb-2\">\n <h3 class=\"ax-text-base ax-font-semibold\">All {{ allItems().length }} Items</h3>\n </div>\n <div class=\"ax-max-h-64 ax-flex ax-flex-col ax-gap-3\">\n @for (item of allItems(); track $index) {\n <span class=\"ax-cursor-pointer hover:ax-text-primary hover:ax-underline\" (click)=\"showItemDetail(item, $index)\">\n {{ getDisplayText(item) }}\n </span>\n }\n </div>\n </div>\n</ax-popover>", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "ngmodule", type: AXPopoverModule }, { kind: "component", type: i2.AXPopoverComponent, selector: "ax-popover", inputs: ["width", "disabled", "offsetX", "offsetY", "target", "placement", "content", "openOn", "closeOn", "hasBackdrop", "openAfter", "closeAfter", "repositionOnScroll", "backdropClass", "panelClass", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
12561
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AXPLookupWidgetColumnComponent, isStandalone: true, selector: "ng-component", inputs: { rawValue: "rawValue", rowData: "rowData" }, viewQueries: [{ propertyName: "moreButton", first: true, predicate: ["moreButton"], descendants: true, isSignal: true }, { propertyName: "morePopover", first: true, predicate: ["morePopover"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-flex ax-gap-1 ax-items-center\">\n @if (visibleItems().length > 0) {\n @for (item of visibleItems(); track $index) {\n <span class=\"ax-cursor-pointer hover:ax-text-primary hover:ax-underline\" (click)=\"handleItemClick($index)\">\n {{ getDisplayText(item) }}\n </span>\n @if ($index < visibleItems().length - 1) { <span class=\"ax-text-muted\">\u2022</span>\n }\n }\n } @else {\n <span class=\"ax-text-muted\">---</span>\n }\n\n @if (hasMoreItems()) {\n <span\n class=\"ax-absolute ax-flex ax-items-center ax-end-0 ax-px-1 ax-cursor-pointer ax-h-full hover:ax-primary-lighter\"\n (click)=\"showMoreItems()\" #moreButton>\n <i class=\"fa-light fa-ellipsis-vertical\"></i>\n </span>\n }\n</div>\n\n<!-- More Items Popover -->\n<ax-popover [openOn]=\"'manual'\" #morePopover (openChange)=\"onMorePopoverOpenChange($event)\">\n <div class=\"ax-lightest-surface ax-border ax-rounded-lg ax-shadow-lg ax-p-4 ax-min-w-[280px]\">\n <div class=\"ax-mb-4 ax-border-b ax-pb-2\">\n <h3 class=\"ax-text-base ax-font-semibold\">All {{ allItems().length }} Items</h3>\n </div>\n <div class=\"ax-max-h-64 ax-flex ax-flex-col ax-gap-3\">\n @for (item of allItems(); track $index) {\n <span class=\"ax-cursor-pointer hover:ax-text-primary hover:ax-underline\" (click)=\"showItemDetail(item, $index)\">\n {{ getDisplayText(item) }}\n </span>\n }\n </div>\n </div>\n</ax-popover>", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: AXBadgeModule }, { kind: "ngmodule", type: AXButtonModule }, { kind: "ngmodule", type: AXPopoverModule }, { kind: "component", type: i2.AXPopoverComponent, selector: "ax-popover", inputs: ["width", "forceDisableActionSheetStyle", "disabled", "offsetX", "offsetY", "target", "placement", "content", "openOn", "closeOn", "hasBackdrop", "openAfter", "closeAfter", "repositionOnScroll", "backdropClass", "panelClass", "adaptivityEnabled"], outputs: ["onOpened", "onClosed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
12880
12562
|
}
|
|
12881
12563
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPLookupWidgetColumnComponent, decorators: [{
|
|
12882
12564
|
type: Component,
|
|
@@ -14711,11 +14393,11 @@ class AXPEntityModule {
|
|
|
14711
14393
|
useClass: AXPEntitiesListDataSourceDefinition,
|
|
14712
14394
|
multi: true,
|
|
14713
14395
|
},
|
|
14714
|
-
|
|
14715
|
-
|
|
14716
|
-
|
|
14717
|
-
|
|
14718
|
-
|
|
14396
|
+
{
|
|
14397
|
+
provide: AXP_ENTITY_MODIFIER,
|
|
14398
|
+
useValue: layoutOrderingMiddlewareProvider,
|
|
14399
|
+
multi: true,
|
|
14400
|
+
},
|
|
14719
14401
|
provideCommandSetups([
|
|
14720
14402
|
{
|
|
14721
14403
|
key: 'Entity:OpenDetails',
|
|
@@ -14884,11 +14566,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
14884
14566
|
useClass: AXPEntitiesListDataSourceDefinition,
|
|
14885
14567
|
multi: true,
|
|
14886
14568
|
},
|
|
14887
|
-
|
|
14888
|
-
|
|
14889
|
-
|
|
14890
|
-
|
|
14891
|
-
|
|
14569
|
+
{
|
|
14570
|
+
provide: AXP_ENTITY_MODIFIER,
|
|
14571
|
+
useValue: layoutOrderingMiddlewareProvider,
|
|
14572
|
+
multi: true,
|
|
14573
|
+
},
|
|
14892
14574
|
provideCommandSetups([
|
|
14893
14575
|
{
|
|
14894
14576
|
key: 'Entity:OpenDetails',
|
|
@@ -15052,7 +14734,7 @@ function entityMasterCrudActions(options) {
|
|
|
15052
14734
|
create: true,
|
|
15053
14735
|
delete: true,
|
|
15054
14736
|
view: true,
|
|
15055
|
-
edit:
|
|
14737
|
+
edit: false,
|
|
15056
14738
|
...options,
|
|
15057
14739
|
};
|
|
15058
14740
|
const actions = [];
|