From c490674ae753e469dbe5b554df00224e0671d9ac Mon Sep 17 00:00:00 2001 From: Robert Lukasonok <70327485+roluk@users.noreply.github.com> Date: Thu, 27 Jun 2024 18:56:01 +0300 Subject: [PATCH] Update documentation (#83) Update existing and fills in missing documentation for both published packages. Also fix broken URLs in package.json files. --- README.md | 8 +-- packages/saved-views-client/README.md | 17 +++++- packages/saved-views-client/package.json | 2 +- packages/saved-views-react/README.md | 53 ++++++++++++++++++- packages/saved-views-react/package.json | 2 +- packages/saved-views-react/src/SavedView.ts | 17 ++++++ .../SavedViewsClient/ITwinSavedViewsClient.ts | 10 ++-- .../saved-views-react/src/applySavedView.ts | 11 ++-- .../src/captureSavedViewData.ts | 15 +++++- .../src/captureSavedViewThumbnail.ts | 9 +++- .../saved-views-react/src/createViewState.ts | 26 +++++++-- .../saved-views-react/src/useSavedViews.tsx | 1 + 12 files changed, 146 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index db698594..c555cae6 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,11 @@ ## Packages -[@itwin/saved-views-client](./packages/saved-views-client/) +| Repository | npmjs | +| ----------------------------------------------------------- | --------------------------------------------------------------- | +| [@itwin/saved-views-client](./packages/saved-views-client/) | [link](https://www.npmjs.com/package/@itwin/saved-views-client) | +| [@itwin/saved-views-react](./packages/saved-views-react/) | [link](https://www.npmjs.com/package/@itwin/saved-views-react) | -[@itwin/saved-views-react](./packages/saved-views-react/) ## Development setup @@ -22,7 +24,7 @@ npm start ``` -* You can now edit TypeScript and CSS source files and the changes will automatically be reflected in the test app. +* You can now edit TypeScript and CSS source files and changes will automatically be reflected in the test app. ## Top-level commands diff --git a/packages/saved-views-client/README.md b/packages/saved-views-client/README.md index d9d4f9d2..b618aa68 100644 --- a/packages/saved-views-client/README.md +++ b/packages/saved-views-client/README.md @@ -11,10 +11,23 @@ import { ITwinSavedViewsClient } from "@itwin/saved-views-client"; const client = new ITwinSavedViewsClient({ // auth_token should have access to savedviews:read and savedviews:modify OIDC scopes - getAccessToken: async () => "auth_token", + getAccessToken: async () => "", }); -const response = await client.getSavedViewMinimal({ savedViewId: "id" }); +const { savedView } = await client.getSavedViewMinimal({ savedViewId: "" }); +console.log(savedView); /* +{ + id: "", + displayName: "Saved View", + creationTime: "2024-01-01T10:00:00.000Z", + lastModified: "2024-01-01T10:01:00.000Z", + groupId: undefined, + shared: false, + tags: [...], + savedViewData: { itwin3dView: {...} }, + extensions: [...], + _links: {...} +} */ ``` ## Contributing diff --git a/packages/saved-views-client/package.json b/packages/saved-views-client/package.json index e2a42283..8ef20e17 100644 --- a/packages/saved-views-client/package.json +++ b/packages/saved-views-client/package.json @@ -7,7 +7,7 @@ "url": "https://github.com/iTwin/saved-views.git", "directory": "packages/saved-views-client" }, - "homepage": "https://github.com/iTwin/saved-views/packages/saved-views-client", + "homepage": "https://github.com/iTwin/saved-views/tree/master/packages/saved-views-client", "author": { "name": "Bentley Systems, Inc.", "url": "https://www.bentley.com" diff --git a/packages/saved-views-react/README.md b/packages/saved-views-react/README.md index b7af53bf..fe283412 100644 --- a/packages/saved-views-react/README.md +++ b/packages/saved-views-react/README.md @@ -2,10 +2,59 @@ ## About -A collection of React components for building applications that deal with [Saved Views](https://developer.bentley.com/apis/savedviews/overview/). +A collection of utilities and React components for building iTwin applications that deal with [Saved Views](https://developer.bentley.com/apis/savedviews/overview/). ## Documentation +### [captureSavedViewData](./src/captureSavedViewData.ts) + +Captures current viewport state into serializable format. The returned data can later be used to restore viewport's view. + +```ts +const { viewData, extensions = [] } = await captureSavedViewData({ viewport }); +extensions.push(myCustomExtension(viewport)); +console.log({ viewData, extensions }); /* +{ + viewData: { itwin3dView: {...} }, + extensions: { + { extensionName: "EmphasizeElements", data: "{...}" }, + { extensionName: "MyCustomExtension", data: "my_custom_extension_data" }, + } +} */ +``` + +### [captureSavedViewThumbnail](./src/captureSavedViewThumbnail.ts) + +Generates Saved View thumbnail based on what is currently displayed on the viewport. + +```ts +const thumbnail = captureSavedViewThumbnail(viewport); +console.log(thumbnail); // "data:image/png;base64,iVBORw0KGoAAAANSUhEUg..." +``` + +### [applySavedView](./src/applySavedView.ts) + +Updates viewport state to match captured Saved View. + +```ts +// Capture viewport state +const savedViewData = await captureSavedViewData({ viewport }); +// Restore viewport state +await applySavedView(iModel, viewport, savedViewData); +``` + +### [createViewState](./src/createViewState.ts) + +Creates ViewState object out of Saved View data. It provides a lower-level access to view data for advanced use. + +```ts +const viewState = await createViewState(iModel, savedViewData.viewData); +await applySavedView(iModel, viewport, savedViewData, { viewState }); + +// The two lines above are equivalent to +await applySavedView(iModel, viewport, savedViewData); +``` + ### React components * [SavedViewTile](./src/SavedViewTile/SavedViewTile.tsx) @@ -42,7 +91,7 @@ export function SavedViewsWidget(props) { } ``` -### useSavedViews +### [useSavedViews](./src/useSavedViews.tsx) [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/). diff --git a/packages/saved-views-react/package.json b/packages/saved-views-react/package.json index cda5556e..034498bf 100644 --- a/packages/saved-views-react/package.json +++ b/packages/saved-views-react/package.json @@ -7,7 +7,7 @@ "url": "https://github.com/iTwin/saved-views.git", "directory": "packages/saved-views-react" }, - "homepage": "https://github.com/iTwin/saved-views", + "homepage": "https://github.com/iTwin/saved-views/tree/master/packages/saved-views-react", "author": { "name": "Bentley Systems, Inc.", "url": "https://www.bentley.com" diff --git a/packages/saved-views-react/src/SavedView.ts b/packages/saved-views-react/src/SavedView.ts index 864d9afe..eac4ebff 100644 --- a/packages/saved-views-react/src/SavedView.ts +++ b/packages/saved-views-react/src/SavedView.ts @@ -22,7 +22,24 @@ export interface SavedView { } export interface SavedViewExtension { + /** Extension identifier. Saved View cannot contain multiple extensions that share the same `extensionName`. */ extensionName: string; + + /** + * Serialized extension data. + * + * @example + * const extension = { + * // Unique identifier makes extension data format portable between applications because it avoids collision + * // with different implementations + * extensionName: "CustomHighlight_$5be36494-ae03-4400-bb80-24ffd9db2a87", + * data: JSON.stringify({ + * description: "For illustrative purposes only. We do not provide implementation for this extensionName." + * highlightColor: "#f05599", + * models: ["0x20000000006"], + * }), + * }; + */ data: string; } diff --git a/packages/saved-views-react/src/SavedViewsClient/ITwinSavedViewsClient.ts b/packages/saved-views-react/src/SavedViewsClient/ITwinSavedViewsClient.ts index 5f088868..4c95f9cc 100644 --- a/packages/saved-views-react/src/SavedViewsClient/ITwinSavedViewsClient.ts +++ b/packages/saved-views-react/src/SavedViewsClient/ITwinSavedViewsClient.ts @@ -14,14 +14,14 @@ import type { } from "./SavedViewsClient.js"; interface ITwinSavedViewsClientParams { - /** @default "https://api.bentley.com/savedviews" */ - getAccessToken: () => Promise; - /** * Authorization token that grants access to iTwin Saved Views API. The token should be valid for `savedviews:read` * and `savedviews:modify` OIDC scopes. - */ - baseUrl?: string | undefined; + */ + getAccessToken: () => Promise; + + /** @default "https://api.bentley.com/savedviews" */ + baseUrl?: string | undefined; } export class ITwinSavedViewsClient implements SavedViewsClient { diff --git a/packages/saved-views-react/src/applySavedView.ts b/packages/saved-views-react/src/applySavedView.ts index e184515c..78a164f3 100644 --- a/packages/saved-views-react/src/applySavedView.ts +++ b/packages/saved-views-react/src/applySavedView.ts @@ -20,8 +20,8 @@ export interface ApplySavedViewSettings { all?: ApplyStrategy | undefined; /** - * How to handle captured {@link ViewState} data. The default behavior is to generate a new `ViewState` object and - * apply it to viewport. + * How to handle captured {@link ViewState} data. The default behavior is to generate a new `ViewState` object out of + * {@linkcode SavedView.viewData} and apply it to viewport. * * You can optionally provide a pre-made `ViewState` instance to conserve resources. It is usually obtained from * {@link createViewState} result. @@ -34,6 +34,9 @@ export interface ApplySavedViewSettings { * applySavedView(iModel, viewport1, savedView, { viewState }), * applySavedView(iModel, viewport2, savedView, { viewState }), * ]); + * + * @remarks + * When neither `SavedView.viewData` nor `ViewState` is provided, current {@linkcode Viewport.view} is preserved. */ viewState?: ApplyStrategy | ViewState | undefined; @@ -70,8 +73,8 @@ type ApplyStrategy = "apply" | "reset" | "keep"; * // e.g. when applying the same Saved View to multiple viewports * const viewState = await createViewState(iModel, savedView); * await Promise.all([ - * applySavedView(iModel, firstViewport, savedView, { viewState }), - * applySavedView(iModel, secondViewport, savedView, { viewState }), + * applySavedView(iModel, viewport1, savedView, { viewState }), + * applySavedView(iModel, viewport2, savedView, { viewState }), * ]); */ export async function applySavedView( diff --git a/packages/saved-views-react/src/captureSavedViewData.ts b/packages/saved-views-react/src/captureSavedViewData.ts index 460ae6ae..748921d8 100644 --- a/packages/saved-views-react/src/captureSavedViewData.ts +++ b/packages/saved-views-react/src/captureSavedViewData.ts @@ -44,7 +44,20 @@ interface CaptureSavedViewDataResult { extensions: SavedViewExtension[] | undefined; } -/** Captures current viewport state into serializable format. */ +/** + * Captures current {@link Viewport} state into serializable format. The returned data can later be used to restore + * viewport's view. + * + * @example + * import { captureSavedViewData, captureSavedViewThumbnail } from "@itwin/saved-views-react"; + * + * async function saveViewport(viewport) { + * const { viewData, extensions = [] } = await captureSavedViewData({ viewport }); + * const myExtensions = captureMyCustomState(viewport); + * const thumbnail = captureSavedViewThumbnail(viewport); + * return { thumbnail, viewData, extensions: extensions.concat(myExtensions) }; + * } + */ export async function captureSavedViewData(args: CaptureSavedViewDataArgs): Promise { return { viewData: await createSavedViewVariant(args.viewport), diff --git a/packages/saved-views-react/src/captureSavedViewThumbnail.ts b/packages/saved-views-react/src/captureSavedViewThumbnail.ts index 5d7622bc..e7c2d00d 100644 --- a/packages/saved-views-react/src/captureSavedViewThumbnail.ts +++ b/packages/saved-views-react/src/captureSavedViewThumbnail.ts @@ -6,7 +6,14 @@ import type { ImageBuffer } from "@itwin/core-common"; import { getCenteredViewRect, imageBufferToCanvas, type Viewport } from "@itwin/core-frontend"; import { Point2d } from "@itwin/core-geometry"; -/** Generates Saved View thumbnail based on what is currently visible in the {@linkcode viewport}. */ +/** + * Generates Saved View thumbnail based on what is currently displayed on the {@linkcode viewport}. + * @returns base64-encoded URL string + * + * @example + * const thumbnail = captureSavedViewThumbnail(viewport); + * console.log(thumbnail); // "data:image/png;base64,iVBORw0KGoAAAANSUhEUg..." + */ export function captureSavedViewThumbnail(viewport: Viewport, width = 280, height = 200): string | undefined { const thumbnail = getThumbnail(viewport, width, height); if (!thumbnail) { diff --git a/packages/saved-views-react/src/createViewState.ts b/packages/saved-views-react/src/createViewState.ts index 354d0b91..9853ec1e 100644 --- a/packages/saved-views-react/src/createViewState.ts +++ b/packages/saved-views-react/src/createViewState.ts @@ -20,9 +20,14 @@ import { extractDisplayStyle, extractDisplayStyle3d } from "./translation/displa export interface ViewStateCreateSettings { /** - * Normally before {@link createViewState} function returns a {@link ViewState}, its {@linkcode ViewState.load} method - * is called and awaited. You may skip this step if you intend to perform it later. + * Normally {@link createViewState} function invokes and awaits {@linkcode ViewState.load} method before returning. + * You may skip this step if you intend to perform it later. * @default false + * + * @example + * const viewState = await createViewState(iModel, savedView.viewdata, { skipViewStateLoad: true }); + * viewState.categorySelector.addCategories(""); + * await viewState.load(); */ skipViewStateLoad?: boolean | undefined; @@ -33,14 +38,25 @@ export interface ViewStateCreateSettings { modelAndCategoryVisibilityFallback?: "visible" | "hidden" | undefined; } +/** + * Creates {@link ViewState} object out of Saved View data. It provides a lower-level access to view data for advanced + * use. + * + * @example + * const viewState = await createViewState(iModel, savedViewData.viewData); + * await applySavedView(iModel, viewport, savedViewData, { viewState }); + * + * // The two lines above are equivalent to + * await applySavedView(iModel, viewport, savedViewData); + */ export async function createViewState( iModel: IModelConnection, - savedViewData: ViewData, + viewData: ViewData, settings: ViewStateCreateSettings = {}, ): Promise { - const viewState = await createViewStateVariant(iModel, savedViewData); + const viewState = await createViewStateVariant(iModel, viewData); if (settings.modelAndCategoryVisibilityFallback === "visible") { - await unhideNewModelsAndCategories(iModel, viewState, savedViewData); + await unhideNewModelsAndCategories(iModel, viewState, viewData); } if (!settings.skipViewStateLoad) { diff --git a/packages/saved-views-react/src/useSavedViews.tsx b/packages/saved-views-react/src/useSavedViews.tsx index 14c91f3a..412a6011 100644 --- a/packages/saved-views-react/src/useSavedViews.tsx +++ b/packages/saved-views-react/src/useSavedViews.tsx @@ -66,6 +66,7 @@ type SavedViewUpdateProps = WriteableSavedViewProperties & { id: string; }; * the store is performed via {@linkcode SavedViewsClient} interface which could communicate, for instance, with * [iTwin Saved Views API](https://developer.bentley.com/apis/savedviews/overview/) using `ITwinSavedViewsClient`. * + * @remarks * Note on the current implementation limitations. While the result of the first update action is reflected immediately, * subsequent actions are put in a queue and executed serially. This may cause the UI to feel sluggish when user makes * changes to Saved Views faster than the Saved Views store can be updated.