@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
@@ -1,75 +1,98 @@
1
- import type { ExtensionMin, ExtensionSavedViewCreate, SavedViewRepresentation, ViewData } from "@itwin/saved-views-client";
2
- import type { SavedView, SavedViewGroup, SavedViewTag } from "../SavedView.js";
3
- export interface SavedViewInfo {
4
- savedViews: SavedView[];
5
- groups: SavedViewGroup[];
6
- tags: SavedViewTag[];
7
- }
1
+ import type { SavedViewData, SavedViewGroup, SavedView, SavedViewTag } from "../SavedView.js";
8
2
  export interface SavedViewsClient {
9
- getSavedViewInfo: (args: GetSavedViewInfoParams) => Promise<SavedViewInfo>;
10
- getSingularSavedView: (args: GetSingularSavedViewParams) => Promise<SavedViewRepresentation>;
11
- getThumbnailUrl: (args: GetThumbnailUrlParams) => Promise<string | undefined>;
12
- uploadThumbnail: (args: UploadThumbnailParams) => Promise<void>;
13
- createSavedView: (args: CreateSavedViewParams) => Promise<SavedView>;
14
- updateSavedView: (args: UpdateSavedViewParams) => Promise<SavedView>;
15
- deleteSavedView: (args: DeleteSavedViewParams) => Promise<void>;
16
- createGroup: (args: CreateGroupParams) => Promise<SavedViewGroup>;
17
- updateGroup: (args: UpdateGroupParams) => Promise<SavedViewGroup>;
18
- deleteGroup: (args: DeleteGroupParams) => Promise<void>;
19
- createTag: (args: CreateTagParams) => Promise<SavedViewTag>;
20
- updateTag: (args: UpdateTagParams) => Promise<SavedViewTag>;
21
- deleteTag: (args: DeleteTagParams) => Promise<void>;
22
- }
23
- export interface GetSavedViewInfoParams extends CommonParams {
3
+ getSavedViews: (args: GetSavedViewsArgs) => AsyncIterableIterator<SavedView[]>;
4
+ getGroups: (args: GetGroupsArgs) => Promise<SavedViewGroup[]>;
5
+ getTags: (args: GetTagsArgs) => Promise<SavedViewTag[]>;
6
+ getThumbnailUrl: (args: GetThumbnailUrlArgs) => Promise<string | undefined>;
7
+ uploadThumbnail: (args: UploadThumbnailArgs) => Promise<void>;
8
+ getSavedViewById: (args: GetSavedViewByIdArgs) => Promise<SavedView>;
9
+ getSavedViewDataById: (args: GetSavedViewDataByIdArgs) => Promise<SavedViewData>;
10
+ createSavedView: (args: CreateSavedViewArgs) => Promise<SavedView>;
11
+ updateSavedView: (args: UpdateSavedViewArgs) => Promise<SavedView>;
12
+ deleteSavedView: (args: DeleteSavedViewArgs) => Promise<void>;
13
+ createGroup: (args: CreateGroupArgs) => Promise<SavedViewGroup>;
14
+ updateGroup: (args: UpdateGroupArgs) => Promise<SavedViewGroup>;
15
+ deleteGroup: (args: DeleteGroupArgs) => Promise<void>;
16
+ createTag: (args: CreateTagArgs) => Promise<SavedViewTag>;
17
+ updateTag: (args: UpdateTagArgs) => Promise<SavedViewTag>;
18
+ deleteTag: (args: DeleteTagArgs) => Promise<void>;
19
+ }
20
+ export interface GetSavedViewsArgs extends CommonParams {
24
21
  iTwinId: string;
25
22
  iModelId?: string | undefined;
23
+ groupId?: string | undefined;
26
24
  }
