@itwin/unified-selection 0.1.0 → 0.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 (67) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +78 -0
  3. package/lib/cjs/unified-selection/CachingHiliteSetProvider.d.ts +37 -0
  4. package/lib/cjs/unified-selection/CachingHiliteSetProvider.d.ts.map +1 -0
  5. package/lib/cjs/unified-selection/CachingHiliteSetProvider.js +65 -0
  6. package/lib/cjs/unified-selection/CachingHiliteSetProvider.js.map +1 -0
  7. package/lib/cjs/unified-selection/HiliteSetProvider.d.ts +39 -0
  8. package/lib/cjs/unified-selection/HiliteSetProvider.d.ts.map +1 -0
  9. package/lib/cjs/unified-selection/HiliteSetProvider.js +298 -0
  10. package/lib/cjs/unified-selection/HiliteSetProvider.js.map +1 -0
  11. package/lib/cjs/unified-selection/SelectionChangeEvent.d.ts +6 -4
  12. package/lib/cjs/unified-selection/SelectionChangeEvent.d.ts.map +1 -1
  13. package/lib/cjs/unified-selection/SelectionChangeEvent.js +1 -1
  14. package/lib/cjs/unified-selection/SelectionChangeEvent.js.map +1 -1
  15. package/lib/cjs/unified-selection/SelectionScope.d.ts +44 -0
  16. package/lib/cjs/unified-selection/SelectionScope.d.ts.map +1 -0
  17. package/lib/cjs/unified-selection/SelectionScope.js +160 -0
  18. package/lib/cjs/unified-selection/SelectionScope.js.map +1 -0
  19. package/lib/cjs/unified-selection/SelectionStorage.d.ts +16 -3
  20. package/lib/cjs/unified-selection/SelectionStorage.d.ts.map +1 -1
  21. package/lib/cjs/unified-selection/SelectionStorage.js +14 -3
  22. package/lib/cjs/unified-selection/SelectionStorage.js.map +1 -1
  23. package/lib/cjs/unified-selection/queries/ECMetadata.d.ts +44 -0
  24. package/lib/cjs/unified-selection/queries/ECMetadata.d.ts.map +1 -0
  25. package/lib/cjs/unified-selection/queries/ECMetadata.js +17 -0
  26. package/lib/cjs/unified-selection/queries/ECMetadata.js.map +1 -0
  27. package/lib/cjs/unified-selection/queries/ECSqlCore.d.ts +69 -0
  28. package/lib/cjs/unified-selection/queries/ECSqlCore.d.ts.map +1 -0
  29. package/lib/cjs/unified-selection/queries/ECSqlCore.js +23 -0
  30. package/lib/cjs/unified-selection/queries/ECSqlCore.js.map +1 -0
  31. package/lib/cjs/unified-selection.d.ts +4 -1
  32. package/lib/cjs/unified-selection.d.ts.map +1 -1
  33. package/lib/cjs/unified-selection.js +9 -1
  34. package/lib/cjs/unified-selection.js.map +1 -1
  35. package/lib/esm/unified-selection/CachingHiliteSetProvider.d.ts +37 -0
  36. package/lib/esm/unified-selection/CachingHiliteSetProvider.d.ts.map +1 -0
  37. package/lib/esm/unified-selection/CachingHiliteSetProvider.js +61 -0
  38. package/lib/esm/unified-selection/CachingHiliteSetProvider.js.map +1 -0
  39. package/lib/esm/unified-selection/HiliteSetProvider.d.ts +39 -0
  40. package/lib/esm/unified-selection/HiliteSetProvider.d.ts.map +1 -0
  41. package/lib/esm/unified-selection/HiliteSetProvider.js +294 -0
  42. package/lib/esm/unified-selection/HiliteSetProvider.js.map +1 -0
  43. package/lib/esm/unified-selection/SelectionChangeEvent.d.ts +6 -4
  44. package/lib/esm/unified-selection/SelectionChangeEvent.d.ts.map +1 -1
  45. package/lib/esm/unified-selection/SelectionChangeEvent.js +1 -1
  46. package/lib/esm/unified-selection/SelectionChangeEvent.js.map +1 -1
  47. package/lib/esm/unified-selection/SelectionScope.d.ts +44 -0
  48. package/lib/esm/unified-selection/SelectionScope.d.ts.map +1 -0
  49. package/lib/esm/unified-selection/SelectionScope.js +156 -0
  50. package/lib/esm/unified-selection/SelectionScope.js.map +1 -0
  51. package/lib/esm/unified-selection/SelectionStorage.d.ts +16 -3
  52. package/lib/esm/unified-selection/SelectionStorage.d.ts.map +1 -1
  53. package/lib/esm/unified-selection/SelectionStorage.js +13 -2
  54. package/lib/esm/unified-selection/SelectionStorage.js.map +1 -1
  55. package/lib/esm/unified-selection/queries/ECMetadata.d.ts +44 -0
  56. package/lib/esm/unified-selection/queries/ECMetadata.d.ts.map +1 -0
  57. package/lib/esm/unified-selection/queries/ECMetadata.js +13 -0
  58. package/lib/esm/unified-selection/queries/ECMetadata.js.map +1 -0
  59. package/lib/esm/unified-selection/queries/ECSqlCore.d.ts +69 -0
  60. package/lib/esm/unified-selection/queries/ECSqlCore.d.ts.map +1 -0
  61. package/lib/esm/unified-selection/queries/ECSqlCore.js +19 -0
  62. package/lib/esm/unified-selection/queries/ECSqlCore.js.map +1 -0
  63. package/lib/esm/unified-selection.d.ts +4 -1
  64. package/lib/esm/unified-selection.d.ts.map +1 -1
  65. package/lib/esm/unified-selection.js +4 -1
  66. package/lib/esm/unified-selection.js.map +1 -1
  67. package/package.json +9 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @itwin/unified-selection
