@itwin/presentation-hierarchies
Version:
A package for creating hierarchies based on data in iTwin.js iModels.
109 lines • 5.15 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { createMainThreadReleaseOnTimePassedHandler, getClass } from "@itwin/presentation-shared";
import { HierarchyNode } from "../../../HierarchyNode.js";
/** @internal */
export async function getBaseClassGroupingECClasses(schemaProvider, parentNode, nodes) {
// Get all base class names that are provided in the grouping information
const baseClassesFullClassNames = await getGroupingBaseClassNames(nodes);
if (baseClassesFullClassNames.size === 0) {
return [];
}
const releaseMainThread = createMainThreadReleaseOnTimePassedHandler();
const baseClasses = [];
for (const fullName of baseClassesFullClassNames) {
await releaseMainThread();
baseClasses.push(await getClass(schemaProvider, fullName));
}
const sortedClasses = await sortByBaseClass(baseClasses.filter((baseClass) => baseClass.isRelationshipClass() || baseClass.isEntityClass()));
if (parentNode && HierarchyNode.isClassGroupingNode(parentNode)) {
// if we have a class grouping node, we can cut the front of sortedClasses up to a point where our grouping class is
const cutPosition = sortedClasses.findIndex((c) => c.fullName === parentNode.key.className);
if (cutPosition >= 0) {
return sortedClasses.slice(cutPosition + 1);
}
return [];
}
return sortedClasses;
}
/** @internal */
export async function createBaseClassGroupsForSingleBaseClass(nodes, baseECClass, classHierarchyInspector) {
const releaseMainThread = createMainThreadReleaseOnTimePassedHandler();
const groupedNodes = new Array();
const ungroupedNodes = new Array();
const baseClassFullName = baseECClass.fullName;
for (const node of nodes) {
await releaseMainThread();
if (!node.processingParams?.grouping?.byBaseClasses ||
!node.processingParams.grouping.byBaseClasses.fullClassNames.some((className) => className === baseClassFullName)) {
ungroupedNodes.push(node);
continue;
}
const fullCurrentNodeClassName = node.key.instanceKeys[0].className;
const baseCheckerResult = classHierarchyInspector.classDerivesFrom(fullCurrentNodeClassName, baseClassFullName);
/* c8 ignore next */
const isCurrentNodeClassOfBase = baseCheckerResult instanceof Promise ? await baseCheckerResult : baseCheckerResult;
if (isCurrentNodeClassOfBase) {
groupedNodes.push(node);
}
else {
ungroupedNodes.push(node);
}
}
const result = { grouped: [], ungrouped: ungroupedNodes, groupingType: "base-class" };
if (groupedNodes.length > 0) {
const groupingNodeKey = {
type: "class-grouping",
className: baseClassFullName,
};
result.grouped.push({
label: baseECClass.label ?? baseECClass.name,
key: groupingNodeKey,
parentKeys: [...groupedNodes[0].parentKeys],
groupedInstanceKeys: groupedNodes.flatMap((groupedInstanceNode) => groupedInstanceNode.key.instanceKeys),
children: groupedNodes.map((gn) => Object.assign(gn, { parentKeys: [...gn.parentKeys, groupingNodeKey] })),
});
}
return result;
}
async function getGroupingBaseClassNames(nodes) {
const releaseMainThread = createMainThreadReleaseOnTimePassedHandler();
const baseClasses = new Set();
for (const node of nodes) {
await releaseMainThread();
if (node.processingParams?.grouping?.byBaseClasses) {
for (const className of node.processingParams.grouping.byBaseClasses.fullClassNames) {
baseClasses.add(className);
}
}
}
return baseClasses;
}
async function sortByBaseClass(classes) {
if (classes.length === 0) {
return classes;
}
const output = [classes[0]];
for (let inputIndex = 1; inputIndex < classes.length; ++inputIndex) {
let wasAdded = false;
for (let outputIndex = output.length - 1; outputIndex >= 0; --outputIndex) {
if (await classes[inputIndex].is(output[outputIndex])) {
output.splice(outputIndex + 1, 0, classes[inputIndex]);
wasAdded = true;
break;
}
}
if (!wasAdded) {
output.splice(0, 0, classes[inputIndex]);
}
}
return output;
}
/** @internal */
export async function createBaseClassGroupingHandlers(imodelAccess, parentNode, nodes) {
const baseClassGroupingECClasses = await getBaseClassGroupingECClasses(imodelAccess, parentNode, nodes);
return baseClassGroupingECClasses.map((baseECClass) => async (allNodes) => createBaseClassGroupsForSingleBaseClass(allNodes, baseECClass, imodelAccess));
}
//# sourceMappingURL=BaseClassGrouping.js.map