27
- export interface GetSingularSavedViewParams extends CommonParams {
28
- savedViewId: string;
25
+ export interface GetGroupsArgs extends CommonParams {
26
+ iTwinId: string;
27
+ iModelId?: string | undefined;
29
28
  }
30
- export interface GetThumbnailUrlParams extends CommonParams {
29
+ export interface GetTagsArgs extends CommonParams {
30
+ iTwinId: string;
31
+ iModelId?: string | undefined;
32
+ }
33
+ export interface GetThumbnailUrlArgs extends CommonParams {
31
34
  savedViewId: string;
32
35
  }
33
- export interface UploadThumbnailParams extends CommonParams {
36
+ export interface UploadThumbnailArgs extends CommonParams {
34
37
  savedViewId: string;
35
- /** Image data encoded as base64 data URL. */
38
+ /**
39
+ * Image data encoded as base64 data URL.
40
+ *
41
+ * @example
42
+ * "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVQYV2NgYAAAAAMAAWgmWQ0AAAAASUVORK5CYII="
43
+ */
36
44
  image: string;
37
45
  }
38
- export interface CreateSavedViewParams extends CommonParams {
46
+ export interface GetSavedViewByIdArgs extends CommonParams {
47
+ savedViewId: string;
48
+ }
49
+ export interface GetSavedViewDataByIdArgs extends CommonParams {
50
+ savedViewId: string;
51
+ }
52
+ export interface CreateSavedViewArgs extends CommonParams {
39
53
  iTwinId: string;
40
54
  iModelId?: string | undefined;
41
- savedView: Pick<SavedView, "displayName" | "tagIds" | "groupId" | "shared">;
42
- savedViewData: ViewData;
43
- extensions?: ExtensionSavedViewCreate[] | undefined;
55
+ displayName: string;
56
+ groupId?: string | undefined;
57
+ tagIds?: string[] | undefined;
58
+ shared?: boolean | undefined;
59
+ savedViewData: SavedViewData;
44
60
  }
45
- export interface UpdateSavedViewParams extends CommonParams {
46
- savedView: Pick<SavedView, "id"> & Partial<SavedView>;
47
- savedViewData?: ViewData | undefined;
48
- extensions?: ExtensionMin[] | undefined;
61
+ export interface UpdateSavedViewArgs extends CommonParams {
62
+ savedViewId: string;
63
+ displayName?: string | undefined;
64
+ groupId?: string | undefined;
65
+ tagIds?: string[] | undefined;
66
+ shared?: boolean | undefined;
67
+ savedViewData?: SavedViewData | undefined;
49
68
  }
50
- export interface DeleteSavedViewParams extends CommonParams {
69
+ export interface DeleteSavedViewArgs extends CommonParams {
51
70
  savedViewId: string;
52
71
  }
53
- export interface CreateGroupParams extends CommonParams {
72
+ export interface CreateGroupArgs extends CommonParams {
54
73
  iTwinId: string;
55
74
  iModelId?: string | undefined;
56
- group: Pick<SavedViewGroup, "displayName" | "shared">;
75
+ displayName: string;
76
+ shared?: boolean | undefined;
57
77
  }
58
- export interface UpdateGroupParams extends CommonParams {
59
- group: Pick<SavedViewGroup, "id"> & Partial<SavedViewGroup>;
78
+ export interface UpdateGroupArgs extends CommonParams {
79
+ groupId: string;
80
+ displayName?: string | undefined;
81
+ shared?: boolean | undefined;
60
82
  }
61
- export interface DeleteGroupParams extends CommonParams {
83
+ export interface DeleteGroupArgs extends CommonParams {
62
84
  groupId: string;
63
85
  }
64
- export interface CreateTagParams extends CommonParams {
86
+ export interface CreateTagArgs extends CommonParams {
65
87
  iTwinId: string;
66
88
  iModelId?: string;
67
89
  displayName: string;
68
90
  }
69
- export interface UpdateTagParams extends CommonParams {
70
- tag: Pick<SavedViewTag, "id"> & Partial<SavedViewTag>;
91
+ export interface UpdateTagArgs extends CommonParams {
92
+ tagId: string;
93
+ displayName?: string | undefined;
71
94
  }
72
- export interface DeleteTagParams extends CommonParams {
95
+ export interface DeleteTagArgs extends CommonParams {
73
96
  tagId: string;
74
97
  }
75
98
  interface CommonParams {
@@ -23,15 +23,14 @@ export function SavedViewGroupTile(props) {
23
23
  },
24
24
  // eslint-disable-next-line react-hooks/exhaustive-deps
25
25
  []);
26
- const dispatchOpenGroup = () => props.onOpen(props.group.id);
27
26
  const savedViewGroupTileContext = useMemo(() => ({ group: props.group, setEditingName }), [props.group]);
28
27
  return (_jsx(SavedViewGroupTileContextProvider, { value: savedViewGroupTileContext, children: _jsx("div", { ref: divRef, children: _jsx(Tile, { className: "svr-folder", variant: "folder", name: _jsx(EditableTileName, { displayName: props.group.displayName, editable: props.editable || editingName, editing: editingName, actions: {
29
28
  onStartEditing: () => setEditingName(true),
30
29
  onEndEditing: (newName) => {
31
30
  setEditingName(false);
32
31
  if (newName !== props.group.displayName) {
33
- props.onRename?.(props.group.id, newName);
32
+ props.onRename?.(props.group.groupId, newName);
34
33
  }
35
34
  },
36
- } }), thumbnail: _jsx(SvgFolder, { className: "iui-thumbnail-icon" }), isActionable: !props.editable && !editingName, moreOptions: (props.options && props.options.length > 0) ? props.options : undefined, onClick: dispatchOpenGroup, children: _jsxs(Text, { isMuted: true, children: [props.numItems, " items"] }) }) }) }));
35
+ } }), thumbnail: _jsx(SvgFolder, { className: "iui-thumbnail-icon" }), isActionable: !props.editable && !editingName, moreOptions: (props.options && props.options.length > 0) ? props.options : undefined, onClick: () => props.onOpen(props.group.groupId), children: _jsxs(Text, { isMuted: true, children: [props.numItems, " items"] }) }) }) }));
37
36
  }
@@ -1,4 +1,3 @@
1
- /// <reference types="react" />
2
1
  import type { SavedViewGroup } from "../../SavedView.js";
3
2
  /** Context that's accessable within `<SavedViewGroupTile />` component. */
4
3
  export interface SavedViewGroupTileContext {
@@ -14,7 +14,7 @@ export const SavedViewGroupTileContextProvider = savedViewGroupTileContext.Provi
14
14
  export function useSavedViewGroupTileContext() {
15
15
  const contextValue = useContext(savedViewGroupTileContext);
16
16
  return contextValue ?? {
17
- group: { id: "SavedViewGroupTileContext_NoContext", displayName: "" },
17
+ group: { groupId: "SavedViewGroupTileContext_NoContext", displayName: "" },
18
18
  setEditingName: () => { },
19
19
  };
20
20
  }
@@ -1,12 +1,13 @@
1
1
  import { type ReactElement, type ReactNode } from "react";
2
2
  import type { SavedView, SavedViewGroup, SavedViewTag } from "../SavedView.js";
3
- import type { SavedViewActions } from "../useSavedViews.js";
3
+ import type { SavedViewsActions } from "../useSavedViews.js";
4
4
  import "./SavedViewsExpandableBlockWidget.css";
5
5
  interface SavedViewsExpandableBlockWidgetProps {
6
6
  savedViews: Map<string, SavedView>;
7
7
  groups: Map<string, SavedViewGroup>;
8
8
  tags: Map<string, SavedViewTag>;
9
- actions?: Partial<SavedViewActions> | undefined;
9
+ thumbnails: Map<string, ReactNode>;
10
+ actions?: Partial<SavedViewsActions> | undefined;
10
11
  editable?: boolean | undefined;
11
12
  options?: ((savedView: SavedView) => (((close: () => void) => ReactElement[]) | ReactElement[])) | undefined;
12
13
  }
@@ -16,8 +17,9 @@ interface SavedViewsGroupProps {
16
17
  savedViews: Map<string, SavedView>;
17
18
  groups: Map<string, SavedViewGroup>;
18
19
  tags: Map<string, SavedViewTag>;
20
+ thumbnails: Map<string, ReactNode>;
19
21
  expanded?: boolean | undefined;
20
- actions?: Partial<SavedViewActions> | undefined;
22
+ actions?: Partial<SavedViewsActions> | undefined;
21
23
  editable?: boolean | undefined;
22
24
  options?: ((savedView: SavedView) => (((close: () => void) => ReactElement[]) | ReactElement[])) | undefined;
23
25
  }
@@ -11,11 +11,11 @@ import { StickyExpandableBlock } from "../StickyExpandableBlock/StickyExpandable
11
11
  import { TileGrid } from "../TileGrid/TileGrid.js";
12
12
  import "./SavedViewsExpandableBlockWidget.css";
13
13
  export function SavedViewsExpandableBlockWidget(props) {
14
- return (_jsxs("div", { className: "svr-saved-views-widget", children: [_jsx(SavedViewsGroup, { group: undefined, savedViews: props.savedViews, actions: props.actions, groups: props.groups, tags: props.tags, editable: props.editable, options: props.options, expanded: true }), [...props.groups.values()].map((group) => _jsx(SavedViewsGroup, { group: group, savedViews: props.savedViews, groups: props.groups, tags: props.tags, actions: props.actions, editable: props.editable, options: props.options }, group.id))] }));
14
+ return (_jsxs("div", { className: "svr-saved-views-widget", children: [_jsx(SavedViewsGroup, { group: undefined, savedViews: props.savedViews, actions: props.actions, groups: props.groups, tags: props.tags, thumbnails: props.thumbnails, editable: props.editable, options: props.options, expanded: true }), [...props.groups.values()].map((group) => _jsx(SavedViewsGroup, { group: group, savedViews: props.savedViews, groups: props.groups, tags: props.tags, thumbnails: props.thumbnails, actions: props.actions, editable: props.editable, options: props.options }, group.groupId))] }));
15
15
  }
16
16
  export function SavedViewsGroup(props) {
17
- const savedViews = [...props.savedViews.values()].filter(({ groupId }) => groupId === props.group?.id);
18
- return (_jsx(BorderlessExpandableBlock, { displayName: props.group?.displayName ?? "Ungrouped", numItems: savedViews.length, expanded: props.expanded, shared: props.group?.shared, editable: props.editable, children: _jsx(TileGrid, { gridItems: savedViews, children: (savedView) => (_jsx(SavedViewTile, { savedView: savedView, tags: props.tags, editable: props.editable, onRename: props.actions?.renameSavedView, options: props.options?.(savedView) }, savedView.id)) }) }));
17
+ const savedViews = [...props.savedViews.values()].filter(({ groupId }) => groupId === props.group?.groupId);
18
+ return (_jsx(BorderlessExpandableBlock, { displayName: props.group?.displayName ?? "Ungrouped", numItems: savedViews.length, expanded: props.expanded, shared: props.group?.shared, editable: props.editable, children: _jsx(TileGrid, { gridItems: savedViews, children: (savedView) => (_jsx(SavedViewTile, { savedView: savedView, thumbnail: props.thumbnails.get(savedView.savedViewId), tags: props.tags, editable: props.editable, onRename: props.actions?.renameSavedView, options: props.options?.(savedView) }, savedView.savedViewId)) }) }));
19
19
  }
20
20
  export function BorderlessExpandableBlock(props) {
21
21
  const handleEditGroupClick = (closeDropdown) => {
@@ -1,11 +1,12 @@
1
- import { type ReactElement } from "react";
2
- import type { SavedView, SavedViewGroup, SavedViewTag } from "../SavedView.js";
3
- import type { SavedViewActions } from "../useSavedViews.js";
1
+ import { type ReactElement, type ReactNode } from "react";
2
+ import type { SavedViewGroup, SavedView, SavedViewTag } from "../SavedView.js";
3
+ import type { SavedViewsActions } from "../useSavedViews.js";
4
4
  interface SavedViewsFolderWidgetProps {
5
5
  savedViews: Map<string, SavedView>;
6
6
  groups: Map<string, SavedViewGroup>;
7
7
  tags: Map<string, SavedViewTag>;
8
- actions?: Partial<SavedViewActions> | undefined;
8
+ thumbnails: Map<string, ReactNode>;
9
+ actions?: Partial<SavedViewsActions> | undefined;
9
10
  editable?: boolean | undefined;
10
11
  options?: ((savedView: SavedView) => (((close: () => void) => ReactElement[]) | ReactElement[])) | undefined;
11
12
  onTileClick?: ((selectedViewId: string) => void) | undefined;
@@ -29,16 +29,16 @@ export function SavedViewsFolderWidget(props) {
29
29
  const handleGroupOpen = useCallback((activeGroupId) => setState({ activeGroupId, focusedGroupId: undefined }), []);
30
30
  const activeGroup = state.activeGroupId !== undefined && props.groups.get(state.activeGroupId);
31
31
  if (!activeGroup) {
32
- return (_jsx(SavedViewsHomeScreen, { groupedSavedViews: groupedSavedViews, groups: props.groups, tags: props.tags, focusedGroupId: state.focusedGroupId, clearFocusedGroup: () => setState(({ activeGroupId }) => ({ activeGroupId, focusedGroupId: undefined })), onGroupOpen: handleGroupOpen, initialScrollTop: storedScrollOffset, storeScrollOffset: setStoredScrollOffset, actions: props.actions, editable: props.editable, savedViewOptions: props.options, onTileClick: props.onTileClick }));
32
+ return (_jsx(SavedViewsHomeScreen, { groupedSavedViews: groupedSavedViews, groups: props.groups, tags: props.tags, thumbnails: props.thumbnails, focusedGroupId: state.focusedGroupId, clearFocusedGroup: () => setState(({ activeGroupId }) => ({ activeGroupId, focusedGroupId: undefined })), onGroupOpen: handleGroupOpen, initialScrollTop: storedScrollOffset, storeScrollOffset: setStoredScrollOffset, actions: props.actions, editable: props.editable, savedViewOptions: props.options, onTileClick: props.onTileClick }));
33
33
  }
34
- return (_jsx(SavedViewsGroupScreen, { activeGroup: activeGroup, groups: props.groups, tags: props.tags, savedViews: groupedSavedViews.get(activeGroup.id) ?? [], setActiveGroup: (activeGroupId) => setState({ activeGroupId, focusedGroupId: state.activeGroupId }), actions: props.actions, editable: props.editable, options: props.options, onTileClick: props.onTileClick }, activeGroup.id));
34
+ return (_jsx(SavedViewsGroupScreen, { activeGroup: activeGroup, groups: props.groups, tags: props.tags, thumbnails: props.thumbnails, savedViews: groupedSavedViews.get(activeGroup.groupId) ?? [], setActiveGroup: (activeGroupId) => setState({ activeGroupId, focusedGroupId: state.activeGroupId }), actions: props.actions, editable: props.editable, options: props.options, onTileClick: props.onTileClick }, activeGroup.groupId));
35
35
  }
36
36
  function SavedViewsHomeScreen(props) {
37
37
  const ungroupedSavedViews = props.groupedSavedViews.get(undefined) ?? [];
38
- const groupTiles = useMemo(() => [...props.groups.values()].map((group) => _jsx(SavedViewGroupTile, { group: group, numItems: props.groupedSavedViews.get(group.id)?.length ?? 0, onOpen: props.onGroupOpen, focused: group.id === props.focusedGroupId, initialScrollTop: props.initialScrollTop, editable: props.editable, options: [
38
+ const groupTiles = useMemo(() => [...props.groups.values()].map((group) => _jsx(SavedViewGroupTile, { group: group, numItems: props.groupedSavedViews.get(group.groupId)?.length ?? 0, onOpen: props.onGroupOpen, focused: group.groupId === props.focusedGroupId, initialScrollTop: props.initialScrollTop, editable: props.editable, options: [
39
39
  _jsx(SavedViewGroupOptions.Rename, {}, "rename"),
40
- _jsx(MenuItem, { onClick: () => props.actions?.deleteGroup?.(group.id), children: "Delete" }, "delete"),
41
- ], onRename: props.actions?.renameGroup }, group.id)), [
40
+ _jsx(MenuItem, { onClick: () => props.actions?.deleteGroup?.(group.groupId), children: "Delete" }, "delete"),
41
+ ], onRename: props.actions?.renameGroup }, group.groupId)), [
42
42
  props.groups,
43
43
  props.groupedSavedViews,
44
44
  props.onGroupOpen,
@@ -47,7 +47,7 @@ function SavedViewsHomeScreen(props) {
47
47
  props.editable,
48
48
  props.actions,
49
49
  ]);
50
- return (_jsxs("div", { className: "svr-saved-views-widget", style: { overflow: "auto" }, onScroll: (event) => props.storeScrollOffset(event.target.scrollTop), children: [_jsx(BorderlessExpandableBlock, { displayName: "Saved views", numItems: ungroupedSavedViews.length, expanded: true, children: _jsx(TileGrid, { gridItems: ungroupedSavedViews, children: (savedView) => _jsx(SavedViewTile, { savedView: savedView, tags: props.tags, editable: props.editable, onRename: props.actions?.renameSavedView, options: props.savedViewOptions?.(savedView), onClick: props.onTileClick }, savedView.id) }) }), _jsx(BorderlessExpandableBlock, { className: "svr-group-grid", displayName: "Groups", numItems: props.groups.size, expanded: true, onExpandToggle: (expanded) => !expanded && props.clearFocusedGroup?.(), editable: props.editable, children: _jsx("div", { className: "svr-tile-grid", children: groupTiles }) })] }));
50
+ return (_jsxs("div", { className: "svr-saved-views-widget", style: { overflow: "auto" }, onScroll: (event) => props.storeScrollOffset(event.target.scrollTop), children: [_jsx(BorderlessExpandableBlock, { displayName: "Saved views", numItems: ungroupedSavedViews.length, expanded: true, children: _jsx(TileGrid, { gridItems: ungroupedSavedViews, children: (savedView) => _jsx(SavedViewTile, { savedView: savedView, thumbnail: props.thumbnails.get(savedView.savedViewId), tags: props.tags, editable: props.editable, onRename: props.actions?.renameSavedView, options: props.savedViewOptions?.(savedView), onClick: props.onTileClick }, savedView.savedViewId) }) }), _jsx(BorderlessExpandableBlock, { className: "svr-group-grid", displayName: "Groups", numItems: props.groups.size, expanded: true, onExpandToggle: (expanded) => !expanded && props.clearFocusedGroup?.(), editable: props.editable, children: _jsx("div", { className: "svr-tile-grid", children: groupTiles }) })] }));
51
51
  }
52
52
  function SavedViewsGroupScreen(props) {
53
53
  const groups = useMemo(() => [...props.groups.values()], [props.groups]);
@@ -56,5 +56,5 @@ function SavedViewsGroupScreen(props) {
56
56
  gap: "var(--iui-size-s)",
57
57
  background: "var(--iui-color-background)",
58
58
  padding: "var(--iui-size-s)",
59
- }, children: [_jsx(Button, { styleType: "borderless", startIcon: _jsx(SvgChevronLeft, {}), onClick: () => props.setActiveGroup(undefined), children: "Back" }), _jsxs(Breadcrumbs, { children: [_jsx(IconButton, { styleType: "borderless", onClick: () => props.setActiveGroup(undefined), children: _jsx(SvgHome, {}) }), _jsx(DropdownButton, { styleType: "borderless", menuItems: (close) => groups.map((group) => _jsx(MenuItem, { onClick: () => { close(); props.setActiveGroup(group.id); }, children: group.displayName }, group.id)), children: props.activeGroup.displayName })] })] }), _jsx("div", { style: { overflow: "auto" }, children: _jsx(TileGrid, { gridItems: props.savedViews, children: (savedView) => (_jsx(SavedViewTile, { savedView: savedView, tags: props.tags, editable: props.editable, options: props.options?.(savedView), onClick: props.onTileClick }, savedView.id)) }) })] }));
59
+ }, children: [_jsx(Button, { styleType: "borderless", startIcon: _jsx(SvgChevronLeft, {}), onClick: () => props.setActiveGroup(undefined), children: "Back" }), _jsxs(Breadcrumbs, { children: [_jsx(IconButton, { styleType: "borderless", onClick: () => props.setActiveGroup(undefined), children: _jsx(SvgHome, {}) }), _jsx(DropdownButton, { styleType: "borderless", menuItems: (close) => groups.map((group) => _jsx(MenuItem, { onClick: () => { close(); props.setActiveGroup(group.groupId); }, children: group.displayName }, group.groupId)), children: props.activeGroup.displayName })] })] }), _jsx("div", { style: { overflow: "auto" }, children: _jsx(TileGrid, { gridItems: props.savedViews, children: (savedView) => (_jsx(SavedViewTile, { savedView: savedView, thumbnail: props.thumbnails.get(savedView.savedViewId), tags: props.tags, editable: props.editable, options: props.options?.(savedView), onClick: props.onTileClick }, savedView.savedViewId)) }) })] }));
60
60
  }