2
2
 
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#488](https://github.com/iTwin/presentation/pull/488): Added API for getting hilite sets for `Selectables` and active `SelectionStorage` selection.
8
+
9
+ ### Patch Changes
10
+
11
+ - [#495](https://github.com/iTwin/presentation/pull/495): Added license field to `package.json`.
12
+
3
13
  ## 0.1.0
4
14
 
5
15
  ### Minor Changes
package/README.md CHANGED
@@ -88,3 +88,81 @@ With that in mind, the above components _A_, _B_ and _C_ can be configured as fo
88
88
  - _Component A_ only cares about top level selection. Whenever something is selected in the component, unified selection is updated at the top level. Similarly, whenever unified selection changes, the component only reacts if that happened at the top level.
89
89
  - _Component B_ reloads its content if the selection changes at the top level. Row selection is handled using lower level, so selecting a row doesn't affect _Component A's_ selection or _Component B's_ content.
90
90
  - _Component C_ reloads its content no matter the selection level.
91
+
92
+ ### Hilite sets
93
+
94
+ > **Note:** hilite = highlight
95
+
96
+ `@itwin/core-frontend` contains a concept called the [HiliteSet](https://www.itwinjs.org/reference/core-frontend/selectionset/hiliteset/). This concept is tightly related to unified selection, because, generally, we want selected elements to be highlighted in the application's graphics views. The `HiliteSet` object contains IDs of 3 types of elements: [GeometricModel](https://www.itwinjs.org/reference/core-backend/models/geometricmodel/), [SubCategory](https://www.itwinjs.org/reference/core-backend/categories/subcategory/) and [GeometricElement](https://www.itwinjs.org/reference/core-backend/elements/geometricelement/). On the other hand, the unified selection API allows selecting other kinds of elements too, so IDs of these elements need to be mapped to the supported ones. The rules are as follows:
97
+
98
+ - for `BisCore.Subject` return IDs of all geometric models that are recursively under that Subject,
99
+ - for `BisCore.Model` just return its ID,
100
+ - for `BisCore.PhysicalPartition` just return ID of a model that models it,
101
+ - for `BisCore.Category` return IDs of all its _SubCategories_,
102
+ - for `BisCore.SubCategory` just return its ID,
103
+ - for `BisCore.GeometricElement` return ID of its own and all its child elements recursively.
104
+
105
+ So for example when unified selection contains a Subject, the hilite set for it will contain all models under that Subject, it's child Subjects, their child Subjects, etc. Given such hilite set, the viewport component hilites all elements in those models.
106
+
107
+ The `@itwin/unified-selection` package delivers APIs for creating a `HiliteSet` or retrieving it for _current_ selection in a `SelectionStorage`:
108
+
109
+ ```ts
110
+ // Components may want to get a hilite set for arbitrary set of Selectables - use `createHiliteSetProvider` for that.
111
+ import { IModelConnection } from "@itwin/core-frontend";
112
+ import { createECSqlQueryExecutor, createMetadataProvider } from "@itwin/presentation-core-interop";
113
+ import { createHiliteSetProvider } from "@itwin/unified-selection";
114
+ const hiliteProvider = createHiliteSetProvider({
115
+ metadataProvider: createMetadataProvider(imodel),
116
+ queryExecutor: createECSqlQueryExecutor(imodel),
117
+ });
118
+ const hiliteSet = await hiliteProvider.getHiliteSet({ selectables });
119
+
120
+ // Some others may want to get a hilite set for _current_ selection in storage - use `createCachingHiliteSetProvider` for that. It's
121
+ // recommended to keep a single instance of this provider per application as it caches hilite sets per each iModel's selection.
122
+ import { createCachingHiliteSetProvider } from "@itwin/unified-selection";
123
+ const selectionHiliteProvider = createCachingHiliteSetProvider({
124
+ selectionStorage,
125
+ iModelProvider: (iModelKey: string) => {
126
+ queryExecutor: IECSqlQueryExecutor;
127
+ metadataProvider: IMetadataProvider;
128
+ },
129
+ });
130
+ const selectionHiliteSet = await selectionHiliteProvider.getHiliteSet({ imodel.key });
131
+ // The caching provider registers a selection change listener and should be disposed, in case its lifetime
132
+ // is shorter than that of `SelectionStorage`, to unregister the listener.
133
+ selectionHiliteProvider.dispose();
134
+ ```
135
+
136
+ #### Selection scopes
137
+
138
+ Selection scopes allow decoupling of what gets picked and what gets selected. Without selection scopes, whenever a user picks an element in the viewport, its ID goes straight into unified selection storage. With selection scopes we can modify that and add something different. The input to the selection scopes' processor is a query executor, element IDs, and the scope to apply, and the output is an iterator of `SelectableInstanceKey`. We get the input when the user picks some elements in the viewport, run that through the selection scope processor and put the output into unified selection storage.
139
+
140
+ Here are the scopes we support at the moment:
141
+
142
+ - `element` - return key of selected element.
143
+ - `category` - return key of geometric element's category.
144
+ - `model` - return key of element's model.
145
+ - `functional` - return key of element's related functional element. For `BisCore.GeometricElement3d` the related functional element is found using the `Functional.PhysicalElementFulfillsFunction` relationship. For `BisCore.GeometricElement2d` the nearest functional element is searched for using the `Functional.DrawingGraphicRepresentsFunctionalElement` relationship - if the given element has a related functional element, it will be returned, otherwise the element's parent will be checked and if it also does not have a related functional, then the parent of the parent will be checked until no more ancestors can be traversed or a functional element is found. Regardless whether it is an `BisCore.GeometricElement2d` or `BisCore.GeometricElement3d` if no functional element is found then the element itself will be returned.
146
+
147
+ The `@itwin/unified-selection` package delivers a `computeSelection` function for computing which elements should be added into unified selection storage based on the given element ID's and a specified selection scope:
148
+
149
+ ```ts
150
+ import { computeSelection } from "@itwin/unified-selection";
151
+ import { IModelConnection } from "@itwin/core-frontend";
152
+ import { createECSqlQueryExecutor } from "@itwin/presentation-core-interop";
153
+ const queryExecutor = createECSqlQueryExecutor(imodel);
154
+ const selection = computeSelection({ queryExecutor, elementIds, scope: "element" });
155
+ ```
156
+
157
+ `element` and `functional` scopes additionally allow selecting assembly elements by specifying the `ancestorLevel` property in the selection scope argument of `computeSelection` function. The `ancestorLevel` property specifies how far "up" we should walk to find the target element. When not specified or `0`, the target element matches the request element. When set to `1`, the target element matches the direct parent element. When `2`, the target element is the parent of the parent element, and so on. In all situations when this is `> 0`, we're not walking further than the last existing element, for example, when `ancestorLevel = 1` (direct parent element is requested), but the request element doesn't have a parent, the request element is returned as the result. A negative value would result in the top-most element to be returned.
158
+
159
+ For the `functional` scope, the `ancestorLevel` property is used as follows: if an element is a `BisCore.GeometricElement3d` element, its ancestor is selected based on the given `ancestorLevel` the same as with non-functional elements, and then the resulting element's related functional element will be returned (using the `Functional.PhysicalElementFulfillsFunction` relationship), or if it does not have one, then the resulting element will be returned. For `BisCore.GeometricElement2d` elements, the nearest related functional element is found in the same way it is done when the `ancestorLevel` property is not provided, and then the ancestor of that element is returned (based on the provided value of `ancestorLevel`).
160
+
161
+ ```ts
162
+ import { computeSelection } from "@itwin/unified-selection";
163
+ import { IModelConnection } from "@itwin/core-frontend";
164
+ import { createECSqlQueryExecutor } from "@itwin/presentation-core-interop";
165
+ const queryExecutor = createECSqlQueryExecutor(imodel);
166
+ // Returns the parent element, or the element itself if it does not have a parent, for each element specified in `elementIds` argument.
167
+ const selection = computeSelection({ queryExecutor, elementIds, scope: { id: "element", ancestorLevel: 1 } });
168
+ ```
@@ -0,0 +1,37 @@
1
+ import { HiliteSet } from "./HiliteSetProvider";
2
+ import { IMetadataProvider } from "./queries/ECMetadata";
3
+ import { IECSqlQueryExecutor } from "./queries/ECSqlCore";
4
+ import { SelectionStorage } from "./SelectionStorage";
5
+ /**
6
+ * Props for creating a `CachingHiliteSetProvider` instance.
7
+ * @internal Not exported through barrel, but used in public API as an argument. May be supplemented with optional attributes any time.
8
+ */
9
+ export interface CachingHiliteSetProviderProps {
10
+ selectionStorage: SelectionStorage;
11
+ iModelProvider: (iModelKey: string) => {
12
+ queryExecutor: IECSqlQueryExecutor;
13
+ metadataProvider: IMetadataProvider;
14
+ };
15
+ }
16
+ /**
17
+ * Defines return value of `createCachingHiliteSetProvider`.
18
+ *
19
+ * @beta Used in public API as a return value. Not expected to be created / extended by package
20
+ * consumers, may be supplemented with required attributes any time.
21
+ */
22
+ export interface CachingHiliteSetProvider {
23
+ /** Get the current hilite set iterator for the specified imodel */
24
+ getHiliteSet(props: {
25
+ /** iModel to get hilite set for */
26
+ iModelKey: string;
27
+ }): AsyncIterableIterator<HiliteSet>;
28
+ /** Disposes the cache. */
29
+ dispose(): void;
30
+ }
31
+ /**
32
+ * Creates a hilite set provider that caches hilite set for current selection for given iModel so any subsequent
33
+ * hilite set requests for the same iModel don't cost until selection in given selection storage changes.
34
+ * @beta
35
+ */
36
+ export declare function createCachingHiliteSetProvider(props: CachingHiliteSetProviderProps): CachingHiliteSetProvider;
37
+ //# sourceMappingURL=CachingHiliteSetProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CachingHiliteSetProvider.d.ts","sourceRoot":"","sources":["../../../src/unified-selection/CachingHiliteSetProvider.ts"],"names":[],"mappings":"AAWA,OAAO,EAA2B,SAAS,EAAqB,MAAM,qBAAqB,CAAC;AAC5F,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,EAAuC,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3F;;;GAGG;AACH,MAAM,WAAW,6BAA6B;IAC5C,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK;QAAE,aAAa,EAAE,mBAAmB,CAAC;QAAC,gBAAgB,EAAE,iBAAiB,CAAA;KAAE,CAAC;CACpH;AAED;;;;;GAKG;AACH,MAAM,WAAW,wBAAwB;IACvC,mEAAmE;IACnE,YAAY,CAAC,KAAK,EAAE;QAClB,mCAAmC;QACnC,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAErC,0BAA0B;IAC1B,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,6BAA6B,GAAG,wBAAwB,CAE7G"}
@@ -0,0 +1,65 @@
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.createCachingHiliteSetProvider = void 0;
8
+ /** @packageDocumentation
9
+ * @module UnifiedSelection
10
+ */
11
+ const rxjs_1 = require("rxjs");
12
+ const rxjs_for_await_1 = require("rxjs-for-await");
13
+ const HiliteSetProvider_1 = require("./HiliteSetProvider");
14
+ const SelectionStorage_1 = require("./SelectionStorage");
15
+ /**
16
+ * Creates a hilite set provider that caches hilite set for current selection for given iModel so any subsequent
17
+ * hilite set requests for the same iModel don't cost until selection in given selection storage changes.
18
+ * @beta
19
+ */
20
+ function createCachingHiliteSetProvider(props) {
21
+ return new CachingHiliteSetProviderImpl(props);
22
+ }
23
+ exports.createCachingHiliteSetProvider = createCachingHiliteSetProvider;
24
+ class CachingHiliteSetProviderImpl {
25
+ _selectionStorage;
26
+ _hiliteSetProviders = new Map();
27
+ _cache = new Map();
28
+ _removeListener;
29
+ _iModelProvider;
30
+ constructor(props) {
31
+ this._selectionStorage = props.selectionStorage;
32
+ this._iModelProvider = props.iModelProvider;
33
+ this._removeListener = this._selectionStorage.selectionChangeEvent.addListener((args) => {
34
+ this._cache.delete(args.iModelKey);
35
+ if (args.changeType === "clear" && args.source === SelectionStorage_1.IMODEL_CLOSE_SELECTION_CLEAR_SOURCE) {
36
+ this._hiliteSetProviders.delete(args.iModelKey);
37
+ }
38
+ });
39
+ }
40
+ getHiliteSet({ iModelKey }) {
41
+ const { queryExecutor, metadataProvider } = this._iModelProvider(iModelKey);
42
+ const provider = this.getHiliteSetProvider(iModelKey, queryExecutor, metadataProvider);
43
+ let hiliteSet = this._cache.get(iModelKey);
44
+ if (!hiliteSet) {
45
+ const selectables = this._selectionStorage.getSelection({ iModelKey });
46
+ hiliteSet = (0, rxjs_1.from)(provider.getHiliteSet({ selectables })).pipe((0, rxjs_1.shareReplay)({ refCount: true }));
47
+ this._cache.set(iModelKey, hiliteSet);
48
+ }
49
+ return (0, rxjs_for_await_1.eachValueFrom)(hiliteSet);
50
+ }
51
+ dispose() {
52
+ this._removeListener();
53
+ this._hiliteSetProviders = new Map();
54
+ this._cache = new Map();
55
+ }
56
+ getHiliteSetProvider(iModelKey, queryExecutor, metadataProvider) {
57
+ let provider = this._hiliteSetProviders.get(iModelKey);
58
+ if (!provider) {
59
+ provider = (0, HiliteSetProvider_1.createHiliteSetProvider)({ queryExecutor, metadataProvider });
60
+ this._hiliteSetProviders.set(iModelKey, provider);
61
+ }
62
+ return provider;
63
+ }
64
+ }
65
+ //# sourceMappingURL=CachingHiliteSetProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CachingHiliteSetProvider.js","sourceRoot":"","sources":["../../../src/unified-selection/CachingHiliteSetProvider.ts"],"names":[],"mappings":";AAAA;;;gGAGgG;;;AAEhG;;GAEG;AAEH,+BAAqD;AACrD,mDAA+C;AAC/C,2DAA4F;AAI5F,yDAA2F;AA4B3F;;;;GAIG;AACH,SAAgB,8BAA8B,CAAC,KAAoC;IACjF,OAAO,IAAI,4BAA4B,CAAC,KAAK,CAAC,CAAC;AACjD,CAAC;AAFD,wEAEC;AAED,MAAM,4BAA4B;IACxB,iBAAiB,CAAmB;IACpC,mBAAmB,GAAG,IAAI,GAAG,EAA6B,CAAC;IAC3D,MAAM,GAAG,IAAI,GAAG,EAAiC,CAAC;IAClD,eAAe,CAAa;IAC5B,eAAe,CAAqG;IAE5H,YAAY,KAAoC;QAC9C,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC,gBAAgB,CAAC;QAChD,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC,cAAc,CAAC;QAC5C,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,iBAAiB,CAAC,oBAAoB,CAAC,WAAW,CAAC,CAAC,IAAqC,EAAE,EAAE;YACvH,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACnC,IAAI,IAAI,CAAC,UAAU,KAAK,OAAO,IAAI,IAAI,CAAC,MAAM,KAAK,sDAAmC,EAAE;gBACtF,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;aACjD;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAEM,YAAY,CAAC,EAAE,SAAS,EAAyB;QACtD,MAAM,EAAE,aAAa,EAAE,gBAAgB,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;QAC5E,MAAM,QAAQ,GAAG,IAAI,CAAC,oBAAoB,CAAC,SAAS,EAAE,aAAa,EAAE,gBAAgB,CAAC,CAAC;QACvF,IAAI,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAE3C,IAAI,CAAC,SAAS,EAAE;YACd,MAAM,WAAW,GAAG,IAAI,CAAC,iBAAiB,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC;YACvE,SAAS,GAAG,IAAA,WAAI,EAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAA,kBAAW,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAC/F,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;SACvC;QAED,OAAO,IAAA,8BAAa,EAAC,SAAS,CAAC,CAAC;IAClC,CAAC;IAEM,OAAO;QACZ,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,IAAI,CAAC,mBAAmB,GAAG,IAAI,GAAG,EAAE,CAAC;QACrC,IAAI,CAAC,MAAM,GAAG,IAAI,GAAG,EAAE,CAAC;IAC1B,CAAC;IAEO,oBAAoB,CAAC,SAAiB,EAAE,aAAkC,EAAE,gBAAmC;QACrH,IAAI,QAAQ,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACvD,IAAI,CAAC,QAAQ,EAAE;YACb,QAAQ,GAAG,IAAA,2CAAuB,EAAC,EAAE,aAAa,EAAE,gBAAgB,EAAE,CAAC,CAAC;YACxE,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;SACnD;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF","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\n/** @packageDocumentation\n * @module UnifiedSelection\n */\n\nimport { from, Observable, shareReplay } from \"rxjs\";\nimport { eachValueFrom } from \"rxjs-for-await\";\nimport { createHiliteSetProvider, HiliteSet, HiliteSetProvider } from \"./HiliteSetProvider\";\nimport { IMetadataProvider } from \"./queries/ECMetadata\";\nimport { IECSqlQueryExecutor } from \"./queries/ECSqlCore\";\nimport { StorageSelectionChangeEventArgs } from \"./SelectionChangeEvent\";\nimport { IMODEL_CLOSE_SELECTION_CLEAR_SOURCE, SelectionStorage } from \"./SelectionStorage\";\n\n/**\n * Props for creating a `CachingHiliteSetProvider` instance.\n * @internal Not exported through barrel, but used in public API as an argument. May be supplemented with optional attributes any time.\n */\nexport interface CachingHiliteSetProviderProps {\n selectionStorage: SelectionStorage;\n iModelProvider: (iModelKey: string) => { queryExecutor: IECSqlQueryExecutor; metadataProvider: IMetadataProvider };\n}\n\n/**\n * Defines return value of `createCachingHiliteSetProvider`.\n *\n * @beta Used in public API as a return value. Not expected to be created / extended by package\n * consumers, may be supplemented with required attributes any time.\n */\nexport interface CachingHiliteSetProvider {\n /** Get the current hilite set iterator for the specified imodel */\n getHiliteSet(props: {\n /** iModel to get hilite set for */\n iModelKey: string;\n }): AsyncIterableIterator<HiliteSet>;\n\n /** Disposes the cache. */\n dispose(): void;\n}\n\n/**\n * Creates a hilite set provider that caches hilite set for current selection for given iModel so any subsequent\n * hilite set requests for the same iModel don't cost until selection in given selection storage changes.\n * @beta\n */\nexport function createCachingHiliteSetProvider(props: CachingHiliteSetProviderProps): CachingHiliteSetProvider {\n return new CachingHiliteSetProviderImpl(props);\n}\n\nclass CachingHiliteSetProviderImpl implements CachingHiliteSetProvider {\n private _selectionStorage: SelectionStorage;\n private _hiliteSetProviders = new Map<string, HiliteSetProvider>();\n private _cache = new Map<string, Observable<HiliteSet>>();\n private _removeListener: () => void;\n private _iModelProvider: (iModelKey: string) => { queryExecutor: IECSqlQueryExecutor; metadataProvider: IMetadataProvider };\n\n constructor(props: CachingHiliteSetProviderProps) {\n this._selectionStorage = props.selectionStorage;\n this._iModelProvider = props.iModelProvider;\n this._removeListener = this._selectionStorage.selectionChangeEvent.addListener((args: StorageSelectionChangeEventArgs) => {\n this._cache.delete(args.iModelKey);\n if (args.changeType === \"clear\" && args.source === IMODEL_CLOSE_SELECTION_CLEAR_SOURCE) {\n this._hiliteSetProviders.delete(args.iModelKey);\n }\n });\n }\n\n public getHiliteSet({ iModelKey }: { iModelKey: string }): AsyncIterableIterator<HiliteSet> {\n const { queryExecutor, metadataProvider } = this._iModelProvider(iModelKey);\n const provider = this.getHiliteSetProvider(iModelKey, queryExecutor, metadataProvider);\n let hiliteSet = this._cache.get(iModelKey);\n\n if (!hiliteSet) {\n const selectables = this._selectionStorage.getSelection({ iModelKey });\n hiliteSet = from(provider.getHiliteSet({ selectables })).pipe(shareReplay({ refCount: true }));\n this._cache.set(iModelKey, hiliteSet);\n }\n\n return eachValueFrom(hiliteSet);\n }\n\n public dispose(): void {\n this._removeListener();\n this._hiliteSetProviders = new Map();\n this._cache = new Map();\n }\n\n private getHiliteSetProvider(iModelKey: string, queryExecutor: IECSqlQueryExecutor, metadataProvider: IMetadataProvider) {\n let provider = this._hiliteSetProviders.get(iModelKey);\n if (!provider) {\n provider = createHiliteSetProvider({ queryExecutor, metadataProvider });\n this._hiliteSetProviders.set(iModelKey, provider);\n }\n return provider;\n }\n}\n"]}
@@ -0,0 +1,39 @@
1
+ import { IMetadataProvider } from "./queries/ECMetadata";
2
+ import { IECSqlQueryExecutor } from "./queries/ECSqlCore";
3
+ import { Selectables } from "./Selectable";
4
+ /**
5
+ * A set of model, subcategory and element ids that can be used for specifying hilite.
6
+ * @see https://www.itwinjs.org/reference/core-frontend/selectionset/hiliteset/
7
+ * @beta
8
+ */
9
+ export interface HiliteSet {
10
+ models: string[];
11
+ subCategories: string[];
12
+ elements: string[];
13
+ }
14
+ /**
15
+ * Props for creating a `HiliteSetProvider` instance.
16
+ * @internal Not exported through barrel, but used in public API as an argument. May be supplemented with optional attributes any time.
17
+ */
18
+ export interface HiliteSetProviderProps {
19
+ queryExecutor: IECSqlQueryExecutor;
20
+ metadataProvider: IMetadataProvider;
21
+ }
22
+ /**
23
+ * Defines return value of `createHiliteSetProvider`.
24
+ *
25
+ * @beta Used in public API as a return value. Not expected to be created / extended by package
26
+ * consumers, may be supplemented with required attributes any time.
27
+ */
28
+ export interface HiliteSetProvider {
29
+ /** Get the current hilite set iterator for the specified imodel */
30
+ getHiliteSet(props: {
31
+ selectables: Selectables;
32
+ }): AsyncIterableIterator<HiliteSet>;
33
+ }
34
+ /**
35
+ * Creates a hilite set provider that returns a `HiliteSet` for given selectables.
36
+ * @beta
37
+ */
38
+ export declare function createHiliteSetProvider(props: HiliteSetProviderProps): HiliteSetProvider;
39
+ //# sourceMappingURL=HiliteSetProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HiliteSetProvider.d.ts","sourceRoot":"","sources":["../../../src/unified-selection/HiliteSetProvider.ts"],"names":[],"mappings":"AAWA,OAAO,EAAW,iBAAiB,EAAsB,MAAM,sBAAsB,CAAC;AACtF,OAAO,EAAgC,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AACxF,OAAO,EAAyB,WAAW,EAAE,MAAM,cAAc,CAAC;AAIlE;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,aAAa,EAAE,mBAAmB,CAAC;IACnC,gBAAgB,EAAE,iBAAiB,CAAC;CACrC;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,mEAAmE;IACnE,YAAY,CAAC,KAAK,EAAE;QAAE,WAAW,EAAE,WAAW,CAAA;KAAE,GAAG,qBAAqB,CAAC,SAAS,CAAC,CAAC;CACrF;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,sBAAsB,GAAG,iBAAiB,CAExF"}
@@ -0,0 +1,298 @@
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.createHiliteSetProvider = void 0;
8
+ /** @packageDocumentation
9
+ * @module UnifiedSelection
10
+ */
11
+ const rxjs_1 = require("rxjs");
12
+ const rxjs_for_await_1 = require("rxjs-for-await");
13
+ const ECMetadata_1 = require("./queries/ECMetadata");
14
+ const ECSqlCore_1 = require("./queries/ECSqlCore");
15
+ const HILITE_SET_EMIT_FREQUENCY = 20;
16
+ /**
17
+ * Creates a hilite set provider that returns a `HiliteSet` for given selectables.
18
+ * @beta
19
+ */
20
+ function createHiliteSetProvider(props) {
21
+ return new HiliteSetProviderImpl(props);
22
+ }
23
+ exports.createHiliteSetProvider = createHiliteSetProvider;
24
+ class HiliteSetProviderImpl {
25
+ _queryExecutor;
26
+ _metadataProvider;
27
+ // Map between a class name and its type
28
+ _classRelationCache;
29
+ constructor(props) {
30
+ this._queryExecutor = props.queryExecutor;
31
+ this._metadataProvider = props.metadataProvider;
32
+ this._classRelationCache = new Map();
33
+ }
34
+ /**
35
+ * Get hilite set iterator for supplied `Selectables`.
36
+ */
37
+ getHiliteSet(props) {
38
+ const obs = this.getHiliteSetObservable(props);
39
+ return (0, rxjs_for_await_1.eachValueFrom)(obs);
40
+ }
41
+ /**
42
+ * Returns a "hot" observable of hilite sets.
43
+ */
44
+ getHiliteSetObservable({ selectables }) {
45
+ const instancesByType = this.getInstancesByType(selectables);
46
+ const observables = {
47
+ models: this.getHilitedModels(instancesByType),
48
+ subCategories: this.getHilitedSubCategories(instancesByType),
49
+ elements: this.getHilitedElements(instancesByType),
50
+ };
51
+ let hiliteSet = { models: [], subCategories: [], elements: [] };
52
+ let lastEmitTime = performance.now();
53
+ const subject = new rxjs_1.Subject();
54
+ const subscriptions = ["models", "subCategories", "elements"].map((key) => observables[key].subscribe({
55
+ next(val) {
56
+ hiliteSet[key].push(val);
57
+ if (performance.now() - lastEmitTime < HILITE_SET_EMIT_FREQUENCY) {
58
+ return;
59
+ }
60
+ subject.next(hiliteSet);
61
+ hiliteSet = { models: [], subCategories: [], elements: [] };
62
+ lastEmitTime = performance.now();
63
+ },
64
+ complete() {
65
+ observables[key] = undefined;
66
+ if (observables.models || observables.subCategories || observables.elements) {
67
+ return;
68
+ }
69
+ // Emit last batch before completing the observable.
70
+ if (hiliteSet.elements.length || hiliteSet.models.length || hiliteSet.subCategories.length) {
71
+ subject.next(hiliteSet);
72
+ }
73
+ subject.complete();
74
+ },
75
+ error(err) {
76
+ subscriptions.forEach((x) => x.unsubscribe());
77
+ subscriptions.length = 0;
78
+ subject.error(err);
79
+ },
80
+ }));
81
+ return subject;
82
+ }
83
+ async getType(key) {
84
+ const cachedType = this._classRelationCache.get(key.className.replace(".", ":"));
85
+ if (cachedType) {
86
+ return cachedType;
87
+ }
88
+ const promise = this.getTypeImpl(key).then((res) => {
89
+ // Update the cache with the result of the promise.
90
+ this._classRelationCache.set(key.className, res);
91
+ return res;
92
+ });
93
+ // Add the promise to cache to prevent `getTypeImpl` being called multiple times.
94
+ this._classRelationCache.set(key.className, promise);
95
+ return promise;
96
+ }
97
+ async getTypeImpl(key) {
98
+ const keyClass = await this.getClass(key.className);
99
+ return ((keyClass &&
100
+ ((await this.checkType(keyClass, "BisCore", "Subject", "subject")) ??
101
+ (await this.checkType(keyClass, "BisCore", "Model", "model")) ??
102
+ (await this.checkType(keyClass, "BisCore", "Category", "category")) ??
103
+ (await this.checkType(keyClass, "BisCore", "SubCategory", "subCategory")) ??
104
+ (await this.checkType(keyClass, "Functional", "FunctionalElement", "functionalElement")) ??
105
+ (await this.checkType(keyClass, "BisCore", "GroupInformationElement", "groupInformationElement")) ??
106
+ (await this.checkType(keyClass, "BisCore", "GeometricElement", "geometricElement")) ??
107
+ (await this.checkType(keyClass, "BisCore", "Element", "element")))) ??
108
+ "unknown");
109
+ }
110
+ async checkType(keyClass, schemaName, className, type) {
111
+ return (await keyClass.is(className, schemaName)) ? type : undefined;
112
+ }
113
+ getInstancesByType(selectables) {
114
+ const keyTypeObs = (0, rxjs_1.merge)((0, rxjs_1.from)(selectables.custom.values()).pipe((0, rxjs_1.mergeMap)((selectable) => selectable.loadInstanceKeys())), (0, rxjs_1.from)(selectables.instanceKeys).pipe((0, rxjs_1.mergeMap)(([className, idSet]) => (0, rxjs_1.from)(idSet).pipe((0, rxjs_1.map)((id) => ({ className, id })))))).pipe(
115
+ // Get types for each instance key
116
+ (0, rxjs_1.mergeMap)((instanceKey) => (0, rxjs_1.from)(this.getType(instanceKey)).pipe((0, rxjs_1.map)((instanceIdType) => ({ instanceId: instanceKey.id, instanceIdType })))),
117
+ // Cache the results
118
+ (0, rxjs_1.shareReplay)());
119
+ return Object.fromEntries(INSTANCE_TYPES.map((type) => [
120
+ type,
121
+ keyTypeObs.pipe((0, rxjs_1.filter)(({ instanceIdType }) => instanceIdType === type), (0, rxjs_1.map)(({ instanceId }) => instanceId), unique()),
122
+ ]));
123
+ }
124
+ getHilitedModels(instancesByType) {
125
+ return (0, rxjs_1.forkJoin)({
126
+ modelKeys: instancesByType.model.pipe((0, rxjs_1.toArray)()),
127
+ subjectKeys: instancesByType.subject.pipe((0, rxjs_1.toArray)()),
128
+ }).pipe((0, rxjs_1.mergeMap)(({ modelKeys, subjectKeys }) => {
129
+ if (!modelKeys.length && !subjectKeys.length) {
130
+ return rxjs_1.EMPTY;
131
+ }
132
+ const bindings = [];
133
+ const query = `WITH RECURSIVE
134
+ ChildSubjects (ECInstanceId, JsonProperties) AS (
135
+ SELECT ECInstanceId, JsonProperties FROM BisCore.Subject WHERE ${(0, ECSqlCore_1.formIdBindings)("ECInstanceId", subjectKeys, bindings)}
136
+ UNION ALL
137
+ SELECT r.ECInstanceId, r.JsonProperties FROM ChildSubjects s
138
+ JOIN BisCore.Subject r ON r.Parent.Id = s.ECInstanceId
139
+ ),
140
+ Models (ECInstanceId) AS (
141
+ SELECT s.ECInstanceId as \`ECInstanceId\` FROM BisCore.Model s
142
+ WHERE ${(0, ECSqlCore_1.formIdBindings)("ECInstanceId", modelKeys, bindings)}
143
+ )
144
+ SELECT r.ECInstanceId as \`ECInstanceId\` FROM ChildSubjects s
145
+ JOIN BisCore.PhysicalPartition r
146
+ ON r.Parent.Id = s.ECInstanceId
147
+ OR json_extract(s.JsonProperties,'$.Subject.Model.TargetPartition') = printf('0x%x', r.ECInstanceId)
148
+ UNION
149
+ SELECT ECInstanceId FROM Models;`;
150
+ return (0, rxjs_1.from)(this._queryExecutor.createQueryReader(query, bindings));
151
+ }), (0, rxjs_1.map)((row) => row.ECInstanceId));
152
+ }
153
+ getHilitedSubCategories(instancesByType) {
154
+ return (0, rxjs_1.forkJoin)({
155
+ subCategoryKeys: instancesByType.subCategory.pipe((0, rxjs_1.toArray)()),
156
+ categoryKeys: instancesByType.category.pipe((0, rxjs_1.toArray)()),
157
+ }).pipe((0, rxjs_1.mergeMap)(({ subCategoryKeys, categoryKeys }) => {
158
+ if (!subCategoryKeys.length && !categoryKeys.length) {
159
+ return rxjs_1.EMPTY;
160
+ }
161
+ const bindings = [];
162
+ const query = `WITH
163
+ CategorySubCategories (ECInstanceId) AS (
164
+ SELECT r.ECInstanceId as \`ECInstanceId\` FROM BisCore.Category s
165
+ JOIN BisCore.SubCategory r ON r.Parent.Id = s.ECInstanceId
166
+ WHERE ${(0, ECSqlCore_1.formIdBindings)("s.ECInstanceId", categoryKeys, bindings)}
167
+ ),
168
+ SubCategories (ECInstanceId) AS (
169
+ SELECT s.ECInstanceId as \`ECInstanceId\` FROM BisCore.SubCategory s
170
+ WHERE ${(0, ECSqlCore_1.formIdBindings)("s.ECInstanceId", subCategoryKeys, bindings)}
171
+ )
172
+ SELECT ECInstanceId FROM CategorySubCategories
173
+ UNION
174
+ SELECT ECInstanceId FROM SubCategories;`;
175
+ return (0, rxjs_1.from)(this._queryExecutor.createQueryReader(query, bindings));
176
+ }), (0, rxjs_1.map)((row) => row.ECInstanceId));
177
+ }
178
+ getHilitedElements(instancesByType) {
179
+ return (0, rxjs_1.forkJoin)({
180
+ groupInformationElementKeys: instancesByType.groupInformationElement.pipe((0, rxjs_1.toArray)()),
181
+ geometricElementKeys: instancesByType.geometricElement.pipe((0, rxjs_1.toArray)()),
182
+ functionalElements: instancesByType.functionalElement.pipe((0, rxjs_1.toArray)()),
183
+ elementKeys: instancesByType.element.pipe((0, rxjs_1.toArray)()),
184
+ }).pipe((0, rxjs_1.mergeMap)(({ groupInformationElementKeys, geometricElementKeys, functionalElements, elementKeys }) => {
185
+ const hasFunctionalElements = !!functionalElements.length;
186
+ if (!groupInformationElementKeys.length && !geometricElementKeys.length && !elementKeys.length && !hasFunctionalElements) {
187
+ return rxjs_1.EMPTY;
188
+ }
189
+ const bindings = [];
190
+ const query = `WITH RECURSIVE
191
+ ${hasFunctionalElements ? this.getHilitedFunctionalElementsQuery(functionalElements, bindings) : ""}
192
+ GroupMembers (ECInstanceId, ECClassId) AS (
193
+ SELECT TargetECInstanceId, TargetECClassId FROM BisCore.ElementGroupsMembers
194
+ WHERE ${(0, ECSqlCore_1.formIdBindings)("SourceECInstanceId", groupInformationElementKeys, bindings)}
195
+ ),
196
+ GroupGeometricElements (ECInstanceId, ECClassId) AS (
197
+ SELECT ECInstanceId, ECClassId FROM GroupMembers
198
+ UNION ALL
199
+ SELECT r.ECInstanceId, r.ECClassId FROM GroupGeometricElements s
200
+ JOIN BisCore.Element r ON r.Parent.Id = s.ECInstanceId
201
+ ),
202
+ ElementGeometricElements (ECInstanceId, ECClassId) AS (
203
+ SELECT ECInstanceId, ECClassId FROM BisCore.Element WHERE ${(0, ECSqlCore_1.formIdBindings)("ECInstanceId", elementKeys, bindings)}
204
+ UNION ALL
205
+ SELECT r.ECInstanceId, r.ECClassId FROM ElementGeometricElements s
206
+ JOIN BisCore.Element r ON r.Parent.Id = s.ECInstanceId
207
+ ),
208
+ GeometricElementGeometricElements (ECInstanceId, ECClassId) AS (
209
+ SELECT ECInstanceId, ECClassId FROM BisCore.GeometricElement WHERE ${(0, ECSqlCore_1.formIdBindings)("ECInstanceId", geometricElementKeys, bindings)}
210
+ UNION ALL
211
+ SELECT r.ECInstanceId, r.ECClassId FROM GeometricElementGeometricElements s
212
+ JOIN BisCore.Element r ON r.Parent.Id = s.ECInstanceId
213
+ )
214
+ ${hasFunctionalElements
215
+ ? `
216
+ SELECT ECInstanceId FROM FunctionalElementChildGeometricElements WHERE ECClassId IS (BisCore.GeometricElement)
217
+ UNION
218
+ `
219
+ : ``}
220
+ SELECT ECInstanceId FROM GeometricElementGeometricElements WHERE ECClassId IS (BisCore.GeometricElement)
221
+ UNION
222
+ SELECT ECInstanceId FROM GroupGeometricElements WHERE ECClassId IS (BisCore.GeometricElement)
223
+ UNION
224
+ SELECT ECInstanceId FROM ElementGeometricElements WHERE ECClassId IS (BisCore.GeometricElement);`;
225
+ return (0, rxjs_1.from)(this._queryExecutor.createQueryReader(query, bindings));
226
+ }), (0, rxjs_1.map)((row) => row.ECInstanceId));
227
+ }
228
+ getHilitedFunctionalElementsQuery(functionalElements, bindings) {
229
+ return `ChildFunctionalElements (ECInstanceId, ECClassId) AS (
230
+ SELECT ECInstanceId, ECClassId FROM Functional.FunctionalElement WHERE ${(0, ECSqlCore_1.formIdBindings)("ECInstanceId", functionalElements, bindings)}
231
+ UNION ALL
232
+ SELECT r.ECInstanceId, r.ECClassId FROM ChildFunctionalElements s
233
+ JOIN Functional.FunctionalElement r ON r.Parent.Id = s.ECInstanceId
234
+ ),
235
+ PhysicalElements (ECInstanceId, ECClassId) AS (
236
+ SELECT r.SourceECInstanceId, r.SourceECClassId FROM ChildFunctionalElements s
237
+ JOIN Functional.PhysicalElementFulfillsFunction r ON r.TargetECInstanceId = s.ECInstanceId
238
+ ),
239
+ DrawingGraphicElements (ECInstanceId, ECClassId) AS (
240
+ SELECT r.SourceECInstanceId, r.SourceECClassId FROM ChildFunctionalElements s
241
+ JOIN Functional.DrawingGraphicRepresentsFunctionalElement r ON r.TargetECInstanceId = s.ECInstanceId
242
+ ),
243
+ PhysicalElementGeometricElements (ECInstanceId, ECClassId) AS (
244
+ SELECT ECInstanceId, ECClassId FROM PhysicalElements
245
+ UNION ALL
246
+ SELECT r.ECInstanceId, r.ECClassId FROM PhysicalElementGeometricElements s
247
+ JOIN BisCore.Element r ON r.Parent.Id = s.ECInstanceId
248
+ ),
249
+ DrawingGraphicElementGeometricElements (ECInstanceId, ECClassId) AS (
250
+ SELECT ECInstanceId, ECClassId FROM DrawingGraphicElements
251
+ UNION ALL
252
+ SELECT r.ECInstanceId, r.ECClassId FROM DrawingGraphicElementGeometricElements s
253
+ JOIN BisCore.Element r ON r.Parent.Id = s.ECInstanceId
254
+ ),
255
+ FunctionalElementChildGeometricElements (ECInstanceId, ECClassId) AS (
256
+ SELECT ECInstanceId, ECClassId FROM PhysicalElementGeometricElements
257
+ UNION
258
+ SELECT ECInstanceId, ECClassId FROM DrawingGraphicElementGeometricElements
259
+ ),`;
260
+ }
261
+ async getClass(fullClassName) {
262
+ const { schemaName, className } = (0, ECMetadata_1.parseFullClassName)(fullClassName);
263
+ const schema = await this._metadataProvider.getSchema(schemaName);
264
+ if (!schema) {
265
+ return undefined;
266
+ }
267
+ const schemaClass = await schema.getClass(className);
268
+ if (!schemaClass) {
269
+ return undefined;
270
+ }
271
+ return schemaClass;
272
+ }
273
+ }
274
+ const INSTANCE_TYPES = [
275
+ "subject",
276
+ "model",
277
+ "category",
278
+ "subCategory",
279
+ "functionalElement",
280
+ "groupInformationElement",
281
+ "geometricElement",
282
+ "element",
283
+ "unknown",
284
+ ];
285
+ function unique() {
286
+ return function (obs) {
287
+ return obs.pipe((0, rxjs_1.scan)((acc, val) => {
288
+ if (acc.set.has(val)) {
289
+ delete acc.val;
290
+ return acc;
291
+ }
292
+ acc.set.add(val);
293
+ acc.val = val;
294
+ return acc;
295
+ }, { set: new Set() }), (0, rxjs_1.map)(({ val }) => val), (0, rxjs_1.filter)((x) => !!x));
296
+ };
297
+ }
298
+ //# sourceMappingURL=HiliteSetProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HiliteSetProvider.js","sourceRoot":"","sources":["../../../src/unified-selection/HiliteSetProvider.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\n/** @packageDocumentation\n * @module UnifiedSelection\n */\n\nimport { EMPTY, filter, forkJoin, from, map, merge, mergeMap, Observable, scan, shareReplay, Subject, toArray } from \"rxjs\";\nimport { eachValueFrom } from \"rxjs-for-await\";\nimport { ECClass, IMetadataProvider, parseFullClassName } from \"./queries/ECMetadata\";\nimport { ECSqlBinding, formIdBindings, IECSqlQueryExecutor } from \"./queries/ECSqlCore\";\nimport { SelectableInstanceKey, Selectables } from \"./Selectable\";\n\nconst HILITE_SET_EMIT_FREQUENCY = 20;\n\n/**\n * A set of model, subcategory and element ids that can be used for specifying hilite.\n * @see https://www.itwinjs.org/reference/core-frontend/selectionset/hiliteset/\n * @beta\n */\nexport interface HiliteSet {\n models: string[];\n subCategories: string[];\n elements: string[];\n}\n\n/**\n * Props for creating a `HiliteSetProvider` instance.\n * @internal Not exported through barrel, but used in public API as an argument. May be supplemented with optional attributes any time.\n */\nexport interface HiliteSetProviderProps {\n queryExecutor: IECSqlQueryExecutor;\n metadataProvider: IMetadataProvider;\n}\n\n/**\n * Defines return value of `createHiliteSetProvider`.\n *\n * @beta Used in public API as a return value. Not expected to be created / extended by package\n * consumers, may be supplemented with required attributes any time.\n */\nexport interface HiliteSetProvider {\n /** Get the current hilite set iterator for the specified imodel */\n getHiliteSet(props: { selectables: Selectables }): AsyncIterableIterator<HiliteSet>;\n}\n\n/**\n * Creates a hilite set provider that returns a `HiliteSet` for given selectables.\n * @beta\n */\nexport function createHiliteSetProvider(props: HiliteSetProviderProps): HiliteSetProvider {\n return new HiliteSetProviderImpl(props);\n}\n\nclass HiliteSetProviderImpl implements HiliteSetProvider {\n private _queryExecutor: IECSqlQueryExecutor;\n private _metadataProvider: IMetadataProvider;\n // Map between a class name and its type\n private _classRelationCache: Map<string, InstanceIdType | Promise<InstanceIdType>>;\n\n constructor(props: HiliteSetProviderProps) {\n this._queryExecutor = props.queryExecutor;\n this._metadataProvider = props.metadataProvider;\n this._classRelationCache = new Map();\n }\n\n /**\n * Get hilite set iterator for supplied `Selectables`.\n */\n public getHiliteSet(props: { selectables: Selectables }): AsyncIterableIterator<HiliteSet> {\n const obs = this.getHiliteSetObservable(props);\n return eachValueFrom(obs);\n }\n\n /**\n * Returns a \"hot\" observable of hilite sets.\n */\n private getHiliteSetObservable({ selectables }: { selectables: Selectables }): Subject<HiliteSet> {\n const instancesByType = this.getInstancesByType(selectables);\n const observables: { [key in keyof HiliteSet]?: Observable<string> } = {\n models: this.getHilitedModels(instancesByType),\n subCategories: this.getHilitedSubCategories(instancesByType),\n elements: this.getHilitedElements(instancesByType),\n };\n\n let hiliteSet: HiliteSet = { models: [], subCategories: [], elements: [] };\n let lastEmitTime = performance.now();\n const subject = new Subject<HiliteSet>();\n const subscriptions = ([\"models\", \"subCategories\", \"elements\"] as const).map((key) =>\n observables[key]!.subscribe({\n next(val) {\n hiliteSet[key].push(val);\n if (performance.now() - lastEmitTime < HILITE_SET_EMIT_FREQUENCY) {\n return;\n }\n subject.next(hiliteSet);\n hiliteSet = { models: [], subCategories: [], elements: [] };\n lastEmitTime = performance.now();\n },\n complete() {\n observables[key] = undefined;\n if (observables.models || observables.subCategories || observables.elements) {\n return;\n }\n // Emit last batch before completing the observable.\n if (hiliteSet.elements.length || hiliteSet.models.length || hiliteSet.subCategories.length) {\n subject.next(hiliteSet);\n }\n subject.complete();\n },\n error(err) {\n subscriptions.forEach((x) => x.unsubscribe());\n subscriptions.length = 0;\n subject.error(err);\n },\n }),\n );\n\n return subject;\n }\n\n private async getType(key: SelectableInstanceKey): Promise<InstanceIdType> {\n const cachedType = this._classRelationCache.get(key.className.replace(\".\", \":\"));\n if (cachedType) {\n return cachedType;\n }\n\n const promise = this.getTypeImpl(key).then((res) => {\n // Update the cache with the result of the promise.\n this._classRelationCache.set(key.className, res);\n return res;\n });\n\n // Add the promise to cache to prevent `getTypeImpl` being called multiple times.\n this._classRelationCache.set(key.className, promise);\n return promise;\n }\n\n private async getTypeImpl(key: SelectableInstanceKey): Promise<InstanceIdType> {\n const keyClass = await this.getClass(key.className);\n return (\n (keyClass &&\n ((await this.checkType(keyClass, \"BisCore\", \"Subject\", \"subject\")) ??\n (await this.checkType(keyClass, \"BisCore\", \"Model\", \"model\")) ??\n (await this.checkType(keyClass, \"BisCore\", \"Category\", \"category\")) ??\n (await this.checkType(keyClass, \"BisCore\", \"SubCategory\", \"subCategory\")) ??\n (await this.checkType(keyClass, \"Functional\", \"FunctionalElement\", \"functionalElement\")) ??\n (await this.checkType(keyClass, \"BisCore\", \"GroupInformationElement\", \"groupInformationElement\")) ??\n (await this.checkType(keyClass, \"BisCore\", \"GeometricElement\", \"geometricElement\")) ??\n (await this.checkType(keyClass, \"BisCore\", \"Element\", \"element\")))) ??\n \"unknown\"\n );\n }\n\n private async checkType(keyClass: ECClass, schemaName: string, className: string, type: InstanceIdType) {\n return (await keyClass.is(className, schemaName)) ? type : undefined;\n }\n\n private getInstancesByType(selectables: Selectables): InstancesByType {\n const keyTypeObs = merge(\n from(selectables.custom.values()).pipe(mergeMap((selectable) => selectable.loadInstanceKeys())),\n from(selectables.instanceKeys).pipe(mergeMap(([className, idSet]) => from(idSet).pipe(map((id) => ({ className, id }))))),\n ).pipe(\n // Get types for each instance key\n mergeMap((instanceKey) => from(this.getType(instanceKey)).pipe(map((instanceIdType) => ({ instanceId: instanceKey.id, instanceIdType })))),\n // Cache the results\n shareReplay(),\n );\n\n return Object.fromEntries(\n INSTANCE_TYPES.map((type) => [\n type,\n keyTypeObs.pipe(\n filter(({ instanceIdType }) => instanceIdType === type),\n map(({ instanceId }) => instanceId),\n unique(),\n ),\n ]),\n ) as InstancesByType;\n }\n\n private getHilitedModels(instancesByType: InstancesByType): Observable<string> {\n return forkJoin({\n modelKeys: instancesByType.model.pipe(toArray()),\n subjectKeys: instancesByType.subject.pipe(toArray()),\n }).pipe(\n mergeMap(({ modelKeys, subjectKeys }) => {\n if (!modelKeys.length && !subjectKeys.length) {\n return EMPTY;\n }\n\n const bindings: ECSqlBinding[] = [];\n const query = `WITH RECURSIVE\n ChildSubjects (ECInstanceId, JsonProperties) AS (\n SELECT ECInstanceId, JsonProperties FROM BisCore.Subject WHERE ${formIdBindings(\"ECInstanceId\", subjectKeys, bindings)}\n UNION ALL\n SELECT r.ECInstanceId, r.JsonProperties FROM ChildSubjects s\n JOIN BisCore.Subject r ON r.Parent.Id = s.ECInstanceId\n ),\n Models (ECInstanceId) AS (\n SELECT s.ECInstanceId as \\`ECInstanceId\\` FROM BisCore.Model s\n WHERE ${formIdBindings(\"ECInstanceId\", modelKeys, bindings)}\n )\n SELECT r.ECInstanceId as \\`ECInstanceId\\` FROM ChildSubjects s\n JOIN BisCore.PhysicalPartition r\n ON r.Parent.Id = s.ECInstanceId\n OR json_extract(s.JsonProperties,'$.Subject.Model.TargetPartition') = printf('0x%x', r.ECInstanceId)\n UNION\n SELECT ECInstanceId FROM Models;`;\n return from(this._queryExecutor.createQueryReader(query, bindings));\n }),\n map((row) => row.ECInstanceId),\n );\n }\n\n private getHilitedSubCategories(instancesByType: InstancesByType): Observable<string> {\n return forkJoin({\n subCategoryKeys: instancesByType.subCategory.pipe(toArray()),\n categoryKeys: instancesByType.category.pipe(toArray()),\n }).pipe(\n mergeMap(({ subCategoryKeys, categoryKeys }) => {\n if (!subCategoryKeys.length && !categoryKeys.length) {\n return EMPTY;\n }\n\n const bindings: ECSqlBinding[] = [];\n const query = `WITH\n CategorySubCategories (ECInstanceId) AS (\n SELECT r.ECInstanceId as \\`ECInstanceId\\` FROM BisCore.Category s\n JOIN BisCore.SubCategory r ON r.Parent.Id = s.ECInstanceId\n WHERE ${formIdBindings(\"s.ECInstanceId\", categoryKeys, bindings)}\n ),\n SubCategories (ECInstanceId) AS (\n SELECT s.ECInstanceId as \\`ECInstanceId\\` FROM BisCore.SubCategory s\n WHERE ${formIdBindings(\"s.ECInstanceId\", subCategoryKeys, bindings)}\n )\n SELECT ECInstanceId FROM CategorySubCategories\n UNION\n SELECT ECInstanceId FROM SubCategories;`;\n return from(this._queryExecutor.createQueryReader(query, bindings));\n }),\n map((row) => row.ECInstanceId),\n );\n }\n\n private getHilitedElements(instancesByType: InstancesByType): Observable<string> {\n return forkJoin({\n groupInformationElementKeys: instancesByType.groupInformationElement.pipe(toArray()),\n geometricElementKeys: instancesByType.geometricElement.pipe(toArray()),\n functionalElements: instancesByType.functionalElement.pipe(toArray()),\n elementKeys: instancesByType.element.pipe(toArray()),\n }).pipe(\n mergeMap(({ groupInformationElementKeys, geometricElementKeys, functionalElements, elementKeys }) => {\n const hasFunctionalElements = !!functionalElements.length;\n if (!groupInformationElementKeys.length && !geometricElementKeys.length && !elementKeys.length && !hasFunctionalElements) {\n return EMPTY;\n }\n\n const bindings: ECSqlBinding[] = [];\n const query = `WITH RECURSIVE\n ${hasFunctionalElements ? this.getHilitedFunctionalElementsQuery(functionalElements, bindings) : \"\"}\n GroupMembers (ECInstanceId, ECClassId) AS (\n SELECT TargetECInstanceId, TargetECClassId FROM BisCore.ElementGroupsMembers\n WHERE ${formIdBindings(\"SourceECInstanceId\", groupInformationElementKeys, bindings)}\n ),\n GroupGeometricElements (ECInstanceId, ECClassId) AS (\n SELECT ECInstanceId, ECClassId FROM GroupMembers\n UNION ALL\n SELECT r.ECInstanceId, r.ECClassId FROM GroupGeometricElements s\n JOIN BisCore.Element r ON r.Parent.Id = s.ECInstanceId\n ),\n ElementGeometricElements (ECInstanceId, ECClassId) AS (\n SELECT ECInstanceId, ECClassId FROM BisCore.Element WHERE ${formIdBindings(\"ECInstanceId\", elementKeys, bindings)}\n UNION ALL\n SELECT r.ECInstanceId, r.ECClassId FROM ElementGeometricElements s\n JOIN BisCore.Element r ON r.Parent.Id = s.ECInstanceId\n ),\n GeometricElementGeometricElements (ECInstanceId, ECClassId) AS (\n SELECT ECInstanceId, ECClassId FROM BisCore.GeometricElement WHERE ${formIdBindings(\"ECInstanceId\", geometricElementKeys, bindings)}\n UNION ALL\n SELECT r.ECInstanceId, r.ECClassId FROM GeometricElementGeometricElements s\n JOIN BisCore.Element r ON r.Parent.Id = s.ECInstanceId\n )\n ${\n hasFunctionalElements\n ? `\n SELECT ECInstanceId FROM FunctionalElementChildGeometricElements WHERE ECClassId IS (BisCore.GeometricElement)\n UNION\n `\n : ``\n }\n SELECT ECInstanceId FROM GeometricElementGeometricElements WHERE ECClassId IS (BisCore.GeometricElement)\n UNION\n SELECT ECInstanceId FROM GroupGeometricElements WHERE ECClassId IS (BisCore.GeometricElement)\n UNION\n SELECT ECInstanceId FROM ElementGeometricElements WHERE ECClassId IS (BisCore.GeometricElement);`;\n return from(this._queryExecutor.createQueryReader(query, bindings));\n }),\n map((row) => row.ECInstanceId as string),\n );\n }\n\n private getHilitedFunctionalElementsQuery(functionalElements: string[], bindings: ECSqlBinding[]): string {\n return `ChildFunctionalElements (ECInstanceId, ECClassId) AS (\n SELECT ECInstanceId, ECClassId FROM Functional.FunctionalElement WHERE ${formIdBindings(\"ECInstanceId\", functionalElements, bindings)}\n UNION ALL\n SELECT r.ECInstanceId, r.ECClassId FROM ChildFunctionalElements s\n JOIN Functional.FunctionalElement r ON r.Parent.Id = s.ECInstanceId\n ),\n PhysicalElements (ECInstanceId, ECClassId) AS (\n SELECT r.SourceECInstanceId, r.SourceECClassId FROM ChildFunctionalElements s\n JOIN Functional.PhysicalElementFulfillsFunction r ON r.TargetECInstanceId = s.ECInstanceId\n ),\n DrawingGraphicElements (ECInstanceId, ECClassId) AS (\n SELECT r.SourceECInstanceId, r.SourceECClassId FROM ChildFunctionalElements s\n JOIN Functional.DrawingGraphicRepresentsFunctionalElement r ON r.TargetECInstanceId = s.ECInstanceId\n ),\n PhysicalElementGeometricElements (ECInstanceId, ECClassId) AS (\n SELECT ECInstanceId, ECClassId FROM PhysicalElements\n UNION ALL\n SELECT r.ECInstanceId, r.ECClassId FROM PhysicalElementGeometricElements s\n JOIN BisCore.Element r ON r.Parent.Id = s.ECInstanceId\n ),\n DrawingGraphicElementGeometricElements (ECInstanceId, ECClassId) AS (\n SELECT ECInstanceId, ECClassId FROM DrawingGraphicElements\n UNION ALL\n SELECT r.ECInstanceId, r.ECClassId FROM DrawingGraphicElementGeometricElements s\n JOIN BisCore.Element r ON r.Parent.Id = s.ECInstanceId\n ),\n FunctionalElementChildGeometricElements (ECInstanceId, ECClassId) AS (\n SELECT ECInstanceId, ECClassId FROM PhysicalElementGeometricElements\n UNION\n SELECT ECInstanceId, ECClassId FROM DrawingGraphicElementGeometricElements\n ),`;\n }\n\n private async getClass(fullClassName: string): Promise<ECClass | undefined> {\n const { schemaName, className } = parseFullClassName(fullClassName);\n const schema = await this._metadataProvider.getSchema(schemaName);\n if (!schema) {\n return undefined;\n }\n const schemaClass = await schema.getClass(className);\n if (!schemaClass) {\n return undefined;\n }\n return schemaClass;\n }\n}\n\nconst INSTANCE_TYPES = [\n \"subject\",\n \"model\",\n \"category\",\n \"subCategory\",\n \"functionalElement\",\n \"groupInformationElement\",\n \"geometricElement\",\n \"element\",\n \"unknown\",\n] as const;\n\ntype InstanceIdType = (typeof INSTANCE_TYPES)[number];\n\ntype InstancesByType = {\n [idType in InstanceIdType]: Observable<string>;\n};\n\nfunction unique<T>() {\n return function (obs: Observable<T>): Observable<T> {\n return obs.pipe(\n scan(\n (acc, val) => {\n if (acc.set.has(val)) {\n delete acc.val;\n return acc;\n }\n\n acc.set.add(val);\n acc.val = val;\n return acc;\n },\n { set: new Set<T>() } as { set: Set<T>; val?: T },\n ),\n map(({ val }) => val),\n filter((x): x is T => !!x),\n );\n };\n}\n"]}