@itwin/saved-views-react 0.9.8 → 1.1.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.
@@ -1,5 +1,6 @@
1
1
  import { ViewChangeOptions, ViewPose, ViewState, type IModelConnection, type Viewport } from "@itwin/core-frontend";
2
2
  import type { SavedViewData } from "./SavedView.js";
3
+ import { ShowStrategy, type ApplyVisibilitySettings } from "./createViewState.js";
3
4
  import { type DefaultExtensionHandlersApplyOverrides } from "./translation/SavedViewsExtensionHandlers.js";
4
5
  export interface ApplySavedViewSettings {
5
6
  /**
@@ -37,11 +38,22 @@ export interface ApplySavedViewSettings {
37
38
  */
38
39
  perModelCategoryVisibility?: ApplyStrategy | "clear" | undefined;
39
40
  /**
40
- * How to handle visibility of models and categories that exist in iModel but
41
- * are not captured in Saved View data.
42
- * @default "hidden"
41
+ * How to handle the visibility of models that exist in iModel,
42
+ * including those not captured in Saved View data.
43
+ * @default '{ enabled: "ignore", disabled: "ignore", other: "ignore" }'
43
44
  */
44
- modelAndCategoryVisibilityFallback?: "visible" | "hidden" | undefined;
45
+ models?: ApplyVisibilitySettings | undefined;
46
+ /**
47
+ * How to handle the visibility of categories that exist in iModel,
48
+ * including those not captured in Saved View data.
49
+ * @default '{ enabled: "ignore", disabled: "ignore", other: "ignore" }'
50
+ */
51
+ categories?: ApplyVisibilitySettings | undefined;
52
+ /**
53
+ * How to handle the visibility of subcategories that exist in iModel.
54
+ * @default "reset"
55
+ */
56
+ subcategories?: ShowStrategy | undefined;
45
57
  /**
46
58
  * Options forwarded to {@link Viewport.changeView}.
47
59
  * @default undefined
@@ -5,7 +5,7 @@
5
5
  import { Camera, } from "@itwin/core-common";
6
6
  import { ViewPose, ViewPose2d, ViewPose3d, ViewState, } from "@itwin/core-frontend";
7
7
  import { YawPitchRollAngles } from "@itwin/core-geometry";
8
- import { createViewStateFromProps, createViewStateProps, } from "./createViewState.js";
8
+ import { createViewStateFromProps, createViewStateProps, sortCategories, } from "./createViewState.js";
9
9
  import { extensionHandlers, } from "./translation/SavedViewsExtensionHandlers.js";
10
10
  async function createSeedViewStateProps(iModel, viewport, savedViewData, settings = {}) {
11
11
  if (settings.viewState !== "keep") {
@@ -61,15 +61,33 @@ function applyCameraOptions(seedViewStateProps, _iModel, viewport, savedViewData
61
61
  return seedViewStateProps;
62
62
  }
63
63
  async function applyViewStateProps(viewStateProps, iModel, viewport, savedViewData, settings) {
64
- // We use "hidden" as the default value for modelAndCategoryVisibilityFallback
65
- // because users expect modelSelector.enabled and categorySelector.enabled to
66
- // act as exclusive whitelists when modelSelector.disabled or categorySelector.disabled
67
- // arrays are empty, respectively.
68
- const { modelAndCategoryVisibilityFallback = "hidden" } = settings;
64
+ // We use {enabled: "ignore", disabled: "ignore", other: "ignore"} as the default values
65
+ // for models and categories because users expect modelSelector.enabled and
66
+ // categorySelector.enabled to act as exclusive whitelists when modelSelector.disabled
67
+ // or categorySelector.disabled arrays are empty, respectively.
68
+ const models = {
69
+ enabled: settings.models?.enabled ?? "ignore",
70
+ disabled: settings.models?.disabled ?? "ignore",
71
+ other: settings.models?.other ?? "ignore",
72
+ };
73
+ const categories = {
74
+ enabled: settings.categories?.enabled ?? "ignore",
75
+ disabled: settings.categories?.disabled ?? "ignore",
76
+ other: settings.categories?.other ?? "ignore",
77
+ };
69
78
  const viewState = await createViewStateFromProps(viewStateProps, iModel, savedViewData.viewData, {
70
- modelAndCategoryVisibilityFallback,
79
+ models,
80
+ categories,
81
+ subcategories: settings.subcategories ?? "ignore",
71
82
  });
72
83
  viewport.changeView(viewState, settings.viewChangeOptions);
84
+ if (settings.subcategories !== "ignore") {
85
+ // If subcategories are not ignored, we need to reset them here,
86
+ // otherwise we might end up with subcategories not shown correctly
87
+ const { addCategories, dropCategories } = await sortCategories(iModel, savedViewData.viewData, categories);
88
+ viewport.changeCategoryDisplay(dropCategories, false);
89
+ viewport.changeCategoryDisplay(addCategories, true, settings.subcategories === "show");
90
+ }
73
91
  }
74
92
  /**
75
93
  * Updates {@linkcode viewport} state to match captured Saved View.
@@ -35,8 +35,8 @@ interface CaptureSavedViewDataArgs {
35
35
  * }
36
36
  */
37
37
  export declare function captureSavedViewData(args: CaptureSavedViewDataArgs): Promise<SavedViewData>;
38
- export declare function queryMissingModels(iModel: IModelConnection, knownModels: Set<string>): Promise<string[]>;
38
+ export declare function queryMissingModels(iModel: IModelConnection, knownModels?: Set<string>): Promise<string[]>;
39
39
  export declare function queryAllSpatiallyLocatedModels(iModel: IModelConnection): Promise<string[]>;
40
- export declare function queryMissingCategories(iModel: IModelConnection, knownCategories: Set<string>): Promise<Id64Array>;
40
+ export declare function queryMissingCategories(iModel: IModelConnection, knownCategories?: Set<string>): Promise<Id64Array>;
41
41
  export declare function queryAllCategories(iModel: IModelConnection): Promise<string[]>;
42
42
  export {};
@@ -161,6 +161,9 @@ export async function queryMissingModels(iModel, knownModels) {
161
161
  return [];
162
162
  }
163
163
  const allModels = await queryAllSpatiallyLocatedModels(iModel);
164
+ if (!knownModels || knownModels.size === 0) {
165
+ return allModels;
166
+ }
164
167
  return allModels.filter((modelId) => !knownModels.has(modelId));
165
168
  }