@@ -1,64 +1,69 @@
1
- import { ViewState, type IModelConnection, type Viewport } from "@itwin/core-frontend";
2
- import { type SavedViewRepresentation } from "@itwin/saved-views-client";
1
+ import { ViewChangeOptions, ViewPose, ViewState, type IModelConnection, type Viewport } from "@itwin/core-frontend";
2
+ import type { SavedViewData } from "./SavedView.js";
3
3
  export interface ApplySavedViewSettings {
4
4
  /**
5
- * Strategy to use when other setting does not specify one. By default, viewport is reset to default state and then
6
- * all captured Saved View data is applied on top.
7
- * @default "apply"
5
+ * How to make use of captured {@link ViewState} data. The default behavior is
6
+ * to generate a new `ViewState` object out of {@linkcode SavedViewData.viewData}
7
+ * and apply it to viewport.
8
8
  *
9
- * @example
10
- * await applySavedView(iModel, viewport, savedView, { all: "keep", viewState: "apply" });
11
- */
12
- all?: ApplyStrategy | undefined;
13
- /**
14
- * How to handle captured {@link ViewState} data. The default behavior is to generate a new `ViewState` object and
15
- * apply it to viewport.
9
+ * You can optionally provide a pre-made `ViewState` instance.
16
10
  *
17
- * You can optionally provide a pre-made `ViewState` instance to conserve resources. It is usually obtained from
18
- * {@link createViewState} result.
11
+ * @default "apply"
19
12
  *
20
13
  * @example
21
14
  * import { applySavedView, createViewState } from "@itwin/saved-views-react";
22
15
  *
23
- * const viewState = await createViewState(iModel, savedView);
16
+ * const viewState = await createViewState(iModel, savedViewData);
24
17
  * await Promise.all([
25
18
  * applySavedView(iModel, viewport1, savedView, { viewState }),
26
19
  * applySavedView(iModel, viewport2, savedView, { viewState }),
27
20
  * ]);
28
21
  */
29
- viewState?: Exclude<ApplyStrategy, "reset"> | ViewState | undefined;
22
+ viewState?: ApplyStrategy | ViewState | undefined;
23
+ /**
24
+ * How to handle camera pose in captured data.
25
+ * @default "apply"
26
+ */
27
+ camera?: ApplyStrategy | ViewPose | undefined;
30
28
  /**
31
- * How to handle visibility of models and categories that exist in iModel but are not captured in Saved View data. Has
32
- * effect only when `modelAndCategoryVisibility` strategy is set to `"apply"`.
29
+ * How to handle element emphasis in captured data.
30
+ * @default "apply"
31
+ */
32
+ emphasis?: ApplyStrategy | "clear" | undefined;
33
+ /**
34
+ * How to handle captured {@link Viewport.perModelCategoryVisibility} data.
35
+ * @default "apply"
36
+ */
37
+ perModelCategoryVisibility?: ApplyStrategy | "clear" | undefined;
38
+ /**
39
+ * How to handle visibility of models and categories that exist in iModel but
40
+ * are not captured in Saved View data.
33
41
  * @default "hidden"
34
42
  */
35
43
  modelAndCategoryVisibilityFallback?: "visible" | "hidden" | undefined;
36
- /** How to handle captured element emphasis data. In default state emphasis is turned off. */
37
- emphasis?: ApplyStrategy | undefined;
38
44
  /**
39
- * How to handle captured {@link Viewport.perModelCategoryVisibility} data. In default state no overrides are present.
45
+ * Options forwarded to {@link Viewport.changeView}.
46
+ * @default undefined
40
47
  */
41
- perModelCategoryVisibility?: ApplyStrategy | undefined;
48
+ viewChangeOptions?: ViewChangeOptions | undefined;
42
49
  }
