Skip to content

Commit

Permalink
Presentation: Add support for nth level element selection scopes (#3767
Browse files Browse the repository at this point in the history
…) (#3769)

(cherry picked from commit 8d4f86d)

Co-authored-by: Grigas <35135765+grigasp@users.noreply.github.com>
  • Loading branch information
mergify[bot] and grigasp authored Jun 10, 2022
1 parent 3543fe3 commit ba93ac8
Show file tree
Hide file tree
Showing 28 changed files with 510 additions and 205 deletions.
3 changes: 3 additions & 0 deletions common/api/presentation-backend.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
```ts

import { ComputeSelectionRequestOptions } from '@itwin/presentation-common';
import { Content } from '@itwin/presentation-common';
import { ContentDescriptorRequestOptions } from '@itwin/presentation-common';
import { ContentRequestOptions } from '@itwin/presentation-common';
Expand Down Expand Up @@ -179,6 +180,8 @@ export class PresentationManager {
ids: Id64String[];
scopeId: string;
}): Promise<KeySet>;
// @alpha (undocumented)
computeSelection(requestOptions: ComputeSelectionRequestOptions<IModelDb>): Promise<KeySet>;
dispose(): void;
getContent(requestOptions: Prioritized<Paged<ContentRequestOptions<IModelDb, Descriptor | DescriptorOverrides, KeySet>>>): Promise<Content | undefined>;
getContentDescriptor(requestOptions: Prioritized<ContentDescriptorRequestOptions<IModelDb, KeySet>>): Promise<Descriptor | undefined>;
Expand Down
31 changes: 30 additions & 1 deletion common/api/presentation-common.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,17 @@ export interface CompressedClassInfoJSON {
// @public
export type ComputeDisplayValueCallback = (type: string, value: PrimitivePropertyValue, displayValue: string) => Promise<string>;

// @alpha
export interface ComputeSelectionRequestOptions<TIModel> extends RequestOptions<TIModel> {
// (undocumented)
elementIds: Id64String[];
// (undocumented)
scope: SelectionScopeProps;
}

// @alpha (undocumented)
export type ComputeSelectionRpcRequestOptions = PresentationRpcRequestOptions<ComputeSelectionRequestOptions<never>>;

// @public @deprecated
export interface ConditionContainer {
condition?: string;
Expand Down Expand Up @@ -796,6 +807,14 @@ export interface ElementPropertiesStructPropertyItem extends ElementPropertiesPr
type: "struct";
}

// @alpha (undocumented)
export interface ElementSelectionScopeProps {
// (undocumented)
ancestorLevel?: number;
// (undocumented)
id: "element";
}

// @public
export interface EnumerationChoice {
label: string;
Expand Down Expand Up @@ -1301,6 +1320,9 @@ export interface IntsRulesetVariableJSON extends RulesetVariableBaseJSON {
value: number[];
}

// @internal (undocumented)
export function isComputeSelectionRequestOptions<TIModel>(options: ComputeSelectionRequestOptions<TIModel> | SelectionScopeRequestOptions<TIModel>): options is ComputeSelectionRequestOptions<TIModel>;

// @beta
export function isSingleElementPropertiesRequestOptions<TIModel>(options: ElementPropertiesRequestOptions<TIModel>): options is SingleElementPropertiesRequestOptions<TIModel>;

Expand Down Expand Up @@ -1915,6 +1937,8 @@ export interface PresentationIpcInterface {
export class PresentationRpcInterface extends RpcInterface {
// (undocumented)
computeSelection(_token: IModelRpcProps, _options: SelectionScopeRpcRequestOptions, _ids: Id64String[], _scopeId: string): PresentationRpcResponse<KeySetJSON>;
// @alpha (undocumented)
computeSelection(_token: IModelRpcProps, _options: ComputeSelectionRpcRequestOptions): PresentationRpcResponse<KeySetJSON>;
// (undocumented)
getContentDescriptor(_token: IModelRpcProps, _options: ContentDescriptorRpcRequestOptions): PresentationRpcResponse<DescriptorJSON | undefined>;
// @beta (undocumented)
Expand Down Expand Up @@ -2460,7 +2484,7 @@ export class RpcRequestsHandler implements IDisposable {
constructor(props?: RpcRequestsHandlerProps);
readonly clientId: string;
// (undocumented)
computeSelection(options: SelectionScopeRequestOptions<IModelRpcProps>, ids: Id64String[], scopeId: string): Promise<KeySetJSON>;
computeSelection(options: ComputeSelectionRequestOptions<IModelRpcProps>): Promise<KeySetJSON>;
// (undocumented)
dispose(): void;
// (undocumented)
Expand Down Expand Up @@ -2686,6 +2710,11 @@ export interface SelectionScope {
label: string;
}

// @alpha (undocumented)
export type SelectionScopeProps = ElementSelectionScopeProps | {
id: string;
};

// @public
export interface SelectionScopeRequestOptions<TIModel> extends RequestOptions<TIModel> {
}
Expand Down
18 changes: 11 additions & 7 deletions common/api/presentation-frontend.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import { Ruleset } from '@itwin/presentation-common';
import { RulesetVariable } from '@itwin/presentation-common';
import { SelectClassInfo } from '@itwin/presentation-common';
import { SelectionScope } from '@itwin/presentation-common';
import { SelectionScopeProps } from '@itwin/presentation-common';
import { SetRulesetVariableParams } from '@itwin/presentation-common';
import { SingleElementPropertiesRequestOptions } from '@itwin/presentation-common';
import { UnitSystemKey } from '@itwin/core-quantity';
Expand Down Expand Up @@ -89,6 +90,9 @@ export function createFavoritePropertiesStorage(type: DefaultFavoritePropertiesS
// @internal (undocumented)
export const createFieldOrderInfos: (field: Field) => FavoritePropertiesOrderInfo[];

// @internal
export function createSelectionScopeProps(scope: SelectionScopeProps | SelectionScope | string | undefined): SelectionScopeProps;

// @public
export enum DefaultFavoritePropertiesStorageTypes {
BrowserLocalStorage = 1,
Expand Down Expand Up @@ -152,7 +156,7 @@ export enum FavoritePropertiesScope {
// @internal (undocumented)
export const getFieldInfos: (field: Field) => Set<PropertyFullName>;

// @public
// @public @deprecated
export function getScopeId(scope: SelectionScope | string | undefined): string;

// @internal (undocumented)
Expand Down Expand Up @@ -498,17 +502,17 @@ export class SelectionHelper {
export class SelectionManager implements ISelectionProvider {
constructor(props: SelectionManagerProps);
addToSelection(source: string, imodel: IModelConnection, keys: Keys, level?: number, rulesetId?: string): void;
addToSelectionWithScope(source: string, imodel: IModelConnection, ids: Id64Arg, scope: SelectionScope | string, level?: number, rulesetId?: string): Promise<void>;
addToSelectionWithScope(source: string, imodel: IModelConnection, ids: Id64Arg, scope: SelectionScopeProps | SelectionScope | string, level?: number, rulesetId?: string): Promise<void>;
clearSelection(source: string, imodel: IModelConnection, level?: number, rulesetId?: string): void;
getHiliteSet(imodel: IModelConnection): Promise<HiliteSet>;
getSelection(imodel: IModelConnection, level?: number): Readonly<KeySet>;
getSelectionLevels(imodel: IModelConnection): number[];
// @internal (undocumented)
getToolSelectionSyncHandler(imodel: IModelConnection): ToolSelectionSyncHandler | undefined;
removeFromSelection(source: string, imodel: IModelConnection, keys: Keys, level?: number, rulesetId?: string): void;
removeFromSelectionWithScope(source: string, imodel: IModelConnection, ids: Id64Arg, scope: SelectionScope | string, level?: number, rulesetId?: string): Promise<void>;
removeFromSelectionWithScope(source: string, imodel: IModelConnection, ids: Id64Arg, scope: SelectionScopeProps | SelectionScope | string, level?: number, rulesetId?: string): Promise<void>;
replaceSelection(source: string, imodel: IModelConnection, keys: Keys, level?: number, rulesetId?: string): void;
replaceSelectionWithScope(source: string, imodel: IModelConnection, ids: Id64Arg, scope: SelectionScope | string, level?: number, rulesetId?: string): Promise<void>;
replaceSelectionWithScope(source: string, imodel: IModelConnection, ids: Id64Arg, scope: SelectionScopeProps | SelectionScope | string, level?: number, rulesetId?: string): Promise<void>;
readonly scopes: SelectionScopesManager;
readonly selectionChange: SelectionChangeEvent;
setSyncWithIModelToolSelection(imodel: IModelConnection, sync?: boolean): void;
Expand All @@ -524,9 +528,9 @@ export interface SelectionManagerProps {
export class SelectionScopesManager {
constructor(props: SelectionScopesManagerProps);
get activeLocale(): string | undefined;
get activeScope(): SelectionScope | string | undefined;
set activeScope(scope: SelectionScope | string | undefined);
computeSelection(imodel: IModelConnection, ids: Id64Arg, scope: SelectionScope | string): Promise<KeySet>;
get activeScope(): SelectionScopeProps | SelectionScope | string | undefined;
set activeScope(scope: SelectionScopeProps | SelectionScope | string | undefined);
computeSelection(imodel: IModelConnection, ids: Id64Arg, scope: SelectionScopeProps | SelectionScope | string): Promise<KeySet>;
getSelectionScopes(imodel: IModelConnection, locale?: string): Promise<SelectionScope[]>;
}

Expand Down
5 changes: 5 additions & 0 deletions common/api/summary/presentation-common.exports.csv
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public;ClassInfoJSON
internal;CommonIpcParams
public;CompressedClassInfoJSON
public;ComputeDisplayValueCallback = (type: string, value: PrimitivePropertyValue, displayValue: string) => Promise
alpha;ComputeSelectionRequestOptions
alpha;ComputeSelectionRpcRequestOptions = PresentationRpcRequestOptions
public;ConditionContainer
deprecated;ConditionContainer
public;Content
Expand Down Expand Up @@ -111,6 +113,7 @@ beta;ElementPropertiesPropertyValueType = "primitive" | "array" | "struct"
beta;ElementPropertiesRequestOptions
beta;ElementPropertiesStructArrayPropertyItem
beta;ElementPropertiesStructPropertyItem
alpha;ElementSelectionScopeProps
public;EnumerationChoice
public;EnumerationInfo
alpha;ExpandedNodeUpdateRecord
Expand Down Expand Up @@ -178,6 +181,7 @@ public;IntRulesetVariable
public;IntRulesetVariableJSON
public;IntsRulesetVariable
public;IntsRulesetVariableJSON
internal;isComputeSelectionRequestOptions
beta;isSingleElementPropertiesRequestOptions
public;Item
public;ItemJSON
Expand Down Expand Up @@ -350,6 +354,7 @@ public;SelectClassInfoJSON
public;SelectedNodeInstancesSpecification
public;SelectionInfo
public;SelectionScope
alpha;SelectionScopeProps = ElementSelectionScopeProps |
public;SelectionScopeRequestOptions
public;SelectionScopeRpcRequestOptions = PresentationRpcRequestOptions
internal;SetRulesetVariableParams
Expand Down
2 changes: 2 additions & 0 deletions common/api/summary/presentation-frontend.exports.csv
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ alpha;consoleDiagnosticsHandler(scopeLogs: DiagnosticsScopeLogs[]): void
alpha;createCombinedDiagnosticsHandler(handlers: DiagnosticsHandler[]): (scopeLogs: DiagnosticsScopeLogs[]) => void
public;createFavoritePropertiesStorage(type: DefaultFavoritePropertiesStorageTypes): IFavoritePropertiesStorage
internal;createFieldOrderInfos: (field: Field) => FavoritePropertiesOrderInfo[]
internal;createSelectionScopeProps(scope: SelectionScopeProps | SelectionScope | string | undefined): SelectionScopeProps
public;DefaultFavoritePropertiesStorageTypes
internal;DEPRECATED_PROPERTIES_SETTING_NAMESPACE = "Properties"
internal;FAVORITE_PROPERTIES_ORDER_INFO_SETTING_NAME = "FavoritePropertiesOrderInfo"
Expand All @@ -16,6 +17,7 @@ public;FavoritePropertiesOrderInfo
public;FavoritePropertiesScope
internal;getFieldInfos: (field: Field) => Set
public;getScopeId(scope: SelectionScope | string | undefined): string
deprecated;getScopeId(scope: SelectionScope | string | undefined): string
internal;HILITE_RULESET: Ruleset
public;HiliteSet
public;HiliteSetProvider
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@itwin/presentation-backend",
"comment": "Add support for nth level element selection scopes",
"type": "none"
}
],
"packageName": "@itwin/presentation-backend"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@itwin/presentation-common",
"comment": "Add support for nth level element selection scopes",
"type": "none"
}
],
"packageName": "@itwin/presentation-common"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@itwin/presentation-components",
"comment": "",
"type": "none"
}
],
"packageName": "@itwin/presentation-components"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@itwin/presentation-frontend",
"comment": "Add support for nth level element selection scopes",
"type": "none"
}
],
"packageName": "@itwin/presentation-frontend"
}
24 changes: 24 additions & 0 deletions full-stack-tests/presentation/src/frontend/SelectionScopes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ describe("Selection Scopes", () => {
expect(selection.has({ className: elementProps[0].classFullName, id: elementProps[0].id! }));
});

it("sets correct selection with 'element' 1st parent level selection scope", async () => {
const elementProps = await imodel.elements.getProps(Id64.fromUint32Pair(28, 0));
await Presentation.selection.addToSelectionWithScope("", imodel, elementProps[0].id!, { id: "element", ancestorLevel: 1 });
const selection = Presentation.selection.getSelection(imodel);
expect(selection.size).to.eq(1);
expect(selection.has({ className: "BisCore:Subject", id: Id64.fromUint32Pair(27, 0) }));
});

it("sets correct selection with 'assembly' selection scope", async () => {
const elementProps = await imodel.elements.getProps(Id64.fromUint32Pair(28, 0));
await Presentation.selection.addToSelectionWithScope("", imodel, elementProps[0].id!, "assembly");
Expand All @@ -49,6 +57,22 @@ describe("Selection Scopes", () => {
expect(selection.has({ className: "BisCore:Subject", id: Id64.fromUint32Pair(27, 0) }));
});

it("sets correct selection with 'element' 2nd parent level selection scope", async () => {
const elementProps = await imodel.elements.getProps(Id64.fromUint32Pair(28, 0));
await Presentation.selection.addToSelectionWithScope("", imodel, elementProps[0].id!, { id: "element", ancestorLevel: 2 });
const selection = Presentation.selection.getSelection(imodel);
expect(selection.size).to.eq(1);
expect(selection.has({ className: "BisCore:Subject", id: Id64.fromUint32Pair(1, 0) }));
});

it("sets correct selection with 'element' exceeding parent level selection scope", async () => {
const elementProps = await imodel.elements.getProps(Id64.fromUint32Pair(28, 0));
await Presentation.selection.addToSelectionWithScope("", imodel, elementProps[0].id!, { id: "element", ancestorLevel: 999 });
const selection = Presentation.selection.getSelection(imodel);
expect(selection.size).to.eq(1);
expect(selection.has({ className: "BisCore:Subject", id: Id64.fromUint32Pair(1, 0) }));
});

it("sets correct selection with 'top-assembly' selection scope", async () => {
const elementProps = await imodel.elements.getProps(Id64.fromUint32Pair(28, 0));
await Presentation.selection.addToSelectionWithScope("", imodel, elementProps[0].id!, "top-assembly");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ import { IModelDb, IModelJsNative, IpcHost } from "@itwin/core-backend";
import { Id64String } from "@itwin/core-bentley";
import { FormatProps, UnitSystemKey } from "@itwin/core-quantity";
import {
Content, ContentDescriptorRequestOptions, ContentFlags, ContentRequestOptions, ContentSourcesRequestOptions, DefaultContentDisplayTypes, Descriptor,
DescriptorOverrides, DiagnosticsOptionsWithHandler, DisplayLabelRequestOptions, DisplayLabelsRequestOptions, DisplayValueGroup,
DistinctValuesRequestOptions, ElementProperties, ElementPropertiesRequestOptions, FilterByInstancePathsHierarchyRequestOptions,
ComputeSelectionRequestOptions, Content, ContentDescriptorRequestOptions, ContentFlags, ContentRequestOptions, ContentSourcesRequestOptions,
DefaultContentDisplayTypes, Descriptor, DescriptorOverrides, DiagnosticsOptionsWithHandler, DisplayLabelRequestOptions, DisplayLabelsRequestOptions,
DisplayValueGroup, DistinctValuesRequestOptions, ElementProperties, ElementPropertiesRequestOptions, FilterByInstancePathsHierarchyRequestOptions,
FilterByTextHierarchyRequestOptions, HierarchyCompareInfo, HierarchyCompareOptions, HierarchyRequestOptions, InstanceKey,
isSingleElementPropertiesRequestOptions, Key, KeySet, LabelDefinition, MultiElementPropertiesRequestOptions, Node, NodeKey, NodePathElement, Paged,
PagedResponse, PresentationError, PresentationStatus, Prioritized, Ruleset, SelectClassInfo, SelectionScope, SelectionScopeRequestOptions,
SingleElementPropertiesRequestOptions,
isComputeSelectionRequestOptions, isSingleElementPropertiesRequestOptions, Key, KeySet, LabelDefinition, MultiElementPropertiesRequestOptions, Node,
NodeKey, NodePathElement, Paged, PagedResponse, PresentationError, PresentationStatus, Prioritized, Ruleset, SelectClassInfo, SelectionScope,
SelectionScopeRequestOptions, SingleElementPropertiesRequestOptions,
} from "@itwin/presentation-common";
import { PRESENTATION_BACKEND_ASSETS_ROOT, PRESENTATION_COMMON_ASSETS_ROOT } from "./Constants";
import { buildElementsProperties, getElementsCount, iterateElementIds } from "./ElementPropertiesHelper";
Expand Down Expand Up @@ -739,9 +739,17 @@ export class PresentationManager {
* Computes selection set based on provided selection scope.
* @public
*/
public async computeSelection(requestOptions: SelectionScopeRequestOptions<IModelDb> & { ids: Id64String[], scopeId: string }): Promise<KeySet> {
const { ids, scopeId, ...opts } = requestOptions; // eslint-disable-line @typescript-eslint/no-unused-vars
return SelectionScopesHelper.computeSelection(opts, ids, scopeId);
public async computeSelection(requestOptions: SelectionScopeRequestOptions<IModelDb> & { ids: Id64String[], scopeId: string }): Promise<KeySet>;
/** @alpha */
// eslint-disable-next-line @typescript-eslint/unified-signatures
public async computeSelection(requestOptions: ComputeSelectionRequestOptions<IModelDb>): Promise<KeySet>;
public async computeSelection(requestOptions: (SelectionScopeRequestOptions<IModelDb> & { ids: Id64String[], scopeId: string }) | ComputeSelectionRequestOptions<IModelDb>): Promise<KeySet> {
return SelectionScopesHelper.computeSelection(isComputeSelectionRequestOptions(requestOptions)
? requestOptions
: (function () {
const { ids, scopeId, ...rest } = requestOptions;
return { ...rest, elementIds: ids, scope: { id: scopeId } };
})());
}

private async request<TParams extends { diagnostics?: DiagnosticsOptionsWithHandler, requestId: string, imodel: IModelDb, locale?: string, unitSystem?: UnitSystemKey }, TResult>(params: TParams, reviver?: (key: string, value: any) => any): Promise<TResult> {
Expand Down
Loading

0 comments on commit ba93ac8

Please sign in to comment.