Skip to content

Commit b043454

Browse files
authored
[Tree widget]: Prerequisite for children visibility changes (#1472)
* Prerequisite for children visibility changes * Cleanup * Prettier * Change getVisibilityChangeTargets to accept node * Fix isFiltered to hasDirectNonFilteredTargets * Prettier * Apply suggestion * Add back getElementDisplayStatus * Address comments * Change filtered tree to accept correct nodes * Fix lint issues
1 parent 76d1368 commit b043454

File tree

4 files changed

+149
-168
lines changed

4 files changed

+149
-168
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "none",
3+
"comment": "",
4+
"packageName": "@itwin/tree-widget-react",
5+
"email": "[email protected]",
6+
"dependentChangeType": "none"
7+
}

packages/itwin/tree-widget/src/tree-widget-react/components/trees/models-tree/ModelsTreeDefinition.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,13 +188,36 @@ export class ModelsTreeDefinition implements HierarchyDefinition {
188188

189189
public async postProcessNode(node: ProcessedHierarchyNode): Promise<ProcessedHierarchyNode> {
190190
if (ProcessedHierarchyNode.isGroupingNode(node)) {
191+
let hasDirectNonFilteredTargets = false;
192+
let hasFilterTargetAncestor = false;
193+
for (const child of node.children) {
194+
if (child.filtering) {
195+
if (child.filtering.hasFilterTargetAncestor) {
196+
hasFilterTargetAncestor = true;
197+
break;
198+
}
199+
if (!child.filtering.isFilterTarget) {
200+
hasDirectNonFilteredTargets = true;
201+
break;
202+
}
203+
}
204+
}
191205
return {
192206
...node,
207+
...(hasFilterTargetAncestor
208+
? {
209+
filtering: {
210+
...(node.filtering ?? {}),
211+
hasFilterTargetAncestor,
212+
},
213+
}
214+
: {}),
193215
label: this._hierarchyConfig.elementClassGrouping === "enableWithCounts" ? `${node.label} (${node.children.length})` : node.label,
194216
extendedData: {
195217
...node.extendedData,
196218
// add `modelId` and `categoryId` from the first grouped element
197219
...node.children[0].extendedData,
220+
...(hasDirectNonFilteredTargets ? { hasDirectNonFilteredTargets } : {}),
198221
// `imageId` is assigned to instance nodes at query time, but grouping ones need to
199222
// be handled during post-processing
200223
imageId: "icon-ec-class",

packages/itwin/tree-widget/src/tree-widget-react/components/trees/models-tree/internal/FilteredTree.ts

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
* See LICENSE.md in the project root for license terms and full copyright notice.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { assert } from "@itwin/core-bentley";
7-
import { HierarchyFilteringPath, HierarchyNodeIdentifier, HierarchyNodeKey } from "@itwin/presentation-hierarchies";
6+
import { assert, Id64 } from "@itwin/core-bentley";
7+
import { HierarchyFilteringPath, HierarchyNode, HierarchyNodeIdentifier, HierarchyNodeKey } from "@itwin/presentation-hierarchies";
88

9-
import type { Id64String } from "@itwin/core-bentley";
10-
import type { HierarchyNode } from "@itwin/presentation-hierarchies";
11-
import type { ECClassHierarchyInspector, InstanceKey } from "@itwin/presentation-shared";
9+
import type { Id64Arg, Id64String } from "@itwin/core-bentley";
10+
import type { ClassGroupingNodeKey, InstancesNodeKey } from "@itwin/presentation-hierarchies";
11+
import type { ECClassHierarchyInspector } from "@itwin/presentation-shared";
1212

1313
interface FilteredTreeRootNode {
1414
children: Map<Id64String, FilteredTreeNode>;
@@ -37,8 +37,9 @@ interface ElementFilteredTreeNode extends BaseFilteredTreeNode {
3737

3838
type FilteredTreeNode = GenericFilteredTreeNode | CategoryFilteredTreeNode | ElementFilteredTreeNode;
3939

40+
/** @internal */
4041
export interface FilteredTree {
41-
getVisibilityChangeTargets(node: HierarchyNode): VisibilityChangeTargets;
42+
getVisibilityChangeTargets(node: HierarchyNode & { key: ClassGroupingNodeKey | InstancesNodeKey }): VisibilityChangeTargets;
4243
}
4344

4445
export const SUBJECT_CLASS_NAME = "BisCore.Subject" as const;
@@ -52,18 +53,21 @@ function createCategoryKey(modelId: string, categoryId: string): CategoryKey {
5253
return `${modelId}-${categoryId}`;
5354
}
5455

56+
/** @internal */
5557
export function parseCategoryKey(key: CategoryKey) {
5658
const [modelId, categoryId] = key.split("-");
5759
return { modelId, categoryId };
5860
}
5961

60-
interface VisibilityChangeTargets {
62+
/** @internal */
63+
export interface VisibilityChangeTargets {
6164
subjects?: Set<Id64String>;
6265
models?: Set<Id64String>;
6366
categories?: Set<CategoryKey>;
64-
elements?: Map<CategoryKey, Set<Id64String>>;
67+
elements?: Map<CategoryKey, Map<Id64String, { isFilterTarget: boolean }>>;
6568
}
6669

70+
/** @internal */
6771
export async function createFilteredTree(imodelAccess: ECClassHierarchyInspector, filteringPaths: HierarchyFilteringPath[]): Promise<FilteredTree> {
6872
const root: FilteredTreeRootNode = {
6973
children: new Map(),
@@ -102,19 +106,14 @@ export async function createFilteredTree(imodelAccess: ECClassHierarchyInspector
102106
}
103107

104108
return {
105-
getVisibilityChangeTargets: (node: HierarchyNode) => getVisibilityChangeTargets(root, node),
109+
getVisibilityChangeTargets: (node) => getVisibilityChangeTargets(root, node),
106110
};
107111
}
108112

109-
function getVisibilityChangeTargets(root: FilteredTreeRootNode, node: HierarchyNode) {
110-
let lookupParents: Array<{ children?: Map<Id64String, FilteredTreeNode> }> = [root];
113+
function getVisibilityChangeTargets(root: FilteredTreeRootNode, node: HierarchyNode & { key: ClassGroupingNodeKey | InstancesNodeKey }) {
114+
let lookupParents: Array<FilteredTreeRootNode | FilteredTreeNode> = [root];
111115
const changeTargets: VisibilityChangeTargets = {};
112116

113-
const nodeKey = node.key;
114-
if (!HierarchyNodeKey.isInstances(nodeKey)) {
115-
return changeTargets;
116-
}
117-
118117
// find the filtered parent nodes of the `node`
119118
for (const parentKey of node.parentKeys) {
120119
if (!HierarchyNodeKey.isInstances(parentKey)) {
@@ -123,15 +122,18 @@ function getVisibilityChangeTargets(root: FilteredTreeRootNode, node: HierarchyN
123122

124123
// tree node might be merged from multiple instances. As filtered tree stores only one instance per node, we need to find all matching nodes
125124
// and use them when checking for matching node in one level deeper.
126-
const parentNodes = findMatchingFilteredNodes(lookupParents, parentKey.instanceKeys);
125+
const parentNodes = findMatchingFilteredNodes(
126+
lookupParents,
127+
parentKey.instanceKeys.map((key) => key.id),
128+
);
127129
if (parentNodes.length === 0) {
128130
return changeTargets;
129131
}
130132
lookupParents = parentNodes;
131133
}
132-
134+
const ids = HierarchyNode.isInstancesNode(node) ? node.key.instanceKeys.map(({ id }) => id) : node.groupedInstanceKeys.map(({ id }) => id);
133135
// find filtered nodes that match the `node`
134-
const filteredNodes = findMatchingFilteredNodes(lookupParents, nodeKey.instanceKeys);
136+
const filteredNodes = findMatchingFilteredNodes(lookupParents, ids);
135137
if (filteredNodes.length === 0) {
136138
return changeTargets;
137139
}
@@ -140,10 +142,17 @@ function getVisibilityChangeTargets(root: FilteredTreeRootNode, node: HierarchyN
140142
return changeTargets;
141143
}
142144

143-
function findMatchingFilteredNodes(lookupParents: Array<{ children?: Map<Id64String, FilteredTreeNode> }>, keys: InstanceKey[]) {
144-
return lookupParents
145-
.flatMap((lookup) => keys.map((key) => lookup.children?.get(key.id)))
146-
.filter((lookupNode): lookupNode is FilteredTreeNode => lookupNode !== undefined);
145+
function findMatchingFilteredNodes(lookupParents: Array<FilteredTreeRootNode | FilteredTreeNode>, ids: Id64Arg): Array<FilteredTreeNode> {
146+
return lookupParents.flatMap((lookup) => {
147+
const childrenArray = Array<FilteredTreeNode>();
148+
for (const id of Id64.iterable(ids)) {
149+
const node = lookup.children?.get(id);
150+
if (node) {
151+
childrenArray.push(node);
152+
}
153+
}
154+
return childrenArray;
155+
});
147156
}
148157

149158
function collectVisibilityChangeTargets(changeTargets: VisibilityChangeTargets, node: FilteredTreeNode) {
@@ -181,11 +190,10 @@ function addTarget(filterTargets: VisibilityChangeTargets, node: FilteredTreeNod
181190
const categoryKey = createCategoryKey(node.modelId, node.categoryId);
182191
const elements = (filterTargets.elements ??= new Map()).get(categoryKey);
183192
if (elements) {
184-
elements.add(node.id);
185-
return;
193+
elements.set(node.id, { isFilterTarget: node.isFilterTarget });
194+
} else {
195+
filterTargets.elements.set(categoryKey, new Map([[node.id, { isFilterTarget: node.isFilterTarget }]]));
186196
}
187-
filterTargets.elements.set(categoryKey, new Set([node.id]));
188-
return;
189197
}
190198
}
191199

0 commit comments

Comments
 (0)