@itwin/presentation-components
Version:
React components based on iTwin.js Presentation library
177 lines • 7.28 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 Internal
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.NavigationPropertyItemsLoader = void 0;
exports.useNavigationPropertyTargetsLoader = useNavigationPropertyTargetsLoader;
exports.useNavigationPropertyTargetsRuleset = useNavigationPropertyTargetsRuleset;
require("../../common/DisposePolyfill.js");
const react_1 = require("react");
const rxjs_1 = require("rxjs");
const presentation_common_1 = require("@itwin/presentation-common");
const presentation_frontend_1 = require("@itwin/presentation-frontend");
const ItemsLoader_js_1 = require("./ItemsLoader.js");
/** @internal */
function useNavigationPropertyTargetsLoader(props) {
const { imodel, ruleset, filterText, initialSelectedTarget } = props;
const [itemsLoader, setItemsLoader] = (0, react_1.useState)();
const [state, setLoadedOptions] = (0, react_1.useState)({
options: [],
isLoading: false,
});
// Get initial loader and values
(0, react_1.useEffect)(() => {
setLoadedOptions({ options: [], isLoading: false });
if (!ruleset) {
return;
}
const loader = new NavigationPropertyItemsLoader(() => {
setLoadedOptions((prev) => ({ ...prev, isLoading: true }));
}, (newItems) => {
setLoadedOptions((prev) => ({
options: [...prev.options, ...newItems],
isLoading: false,
}));
}, async (filter) => getItems(imodel, ruleset, filter));
void loader.loadItems(initialSelectedTarget ?? "");
setItemsLoader(loader);
return () => {
loader[Symbol.dispose]();
};
}, [imodel, initialSelectedTarget, ruleset]);
// On filter text change, check if more items need to be loaded
(0, react_1.useEffect)(() => {
if (!itemsLoader) {
return;
}
const timeout = setTimeout(() => {
void itemsLoader.loadItems(filterText);
}, 250);
return () => {
clearTimeout(timeout);
};
}, [itemsLoader, filterText]);
return {
selectOptions: (0, react_1.useMemo)(() => {
const options = state.options.map((option) => {
return {
label: option.label.displayValue,
value: option.label.displayValue,
};
});
if (options.length >= ItemsLoader_js_1.VALUE_BATCH_SIZE) {
options.push(ItemsLoader_js_1.FILTER_WARNING_OPTION);
}
return options;
}, [state.options]),
loadedOptions: state.options,
isLoading: state.isLoading,
};
}
/** @internal */
function useNavigationPropertyTargetsRuleset(getNavigationPropertyInfo, property) {
const [ruleset, setRuleset] = (0, react_1.useState)();
(0, react_1.useEffect)(() => {
let disposed = false;
void (async () => {
const propertyInfo = await getNavigationPropertyInfo(property);
if (!disposed && propertyInfo) {
setRuleset(createNavigationPropertyTargetsRuleset(propertyInfo));
}
})();
return () => {
disposed = true;
};
}, [property, getNavigationPropertyInfo]);
return ruleset;
}
function createNavigationPropertyTargetsRuleset(propertyInfo) {
const [schemaName, className] = propertyInfo.targetClassInfo.name.split(":");
return {
id: `navigation-property-targets`,
rules: [
{
ruleType: presentation_common_1.RuleTypes.Content,
specifications: [
{
specType: presentation_common_1.ContentSpecificationTypes.ContentInstancesOfSpecificClasses,
classes: { schemaName, classNames: [className], arePolymorphic: propertyInfo.isTargetPolymorphic },
},
],
},
],
};
}
async function getItems(imodel, ruleset, filter) {
const requestProps = {
imodel,
rulesetOrId: ruleset,
keys: new presentation_common_1.KeySet(),
descriptor: {
contentFlags: presentation_common_1.ContentFlags.ShowLabels | presentation_common_1.ContentFlags.NoFields,
fieldsFilterExpression: filter ? `/DisplayLabel/ ~ \"%${filter}%\"` : undefined,
},
paging: { size: ItemsLoader_js_1.VALUE_BATCH_SIZE },
};
const items = await new Promise((resolve, reject) => {
(presentation_frontend_1.Presentation.presentation.getContentIterator
? (0, rxjs_1.from)(presentation_frontend_1.Presentation.presentation.getContentIterator(requestProps)).pipe((0, rxjs_1.mergeMap)((result) => (result ? result.items : rxjs_1.EMPTY)), (0, rxjs_1.toArray)())
: // eslint-disable-next-line @typescript-eslint/no-deprecated
(0, rxjs_1.from)(presentation_frontend_1.Presentation.presentation.getContent(requestProps)).pipe((0, rxjs_1.map)((content) => (content ? content.contentSet : [])))).subscribe({
next: resolve,
error: reject,
});
});
const results = items.map((item) => ({
label: item.label,
key: item.primaryKeys[0],
}));
return results;
}
/** @internal */
class NavigationPropertyItemsLoader {
_beforeLoad;
_onItemsLoaded;
_loadItems;
_loadedItems = [];
_isLoading = false;
_disposed = false;
constructor(_beforeLoad, _onItemsLoaded, _loadItems) {
this._beforeLoad = _beforeLoad;
this._onItemsLoaded = _onItemsLoaded;
this._loadItems = _loadItems;
}
[Symbol.dispose]() {
this._disposed = true;
}
async loadItems(filterText) {
if (this._isLoading || filterText === undefined || (filterText === "" && this._loadedItems.length >= ItemsLoader_js_1.VALUE_BATCH_SIZE)) {
return;
}
const filteredItems = this._loadedItems.filter((option) => option.label.displayValue.toLowerCase().includes(filterText.toLowerCase()));
if (filteredItems.length >= ItemsLoader_js_1.VALUE_BATCH_SIZE) {
return;
}
this._isLoading = true;
this._beforeLoad();
const options = await this._loadItems(filterText);
if (this._disposed) {
return;
}
const uniqueOptions = options.filter((option) => {
return !this._loadedItems.some((loadedItem) => {
return loadedItem.key.id === option.key.id && loadedItem.label.displayValue === option.label.displayValue;
});
});
this._loadedItems.push(...uniqueOptions);
this._onItemsLoaded(uniqueOptions);
this._isLoading = false;
}
}
exports.NavigationPropertyItemsLoader = NavigationPropertyItemsLoader;
//# sourceMappingURL=UseNavigationPropertyTargetsLoader.js.map