@itwin/presentation-common
Version:
Common pieces for iModel.js presentation packages
197 lines • 8.49 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module Content
*/
import { assert } from "@itwin/core-bentley";
import { Content } from "./Content.js";
import { Value } from "./Value.js";
/**
* Create a `ContentFormatter` that knows how to format `Content` and its `Item`s.
* @public
*/
export function createContentFormatter(props) {
const propertyValueFormatter = new ContentPropertyValueFormatter(props.propertyValueFormatter);
return new ContentFormatterImpl(propertyValueFormatter, props.unitSystem);
}
export class ContentFormatterImpl {
_propertyValueFormatter;
_unitSystem;
constructor(_propertyValueFormatter, _unitSystem) {
this._propertyValueFormatter = _propertyValueFormatter;
this._unitSystem = _unitSystem;
}
async formatContent(content) {
const formattedItems = await this.formatContentItems(content.contentSet, content.descriptor);
return new Content(content.descriptor, formattedItems);
}
async formatContentItems(items, descriptor) {
return Promise.all(items.map(async (item) => {
await this.formatValues(item.values, item.displayValues, descriptor.fields, item.mergedFieldNames);
return item;
}));
}
async formatValues(values, displayValues, fields, mergedFields) {
for (const field of fields) {
const value = values[field.name];
// format display value of merged values
if (mergedFields.includes(field.name)) {
displayValues[field.name] = "@Presentation:label.varies@";
continue;
}
// do not add undefined value to display values
if (value === undefined) {
continue;
}
// format display values of nested content field
if (field.isNestedContentField()) {
assert(Value.isNestedContent(value));
await this.formatNestedContentDisplayValues(value, field.nestedFields);
continue;
}
// format property items
if (field.isPropertiesField()) {
displayValues[field.name] = await this.formatPropertyValue(value, field);
continue;
}
displayValues[field.name] = await this._propertyValueFormatter.formatPropertyValue(field, value, this._unitSystem);
}
}
async formatNestedContentDisplayValues(nestedValues, fields) {
for (const nestedValue of nestedValues) {
await this.formatValues(nestedValue.values, nestedValue.displayValues, fields, nestedValue.mergedFieldNames);
}
}
async formatPropertyValue(value, field) {
if (field.isArrayPropertiesField()) {
assert(Value.isArray(value));
return this.formatArrayItems(value, field);
}
if (field.isStructPropertiesField()) {
assert(Value.isMap(value));
return this.formatStructMembers(value, field);
}
return this._propertyValueFormatter.formatPropertyValue(field, value, this._unitSystem);
}
async formatArrayItems(itemValues, field) {
return Promise.all(itemValues.map(async (value) => this.formatPropertyValue(value, field.itemsField)));
}
async formatStructMembers(memberValues, field) {
const displayValues = {};
await Promise.all(field.memberFields.map(async (memberField) => {
const memberValue = memberValues[memberField.name];
// do not add undefined value to display values
if (memberValue === undefined) {
return;
}
displayValues[memberField.name] = await this.formatPropertyValue(memberValue, memberField);
}));
return displayValues;
}
}
export class ContentPropertyValueFormatter {
_propertyValueFormatter;
constructor(_propertyValueFormatter) {
this._propertyValueFormatter = _propertyValueFormatter;
}
async formatPropertyValue(field, value, unitSystem) {
const doubleFormatter = isFieldWithKoq(field)
? async (rawValue) => {
const koq = field.properties[0].property.kindOfQuantity;
const formattedValue = await this._propertyValueFormatter.format(rawValue, { koqName: koq.name, unitSystem });
if (formattedValue !== undefined) {
return formattedValue;
}
return formatDouble(rawValue);
}
: async (rawValue) => formatDouble(rawValue);
return this.formatValue(field, value, { doubleFormatter });
}
async formatValue(field, value, ctx) {
if (field.isPropertiesField()) {
if (field.isArrayPropertiesField()) {
return this.formatArrayValue(field, value);
}
if (field.isStructPropertiesField()) {
return this.formatStructValue(field, value);
}
}
return this.formatPrimitiveValue(field, value, ctx);
}
async formatPrimitiveValue(field, value, ctx) {
if (value === undefined) {
return "";
}
const formatDoubleValue = async (raw) => (ctx ? ctx.doubleFormatter(raw) : formatDouble(raw));
if (field.type.typeName === "point2d" && isPoint2d(value)) {
return `X: ${await formatDoubleValue(value.x)}; Y: ${await formatDoubleValue(value.y)}`;
}
if (field.type.typeName === "point3d" && isPoint3d(value)) {
return `X: ${await formatDoubleValue(value.x)}; Y: ${await formatDoubleValue(value.y)}; Z: ${await formatDoubleValue(value.z)}`;
}
if (field.type.typeName === "dateTime") {
assert(typeof value === "string");
return value;
}
if (field.type.typeName === "bool" || field.type.typeName === "boolean") {
assert(typeof value === "boolean");
return value ? "@Presentation:value.true@" : "@Presentation:value.false@";
}
if (field.type.typeName === "int" || field.type.typeName === "long") {
assert(isNumber(value));
return value.toFixed(0);
}
if (field.type.typeName === "double") {
assert(isNumber(value));
return formatDoubleValue(value);
}
if (field.type.typeName === "navigation") {
assert(Value.isNavigationValue(value));
return value.label.displayValue;
}
if (field.type.typeName === "enum" && field.isPropertiesField()) {
const defaultValue = !field.properties[0].property.enumerationInfo?.isStrict
? value.toString() // eslint-disable-line @typescript-eslint/no-base-to-string
: undefined;
return field.properties[0].property.enumerationInfo?.choices.find(({ value: enumValue }) => enumValue === value)?.label ?? defaultValue;
}
// eslint-disable-next-line @typescript-eslint/no-base-to-string
return value.toString();
}
async formatStructValue(field, value) {
if (!Value.isMap(value)) {
return {};
}
const formattedMember = {};
for (const member of field.memberFields) {
formattedMember[member.name] = await this.formatValue(member, value[member.name]);
}
return formattedMember;
}
async formatArrayValue(field, value) {
if (!Value.isArray(value)) {
return [];
}
return Promise.all(value.map(async (arrayVal) => {
return this.formatValue(field.itemsField, arrayVal);
}));
}
}
function formatDouble(value) {
return value.toFixed(2);
}
function isFieldWithKoq(field) {
return field.isPropertiesField() && field.properties.length > 0 && field.properties[0].property.kindOfQuantity !== undefined;
}
function isPoint2d(obj) {
return obj !== undefined && isNumber(obj.x) && isNumber(obj.y);
}
function isPoint3d(obj) {
return isPoint2d(obj) && isNumber(obj.z);
}
function isNumber(obj) {
return !isNaN(Number(obj));
}
//# sourceMappingURL=PropertyValueFormatter.js.map