166
169
  export async function queryAllSpatiallyLocatedModels(iModel) {
@@ -181,6 +184,9 @@ export async function queryMissingCategories(iModel, knownCategories) {
181
184
  return [];
182
185
  }
183
186
  const allCategories = await queryAllCategories(iModel);
187
+ if (!knownCategories || knownCategories.size === 0) {
188
+ return allCategories;
189
+ }
184
190
  return allCategories.filter((categoryId) => !knownCategories.has(categoryId));
185
191
  }
186
192
  export async function queryAllCategories(iModel) {
@@ -1,6 +1,27 @@
1
1
  import { type ViewStateProps } from "@itwin/core-common";
2
2
  import { type IModelConnection, type ViewState } from "@itwin/core-frontend";
3
3
  import type { ViewData } from "./SavedView.js";
4
+ import { Id64Array } from "@itwin/core-bentley";
5
+ /**
6
+ * Settings for how to handle the visibility of elements (models or categories) in iModel.
7
+ * * `enabled` – Enabled is the set of enabled elements (models or categories) that are stored in the saved view. Default is ignore.
8
+ * * `disabled` – Disabled is the set of disabled elements (models or categories) that are stored in the saved view. Default is ignore.
9
+ * * `other` – Other is the set of elements (models or categories) that are not stored in the saved view. Default is ignore.
10
+ *
11
+ * @default '{ enabled: "ignore", disabled: "ignore", other: "ignore" }'
12
+ */
13
+ export interface ApplyVisibilitySettings {
14
+ enabled?: ShowStrategy | undefined;
15
+ disabled?: ShowStrategy | undefined;
16
+ other?: ShowStrategy | undefined;
17
+ }
18
+ /**
19
+ * Controls how models or categories are going to be altered.
20
+ * * `"show"` – Show the elements in this list
21
+ * * `"hide"` – Hide (ie do not show) the elements in this list
22
+ * * `"ignore"` – Preserve current elements that are shown in viewport
23
+ */
24
+ export type ShowStrategy = "show" | "hide" | "ignore";
4
25
  export interface ViewStateCreateSettings {
5
26
  /**
6
27
  * Normally {@link createViewState} function invokes and awaits {@linkcode ViewState.load}
@@ -14,11 +35,30 @@ export interface ViewStateCreateSettings {
14
35
  */
15
36
  skipViewStateLoad?: boolean | undefined;
16
37
  /**
17
- * How to handle visibility of models and categories that exist in iModel but
18
- * not captured in Saved View data.
19
- * @default "hidden"
38
+ * How to handle the visibility of models that exist in iModel,
39
+ * including those not captured in Saved View data.
40
+ * Settings for how to handle the visibility of models in iModel.
41
+ * * `enabled` – Enabled is the set of enabled models that are stored in the saved view. Default is ignore.
42
+ * * `disabled` – Disabled is the set of disabled models that are stored in the saved view. Default is ignore.
43
+ * * `other` – Other is the set of models that are not stored in the saved view. Default is ignore.
44
+ * @default '{ enabled: "ignore", disabled: "ignore", other: "ignore" }'
20
45
  */
21
- modelAndCategoryVisibilityFallback?: "visible" | "hidden" | undefined;
46
+ models?: ApplyVisibilitySettings | undefined;
47
+ /**
48
+ * How to handle the visibility of categories that exist in iModel,
49
+ * including those not captured in Saved View data.
50
+ * Settings for how to handle the visibility of categories in iModel.
51
+ * * `enabled` – Enabled is the set of enabled categories that are stored in the saved view. Default is ignore.
52
+ * * `disabled` – Disabled is the set of disabled categories that are stored in the saved view. Default is ignore.
53
+ * * `other` – Other is the set of categories that are not stored in the saved view. Default is ignore.
54
+ * @default '{ enabled: "ignore", disabled: "ignore", other: "ignore" }'
55
+ */
56
+ categories?: ApplyVisibilitySettings | undefined;
57
+ /**
58
+ * How to handle the visibility of subcategories that exist in iModel.
59
+ * @default "reset"
60
+ */
61
+ subcategories?: ShowStrategy | undefined;
22
62
  }
23
63
  /**
24
64
  * Creates {@link ViewStateProps} object out of Saved View data. It provides a lower-level
@@ -60,3 +100,15 @@ export declare function createViewStateFromProps(props: ViewStateProps, iModel:
60
100
  * await applySavedView(iModel, viewport, savedViewData);
61
101
  */
62
102
  export declare function createViewState(iModel: IModelConnection, viewData: ViewData, settings?: ViewStateCreateSettings): Promise<ViewState>;
103
+ /**
104
+ * Determine, out of all categories in the iModel, which ones should be added or dropped
105
+ * (ie visible or hidden) based on the provided settings.
106
+ * @param iModel The current IModelConnection.
107
+ * @param viewData The view data containing the lists of enabled and disabled categories that will be applied.
108
+ * @param settings The settings for how to handle the visibility of enabled, disabled, and other category lists. Default is 'ignore' for all.
109
+ * @returns A list of categories that should be added or dropped (ie visible or hidden).
110
+ */
111
+ export declare function sortCategories(iModel: IModelConnection, viewData: ViewData, settings?: ApplyVisibilitySettings): Promise<{
112
+ addCategories: Id64Array;
113
+ dropCategories: Id64Array;
114
+ }>;
@@ -25,8 +25,10 @@ export async function createViewStateProps(iModel, viewData) {
25
25
  return viewStateProps;
26
26
  }
27
27
  async function applyViewStateOptions(viewState, iModel, viewData, settings = {}) {
28
- if (settings.modelAndCategoryVisibilityFallback === "visible") {
29
- await unhideNewModelsAndCategories(iModel, viewState, viewData);
28
+ await applyModelSettings(iModel, viewState, viewData, settings.models);
29
+ if (settings.subcategories === "ignore") {
30
+ // If subcategories are ignored, we apply category settings here instead of on the viewport.
31
+ await applyCategorySettings(iModel, viewState, viewData, settings.categories);
30
32
  }
31
33
  if (!settings.skipViewStateLoad) {
32
34
  await viewState.load();
@@ -300,27 +302,112 @@ async function getDefaultViewIdFromClassName(iModel, viewClassName) {
300
302
  function cloneCode({ spec, scope, value }) {
301
303
  return { spec, scope, value };
302
304
  }
303
- async function unhideNewModelsAndCategories(iModel, viewState, viewData) {
305
+ /**
306
+ * Apply the model settings to the view state.
307
+ * This function modifies the model selector of the view state based on the provided settings.
308
+ * @param iModel The current IModelConnection.
309
+ * @param viewState The view state to modify.
310
+ * @param viewData The view data containing the lists of enabled and disabled models that will be applied.
311
+ * @param settings The settings for how to handle the visibility of enabled, disabled, and other model lists. Default is 'ignore' for all.
312
+ * @returns A promise that resolves when the model settings have been applied.
313
+ */
314
+ async function applyModelSettings(iModel, viewState, viewData, settings) {
304
315
  if (viewData.type === "iTwin3d") {
305
316
  if (!viewState.isSpatialView()) {
306
317
  return;
307
318
  }
308
- if (!viewData.categories?.disabled || !viewData.models?.disabled) {
319
+ const addModels = [];
320
+ const dropModels = [];
321
+ if (settings?.enabled === "show") {
322
+ addModels.push(...(viewData.models?.enabled ?? []));
323
+ }
324
+ else if (settings?.enabled === "hide") {
325
+ dropModels.push(...(viewData.models?.enabled ?? []));
326
+ }
327
+ if (settings?.disabled === "show") {
328
+ addModels.push(...(viewData.models?.disabled ?? []));
329
+ }
330
+ else if (settings?.disabled === "hide") {
331
+ dropModels.push(...(viewData.models?.disabled ?? []));
332
+ }
333
+ if (settings?.other !== "ignore") {
334
+ const otherModels = await queryMissingModels(iModel, new Set([
335
+ ...(viewData.models?.disabled ?? []),
336
+ ...(viewData.models?.enabled ?? []),
337
+ ]));
338
+ if (settings?.other === "show") {
339
+ addModels.push(...otherModels);
340
+ }
341
+ else if (settings?.other === "hide") {
342
+ dropModels.push(...otherModels);
343
+ }
344
+ }
345
+ if (addModels.length === 0 && dropModels.length === 0) {
309
346
  return;
310
347
  }
311
- const [visibleCategories, visibleModels] = await Promise.all([
312
- queryMissingCategories(iModel, new Set(viewData.categories.disabled)),
313
- queryMissingModels(iModel, new Set(viewData.models.disabled)),
314
- ]);
315
- viewState.categorySelector.addCategories(visibleCategories);
348
+ // Update model selector
316
349
  const modelSelector = viewState.modelSelector.clone();
317
- modelSelector.addModels(visibleModels);
350
+ modelSelector.addModels(addModels);
351
+ modelSelector.dropModels(dropModels);
318
352
  viewState.modelSelector = modelSelector;
319
353
  return;
320
354
  }
321
- if (!viewData.categories?.disabled) {
355
+ }
356
+ /**
357
+ * Determine, out of all categories in the iModel, which ones should be added or dropped
358
+ * (ie visible or hidden) based on the provided settings.
359
+ * @param iModel The current IModelConnection.
360
+ * @param viewData The view data containing the lists of enabled and disabled categories that will be applied.
361
+ * @param settings The settings for how to handle the visibility of enabled, disabled, and other category lists. Default is 'ignore' for all.
362
+ * @returns A list of categories that should be added or dropped (ie visible or hidden).
363
+ */
364
+ export async function sortCategories(iModel, viewData, settings) {
365
+ const addCategories = [];
366
+ const dropCategories = [];
367
+ if (settings?.enabled === "show") {
368
+ addCategories.push(...(viewData.categories?.enabled ?? []));
369
+ }
370
+ else if (settings?.enabled === "hide") {
371
+ dropCategories.push(...(viewData.categories?.enabled ?? []));
372
+ }
373
+ if (settings?.disabled === "show") {
374
+ addCategories.push(...(viewData.categories?.disabled ?? []));
375
+ }
376
+ else if (settings?.disabled === "hide") {
377
+ dropCategories.push(...(viewData.categories?.disabled ?? []));
378
+ }
379
+ if (settings?.other !== "ignore") {
380
+ const otherCategories = await queryMissingCategories(iModel, new Set([
381
+ ...(viewData.categories?.disabled ?? []),
382
+ ...(viewData.categories?.enabled ?? []),
383
+ ]));
384
+ if (settings?.other === "show") {
385
+ addCategories.push(...otherCategories);
386
+ }
387
+ else if (settings?.other === "hide") {
388
+ dropCategories.push(...otherCategories);
389
+ }
390
+ }
391
+ return { addCategories, dropCategories };
392
+ }
393
+ /**
394
+ * Apply the category settings to the view state.
395
+ * This function modifies the category selector of the view state based on the provided settings.
396
+ * @param iModel The current IModelConnection.
397
+ * @param viewState The view state to modify.
398
+ * @param viewData The view data containing the lists of enabled and disabled categories that will be applied.
399
+ * @param settings The settings for how to handle the visibility of enabled, disabled, and other category lists. Default is 'ignore' for all.
400
+ * @returns A promise that resolves when the category settings have been applied.
401
+ */
402
+ async function applyCategorySettings(iModel, viewState, viewData, settings) {
403
+ const { addCategories, dropCategories } = await sortCategories(iModel, viewData, settings);
404
+ if (addCategories.length === 0 && dropCategories.length === 0) {
322
405
  return;
323
406
  }
324
- const visibleCategories = await queryMissingCategories(iModel, new Set(viewData.categories.disabled));
325
- viewState.categorySelector.addCategories(visibleCategories);
407
+ // Update category selector
408
+ const categorySelector = viewState.categorySelector.clone();
409
+ categorySelector.addCategories(addCategories);
410
+ categorySelector.dropCategories(dropCategories);
411
+ viewState.categorySelector = categorySelector;
412
+ return;
326
413
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itwin/saved-views-react",
3
- "version": "0.9.8",
3
+ "version": "1.1.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",