@itwin/saved-views-react 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -2
- package/lib/SavedView.d.ts +26 -4
- package/lib/SavedViewTile/SavedViewTile.js +2 -2
- package/lib/SavedViewsClient/ITwinSavedViewsClient.d.ts +9 -9
- package/lib/SavedViewsClient/ITwinSavedViewsClient.js +58 -36
- package/lib/SavedViewsClient/SavedViewsClient.d.ts +27 -19
- package/lib/applySavedView.d.ts +10 -7
- package/lib/applySavedView.js +6 -6
- package/lib/captureSavedViewData.d.ts +31 -1
- package/lib/captureSavedViewData.js +33 -7
- package/lib/captureSavedViewThumbnail.d.ts +8 -1
- package/lib/captureSavedViewThumbnail.js +8 -1
- package/lib/createViewState.d.ts +19 -3
- package/lib/createViewState.js +14 -3
- package/lib/index.d.ts +1 -1
- package/lib/translation/SavedViewsExtensionHandlers.d.ts +3 -0
- package/lib/translation/SavedViewsExtensionHandlers.js +14 -0
- package/lib/translation/displayStyleExtractor.js +72 -0
- package/lib/useSavedViews.d.ts +8 -3
- package/lib/useSavedViews.js +27 -24
- package/lib/utils.d.ts +1 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -2,10 +2,59 @@
|
|
|
2
2
|
|
|
3
3
|
## About
|
|
4
4
|
|
|
5
|
-
A collection of React components for building applications that deal with [Saved Views](https://developer.bentley.com/apis/savedviews/overview/).
|
|
5
|
+
A collection of utilities and React components for building iTwin applications that deal with [Saved Views](https://developer.bentley.com/apis/savedviews/overview/).
|
|
6
6
|
|
|
7
7
|
## Documentation
|
|
8
8
|
|
|
9
|
+
### [captureSavedViewData](./src/captureSavedViewData.ts)
|
|
10
|
+
|
|
11
|
+
Captures current viewport state into serializable format. The returned data can later be used to restore viewport's view.
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
const { viewData, extensions = [] } = await captureSavedViewData({ viewport });
|
|
15
|
+
extensions.push(myCustomExtension(viewport));
|
|
16
|
+
console.log({ viewData, extensions }); /*
|
|
17
|
+
{
|
|
18
|
+
viewData: { itwin3dView: {...} },
|
|
19
|
+
extensions: {
|
|
20
|
+
{ extensionName: "EmphasizeElements", data: "{...}" },
|
|
21
|
+
{ extensionName: "MyCustomExtension", data: "my_custom_extension_data" },
|
|
22
|
+
}
|
|
23
|
+
} */
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### [captureSavedViewThumbnail](./src/captureSavedViewThumbnail.ts)
|
|
27
|
+
|
|
28
|
+
Generates Saved View thumbnail based on what is currently displayed on the viewport.
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
const thumbnail = captureSavedViewThumbnail(viewport);
|
|
32
|
+
console.log(thumbnail); // "data:image/png;base64,iVBORw0KGoAAAANSUhEUg..."
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### [applySavedView](./src/applySavedView.ts)
|
|
36
|
+
|
|
37
|
+
Updates viewport state to match captured Saved View.
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// Capture viewport state
|
|
41
|
+
const savedViewData = await captureSavedViewData({ viewport });
|
|
42
|
+
// Restore viewport state
|
|
43
|
+
await applySavedView(iModel, viewport, savedViewData);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### [createViewState](./src/createViewState.ts)
|
|
47
|
+
|
|
48
|
+
Creates ViewState object out of Saved View data. It provides a lower-level access to view data for advanced use.
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
const viewState = await createViewState(iModel, savedViewData.viewData);
|
|
52
|
+
await applySavedView(iModel, viewport, savedViewData, { viewState });
|
|
53
|
+
|
|
54
|
+
// The two lines above are equivalent to
|
|
55
|
+
await applySavedView(iModel, viewport, savedViewData);
|
|
56
|
+
```
|
|
57
|
+
|
|
9
58
|
### React components
|
|
10
59
|
|
|
11
60
|
* [SavedViewTile](./src/SavedViewTile/SavedViewTile.tsx)
|
|
@@ -42,7 +91,7 @@ export function SavedViewsWidget(props) {
|
|
|
42
91
|
}
|
|
43
92
|
```
|
|
44
93
|
|
|
45
|
-
### useSavedViews
|
|
94
|
+
### [useSavedViews](./src/useSavedViews.tsx)
|
|
46
95
|
|
|
47
96
|
[useSavedViews](./src/useSavedViews.tsx) React hook provides basic functionality to jump-start your Saved Views widget. It accepts [`ITwinSavedViewsClient`](./src/SavedViewsClient/ITwinSavedViewsClient.ts) which is used to pull Saved View data and synchronize it back to the [Saved Views service](https://developer.bentley.com/apis/savedviews/overview/).
|
|
48
97
|
|
package/lib/SavedView.d.ts
CHANGED
|
@@ -1,18 +1,39 @@
|
|
|
1
|
+
import type { ViewData } from "@itwin/saved-views-client";
|
|
1
2
|
import type { ReactNode } from "react";
|
|
2
3
|
export interface SavedView {
|
|
3
4
|
id: string;
|
|
4
5
|
displayName: string;
|
|
6
|
+
viewData?: ViewData | undefined;
|
|
5
7
|
groupId?: string | undefined;
|
|
6
8
|
creatorId?: string | undefined;
|
|
7
9
|
tagIds?: string[] | undefined;
|
|
8
10
|
shared?: boolean | undefined;
|
|
9
11
|
thumbnail?: ReactNode | string | undefined;
|
|
10
|
-
|
|
11
|
-
extensions?: Map<string, string> | undefined;
|
|
12
|
+
extensions?: SavedViewExtension[] | undefined;
|
|
12
13
|
/** Time the saved view was created as an ISO8601 string, `"YYYY-MM-DDTHH:mm:ss.sssZ"` */
|
|
13
|
-
creationTime?: string;
|
|
14
|
+
creationTime?: string | undefined;
|
|
14
15
|
/** Time the saved view was last modified as an ISO8601 string, `"YYYY-MM-DDTHH:mm:ss.sssZ"` */
|
|
15
|
-
lastModified?: string;
|
|
16
|
+
lastModified?: string | undefined;
|
|
17
|
+
}
|
|
18
|
+
export interface SavedViewExtension {
|
|
19
|
+
/** Extension identifier. Saved View cannot contain multiple extensions that share the same `extensionName`. */
|
|
20
|
+
extensionName: string;
|
|
21
|
+
/**
|
|
22
|
+
* Serialized extension data.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* const extension = {
|
|
26
|
+
* // Unique identifier makes extension data format portable between applications because it avoids collision
|
|
27
|
+
* // with different implementations
|
|
28
|
+
* extensionName: "CustomHighlight_$5be36494-ae03-4400-bb80-24ffd9db2a87",
|
|
29
|
+
* data: JSON.stringify({
|
|
30
|
+
* description: "For illustrative purposes only. We do not provide implementation for this extensionName."
|
|
31
|
+
* highlightColor: "#f05599",
|
|
32
|
+
* models: ["0x20000000006"],
|
|
33
|
+
* }),
|
|
34
|
+
* };
|
|
35
|
+
*/
|
|
36
|
+
data: string;
|
|
16
37
|
}
|
|
17
38
|
export interface SavedViewTag {
|
|
18
39
|
id: string;
|
|
@@ -24,3 +45,4 @@ export interface SavedViewGroup {
|
|
|
24
45
|
creatorId?: string | undefined;
|
|
25
46
|
shared?: boolean | undefined;
|
|
26
47
|
}
|
|
48
|
+
export type WriteableSavedViewProperties = Omit<SavedView, "id" | "creatorId" | "creationTime" | "lastModified">;
|
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
4
|
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
5
|
*--------------------------------------------------------------------------------------------*/
|
|
6
|
-
import { SvgEdit,
|
|
6
|
+
import { SvgEdit, SvgMore, SvgSavedView, SvgShare, SvgTag } from "@itwin/itwinui-icons-react";
|
|
7
7
|
import { Button, IconButton, Input, Text, Tile } from "@itwin/itwinui-react";
|
|
8
8
|
import { useLayoutEffect, useMemo, useRef, useState, } from "react";
|
|
9
9
|
import { LayeredDropdownMenu } from "../LayeredDropdownMenu/LayeredDropdownMenu.js";
|
|
@@ -67,7 +67,7 @@ export function SavedViewTile(props) {
|
|
|
67
67
|
? _jsx(Tile.ThumbnailPicture, { url: props.savedView.thumbnail })
|
|
68
68
|
: props.savedView.thumbnail
|
|
69
69
|
? props.savedView.thumbnail
|
|
70
|
-
: _jsx(Tile.ThumbnailPicture, { children: _jsx(
|
|
70
|
+
: _jsx(Tile.ThumbnailPicture, { children: _jsx(SvgSavedView, {}) }), _jsx(TileIconContainer, { style: { placeSelf: "start" }, icons: props.leftIcons }), _jsx(TileIconContainer, { style: { placeSelf: "start end" }, icons: rightIcons })] }), _jsxs(Tile.ContentArea, { children: [_jsx(Tile.Metadata, { children: metadata }), (typeof props.options === "function" || (props.options && props.options.length > 0)) &&
|
|
71
71
|
_jsx("div", { className: "svr-tile--more-options", onClick: (ev) => ev.stopPropagation(), children: _jsx(LayeredDropdownMenu, { menuItems: props.options, children: _jsx(IconButton, { size: "small", styleType: "borderless", children: _jsx(SvgMore, {}) }) }) })] })] }) }));
|
|
72
72
|
}
|
|
73
73
|
function isOverflowing(element) {
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import { type SavedViewRepresentation } from "@itwin/saved-views-client";
|
|
2
1
|
import type { SavedView, SavedViewGroup, SavedViewTag } from "../SavedView.js";
|
|
3
|
-
import type { CreateGroupParams, CreateSavedViewParams, CreateTagParams, DeleteGroupParams, DeleteSavedViewParams, DeleteTagParams,
|
|
2
|
+
import type { CreateGroupParams, CreateSavedViewParams, CreateTagParams, DeleteGroupParams, DeleteSavedViewParams, DeleteTagParams, GetAllGroupsParams, GetAllSavedViewsParams, GetAllTagsParams, GetSavedViewParams, GetThumbnailUrlParams, SavedViewsClient, UpdateGroupParams, UpdateSavedViewParams, UpdateTagParams, UploadThumbnailParams } from "./SavedViewsClient.js";
|
|
4
3
|
interface ITwinSavedViewsClientParams {
|
|
5
|
-
/** @default "https://api.bentley.com/savedviews" */
|
|
6
|
-
getAccessToken: () => Promise<string>;
|
|
7
4
|
/**
|
|
8
5
|
* Authorization token that grants access to iTwin Saved Views API. The token should be valid for `savedviews:read`
|
|
9
6
|
* and `savedviews:modify` OIDC scopes.
|
|
10
|
-
|
|
7
|
+
*/
|
|
8
|
+
getAccessToken: () => Promise<string>;
|
|
9
|
+
/** @default "https://api.bentley.com/savedviews" */
|
|
11
10
|
baseUrl?: string | undefined;
|
|
12
11
|
}
|
|
13
|
-
/** */
|
|
14
12
|
export declare class ITwinSavedViewsClient implements SavedViewsClient {
|
|
15
|
-
private
|
|
13
|
+
#private;
|
|
16
14
|
constructor(args: ITwinSavedViewsClientParams);
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
getAllSavedViews(args: GetAllSavedViewsParams): AsyncIterableIterator<SavedView[]>;
|
|
16
|
+
getAllGroups(args: GetAllGroupsParams): Promise<SavedViewGroup[]>;
|
|
17
|
+
getAllTags(args: GetAllTagsParams): Promise<SavedViewTag[]>;
|
|
19
18
|
getThumbnailUrl(args: GetThumbnailUrlParams): Promise<string | undefined>;
|
|
20
19
|
uploadThumbnail(args: UploadThumbnailParams): Promise<void>;
|
|
20
|
+
getSavedView(args: GetSavedViewParams): Promise<SavedView>;
|
|
21
21
|
createSavedView(args: CreateSavedViewParams): Promise<SavedView>;
|
|
22
22
|
updateSavedView(args: UpdateSavedViewParams): Promise<SavedView>;
|
|
23
23
|
deleteSavedView(args: DeleteSavedViewParams): Promise<void>;
|
|
@@ -3,33 +3,39 @@
|
|
|
3
3
|
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
5
|
import { ITwinSavedViewsClient as Client, } from "@itwin/saved-views-client";
|
|
6
|
-
/** */
|
|
7
6
|
export class ITwinSavedViewsClient {
|
|
8
|
-
client;
|
|
7
|
+
#client;
|
|
9
8
|
constructor(args) {
|
|
10
|
-
this
|
|
11
|
-
}
|
|
12
|
-
async
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
savedViewId: args.savedViewId,
|
|
9
|
+
this.#client = new Client(args);
|
|
10
|
+
}
|
|
11
|
+
async *getAllSavedViews(args) {
|
|
12
|
+
const iterable = this.#client.getAllSavedViewsRepresentation({
|
|
13
|
+
iTwinId: args.iTwinId,
|
|
14
|
+
iModelId: args.iModelId,
|
|
15
|
+
signal: args.signal,
|
|
16
|
+
});
|
|
17
|
+
for await (const page of iterable) {
|
|
18
|
+
yield page.savedViews.map(savedViewResponseToSavedView);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async getAllGroups(args) {
|
|
22
|
+
const response = await this.#client.getAllGroups({
|
|
23
|
+
iTwinId: args.iTwinId,
|
|
24
|
+
iModelId: args.iModelId,
|
|
27
25
|
signal: args.signal,
|
|
28
26
|
});
|
|
29
|
-
return response.
|
|
27
|
+
return response.groups.map(groupResponseToSavedViewGroup);
|
|
28
|
+
}
|
|
29
|
+
async getAllTags(args) {
|
|
30
|
+
const response = await this.#client.getAllTags({
|
|
31
|
+
iTwinId: args.iTwinId,
|
|
32
|
+
iModelId: args.iModelId,
|
|
33
|
+
signal: args.signal,
|
|
34
|
+
});
|
|
35
|
+
return response.tags.map(tagResponseToSavedViewTag);
|
|
30
36
|
}
|
|
31
37
|
async getThumbnailUrl(args) {
|
|
32
|
-
const response = await this
|
|
38
|
+
const response = await this.#client.getImage({
|
|
33
39
|
savedViewId: args.savedViewId,
|
|
34
40
|
size: "thumbnail",
|
|
35
41
|
signal: args.signal,
|
|
@@ -37,43 +43,55 @@ export class ITwinSavedViewsClient {
|
|
|
37
43
|
return response.href;
|
|
38
44
|
}
|
|
39
45
|
async uploadThumbnail(args) {
|
|
40
|
-
await this
|
|
46
|
+
await this.#client.updateImage({
|
|
41
47
|
savedViewId: args.savedViewId,
|
|
42
48
|
image: args.image,
|
|
43
49
|
signal: args.signal,
|
|
44
50
|
});
|
|
45
51
|
}
|
|
52
|
+
async getSavedView(args) {
|
|
53
|
+
const response = await this.#client.getSavedViewRepresentation({
|
|
54
|
+
savedViewId: args.savedViewId,
|
|
55
|
+
signal: args.signal,
|
|
56
|
+
});
|
|
57
|
+
return savedViewResponseToSavedView(response.savedView);
|
|
58
|
+
}
|
|
46
59
|
async createSavedView(args) {
|
|
47
|
-
const { savedView } = await this
|
|
60
|
+
const { savedView } = await this.#client.createSavedView({
|
|
48
61
|
iTwinId: args.iTwinId,
|
|
49
62
|
iModelId: args.iModelId,
|
|
50
63
|
displayName: args.savedView.displayName,
|
|
51
64
|
tagIds: args.savedView.tagIds,
|
|
52
65
|
groupId: args.savedView.groupId,
|
|
53
66
|
shared: args.savedView.shared,
|
|
54
|
-
savedViewData: args.
|
|
67
|
+
savedViewData: args.savedView.viewData,
|
|
68
|
+
extensions: args.savedView.extensions,
|
|
55
69
|
signal: args.signal,
|
|
56
70
|
});
|
|
57
71
|
return savedViewResponseToSavedView(savedView);
|
|
58
72
|
}
|
|
59
73
|
async updateSavedView(args) {
|
|
60
|
-
const { savedView } = await this
|
|
74
|
+
const { savedView } = await this.#client.updateSavedView({
|
|
61
75
|
savedViewId: args.savedView.id,
|
|
62
76
|
displayName: args.savedView.displayName,
|
|
63
77
|
tagIds: args.savedView.tagIds,
|
|
64
78
|
groupId: args.savedView.groupId,
|
|
65
79
|
shared: args.savedView.shared,
|
|
66
|
-
savedViewData: args.
|
|
67
|
-
extensions: args.extensions,
|
|
80
|
+
savedViewData: args.savedView.viewData,
|
|
68
81
|
signal: args.signal,
|
|
69
82
|
});
|
|
83
|
+
await Promise.all((args.savedView.extensions ?? []).map(({ extensionName, data }) => this.#client.createExtension({
|
|
84
|
+
savedViewId: args.savedView.id,
|
|
85
|
+
extensionName,
|
|
86
|
+
data,
|
|
87
|
+
})));
|
|
70
88
|
return savedViewResponseToSavedView(savedView);
|
|
71
89
|
}
|
|
72
90
|
async deleteSavedView(args) {
|
|
73
|
-
await this
|
|
91
|
+
await this.#client.deleteSavedView({ savedViewId: args.savedViewId, signal: args.signal });
|
|
74
92
|
}
|
|
75
93
|
async createGroup(args) {
|
|
76
|
-
const { group } = await this
|
|
94
|
+
const { group } = await this.#client.createGroup({
|
|
77
95
|
iTwinId: args.iTwinId,
|
|
78
96
|
iModelId: args.iModelId,
|
|
79
97
|
displayName: args.group.displayName,
|
|
@@ -82,7 +100,7 @@ export class ITwinSavedViewsClient {
|
|
|
82
100
|
return groupResponseToSavedViewGroup(group);
|
|
83
101
|
}
|
|
84
102
|
async updateGroup(args) {
|
|
85
|
-
const { group } = await this
|
|
103
|
+
const { group } = await this.#client.updateGroup({
|
|
86
104
|
groupId: args.group.id,
|
|
87
105
|
displayName: args.group.displayName,
|
|
88
106
|
shared: args.group.shared,
|
|
@@ -91,12 +109,14 @@ export class ITwinSavedViewsClient {
|
|
|
91
109
|
return groupResponseToSavedViewGroup(group);
|
|
92
110
|
}
|
|
93
111
|
async deleteGroup(args) {
|
|
94
|
-
const
|
|
95
|
-
await
|
|
96
|
-
|
|
112
|
+
const savedViewPages = this.#client.getAllSavedViewsMinimal({ groupId: args.groupId, signal: args.signal });
|
|
113
|
+
for await (const savedViews of savedViewPages) {
|
|
114
|
+
await Promise.all(savedViews.savedViews.map(({ id }) => this.#client.deleteSavedView({ savedViewId: id, signal: args.signal })));
|
|
115
|
+
}
|
|
116
|
+
await this.#client.deleteGroup({ groupId: args.groupId, signal: args.signal });
|
|
97
117
|
}
|
|
98
118
|
async createTag(args) {
|
|
99
|
-
const { tag } = await this
|
|
119
|
+
const { tag } = await this.#client.createTag({
|
|
100
120
|
iTwinId: args.iTwinId,
|
|
101
121
|
iModelId: args.iModelId,
|
|
102
122
|
displayName: args.displayName,
|
|
@@ -105,7 +125,7 @@ export class ITwinSavedViewsClient {
|
|
|
105
125
|
return tagResponseToSavedViewTag(tag);
|
|
106
126
|
}
|
|
107
127
|
async updateTag(args) {
|
|
108
|
-
const { tag } = await this
|
|
128
|
+
const { tag } = await this.#client.updateTag({
|
|
109
129
|
tagId: args.tag.id,
|
|
110
130
|
displayName: args.tag.displayName,
|
|
111
131
|
signal: args.signal,
|
|
@@ -113,18 +133,20 @@ export class ITwinSavedViewsClient {
|
|
|
113
133
|
return tagResponseToSavedViewTag(tag);
|
|
114
134
|
}
|
|
115
135
|
async deleteTag(args) {
|
|
116
|
-
await this
|
|
136
|
+
await this.#client.deleteTag({ tagId: args.tagId, signal: args.signal });
|
|
117
137
|
}
|
|
118
138
|
}
|
|
119
139
|
function savedViewResponseToSavedView(response) {
|
|
120
140
|
return {
|
|
121
141
|
id: response.id,
|
|
122
142
|
displayName: response.displayName,
|
|
143
|
+
viewData: response.savedViewData,
|
|
123
144
|
tagIds: response.tags?.map((tag) => tag.id),
|
|
124
145
|
groupId: response._links.group?.href.split("/").at(-1),
|
|
125
146
|
creatorId: response._links.creator?.href.split("/").at(-1),
|
|
126
147
|
shared: response.shared,
|
|
127
148
|
thumbnail: undefined,
|
|
149
|
+
extensions: response.extensions,
|
|
128
150
|
creationTime: response.creationTime,
|
|
129
151
|
lastModified: response.lastModified,
|
|
130
152
|
};
|
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
export interface SavedViewInfo {
|
|
4
|
-
savedViews: SavedView[];
|
|
5
|
-
groups: SavedViewGroup[];
|
|
6
|
-
tags: SavedViewTag[];
|
|
7
|
-
}
|
|
1
|
+
import type { SavedView, SavedViewGroup, SavedViewTag, WriteableSavedViewProperties } from "../SavedView.js";
|
|
2
|
+
import type { PartialExcept } from "../utils.js";
|
|
8
3
|
export interface SavedViewsClient {
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
getAllSavedViews: (args: GetAllSavedViewsParams) => AsyncIterableIterator<SavedView[]>;
|
|
5
|
+
getAllGroups: (args: GetAllGroupsParams) => Promise<SavedViewGroup[]>;
|
|
6
|
+
getAllTags: (args: GetAllTagsParams) => Promise<SavedViewTag[]>;
|
|
11
7
|
getThumbnailUrl: (args: GetThumbnailUrlParams) => Promise<string | undefined>;
|
|
12
8
|
uploadThumbnail: (args: UploadThumbnailParams) => Promise<void>;
|
|
9
|
+
getSavedView: (args: GetSavedViewParams) => Promise<SavedView>;
|
|
13
10
|
createSavedView: (args: CreateSavedViewParams) => Promise<SavedView>;
|
|
14
11
|
updateSavedView: (args: UpdateSavedViewParams) => Promise<SavedView>;
|
|
15
12
|
deleteSavedView: (args: DeleteSavedViewParams) => Promise<void>;
|
|
@@ -20,32 +17,43 @@ export interface SavedViewsClient {
|
|
|
20
17
|
updateTag: (args: UpdateTagParams) => Promise<SavedViewTag>;
|
|
21
18
|
deleteTag: (args: DeleteTagParams) => Promise<void>;
|
|
22
19
|
}
|
|
23
|
-
export interface
|
|
20
|
+
export interface GetAllSavedViewsParams extends CommonParams {
|
|
24
21
|
iTwinId: string;
|
|
25
22
|
iModelId?: string | undefined;
|
|
26
23
|
}
|
|
27
|
-
export interface
|
|
28
|
-
|
|
24
|
+
export interface GetAllGroupsParams extends CommonParams {
|
|
25
|
+
iTwinId: string;
|
|
26
|
+
iModelId?: string | undefined;
|
|
27
|
+
}
|
|
28
|
+
export interface GetAllTagsParams extends CommonParams {
|
|
29
|
+
iTwinId: string;
|
|
30
|
+
iModelId?: string | undefined;
|
|
29
31
|
}
|
|
30
32
|
export interface GetThumbnailUrlParams extends CommonParams {
|
|
31
33
|
savedViewId: string;
|
|
32
34
|
}
|
|
33
35
|
export interface UploadThumbnailParams extends CommonParams {
|
|
34
36
|
savedViewId: string;
|
|
35
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* Image data encoded as base64 data URL.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVQYV2NgYAAAAAMAAWgmWQ0AAAAASUVORK5CYII="
|
|
42
|
+
*/
|
|
36
43
|
image: string;
|
|
37
44
|
}
|
|
45
|
+
export interface GetSavedViewParams extends CommonParams {
|
|
46
|
+
savedViewId: string;
|
|
47
|
+
}
|
|
38
48
|
export interface CreateSavedViewParams extends CommonParams {
|
|
39
49
|
iTwinId: string;
|
|
40
50
|
iModelId?: string | undefined;
|
|
41
|
-
savedView:
|
|
42
|
-
savedViewData: ViewData;
|
|
43
|
-
extensions?: ExtensionSavedViewCreate[] | undefined;
|
|
51
|
+
savedView: PartialExcept<WriteableSavedViewProperties, "displayName" | "viewData">;
|
|
44
52
|
}
|
|
45
53
|
export interface UpdateSavedViewParams extends CommonParams {
|
|
46
|
-
savedView:
|
|
47
|
-
|
|
48
|
-
|
|
54
|
+
savedView: Partial<WriteableSavedViewProperties> & {
|
|
55
|
+
id: string;
|
|
56
|
+
};
|
|
49
57
|
}
|
|
50
58
|
export interface DeleteSavedViewParams extends CommonParams {
|
|
51
59
|
savedViewId: string;
|
package/lib/applySavedView.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ViewState, type IModelConnection, type Viewport } from "@itwin/core-frontend";
|
|
2
|
-
import {
|
|
2
|
+
import type { SavedView } from "./SavedView.js";
|
|
3
3
|
export interface ApplySavedViewSettings {
|
|
4
4
|
/**
|
|
5
5
|
* Strategy to use when other setting does not specify one. By default, viewport is reset to default state and then
|
|
@@ -11,8 +11,8 @@ export interface ApplySavedViewSettings {
|
|
|
11
11
|
*/
|
|
12
12
|
all?: ApplyStrategy | undefined;
|
|
13
13
|
/**
|
|
14
|
-
* How to handle captured {@link ViewState} data. The default behavior is to generate a new `ViewState` object
|
|
15
|
-
* apply it to viewport.
|
|
14
|
+
* How to handle captured {@link ViewState} data. The default behavior is to generate a new `ViewState` object out of
|
|
15
|
+
* {@linkcode SavedView.viewData} and apply it to viewport.
|
|
16
16
|
*
|
|
17
17
|
* You can optionally provide a pre-made `ViewState` instance to conserve resources. It is usually obtained from
|
|
18
18
|
* {@link createViewState} result.
|
|
@@ -25,8 +25,11 @@ export interface ApplySavedViewSettings {
|
|
|
25
25
|
* applySavedView(iModel, viewport1, savedView, { viewState }),
|
|
26
26
|
* applySavedView(iModel, viewport2, savedView, { viewState }),
|
|
27
27
|
* ]);
|
|
28
|
+
*
|
|
29
|
+
* @remarks
|
|
30
|
+
* When neither `SavedView.viewData` nor `ViewState` is provided, current {@linkcode Viewport.view} is preserved.
|
|
28
31
|
*/
|
|
29
|
-
viewState?:
|
|
32
|
+
viewState?: ApplyStrategy | ViewState | undefined;
|
|
30
33
|
/**
|
|
31
34
|
* How to handle visibility of models and categories that exist in iModel but are not captured in Saved View data. Has
|
|
32
35
|
* effect only when `modelAndCategoryVisibility` strategy is set to `"apply"`.
|
|
@@ -56,9 +59,9 @@ type ApplyStrategy = "apply" | "reset" | "keep";
|
|
|
56
59
|
* // e.g. when applying the same Saved View to multiple viewports
|
|
57
60
|
* const viewState = await createViewState(iModel, savedView);
|
|
58
61
|
* await Promise.all([
|
|
59
|
-
* applySavedView(iModel,
|
|
60
|
-
* applySavedView(iModel,
|
|
62
|
+
* applySavedView(iModel, viewport1, savedView, { viewState }),
|
|
63
|
+
* applySavedView(iModel, viewport2, savedView, { viewState }),
|
|
61
64
|
* ]);
|
|
62
65
|
*/
|
|
63
|
-
export declare function applySavedView(iModel: IModelConnection, viewport: Viewport,
|
|
66
|
+
export declare function applySavedView(iModel: IModelConnection, viewport: Viewport, savedViewData: Pick<SavedView, "viewData" | "extensions">, settings?: ApplySavedViewSettings | undefined): Promise<void>;
|
|
64
67
|
export {};
|
package/lib/applySavedView.js
CHANGED
|
@@ -13,23 +13,23 @@ import { extensionHandlers } from "./translation/SavedViewsExtensionHandlers.js"
|
|
|
13
13
|
* // e.g. when applying the same Saved View to multiple viewports
|
|
14
14
|
* const viewState = await createViewState(iModel, savedView);
|
|
15
15
|
* await Promise.all([
|
|
16
|
-
* applySavedView(iModel,
|
|
17
|
-
* applySavedView(iModel,
|
|
16
|
+
* applySavedView(iModel, viewport1, savedView, { viewState }),
|
|
17
|
+
* applySavedView(iModel, viewport2, savedView, { viewState }),
|
|
18
18
|
* ]);
|
|
19
19
|
*/
|
|
20
|
-
export async function applySavedView(iModel, viewport,
|
|
20
|
+
export async function applySavedView(iModel, viewport, savedViewData, settings = {}) {
|
|
21
21
|
const defaultStrategy = settings.all ?? "apply";
|
|
22
22
|
if ((settings.viewState ?? defaultStrategy) !== "keep") {
|
|
23
23
|
if (settings.viewState instanceof ViewState) {
|
|
24
24
|
viewport.changeView(settings.viewState);
|
|
25
25
|
}
|
|
26
|
-
else {
|
|
26
|
+
else if (savedViewData.viewData) {
|
|
27
27
|
const { modelAndCategoryVisibilityFallback } = settings;
|
|
28
|
-
const viewState = await createViewState(iModel,
|
|
28
|
+
const viewState = await createViewState(iModel, savedViewData.viewData, { modelAndCategoryVisibilityFallback });
|
|
29
29
|
viewport.changeView(viewState);
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
-
const extensions = new Map(
|
|
32
|
+
const extensions = new Map(savedViewData.extensions?.map(({ extensionName, data }) => [extensionName, data]));
|
|
33
33
|
const processExtension = (extensionHandler, strategy = defaultStrategy) => {
|
|
34
34
|
if (strategy === "keep") {
|
|
35
35
|
return;
|
|
@@ -1,11 +1,41 @@
|
|
|
1
1
|
import { Id64Array } from "@itwin/core-bentley";
|
|
2
2
|
import { type IModelConnection, type Viewport } from "@itwin/core-frontend";
|
|
3
3
|
import { type ViewData } from "@itwin/saved-views-client";
|
|
4
|
+
import type { SavedViewExtension } from "./SavedView.js";
|
|
4
5
|
interface CaptureSavedViewDataArgs {
|
|
5
6
|
/** Viewport to capture. */
|
|
6
7
|
viewport: Viewport;
|
|
8
|
+
/**
|
|
9
|
+
* Whether to skip capturing data for `"EmphasizeElements"` extension.
|
|
10
|
+
* @default false
|
|
11
|
+
*/
|
|
12
|
+
omitEmphasis?: boolean | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Whether to skip capturing data for `"PerModelCategoryVisibility"` extension.
|
|
15
|
+
* @default false
|
|
16
|
+
*/
|
|
17
|
+
omitPerModelCategoryVisibility?: boolean | undefined;
|
|
7
18
|
}
|
|
8
|
-
|
|
19
|
+
interface CaptureSavedViewDataResult {
|
|
20
|
+
viewData: ViewData;
|
|
21
|
+
/** Value is `undefined` when no extension data is captured. */
|
|
22
|
+
extensions: SavedViewExtension[] | undefined;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Captures current {@link Viewport} state into serializable format. The returned data can later be used to restore
|
|
26
|
+
* viewport's view.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* import { captureSavedViewData, captureSavedViewThumbnail } from "@itwin/saved-views-react";
|
|
30
|
+
*
|
|
31
|
+
* async function saveViewport(viewport) {
|
|
32
|
+
* const { viewData, extensions = [] } = await captureSavedViewData({ viewport });
|
|
33
|
+
* const myExtensions = captureMyCustomState(viewport);
|
|
34
|
+
* const thumbnail = captureSavedViewThumbnail(viewport);
|
|
35
|
+
* return { thumbnail, viewData, extensions: extensions.concat(myExtensions) };
|
|
36
|
+
* }
|
|
37
|
+
*/
|
|
38
|
+
export declare function captureSavedViewData(args: CaptureSavedViewDataArgs): Promise<CaptureSavedViewDataResult>;
|
|
9
39
|
export declare function getMissingModels(iModel: IModelConnection, knownModels: Set<string>): Promise<string[]>;
|
|
10
40
|
export declare function getMissingCategories(iModel: IModelConnection, knownCategories: Set<string>): Promise<Id64Array>;
|
|
11
41
|
export {};
|
|
@@ -1,19 +1,45 @@
|
|
|
1
1
|
import { QueryRowFormat } from "@itwin/core-common";
|
|
2
|
+
import { extensionHandlers } from "./translation/SavedViewsExtensionHandlers.js";
|
|
2
3
|
import { extractClipVectorsFromLegacy } from "./translation/clipVectorsLegacyExtractor.js";
|
|
3
4
|
import { extractDisplayStyle2dFromLegacy, extractDisplayStyle3dFromLegacy, } from "./translation/displayStyleExtractor.js";
|
|
5
|
+
/**
|
|
6
|
+
* Captures current {@link Viewport} state into serializable format. The returned data can later be used to restore
|
|
7
|
+
* viewport's view.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* import { captureSavedViewData, captureSavedViewThumbnail } from "@itwin/saved-views-react";
|
|
11
|
+
*
|
|
12
|
+
* async function saveViewport(viewport) {
|
|
13
|
+
* const { viewData, extensions = [] } = await captureSavedViewData({ viewport });
|
|
14
|
+
* const myExtensions = captureMyCustomState(viewport);
|
|
15
|
+
* const thumbnail = captureSavedViewThumbnail(viewport);
|
|
16
|
+
* return { thumbnail, viewData, extensions: extensions.concat(myExtensions) };
|
|
17
|
+
* }
|
|
18
|
+
*/
|
|
4
19
|
export async function captureSavedViewData(args) {
|
|
5
|
-
|
|
6
|
-
|
|
20
|
+
return {
|
|
21
|
+
viewData: await createSavedViewVariant(args.viewport),
|
|
22
|
+
extensions: [extensionHandlers.emphasizeElements, extensionHandlers.perModelCategoryVisibility]
|
|
23
|
+
.map((extension) => ({
|
|
24
|
+
extensionName: extension.extensionName,
|
|
25
|
+
data: extension.capture(args.viewport),
|
|
26
|
+
}))
|
|
27
|
+
.filter(({ data }) => data !== undefined),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
async function createSavedViewVariant(viewport) {
|
|
31
|
+
const hiddenCategoriesPromise = getMissingCategories(viewport.iModel, new Set(viewport.view.categorySelector.toJSON().categories));
|
|
32
|
+
if (viewport.view.isSpatialView()) {
|
|
7
33
|
const [hiddenCategories, hiddenModels] = await Promise.all([
|
|
8
34
|
hiddenCategoriesPromise,
|
|
9
|
-
getMissingModels(
|
|
35
|
+
getMissingModels(viewport.iModel, new Set(viewport.view.modelSelector.toJSON().models)),
|
|
10
36
|
]);
|
|
11
|
-
return createSpatialSavedViewObject(
|
|
37
|
+
return createSpatialSavedViewObject(viewport, hiddenCategories, hiddenModels);
|
|
12
38
|
}
|
|
13
|
-
if (
|
|
14
|
-
return createDrawingSavedViewObject(
|
|
39
|
+
if (viewport.view.isDrawingView()) {
|
|
40
|
+
return createDrawingSavedViewObject(viewport, await hiddenCategoriesPromise);
|
|
15
41
|
}
|
|
16
|
-
return createSheetSavedViewObject(
|
|
42
|
+
return createSheetSavedViewObject(viewport, await hiddenCategoriesPromise);
|
|
17
43
|
}
|
|
18
44
|
function createSpatialSavedViewObject(vp, hiddenCategories, hiddenModels) {
|
|
19
45
|
const viewState = vp.view;
|
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
import { type Viewport } from "@itwin/core-frontend";
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Generates Saved View thumbnail based on what is currently displayed on the {@linkcode viewport}.
|
|
4
|
+
* @returns base64-encoded URL string
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* const thumbnail = captureSavedViewThumbnail(viewport);
|
|
8
|
+
* console.log(thumbnail); // "data:image/png;base64,iVBORw0KGoAAAANSUhEUg..."
|
|
9
|
+
*/
|
|
3
10
|
export declare function captureSavedViewThumbnail(viewport: Viewport, width?: number, height?: number): string | undefined;
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { getCenteredViewRect, imageBufferToCanvas } from "@itwin/core-frontend";
|
|
2
2
|
import { Point2d } from "@itwin/core-geometry";
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Generates Saved View thumbnail based on what is currently displayed on the {@linkcode viewport}.
|
|
5
|
+
* @returns base64-encoded URL string
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* const thumbnail = captureSavedViewThumbnail(viewport);
|
|
9
|
+
* console.log(thumbnail); // "data:image/png;base64,iVBORw0KGoAAAANSUhEUg..."
|
|
10
|
+
*/
|
|
4
11
|
export function captureSavedViewThumbnail(viewport, width = 280, height = 200) {
|
|
5
12
|
const thumbnail = getThumbnail(viewport, width, height);
|
|
6
13
|
if (!thumbnail) {
|
package/lib/createViewState.d.ts
CHANGED
|
@@ -2,9 +2,14 @@ import { type IModelConnection, type ViewState } from "@itwin/core-frontend";
|
|
|
2
2
|
import { ViewData } from "@itwin/saved-views-client";
|
|
3
3
|
export interface ViewStateCreateSettings {
|
|
4
4
|
/**
|
|
5
|
-
* Normally
|
|
6
|
-
*
|
|
5
|
+
* Normally {@link createViewState} function invokes and awaits {@linkcode ViewState.load} method before returning.
|
|
6
|
+
* You may skip this step if you intend to perform it later.
|
|
7
7
|
* @default false
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const viewState = await createViewState(iModel, savedView.viewdata, { skipViewStateLoad: true });
|
|
11
|
+
* viewState.categorySelector.addCategories("<additional_category_id>");
|
|
12
|
+
* await viewState.load();
|
|
8
13
|
*/
|
|
9
14
|
skipViewStateLoad?: boolean | undefined;
|
|
10
15
|
/**
|
|
@@ -13,4 +18,15 @@ export interface ViewStateCreateSettings {
|
|
|
13
18
|
*/
|
|
14
19
|
modelAndCategoryVisibilityFallback?: "visible" | "hidden" | undefined;
|
|
15
20
|
}
|
|
16
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Creates {@link ViewState} object out of Saved View data. It provides a lower-level access to view data for advanced
|
|
23
|
+
* use.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* const viewState = await createViewState(iModel, savedViewData.viewData);
|
|
27
|
+
* await applySavedView(iModel, viewport, savedViewData, { viewState });
|
|
28
|
+
*
|
|
29
|
+
* // The two lines above are equivalent to
|
|
30
|
+
* await applySavedView(iModel, viewport, savedViewData);
|
|
31
|
+
*/
|
|
32
|
+
export declare function createViewState(iModel: IModelConnection, viewData: ViewData, settings?: ViewStateCreateSettings): Promise<ViewState>;
|
package/lib/createViewState.js
CHANGED
|
@@ -8,10 +8,21 @@ import { isViewDataITwin3d, isViewDataITwinDrawing, isViewDataITwinSheet, } from
|
|
|
8
8
|
import { getMissingCategories, getMissingModels } from "./captureSavedViewData.js";
|
|
9
9
|
import { extractClipVectors } from "./translation/clipVectorsExtractor.js";
|
|
10
10
|
import { extractDisplayStyle, extractDisplayStyle3d } from "./translation/displayStyleExtractor.js";
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Creates {@link ViewState} object out of Saved View data. It provides a lower-level access to view data for advanced
|
|
13
|
+
* use.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* const viewState = await createViewState(iModel, savedViewData.viewData);
|
|
17
|
+
* await applySavedView(iModel, viewport, savedViewData, { viewState });
|
|
18
|
+
*
|
|
19
|
+
* // The two lines above are equivalent to
|
|
20
|
+
* await applySavedView(iModel, viewport, savedViewData);
|
|
21
|
+
*/
|
|
22
|
+
export async function createViewState(iModel, viewData, settings = {}) {
|
|
23
|
+
const viewState = await createViewStateVariant(iModel, viewData);
|
|
13
24
|
if (settings.modelAndCategoryVisibilityFallback === "visible") {
|
|
14
|
-
await unhideNewModelsAndCategories(iModel, viewState,
|
|
25
|
+
await unhideNewModelsAndCategories(iModel, viewState, viewData);
|
|
15
26
|
}
|
|
16
27
|
if (!settings.skipViewStateLoad) {
|
|
17
28
|
await viewState.load();
|
package/lib/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { SavedViewOptions } from "./SavedViewTile/SavedViewOptions.js";
|
|
|
4
4
|
export { SavedViewTile } from "./SavedViewTile/SavedViewTile.js";
|
|
5
5
|
export { useSavedViewTileContext, type SavedViewTileContext } from "./SavedViewTile/SavedViewTileContext.js";
|
|
6
6
|
export { ITwinSavedViewsClient } from "./SavedViewsClient/ITwinSavedViewsClient.js";
|
|
7
|
-
export type { CreateGroupParams, CreateSavedViewParams, CreateTagParams, DeleteGroupParams, DeleteSavedViewParams, DeleteTagParams,
|
|
7
|
+
export type { CreateGroupParams, CreateSavedViewParams, CreateTagParams, DeleteGroupParams, DeleteSavedViewParams, DeleteTagParams, GetAllGroupsParams, GetAllSavedViewsParams, GetAllTagsParams, GetSavedViewParams, GetThumbnailUrlParams, SavedViewsClient, UpdateGroupParams, UpdateSavedViewParams, UpdateTagParams, UploadThumbnailParams, } from "./SavedViewsClient/SavedViewsClient.js";
|
|
8
8
|
export { SavedViewsContextProvider, type SavedViewsContext } from "./SavedViewsContext.js";
|
|
9
9
|
export { StickyExpandableBlock } from "./StickyExpandableBlock/StickyExpandableBlock.js";
|
|
10
10
|
export { TileGrid } from "./TileGrid/TileGrid.js";
|
|
@@ -3,16 +3,19 @@ export interface ExtensionHandler {
|
|
|
3
3
|
extensionName: string;
|
|
4
4
|
apply: (extensionData: string, viewport: Viewport) => void;
|
|
5
5
|
reset: (viewport: Viewport) => void;
|
|
6
|
+
capture: (viewport: Viewport) => string | undefined;
|
|
6
7
|
}
|
|
7
8
|
export declare const extensionHandlers: {
|
|
8
9
|
emphasizeElements: {
|
|
9
10
|
extensionName: string;
|
|
10
11
|
apply: (extensionData: string, viewport: Viewport) => void;
|
|
11
12
|
reset: (viewport: Viewport) => void;
|
|
13
|
+
capture: (viewport: Viewport) => string | undefined;
|
|
12
14
|
};
|
|
13
15
|
perModelCategoryVisibility: {
|
|
14
16
|
extensionName: string;
|
|
15
17
|
apply: (extensionData: string, viewport: Viewport) => void;
|
|
16
18
|
reset: (viewport: Viewport) => void;
|
|
19
|
+
capture: (viewport: Viewport) => string | undefined;
|
|
17
20
|
};
|
|
18
21
|
};
|
|
@@ -21,6 +21,10 @@ export const extensionHandlers = {
|
|
|
21
21
|
viewport.isFadeOutActive = false;
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
|
+
capture: (viewport) => {
|
|
25
|
+
const emphasizeElementsProps = EmphasizeElements.get(viewport)?.toJSON(viewport);
|
|
26
|
+
return emphasizeElementsProps ? JSON.stringify({ emphasizeElementsProps }) : undefined;
|
|
27
|
+
},
|
|
24
28
|
},
|
|
25
29
|
perModelCategoryVisibility: {
|
|
26
30
|
extensionName: "PerModelCategoryVisibility",
|
|
@@ -35,5 +39,15 @@ export const extensionHandlers = {
|
|
|
35
39
|
reset: (viewport) => {
|
|
36
40
|
viewport.perModelCategoryVisibility.clearOverrides();
|
|
37
41
|
},
|
|
42
|
+
capture: (viewport) => {
|
|
43
|
+
if (!viewport.view.isSpatialView()) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
const props = [];
|
|
47
|
+
for (const { modelId, categoryId, visible } of viewport.perModelCategoryVisibility) {
|
|
48
|
+
props.push({ modelId, categoryId, visible });
|
|
49
|
+
}
|
|
50
|
+
return props.length > 0 ? JSON.stringify({ perModelCategoryVisibilityProps: props }) : undefined;
|
|
51
|
+
},
|
|
38
52
|
},
|
|
39
53
|
};
|
|
@@ -14,9 +14,18 @@ const viewFlagMappings = [
|
|
|
14
14
|
extractBoolean("hiddenEdges", "hidEdges"),
|
|
15
15
|
extractBoolean("shadows"),
|
|
16
16
|
extractBoolean("clipVolume", "clipVol"),
|
|
17
|
+
extractBoolean("hiddenLineMaterialColors", "hlMatColors"),
|
|
17
18
|
extractBoolean("monochrome"),
|
|
18
19
|
extractBoolean("backgroundMap"),
|
|
19
20
|
extractBoolean("ambientOcclusion"),
|
|
21
|
+
extractBoolean("acs"),
|
|
22
|
+
extractBoolean("thematicDisplay"),
|
|
23
|
+
extractBoolean("wiremesh"),
|
|
24
|
+
extractBoolean("forceSurfaceDiscard"),
|
|
25
|
+
extractBoolean("noWhiteOnWhiteReversal"),
|
|
26
|
+
extractBoolean("noSolarLight"),
|
|
27
|
+
extractBoolean("noSourceLights"),
|
|
28
|
+
extractBoolean("noCameraLights"),
|
|
20
29
|
];
|
|
21
30
|
const viewFlagLegacyMappings = [
|
|
22
31
|
extractNumber("renderMode"),
|
|
@@ -33,9 +42,18 @@ const viewFlagLegacyMappings = [
|
|
|
33
42
|
extractBoolean("hidEdges", "hiddenEdges"),
|
|
34
43
|
extractBoolean("shadows"),
|
|
35
44
|
extractBoolean("clipVol", "clipVolume"),
|
|
45
|
+
extractBoolean("hlMatColors", "hiddenLineMaterialColors"),
|
|
36
46
|
extractBoolean("monochrome"),
|
|
37
47
|
extractBoolean("backgroundMap"),
|
|
38
48
|
extractBoolean("ambientOcclusion"),
|
|
49
|
+
extractBoolean("acs"),
|
|
50
|
+
extractBoolean("thematicDisplay"),
|
|
51
|
+
extractBoolean("wiremesh"),
|
|
52
|
+
extractBoolean("forceSurfaceDiscard"),
|
|
53
|
+
extractBoolean("noWhiteOnWhiteReversal"),
|
|
54
|
+
extractBoolean("noSolarLight"),
|
|
55
|
+
extractBoolean("noSourceLights"),
|
|
56
|
+
extractBoolean("noCameraLights"),
|
|
39
57
|
];
|
|
40
58
|
const planarClipMaskMappings = [
|
|
41
59
|
extractNumber("mode"),
|
|
@@ -284,6 +302,56 @@ const hiddenLineSettingsLegacyMappings = [
|
|
|
284
302
|
extractObject(hiddenLineStyleLegacyMappings, "hidden"),
|
|
285
303
|
extractNumber("transThreshold", "transparencyThreshold"),
|
|
286
304
|
];
|
|
305
|
+
const keyColorPropsMappings = [
|
|
306
|
+
extractNumber("value"),
|
|
307
|
+
extractColor("color"),
|
|
308
|
+
];
|
|
309
|
+
const keyColorPropsLegacyMappings = [
|
|
310
|
+
extractNumber("value"),
|
|
311
|
+
extractColorLegacy("color"),
|
|
312
|
+
];
|
|
313
|
+
const thematicGradientSettingsPropsMappings = [
|
|
314
|
+
extractNumber("mode"),
|
|
315
|
+
extractNumber("stepCount"),
|
|
316
|
+
extractColor("marginColor"),
|
|
317
|
+
extractNumber("colorScheme"),
|
|
318
|
+
extractArray(keyColorPropsMappings, "customKeys"),
|
|
319
|
+
extractNumber("colorMix"),
|
|
320
|
+
extractNumber("transparencyMode"),
|
|
321
|
+
];
|
|
322
|
+
const thematicGradientSettingsPropsLegacyMappings = [
|
|
323
|
+
extractNumber("mode"),
|
|
324
|
+
extractNumber("stepCount"),
|
|
325
|
+
extractColorLegacy("marginColor"),
|
|
326
|
+
extractNumber("colorScheme"),
|
|
327
|
+
extractArray(keyColorPropsLegacyMappings, "customKeys"),
|
|
328
|
+
extractNumber("colorMix"),
|
|
329
|
+
extractNumber("transparencyMode"),
|
|
330
|
+
];
|
|
331
|
+
const thematicDisplaySensorPropsMappings = [
|
|
332
|
+
extractSimpleArray(simpleTypeOf("number"), "position"),
|
|
333
|
+
extractNumber("value"),
|
|
334
|
+
];
|
|
335
|
+
const thematicDisplaySensorSettingsPropsMappings = [
|
|
336
|
+
extractArray(thematicDisplaySensorPropsMappings, "sensors"),
|
|
337
|
+
extractNumber("distanceCutoff"),
|
|
338
|
+
];
|
|
339
|
+
const thematicDisplaySettingsMappings = [
|
|
340
|
+
extractNumber("displayMode"),
|
|
341
|
+
extractObject(thematicGradientSettingsPropsMappings, "gradientSettings"),
|
|
342
|
+
extractSimpleArray(simpleTypeOf("number"), "range"),
|
|
343
|
+
extractSimpleArray(simpleTypeOf("number"), "axis"),
|
|
344
|
+
extractSimpleArray(simpleTypeOf("number"), "sunDirection"),
|
|
345
|
+
extractObject(thematicDisplaySensorSettingsPropsMappings, "gradientSettings"),
|
|
346
|
+
];
|
|
347
|
+
const thematicDisplaySettingsLegacyMappings = [
|
|
348
|
+
extractNumber("displayMode"),
|
|
349
|
+
extractObject(thematicGradientSettingsPropsLegacyMappings, "gradientSettings"),
|
|
350
|
+
extractSimpleArray(simpleTypeOf("number"), "range"),
|
|
351
|
+
extractSimpleArray(simpleTypeOf("number"), "axis"),
|
|
352
|
+
extractSimpleArray(simpleTypeOf("number"), "sunDirection"),
|
|
353
|
+
extractObject(thematicDisplaySensorSettingsPropsMappings, "gradientSettings"),
|
|
354
|
+
];
|
|
287
355
|
const cutStyleMappings = [
|
|
288
356
|
extractObject(viewFlagOverridesMapping, "viewflags"),
|
|
289
357
|
extractObject(hiddenLineSettingsMappings, "hiddenLine"),
|
|
@@ -475,6 +543,8 @@ const displayStyle3dMapping = [
|
|
|
475
543
|
extractObject(solarShadowMappings, "solarShadows"),
|
|
476
544
|
extractObject(lightsMappings, "lights"),
|
|
477
545
|
extractPlainTypedMap(planProjectionSettingsMappings, simpleTypeOf("string"), "planProjections"),
|
|
546
|
+
extractObject(thematicDisplaySettingsMappings, "thematic"),
|
|
547
|
+
extractObject(hiddenLineSettingsMappings, "hiddenLine", "hline"),
|
|
478
548
|
];
|
|
479
549
|
const displayStyle3dLegacyMapping = [
|
|
480
550
|
...displayStylesLegacyMapping,
|
|
@@ -483,6 +553,8 @@ const displayStyle3dLegacyMapping = [
|
|
|
483
553
|
extractObject(solarShadowLegacyMappings, "solarShadows"),
|
|
484
554
|
extractObject(lightsLegacyMappings, "lights"),
|
|
485
555
|
extractPlainTypedMap(planProjectionSettingsMappings, simpleTypeOf("string"), "planProjections"),
|
|
556
|
+
extractObject(thematicDisplaySettingsLegacyMappings, "thematic"),
|
|
557
|
+
extractObject(hiddenLineSettingsLegacyMappings, "hline", "hiddenLine"),
|
|
486
558
|
];
|
|
487
559
|
/**
|
|
488
560
|
* Extracts the display style from a legacy view displayStyle field
|
package/lib/useSavedViews.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { SavedView, SavedViewGroup, SavedViewTag } from "./SavedView.js";
|
|
1
|
+
import type { SavedView, SavedViewGroup, SavedViewTag, WriteableSavedViewProperties } from "./SavedView.js";
|
|
3
2
|
import type { SavedViewsClient } from "./SavedViewsClient/SavedViewsClient.js";
|
|
3
|
+
import type { PartialExcept } from "./utils.js";
|
|
4
4
|
interface UseSavedViewsParams {
|
|
5
5
|
/** iTwin identifier. */
|
|
6
6
|
iTwinId: string;
|
|
@@ -25,7 +25,7 @@ interface UseSavedViewsResult {
|
|
|
25
25
|
actions: SavedViewActions;
|
|
26
26
|
}
|
|
27
27
|
export interface SavedViewActions {
|
|
28
|
-
submitSavedView: (savedView:
|
|
28
|
+
submitSavedView: (savedView: SavedViewCreationProps | SavedViewUpdateProps) => Promise<string>;
|
|
29
29
|
renameSavedView: (savedViewId: string, newName: string | undefined) => void;
|
|
30
30
|
shareSavedView: (savedViewId: string, share: boolean) => void;
|
|
31
31
|
deleteSavedView: (savedViewId: string) => void;
|
|
@@ -40,11 +40,16 @@ export interface SavedViewActions {
|
|
|
40
40
|
removeTag: (savedViewId: string, tagId: string) => void;
|
|
41
41
|
uploadThumbnail: (savedViewId: string, imageDataUrl: string) => void;
|
|
42
42
|
}
|
|
43
|
+
type SavedViewCreationProps = PartialExcept<WriteableSavedViewProperties, "displayName" | "viewData">;
|
|
44
|
+
type SavedViewUpdateProps = WriteableSavedViewProperties & {
|
|
45
|
+
id: string;
|
|
46
|
+
};
|
|
43
47
|
/**
|
|
44
48
|
* Pulls Saved View data from a store and provides means to update and synchronize the data back to it. Interaction with
|
|
45
49
|
* the store is performed via {@linkcode SavedViewsClient} interface which could communicate, for instance, with
|
|
46
50
|
* [iTwin Saved Views API](https://developer.bentley.com/apis/savedviews/overview/) using `ITwinSavedViewsClient`.
|
|
47
51
|
*
|
|
52
|
+
* @remarks
|
|
48
53
|
* Note on the current implementation limitations. While the result of the first update action is reflected immediately,
|
|
49
54
|
* subsequent actions are put in a queue and executed serially. This may cause the UI to feel sluggish when user makes
|
|
50
55
|
* changes to Saved Views faster than the Saved Views store can be updated.
|
package/lib/useSavedViews.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/*---------------------------------------------------------------------------------------------
|
|
3
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
4
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
5
|
+
*--------------------------------------------------------------------------------------------*/
|
|
2
6
|
import { useCallback, useEffect, useRef, useState, } from "react";
|
|
3
7
|
/**
|
|
4
8
|
* Pulls Saved View data from a store and provides means to update and synchronize the data back to it. Interaction with
|
|
5
9
|
* the store is performed via {@linkcode SavedViewsClient} interface which could communicate, for instance, with
|
|
6
10
|
* [iTwin Saved Views API](https://developer.bentley.com/apis/savedviews/overview/) using `ITwinSavedViewsClient`.
|
|
7
11
|
*
|
|
12
|
+
* @remarks
|
|
8
13
|
* Note on the current implementation limitations. While the result of the first update action is reflected immediately,
|
|
9
14
|
* subsequent actions are put in a queue and executed serially. This may cause the UI to feel sluggish when user makes
|
|
10
15
|
* changes to Saved Views faster than the Saved Views store can be updated.
|
|
@@ -66,11 +71,7 @@ export function useSavedViews(args) {
|
|
|
66
71
|
providerRef.current.actionQueue = [];
|
|
67
72
|
void (async () => {
|
|
68
73
|
try {
|
|
69
|
-
const result = await args.client.
|
|
70
|
-
iTwinId: args.iTwinId,
|
|
71
|
-
iModelId: args.iModelId,
|
|
72
|
-
signal: signal,
|
|
73
|
-
});
|
|
74
|
+
const result = await getSavedViewInfo(args.client, args.iTwinId, args.iModelId, signal);
|
|
74
75
|
if (signal.aborted) {
|
|
75
76
|
return;
|
|
76
77
|
}
|
|
@@ -118,28 +119,30 @@ function useEvent(handleEvent) {
|
|
|
118
119
|
handleEventRef.current = handleEvent;
|
|
119
120
|
return useCallback((...args) => handleEventRef.current(...args), []);
|
|
120
121
|
}
|
|
122
|
+
async function getSavedViewInfo(client, iTwinId, iModelId, signal) {
|
|
123
|
+
const args = { iTwinId, iModelId, signal };
|
|
124
|
+
const collectSavedViews = async () => {
|
|
125
|
+
let savedViews = [];
|
|
126
|
+
const iterable = client.getAllSavedViews(args);
|
|
127
|
+
for await (const page of iterable) {
|
|
128
|
+
savedViews = savedViews.concat(page);
|
|
129
|
+
}
|
|
130
|
+
return savedViews;
|
|
131
|
+
};
|
|
132
|
+
const [savedViews, groups, tags] = await Promise.all([
|
|
133
|
+
collectSavedViews(),
|
|
134
|
+
client.getAllGroups(args),
|
|
135
|
+
client.getAllTags(args),
|
|
136
|
+
]);
|
|
137
|
+
return { savedViews, groups, tags };
|
|
138
|
+
}
|
|
121
139
|
function createSavedViewActions(iTwinId, iModelId, client, setState, ref, onUpdateInProgress, onUpdateComplete, onUpdateError) {
|
|
122
140
|
const signal = ref.current.abortController.signal;
|
|
123
141
|
return {
|
|
124
|
-
submitSavedView: actionWrapper(async (savedView
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
// TypeScript cannot tell that `savedView` object contains `id` string without a little help
|
|
129
|
-
savedView: { id: savedView.id, ...savedView },
|
|
130
|
-
savedViewData,
|
|
131
|
-
signal,
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
newSavedView = await client.createSavedView({
|
|
136
|
-
iTwinId: iTwinId,
|
|
137
|
-
iModelId: iModelId,
|
|
138
|
-
savedView: typeof savedView === "string" ? { displayName: savedView } : savedView,
|
|
139
|
-
savedViewData,
|
|
140
|
-
signal,
|
|
141
|
-
});
|
|
142
|
-
}
|
|
142
|
+
submitSavedView: actionWrapper(async (savedView) => {
|
|
143
|
+
const newSavedView = "id" in savedView
|
|
144
|
+
? await client.updateSavedView({ savedView, signal })
|
|
145
|
+
: await client.createSavedView({ iTwinId, iModelId, savedView, signal });
|
|
143
146
|
updateSavedViews((savedViews) => {
|
|
144
147
|
const entries = Array.from(savedViews.values());
|
|
145
148
|
entries.push(newSavedView);
|
package/lib/utils.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@itwin/saved-views-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/iTwin/saved-views.git",
|
|
8
8
|
"directory": "packages/saved-views-react"
|
|
9
9
|
},
|
|
10
|
-
"homepage": "https://github.com/iTwin/saved-views",
|
|
10
|
+
"homepage": "https://github.com/iTwin/saved-views/tree/master/packages/saved-views-react",
|
|
11
11
|
"author": {
|
|
12
12
|
"name": "Bentley Systems, Inc.",
|
|
13
13
|
"url": "https://www.bentley.com"
|
|
@@ -58,10 +58,10 @@
|
|
|
58
58
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@itwin/itwinui-icons-react": "^2.
|
|
61
|
+
"@itwin/itwinui-icons-react": "^2.9.0",
|
|
62
62
|
"@itwin/itwinui-react": "^3.8.1",
|
|
63
63
|
"fuse.js": "^6.6.2",
|
|
64
|
-
"@itwin/saved-views-client": "^0.
|
|
64
|
+
"@itwin/saved-views-client": "^0.4.0"
|
|
65
65
|
},
|
|
66
66
|
"scripts": {
|
|
67
67
|
"build": "run-p build:*",
|