43
50
  /**
44
51
  * Controls how viewport state is going to be altered.
45
- *
46
- * * `"apply"` – Apply captured Saved View state. Falls back to `"reset"` on failure (e.g. missing Saved View data).
47
- * * `"reset"` – Reset to the default viewport state
48
- * * `"keep"` – Keep the current viewport state
52
+ * * `"apply"` – Apply captured Saved View state to viewport
53
+ * * `"keep"` – Preserve current viewport state
49
54
  */
50
- type ApplyStrategy = "apply" | "reset" | "keep";
55
+ type ApplyStrategy = "apply" | "keep";
51
56
  /**
52
57
  * Updates {@linkcode viewport} state to match captured Saved View.
53
58
  *
54
59
  * @example
55
60
  * // Optionally, you can create and manage ViewState object yourself to avoid redundant work,
56
61
  * // e.g. when applying the same Saved View to multiple viewports
57
- * const viewState = await createViewState(iModel, savedView);
62
+ * const viewState = await createViewState(iModel, savedViewData);
58
63
  * await Promise.all([
59
- * applySavedView(iModel, firstViewport, savedView, { viewState }),
60
- * applySavedView(iModel, secondViewport, savedView, { viewState }),
64
+ * applySavedView(iModel, viewport1, savedView, { viewState }),
65
+ * applySavedView(iModel, viewport2, savedView, { viewState }),
61
66
  * ]);
62
67
  */
