@itwin/core-backend 5.14.0-dev.21 → 5.14.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.
Files changed (67) hide show
  1. package/lib/cjs/ECDb.d.ts +11 -6
  2. package/lib/cjs/ECDb.d.ts.map +1 -1
  3. package/lib/cjs/ECDb.js +11 -6
  4. package/lib/cjs/ECDb.js.map +1 -1
  5. package/lib/cjs/ECSqlStatement.d.ts +2 -2
  6. package/lib/cjs/ECSqlStatement.js +2 -2
  7. package/lib/cjs/ECSqlStatement.js.map +1 -1
  8. package/lib/cjs/IModelDb.d.ts +12 -10
  9. package/lib/cjs/IModelDb.d.ts.map +1 -1
  10. package/lib/cjs/IModelDb.js +19 -12
  11. package/lib/cjs/IModelDb.js.map +1 -1
  12. package/lib/cjs/annotations/ElementDrivesTextAnnotation.d.ts +90 -5
  13. package/lib/cjs/annotations/ElementDrivesTextAnnotation.d.ts.map +1 -1
  14. package/lib/cjs/annotations/ElementDrivesTextAnnotation.js +59 -1
  15. package/lib/cjs/annotations/ElementDrivesTextAnnotation.js.map +1 -1
  16. package/lib/cjs/internal/annotations/fieldSpecs.d.ts +82 -0
  17. package/lib/cjs/internal/annotations/fieldSpecs.d.ts.map +1 -0
  18. package/lib/cjs/internal/annotations/fieldSpecs.js +187 -0
  19. package/lib/cjs/internal/annotations/fieldSpecs.js.map +1 -0
  20. package/lib/cjs/internal/annotations/fields.d.ts +39 -2
  21. package/lib/cjs/internal/annotations/fields.d.ts.map +1 -1
  22. package/lib/cjs/internal/annotations/fields.js +140 -13
  23. package/lib/cjs/internal/annotations/fields.js.map +1 -1
  24. package/lib/esm/ECDb.d.ts +11 -6
  25. package/lib/esm/ECDb.d.ts.map +1 -1
  26. package/lib/esm/ECDb.js +11 -6
  27. package/lib/esm/ECDb.js.map +1 -1
  28. package/lib/esm/ECSqlStatement.d.ts +2 -2
  29. package/lib/esm/ECSqlStatement.js +2 -2
  30. package/lib/esm/ECSqlStatement.js.map +1 -1
  31. package/lib/esm/IModelDb.d.ts +12 -10
  32. package/lib/esm/IModelDb.d.ts.map +1 -1
  33. package/lib/esm/IModelDb.js +19 -12
  34. package/lib/esm/IModelDb.js.map +1 -1
  35. package/lib/esm/annotations/ElementDrivesTextAnnotation.d.ts +90 -5
  36. package/lib/esm/annotations/ElementDrivesTextAnnotation.d.ts.map +1 -1
  37. package/lib/esm/annotations/ElementDrivesTextAnnotation.js +60 -2
  38. package/lib/esm/annotations/ElementDrivesTextAnnotation.js.map +1 -1
  39. package/lib/esm/internal/annotations/fieldSpecs.d.ts +82 -0
  40. package/lib/esm/internal/annotations/fieldSpecs.d.ts.map +1 -0
  41. package/lib/esm/internal/annotations/fieldSpecs.js +179 -0
  42. package/lib/esm/internal/annotations/fieldSpecs.js.map +1 -0
  43. package/lib/esm/internal/annotations/fields.d.ts +39 -2
  44. package/lib/esm/internal/annotations/fields.d.ts.map +1 -1
  45. package/lib/esm/internal/annotations/fields.js +140 -13
  46. package/lib/esm/internal/annotations/fields.js.map +1 -1
  47. package/lib/esm/test/AnnotationTestUtils.d.ts +9 -0
  48. package/lib/esm/test/AnnotationTestUtils.d.ts.map +1 -1
  49. package/lib/esm/test/AnnotationTestUtils.js +17 -0
  50. package/lib/esm/test/AnnotationTestUtils.js.map +1 -1
  51. package/lib/esm/test/annotations/FieldFormat.test.d.ts +2 -0
  52. package/lib/esm/test/annotations/FieldFormat.test.d.ts.map +1 -0
  53. package/lib/esm/test/annotations/FieldFormat.test.js +738 -0
  54. package/lib/esm/test/annotations/FieldFormat.test.js.map +1 -0
  55. package/lib/esm/test/annotations/FieldFormattingLifecycle.test.d.ts +2 -0
  56. package/lib/esm/test/annotations/FieldFormattingLifecycle.test.d.ts.map +1 -0
  57. package/lib/esm/test/annotations/FieldFormattingLifecycle.test.js +323 -0
  58. package/lib/esm/test/annotations/FieldFormattingLifecycle.test.js.map +1 -0
  59. package/lib/esm/test/annotations/FieldSpecs.test.d.ts +2 -0
  60. package/lib/esm/test/annotations/FieldSpecs.test.d.ts.map +1 -0
  61. package/lib/esm/test/annotations/FieldSpecs.test.js +54 -0
  62. package/lib/esm/test/annotations/FieldSpecs.test.js.map +1 -0
  63. package/lib/esm/test/annotations/Fields.test.js +221 -27
  64. package/lib/esm/test/annotations/Fields.test.js.map +1 -1
  65. package/lib/esm/test/ecdb/QueryReaders.test.js +105 -30
  66. package/lib/esm/test/ecdb/QueryReaders.test.js.map +1 -1
  67. package/package.json +15 -13
@@ -1,14 +1,51 @@
1
- import { FieldRun, FieldValue, RelationshipProps, TextBlock } from "@itwin/core-common";
1
+ import { FieldRun, RelationshipProps, TextBlock } from "@itwin/core-common";
2
2
  import { IModelDb } from "../../IModelDb";
3
3
  import { Id64String } from "@itwin/core-bentley";
4
+ import { FieldFormatting, ResolvedFieldValue } from "./fieldSpecs";
4
5
  import type { EditTxn } from "../../EditTxn";
6
+ /** The per-evaluation state [[updateField]] needs: which element's fields to recompute, how to
7
+ * read a field's property value, and how to format quantities.
8
+ */
5
9
  export interface UpdateFieldsContext {
10
+ /** When set, only fields whose [FieldPropertyHost.elementId]($common) matches are recomputed —
11
+ * an edit to one source element leaves the annotation's other fields untouched.
12
+ */
6
13
  readonly hostElementId: Id64String | undefined;
7
- getProperty(field: FieldRun): FieldValue | undefined;
14
+ /** Reads the value a field points at, or `undefined` if it cannot be resolved — including
15
+ * every field when the source element was deleted.
16
+ */
17
+ getProperty(field: FieldRun): ResolvedFieldValue | undefined;
18
+ /** The formats `"quantity"` and `"coordinate"` values resolve through. [[updateField]] builds
19
+ * a [FormatterSpec]($core-quantity) per field from the FormatSet named by
20
+ * [QuantityFieldFormatOptions.formatSet]($common), falling back to the adopted FormatSet and
21
+ * the schemas; a value none of them can format falls back to `value.toString()`.
22
+ * [[createUpdateContext]] always supplies the iModel's formatting; only hand-built contexts in
23
+ * tests that never format a quantity leave it out.
24
+ */
25
+ readonly formatting?: FieldFormatting;
8
26
  }
9
27
  export declare function createUpdateContext(hostElementId: string | undefined, iModel: IModelDb, deleted: boolean): UpdateFieldsContext;
28
+ /** Recomputes a single field's cached display string synchronously. Returns true iff
29
+ * cachedContent changed.
30
+ *
31
+ * Resolving the property value and formatting it are both fallible. A failure of either is
32
+ * logged and degrades *this* field to [FieldRun.invalidContentIndicator]($common); other
33
+ * fields in the same block are unaffected.
34
+ */
10
35
  export declare function updateField(field: FieldRun, context: UpdateFieldsContext): boolean;
36
+ /** Re-evaluates every [FieldRun]($common) in `textBlock` synchronously and returns the number
37
+ * whose cached display string changed. Fields targeting an element other than
38
+ * `context.hostElementId` (when set) are skipped.
39
+ */
11
40
  export declare function updateFields(textBlock: TextBlock, context: UpdateFieldsContext): number;
41
+ /** Re-evaluates the fields of the `props.targetId` annotation in response to a source-element
42
+ * change (`deleted=false`) or delete (`deleted=true`). Invoked from
43
+ * [[ElementDrivesTextAnnotation.onRootChangedArg]] / `onDeletedDependencyArg`.
44
+ */
12
45
  export declare function updateElementFields(props: RelationshipProps, txn: EditTxn, deleted: boolean): void;
