UNPKG

@itwin/presentation-shared

Version:

The package contains types and utilities used across different iTwin.js Presentation packages.

160 lines 6.95 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ import { createConcatenatedValueJsonSelector, createRawPropertyValueSelector, } from "./ecsql-snippets/ECSqlValueSelectorSnippets.js"; /** * Parses an instance label from query result into a string or a `ConcatenatedValue`. The latter type of result * is expected when label selector is created using `IInstanceLabelSelectClauseFactory.createSelectClause` with * `createConcatenatedValueJsonSelector`. * * @public */ export function parseInstanceLabel(value) { if (!value) { return ""; } if ((value.startsWith("[") && value.endsWith("]")) || (value.startsWith("{") && value.endsWith("}"))) { try { return JSON.parse(value); } catch { // fall through } } // not a JSON object/array return value; } /** * Creates a label select clause in a format `Class label [base36(briefcase id)-base36(local id)]`, where * local and briefcase IDs are calculated based on ECInstance ID: * - `{briefcase id} = ECInstanceId >> 40` * - `{local id} = ECInstanceId & (1 << 40 - 1)` * * @see https://www.itwinjs.org/presentation/advanced/defaultbisrules/#label-overrides * @public */ export function createDefaultInstanceLabelSelectClauseFactory() { return { async createSelectClause(props) { return `( SELECT ${concatenate(props, [ { selector: `COALESCE( ${createRawPropertyValueSelector("c", "DisplayLabel")}, ${createRawPropertyValueSelector("c", "Name")} )`, }, ...createECInstanceIdSuffixSelectors(props.classAlias), ])} FROM [meta].[ECClassDef] AS [c] WHERE [c].[ECInstanceId] = ${createRawPropertyValueSelector(props.classAlias, "ECClassId")} )`; }, }; } /** * Creates an instance label select clause based on its class. * @public */ export function createClassBasedInstanceLabelSelectClauseFactory(props) { const { classHierarchyInspector, clauses: labelClausesByClass } = props; const defaultClauseFactory = props.defaultClauseFactory ?? createDefaultInstanceLabelSelectClauseFactory(); async function getLabelClausesForClass(queryClassName) { const matchingLabelClauses = await Promise.all(labelClausesByClass.map(async (entry) => { if (await classHierarchyInspector.classDerivesFrom(entry.className, queryClassName)) { // label selector is intended for a more specific class than we're selecting from - need to include it // as query results (on polymorphic select) are going to include more specific class instances too return entry; } if (await classHierarchyInspector.classDerivesFrom(queryClassName, entry.className)) { // label selector is intended for a base class of what query is selecting - need to include it as // we want base class label selectors to apply to subclass instances return entry; } return undefined; })); function filterNotUndefined(x) { return !!x; } return matchingLabelClauses.filter(filterNotUndefined); } return { async createSelectClause(clauseProps) { if (labelClausesByClass.length === 0) { return defaultClauseFactory.createSelectClause(clauseProps); } const labelClausePromises = clauseProps.className ? await getLabelClausesForClass(clauseProps.className) : labelClausesByClass; if (labelClausePromises.length === 0) { return defaultClauseFactory.createSelectClause(clauseProps); } const labelClauses = await Promise.all(labelClausePromises.map(async ({ className, clause }) => ({ className, clause: await clause(clauseProps), }))); return `COALESCE( ${labelClauses .map(({ className, clause }) => ` IIF( ${createRawPropertyValueSelector(clauseProps.classAlias, "ECClassId")} IS (${className}), ${clause.trim()}, NULL ) `.trim()) .join(", ")}, ${await defaultClauseFactory.createSelectClause(clauseProps)} )`; }, }; } /** * Creates a label select clause according to BIS instance label rules. * @see https://www.itwinjs.org/presentation/advanced/defaultbisrules/#label-overrides * @public */ export function createBisInstanceLabelSelectClauseFactory(props) { const clauses = []; const factory = createClassBasedInstanceLabelSelectClauseFactory({ classHierarchyInspector: props.classHierarchyInspector, clauses, }); clauses.push({ className: "BisCore.GeometricElement", clause: async ({ classAlias, ...rest }) => ` COALESCE( ${createRawPropertyValueSelector(classAlias, "CodeValue")}, ${concatenate(rest, [{ selector: createRawPropertyValueSelector(classAlias, "UserLabel") }, ...createECInstanceIdSuffixSelectors(classAlias)], `${createRawPropertyValueSelector(classAlias, "UserLabel")} IS NOT NULL`)} ) `, }, { className: "BisCore.Element", clause: async ({ classAlias }) => ` COALESCE( ${createRawPropertyValueSelector(classAlias, "UserLabel")}, ${createRawPropertyValueSelector(classAlias, "CodeValue")} ) `, }, { className: "BisCore.Model", clause: async ({ classAlias, ...rest }) => `( SELECT ${await factory.createSelectClause({ ...rest, classAlias: "e", className: "BisCore.Element" })} FROM [bis].[Element] AS [e] WHERE [e].[ECInstanceId] = ${createRawPropertyValueSelector(classAlias, "ModeledElement", "Id")} )`, }); return factory; } function createECInstanceIdSuffixSelectors(classAlias) { return [ { value: ` [`, type: "String" }, { selector: `CAST(base36(${createRawPropertyValueSelector(classAlias, "ECInstanceId")} >> 40) AS TEXT)` }, { value: `-`, type: "String" }, { selector: `CAST(base36(${createRawPropertyValueSelector(classAlias, "ECInstanceId")} & ((1 << 40) - 1)) AS TEXT)` }, { value: `]`, type: "String" }, ]; } function concatenate(props, selectors, checkSelector) { return (props.selectorsConcatenator ?? createConcatenatedValueJsonSelector)(selectors, checkSelector); } //# sourceMappingURL=InstanceLabelSelectClauseFactory.js.map