@itwin/core-frontend 5.9.0-dev.9 → 5.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -1
- package/lib/cjs/BriefcaseConnection.d.ts +2 -2
- package/lib/cjs/BriefcaseConnection.js +2 -2
- package/lib/cjs/BriefcaseConnection.js.map +1 -1
- package/lib/cjs/PerModelCategoryVisibility.d.ts.map +1 -1
- package/lib/cjs/PerModelCategoryVisibility.js +83 -81
- package/lib/cjs/PerModelCategoryVisibility.js.map +1 -1
- package/lib/cjs/SpatialViewState.d.ts.map +1 -1
- package/lib/cjs/SpatialViewState.js +3 -1
- package/lib/cjs/SpatialViewState.js.map +1 -1
- package/lib/cjs/ViewState.d.ts +32 -0
- package/lib/cjs/ViewState.d.ts.map +1 -1
- package/lib/cjs/ViewState.js +41 -4
- package/lib/cjs/ViewState.js.map +1 -1
- package/lib/cjs/internal/render/webgl/FrustumUniforms.d.ts.map +1 -1
- package/lib/cjs/internal/render/webgl/FrustumUniforms.js.map +1 -1
- package/lib/cjs/internal/render/webgl/RenderCommands.js.map +1 -1
- package/lib/cjs/quantity-formatting/AlternateUnitLabels.d.ts +8 -0
- package/lib/cjs/quantity-formatting/AlternateUnitLabels.d.ts.map +1 -0
- package/lib/cjs/quantity-formatting/AlternateUnitLabels.js +26 -0
- package/lib/cjs/quantity-formatting/AlternateUnitLabels.js.map +1 -0
- package/lib/cjs/quantity-formatting/QuantityFormatter.d.ts +119 -22
- package/lib/cjs/quantity-formatting/QuantityFormatter.d.ts.map +1 -1
- package/lib/cjs/quantity-formatting/QuantityFormatter.js +271 -65
- package/lib/cjs/quantity-formatting/QuantityFormatter.js.map +1 -1
- package/lib/cjs/tile/GltfReader.js.map +1 -1
- package/lib/cjs/tile/map/MapLayerFormatRegistry.js.map +1 -1
- package/lib/cjs/tools/PrimitiveTool.d.ts +1 -1
- package/lib/cjs/tools/PrimitiveTool.js +1 -1
- package/lib/cjs/tools/PrimitiveTool.js.map +1 -1
- package/lib/cjs/tools/ToolAdmin.d.ts +16 -1
- package/lib/cjs/tools/ToolAdmin.d.ts.map +1 -1
- package/lib/cjs/tools/ToolAdmin.js +52 -5
- package/lib/cjs/tools/ToolAdmin.js.map +1 -1
- package/lib/cjs/tools/ToolSettings.d.ts +4 -0
- package/lib/cjs/tools/ToolSettings.d.ts.map +1 -1
- package/lib/cjs/tools/ToolSettings.js +4 -0
- package/lib/cjs/tools/ToolSettings.js.map +1 -1
- package/lib/esm/BriefcaseConnection.d.ts +2 -2
- package/lib/esm/BriefcaseConnection.js +2 -2
- package/lib/esm/BriefcaseConnection.js.map +1 -1
- package/lib/esm/PerModelCategoryVisibility.d.ts.map +1 -1
- package/lib/esm/PerModelCategoryVisibility.js +84 -82
- package/lib/esm/PerModelCategoryVisibility.js.map +1 -1
- package/lib/esm/SpatialViewState.d.ts.map +1 -1
- package/lib/esm/SpatialViewState.js +3 -1
- package/lib/esm/SpatialViewState.js.map +1 -1
- package/lib/esm/ViewState.d.ts +32 -0
- package/lib/esm/ViewState.d.ts.map +1 -1
- package/lib/esm/ViewState.js +42 -5
- package/lib/esm/ViewState.js.map +1 -1
- package/lib/esm/internal/render/webgl/FrustumUniforms.d.ts.map +1 -1
- package/lib/esm/internal/render/webgl/FrustumUniforms.js.map +1 -1
- package/lib/esm/internal/render/webgl/RenderCommands.js.map +1 -1
- package/lib/esm/quantity-formatting/AlternateUnitLabels.d.ts +8 -0
- package/lib/esm/quantity-formatting/AlternateUnitLabels.d.ts.map +1 -0
- package/lib/esm/quantity-formatting/AlternateUnitLabels.js +23 -0
- package/lib/esm/quantity-formatting/AlternateUnitLabels.js.map +1 -0
- package/lib/esm/quantity-formatting/QuantityFormatter.d.ts +119 -22
- package/lib/esm/quantity-formatting/QuantityFormatter.d.ts.map +1 -1
- package/lib/esm/quantity-formatting/QuantityFormatter.js +269 -63
- package/lib/esm/quantity-formatting/QuantityFormatter.js.map +1 -1
- package/lib/esm/tile/GltfReader.js.map +1 -1
- package/lib/esm/tile/map/MapLayerFormatRegistry.js.map +1 -1
- package/lib/esm/tools/PrimitiveTool.d.ts +1 -1
- package/lib/esm/tools/PrimitiveTool.js +1 -1
- package/lib/esm/tools/PrimitiveTool.js.map +1 -1
- package/lib/esm/tools/ToolAdmin.d.ts +16 -1
- package/lib/esm/tools/ToolAdmin.d.ts.map +1 -1
- package/lib/esm/tools/ToolAdmin.js +52 -5
- package/lib/esm/tools/ToolAdmin.js.map +1 -1
- package/lib/esm/tools/ToolSettings.d.ts +4 -0
- package/lib/esm/tools/ToolSettings.d.ts.map +1 -1
- package/lib/esm/tools/ToolSettings.js +4 -0
- package/lib/esm/tools/ToolSettings.js.map +1 -1
- package/lib/public/scripts/parse-imdl-worker.js +1 -1
- package/lib/workers/webpack/parse-imdl-worker.js +1 -1
- package/package.json +20 -20
- package/lib/cjs/quantity-formatting/BasicUnitsProvider.d.ts +0 -38
- package/lib/cjs/quantity-formatting/BasicUnitsProvider.d.ts.map +0 -1
- package/lib/cjs/quantity-formatting/BasicUnitsProvider.js +0 -160
- package/lib/cjs/quantity-formatting/BasicUnitsProvider.js.map +0 -1
- package/lib/esm/quantity-formatting/BasicUnitsProvider.d.ts +0 -38
- package/lib/esm/quantity-formatting/BasicUnitsProvider.d.ts.map +0 -1
- package/lib/esm/quantity-formatting/BasicUnitsProvider.js +0 -155
- package/lib/esm/quantity-formatting/BasicUnitsProvider.js.map +0 -1
|
@@ -14,7 +14,7 @@ const core_bentley_1 = require("@itwin/core-bentley");
|
|
|
14
14
|
const core_quantity_1 = require("@itwin/core-quantity");
|
|
15
15
|
const FrontendLoggerCategory_1 = require("../common/FrontendLoggerCategory");
|
|
16
16
|
const IModelApp_1 = require("../IModelApp");
|
|
17
|
-
const
|
|
17
|
+
const AlternateUnitLabels_1 = require("./AlternateUnitLabels");
|
|
18
18
|
// cSpell:ignore FORMATPROPS FORMATKEY ussurvey uscustomary USCUSTOM
|
|
19
19
|
/**
|
|
20
20
|
* Defines standard format types for tools that need to display measurements to user.
|
|
@@ -168,7 +168,7 @@ class QuantityTypeFormatsProvider {
|
|
|
168
168
|
["CivilUnits.LENGTH", QuantityType.LengthEngineering],
|
|
169
169
|
["AecUnits.LENGTH", QuantityType.LengthEngineering]
|
|
170
170
|
]);
|
|
171
|
-
async getFormat(name) {
|
|
171
|
+
async getFormat(name, _system) {
|
|
172
172
|
const quantityType = this._kindOfQuantityMap.get(name);
|
|
173
173
|
if (!quantityType)
|
|
174
174
|
return undefined;
|
|
@@ -184,39 +184,37 @@ exports.QuantityTypeFormatsProvider = QuantityTypeFormatsProvider;
|
|
|
184
184
|
class FormatsProviderManager {
|
|
185
185
|
_formatsProvider;
|
|
186
186
|
onFormatsChanged = new core_bentley_1.BeEvent();
|
|
187
|
+
_removeProviderListener;
|
|
187
188
|
constructor(_formatsProvider) {
|
|
188
189
|
this._formatsProvider = _formatsProvider;
|
|
189
|
-
this._formatsProvider.onFormatsChanged.addListener((args) => {
|
|
190
|
+
this._removeProviderListener = this._formatsProvider.onFormatsChanged.addListener((args) => {
|
|
190
191
|
this.onFormatsChanged.raiseEvent(args);
|
|
191
192
|
});
|
|
192
193
|
}
|
|
193
|
-
async getFormat(name) {
|
|
194
|
-
return this._formatsProvider.getFormat(name);
|
|
194
|
+
async getFormat(name, system) {
|
|
195
|
+
return this._formatsProvider.getFormat(name, system);
|
|
195
196
|
}
|
|
196
197
|
get formatsProvider() { return this; }
|
|
197
198
|
set formatsProvider(formatsProvider) {
|
|
199
|
+
this._removeProviderListener?.();
|
|
198
200
|
this._formatsProvider = formatsProvider;
|
|
199
|
-
this._formatsProvider.onFormatsChanged.addListener((args) => {
|
|
201
|
+
this._removeProviderListener = this._formatsProvider.onFormatsChanged.addListener((args) => {
|
|
200
202
|
this.onFormatsChanged.raiseEvent(args);
|
|
201
203
|
});
|
|
202
204
|
this.onFormatsChanged.raiseEvent({ formatsChanged: "all" });
|
|
203
205
|
}
|
|
204
206
|
}
|
|
205
207
|
exports.FormatsProviderManager = FormatsProviderManager;
|
|
206
|
-
/**
|
|
207
|
-
* the "active" unit system and caches FormatterSpecs and ParserSpecs for the "active" unit system to allow synchronous access to
|
|
208
|
-
* parsing and formatting values. The support unit systems are defined by [[UnitSystemKey]] and is kept in synch with the unit systems
|
|
209
|
-
* provided by the Presentation Manager on the backend. The QuantityFormatter contains a registry of quantity type definitions. These definitions implement
|
|
210
|
-
* the [[QuantityTypeDefinition]] interface, which among other things, provide default [[FormatProps]], and provide methods
|
|
211
|
-
* to generate both a [[FormatterSpec]] and a [[ParserSpec]]. There are built-in quantity types that are
|
|
208
|
+
/** The QuantityFormatter class provides methods for formatting and parsing quantities. There are a set of standard quantity types
|
|
212
209
|
* identified by the [[QuantityType]] enum. [[CustomQuantityTypeDefinition]] can be registered to extend the available quantity types available
|
|
213
210
|
* by frontend tools. The QuantityFormatter also allows the default formats to be overriden.
|
|
214
211
|
*
|
|
215
212
|
* @public
|
|
216
213
|
*/
|
|
217
214
|
class QuantityFormatter {
|
|
218
|
-
|
|
219
|
-
|
|
215
|
+
static _allUnitSystems = ["metric", "imperial", "usCustomary", "usSurvey"];
|
|
216
|
+
_unitsProvider = new core_quantity_1.BasicUnitsProvider();
|
|
217
|
+
_alternateUnitLabelsRegistry = new AlternateUnitLabelsRegistry((0, AlternateUnitLabels_1.getDefaultAlternateUnitLabels)());
|
|
220
218
|
/** Registry containing available quantity type definitions. */
|
|
221
219
|
_quantityTypeRegistry = new Map();
|
|
222
220
|
/** Registry containing available FormatterSpec and ParserSpec, mapped by keys.
|
|
@@ -225,9 +223,9 @@ class QuantityFormatter {
|
|
|
225
223
|
_formatSpecsRegistry = new Map();
|
|
226
224
|
/** Active UnitSystem key - must be one of "imperial", "metric", "usCustomary", or "usSurvey". */
|
|
227
225
|
_activeUnitSystem = "imperial";
|
|
228
|
-
/** Map of FormatSpecs for all available QuantityTypes
|
|
226
|
+
/** Map of FormatSpecs for all available QuantityTypes, keyed by quantity type */
|
|
229
227
|
_activeFormatSpecsByType = new Map();
|
|
230
|
-
/** Map of ParserSpecs for all available QuantityTypes
|
|
228
|
+
/** Map of ParserSpecs for all available QuantityTypes, keyed by quantity type */
|
|
231
229
|
_activeParserSpecsByType = new Map();
|
|
232
230
|
/** Map of FormatSpecs that have been overriden from the default. */
|
|
233
231
|
_overrideFormatPropsByUnitSystem = new Map();
|
|
@@ -249,6 +247,45 @@ class QuantityFormatter {
|
|
|
249
247
|
onQuantityFormatsChanged = new core_bentley_1.BeUiEvent();
|
|
250
248
|
/** Fired when the active UnitsProvider is updated. This will allow cached Formatter and Parser specs to be updated if necessary. */
|
|
251
249
|
onUnitsProviderChanged = new core_bentley_1.BeUiEvent();
|
|
250
|
+
/** Fired after every reload path completes (initialization, unit system change, provider change, reinitialize).
|
|
251
|
+
* This is the terminal "ready" signal — it fires once the QuantityFormatter has fully rebuilt its caches and is
|
|
252
|
+
* ready to format/parse values. Subscribe to this event to know when formatting specs are available.
|
|
253
|
+
*
|
|
254
|
+
* Uses `BeUnorderedUiEvent` (Set-backed) so listeners can safely add/remove themselves during emit —
|
|
255
|
+
* this is critical for `FormatSpecHandle` instances that subscribe/dispose at volume.
|
|
256
|
+
* @beta
|
|
257
|
+
*/
|
|
258
|
+
onFormattingReady = new core_bentley_1.BeUnorderedUiEvent();
|
|
259
|
+
/** Event for formatting providers to register async work before the formatter signals ready.
|
|
260
|
+
* Fires synchronously after each reload completes. Providers should call
|
|
261
|
+
* `collector.addPendingWork(promise)` to register work. The formatter awaits all
|
|
262
|
+
* registered work (with a 20-second default timeout) before emitting [[onFormattingReady]].
|
|
263
|
+
*
|
|
264
|
+
* Use this for **providers** that need async loading. Use [[onFormattingReady]] for
|
|
265
|
+
* **consumers** that read specs.
|
|
266
|
+
* @beta
|
|
267
|
+
*/
|
|
268
|
+
onBeforeFormattingReady = new core_bentley_1.BeEvent();
|
|
269
|
+
/** Whether the QuantityFormatter has completed at least one successful reload and is ready to format/parse.
|
|
270
|
+
* @beta
|
|
271
|
+
*/
|
|
272
|
+
get isReady() { return this._isReady; }
|
|
273
|
+
/** A promise that resolves after the first successful initialization. This is one-shot — it resolves once
|
|
274
|
+
* and stays resolved forever. For subsequent reloads, subscribe to [[onFormattingReady]].
|
|
275
|
+
*
|
|
276
|
+
* This promise never rejects. If the first initialization attempt fails, it stays pending until a
|
|
277
|
+
* subsequent reload succeeds (e.g., triggered by `setUnitsProvider` or a format change). There is no
|
|
278
|
+
* finite retry limit, so rejection would prematurely close the door on recovery.
|
|
279
|
+
* @beta
|
|
280
|
+
*/
|
|
281
|
+
get whenInitialized() { return this._initializedPromise; }
|
|
282
|
+
_isReady = false;
|
|
283
|
+
_hasEverBeenReady = false;
|
|
284
|
+
_initializedPromise;
|
|
285
|
+
_resolveInitialized;
|
|
286
|
+
_reloadInFlight = false;
|
|
287
|
+
_pendingReload;
|
|
288
|
+
_deferredSystemChangedEmit;
|
|
252
289
|
_removeFormatsProviderListener;
|
|
253
290
|
/**
|
|
254
291
|
* constructor
|
|
@@ -256,6 +293,9 @@ class QuantityFormatter {
|
|
|
256
293
|
* set it to a specific unit system pass a UnitSystemKey.
|
|
257
294
|
*/
|
|
258
295
|
constructor(showMetricOrUnitSystem) {
|
|
296
|
+
this._initializedPromise = new Promise((resolve) => {
|
|
297
|
+
this._resolveInitialized = resolve;
|
|
298
|
+
});
|
|
259
299
|
if (undefined !== showMetricOrUnitSystem) {
|
|
260
300
|
if (typeof showMetricOrUnitSystem === "boolean")
|
|
261
301
|
this._activeUnitSystem = showMetricOrUnitSystem ? "metric" : "imperial";
|
|
@@ -269,6 +309,116 @@ class QuantityFormatter {
|
|
|
269
309
|
this._removeFormatsProviderListener = undefined;
|
|
270
310
|
}
|
|
271
311
|
}
|
|
312
|
+
/** Schedule an async reload. If no reload is in flight, runs immediately. If a reload is
|
|
313
|
+
* already in flight, stores the intent as pending (latest-wins: only the last scheduled reload
|
|
314
|
+
* is kept). `finalizeReload()` fires only when the queue is fully drained.
|
|
315
|
+
*
|
|
316
|
+
* **Await semantics:** When a reload is already in flight, the returned promise resolves
|
|
317
|
+
* immediately *without* the requested reload having run. Callers that need to know when
|
|
318
|
+
* the reload has actually completed should listen for `onFormattingReady` instead.
|
|
319
|
+
*
|
|
320
|
+
* Reload intents:
|
|
321
|
+
* - `"full"` — rebuild entire registry + re-register provider listener (onInitialized, setUnitsProvider)
|
|
322
|
+
* - `"formatsChanged"` — patch registry from provider + load maps (formatsChanged listener)
|
|
323
|
+
* - `"activeSystem"` — reload format/parsing maps for current unit system (setActiveUnitSystem, reinitializeFormatAndParsingsMaps)
|
|
324
|
+
* @internal
|
|
325
|
+
*/
|
|
326
|
+
async scheduleReload(intent) {
|
|
327
|
+
if (this._reloadInFlight) {
|
|
328
|
+
// A reload is already running — queue this one (latest-wins)
|
|
329
|
+
this._pendingReload = intent;
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
this._reloadInFlight = true;
|
|
333
|
+
this._isReady = false;
|
|
334
|
+
this._deferredSystemChangedEmit = undefined; // Clear stale deferred from prior cycle
|
|
335
|
+
try {
|
|
336
|
+
await this._executeReload(intent);
|
|
337
|
+
}
|
|
338
|
+
catch (err) {
|
|
339
|
+
core_bentley_1.Logger.logError(`${FrontendLoggerCategory_1.FrontendLoggerCategory.Package}.QuantityFormatter`, core_bentley_1.BentleyError.getErrorMessage(err));
|
|
340
|
+
this._reloadInFlight = false;
|
|
341
|
+
// If there's a pending reload, still try to run it
|
|
342
|
+
if (this._pendingReload) {
|
|
343
|
+
const next = this._pendingReload;
|
|
344
|
+
this._pendingReload = undefined;
|
|
345
|
+
return this.scheduleReload(next);
|
|
346
|
+
}
|
|
347
|
+
// Restore prior ready state so stale-but-usable specs remain accessible
|
|
348
|
+
if (this._hasEverBeenReady) {
|
|
349
|
+
core_bentley_1.Logger.logWarning(`${FrontendLoggerCategory_1.FrontendLoggerCategory.Package}.QuantityFormatter`, "Reload failed — restoring previous ready state. Cached specs may be stale.");
|
|
350
|
+
this._isReady = true;
|
|
351
|
+
}
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
// Current reload succeeded — check if another was queued
|
|
355
|
+
if (this._pendingReload) {
|
|
356
|
+
const next = this._pendingReload;
|
|
357
|
+
this._pendingReload = undefined;
|
|
358
|
+
this._reloadInFlight = false;
|
|
359
|
+
return this.scheduleReload(next);
|
|
360
|
+
}
|
|
361
|
+
// Queue is drained — finalize
|
|
362
|
+
await this.finalizeReload();
|
|
363
|
+
// A new reload may have been queued during the async finalizeReload window
|
|
364
|
+
if (this._pendingReload) {
|
|
365
|
+
const next = this._pendingReload;
|
|
366
|
+
this._pendingReload = undefined;
|
|
367
|
+
this._reloadInFlight = false;
|
|
368
|
+
return this.scheduleReload(next);
|
|
369
|
+
}
|
|
370
|
+
this._reloadInFlight = false;
|
|
371
|
+
}
|
|
372
|
+
/** Execute the reload work for a given intent. All reload logic is centralized here.
|
|
373
|
+
* @internal
|
|
374
|
+
*/
|
|
375
|
+
async _executeReload(intent) {
|
|
376
|
+
switch (intent.scope) {
|
|
377
|
+
case "full":
|
|
378
|
+
await this._reloadCore();
|
|
379
|
+
break;
|
|
380
|
+
case "formatsChanged": {
|
|
381
|
+
const { args } = intent;
|
|
382
|
+
await this._rebuildRegistryFromProvider(args);
|
|
383
|
+
if (args.impliedUnitSystem && args.impliedUnitSystem !== this._activeUnitSystem) {
|
|
384
|
+
this._activeUnitSystem = args.impliedUnitSystem;
|
|
385
|
+
}
|
|
386
|
+
await this.loadFormatAndParsingMapsForSystem(this._activeUnitSystem);
|
|
387
|
+
if (args.impliedUnitSystem) {
|
|
388
|
+
this._deferredSystemChangedEmit = { system: this._activeUnitSystem };
|
|
389
|
+
}
|
|
390
|
+
break;
|
|
391
|
+
}
|
|
392
|
+
case "activeSystem":
|
|
393
|
+
await this.loadFormatAndParsingMapsForSystem(this._activeUnitSystem);
|
|
394
|
+
if (intent.emitSystemChanged) {
|
|
395
|
+
this._deferredSystemChangedEmit = { system: this._activeUnitSystem };
|
|
396
|
+
}
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
/** Called when the reload queue is fully drained after a successful reload.
|
|
401
|
+
* Sets `isReady` to true, resolves `whenInitialized` (one-shot), and emits ready events.
|
|
402
|
+
* @internal
|
|
403
|
+
*/
|
|
404
|
+
async finalizeReload() {
|
|
405
|
+
// Phase 1: Let providers register async work
|
|
406
|
+
const collector = new core_quantity_1.FormattingReadyCollector();
|
|
407
|
+
this.onBeforeFormattingReady.raiseEvent(collector);
|
|
408
|
+
await collector.awaitAll();
|
|
409
|
+
// Phase 2: Signal ready to consumers
|
|
410
|
+
this._isReady = true;
|
|
411
|
+
this._hasEverBeenReady = true;
|
|
412
|
+
this._resolveInitialized();
|
|
413
|
+
this.onFormattingReady.emit();
|
|
414
|
+
// Phase 3: Emit deferred unit-system-changed if the winning reload set one.
|
|
415
|
+
// This fires after isReady === true so listeners can safely use the formatter.
|
|
416
|
+
if (this._deferredSystemChangedEmit) {
|
|
417
|
+
const args = this._deferredSystemChangedEmit;
|
|
418
|
+
this._deferredSystemChangedEmit = undefined;
|
|
419
|
+
this.onActiveFormattingUnitSystemChanged.emit(args);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
272
422
|
getOverrideFormatPropsByQuantityType(quantityTypeKey, unitSystem) {
|
|
273
423
|
const requestedUnitSystem = unitSystem ?? this.activeUnitSystem;
|
|
274
424
|
const overrideMap = this._overrideFormatPropsByUnitSystem.get(requestedUnitSystem);
|
|
@@ -395,6 +545,12 @@ class QuantityFormatter {
|
|
|
395
545
|
* @internal
|
|
396
546
|
*/
|
|
397
547
|
async onInitialized() {
|
|
548
|
+
await this.scheduleReload({ scope: "full" });
|
|
549
|
+
}
|
|
550
|
+
/** Core reload logic — does all async I/O and cache rebuilding without events or state management.
|
|
551
|
+
* @internal
|
|
552
|
+
*/
|
|
553
|
+
async _reloadCore() {
|
|
398
554
|
// Remove any existing listener before re-registering to avoid duplicates when called via setUnitsProvider.
|
|
399
555
|
if (this._removeFormatsProviderListener) {
|
|
400
556
|
this._removeFormatsProviderListener();
|
|
@@ -403,46 +559,61 @@ class QuantityFormatter {
|
|
|
403
559
|
await this.initializeQuantityTypesRegistry();
|
|
404
560
|
const initialKoQs = [["DefaultToolsUnits.LENGTH", "Units.M"], ["DefaultToolsUnits.ANGLE", "Units.RAD"], ["DefaultToolsUnits.AREA", "Units.SQ_M"], ["DefaultToolsUnits.VOLUME", "Units.CUB_M"], ["DefaultToolsUnits.LENGTH_COORDINATE", "Units.M"], ["CivilUnits.STATION", "Units.M"], ["CivilUnits.LENGTH", "Units.M"], ["AecUnits.LENGTH", "Units.M"]];
|
|
405
561
|
for (const entry of initialKoQs) {
|
|
406
|
-
|
|
407
|
-
|
|
562
|
+
for (const system of QuantityFormatter._allUnitSystems) {
|
|
563
|
+
try {
|
|
564
|
+
await this.addFormattingSpecsToRegistry({ name: entry[0], persistenceUnitName: entry[1], system });
|
|
565
|
+
}
|
|
566
|
+
catch (err) {
|
|
567
|
+
core_bentley_1.Logger.logWarning(`${FrontendLoggerCategory_1.FrontendLoggerCategory.Package}.QuantityFormatter`, err.toString());
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
// Register formatsProvider listener that triggers a queued reload when formats change
|
|
572
|
+
this._removeFormatsProviderListener = IModelApp_1.IModelApp.formatsProvider.onFormatsChanged.addListener((args) => {
|
|
573
|
+
void this.scheduleReload({ scope: "formatsChanged", args });
|
|
574
|
+
});
|
|
575
|
+
// initialize default format and parsing specs
|
|
576
|
+
await this.loadFormatAndParsingMapsForSystem();
|
|
577
|
+
}
|
|
578
|
+
/** Rebuild the formatting specs registry from the current formatsProvider based on changed args.
|
|
579
|
+
* @internal
|
|
580
|
+
*/
|
|
581
|
+
async _rebuildRegistryFromProvider(args) {
|
|
582
|
+
if (args.formatsChanged === "all") {
|
|
583
|
+
for (const [name, unitMap] of this._formatSpecsRegistry.entries()) {
|
|
584
|
+
await this._rebuildRegistryForName(name, unitMap);
|
|
408
585
|
}
|
|
409
|
-
|
|
410
|
-
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
for (const name of args.formatsChanged) {
|
|
589
|
+
const unitMap = this._formatSpecsRegistry.get(name);
|
|
590
|
+
if (unitMap) {
|
|
591
|
+
await this._rebuildRegistryForName(name, unitMap);
|
|
592
|
+
}
|
|
411
593
|
}
|
|
412
594
|
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
}
|
|
595
|
+
}
|
|
596
|
+
/** Rebuild all system entries for a single KoQ name in the registry. */
|
|
597
|
+
async _rebuildRegistryForName(name, unitMap) {
|
|
598
|
+
let anySystemHadFormat = false;
|
|
599
|
+
for (const system of QuantityFormatter._allUnitSystems) {
|
|
600
|
+
const formatProps = await IModelApp_1.IModelApp.formatsProvider.getFormat(name, system);
|
|
601
|
+
if (formatProps) {
|
|
602
|
+
anySystemHadFormat = true;
|
|
603
|
+
for (const [persistenceUnitName] of unitMap.entries()) {
|
|
604
|
+
await this.addFormattingSpecsToRegistry({ name, persistenceUnitName, formatProps, system });
|
|
424
605
|
}
|
|
425
606
|
}
|
|
426
607
|
else {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
if (formatProps) {
|
|
431
|
-
const existingEntry = this._formatSpecsRegistry.get(name);
|
|
432
|
-
if (existingEntry) {
|
|
433
|
-
const persistenceUnitName = existingEntry.formatterSpec.persistenceUnit.name;
|
|
434
|
-
await this.addFormattingSpecsToRegistry(name, persistenceUnitName, formatProps);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
else {
|
|
438
|
-
this._formatSpecsRegistry.delete(name);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
608
|
+
// Remove stale entries for this system
|
|
609
|
+
for (const [, systemMap] of unitMap.entries()) {
|
|
610
|
+
systemMap.delete(system);
|
|
441
611
|
}
|
|
442
612
|
}
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
|
|
613
|
+
}
|
|
614
|
+
if (!anySystemHadFormat) {
|
|
615
|
+
this._formatSpecsRegistry.delete(name);
|
|
616
|
+
}
|
|
446
617
|
}
|
|
447
618
|
/** Return a map that serves as a registry of all standard and custom quantity types. */
|
|
448
619
|
get quantityTypesRegistry() {
|
|
@@ -477,7 +648,7 @@ class QuantityFormatter {
|
|
|
477
648
|
this._unitsProvider = unitsProvider;
|
|
478
649
|
try {
|
|
479
650
|
// force all cached data to be reinitialized
|
|
480
|
-
await
|
|
651
|
+
await this.scheduleReload({ scope: "full" });
|
|
481
652
|
}
|
|
482
653
|
catch (err) {
|
|
483
654
|
core_bentley_1.Logger.logWarning(`${FrontendLoggerCategory_1.FrontendLoggerCategory.Package}.quantityFormatter`, core_bentley_1.BentleyError.getErrorMessage(err), core_bentley_1.BentleyError.getErrorMetadata(err));
|
|
@@ -495,9 +666,10 @@ class QuantityFormatter {
|
|
|
495
666
|
* `IModelApp.toolAdmin.restartPrimitiveTool()` to allow the tool to reinitialize itself.
|
|
496
667
|
*/
|
|
497
668
|
async resetToUseInternalUnitsProvider() {
|
|
498
|
-
|
|
669
|
+
// Coupled to createUnitsProvider() returning BasicUnitsProvider directly when no primary is set.
|
|
670
|
+
if (this._unitsProvider instanceof core_quantity_1.BasicUnitsProvider)
|
|
499
671
|
return;
|
|
500
|
-
await this.setUnitsProvider(new
|
|
672
|
+
await this.setUnitsProvider(new core_quantity_1.BasicUnitsProvider());
|
|
501
673
|
}
|
|
502
674
|
/** Async call to register a CustomQuantityType and load the FormatSpec and ParserSpec for the new type. */
|
|
503
675
|
async registerQuantityType(entry, replace) {
|
|
@@ -524,8 +696,7 @@ class QuantityFormatter {
|
|
|
524
696
|
this._overrideFormatPropsByUnitSystem = overrideFormatPropsByUnitSystem;
|
|
525
697
|
}
|
|
526
698
|
unitSystemKey && (this._activeUnitSystem = unitSystemKey);
|
|
527
|
-
await this.
|
|
528
|
-
fireUnitSystemChanged && this.onActiveFormattingUnitSystemChanged.emit({ system: this._activeUnitSystem });
|
|
699
|
+
await this.scheduleReload({ scope: "activeSystem", emitSystemChanged: fireUnitSystemChanged });
|
|
529
700
|
IModelApp_1.IModelApp.toolAdmin && startDefaultTool && await IModelApp_1.IModelApp.toolAdmin.startDefaultTool();
|
|
530
701
|
}
|
|
531
702
|
/** Set the Active unit system to one of the supported types. This will asynchronously load the formatter and parser specs for the activated system. */
|
|
@@ -538,11 +709,9 @@ class QuantityFormatter {
|
|
|
538
709
|
if (this._activeUnitSystem === systemType)
|
|
539
710
|
return;
|
|
540
711
|
this._activeUnitSystem = systemType;
|
|
541
|
-
await this.
|
|
712
|
+
await this.scheduleReload({ scope: "activeSystem", emitSystemChanged: true });
|
|
542
713
|
// allow settings provider to store the change
|
|
543
714
|
await this._unitFormattingSettingsProvider?.storeUnitSystemSetting({ system: systemType });
|
|
544
|
-
// fire current event
|
|
545
|
-
this.onActiveFormattingUnitSystemChanged.emit({ system: systemType });
|
|
546
715
|
if (IModelApp_1.IModelApp.toolAdmin && restartActiveTool)
|
|
547
716
|
return IModelApp_1.IModelApp.toolAdmin.startDefaultTool();
|
|
548
717
|
}
|
|
@@ -788,7 +957,10 @@ class QuantityFormatter {
|
|
|
788
957
|
}
|
|
789
958
|
/** Returns data needed to convert from one Unit to another in the same Unit Family/Phenomenon. */
|
|
790
959
|
async getConversion(fromUnit, toUnit) {
|
|
791
|
-
|
|
960
|
+
const result = await this._unitsProvider.getConversion(fromUnit, toUnit);
|
|
961
|
+
if (result.error)
|
|
962
|
+
core_bentley_1.Logger.logWarning(`${FrontendLoggerCategory_1.FrontendLoggerCategory.Package}.quantityFormatter`, `Unit conversion from "${fromUnit.name}" to "${toUnit.name}" could not be resolved.`);
|
|
963
|
+
return result;
|
|
792
964
|
}
|
|
793
965
|
/**
|
|
794
966
|
* Creates a [[FormatterSpec]] for a given persistence unit name and format properties, using the [[UnitsProvider]] to resolve the persistence unit.
|
|
@@ -814,21 +986,50 @@ class QuantityFormatter {
|
|
|
814
986
|
}
|
|
815
987
|
/**
|
|
816
988
|
* @beta
|
|
817
|
-
* Returns a [[FormattingSpecEntry]] for a given name, typically a KindOfQuantity full name.
|
|
989
|
+
* Returns a map of [[FormattingSpecEntry]] keyed by persistence unit for a given name, typically a KindOfQuantity full name.
|
|
818
990
|
*/
|
|
819
991
|
getSpecsByName(name) {
|
|
820
|
-
|
|
992
|
+
const unitMap = this._formatSpecsRegistry.get(name);
|
|
993
|
+
if (!unitMap)
|
|
994
|
+
return undefined;
|
|
995
|
+
// Return active-system projection
|
|
996
|
+
const result = new Map();
|
|
997
|
+
for (const [persistenceUnit, systemMap] of unitMap) {
|
|
998
|
+
const entry = systemMap.get(this._activeUnitSystem);
|
|
999
|
+
if (entry)
|
|
1000
|
+
result.set(persistenceUnit, entry);
|
|
1001
|
+
}
|
|
1002
|
+
return result.size > 0 ? result : undefined;
|
|
1003
|
+
}
|
|
1004
|
+
/**
|
|
1005
|
+
* Returns a [[FormattingSpecEntry]] for a given name and persistence unit.
|
|
1006
|
+
* @beta
|
|
1007
|
+
*/
|
|
1008
|
+
getSpecsByNameAndUnit(args) {
|
|
1009
|
+
const effectiveSystem = args.system ?? this._activeUnitSystem;
|
|
1010
|
+
return this._formatSpecsRegistry.get(args.name)?.get(args.persistenceUnitName)?.get(effectiveSystem);
|
|
1011
|
+
}
|
|
1012
|
+
/** Create a cacheable handle to formatting specs for a specific KoQ and persistence unit.
|
|
1013
|
+
* The handle auto-refreshes when the QuantityFormatter reloads. Call `dispose()` when done.
|
|
1014
|
+
*
|
|
1015
|
+
* @param koqName - The KindOfQuantity name (e.g., "DefaultToolsUnits.LENGTH")
|
|
1016
|
+
* @param persistenceUnit - The persistence unit name (e.g., "Units.M")
|
|
1017
|
+
* @returns A FormatSpecHandle that auto-updates on reload
|
|
1018
|
+
* @beta
|
|
1019
|
+
*/
|
|
1020
|
+
getFormatSpecHandle(koqName, persistenceUnit, system) {
|
|
1021
|
+
return new core_quantity_1.FormatSpecHandle({ provider: this, name: koqName, persistenceUnitName: persistenceUnit, system });
|
|
821
1022
|
}
|
|
822
1023
|
/**
|
|
823
1024
|
* Populates the registry with a new FormatterSpec and ParserSpec entry for the given format name.
|
|
824
1025
|
* @beta
|
|
825
|
-
* @param name The key used to identify the formatter and parser spec
|
|
826
|
-
* @param persistenceUnitName The name of the persistence unit
|
|
827
|
-
* @param formatProps If not supplied, tries to retrieve the [[FormatProps]] from [[IModelApp.formatsProvider]]
|
|
828
1026
|
*/
|
|
829
|
-
async addFormattingSpecsToRegistry(
|
|
1027
|
+
async addFormattingSpecsToRegistry(args) {
|
|
1028
|
+
const { name, persistenceUnitName } = args;
|
|
1029
|
+
const effectiveSystem = args.system ?? this._activeUnitSystem;
|
|
1030
|
+
let formatProps = args.formatProps;
|
|
830
1031
|
if (!formatProps) {
|
|
831
|
-
formatProps = await IModelApp_1.IModelApp.formatsProvider.getFormat(name);
|
|
1032
|
+
formatProps = await IModelApp_1.IModelApp.formatsProvider.getFormat(name, effectiveSystem);
|
|
832
1033
|
}
|
|
833
1034
|
if (formatProps) {
|
|
834
1035
|
const formatterSpec = await this.createFormatterSpec({
|
|
@@ -841,7 +1042,12 @@ class QuantityFormatter {
|
|
|
841
1042
|
formatProps,
|
|
842
1043
|
formatName: name,
|
|
843
1044
|
});
|
|
844
|
-
this._formatSpecsRegistry.
|
|
1045
|
+
if (!this._formatSpecsRegistry.has(name))
|
|
1046
|
+
this._formatSpecsRegistry.set(name, new Map());
|
|
1047
|
+
const unitMap = this._formatSpecsRegistry.get(name);
|
|
1048
|
+
if (!unitMap.has(persistenceUnitName))
|
|
1049
|
+
unitMap.set(persistenceUnitName, new Map());
|
|
1050
|
+
unitMap.get(persistenceUnitName).set(effectiveSystem, { formatterSpec, parserSpec });
|
|
845
1051
|
}
|
|
846
1052
|
else {
|
|
847
1053
|
throw new Error(`Unable to find format properties for ${name} with persistence unit ${persistenceUnitName}`);
|