UNPKG

@itwin/presentation-components

Version:

React components based on iTwin.js Presentation library

309 lines 15.4 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. *--------------------------------------------------------------------------------------------*/ /* eslint-disable @typescript-eslint/no-deprecated */ /** @packageDocumentation * @module Tree */ Object.defineProperty(exports, "__esModule", { value: true }); exports.PresentationTreeDataProvider = void 0; require("../common/DisposePolyfill.js"); 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_frontend_1 = require("@itwin/presentation-frontend"); const Diagnostics_js_1 = require("../common/Diagnostics.js"); const Utils_js_1 = require("../common/Utils.js"); const ComponentsLoggerCategory_js_1 = require("../ComponentsLoggerCategory.js"); const PresentationFilterBuilder_js_1 = require("../instance-filter-builder/PresentationFilterBuilder.js"); const PresentationTreeNodeItem_js_1 = require("./PresentationTreeNodeItem.js"); const Utils_js_2 = require("./Utils.js"); /** * Presentation Rules-driven tree data provider. * @public * @deprecated in 5.7. All tree-related APIs have been deprecated in favor of the new generation hierarchy * building APIs (see https://github.com/iTwin/presentation/blob/33e79ee8d77f30580a9bab81a72884bda008db25/README.md#the-packages). */ class PresentationTreeDataProvider { _unregisterVariablesChangeListener; _dataSource; _diagnosticsOptions; _onHierarchyLimitExceeded; _props; hierarchyLevelSizeLimit; /** Constructor. */ constructor(props) { this._props = { ...props }; this._dataSource = { getNodesIterator: async (requestOptions) => { // we can't just drop support for the `getNodesAndCount` override, so if it's set - need to take data from it if (props.dataSourceOverrides?.getNodesAndCount) { return createNodesIteratorFromDeprecatedResponse(await props.dataSourceOverrides.getNodesAndCount(requestOptions)); } // the `PresentationManager.getNodesIterator` has only been added to @itwin/presentation-frontend in 4.5.1, and our peerDependency is // set to 4.0.0, so we need to check if the method is really there if (presentation_frontend_1.Presentation.presentation.getNodesIterator) { return presentation_frontend_1.Presentation.presentation.getNodesIterator(requestOptions); } return createNodesIteratorFromDeprecatedResponse(await presentation_frontend_1.Presentation.presentation.getNodesAndCount(requestOptions)); }, getFilteredNodePaths: async (requestOptions) => presentation_frontend_1.Presentation.presentation.getFilteredNodePaths(requestOptions), ...props.dataSourceOverrides, }; this._diagnosticsOptions = (0, Diagnostics_js_1.createDiagnosticsOptions)(props); this.hierarchyLevelSizeLimit = props.hierarchyLevelSizeLimit; this._onHierarchyLimitExceeded = props.onHierarchyLimitExceeded; } #dispose() { this._unregisterVariablesChangeListener?.(); this._unregisterVariablesChangeListener = undefined; } /** Destructor. Must be called to clean up. */ [Symbol.dispose]() { this.#dispose(); } /** @deprecated in 5.7. Use `[Symbol.dispose]` instead. */ /* c8 ignore next 3 */ dispose() { this.#dispose(); } get props() { return this._props; } /** Id of the ruleset used by this data provider */ get rulesetId() { return (0, Utils_js_1.getRulesetId)(this.props.ruleset); } /** [IModelConnection]($core-frontend) used by this data provider */ get imodel() { return this.props.imodel; } /** * Paging options for obtaining nodes. * @see `PresentationTreeDataProviderProps.pagingSize` */ get pagingSize() { return this.props.pagingSize; } set pagingSize(value) { this._props.pagingSize = value; } /** Called to get base options for requests */ createBaseRequestOptions() { return { imodel: this.props.imodel, rulesetOrId: this.props.ruleset, ...(this._diagnosticsOptions ? { diagnostics: this._diagnosticsOptions } : undefined), }; } /** Called to get options for node requests */ createPagedRequestOptions(parentKey, pageOptions, instanceFilter) { const isPaging = pageOptions && (pageOptions.start || pageOptions.size !== undefined); return { ...this.createRequestOptions(parentKey, instanceFilter), ...(isPaging ? { paging: (0, Utils_js_2.pageOptionsUiToPresentation)(pageOptions) } : undefined), }; } /** Creates options for nodes requests. */ createRequestOptions(parentKey, instanceFilter) { const isHierarchyLevelLimitingSupported = !!this.hierarchyLevelSizeLimit && parentKey; return { ...this.createBaseRequestOptions(), ...(parentKey ? { parentKey } : undefined), ...(isHierarchyLevelLimitingSupported ? { sizeLimit: this.hierarchyLevelSizeLimit } : undefined), ...(instanceFilter ? { instanceFilter } : undefined), }; } /** * Returns a [NodeKey]($presentation-common) from given [TreeNodeItem]($components-react). * * **Warning**: Returns invalid [NodeKey]($presentation-common) if `node` is not a [[PresentationTreeNodeItem]]. * * @deprecated in 4.0. Use [[isPresentationTreeNodeItem]] and [[PresentationTreeNodeItem.key]] to get [NodeKey]($presentation-common). */ getNodeKey(node) { const invalidKey = { type: "", pathFromRoot: [], version: 0 }; return (0, PresentationTreeNodeItem_js_1.isPresentationTreeNodeItem)(node) ? node.key : invalidKey; } /** * Returns nodes * @param parentNode The parent node to return children for. * @param pageOptions Information about the requested page of data. */ async getNodes(parentNode, pageOptions) { if (undefined !== pageOptions && pageOptions.size !== this.pagingSize) { const msg = `PresentationTreeDataProvider.pagingSize doesn't match pageOptions in PresentationTreeDataProvider.getNodes call. Make sure you set PresentationTreeDataProvider.pagingSize to avoid excessive backend requests.`; core_bentley_1.Logger.logWarning(ComponentsLoggerCategory_js_1.PresentationComponentsLoggerCategory.Hierarchy, msg); } const instanceFilter = await getFilterDefinition(this.imodel, parentNode); return (await this._getNodesAndCount(parentNode, pageOptions, instanceFilter)).nodes; } /** * Returns the total number of nodes * @param parentNode The parent node to return children count for. */ async getNodesCount(parentNode) { const instanceFilter = await getFilterDefinition(this.imodel, parentNode); return (await this._getNodesAndCount(parentNode, { start: 0, size: this.pagingSize }, instanceFilter)).count; } _getNodesAndCount = (0, Utils_js_1.memoize)(async (parentNode, pageOptions, instanceFilter) => { this.setupRulesetVariablesListener(); const parentKey = parentNode && (0, PresentationTreeNodeItem_js_1.isPresentationTreeNodeItem)(parentNode) ? parentNode.key : undefined; const requestOptions = this.createPagedRequestOptions(parentKey, pageOptions, instanceFilter); return createNodesAndCountResult(async () => this._dataSource.getNodesIterator(requestOptions), this.createBaseRequestOptions(), (node, parentId) => (0, Utils_js_2.createTreeNodeItem)(node, parentId, this.props), parentNode, this.hierarchyLevelSizeLimit, this._onHierarchyLimitExceeded); }, // eslint-disable-next-line @typescript-eslint/unbound-method { isMatchingKey: MemoizationHelpers.areNodesRequestsEqual }); /** * Returns filtered node paths. * @param filter Filter. */ async getFilteredNodePaths(filter) { return this._dataSource.getFilteredNodePaths({ ...this.createBaseRequestOptions(), filterText: filter, }); } setupRulesetVariablesListener() { if (this._unregisterVariablesChangeListener) { return; } this._unregisterVariablesChangeListener = presentation_frontend_1.Presentation.presentation.vars((0, Utils_js_1.getRulesetId)(this.props.ruleset)).onVariableChanged.addListener(() => { this._getNodesAndCount.cache.values.length = 0; this._getNodesAndCount.cache.keys.length = 0; }); } } exports.PresentationTreeDataProvider = PresentationTreeDataProvider; async function getFilterDefinition(imodel, node) { if (!node || !(0, PresentationTreeNodeItem_js_1.isPresentationTreeNodeItem)(node) || !node.filtering) { return undefined; } // combine ancestors and current filters const allFilters = [...node.filtering.ancestorFilters, ...(node.filtering.active ? [node.filtering.active] : [])]; if (allFilters.length === 0) { return undefined; } if (allFilters.length === 1) { return (0, PresentationFilterBuilder_js_1.createInstanceFilterDefinition)(allFilters[0], imodel); } const appliedFilters = allFilters.map((filterInfo) => filterInfo.filter).filter((filter) => filter !== undefined); const usedClasses = getConcatenatedDistinctClassInfos(allFilters); // if there are more than one filter applied, combine them using `AND` operator // otherwise apply filter directly const info = { filter: appliedFilters.length > 0 ? { operator: components_react_1.PropertyFilterRuleGroupOperator.And, conditions: appliedFilters, } : undefined, usedClasses, }; return (0, PresentationFilterBuilder_js_1.createInstanceFilterDefinition)(info, imodel); } function getConcatenatedDistinctClassInfos(appliedFilters) { const concatenatedClassInfos = appliedFilters.reduce((accumulator, value) => [...accumulator, ...value.usedClasses], []); return [...new Map(concatenatedClassInfos.map((item) => [item.id, item])).values()]; } async function createNodesAndCountResult(resultFactory, baseOptions, treeItemFactory, parentNode, hierarchyLevelSizeLimit, onHierarchyLimitExceeded) { try { const result = await resultFactory(); const { items, total: count } = result; const isParentFiltered = parentNode && (0, PresentationTreeNodeItem_js_1.isPresentationTreeNodeItem)(parentNode) && parentNode.filtering?.active; if (count === 0 && isParentFiltered) { return createStatusNodeResult(parentNode, "tree.no-filtered-children", PresentationTreeNodeItem_js_1.InfoTreeNodeItemType.NoChildren); } return { nodes: await createTreeItems(items, baseOptions, treeItemFactory, parentNode), count }; } catch (e) { if (e instanceof Error) { if (hasErrorNumber(e)) { switch (e.errorNumber) { case presentation_common_1.PresentationStatus.Canceled: return { nodes: [], count: 0 }; case presentation_common_1.PresentationStatus.BackendTimeout: return createStatusNodeResult(parentNode, "tree.timeout", PresentationTreeNodeItem_js_1.InfoTreeNodeItemType.BackendTimeout); case presentation_common_1.PresentationStatus.ResultSetTooLarge: // ResultSetTooLarge error can't occur if hierarchyLevelSizeLimit is undefined. onHierarchyLimitExceeded?.(); return { nodes: [ (0, Utils_js_2.createInfoNode)(parentNode, `${(0, Utils_js_1.translate)("tree.result-limit-exceeded")} ${hierarchyLevelSizeLimit}.`, PresentationTreeNodeItem_js_1.InfoTreeNodeItemType.ResultSetTooLarge), ], count: 1, }; } } // eslint-disable-next-line no-console console.error(`Error creating nodes: ${e.toString()}`); } return createStatusNodeResult(parentNode, "tree.unknown-error"); } } function createStatusNodeResult(parentNode, labelKey, type) { return { nodes: [(0, Utils_js_2.createInfoNode)(parentNode, (0, Utils_js_1.translate)(labelKey), type)], count: 1, }; } async function createTreeItems(nodes, baseOptions, treeItemFactory, parentNode) { const items = []; // collect filters for child elements. These filter will be applied for grouping nodes // if current node has `ancestorFilters` it means it is grouping node and those filter should be forwarded to child grouping nodes alongside current node filter. // if current node does not have `ancestorFilters` it means it is an instance node and only it's filter should be applied to child grouping nodes. const ancestorFilters = parentNode && (0, PresentationTreeNodeItem_js_1.isPresentationTreeNodeItem)(parentNode) && parentNode.filtering ? [...parentNode.filtering.ancestorFilters, ...(parentNode.filtering.active ? [parentNode.filtering.active] : [])] : []; for await (const node of nodes) { const item = treeItemFactory(node, parentNode?.id); if (node.supportsFiltering) { item.filtering = { descriptor: async () => { const descriptor = await presentation_frontend_1.Presentation.presentation.getNodesDescriptor({ ...baseOptions, parentKey: node.key }); if (!descriptor) { throw new presentation_common_1.PresentationError(presentation_common_1.PresentationStatus.Error, `Failed to get descriptor for node - ${node.label.displayValue}`); } return descriptor; }, ancestorFilters: presentation_common_1.NodeKey.isGroupingNodeKey(item.key) ? ancestorFilters : [], }; } items.push(item); } return items; } class MemoizationHelpers { static areNodesRequestsEqual(lhsArgs, rhsArgs) { if (lhsArgs[0]?.id !== rhsArgs[0]?.id) { return false; } if ((lhsArgs[1]?.start ?? 0) !== (rhsArgs[1]?.start ?? 0)) { return false; } if ((lhsArgs[1]?.size ?? 0) !== (rhsArgs[1]?.size ?? 0)) { return false; } if (lhsArgs[2]?.expression !== rhsArgs[2]?.expression) { return false; } return true; } } function createNodesIteratorFromDeprecatedResponse({ count, nodes }) { return { total: count, items: (async function* () { for (const node of nodes) { yield node; } })(), }; } function hasErrorNumber(e) { return "errorNumber" in e && e.errorNumber !== undefined; } //# sourceMappingURL=DataProvider.js.map