63
- export declare function applySavedView(iModel: IModelConnection, viewport: Viewport, savedView: Pick<SavedViewRepresentation, "savedViewData" | "extensions">, settings?: ApplySavedViewSettings | undefined): Promise<void>;
68
+ export declare function applySavedView(iModel: IModelConnection, viewport: Viewport, savedViewData: SavedViewData, settings?: ApplySavedViewSettings | undefined): Promise<void>;
64
69
  export {};
@@ -2,7 +2,7 @@
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 { ViewState } from "@itwin/core-frontend";
5
+ import { ViewPose, ViewState } from "@itwin/core-frontend";
6
6
  import { createViewState } from "./createViewState.js";
7
7
  import { extensionHandlers } from "./translation/SavedViewsExtensionHandlers.js";
8
8
  /**
@@ -11,37 +11,72 @@ import { extensionHandlers } from "./translation/SavedViewsExtensionHandlers.js"
11
11
  * @example
12
12
  * // Optionally, you can create and manage ViewState object yourself to avoid redundant work,
13
13
  * // e.g. when applying the same Saved View to multiple viewports
14
- * const viewState = await createViewState(iModel, savedView);
14
+ * const viewState = await createViewState(iModel, savedViewData);
15
15
  * await Promise.all([
16
- * applySavedView(iModel, firstViewport, savedView, { viewState }),
17
- * applySavedView(iModel, secondViewport, savedView, { viewState }),
16
+ * applySavedView(iModel, viewport1, savedView, { viewState }),
17
+ * applySavedView(iModel, viewport2, savedView, { viewState }),
18
18
  * ]);
19
19
  */
