@itwin/saved-views-react 0.5.0 → 0.7.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.
Files changed (40) hide show
  1. package/README.md +38 -4
  2. package/lib/SavedView.d.ts +41 -11
  3. package/lib/SavedViewTile/SavedViewOptions.js +10 -10
  4. package/lib/SavedViewTile/SavedViewTile.css +8 -1
  5. package/lib/SavedViewTile/SavedViewTile.d.ts +4 -3
  6. package/lib/SavedViewTile/SavedViewTile.js +5 -9
  7. package/lib/SavedViewTile/SavedViewTileContext.d.ts +0 -1
  8. package/lib/SavedViewTile/SavedViewTileContext.js +1 -1
  9. package/lib/SavedViewsClient/ITwinSavedViewsClient.d.ts +21 -23
  10. package/lib/SavedViewsClient/ITwinSavedViewsClient.js +120 -57
  11. package/lib/SavedViewsClient/SavedViewsClient.d.ts +68 -45
  12. package/lib/SavedViewsWidget/SavedViewGroupTile/SavedViewGroupTile.js +2 -3
  13. package/lib/SavedViewsWidget/SavedViewGroupTile/SavedViewGroupTileContext.d.ts +0 -1
  14. package/lib/SavedViewsWidget/SavedViewGroupTile/SavedViewGroupTileContext.js +1 -1
  15. package/lib/SavedViewsWidget/SavedViewsExpandableBlockWidget.d.ts +5 -3
  16. package/lib/SavedViewsWidget/SavedViewsExpandableBlockWidget.js +3 -3
  17. package/lib/SavedViewsWidget/SavedViewsFolderWidget.d.ts +5 -4
  18. package/lib/SavedViewsWidget/SavedViewsFolderWidget.js +7 -7
  19. package/lib/applySavedView.d.ts +36 -31
  20. package/lib/applySavedView.js +61 -26
  21. package/lib/captureSavedViewData.d.ts +31 -5
  22. package/lib/captureSavedViewData.js +103 -63
  23. package/lib/captureSavedViewThumbnail.d.ts +8 -1
  24. package/lib/captureSavedViewThumbnail.js +15 -7
  25. package/lib/createViewState.d.ts +22 -5
  26. package/lib/createViewState.js +28 -25
  27. package/lib/index.d.ts +10 -10
  28. package/lib/index.js +7 -7
  29. package/lib/translation/SavedViewTypes.d.ts +1 -1
  30. package/lib/translation/SavedViewsExtensionHandlers.d.ts +3 -0
  31. package/lib/translation/SavedViewsExtensionHandlers.js +14 -0
  32. package/lib/translation/clipVectorsLegacyExtractor.js +4 -0
  33. package/lib/translation/displayStyleExtractor.js +80 -3
  34. package/lib/translation/extractionUtilities.d.ts +9 -1
  35. package/lib/translation/extractionUtilities.js +16 -0
  36. package/lib/useSavedViews.d.ts +172 -34
  37. package/lib/useSavedViews.js +475 -503
  38. package/lib/utils.d.ts +8 -0
  39. package/lib/utils.js +13 -0
  40. package/package.json +9 -9
package/README.md CHANGED
@@ -2,10 +2,45 @@
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
+ Capture current viewport state into serializable format. You can use this data later to restore the view.
12
+
13
+ ```ts
14
+ const { viewData, extensions } = await captureSavedViewData({ viewport });
15
+ console.log({ viewData, extensions }); /*
16
+ {
17
+ viewData: { itwin3dView: {...} },
18
+ extensions: {
19
+ { extensionName: "EmphasizeElements", data: "{...}" },
20
+ }
21
+ } */
22
+ ```
23
+
24
+ ### [captureSavedViewThumbnail](./src/captureSavedViewThumbnail.ts)
25
+
26
+ Generate Saved View thumbnail based on what is currently displayed on the viewport.
27
+
28
+ ```ts
29
+ const thumbnail = captureSavedViewThumbnail(viewport);
30
+ console.log(thumbnail); // "data:image/png;base64,iVBORw0KGoAAAANSUhEUg..."
31
+ ```
32
+
33
+ ### [applySavedView](./src/applySavedView.ts)
34
+
35
+ Update viewport state to match captured Saved View.
36
+
37
+ ```ts
38
+ // Capture viewport state
39
+ const savedViewData = await captureSavedViewData({ viewport });
40
+ // Restore viewport state
41
+ await applySavedView(iModel, viewport, savedViewData);
42
+ ```
43
+
9
44
  ### React components
10
45
 
11
46
  * [SavedViewTile](./src/SavedViewTile/SavedViewTile.tsx)
@@ -42,7 +77,7 @@ export function SavedViewsWidget(props) {
42
77
  }