46
+ /** Re-evaluates every field of the given annotation element against its current property
47
+ * values. Invoked from [[ElementDrivesTextAnnotation.updateFieldDependencies]] when
48
+ * establishing / refreshing relationships.
49
+ */
13
50
  export declare function updateAllFields(annotationElementId: Id64String, txn: EditTxn): void;
14
51
  //# sourceMappingURL=fields.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../../../../src/internal/annotations/fields.ts"],"names":[],"mappings":"AAKA,OAAO,EAA0C,QAAQ,EAAE,UAAU,EAAiD,iBAAiB,EAAE,SAAS,EAA8B,MAAM,oBAAoB,CAAC;AAC3M,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAyB,UAAU,EAAU,MAAM,qBAAqB,CAAC;AAKhF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AA0B7C,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,aAAa,EAAE,UAAU,GAAG,SAAS,CAAC;IAE/C,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAA;CACrD;AAoKD,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,mBAAmB,CAK9H;AAGD,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAsBlF;AAID,wBAAgB,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,mBAAmB,GAAG,MAAM,CASvF;AA0BD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAElG;AAED,wBAAgB,eAAe,CAAC,mBAAmB,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CAEnF"}
1
+ {"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../../../../src/internal/annotations/fields.ts"],"names":[],"mappings":"AAKA,OAAO,EAA0C,QAAQ,EAA8E,iBAAiB,EAAE,SAAS,EAA8B,MAAM,oBAAoB,CAAC;AAC5N,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAyB,UAAU,EAAU,MAAM,qBAAqB,CAAC;AAKhF,OAAO,EAAE,eAAe,EAAuC,kBAAkB,EAAE,MAAM,cAAc,CAAC;AACxG,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AA0B7C;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,QAAQ,CAAC,aAAa,EAAE,UAAU,GAAG,SAAS,CAAC;IAE/C;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,kBAAkB,GAAG,SAAS,CAAC;IAE7D;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,eAAe,CAAC;CACvC;AA+OD,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,mBAAmB,CAM9H;AA0CD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CA0BlF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,mBAAmB,GAAG,MAAM,CASvF;AAyBD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAElG;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,mBAAmB,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CAEnF"}
@@ -8,7 +8,9 @@ import { BackendLoggerCategory } from "../../BackendLoggerCategory";
8
8
  import { isITextAnnotation } from "../../annotations/ElementDrivesTextAnnotation";
9
9
  import { EntityClass, PrimitiveType, PropertyType, StructArrayProperty } from "@itwin/ecschema-metadata";
10
10
  import { reshapePropertyValue } from "../ECSqlInstanceReshaper";
11
- // Resolve the raw primitive value of the property that a field points to.
11
+ import { getFieldFormatting, lookupFieldSpec } from "./fieldSpecs";
12
+ // Resolves the property a field points at into a [[ResolvedFieldValue]] — primitive value plus, for
13
+ // `"quantity"` / `"coordinate"` types, the property-side KoQ and persistence unit.
12
14
  function getFieldPropertyValue(field, iModel) {
13
15
  const host = field.propertyHost;
14
16
  const schemaItem = iModel.schemaContext.getSchemaItemSync(host.schemaName, host.className);
@@ -16,7 +18,7 @@ function getFieldPropertyValue(field, iModel) {
16
18
  return undefined;
17
19
  }
18
20
  let ecClass = schemaItem;
19
- const { propertyName, accessors } = field.propertyPath;
21
+ const { propertyName, accessors, jsonAccessors } = field.propertyPath;
20
22
  let ecProp = ecClass.getPropertySync(propertyName);
21
23
  if (!ecProp) {
22
24
  return undefined;
@@ -108,15 +110,81 @@ function getFieldPropertyValue(field, iModel) {
108
110
  }
109
111
  }
110
112
  }
113
+ // The ultimate result must be a primitive value.
114
+ if (undefined === curValue.primitive) {
115
+ return undefined;
116
+ }
117
+ // `jsonAccessors` continue the walk inside a serialized JSON string. There is no schema behind a JSON blob, so the
118
+ // property must declare itself as JSON via its extended type; an un-indexed JSON property resolves to the raw string.
119
+ if (jsonAccessors && jsonAccessors.length > 0) {
120
+ if (!isJsonProperty(ecProp) || typeof curValue.primitive !== "string") {
121
+ return undefined;
122
+ }
123
+ return readJsonLeaf(curValue.primitive, jsonAccessors);
124
+ }
111
125
  const propertyType = determineFieldPropertyType(ecProp);
112
126
  if (!propertyType) {
113
127
  return undefined;
114
128
  }
115
- // The ultimate result must be a primitive value.
116
- if (undefined === curValue.primitive) {
129
+ // Property-side KoQ + persistence unit only. Overrides in `formatOptions.quantity` are
130
+ // merged at formatting time (see `collectFieldQuantityPairs`) so these serve as the fallback
131
+ // when the override doesn't resolve.
132
+ let kindOfQuantityFullName;
133
+ let persistenceUnitFullName;
134
+ if (propertyType === "quantity" || propertyType === "coordinate") {
135
+ const koq = ecProp.kindOfQuantity ? ecProp.getKindOfQuantitySync() : undefined;
136
+ kindOfQuantityFullName = koq?.fullName;
137
+ persistenceUnitFullName = koq?.persistenceUnit?.fullName;
138
+ }
139
+ return { value: curValue.primitive, type: propertyType, kindOfQuantityFullName, persistenceUnitFullName };
140
+ }
141
+ function isJsonProperty(prop) {
142
+ return prop.isPrimitive() && !prop.isArray() && prop.primitiveType === PrimitiveType.String && prop.extendedTypeName === "Json";
143
+ }
144
+ /** Applies [FieldPropertyPath.jsonAccessors]($common) to a string property holding serialized JSON,
145
+ * as a walk entirely separate from the EC one: there is no schema behind a JSON blob, so no EC
146
+ * metadata is consulted.
147
+ *
148
+ * Returns `undefined` when `raw` is not JSON, when an accessor does not resolve, or when the path
149
+ * stops anywhere but a scalar — including on a JSON `null`, which is not a
150
+ * [FieldPrimitiveValue]($common).
151
+ */
152
+ function readJsonLeaf(raw, accessors) {
153
+ let cur;
154
+ try {
155
+ cur = JSON.parse(raw);
156
+ }
157
+ catch {
158
+ // Not valid JSON; treat as a normal string.
117
159
  return undefined;
118
160
  }
119
- return { value: curValue.primitive, type: propertyType };
161
+ for (const accessor of accessors) {
162
+ // A number indexes an array and a string keys an object; anything else doesn't resolve.
163
+ const isIndex = typeof accessor === "number";
164
+ if (typeof cur !== "object" || null === cur || Array.isArray(cur) !== isIndex) {
165
+ return undefined;
166
+ }
167
+ if (isIndex) {
168
+ cur = cur[accessor < 0 ? cur.length + accessor : accessor];
169
+ }
170
+ else {
171
+ // Own keys only, so a key missing from the JSON can't resolve through Object.prototype.
172
+ cur = Object.hasOwn(cur, accessor) ? cur[accessor] : undefined;
173
+ }
174
+ }
175
+ // JSON carries no type metadata, so the leaf's JavaScript type decides the field property type.
176
+ // A numeric leaf is typed `"quantity"` but has no property-side KoQ, so only the field's own
177
+ // `kindOfQuantity` + `persistenceUnit` overrides can name a format for it.
178
+ switch (typeof cur) {
179
+ case "number":
180
+ return { value: cur, type: "quantity" };
181
+ case "boolean":
182
+ return { value: cur, type: "boolean" };
183
+ case "string":
184
+ return { value: cur, type: "string" };
185
+ default:
186
+ return undefined;
187
+ }
120
188
  }
