@itwin/tree-widget-react 3.0.3 → 3.2.0
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/CHANGELOG.md +31 -19
- package/README.md +8 -8
- package/lib/cjs/components/tree-header/TreeHeader.js +1 -1
- package/lib/cjs/components/tree-header/TreeHeader.js.map +1 -1
- package/lib/cjs/components/trees/categories-tree/CategoriesTreeButtons.js +3 -3
- package/lib/cjs/components/trees/categories-tree/CategoriesTreeButtons.js.map +1 -1
- package/lib/cjs/components/trees/categories-tree/CategoriesVisibilityHandler.js +2 -1
- package/lib/cjs/components/trees/categories-tree/CategoriesVisibilityHandler.js.map +1 -1
- package/lib/cjs/components/trees/{models-tree/internal → common}/Tooltip.d.ts +7 -3
- package/lib/cjs/components/trees/{models-tree/internal → common}/Tooltip.js +5 -5
- package/lib/cjs/components/trees/common/Tooltip.js.map +1 -0
- package/lib/cjs/components/trees/common/UseActiveViewport.js.map +1 -1
- package/lib/cjs/components/trees/common/UseHierarchyVisibility.js +3 -3
- package/lib/cjs/components/trees/common/UseHierarchyVisibility.js.map +1 -1
- package/lib/cjs/components/trees/common/components/Tree.d.ts +6 -1
- package/lib/cjs/components/trees/common/components/Tree.js +14 -4
- package/lib/cjs/components/trees/common/components/Tree.js.map +1 -1
- package/lib/cjs/components/trees/common/components/TreeNodeCheckbox.js +5 -5
- package/lib/cjs/components/trees/common/components/TreeNodeCheckbox.js.map +1 -1
- package/lib/cjs/components/trees/common/components/TreeRenderer.d.ts +1 -1
- package/lib/cjs/components/trees/common/components/TreeRenderer.js +4 -3
- package/lib/cjs/components/trees/common/components/TreeRenderer.js.map +1 -1
- package/lib/cjs/components/trees/common/components/TreeRenderer.scss +0 -6
- package/lib/cjs/components/trees/models-tree/ModelsTree.d.ts +1 -1
- package/lib/cjs/components/trees/models-tree/ModelsTree.js +2 -1
- package/lib/cjs/components/trees/models-tree/ModelsTree.js.map +1 -1
- package/lib/cjs/components/trees/models-tree/ModelsTreeButtons.js +13 -13
- package/lib/cjs/components/trees/models-tree/ModelsTreeButtons.js.map +1 -1
- package/lib/cjs/components/trees/models-tree/ModelsTreeComponent.d.ts +1 -1
- package/lib/cjs/components/trees/models-tree/ModelsTreeComponent.js.map +1 -1
- package/lib/cjs/components/trees/models-tree/ModelsTreeDefinition.js +1 -1
- package/lib/cjs/components/trees/models-tree/ModelsTreeDefinition.js.map +1 -1
- package/lib/cjs/components/trees/models-tree/UseModelsTree.d.ts +12 -3
- package/lib/cjs/components/trees/models-tree/UseModelsTree.js +32 -13
- package/lib/cjs/components/trees/models-tree/UseModelsTree.js.map +1 -1
- package/lib/cjs/components/trees/models-tree/internal/AlwaysAndNeverDrawnElementInfo.d.ts +5 -0
- package/lib/cjs/components/trees/models-tree/internal/AlwaysAndNeverDrawnElementInfo.js +28 -7
- package/lib/cjs/components/trees/models-tree/internal/AlwaysAndNeverDrawnElementInfo.js.map +1 -1
- package/lib/cjs/components/trees/models-tree/internal/FilteredTree.d.ts +25 -0
- package/lib/cjs/components/trees/models-tree/internal/FilteredTree.js +178 -0
- package/lib/cjs/components/trees/models-tree/internal/FilteredTree.js.map +1 -0
- package/lib/cjs/components/trees/models-tree/internal/ModelsTreeIdsCache.d.ts +2 -1
- package/lib/cjs/components/trees/models-tree/internal/ModelsTreeIdsCache.js +55 -23
- package/lib/cjs/components/trees/models-tree/internal/ModelsTreeIdsCache.js.map +1 -1
- package/lib/cjs/components/trees/models-tree/internal/ModelsTreeNode.d.ts +12 -6
- package/lib/cjs/components/trees/models-tree/internal/ModelsTreeNode.js +18 -1
- package/lib/cjs/components/trees/models-tree/internal/ModelsTreeNode.js.map +1 -1
- package/lib/cjs/components/trees/models-tree/internal/ModelsTreeVisibilityHandler.d.ts +8 -8
- package/lib/cjs/components/trees/models-tree/internal/ModelsTreeVisibilityHandler.js +162 -207
- package/lib/cjs/components/trees/models-tree/internal/ModelsTreeVisibilityHandler.js.map +1 -1
- package/lib/cjs/components/trees/models-tree/internal/VisibilityChangeEventListener.d.ts +2 -0
- package/lib/cjs/components/trees/models-tree/internal/VisibilityChangeEventListener.js +11 -1
- package/lib/cjs/components/trees/models-tree/internal/VisibilityChangeEventListener.js.map +1 -1
- package/lib/esm/components/tree-header/TreeHeader.js +1 -1
- package/lib/esm/components/tree-header/TreeHeader.js.map +1 -1
- package/lib/esm/components/trees/categories-tree/CategoriesTreeButtons.js +3 -3
- package/lib/esm/components/trees/categories-tree/CategoriesTreeButtons.js.map +1 -1
- package/lib/esm/components/trees/categories-tree/CategoriesVisibilityHandler.js +2 -1
- package/lib/esm/components/trees/categories-tree/CategoriesVisibilityHandler.js.map +1 -1
- package/lib/esm/components/trees/{models-tree/internal → common}/Tooltip.d.ts +7 -3
- package/lib/esm/components/trees/{models-tree/internal → common}/Tooltip.js +5 -5
- package/lib/esm/components/trees/common/Tooltip.js.map +1 -0
- package/lib/esm/components/trees/common/UseActiveViewport.js.map +1 -1
- package/lib/esm/components/trees/common/UseHierarchyVisibility.js +4 -4
- package/lib/esm/components/trees/common/UseHierarchyVisibility.js.map +1 -1
- package/lib/esm/components/trees/common/components/Tree.d.ts +6 -1
- package/lib/esm/components/trees/common/components/Tree.js +14 -4
- package/lib/esm/components/trees/common/components/Tree.js.map +1 -1
- package/lib/esm/components/trees/common/components/TreeNodeCheckbox.js +6 -6
- package/lib/esm/components/trees/common/components/TreeNodeCheckbox.js.map +1 -1
- package/lib/esm/components/trees/common/components/TreeRenderer.d.ts +1 -1
- package/lib/esm/components/trees/common/components/TreeRenderer.js +4 -3
- package/lib/esm/components/trees/common/components/TreeRenderer.js.map +1 -1
- package/lib/esm/components/trees/common/components/TreeRenderer.scss +0 -6
- package/lib/esm/components/trees/models-tree/ModelsTree.d.ts +1 -1
- package/lib/esm/components/trees/models-tree/ModelsTree.js +2 -1
- package/lib/esm/components/trees/models-tree/ModelsTree.js.map +1 -1
- package/lib/esm/components/trees/models-tree/ModelsTreeButtons.js +14 -14
- package/lib/esm/components/trees/models-tree/ModelsTreeButtons.js.map +1 -1
- package/lib/esm/components/trees/models-tree/ModelsTreeComponent.d.ts +1 -1
- package/lib/esm/components/trees/models-tree/ModelsTreeComponent.js.map +1 -1
- package/lib/esm/components/trees/models-tree/ModelsTreeDefinition.js.map +1 -1
- package/lib/esm/components/trees/models-tree/UseModelsTree.d.ts +12 -3
- package/lib/esm/components/trees/models-tree/UseModelsTree.js +33 -14
- package/lib/esm/components/trees/models-tree/UseModelsTree.js.map +1 -1
- package/lib/esm/components/trees/models-tree/internal/AlwaysAndNeverDrawnElementInfo.d.ts +5 -0
- package/lib/esm/components/trees/models-tree/internal/AlwaysAndNeverDrawnElementInfo.js +29 -8
- package/lib/esm/components/trees/models-tree/internal/AlwaysAndNeverDrawnElementInfo.js.map +1 -1
- package/lib/esm/components/trees/models-tree/internal/FilteredTree.d.ts +25 -0
- package/lib/esm/components/trees/models-tree/internal/FilteredTree.js +173 -0
- package/lib/esm/components/trees/models-tree/internal/FilteredTree.js.map +1 -0
- package/lib/esm/components/trees/models-tree/internal/ModelsTreeIdsCache.d.ts +2 -1
- package/lib/esm/components/trees/models-tree/internal/ModelsTreeIdsCache.js +55 -23
- package/lib/esm/components/trees/models-tree/internal/ModelsTreeIdsCache.js.map +1 -1
- package/lib/esm/components/trees/models-tree/internal/ModelsTreeNode.d.ts +12 -6
- package/lib/esm/components/trees/models-tree/internal/ModelsTreeNode.js +17 -0
- package/lib/esm/components/trees/models-tree/internal/ModelsTreeNode.js.map +1 -1
- package/lib/esm/components/trees/models-tree/internal/ModelsTreeVisibilityHandler.d.ts +8 -8
- package/lib/esm/components/trees/models-tree/internal/ModelsTreeVisibilityHandler.js +161 -206
- package/lib/esm/components/trees/models-tree/internal/ModelsTreeVisibilityHandler.js.map +1 -1
- package/lib/esm/components/trees/models-tree/internal/VisibilityChangeEventListener.d.ts +2 -0
- package/lib/esm/components/trees/models-tree/internal/VisibilityChangeEventListener.js +11 -1
- package/lib/esm/components/trees/models-tree/internal/VisibilityChangeEventListener.js.map +1 -1
- package/lib/public/locales/en/TreeWidget.json +47 -43
- package/package.json +43 -37
- package/lib/cjs/components/trees/models-tree/internal/Tooltip.js.map +0 -1
- package/lib/esm/components/trees/models-tree/internal/Tooltip.js.map +0 -1
|
@@ -13,15 +13,32 @@ class AlwaysAndNeverDrawnElementInfo {
|
|
|
13
13
|
constructor(_viewport) {
|
|
14
14
|
this._viewport = _viewport;
|
|
15
15
|
this._disposeSubject = new rxjs_1.Subject();
|
|
16
|
+
this._suppress = new rxjs_1.Subject();
|
|
17
|
+
this._forceUpdate = new rxjs_1.Subject();
|
|
16
18
|
this._alwaysDrawn = this.createCacheEntryObservable({
|
|
17
19
|
event: this._viewport.onAlwaysDrawnChanged,
|
|
18
20
|
getSet: () => this._viewport.alwaysDrawn,
|
|
21
|
+
id: "alwaysDrawn",
|
|
19
22
|
});
|
|
20
23
|
this._neverDrawn = this.createCacheEntryObservable({
|
|
21
24
|
event: this._viewport.onNeverDrawnChanged,
|
|
22
25
|
getSet: () => this._viewport.neverDrawn,
|
|
26
|
+
id: "neverDrawn",
|
|
23
27
|
});
|
|
24
|
-
this.
|
|
28
|
+
this._suppressors = this._suppress.pipe((0, rxjs_1.scan)((acc, suppress) => acc + (suppress ? 1 : -1), 0), (0, rxjs_1.startWith)(0), (0, rxjs_1.shareReplay)(1));
|
|
29
|
+
this._subscriptions = [
|
|
30
|
+
this._alwaysDrawn.subscribe(),
|
|
31
|
+
this._neverDrawn.subscribe(),
|
|
32
|
+
this._suppressors.pipe((0, rxjs_1.filter)((suppressors) => suppressors === 0)).subscribe({
|
|
33
|
+
next: () => this._forceUpdate.next(),
|
|
34
|
+
}),
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
suppressChangeEvents() {
|
|
38
|
+
this._suppress.next(true);
|
|
39
|
+
}
|
|
40
|
+
resumeChangeEvents() {
|
|
41
|
+
this._suppress.next(false);
|
|
25
42
|
}
|
|
26
43
|
getElements({ setType, modelId, categoryId }) {
|
|
27
44
|
const cache = setType === "always" ? this._alwaysDrawn : this._neverDrawn;
|
|
@@ -42,7 +59,7 @@ class AlwaysAndNeverDrawnElementInfo {
|
|
|
42
59
|
createCacheEntryObservable(props) {
|
|
43
60
|
const event = props.event;
|
|
44
61
|
const resultSubject = new rxjs_1.BehaviorSubject(undefined);
|
|
45
|
-
const obs = (0, rxjs_1.fromEventPattern)((handler) => event.addListener(handler), (handler) => event.removeListener(handler)).pipe(
|
|
62
|
+
const obs = (0, rxjs_1.merge)((0, rxjs_1.fromEventPattern)((handler) => event.addListener(handler), (handler) => event.removeListener(handler)), this._forceUpdate).pipe(
|
|
46
63
|
// Fire the observable once at the beginning
|
|
47
64
|
(0, rxjs_1.startWith)(undefined),
|
|
48
65
|
// Stop listening to events when dispose() is called
|
|
@@ -50,11 +67,13 @@ class AlwaysAndNeverDrawnElementInfo {
|
|
|
50
67
|
// Reset result subject as soon as a new event is emitted.
|
|
51
68
|
// This will make newly subscribed observers wait for the debounce period to pass
|
|
52
69
|
// instead of consuming the cached value which at this point becomes invalid.
|
|
53
|
-
(0, rxjs_1.tap)(() => resultSubject.next(undefined)),
|
|
70
|
+
(0, rxjs_1.tap)(() => resultSubject.next(undefined)),
|
|
71
|
+
// Check if cache updates are not suppressed.
|
|
72
|
+
(0, rxjs_1.switchMap)(() => this._suppressors.pipe((0, rxjs_1.filter)((suppressors) => suppressors === 0), (0, rxjs_1.take)(1))), (0, rxjs_1.debounceTime)(exports.SET_CHANGE_DEBOUNCE_TIME),
|
|
54
73
|
// Cancel pending request if dispose() is called.
|
|
55
74
|
(0, rxjs_1.takeUntil)(this._disposeSubject),
|
|
56
75
|
// If multiple requests are sent at once, preserve only the result of the newest.
|
|
57
|
-
(0, rxjs_1.switchMap)(() => this.queryAlwaysOrNeverDrawnElementInfo(props.getSet())),
|
|
76
|
+
(0, rxjs_1.switchMap)(() => this.queryAlwaysOrNeverDrawnElementInfo(props.getSet(), props.id)),
|
|
58
77
|
// Share the result by using a subject which always emits the saved result.
|
|
59
78
|
(0, rxjs_1.share)({
|
|
60
79
|
connector: () => resultSubject,
|
|
@@ -69,8 +88,8 @@ class AlwaysAndNeverDrawnElementInfo {
|
|
|
69
88
|
this._subscriptions = [];
|
|
70
89
|
this._disposeSubject.next();
|
|
71
90
|
}
|
|
72
|
-
queryAlwaysOrNeverDrawnElementInfo(set) {
|
|
73
|
-
const elementInfo = set?.size ? this.queryElementInfo([...set]) : rxjs_1.EMPTY;
|
|
91
|
+
queryAlwaysOrNeverDrawnElementInfo(set, requestId) {
|
|
92
|
+
const elementInfo = set?.size ? this.queryElementInfo([...set], requestId) : rxjs_1.EMPTY;
|
|
74
93
|
return elementInfo.pipe((0, rxjs_1.reduce)((state, val) => {
|
|
75
94
|
let entry = state.get(val.modelId);
|
|
76
95
|
if (!entry) {
|
|
@@ -81,7 +100,7 @@ class AlwaysAndNeverDrawnElementInfo {
|
|
|
81
100
|
return state;
|
|
82
101
|
}, new Map()));
|
|
83
102
|
}
|
|
84
|
-
queryElementInfo(elementIds) {
|
|
103
|
+
queryElementInfo(elementIds, requestId) {
|
|
85
104
|
const executor = (0, presentation_core_interop_1.createECSqlQueryExecutor)(this._viewport.iModel);
|
|
86
105
|
const reader = executor.createQueryReader({
|
|
87
106
|
ctes: [
|
|
@@ -113,6 +132,8 @@ class AlwaysAndNeverDrawnElementInfo {
|
|
|
113
132
|
WHERE parentId IS NULL
|
|
114
133
|
`,
|
|
115
134
|
bindings: [{ type: "idset", value: elementIds }],
|
|
135
|
+
}, {
|
|
136
|
+
restartToken: `ModelsTreeVisibilityHandler/${requestId}`,
|
|
116
137
|
});
|
|
117
138
|
return (0, rxjs_1.from)(reader).pipe((0, rxjs_1.map)((row) => ({ elementId: row.elementId, modelId: row.modelId, categoryId: row.categoryId })));
|
|
118
139
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AlwaysAndNeverDrawnElementInfo.js","sourceRoot":"","sources":["../../../../../../src/components/trees/models-tree/internal/AlwaysAndNeverDrawnElementInfo.ts"],"names":[],"mappings":";AAAA;;;gGAGgG;;;AAEhG,+BAA8J;AAC9J,gFAA4E;AAC5E,8CAA+C;AAmBlC,QAAA,wBAAwB,GAAG,EAAE,CAAC;AAE3C,MAAa,8BAA8B;IAMzC,YAA6B,SAAmB;QAAnB,cAAS,GAAT,SAAS,CAAU;QAFxC,oBAAe,GAAG,IAAI,cAAO,EAAQ,CAAC;QAG5C,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,0BAA0B,CAAC;YAClD,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,oBAAoB;YAC1C,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW;SACzC,CAAC,CAAC;QACH,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,0BAA0B,CAAC;YACjD,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,mBAAmB;YACzC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU;SACxC,CAAC,CAAC;QACH,IAAI,CAAC,cAAc,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,EAAE,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,CAAC,CAAC;IACtF,CAAC;IAEM,WAAW,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAA0E;QACzH,MAAM,KAAK,GAAG,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC;QAC1E,MAAM,WAAW,GAAG,UAAU;YAC5B,CAAC,CAAC,CAAC,KAA6B,EAAE,EAAE;gBAChC,OAAO,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,UAAU,CAAC,IAAI,IAAI,GAAG,EAAE,CAAC;YAC3D,CAAC;YACH,CAAC,CAAC,CAAC,KAA6B,EAAE,EAAE;gBAChC,MAAM,UAAU,GAAG,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;gBACvC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAc,CAAC;gBACvC,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE;oBAC5C,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;iBACvC;gBACD,OAAO,QAAQ,CAAC;YAClB,CAAC,CAAC;QAEN,OAAO,KAAK,CAAC,IAAI,CAAC,IAAA,UAAG,EAAC,WAAW,CAAC,CAAC,CAAC;IACtC,CAAC;IAEO,0BAA0B,CAAC,KAAoE;QACrG,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QAC1B,MAAM,aAAa,GAAG,IAAI,sBAAe,CAAyB,SAAS,CAAC,CAAC;QAC7E,MAAM,GAAG,GAAG,IAAA,uBAAgB,EAC1B,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,EACvC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAC3C,CAAC,IAAI;QACJ,4CAA4C;QAC5C,IAAA,gBAAS,EAAC,SAAS,CAAC;QACpB,oDAAoD;QACpD,IAAA,gBAAS,EAAC,IAAI,CAAC,eAAe,CAAC;QAC/B,0DAA0D;QAC1D,iFAAiF;QACjF,6EAA6E;QAC7E,IAAA,UAAG,EAAC,GAAG,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EACxC,IAAA,mBAAY,EAAC,gCAAwB,CAAC;QACtC,iDAAiD;QACjD,IAAA,gBAAS,EAAC,IAAI,CAAC,eAAe,CAAC;QAC/B,iFAAiF;QACjF,IAAA,gBAAS,EAAC,GAAG,EAAE,CAAC,IAAI,CAAC,kCAAkC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QACxE,2EAA2E;QAC3E,IAAA,YAAK,EAAC;YACJ,SAAS,EAAE,GAAG,EAAE,CAAC,aAAa;YAC9B,mBAAmB,EAAE,KAAK;SAC3B,CAAC;QACF,sCAAsC;QACtC,IAAA,YAAK,EAAC,CAAC,CAAC,EAAmB,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CACnC,CAAC;QACF,OAAO,GAAG,CAAC;IACb,CAAC;IAEM,OAAO;QACZ,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC,cAAc,GAAG,EAAE,CAAC;QACzB,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;IAC9B,CAAC;IAEO,kCAAkC,CAAC,GAAwB;QACjE,MAAM,WAAW,GAAG,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,YAAK,CAAC;QACxE,OAAO,WAAW,CAAC,IAAI,CACrB,IAAA,aAAM,EAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YACpB,IAAI,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACnC,IAAI,CAAC,KAAK,EAAE;gBACV,KAAK,GAAG,IAAI,GAAG,EAAE,CAAC;gBAClB,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;aAC/B;YACD,IAAA,iBAAS,EAAC,KAAK,EAAE,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC;YAChD,OAAO,KAAK,CAAC;QACf,CAAC,EAAE,IAAI,GAAG,EAAwC,CAAC,CACpD,CAAC;IACJ,CAAC;IAEO,gBAAgB,CAAC,UAAqB;QAC5C,MAAM,QAAQ,GAAG,IAAA,oDAAwB,EAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACjE,MAAM,MAAM,GAAG,QAAQ,CAAC,iBAAiB,CAAC;YACxC,IAAI,EAAE;gBACJ;;;;;;;;;;;;;;;;;;;;SAoBC;aACF;YACD,KAAK,EAAE;;;;OAIN;YACD,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;SACjD,CAAC,CAAC;QAEH,OAAO,IAAA,WAAI,EAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAA,UAAG,EAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3H,CAAC;CACF;AA5HD,wEA4HC","sourcesContent":["/*---------------------------------------------------------------------------------------------\n * Copyright (c) Bentley Systems, Incorporated. All rights reserved.\n * See LICENSE.md in the project root for license terms and full copyright notice.\n *--------------------------------------------------------------------------------------------*/\n\nimport { BehaviorSubject, debounceTime, EMPTY, first, from, fromEventPattern, map, reduce, share, startWith, Subject, switchMap, takeUntil, tap } from \"rxjs\";\nimport { createECSqlQueryExecutor } from \"@itwin/presentation-core-interop\";\nimport { pushToMap } from \"../../common/Utils\";\n\nimport type { Observable, Subscription } from \"rxjs\";\nimport type { Viewport } from \"@itwin/core-frontend\";\nimport type { BeEvent, Id64Array, Id64Set, Id64String, IDisposable } from \"@itwin/core-bentley\";\n\ninterface ElementInfo {\n elementId: Id64String;\n modelId: Id64String;\n categoryId: Id64String;\n}\n\ntype CacheEntry = Map<Id64String, Map<Id64String, Id64Set>>;\n\nexport interface AlwaysOrNeverDrawnElementsQueryProps {\n modelId: Id64String;\n categoryId?: Id64String;\n}\n\nexport const SET_CHANGE_DEBOUNCE_TIME = 20;\n\nexport class AlwaysAndNeverDrawnElementInfo implements IDisposable {\n private _subscriptions: Subscription[];\n private _alwaysDrawn: Observable<CacheEntry>;\n private _neverDrawn: Observable<CacheEntry>;\n private _disposeSubject = new Subject<void>();\n\n constructor(private readonly _viewport: Viewport) {\n this._alwaysDrawn = this.createCacheEntryObservable({\n event: this._viewport.onAlwaysDrawnChanged,\n getSet: () => this._viewport.alwaysDrawn,\n });\n this._neverDrawn = this.createCacheEntryObservable({\n event: this._viewport.onNeverDrawnChanged,\n getSet: () => this._viewport.neverDrawn,\n });\n this._subscriptions = [this._alwaysDrawn.subscribe(), this._neverDrawn.subscribe()];\n }\n\n public getElements({ setType, modelId, categoryId }: { setType: \"always\" | \"never\" } & AlwaysOrNeverDrawnElementsQueryProps): Observable<Id64Set> {\n const cache = setType === \"always\" ? this._alwaysDrawn : this._neverDrawn;\n const getElements = categoryId\n ? (entry: CacheEntry | undefined) => {\n return entry?.get(modelId)?.get(categoryId) ?? new Set();\n }\n : (entry: CacheEntry | undefined) => {\n const modelEntry = entry?.get(modelId);\n const elements = new Set<Id64String>();\n for (const set of modelEntry?.values() ?? []) {\n set.forEach((id) => elements.add(id));\n }\n return elements;\n };\n\n return cache.pipe(map(getElements));\n }\n\n private createCacheEntryObservable(props: { event: BeEvent<() => void>; getSet(): Id64Set | undefined }) {\n const event = props.event;\n const resultSubject = new BehaviorSubject<CacheEntry | undefined>(undefined);\n const obs = fromEventPattern(\n (handler) => event.addListener(handler),\n (handler) => event.removeListener(handler),\n ).pipe(\n // Fire the observable once at the beginning\n startWith(undefined),\n // Stop listening to events when dispose() is called\n takeUntil(this._disposeSubject),\n // Reset result subject as soon as a new event is emitted.\n // This will make newly subscribed observers wait for the debounce period to pass\n // instead of consuming the cached value which at this point becomes invalid.\n tap(() => resultSubject.next(undefined)),\n debounceTime(SET_CHANGE_DEBOUNCE_TIME),\n // Cancel pending request if dispose() is called.\n takeUntil(this._disposeSubject),\n // If multiple requests are sent at once, preserve only the result of the newest.\n switchMap(() => this.queryAlwaysOrNeverDrawnElementInfo(props.getSet())),\n // Share the result by using a subject which always emits the saved result.\n share({\n connector: () => resultSubject,\n resetOnRefCountZero: false,\n }),\n // Wait until the result is available.\n first((x): x is CacheEntry => !!x),\n );\n return obs;\n }\n\n public dispose(): void {\n this._subscriptions.forEach((x) => x.unsubscribe());\n this._subscriptions = [];\n this._disposeSubject.next();\n }\n\n private queryAlwaysOrNeverDrawnElementInfo(set: Id64Set | undefined): Observable<CacheEntry> {\n const elementInfo = set?.size ? this.queryElementInfo([...set]) : EMPTY;\n return elementInfo.pipe(\n reduce((state, val) => {\n let entry = state.get(val.modelId);\n if (!entry) {\n entry = new Map();\n state.set(val.modelId, entry);\n }\n pushToMap(entry, val.categoryId, val.elementId);\n return state;\n }, new Map<Id64String, Map<Id64String, Id64Set>>()),\n );\n }\n\n private queryElementInfo(elementIds: Id64Array): Observable<ElementInfo> {\n const executor = createECSqlQueryExecutor(this._viewport.iModel);\n const reader = executor.createQueryReader({\n ctes: [\n `\n ElementInfo(elementId, modelId, categoryId, parentId) AS (\n SELECT\n ECInstanceId elementId,\n Model.Id modelId,\n Category.Id categoryId,\n Parent.Id parentId\n FROM bis.GeometricElement3d\n WHERE InVirtualSet(?, ECInstanceId)\n\n UNION ALL\n\n SELECT\n e.elementId,\n e.modelId,\n p.Category.Id categoryId,\n p.Parent.Id parentId\n FROM bis.GeometricElement3d p\n JOIN ElementInfo e ON p.ECInstanceId = e.parentId\n )\n `,\n ],\n ecsql: `\n SELECT elementId, modelId, categoryId\n FROM ElementInfo\n WHERE parentId IS NULL\n `,\n bindings: [{ type: \"idset\", value: elementIds }],\n });\n\n return from(reader).pipe(map((row) => ({ elementId: row.elementId, modelId: row.modelId, categoryId: row.categoryId })));\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"AlwaysAndNeverDrawnElementInfo.js","sourceRoot":"","sources":["../../../../../../src/components/trees/models-tree/internal/AlwaysAndNeverDrawnElementInfo.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\n * Copyright (c) Bentley Systems, Incorporated. All rights reserved.\n * See LICENSE.md in the project root for license terms and full copyright notice.\n *--------------------------------------------------------------------------------------------*/\n\nimport {\n BehaviorSubject,\n debounceTime,\n EMPTY,\n filter,\n first,\n from,\n fromEventPattern,\n map,\n merge,\n reduce,\n scan,\n share,\n shareReplay,\n startWith,\n Subject,\n switchMap,\n take,\n takeUntil,\n tap,\n} from \"rxjs\";\nimport { createECSqlQueryExecutor } from \"@itwin/presentation-core-interop\";\nimport { pushToMap } from \"../../common/Utils\";\n\nimport type { Observable, Subscription } from \"rxjs\";\nimport type { Viewport } from \"@itwin/core-frontend\";\nimport type { BeEvent, Id64Array, Id64Set, Id64String, IDisposable } from \"@itwin/core-bentley\";\n\ninterface ElementInfo {\n elementId: Id64String;\n modelId: Id64String;\n categoryId: Id64String;\n}\n\ntype CacheEntry = Map<Id64String, Map<Id64String, Id64Set>>;\n\nexport interface AlwaysOrNeverDrawnElementsQueryProps {\n modelId: Id64String;\n categoryId?: Id64String;\n}\n\nexport const SET_CHANGE_DEBOUNCE_TIME = 20;\n\nexport class AlwaysAndNeverDrawnElementInfo implements IDisposable {\n private _subscriptions: Subscription[];\n private _alwaysDrawn: Observable<CacheEntry>;\n private _neverDrawn: Observable<CacheEntry>;\n private _disposeSubject = new Subject<void>();\n\n private _suppressors: Observable<number>;\n private _suppress = new Subject<boolean>();\n private _forceUpdate = new Subject<void>();\n\n constructor(private readonly _viewport: Viewport) {\n this._alwaysDrawn = this.createCacheEntryObservable({\n event: this._viewport.onAlwaysDrawnChanged,\n getSet: () => this._viewport.alwaysDrawn,\n id: \"alwaysDrawn\",\n });\n this._neverDrawn = this.createCacheEntryObservable({\n event: this._viewport.onNeverDrawnChanged,\n getSet: () => this._viewport.neverDrawn,\n id: \"neverDrawn\",\n });\n this._suppressors = this._suppress.pipe(\n scan((acc, suppress) => acc + (suppress ? 1 : -1), 0),\n startWith(0),\n shareReplay(1),\n );\n this._subscriptions = [\n this._alwaysDrawn.subscribe(),\n this._neverDrawn.subscribe(),\n this._suppressors.pipe(filter((suppressors) => suppressors === 0)).subscribe({\n next: () => this._forceUpdate.next(),\n }),\n ];\n }\n\n public suppressChangeEvents() {\n this._suppress.next(true);\n }\n\n public resumeChangeEvents() {\n this._suppress.next(false);\n }\n\n public getElements({ setType, modelId, categoryId }: { setType: \"always\" | \"never\" } & AlwaysOrNeverDrawnElementsQueryProps): Observable<Id64Set> {\n const cache = setType === \"always\" ? this._alwaysDrawn : this._neverDrawn;\n const getElements = categoryId\n ? (entry: CacheEntry | undefined) => {\n return entry?.get(modelId)?.get(categoryId) ?? new Set();\n }\n : (entry: CacheEntry | undefined) => {\n const modelEntry = entry?.get(modelId);\n const elements = new Set<Id64String>();\n for (const set of modelEntry?.values() ?? []) {\n set.forEach((id) => elements.add(id));\n }\n return elements;\n };\n\n return cache.pipe(map(getElements));\n }\n\n private createCacheEntryObservable(props: { event: BeEvent<() => void>; getSet(): Id64Set | undefined; id: string }) {\n const event = props.event;\n const resultSubject = new BehaviorSubject<CacheEntry | undefined>(undefined);\n\n const obs = merge(\n fromEventPattern(\n (handler) => event.addListener(handler),\n (handler) => event.removeListener(handler),\n ),\n this._forceUpdate,\n ).pipe(\n // Fire the observable once at the beginning\n startWith(undefined),\n // Stop listening to events when dispose() is called\n takeUntil(this._disposeSubject),\n // Reset result subject as soon as a new event is emitted.\n // This will make newly subscribed observers wait for the debounce period to pass\n // instead of consuming the cached value which at this point becomes invalid.\n tap(() => resultSubject.next(undefined)),\n // Check if cache updates are not suppressed.\n switchMap(() =>\n this._suppressors.pipe(\n filter((suppressors) => suppressors === 0),\n take(1),\n ),\n ),\n debounceTime(SET_CHANGE_DEBOUNCE_TIME),\n // Cancel pending request if dispose() is called.\n takeUntil(this._disposeSubject),\n // If multiple requests are sent at once, preserve only the result of the newest.\n switchMap(() => this.queryAlwaysOrNeverDrawnElementInfo(props.getSet(), props.id)),\n // Share the result by using a subject which always emits the saved result.\n share({\n connector: () => resultSubject,\n resetOnRefCountZero: false,\n }),\n // Wait until the result is available.\n first((x): x is CacheEntry => !!x),\n );\n return obs;\n }\n\n public dispose(): void {\n this._subscriptions.forEach((x) => x.unsubscribe());\n this._subscriptions = [];\n this._disposeSubject.next();\n }\n\n private queryAlwaysOrNeverDrawnElementInfo(set: Id64Set | undefined, requestId: string): Observable<CacheEntry> {\n const elementInfo = set?.size ? this.queryElementInfo([...set], requestId) : EMPTY;\n return elementInfo.pipe(\n reduce((state, val) => {\n let entry = state.get(val.modelId);\n if (!entry) {\n entry = new Map();\n state.set(val.modelId, entry);\n }\n pushToMap(entry, val.categoryId, val.elementId);\n return state;\n }, new Map<Id64String, Map<Id64String, Id64Set>>()),\n );\n }\n\n private queryElementInfo(elementIds: Id64Array, requestId: string): Observable<ElementInfo> {\n const executor = createECSqlQueryExecutor(this._viewport.iModel);\n const reader = executor.createQueryReader(\n {\n ctes: [\n `\n ElementInfo(elementId, modelId, categoryId, parentId) AS (\n SELECT\n ECInstanceId elementId,\n Model.Id modelId,\n Category.Id categoryId,\n Parent.Id parentId\n FROM bis.GeometricElement3d\n WHERE InVirtualSet(?, ECInstanceId)\n\n UNION ALL\n\n SELECT\n e.elementId,\n e.modelId,\n p.Category.Id categoryId,\n p.Parent.Id parentId\n FROM bis.GeometricElement3d p\n JOIN ElementInfo e ON p.ECInstanceId = e.parentId\n )\n `,\n ],\n ecsql: `\n SELECT elementId, modelId, categoryId\n FROM ElementInfo\n WHERE parentId IS NULL\n `,\n bindings: [{ type: \"idset\", value: elementIds }],\n },\n {\n restartToken: `ModelsTreeVisibilityHandler/${requestId}`,\n },\n );\n\n return from(reader).pipe(map((row) => ({ elementId: row.elementId, modelId: row.modelId, categoryId: row.categoryId })));\n }\n}\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { HierarchyFilteringPath } from "@itwin/presentation-hierarchies";
|
|
2
|
+
import type { Id64String } from "@itwin/core-bentley";
|
|
3
|
+
import type { HierarchyNode } from "@itwin/presentation-hierarchies";
|
|
4
|
+
import type { ECClassHierarchyInspector } from "@itwin/presentation-shared";
|
|
5
|
+
export interface FilteredTree {
|
|
6
|
+
getVisibilityChangeTargets(node: HierarchyNode): VisibilityChangeTargets;
|
|
7
|
+
}
|
|
8
|
+
export declare const SUBJECT_CLASS_NAME: "BisCore.Subject";
|
|
9
|
+
export declare const MODEL_CLASS_NAME: "BisCore.GeometricModel3d";
|
|
10
|
+
export declare const CATEGORY_CLASS_NAME: "BisCore.SpatialCategory";
|
|
11
|
+
export declare const ELEMENT_CLASS_NAME: "BisCore.GeometricElement3d";
|
|
12
|
+
type CategoryKey = `${Id64String}-${Id64String}`;
|
|
13
|
+
export declare function parseCategoryKey(key: CategoryKey): {
|
|
14
|
+
modelId: string;
|
|
15
|
+
categoryId: string;
|
|
16
|
+
};
|
|
17
|
+
interface VisibilityChangeTargets {
|
|
18
|
+
subjects?: Set<Id64String>;
|
|
19
|
+
models?: Set<Id64String>;
|
|
20
|
+
categories?: Set<CategoryKey>;
|
|
21
|
+
elements?: Map<CategoryKey, Set<Id64String>>;
|
|
22
|
+
}
|
|
23
|
+
export declare function createFilteredTree(imodelAccess: ECClassHierarchyInspector, filteringPaths: HierarchyFilteringPath[]): Promise<FilteredTree>;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=FilteredTree.d.ts.map
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*---------------------------------------------------------------------------------------------
|
|
3
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
+
*--------------------------------------------------------------------------------------------*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.createFilteredTree = exports.parseCategoryKey = exports.ELEMENT_CLASS_NAME = exports.CATEGORY_CLASS_NAME = exports.MODEL_CLASS_NAME = exports.SUBJECT_CLASS_NAME = void 0;
|
|
8
|
+
const core_bentley_1 = require("@itwin/core-bentley");
|
|
9
|
+
const presentation_hierarchies_1 = require("@itwin/presentation-hierarchies");
|
|
10
|
+
exports.SUBJECT_CLASS_NAME = "BisCore.Subject";
|
|
11
|
+
exports.MODEL_CLASS_NAME = "BisCore.GeometricModel3d";
|
|
12
|
+
exports.CATEGORY_CLASS_NAME = "BisCore.SpatialCategory";
|
|
13
|
+
exports.ELEMENT_CLASS_NAME = "BisCore.GeometricElement3d";
|
|
14
|
+
function createCategoryKey(modelId, categoryId) {
|
|
15
|
+
return `${modelId}-${categoryId}`;
|
|
16
|
+
}
|
|
17
|
+
function parseCategoryKey(key) {
|
|
18
|
+
const [modelId, categoryId] = key.split("-");
|
|
19
|
+
return { modelId, categoryId };
|
|
20
|
+
}
|
|
21
|
+
exports.parseCategoryKey = parseCategoryKey;
|
|
22
|
+
async function createFilteredTree(imodelAccess, filteringPaths) {
|
|
23
|
+
const root = {
|
|
24
|
+
children: new Map(),
|
|
25
|
+
};
|
|
26
|
+
for (const filteringPath of filteringPaths) {
|
|
27
|
+
const normalizedPath = presentation_hierarchies_1.HierarchyFilteringPath.normalize(filteringPath).path;
|
|
28
|
+
let parentNode = root;
|
|
29
|
+
for (let i = 0; i < normalizedPath.length; i++) {
|
|
30
|
+
if ("type" in parentNode && parentNode.isFilterTarget) {
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
const identifier = normalizedPath[i];
|
|
34
|
+
if (!presentation_hierarchies_1.HierarchyNodeIdentifier.isInstanceNodeIdentifier(identifier)) {
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
const currentNode = parentNode.children?.get(identifier.id);
|
|
38
|
+
if (currentNode !== undefined) {
|
|
39
|
+
parentNode = currentNode;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const type = await getType(imodelAccess, identifier.className);
|
|
43
|
+
const newNode = createFilteredTreeNode({
|
|
44
|
+
type,
|
|
45
|
+
id: identifier.id,
|
|
46
|
+
isFilterTarget: i === normalizedPath.length - 1,
|
|
47
|
+
parent: parentNode,
|
|
48
|
+
});
|
|
49
|
+
(parentNode.children ??= new Map()).set(identifier.id, newNode);
|
|
50
|
+
parentNode = newNode;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
getVisibilityChangeTargets: (node) => getVisibilityChangeTargets(root, node),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
exports.createFilteredTree = createFilteredTree;
|
|
58
|
+
function getVisibilityChangeTargets(root, node) {
|
|
59
|
+
let lookupParents = [root];
|
|
60
|
+
const changeTargets = {};
|
|
61
|
+
const nodeKey = node.key;
|
|
62
|
+
if (!presentation_hierarchies_1.HierarchyNodeKey.isInstances(nodeKey)) {
|
|
63
|
+
return changeTargets;
|
|
64
|
+
}
|
|
65
|
+
// find the filtered parent nodes of the `node`
|
|
66
|
+
for (const parentKey of node.parentKeys) {
|
|
67
|
+
if (!presentation_hierarchies_1.HierarchyNodeKey.isInstances(parentKey)) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
// tree node might be merged from multiple instances. As filtered tree stores only one instance per node, we need to find all matching nodes
|
|
71
|
+
// and use them when checking for matching node in one level deeper.
|
|
72
|
+
const parentNodes = findMatchingFilteredNodes(lookupParents, parentKey.instanceKeys);
|
|
73
|
+
if (parentNodes.length === 0) {
|
|
74
|
+
return changeTargets;
|
|
75
|
+
}
|
|
76
|
+
lookupParents = parentNodes;
|
|
77
|
+
}
|
|
78
|
+
// find filtered nodes that match the `node`
|
|
79
|
+
const filteredNodes = findMatchingFilteredNodes(lookupParents, nodeKey.instanceKeys);
|
|
80
|
+
if (filteredNodes.length === 0) {
|
|
81
|
+
return changeTargets;
|
|
82
|
+
}
|
|
83
|
+
filteredNodes.forEach((filteredNode) => collectVisibilityChangeTargets(changeTargets, filteredNode));
|
|
84
|
+
return changeTargets;
|
|
85
|
+
}
|
|
86
|
+
function findMatchingFilteredNodes(lookupParents, keys) {
|
|
87
|
+
return lookupParents
|
|
88
|
+
.flatMap((lookup) => keys.map((key) => lookup.children?.get(key.id)))
|
|
89
|
+
.filter((lookupNode) => lookupNode !== undefined);
|
|
90
|
+
}
|
|
91
|
+
function collectVisibilityChangeTargets(changeTargets, node) {
|
|
92
|
+
if (node.isFilterTarget) {
|
|
93
|
+
addTarget(changeTargets, node);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (node.type === "element") {
|
|
97
|
+
// need to add parent ids as filter target will be an element
|
|
98
|
+
addTarget(changeTargets, node);
|
|
99
|
+
}
|
|
100
|
+
if (!node.children) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
for (const child of node.children.values()) {
|
|
104
|
+
collectVisibilityChangeTargets(changeTargets, child);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function addTarget(filterTargets, node) {
|
|
108
|
+
switch (node.type) {
|
|
109
|
+
case "subject":
|
|
110
|
+
(filterTargets.subjects ??= new Set()).add(node.id);
|
|
111
|
+
return;
|
|
112
|
+
case "model":
|
|
113
|
+
(filterTargets.models ??= new Set()).add(node.id);
|
|
114
|
+
return;
|
|
115
|
+
case "category":
|
|
116
|
+
(filterTargets.categories ??= new Set()).add(createCategoryKey(node.modelId, node.id));
|
|
117
|
+
return;
|
|
118
|
+
case "element":
|
|
119
|
+
const categoryKey = createCategoryKey(node.modelId, node.categoryId);
|
|
120
|
+
const elements = (filterTargets.elements ??= new Map()).get(categoryKey);
|
|
121
|
+
if (elements) {
|
|
122
|
+
elements.add(node.id);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
filterTargets.elements.set(categoryKey, new Set([node.id]));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function createFilteredTreeNode({ type, id, isFilterTarget, parent, }) {
|
|
130
|
+
if (type === "subject" || type === "model") {
|
|
131
|
+
return {
|
|
132
|
+
id,
|
|
133
|
+
isFilterTarget,
|
|
134
|
+
type,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
if (type === "category") {
|
|
138
|
+
(0, core_bentley_1.assert)("type" in parent && parent.type === "model");
|
|
139
|
+
return {
|
|
140
|
+
id,
|
|
141
|
+
isFilterTarget,
|
|
142
|
+
type,
|
|
143
|
+
modelId: parent.id,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
if ("type" in parent && parent.type === "category") {
|
|
147
|
+
return {
|
|
148
|
+
id,
|
|
149
|
+
isFilterTarget,
|
|
150
|
+
type,
|
|
151
|
+
modelId: parent.modelId,
|
|
152
|
+
categoryId: parent.id,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
if ("type" in parent && parent.type === "element") {
|
|
156
|
+
return {
|
|
157
|
+
id,
|
|
158
|
+
isFilterTarget,
|
|
159
|
+
type,
|
|
160
|
+
modelId: parent.modelId,
|
|
161
|
+
categoryId: parent.categoryId,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
throw new Error("Invalid parent node type");
|
|
165
|
+
}
|
|
166
|
+
async function getType(hierarchyChecker, className) {
|
|
167
|
+
if (await hierarchyChecker.classDerivesFrom(className, exports.SUBJECT_CLASS_NAME)) {
|
|
168
|
+
return "subject";
|
|
169
|
+
}
|
|
170
|
+
if (await hierarchyChecker.classDerivesFrom(className, exports.MODEL_CLASS_NAME)) {
|
|
171
|
+
return "model";
|
|
172
|
+
}
|
|
173
|
+
if (await hierarchyChecker.classDerivesFrom(className, exports.CATEGORY_CLASS_NAME)) {
|
|
174
|
+
return "category";
|
|
175
|
+
}
|
|
176
|
+
return "element";
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=FilteredTree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FilteredTree.js","sourceRoot":"","sources":["../../../../../../src/components/trees/models-tree/internal/FilteredTree.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\n * Copyright (c) Bentley Systems, Incorporated. All rights reserved.\n * See LICENSE.md in the project root for license terms and full copyright notice.\n *--------------------------------------------------------------------------------------------*/\n\nimport { assert } from \"@itwin/core-bentley\";\nimport { HierarchyFilteringPath, HierarchyNodeIdentifier, HierarchyNodeKey } from \"@itwin/presentation-hierarchies\";\n\nimport type { Id64String } from \"@itwin/core-bentley\";\nimport type { HierarchyNode } from \"@itwin/presentation-hierarchies\";\nimport type { ECClassHierarchyInspector, InstanceKey } from \"@itwin/presentation-shared\";\n\ninterface FilteredTreeRootNode {\n children: Map<Id64String, FilteredTreeNode>;\n}\n\ninterface BaseFilteredTreeNode {\n id: string;\n children?: Map<Id64String, FilteredTreeNode>;\n isFilterTarget: boolean;\n}\n\ninterface GenericFilteredTreeNode extends BaseFilteredTreeNode {\n type: \"subject\" | \"model\";\n}\n\ninterface CategoryFilteredTreeNode extends BaseFilteredTreeNode {\n type: \"category\";\n modelId: Id64String;\n}\n\ninterface ElementFilteredTreeNode extends BaseFilteredTreeNode {\n type: \"element\";\n modelId: Id64String;\n categoryId: Id64String;\n}\n\ntype FilteredTreeNode = GenericFilteredTreeNode | CategoryFilteredTreeNode | ElementFilteredTreeNode;\n\nexport interface FilteredTree {\n getVisibilityChangeTargets(node: HierarchyNode): VisibilityChangeTargets;\n}\n\nexport const SUBJECT_CLASS_NAME = \"BisCore.Subject\" as const;\nexport const MODEL_CLASS_NAME = \"BisCore.GeometricModel3d\" as const;\nexport const CATEGORY_CLASS_NAME = \"BisCore.SpatialCategory\" as const;\nexport const ELEMENT_CLASS_NAME = \"BisCore.GeometricElement3d\" as const;\n\ntype CategoryKey = `${Id64String}-${Id64String}`;\n\nfunction createCategoryKey(modelId: string, categoryId: string): CategoryKey {\n return `${modelId}-${categoryId}`;\n}\n\nexport function parseCategoryKey(key: CategoryKey) {\n const [modelId, categoryId] = key.split(\"-\");\n return { modelId, categoryId };\n}\n\ninterface VisibilityChangeTargets {\n subjects?: Set<Id64String>;\n models?: Set<Id64String>;\n categories?: Set<CategoryKey>;\n elements?: Map<CategoryKey, Set<Id64String>>;\n}\n\nexport async function createFilteredTree(imodelAccess: ECClassHierarchyInspector, filteringPaths: HierarchyFilteringPath[]): Promise<FilteredTree> {\n const root: FilteredTreeRootNode = {\n children: new Map(),\n };\n\n for (const filteringPath of filteringPaths) {\n const normalizedPath = HierarchyFilteringPath.normalize(filteringPath).path;\n\n let parentNode: FilteredTreeRootNode | FilteredTreeNode = root;\n for (let i = 0; i < normalizedPath.length; i++) {\n if (\"type\" in parentNode && parentNode.isFilterTarget) {\n break;\n }\n\n const identifier = normalizedPath[i];\n if (!HierarchyNodeIdentifier.isInstanceNodeIdentifier(identifier)) {\n break;\n }\n\n const currentNode: FilteredTreeNode | undefined = parentNode.children?.get(identifier.id);\n if (currentNode !== undefined) {\n parentNode = currentNode;\n continue;\n }\n\n const type = await getType(imodelAccess, identifier.className);\n const newNode: FilteredTreeNode = createFilteredTreeNode({\n type,\n id: identifier.id,\n isFilterTarget: i === normalizedPath.length - 1,\n parent: parentNode as FilteredTreeNode,\n });\n (parentNode.children ??= new Map()).set(identifier.id, newNode);\n parentNode = newNode;\n }\n }\n\n return {\n getVisibilityChangeTargets: (node: HierarchyNode) => getVisibilityChangeTargets(root, node),\n };\n}\n\nfunction getVisibilityChangeTargets(root: FilteredTreeRootNode, node: HierarchyNode) {\n let lookupParents: Array<{ children?: Map<Id64String, FilteredTreeNode> }> = [root];\n const changeTargets: VisibilityChangeTargets = {};\n\n const nodeKey = node.key;\n if (!HierarchyNodeKey.isInstances(nodeKey)) {\n return changeTargets;\n }\n\n // find the filtered parent nodes of the `node`\n for (const parentKey of node.parentKeys) {\n if (!HierarchyNodeKey.isInstances(parentKey)) {\n continue;\n }\n\n // tree node might be merged from multiple instances. As filtered tree stores only one instance per node, we need to find all matching nodes\n // and use them when checking for matching node in one level deeper.\n const parentNodes = findMatchingFilteredNodes(lookupParents, parentKey.instanceKeys);\n if (parentNodes.length === 0) {\n return changeTargets;\n }\n lookupParents = parentNodes;\n }\n\n // find filtered nodes that match the `node`\n const filteredNodes = findMatchingFilteredNodes(lookupParents, nodeKey.instanceKeys);\n if (filteredNodes.length === 0) {\n return changeTargets;\n }\n\n filteredNodes.forEach((filteredNode) => collectVisibilityChangeTargets(changeTargets, filteredNode));\n return changeTargets;\n}\n\nfunction findMatchingFilteredNodes(lookupParents: Array<{ children?: Map<Id64String, FilteredTreeNode> }>, keys: InstanceKey[]) {\n return lookupParents\n .flatMap((lookup) => keys.map((key) => lookup.children?.get(key.id)))\n .filter((lookupNode): lookupNode is FilteredTreeNode => lookupNode !== undefined);\n}\n\nfunction collectVisibilityChangeTargets(changeTargets: VisibilityChangeTargets, node: FilteredTreeNode) {\n if (node.isFilterTarget) {\n addTarget(changeTargets, node);\n return;\n }\n\n if (node.type === \"element\") {\n // need to add parent ids as filter target will be an element\n addTarget(changeTargets, node);\n }\n\n if (!node.children) {\n return;\n }\n\n for (const child of node.children.values()) {\n collectVisibilityChangeTargets(changeTargets, child);\n }\n}\n\nfunction addTarget(filterTargets: VisibilityChangeTargets, node: FilteredTreeNode) {\n switch (node.type) {\n case \"subject\":\n (filterTargets.subjects ??= new Set()).add(node.id);\n return;\n case \"model\":\n (filterTargets.models ??= new Set()).add(node.id);\n return;\n case \"category\":\n (filterTargets.categories ??= new Set()).add(createCategoryKey(node.modelId, node.id));\n return;\n case \"element\":\n const categoryKey = createCategoryKey(node.modelId, node.categoryId);\n const elements = (filterTargets.elements ??= new Map()).get(categoryKey);\n if (elements) {\n elements.add(node.id);\n return;\n }\n filterTargets.elements.set(categoryKey, new Set([node.id]));\n return;\n }\n}\n\nfunction createFilteredTreeNode({\n type,\n id,\n isFilterTarget,\n parent,\n}: {\n type: FilteredTreeNode[\"type\"];\n id: string;\n isFilterTarget: boolean;\n parent: FilteredTreeNode | FilteredTreeRootNode;\n}): FilteredTreeNode {\n if (type === \"subject\" || type === \"model\") {\n return {\n id,\n isFilterTarget,\n type,\n };\n }\n\n if (type === \"category\") {\n assert(\"type\" in parent && parent.type === \"model\");\n return {\n id,\n isFilterTarget,\n type,\n modelId: parent.id,\n };\n }\n\n if (\"type\" in parent && parent.type === \"category\") {\n return {\n id,\n isFilterTarget,\n type,\n modelId: parent.modelId,\n categoryId: parent.id,\n };\n }\n\n if (\"type\" in parent && parent.type === \"element\") {\n return {\n id,\n isFilterTarget,\n type,\n modelId: parent.modelId,\n categoryId: parent.categoryId,\n };\n }\n\n throw new Error(\"Invalid parent node type\");\n}\n\nasync function getType(hierarchyChecker: ECClassHierarchyInspector, className: string) {\n if (await hierarchyChecker.classDerivesFrom(className, SUBJECT_CLASS_NAME)) {\n return \"subject\";\n }\n if (await hierarchyChecker.classDerivesFrom(className, MODEL_CLASS_NAME)) {\n return \"model\";\n }\n if (await hierarchyChecker.classDerivesFrom(className, CATEGORY_CLASS_NAME)) {\n return \"category\";\n }\n return \"element\";\n}\n"]}
|
|
@@ -11,6 +11,7 @@ export declare class ModelsTreeIdsCache {
|
|
|
11
11
|
private _parentSubjectIds;
|
|
12
12
|
private _modelInfos;
|
|
13
13
|
constructor(_queryExecutor: LimitingECSqlQueryExecutor, _hierarchyConfig: ModelsTreeHierarchyConfiguration);
|
|
14
|
+
[Symbol.dispose](): void;
|
|
14
15
|
private querySubjects;
|
|
15
16
|
private queryModels;
|
|
16
17
|
private getSubjectInfos;
|
|
@@ -36,7 +37,7 @@ export declare class ModelsTreeIdsCache {
|
|
|
36
37
|
getModelCategories(modelId: Id64String): Promise<Id64Array>;
|
|
37
38
|
getModelElementCount(modelId: Id64String): Promise<number>;
|
|
38
39
|
getModelSubjects(modelId: Id64String): Promise<Id64Array>;
|
|
39
|
-
private
|
|
40
|
+
private queryCategoryElementCounts;
|
|
40
41
|
getCategoryElementsCount(modelId: Id64String, categoryId: Id64String): Promise<number>;
|
|
41
42
|
getCategoryModels(categoryId: Id64String): Promise<Id64Array>;
|
|
42
43
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*--------------------------------------------------------------------------------------------*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.ModelsTreeIdsCache = void 0;
|
|
8
|
+
const rxjs_1 = require("rxjs");
|
|
8
9
|
const core_bentley_1 = require("@itwin/core-bentley");
|
|
9
10
|
const Utils_1 = require("../../common/Utils");
|
|
10
11
|
/** @internal */
|
|
@@ -12,7 +13,10 @@ class ModelsTreeIdsCache {
|
|
|
12
13
|
constructor(_queryExecutor, _hierarchyConfig) {
|
|
13
14
|
this._queryExecutor = _queryExecutor;
|
|
14
15
|
this._hierarchyConfig = _hierarchyConfig;
|
|
15
|
-
this._categoryElementCounts = new
|
|
16
|
+
this._categoryElementCounts = new ModelCategoryElementsCountCache(async (input) => this.queryCategoryElementCounts(input));
|
|
17
|
+
}
|
|
18
|
+
[Symbol.dispose]() {
|
|
19
|
+
this._categoryElementCounts[Symbol.dispose]();
|
|
16
20
|
}
|
|
17
21
|
async *querySubjects() {
|
|
18
22
|
const subjectsQuery = `
|
|
@@ -261,44 +265,42 @@ class ModelsTreeIdsCache {
|
|
|
261
265
|
});
|
|
262
266
|
return result;
|
|
263
267
|
}
|
|
264
|
-
async
|
|
268
|
+
async queryCategoryElementCounts(input) {
|
|
265
269
|
const reader = this._queryExecutor.createQueryReader({
|
|
266
270
|
ctes: [
|
|
267
271
|
/* sql */ `
|
|
268
|
-
CategoryElements(id) AS (
|
|
269
|
-
SELECT ECInstanceId
|
|
272
|
+
CategoryElements(id, modelId, categoryId) AS (
|
|
273
|
+
SELECT ECInstanceId, Model.Id, Category.Id
|
|
270
274
|
FROM ${this._hierarchyConfig.elementClassSpecification}
|
|
271
275
|
WHERE
|
|
272
|
-
|
|
273
|
-
AND
|
|
274
|
-
|
|
276
|
+
Parent.Id IS NULL
|
|
277
|
+
AND (
|
|
278
|
+
${input.map(({ modelId, categoryId }) => `Model.Id = ${modelId} AND Category.Id = ${categoryId}`).join(" OR ")}
|
|
279
|
+
)
|
|
275
280
|
|
|
276
281
|
UNION ALL
|
|
277
282
|
|
|
278
|
-
SELECT c.ECInstanceId
|
|
283
|
+
SELECT c.ECInstanceId, p.modelId, p.categoryId
|
|
279
284
|
FROM ${this._hierarchyConfig.elementClassSpecification} c
|
|
280
285
|
JOIN CategoryElements p ON c.Parent.Id = p.id
|
|
281
286
|
)
|
|
282
287
|
`,
|
|
283
288
|
],
|
|
284
|
-
ecsql: `
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
}, { rowFormat: "
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const cacheKey = `${modelId}${categoryId}`;
|
|
294
|
-
let result = this._categoryElementCounts.get(cacheKey);
|
|
295
|
-
if (result !== undefined) {
|
|
296
|
-
return result;
|
|
289
|
+
ecsql: `
|
|
290
|
+
SELECT modelId, categoryId, COUNT(id) elementsCount
|
|
291
|
+
FROM CategoryElements
|
|
292
|
+
GROUP BY modelId, categoryId
|
|
293
|
+
`,
|
|
294
|
+
}, { rowFormat: "ECSqlPropertyNames", limit: "unbounded" });
|
|
295
|
+
const result = new Array();
|
|
296
|
+
for await (const row of reader) {
|
|
297
|
+
result.push({ modelId: row.modelId, categoryId: row.categoryId, elementsCount: row.elementsCount });
|
|
297
298
|
}
|
|
298
|
-
result = await this.queryCategoryElementsCount(modelId, categoryId);
|
|
299
|
-
this._categoryElementCounts.set(cacheKey, result);
|
|
300
299
|
return result;
|
|
301
300
|
}
|
|
301
|
+
async getCategoryElementsCount(modelId, categoryId) {
|
|
302
|
+
return this._categoryElementCounts.getCategoryElementsCount(modelId, categoryId);
|
|
303
|
+
}
|
|
302
304
|
async getCategoryModels(categoryId) {
|
|
303
305
|
const result = new Set();
|
|
304
306
|
const modelInfos = await this.getModelInfos();
|
|
@@ -322,4 +324,34 @@ function forEachChildSubject(subjectInfos, parentSubject, cb) {
|
|
|
322
324
|
forEachChildSubject(subjectInfos, childSubjectInfo, cb);
|
|
323
325
|
});
|
|
324
326
|
}
|
|
327
|
+
class ModelCategoryElementsCountCache {
|
|
328
|
+
constructor(_loader) {
|
|
329
|
+
this._loader = _loader;
|
|
330
|
+
this._cache = new Map();
|
|
331
|
+
this._requestsStream = new rxjs_1.Subject();
|
|
332
|
+
this._subscription = this._requestsStream
|
|
333
|
+
.pipe((0, rxjs_1.bufferTime)(20), (0, rxjs_1.filter)((requests) => requests.length > 0), (0, rxjs_1.mergeMap)(async (requests) => this._loader(requests)), (0, rxjs_1.mergeAll)())
|
|
334
|
+
.subscribe({
|
|
335
|
+
next: ({ modelId, categoryId, elementsCount }) => {
|
|
336
|
+
const subject = this._cache.get(`${modelId}${categoryId}`);
|
|
337
|
+
(0, core_bentley_1.assert)(!!subject);
|
|
338
|
+
subject.next(elementsCount);
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
[Symbol.dispose]() {
|
|
343
|
+
this._subscription.unsubscribe();
|
|
344
|
+
}
|
|
345
|
+
async getCategoryElementsCount(modelId, categoryId) {
|
|
346
|
+
const cacheKey = `${modelId}${categoryId}`;
|
|
347
|
+
let result = this._cache.get(cacheKey);
|
|
348
|
+
if (result !== undefined) {
|
|
349
|
+
return (0, rxjs_1.firstValueFrom)(result);
|
|
350
|
+
}
|
|
351
|
+
result = new rxjs_1.ReplaySubject(1);
|
|
352
|
+
this._cache.set(cacheKey, result);
|
|
353
|
+
this._requestsStream.next({ modelId, categoryId });
|
|
354
|
+
return (0, rxjs_1.firstValueFrom)(result);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
325
357
|
//# sourceMappingURL=ModelsTreeIdsCache.js.map
|