20
- export async function applySavedView(iModel, viewport, savedView, settings = {}) {
21
- const defaultStrategy = settings.all ?? "apply";
22
- if ((settings.viewState ?? defaultStrategy) !== "keep") {
23
- if (settings.viewState instanceof ViewState) {
24
- viewport.changeView(settings.viewState);
20
+ export async function applySavedView(iModel, viewport, savedViewData, settings = {}) {
21
+ if (settings.viewState !== "keep") {
22
+ // We use "hidden" as the default value for modelAndCategoryVisibilityFallback
23
+ // because users expect modelSelector.enabled and categorySelector.enabled to
24
+ // act as exclusive whitelists when modelSelector.disabled or categorySelector.disabled
25
+ // arrays are empty, respectively.
26
+ const { modelAndCategoryVisibilityFallback = "hidden" } = settings;
27
+ const viewState = settings.viewState instanceof ViewState
28
+ ? settings.viewState
29
+ : await createViewState(iModel, savedViewData.viewData, { modelAndCategoryVisibilityFallback });
30
+ if (settings.camera instanceof ViewPose) {
31
+ viewState.applyPose(settings.camera);
25
32
  }
26
- else {
27
- const { modelAndCategoryVisibilityFallback } = settings;
28
- const viewState = await createViewState(iModel, savedView.savedViewData, { modelAndCategoryVisibilityFallback });
29
- viewport.changeView(viewState);
33
+ else if (settings.camera === "keep") {
34
+ viewState.applyPose(viewport.view.savePose());
30
35
  }
36
+ viewport.changeView(viewState, settings.viewChangeOptions);
31
37
  }
32
- const extensions = new Map(savedView.extensions?.map(({ extensionName, data }) => [extensionName, data]));
33
- const processExtension = (extensionHandler, strategy = defaultStrategy) => {
34
- if (strategy === "keep") {
35
- return;
36
- }
37
- extensionHandler.reset(viewport);
38
- if (strategy === "apply") {
39
- const extensionData = extensions.get(extensionHandler.extensionName);
40
- if (extensionData) {
41
- extensionHandler.apply(extensionData, viewport);
42
- }
38
+ const extensions = findKnownExtensions(savedViewData.extensions ?? []);
39
+ if (extensions.emphasis) {
40
+ if (settings.emphasis !== "keep") {
41
+ extensionHandlers.emphasizeElements.reset(viewport);
42
+ }
43
+ if (settings.emphasis === "apply") {
44
+ extensionHandlers.emphasizeElements.apply(extensions.emphasis, viewport);
45
+ }
46
+ }
47
+ if (extensions.perModelCategoryVisibility) {
48
+ if (settings.perModelCategoryVisibility !== "keep") {
49
+ extensionHandlers.perModelCategoryVisibility.reset(viewport);
43
50
  }
51
+ if (settings.perModelCategoryVisibility === "apply") {
52
+ extensionHandlers.perModelCategoryVisibility.apply(extensions.perModelCategoryVisibility, viewport);
53
+ }
54
+ }
55
+ }
56
+ /** Finds first occurences of known extensions. */
57
+ function findKnownExtensions(extensions) {
58
+ const result = {
59
+ emphasis: undefined,
60
+ perModelCategoryVisibility: undefined,
44
61
  };
45
- processExtension(extensionHandlers.emphasizeElements, settings.emphasis);
46
- processExtension(extensionHandlers.perModelCategoryVisibility, settings.perModelCategoryVisibility);
62
+ for (const extension of extensions) {
63
+ if (result.emphasis === undefined &&
64
+ extension.extensionName === extensionHandlers.emphasizeElements.extensionName) {
65
+ result.emphasis = extension.data;
66
+ if (result.perModelCategoryVisibility) {
67
+ break;
68
+ }
69
+ }
70
+ if (result.perModelCategoryVisibility === undefined &&
71
+ extension.extensionName === extensionHandlers.perModelCategoryVisibility.extensionName) {
72
+ result.perModelCategoryVisibility = extension.data;
73
+ if (result.emphasis) {
74
+ break;
75
+ }
76
+ }
77
+ if (result.emphasis && result.perModelCategoryVisibility) {
78
+ break;
79
+ }
80
+ }
81
+ return result;
47
82
  }
@@ -1,11 +1,37 @@
1
1
  import { Id64Array } from "@itwin/core-bentley";
2
- import { type IModelConnection, type Viewport } from "@itwin/core-frontend";
3
- import { type ViewData } from "@itwin/saved-views-client";
2
+ import type { IModelConnection, Viewport } from "@itwin/core-frontend";
3
+ import type { SavedViewData } from "./SavedView.js";
4
4
  interface CaptureSavedViewDataArgs {
5
5
  /** Viewport to capture. */
6
6
  viewport: Viewport;
7
+ /**
8
+ * Whether function will skip capturing element emphasis state.
9
+ * @default false
10
+ */
11
+ omitEmphasis?: boolean | undefined;
12
+ /**
13
+ * Whether function will skip capturing `viewport.perModelCategoryVisibility` state.
14
+ * @default false
15
+ */
16
+ omitPerModelCategoryVisibility?: boolean | undefined;
7
17
  }
8
- export declare function captureSavedViewData(args: CaptureSavedViewDataArgs): Promise<ViewData>;
9
- export declare function getMissingModels(iModel: IModelConnection, knownModels: Set<string>): Promise<string[]>;
10
- export declare function getMissingCategories(iModel: IModelConnection, knownCategories: Set<string>): Promise<Id64Array>;
18
+ /**
19
+ * Captures current {@link Viewport} state into serializable format. The returned
20
+ * data can later be used to restore viewport's view.
21
+ *
22
+ * @example
23
+ * import { captureSavedViewData, captureSavedViewThumbnail } from "@itwin/saved-views-react";
24
+ *
25
+ * async function saveViewport(viewport) {
26
+ * const { viewData, extensions = [] } = await captureSavedViewData({ viewport });
27
+ * const myExtensions = captureMyCustomViewportState(viewport);
28
+ * const thumbnail = captureSavedViewThumbnail(viewport);
29
+ * return { thumbnail, viewData, extensions: extensions.concat(myExtensions) };
30
+ * }
31
+ */
32
+ export declare function captureSavedViewData(args: CaptureSavedViewDataArgs): Promise<SavedViewData>;
33
+ export declare function queryMissingModels(iModel: IModelConnection, knownModels: Set<string>): Promise<string[]>;
34
+ export declare function queryAllSpatiallyLocatedModels(iModel: IModelConnection): Promise<string[]>;
35
+ export declare function queryMissingCategories(iModel: IModelConnection, knownCategories: Set<string>): Promise<Id64Array>;
36
+ export declare function queryAllCategories(iModel: IModelConnection): Promise<string[]>;
11
37
  export {};