121
189
  function determineFieldPropertyType(prop) {
122
190
  if (prop.isEnumeration()) {
@@ -138,16 +206,18 @@ function determineFieldPropertyType(prop) {
138
206
  case PrimitiveType.DateTime:
139
207
  return "datetime";
140
208
  case PrimitiveType.Double:
209
+ case PrimitiveType.Integer:
141
210
  case PrimitiveType.Long:
211
+ // Any numeric property is a potential quantity. Classifying one as "quantity" is not an
212
+ // assertion that it *has* units -- it only decides whether the KoQ/units pipeline is
213
+ // consulted. A number that resolves no spec falls back to the same `toString()` the
214
+ // "string" formatter would have produced, so counts and identifiers still render bare.
142
215
  return "quantity";
143
216
  case PrimitiveType.Point2d:
144
217
  case PrimitiveType.Point3d:
145
218
  return "coordinate";
146
219
  case PrimitiveType.Binary:
147
220
  return prop.extendedTypeName === "BeGuid" ? "string" : undefined;
148
- case PrimitiveType.Integer:
149
- case PrimitiveType.Long:
150
- return "string";
151
221
  default:
152
222
  return undefined;
153
223
  }
@@ -158,9 +228,53 @@ export function createUpdateContext(hostElementId, iModel, deleted) {
158
228
  return {
159
229
  hostElementId,
160
230
  getProperty: deleted ? () => undefined : (field) => getFieldPropertyValue(field, iModel),
231
+ formatting: getFieldFormatting(iModel),
161
232
  };
162
233
  }
163
- // Recompute the display value of a single field, return false if it couldn't be evaluated.
234
+ /** Resolves the [FormatterSpec]($core-quantity) this field should render its magnitudes through,
235
+ * returning a callback bound to it, or `undefined` when not a quantity or coordinate, no formats
236
+ * were supplied, or no format resolves for any of the (KindOfQuantity, persistence unit) pairs the
237
+ * field may format through. In that last case the shortfall is logged: a persistence unit or
238
+ * format unit outside the bundled BIS set, a KindOfQuantity with no presentation format, or a
239
+ * format whose units belong to a different phenomenon than the persisted value. A
240
+ * `persistenceUnit` override that disagrees with the property's own unit is ignored and logged
241
+ * too; the property's unit is what the stored magnitude means.
242
+ */
243
+ function resolveFormatMagnitude(value, field, context) {
244
+ const formatting = context.formatting;
245
+ if (!formatting || (value.type !== "quantity" && value.type !== "coordinate")) {
246
+ return undefined;
247
+ }
248
+ const quantityOptions = field.formatOptions?.quantity;
249
+ const { spec, candidates, ignoredPersistenceUnit } = lookupFieldSpec(quantityOptions, value, formatting);
250
+ if (ignoredPersistenceUnit) {
251
+ Logger.logWarning(BackendLoggerCategory.IModelDb, "Ignoring persistenceUnit override that disagrees with the property's own persistence unit", () => ({
252
+ elementId: field.propertyHost.elementId,
253
+ propertyName: field.propertyPath.propertyName,
254
+ persistenceUnit: ignoredPersistenceUnit,
255
+ propertyPersistenceUnit: value.persistenceUnitFullName,
256
+ }));
257
+ }
258
+ if (!spec) {
259
+ if (candidates.length > 0) {
260
+ Logger.logWarning(BackendLoggerCategory.IModelDb, "No format resolved for text annotation field; rendering raw value", () => ({
261
+ elementId: field.propertyHost.elementId,
262
+ propertyName: field.propertyPath.propertyName,
263
+ formatSet: quantityOptions?.formatSet,
264
+ tried: candidates.map((c) => `${c.name} in ${c.persistenceUnitName}`),
265
+ }));
266
+ }
267
+ return undefined;
268
+ }
269
+ return (magnitude) => spec.applyFormatting(magnitude);
270
+ }
271
+ /** Recomputes a single field's cached display string synchronously. Returns true iff
272
+ * cachedContent changed.
273
+ *
274
+ * Resolving the property value and formatting it are both fallible. A failure of either is
275
+ * logged and degrades *this* field to [FieldRun.invalidContentIndicator]($common); other
276
+ * fields in the same block are unaffected.
277
+ */
164
278
  export function updateField(field, context) {
165
279
  if (context.hostElementId && context.hostElementId !== field.propertyHost.elementId) {
166
280
  return false;
@@ -169,7 +283,11 @@ export function updateField(field, context) {
169
283
  try {
170
284
  const propValue = context.getProperty(field);
171
285
  if (undefined !== propValue) {
172
- newContent = formatFieldValue(propValue, field.formatOptions);
286
+ newContent = formatFieldValue({
287
+ value: propValue,
288
+ options: field.formatOptions,
289
+ formatMagnitude: resolveFormatMagnitude(propValue, field, context),
290
+ });
173
291
  }
174
292
  }
175
293
  catch (err) {
@@ -182,8 +300,10 @@ export function updateField(field, context) {
182
300
  field.setCachedContent(newContent);
183
301
  return true;
184
302
  }
185
- // Re-evaluates the display strings for all fields that target the element specified by `context` and returns the number
186
- // of fields whose display strings changed as a result.
303
+ /** Re-evaluates every [FieldRun]($common) in `textBlock` synchronously and returns the number
304
+ * whose cached display string changed. Fields targeting an element other than
305
+ * `context.hostElementId` (when set) are skipped.
306
+ */
187
307
  export function updateFields(textBlock, context) {
188
308
  let numUpdated = 0;
189
309
  for (const { child } of traverseTextBlockComponent(textBlock)) {
@@ -215,10 +335,17 @@ function doUpdateFields(txn, annotationId, sourceId, deleted) {
215
335
  Logger.logError(BackendLoggerCategory.IModelDb, err);
216
336
  }
217
337
  }
218
- // Invoked by ElementDrivesTextAnnotation to update fields in target element when source element changes or is deleted.
338
+ /** Re-evaluates the fields of the `props.targetId` annotation in response to a source-element
339
+ * change (`deleted=false`) or delete (`deleted=true`). Invoked from
340
+ * [[ElementDrivesTextAnnotation.onRootChangedArg]] / `onDeletedDependencyArg`.
341
+ */
219
342
  export function updateElementFields(props, txn, deleted) {
220
343
  doUpdateFields(txn, props.targetId, props.sourceId, deleted);
221
344
  }
345
+ /** Re-evaluates every field of the given annotation element against its current property
346
+ * values. Invoked from [[ElementDrivesTextAnnotation.updateFieldDependencies]] when
347
+ * establishing / refreshing relationships.
348
+ */
222
349
  export function updateAllFields(annotationElementId, txn) {
223
350
  doUpdateFields(txn, annotationElementId, undefined, false);
224
351
  }
@@ -1 +1 @@
1
- {"version":3,"file":"fields.js","sourceRoot":"","sources":["../../../../src/internal/annotations/fields.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\nimport { FieldPrimitiveValue, FieldPropertyType, FieldRun, FieldValue, formatFieldValue, QueryBinder, QueryRowFormat, RelationshipProps, TextBlock, traverseTextBlockComponent } from \"@itwin/core-common\";\r\nimport { IModelDb } from \"../../IModelDb\";\r\nimport { assert, expectDefined, Id64String, Logger } from \"@itwin/core-bentley\";\r\nimport { BackendLoggerCategory } from \"../../BackendLoggerCategory\";\r\nimport { isITextAnnotation } from \"../../annotations/ElementDrivesTextAnnotation\";\r\nimport { AnyClass, EntityClass, PrimitiveType, Property, PropertyType, StructArrayProperty } from \"@itwin/ecschema-metadata\";\r\nimport { reshapePropertyValue } from \"../ECSqlInstanceReshaper\";\r\nimport type { EditTxn } from \"../../EditTxn\";\r\ninterface FieldStructValue { [key: string]: any }\r\n\r\n// An intermediate value obtained while evaluating a FieldPropertyPath.\r\ntype FieldValueType = {\r\n primitive: FieldPrimitiveValue;\r\n struct?: never;\r\n primitiveArray?: never;\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct: FieldStructValue;\r\n primitiveArray?: never;\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct?: never;\r\n primitiveArray: FieldPrimitiveValue[];\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct?: never;\r\n primitiveArray?: never;\r\n structArray: FieldStructValue[];\r\n}\r\n\r\nexport interface UpdateFieldsContext {\r\n readonly hostElementId: Id64String | undefined;\r\n\r\n getProperty(field: FieldRun): FieldValue | undefined\r\n}\r\n\r\n// Resolve the raw primitive value of the property that a field points to.\r\nfunction getFieldPropertyValue(field: FieldRun, iModel: IModelDb): FieldValue | undefined {\r\n const host = field.propertyHost;\r\n const schemaItem = iModel.schemaContext.getSchemaItemSync(host.schemaName, host.className);\r\n if (!EntityClass.isEntityClass(schemaItem)) {\r\n return undefined;\r\n }\r\n\r\n let ecClass: AnyClass = schemaItem;\r\n const { propertyName, accessors } = field.propertyPath;\r\n let ecProp = ecClass.getPropertySync(propertyName);\r\n if (!ecProp) {\r\n return undefined;\r\n }\r\n\r\n const isAspect = ecClass.isSync(\"ElementAspect\", \"BisCore\");\r\n const where = ` WHERE ${isAspect ? \"Element.Id\" : \"ECInstanceId\"}=:elementId`;\r\n // `propertyName` may itself be a struct/array/point/navigation property, so its value can't be\r\n // decomposed into scalar sub-columns ahead of time. Query using the non-deprecated\r\n // UseECSqlPropertyNames format and reshape the value into the legacy UseJsPropertyNames shape using\r\n // ECSchema metadata (see ECSqlInstanceReshaper for why a naive, non-schema-aware rename isn't safe here).\r\n let curValue: FieldValueType | undefined = iModel.withQueryReader(`SELECT ${propertyName} FROM ${host.schemaName}.${host.className} ${where}`, (reader): FieldValueType | undefined => {\r\n if (!reader.step()) {\r\n return undefined;\r\n }\r\n\r\n const rawRootValue = reader.current[0];\r\n if (undefined === rawRootValue) {\r\n return undefined;\r\n }\r\n\r\n ecProp = expectDefined(ecProp);\r\n const rootValue = reshapePropertyValue(rawRootValue, ecProp, iModel);\r\n if (ecProp.isArray()) {\r\n return ecProp.isStruct() ? { structArray: rootValue } : { primitiveArray: rootValue };\r\n }\r\n\r\n if (ecProp.isStruct()) {\r\n ecClass = ecProp.structClass;\r\n return { struct: rootValue };\r\n }\r\n\r\n if (ecProp.isPrimitive()) {\r\n return {\r\n primitive: ecProp.primitiveType === PrimitiveType.DateTime ? new Date(rootValue) : rootValue,\r\n };\r\n }\r\n\r\n return undefined;\r\n }, new QueryBinder().bindId(\"elementId\", host.elementId), { rowFormat: QueryRowFormat.UseECSqlPropertyNames });\r\n\r\n if (undefined === curValue) {\r\n return undefined;\r\n }\r\n\r\n if (accessors) {\r\n for (const accessor of accessors) {\r\n if (undefined !== curValue.primitive) {\r\n // Can't index into a primitive.\r\n return undefined;\r\n }\r\n\r\n if (typeof accessor === \"number\") {\r\n const array: FieldPrimitiveValue[] | FieldStructValue[] | undefined = curValue.primitiveArray ?? curValue.structArray;\r\n if (!array) {\r\n return undefined;\r\n }\r\n\r\n const index: number = accessor < 0 ? (array.length + accessor) : accessor;\r\n const item: FieldPrimitiveValue | FieldStructValue = array[index];\r\n if (undefined === item) {\r\n return undefined;\r\n } else if (curValue.primitiveArray) {\r\n curValue = { primitive: curValue.primitiveArray[index] };\r\n } else {\r\n assert(undefined !== curValue.structArray);\r\n assert(ecProp instanceof StructArrayProperty);\r\n\r\n ecClass = ecProp.structClass;\r\n curValue = { struct: curValue.structArray[index] };\r\n }\r\n } else {\r\n if (undefined === curValue.struct) {\r\n return undefined;\r\n }\r\n\r\n const item: any = curValue.struct[accessor];\r\n if (undefined === item) {\r\n return undefined;\r\n }\r\n\r\n ecProp = ecClass.getPropertySync(accessor);\r\n if (!ecProp) {\r\n return undefined;\r\n }\r\n\r\n if (ecProp.isArray()) {\r\n curValue = ecProp.isStruct() ? { structArray: item } : { primitiveArray: item };\r\n } else if (ecProp.isStruct()) {\r\n ecClass = ecProp.structClass;\r\n curValue = { struct: item };\r\n } else if (ecProp.isPrimitive()) {\r\n curValue = { primitive: item };\r\n } else {\r\n return undefined;\r\n }\r\n }\r\n }\r\n }\r\n\r\n const propertyType = determineFieldPropertyType(ecProp);\r\n if (!propertyType) {\r\n return undefined;\r\n }\r\n\r\n // The ultimate result must be a primitive value.\r\n if (undefined === curValue.primitive) {\r\n return undefined;\r\n }\r\n\r\n return { value: curValue.primitive, type: propertyType };\r\n}\r\n\r\nfunction determineFieldPropertyType(prop: Property): FieldPropertyType | undefined {\r\n if (prop.isEnumeration()) {\r\n switch (prop.propertyType) {\r\n case PropertyType.Integer_Enumeration:\r\n return \"int-enum\";\r\n case PropertyType.String_Enumeration:\r\n return \"string-enum\";\r\n default:\r\n return undefined;\r\n }\r\n }\r\n\r\n if (prop.isPrimitive()) {\r\n switch (prop.primitiveType) {\r\n case PrimitiveType.Boolean:\r\n return \"boolean\";\r\n case PrimitiveType.String:\r\n return prop.extendedTypeName === \"DateTime\" ? \"datetime\" : \"string\";\r\n case PrimitiveType.DateTime:\r\n return \"datetime\";\r\n case PrimitiveType.Double:\r\n case PrimitiveType.Long:\r\n return \"quantity\";\r\n case PrimitiveType.Point2d:\r\n case PrimitiveType.Point3d:\r\n return \"coordinate\";\r\n case PrimitiveType.Binary:\r\n return prop.extendedTypeName === \"BeGuid\" ? \"string\" : undefined;\r\n case PrimitiveType.Integer:\r\n case PrimitiveType.Long:\r\n return \"string\";\r\n default:\r\n return undefined;\r\n }\r\n }\r\n\r\n return undefined;\r\n}\r\n\r\nexport function createUpdateContext(hostElementId: string | undefined, iModel: IModelDb, deleted: boolean): UpdateFieldsContext {\r\n return {\r\n hostElementId,\r\n getProperty: deleted ? () => undefined : (field) => getFieldPropertyValue(field, iModel),\r\n };\r\n}\r\n\r\n// Recompute the display value of a single field, return false if it couldn't be evaluated.\r\nexport function updateField(field: FieldRun, context: UpdateFieldsContext): boolean {\r\n if (context.hostElementId && context.hostElementId !== field.propertyHost.elementId) {\r\n return false;\r\n }\r\n\r\n let newContent: string | undefined;\r\n try {\r\n const propValue = context.getProperty(field);\r\n if (undefined !== propValue) {\r\n newContent = formatFieldValue(propValue, field.formatOptions);\r\n }\r\n } catch (err) {\r\n Logger.logError(BackendLoggerCategory.IModelDb, err);\r\n }\r\n\r\n newContent = newContent ?? FieldRun.invalidContentIndicator;\r\n if (newContent === field.cachedContent) {\r\n return false;\r\n }\r\n\r\n field.setCachedContent(newContent);\r\n return true;\r\n}\r\n\r\n// Re-evaluates the display strings for all fields that target the element specified by `context` and returns the number\r\n// of fields whose display strings changed as a result.\r\nexport function updateFields(textBlock: TextBlock, context: UpdateFieldsContext): number {\r\n let numUpdated = 0;\r\n for (const { child } of traverseTextBlockComponent(textBlock)) {\r\n if (child.type === \"field\" && updateField(child, context)) {\r\n ++numUpdated;\r\n }\r\n }\r\n\r\n return numUpdated;\r\n}\r\n\r\nfunction doUpdateFields(txn: EditTxn, annotationId: Id64String, sourceId: Id64String | undefined, deleted: boolean): void {\r\n const iModel = txn.iModel;\r\n try {\r\n const target = iModel.elements.getElement(annotationId);\r\n if (isITextAnnotation(target)) {\r\n const context = createUpdateContext(sourceId, iModel, deleted);\r\n const updatedBlocks = [];\r\n for (const block of target.getTextBlocks()) {\r\n if (updateFields(block.textBlock, context)) {\r\n updatedBlocks.push(block);\r\n }\r\n }\r\n\r\n if (updatedBlocks.length > 0) {\r\n target.updateTextBlocks(updatedBlocks);\r\n target.update(txn);\r\n }\r\n }\r\n } catch (err) {\r\n Logger.logError(BackendLoggerCategory.IModelDb, err);\r\n }\r\n}\r\n\r\n// Invoked by ElementDrivesTextAnnotation to update fields in target element when source element changes or is deleted.\r\nexport function updateElementFields(props: RelationshipProps, txn: EditTxn, deleted: boolean): void {\r\n doUpdateFields(txn, props.targetId, props.sourceId, deleted);\r\n}\r\n\r\nexport function updateAllFields(annotationElementId: Id64String, txn: EditTxn): void {\r\n doUpdateFields(txn, annotationElementId, undefined, false);\r\n}\r\n"]}
1
+ {"version":3,"file":"fields.js","sourceRoot":"","sources":["../../../../src/internal/annotations/fields.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\nimport { FieldPrimitiveValue, FieldPropertyType, FieldRun, FieldValue, formatFieldValue, FormatMagnitude, QueryBinder, QueryRowFormat, RelationshipProps, TextBlock, traverseTextBlockComponent } from \"@itwin/core-common\";\r\nimport { IModelDb } from \"../../IModelDb\";\r\nimport { assert, expectDefined, Id64String, Logger } from \"@itwin/core-bentley\";\r\nimport { BackendLoggerCategory } from \"../../BackendLoggerCategory\";\r\nimport { isITextAnnotation } from \"../../annotations/ElementDrivesTextAnnotation\";\r\nimport { AnyClass, EntityClass, PrimitiveType, Property, PropertyType, StructArrayProperty } from \"@itwin/ecschema-metadata\";\r\nimport { reshapePropertyValue } from \"../ECSqlInstanceReshaper\";\r\nimport { FieldFormatting, getFieldFormatting, lookupFieldSpec, ResolvedFieldValue } from \"./fieldSpecs\";\r\nimport type { EditTxn } from \"../../EditTxn\";\r\ninterface FieldStructValue { [key: string]: any }\r\n\r\n// An intermediate value obtained while evaluating a FieldPropertyPath.\r\ntype FieldValueType = {\r\n primitive: FieldPrimitiveValue;\r\n struct?: never;\r\n primitiveArray?: never;\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct: FieldStructValue;\r\n primitiveArray?: never;\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct?: never;\r\n primitiveArray: FieldPrimitiveValue[];\r\n structArray?: never;\r\n} | {\r\n primitive?: never;\r\n struct?: never;\r\n primitiveArray?: never;\r\n structArray: FieldStructValue[];\r\n}\r\n\r\n/** The per-evaluation state [[updateField]] needs: which element's fields to recompute, how to\r\n * read a field's property value, and how to format quantities.\r\n */\r\nexport interface UpdateFieldsContext {\r\n /** When set, only fields whose [FieldPropertyHost.elementId]($common) matches are recomputed —\r\n * an edit to one source element leaves the annotation's other fields untouched.\r\n */\r\n readonly hostElementId: Id64String | undefined;\r\n\r\n /** Reads the value a field points at, or `undefined` if it cannot be resolved — including\r\n * every field when the source element was deleted.\r\n */\r\n getProperty(field: FieldRun): ResolvedFieldValue | undefined;\r\n\r\n /** The formats `\"quantity\"` and `\"coordinate\"` values resolve through. [[updateField]] builds\r\n * a [FormatterSpec]($core-quantity) per field from the FormatSet named by\r\n * [QuantityFieldFormatOptions.formatSet]($common), falling back to the adopted FormatSet and\r\n * the schemas; a value none of them can format falls back to `value.toString()`.\r\n * [[createUpdateContext]] always supplies the iModel's formatting; only hand-built contexts in\r\n * tests that never format a quantity leave it out.\r\n */\r\n readonly formatting?: FieldFormatting;\r\n}\r\n\r\n// Resolves the property a field points at into a [[ResolvedFieldValue]] — primitive value plus, for\r\n// `\"quantity\"` / `\"coordinate\"` types, the property-side KoQ and persistence unit.\r\nfunction getFieldPropertyValue(field: FieldRun, iModel: IModelDb): ResolvedFieldValue | undefined {\r\n const host = field.propertyHost;\r\n const schemaItem = iModel.schemaContext.getSchemaItemSync(host.schemaName, host.className);\r\n if (!EntityClass.isEntityClass(schemaItem)) {\r\n return undefined;\r\n }\r\n\r\n let ecClass: AnyClass = schemaItem;\r\n const { propertyName, accessors, jsonAccessors } = field.propertyPath;\r\n let ecProp = ecClass.getPropertySync(propertyName);\r\n if (!ecProp) {\r\n return undefined;\r\n }\r\n\r\n const isAspect = ecClass.isSync(\"ElementAspect\", \"BisCore\");\r\n const where = ` WHERE ${isAspect ? \"Element.Id\" : \"ECInstanceId\"}=:elementId`;\r\n // `propertyName` may itself be a struct/array/point/navigation property, so its value can't be\r\n // decomposed into scalar sub-columns ahead of time. Query using the non-deprecated\r\n // UseECSqlPropertyNames format and reshape the value into the legacy UseJsPropertyNames shape using\r\n // ECSchema metadata (see ECSqlInstanceReshaper for why a naive, non-schema-aware rename isn't safe here).\r\n let curValue: FieldValueType | undefined = iModel.withQueryReader(`SELECT ${propertyName} FROM ${host.schemaName}.${host.className} ${where}`, (reader): FieldValueType | undefined => {\r\n if (!reader.step()) {\r\n return undefined;\r\n }\r\n\r\n const rawRootValue = reader.current[0];\r\n if (undefined === rawRootValue) {\r\n return undefined;\r\n }\r\n\r\n ecProp = expectDefined(ecProp);\r\n const rootValue = reshapePropertyValue(rawRootValue, ecProp, iModel);\r\n if (ecProp.isArray()) {\r\n return ecProp.isStruct() ? { structArray: rootValue } : { primitiveArray: rootValue };\r\n }\r\n\r\n if (ecProp.isStruct()) {\r\n ecClass = ecProp.structClass;\r\n return { struct: rootValue };\r\n }\r\n\r\n if (ecProp.isPrimitive()) {\r\n return {\r\n primitive: ecProp.primitiveType === PrimitiveType.DateTime ? new Date(rootValue) : rootValue,\r\n };\r\n }\r\n\r\n return undefined;\r\n }, new QueryBinder().bindId(\"elementId\", host.elementId), { rowFormat: QueryRowFormat.UseECSqlPropertyNames });\r\n\r\n if (undefined === curValue) {\r\n return undefined;\r\n }\r\n\r\n if (accessors) {\r\n for (const accessor of accessors) {\r\n if (undefined !== curValue.primitive) {\r\n // Can't index into a primitive.\r\n return undefined;\r\n }\r\n\r\n if (typeof accessor === \"number\") {\r\n const array: FieldPrimitiveValue[] | FieldStructValue[] | undefined = curValue.primitiveArray ?? curValue.structArray;\r\n if (!array) {\r\n return undefined;\r\n }\r\n\r\n const index: number = accessor < 0 ? (array.length + accessor) : accessor;\r\n const item: FieldPrimitiveValue | FieldStructValue = array[index];\r\n if (undefined === item) {\r\n return undefined;\r\n } else if (curValue.primitiveArray) {\r\n curValue = { primitive: curValue.primitiveArray[index] };\r\n } else {\r\n assert(undefined !== curValue.structArray);\r\n assert(ecProp instanceof StructArrayProperty);\r\n\r\n ecClass = ecProp.structClass;\r\n curValue = { struct: curValue.structArray[index] };\r\n }\r\n } else {\r\n if (undefined === curValue.struct) {\r\n return undefined;\r\n }\r\n\r\n const item: any = curValue.struct[accessor];\r\n if (undefined === item) {\r\n return undefined;\r\n }\r\n\r\n ecProp = ecClass.getPropertySync(accessor);\r\n if (!ecProp) {\r\n return undefined;\r\n }\r\n\r\n if (ecProp.isArray()) {\r\n curValue = ecProp.isStruct() ? { structArray: item } : { primitiveArray: item };\r\n } else if (ecProp.isStruct()) {\r\n ecClass = ecProp.structClass;\r\n curValue = { struct: item };\r\n } else if (ecProp.isPrimitive()) {\r\n curValue = { primitive: item };\r\n } else {\r\n return undefined;\r\n }\r\n }\r\n }\r\n }\r\n\r\n // The ultimate result must be a primitive value.\r\n if (undefined === curValue.primitive) {\r\n return undefined;\r\n }\r\n\r\n // `jsonAccessors` continue the walk inside a serialized JSON string. There is no schema behind a JSON blob, so the\r\n // property must declare itself as JSON via its extended type; an un-indexed JSON property resolves to the raw string.\r\n if (jsonAccessors && jsonAccessors.length > 0) {\r\n if (!isJsonProperty(ecProp) || typeof curValue.primitive !== \"string\") {\r\n return undefined;\r\n }\r\n\r\n return readJsonLeaf(curValue.primitive, jsonAccessors);\r\n }\r\n\r\n const propertyType = determineFieldPropertyType(ecProp);\r\n if (!propertyType) {\r\n return undefined;\r\n }\r\n\r\n // Property-side KoQ + persistence unit only. Overrides in `formatOptions.quantity` are\r\n // merged at formatting time (see `collectFieldQuantityPairs`) so these serve as the fallback\r\n // when the override doesn't resolve.\r\n let kindOfQuantityFullName: string | undefined;\r\n let persistenceUnitFullName: string | undefined;\r\n if (propertyType === \"quantity\" || propertyType === \"coordinate\") {\r\n const koq = ecProp.kindOfQuantity ? ecProp.getKindOfQuantitySync() : undefined;\r\n kindOfQuantityFullName = koq?.fullName;\r\n persistenceUnitFullName = koq?.persistenceUnit?.fullName;\r\n }\r\n\r\n return { value: curValue.primitive, type: propertyType, kindOfQuantityFullName, persistenceUnitFullName };\r\n}\r\n\r\nfunction isJsonProperty(prop: Property): boolean {\r\n return prop.isPrimitive() && !prop.isArray() && prop.primitiveType === PrimitiveType.String && prop.extendedTypeName === \"Json\";\r\n}\r\n\r\n/** Applies [FieldPropertyPath.jsonAccessors]($common) to a string property holding serialized JSON,\r\n * as a walk entirely separate from the EC one: there is no schema behind a JSON blob, so no EC\r\n * metadata is consulted.\r\n *\r\n * Returns `undefined` when `raw` is not JSON, when an accessor does not resolve, or when the path\r\n * stops anywhere but a scalar — including on a JSON `null`, which is not a\r\n * [FieldPrimitiveValue]($common).\r\n */\r\nfunction readJsonLeaf(raw: string, accessors: ReadonlyArray<string | number>): FieldValue | undefined {\r\n let cur: any;\r\n try {\r\n cur = JSON.parse(raw);\r\n } catch {\r\n // Not valid JSON; treat as a normal string.\r\n return undefined;\r\n }\r\n\r\n for (const accessor of accessors) {\r\n // A number indexes an array and a string keys an object; anything else doesn't resolve.\r\n const isIndex = typeof accessor === \"number\";\r\n if (typeof cur !== \"object\" || null === cur || Array.isArray(cur) !== isIndex) {\r\n return undefined;\r\n }\r\n\r\n if (isIndex) {\r\n cur = cur[accessor < 0 ? cur.length + accessor : accessor];\r\n } else {\r\n // Own keys only, so a key missing from the JSON can't resolve through Object.prototype.\r\n cur = Object.hasOwn(cur, accessor) ? cur[accessor] : undefined;\r\n }\r\n }\r\n\r\n // JSON carries no type metadata, so the leaf's JavaScript type decides the field property type.\r\n // A numeric leaf is typed `\"quantity\"` but has no property-side KoQ, so only the field's own\r\n // `kindOfQuantity` + `persistenceUnit` overrides can name a format for it.\r\n switch (typeof cur) {\r\n case \"number\":\r\n return { value: cur, type: \"quantity\" };\r\n case \"boolean\":\r\n return { value: cur, type: \"boolean\" };\r\n case \"string\":\r\n return { value: cur, type: \"string\" };\r\n default:\r\n return undefined;\r\n }\r\n}\r\n\r\nfunction determineFieldPropertyType(prop: Property): FieldPropertyType | undefined {\r\n if (prop.isEnumeration()) {\r\n switch (prop.propertyType) {\r\n case PropertyType.Integer_Enumeration:\r\n return \"int-enum\";\r\n case PropertyType.String_Enumeration:\r\n return \"string-enum\";\r\n default:\r\n return undefined;\r\n }\r\n }\r\n\r\n if (prop.isPrimitive()) {\r\n switch (prop.primitiveType) {\r\n case PrimitiveType.Boolean:\r\n return \"boolean\";\r\n case PrimitiveType.String:\r\n return prop.extendedTypeName === \"DateTime\" ? \"datetime\" : \"string\";\r\n case PrimitiveType.DateTime:\r\n return \"datetime\";\r\n case PrimitiveType.Double:\r\n case PrimitiveType.Integer:\r\n case PrimitiveType.Long:\r\n // Any numeric property is a potential quantity. Classifying one as \"quantity\" is not an\r\n // assertion that it *has* units -- it only decides whether the KoQ/units pipeline is\r\n // consulted. A number that resolves no spec falls back to the same `toString()` the\r\n // \"string\" formatter would have produced, so counts and identifiers still render bare.\r\n return \"quantity\";\r\n case PrimitiveType.Point2d:\r\n case PrimitiveType.Point3d:\r\n return \"coordinate\";\r\n case PrimitiveType.Binary:\r\n return prop.extendedTypeName === \"BeGuid\" ? \"string\" : undefined;\r\n default:\r\n return undefined;\r\n }\r\n }\r\n\r\n return undefined;\r\n}\r\n\r\nexport function createUpdateContext(hostElementId: string | undefined, iModel: IModelDb, deleted: boolean): UpdateFieldsContext {\r\n return {\r\n hostElementId,\r\n getProperty: deleted ? () => undefined : (field) => getFieldPropertyValue(field, iModel),\r\n formatting: getFieldFormatting(iModel),\r\n };\r\n}\r\n\r\n/** Resolves the [FormatterSpec]($core-quantity) this field should render its magnitudes through,\r\n * returning a callback bound to it, or `undefined` when not a quantity or coordinate, no formats\r\n * were supplied, or no format resolves for any of the (KindOfQuantity, persistence unit) pairs the\r\n * field may format through. In that last case the shortfall is logged: a persistence unit or\r\n * format unit outside the bundled BIS set, a KindOfQuantity with no presentation format, or a\r\n * format whose units belong to a different phenomenon than the persisted value. A\r\n * `persistenceUnit` override that disagrees with the property's own unit is ignored and logged\r\n * too; the property's unit is what the stored magnitude means.\r\n */\r\nfunction resolveFormatMagnitude(value: ResolvedFieldValue, field: FieldRun, context: UpdateFieldsContext): FormatMagnitude | undefined {\r\n const formatting = context.formatting;\r\n if (!formatting || (value.type !== \"quantity\" && value.type !== \"coordinate\")) {\r\n return undefined;\r\n }\r\n\r\n const quantityOptions = field.formatOptions?.quantity;\r\n const { spec, candidates, ignoredPersistenceUnit } = lookupFieldSpec(quantityOptions, value, formatting);\r\n if (ignoredPersistenceUnit) {\r\n Logger.logWarning(BackendLoggerCategory.IModelDb, \"Ignoring persistenceUnit override that disagrees with the property's own persistence unit\", () => ({\r\n elementId: field.propertyHost.elementId,\r\n propertyName: field.propertyPath.propertyName,\r\n persistenceUnit: ignoredPersistenceUnit,\r\n propertyPersistenceUnit: value.persistenceUnitFullName,\r\n }));\r\n }\r\n if (!spec) {\r\n if (candidates.length > 0) {\r\n Logger.logWarning(BackendLoggerCategory.IModelDb, \"No format resolved for text annotation field; rendering raw value\", () => ({\r\n elementId: field.propertyHost.elementId,\r\n propertyName: field.propertyPath.propertyName,\r\n formatSet: quantityOptions?.formatSet,\r\n tried: candidates.map((c) => `${c.name} in ${c.persistenceUnitName}`),\r\n }));\r\n }\r\n return undefined;\r\n }\r\n\r\n return (magnitude) => spec.applyFormatting(magnitude);\r\n}\r\n\r\n/** Recomputes a single field's cached display string synchronously. Returns true iff\r\n * cachedContent changed.\r\n *\r\n * Resolving the property value and formatting it are both fallible. A failure of either is\r\n * logged and degrades *this* field to [FieldRun.invalidContentIndicator]($common); other\r\n * fields in the same block are unaffected.\r\n */\r\nexport function updateField(field: FieldRun, context: UpdateFieldsContext): boolean {\r\n if (context.hostElementId && context.hostElementId !== field.propertyHost.elementId) {\r\n return false;\r\n }\r\n\r\n let newContent: string | undefined;\r\n try {\r\n const propValue = context.getProperty(field);\r\n if (undefined !== propValue) {\r\n newContent = formatFieldValue({\r\n value: propValue,\r\n options: field.formatOptions,\r\n formatMagnitude: resolveFormatMagnitude(propValue, field, context),\r\n });\r\n }\r\n } catch (err) {\r\n Logger.logError(BackendLoggerCategory.IModelDb, err);\r\n }\r\n\r\n newContent = newContent ?? FieldRun.invalidContentIndicator;\r\n if (newContent === field.cachedContent) {\r\n return false;\r\n }\r\n\r\n field.setCachedContent(newContent);\r\n return true;\r\n}\r\n\r\n/** Re-evaluates every [FieldRun]($common) in `textBlock` synchronously and returns the number\r\n * whose cached display string changed. Fields targeting an element other than\r\n * `context.hostElementId` (when set) are skipped.\r\n */\r\nexport function updateFields(textBlock: TextBlock, context: UpdateFieldsContext): number {\r\n let numUpdated = 0;\r\n for (const { child } of traverseTextBlockComponent(textBlock)) {\r\n if (child.type === \"field\" && updateField(child, context)) {\r\n ++numUpdated;\r\n }\r\n }\r\n\r\n return numUpdated;\r\n}\r\n\r\nfunction doUpdateFields(txn: EditTxn, annotationId: Id64String, sourceId: Id64String | undefined, deleted: boolean): void {\r\n const iModel = txn.iModel;\r\n try {\r\n const target = iModel.elements.getElement(annotationId);\r\n if (isITextAnnotation(target)) {\r\n const context = createUpdateContext(sourceId, iModel, deleted);\r\n const updatedBlocks = [];\r\n for (const block of target.getTextBlocks()) {\r\n if (updateFields(block.textBlock, context)) {\r\n updatedBlocks.push(block);\r\n }\r\n }\r\n\r\n if (updatedBlocks.length > 0) {\r\n target.updateTextBlocks(updatedBlocks);\r\n target.update(txn);\r\n }\r\n }\r\n } catch (err) {\r\n Logger.logError(BackendLoggerCategory.IModelDb, err);\r\n }\r\n}\r\n\r\n/** Re-evaluates the fields of the `props.targetId` annotation in response to a source-element\r\n * change (`deleted=false`) or delete (`deleted=true`). Invoked from\r\n * [[ElementDrivesTextAnnotation.onRootChangedArg]] / `onDeletedDependencyArg`.\r\n */\r\nexport function updateElementFields(props: RelationshipProps, txn: EditTxn, deleted: boolean): void {\r\n doUpdateFields(txn, props.targetId, props.sourceId, deleted);\r\n}\r\n\r\n/** Re-evaluates every field of the given annotation element against its current property\r\n * values. Invoked from [[ElementDrivesTextAnnotation.updateFieldDependencies]] when\r\n * establishing / refreshing relationships.\r\n */\r\nexport function updateAllFields(annotationElementId: Id64String, txn: EditTxn): void {\r\n doUpdateFields(txn, annotationElementId, undefined, false);\r\n}\r\n"]}
@@ -2,6 +2,15 @@ import { ElementGeometry, GeometryParams, TextBlock, TextString } from "@itwin/c
2
2
  import { AnyCurvePrimitive } from "@itwin/core-geometry";
3
3
  import { ComputeRangesForTextLayoutArgs, FindFontId, FindTextStyle, TextBlockLayout, TextLayoutRanges } from "../core-backend";
4
4
  import { Id64String } from "@itwin/core-bentley";
5
+ import { FormatDefinition } from "@itwin/core-quantity";
6
+ import { FormatSet } from "@itwin/ecschema-metadata";
7
+ /** Builds a decimal [FormatDefinition] rendering a magnitude in `unitName`, labelled `unitLabel`. */
8
+ export declare function decimalFormat(unitName: string, unitLabel: string, precision?: number): FormatDefinition;
9
+ /** Wraps a KindOfQuantity -> format map in a metric [FormatSet] named `name`.
10
+ * `name` is cosmetic -- a FormatSet is routed by the id it is registered under, not by its name --
11
+ * so it exists to tell a reader which set an assertion is about.
12
+ */
13
+ export declare function toFormatSet(name: string, formats?: Record<string, FormatDefinition>): FormatSet;
5
14
  export declare function computeTextRangeAsStringLength(args: ComputeRangesForTextLayoutArgs): TextLayoutRanges;
6
15
  export declare function doLayout(textBlock: TextBlock, args?: {
7
16
  textStyleId?: Id64String;
@@ -1 +1 @@
1
- {"version":3,"file":"AnnotationTestUtils.d.ts","sourceRoot":"","sources":["../../../src/test/AnnotationTestUtils.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,SAAS,EAAE,UAAU,EAAqB,MAAM,oBAAoB,CAAC;AAC/G,OAAO,EAAE,iBAAiB,EAAW,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,8BAA8B,EAAE,UAAU,EAAE,aAAa,EAAmB,eAAe,EAAE,gBAAgB,EAAqB,MAAM,iBAAiB,CAAC;AACnK,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGjD,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,8BAA8B,GAAG,gBAAgB,CAGrG;AAED,wBAAgB,QAAQ,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE;IACpD,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,GAAG,eAAe,CAgBlB;AAGD,qBAAa,WAAY,SAAQ,eAAe,CAAC,OAAO;IAC/C,MAAM,EAAE,cAAc,EAAE,CAAM;IAC9B,UAAU,EAAE,iBAAiB,EAAE,CAAM;IACrC,WAAW,EAAE,UAAU,EAAE,CAAK;IAErB,0BAA0B,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO;IAI3D,mBAAmB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO;IAKzD,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO;CAK5D"}
1
+ {"version":3,"file":"AnnotationTestUtils.d.ts","sourceRoot":"","sources":["../../../src/test/AnnotationTestUtils.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,SAAS,EAAE,UAAU,EAAqB,MAAM,oBAAoB,CAAC;AAC/G,OAAO,EAAE,iBAAiB,EAAW,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,8BAA8B,EAAE,UAAU,EAAE,aAAa,EAAmB,eAAe,EAAE,gBAAgB,EAAqB,MAAM,iBAAiB,CAAC;AACnK,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAGrD,qGAAqG;AACrG,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,SAAI,GAAG,gBAAgB,CAQlG;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAM,GAAG,SAAS,CAEnG;AAGD,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,8BAA8B,GAAG,gBAAgB,CAGrG;AAED,wBAAgB,QAAQ,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE;IACpD,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,GAAG,eAAe,CAgBlB;AAGD,qBAAa,WAAY,SAAQ,eAAe,CAAC,OAAO;IAC/C,MAAM,EAAE,cAAc,EAAE,CAAM;IAC9B,UAAU,EAAE,iBAAiB,EAAE,CAAM;IACrC,WAAW,EAAE,UAAU,EAAE,CAAK;IAErB,0BAA0B,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO;IAI3D,mBAAmB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO;IAKzD,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO;CAK5D"}
@@ -5,6 +5,23 @@
5
5
  import { ElementGeometry, TextStyleSettings } from "@itwin/core-common";
6
6
  import { Range2d } from "@itwin/core-geometry";
7
7
  import { layoutTextBlock, TextStyleResolver } from "../core-backend";
8
+ /** Builds a decimal [FormatDefinition] rendering a magnitude in `unitName`, labelled `unitLabel`. */
9
+ export function decimalFormat(unitName, unitLabel, precision = 4) {
10
+ return {
11
+ composite: { includeZero: true, units: [{ label: unitLabel, name: unitName }] },
12
+ formatTraits: ["keepSingleZero", "showUnitLabel"],
13
+ precision,
14
+ type: "Decimal",
15
+ uomSeparator: " ",
16
+ };
17
+ }
18
+ /** Wraps a KindOfQuantity -> format map in a metric [FormatSet] named `name`.
19
+ * `name` is cosmetic -- a FormatSet is routed by the id it is registered under, not by its name --
20
+ * so it exists to tell a reader which set an assertion is about.
21
+ */
22
+ export function toFormatSet(name, formats = {}) {
23
+ return { name, label: name, unitSystem: "metric", formats };
24
+ }
8
25
  export function computeTextRangeAsStringLength(args) {
9
26
  const range = new Range2d(0, 0, args.chars.length, args.textHeight);
10
27
  return { layout: range, justification: range };
@@ -1 +1 @@
1
- {"version":3,"file":"AnnotationTestUtils.js","sourceRoot":"","sources":["../../../src/test/AnnotationTestUtils.ts"],"names":[],"mappings":"AAAA;;;+FAG+F;AAE/F,OAAO,EAAE,eAAe,EAAyC,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC/G,OAAO,EAAqB,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAA6D,eAAe,EAAqC,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAInK,MAAM,UAAU,8BAA8B,CAAC,IAAoC;IACjF,MAAM,KAAK,GAAG,IAAI,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IACpE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,SAAoB,EAAE,IAI9C;IACC,MAAM,iBAAiB,GAAG,IAAI,iBAAiB,CAAC;QAC9C,SAAS;QACT,WAAW,EAAE,IAAI,EAAE,WAAW,IAAI,EAAE;QACpC,MAAM,EAAE,EAAS;QACjB,aAAa,EAAE,IAAI,EAAE,aAAa,IAAI,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC,QAAQ,CAAC;KACzE,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,eAAe,CAAC;QAC7B,SAAS;QACT,MAAM,EAAE,EAAS;QACjB,iBAAiB;QACjB,UAAU,EAAE,IAAI,EAAE,UAAU,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACzC,gBAAgB,EAAE,8BAA8B;KACjD,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,wFAAwF;AACxF,MAAM,OAAO,WAAY,SAAQ,eAAe,CAAC,OAAO;IAC/C,MAAM,GAAqB,EAAE,CAAC;IAC9B,UAAU,GAAwB,EAAE,CAAC;IACrC,WAAW,GAAiB,EAAE,CAAA;IAErB,0BAA0B,CAAC,MAAsB;QAC/D,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;QACjC,OAAO,KAAK,CAAC,0BAA0B,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IACe,mBAAmB,CAAC,QAA2B;QAC7D,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/B,OAAO,KAAK,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAC7C,CAAC;IAEe,gBAAgB,CAAC,IAAgB;QAC/C,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5B,OAAO,KAAK,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;CAEF","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\nimport { ElementGeometry, GeometryParams, TextBlock, TextString, TextStyleSettings } from \"@itwin/core-common\";\r\nimport { AnyCurvePrimitive, Range2d } from \"@itwin/core-geometry\";\r\nimport { ComputeRangesForTextLayoutArgs, FindFontId, FindTextStyle, layoutTextBlock, TextBlockLayout, TextLayoutRanges, TextStyleResolver } from \"../core-backend\";\r\nimport { Id64String } from \"@itwin/core-bentley\";\r\n\r\n\r\nexport function computeTextRangeAsStringLength(args: ComputeRangesForTextLayoutArgs): TextLayoutRanges {\r\n const range = new Range2d(0, 0, args.chars.length, args.textHeight);\r\n return { layout: range, justification: range };\r\n}\r\n\r\nexport function doLayout(textBlock: TextBlock, args?: {\r\n textStyleId?: Id64String,\r\n findTextStyle?: FindTextStyle;\r\n findFontId?: FindFontId;\r\n}): TextBlockLayout {\r\n const textStyleResolver = new TextStyleResolver({\r\n textBlock,\r\n textStyleId: args?.textStyleId ?? \"\",\r\n iModel: {} as any,\r\n findTextStyle: args?.findTextStyle ?? (() => TextStyleSettings.defaults)\r\n });\r\n const layout = layoutTextBlock({\r\n textBlock,\r\n iModel: {} as any,\r\n textStyleResolver,\r\n findFontId: args?.findFontId ?? (() => 0),\r\n computeTextRange: computeTextRangeAsStringLength,\r\n });\r\n\r\n return layout;\r\n}\r\n\r\n// Extending this because the ElementGeometry.Builder.entries attribute is hard to parse\r\nexport class MockBuilder extends ElementGeometry.Builder {\r\n public params: GeometryParams[] = [];\r\n public geometries: AnyCurvePrimitive[] = [];\r\n public textStrings: TextString[] = []\r\n\r\n public override appendGeometryParamsChange(params: GeometryParams): boolean {\r\n this.params.push(params.clone());\r\n return super.appendGeometryParamsChange(params);\r\n }\r\n public override appendGeometryQuery(geometry: AnyCurvePrimitive): boolean {\r\n this.geometries.push(geometry);\r\n return super.appendGeometryQuery(geometry);\r\n }\r\n\r\n public override appendTextString(text: TextString): boolean {\r\n this.textStrings.push(text);\r\n return super.appendTextString(text);\r\n }\r\n\r\n}\r\n"]}
1
+ {"version":3,"file":"AnnotationTestUtils.js","sourceRoot":"","sources":["../../../src/test/AnnotationTestUtils.ts"],"names":[],"mappings":"AAAA;;;+FAG+F;AAE/F,OAAO,EAAE,eAAe,EAAyC,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC/G,OAAO,EAAqB,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAA6D,eAAe,EAAqC,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAMnK,qGAAqG;AACrG,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,SAAiB,EAAE,SAAS,GAAG,CAAC;IAC9E,OAAO;QACL,SAAS,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE;QAC/E,YAAY,EAAE,CAAC,gBAAgB,EAAE,eAAe,CAAC;QACjD,SAAS;QACT,IAAI,EAAE,SAAS;QACf,YAAY,EAAE,GAAG;KAClB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,UAA4C,EAAE;IACtF,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAC9D,CAAC;AAGD,MAAM,UAAU,8BAA8B,CAAC,IAAoC;IACjF,MAAM,KAAK,GAAG,IAAI,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IACpE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,SAAoB,EAAE,IAI9C;IACC,MAAM,iBAAiB,GAAG,IAAI,iBAAiB,CAAC;QAC9C,SAAS;QACT,WAAW,EAAE,IAAI,EAAE,WAAW,IAAI,EAAE;QACpC,MAAM,EAAE,EAAS;QACjB,aAAa,EAAE,IAAI,EAAE,aAAa,IAAI,CAAC,GAAG,EAAE,CAAC,iBAAiB,CAAC,QAAQ,CAAC;KACzE,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,eAAe,CAAC;QAC7B,SAAS;QACT,MAAM,EAAE,EAAS;QACjB,iBAAiB;QACjB,UAAU,EAAE,IAAI,EAAE,UAAU,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACzC,gBAAgB,EAAE,8BAA8B;KACjD,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,wFAAwF;AACxF,MAAM,OAAO,WAAY,SAAQ,eAAe,CAAC,OAAO;IAC/C,MAAM,GAAqB,EAAE,CAAC;IAC9B,UAAU,GAAwB,EAAE,CAAC;IACrC,WAAW,GAAiB,EAAE,CAAA;IAErB,0BAA0B,CAAC,MAAsB;QAC/D,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;QACjC,OAAO,KAAK,CAAC,0BAA0B,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC;IACe,mBAAmB,CAAC,QAA2B;QAC7D,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/B,OAAO,KAAK,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAC7C,CAAC;IAEe,gBAAgB,CAAC,IAAgB;QAC/C,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5B,OAAO,KAAK,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;CAEF","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n* See LICENSE.md in the project root for license terms and full copyright notice.\r\n*--------------------------------------------------------------------------------------------*/\r\n\r\nimport { ElementGeometry, GeometryParams, TextBlock, TextString, TextStyleSettings } from \"@itwin/core-common\";\r\nimport { AnyCurvePrimitive, Range2d } from \"@itwin/core-geometry\";\r\nimport { ComputeRangesForTextLayoutArgs, FindFontId, FindTextStyle, layoutTextBlock, TextBlockLayout, TextLayoutRanges, TextStyleResolver } from \"../core-backend\";\r\nimport { Id64String } from \"@itwin/core-bentley\";\r\nimport { FormatDefinition } from \"@itwin/core-quantity\";\r\nimport { FormatSet } from \"@itwin/ecschema-metadata\";\r\n\r\n\r\n/** Builds a decimal [FormatDefinition] rendering a magnitude in `unitName`, labelled `unitLabel`. */\r\nexport function decimalFormat(unitName: string, unitLabel: string, precision = 4): FormatDefinition {\r\n return {\r\n composite: { includeZero: true, units: [{ label: unitLabel, name: unitName }] },\r\n formatTraits: [\"keepSingleZero\", \"showUnitLabel\"],\r\n precision,\r\n type: \"Decimal\",\r\n uomSeparator: \" \",\r\n };\r\n}\r\n\r\n/** Wraps a KindOfQuantity -> format map in a metric [FormatSet] named `name`.\r\n * `name` is cosmetic -- a FormatSet is routed by the id it is registered under, not by its name --\r\n * so it exists to tell a reader which set an assertion is about.\r\n */\r\nexport function toFormatSet(name: string, formats: Record<string, FormatDefinition> = {}): FormatSet {\r\n return { name, label: name, unitSystem: \"metric\", formats };\r\n}\r\n\r\n\r\nexport function computeTextRangeAsStringLength(args: ComputeRangesForTextLayoutArgs): TextLayoutRanges {\r\n const range = new Range2d(0, 0, args.chars.length, args.textHeight);\r\n return { layout: range, justification: range };\r\n}\r\n\r\nexport function doLayout(textBlock: TextBlock, args?: {\r\n textStyleId?: Id64String,\r\n findTextStyle?: FindTextStyle;\r\n findFontId?: FindFontId;\r\n}): TextBlockLayout {\r\n const textStyleResolver = new TextStyleResolver({\r\n textBlock,\r\n textStyleId: args?.textStyleId ?? \"\",\r\n iModel: {} as any,\r\n findTextStyle: args?.findTextStyle ?? (() => TextStyleSettings.defaults)\r\n });\r\n const layout = layoutTextBlock({\r\n textBlock,\r\n iModel: {} as any,\r\n textStyleResolver,\r\n findFontId: args?.findFontId ?? (() => 0),\r\n computeTextRange: computeTextRangeAsStringLength,\r\n });\r\n\r\n return layout;\r\n}\r\n\r\n// Extending this because the ElementGeometry.Builder.entries attribute is hard to parse\r\nexport class MockBuilder extends ElementGeometry.Builder {\r\n public params: GeometryParams[] = [];\r\n public geometries: AnyCurvePrimitive[] = [];\r\n public textStrings: TextString[] = []\r\n\r\n public override appendGeometryParamsChange(params: GeometryParams): boolean {\r\n this.params.push(params.clone());\r\n return super.appendGeometryParamsChange(params);\r\n }\r\n public override appendGeometryQuery(geometry: AnyCurvePrimitive): boolean {\r\n this.geometries.push(geometry);\r\n return super.appendGeometryQuery(geometry);\r\n }\r\n\r\n public override appendTextString(text: TextString): boolean {\r\n this.textStrings.push(text);\r\n return super.appendTextString(text);\r\n }\r\n\r\n}\r\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=FieldFormat.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FieldFormat.test.d.ts","sourceRoot":"","sources":["../../../../src/test/annotations/FieldFormat.test.ts"],"names":[],"mappings":""}