@itwin/presentation-frontend 4.0.0-dev.21 → 4.0.0-dev.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/presentation-frontend/ConnectivityInformationProvider.d.ts +29 -29
- package/lib/cjs/presentation-frontend/ConnectivityInformationProvider.js +47 -47
- package/lib/cjs/presentation-frontend/ConnectivityInformationProvider.js.map +1 -1
- package/lib/cjs/presentation-frontend/Diagnostics.d.ts +14 -14
- package/lib/cjs/presentation-frontend/Diagnostics.js +61 -61
- package/lib/cjs/presentation-frontend/Diagnostics.js.map +1 -1
- package/lib/cjs/presentation-frontend/FrontendLoggerCategory.d.ts +12 -12
- package/lib/cjs/presentation-frontend/FrontendLoggerCategory.js +20 -20
- package/lib/cjs/presentation-frontend/FrontendLoggerCategory.js.map +1 -1
- package/lib/cjs/presentation-frontend/IpcRequestsHandler.d.ts +10 -10
- package/lib/cjs/presentation-frontend/IpcRequestsHandler.js +41 -41
- package/lib/cjs/presentation-frontend/IpcRequestsHandler.js.map +1 -1
- package/lib/cjs/presentation-frontend/LocalizationHelper.d.ts +10 -10
- package/lib/cjs/presentation-frontend/LocalizationHelper.js +31 -31
- package/lib/cjs/presentation-frontend/LocalizationHelper.js.map +1 -1
- package/lib/cjs/presentation-frontend/Presentation.d.ts +73 -73
- package/lib/cjs/presentation-frontend/Presentation.js +160 -160
- package/lib/cjs/presentation-frontend/Presentation.js.map +1 -1
- package/lib/cjs/presentation-frontend/PresentationManager.d.ts +186 -186
- package/lib/cjs/presentation-frontend/PresentationManager.js +436 -436
- package/lib/cjs/presentation-frontend/PresentationManager.js.map +1 -1
- package/lib/cjs/presentation-frontend/RulesetManager.d.ts +63 -63
- package/lib/cjs/presentation-frontend/RulesetManager.js +85 -85
- package/lib/cjs/presentation-frontend/RulesetManager.js.map +1 -1
- package/lib/cjs/presentation-frontend/RulesetVariablesManager.d.ts +143 -143
- package/lib/cjs/presentation-frontend/RulesetVariablesManager.js +195 -195
- package/lib/cjs/presentation-frontend/RulesetVariablesManager.js.map +1 -1
- package/lib/cjs/presentation-frontend/StateTracker.d.ts +38 -38
- package/lib/cjs/presentation-frontend/StateTracker.js +129 -129
- package/lib/cjs/presentation-frontend/StateTracker.js.map +1 -1
- package/lib/cjs/presentation-frontend/favorite-properties/FavoritePropertiesManager.d.ts +132 -132
- package/lib/cjs/presentation-frontend/favorite-properties/FavoritePropertiesManager.js +525 -525
- package/lib/cjs/presentation-frontend/favorite-properties/FavoritePropertiesManager.js.map +1 -1
- package/lib/cjs/presentation-frontend/favorite-properties/FavoritePropertiesStorage.d.ts +111 -111
- package/lib/cjs/presentation-frontend/favorite-properties/FavoritePropertiesStorage.js +293 -293
- package/lib/cjs/presentation-frontend/favorite-properties/FavoritePropertiesStorage.js.map +1 -1
- package/lib/cjs/presentation-frontend/selection/HiliteRules.json +172 -172
- package/lib/cjs/presentation-frontend/selection/HiliteSetProvider.d.ts +52 -52
- package/lib/cjs/presentation-frontend/selection/HiliteSetProvider.js +97 -97
- package/lib/cjs/presentation-frontend/selection/HiliteSetProvider.js.map +1 -1
- package/lib/cjs/presentation-frontend/selection/ISelectionProvider.d.ts +19 -19
- package/lib/cjs/presentation-frontend/selection/ISelectionProvider.js +9 -9
- package/lib/cjs/presentation-frontend/selection/ISelectionProvider.js.map +1 -1
- package/lib/cjs/presentation-frontend/selection/SelectionChangeEvent.d.ts +52 -52
- package/lib/cjs/presentation-frontend/selection/SelectionChangeEvent.js +33 -33
- package/lib/cjs/presentation-frontend/selection/SelectionChangeEvent.js.map +1 -1
- package/lib/cjs/presentation-frontend/selection/SelectionHandler.d.ts +103 -103
- package/lib/cjs/presentation-frontend/selection/SelectionHandler.js +108 -108
- package/lib/cjs/presentation-frontend/selection/SelectionHandler.js.map +1 -1
- package/lib/cjs/presentation-frontend/selection/SelectionHelper.d.ts +17 -17
- package/lib/cjs/presentation-frontend/selection/SelectionHelper.js +40 -40
- package/lib/cjs/presentation-frontend/selection/SelectionHelper.js.map +1 -1
- package/lib/cjs/presentation-frontend/selection/SelectionManager.d.ts +139 -139
- package/lib/cjs/presentation-frontend/selection/SelectionManager.js +414 -414
- package/lib/cjs/presentation-frontend/selection/SelectionManager.js.map +1 -1
- package/lib/cjs/presentation-frontend/selection/SelectionScopesManager.d.ts +59 -59
- package/lib/cjs/presentation-frontend/selection/SelectionScopesManager.js +91 -91
- package/lib/cjs/presentation-frontend/selection/SelectionScopesManager.js.map +1 -1
- package/lib/cjs/presentation-frontend.d.ts +34 -34
- package/lib/cjs/presentation-frontend.js +50 -50
- package/lib/cjs/presentation-frontend.js.map +1 -1
- package/lib/esm/presentation-frontend/ConnectivityInformationProvider.d.ts +29 -29
- package/lib/esm/presentation-frontend/ConnectivityInformationProvider.js +43 -43
- package/lib/esm/presentation-frontend/ConnectivityInformationProvider.js.map +1 -1
- package/lib/esm/presentation-frontend/Diagnostics.d.ts +14 -14
- package/lib/esm/presentation-frontend/Diagnostics.js +56 -56
- package/lib/esm/presentation-frontend/Diagnostics.js.map +1 -1
- package/lib/esm/presentation-frontend/FrontendLoggerCategory.d.ts +12 -12
- package/lib/esm/presentation-frontend/FrontendLoggerCategory.js +17 -17
- package/lib/esm/presentation-frontend/FrontendLoggerCategory.js.map +1 -1
- package/lib/esm/presentation-frontend/IpcRequestsHandler.d.ts +10 -10
- package/lib/esm/presentation-frontend/IpcRequestsHandler.js +37 -37
- package/lib/esm/presentation-frontend/IpcRequestsHandler.js.map +1 -1
- package/lib/esm/presentation-frontend/LocalizationHelper.d.ts +10 -10
- package/lib/esm/presentation-frontend/LocalizationHelper.js +27 -27
- package/lib/esm/presentation-frontend/LocalizationHelper.js.map +1 -1
- package/lib/esm/presentation-frontend/Presentation.d.ts +73 -73
- package/lib/esm/presentation-frontend/Presentation.js +156 -156
- package/lib/esm/presentation-frontend/Presentation.js.map +1 -1
- package/lib/esm/presentation-frontend/PresentationManager.d.ts +186 -186
- package/lib/esm/presentation-frontend/PresentationManager.js +431 -431
- package/lib/esm/presentation-frontend/PresentationManager.js.map +1 -1
- package/lib/esm/presentation-frontend/RulesetManager.d.ts +63 -63
- package/lib/esm/presentation-frontend/RulesetManager.js +81 -81
- package/lib/esm/presentation-frontend/RulesetManager.js.map +1 -1
- package/lib/esm/presentation-frontend/RulesetVariablesManager.d.ts +143 -143
- package/lib/esm/presentation-frontend/RulesetVariablesManager.js +191 -191
- package/lib/esm/presentation-frontend/RulesetVariablesManager.js.map +1 -1
- package/lib/esm/presentation-frontend/StateTracker.d.ts +38 -38
- package/lib/esm/presentation-frontend/StateTracker.js +125 -125
- package/lib/esm/presentation-frontend/StateTracker.js.map +1 -1
- package/lib/esm/presentation-frontend/favorite-properties/FavoritePropertiesManager.d.ts +132 -132
- package/lib/esm/presentation-frontend/favorite-properties/FavoritePropertiesManager.js +519 -519
- package/lib/esm/presentation-frontend/favorite-properties/FavoritePropertiesManager.js.map +1 -1
- package/lib/esm/presentation-frontend/favorite-properties/FavoritePropertiesStorage.d.ts +111 -111
- package/lib/esm/presentation-frontend/favorite-properties/FavoritePropertiesStorage.js +285 -285
- package/lib/esm/presentation-frontend/favorite-properties/FavoritePropertiesStorage.js.map +1 -1
- package/lib/esm/presentation-frontend/selection/HiliteRules.json +172 -172
- package/lib/esm/presentation-frontend/selection/HiliteSetProvider.d.ts +52 -52
- package/lib/esm/presentation-frontend/selection/HiliteSetProvider.js +93 -93
- package/lib/esm/presentation-frontend/selection/HiliteSetProvider.js.map +1 -1
- package/lib/esm/presentation-frontend/selection/ISelectionProvider.d.ts +19 -19
- package/lib/esm/presentation-frontend/selection/ISelectionProvider.js +8 -8
- package/lib/esm/presentation-frontend/selection/ISelectionProvider.js.map +1 -1
- package/lib/esm/presentation-frontend/selection/SelectionChangeEvent.d.ts +52 -52
- package/lib/esm/presentation-frontend/selection/SelectionChangeEvent.js +29 -29
- package/lib/esm/presentation-frontend/selection/SelectionChangeEvent.js.map +1 -1
- package/lib/esm/presentation-frontend/selection/SelectionHandler.d.ts +103 -103
- package/lib/esm/presentation-frontend/selection/SelectionHandler.js +104 -104
- package/lib/esm/presentation-frontend/selection/SelectionHandler.js.map +1 -1
- package/lib/esm/presentation-frontend/selection/SelectionHelper.d.ts +17 -17
- package/lib/esm/presentation-frontend/selection/SelectionHelper.js +36 -36
- package/lib/esm/presentation-frontend/selection/SelectionHelper.js.map +1 -1
- package/lib/esm/presentation-frontend/selection/SelectionManager.d.ts +139 -139
- package/lib/esm/presentation-frontend/selection/SelectionManager.js +409 -409
- package/lib/esm/presentation-frontend/selection/SelectionManager.js.map +1 -1
- package/lib/esm/presentation-frontend/selection/SelectionScopesManager.d.ts +59 -59
- package/lib/esm/presentation-frontend/selection/SelectionScopesManager.js +85 -85
- package/lib/esm/presentation-frontend/selection/SelectionScopesManager.js.map +1 -1
- package/lib/esm/presentation-frontend.d.ts +34 -34
- package/lib/esm/presentation-frontend.js +38 -38
- package/lib/esm/presentation-frontend.js.map +1 -1
- package/package.json +14 -14
|
@@ -1,526 +1,526 @@
|
|
|
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
|
-
/** @packageDocumentation
|
|
7
|
-
* @module Core
|
|
8
|
-
*/
|
|
9
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.createFieldOrderInfos = exports.getFieldInfos = exports.FavoritePropertiesManager = exports.FavoritePropertiesScope = void 0;
|
|
11
|
-
const core_bentley_1 = require("@itwin/core-bentley");
|
|
12
|
-
const core_common_1 = require("@itwin/core-common");
|
|
13
|
-
/**
|
|
14
|
-
* Scopes that favorite properties can be stored in.
|
|
15
|
-
* @public
|
|
16
|
-
*/
|
|
17
|
-
var FavoritePropertiesScope;
|
|
18
|
-
(function (FavoritePropertiesScope) {
|
|
19
|
-
FavoritePropertiesScope[FavoritePropertiesScope["Global"] = 0] = "Global";
|
|
20
|
-
FavoritePropertiesScope[FavoritePropertiesScope["ITwin"] = 1] = "ITwin";
|
|
21
|
-
FavoritePropertiesScope[FavoritePropertiesScope["IModel"] = 2] = "IModel";
|
|
22
|
-
})(FavoritePropertiesScope = exports.FavoritePropertiesScope || (exports.FavoritePropertiesScope = {}));
|
|
23
|
-
/**
|
|
24
|
-
* The favorite property manager which lets to store favorite properties
|
|
25
|
-
* and check if field contains favorite properties.
|
|
26
|
-
*
|
|
27
|
-
* @public
|
|
28
|
-
*/
|
|
29
|
-
class FavoritePropertiesManager {
|
|
30
|
-
constructor(props) {
|
|
31
|
-
/** Event raised after favorite properties have changed. */
|
|
32
|
-
this.onFavoritesChanged = new core_bentley_1.BeEvent();
|
|
33
|
-
/**
|
|
34
|
-
* Initialize favorite properties for the provided IModelConnection.
|
|
35
|
-
*/
|
|
36
|
-
this.initializeConnection = async (imodel) => {
|
|
37
|
-
const imodelId = imodel.iModelId;
|
|
38
|
-
const iTwinId = imodel.iTwinId;
|
|
39
|
-
if (this._globalProperties === undefined)
|
|
40
|
-
this._globalProperties = await this._storage.loadProperties() || new Set();
|
|
41
|
-
if (!this._iTwinProperties.has(iTwinId)) {
|
|
42
|
-
const iTwinProperties = await this._storage.loadProperties(iTwinId) || new Set();
|
|
43
|
-
this._iTwinProperties.set(iTwinId, iTwinProperties);
|
|
44
|
-
}
|
|
45
|
-
if (!this._imodelProperties.has(getiModelInfo(iTwinId, imodelId))) {
|
|
46
|
-
const imodelProperties = await this._storage.loadProperties(iTwinId, imodelId) || new Set();
|
|
47
|
-
this._imodelProperties.set(getiModelInfo(iTwinId, imodelId), imodelProperties);
|
|
48
|
-
}
|
|
49
|
-
const propertiesOrder = await this._storage.loadPropertiesOrder(iTwinId, imodelId) || [];
|
|
50
|
-
this._propertiesOrder.set(getiModelInfo(iTwinId, imodelId), propertiesOrder);
|
|
51
|
-
await this._adjustPropertyOrderInfos(iTwinId, imodelId);
|
|
52
|
-
};
|
|
53
|
-
/**
|
|
54
|
-
* Function that removes order information of properties that are no longer
|
|
55
|
-
* favorited and adds missing order information for favorited properties.
|
|
56
|
-
*/
|
|
57
|
-
this._adjustPropertyOrderInfos = async (iTwinId, imodelId) => {
|
|
58
|
-
const propertiesOrder = this._propertiesOrder.get(getiModelInfo(iTwinId, imodelId));
|
|
59
|
-
const globalProperties = this._globalProperties;
|
|
60
|
-
const iTwinProperties = this._iTwinProperties.get(iTwinId);
|
|
61
|
-
const imodelProperties = this._imodelProperties.get(getiModelInfo(iTwinId, imodelId));
|
|
62
|
-
// favorite property infos that need to be added to the propertiesOrder array
|
|
63
|
-
const infosToAdd = new Set([...globalProperties, ...iTwinProperties, ...imodelProperties]);
|
|
64
|
-
for (let i = propertiesOrder.length - 1; i >= 0; i--) {
|
|
65
|
-
if (infosToAdd.has(propertiesOrder[i].name))
|
|
66
|
-
infosToAdd.delete(propertiesOrder[i].name);
|
|
67
|
-
else
|
|
68
|
-
propertiesOrder.splice(i, 1);
|
|
69
|
-
}
|
|
70
|
-
infosToAdd.forEach((info) => propertiesOrder.push({
|
|
71
|
-
name: info,
|
|
72
|
-
parentClassName: getPropertyClassName(info),
|
|
73
|
-
orderedTimestamp: new Date(),
|
|
74
|
-
priority: 0,
|
|
75
|
-
}));
|
|
76
|
-
let priority = propertiesOrder.length;
|
|
77
|
-
propertiesOrder.forEach((oi) => oi.priority = priority--);
|
|
78
|
-
};
|
|
79
|
-
/**
|
|
80
|
-
* Sorts an array of fields with respect to favorite property order.
|
|
81
|
-
* Non-favorited fields get sorted by their default priority and always have lower priority than favorited fields.
|
|
82
|
-
* @param imodel IModelConnection.
|
|
83
|
-
* @param fields Array of Field's that needs to be sorted.
|
|
84
|
-
* @note `initializeConnection` must be called with the `imodel` before calling this function.
|
|
85
|
-
*/
|
|
86
|
-
this.sortFields = (imodel, fields) => {
|
|
87
|
-
this.validateInitialization(imodel);
|
|
88
|
-
const iTwinId = imodel.iTwinId;
|
|
89
|
-
const imodelId = imodel.iModelId;
|
|
90
|
-
const fieldPriority = new Map();
|
|
91
|
-
fields.forEach((field) => fieldPriority.set(field, this.getFieldPriority(field, iTwinId, imodelId)));
|
|
92
|
-
const sortFunction = (left, right) => {
|
|
93
|
-
const lp = fieldPriority.get(left);
|
|
94
|
-
const rp = fieldPriority.get(right);
|
|
95
|
-
return lp < rp ? 1 :
|
|
96
|
-
lp > rp ? -1 :
|
|
97
|
-
left.priority < right.priority ? 1 : // if favorite fields have equal priorities, sort by field priority
|
|
98
|
-
left.priority > right.priority ? -1 :
|
|
99
|
-
left.name.localeCompare(right.name);
|
|
100
|
-
};
|
|
101
|
-
return fields.sort(sortFunction);
|
|
102
|
-
};
|
|
103
|
-
this._getBaseClassesByClass = async (imodel, neededClasses) => {
|
|
104
|
-
const iTwinId = imodel.iTwinId;
|
|
105
|
-
const imodelId = imodel.iModelId;
|
|
106
|
-
const imodelInfo = getiModelInfo(iTwinId, imodelId);
|
|
107
|
-
let baseClasses;
|
|
108
|
-
if (this._imodelBaseClassesByClass.has(imodelInfo))
|
|
109
|
-
baseClasses = this._imodelBaseClassesByClass.get(imodelInfo);
|
|
110
|
-
else
|
|
111
|
-
this._imodelBaseClassesByClass.set(imodelInfo, baseClasses = {});
|
|
112
|
-
const missingClasses = new Set();
|
|
113
|
-
neededClasses.forEach((className) => {
|
|
114
|
-
if (!baseClasses.hasOwnProperty(className))
|
|
115
|
-
missingClasses.add(className);
|
|
116
|
-
});
|
|
117
|
-
if (missingClasses.size === 0)
|
|
118
|
-
return baseClasses;
|
|
119
|
-
const query = `
|
|
120
|
-
SELECT (derivedSchema.Name || ':' || derivedClass.Name) AS "ClassFullName", (baseSchema.Name || ':' || baseClass.Name) AS "BaseClassFullName"
|
|
121
|
-
FROM ECDbMeta.ClassHasAllBaseClasses baseClassRels
|
|
122
|
-
INNER JOIN ECDbMeta.ECClassDef derivedClass ON derivedClass.ECInstanceId = baseClassRels.SourceECInstanceId
|
|
123
|
-
INNER JOIN ECDbMeta.ECSchemaDef derivedSchema ON derivedSchema.ECInstanceId = derivedClass.Schema.Id
|
|
124
|
-
INNER JOIN ECDbMeta.ECClassDef baseClass ON baseClass.ECInstanceId = baseClassRels.TargetECInstanceId
|
|
125
|
-
INNER JOIN ECDbMeta.ECSchemaDef baseSchema ON baseSchema.ECInstanceId = baseClass.Schema.Id
|
|
126
|
-
WHERE (derivedSchema.Name || ':' || derivedClass.Name) IN (${[...missingClasses].map((className) => `'${className}'`).join(",")})`;
|
|
127
|
-
for await (const row of imodel.query(query, undefined, { rowFormat: core_common_1.QueryRowFormat.UseJsPropertyNames })) {
|
|
128
|
-
if (!(row.classFullName in baseClasses))
|
|
129
|
-
baseClasses[row.classFullName] = [];
|
|
130
|
-
baseClasses[row.classFullName].push(row.baseClassFullName);
|
|
131
|
-
}
|
|
132
|
-
return baseClasses;
|
|
133
|
-
};
|
|
134
|
-
this._storage = props.storage;
|
|
135
|
-
this._iTwinProperties = new Map();
|
|
136
|
-
this._imodelProperties = new Map();
|
|
137
|
-
this._propertiesOrder = new Map();
|
|
138
|
-
this._imodelBaseClassesByClass = new Map();
|
|
139
|
-
}
|
|
140
|
-
dispose() {
|
|
141
|
-
// istanbul ignore else
|
|
142
|
-
if ((0, core_bentley_1.isIDisposable)(this._storage))
|
|
143
|
-
this._storage.dispose();
|
|
144
|
-
}
|
|
145
|
-
validateInitialization(imodel) {
|
|
146
|
-
const iTwinId = imodel.iTwinId;
|
|
147
|
-
const imodelId = imodel.iModelId;
|
|
148
|
-
if (!this._imodelProperties.has(getiModelInfo(iTwinId, imodelId)))
|
|
149
|
-
throw Error(`Favorite properties are not initialized for iModel: '${imodelId}', in iTwin: '${iTwinId}'. Call initializeConnection() with an IModelConnection to initialize.`);
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
152
|
-
* Adds favorite properties into a certain scope.
|
|
153
|
-
* @param field Field that contains properties. If field contains multiple properties, all of them will be favorited.
|
|
154
|
-
* @param imodel IModelConnection.
|
|
155
|
-
* @param scope FavoritePropertiesScope to put the favorite properties into.
|
|
156
|
-
* @note `initializeConnection` must be called with the `imodel` before calling this function.
|
|
157
|
-
*/
|
|
158
|
-
async add(field, imodel, scope) {
|
|
159
|
-
this.validateInitialization(imodel);
|
|
160
|
-
const iTwinId = imodel.iTwinId;
|
|
161
|
-
const imodelId = imodel.iModelId;
|
|
162
|
-
let favoriteProperties;
|
|
163
|
-
let saveProperties;
|
|
164
|
-
switch (scope) {
|
|
165
|
-
case FavoritePropertiesScope.Global:
|
|
166
|
-
favoriteProperties = this._globalProperties;
|
|
167
|
-
saveProperties = async (properties) => this._storage.saveProperties(properties);
|
|
168
|
-
break;
|
|
169
|
-
case FavoritePropertiesScope.ITwin:
|
|
170
|
-
favoriteProperties = this._iTwinProperties.get(iTwinId);
|
|
171
|
-
saveProperties = async (properties) => this._storage.saveProperties(properties, iTwinId);
|
|
172
|
-
break;
|
|
173
|
-
default:
|
|
174
|
-
favoriteProperties = this._imodelProperties.get(getiModelInfo(iTwinId, imodelId));
|
|
175
|
-
saveProperties = async (properties) => this._storage.saveProperties(properties, iTwinId, imodelId);
|
|
176
|
-
}
|
|
177
|
-
const countBefore = favoriteProperties.size;
|
|
178
|
-
const fieldInfos = (0, exports.getFieldInfos)(field);
|
|
179
|
-
fieldInfos.forEach((info) => favoriteProperties.add(info));
|
|
180
|
-
if (favoriteProperties.size !== countBefore) {
|
|
181
|
-
const saves = [];
|
|
182
|
-
saves.push(saveProperties(favoriteProperties));
|
|
183
|
-
const propertiesOrder = this._propertiesOrder.get(getiModelInfo(iTwinId, imodelId));
|
|
184
|
-
addOrderInfos(propertiesOrder, (0, exports.createFieldOrderInfos)(field));
|
|
185
|
-
saves.push(this._storage.savePropertiesOrder(propertiesOrder, iTwinId, imodelId));
|
|
186
|
-
await Promise.all(saves);
|
|
187
|
-
this.onFavoritesChanged.raiseEvent();
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* Removes favorite properties from a scope specified and all the more general scopes.
|
|
192
|
-
* @param field Field that contains properties. If field contains multiple properties, all of them will be un-favorited.
|
|
193
|
-
* @param imodel IModelConnection.
|
|
194
|
-
* @param scope FavoritePropertiesScope to remove the favorite properties from. It also removes from more general scopes.
|
|
195
|
-
* @note `initializeConnection` must be called with the `imodel` before calling this function.
|
|
196
|
-
*/
|
|
197
|
-
async remove(field, imodel, scope) {
|
|
198
|
-
this.validateInitialization(imodel);
|
|
199
|
-
const iTwinId = imodel.iTwinId;
|
|
200
|
-
const imodelId = imodel.iModelId;
|
|
201
|
-
const fieldInfos = (0, exports.getFieldInfos)(field);
|
|
202
|
-
const workingScopes = [];
|
|
203
|
-
workingScopes.push({
|
|
204
|
-
properties: this._globalProperties,
|
|
205
|
-
save: async (properties) => this._storage.saveProperties(properties),
|
|
206
|
-
});
|
|
207
|
-
if (scope === FavoritePropertiesScope.ITwin || scope === FavoritePropertiesScope.IModel) {
|
|
208
|
-
workingScopes.push({
|
|
209
|
-
properties: this._iTwinProperties.get(iTwinId),
|
|
210
|
-
save: async (properties) => this._storage.saveProperties(properties, iTwinId),
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
if (scope === FavoritePropertiesScope.IModel) {
|
|
214
|
-
workingScopes.push({
|
|
215
|
-
properties: this._imodelProperties.get(getiModelInfo(iTwinId, imodelId)),
|
|
216
|
-
save: async (properties) => this._storage.saveProperties(properties, iTwinId, imodelId),
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
const saves = [];
|
|
220
|
-
let favoritesChanged = false;
|
|
221
|
-
for (const { properties, save } of workingScopes) {
|
|
222
|
-
const countBefore = properties.size;
|
|
223
|
-
fieldInfos.forEach((info) => properties.delete(info));
|
|
224
|
-
if (properties.size !== countBefore) {
|
|
225
|
-
saves.push(save(properties));
|
|
226
|
-
favoritesChanged = true;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
if (!favoritesChanged)
|
|
230
|
-
return;
|
|
231
|
-
const propertiesOrder = this._propertiesOrder.get(getiModelInfo(iTwinId, imodelId));
|
|
232
|
-
removeOrderInfos(propertiesOrder, (0, exports.createFieldOrderInfos)(field));
|
|
233
|
-
saves.push(this._storage.savePropertiesOrder(propertiesOrder, iTwinId, imodelId));
|
|
234
|
-
await Promise.all(saves);
|
|
235
|
-
this.onFavoritesChanged.raiseEvent();
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* Removes all favorite properties from a certain scope.
|
|
239
|
-
* @param imodel IModelConnection.
|
|
240
|
-
* @param scope FavoritePropertiesScope to remove the favorite properties from.
|
|
241
|
-
* @note `initializeConnection` must be called with the `imodel` before calling this function.
|
|
242
|
-
*/
|
|
243
|
-
async clear(imodel, scope) {
|
|
244
|
-
this.validateInitialization(imodel);
|
|
245
|
-
const iTwinId = imodel.iTwinId;
|
|
246
|
-
const imodelId = imodel.iModelId;
|
|
247
|
-
let favoriteProperties;
|
|
248
|
-
let saveProperties;
|
|
249
|
-
switch (scope) {
|
|
250
|
-
case FavoritePropertiesScope.Global:
|
|
251
|
-
favoriteProperties = this._globalProperties;
|
|
252
|
-
saveProperties = async () => this._storage.saveProperties(new Set());
|
|
253
|
-
break;
|
|
254
|
-
case FavoritePropertiesScope.ITwin:
|
|
255
|
-
favoriteProperties = this._iTwinProperties.get(iTwinId);
|
|
256
|
-
saveProperties = async () => this._storage.saveProperties(new Set(), iTwinId);
|
|
257
|
-
break;
|
|
258
|
-
default:
|
|
259
|
-
favoriteProperties = this._imodelProperties.get(getiModelInfo(iTwinId, imodelId));
|
|
260
|
-
saveProperties = async () => this._storage.saveProperties(new Set(), iTwinId, imodelId);
|
|
261
|
-
}
|
|
262
|
-
if (favoriteProperties.size === 0)
|
|
263
|
-
return;
|
|
264
|
-
favoriteProperties.clear();
|
|
265
|
-
const saves = [];
|
|
266
|
-
saves.push(saveProperties());
|
|
267
|
-
saves.push(this._adjustPropertyOrderInfos(iTwinId, imodelId));
|
|
268
|
-
await Promise.all(saves);
|
|
269
|
-
this.onFavoritesChanged.raiseEvent();
|
|
270
|
-
}
|
|
271
|
-
/**
|
|
272
|
-
* Check if field contains at least one favorite property.
|
|
273
|
-
* @param field Field that contains properties.
|
|
274
|
-
* @param imodel IModelConnection.
|
|
275
|
-
* @param scope FavoritePropertiesScope to check for favorite properties. It also checks the more general scopes.
|
|
276
|
-
* @note `initializeConnection` must be called with the `imodel` before calling this function.
|
|
277
|
-
*/
|
|
278
|
-
has(field, imodel, scope) {
|
|
279
|
-
this.validateInitialization(imodel);
|
|
280
|
-
const iTwinId = imodel.iTwinId;
|
|
281
|
-
const imodelId = imodel.iModelId;
|
|
282
|
-
const fieldInfos = (0, exports.getFieldInfos)(field);
|
|
283
|
-
return setHasAny(this._globalProperties, fieldInfos) ||
|
|
284
|
-
(scope !== FavoritePropertiesScope.Global && setHasAny(this._iTwinProperties.get(iTwinId), fieldInfos)) ||
|
|
285
|
-
(scope === FavoritePropertiesScope.IModel && setHasAny(this._imodelProperties.get(getiModelInfo(iTwinId, imodelId)), fieldInfos));
|
|
286
|
-
}
|
|
287
|
-
getFieldPriority(field, iTwinId, imodelId) {
|
|
288
|
-
const orderInfos = this._propertiesOrder.get(getiModelInfo(iTwinId, imodelId));
|
|
289
|
-
const fieldOrderInfos = getFieldOrderInfos(field, orderInfos);
|
|
290
|
-
if (fieldOrderInfos.length === 0)
|
|
291
|
-
return -1;
|
|
292
|
-
const mostRecent = getMostRecentOrderInfo(fieldOrderInfos);
|
|
293
|
-
return mostRecent.priority;
|
|
294
|
-
}
|
|
295
|
-
/** Changes field properties priorities to lower than another fields priority
|
|
296
|
-
* @param imodel IModelConnection.
|
|
297
|
-
* @param field Field that priority is being changed.
|
|
298
|
-
* @param afterField Field that goes before the moved field. If undefined the moving field is changed to the highest priority (to the top).
|
|
299
|
-
* @param visibleFields Array of fields to move the field in.
|
|
300
|
-
* @note `initializeConnection` must be called with the `imodel` before calling this function.
|
|
301
|
-
*/
|
|
302
|
-
async changeFieldPriority(imodel, field, afterField, visibleFields) {
|
|
303
|
-
/**
|
|
304
|
-
* How it works:
|
|
305
|
-
* 1. Gets the orderInfo's for `field` (`orderInfo`) and `afterField` (`afterOrderInfo`) by selecting the most recent order informations for each field
|
|
306
|
-
* 2. Iterates all orderInfo's that are in between `afterOrderInfo` and `orderInfo` when sorted by priority
|
|
307
|
-
* 3. For each iterated orderInfo it checks if it is relevant:
|
|
308
|
-
* 3.1. If orderInfo belongs to a primitive property, orderInfo is relevant
|
|
309
|
-
* 3.2. If orderInfo's field is visible, orderInfo is relevant
|
|
310
|
-
* 3.3. If orderInfo's class has a base class or itself in previously labeled relevant orderInfo's, orderInfo is relevant
|
|
311
|
-
* 3.4. If 3.1 - 3.3 don't pass, orderInfo is irrelevant
|
|
312
|
-
* 4. Irrelevant orderInfos's get moved after `orderInfo` (depends on the direction)
|
|
313
|
-
* 5. All `field` orderInfo's get moved after `afterOrderInfo`
|
|
314
|
-
*/
|
|
315
|
-
this.validateInitialization(imodel);
|
|
316
|
-
const iTwinId = imodel.iTwinId;
|
|
317
|
-
const imodelId = imodel.iModelId;
|
|
318
|
-
if (field === afterField)
|
|
319
|
-
throw Error("`field` can not be the same as `afterField`.");
|
|
320
|
-
const allOrderInfos = this._propertiesOrder.get(getiModelInfo(iTwinId, imodelId));
|
|
321
|
-
const findFieldOrderInfoData = (f) => {
|
|
322
|
-
if (!visibleFields.includes(f))
|
|
323
|
-
throw Error("Field is not contained in visible fields.");
|
|
324
|
-
const infos = getFieldOrderInfos(f, allOrderInfos);
|
|
325
|
-
if (infos.length === 0)
|
|
326
|
-
throw Error("Field has no property order information.");
|
|
327
|
-
const info = getMostRecentOrderInfo(infos);
|
|
328
|
-
const index = allOrderInfos.indexOf(info);
|
|
329
|
-
return { infos, mostRecent: { info, index } };
|
|
330
|
-
};
|
|
331
|
-
const { infos: movingOrderInfos, mostRecent: { index: orderInfoIndex } } = findFieldOrderInfoData(field);
|
|
332
|
-
let afterOrderInfo;
|
|
333
|
-
let afterOrderInfoIndex;
|
|
334
|
-
if (afterField === undefined) {
|
|
335
|
-
afterOrderInfo = undefined;
|
|
336
|
-
afterOrderInfoIndex = -1;
|
|
337
|
-
}
|
|
338
|
-
else {
|
|
339
|
-
({ mostRecent: { info: afterOrderInfo, index: afterOrderInfoIndex } } = findFieldOrderInfoData(afterField));
|
|
340
|
-
}
|
|
341
|
-
let direction; // where to go from `afterOrderInfo` to `orderInfo`
|
|
342
|
-
let startIndex;
|
|
343
|
-
if (orderInfoIndex < afterOrderInfoIndex) {
|
|
344
|
-
direction = Direction.Up;
|
|
345
|
-
startIndex = afterOrderInfoIndex;
|
|
346
|
-
}
|
|
347
|
-
else {
|
|
348
|
-
direction = Direction.Down;
|
|
349
|
-
startIndex = afterOrderInfoIndex + 1;
|
|
350
|
-
}
|
|
351
|
-
const neededClassNames = allOrderInfos.reduce((classNames, oi) => {
|
|
352
|
-
if (oi.parentClassName)
|
|
353
|
-
classNames.add(oi.parentClassName);
|
|
354
|
-
return classNames;
|
|
355
|
-
}, new Set());
|
|
356
|
-
const baseClassesByClass = await this._getBaseClassesByClass(imodel, neededClassNames);
|
|
357
|
-
const visibleOrderInfos = visibleFields.reduce((union, currField) => union.concat(getFieldOrderInfos(currField, allOrderInfos)), []);
|
|
358
|
-
const irrelevantOrderInfos = []; // orderInfos's that won't change their logical order in respect to other properties
|
|
359
|
-
const relevantClasses = new Set(); // currently relevant classes
|
|
360
|
-
for (let i = startIndex; i !== orderInfoIndex; i += direction) {
|
|
361
|
-
const currOrderInfo = allOrderInfos[i];
|
|
362
|
-
// primitive properties are always relevant, because we can't determine their relevance based on the class hierarchy
|
|
363
|
-
if (currOrderInfo.parentClassName === undefined)
|
|
364
|
-
continue;
|
|
365
|
-
const visible = visibleOrderInfos.includes(currOrderInfo);
|
|
366
|
-
if (visible) {
|
|
367
|
-
relevantClasses.add(currOrderInfo.parentClassName);
|
|
368
|
-
continue;
|
|
369
|
-
}
|
|
370
|
-
const hasBaseClasses = baseClassesByClass[currOrderInfo.parentClassName].some((classId) => relevantClasses.has(classId));
|
|
371
|
-
if (hasBaseClasses)
|
|
372
|
-
continue;
|
|
373
|
-
if (direction === Direction.Down)
|
|
374
|
-
irrelevantOrderInfos.push(currOrderInfo);
|
|
375
|
-
else
|
|
376
|
-
irrelevantOrderInfos.unshift(currOrderInfo);
|
|
377
|
-
}
|
|
378
|
-
// remove irrelevantOrderInfo's to add them after the `orderInfo`
|
|
379
|
-
irrelevantOrderInfos.forEach((foi) => {
|
|
380
|
-
const index = allOrderInfos.findIndex((oi) => oi.parentClassName === foi.parentClassName && oi.name === foi.name);
|
|
381
|
-
allOrderInfos.splice(index, 1);
|
|
382
|
-
});
|
|
383
|
-
// remove movingOrderInfos's to add them after the `afterOrderInfo`
|
|
384
|
-
movingOrderInfos.forEach((foi) => {
|
|
385
|
-
const index = allOrderInfos.findIndex((oi) => oi.parentClassName === foi.parentClassName && oi.name === foi.name);
|
|
386
|
-
allOrderInfos.splice(index, 1);
|
|
387
|
-
});
|
|
388
|
-
movingOrderInfos.forEach((oi) => oi.orderedTimestamp = new Date());
|
|
389
|
-
afterOrderInfoIndex = afterOrderInfo === undefined ? -1 : allOrderInfos.indexOf(afterOrderInfo);
|
|
390
|
-
allOrderInfos.splice(afterOrderInfoIndex + 1, 0, ...movingOrderInfos);
|
|
391
|
-
allOrderInfos.splice(afterOrderInfoIndex + 1 + (direction === Direction.Up ? movingOrderInfos.length : 0), 0, ...irrelevantOrderInfos);
|
|
392
|
-
// reassign priority numbers
|
|
393
|
-
let priority = allOrderInfos.length;
|
|
394
|
-
allOrderInfos.forEach((oi) => oi.priority = priority--);
|
|
395
|
-
await this._storage.savePropertiesOrder(allOrderInfos, iTwinId, imodelId);
|
|
396
|
-
this.onFavoritesChanged.raiseEvent();
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
exports.FavoritePropertiesManager = FavoritePropertiesManager;
|
|
400
|
-
/**
|
|
401
|
-
* Used in tests to avoid collisions between multiple runs using the same storage
|
|
402
|
-
* @internal
|
|
403
|
-
*/
|
|
404
|
-
FavoritePropertiesManager.FAVORITES_IDENTIFIER_PREFIX = "";
|
|
405
|
-
var Direction;
|
|
406
|
-
(function (Direction) {
|
|
407
|
-
Direction[Direction["Up"] = -1] = "Up";
|
|
408
|
-
Direction[Direction["Down"] = 1] = "Down";
|
|
409
|
-
})(Direction || (Direction = {}));
|
|
410
|
-
const getiModelInfo = (iTwinId, imodelId) => `${iTwinId}/${imodelId}`;
|
|
411
|
-
const getPropertiesFieldPropertyNames = (field) => {
|
|
412
|
-
const nestingPrefix = getNestingPrefix(field.parent);
|
|
413
|
-
return field.properties.map((property) => `${FavoritePropertiesManager.FAVORITES_IDENTIFIER_PREFIX}${nestingPrefix}${property.property.classInfo.name}:${property.property.name}`);
|
|
414
|
-
};
|
|
415
|
-
const getNestedContentFieldPropertyName = (field) => {
|
|
416
|
-
const nestingPrefix = getNestingPrefix(field);
|
|
417
|
-
return `${FavoritePropertiesManager.FAVORITES_IDENTIFIER_PREFIX}${nestingPrefix}${field.contentClassInfo.name}`;
|
|
418
|
-
};
|
|
419
|
-
const getNestingPrefix = (field) => {
|
|
420
|
-
const path = [];
|
|
421
|
-
let curr = field;
|
|
422
|
-
while (curr !== undefined) {
|
|
423
|
-
curr.pathToPrimaryClass.forEach((rel) => {
|
|
424
|
-
// Relationship directions are reversed, because we are generating a relationship list starting from the parent
|
|
425
|
-
path.push(`${rel.isForwardRelationship ? "B" : "F"}:${rel.relationshipInfo.name}`);
|
|
426
|
-
path.push(rel.targetClassInfo.name);
|
|
427
|
-
});
|
|
428
|
-
curr = curr.parent;
|
|
429
|
-
}
|
|
430
|
-
if (path.length === 0)
|
|
431
|
-
return "";
|
|
432
|
-
path.reverse();
|
|
433
|
-
return `${path.join("-")}-`;
|
|
434
|
-
};
|
|
435
|
-
const getPropertyClassName = (propertyName) => {
|
|
436
|
-
const propertyNameStart = propertyName.split("-")[0];
|
|
437
|
-
const parts = propertyNameStart.split(":").length;
|
|
438
|
-
if (parts === 1) // primitive
|
|
439
|
-
return undefined;
|
|
440
|
-
if (parts === 2) // nested property OR nested property parent class OR regular property parent class
|
|
441
|
-
return propertyNameStart;
|
|
442
|
-
// regular property without parent class
|
|
443
|
-
return propertyNameStart.substr(0, propertyName.lastIndexOf(":"));
|
|
444
|
-
};
|
|
445
|
-
/** @internal */
|
|
446
|
-
const getFieldInfos = (field) => {
|
|
447
|
-
const fieldInfos = new Set();
|
|
448
|
-
if (field.isPropertiesField())
|
|
449
|
-
getPropertiesFieldPropertyNames(field).forEach((info) => fieldInfos.add(info));
|
|
450
|
-
else if (field.isNestedContentField())
|
|
451
|
-
fieldInfos.add(getNestedContentFieldPropertyName(field));
|
|
452
|
-
else
|
|
453
|
-
fieldInfos.add(`${FavoritePropertiesManager.FAVORITES_IDENTIFIER_PREFIX}${field.name}`);
|
|
454
|
-
return fieldInfos;
|
|
455
|
-
};
|
|
456
|
-
exports.getFieldInfos = getFieldInfos;
|
|
457
|
-
const setHasAny = (set, lookup) => {
|
|
458
|
-
for (const key of lookup) {
|
|
459
|
-
if (set.has(key))
|
|
460
|
-
return true;
|
|
461
|
-
}
|
|
462
|
-
return false;
|
|
463
|
-
};
|
|
464
|
-
const addOrderInfos = (dest, source) => {
|
|
465
|
-
source.forEach((si) => {
|
|
466
|
-
const index = dest.findIndex((di) => di.name === si.name);
|
|
467
|
-
if (index === -1) {
|
|
468
|
-
si.orderedTimestamp = new Date();
|
|
469
|
-
dest.push(si);
|
|
470
|
-
}
|
|
471
|
-
});
|
|
472
|
-
let priority = dest.length;
|
|
473
|
-
dest.forEach((info) => info.priority = priority--);
|
|
474
|
-
};
|
|
475
|
-
const removeOrderInfos = (container, toRemove) => {
|
|
476
|
-
toRemove.forEach((roi) => {
|
|
477
|
-
const index = container.findIndex((oi) => oi.name === roi.name);
|
|
478
|
-
/* istanbul ignore else */
|
|
479
|
-
if (index >= 0)
|
|
480
|
-
container.splice(index, 1);
|
|
481
|
-
});
|
|
482
|
-
};
|
|
483
|
-
/** @internal */
|
|
484
|
-
const createFieldOrderInfos = (field) => {
|
|
485
|
-
if (field.isNestedContentField()) {
|
|
486
|
-
const propertyName = getNestedContentFieldPropertyName(field);
|
|
487
|
-
return [{
|
|
488
|
-
parentClassName: getPropertyClassName(propertyName),
|
|
489
|
-
name: propertyName,
|
|
490
|
-
priority: 0,
|
|
491
|
-
orderedTimestamp: new Date(),
|
|
492
|
-
}];
|
|
493
|
-
}
|
|
494
|
-
if (field.isPropertiesField()) {
|
|
495
|
-
return getPropertiesFieldPropertyNames(field).map((propertyName) => ({
|
|
496
|
-
parentClassName: getPropertyClassName(propertyName),
|
|
497
|
-
name: propertyName,
|
|
498
|
-
priority: 0,
|
|
499
|
-
orderedTimestamp: new Date(),
|
|
500
|
-
}));
|
|
501
|
-
}
|
|
502
|
-
return [{
|
|
503
|
-
parentClassName: undefined,
|
|
504
|
-
name: field.name,
|
|
505
|
-
priority: 0,
|
|
506
|
-
orderedTimestamp: new Date(),
|
|
507
|
-
}];
|
|
508
|
-
};
|
|
509
|
-
exports.createFieldOrderInfos = createFieldOrderInfos;
|
|
510
|
-
const getFieldOrderInfos = (field, orderInfos) => {
|
|
511
|
-
const fieldOrderInfos = [];
|
|
512
|
-
const tryAddOrderInfo = (name) => {
|
|
513
|
-
const fieldOrderInfo = orderInfos.find((oi) => oi.name === name);
|
|
514
|
-
if (fieldOrderInfo !== undefined)
|
|
515
|
-
fieldOrderInfos.push(fieldOrderInfo);
|
|
516
|
-
};
|
|
517
|
-
if (field.isPropertiesField())
|
|
518
|
-
getPropertiesFieldPropertyNames(field).forEach(tryAddOrderInfo);
|
|
519
|
-
else if (field.isNestedContentField())
|
|
520
|
-
tryAddOrderInfo(getNestedContentFieldPropertyName(field));
|
|
521
|
-
else
|
|
522
|
-
tryAddOrderInfo(field.name);
|
|
523
|
-
return fieldOrderInfos;
|
|
524
|
-
};
|
|
525
|
-
const getMostRecentOrderInfo = (orderInfos) => orderInfos.reduce((recent, curr) => (recent && recent.orderedTimestamp >= curr.orderedTimestamp) ? recent : curr);
|
|
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
|
+
/** @packageDocumentation
|
|
7
|
+
* @module Core
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.createFieldOrderInfos = exports.getFieldInfos = exports.FavoritePropertiesManager = exports.FavoritePropertiesScope = void 0;
|
|
11
|
+
const core_bentley_1 = require("@itwin/core-bentley");
|
|
12
|
+
const core_common_1 = require("@itwin/core-common");
|
|
13
|
+
/**
|
|
14
|
+
* Scopes that favorite properties can be stored in.
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
var FavoritePropertiesScope;
|
|
18
|
+
(function (FavoritePropertiesScope) {
|
|
19
|
+
FavoritePropertiesScope[FavoritePropertiesScope["Global"] = 0] = "Global";
|
|
20
|
+
FavoritePropertiesScope[FavoritePropertiesScope["ITwin"] = 1] = "ITwin";
|
|
21
|
+
FavoritePropertiesScope[FavoritePropertiesScope["IModel"] = 2] = "IModel";
|
|
22
|
+
})(FavoritePropertiesScope = exports.FavoritePropertiesScope || (exports.FavoritePropertiesScope = {}));
|
|
23
|
+
/**
|
|
24
|
+
* The favorite property manager which lets to store favorite properties
|
|
25
|
+
* and check if field contains favorite properties.
|
|
26
|
+
*
|
|
27
|
+
* @public
|
|
28
|
+
*/
|
|
29
|
+
class FavoritePropertiesManager {
|
|
30
|
+
constructor(props) {
|
|
31
|
+
/** Event raised after favorite properties have changed. */
|
|
32
|
+
this.onFavoritesChanged = new core_bentley_1.BeEvent();
|
|
33
|
+
/**
|
|
34
|
+
* Initialize favorite properties for the provided IModelConnection.
|
|
35
|
+
*/
|
|
36
|
+
this.initializeConnection = async (imodel) => {
|
|
37
|
+
const imodelId = imodel.iModelId;
|
|
38
|
+
const iTwinId = imodel.iTwinId;
|
|
39
|
+
if (this._globalProperties === undefined)
|
|
40
|
+
this._globalProperties = await this._storage.loadProperties() || new Set();
|
|
41
|
+
if (!this._iTwinProperties.has(iTwinId)) {
|
|
42
|
+
const iTwinProperties = await this._storage.loadProperties(iTwinId) || new Set();
|
|
43
|
+
this._iTwinProperties.set(iTwinId, iTwinProperties);
|
|
44
|
+
}
|
|
45
|
+
if (!this._imodelProperties.has(getiModelInfo(iTwinId, imodelId))) {
|
|
46
|
+
const imodelProperties = await this._storage.loadProperties(iTwinId, imodelId) || new Set();
|
|
47
|
+
this._imodelProperties.set(getiModelInfo(iTwinId, imodelId), imodelProperties);
|
|
48
|
+
}
|
|
49
|
+
const propertiesOrder = await this._storage.loadPropertiesOrder(iTwinId, imodelId) || [];
|
|
50
|
+
this._propertiesOrder.set(getiModelInfo(iTwinId, imodelId), propertiesOrder);
|
|
51
|
+
await this._adjustPropertyOrderInfos(iTwinId, imodelId);
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Function that removes order information of properties that are no longer
|
|
55
|
+
* favorited and adds missing order information for favorited properties.
|
|
56
|
+
*/
|
|
57
|
+
this._adjustPropertyOrderInfos = async (iTwinId, imodelId) => {
|
|
58
|
+
const propertiesOrder = this._propertiesOrder.get(getiModelInfo(iTwinId, imodelId));
|
|
59
|
+
const globalProperties = this._globalProperties;
|
|
60
|
+
const iTwinProperties = this._iTwinProperties.get(iTwinId);
|
|
61
|
+
const imodelProperties = this._imodelProperties.get(getiModelInfo(iTwinId, imodelId));
|
|
62
|
+
// favorite property infos that need to be added to the propertiesOrder array
|
|
63
|
+
const infosToAdd = new Set([...globalProperties, ...iTwinProperties, ...imodelProperties]);
|
|
64
|
+
for (let i = propertiesOrder.length - 1; i >= 0; i--) {
|
|
65
|
+
if (infosToAdd.has(propertiesOrder[i].name))
|
|
66
|
+
infosToAdd.delete(propertiesOrder[i].name);
|
|
67
|
+
else
|
|
68
|
+
propertiesOrder.splice(i, 1);
|
|
69
|
+
}
|
|
70
|
+
infosToAdd.forEach((info) => propertiesOrder.push({
|
|
71
|
+
name: info,
|
|
72
|
+
parentClassName: getPropertyClassName(info),
|
|
73
|
+
orderedTimestamp: new Date(),
|
|
74
|
+
priority: 0,
|
|
75
|
+
}));
|
|
76
|
+
let priority = propertiesOrder.length;
|
|
77
|
+
propertiesOrder.forEach((oi) => oi.priority = priority--);
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Sorts an array of fields with respect to favorite property order.
|
|
81
|
+
* Non-favorited fields get sorted by their default priority and always have lower priority than favorited fields.
|
|
82
|
+
* @param imodel IModelConnection.
|
|
83
|
+
* @param fields Array of Field's that needs to be sorted.
|
|
84
|
+
* @note `initializeConnection` must be called with the `imodel` before calling this function.
|
|
85
|
+
*/
|
|
86
|
+
this.sortFields = (imodel, fields) => {
|
|
87
|
+
this.validateInitialization(imodel);
|
|
88
|
+
const iTwinId = imodel.iTwinId;
|
|
89
|
+
const imodelId = imodel.iModelId;
|
|
90
|
+
const fieldPriority = new Map();
|
|
91
|
+
fields.forEach((field) => fieldPriority.set(field, this.getFieldPriority(field, iTwinId, imodelId)));
|
|
92
|
+
const sortFunction = (left, right) => {
|
|
93
|
+
const lp = fieldPriority.get(left);
|
|
94
|
+
const rp = fieldPriority.get(right);
|
|
95
|
+
return lp < rp ? 1 :
|
|
96
|
+
lp > rp ? -1 :
|
|
97
|
+
left.priority < right.priority ? 1 : // if favorite fields have equal priorities, sort by field priority
|
|
98
|
+
left.priority > right.priority ? -1 :
|
|
99
|
+
left.name.localeCompare(right.name);
|
|
100
|
+
};
|
|
101
|
+
return fields.sort(sortFunction);
|
|
102
|
+
};
|
|
103
|
+
this._getBaseClassesByClass = async (imodel, neededClasses) => {
|
|
104
|
+
const iTwinId = imodel.iTwinId;
|
|
105
|
+
const imodelId = imodel.iModelId;
|
|
106
|
+
const imodelInfo = getiModelInfo(iTwinId, imodelId);
|
|
107
|
+
let baseClasses;
|
|
108
|
+
if (this._imodelBaseClassesByClass.has(imodelInfo))
|
|
109
|
+
baseClasses = this._imodelBaseClassesByClass.get(imodelInfo);
|
|
110
|
+
else
|
|
111
|
+
this._imodelBaseClassesByClass.set(imodelInfo, baseClasses = {});
|
|
112
|
+
const missingClasses = new Set();
|
|
113
|
+
neededClasses.forEach((className) => {
|
|
114
|
+
if (!baseClasses.hasOwnProperty(className))
|
|
115
|
+
missingClasses.add(className);
|
|
116
|
+
});
|
|
117
|
+
if (missingClasses.size === 0)
|
|
118
|
+
return baseClasses;
|
|
119
|
+
const query = `
|
|
120
|
+
SELECT (derivedSchema.Name || ':' || derivedClass.Name) AS "ClassFullName", (baseSchema.Name || ':' || baseClass.Name) AS "BaseClassFullName"
|
|
121
|
+
FROM ECDbMeta.ClassHasAllBaseClasses baseClassRels
|
|
122
|
+
INNER JOIN ECDbMeta.ECClassDef derivedClass ON derivedClass.ECInstanceId = baseClassRels.SourceECInstanceId
|
|
123
|
+
INNER JOIN ECDbMeta.ECSchemaDef derivedSchema ON derivedSchema.ECInstanceId = derivedClass.Schema.Id
|
|
124
|
+
INNER JOIN ECDbMeta.ECClassDef baseClass ON baseClass.ECInstanceId = baseClassRels.TargetECInstanceId
|
|
125
|
+
INNER JOIN ECDbMeta.ECSchemaDef baseSchema ON baseSchema.ECInstanceId = baseClass.Schema.Id
|
|
126
|
+
WHERE (derivedSchema.Name || ':' || derivedClass.Name) IN (${[...missingClasses].map((className) => `'${className}'`).join(",")})`;
|
|
127
|
+
for await (const row of imodel.query(query, undefined, { rowFormat: core_common_1.QueryRowFormat.UseJsPropertyNames })) {
|
|
128
|
+
if (!(row.classFullName in baseClasses))
|
|
129
|
+
baseClasses[row.classFullName] = [];
|
|
130
|
+
baseClasses[row.classFullName].push(row.baseClassFullName);
|
|
131
|
+
}
|
|
132
|
+
return baseClasses;
|
|
133
|
+
};
|
|
134
|
+
this._storage = props.storage;
|
|
135
|
+
this._iTwinProperties = new Map();
|
|
136
|
+
this._imodelProperties = new Map();
|
|
137
|
+
this._propertiesOrder = new Map();
|
|
138
|
+
this._imodelBaseClassesByClass = new Map();
|
|
139
|
+
}
|
|
140
|
+
dispose() {
|
|
141
|
+
// istanbul ignore else
|
|
142
|
+
if ((0, core_bentley_1.isIDisposable)(this._storage))
|
|
143
|
+
this._storage.dispose();
|
|
144
|
+
}
|
|
145
|
+
validateInitialization(imodel) {
|
|
146
|
+
const iTwinId = imodel.iTwinId;
|
|
147
|
+
const imodelId = imodel.iModelId;
|
|
148
|
+
if (!this._imodelProperties.has(getiModelInfo(iTwinId, imodelId)))
|
|
149
|
+
throw Error(`Favorite properties are not initialized for iModel: '${imodelId}', in iTwin: '${iTwinId}'. Call initializeConnection() with an IModelConnection to initialize.`);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Adds favorite properties into a certain scope.
|
|
153
|
+
* @param field Field that contains properties. If field contains multiple properties, all of them will be favorited.
|
|
154
|
+
* @param imodel IModelConnection.
|
|
155
|
+
* @param scope FavoritePropertiesScope to put the favorite properties into.
|
|
156
|
+
* @note `initializeConnection` must be called with the `imodel` before calling this function.
|
|
157
|
+
*/
|
|
158
|
+
async add(field, imodel, scope) {
|
|
159
|
+
this.validateInitialization(imodel);
|
|
160
|
+
const iTwinId = imodel.iTwinId;
|
|
161
|
+
const imodelId = imodel.iModelId;
|
|
162
|
+
let favoriteProperties;
|
|
163
|
+
let saveProperties;
|
|
164
|
+
switch (scope) {
|
|
165
|
+
case FavoritePropertiesScope.Global:
|
|
166
|
+
favoriteProperties = this._globalProperties;
|
|
167
|
+
saveProperties = async (properties) => this._storage.saveProperties(properties);
|
|
168
|
+
break;
|
|
169
|
+
case FavoritePropertiesScope.ITwin:
|
|
170
|
+
favoriteProperties = this._iTwinProperties.get(iTwinId);
|
|
171
|
+
saveProperties = async (properties) => this._storage.saveProperties(properties, iTwinId);
|
|
172
|
+
break;
|
|
173
|
+
default:
|
|
174
|
+
favoriteProperties = this._imodelProperties.get(getiModelInfo(iTwinId, imodelId));
|
|
175
|
+
saveProperties = async (properties) => this._storage.saveProperties(properties, iTwinId, imodelId);
|
|
176
|
+
}
|
|
177
|
+
const countBefore = favoriteProperties.size;
|
|
178
|
+
const fieldInfos = (0, exports.getFieldInfos)(field);
|
|
179
|
+
fieldInfos.forEach((info) => favoriteProperties.add(info));
|
|
180
|
+
if (favoriteProperties.size !== countBefore) {
|
|
181
|
+
const saves = [];
|
|
182
|
+
saves.push(saveProperties(favoriteProperties));
|
|
183
|
+
const propertiesOrder = this._propertiesOrder.get(getiModelInfo(iTwinId, imodelId));
|
|
184
|
+
addOrderInfos(propertiesOrder, (0, exports.createFieldOrderInfos)(field));
|
|
185
|
+
saves.push(this._storage.savePropertiesOrder(propertiesOrder, iTwinId, imodelId));
|
|
186
|
+
await Promise.all(saves);
|
|
187
|
+
this.onFavoritesChanged.raiseEvent();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Removes favorite properties from a scope specified and all the more general scopes.
|
|
192
|
+
* @param field Field that contains properties. If field contains multiple properties, all of them will be un-favorited.
|
|
193
|
+
* @param imodel IModelConnection.
|
|
194
|
+
* @param scope FavoritePropertiesScope to remove the favorite properties from. It also removes from more general scopes.
|
|
195
|
+
* @note `initializeConnection` must be called with the `imodel` before calling this function.
|
|
196
|
+
*/
|
|
197
|
+
async remove(field, imodel, scope) {
|
|
198
|
+
this.validateInitialization(imodel);
|
|
199
|
+
const iTwinId = imodel.iTwinId;
|
|
200
|
+
const imodelId = imodel.iModelId;
|
|
201
|
+
const fieldInfos = (0, exports.getFieldInfos)(field);
|
|
202
|
+
const workingScopes = [];
|
|
203
|
+
workingScopes.push({
|
|
204
|
+
properties: this._globalProperties,
|
|
205
|
+
save: async (properties) => this._storage.saveProperties(properties),
|
|
206
|
+
});
|
|
207
|
+
if (scope === FavoritePropertiesScope.ITwin || scope === FavoritePropertiesScope.IModel) {
|
|
208
|
+
workingScopes.push({
|
|
209
|
+
properties: this._iTwinProperties.get(iTwinId),
|
|
210
|
+
save: async (properties) => this._storage.saveProperties(properties, iTwinId),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
if (scope === FavoritePropertiesScope.IModel) {
|
|
214
|
+
workingScopes.push({
|
|
215
|
+
properties: this._imodelProperties.get(getiModelInfo(iTwinId, imodelId)),
|
|
216
|
+
save: async (properties) => this._storage.saveProperties(properties, iTwinId, imodelId),
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
const saves = [];
|
|
220
|
+
let favoritesChanged = false;
|
|
221
|
+
for (const { properties, save } of workingScopes) {
|
|
222
|
+
const countBefore = properties.size;
|
|
223
|
+
fieldInfos.forEach((info) => properties.delete(info));
|
|
224
|
+
if (properties.size !== countBefore) {
|
|
225
|
+
saves.push(save(properties));
|
|
226
|
+
favoritesChanged = true;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (!favoritesChanged)
|
|
230
|
+
return;
|
|
231
|
+
const propertiesOrder = this._propertiesOrder.get(getiModelInfo(iTwinId, imodelId));
|
|
232
|
+
removeOrderInfos(propertiesOrder, (0, exports.createFieldOrderInfos)(field));
|
|
233
|
+
saves.push(this._storage.savePropertiesOrder(propertiesOrder, iTwinId, imodelId));
|
|
234
|
+
await Promise.all(saves);
|
|
235
|
+
this.onFavoritesChanged.raiseEvent();
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Removes all favorite properties from a certain scope.
|
|
239
|
+
* @param imodel IModelConnection.
|
|
240
|
+
* @param scope FavoritePropertiesScope to remove the favorite properties from.
|
|
241
|
+
* @note `initializeConnection` must be called with the `imodel` before calling this function.
|
|
242
|
+
*/
|
|
243
|
+
async clear(imodel, scope) {
|
|
244
|
+
this.validateInitialization(imodel);
|
|
245
|
+
const iTwinId = imodel.iTwinId;
|
|
246
|
+
const imodelId = imodel.iModelId;
|
|
247
|
+
let favoriteProperties;
|
|
248
|
+
let saveProperties;
|
|
249
|
+
switch (scope) {
|
|
250
|
+
case FavoritePropertiesScope.Global:
|
|
251
|
+
favoriteProperties = this._globalProperties;
|
|
252
|
+
saveProperties = async () => this._storage.saveProperties(new Set());
|
|
253
|
+
break;
|
|
254
|
+
case FavoritePropertiesScope.ITwin:
|
|
255
|
+
favoriteProperties = this._iTwinProperties.get(iTwinId);
|
|
256
|
+
saveProperties = async () => this._storage.saveProperties(new Set(), iTwinId);
|
|
257
|
+
break;
|
|
258
|
+
default:
|
|
259
|
+
favoriteProperties = this._imodelProperties.get(getiModelInfo(iTwinId, imodelId));
|
|
260
|
+
saveProperties = async () => this._storage.saveProperties(new Set(), iTwinId, imodelId);
|
|
261
|
+
}
|
|
262
|
+
if (favoriteProperties.size === 0)
|
|
263
|
+
return;
|
|
264
|
+
favoriteProperties.clear();
|
|
265
|
+
const saves = [];
|
|
266
|
+
saves.push(saveProperties());
|
|
267
|
+
saves.push(this._adjustPropertyOrderInfos(iTwinId, imodelId));
|
|
268
|
+
await Promise.all(saves);
|
|
269
|
+
this.onFavoritesChanged.raiseEvent();
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Check if field contains at least one favorite property.
|
|
273
|
+
* @param field Field that contains properties.
|
|
274
|
+
* @param imodel IModelConnection.
|
|
275
|
+
* @param scope FavoritePropertiesScope to check for favorite properties. It also checks the more general scopes.
|
|
276
|
+
* @note `initializeConnection` must be called with the `imodel` before calling this function.
|
|
277
|
+
*/
|
|
278
|
+
has(field, imodel, scope) {
|
|
279
|
+
this.validateInitialization(imodel);
|
|
280
|
+
const iTwinId = imodel.iTwinId;
|
|
281
|
+
const imodelId = imodel.iModelId;
|
|
282
|
+
const fieldInfos = (0, exports.getFieldInfos)(field);
|
|
283
|
+
return setHasAny(this._globalProperties, fieldInfos) ||
|
|
284
|
+
(scope !== FavoritePropertiesScope.Global && setHasAny(this._iTwinProperties.get(iTwinId), fieldInfos)) ||
|
|
285
|
+
(scope === FavoritePropertiesScope.IModel && setHasAny(this._imodelProperties.get(getiModelInfo(iTwinId, imodelId)), fieldInfos));
|
|
286
|
+
}
|
|
287
|
+
getFieldPriority(field, iTwinId, imodelId) {
|
|
288
|
+
const orderInfos = this._propertiesOrder.get(getiModelInfo(iTwinId, imodelId));
|
|
289
|
+
const fieldOrderInfos = getFieldOrderInfos(field, orderInfos);
|
|
290
|
+
if (fieldOrderInfos.length === 0)
|
|
291
|
+
return -1;
|
|
292
|
+
const mostRecent = getMostRecentOrderInfo(fieldOrderInfos);
|
|
293
|
+
return mostRecent.priority;
|
|
294
|
+
}
|
|
295
|
+
/** Changes field properties priorities to lower than another fields priority
|
|
296
|
+
* @param imodel IModelConnection.
|
|
297
|
+
* @param field Field that priority is being changed.
|
|
298
|
+
* @param afterField Field that goes before the moved field. If undefined the moving field is changed to the highest priority (to the top).
|
|
299
|
+
* @param visibleFields Array of fields to move the field in.
|
|
300
|
+
* @note `initializeConnection` must be called with the `imodel` before calling this function.
|
|
301
|
+
*/
|
|
302
|
+
async changeFieldPriority(imodel, field, afterField, visibleFields) {
|
|
303
|
+
/**
|
|
304
|
+
* How it works:
|
|
305
|
+
* 1. Gets the orderInfo's for `field` (`orderInfo`) and `afterField` (`afterOrderInfo`) by selecting the most recent order informations for each field
|
|
306
|
+
* 2. Iterates all orderInfo's that are in between `afterOrderInfo` and `orderInfo` when sorted by priority
|
|
307
|
+
* 3. For each iterated orderInfo it checks if it is relevant:
|
|
308
|
+
* 3.1. If orderInfo belongs to a primitive property, orderInfo is relevant
|
|
309
|
+
* 3.2. If orderInfo's field is visible, orderInfo is relevant
|
|
310
|
+
* 3.3. If orderInfo's class has a base class or itself in previously labeled relevant orderInfo's, orderInfo is relevant
|
|
311
|
+
* 3.4. If 3.1 - 3.3 don't pass, orderInfo is irrelevant
|
|
312
|
+
* 4. Irrelevant orderInfos's get moved after `orderInfo` (depends on the direction)
|
|
313
|
+
* 5. All `field` orderInfo's get moved after `afterOrderInfo`
|
|
314
|
+
*/
|
|
315
|
+
this.validateInitialization(imodel);
|
|
316
|
+
const iTwinId = imodel.iTwinId;
|
|
317
|
+
const imodelId = imodel.iModelId;
|
|
318
|
+
if (field === afterField)
|
|
319
|
+
throw Error("`field` can not be the same as `afterField`.");
|
|
320
|
+
const allOrderInfos = this._propertiesOrder.get(getiModelInfo(iTwinId, imodelId));
|
|
321
|
+
const findFieldOrderInfoData = (f) => {
|
|
322
|
+
if (!visibleFields.includes(f))
|
|
323
|
+
throw Error("Field is not contained in visible fields.");
|
|
324
|
+
const infos = getFieldOrderInfos(f, allOrderInfos);
|
|
325
|
+
if (infos.length === 0)
|
|
326
|
+
throw Error("Field has no property order information.");
|
|
327
|
+
const info = getMostRecentOrderInfo(infos);
|
|
328
|
+
const index = allOrderInfos.indexOf(info);
|
|
329
|
+
return { infos, mostRecent: { info, index } };
|
|
330
|
+
};
|
|
331
|
+
const { infos: movingOrderInfos, mostRecent: { index: orderInfoIndex } } = findFieldOrderInfoData(field);
|
|
332
|
+
let afterOrderInfo;
|
|
333
|
+
let afterOrderInfoIndex;
|
|
334
|
+
if (afterField === undefined) {
|
|
335
|
+
afterOrderInfo = undefined;
|
|
336
|
+
afterOrderInfoIndex = -1;
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
({ mostRecent: { info: afterOrderInfo, index: afterOrderInfoIndex } } = findFieldOrderInfoData(afterField));
|
|
340
|
+
}
|
|
341
|
+
let direction; // where to go from `afterOrderInfo` to `orderInfo`
|
|
342
|
+
let startIndex;
|
|
343
|
+
if (orderInfoIndex < afterOrderInfoIndex) {
|
|
344
|
+
direction = Direction.Up;
|
|
345
|
+
startIndex = afterOrderInfoIndex;
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
direction = Direction.Down;
|
|
349
|
+
startIndex = afterOrderInfoIndex + 1;
|
|
350
|
+
}
|
|
351
|
+
const neededClassNames = allOrderInfos.reduce((classNames, oi) => {
|
|
352
|
+
if (oi.parentClassName)
|
|
353
|
+
classNames.add(oi.parentClassName);
|
|
354
|
+
return classNames;
|
|
355
|
+
}, new Set());
|
|
356
|
+
const baseClassesByClass = await this._getBaseClassesByClass(imodel, neededClassNames);
|
|
357
|
+
const visibleOrderInfos = visibleFields.reduce((union, currField) => union.concat(getFieldOrderInfos(currField, allOrderInfos)), []);
|
|
358
|
+
const irrelevantOrderInfos = []; // orderInfos's that won't change their logical order in respect to other properties
|
|
359
|
+
const relevantClasses = new Set(); // currently relevant classes
|
|
360
|
+
for (let i = startIndex; i !== orderInfoIndex; i += direction) {
|
|
361
|
+
const currOrderInfo = allOrderInfos[i];
|
|
362
|
+
// primitive properties are always relevant, because we can't determine their relevance based on the class hierarchy
|
|
363
|
+
if (currOrderInfo.parentClassName === undefined)
|
|
364
|
+
continue;
|
|
365
|
+
const visible = visibleOrderInfos.includes(currOrderInfo);
|
|
366
|
+
if (visible) {
|
|
367
|
+
relevantClasses.add(currOrderInfo.parentClassName);
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
const hasBaseClasses = baseClassesByClass[currOrderInfo.parentClassName].some((classId) => relevantClasses.has(classId));
|
|
371
|
+
if (hasBaseClasses)
|
|
372
|
+
continue;
|
|
373
|
+
if (direction === Direction.Down)
|
|
374
|
+
irrelevantOrderInfos.push(currOrderInfo);
|
|
375
|
+
else
|
|
376
|
+
irrelevantOrderInfos.unshift(currOrderInfo);
|
|
377
|
+
}
|
|
378
|
+
// remove irrelevantOrderInfo's to add them after the `orderInfo`
|
|
379
|
+
irrelevantOrderInfos.forEach((foi) => {
|
|
380
|
+
const index = allOrderInfos.findIndex((oi) => oi.parentClassName === foi.parentClassName && oi.name === foi.name);
|
|
381
|
+
allOrderInfos.splice(index, 1);
|
|
382
|
+
});
|
|
383
|
+
// remove movingOrderInfos's to add them after the `afterOrderInfo`
|
|
384
|
+
movingOrderInfos.forEach((foi) => {
|
|
385
|
+
const index = allOrderInfos.findIndex((oi) => oi.parentClassName === foi.parentClassName && oi.name === foi.name);
|
|
386
|
+
allOrderInfos.splice(index, 1);
|
|
387
|
+
});
|
|
388
|
+
movingOrderInfos.forEach((oi) => oi.orderedTimestamp = new Date());
|
|
389
|
+
afterOrderInfoIndex = afterOrderInfo === undefined ? -1 : allOrderInfos.indexOf(afterOrderInfo);
|
|
390
|
+
allOrderInfos.splice(afterOrderInfoIndex + 1, 0, ...movingOrderInfos);
|
|
391
|
+
allOrderInfos.splice(afterOrderInfoIndex + 1 + (direction === Direction.Up ? movingOrderInfos.length : 0), 0, ...irrelevantOrderInfos);
|
|
392
|
+
// reassign priority numbers
|
|
393
|
+
let priority = allOrderInfos.length;
|
|
394
|
+
allOrderInfos.forEach((oi) => oi.priority = priority--);
|
|
395
|
+
await this._storage.savePropertiesOrder(allOrderInfos, iTwinId, imodelId);
|
|
396
|
+
this.onFavoritesChanged.raiseEvent();
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
exports.FavoritePropertiesManager = FavoritePropertiesManager;
|
|
400
|
+
/**
|
|
401
|
+
* Used in tests to avoid collisions between multiple runs using the same storage
|
|
402
|
+
* @internal
|
|
403
|
+
*/
|
|
404
|
+
FavoritePropertiesManager.FAVORITES_IDENTIFIER_PREFIX = "";
|
|
405
|
+
var Direction;
|
|
406
|
+
(function (Direction) {
|
|
407
|
+
Direction[Direction["Up"] = -1] = "Up";
|
|
408
|
+
Direction[Direction["Down"] = 1] = "Down";
|
|
409
|
+
})(Direction || (Direction = {}));
|
|
410
|
+
const getiModelInfo = (iTwinId, imodelId) => `${iTwinId}/${imodelId}`;
|
|
411
|
+
const getPropertiesFieldPropertyNames = (field) => {
|
|
412
|
+
const nestingPrefix = getNestingPrefix(field.parent);
|
|
413
|
+
return field.properties.map((property) => `${FavoritePropertiesManager.FAVORITES_IDENTIFIER_PREFIX}${nestingPrefix}${property.property.classInfo.name}:${property.property.name}`);
|
|
414
|
+
};
|
|
415
|
+
const getNestedContentFieldPropertyName = (field) => {
|
|
416
|
+
const nestingPrefix = getNestingPrefix(field);
|
|
417
|
+
return `${FavoritePropertiesManager.FAVORITES_IDENTIFIER_PREFIX}${nestingPrefix}${field.contentClassInfo.name}`;
|
|
418
|
+
};
|
|
419
|
+
const getNestingPrefix = (field) => {
|
|
420
|
+
const path = [];
|
|
421
|
+
let curr = field;
|
|
422
|
+
while (curr !== undefined) {
|
|
423
|
+
curr.pathToPrimaryClass.forEach((rel) => {
|
|
424
|
+
// Relationship directions are reversed, because we are generating a relationship list starting from the parent
|
|
425
|
+
path.push(`${rel.isForwardRelationship ? "B" : "F"}:${rel.relationshipInfo.name}`);
|
|
426
|
+
path.push(rel.targetClassInfo.name);
|
|
427
|
+
});
|
|
428
|
+
curr = curr.parent;
|
|
429
|
+
}
|
|
430
|
+
if (path.length === 0)
|
|
431
|
+
return "";
|
|
432
|
+
path.reverse();
|
|
433
|
+
return `${path.join("-")}-`;
|
|
434
|
+
};
|
|
435
|
+
const getPropertyClassName = (propertyName) => {
|
|
436
|
+
const propertyNameStart = propertyName.split("-")[0];
|
|
437
|
+
const parts = propertyNameStart.split(":").length;
|
|
438
|
+
if (parts === 1) // primitive
|
|
439
|
+
return undefined;
|
|
440
|
+
if (parts === 2) // nested property OR nested property parent class OR regular property parent class
|
|
441
|
+
return propertyNameStart;
|
|
442
|
+
// regular property without parent class
|
|
443
|
+
return propertyNameStart.substr(0, propertyName.lastIndexOf(":"));
|
|
444
|
+
};
|
|
445
|
+
/** @internal */
|
|
446
|
+
const getFieldInfos = (field) => {
|
|
447
|
+
const fieldInfos = new Set();
|
|
448
|
+
if (field.isPropertiesField())
|
|
449
|
+
getPropertiesFieldPropertyNames(field).forEach((info) => fieldInfos.add(info));
|
|
450
|
+
else if (field.isNestedContentField())
|
|
451
|
+
fieldInfos.add(getNestedContentFieldPropertyName(field));
|
|
452
|
+
else
|
|
453
|
+
fieldInfos.add(`${FavoritePropertiesManager.FAVORITES_IDENTIFIER_PREFIX}${field.name}`);
|
|
454
|
+
return fieldInfos;
|
|
455
|
+
};
|
|
456
|
+
exports.getFieldInfos = getFieldInfos;
|
|
457
|
+
const setHasAny = (set, lookup) => {
|
|
458
|
+
for (const key of lookup) {
|
|
459
|
+
if (set.has(key))
|
|
460
|
+
return true;
|
|
461
|
+
}
|
|
462
|
+
return false;
|
|
463
|
+
};
|
|
464
|
+
const addOrderInfos = (dest, source) => {
|
|
465
|
+
source.forEach((si) => {
|
|
466
|
+
const index = dest.findIndex((di) => di.name === si.name);
|
|
467
|
+
if (index === -1) {
|
|
468
|
+
si.orderedTimestamp = new Date();
|
|
469
|
+
dest.push(si);
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
let priority = dest.length;
|
|
473
|
+
dest.forEach((info) => info.priority = priority--);
|
|
474
|
+
};
|
|
475
|
+
const removeOrderInfos = (container, toRemove) => {
|
|
476
|
+
toRemove.forEach((roi) => {
|
|
477
|
+
const index = container.findIndex((oi) => oi.name === roi.name);
|
|
478
|
+
/* istanbul ignore else */
|
|
479
|
+
if (index >= 0)
|
|
480
|
+
container.splice(index, 1);
|
|
481
|
+
});
|
|
482
|
+
};
|
|
483
|
+
/** @internal */
|
|
484
|
+
const createFieldOrderInfos = (field) => {
|
|
485
|
+
if (field.isNestedContentField()) {
|
|
486
|
+
const propertyName = getNestedContentFieldPropertyName(field);
|
|
487
|
+
return [{
|
|
488
|
+
parentClassName: getPropertyClassName(propertyName),
|
|
489
|
+
name: propertyName,
|
|
490
|
+
priority: 0,
|
|
491
|
+
orderedTimestamp: new Date(),
|
|
492
|
+
}];
|
|
493
|
+
}
|
|
494
|
+
if (field.isPropertiesField()) {
|
|
495
|
+
return getPropertiesFieldPropertyNames(field).map((propertyName) => ({
|
|
496
|
+
parentClassName: getPropertyClassName(propertyName),
|
|
497
|
+
name: propertyName,
|
|
498
|
+
priority: 0,
|
|
499
|
+
orderedTimestamp: new Date(),
|
|
500
|
+
}));
|
|
501
|
+
}
|
|
502
|
+
return [{
|
|
503
|
+
parentClassName: undefined,
|
|
504
|
+
name: field.name,
|
|
505
|
+
priority: 0,
|
|
506
|
+
orderedTimestamp: new Date(),
|
|
507
|
+
}];
|
|
508
|
+
};
|
|
509
|
+
exports.createFieldOrderInfos = createFieldOrderInfos;
|
|
510
|
+
const getFieldOrderInfos = (field, orderInfos) => {
|
|
511
|
+
const fieldOrderInfos = [];
|
|
512
|
+
const tryAddOrderInfo = (name) => {
|
|
513
|
+
const fieldOrderInfo = orderInfos.find((oi) => oi.name === name);
|
|
514
|
+
if (fieldOrderInfo !== undefined)
|
|
515
|
+
fieldOrderInfos.push(fieldOrderInfo);
|
|
516
|
+
};
|
|
517
|
+
if (field.isPropertiesField())
|
|
518
|
+
getPropertiesFieldPropertyNames(field).forEach(tryAddOrderInfo);
|
|
519
|
+
else if (field.isNestedContentField())
|
|
520
|
+
tryAddOrderInfo(getNestedContentFieldPropertyName(field));
|
|
521
|
+
else
|
|
522
|
+
tryAddOrderInfo(field.name);
|
|
523
|
+
return fieldOrderInfos;
|
|
524
|
+
};
|
|
525
|
+
const getMostRecentOrderInfo = (orderInfos) => orderInfos.reduce((recent, curr) => (recent && recent.orderedTimestamp >= curr.orderedTimestamp) ? recent : curr);
|
|
526
526
|
//# sourceMappingURL=FavoritePropertiesManager.js.map
|