43
78
  ```
44
79
 
45
- ### useSavedViews
80
+ ### [useSavedViews](./src/useSavedViews.tsx)
46
81
 
47
82
  [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
83
 
@@ -50,8 +85,7 @@ export function SavedViewsWidget(props) {
50
85
  import { useSavedViews, ITwinSavedViewsClient } from "@itwin/saved-views-react";
51
86
 
52
87
  const client = new ITwinSavedViewsClient({
53
- // auth_token should have access to savedviews:read and savedviews:modify OIDC scopes
54
- getAccessToken: async () => "auth_token",
88
+ getAccessToken: async () => "itwin_platform_auth_token",
55
89
  });
56
90
 
57
91
  export function SavedViewsWidget(props) {
@@ -1,26 +1,56 @@
1
- import type { ReactNode } from "react";
1
+ import type { ViewITwin3d, ViewITwinDrawing, ViewITwinSheet } from "@itwin/saved-views-client";
2
2
  export interface SavedView {
3
- id: string;
3
+ savedViewId: string;
4
4
  displayName: string;
5
5
  groupId?: string | undefined;
6
6
  creatorId?: string | undefined;
7
7
  tagIds?: string[] | undefined;
8
8
  shared?: boolean | undefined;
9
- thumbnail?: ReactNode | string | undefined;
10
- /** `extensionName` and `data` pairs. */
11
- extensions?: Map<string, string> | undefined;
12
- /** Time the saved view was created as an ISO8601 string, `"YYYY-MM-DDTHH:mm:ss.sssZ"` */
13
- creationTime?: string;
14
- /** Time the saved view was last modified as an ISO8601 string, `"YYYY-MM-DDTHH:mm:ss.sssZ"` */
15
- lastModified?: string;
9
+ creationTime?: Date | undefined;
10
+ lastModified?: Date | undefined;
11
+ }
12
+ export interface SavedViewData {
13
+ viewData: ViewData;
14
+ extensions?: SavedViewExtension[] | undefined;
15
+ }
16
+ export type ViewData = ITwin3dViewData | ITwinDrawingdata | ITwinSheetData;
17
+ export interface ITwin3dViewData extends ViewITwin3d {
18
+ type: "iTwin3d";
19
+ }
20
+ export interface ITwinDrawingdata extends ViewITwinDrawing {
21
+ type: "iTwinDrawing";
22
+ }
23
+ export interface ITwinSheetData extends ViewITwinSheet {
24
+ type: "iTwinSheet";
25
+ }
26
+ export interface SavedViewExtension {
27
+ /** Extension identifier. Saved View cannot contain multiple extensions that share the same `extensionName`. */
28
+ extensionName: string;
29
+ /**
30
+ * Serialized extension data.
31
+ *
32
+ * @example
33
+ * const extension = {
34
+ * // Unique identifier makes extension data format portable between applications because it avoids collision
35
+ * // with different implementations
36
+ * extensionName: "CustomHighlight_$5be36494-ae03-4400-bb80-24ffd9db2a87",
37
+ * data: JSON.stringify({
38
+ * description: "For illustrative purposes only. We do not provide implementation for this extensionName."
39
+ * highlightColor: "#f05599",
40
+ * models: ["0x20000000006"],
41
+ * }),
42
+ * };
43
+ */
44
+ data: string;
16
45
  }
17
46
  export interface SavedViewTag {
18
- id: string;
47
+ tagId: string;
19
48
  displayName: string;
20
49
  }
21
50
  export interface SavedViewGroup {
22
- id: string;
51
+ groupId: string;
23
52
  displayName: string;
24
53
  creatorId?: string | undefined;
25
54
  shared?: boolean | undefined;
26
55
  }
56
+ export type WriteableSavedViewProperties = Pick<SavedView, "displayName" | "groupId" | "tagIds" | "shared">;
@@ -117,14 +117,14 @@ function MoveToGroup(props) {
117
117
  function MoveToGroupSubmenu(props) {
118
118
  const { localization: { moveToGroupMenu } } = useSavedViewsContext();
119
119
  const handleMoveToGroup = (groupId) => {
120
- props.moveToGroup(props.savedView.id, groupId);
120
+ props.moveToGroup(props.savedView.savedViewId, groupId);
121
121
  };
122
122
  const { moveToNewGroup } = props;
123
123
  const handleCreate = moveToNewGroup && ((groupName) => {
124
- moveToNewGroup(props.savedView.id, groupName);
124
+ moveToNewGroup(props.savedView.savedViewId, groupName);
125
125
  });
126
- return (_jsx(SearchableSubmenu, { collection: props.groups, placeholder: props.moveToNewGroup ? moveToGroupMenu.findOrCreateGroup : moveToGroupMenu.findGroup, creationLabel: moveToGroupMenu.createGroup, onCreate: handleCreate, children: (searchResults) => searchResults.map((group) => (_jsxs(MenuItem, { className: "svr-searchable-submenu-item", startIcon: group.shared ? _jsx(SvgShare, {}) : _jsx(SvgBlank, {}), onClick: () => handleMoveToGroup(group.id), disabled: group.id === props.savedView.groupId, children: [group.displayName, group.id === props.savedView.groupId &&
127
- _jsx(Text, { style: { marginLeft: "var(--iui-size-xs)" }, as: "span", children: moveToGroupMenu.current })] }, group.id))) }));
126
+ return (_jsx(SearchableSubmenu, { collection: props.groups, indexer: "groupId", placeholder: props.moveToNewGroup ? moveToGroupMenu.findOrCreateGroup : moveToGroupMenu.findGroup, creationLabel: moveToGroupMenu.createGroup, onCreate: handleCreate, children: (searchResults) => searchResults.map((group) => (_jsxs(MenuItem, { className: "svr-searchable-submenu-item", startIcon: group.shared ? _jsx(SvgShare, {}) : _jsx(SvgBlank, {}), onClick: () => handleMoveToGroup(group.groupId), disabled: group.groupId === props.savedView.groupId, children: [group.displayName, group.groupId === props.savedView.groupId &&
127
+ _jsx(Text, { style: { marginLeft: "var(--iui-size-xs)" }, as: "span", children: moveToGroupMenu.current })] }, group.groupId))) }));
128
128
  }
129
129
  function ManageTags(props) {
130
130
  const { savedView } = useSavedViewTileContext();
@@ -135,21 +135,21 @@ function ManageTagsSubmenu(props) {
135
135
  const { localization } = useSavedViewsContext();
136
136
  const handleTagClick = (tagId) => {
137
137
  if (props.savedView.tagIds?.includes(tagId)) {
138
- props.removeTag(props.savedView.id, tagId);
138
+ props.removeTag(props.savedView.savedViewId, tagId);
139
139
  }
140
140
  else {
141
- props.addTag(props.savedView.id, tagId);
141
+ props.addTag(props.savedView.savedViewId, tagId);
142
142
  }
143
143
  };
144
144
  const { addNewTag } = props;
145
145
  const handleCreate = addNewTag && ((tagName) => {
146
- addNewTag(props.savedView.id, tagName);
146
+ addNewTag(props.savedView.savedViewId, tagName);
147
147
  });
148
- return (_jsx(SearchableSubmenu, { collection: props.tags, placeholder: props.addNewTag ? localization.tagsMenu.findOrCreateTag : localization.tagsMenu.findTag, creationLabel: localization.tagsMenu.createTag, onCreate: handleCreate, children: (searchResults) => searchResults.map((tag) => (_jsx(MenuItem, { className: "svr-searchable-submenu-item", startIcon: props.savedView.tagIds?.includes(tag.id) ? _jsx(SvgCheckmarkSmall, {}) : _jsx(SvgBlank, {}), onClick: () => handleTagClick(tag.id), children: tag.displayName }, tag.id))) }));
148
+ return (_jsx(SearchableSubmenu, { collection: props.tags, indexer: "tagId", placeholder: props.addNewTag ? localization.tagsMenu.findOrCreateTag : localization.tagsMenu.findTag, creationLabel: localization.tagsMenu.createTag, onCreate: handleCreate, children: (searchResults) => searchResults.map((tag) => (_jsx(MenuItem, { className: "svr-searchable-submenu-item", startIcon: props.savedView.tagIds?.includes(tag.tagId) ? _jsx(SvgCheckmarkSmall, {}) : _jsx(SvgBlank, {}), onClick: () => handleTagClick(tag.tagId), children: tag.displayName }, tag.tagId))) }));
149
149
  }
150
150
  function SearchableSubmenu(props) {
151
151
  const { localization } = useSavedViewsContext();
152
- const fuse = useMemo(() => new Fuse(props.collection, { keys: ["displayName"], threshold: 0.5 }), [props.collection]);
152
+ const fuse = useMemo(() => new Fuse(props.collection, { keys: [props.indexer], threshold: 0.5 }), [props.collection, props.indexer]);
153
153
  const [inputValue, setInputValue] = useState("");
154
154
  const searchResults = useMemo(() => inputValue.length === 0 ? props.collection : fuse.search(inputValue, { limit: 6 }).map(({ item }) => item), [inputValue, props.collection, fuse]);
155
155
  const handleSearchChange = (event) => {
@@ -169,5 +169,5 @@ function SearchableSubmenu(props) {
169
169
  function Delete(props) {
170
170
  const { savedView } = useSavedViewTileContext();
171
171
  const { localization } = useSavedViewsContext();
172
- return (_jsx(MenuItem, { className: props.className, startIcon: props.icon, onClick: () => props.deleteSavedView(savedView.id), children: localization.delete }));
172
+ return (_jsx(MenuItem, { className: props.className, startIcon: props.icon, onClick: () => props.deleteSavedView(savedView.savedViewId), children: localization.delete }));
173
173
  }
@@ -11,6 +11,13 @@
11
11
 
12
12
  .svr-tile .svr-tile-thumbnail {
13
13
  height: var(--itwin-svr-thumbnail-height, calc(4 * var(--iui-size-xl)));
14
+
15
+ > img {
16
+ object-fit: cover;
17
+ width: 100%;
18
+ max-width: 100%;
19
+ max-height: 100%;
20
+ }
14
21
  }
15
22
 
16
23
  .svr-tile .svr-tile-name {
@@ -96,4 +103,4 @@
96
103
  }
97
104
  }
98
105
 
99
- /*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9TYXZlZFZpZXdUaWxlL1NhdmVkVmlld1RpbGUuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBIiwiZmlsZSI6IlNhdmVkVmlld1RpbGUuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiogQ29weXJpZ2h0IChjKSBCZW50bGV5IFN5c3RlbXMsIEluY29ycG9yYXRlZC4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiogU2VlIExJQ0VOU0UubWQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSB0ZXJtcyBhbmQgZnVsbCBjb3B5cmlnaHQgbm90aWNlLlxuKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbkBsYXllciBpdHdpbnVpO1xuXG5AbGF5ZXIgaXR3aW4tc3ZyIHtcbiAgLnN2ci10aWxlIHtcbiAgICB3aWR0aDogdmFyKC0taXR3aW4tc3ZyLXRpbGUtd2lkdGgsIGNhbGMoOCAqIHZhcigtLWl1aS1zaXplLXhsKSkpO1xuICB9XG5cbiAgLnN2ci10aWxlIC5zdnItdGlsZS10aHVtYm5haWwge1xuICAgIGhlaWdodDogdmFyKC0taXR3aW4tc3ZyLXRodW1ibmFpbC1oZWlnaHQsIGNhbGMoNCAqIHZhcigtLWl1aS1zaXplLXhsKSkpO1xuICB9XG5cbiAgLnN2ci10aWxlIC5zdnItdGlsZS1uYW1lIHtcbiAgICBoZWlnaHQ6IGNhbGModmFyKC0taXVpLXNpemUtbCkgKyAycHgpO1xuICAgIHVzZXItc2VsZWN0OiB1bnNldDtcbiAgICBtaW4td2lkdGg6IDA7XG4gICAgdGV4dC13cmFwOiBub3dyYXA7XG5cbiAgICA+IGRpdiB7XG4gICAgICBvdmVyZmxvdzogaGlkZGVuO1xuICAgICAgdGV4dC1vdmVyZmxvdzogZWxsaXBzaXM7XG4gICAgfVxuICB9XG5cbiAgLnN2ci10aWxlLS1tZXRhZGF0YSB7XG4gICAgZGlzcGxheTogZ3JpZDtcbiAgICBncmlkOiAxZnIgLyBhdXRvIG1pbm1heCgwLCAxZnIpIGF1dG87XG4gICAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAgfVxuXG4gIC5zdnItdGlsZS0tbWV0YWRhdGEgPiBzdmcge1xuICAgIG1hcmdpbi1yaWdodDogdmFyKC0taXVpLXNpemUteHMpO1xuICB9XG5cbiAgLnN2ci10aWxlLS10YWctY29udGFpbmVyIHtcbiAgICBkaXNwbGF5OiBmbGV4O1xuICAgIGdhcDogdmFyKC0taXVpLXNpemUtcyk7XG4gIH1cblxuICAuc3ZyLXRpbGUtLXRhZyB7XG4gICAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAgICB0ZXh0LW92ZXJmbG93OiBlbGxpcHNpcztcbiAgICB3aGl0ZS1zcGFjZTogbm93cmFwO1xuICAgIG92ZXJmbG93OiBoaWRkZW47XG4gICAgbWluLXdpZHRoOiAwO1xuICB9XG5cbiAgLnN2ci10aWxlLS10YWctb3ZlcmZsb3cge1xuICAgIG1hcmdpbi1sZWZ0OiB2YXIoLS1pdWktc2l6ZS1zKTtcbiAgfVxuXG4gIC5zdnItdGlsZS0tbW9yZS1vcHRpb25zIHtcbiAgICB6LWluZGV4OiAyO1xuICAgIGdyaWQtYXJlYTogMSAvIDEgLyAtMSAvIC0xO1xuICAgIHBsYWNlLXNlbGY6IGVuZDtcbiAgICBtYXJnaW46IDA7XG4gICAgbWFyZ2luLWlubGluZS1lbmQ6IGNhbGMoLTEqdmFyKC0taXVpLXNpemUtMnhzKSk7XG4gICAgZGlzcGxheTogZ3JpZDtcbiAgICBwb3NpdGlvbjogYWJzb2x1dGU7XG4gIH1cblxuICAuc3ZyLXRpbGU6bm90KDpob3ZlciwgOmZvY3VzLXdpdGhpbikgLnN2ci10aWxlLS1tb3JlLW9wdGlvbnM6d2hlcmUoOm5vdCguc3ZyLXZpc2libGUpKSB7XG4gICAgdmlzaWJpbGl0eTogaGlkZGVuO1xuICB9XG5cbiAgLnN2ci10aWxlLS1pY29uLWNvbnRhaW5lciB7XG4gICAgZGlzcGxheTogZmxleDtcbiAgICBtYXJnaW4tdG9wOiBjYWxjKDAuNSAqIHZhcigtLWl1aS1zaXplLXMpKTtcbiAgICBtYXJnaW4taW5saW5lOiB2YXIoLS1pdWktc2l6ZS14cyk7XG4gICAgZ2FwOiB2YXIoLS1pdWktc2l6ZS0yeHMpO1xuICAgIHotaW5kZXg6IDE7XG4gIH1cblxuICAuc3ZyLXRpbGUtLWljb24ge1xuICAgIGJvcmRlci1yYWRpdXM6IDUwJTtcbiAgICBiYWNrZ3JvdW5kOiByZ2IoMCwgMCwgMCwgdmFyKC0taXVpLW9wYWNpdHktMykpO1xuICAgIG1pbi13aWR0aDogdmFyKC0taXVpLWNvbXBvbmVudC1oZWlnaHQtc21hbGwpO1xuICAgIG1pbi1oZWlnaHQ6IHZhcigtLWl1aS1jb21wb25lbnQtaGVpZ2h0LXNtYWxsKTtcbiAgICBkaXNwbGF5OiBmbGV4O1xuICAgIHBsYWNlLWl0ZW1zOiBjZW50ZXI7XG4gICAgcGFkZGluZy1pbmxpbmU6IHZhcigtLWl1aS1zaXplLTJ4cyk7XG4gICAgYm9yZGVyOiAxcHggc29saWQgdHJhbnNwYXJlbnQ7XG4gIH1cblxuICAuc3ZyLXRpbGUtLWljb24gc3ZnIHtcbiAgICBmaWxsOiB2YXIoLS1pdWktY29sb3Itd2hpdGUpO1xuICB9XG5cbiAgLnN2ci10aWxlLS1lZGl0YWJsZS10aXRsZSA+IHNwYW4ge1xuICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAgICBnYXA6IHZhcigtLWl1aS1zaXplLXMpO1xuICB9XG59XG4iXX0= */
106
+ /*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9TYXZlZFZpZXdUaWxlL1NhdmVkVmlld1RpbGUuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBIiwiZmlsZSI6IlNhdmVkVmlld1RpbGUuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiLyotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiogQ29weXJpZ2h0IChjKSBCZW50bGV5IFN5c3RlbXMsIEluY29ycG9yYXRlZC4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiogU2VlIExJQ0VOU0UubWQgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSB0ZXJtcyBhbmQgZnVsbCBjb3B5cmlnaHQgbm90aWNlLlxuKi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKi9cbkBsYXllciBpdHdpbnVpO1xuXG5AbGF5ZXIgaXR3aW4tc3ZyIHtcbiAgLnN2ci10aWxlIHtcbiAgICB3aWR0aDogdmFyKC0taXR3aW4tc3ZyLXRpbGUtd2lkdGgsIGNhbGMoOCAqIHZhcigtLWl1aS1zaXplLXhsKSkpO1xuICB9XG5cbiAgLnN2ci10aWxlIC5zdnItdGlsZS10aHVtYm5haWwge1xuICAgIGhlaWdodDogdmFyKC0taXR3aW4tc3ZyLXRodW1ibmFpbC1oZWlnaHQsIGNhbGMoNCAqIHZhcigtLWl1aS1zaXplLXhsKSkpO1xuXG4gICAgPiBpbWcge1xuICAgICAgb2JqZWN0LWZpdDogY292ZXI7XG4gICAgICB3aWR0aDogMTAwJTtcbiAgICAgIG1heC13aWR0aDogMTAwJTtcbiAgICAgIG1heC1oZWlnaHQ6IDEwMCU7XG4gICAgfVxuICB9XG5cbiAgLnN2ci10aWxlIC5zdnItdGlsZS1uYW1lIHtcbiAgICBoZWlnaHQ6IGNhbGModmFyKC0taXVpLXNpemUtbCkgKyAycHgpO1xuICAgIHVzZXItc2VsZWN0OiB1bnNldDtcbiAgICBtaW4td2lkdGg6IDA7XG4gICAgdGV4dC13cmFwOiBub3dyYXA7XG5cbiAgICA+IGRpdiB7XG4gICAgICBvdmVyZmxvdzogaGlkZGVuO1xuICAgICAgdGV4dC1vdmVyZmxvdzogZWxsaXBzaXM7XG4gICAgfVxuICB9XG5cbiAgLnN2ci10aWxlLS1tZXRhZGF0YSB7XG4gICAgZGlzcGxheTogZ3JpZDtcbiAgICBncmlkOiAxZnIgLyBhdXRvIG1pbm1heCgwLCAxZnIpIGF1dG87XG4gICAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAgfVxuXG4gIC5zdnItdGlsZS0tbWV0YWRhdGEgPiBzdmcge1xuICAgIG1hcmdpbi1yaWdodDogdmFyKC0taXVpLXNpemUteHMpO1xuICB9XG5cbiAgLnN2ci10aWxlLS10YWctY29udGFpbmVyIHtcbiAgICBkaXNwbGF5OiBmbGV4O1xuICAgIGdhcDogdmFyKC0taXVpLXNpemUtcyk7XG4gIH1cblxuICAuc3ZyLXRpbGUtLXRhZyB7XG4gICAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAgICB0ZXh0LW92ZXJmbG93OiBlbGxpcHNpcztcbiAgICB3aGl0ZS1zcGFjZTogbm93cmFwO1xuICAgIG92ZXJmbG93OiBoaWRkZW47XG4gICAgbWluLXdpZHRoOiAwO1xuICB9XG5cbiAgLnN2ci10aWxlLS10YWctb3ZlcmZsb3cge1xuICAgIG1hcmdpbi1sZWZ0OiB2YXIoLS1pdWktc2l6ZS1zKTtcbiAgfVxuXG4gIC5zdnItdGlsZS0tbW9yZS1vcHRpb25zIHtcbiAgICB6LWluZGV4OiAyO1xuICAgIGdyaWQtYXJlYTogMSAvIDEgLyAtMSAvIC0xO1xuICAgIHBsYWNlLXNlbGY6IGVuZDtcbiAgICBtYXJnaW46IDA7XG4gICAgbWFyZ2luLWlubGluZS1lbmQ6IGNhbGMoLTEqdmFyKC0taXVpLXNpemUtMnhzKSk7XG4gICAgZGlzcGxheTogZ3JpZDtcbiAgICBwb3NpdGlvbjogYWJzb2x1dGU7XG4gIH1cblxuICAuc3ZyLXRpbGU6bm90KDpob3ZlciwgOmZvY3VzLXdpdGhpbikgLnN2ci10aWxlLS1tb3JlLW9wdGlvbnM6d2hlcmUoOm5vdCguc3ZyLXZpc2libGUpKSB7XG4gICAgdmlzaWJpbGl0eTogaGlkZGVuO1xuICB9XG5cbiAgLnN2ci10aWxlLS1pY29uLWNvbnRhaW5lciB7XG4gICAgZGlzcGxheTogZmxleDtcbiAgICBtYXJnaW4tdG9wOiBjYWxjKDAuNSAqIHZhcigtLWl1aS1zaXplLXMpKTtcbiAgICBtYXJnaW4taW5saW5lOiB2YXIoLS1pdWktc2l6ZS14cyk7XG4gICAgZ2FwOiB2YXIoLS1pdWktc2l6ZS0yeHMpO1xuICAgIHotaW5kZXg6IDE7XG4gIH1cblxuICAuc3ZyLXRpbGUtLWljb24ge1xuICAgIGJvcmRlci1yYWRpdXM6IDUwJTtcbiAgICBiYWNrZ3JvdW5kOiByZ2IoMCwgMCwgMCwgdmFyKC0taXVpLW9wYWNpdHktMykpO1xuICAgIG1pbi13aWR0aDogdmFyKC0taXVpLWNvbXBvbmVudC1oZWlnaHQtc21hbGwpO1xuICAgIG1pbi1oZWlnaHQ6IHZhcigtLWl1aS1jb21wb25lbnQtaGVpZ2h0LXNtYWxsKTtcbiAgICBkaXNwbGF5OiBmbGV4O1xuICAgIHBsYWNlLWl0ZW1zOiBjZW50ZXI7XG4gICAgcGFkZGluZy1pbmxpbmU6IHZhcigtLWl1aS1zaXplLTJ4cyk7XG4gICAgYm9yZGVyOiAxcHggc29saWQgdHJhbnNwYXJlbnQ7XG4gIH1cblxuICAuc3ZyLXRpbGUtLWljb24gc3ZnIHtcbiAgICBmaWxsOiB2YXIoLS1pdWktY29sb3Itd2hpdGUpO1xuICB9XG5cbiAgLnN2ci10aWxlLS1lZGl0YWJsZS10aXRsZSA+IHNwYW4ge1xuICAgIGRpc3BsYXk6IGZsZXg7XG4gICAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAgICBnYXA6IHZhcigtLWl1aS1zaXplLXMpO1xuICB9XG59XG4iXX0= */
@@ -4,6 +4,8 @@ import "./SavedViewTile.css";
4
4
  interface SavedViewTileProps {
5
5
  /** A Saved View that is being represented by the tile. */
6
6
  savedView: SavedView;
7
+ /** Image to be displayed on the tile. */
8
+ thumbnail?: ReactNode | undefined;
7
9
  /** A collection of available Saved View tags. Used for displaying tags on the tile. */
8
10
  tags?: Map<string, SavedViewTag> | undefined;
9
11
  /** When `true`, the tile becomes non-interactive and tile editing controls are shown. */
@@ -17,13 +19,12 @@ interface SavedViewTileProps {
17
19
  /**
18
20
  * Invoked when user submits a new name for the Saved View.
19
21
  * @param savedViewId Id of the associated Saved View
20
- * @param newName User-submitted string for the Saved View title. This value is `undefined` when user cancels rename
21
- * operaton.
22
+ * @param newName User-submitted string for the Saved View title
22
23
  *
23
24
  * @example
24
25
  * <SavedViewTile savedView={savedView} onRename={handleRename} editable />
25
26
  */
26
- onRename?: ((savedViewId: string, newName: string | undefined) => void) | undefined;
27
+ onRename?: ((savedViewId: string, newName: string) => void) | undefined;
27
28
  /**
28
29
  * Click handler meant for triggering the render of iModel onto the screen with the saved view applied
29
30
  */
@@ -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, SvgImageFrame, SvgMore, SvgShare, SvgTag } from "@itwin/itwinui-icons-react";
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";
@@ -34,7 +34,7 @@ export function SavedViewTile(props) {
34
34
  }, [props.savedView.tagIds, props.tags]);
35
35
  const [numShownTags, setNumShownTags] = useState(savedViewTags.length);
36
36
  const metadataRef = useRef(null);
37
- const metadata = (_jsxs("div", { className: "svr-tile--metadata", children: [savedViewTags.length > 0 && _jsx(SvgTag, {}), _jsx("div", { ref: metadataRef, className: "svr-tile--tag-container", children: savedViewTags.slice(0, numShownTags).map((tag, i) => (_jsx("div", { className: "svr-tile--tag", style: { flexShrink: i === numShownTags - 1 ? "unset" : 0 }, children: tag.displayName }, tag.id))) }), numShownTags < savedViewTags.length &&
37
+ const metadata = (_jsxs("div", { className: "svr-tile--metadata", children: [savedViewTags.length > 0 && _jsx(SvgTag, {}), _jsx("div", { ref: metadataRef, className: "svr-tile--tag-container", children: savedViewTags.slice(0, numShownTags).map((tag, i) => (_jsx("div", { className: "svr-tile--tag", style: { flexShrink: i === numShownTags - 1 ? "unset" : 0 }, children: tag.displayName }, tag.tagId))) }), numShownTags < savedViewTags.length &&
38
38
  _jsx(Text, { className: "svr-tile--tag-overflow", variant: "small", children: `+${savedViewTags.length - numShownTags} ${localization.tile.moreTags}` })] }));
39
39
  useLayoutEffect(() => { setNumShownTags(savedViewTags.length); }, [savedViewTags]);
40
40
  useLayoutEffect(() => {
@@ -57,17 +57,13 @@ export function SavedViewTile(props) {
57
57
  }, [numShownTags]);
58
58
  const rightIcons = props.rightIcons ?? (props.savedView.shared ? [_jsx(SvgShare, {}, "share")] : []);
59
59
  const savedViewTileContext = useMemo(() => ({ savedView: props.savedView, setEditingName }), [props.savedView]);
60
- return (_jsx(SavedViewTileContextProvider, { value: savedViewTileContext, children: _jsxs(Tile.Wrapper, { className: `svr-tile ${props.className || ""}`, onClick: () => props.onClick?.(props.savedView.id), children: [!props.editable && _jsx(Tile.Action, {}), _jsx(Tile.Name, { className: "svr-tile-name", children: _jsx(EditableTileName, { displayName: props.savedView.displayName, editing: editingName, actions: {
60
+ return (_jsx(SavedViewTileContextProvider, { value: savedViewTileContext, children: _jsxs(Tile.Wrapper, { className: `svr-tile ${props.className || ""}`, onClick: () => props.onClick?.(props.savedView.savedViewId), children: [!props.editable && _jsx(Tile.Action, {}), _jsx(Tile.Name, { className: "svr-tile-name", children: _jsx(EditableTileName, { displayName: props.savedView.displayName, editing: editingName, actions: {
61
61
  onStartEditing: () => setEditingName(true),
62
62
  onEndEditing: (newName, commit) => {
63
63
  setEditingName(false);
64
- props.onRename?.(props.savedView.id, commit ? newName : undefined);
64
+ commit && props.onRename?.(props.savedView.savedViewId, newName);
65
65
  },
66
- }, editable: props.editable || editingName }) }), _jsxs(Tile.ThumbnailArea, { className: "svr-tile-thumbnail", children: [typeof props.savedView.thumbnail === "string"
67
- ? _jsx(Tile.ThumbnailPicture, { url: props.savedView.thumbnail })
68
- : props.savedView.thumbnail
69
- ? props.savedView.thumbnail
70
- : _jsx(Tile.ThumbnailPicture, { children: _jsx(SvgImageFrame, {}) }), _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)) &&
66
+ }, editable: props.editable || editingName }) }), _jsxs(Tile.ThumbnailArea, { className: "svr-tile-thumbnail", children: [props.thumbnail ?? _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
67
  _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
68
  }
73
69
  function isOverflowing(element) {
@@ -1,4 +1,3 @@
1
- /// <reference types="react" />
2
1
  import type { SavedView } from "../SavedView.js";
3
2
  /** Context that's accessable within `<SavedViewTile />` component. */
4
3
  export interface SavedViewTileContext {
@@ -14,7 +14,7 @@ export const SavedViewTileContextProvider = savedViewTileContext.Provider;
14
14
  export function useSavedViewTileContext() {
15
15
  const contextValue = useContext(savedViewTileContext);
16
16
  return contextValue ?? {
17
- savedView: { id: "SavedViewTileContext_NoContext", displayName: "" },
17
+ savedView: { savedViewId: "SavedViewTileContext_NoContext", displayName: "" },
18
18
  setEditingName: () => { },
19
19
  };
20
20
  }
@@ -1,31 +1,29 @@
1
- import { type SavedViewRepresentation } from "@itwin/saved-views-client";
2
- import type { SavedView, SavedViewGroup, SavedViewTag } from "../SavedView.js";
3
- import type { CreateGroupParams, CreateSavedViewParams, CreateTagParams, DeleteGroupParams, DeleteSavedViewParams, DeleteTagParams, GetSavedViewInfoParams, GetSingularSavedViewParams, GetThumbnailUrlParams, SavedViewInfo, SavedViewsClient, UpdateGroupParams, UpdateSavedViewParams, UpdateTagParams, UploadThumbnailParams } from "./SavedViewsClient.js";
1
+ import type { SavedView, SavedViewData, SavedViewGroup, SavedViewTag } from "../SavedView.js";
2
+ import type { CreateGroupArgs, CreateSavedViewArgs, CreateTagArgs, DeleteGroupArgs, DeleteSavedViewArgs, DeleteTagArgs, GetGroupsArgs, GetSavedViewByIdArgs, GetSavedViewDataByIdArgs, GetSavedViewsArgs, GetTagsArgs, GetThumbnailUrlArgs, SavedViewsClient, UpdateGroupArgs, UpdateSavedViewArgs, UpdateTagArgs, UploadThumbnailArgs } from "./SavedViewsClient.js";
4
3
  interface ITwinSavedViewsClientParams {
5
- /** @default "https://api.bentley.com/savedviews" */
4
+ /** Authorization token that grants access to iTwin Platform API. */
6
5
  getAccessToken: () => Promise<string>;
7
- /**
8
- * Authorization token that grants access to iTwin Saved Views API. The token should be valid for `savedviews:read`
9
- * and `savedviews:modify` OIDC scopes.
10
- */
6
+ /** @default "https://api.bentley.com/savedviews" */
11
7
  baseUrl?: string | undefined;
12
8
  }
13
- /** */
14
9
  export declare class ITwinSavedViewsClient implements SavedViewsClient {
15
- private client;
10
+ #private;
16
11
  constructor(args: ITwinSavedViewsClientParams);
17
- getSavedViewInfo(args: GetSavedViewInfoParams): Promise<SavedViewInfo>;
18
- getSingularSavedView(args: GetSingularSavedViewParams): Promise<SavedViewRepresentation>;
19
- getThumbnailUrl(args: GetThumbnailUrlParams): Promise<string | undefined>;
20
- uploadThumbnail(args: UploadThumbnailParams): Promise<void>;
21
- createSavedView(args: CreateSavedViewParams): Promise<SavedView>;
22
- updateSavedView(args: UpdateSavedViewParams): Promise<SavedView>;
23
- deleteSavedView(args: DeleteSavedViewParams): Promise<void>;
24
- createGroup(args: CreateGroupParams): Promise<SavedViewGroup>;
25
- updateGroup(args: UpdateGroupParams): Promise<SavedViewGroup>;
26
- deleteGroup(args: DeleteGroupParams): Promise<void>;
27
- createTag(args: CreateTagParams): Promise<SavedViewTag>;
28
- updateTag(args: UpdateTagParams): Promise<SavedViewTag>;
29
- deleteTag(args: DeleteTagParams): Promise<void>;
12
+ getSavedViews(args: GetSavedViewsArgs): AsyncIterableIterator<SavedView[]>;
13
+ getGroups(args: GetGroupsArgs): Promise<SavedViewGroup[]>;
14
+ getTags(args: GetTagsArgs): Promise<SavedViewTag[]>;
15
+ getThumbnailUrl(args: GetThumbnailUrlArgs): Promise<string | undefined>;
16
+ uploadThumbnail(args: UploadThumbnailArgs): Promise<void>;
17
+ getSavedViewById(args: GetSavedViewByIdArgs): Promise<SavedView>;
18
+ getSavedViewDataById(args: GetSavedViewDataByIdArgs): Promise<SavedViewData>;
19
+ createSavedView(args: CreateSavedViewArgs): Promise<SavedView>;
20
+ updateSavedView(args: UpdateSavedViewArgs): Promise<SavedView>;
21
+ deleteSavedView(args: DeleteSavedViewArgs): Promise<void>;
22
+ createGroup(args: CreateGroupArgs): Promise<SavedViewGroup>;
23
+ updateGroup(args: UpdateGroupArgs): Promise<SavedViewGroup>;
24
+ deleteGroup(args: DeleteGroupArgs): Promise<void>;
25
+ createTag(args: CreateTagArgs): Promise<SavedViewTag>;
26
+ updateTag(args: UpdateTagArgs): Promise<SavedViewTag>;
27
+ deleteTag(args: DeleteTagArgs): Promise<void>;
30
28
  }
31
29
  export {};
@@ -2,34 +2,41 @@
2
2
  * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
3
3
  * See LICENSE.md in the project root for license terms and full copyright notice.
4
4
  *--------------------------------------------------------------------------------------------*/
5
- import { ITwinSavedViewsClient as Client, } from "@itwin/saved-views-client";
6
- /** */
5
+ import { ITwinSavedViewsClient as Client, isViewDataITwin3d, isViewDataITwinDrawing, } from "@itwin/saved-views-client";
7
6
  export class ITwinSavedViewsClient {
8
- client;
7
+ #client;
9
8
  constructor(args) {
10
- this.client = new Client(args);
11
- }
12
- async getSavedViewInfo(args) {
13
- const [{ savedViews }, { groups }, { tags }] = await Promise.all([
14
- this.client.getAllSavedViewsMinimal(args),
15
- this.client.getAllGroups(args),
16
- this.client.getAllTags(args),
17
- ]);
18
- return {
19
- savedViews: savedViews.map(savedViewResponseToSavedView),
20
- groups: groups.map(groupResponseToSavedViewGroup),
21
- tags: tags.map(tagResponseToSavedViewTag),
22
- };
9
+ this.#client = new Client(args);
23
10
  }
24
- async getSingularSavedView(args) {
25
- const response = await this.client.getSavedViewRepresentation({
26
- savedViewId: args.savedViewId,
11
+ async *getSavedViews(args) {
12
+ const iterable = this.#client.getAllSavedViewsMinimal({
13
+ iTwinId: args.iTwinId,
14
+ iModelId: args.iModelId,
15
+ groupId: args.groupId,
27
16
  signal: args.signal,
28
17
  });
29
- return response.savedView;
18
+ for await (const page of iterable) {
19
+ yield page.savedViews.map(savedViewResponseToSavedView);
20
+ }
21
+ }
22
+ async getGroups(args) {
23
+ const response = await this.#client.getAllGroups({
24
+ iTwinId: args.iTwinId,
25
+ iModelId: args.iModelId,
26
+ signal: args.signal,
27
+ });
28
+ return response.groups.map(groupResponseToSavedViewGroup);
29
+ }
30
+ async getTags(args) {
31
+ const response = await this.#client.getAllTags({
32
+ iTwinId: args.iTwinId,
33
+ iModelId: args.iModelId,
34
+ signal: args.signal,
35
+ });
36
+ return response.tags.map(tagResponseToSavedViewTag);
30
37
  }
31
38
  async getThumbnailUrl(args) {
32
- const response = await this.client.getImage({
39
+ const response = await this.#client.getImage({
33
40
  savedViewId: args.savedViewId,
34
41
  size: "thumbnail",
35
42
  signal: args.signal,
@@ -37,66 +44,93 @@ export class ITwinSavedViewsClient {
37
44
  return response.href;
38
45
  }
39
46
  async uploadThumbnail(args) {
40
- await this.client.updateImage({
47
+ await this.#client.updateImage({
41
48
  savedViewId: args.savedViewId,
42
49
  image: args.image,
43
50
  signal: args.signal,
44
51
  });
45
52
  }
53
+ async getSavedViewById(args) {
54
+ const response = await this.#client.getSavedViewMinimal({
55
+ savedViewId: args.savedViewId,
56
+ signal: args.signal,
57
+ });
58
+ return savedViewResponseToSavedView(response.savedView);
59
+ }
60
+ async getSavedViewDataById(args) {
61
+ const response = await this.#client.getSavedViewRepresentation({
62
+ savedViewId: args.savedViewId, signal: args.signal,
63
+ });
64
+ return {
65
+ viewData: getViewData(response.savedView),
66
+ extensions: response.savedView.extensions,
67
+ };
68
+ }
46
69
  async createSavedView(args) {
47
- const { savedView } = await this.client.createSavedView({
70
+ const savedViewData = toApiSavedViewData(args.savedViewData.viewData);
71
+ const { savedView } = await this.#client.createSavedView({
48
72
  iTwinId: args.iTwinId,
49
73
  iModelId: args.iModelId,
50
- displayName: args.savedView.displayName,
51
- tagIds: args.savedView.tagIds,
52
- groupId: args.savedView.groupId,
53
- shared: args.savedView.shared,
54
- savedViewData: args.savedViewData,
74
+ displayName: args.displayName,
75
+ groupId: args.groupId,
76
+ tagIds: args.tagIds,
77
+ shared: args.shared,
78
+ savedViewData,
79
+ extensions: args.savedViewData.extensions,
55
80
  signal: args.signal,
56
81
  });
57
82
  return savedViewResponseToSavedView(savedView);
58
83
  }
59
84
  async updateSavedView(args) {
60
- const { savedView } = await this.client.updateSavedView({
61
- savedViewId: args.savedView.id,
62
- displayName: args.savedView.displayName,
63
- tagIds: args.savedView.tagIds,
64
- groupId: args.savedView.groupId,
65
- shared: args.savedView.shared,
66
- savedViewData: args.savedViewData,
67
- extensions: args.extensions,
85
+ const savedViewData = args.savedViewData && toApiSavedViewData(args.savedViewData.viewData);
86
+ const { savedView } = await this.#client.updateSavedView({
87
+ savedViewId: args.savedViewId,
88
+ displayName: args.displayName,
89
+ tagIds: args.tagIds,
90
+ groupId: args.groupId,
91
+ shared: args.shared,
92
+ savedViewData,
68
93
  signal: args.signal,
69
94
  });
95
+ await Promise.all((args.savedViewData?.extensions ?? []).map(({ extensionName, data }) => {
96
+ this.#client.createExtension({
97
+ savedViewId: args.savedViewId,
98
+ extensionName,
99
+ data,
100
+ });
101
+ }));
70
102
  return savedViewResponseToSavedView(savedView);
71
103
  }
72
104
  async deleteSavedView(args) {
73
- await this.client.deleteSavedView({ savedViewId: args.savedViewId, signal: args.signal });
105
+ await this.#client.deleteSavedView({ savedViewId: args.savedViewId, signal: args.signal });
74
106
  }
75
107
  async createGroup(args) {
76
- const { group } = await this.client.createGroup({
108
+ const { group } = await this.#client.createGroup({
77
109
  iTwinId: args.iTwinId,
78
110
  iModelId: args.iModelId,
79
- displayName: args.group.displayName,
111
+ displayName: args.displayName,
80
112
  signal: args.signal,
81
113
  });
82
114
  return groupResponseToSavedViewGroup(group);
83
115
  }
84
116
  async updateGroup(args) {
85
- const { group } = await this.client.updateGroup({
86
- groupId: args.group.id,
87
- displayName: args.group.displayName,
88
- shared: args.group.shared,
117
+ const { group } = await this.#client.updateGroup({
118
+ groupId: args.groupId,
119
+ displayName: args.displayName,
120
+ shared: args.shared,
89
121
  signal: args.signal,
90
122
  });
91
123
  return groupResponseToSavedViewGroup(group);
92
124
  }
93
125
  async deleteGroup(args) {
94
- const savedViews = await this.client.getAllSavedViewsMinimal({ groupId: args.groupId, signal: args.signal });
95
- await Promise.all(savedViews.savedViews.map(({ id }) => this.client.deleteSavedView({ savedViewId: id, signal: args.signal })));
96
- await this.client.deleteGroup({ groupId: args.groupId, signal: args.signal });
126
+ const savedViewPages = this.#client.getAllSavedViewsMinimal({ groupId: args.groupId, signal: args.signal });
127
+ for await (const { savedViews } of savedViewPages) {
128
+ await Promise.all(savedViews.map(({ id }) => this.#client.deleteSavedView({ savedViewId: id, signal: args.signal })));
129
+ }
130
+ await this.#client.deleteGroup({ groupId: args.groupId, signal: args.signal });
97
131
  }
98
132
  async createTag(args) {
99
- const { tag } = await this.client.createTag({
133
+ const { tag } = await this.#client.createTag({
100
134
  iTwinId: args.iTwinId,
101
135
  iModelId: args.iModelId,
102
136
  displayName: args.displayName,
@@ -105,33 +139,32 @@ export class ITwinSavedViewsClient {
105
139
  return tagResponseToSavedViewTag(tag);
106
140
  }
107
141
  async updateTag(args) {
108
- const { tag } = await this.client.updateTag({
109
- tagId: args.tag.id,
110
- displayName: args.tag.displayName,
142
+ const { tag } = await this.#client.updateTag({
143
+ tagId: args.tagId,
144
+ displayName: args.displayName,
111
145
  signal: args.signal,
112
146
  });
113
147
  return tagResponseToSavedViewTag(tag);
114
148
  }
115
149
  async deleteTag(args) {
116
- await this.client.deleteTag({ tagId: args.tagId, signal: args.signal });
150
+ await this.#client.deleteTag({ tagId: args.tagId, signal: args.signal });
117
151
  }
118
152
  }
119
153
  function savedViewResponseToSavedView(response) {
120
154
  return {
121
- id: response.id,
155
+ savedViewId: response.id,
122
156
  displayName: response.displayName,
123
157
  tagIds: response.tags?.map((tag) => tag.id),
124
158
  groupId: response._links.group?.href.split("/").at(-1),
125
159
  creatorId: response._links.creator?.href.split("/").at(-1),
126
160
  shared: response.shared,
127
- thumbnail: undefined,
128
- creationTime: response.creationTime,
129
- lastModified: response.lastModified,
161
+ creationTime: new Date(response.creationTime),
162
+ lastModified: new Date(response.lastModified),
130
163
  };
131
164
  }
132
165
  function groupResponseToSavedViewGroup(response) {
133
166
  return {
134
- id: response.id,
167
+ groupId: response.id,
135
168
  displayName: response.displayName,
136
169
  creatorId: response._links.creator?.href.split("/").at(-1),
137
170
  shared: response.shared,
@@ -139,7 +172,37 @@ function groupResponseToSavedViewGroup(response) {
139
172
  }
140
173
  function tagResponseToSavedViewTag(response) {
141
174
  return {
142
- id: response.id,
175
+ tagId: response.id,
143
176
  displayName: response.displayName,
144
177
  };
145
178
  }
179
+ function getViewData(savedView) {
180
+ if (isViewDataITwin3d(savedView.savedViewData)) {
181
+ return {
182
+ type: "iTwin3d",
183
+ ...savedView.savedViewData.itwin3dView,
184
+ };
185
+ }
186
+ if (isViewDataITwinDrawing(savedView.savedViewData)) {
187
+ return {
188
+ type: "iTwinDrawing",
189
+ ...savedView.savedViewData.itwinDrawingView,
190
+ };
191
+ }
192
+ return {
193
+ type: "iTwinSheet",
194
+ ...savedView.savedViewData.itwinSheetView,
195
+ };
196
+ }
197
+ function toApiSavedViewData(viewData) {
198
+ if (viewData.type === "iTwin3d") {
199
+ const { type, ...rest } = viewData;
200
+ return { itwin3dView: rest };
201
+ }
202
+ if (viewData.type === "iTwinDrawing") {
203
+ const { type, ...rest } = viewData;
204
+ return { itwinDrawingView: rest };
205
+ }
206
+ const { type, ...rest } = viewData;
207
+ return { itwinSheetView: rest };
208
+ }