@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.
Files changed (107) hide show
  1. package/CHANGELOG.md +31 -19
  2. package/README.md +8 -8
  3. package/lib/cjs/components/tree-header/TreeHeader.js +1 -1
  4. package/lib/cjs/components/tree-header/TreeHeader.js.map +1 -1
  5. package/lib/cjs/components/trees/categories-tree/CategoriesTreeButtons.js +3 -3
  6. package/lib/cjs/components/trees/categories-tree/CategoriesTreeButtons.js.map +1 -1
  7. package/lib/cjs/components/trees/categories-tree/CategoriesVisibilityHandler.js +2 -1
  8. package/lib/cjs/components/trees/categories-tree/CategoriesVisibilityHandler.js.map +1 -1
  9. package/lib/cjs/components/trees/{models-tree/internal → common}/Tooltip.d.ts +7 -3
  10. package/lib/cjs/components/trees/{models-tree/internal → common}/Tooltip.js +5 -5
  11. package/lib/cjs/components/trees/common/Tooltip.js.map +1 -0
  12. package/lib/cjs/components/trees/common/UseActiveViewport.js.map +1 -1
  13. package/lib/cjs/components/trees/common/UseHierarchyVisibility.js +3 -3
  14. package/lib/cjs/components/trees/common/UseHierarchyVisibility.js.map +1 -1
  15. package/lib/cjs/components/trees/common/components/Tree.d.ts +6 -1
  16. package/lib/cjs/components/trees/common/components/Tree.js +14 -4
  17. package/lib/cjs/components/trees/common/components/Tree.js.map +1 -1
  18. package/lib/cjs/components/trees/common/components/TreeNodeCheckbox.js +5 -5
  19. package/lib/cjs/components/trees/common/components/TreeNodeCheckbox.js.map +1 -1
  20. package/lib/cjs/components/trees/common/components/TreeRenderer.d.ts +1 -1
  21. package/lib/cjs/components/trees/common/components/TreeRenderer.js +4 -3
  22. package/lib/cjs/components/trees/common/components/TreeRenderer.js.map +1 -1
  23. package/lib/cjs/components/trees/common/components/TreeRenderer.scss +0 -6
  24. package/lib/cjs/components/trees/models-tree/ModelsTree.d.ts +1 -1
  25. package/lib/cjs/components/trees/models-tree/ModelsTree.js +2 -1
  26. package/lib/cjs/components/trees/models-tree/ModelsTree.js.map +1 -1
  27. package/lib/cjs/components/trees/models-tree/ModelsTreeButtons.js +13 -13
  28. package/lib/cjs/components/trees/models-tree/ModelsTreeButtons.js.map +1 -1
  29. package/lib/cjs/components/trees/models-tree/ModelsTreeComponent.d.ts +1 -1
  30. package/lib/cjs/components/trees/models-tree/ModelsTreeComponent.js.map +1 -1
  31. package/lib/cjs/components/trees/models-tree/ModelsTreeDefinition.js +1 -1
  32. package/lib/cjs/components/trees/models-tree/ModelsTreeDefinition.js.map +1 -1
  33. package/lib/cjs/components/trees/models-tree/UseModelsTree.d.ts +12 -3
  34. package/lib/cjs/components/trees/models-tree/UseModelsTree.js +32 -13
  35. package/lib/cjs/components/trees/models-tree/UseModelsTree.js.map +1 -1
  36. package/lib/cjs/components/trees/models-tree/internal/AlwaysAndNeverDrawnElementInfo.d.ts +5 -0
  37. package/lib/cjs/components/trees/models-tree/internal/AlwaysAndNeverDrawnElementInfo.js +28 -7
  38. package/lib/cjs/components/trees/models-tree/internal/AlwaysAndNeverDrawnElementInfo.js.map +1 -1
  39. package/lib/cjs/components/trees/models-tree/internal/FilteredTree.d.ts +25 -0
  40. package/lib/cjs/components/trees/models-tree/internal/FilteredTree.js +178 -0
  41. package/lib/cjs/components/trees/models-tree/internal/FilteredTree.js.map +1 -0
  42. package/lib/cjs/components/trees/models-tree/internal/ModelsTreeIdsCache.d.ts +2 -1
  43. package/lib/cjs/components/trees/models-tree/internal/ModelsTreeIdsCache.js +55 -23
  44. package/lib/cjs/components/trees/models-tree/internal/ModelsTreeIdsCache.js.map +1 -1
  45. package/lib/cjs/components/trees/models-tree/internal/ModelsTreeNode.d.ts +12 -6
  46. package/lib/cjs/components/trees/models-tree/internal/ModelsTreeNode.js +18 -1
  47. package/lib/cjs/components/trees/models-tree/internal/ModelsTreeNode.js.map +1 -1
  48. package/lib/cjs/components/trees/models-tree/internal/ModelsTreeVisibilityHandler.d.ts +8 -8
  49. package/lib/cjs/components/trees/models-tree/internal/ModelsTreeVisibilityHandler.js +162 -207
  50. package/lib/cjs/components/trees/models-tree/internal/ModelsTreeVisibilityHandler.js.map +1 -1
  51. package/lib/cjs/components/trees/models-tree/internal/VisibilityChangeEventListener.d.ts +2 -0
  52. package/lib/cjs/components/trees/models-tree/internal/VisibilityChangeEventListener.js +11 -1
  53. package/lib/cjs/components/trees/models-tree/internal/VisibilityChangeEventListener.js.map +1 -1
  54. package/lib/esm/components/tree-header/TreeHeader.js +1 -1
  55. package/lib/esm/components/tree-header/TreeHeader.js.map +1 -1
  56. package/lib/esm/components/trees/categories-tree/CategoriesTreeButtons.js +3 -3
  57. package/lib/esm/components/trees/categories-tree/CategoriesTreeButtons.js.map +1 -1
  58. package/lib/esm/components/trees/categories-tree/CategoriesVisibilityHandler.js +2 -1
  59. package/lib/esm/components/trees/categories-tree/CategoriesVisibilityHandler.js.map +1 -1
  60. package/lib/esm/components/trees/{models-tree/internal → common}/Tooltip.d.ts +7 -3
  61. package/lib/esm/components/trees/{models-tree/internal → common}/Tooltip.js +5 -5
  62. package/lib/esm/components/trees/common/Tooltip.js.map +1 -0
  63. package/lib/esm/components/trees/common/UseActiveViewport.js.map +1 -1
  64. package/lib/esm/components/trees/common/UseHierarchyVisibility.js +4 -4
  65. package/lib/esm/components/trees/common/UseHierarchyVisibility.js.map +1 -1
  66. package/lib/esm/components/trees/common/components/Tree.d.ts +6 -1
  67. package/lib/esm/components/trees/common/components/Tree.js +14 -4
  68. package/lib/esm/components/trees/common/components/Tree.js.map +1 -1
  69. package/lib/esm/components/trees/common/components/TreeNodeCheckbox.js +6 -6
  70. package/lib/esm/components/trees/common/components/TreeNodeCheckbox.js.map +1 -1
  71. package/lib/esm/components/trees/common/components/TreeRenderer.d.ts +1 -1
  72. package/lib/esm/components/trees/common/components/TreeRenderer.js +4 -3
  73. package/lib/esm/components/trees/common/components/TreeRenderer.js.map +1 -1
  74. package/lib/esm/components/trees/common/components/TreeRenderer.scss +0 -6
  75. package/lib/esm/components/trees/models-tree/ModelsTree.d.ts +1 -1
  76. package/lib/esm/components/trees/models-tree/ModelsTree.js +2 -1
  77. package/lib/esm/components/trees/models-tree/ModelsTree.js.map +1 -1
  78. package/lib/esm/components/trees/models-tree/ModelsTreeButtons.js +14 -14
  79. package/lib/esm/components/trees/models-tree/ModelsTreeButtons.js.map +1 -1
  80. package/lib/esm/components/trees/models-tree/ModelsTreeComponent.d.ts +1 -1
  81. package/lib/esm/components/trees/models-tree/ModelsTreeComponent.js.map +1 -1
  82. package/lib/esm/components/trees/models-tree/ModelsTreeDefinition.js.map +1 -1
  83. package/lib/esm/components/trees/models-tree/UseModelsTree.d.ts +12 -3
  84. package/lib/esm/components/trees/models-tree/UseModelsTree.js +33 -14
  85. package/lib/esm/components/trees/models-tree/UseModelsTree.js.map +1 -1
  86. package/lib/esm/components/trees/models-tree/internal/AlwaysAndNeverDrawnElementInfo.d.ts +5 -0
  87. package/lib/esm/components/trees/models-tree/internal/AlwaysAndNeverDrawnElementInfo.js +29 -8
  88. package/lib/esm/components/trees/models-tree/internal/AlwaysAndNeverDrawnElementInfo.js.map +1 -1
  89. package/lib/esm/components/trees/models-tree/internal/FilteredTree.d.ts +25 -0
  90. package/lib/esm/components/trees/models-tree/internal/FilteredTree.js +173 -0
  91. package/lib/esm/components/trees/models-tree/internal/FilteredTree.js.map +1 -0
  92. package/lib/esm/components/trees/models-tree/internal/ModelsTreeIdsCache.d.ts +2 -1
  93. package/lib/esm/components/trees/models-tree/internal/ModelsTreeIdsCache.js +55 -23
  94. package/lib/esm/components/trees/models-tree/internal/ModelsTreeIdsCache.js.map +1 -1
  95. package/lib/esm/components/trees/models-tree/internal/ModelsTreeNode.d.ts +12 -6
  96. package/lib/esm/components/trees/models-tree/internal/ModelsTreeNode.js +17 -0
  97. package/lib/esm/components/trees/models-tree/internal/ModelsTreeNode.js.map +1 -1
  98. package/lib/esm/components/trees/models-tree/internal/ModelsTreeVisibilityHandler.d.ts +8 -8
  99. package/lib/esm/components/trees/models-tree/internal/ModelsTreeVisibilityHandler.js +161 -206
  100. package/lib/esm/components/trees/models-tree/internal/ModelsTreeVisibilityHandler.js.map +1 -1
  101. package/lib/esm/components/trees/models-tree/internal/VisibilityChangeEventListener.d.ts +2 -0
  102. package/lib/esm/components/trees/models-tree/internal/VisibilityChangeEventListener.js +11 -1
  103. package/lib/esm/components/trees/models-tree/internal/VisibilityChangeEventListener.js.map +1 -1
  104. package/lib/public/locales/en/TreeWidget.json +47 -43
  105. package/package.json +43 -37
  106. package/lib/cjs/components/trees/models-tree/internal/Tooltip.js.map +0 -1
  107. 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._subscriptions = [this._alwaysDrawn.subscribe(), this._neverDrawn.subscribe()];
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)), (0, rxjs_1.debounceTime)(exports.SET_CHANGE_DEBOUNCE_TIME),
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":"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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 { 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 queryCategoryElementsCount;
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 Map();
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 queryCategoryElementsCount(modelId, categoryId) {
268
+ async queryCategoryElementCounts(input) {
265
269
  const reader = this._queryExecutor.createQueryReader({
266
270
  ctes: [
267
271
  /* sql */ `
268
- CategoryElements(id) AS (
269
- SELECT ECInstanceId id
272
+ CategoryElements(id, modelId, categoryId) AS (
273
+ SELECT ECInstanceId, Model.Id, Category.Id
270
274
  FROM ${this._hierarchyConfig.elementClassSpecification}
271
275
  WHERE
272
- Model.Id = ?
273
- AND Category.Id = ?
274
- AND Parent.Id IS NULL
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 id
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: `SELECT COUNT(*) FROM CategoryElements`,
285
- bindings: [
286
- { type: "id", value: modelId },
287
- { type: "id", value: categoryId },
288
- ],
289
- }, { rowFormat: "Indexes", limit: "unbounded" });
290
- return (await reader.next()).value[0];
291
- }
292
- async getCategoryElementsCount(modelId, categoryId) {
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