UNPKG

@itwin/presentation-components

Version:

React components based on iTwin.js Presentation library

429 lines 18.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. *--------------------------------------------------------------------------------------------*/ /** @packageDocumentation * @module InstancesFilter */ Object.defineProperty(exports, "__esModule", { value: true }); exports.PresentationInstanceFilter = void 0; 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 core_common_1 = require("@itwin/core-common"); const presentation_common_1 = require("@itwin/presentation-common"); const Utils_js_1 = require("../common/Utils.js"); const Utils_js_2 = require("./Utils.js"); /** @public */ // eslint-disable-next-line @typescript-eslint/no-redeclare var PresentationInstanceFilter; (function (PresentationInstanceFilter) { /** * Converts filter built by [usePropertyFilterBuilder]($components-react) into presentation specific format. * @throws if presentation data cannot be found for properties used in `filter`. */ function fromComponentsPropertyFilter(descriptor, filter) { if ((0, components_react_1.isPropertyFilterRuleGroup)(filter)) { return createPresentationInstanceFilterConditionGroup(descriptor, filter); } return createPresentationInstanceFilterCondition(descriptor, filter); } PresentationInstanceFilter.fromComponentsPropertyFilter = fromComponentsPropertyFilter; /** * Converts [[PresentationInstanceFilter]] into format used by [usePropertyFilterBuilder]($components-react). * @throws if fields used in filter cannot be found in `descriptor`. */ function toComponentsPropertyFilter(descriptor, filter) { if (PresentationInstanceFilter.isConditionGroup(filter)) { return createPropertyFilterRuleGroup(filter, descriptor); } return createPropertyFilterRule(filter, descriptor); } PresentationInstanceFilter.toComponentsPropertyFilter = toComponentsPropertyFilter; /** * Extracts information from [[PresentationInstanceFilter]] and creates a [GenericInstanceFilter]($common) for building queries. */ function toGenericInstanceFilter(filter, filteredClasses) { const context = { relatedInstances: [], propertyClasses: [], usedRelatedAliases: new Map() }; const rules = createGenericInstanceFilter(filter, context); return { rules, relatedInstances: context.relatedInstances.map((instance) => ({ path: toRelationshipStep(instance.path), alias: instance.alias })), propertyClassNames: context.propertyClasses.map((classInfo) => classInfo.name), filteredClassNames: filteredClasses?.map((classInfo) => classInfo.name), }; } PresentationInstanceFilter.toGenericInstanceFilter = toGenericInstanceFilter; /** * Creates [[PresentationInstanceFilter]] from given [GenericInstanceFilter]($common). * @throws if fields used in `filter` cannot be found in `descriptor`. */ function fromGenericInstanceFilter(descriptor, filter) { return parseGenericFilter(filter, descriptor); } PresentationInstanceFilter.fromGenericInstanceFilter = fromGenericInstanceFilter; /** * Function that checks if supplied [[PresentationInstanceFilter]] is [[PresentationInstanceFilterConditionGroup]]. */ function isConditionGroup(filter) { return filter.conditions !== undefined; } PresentationInstanceFilter.isConditionGroup = isConditionGroup; /** * Function that creates equality condition based on supplied [[PropertiesField]] and [[PrimitiveValue]] that is compatible * with `UniquePropertyValuesSelector`. * If [[PrimitiveValue.value]] is `undefined` created condition uses `is-null` or `is-not-null` operator. */ function createPrimitiveValueEqualityCondition(field, operator, value) { if (!isValidPrimitiveValue(value)) { return { field, operator: operator === "is-equal" ? "is-null" : "is-not-null", }; } return { field, operator, value: createUniqueValue(value), }; } PresentationInstanceFilter.createPrimitiveValueEqualityCondition = createPrimitiveValueEqualityCondition; })(PresentationInstanceFilter || (exports.PresentationInstanceFilter = PresentationInstanceFilter = {})); function createPresentationInstanceFilterConditionGroup(descriptor, group) { return { operator: group.operator, conditions: group.rules.map((rule) => PresentationInstanceFilter.fromComponentsPropertyFilter(descriptor, rule)), }; } function createPresentationInstanceFilterCondition(descriptor, condition) { const field = (0, Utils_js_1.findField)(descriptor, (0, Utils_js_2.getInstanceFilterFieldName)(condition.property)); if (!field || !field.isPropertiesField()) { throw new presentation_common_1.PresentationError(presentation_common_1.PresentationStatus.Error, `Failed to find properties field for property - ${condition.property.name}`); } if (condition.value && condition.value.valueFormat !== appui_abstract_1.PropertyValueFormat.Primitive) { throw new presentation_common_1.PresentationError(presentation_common_1.PresentationStatus.Error, `Property '${condition.property.name}' cannot be compared with non primitive value.`); } return { operator: condition.operator, field, value: condition.value, }; } function createPropertyFilterRule(condition, descriptor) { const field = descriptor.getFieldByName(condition.field.name, true); if (!field || !field.isPropertiesField()) { throw new presentation_common_1.PresentationError(presentation_common_1.PresentationStatus.Error, `Failed to find properties field - ${condition.field.name} in descriptor`); } return { property: (0, Utils_js_2.createPropertyInfoFromPropertiesField)(field).propertyDescription, operator: condition.operator, value: condition.value, }; } function createPropertyFilterRuleGroup(group, descriptor) { return { operator: group.operator, rules: group.conditions.map((condition) => PresentationInstanceFilter.toComponentsPropertyFilter(descriptor, condition)), }; } function createGenericInstanceFilter(filter, ctx) { if (PresentationInstanceFilter.isConditionGroup(filter)) { return createGenericInstanceFilterRuleGroup(filter, ctx); } const result = createGenericInstanceFilterUniqueValueRules(filter, ctx); if (result !== undefined) { return result; } return createGenericInstanceFilterRule(filter, ctx); } function createGenericInstanceFilterUniqueValueRules(filter, ctx) { // Unique values works only with `IsEqual` and `IsNotEqual` operators. if (filter.operator !== "is-equal" && filter.operator !== "is-not-equal") { return undefined; } if (typeof filter.value?.value !== "string" || typeof filter.value?.displayValue !== "string") { return undefined; } const result = createUniqueValueConditions(filter, filter.value.displayValue, filter.value.value); if (result === undefined) { return undefined; } return createGenericInstanceFilterRuleGroup(result, ctx); } function createGenericInstanceFilterRuleGroup(group, ctx) { const convertedConditions = group.conditions.map((condition) => createGenericInstanceFilter(condition, ctx)); return { operator: group.operator, rules: convertedConditions, }; } function createGenericInstanceFilterRule(condition, ctx) { const { field, operator, value } = condition; // we can use first property when creating `GenericInstanceFilterRule` because all properties in this field will have same name. const property = field.properties[0].property; const relatedInstance = getRelatedInstanceDescription(field, property.classInfo.name, ctx); addClassInfoToContext(relatedInstance ? [relatedInstance.path[0].sourceClassInfo] : field.properties.map((prop) => prop.property.classInfo), ctx); const propertyAlias = relatedInstance?.alias ?? "this"; return { operator, value: toGenericInstanceFilterRuleValue(value), sourceAlias: propertyAlias, propertyName: property.name, propertyTypeName: field.type.typeName, }; } function createUniqueValueConditions(filter, serializedDisplayValues, serializedGroupedRawValues) { const { field, operator } = filter; const uniqueValues = (0, Utils_js_1.deserializeUniqueValues)(serializedDisplayValues, serializedGroupedRawValues); if (uniqueValues === undefined) { return undefined; } const conditionGroup = { operator: operator === "is-equal" ? "or" : "and", conditions: [], }; for (const { displayValue, groupedRawValues } of uniqueValues) { for (const value of groupedRawValues) { conditionGroup.conditions.push({ field, operator, value: { valueFormat: appui_abstract_1.PropertyValueFormat.Primitive, displayValue, value }, }); } } return conditionGroup; } function getRelatedInstanceDescription(field, propClassName, ctx) { if (!field.parent) { return undefined; } const pathToProperty = presentation_common_1.RelationshipPath.reverse(getPathToPrimaryClass(field.parent)); const existing = ctx.relatedInstances.find((instance) => presentation_common_1.RelationshipPath.equals(pathToProperty, instance.path)); if (existing) { return existing; } const baseAlias = `rel_${propClassName.split(":")[1]}`; const index = getAliasIndex(baseAlias, ctx.usedRelatedAliases); const newRelated = { path: pathToProperty, alias: `${baseAlias}_${index}`, }; ctx.relatedInstances.push(newRelated); return newRelated; } function addClassInfoToContext(classInfos, ctx) { for (const classInfo of classInfos) { if (ctx.propertyClasses.find((existing) => existing.id === classInfo.id)) { return; } ctx.propertyClasses.push(classInfo); } } function getPathToPrimaryClass(field) { if (field.parent) { return [...field.pathToPrimaryClass, ...getPathToPrimaryClass(field.parent)]; } return [...field.pathToPrimaryClass]; } function getAliasIndex(alias, usedAliases) { const index = usedAliases.has(alias) ? usedAliases.get(alias) + 1 : 0; usedAliases.set(alias, index); return index; } function parseGenericFilter(filter, descriptor) { const ctx = { findField: (propName, alias) => { const field = alias === "this" ? findDirectField(descriptor.fields, propName) : findRelatedField(descriptor.fields, propName, alias, filter.relatedInstances); if (!field) { throw new presentation_common_1.PresentationError(presentation_common_1.PresentationStatus.Error, `Failed to find field for property - ${alias}.${propName}`); } return field; }, }; return parseGenericFilterRules(filter.rules, ctx); } function parseGenericFilterRules(rules, ctx) { if (core_common_1.GenericInstanceFilter.isFilterRuleGroup(rules)) { return parseGenericFilterRuleGroup(rules, ctx); } return parseGenericFilterRule(rules, ctx); } function parseGenericFilterRuleGroup(group, ctx) { if (group.rules.every((rule) => !core_common_1.GenericInstanceFilter.isFilterRuleGroup(rule) && (rule.operator === "is-equal" || rule.operator === "is-not-equal"))) { const uniqueValueRule = parseUniqueValuesRule(group.rules, ctx); if (uniqueValueRule) { return uniqueValueRule; } } return { operator: group.operator, conditions: group.rules.map((rule) => parseGenericFilterRules(rule, ctx)), }; } function parseGenericFilterRule(rule, ctx) { const field = ctx.findField(rule.propertyName, rule.sourceAlias); return { field, operator: rule.operator, value: rule.value ? { valueFormat: appui_abstract_1.PropertyValueFormat.Primitive, displayValue: rule.value.displayValue, value: rule.value.rawValue, ...("roundingError" in rule.value ? { roundingError: rule.value.roundingError } : undefined), } : undefined, }; } function parseUniqueValuesRule(rules, ctx) { if (rules.length === 0) { return undefined; } // check if all rules in group use same property and operator const ruleInfo = { propName: rules[0].propertyName, alias: rules[0].sourceAlias, operator: rules[0].operator }; if (!rules.every((rule) => rule.operator === ruleInfo.operator && rule.propertyName === ruleInfo.propName && rule.sourceAlias === ruleInfo.alias)) { return undefined; } const field = ctx.findField(rules[0].propertyName, rules[0].sourceAlias); const uniqueValues = []; for (const rule of rules) { (0, core_bentley_1.assert)(rule.value?.displayValue !== undefined && rule.value.rawValue !== undefined); const displayValue = rule.value.displayValue; const value = rule.value.rawValue; const currentValue = uniqueValues.find((val) => val.displayValue === displayValue); if (currentValue) { currentValue.groupedRawValues.push(value); } else { uniqueValues.push({ displayValue, groupedRawValues: [value], }); } } const { displayValues, groupedRawValues } = (0, Utils_js_1.serializeUniqueValues)(uniqueValues); return { operator: rules[0].operator, field, value: { valueFormat: appui_abstract_1.PropertyValueFormat.Primitive, displayValue: displayValues, value: groupedRawValues }, }; } function findDirectField(fields, propName) { for (const field of fields) { if (!field.isPropertiesField()) { continue; } // check only the name of the first property because only properties with the same name can be merged under single field. if (field.properties[0].property.name === propName) { return field; } } return undefined; } function findRelatedField(fields, propName, alias, relatedInstances) { const relatedInstance = relatedInstances.find((rel) => alias === rel.alias); if (!relatedInstance) { return undefined; } const relatedField = findFieldByPath(fields, relatedInstance.path); return relatedField ? findDirectField(relatedField.nestedFields, propName) : undefined; } function findFieldByPath(fields, pathToField) { for (const field of fields) { if (!field.isNestedContentField()) { continue; } const pathMatchResult = pathStartsWith(pathToField, presentation_common_1.RelationshipPath.reverse(field.pathToPrimaryClass)); if (!pathMatchResult.matches) { continue; } if (pathMatchResult.leftOver.length === 0) { return field; } const nestedField = findFieldByPath(field.nestedFields, pathMatchResult.leftOver); if (nestedField) { return nestedField; } } return undefined; } function pathStartsWith(prefix, path) { if (prefix.length < path.length) { return { matches: false }; } for (let i = 0; i < path.length; ++i) { const prefixStep = prefix[i]; const pathStep = path[i]; if (prefixStep.sourceClassName !== pathStep.sourceClassInfo.name || prefixStep.targetClassName !== pathStep.targetClassInfo.name || prefixStep.relationshipClassName !== pathStep.relationshipInfo.name || prefixStep.isForwardRelationship !== pathStep.isForwardRelationship) { return { matches: false }; } } const leftOver = prefix.slice(path.length); return { matches: true, leftOver, }; } function toRelationshipStep(path) { return path.map((step) => ({ sourceClassName: step.sourceClassInfo.name, targetClassName: step.targetClassInfo.name, relationshipClassName: step.relationshipInfo.name, isForwardRelationship: step.isForwardRelationship, })); } function toGenericInstanceFilterRuleValue(primitiveValue) { if (!primitiveValue || primitiveValue.value === undefined || !isGenericPrimitiveValueLike(primitiveValue.value)) { return undefined; } if (typeof primitiveValue.value === "number") { return { displayValue: primitiveValue.displayValue ?? "", rawValue: primitiveValue.value, ...("roundingError" in primitiveValue ? { roundingError: primitiveValue.roundingError } : undefined), }; } return { displayValue: primitiveValue.displayValue ?? "", rawValue: primitiveValue.value, }; } function isGenericPrimitiveValueLike(value) { switch (typeof value) { case "string": case "number": case "boolean": return true; } return isPoint3dLike(value) || isPoint2dLike(value) || isInstanceKeyLike(value); } function isPoint2dLike(value) { return value.x !== undefined && value.y !== undefined; } function isPoint3dLike(value) { return isPoint2dLike(value) && value.z !== undefined; } function isInstanceKeyLike(value) { return value.id !== undefined && value.className !== undefined; } function isValidPrimitiveValue(val) { return val.value !== undefined && val.displayValue !== undefined; } function createUniqueValue(value) { const { displayValues, groupedRawValues } = (0, Utils_js_1.serializeUniqueValues)([ { displayValue: value.displayValue, groupedRawValues: [value.value], }, ]); return { displayValue: displayValues, value: groupedRawValues, valueFormat: appui_abstract_1.PropertyValueFormat.Primitive, }; } //# sourceMappingURL=PresentationInstanceFilter.js.map