@acorex/platform 20.7.8 → 20.7.9
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 +8 -4
- package/fesm2022/acorex-platform-common.mjs +5 -1
- package/fesm2022/acorex-platform-common.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 +392 -746
- package/fesm2022/acorex-platform-layout-entity.mjs.map +1 -1
- package/fesm2022/acorex-platform-layout-widgets.mjs +105 -192
- package/fesm2022/acorex-platform-layout-widgets.mjs.map +1 -1
- package/layout/builder/index.d.ts +25 -14
- package/layout/components/index.d.ts +295 -2
- package/layout/entity/index.d.ts +33 -55
- package/layout/widgets/index.d.ts +32 -31
- package/package.json +5 -5
|
@@ -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
|
}
|
|
@@ -1376,12 +1383,9 @@ function sortMergedSections(mainSections, extraSections, mainSectionIds) {
|
|
|
1376
1383
|
class AXPCreateEntityCommand {
|
|
1377
1384
|
constructor() {
|
|
1378
1385
|
this.entityForm = inject(AXPEntityFormBuilderService);
|
|
1379
|
-
this.settingsService = inject(AXPSettingsService);
|
|
1380
1386
|
this.entityService = inject(AXPEntityDefinitionRegistryService);
|
|
1381
1387
|
this.toastService = inject(AXToastService);
|
|
1382
1388
|
this.translationService = inject(AXTranslationService);
|
|
1383
|
-
this.eventService = inject(AXPBroadcastEventService);
|
|
1384
|
-
this.platform = inject(AXPlatform);
|
|
1385
1389
|
this.context = {};
|
|
1386
1390
|
}
|
|
1387
1391
|
async execute(input) {
|
|
@@ -1404,100 +1408,83 @@ class AXPCreateEntityCommand {
|
|
|
1404
1408
|
};
|
|
1405
1409
|
}
|
|
1406
1410
|
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
|
-
|
|
1411
|
+
let chain = this.entityForm.entity(`${moduleName}.${entityName}`).create(data);
|
|
1412
|
+
chain.actions((actions) => {
|
|
1413
|
+
actions.cancel('@general:actions.cancel.title');
|
|
1414
|
+
actions.submit('@general:actions.create.title');
|
|
1415
|
+
});
|
|
1416
|
+
if (excludeProperties && excludeProperties.length > 0) {
|
|
1417
|
+
chain = chain.exclude(...excludeProperties);
|
|
1418
|
+
}
|
|
1419
|
+
if (includeProperties && includeProperties.length > 0) {
|
|
1420
|
+
chain = chain.include(...includeProperties);
|
|
1421
|
+
}
|
|
1422
|
+
// Set dialog title: use decoration.header.title if available, otherwise use entityInfo.title
|
|
1423
|
+
if (headerTitle) {
|
|
1424
|
+
chain = chain.title(headerTitle);
|
|
1425
|
+
}
|
|
1426
|
+
else if (entityInfo?.title) {
|
|
1427
|
+
const createText = await this.translationService.translateAsync('@general:actions.create.title');
|
|
1428
|
+
const translatedTitle = await this.translationService.translateAsync(entityInfo.title);
|
|
1429
|
+
chain = chain.title(`${createText} ${translatedTitle}`);
|
|
1430
|
+
}
|
|
1431
|
+
// Set dialog size: prioritize layout.size, then dialogSize from input
|
|
1432
|
+
const finalSize = layoutSize || dialogSize;
|
|
1433
|
+
if (finalSize) {
|
|
1434
|
+
chain.size(finalSize);
|
|
1435
|
+
}
|
|
1436
|
+
const result = await chain
|
|
1437
|
+
.onAction(async (dialogRef) => {
|
|
1438
|
+
if (dialogRef.action() === 'cancel') {
|
|
1439
|
+
return { success: false };
|
|
1440
|
+
}
|
|
1441
|
+
const createFn = entityRef.commands?.create?.execute;
|
|
1442
|
+
if (!createFn) {
|
|
1443
|
+
const msg = await this.translationService.translateAsync('@general:messages.entity.create-command-unavailable');
|
|
1444
|
+
this.toastService.show({
|
|
1445
|
+
color: 'danger',
|
|
1446
|
+
title: await this.translationService.translateAsync('@general:messages.generic.error.title'),
|
|
1447
|
+
content: msg,
|
|
1448
|
+
});
|
|
1449
|
+
throw new Error(msg);
|
|
1446
1450
|
}
|
|
1447
|
-
|
|
1448
|
-
|
|
1451
|
+
dialogRef.setLoading(true);
|
|
1452
|
+
try {
|
|
1449
1453
|
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
1454
|
const result = await createFn(context);
|
|
1460
1455
|
if (result) {
|
|
1461
|
-
dialogRef.close();
|
|
1462
1456
|
return {
|
|
1463
1457
|
success: true,
|
|
1464
|
-
data: result,
|
|
1458
|
+
data: result.data ?? result,
|
|
1465
1459
|
message: {
|
|
1466
1460
|
text: await this.translationService.translateAsync('@general:messages.generic.success.description'),
|
|
1467
1461
|
},
|
|
1468
1462
|
};
|
|
1469
1463
|
}
|
|
1470
1464
|
else {
|
|
1471
|
-
return
|
|
1465
|
+
return {
|
|
1472
1466
|
success: false,
|
|
1473
1467
|
message: {
|
|
1474
|
-
text: await this.translationService.translateAsync('@general:messages.entity.
|
|
1468
|
+
text: await this.translationService.translateAsync('@general:messages.entity.create-failed'),
|
|
1475
1469
|
},
|
|
1476
|
-
}
|
|
1470
|
+
};
|
|
1477
1471
|
}
|
|
1478
1472
|
}
|
|
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) {
|
|
1473
|
+
catch (e) {
|
|
1474
|
+
const errorMsg = e.message ?? (await this.translationService.translateAsync('@general:messages.entity.create-failed'));
|
|
1475
|
+
this.toastService.show({
|
|
1476
|
+
color: 'danger',
|
|
1477
|
+
title: await this.translationService.translateAsync('@general:messages.generic.error.title'),
|
|
1478
|
+
content: errorMsg,
|
|
1479
|
+
});
|
|
1480
|
+
throw e;
|
|
1481
|
+
}
|
|
1482
|
+
finally {
|
|
1498
1483
|
dialogRef.setLoading(false);
|
|
1499
1484
|
}
|
|
1500
|
-
}
|
|
1485
|
+
})
|
|
1486
|
+
.show();
|
|
1487
|
+
return result;
|
|
1501
1488
|
}
|
|
1502
1489
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPCreateEntityCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1503
1490
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AXPCreateEntityCommand, providedIn: 'root' }); }
|
|
@@ -6640,11 +6627,22 @@ class AXPCategoryTreeService {
|
|
|
6640
6627
|
convertToTreeNode(item, config) {
|
|
6641
6628
|
const textField = config.textField ?? 'title';
|
|
6642
6629
|
const valueField = config.valueField ?? 'id';
|
|
6630
|
+
// Determine childrenCount properly:
|
|
6631
|
+
// - Use explicit childrenCount from backend if it's a number (including 0)
|
|
6632
|
+
// - Or use children array length if available
|
|
6633
|
+
// - Default to undefined to indicate "unknown" - this allows lazy loading to work
|
|
6634
|
+
let childrenCount;
|
|
6635
|
+
if (typeof item['childrenCount'] === 'number') {
|
|
6636
|
+
childrenCount = item['childrenCount'];
|
|
6637
|
+
}
|
|
6638
|
+
else if (Array.isArray(item['children'])) {
|
|
6639
|
+
childrenCount = item['children'].length;
|
|
6640
|
+
}
|
|
6643
6641
|
return {
|
|
6644
6642
|
id: String(item[valueField] ?? ''),
|
|
6645
6643
|
title: String(item[textField] ?? ''),
|
|
6646
6644
|
icon: 'fa-solid fa-folder',
|
|
6647
|
-
childrenCount
|
|
6645
|
+
childrenCount,
|
|
6648
6646
|
expanded: false,
|
|
6649
6647
|
data: item,
|
|
6650
6648
|
};
|
|
@@ -6800,7 +6798,6 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
6800
6798
|
this.relevantNodeIds = new Set(); // For search filtering
|
|
6801
6799
|
this.expandedNodesBeforeSearch = []; // Store expanded nodes before search to restore after
|
|
6802
6800
|
this.nodesExpandedDuringSearch = []; // Track nodes we expanded during search
|
|
6803
|
-
this.isInitializing = false; // Flag to track if we're in initialization phase
|
|
6804
6801
|
/** Datasource callback for tree-view component. */
|
|
6805
6802
|
this.datasource = async (id) => {
|
|
6806
6803
|
if (!this.treeData || !this.treeConfig) {
|
|
@@ -6847,31 +6844,48 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
6847
6844
|
}
|
|
6848
6845
|
});
|
|
6849
6846
|
}
|
|
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
|
-
}
|
|
6847
|
+
// Mark pre-selected nodes as selected in the child nodes (for visual display)
|
|
6848
|
+
// ONLY do this during initial load, NOT during user selection changes
|
|
6849
|
+
if (!this.isUpdatingSelection) {
|
|
6850
|
+
const selectedIds = this.selectedNodeIds();
|
|
6851
|
+
childNodes.forEach((node) => {
|
|
6852
|
+
const nodeId = String(node['id'] ?? '');
|
|
6853
|
+
if (nodeId && selectedIds.includes(nodeId)) {
|
|
6854
|
+
node['selected'] = true;
|
|
6855
|
+
}
|
|
6856
|
+
});
|
|
6857
|
+
// After children load, programmatically select pre-selected nodes in tree component
|
|
6858
|
+
// This ensures the tree's internal state matches our selectedNodeIds
|
|
6859
|
+
// Skip this if we're in the middle of a selection update (user is selecting/deselecting)
|
|
6860
|
+
const treeComponent = this.tree();
|
|
6861
|
+
if (treeComponent && selectedIds.length > 0) {
|
|
6862
|
+
// Use setTimeout to ensure nodes are in tree structure before selecting
|
|
6863
|
+
setTimeout(() => {
|
|
6864
|
+
// Double-check we're not in a selection update when timeout fires
|
|
6865
|
+
if (this.isUpdatingSelection) {
|
|
6866
|
+
return;
|
|
6872
6867
|
}
|
|
6873
|
-
|
|
6874
|
-
|
|
6868
|
+
this.isUpdatingSelection = true;
|
|
6869
|
+
try {
|
|
6870
|
+
// Re-check selectedNodeIds at this point (might have changed)
|
|
6871
|
+
const currentSelectedIds = this.selectedNodeIds();
|
|
6872
|
+
childNodes.forEach((node) => {
|
|
6873
|
+
const nodeId = String(node['id'] ?? '');
|
|
6874
|
+
if (nodeId && currentSelectedIds.includes(nodeId) && nodeId !== 'all') {
|
|
6875
|
+
try {
|
|
6876
|
+
treeComponent.selectNode(nodeId);
|
|
6877
|
+
}
|
|
6878
|
+
catch {
|
|
6879
|
+
// Node might not be in tree yet
|
|
6880
|
+
}
|
|
6881
|
+
}
|
|
6882
|
+
});
|
|
6883
|
+
}
|
|
6884
|
+
finally {
|
|
6885
|
+
this.isUpdatingSelection = false;
|
|
6886
|
+
}
|
|
6887
|
+
}, 10);
|
|
6888
|
+
}
|
|
6875
6889
|
}
|
|
6876
6890
|
return childNodes;
|
|
6877
6891
|
};
|
|
@@ -6890,7 +6904,6 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
6890
6904
|
return;
|
|
6891
6905
|
}
|
|
6892
6906
|
this.loading.set(true);
|
|
6893
|
-
this.isInitializing = true; // Mark that we're in initialization phase
|
|
6894
6907
|
try {
|
|
6895
6908
|
this.treeConfig = {
|
|
6896
6909
|
entityKey: this.entityKey(),
|
|
@@ -6900,7 +6913,6 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
6900
6913
|
this.treeData = await this.categoryTreeService.initializeCategoryTree(this.treeConfig);
|
|
6901
6914
|
if (!this.treeData) {
|
|
6902
6915
|
this.loading.set(false);
|
|
6903
|
-
this.isInitializing = false;
|
|
6904
6916
|
return;
|
|
6905
6917
|
}
|
|
6906
6918
|
// Get parentKey from entity definition
|
|
@@ -6946,7 +6958,6 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
6946
6958
|
console.error('Error syncing selection after tree render:', error);
|
|
6947
6959
|
}
|
|
6948
6960
|
}
|
|
6949
|
-
this.isInitializing = false; // Mark initialization as complete
|
|
6950
6961
|
}
|
|
6951
6962
|
//#endregion
|
|
6952
6963
|
//#region ---- Public Methods ----
|
|
@@ -7368,6 +7379,8 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
7368
7379
|
this.nodesExpandedDuringSearch = [];
|
|
7369
7380
|
return;
|
|
7370
7381
|
}
|
|
7382
|
+
// Store current selected IDs before reload
|
|
7383
|
+
const selectedIds = this.selectedNodeIds();
|
|
7371
7384
|
// Reload tree to show all nodes (no filtering)
|
|
7372
7385
|
await treeComponent.reloadData();
|
|
7373
7386
|
// Collapse nodes that were expanded during search (in reverse order - leaves first)
|
|
@@ -7386,88 +7399,136 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
7386
7399
|
}
|
|
7387
7400
|
// Clear the stored expanded nodes
|
|
7388
7401
|
this.expandedNodesBeforeSearch = [];
|
|
7402
|
+
// Restore selection state after tree reload
|
|
7403
|
+
if (selectedIds.length > 0) {
|
|
7404
|
+
// Wait for tree to stabilize after reload
|
|
7405
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
7406
|
+
// Re-sync selection with tree
|
|
7407
|
+
await this.restoreSelectionAfterReload(selectedIds);
|
|
7408
|
+
}
|
|
7389
7409
|
}
|
|
7390
|
-
|
|
7391
|
-
|
|
7410
|
+
/**
|
|
7411
|
+
* Restores selection state after tree reload.
|
|
7412
|
+
* Expands ancestor nodes and selects the previously selected leaf nodes.
|
|
7413
|
+
*/
|
|
7414
|
+
async restoreSelectionAfterReload(selectedIds) {
|
|
7415
|
+
const treeComponent = this.tree();
|
|
7416
|
+
if (!treeComponent || selectedIds.length === 0) {
|
|
7417
|
+
return;
|
|
7418
|
+
}
|
|
7419
|
+
this.isUpdatingSelection = true;
|
|
7420
|
+
try {
|
|
7421
|
+
// Build ancestor chains for selected nodes
|
|
7422
|
+
const ancestorChains = await this.buildAncestorChains(selectedIds);
|
|
7423
|
+
// Expand ancestor nodes to make selected nodes visible
|
|
7424
|
+
await this.expandAncestorNodesInOrder(ancestorChains);
|
|
7425
|
+
// Wait for tree to render expanded nodes
|
|
7426
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
7427
|
+
// Select the nodes visually in the tree
|
|
7428
|
+
for (const id of selectedIds) {
|
|
7429
|
+
try {
|
|
7430
|
+
treeComponent.selectNode(id);
|
|
7431
|
+
}
|
|
7432
|
+
catch {
|
|
7433
|
+
// Node might not be in tree yet
|
|
7434
|
+
}
|
|
7435
|
+
}
|
|
7436
|
+
}
|
|
7437
|
+
finally {
|
|
7438
|
+
this.isUpdatingSelection = false;
|
|
7439
|
+
}
|
|
7392
7440
|
}
|
|
7393
7441
|
async onNodeSelect(event) {
|
|
7394
7442
|
const node = event.node;
|
|
7395
7443
|
const nodeId = String(node['id'] ?? '');
|
|
7396
|
-
if (!node
|
|
7444
|
+
if (!node) {
|
|
7397
7445
|
return;
|
|
7398
7446
|
}
|
|
7399
|
-
//
|
|
7400
|
-
|
|
7401
|
-
if (
|
|
7402
|
-
|
|
7447
|
+
// Only process USER interactions, not programmatic selections
|
|
7448
|
+
// This prevents infinite loops when datasource callback syncs selection
|
|
7449
|
+
if (!event.isUserInteraction) {
|
|
7450
|
+
return;
|
|
7403
7451
|
}
|
|
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) {
|
|
7452
|
+
// Don't process if we're already updating selection
|
|
7453
|
+
if (this.isUpdatingSelection) {
|
|
7412
7454
|
return;
|
|
7413
7455
|
}
|
|
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'] ?? '');
|
|
7456
|
+
// Cache node data for getSelectedItems (except for 'all' node)
|
|
7457
|
+
if (nodeId !== 'all') {
|
|
7420
7458
|
const nodeData = node['data'];
|
|
7421
|
-
if (nodeData &&
|
|
7459
|
+
if (nodeData && typeof nodeData === 'object' && nodeData !== null && !Array.isArray(nodeData)) {
|
|
7422
7460
|
this.nodeDataCache.set(nodeId, nodeData);
|
|
7423
7461
|
}
|
|
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
|
|
7462
|
+
}
|
|
7463
|
+
// Check if this node is being selected or deselected
|
|
7464
|
+
const isSelected = node['selected'] === true;
|
|
7465
|
+
if (this.allowMultiple()) {
|
|
7433
7466
|
const children = node['children'];
|
|
7434
7467
|
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
|
-
|
|
7468
|
+
// Determine if this node has children (is a parent node)
|
|
7469
|
+
// A node has children if: childrenCount > 0, or has non-empty children array
|
|
7470
|
+
// Note: childrenCount === undefined means "unknown" - we need to check via datasource
|
|
7471
|
+
const hasLoadedChildren = children && children.length > 0;
|
|
7472
|
+
const hasChildrenCount = childrenCount !== undefined && childrenCount > 0;
|
|
7473
|
+
const isDefinitelyLeaf = childrenCount === 0 && (!children || children.length === 0);
|
|
7474
|
+
if (isSelected) {
|
|
7475
|
+
// SELECTION: Only add LEAF nodes to selectedNodeIds
|
|
7476
|
+
this.isUpdatingSelection = true;
|
|
7477
|
+
try {
|
|
7478
|
+
if (nodeId === 'all') {
|
|
7479
|
+
// "All Items" selected - recursively select all leaf descendants
|
|
7480
|
+
await this.selectAllLeafDescendants(nodeId);
|
|
7481
|
+
}
|
|
7482
|
+
else if (isDefinitelyLeaf) {
|
|
7483
|
+
// This is definitely a leaf node - add it directly
|
|
7484
|
+
const currentSelected = new Set(this.selectedNodeIds());
|
|
7485
|
+
currentSelected.add(nodeId);
|
|
7486
|
+
this.selectedNodeIds.set(Array.from(currentSelected));
|
|
7487
|
+
}
|
|
7488
|
+
else if (hasLoadedChildren || hasChildrenCount) {
|
|
7489
|
+
// This node has children - only add its leaf descendants
|
|
7490
|
+
await this.selectAllLeafDescendants(nodeId);
|
|
7491
|
+
}
|
|
7492
|
+
else {
|
|
7493
|
+
// childrenCount is undefined - need to check if it has children
|
|
7494
|
+
const childNodes = await this.datasource(nodeId);
|
|
7495
|
+
if (!childNodes || childNodes.length === 0) {
|
|
7496
|
+
// No children - this is a leaf node, add it
|
|
7497
|
+
const currentSelected = new Set(this.selectedNodeIds());
|
|
7498
|
+
currentSelected.add(nodeId);
|
|
7499
|
+
this.selectedNodeIds.set(Array.from(currentSelected));
|
|
7500
|
+
}
|
|
7501
|
+
else {
|
|
7502
|
+
// Has children - only add leaf descendants
|
|
7503
|
+
await this.selectAllLeafDescendants(nodeId);
|
|
7504
|
+
}
|
|
7505
|
+
}
|
|
7506
|
+
}
|
|
7507
|
+
finally {
|
|
7508
|
+
this.isUpdatingSelection = false;
|
|
7509
|
+
}
|
|
7510
|
+
}
|
|
7511
|
+
else {
|
|
7512
|
+
// DESELECTION: Remove node (if leaf) and all leaf descendants from selectedNodeIds
|
|
7513
|
+
this.isUpdatingSelection = true;
|
|
7514
|
+
try {
|
|
7515
|
+
await this.deselectAllLeafDescendants(nodeId);
|
|
7516
|
+
}
|
|
7517
|
+
finally {
|
|
7518
|
+
this.isUpdatingSelection = false;
|
|
7519
|
+
}
|
|
7520
|
+
}
|
|
7521
|
+
}
|
|
7522
|
+
else {
|
|
7523
|
+
// Single selection mode: just update selectedNodeIds
|
|
7524
|
+
if (isSelected) {
|
|
7525
|
+
this.selectedNodeIds.set([nodeId]);
|
|
7526
|
+
}
|
|
7527
|
+
else {
|
|
7528
|
+
this.selectedNodeIds.set([]);
|
|
7529
|
+
}
|
|
7530
|
+
}
|
|
7531
|
+
}
|
|
7471
7532
|
async onConfirm() {
|
|
7472
7533
|
// Use internal selectedNodeIds state which is kept in sync via onNodeSelect events
|
|
7473
7534
|
const selectedItems = await this.getSelectedItems();
|
|
@@ -7480,21 +7541,11 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
7480
7541
|
* Clears all selected items
|
|
7481
7542
|
*/
|
|
7482
7543
|
onClearAll() {
|
|
7544
|
+
this.selectedNodeIds.set([]);
|
|
7483
7545
|
const treeComponent = this.tree();
|
|
7484
7546
|
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
|
-
}
|
|
7547
|
+
treeComponent.deselectAll();
|
|
7495
7548
|
}
|
|
7496
|
-
// Clear the selection
|
|
7497
|
-
this.selectedNodeIds.set([]);
|
|
7498
7549
|
}
|
|
7499
7550
|
/**
|
|
7500
7551
|
* Checks if a node matches the current search term
|
|
@@ -7513,543 +7564,207 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
7513
7564
|
return this.selectedNodeIds().includes(id);
|
|
7514
7565
|
}
|
|
7515
7566
|
/**
|
|
7516
|
-
*
|
|
7517
|
-
*
|
|
7518
|
-
* Parent states are calculated based on leaf descendants
|
|
7567
|
+
* Expands parent nodes, collects all LEAF descendants, and selects them visually.
|
|
7568
|
+
* If the parent itself is a leaf (no children), it will be added.
|
|
7519
7569
|
*/
|
|
7520
|
-
async
|
|
7521
|
-
if (!
|
|
7570
|
+
async selectAllLeafDescendants(parentId) {
|
|
7571
|
+
if (!this.treeData || !this.treeConfig) {
|
|
7522
7572
|
return;
|
|
7523
7573
|
}
|
|
7524
|
-
const id = String(nodeId);
|
|
7525
7574
|
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
7575
|
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
|
-
}
|
|
7576
|
+
const leafIds = new Set();
|
|
7577
|
+
// Expand and collect leaf nodes simultaneously
|
|
7578
|
+
await this.collectLeafNodes(parentId, leafIds, treeComponent);
|
|
7579
|
+
// Also check if the parent itself is a leaf (has no children)
|
|
7580
|
+
if (parentId !== 'all') {
|
|
7581
|
+
const isLeaf = await this.isLeafNodeCheck(parentId);
|
|
7582
|
+
if (isLeaf) {
|
|
7583
|
+
leafIds.add(parentId);
|
|
7570
7584
|
}
|
|
7571
7585
|
}
|
|
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();
|
|
7586
|
+
if (leafIds.size === 0) {
|
|
7587
|
+
return;
|
|
7588
|
+
}
|
|
7589
|
+
// Update our internal state
|
|
7590
|
+
const currentSelected = new Set(this.selectedNodeIds());
|
|
7591
|
+
for (const leafId of leafIds) {
|
|
7592
|
+
currentSelected.add(leafId);
|
|
7593
|
+
}
|
|
7594
|
+
this.selectedNodeIds.set(Array.from(currentSelected));
|
|
7595
|
+
// Select all leaf nodes visually in the tree
|
|
7596
7596
|
if (treeComponent) {
|
|
7597
|
-
for (const leafId of
|
|
7597
|
+
for (const leafId of leafIds) {
|
|
7598
7598
|
try {
|
|
7599
|
-
|
|
7600
|
-
if (node) {
|
|
7601
|
-
treeComponent.deselectNode(leafId);
|
|
7602
|
-
}
|
|
7599
|
+
treeComponent.selectNode(leafId);
|
|
7603
7600
|
}
|
|
7604
7601
|
catch {
|
|
7605
|
-
// Node might not be in tree
|
|
7602
|
+
// Node might not be in tree yet
|
|
7606
7603
|
}
|
|
7607
7604
|
}
|
|
7608
7605
|
}
|
|
7609
7606
|
}
|
|
7610
|
-
|
|
7611
|
-
|
|
7607
|
+
catch (error) {
|
|
7608
|
+
console.error(`Error selecting leaf descendants for node ${parentId}:`, error);
|
|
7612
7609
|
}
|
|
7613
7610
|
}
|
|
7614
7611
|
/**
|
|
7615
|
-
*
|
|
7616
|
-
*
|
|
7612
|
+
* Removes all LEAF descendants from selectedNodeIds.
|
|
7613
|
+
* For 'all' node: clears everything and uses tree's deselectAll().
|
|
7614
|
+
* For other nodes: tree handles visual state via user click.
|
|
7617
7615
|
*/
|
|
7618
|
-
async
|
|
7619
|
-
if (!this.treeData || !this.treeConfig
|
|
7616
|
+
async deselectAllLeafDescendants(parentId) {
|
|
7617
|
+
if (!this.treeData || !this.treeConfig) {
|
|
7620
7618
|
return;
|
|
7621
7619
|
}
|
|
7622
7620
|
try {
|
|
7623
|
-
|
|
7624
|
-
if (
|
|
7625
|
-
|
|
7626
|
-
//
|
|
7621
|
+
// Special case: deselecting 'all' clears everything
|
|
7622
|
+
if (parentId === 'all') {
|
|
7623
|
+
this.selectedNodeIds.set([]);
|
|
7624
|
+
// Use tree's deselectAll() to clear all visual selections
|
|
7625
|
+
const treeComponent = this.tree();
|
|
7626
|
+
if (treeComponent) {
|
|
7627
|
+
treeComponent.deselectAll();
|
|
7628
|
+
}
|
|
7627
7629
|
return;
|
|
7628
7630
|
}
|
|
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
|
-
}
|
|
7631
|
+
const leafIds = new Set();
|
|
7632
|
+
await this.collectLeafNodes(parentId, leafIds);
|
|
7633
|
+
// Also check if the parent itself is a leaf
|
|
7634
|
+
const isLeaf = await this.isLeafNodeCheck(parentId);
|
|
7635
|
+
if (isLeaf) {
|
|
7636
|
+
leafIds.add(parentId);
|
|
7649
7637
|
}
|
|
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
|
-
}
|
|
7638
|
+
if (leafIds.size === 0) {
|
|
7639
|
+
return;
|
|
7688
7640
|
}
|
|
7641
|
+
// Only update our internal state - tree handles visual state
|
|
7642
|
+
const currentSelected = this.selectedNodeIds();
|
|
7643
|
+
const newSelected = currentSelected.filter((id) => !leafIds.has(id));
|
|
7689
7644
|
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
7645
|
}
|
|
7705
|
-
|
|
7706
|
-
|
|
7646
|
+
catch (error) {
|
|
7647
|
+
console.error(`Error deselecting leaf descendants for node ${parentId}:`, error);
|
|
7707
7648
|
}
|
|
7708
7649
|
}
|
|
7709
7650
|
/**
|
|
7710
|
-
*
|
|
7651
|
+
* Recursively expands parent nodes and collects LEAF node IDs.
|
|
7652
|
+
* When treeComponent is provided, expands each parent node before loading children.
|
|
7711
7653
|
*/
|
|
7712
|
-
async
|
|
7713
|
-
if (!this.treeData || !this.treeConfig
|
|
7654
|
+
async collectLeafNodes(parentId, collection, treeComponent) {
|
|
7655
|
+
if (!this.treeData || !this.treeConfig) {
|
|
7714
7656
|
return;
|
|
7715
7657
|
}
|
|
7716
7658
|
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);
|
|
7659
|
+
let childNodes;
|
|
7660
|
+
if (parentId === 'all') {
|
|
7661
|
+
// Expand 'all' node if tree component provided
|
|
7662
|
+
if (treeComponent) {
|
|
7663
|
+
try {
|
|
7664
|
+
treeComponent.expandNode('all');
|
|
7729
7665
|
}
|
|
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);
|
|
7666
|
+
catch {
|
|
7667
|
+
// Ignore expand errors
|
|
7739
7668
|
}
|
|
7740
|
-
// Recursively collect descendants
|
|
7741
|
-
await this.collectAllDescendants(childId, collection);
|
|
7742
7669
|
}
|
|
7670
|
+
// Get root node's children
|
|
7671
|
+
const rootNodes = await this.datasource();
|
|
7672
|
+
if (!rootNodes || rootNodes.length === 0) {
|
|
7673
|
+
return;
|
|
7674
|
+
}
|
|
7675
|
+
const rootNode = rootNodes[0];
|
|
7676
|
+
childNodes = rootNode['children'] || [];
|
|
7743
7677
|
}
|
|
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));
|
|
7678
|
+
else {
|
|
7679
|
+
// Expand this parent node if tree component provided
|
|
7680
|
+
if (treeComponent) {
|
|
7681
|
+
try {
|
|
7682
|
+
treeComponent.expandNode(parentId);
|
|
7683
|
+
}
|
|
7684
|
+
catch {
|
|
7685
|
+
// Ignore expand errors
|
|
7775
7686
|
}
|
|
7776
7687
|
}
|
|
7777
|
-
|
|
7778
|
-
|
|
7779
|
-
}
|
|
7688
|
+
// Load children via datasource
|
|
7689
|
+
childNodes = await this.datasource(parentId);
|
|
7780
7690
|
}
|
|
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
7691
|
if (!childNodes || childNodes.length === 0) {
|
|
7801
7692
|
return;
|
|
7802
7693
|
}
|
|
7803
|
-
|
|
7804
|
-
// Collect all child IDs
|
|
7694
|
+
// Process each child
|
|
7805
7695
|
for (const childNode of childNodes) {
|
|
7806
7696
|
const childId = String(childNode['id'] ?? '');
|
|
7807
|
-
if (childId
|
|
7808
|
-
|
|
7697
|
+
if (!childId || childId === 'all' || collection.has(childId)) {
|
|
7698
|
+
continue;
|
|
7809
7699
|
}
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
try {
|
|
7818
|
-
treeComponent.deselectNode(childId);
|
|
7700
|
+
// Cache node data
|
|
7701
|
+
this.cacheNodeFromTreeNode(childNode);
|
|
7702
|
+
// Check if this child has children
|
|
7703
|
+
const hasChildren = await this.nodeHasChildren(childId, childNode);
|
|
7704
|
+
if (!hasChildren) {
|
|
7705
|
+
// This is a LEAF node - add it
|
|
7706
|
+
collection.add(childId);
|
|
7819
7707
|
}
|
|
7820
|
-
|
|
7821
|
-
//
|
|
7708
|
+
else {
|
|
7709
|
+
// Has children - expand and recurse (pass tree component to expand children too)
|
|
7710
|
+
await this.collectLeafNodes(childId, collection, treeComponent);
|
|
7822
7711
|
}
|
|
7823
7712
|
}
|
|
7824
|
-
// Recursively deselect children of each child
|
|
7825
|
-
await Promise.all(childIdsToDeselect.map((childId) => this.loadAndDeselectChildrenRecursively(childId)));
|
|
7826
7713
|
}
|
|
7827
7714
|
catch (error) {
|
|
7828
|
-
console.error(`Error
|
|
7715
|
+
console.error(`Error collecting leaf nodes for ${parentId}:`, error);
|
|
7829
7716
|
}
|
|
7830
7717
|
}
|
|
7831
7718
|
/**
|
|
7832
|
-
*
|
|
7833
|
-
* Called after a node is selected/deselected to update parent checkbox states
|
|
7719
|
+
* Checks if a node has children
|
|
7834
7720
|
*/
|
|
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;
|
|
7721
|
+
async nodeHasChildren(nodeId, node) {
|
|
7722
|
+
// First check node properties if available
|
|
7723
|
+
if (node) {
|
|
7724
|
+
const children = node['children'];
|
|
7725
|
+
const childrenCount = node['childrenCount'];
|
|
7726
|
+
if (children && children.length > 0) {
|
|
7727
|
+
return true;
|
|
7860
7728
|
}
|
|
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;
|
|
7729
|
+
if (childrenCount !== undefined && childrenCount > 0) {
|
|
7730
|
+
return true;
|
|
7935
7731
|
}
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
break;
|
|
7732
|
+
if (childrenCount === 0) {
|
|
7733
|
+
return false;
|
|
7939
7734
|
}
|
|
7940
7735
|
}
|
|
7736
|
+
// Query datasource to check
|
|
7737
|
+
const childNodes = await this.datasource(nodeId);
|
|
7738
|
+
return childNodes && childNodes.length > 0;
|
|
7941
7739
|
}
|
|
7942
7740
|
/**
|
|
7943
|
-
*
|
|
7944
|
-
* This method directly calls datasource without expanding/collapsing nodes to avoid UI glitches
|
|
7741
|
+
* Checks if a node is a leaf (has no children)
|
|
7945
7742
|
*/
|
|
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
|
-
}
|
|
7743
|
+
async isLeafNodeCheck(nodeId) {
|
|
7744
|
+
const hasChildren = await this.nodeHasChildren(nodeId);
|
|
7745
|
+
return !hasChildren;
|
|
8027
7746
|
}
|
|
8028
|
-
|
|
8029
|
-
|
|
8030
|
-
|
|
8031
|
-
|
|
7747
|
+
/**
|
|
7748
|
+
* Caches node data from a tree node
|
|
7749
|
+
*/
|
|
7750
|
+
cacheNodeFromTreeNode(node) {
|
|
7751
|
+
const nodeId = String(node['id'] ?? '');
|
|
7752
|
+
if (!nodeId || !this.treeConfig) {
|
|
8032
7753
|
return;
|
|
8033
7754
|
}
|
|
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;
|
|
7755
|
+
let nodeData = node['data'];
|
|
7756
|
+
if (!nodeData || typeof nodeData !== 'object') {
|
|
7757
|
+
const valueField = this.treeConfig.valueField || 'id';
|
|
7758
|
+
const textField = this.treeConfig.textField || 'title';
|
|
7759
|
+
nodeData = {
|
|
7760
|
+
[valueField]: nodeId,
|
|
7761
|
+
[textField]: node['title'] ?? '',
|
|
7762
|
+
...node,
|
|
7763
|
+
};
|
|
8051
7764
|
}
|
|
7765
|
+
this.nodeDataCache.set(nodeId, nodeData);
|
|
8052
7766
|
}
|
|
7767
|
+
//#endregion
|
|
8053
7768
|
/**
|
|
8054
7769
|
* Builds complete ancestor chains for all selected node IDs.
|
|
8055
7770
|
* Returns a Map where key is the selected node ID and value is array of ancestor IDs from root to parent.
|
|
@@ -8430,42 +8145,38 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
8430
8145
|
const treeComponent = this.tree();
|
|
8431
8146
|
if (treeComponent) {
|
|
8432
8147
|
setTimeout(() => {
|
|
8433
|
-
|
|
8434
|
-
|
|
8435
|
-
|
|
8436
|
-
|
|
8437
|
-
|
|
8438
|
-
|
|
8148
|
+
this.isUpdatingSelection = true;
|
|
8149
|
+
try {
|
|
8150
|
+
const selectedIds = this.selectedNodeIds();
|
|
8151
|
+
selectedIds.forEach((id) => {
|
|
8152
|
+
if (id && id !== 'all') {
|
|
8153
|
+
try {
|
|
8154
|
+
treeComponent.selectNode(id);
|
|
8155
|
+
}
|
|
8156
|
+
catch {
|
|
8157
|
+
// Node might not be in tree yet
|
|
8158
|
+
}
|
|
8159
|
+
}
|
|
8160
|
+
});
|
|
8161
|
+
}
|
|
8162
|
+
finally {
|
|
8163
|
+
this.isUpdatingSelection = false;
|
|
8164
|
+
}
|
|
8439
8165
|
}, 0);
|
|
8440
8166
|
}
|
|
8441
8167
|
}
|
|
8442
8168
|
/**
|
|
8443
|
-
* Processes child nodes: marks excluded as disabled
|
|
8169
|
+
* Processes child nodes: marks excluded as disabled
|
|
8170
|
+
* Selection marking is handled in datasource callback ONLY during initial load
|
|
8444
8171
|
*/
|
|
8445
8172
|
processChildNodes(childNodes) {
|
|
8446
8173
|
const excludedId = this.excludedNodeId();
|
|
8447
|
-
const selectedIds = this.selectedNodeIds();
|
|
8448
8174
|
childNodes.forEach((node) => {
|
|
8449
8175
|
const nodeId = String(node['id'] ?? '');
|
|
8450
8176
|
if (excludedId && nodeId === excludedId) {
|
|
8451
8177
|
node['disabled'] = true;
|
|
8452
8178
|
}
|
|
8453
|
-
if (nodeId && selectedIds.includes(nodeId)) {
|
|
8454
|
-
node['selected'] = true;
|
|
8455
|
-
}
|
|
8456
8179
|
});
|
|
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
8180
|
}
|
|
8470
8181
|
/**
|
|
8471
8182
|
* Caches node data from items array
|
|
@@ -8609,67 +8320,6 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
8609
8320
|
const textField = this.treeConfig.textField || 'title';
|
|
8610
8321
|
return String(nodeData[textField] ?? '');
|
|
8611
8322
|
}
|
|
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
8323
|
async getSelectedItems() {
|
|
8674
8324
|
// selectedNodeIds now only contains LEAF nodes (already filtered)
|
|
8675
8325
|
const selectedIds = this.selectedNodeIds();
|
|
@@ -8769,8 +8419,6 @@ class AXPEntityCategoryTreeSelectorComponent extends AXBasePageComponent {
|
|
|
8769
8419
|
[titleField]="textField()"
|
|
8770
8420
|
[idField]="valueField()"
|
|
8771
8421
|
(onNodeSelect)="onNodeSelect($event)"
|
|
8772
|
-
(onSelectionChange)="onSelectionChange($event)"
|
|
8773
|
-
(onNodeToggle)="onNodeToggle($event)"
|
|
8774
8422
|
[nodeTemplate]="itemTemplate"
|
|
8775
8423
|
#tree
|
|
8776
8424
|
>
|
|
@@ -8900,8 +8548,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
8900
8548
|
[titleField]="textField()"
|
|
8901
8549
|
[idField]="valueField()"
|
|
8902
8550
|
(onNodeSelect)="onNodeSelect($event)"
|
|
8903
|
-
(onSelectionChange)="onSelectionChange($event)"
|
|
8904
|
-
(onNodeToggle)="onNodeToggle($event)"
|
|
8905
8551
|
[nodeTemplate]="itemTemplate"
|
|
8906
8552
|
#tree
|
|
8907
8553
|
>
|