UNPKG

@itwin/presentation-components

Version:

React components based on iTwin.js Presentation library

315 lines 13 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 Core */ Object.defineProperty(exports, "__esModule", { value: true }); exports.ContentDataProvider = exports.CacheInvalidationProps = void 0; require("./DisposePolyfill.js"); const core_bentley_1 = require("@itwin/core-bentley"); const core_frontend_1 = require("@itwin/core-frontend"); const presentation_common_1 = require("@itwin/presentation-common"); const presentation_frontend_1 = require("@itwin/presentation-frontend"); const ComponentsLoggerCategory_js_1 = require("../ComponentsLoggerCategory.js"); const Diagnostics_js_1 = require("./Diagnostics.js"); const Utils_js_1 = require("./Utils.js"); /** @public */ var CacheInvalidationProps; (function (CacheInvalidationProps) { /** * Create CacheInvalidationProps to fully invalidate all caches. */ CacheInvalidationProps.full = () => ({ descriptor: true, descriptorConfiguration: true, size: true, content: true }); })(CacheInvalidationProps || (exports.CacheInvalidationProps = CacheInvalidationProps = {})); /** * Base class for all presentation-driven content providers. * @public */ class ContentDataProvider { _imodel; _ruleset; _displayType; _keys; _previousKeysGuid; _selectionInfo; _pagingSize; _diagnosticsOptions; _listeners = []; /** Constructor. */ constructor(props) { this._displayType = props.displayType; this._imodel = props.imodel; this._ruleset = props.ruleset; this._keys = new presentation_common_1.KeySet(); this._previousKeysGuid = this._keys.guid; this._pagingSize = props.pagingSize; this._diagnosticsOptions = (0, Diagnostics_js_1.createDiagnosticsOptions)(props); } /** Destructor. Must be called to clean up. */ [Symbol.dispose]() { for (const removeListener of this._listeners) { removeListener(); } this._listeners = []; } /** @deprecated in 5.7. Use `[Symbol.dispose]` instead. */ /* c8 ignore next 3 */ dispose() { this[Symbol.dispose](); } /** Display type used to format content */ get displayType() { return this._displayType; } /** * Paging options for obtaining content. * @see `ContentDataProviderProps.pagingSize` */ get pagingSize() { return this._pagingSize; } set pagingSize(value) { this._pagingSize = value; } /** IModel to pull data from */ get imodel() { return this._imodel; } set imodel(imodel) { if (this._imodel === imodel) { return; } this._imodel = imodel; this.invalidateCache(CacheInvalidationProps.full()); } /** Id of the ruleset to use when requesting content */ get rulesetId() { return (0, Utils_js_1.getRulesetId)(this._ruleset); } set rulesetId(value) { if (this.rulesetId === value) { return; } this._ruleset = value; this.invalidateCache(CacheInvalidationProps.full()); } /** Keys defining what to request content for */ get keys() { return this._keys; } set keys(keys) { if (keys.guid === this._previousKeysGuid) { return; } this._keys = keys; this._previousKeysGuid = this._keys.guid; this.invalidateCache(CacheInvalidationProps.full()); } /** Information about selection event that results in content change */ get selectionInfo() { return this._selectionInfo; } set selectionInfo(info) { if (this._selectionInfo === info) { return; } this._selectionInfo = info; this.invalidateCache(CacheInvalidationProps.full()); } /** * Invalidates cached content. */ invalidateCache(props) { if (props.descriptor && this.getDefaultContentDescriptor) { this.getDefaultContentDescriptor.cache.keys.length = 0; this.getDefaultContentDescriptor.cache.values.length = 0; } if (props.descriptorConfiguration && this.getContentDescriptor) { this.getContentDescriptor.cache.keys.length = 0; this.getContentDescriptor.cache.values.length = 0; } if ((props.content || props.size) && this._getContentAndSize) { this._getContentAndSize.cache.keys.length = 0; this._getContentAndSize.cache.values.length = 0; } } createRequestOptions() { return { imodel: this._imodel, rulesetOrId: this._ruleset, ...(this._diagnosticsOptions ? { diagnostics: this._diagnosticsOptions } : undefined), }; } setupListeners() { if (this._listeners.length > 0) { return; } this._listeners.push(presentation_frontend_1.Presentation.presentation.onIModelContentChanged.addListener(this.onIModelContentChanged)); this._listeners.push(presentation_frontend_1.Presentation.presentation.rulesets().onRulesetModified.addListener(this.onRulesetModified)); this._listeners.push(presentation_frontend_1.Presentation.presentation.vars((0, Utils_js_1.getRulesetId)(this._ruleset)).onVariableChanged.addListener(this.onRulesetVariableChanged)); this._listeners.push(core_frontend_1.IModelApp.quantityFormatter.onActiveFormattingUnitSystemChanged.addListener(this.onUnitSystemChanged)); } /** * Called to check if content should be requested even when `keys` is empty. If this * method returns `false`, then content is not requested and this saves a trip * to the backend. */ shouldRequestContentForEmptyKeyset() { return false; } /** * Get the content descriptor overrides. * * The method may be overriden to configure the content based on content descriptor. If necessary, * it may use [[getContentDescriptor]] to get the descriptor first. */ async getDescriptorOverrides() { return { displayType: this.displayType }; } getDefaultContentDescriptor = (0, Utils_js_1.memoize)(async () => { this.setupListeners(); /* c8 ignore next 5 */ if (this.keys.size > presentation_common_1.DEFAULT_KEYS_BATCH_SIZE) { const msg = `ContentDataProvider.getContentDescriptor requesting descriptor with ${this.keys.size} keys which exceeds the suggested size of ${presentation_common_1.DEFAULT_KEYS_BATCH_SIZE}. Possible "HTTP 413 Payload Too Large" error.`; core_bentley_1.Logger.logWarning(ComponentsLoggerCategory_js_1.PresentationComponentsLoggerCategory.Content, msg); } return presentation_frontend_1.Presentation.presentation.getContentDescriptor({ ...this.createRequestOptions(), displayType: this._displayType, keys: this.keys, selection: this.selectionInfo, }); }); /** * Get the content descriptor. * * The method may return `undefined ` descriptor if: * - [[shouldRequestContentForEmptyKeyset]] returns `false` and `this.keys` is empty * - there is no content based on the ruleset and input */ getContentDescriptor = (0, Utils_js_1.memoize)(async () => { if (!this.shouldRequestContentForEmptyKeyset() && this.keys.isEmpty) { return undefined; } const descriptor = await this.getDefaultContentDescriptor(); if (!descriptor) { return undefined; } return new presentation_common_1.Descriptor({ ...descriptor }); }); /** * Get the number of content records. */ async getContentSetSize() { const paging = undefined !== this.pagingSize ? { start: 0, size: this.pagingSize } : undefined; const contentAndSize = await this._getContentAndSize(paging); return contentAndSize?.size ?? 0; } /** * Get the content. * @param pageOptions Paging options. */ async getContent(pageOptions) { if (undefined !== pageOptions && pageOptions.size !== this.pagingSize) { const msg = `ContentDataProvider.pagingSize doesn't match pageOptions in ContentDataProvider.getContent call. Make sure you set provider's pagingSize to avoid excessive backend requests.`; core_bentley_1.Logger.logWarning(ComponentsLoggerCategory_js_1.PresentationComponentsLoggerCategory.Content, msg); } const contentAndSize = await this._getContentAndSize(pageOptions); return contentAndSize?.content; } /** * Get field using PropertyRecord. * @deprecated in 4.0. Use [[getFieldByPropertyDescription]] instead. */ async getFieldByPropertyRecord(propertyRecord) { return this.getFieldByPropertyDescription(propertyRecord.property); } /** Get field that was used to create a property record with given property description. */ async getFieldByPropertyDescription(descr) { const descriptor = await this.getContentDescriptor(); return descriptor ? (0, Utils_js_1.findField)(descriptor, descr.name) : undefined; } _getContentAndSize = (0, Utils_js_1.memoize)(async (pageOptions) => { if (!this.shouldRequestContentForEmptyKeyset() && this.keys.isEmpty) { return undefined; } this.setupListeners(); const descriptorOverrides = await this.getDescriptorOverrides(); /* c8 ignore next 5 */ if (this.keys.size > presentation_common_1.DEFAULT_KEYS_BATCH_SIZE) { const msg = `ContentDataProvider.getContent requesting with ${this.keys.size} keys which exceeds the suggested size of ${presentation_common_1.DEFAULT_KEYS_BATCH_SIZE}. Possible "HTTP 413 Payload Too Large" error.`; core_bentley_1.Logger.logWarning(ComponentsLoggerCategory_js_1.PresentationComponentsLoggerCategory.Content, msg); } const options = { ...this.createRequestOptions(), descriptor: descriptorOverrides, keys: this.keys, paging: pageOptions, }; if (presentation_frontend_1.Presentation.presentation.getContentIterator) { const result = await presentation_frontend_1.Presentation.presentation.getContentIterator(options); return result ? { size: result.total, content: new presentation_common_1.Content(result.descriptor, await (async () => { const items = []; for await (const item of result.items) { items.push(item); } return items; })()), } : undefined; } const requestSize = undefined !== pageOptions && 0 === pageOptions.start && undefined !== pageOptions.size; if (requestSize) { // eslint-disable-next-line @typescript-eslint/no-deprecated return presentation_frontend_1.Presentation.presentation.getContentAndSize(options); } // eslint-disable-next-line @typescript-eslint/no-deprecated const content = await presentation_frontend_1.Presentation.presentation.getContent(options); return content ? { content, size: content.contentSet.length } : undefined; }, { isMatchingKey: MemoizationHelpers.areContentRequestsEqual }); onContentUpdate() { // note: subclasses are expected to override `invalidateCache` and notify components about // the changed content so components know to reload this.invalidateCache(CacheInvalidationProps.full()); } onIModelContentChanged = (args) => { if (args.rulesetId === this.rulesetId && args.imodelKey === this.imodel.key) { this.onContentUpdate(); } }; onRulesetModified = (curr) => { if (curr.id === this.rulesetId) { this.onContentUpdate(); } }; onRulesetVariableChanged = () => { this.onContentUpdate(); }; onUnitSystemChanged = () => { this.invalidateCache({ content: true }); }; } exports.ContentDataProvider = ContentDataProvider; class MemoizationHelpers { static areContentRequestsEqual(lhsArgs, rhsArgs) { /* c8 ignore next 3 */ if ((lhsArgs[0]?.start ?? 0) !== (rhsArgs[0]?.start ?? 0)) { return false; } /* c8 ignore next 3 */ if ((lhsArgs[0]?.size ?? 0) !== (rhsArgs[0]?.size ?? 0)) { return false; } return true; } } //# sourceMappingURL=ContentDataProvider.js.map