UNPKG

@itwin/presentation-common

Version:

Common pieces for iModel.js presentation packages

214 lines 7.47 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module RPC */ import { assert, DuplicatePolicy, Id64, SortedArray } from "@itwin/core-bentley"; import { createContentTraverser, } from "./content/ContentTraverser.js"; import { PropertyValueFormat } from "./content/TypeDescription.js"; /** @internal */ export const createElementPropertiesBuilder = () => { const builder = new ElementPropertiesBuilder(); const traverseContent = createContentTraverser(builder); return (descriptor, item) => { traverseContent(descriptor, [item]); return builder.items[0]; }; }; class ElementPropertiesAppender { _item; _onItemFinished; _propertyItems = {}; _categoryItemAppenders = {}; constructor(_item, _onItemFinished) { this._item = _item; this._onItemFinished = _onItemFinished; } append(label, item) { this._propertyItems[label] = item; } finish() { // create an ordered list of category depths / appenders starting with categories that have the most ancestors and finishing with categories // that have no ancestors, so that when we call `finish` on appenders, child categories are finished before parent categories, otherwise // we may skip parent categories, thinking they have no items const categoriesNestedToRoot = new SortedArray((lhs, rhs) => rhs.categoryDepth - lhs.categoryDepth, DuplicatePolicy.Allow); Object.entries(this._categoryItemAppenders).forEach(([_, { category, appender }]) => { categoriesNestedToRoot.insert({ categoryDepth: countAncestors(category), appender }); }); categoriesNestedToRoot.forEach(({ appender }) => appender.finish()); this._onItemFinished({ class: this._item.classInfo?.label ?? "", id: this._item.primaryKeys[0]?.id ?? Id64.invalid, label: this._item.label.displayValue, items: this._propertyItems, }); } getCategoryAppender(parentAppender, category) { let entry = this._categoryItemAppenders[category.name]; if (!entry) { entry = { category, appender: new CategoryItemAppender(parentAppender, category) }; this._categoryItemAppenders[category.name] = entry; } return entry.appender; } } class CategoryItemAppender { _parentAppender; _category; _items = {}; constructor(_parentAppender, _category) { this._parentAppender = _parentAppender; this._category = _category; } append(label, item) { this._items[label] = item; } finish() { if (Object.keys(this._items).length === 0) { return; } this._parentAppender.append(this._category.label, { type: "category", items: this._items, }); } } class ArrayItemAppender { _parentAppender; _props; _items = []; constructor(_parentAppender, _props) { this._parentAppender = _parentAppender; this._props = _props; } append(_label, item) { assert(item.type !== "category"); this._items.push(item); } finish() { assert(this._props.valueType.valueFormat === PropertyValueFormat.Array); if (this._props.valueType.memberType.valueFormat === PropertyValueFormat.Primitive) { this._parentAppender.append(this._props.hierarchy.field.label, this.createPrimitivesArray()); } else { this._parentAppender.append(this._props.hierarchy.field.label, this.createStructsArray()); } } createPrimitivesArray() { return { type: "array", valueType: "primitive", values: this._items.map((item) => { assert(item.type === "primitive"); return item.value; }), }; } createStructsArray() { return { type: "array", valueType: "struct", values: this._items.map((item) => { assert(item.type === "struct"); return item.members; }), }; } } class StructItemAppender { _parentAppender; _props; _members = {}; constructor(_parentAppender, _props) { this._parentAppender = _parentAppender; this._props = _props; } append(label, item) { assert(item.type !== "category"); this._members[label] = item; } finish() { assert(this._props.valueType.valueFormat === PropertyValueFormat.Struct); this._parentAppender.append(this._props.hierarchy.field.label, { type: "struct", members: this._members, }); } } class ElementPropertiesBuilder { _appendersStack = []; _items = []; _elementPropertiesAppender; get items() { return this._items; } get _currentAppender() { const appender = this._appendersStack[this._appendersStack.length - 1]; assert(appender !== undefined); return appender; } startContent(_props) { this._appendersStack = []; this._items = []; this._elementPropertiesAppender = undefined; return true; } finishContent() { } processFieldHierarchies(_props) { } startItem(props) { this._elementPropertiesAppender = new ElementPropertiesAppender(props.item, (item) => this._items.push(item)); this._appendersStack.push(this._elementPropertiesAppender); return true; } finishItem() { this._appendersStack.pop(); assert(this._elementPropertiesAppender !== undefined); this._elementPropertiesAppender.finish(); this._elementPropertiesAppender = undefined; } startCategory(props) { assert(this._elementPropertiesAppender !== undefined); this._appendersStack.push(this._elementPropertiesAppender.getCategoryAppender(this._currentAppender, props.category)); return true; } finishCategory() { this._appendersStack.pop(); } startField(_props) { return true; } finishField() { } startStruct(props) { this._appendersStack.push(new StructItemAppender(this._currentAppender, props)); return true; } finishStruct() { this._appendersStack.pop().finish(); } startArray(props) { this._appendersStack.push(new ArrayItemAppender(this._currentAppender, props)); return true; } finishArray() { this._appendersStack.pop().finish(); } processMergedValue(props) { this._currentAppender.append(props.mergedField.label, { type: "primitive", value: "", }); } processPrimitiveValue(props) { this._currentAppender.append(props.field.label, { type: "primitive", // eslint-disable-next-line @typescript-eslint/no-base-to-string value: props.displayValue?.toString() ?? "", }); } } function countAncestors(child) { return child.parent ? 1 + countAncestors(child.parent) : 0; } //# sourceMappingURL=ElementProperties.js.map