UNPKG

@itwin/presentation-components

Version:

React components based on iTwin.js Presentation library

723 lines 32.3 kB
"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module PropertyGrid */ Object.defineProperty(exports, "__esModule", { value: true }); exports.PresentationPropertyDataProvider = exports.DEFAULT_PROPERTY_GRID_RULESET = void 0; require("../common/DisposePolyfill.js"); const fast_sort_1 = require("fast-sort"); const appui_abstract_1 = require("@itwin/appui-abstract"); const components_react_1 = require("@itwin/components-react"); const core_bentley_1 = require("@itwin/core-bentley"); const presentation_common_1 = require("@itwin/presentation-common"); const presentation_core_interop_1 = require("@itwin/presentation-core-interop"); const presentation_frontend_1 = require("@itwin/presentation-frontend"); const ContentBuilder_js_1 = require("../common/ContentBuilder.js"); const ContentDataProvider_js_1 = require("../common/ContentDataProvider.js"); const Utils_js_1 = require("../common/Utils.js"); const Utils_js_2 = require("../favorite-properties/Utils.js"); // eslint-disable-next-line @typescript-eslint/unbound-method const labelsComparer = new Intl.Collator(undefined, { sensitivity: "base" }).compare; /** * Default presentation ruleset used by [[PresentationPropertyDataProvider]]. The ruleset just gets properties * of the selected elements. * * @public */ exports.DEFAULT_PROPERTY_GRID_RULESET = { id: "presentation-components/DefaultPropertyGridContent", rules: [ { ruleType: "Content", onlyIfNotHandled: true, specifications: [ { specType: "SelectedNodeInstances", }, ], }, ], }; /** * Presentation Rules-driven property data provider implementation. * @public */ class PresentationPropertyDataProvider extends ContentDataProvider_js_1.ContentDataProvider { onDataChanged = new components_react_1.PropertyDataChangeEvent(); _includeFieldsWithNoValues; _includeFieldsWithCompositeValues; _isNestedPropertyCategoryGroupingEnabled; _onFavoritesChangedRemoveListener; _shouldCreateFavoritesCategory; /** * Constructor */ constructor(props) { super({ imodel: props.imodel, ruleset: props.ruleset ? props.ruleset : exports.DEFAULT_PROPERTY_GRID_RULESET, displayType: presentation_common_1.DefaultContentDisplayTypes.PropertyPane, ruleDiagnostics: props.ruleDiagnostics, devDiagnostics: props.devDiagnostics, }); this._includeFieldsWithNoValues = true; this._includeFieldsWithCompositeValues = true; this._isNestedPropertyCategoryGroupingEnabled = true; this._shouldCreateFavoritesCategory = !props.disableFavoritesCategory; } #dispose() { super[Symbol.dispose](); if (this._onFavoritesChangedRemoveListener) { this._onFavoritesChangedRemoveListener(); this._onFavoritesChangedRemoveListener = undefined; } } /** * Dispose the presentation property data provider. */ [Symbol.dispose]() { this.#dispose(); } /** @deprecated in 5.7. Use `[Symbol.dispose]` instead. */ /* c8 ignore next 3 */ dispose() { this.#dispose(); } /** * Invalidates cached content and clears categorized data. */ invalidateCache(props) { super.invalidateCache(props); this.getMemoizedData.cache.keys.length = 0; this.getMemoizedData.cache.values.length = 0; this.onDataChanged.raiseEvent(); } /** * Provides content configuration for the property grid */ async getDescriptorOverrides() { return { ...(await super.getDescriptorOverrides()), contentFlags: presentation_common_1.ContentFlags.ShowLabels | presentation_common_1.ContentFlags.MergeResults, }; } /** * Hides the computed display label field from the list of properties */ isFieldHidden(field) { return field.name === "/DisplayLabel/"; } /** * Should fields with no values be included in the property list. No value means: * - For *primitive* fields: `null`, `undefined`, `""` (empty string) * - For *array* fields: `[]` (empty array) * - For *struct* fields: `{}` (object with no members) * * @deprecated in 3.x. Use [FilteringPropertyDataProvider]($components-react) and [IPropertyDataFilterer]($components-react) APIs for filtering-out properties. */ get includeFieldsWithNoValues() { return this._includeFieldsWithNoValues; } set includeFieldsWithNoValues(value) { if (this._includeFieldsWithNoValues === value) { return; } this._includeFieldsWithNoValues = value; this.invalidateCache({ content: true }); } /** * Should fields with composite values be included in the property list. * Fields with composite values: * - *array* fields. * - *struct* fields. * * @deprecated in 3.x. Use [FilteringPropertyDataProvider]($components-react) and [IPropertyDataFilterer]($components-react) APIs for filtering-out properties. */ get includeFieldsWithCompositeValues() { return this._includeFieldsWithCompositeValues; } set includeFieldsWithCompositeValues(value) { if (this._includeFieldsWithCompositeValues === value) { return; } this._includeFieldsWithCompositeValues = value; this.invalidateCache({ content: true }); } /** * Is nested property categories enabled. Defaults to `true`. */ get isNestedPropertyCategoryGroupingEnabled() { return this._isNestedPropertyCategoryGroupingEnabled; } set isNestedPropertyCategoryGroupingEnabled(value) { if (this._isNestedPropertyCategoryGroupingEnabled === value) { return; } this._isNestedPropertyCategoryGroupingEnabled = value; this.invalidateCache({ content: true }); } /* eslint-disable @typescript-eslint/no-deprecated */ /** * Should the specified field be included in the favorites category. * @deprecated in 5.2. Use `isFieldFavoriteAsync` instead. */ isFieldFavorite(field) { return this._shouldCreateFavoritesCategory && presentation_frontend_1.Presentation.favoriteProperties.has(field, this.imodel, presentation_frontend_1.FavoritePropertiesScope.IModel); } /** Should the specified field be included in the favorites category. */ async isFieldFavoriteAsync(field) { if (this.isFieldFavorite === PresentationPropertyDataProvider.prototype.isFieldFavorite) { // if the actual `isFieldFavorite` matches the one in prototype, it means it wasn't overridden, // so we can just ignore it and use the new async version return this._shouldCreateFavoritesCategory ? isFieldFavorite(field, this.imodel) : false; } // otherwise, we need to call the deprecated method to stay backwards compatible... return this.isFieldFavorite(field); } /* eslint-enable @typescript-eslint/no-deprecated */ /** * Sorts the specified list of categories by priority. May be overridden * to supply a different sorting algorithm. */ sortCategories(categories) { (0, fast_sort_1.inPlaceSort)(categories).by([{ desc: (c) => c.priority }, { asc: (c) => c.label, comparer: labelsComparer }]); } /* eslint-disable @typescript-eslint/no-deprecated */ /** * Sorts the specified list of fields by priority. May be overridden to supply a different sorting algorithm. * @deprecated in 5.2. Use `sortFieldsAsync` instead. */ sortFields(category, fields) { /* c8 ignore start */ if (category.name === Utils_js_2.FAVORITES_CATEGORY_NAME) { presentation_frontend_1.Presentation.favoriteProperties.sortFields(this.imodel, fields); } else { (0, fast_sort_1.inPlaceSort)(fields).by([{ desc: (f) => f.priority }, { asc: (f) => f.label, comparer: labelsComparer }]); } /* c8 ignore end */ } /** * Sorts the specified list of fields by priority. May be overridden to supply a different sorting algorithm. */ async sortFieldsAsync(category, fields) { if (this.sortFields === PresentationPropertyDataProvider.prototype.sortFields) { // if the actual `sortFields` matches the one in prototype, it means it wasn't overridden, // so we can just ignore it and use the new async version category.name === Utils_js_2.FAVORITES_CATEGORY_NAME ? await sortFavoriteFields(fields, this.imodel) : (0, fast_sort_1.inPlaceSort)(fields).by([{ desc: (f) => f.priority }, { asc: (f) => f.label, comparer: labelsComparer }]); return; } // otherwise, we need to call the deprecated method to stay backwards compatible... this.sortFields(category, fields); } /* eslint-enable @typescript-eslint/no-deprecated */ /** * Returns property data. */ getMemoizedData = (0, Utils_js_1.memoize)(async () => { this.setupFavoritePropertiesListener(); const content = await this.getContent(); if (!content || 0 === content.contentSet.length) { return createDefaultPropertyData(); } const contentItem = content.contentSet[0]; const callbacks = { isFavorite: async (field) => this.isFieldFavoriteAsync(field), isHidden: (field) => this.isFieldHidden(field), sortCategories: (categories) => this.sortCategories(categories), sortFields: async (category, fields) => this.sortFieldsAsync(category, fields), }; const builder = await PropertyDataBuilder.create({ imodel: this.imodel, descriptor: content.descriptor, // eslint-disable-next-line @typescript-eslint/no-deprecated includeWithNoValues: this.includeFieldsWithNoValues, // eslint-disable-next-line @typescript-eslint/no-deprecated includeWithCompositeValues: this.includeFieldsWithCompositeValues, wantNestedCategories: this._isNestedPropertyCategoryGroupingEnabled, callbacks, }); // note: using deprecated `traverseContent`, because we can't use the replacement `createContentTraverser` due to our peer dep version // eslint-disable-next-line @typescript-eslint/no-deprecated (0, presentation_common_1.traverseContentItem)(builder, content.descriptor, contentItem); return builder.getPropertyData(); }); /** * Returns property data. */ async getData() { return this.getMemoizedData(); } /** * Get keys of instances which were used to create given [PropertyRecord]($appui-abstract). */ async getPropertyRecordInstanceKeys(record) { const content = await this.getContent(); if (!content || 0 === content.contentSet.length) { return []; } let recordField = (0, Utils_js_1.findField)(content.descriptor, record.property.name); if (!recordField) { return []; } const fieldsStack = []; while (recordField.parent) { recordField = recordField.parent; fieldsStack.push(recordField); } fieldsStack.reverse(); let contentItems = content.contentSet; fieldsStack.forEach((field) => { const nestedContent = contentItems.reduce((nc, curr) => { const currItemValue = curr.values[field.name]; (0, core_bentley_1.assert)(presentation_common_1.Value.isNestedContent(currItemValue)); nc.push(...currItemValue); return nc; }, new Array()); contentItems = nestedContent.map((nc) => ({ primaryKeys: nc.primaryKeys, values: nc.values, })); }); return contentItems.reduce((keys, curr) => { keys.push(...curr.primaryKeys); return keys; }, new Array()); } setupFavoritePropertiesListener() { /* c8 ignore next 3 */ if (this._onFavoritesChangedRemoveListener) { return; } this._onFavoritesChangedRemoveListener = presentation_frontend_1.Presentation.favoriteProperties.onFavoritesChanged.addListener(() => this.invalidateCache({})); } } exports.PresentationPropertyDataProvider = PresentationPropertyDataProvider; async function isFieldFavorite(field, imodel) { if (presentation_frontend_1.Presentation.favoriteProperties.hasAsync) { return presentation_frontend_1.Presentation.favoriteProperties.hasAsync(field, imodel, presentation_frontend_1.FavoritePropertiesScope.IModel); } // eslint-disable-next-line @typescript-eslint/no-deprecated return presentation_frontend_1.Presentation.favoriteProperties.has(field, imodel, presentation_frontend_1.FavoritePropertiesScope.IModel); } async function sortFavoriteFields(fields, imodel) { if (presentation_frontend_1.Presentation.favoriteProperties.sortFieldsAsync) { await presentation_frontend_1.Presentation.favoriteProperties.sortFieldsAsync(imodel, fields); return; } // eslint-disable-next-line @typescript-eslint/no-deprecated presentation_frontend_1.Presentation.favoriteProperties.sortFields(imodel, fields); } const createDefaultPropertyData = () => ({ label: appui_abstract_1.PropertyRecord.fromString("", "label"), categories: [], records: {}, }); class PropertyDataBuilder extends ContentBuilder_js_1.InternalPropertyRecordsBuilder { _props; _result; _categoriesCache; _categorizedRecords = new Map(); _favoriteFieldHierarchies = []; _asyncTasks = []; constructor(props) { super((item) => ({ item, append: (record) => { const category = record.fieldHierarchy.field.category; let records = this._categorizedRecords.get(category.name); if (!records) { records = []; this._categorizedRecords.set(category.name, records); } records.push(record); }, }), (record) => { record.imodelKey = (0, presentation_core_interop_1.createIModelKey)(props.imodel); }); this._props = props; this._categoriesCache = new PropertyCategoriesCache(props.wantNestedCategories); } static async create(props) { const builder = new PropertyDataBuilder(props); await builder.initFavoriteFields(props.descriptor); return builder; } async initFavoriteFields(descriptor) { const fieldHierarchies = (0, presentation_common_1.createFieldHierarchies)(descriptor.fields); this._favoriteFieldHierarchies = await this.createFavoriteFieldsList(fieldHierarchies); } async getPropertyData() { await Promise.all(this._asyncTasks); (0, core_bentley_1.assert)(this._result !== undefined); return this._result; } startContent(props) { this._categoriesCache.initFromDescriptor(props.descriptor); return super.startContent(props); } finishItem() { (0, core_bentley_1.assert)(this._result === undefined); const categorizedRecords = {}; this._categorizedRecords.forEach((recs, categoryName) => { destructureRecords(recs); if (recs.length) { // note: will await on all async tasks before returning the result this._asyncTasks.push((async () => { const sortedFields = recs.map((r) => r.fieldHierarchy.field); await this._props.callbacks.sortFields(this._categoriesCache.getEntry(categoryName), sortedFields); categorizedRecords[categoryName] = sortedFields.map((field) => recs.find((r) => r.fieldHierarchy.field === field).record); })()); } }); (0, core_bentley_1.assert)(ContentBuilder_js_1.IPropertiesAppender.isRoot(this.currentPropertiesAppender)); const item = this.currentPropertiesAppender.item; this._result = { label: (0, Utils_js_1.createLabelRecord)(item.label, "label"), description: item.classInfo ? item.classInfo.label : undefined, categories: this.createPropertyCategories() .filter(({ categoryHasParent }) => !categoryHasParent) .map(({ category }) => category), records: categorizedRecords, reusePropertyDataState: true, }; } createPropertyCategories() { // determine which categories are actually used const usedCategoryNames = new Set(); this._categorizedRecords.forEach((records, categoryName) => { /* c8 ignore next 3 */ if (records.length === 0) { return; } let category = this._categoriesCache.getEntry(categoryName); while (category) { usedCategoryNames.add(category.name); if (!this._props.wantNestedCategories) { break; } category = category.parent ? this._categoriesCache.getEntry(category.parent.name) : undefined; } }); // set up categories hierarchy const categoriesHierarchy = new Map(); this._categoriesCache.getEntries().forEach((category) => { if (!usedCategoryNames.has(category.name)) { // skip unused categories return; } const parentCategory = this._props.wantNestedCategories ? category.parent : undefined; let childCategories = categoriesHierarchy.get(parentCategory); if (!childCategories) { childCategories = []; categoriesHierarchy.set(parentCategory, childCategories); } childCategories.push(category); }); // sort categories const nestedSortCategory = (category) => { const childCategories = categoriesHierarchy.get(category); if (childCategories && childCategories.length > 1) { this._props.callbacks.sortCategories(childCategories); } if (childCategories) { childCategories.forEach(nestedSortCategory); } }; nestedSortCategory(undefined); // create a hierarchy of PropertyCategory const propertyCategories = new Array(); const pushPropertyCategories = (parentDescription) => { const childCategoryDescriptions = categoriesHierarchy.get(parentDescription); const childPropertyCategories = (childCategoryDescriptions ?? []).map((categoryDescription) => { const category = { name: categoryDescription.name, label: categoryDescription.label, expand: categoryDescription.expand, }; if (categoryDescription.renderer) { category.renderer = categoryDescription.renderer; } return { category, source: categoryDescription, categoryHasParent: parentDescription !== undefined }; }); propertyCategories.push(...childPropertyCategories); for (const categoryInfo of childPropertyCategories) { const childCategories = pushPropertyCategories(categoryInfo.source); if (childCategories.length) { categoryInfo.category.childCategories = childCategories; } } return childPropertyCategories.map((categoryInfo) => categoryInfo.category); }; pushPropertyCategories(undefined); return propertyCategories; } buildFavoriteFieldAncestors(field) { let parentField = field.parent; while (parentField) { parentField = parentField.clone(); parentField.nestedFields = [field]; parentField.category = this._categoriesCache.getFavoriteCategory(parentField.category); field = parentField; parentField = parentField.parent; } field.rebuildParentship(); } createFavoriteFieldsHierarchy(hierarchy) { const favoriteField = hierarchy.field.clone(); favoriteField.category = this._categoriesCache.getFavoriteCategory(hierarchy.field.category); this.buildFavoriteFieldAncestors(favoriteField); return { field: favoriteField, childFields: hierarchy.childFields.map((c) => this.createFavoriteFieldsHierarchy(c)), }; } async createFavoriteFieldsList(fieldHierarchies) { const favorites = []; await Promise.all(fieldHierarchies.map(async (fh) => traverseFieldHierarchy(fh, async (hierarchy) => { if (!(await this._props.callbacks.isFavorite(hierarchy.field))) { return true; } (0, presentation_common_1.addFieldHierarchy)(favorites, this.createFavoriteFieldsHierarchy(hierarchy)); return false; }))); return favorites; } processFieldHierarchies(props) { super.processFieldHierarchies(props); props.hierarchies.push(...this._favoriteFieldHierarchies); } startStruct(props) { if (this.shouldSkipField(props.hierarchy.field, () => !Object.keys(props.rawValues).length)) { return false; } return super.startStruct(props); } startArray(props) { if (this.shouldSkipField(props.hierarchy.field, () => !props.rawValues.length)) { return false; } return super.startArray(props); } processPrimitiveValue(props) { if (this.shouldSkipField(props.field, () => null === props.rawValue || undefined === props.rawValue || "" === props.rawValue)) { return; } super.processPrimitiveValue(props); } shouldSkipField(field, isValueEmpty) { const isFavorite = this._favoriteFieldHierarchies.find((h) => h.field.name === field.name) !== undefined; // skip values of hidden fields if (!isFavorite && this._props.callbacks.isHidden(field)) { return true; } // skip empty values if (!isFavorite && !this._props.includeWithNoValues && isValueEmpty()) { return true; } if (field.type.valueFormat !== presentation_common_1.PropertyValueFormat.Primitive && !this._props.includeWithCompositeValues) { // skip composite fields if requested return true; } return false; } } class PropertyCategoriesCache { _enableCategoryNesting; _byName = new Map(); constructor(_enableCategoryNesting) { this._enableCategoryNesting = _enableCategoryNesting; } initFromDescriptor(descriptor) { descriptor.categories.forEach((category) => { this.cache(category); }); this.initFromFields(descriptor.fields); } initFromFields(fields) { fields.forEach((field) => { if (field.isNestedContentField()) { this.initFromFields(field.nestedFields); } else { this.cache(field.category); } }); // add parent categories that have no fields of their own [...this._byName.values()].forEach((entry) => { let curr = entry; while (curr) { curr = curr.parent ? this.cache(curr.parent) : undefined; } }); } cache(category) { const entry = this._byName.get(category.name); if (entry) { return entry; } this._byName.set(category.name, category); return category; } getEntry(description) { return this._byName.get(description); } getEntries() { return [...this._byName.values()]; } getFavoriteCategory(sourceCategory) { if (!this._enableCategoryNesting) { return this.getRootFavoritesCategory(); } const fieldCategoryRenameStatus = this.getRenamedCategory(`${Utils_js_2.FAVORITES_CATEGORY_NAME}-${sourceCategory.name}`, sourceCategory); let curr = fieldCategoryRenameStatus; while (!curr.fromCache && curr.category.parent) { const parentCategoryRenameStatus = this.getRenamedCategory(`${Utils_js_2.FAVORITES_CATEGORY_NAME}-${curr.category.parent.name}`, curr.category.parent); curr.category.parent = parentCategoryRenameStatus.category; curr = parentCategoryRenameStatus; } if (!curr.fromCache) { curr.category.parent = this.getRootFavoritesCategory(); } return fieldCategoryRenameStatus.category; } getCachedCategory(name, factory) { let cached = this._byName.get(name); if (cached) { return { category: cached, fromCache: true }; } cached = factory(); this._byName.set(name, cached); return { category: cached, fromCache: false }; } getRootFavoritesCategory() { return this.getCachedCategory(Utils_js_2.FAVORITES_CATEGORY_NAME, Utils_js_2.getFavoritesCategory).category; } getRenamedCategory(name, source) { return this.getCachedCategory(name, () => ({ ...source, name })); } } function shouldDestructureArrayField(field) { // destructure arrays if they're based on nested content field or nested under a nested content field return field.isNestedContentField() || field.parent; } function shouldDestructureStructField(field, totalRecordsCount) { // destructure structs if they're based on nested content and: // - if relationship meaning is 'same instance' - always destructure // - if relationship meaning is 'related instance' - only if it's the only record in the list return field.isNestedContentField() && (field.relationshipMeaning === presentation_common_1.RelationshipMeaning.SameInstance || totalRecordsCount === 1); } function destructureStructMember(member) { // only destructure array member items if (member.record.value.valueFormat !== appui_abstract_1.PropertyValueFormat.Array || !shouldDestructureArrayField(member.fieldHierarchy.field) || !shouldDestructureStructField(member.fieldHierarchy.field, undefined)) { return [member]; } // don't want to include struct arrays without items - just return empty array if (member.record.value.items.length === 0) { return []; } // the array should be of size 1 if (member.record.value.items.length > 1) { return [member]; } // the single item should be a struct const item = member.record.value.items[0]; (0, core_bentley_1.assert)(item.value.valueFormat === appui_abstract_1.PropertyValueFormat.Struct); // if all above checks pass, destructure the struct item const recs = [{ ...member, record: item }]; destructureRecords(recs); return recs; } function destructureStructArrayItems(items, fieldHierarchy) { const destructuredFields = []; fieldHierarchy.childFields.forEach((nestedFieldHierarchy) => { let didDestructure = false; items.forEach((item) => { (0, core_bentley_1.assert)(item.value.valueFormat === appui_abstract_1.PropertyValueFormat.Struct); if (item.value.members[nestedFieldHierarchy.field.name] === undefined) { // the member may not exist at all if we decided to skip it beforehand return; } // destructure a single struct array item member const destructuredMembers = destructureStructMember({ fieldHierarchy: nestedFieldHierarchy, record: item.value.members[nestedFieldHierarchy.field.name], }); // remove the old member and insert all destructured new members delete item.value.members[nestedFieldHierarchy.field.name]; destructuredMembers.forEach((destructuredMember) => { (0, core_bentley_1.assert)(item.value.valueFormat === appui_abstract_1.PropertyValueFormat.Struct); item.value.members[destructuredMember.fieldHierarchy.field.name] = destructuredMember.record; }); // store new members. all items are expected to have the same members, so only need to do this once if (!didDestructure) { destructuredMembers.forEach((destructuredMember) => destructuredFields.push(destructuredMember.fieldHierarchy)); didDestructure = true; } }); }); // if we got a chance to destructure at least one item, replace old members with new ones // in the field hierarchy that we got if (items.length > 0) { fieldHierarchy.childFields = destructuredFields; } } function destructureRecords(records) { let i = 0; while (i < records.length) { const entry = records[i]; if (entry.record.value.valueFormat === appui_abstract_1.PropertyValueFormat.Array && shouldDestructureArrayField(entry.fieldHierarchy.field)) { if (shouldDestructureStructField(entry.fieldHierarchy.field, 1)) { // destructure individual array items destructureStructArrayItems(entry.record.value.items, entry.fieldHierarchy); } // destructure 0 or 1 sized arrays by removing the array record and putting its first item in its place (if any) if (entry.record.value.items.length <= 1) { records.splice(i, 1); if (entry.record.value.items.length > 0) { const item = entry.record.value.items[0]; records.splice(i, 0, { ...entry, fieldHierarchy: entry.fieldHierarchy, record: item }); } continue; } } if (entry.record.value.valueFormat === appui_abstract_1.PropertyValueFormat.Struct && shouldDestructureStructField(entry.fieldHierarchy.field, records.length)) { // destructure structs by replacing them with their member records const members = entry.fieldHierarchy.childFields.reduce((list, nestedFieldHierarchy) => { (0, core_bentley_1.assert)(entry.record.value.valueFormat === appui_abstract_1.PropertyValueFormat.Struct); (0, core_bentley_1.assert)(entry.record.value.members[nestedFieldHierarchy.field.name] !== undefined); const member = { fieldHierarchy: nestedFieldHierarchy, field: nestedFieldHierarchy.field, record: entry.record.value.members[nestedFieldHierarchy.field.name], }; list.push(...destructureStructMember(member)); return list; }, new Array()); records.splice(i, 1, ...members); continue; } ++i; } // lastly, when there's only one record in the list and it's an array that we want destructured, set the `hideCompositePropertyLabel` // attribute so only the items are rendered if (records.length === 1 && records[0].record.value.valueFormat === appui_abstract_1.PropertyValueFormat.Array && shouldDestructureArrayField(records[0].fieldHierarchy.field)) { records[0].record.property.hideCompositePropertyLabel = true; } } async function traverseFieldHierarchy(hierarchy, cb) { if (await cb(hierarchy)) { await Promise.all(hierarchy.childFields.map(async (childHierarchy) => traverseFieldHierarchy(childHierarchy, cb))); } } //# sourceMappingURL=DataProvider.js.map